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卡那样,通过电脑存储文件,需要单片机提供下载途径,即新建文件,写数据
接口函数
头文件
#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 flash
switch(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)