我是靠谱客的博主 大意手套,最近开发中收集的这篇文章主要介绍keil接收别人发的工程打不开_STM32CubeMX6.0 + HAL + LittleVGL7.6 等学习[最全附工程源码],觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

STM32CubeMX + HAL一些说明底层配置Cube基本使用HAL库函数中断回调函数外设对应时钟配置示例小编有话说USARTRTCSDIO + FATFSSDRAMLTDC + DMA2DFreeRTOSTouchGFX显示LittleVGL待补充...

STM32CubeMX + HAL

一些说明

底层配置

使用STM32CubeMX代码生成工具,不用关注底层配置的细节,真舒服。

使用教程:

https://sxf1024.lanzoui.com/b09rf2dwj 密码:bgvi

虽然Cube+HAL很舒服,但新手不建议用。最好还是先去学一下标准库怎么用,有个大致概念后,再来学这一套。


Cube基本使用
  1. 新建工程
  2. 选择芯片
  3. Pinout&Configuration,选择RCC(HSE:Crystal/Ceramic Resonator)SYS(Debug:Serial Wiire)
  4. Clock Configuration,配置时钟树

  1. Project Manager,配置工程输出项

  1. Pinout&Configuration,选择功能(若是选GPIO相关,可以直接在Pinout view选择;若是其他功能,可以在左边Categories打开,会自动配置引脚)、设置Parameter Settings/NVIC

  1. GENERATE CODE,生成工程,用KEIL打开编辑

HAL库函数
  • 函数形式:均以HAL_开头
  • 寻找过程:在驱动文件stm32f4xx_hal_XXX.c或其.h文件中找函数定义,一般在靠后位置
  • 其他说明:
    • HAL库并没有把所有的操作都封装成凼数。
    • 对于底层的寄存器操作(如读取捕获/比较寄存器),还有修改外设的某个配置参数(如改变输入捕获的极性),HAL库会使用宏定义来实现。而且会用__HAL_作为这类宏定义的前缀
    • 获取某个参数,宏定义中一般会有_GET;而设置某个参数的,宏定义中就会有_SET
    • 在开发过程中,如果遇到寄存器级别或者更小范围的操作时,可以到该外设的头文件中查找,一般都能找到相应的宏定义。
    • HAL库函数第一个参数一般都是句柄(一个包含了当前对象绝大部分状态的结构体),虽然增加了开销,但是用起来便捷了非常多。

中断回调函数
  • 函数形式:HAL_XXX_XXXCallback()
  • 寻找过程:中断文件stm32f4xx_it.c - > 中断函数XXX_IRQHandler(void) -> HAL库中断函数HAL_XXX_IRQHandler(GPIO_PIN_13) -> 回调函数HAL_XXX_XXXCallback()

外设对应时钟
  1. 随便进入一个外设初始化函数,如MX_GPIO_Init()
  2. 随便进入一个时钟使能函数,如__HAL_RCC_GPIOC_CLK_ENABLE()
  3. 随便进入一个RCC宏定义,如RCC_AHB1ENR_GPIOCEN
  4. 或者直接进入stm32f429xx.h文件
  5. 里面有所有外设与时钟对应关系,如RCC_AHB1ENR_DMA1EN

配置示例

小编有话说

  • 例子源码:https://sxf1024.lanzoui.com/b09rf535a 密码:bf5q
  • 如果配置过程中,参数不知道怎么设置,可以去标准库例程(如野火、正点原子)中看对应的参数是什么
  • Cube软件只是帮你配置了底层,一些初始化代码还是需要自己手动加的,如SDRAM充电初始化、读写函数等
  • 以下内容都是基于“野火F429IGT6挑战者V2开发板”,其他板子按照原理图改改引脚都能用的

USART

源码链接:

https://sxf1024.lanzoui.com/b09rf535a 密码:bf5q

详细教程网上挺多,配置也简单,只要勾选一下USARTx,再开一下中断就行。

