Github
从这里开始,整个RTT学习过程中对工程的完善和开发都记录在Github上的一个项目中,项目还会随着学习的深入进行更新
项目URL:https://github.com/HITLIVING/-RT-Thread-.git
读者可以通过Pull下来整个项目进一步了解
需要格外关注drv_lil9341_lcd.cdrv_fonts.c
这两个文件
前期准备
- RT-Thread文件系统配置成功,且可以正常使用
- 可以通过Finsh命令行操作存储体,包括新建文件,新建文件夹,存内容,读内容
- 通过字模软件或例程中取到标准ASCII码的字模数据数组
- RT-Thread文件系统配置成功,将 存储块设备格式化 这一步骤注释掉,避免反复刷掉数据
概述
在使用LCD等显示设备时,需要字模数据
而字模数据往往是数量庞大的字符数组,如果直接作为常量存到芯片ROM中,会占据庞大的空间造成资源浪费
这类数据可以放置在外置存储体内,需要时通过驱动芯片读取字模数据,即可为芯片内的存储空间腾出地方
以字模的存取为例,讲解RT-Thread文件系统的实际使用
将字模数据以txt文件格式,存储到已经按照文件系统格式化好的存储体中
因为实验以Flash为存储体,无法像SD卡那样,通过电脑存储文件,需要单片机提供下载途径,即新建文件,写数据
接口函数
头文件
#include <dfs_posix.h> /* 当需要使用文件操作时,需要包含这个头文件 */
打开和关闭文件
打开或创建一个文件可以调用下面的 open() 函数:
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() 会让数据写回磁盘,并释放该文件所占用的资源。
int close(int fd);
| 参数 | 描述 |
|---|---|
| fd | 文件描述符 |
| 返回 | —— |
| 0 | 文件关闭成功 |
| -1 | 文件关闭失败 |
读写数据
读取文件内容可使用 read() 函数:
int read(int fd, void *buf, size_t len);
| 参数 | 描述 |
|---|---|
| fd | 文件描述符 |
| buf | 缓冲区指针 |
| len | 读取文件的字节数 |
| 返回 | —— |
| int | 实际读取到的字节数 |
| 0 | 读取数据已到达文件结尾或者无可读取的数据 |
| -1 | 读取出错,错误代码查看当前线程的 errno |
该函数会把参数 fd 所指的文件的 len 个字节读取到 buf 指针所指的内存中。
此外文件的读写位置指针会随读取到的字节移动。
向文件中写入数据可使用 write() 函数:
int write(int fd, const void *buf, size_t len);
| 参数 | 描述 |
|---|---|
| Fd | 文件描述符 |
| buf | 缓冲区指针 |
| len | 写入文件的字节数 |
| 返回 | —— |
| int | 实际写入的字节数 |
| -1 | 写入出错,错误代码查看当前线程的 errno |
该函数会把 buf 指针所指向的内存中 len 个字节写入到参数 fd 所指的文件内。
此外文件的读写位置指针会随着写入的字节移动。
指定读写位置函数
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);
上机实验
- 在指定文件中下载内容
//手动下载字符指令#include <dfs_posix.h>/** 常用ASCII表,偏移量32,大小:16(高度)* 8 (宽度)*/const uint8_t ASCII8x16_Table [ ] = {0x00 0x01 0x02..............//@conslons字体,阴码点阵格式,逐行顺向取摸};static int Download(int argc, char *argv[]){int fd;char Filename[] = "/ASCII8x16_Table.txt";fd = open(Filename, O_WRONLY | O_CREAT);if (fd>= 0){write(fd, ASCII8x16_Table, sizeof(ASCII8x16_Table));close(fd);rt_kprintf("Write done.\n");}return 0;}/* 导出到 msh 命令列表中 */MSH_CMD_EXPORT(Download, download fonts to flash);
- 读取指定文件的指定内容
uint8_t byteCount, bitCount,fontLength;uint16_t ucRelativePositon;uint8_t *Pfont;//对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号)ucRelativePositon = cChar - ' ';//每个字模的字节数fontLength = (LCD_Currentfonts->Width*LCD_Currentfonts->Height)/8;//-------------------------------------------------------------------------//Get font data from flashswitch(LCD_Currentfonts->Width){case 8:{rt_int32_t fd = 0;//读取缓存区rt_uint8_t buffer[8*16/8] = {0};//打开指定文件,需要先保证文件存在,否则会导致读取失败fd = open("/ASCII8x16_Table.txt", O_RDONLY);Pfont = buffer;if (fd>= 0){//由对ascii码表偏移和每个字模的字节数定位读取文件的内容lseek(fd, ucRelativePositon * fontLength, SEEK_SET);read(fd, buffer, sizeof(buffer));close(fd);}else{rt_kprintf("Read from file fonts error\n");}break;}case 16:{...}case 24:{...}default:{...}...}
模块化实现
Module_FILE.h
/** Copyright (c) 2020 - ~, HIT_HERO Team** FILE MODULE HEAD FILE* Used file system* Used in RT-Thread Operate System** Change Logs:* Date Author Notes QQ* 2020-06-04 WangXi first version 1098547591*/#ifndef _MODULE_FILE_H_#define _MODULE_FILE_H_#include <rtthread.h>#include <rtdevice.h>#include <board.h>struct _MODULE_FILE{/* Property */char *SpiBusName;char *SpiDevName;char *BlockDevName;rt_uint32_t ChipIDRegist;rt_uint32_t BlockChipID;GPIO_TypeDef *CsPort;rt_uint16_t CsPinNum;rt_bool_t IfFormatting;/* Value */struct rt_spi_device *SPI_DevHandle;/* Method */rt_err_t (*Init)(struct _MODULE_FILE *module);};typedef struct _MODULE_FILE MODULE_FILE;/* Glodal Method */rt_err_t Module_File_Config(MODULE_FILE *Dev_LED);#endif/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
Module_FILE.c
/** Copyright (c) 2020 - ~, HIT_HERO Team** FILE MODULE SOURCE FILE* Used file system* Used in RT-Thread Operate System** Change Logs:* Date Author Notes QQ* 2020-06-04 WangXi first version 1098547591*/#include "Module_FILE.h"#include "drv_spi.h"#include "spi_flash_sfud.h"#include "dfs_fs.h"static rt_err_t Module_FILEInit(MODULE_FILE *Dev_FILE);rt_err_t Module_File_Config(MODULE_FILE *Dev_FILE){rt_err_t res = RT_EOK;if( Dev_FILE->Init==NULL){/* Link the Method */Dev_FILE->Init = Module_FILEInit;}else{rt_kprintf("Warning: Module FILE is Configed twice\r\n");return RT_ERROR;}/* Device Init */res = Dev_FILE->Init(Dev_FILE);if(res != RT_EOK){rt_kprintf("Error: FILE init failed\r\n");return RT_ERROR;}return RT_EOK;}static rt_err_t Module_FILEInit(MODULE_FILE *Dev_FILE){rt_err_t res = RT_EOK;/* Init the spi communication */res = rt_hw_spi_device_attach( Dev_FILE->SpiBusName, Dev_FILE->SpiDevName,Dev_FILE->CsPort, Dev_FILE->CsPinNum);if(res != RT_EOK){rt_kprintf("Error: Spi device attach failed\r\n");return RT_ERROR;}Dev_FILE->SPI_DevHandle = (struct rt_spi_device *)rt_device_find(Dev_FILE->SpiDevName);if(Dev_FILE->SPI_DevHandle == NULL){rt_kprintf("Error: Can't find %s device\r\n",Dev_FILE->SpiDevName);return RT_ERROR;}struct rt_spi_configuration cfg;cfg.data_width = 8;cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;cfg.max_hz = 20 * 1000 *1000; /* 20M */rt_spi_configure(Dev_FILE->SPI_DevHandle, &cfg);/* Check the chip ID */rt_uint8_t ChipIDReg = Dev_FILE->ChipIDRegist;rt_uint8_t ChipID[3] = {0};rt_spi_send_then_recv(Dev_FILE->SPI_DevHandle, &ChipIDReg, 1, ChipID, 3);rt_uint32_t ChipID_32 = (ChipID[0]<<16) + (ChipID[1]<<8) + (ChipID[2]);if(ChipID_32!=Dev_FILE->BlockChipID){rt_kprintf("Error: Can't find %s\r\n",Dev_FILE->BlockDevName);return RT_ERROR;}/* Make the file system */rt_sfud_flash_probe(Dev_FILE->BlockDevName, Dev_FILE->SpiDevName);if(Dev_FILE->IfFormatting == true){dfs_mkfs("elm", Dev_FILE->BlockDevName);rt_kprintf("Info: File system formatting completed\r\n");}res = dfs_mount(Dev_FILE->BlockDevName,"/","elm",0,0);if(res == -1){rt_kprintf("Error: File system mounted failed\r\n");return RT_ERROR;}return RT_EOK;}/************************ (C) COPYRIGHT 2020 WANGXI **************END OF FILE****/
实验结果
单片机仍然可以正常调用字模,打印字符
相较于直接将字模数据存储在ROM中,打印速度有所下降,但为芯片内部节省了(28122-12926=15196≈14.8KB)
