Github

从这里开始,整个RTT学习过程中对工程的完善和开发都记录在Github上的一个项目中,项目还会随着学习的深入进行更新
项目URL:https://github.com/HITLIVING/-RT-Thread-.git
读者可以通过Pull下来整个项目进一步了解
需要格外关注
drv_lil9341_lcd.c
drv_fonts.c
这两个文件

前期准备

  • RT-Thread文件系统配置成功,且可以正常使用
  • 可以通过Finsh命令行操作存储体,包括新建文件,新建文件夹,存内容,读内容
  • 通过字模软件或例程中取到标准ASCII码的字模数据数组
  • RT-Thread文件系统配置成功,将 存储块设备格式化 这一步骤注释掉,避免反复刷掉数据

概述

在使用LCD等显示设备时,需要字模数据
而字模数据往往是数量庞大的字符数组,如果直接作为常量存到芯片ROM中,会占据庞大的空间造成资源浪费
这类数据可以放置在外置存储体内,需要时通过驱动芯片读取字模数据,即可为芯片内的存储空间腾出地方
以字模的存取为例,讲解RT-Thread文件系统的实际使用
将字模数据以txt文件格式,存储到已经按照文件系统格式化好的存储体中
因为实验以Flash为存储体,无法像SD卡那样,通过电脑存储文件,需要单片机提供下载途径,即新建文件,写数据

接口函数

头文件

  1. #include <dfs_posix.h> /* 当需要使用文件操作时,需要包含这个头文件 */

打开和关闭文件

打开或创建一个文件可以调用下面的 open() 函数:

  1. int open(const char *file, int flags, ...);
参数 描述
file 打开或创建的文件名
flags 指定打开文件的方式,取值可参考下表
返回 ——
文件描述符 文件打开成功
-1 文件打开失败

一个文件可以以多种方式打开,并且可以同时指定多种打开方式。例如,一个文件以 O_WRONLY 和 O_CREAT 的方式打开,那么当指定打开的文件不存在时,就会先创建这个文件,然后再以只读的方式打开。

参数 描述
O_RDONLY 只读方式打开文件
O_WRONLY 只写方式打开文件
O_RDWR 以读写方式打开文件
O_CREAT 如果要打开的文件不存在,则建立该文件
O_APPEND 当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式添加到文件的尾部
O_TRUNC 如果文件已经存在,则清空文件中的内容

当使用完文件后若不再需要使用则可使用 close() 函数关闭该文件,而 close() 会让数据写回磁盘,并释放该文件所占用的资源。

  1. int close(int fd);
参数 描述
fd 文件描述符
返回 ——
0 文件关闭成功
-1 文件关闭失败

读写数据

读取文件内容可使用 read() 函数:

  1. int read(int fd, void *buf, size_t len);
参数 描述
fd 文件描述符
buf 缓冲区指针
len 读取文件的字节数
返回 ——
int 实际读取到的字节数
0 读取数据已到达文件结尾或者无可读取的数据
-1 读取出错,错误代码查看当前线程的 errno

该函数会把参数 fd 所指的文件的 len 个字节读取到 buf 指针所指的内存中。
此外文件的读写位置指针会随读取到的字节移动。
向文件中写入数据可使用 write() 函数:

  1. int write(int fd, const void *buf, size_t len);
参数 描述
Fd 文件描述符
buf 缓冲区指针
len 写入文件的字节数
返回 ——
int 实际写入的字节数
-1 写入出错,错误代码查看当前线程的 errno

该函数会把 buf 指针所指向的内存中 len 个字节写入到参数 fd 所指的文件内。
此外文件的读写位置指针会随着写入的字节移动。

指定读写位置函数

  1. off_t lseek(int Fd, off_t offset, int whence);
参数 描述
Fd 文件描述符
offset 文件指针偏移量
whence 附加说明
返回 ——
off_t 当前指针位置
-1 写入出错,错误代码查看当前线程的 errno

参数 whence 为下列其中一种:
SEEK_SET 参数offset 即为新的读写位置.
SEEK_CUR 以目前的读写位置往后增加offset 个位移量.
SEEK_END 将读写位置指向文件尾后再增加offset 个位移量. 当whence 值为SEEK_CUR 或
SEEK_END 时, 参数offet 允许负值的出现.