在Keil就比较要注意了。

由于每次接收完,程序内部自动把接收中断关了,所以每次要手动打开。

总的来说,加这几部分:

  • main函数中,while之前:
 // 使能串口中断接收
HAL_UART_Receive_IT(&huart1, (uint8_t*)&DataTemp_UART1, 1);
  • 任意位置添加printf重定向函数
 #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: %srn", 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%drn",
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%drn",
sTime.Hours/10%10, sTime.Hours%10,
sTime.Minutes/10%10, sTime.Minutes%10,
sTime.Seconds/10%10, sTime.Seconds%10);
}

SDIO + FATFS
  1. 选择SDIO功能,Pinout&Clock ConfigurationConnectivity -> SDIO -> Mode: SD 4bit Wide bus -> 勾选NVIC

  1. 配置SDIO时钟,Clock Configuration,SDIO模块输入要求为48MHz,系统提示可以自动设置时钟问题,选择Yes。SDIO时钟分频系数CLKDIV,计算公式为SDIO_CK=48MHz/(CLKDIV+2)也可手动修改时钟配置。

  1. 启用文件系统中间件Pinout&Clock ConfigurationMiddleware -> FATFS,模式选择SD卡,配置文件系统:CODE_PAGE为Simplified ChineseUSE_LEN

  1. 继续上面界面,Advanced Settings勾选Use dma template

  1. 设置DMA传输,Pinout&Clock ConfigurationConnectivity -> SDIO-> DMA Settings

  1. 配置NVIC,Pinout&Clock ConfigurationSystem Core -> NVIC

注意,SDIO中断优先级必须高于DMA2 stream3和DMA2 stream6的中断优先级

  1. 堆栈设置,Progect Manager -> Project -> Linker Settings,加大堆栈大小(注意:由于刚才设置长文件名动态缓存存储在堆中,故需要增大堆大小,如果不修改则程序运行时堆会生成溢出,程序进入硬件错误中断(HardFault),死循环)。

  1. 生成工程,GENERATE CODE ,用KEIL打开

  1. 注意,如果是野火的F429开发板,还需要禁用WIFI引脚才行!!!
 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模块
}
  1. 测试一下
 UINT bw;
