概述
STM32CubeMX + HAL一些说明底层配置Cube基本使用HAL库函数中断回调函数外设对应时钟配置示例小编有话说USARTRTCSDIO + FATFSSDRAMLTDC + DMA2DFreeRTOSTouchGFX显示LittleVGL待补充...
STM32CubeMX + HAL
一些说明
底层配置
使用STM32CubeMX
代码生成工具,不用关注底层配置的细节,真舒服。
使用教程:
https://sxf1024.lanzoui.com/b09rf2dwj 密码:bgvi
虽然Cube+HAL
很舒服,但新手不建议用。最好还是先去学一下标准库怎么用,有个大致概念后,再来学这一套。
Cube基本使用
- 新建工程
- 选择芯片
- Pinout&Configuration,选择
RCC(HSE:Crystal/Ceramic Resonator)
、SYS(Debug:Serial Wiire)
- Clock Configuration,配置时钟树
- Project Manager,配置工程输出项
- Pinout&Configuration,选择功能(若是选
GPIO
相关,可以直接在Pinout view选择;若是其他功能,可以在左边Categories打开,会自动配置引脚)、设置Parameter Settings/NVIC
等
- 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()
外设对应时钟
- 随便进入一个外设初始化函数,如
MX_GPIO_Init()
- 随便进入一个时钟使能函数,如
__HAL_RCC_GPIOC_CLK_ENABLE()
- 随便进入一个RCC宏定义,如
RCC_AHB1ENR_GPIOCEN
- 或者直接进入
stm32f429xx.h
文件 - 里面有所有外设与时钟对应关系,如
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
- 选择SDIO功能,Pinout&Clock Configuration,
Connectivity -> SDIO -> Mode: SD 4bit Wide bus -> 勾选NVIC
- 配置SDIO时钟,Clock Configuration,SDIO模块输入要求为48MHz,系统提示可以自动设置时钟问题,选择
Yes
。SDIO时钟分频系数CLKDIV
,计算公式为SDIO_CK=48MHz/(CLKDIV+2)
也可手动修改时钟配置。
- 启用文件系统中间件,Pinout&Clock Configuration,
Middleware -> FATFS
,模式选择SD卡,配置文件系统:
CODE_PAGE为Simplified Chinese
USE_LEN
- 继续上面界面,Advanced Settings勾选
Use dma template
- 设置DMA传输,Pinout&Clock Configuration,
Connectivity -> SDIO-> DMA Settings
- 配置NVIC,Pinout&Clock Configuration,
System Core -> NVIC
。
注意,SDIO中断优先级必须高于DMA2 stream3和DMA2 stream6的中断优先级
- 堆栈设置,Progect Manager -> Project -> Linker Settings,加大堆栈大小(注意:由于刚才设置长文件名动态缓存存储在堆中,故需要增大堆大小,如果不修改则程序运行时堆会生成溢出,程序进入硬件错误中断(HardFault),死循环)。
- 生成工程,GENERATE CODE ,用KEIL打开
- 注意,如果是野火的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模块
}
- 测试一下
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
- 开启FMC功能,Pinout&Configuration ,
Connectivity -> FMC -> SDRAM2
SDRAM1的起始地址为0XC0000000,SDRAM2的起始地址为0XD0000000。
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 FFFFBank
由硬件连接决定需要选择SDRAM bank 2Column bit number
表示列数,8位Row bit number
表示行数,12位CAS latency
表示CAS潜伏期,即上面说的CL,该配置需要与之后的SDRAM模式寄存器的配置相同,这里先配置为2 memory clock cycles(对于SDRAM时钟超过133MHz的,则需要配置为3 memory clock cycles)Write protection
表示写保护,一般配置为DisabledSDRAM common clock
为SDRAM 时钟配置,可选HCLK的2分频3分频不使能SDCLK时钟。前面主频配置为216MHz,SDRAM common clock设置为2分频,那SDCLK时钟为108MHz,每个时钟周期为9.25nsSDRAM common burst read
表示突发读,这里选择使能SDRAM common read pipe delay
表示CAS潜伏期后延迟多少个时钟在进行读数据,这里选择0 HCLK clock cycleLoad 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 = 5 SDRAM common row cycle delay = 7 SDRAM common row precharge delay = 2 Row to column delay
Row to column delay应该取3
- 生成代码,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]);
}
- 到这里只是借助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时,大部分参数默认即可
- 使能freertos
- 由于freertos工作时会调用systick,为了防止其他进程干扰系统,所以当使用RTOS时,强烈建议使用HAL时基源而不是Systick。HAL时基源可以从SYS下的Pinout选项卡更改。因此更改系统时基源,这里选TIM6
改完之后,注意:中断处理程序调用RTOS函数,请确保它们的优先级比最高的系统调用中断优先级低(数字上高),例如FreeRTOS中的LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
- 如使用了FreeRTOS,会要求强制使用DMA模板的Fatfs,所以打开DMA通道,开中断,以及开SDIO中断是必须的,否则后面配置FATFS无法运行。
5的,而FreeRTOS要求优先级从5
开始
- 当配置完发现无法mout SD卡,可以尝试加大CLKDIV值
- 修改
Project Manager->Project->Linker Setting
中的最小堆栈大小,太小就会无法挂载SD卡或者读写时失败,基本上默认值都是无法正常运行的
- FreeRTOS基本都是使用默认值,需要增大
MINIMAL_STACK_SIZE
,默认值是128,使用默认值会造成f_mount直接卡死在内部,这里使用256
- 生成代码,使用Keil打开。RTOS默认创建了一个
defaultTask()
,在freertos.c
文件中 - 由于SD卡初始化时有检测读写是否在task任务中,所以SD读写测试代码需要放到
defaultTask()
中
- 由于任务调度启动后就不再往下执行,所以把之前的LTDC显示测试代码也可以放到task中,或往前挪一挪
- 其他的无需改动,运行即可!
- 发现它创建任务用的是
osThreadNew
,对xTaskCreate
又进行了封装,省去了繁琐 - 网上有人说要“在TIM6中断中,加入临界段保护(或进入中断保护)”,不知真假,没试
TouchGFX显示
虽然方便,但它是用C++开发的,所以不是特别友好...说白了就是看不懂,不知道怎么去改
- 先在
Cube
里安装TouchGFX
包
- 这里要注意,
FreeRTOS
要选CMSIS V1
版本!!!
- 开启
TouchGFX
包,并配置如下
- 生成工程,但先别用keil打开!!!
- 到目录下安装TouchGFX Designer软件,它用来绘制界面的
C:Users10617STM32CubeRepositoryPacksSTMicroelectronicsX-CUBE-TOUCHGFX4.15.0UtilitiesPC_SoftwareTouchGFXDesignerTouchGFX-4.14.0.msi
- 安装完成后,回到Cube工程目录,会发现多了一个
TouchGFX
文件夹,打开其中的.touchgfx
文件,绘制界面
- 然后就可以打开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();
- 这里要注意!由于
MicroLib
不支持C++
,所以需要在keil里取消勾选!!!
- 但
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
- 完成以上内容后,编译、烧录即可
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 等学习[最全附工程源码]所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复