当whence 值为SEEK_CUR 或SEEK_END 时, 参数offet 允许负值的出现.

该函数用来读取文件的特定内容

下列是较特别的使用方式:
1) 欲将读写位置移到文件开头时:lseek(int fildes, 0, SEEK_SET);
2) 欲将读写位置移到文件尾时:lseek(int fildes, 0, SEEK_END);
3) 想要取得目前文件位置时:lseek(int fildes, 0, SEEK_CUR);

上机实验

  • 在指定文件中下载内容
  1. //手动下载字符指令
  2. #include <dfs_posix.h>
  3. /*
  4. * 常用ASCII表,偏移量32,大小:16(高度)* 8 (宽度)
  5. */
  6. const uint8_t ASCII8x16_Table [ ] = {0x00 0x01 0x02....
  7. .....
  8. .....
  9. //@conslons字体,阴码点阵格式,逐行顺向取摸
  10. };
  11. static int Download(int argc, char *argv[])
  12. {
  13. int fd;
  14. char Filename[] = "/ASCII8x16_Table.txt";
  15. fd = open(Filename, O_WRONLY | O_CREAT);
  16. if (fd>= 0)
  17. {
  18. write(fd, ASCII8x16_Table, sizeof(ASCII8x16_Table));
  19. close(fd);
  20. rt_kprintf("Write done.\n");
  21. }
  22. return 0;
  23. }
  24. /* 导出到 msh 命令列表中 */
  25. MSH_CMD_EXPORT(Download, download fonts to flash);
  • 读取指定文件的指定内容
  1. uint8_t byteCount, bitCount,fontLength;
  2. uint16_t ucRelativePositon;
  3. uint8_t *Pfont;
  4. //对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号)
  5. ucRelativePositon = cChar - ' ';
  6. //每个字模的字节数
  7. fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8;
  8. //-------------------------------------------------------------------------
  9. //Get font data from flash
  10. switch(LCD_Currentfonts->Width)
  11. {
  12. case 8:
  13. {
  14. rt_int32_t fd = 0;
  15. //读取缓存区
  16. rt_uint8_t buffer[8*16/8] = {0};
  17. //打开指定文件,需要先保证文件存在,否则会导致读取失败
  18. fd = open("/ASCII8x16_Table.txt", O_RDONLY);
  19. Pfont = buffer;
  20. if (fd>= 0)
  21. {
  22. //由对ascii码表偏移和每个字模的字节数定位读取文件的内容
  23. lseek(fd, ucRelativePositon * fontLength, SEEK_SET);
  24. read(fd, buffer, sizeof(buffer));
  25. close(fd);
  26. }
  27. else
  28. {
  29. rt_kprintf("Read from file fonts error\n");
  30. }
  31. break;
  32. }
  33. case 16:
  34. {
  35. ...
  36. }
  37. case 24:
  38. {
  39. ...
  40. }
  41. default:
  42. {
  43. ...
  44. }
  45. ...
  46. }

模块化实现