retSD = f_mount(&SDFatFS, SDPath, 0);
if(retSD != FR_OK) {
printf("Mount Error :%drn", retSD);
}
retSD = f_open(&SDFile, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if(retSD != FR_OK){
printf("Open Error :%drn", retSD);
}
retSD = f_write(&SDFile, "abcde", 5, &bw);
if(retSD != FR_OK){
printf("Write Error :%drn", 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("%srn", buff);
}
f_close(&SDFile);

SDRAM
  1. 开启FMC功能,Pinout&ConfigurationConnectivity -> FMC -> SDRAM2SDRAM1的起始地址为0XC0000000SDRAM2的起始地址为0XD00000004个bank。Configuration中的参数可从SDRAM的数据手册上找到。

各个选项的配置(只做解释,不对应上图):

  • Clock and chip enableFMC_SDCKE0FMC_SDCLK0对应的存储区域1 的地址范围是0xC000 0000-0xCFFF FFFF;而FMC_SDCKE1FMC_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 加载模式寄存器命令和激活或刷新命令之间的延迟,按存储器时钟周期计

  • Exit self-refresh delay从发出自刷新命令到发出激活命令之间的延迟,按存储器时钟周期数计查数据手册知道其最小值为70ns,由于我们每个时钟周期为9.25ns,所以设为8 (70÷9.25,向上取整)

  • SDRAM common row cycle delay刷新命令和激活命令之间的延迟,以及两个相邻刷新命令之间的延迟, 以存储器时钟周期数表示7 (63÷9.25,向上取整)

  • Write recovery time写命令和预充电命令之间的延迟,按存储器时钟周期数计

  • SDRAM common row precharge delay预充电命令与其它命令之间的延迟,按存储器时钟周期数计2 (15÷9.25,向上取整)

  • Row to column delay激活命令与读/写命令之间的延迟,按存储器时钟周期数计2 (15÷9.25,向上取整)Write recovery time = 2Self refresh time = 5SDRAM common row cycle delay = 7SDRAM common row precharge delay = 2Row to column delayRow to column delay应该取3

  1. 生成代码,GENERATE CODE, 用KEIL打开
 uint8_t temp[100]__attribute__((at(0xD0000000)));
for(int i=0;i<100;i++){
temp[i] = i;
}
for(int i=0;i<100;i++){
printf("%d
", temp[i]);
}
  1. 到这里只是借助Cube完成了引脚配置,还需要SDRAM初始化操作和读写函数,可从官方例程里获取,路径:
 /*****************************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,源码链接:

https://sxf1024.lanzoui.com/b09rf535a 密码:bf5q

添加到工程Core路径下,然后在KEIL中初始化操作:

(注意这个SDRAM_InitSequence();不能加在HAL_SDRAM_MspInit()后面!!! 因为它这里还没FMC初始化完成!!!! 加在这里是没有用的!!!)

#include "bsp_sdram.h"
MX_FMC_Init();
SDRAM_InitSequence();
SDRAM_Test();

LTDC + DMA2D

务必在上面SDRAM配置成功后,再来搞这个!!!

详细教程看这个:https://zzttzz.gitee.io/blog/posts/7109b92c

但他给的源码还有点问题,运行处理没效果。

我提供的源码链接:

https://sxf1024.lanzoui.com/b09rf535a 密码:bf5q

注意:

  • 我跟他的配置有点不一样,我的是:
    • Pixel Clock Plparity:Normal Input
    • DMA2D要开一下中断,等级可以不用很高。如果不开的话,有可能会传图时候卡住。
    • LCD-TFT时钟:25MHz
  • 层 1 = layer 0,层 2 = layer 1


FreeRTOS

后面要上TouchGFX,这里先加操作系统。

当FreeRTOS遇到FATFS+SDIO时,这里有挺多注意细节的!!!

针对初学者,使用STM32CubeMX配置FreeRTOS时,大部分参数默认即可

  1. 使能freertos

  1. 由于freertos工作时会调用systick,为了防止其他进程干扰系统,所以当使用RTOS时,强烈建议使用HAL时基源而不是Systick。HAL时基源可以从SYS下的Pinout选项卡更改。因此更改系统时基源,这里选TIM6

改完之后,注意:中断处理程序调用RTOS函数,请确保它们的优先级比最高的系统调用中断优先级低(数字上高),例如FreeRTOS中的LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

  1. 如使用了FreeRTOS,会要求强制使用DMA模板的Fatfs,所以打开DMA通道开中断,以及开SDIO中断是必须的,否则后面配置FATFS无法运行。5的,而FreeRTOS要求优先级从5开始

  1. 当配置完发现无法mout SD卡,可以尝试加大CLKDIV值

  1. 修改Project Manager->Project->Linker Setting中的最小堆栈大小,太小就会无法挂载SD卡或者读写时失败,基本上默认值都是无法正常运行的

  1. FreeRTOS基本都是使用默认值,需要增大MINIMAL_STACK_SIZE,默认值是128,使用默认值会造成f_mount直接卡死在内部,这里使用256

  1. 生成代码,使用Keil打开。RTOS默认创建了一个defaultTask(),在freertos.c文件中
  2. 由于SD卡初始化时有检测读写是否在task任务中,所以SD读写测试代码需要放到defaultTask()

  1. 由于任务调度启动后就不再往下执行,所以把之前的LTDC显示测试代码也可以放到task中,或往前挪一挪

  1. 其他的无需改动,运行即可!
  2. 发现它创建任务用的是osThreadNew,对xTaskCreate又进行了封装,省去了繁琐
  3. 网上有人说要“在TIM6中断中,加入临界段保护(或进入中断保护)”,不知真假,没试

TouchGFX显示

虽然方便,但它是用C++开发的,所以不是特别友好...说白了就是看不懂,不知道怎么去改

  1. 先在Cube里安装TouchGFX

  1. 这里要注意,FreeRTOS要选CMSIS V1版本!!!

  1. 开启TouchGFX包,并配置如下

  1. 生成工程,但先别用keil打开!!!
  2. 到目录下安装TouchGFX Designer软件,它用来绘制界面的
C:Users10617STM32CubeRepositoryPacksSTMicroelectronicsX-CUBE-TOUCHGFX4.15.0UtilitiesPC_SoftwareTouchGFXDesignerTouchGFX-4.14.0.msi
  1. 安装完成后,回到Cube工程目录,会发现多了一个TouchGFX文件夹,打开其中的.touchgfx文件,绘制界面

  1. 然后就可以打开keil,添加以下内容
#include "app_touchgfx.h"
// 开启LCD
LCD_DisplayOn();
LCD_SetLayerVisible(1,DISABLE);
LCD_SetLayerVisible(0,ENABLE);
LCD_SetTransparency(1,0);
LCD_SetTransparency(0,255);
LCD_SelectLayer(0);
// 显示TouchGFX内容
MX_TouchGFX_Process();
  1. 这里要注意!由于MicroLib不支持C++,所以需要在keil里取消勾选!!!

  1. printf函数又需要MicroLib支持,所以需要添加以下函数,否则会开机无法运行!!!
#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 
  1. 完成以上内容后,编译、烧录即可

LittleVGL

奈何不会C++,只能另谋出路,LittltVGL设计的界面似乎还挺好看的,而且用C编写,兼容C++,更新很活跃。EMWIN风格类似window XP ,littlevGL风格类似android 。移植很简单(并没有多简单)。

官网github:https://github.com/lvgl/lvgl

官方文档:https://docs.lvgl.io/latest/en/html

官方推荐方法学习路线:

  • 点击在线演示以查看LVGL的运行情况(3分钟)
  • 阅读文档的介绍页面(5分钟)
  • 熟悉Quick overview页面的基础知识(15分钟)
  • 设置模拟器(10分钟)
  • 尝试一些例子
  • 移植LVGL到一个板子。请参阅移植指南或准备使用项目
  • 阅读概述页,更好地了解库(2-3小时)
  • 查看小部件的文档,了解它们的特性和用法
  • 如果你有问题可以去论坛
  • 阅读贡献指南,了解如何帮助改进LVGL(15分钟)

1、 教程可交叉参考以下这几篇,取长补短吧:

  • csdn移植教程:https://blog.csdn.net/t01051/article/details/108748462
  • 微雪移植教程:https://www.waveshare.net/study/article-964-1.html
  • No space解决方案:https://blog.csdn.net/qq_36075612/article/details/107671669
  • csdn移植教程:https://blog.csdn.net/zcy_cyril/article/details/107457371
  • 放SRAM/SDRAM:https://blog.csdn.net/qq_41543888/article/details/106532577

2、在Cube里开一个MTM的DMA

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 %drn",err);
while(1);	/*Halt on error*/
}
lv_disp_flush_ready(disp_drv);
}

4、按着上面教程,把littlvgl的显存地址改为SDRAM的

5、keil测试:

lv_init();
lv_port_disp_init();
// 开启LCD
LCD_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);
}

当然,我的源码链接(只有显示部分,触摸目前没用到):

https://sxf1024.lanzoui.com/b09rf535a 密码:bf5q


待补充...

最后

以上就是大意手套为你收集整理的keil接收别人发的工程打不开_STM32CubeMX6.0 + HAL + LittleVGL7.6 等学习[最全附工程源码]的全部内容,希望文章能够帮你解决keil接收别人发的工程打不开_STM32CubeMX6.0 + HAL + LittleVGL7.6 等学习[最全附工程源码]所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部