本帖最后由 1061700625 于 2020-11-5 19:46 编辑
markdown复制过来格式不大好,可以看这个:
https://blog.csdn.net/sxf1061700625/article/details/109015919
[size=0.9][url=]STM32CubeMX + HAL[/url][url=]前言[/url][url=]紧急避坑[/url][url=]USART[/url][url=]freertos+fatfs+sdio[/url][url=]一些说明[/url][url=]Cube基本使用[/url][url=]HAL库函数[/url][url=]中断回调函数[/url][url=]外设对应时钟[/url][url=]配置示例[/url][url=]小编有话说[/url][url=]USART[/url][url=]RTC[/url][url=]SDIO + FATFS[/url][url=]SDRAM[/url][url=]LTDC + DMA2D[/url][url=]FreeRTOS[/url][url=]TouchGFX显示[/url][url=]LittleVGL[/url][url=]显示图片[/url][url=]C数组形式[/url][url=]canvas画图[/url][url=]文件系统[/url][url=]显示中文[/url][url=]待补充...[/url]
STM32CubeMX + HAL前言紧急避坑USART问题1: 打印正常,但是加入接收中断后,开始出bug,最后锁定接收中断挂掉了。原因: HAL库的串口接收发送函数有bug,就是收发同时进行的时候,会出现锁死的现象。解决: 需要注释掉 HAL_UART_Receive_IT 和 HAL_UART_Transmit_IT 中的 __HAL_LOCK(huart) 函数。或者不要在接收里面,每接收到一个字符就printf一下。 问题2: 在接收中断中使用HAL_UART_Receive_IT()函数,会导致CR1的RXNEIE 置0,最后一直处于错误状态,无法进行接收。解决: 注释掉 HAL_UART_Receive_IT 中的 HAL_LOCK(huart) 函数
freertos+fatfs+sdio问题:没有加freertos时候,sd卡读写正常;加上freertos时候,mout成功,但read等其他操作返回错误3 not ready 解决: sdio和sddma的中断优先级要小于freertos的最小优先级 一些说明使用STM32CubeMX代码生成工具,不用关注底层配置的细节,真舒服。 使用教程: 虽然Cube+HAL很舒服,但新手不建议用。最好还是先去学一下标准库怎么用,有个大致概念后,再来学这一套。 自动化的东西虽好,但一旦出了问题,解决起来也是挺头疼的。
Cube基本使用
HAL库函数
中断回调函数
外设对应时钟随便进入一个外设初始化函数,如MX_GPIO_Init() 随便进入一个时钟使能函数,如__HAL_RCC_GPIOC_CLK_ENABLE() 随便进入一个RCC宏定义,如RCC_AHB1ENR_GPIOCEN 或者直接进入stm32f429xx.h文件 里面有所有外设与时钟对应关系,如RCC_AHB1ENR_DMA1EN
配置示例小编有话说例子源码: 如果配置过程中,参数不知道怎么设置,可以去标准库例程(如野火、正点原子)中看对应的参数是什么 Cube软件只是帮你配置了底层,一些初始化代码还是需要自己手动加的,如SDRAM充电初始化、读写函数等 以下内容都是基于“野火F429IGT6挑战者V2开发板”,其他板子按照原理图改改引脚都能用的
USART源码链接: 详细教程网上挺多,配置也简单,只要勾选一下USARTx,再开一下中断就行。 在Keil就比较要注意了。 由于每次接收完,程序内部自动把接收中断关了,所以每次要手动打开。 总的来说,加这几部分: // 使能串口中断接收
HAL_UART_Receive_IT(&huart1, (uint8_t*)&DataTemp_UART1, 1); #include "stdio.h"
int fputc(int ch, FILE *f){
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0XFF);
return ch;
} #define UART1BuffLen 200
extern uint8_t DataBuff_UART1[UART1BuffLen];
extern uint32_t DataTemp_UART1;
extern uint16_t DataSTA_UART1;
​
uint32_t DataTemp_UART1;
uint8_t DataBuff_UART1[UART1BuffLen];
uint16_t DataSTA_UART1;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
if(DataSTA_UART1 < UART1BuffLen){
if(DataTemp_UART1 == 0x0A && DataSTA_UART1>0 && DataBuff_UART1[DataSTA_UART1-1]==0X0D){
printf("USART: %s\r\n", DataBuff_UART1);
DataSTA_UART1 = 0;
}
else{
if(DataSTA_UART1 == 0){
memset(DataBuff_UART1, 0, sizeof(DataBuff_UART1));
}
DataBuff_UART1[DataSTA_UART1++] = DataTemp_UART1;
}
}
// 使能串口中断接收
HAL_UART_Receive_IT(&huart1, (uint8_t*)&DataTemp_UART1, 1);
}
}
RTC RTC_DateTypeDef sDate;
RTC_TimeTypeDef sTime;
uint8_t second_tmp = 0;
​
​
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); // 读取时间
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); // 读取日期
if(second_tmp != sTime.Seconds) { // 读取秒
second_tmp = sTime.Seconds;
printf("20%d%d-%d%d-%d%d\r\n",
sDate.Year/10%10, sDate.Year%10,
sDate.Month/10%10, sDate.Month%10,
sDate.Date/10%10, sDate.Date%10);
printf("%d%d:%d%d:%d%d\r\n",
sTime.Hours/10%10, sTime.Hours%10,
sTime.Minutes/10%10, sTime.Minutes%10,
sTime.Seconds/10%10, sTime.Seconds%10);
}
SDIO + FATFS注意,SDIO中断优先级必须高于DMA2 stream3和DMA2 stream6的中断优先级 紧急避坑!!!如果没有用freertos,那中断优先级设置没啥关系。但如果用了freertos,那SDIO的优先级必须要注意跟freertos区分开来,不能高过他!不然就是mout正常,read等其他操作都返回错误3 not ready。其实当你开启freertos,然后点击NVIC时候,cube会提醒你,要注意函数的中断优先级和freertos优先级的关系。(如果中断处理程序调用RTOS函数,请确保其抢占优先级低于最高的SysCall中断优先级。如FreeRTOS中的“LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY”) static void BL8782_PDN_INIT(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
​
RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_13); //禁用WiFi模块
} UINT bw;
retSD = f_mount(&SDFatFS, SDPath, 0);
if(retSD != FR_OK) {
printf("Mount Error :%d\r\n", retSD);
}
retSD = f_open(&SDFile, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if(retSD != FR_OK){
printf("Open Error :%d\r\n", retSD);
}
retSD = f_write(&SDFile, "abcde", 5, &bw);
if(retSD != FR_OK){
printf("Write Error :%d\r\n", retSD);
}
f_close(&SDFile);
retSD = f_open(&SDFile, "0:/test.txt", FA_READ);
char buff[10] = {0};
retSD = f_read(&SDFile, buff, 5, &bw);
if(retSD == FR_OK){
printf("%s\r\n", buff);
}
f_close(&SDFile);
SDRAM开启FMC功能,Pinout&Configuration ,Connectivity -> FMC -> SDRAM2 SDRAM1的起始地址为0XC0000000,SDRAM2的起始地址为0XD0000000。 一般SDRAM都含有4个bank。 Configuration中的参数可从SDRAM的数据手册上找到。
各个选项的配置(只做解释,不对应上图): Clock and chip enable:FMC_SDCKE0 和FMC_SDCLK0对应的存储区域1 的地址范围是0xC000 0000-0xCFFF FFFF;而FMC_SDCKE1 和FMC_SDCLK1 对应的存储区域2 的地址范围是0xD000 0000- 0xDFFF FFFF Bank由硬件连接决定需要选择SDRAM bank 2 Column bit number表示列数,8位 Row bit number表示行数,12位 CAS latency表示CAS潜伏期,即上面说的CL,该配置需要与之后的SDRAM模式寄存器的配置相同,这里先配置为2 memory clock cycles(对于SDRAM时钟超过133MHz的,则需要配置为3 memory clock cycles) Write protection 表示写保护,一般配置为Disabled SDRAM common clock为SDRAM 时钟配置,可选HCLK的2分频\3分频\不使能SDCLK时钟。前面主频配置为216MHz,SDRAM common clock设置为2分频,那SDCLK时钟为108MHz,每个时钟周期为9.25ns SDRAM common burst read 表示突发读,这里选择使能 SDRAM common read pipe delay 表示CAS潜伏期后延迟多少个时钟在进行读数据,这里选择0 HCLK clock cycle Load mode register to active delay 加载模式寄存器命令和激活或刷新命令之间的延迟,按存储器时钟周期计
 Row to column delay激活命令与读/写命令之间的延迟,按存储器时钟周期数计 查数据手册知道其最小值为15ns,由于我们每个时钟周期为9.25ns,所以这里本应该设为2 (15÷9.25,向上取整) 但要注意,时序必须满足以下式子: TWR ≥ TRAS - TRCD TWR ≥ TRC - TRCD - TRP 其中:TWR = Write recovery time = 2 TRAS = Self refresh time = 5 TRC = SDRAM common row cycle delay = 7 TRP = SDRAM common row precharge delay = 2 TRCD = Row to column delay 所以这里Row to column delay应该取3
 uint8_t temp[100]__attribute__((at(0xD0000000)));
for(int i=0;i<100;i++){
temp = i;
}
for(int i=0;i<100;i++){
printf("%d ", temp);
} /*****************************SDRAM使能函数******************************/
​
/**
* @brief 对SDRAM芯片进行初始化配置
* @param None.
* @retval None.
*/
static void USER_SDRAM_ENABLE(void)
{
FMC_SDRAM_CommandTypeDef Command;
__IO uint32_t tmpmrd =0;
/* Step 1: Configure a clock configuration enable command */
Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
​
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
​
/* Step 2: Insert 100 us minimum delay */
/* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
HAL_Delay(1);
​
/* Step 3: Configure a PALL (precharge all) command */
Command.CommandMode = FMC_SDRAM_CMD_PALL;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
​
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 4: Configure an Auto Refresh command */
Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 4;
Command.ModeRegisterDefinition = 0;
​
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 5: Program the external memory mode register */
tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = tmpmrd;
​
/* Send the command */
HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);
/* Step 6: Set the refresh rate counter */
/* Set the device refresh rate */
HAL_SDRAM_ProgramRefreshRate(&hsdram1, REFRESH_COUNT);
}
/*****************************使能函数结束******************************/或者以我的野火STM32F429IGT6的版本SDRAM为8M,源码链接: 添加到工程Core路径下,然后在KEIL中初始化操作: (注意这个SDRAM_InitSequence();不能加在HAL_SDRAM_MspInit()后面!!! 因为它这里还没FMC初始化完成!!!! 加在这里是没有用的!!!) #include "bsp_sdram.h"MX_FMC_Init();SDRAM_InitSequence(); SDRAM_Test();
LTDC + DMA2D务必在上面SDRAM配置成功后,再来搞这个!!! 但他给的源码还有点问题,运行处理没效果。 我提供的源码链接: 注意:
FreeRTOS后面要上TouchGFX,这里先加操作系统。 当FreeRTOS遇到FATFS+SDIO时,这里有挺多注意细节的!!! 针对初学者,使用STM32CubeMX配置FreeRTOS时,大部分参数默认即可 改完之后,注意:中断处理程序调用RTOS函数,请确保它们的优先级比最高的系统调用中断优先级低(数字上高),例如FreeRTOS中的LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
TouchGFX显示虽然方便,但它是用C++开发的,所以不是特别友好...说白了就是看不懂,不知道怎么去改 C:\Users\10617\STM32Cube\Repository\Packs\STMicroelectronics\X-CUBE-TOUCHGFX\4.15.0\Utilities\PC_Software\TouchGFXDesigner\TouchGFX-4.14.0.msi#include "app_touchgfx.h"// 开启LCDLCD_DisplayOn();LCD_SetLayerVisible(1,DISABLE);LCD_SetLayerVisible(0,ENABLE);LCD_SetTransparency(1,0);LCD_SetTransparency(0,255);LCD_SelectLayer(0);// 显示TouchGFX内容MX_TouchGFX_Process();#if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle; }; FILE __stdout; //定义_sys_exit()以避免使用半主机模式 void _sys_exit(int x) { x = x; } void _ttywrch(int ch){ ch = ch;}//重定义fputc函数 int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0XFF); return ch; } #endif
LittleVGL奈何不会C++,只能另谋出路,LittltVGL设计的界面似乎还挺好看的,而且用C编写,兼容C++,更新很活跃。EMWIN风格类似window XP ,littlevGL风格类似android 。移植很简单(并没有多简单)。 官方推荐方法学习路线: 点击在线演示以查看LVGL的运行情况(3分钟) 阅读文档的介绍页面(5分钟) 熟悉Quick overview页面的基础知识(15分钟) 设置模拟器(10分钟) 尝试一些例子 移植LVGL到一个板子。请参阅移植指南或准备使用项目 阅读概述页,更好地了解库(2-3小时) 查看小部件的文档,了解它们的特性和用法 如果你有问题可以去论坛 阅读贡献指南,了解如何帮助改进LVGL(15分钟)
1、 教程可交叉参考以下这几篇,取长补短吧: 2、在Cube里开一个MTM的DMA(或者不开它,直接用DMA2D) 3、生成工程,修改disp_flush函数 /********************** * STATIC VARIABLES **********************/static __IO uint16_t * my_fb = (__IO uint16_t*) (0xD0000000);static DMA_HandleTypeDef DmaHandle;static int32_t x1_flush;//static int32_t y1_flush;static int32_t x2_flush;static int32_t y2_fill;static int32_t y_fill_act;static const lv_color_t * buf_to_flush;static lv_disp_t *our_disp = NULL;/********************* * INCLUDES *********************/#include "lv_port_disp.h"#include "bsp_lcd.h"#include "dma2d.h"#include "stm32f4xx_hal_dma.h"#include "dma.h"static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){// int32_t x;// int32_t y;// for(y = area->y1; y <= area->y2; y++) {// for(x = area->x1; x <= area->x2; x++) {// /* Put a pixel to the display. For example: */// /* put_px(x, y, *color_p)*///// LCD_FillRect_C(area->x1, area->y1, area->x2-area->x1, area->y2-area->y1, (uint32_t)color_p);//// LCD_DrawPixel(x, y, (uint32_t)color_p->full);// LCD_FillRect_C(x, y, 1, 1, (uint32_t)color_p->full);// color_p++;// }// } int32_t x1 = area->x1; int32_t x2 = area->x2; int32_t y1 = area->y1; int32_t y2 = area->y2; /*Return if the area is out the screen*/ if(x2 < 0) return; if(y2 < 0) return; if(x1 > LV_HOR_RES_MAX - 1) return; if(y1 > LV_VER_RES_MAX - 1) return; /*Truncate the area to the screen*/ int32_t act_x1 = x1 < 0 ? 0 : x1; int32_t act_y1 = y1 < 0 ? 0 : y1; int32_t act_x2 = x2 > LV_HOR_RES_MAX - 1 ? LV_HOR_RES_MAX - 1 : x2; int32_t act_y2 = y2 > LV_VER_RES_MAX - 1 ? LV_VER_RES_MAX - 1 : y2; x1_flush = act_x1; // y1_flush = act_y1; x2_flush = act_x2; y2_fill = act_y2; y_fill_act = act_y1; buf_to_flush = color; HAL_StatusTypeDef err; uint32_t length = (x2_flush - x1_flush + 1); #if LV_COLOR_DEPTH == 24 || LV_COLOR_DEPTH == 32 length *= 2; /* STM32 DMA uses 16-bit chunks so multiply by 2 for 32-bit color */ #endif err = HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0,(uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * LV_HOR_RES_MAX + x1_flush], length); if(err != HAL_OK) { printf("disp_flush %d\r\n",err); while(1); /*Halt on error*/ } lv_disp_flush_ready(disp_drv);}注意!!! 微雪这个函数有点问题,如果遇到显示不正确的时候,建议改成以下这个试试: int32_t x1 = area->x1;int32_t x2 = area->x2;int32_t y1 = area->y1;int32_t y2 = area->y2;/*Return if the area is out the screen*/if(x2 < 0) return;if(y2 < 0) return;if(x1 > LV_HOR_RES_MAX - 1) return;if(y1 > LV_VER_RES_MAX - 1) return;/*Truncate the area to the screen*/int32_t act_x1 = x1 < 0 ? 0 : x1;int32_t act_y1 = y1 < 0 ? 0 : y1;int32_t act_x2 = x2 > LV_HOR_RES_MAX - 1 ? LV_HOR_RES_MAX - 1 : x2;int32_t act_y2 = y2 > LV_VER_RES_MAX - 1 ? LV_VER_RES_MAX - 1 : y2;for(int32_t y = act_y1; y <= act_y2; y++) { for(int32_t x = act_x1; x <= act_x2; x++) { /* Put a pixel to the display. For example: */ /* put_px(x, y, *color_p)*/ my_fb[y*LV_HOR_RES_MAX+x] = (uint32_t)color_p->full; color_p++; }}4、按着上面教程,把littlvgl的显存地址改为SDRAM的 5、由于用作时基的TIM6的中断时间是100ms,所以我们可以新开一个定时器如TIM7,设置它的中断时间为1~5ms。但好像是需要手动启动定时器的: HAL_TIM_Base_Start_IT(&htim7);6、keil测试: lv_init();lv_port_disp_init();// 开启LCDLCD_DisplayOn();LCD_SetLayerVisible(1,DISABLE);LCD_SetLayerVisible(0,ENABLE);LCD_SetTransparency(1,0);LCD_SetTransparency(0,255);LCD_SelectLayer(0);LCD_Clear(LCD_COLOR_BLUE);/*Create a Label on the currently active screen*/lv_obj_t * label1 = lv_label_create(lv_scr_act(), NULL);/*Modify the Label's text*/lv_label_set_text(label1, "Hello world!");/* Align the Label to the center * NULL means align on parent (which is the screen now) * 0, 0 at the end means an x, y offset after alignment*/lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0);#include "bsp_lcd.h"static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){ int32_t x; int32_t y; for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { LCD_DrawPixel(x, y, (uint32_t)color_p->full); color_p++; } } lv_disp_flush_ready(disp_drv);}当然,我的源码链接(只有显示部分,触摸目前没用到): 注意!!!:一定要先执行初始化lv_init(); lv_port_disp_init();,再做其他ui操作,不然会死活不显示出来!!! 附,我的一些理解: display是buff区,screen是一整个界面,界面里可以放控件和窗口win,窗口里还能放控件,一个控件可能有多个part 创建screen:lv_obj_t* screen1 = lv_obj_create(NULL, NULL); 创建window:lv_obj_t* win = lv_win_create(screen1, NULL); 创建label:lv_obj_t * label1 = lv_label_create(win, NULL); label中设置text:lv_label_set_text(label1, "Hello world!");` 居中:lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0); screen切换:lv_scr_load(screen1); 添加style:
static lv_style_t loading_style;lv_obj_t* loading_label = lv_label_create(lv_scr_act(), NULL);lv_label_set_text(loading_label, "Loading...");lv_obj_align(loading_label, NULL, LV_ALIGN_CENTER, 0, 0);lv_style_init(&loading_style);lv_style_set_text_color(&loading_style, LV_STATE_DEFAULT, LV_COLOR_BLUE);lv_style_set_text_font(&loading_style,LV_STATE_DEFAULT, &lv_font_montserrat_24);lv_obj_add_style(loading_label, LV_OBJ_PART_MAIN, &loading_style);显示图片C数组形式extern const lv_img_t my_image_name; LV_IMG_DECLARE(my_image_name);const lv_img_dsc_t WaveShare_LOGO = { .header.always_zero = 0, .header.w = 287, .header.h = 81, .data_size = 11728, .header.cf = LV_IMG_CF_INDEXED_4BIT, .data = WaveShare_LOGO_map,};// 先声明一下外部图片结构体LV_IMG_DECLARE(WaveShare_LOGO)// 创建一个图片lv_obj_t * img1 = lv_img_create(lv_scr_act(), NULL);// 将数组内容放入lv_img_set_src(img1, &WaveShare_LOGO);// 图片在屏幕居中lv_obj_align(img1, NULL, LV_ALIGN_CENTER, 0, -20);canvas画图// 声明一个buffstatic lv_color_t buffer[LV_CANVAS_BUF_SIZE_TRUE_COLOR(48, 48)];// 创建canvaslv_obj_t* canvas = lv_canvas_create(lv_scr_act(), NULL);// 关联canvas与bufflv_canvas_set_buffer(canvas, buffer, 48, 48, LV_IMG_CF_TRUE_COLOR);// 背景涂色lv_canvas_fill_bg(canvas, LV_COLOR_BLUE, LV_OPA_50);// 在画布上画点lv_color_t c0;c0.full = 0;uint32_t x;uint32_t y;for( y = 10; y < 30; y++) { for( x = 5; x < 20; x++) { // 这里的x,y都是相对父元素而言 lv_canvas_set_px(canvas, x, y, c0); }}文件系统将下载的.c/.h添加到工程中 添加这些行到lv_conf.h:
/*File system interface*/#define LV_USE_FS_IF 1#if LV_USE_FS_IF# define LV_FS_IF_FATFS '\0' // ‘S’# define LV_FS_IF_PC '\0'#endif /*LV_USE_FS_IF*/通过将'\0'更改为要用于该驱动器的字母来启用所需的接口。如'S'表示FATFS的SD卡 调用lv_fs_if_init()来注册启用的接口 使用lv_fs_fatfs.c中提供的函数完成操作 初始化图像,需要以下回调:
使用示例:
// 挂载SD卡retSD = f_mount(&SDFatFS, SDPath, 0);// 文件系统初始化lv_fs_if_init();// 创建一个图像lv_obj_t *icon = lv_img_create(lv_scr_act(), NULL);// SD卡文件绑定到图像lv_img_set_src(icon, "S:0.bin");// 居中显示lv_obj_align(icon, NULL, LV_ALIGN_CENTER, 0, 0);显示中文注意keil工程必须是UTF8编码! 这时,串口输出中文可能是乱码。没关系,lvgl显示正常就行。或者用SwitchToGbk函数将utf8转成Unicode,这样串口就是正常的了。该函数可以到工程目录的User/SXF下找: 生成的myFont.c下,有个函数需要替换一下(读SD卡方式): static uint8_t *__user_font_getdata(int offset, int size){//如字模保存在SPI FLASH, SPIFLASH_Read(__g_font_buf,offset,size);//如字模已加载到SDRAM,直接返回偏移地址即可如:return (uint8_t*)(sdram_fontddr+offset); uint32_t br; if( f_open(&SDFile, (const TCHAR*)"0:/myFont.bin", FA_READ) != FR_OK ) { printf("myFont.bin open failed\r\n"); } else { if( f_lseek(&SDFile, (FSIZE_t)offset) != FR_OK ) { printf("myFont.bin lseek failed\r\n"); } if( f_read(&SDFile, __g_font_buf, (UINT)size, (UINT*)&br) != FR_OK ) { printf("myFont.bin lseek failed\r\n"); } // printf("offset:%d\t size:%d\t __g_font_buf:%s\r\n", offset, size, __g_font_buf); f_close(&SDFile); } return __g_font_buf;}调用示例: static lv_style_t date_style;lv_style_init(&date_style);lv_obj_t* date_label2 = lv_label_create(lv_scr_act(), NULL);lv_label_set_text(date_label2, "123y呀");lv_style_set_text_color(&date_style, LV_STATE_DEFAULT, LV_COLOR_RED);lv_style_set_text_font(&date_style, LV_STATE_DEFAULT, &myFont);lv_obj_add_style(date_label2, LV_OBJ_PART_MAIN, &date_style);lv_obj_align(date_label2, NULL, LV_ALIGN_IN_TOP_LEFT, 10, 10);lv_obj_set_pos(date_label2, 10, 10);
待补充...
|