Module_FILE.h

  1. /*
  2. * Copyright (c) 2020 - ~, HIT_HERO Team
  3. *
  4. * FILE MODULE HEAD FILE
  5. * Used file system
  6. * Used in RT-Thread Operate System
  7. *
  8. * Change Logs:
  9. * Date Author Notes QQ
  10. * 2020-06-04 WangXi first version 1098547591
  11. */
  12. #ifndef _MODULE_FILE_H_
  13. #define _MODULE_FILE_H_
  14. #include <rtthread.h>
  15. #include <rtdevice.h>
  16. #include <board.h>
  17. struct _MODULE_FILE
  18. {
  19. /* Property */
  20. char *SpiBusName;
  21. char *SpiDevName;
  22. char *BlockDevName;
  23. rt_uint32_t ChipIDRegist;
  24. rt_uint32_t BlockChipID;
  25. GPIO_TypeDef *CsPort;
  26. rt_uint16_t CsPinNum;
  27. rt_bool_t IfFormatting;
  28. /* Value */
  29. struct rt_spi_device *SPI_DevHandle;
  30. /* Method */
  31. rt_err_t (*Init)(struct _MODULE_FILE *module);
  32. };
  33. typedef struct _MODULE_FILE MODULE_FILE;
  34. /* Glodal Method */
  35. rt_err_t Module_File_Config(MODULE_FILE *Dev_LED);
  36. #endif
  37. /************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/

Module_FILE.c

  1. /*
  2. * Copyright (c) 2020 - ~, HIT_HERO Team
  3. *
  4. * FILE MODULE SOURCE FILE
  5. * Used file system
  6. * Used in RT-Thread Operate System
  7. *
  8. * Change Logs:
  9. * Date Author Notes QQ
  10. * 2020-06-04 WangXi first version 1098547591
  11. */
  12. #include "Module_FILE.h"
  13. #include "drv_spi.h"
  14. #include "spi_flash_sfud.h"
  15. #include "dfs_fs.h"
  16. static rt_err_t Module_FILEInit(MODULE_FILE *Dev_FILE);
  17. rt_err_t Module_File_Config(MODULE_FILE *Dev_FILE){
  18. rt_err_t res = RT_EOK;
  19. if( Dev_FILE->Init==NULL
  20. ){
  21. /* Link the Method */
  22. Dev_FILE->Init = Module_FILEInit;
  23. }
  24. else{
  25. rt_kprintf("Warning: Module FILE is Configed twice\r\n");
  26. return RT_ERROR;
  27. }
  28. /* Device Init */
  29. res = Dev_FILE->Init(Dev_FILE);
  30. if(res != RT_EOK){
  31. rt_kprintf("Error: FILE init failed\r\n");
  32. return RT_ERROR;
  33. }
  34. return RT_EOK;
  35. }
  36. static rt_err_t Module_FILEInit(MODULE_FILE *Dev_FILE){
  37. rt_err_t res = RT_EOK;
  38. /* Init the spi communication */
  39. res = rt_hw_spi_device_attach( Dev_FILE->SpiBusName, Dev_FILE->SpiDevName,
  40. Dev_FILE->CsPort, Dev_FILE->CsPinNum);
  41. if(res != RT_EOK){
  42. rt_kprintf("Error: Spi device attach failed\r\n");
  43. return RT_ERROR;
  44. }
  45. Dev_FILE->SPI_DevHandle = (struct rt_spi_device *)rt_device_find(Dev_FILE->SpiDevName);
  46. if(Dev_FILE->SPI_DevHandle == NULL){
  47. rt_kprintf("Error: Can't find %s device\r\n",Dev_FILE->SpiDevName);
  48. return RT_ERROR;
  49. }
  50. struct rt_spi_configuration cfg;
  51. cfg.data_width = 8;
  52. cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
  53. cfg.max_hz = 20 * 1000 *1000; /* 20M */
  54. rt_spi_configure(Dev_FILE->SPI_DevHandle, &cfg);
  55. /* Check the chip ID */
  56. rt_uint8_t ChipIDReg = Dev_FILE->ChipIDRegist;
  57. rt_uint8_t ChipID[3] = {0};
  58. rt_spi_send_then_recv(Dev_FILE->SPI_DevHandle, &ChipIDReg, 1, ChipID, 3);
  59. rt_uint32_t ChipID_32 = (ChipID[0]<<16) + (ChipID[1]<<8) + (ChipID[2]);
  60. if(ChipID_32!=Dev_FILE->BlockChipID){
  61. rt_kprintf("Error: Can't find %s\r\n",Dev_FILE->BlockDevName);
  62. return RT_ERROR;
  63. }
  64. /* Make the file system */
  65. rt_sfud_flash_probe(Dev_FILE->BlockDevName, Dev_FILE->SpiDevName);
  66. if(Dev_FILE->IfFormatting == true){
  67. dfs_mkfs("elm", Dev_FILE->BlockDevName);
  68. rt_kprintf("Info: File system formatting completed\r\n");
  69. }
  70. res = dfs_mount(Dev_FILE->BlockDevName,"/","elm",0,0);
  71. if(res == -1){
  72. rt_kprintf("Error: File system mounted failed\r\n");
  73. return RT_ERROR;
  74. }
  75. return RT_EOK;
  76. }
  77. /************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/

实验结果

单片机仍然可以正常调用字模,打印字符
相较于直接将字模数据存储在ROM中,打印速度有所下降,但为芯片内部节省了(28122-12926=15196≈14.8KB)