




已阅读5页,还剩40页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
一、移植目的1、结合命令界面的改进,实现文件系统与命令界面的结合使用。2、在命令界面中实现以下文件系统操作命令:flist-列出当前目录下的文件;fmkdir-在当前目录下创建目录;fchgdir-改变当前目录;fread-读取文件内容;fwrite-新建文件并写入。二、移植条件1、可以识别参数的串口命令界面。2、FatFS007e版本。三、对命令界面所实现功能的描述1、超级终端设置。首先打开串口终端,设置波特率115200,8位数据,无奇偶校验,无流控,终端仿真选择ANSIW,asicc码设置为以换行符结尾(以前设置的,原因已经忘了),反正这样设置了能够正常工作。 开发板复位后,终端显示:*nthq2004 编写的简单命令接口!*Sh然后可以在这里输入命令执行。比如现在支持的命令包括:help,cls,ledon,ledoff,time,temp,i2cwr,i2crd,sdrd,sdwr,共10个命令。比如输入help me命令,则会显示命令界面当前支持的所有命令,并显示当前所有输入的命令参数。所以命令执行过程的分析以help me为例,help是命令,me是参数。2、串口终端命令输入过程在串口终端输入一个字符时,其工作工程为:void USART1_IRQHandler(void) /该函数在文件stm32f10x_it.c中 OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); OSIntNesting+; /中断嵌套计数 OS_EXIT_CRITICAL();if ( USART_GetITStatus ( USART1, USART_IT_RXNE )!= RESET ) Uart_ReceiveChar(); /串口接收字符 OSIntExit(); /这里可以触发任务切换软中断 串口接收到字符引起终端、然后调用函数Uart_ReceiveChar()获取字符并放入串口消息队列,void Uart_ReceiveChar (void) /该函数在文件uart.c中 u32 RecChar; RecChar = (u32)(USART1-DR & 0xFF); OSQPost ( UartMsgOSQ, ( void* )RecChar ); /将字符指针化放入消息队列,这里接用了周慈航教授书中的方法。在task_uartcmd.c文件创建的串口界面任务中,case UartStateInput:/如果出于输入状态 UartCharIn=Uart_GetChar();/读取输入按键UartCharIn &= 0x7F;该函数从消息队列中取得从串口接收到的字符,然后放入到串口命令缓冲数组。 UartCmdBufUartCharCount=UartCharIn; /将字符存入缓冲区当前位置UartCharCount +=1;Uart_PutChar( UartCharIn );/将有效字符输出。这是在用户电脑的超级终端上进行回显。 如果用户在超级终端上按下了回车键: if(UartCharIn=n) / 如果按下了Enter键,它是触发软件状态机从输入态 / 进入命令解释执行态的关键。 UartCmdBufUartCharCount=0; /命令缓冲区以0结束Uart_PutString( rn ); /显示器上回显换行UartCmdState=UartStateExe; /如果输入过字符,进入命令解释态UartCharCount=0; 3、串口命令执行过程进入命令执行态后,首先进行命令缓冲字符串的解析,从中分析出命令和参数: case UartStateExe:/如果处于命令执行态 UartParseCmdBuf(UartCmdBuf,&CmdArg); /命令缓冲字符串的解析解析后得到一个结构数组,指明参数的个数和一个指向参数指针数组的指针:该数组的第一个指针指向命令字符串,第二个开始指向参数。 UartCmdStr=CmdArg.Argv0; /解析命令缓冲区,得到命令结构信息,我这里还只支持简单命令 /所以处理很简单,直接获得命令字符串,以后可以扩展。 for(UartCmdIndex=0; UartCmdIndexUartCmdMaxCount; UartCmdIndex+ ) / 在命令表里搜索对应命令字符串UartCompResult=strcmp(char*)UartCmdStr, (char*)(UartCmdStrTableUartCmdIndex) );/命令字符串比较的结果if (UartCompResult=0) /如果在字符串表里找到。返回0. break; /此时CmdIndex对应的值为命令在命令表里的索引。 if ( UartCmdIndexUartCmdMaxCount) /找到对应命令,调用相应函数处理 argc=CmdArg.Argc; for ( j=0; jargc; j+ ) argvj=(void*)CmdArg.Argvj; UartCmdTableUartCmdIndex.UartCmdFunc(argc,argv);/利用 实际上输入命令help me,此时调用的函数就是: UartCmdTable0.UartCmdFunc=UartCmdHelp;该函数的源代码在文件uartcmd.c文件中:void UartCmdHelp(u8 argc,void *argv) u8 i; Uart_PutString(You entered the parameter: ); for ( i=1; iargc; i+ ) Uart_PutString( argvi ); /这是将后面的参数一一列出来。Uart_PutString( ); Uart_PutString( rn ); /这是将命令界面支持的命令列举出来。 Uart_PutString(cls ); Uart_PutString(ledon ); Uart_PutString(ledoff ); Uart_PutString(time ); Uart_PutString(temp ); Uart_PutString(i2cwr ); Uart_PutString(i2crd rn); Uart_PutString(sdrd ); Uart_PutString(sdwr rn);四、建立文件系统命令执行框架1、添加新的命令初期目标是5个命令,为了使编写过程简化和快捷,先实现一个命令fread,文件系统移植成功后,再添加剩下的命令。u8* UartCmdStrTableUartCmdMaxCount= /串口命令字符串help,cls,ledon,ledoff,time,temp,i2cwr,i2crd,sdrd,sdwr,fread /先添加命令字符串;#define UartCmdMaxCount 11/现在支持11个内部命令UartCmdTable10.UartCmdFunc=UartCmdFRead; /初始化命令数据结构,这是一个新的命令实现函数,在新的文件fileop.c中实现。该文件加入userlib组。Fileop.c中编写新的函数:void UartCmdFRead(u8 argc,void *argv) Uart_PutString(You put the command: fread. rn);然后在uartcmd.h中添加该函数的声明。然后Rebuild整个工程,下载程序,进行验证。在串口输入命令:fread,显示You put the command: fread.说明命令添加成功。但是忘了在帮助命令中添加该命令的说明,补充一下。2、重新设计fread命令的功能和程序结构构思首先,该命令打开一个文件,读入一个扇区到缓冲区,然后在串口终端上显示;输入任意键,持续该过程。直到输入ESC键,命令执行完成,回到shell界面。这个程序内容的编写就设计到文件系统的操作了,结合以前对fatfs的分析,首先确定文件操作的流程:首先要调用函数FRESULT f_mount (BYTE, FATFS*),进行文件系统的注册,提供逻辑盘的数字序号(要小于逻辑盘个数),提供一个用户的文件系统对象指针,然后让系统内部的文件对象指针数组中对应于该文件系统序号的指针指向用户提供的文件系统对象,共其他函数使用。在使用这个函数之前,先定义一个用户的文件系统对象。然后调用函数FRESULT f_open (FIL*, const XCHAR*, BYTE),打开文件,需要提供参数文件结构对象指针和文件字符串指针。用户先定义一个文件结构对象,然后将地址传入该函数,用户利用字符串对应文件的信息填充该文件结构对象。然后就可以调用函数FRESULT f_read (FIL*, void*, UINT, UINT*);将用户指定文件的内容读入到缓冲区。程序框架如:void UartCmdFRead(int argc,void *argv) FIL FileInf; u8 FileBuf512; u16 i; u32 ByteToRead,ByteRead; u8 res; res=f_open(&FileInf,FilePath,FA_READ|FA_OPENAWAYLS); if ( res!=FR_OK) Uart_PutStr( File Open Error!);return res; do res=f_read(&FileInf,FileBuf,ByteToRead,&ByteRead); /要进去看一看,读后有没有自动调整指针。 if ( res!=FR_OK) Uart_PutStr( File Read Error!);return; for(i=0;i512;i+)Uart_PutChar(FileBufi; while ( Uart_GetChar()!=0x1c );3、搭建框架因为在命令处理函数中调用了f_open等函数,所以先要包含头文件ff.h。然后在interg.h文件中做了如下改变:/typedef enum FALSE = 0, TRUE BOOL;#define BOOL bool;/因为stm32的库中已经定义过TRUE和FALSE。void UartCmdFRead(u8 argc,void *argv) FIL FileInf; u8 FileBuf512; u16 i; UINT ByteToRead,ByteRead; FRESULT res; res=f_open(&FileInf,argv1,FA_READ|FA_OPEN_ALWAYS); if ( res!=FR_OK) Uart_PutString( File Open Error!);return; do res=f_read(&FileInf,FileBuf,ByteToRead,&ByteRead); /要进去看一看,读后有没有自动调整指针。 if ( res!=FR_OK) Uart_PutString( File Read Error!);return; for(i=0;i512;i+)Uart_PutChar(FileBufi); while ( Uart_GetChar()!=0x1B ); 然后单独编译fileop.c,成功。4、加入文件系统文件接下来在工程里面加入fatfs文件中,将ff.c和diskio.c两个文件加入,然后单独进行编译,看有没有什么问题。#define BOOL bool;这个后面千万不能加分号,否则就会出问题。#include stm32f10x_type.h 在integer.h前面要加入stm32库中数据类型的定义,因为#define BOOL bool这句定义要用到bool的原定义。然后单独编译ff.c,成功,没有错误。单独编译diskio.c,没有错误,但有很多警告,主要是底层驱动函数还没有定义和声明。接下来的工作就是编写底层驱动了。五、diskio.c中底层驱动程序的编写。首先删除有关USB、MMC、ATA的定义,因为我这个板上只有SD卡。1、disk_initialize函数DSTATUS disk_initialize (BYTE drv/* Physical drive nmuber (0.) */)这个函数的参数是物理驱动号,在我的文件系统中逻辑盘和物理盘号都设为0,所以这个参数基本不用理会。这个函数的返回值是DSTATUS类型,它是这样定义的:typedef BYTEDSTATUS;#define STA_NOINIT0x01/* Drive not initialized */#define STA_NODISK0x02/* No medium in the drive */#define STA_PROTECT0x04/* Write protected */#define STA_SUCCESS0x00/成功时返回0.所以用户自己的磁盘初始化函数返回0、1、2、4几个值就行了。我在前面已经编写过SD的操作函数了,在sduser.c文件中,SD_Config()函数就是用于SD卡的初始化的。所以只要在文件头部包含sdcard.h文件,就可以调用该函数了。以下是最终函数:int result;result = SD_Config();if ( result=0 ) return 0;return STA_NOINIT; 2、disk_status函数获取磁盘状态,如果初始化成功,磁盘时钟处于可用状态,直接返回0。DSTATUS disk_status (BYTE drv/* Physical drive nmuber (0.) */)3、disk_read函数的编写DRESULT disk_read (BYTE drv,/* Physical drive nmuber (0.) */BYTE *buff,/* Data buffer to store read data */DWORD sector,/* Sector address (LBA) */BYTE count/* Number of sectors to read (1.255) */)实际上是三个参数:缓冲区地址、起始扇区和扇区的个数,最多255个扇区。我在sduser.c文件中编写了SD卡读多个扇区的函数u8 SD_ReadMultiBlock( u32 BlockIndex, u32 BlockNum, u8* ReadBuf ),只是顺序优点不同,稍微改一下。其返回参数是DRESULT类型,这个数据是这么定义的:typedef enum RES_OK = 0,/* 0: Successful */RES_ERROR,/* 1: R/W Error */RES_WRPRT,/* 2: Write Protected */RES_NOTRDY,/* 3: Not Ready */RES_PARERR/* 4: Invalid Parameter */ DRESULT;u8 result;result = SD_ReadMultiBlock(sector,count,buff );/要遵循定义的参数顺序。if ( result=0 ) return RES_OK;return RES_ERROR;4、disk_write函数与读函数类似,不再多说。u8 result;result = SD_WriteMultiBlock(sector,count,buff );/要遵循定义的参数顺序。if ( result=0 ) return RES_OK;return RES_PARERR;5、disk_ioctl函数,直接返回RES_OK.对diskio.c进行单独编译,成功通过。对整个工程进行编译,显示get_fattime这个函数没有定义。添加函数定义,直接返回0,整个工程编译通过。六、下载、测试将程序下载后,进行测试,发现显示读文件错误,才想起来,还没有进行文件系统安装呢,也就是要调用f_mount函数。添加好f_mount函数后,编译下载。出现了一个奇怪的问题:每次调用f_read()函数时,就会出错,并且SD卡只有重新上电才能读出。通过设置断点,发现每次读取文件躲在目录的扇区时,都会错误返回。最好发现问题是一个变量没有正确的初始化造成的:u8 SD_ReadMultiBlock( u32 BlockIndex, u32 BlockNum, u8* ReadBuf ) u16 i=0,j; /就是这里的这个i变量的初始化,花了我两个钟头的时间才调试出来。 u8 TmpByte; BlockIndex GBK - GB18030 ,他们是包含关系,GB我猜是 国标 的意思 k 可能是 扩展, 这些编码都是书面协议,要在计算机内部表示。所以GB2312的内存值须在原来的每个字节值(区位码)上加0x80得到机内码,加上0x80是为了使得每个字节的最高位为1,这样就可以在内存中区分汉字和ASCII了因为ASCII的最高位都为0. 但是后来要在GB2312上扩展(得到GBK,GB18030),就需要更多的编码空间,所以GBK,GB18030就没有要求第二个字节的的最高位为1了,而是通过第一个字节来判断这是一个字节的ASCII还是两个字节的GBK. GB2312、GBK都属于双字节字符集 (DBCS)。所有的国标码汉字及符号组成一个94行94列的二维代码表中。在此方阵中,每一行称为一个区,每一列称为一个位。这个方阵实际上组成一个有94个区(编号由01到94),每个区有94个位(编号由01到94)的汉字字符集。每两个字节分别用两位十进制编码,前字节的编码称为区码,后字节的编码称为位码,此即区位码,其中,高两位为区号,低两位为位号。区位码 国标(GBK) 内码汉 1A1A 3A3A BABA国标 = 区位码 + 0x20 (每个字节)内码 = 国标(GBK) + 0x80 (每个字节)目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。输入码就是使用英文键盘输入汉字时的编码。目前,我国已推出的输入码有数百种,但用户使用较多的约为十几种,按输入码编码的主要依据,大体可分为顺序码、音码、形码、音形码四类,如“保”字,用全拼,输入码为码为“BAO”,用区位码,输入码为“1703”,用五笔字型则为“WKS”。“这是一个长文件名.txt”文件在14号簇,2072扇区,读出来如下:CE D2 B5 C4 D0 C4 D6 D0 D3 D0 D2 BB B8 F6 C3 CE CF EB A1 A361 62 63 64 65 66 67 2C 31 32 33 34 35 36 37 2E 00 00 00 00,abcdefg和1234567是我后面修改后加的,说明这个编码不是unicode编码。因为汉字两个、英文一个编码,这应该就是acii码加上汉字国标机内码。汉字我的编码是: CE D2减去80 80 后得到4e 52,应该是国标码,减去20 20 得到2E 32就是区位码。而我的unicode码是0x62 11=25105。若果造一个内码-unicode码转换表,应该是在第46区 50位。2312转Unicode:基于区位码计算编码库偏移地址,这个大家都做过,编码库的字节大小为2倍2312字库,库里只存Unicode码Unicode转2312:建立64K编码库(128K 字节,库里只存2312码, 不存在2312码的用0x0000补齐),用Unicode码的值做偏移地址直接查找对应的2312码,不用计算中文字库就比较麻烦了。先取得汉字的内码,如使用GBK,上的内码是0xc9,0xcf。在查到在对应字库中位置。取得该汉字的字库。 一般字库文件是按照GB 2312-80标准,也就是通常所说的国标码或区位码的标准排列的。国标码分为 94 个区(Section),每个区 94 个位(Position),所以也称为区位码。其中0109 区为符号、数字区,1687 区为汉字区。而 1015 区、8894 区是空白区域。 如何取得汉字的区位码呢?在计算机处理汉字和ASCII字符时,使每个ASCII字符占用1个字节,而一个汉字占用两个字节,其值称为汉字的内码。其中第一个字节的值为区号加上32(20H),第二个字节的值为位号加上32(20H)。为了与ASCII字符区别开,表示汉字的两个字节的最高位都是1,也就是两个字节的值都又加上了128(80H)。这样,通过汉字的内码,就可以计算出汉字的区位码。 具体算式如下: qh=c1-32-128=c1-160 wh=c2-32-128=c2-160 或 qh=c1-0xa0 wh=c2-0xa0 qh,wh为汉字的区号和位号,c1,c2为汉字的第一字节和第二字节。 根据区号和位号可以得到汉字字模在文件中的位置: location=(94*(qh1)+(wh1)*一个点阵字模的字节数。5、有关字符编码的最后总结上面有关编码的内容都是在百度上搜到的,我把重点标示了出来。我的收获是对于汉字在机内存储和如何显示的原理有了简单的了解。我们在用输入法输入汉字的时候,看到了“真正的字”,但实际文件中一般只是存储的汉字内码。它如何让我们看到的呢?通过简单的转换得到区位码,区位码实际上指出了该汉字在字库文件中字符点阵储位置。通过获取点阵,在屏幕上显示汉字。有时候,汉字在内部以unicode码的格式存储,比如文件系统中长文件名,都是这样。那我们要操作文件系统,怎么样才能由内码得到unicode码呢?这个可以用一张转换表来实现,这张表可以以汉字区位表为基础,共需要94*94*2这么大,大概20K左右。如果要显示文件系统中汉字命名的文件,需要先由unicode码得到内码,然后再获取字型数据显示,这时也可以用一张表,但这个表稍微大一些,因为unicode有65536个,这张表需要128k大。八、读文件执行流程分析l 输入读命令fread 0:/111/aaa.txt在我的SD卡,根目录下有两个可见文件aaa.txt和ccc.txt,两个目录111和222。目录111下面有三个文件aaa.txt、bbb.txt、ccc.txt。上面这个命令的作用是读取111目录下aaa.txt的内容,这个文件的大小是0x0564,里面都是12345这些数字。在我的程序的开头,首先执行了这么一个函数:FATFS FileSys; res=f_mount(0,&FileSys); /这个函数的作用是将ff.c内定义的FATFS *FatFs_DRIVES这个文件系统结构指针指向用户定义的文件系统结构。初始化程序填充这个结构,而其它函数可以使用里面的参数。程序对输入命令进行解析,得到argc=2, argv0=”fread”, argv1=”0:/111/aaa.txt”。系统调用UartCmdFRead函数。l 文件执行到f_open(&FileInf,argv1,FA_READ | FA_OPEN_ALWAYS);FileInf是前面定义的一个文件信息结构体,将指针传递给这个函数,是程序获得argv1文件的信息后,填充该结构体的。单步执行进去,首先定义了DIR dj,这是目录信息结构体,然后NAMEBUF(sfn, lfn);作用是为文件名准备空间。Sfn所指向的空间最后存放标准短文件名,比如aaa.txt,存进去应该是aaa txt。u 然后执行res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);这个函数的主要作用是初始化磁盘、检查是否存在文件系统等,这里这个path= argv1= ”0:/111/aaa.txt”。进入到这个函数,首先const XCHAR *p = *path;这个path= &argv1,而p= argv1,实际指向0:/中的0。然后执行vol = p0 - 0;if (vol drive);这句对磁盘进行初始化,成功则磁盘可用。u fmt = check_fs(fs, bsect = 0);这句是检查是否存在文件系统。进入这个函数,if (disk_read(fs-drive, fs-win, sect, 1) != RES_OK)/*读取引导扇区 */return 3; 这里读入缓冲区是文件系统结构体中自带的缓冲区,其地址为0x2000,1FB0。if (LD_WORD(&fs-winBS_55AA) != 0xAA55)/* 检查引导标志 */return 2;if (LD_DWORD(&fs-winBS_FilSysType32) & 0xFFFFFF) = 0x544146)return 0; /这三个字符表式FATl 回到chk
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 乐理考试题及答案小学
- 消防安全生产人考试题库及答案解析
- 矿山电工考试题及答案
- 课件显示不完整问题
- 教师招聘之《小学教师招聘》考前自测高频考点模拟试题及完整答案详解(典优)
- 课件时间修改
- 铝及铝合金熔铸工晋升考核试卷及答案
- 2025年中国纸浆模包装制品数据监测报告
- 轻冶沉降工技能操作考核试卷及答案
- 经济政治考试题及答案
- 《火灾调查 第2版》 课件 第5-7章 火灾调查分析、放火火灾调查、电气火灾调查
- 2024-2025学年人教版数学九年级上册第一次月考试题
- 消化道出血诊疗规范2022版
- 退休返聘人员劳务合同范本
- 储能柜质保协议
- 教学课件 《自动化制造系统(第4版)》张根保
- KLA缺陷检查培训
- 收购组织财务尽职调查资料清单
- 产业链风险管理
- 四川省普通高中2022-2023学年学业水平考试英语试题
- 通信管道管线施工安全操作规程
评论
0/150
提交评论