本人手中的是正点原子阿波罗H743+4.3寸RGB屏,最近开始学习点亮屏幕并尝试移植LVGL8.4。学习过程中遇到了好多坑,而且基本很难搜到对应的解答,好在碰巧解决了几个问题,记录一下备忘,也希望对其他新手有帮助。
1.本人直接是从CubeIDE开始学起没有用Keil,刚开始新建项目时会弹出一个窗口,建议开启MPU。我也不懂是啥意思,就选同意了,这个无意中的选择让我后面花了大量的时间去排错。
因为点亮屏幕最重要的一步是设置SDRAM,就是因为MPU这个默认的“ALL ACCESS NOT PERMITTED” 选项,导致我无论如何无法访问到SDRAM,同时屏幕一直花屏。而恰好点亮屏幕需要设置包括FMC、LTDC在内的很多参数,我也吃不准问题出在哪里,于是我对着正点原子的代码以及上网搜了好多帖子,各种调整参数,弄了好几天还是无法运行。几乎就要放弃的时候瞎点进了MPU,试着关掉了MPU Region,结果代码正常运行了。。。
另外,CubeIDE和Keil不同,无法通过at指定图像缓存地址,需要用到section关键字。这个问题也花了一些时间,不过还是搜到了解决方法。下面的参数设置对于H743来说是能正常运行的,记录一下。
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 9;
SdramTiming.SelfRefreshTime = 6;
SdramTiming.RowCycleDelay = 8;
SdramTiming.WriteRecoveryTime = 4;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
//发送SDRAM初始化序列
void SDRAM_Init(void){
uint32_t temp=0;
//SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM
SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_CLK_ENABLE,1,0); //时钟配置使能
HAL_Delay(1); //至少延时200us
SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_PALL,1,0); //对所有存储区预充电
SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_AUTOREFRESH_MODE,8,0);//设置自刷新次数
//配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度,
//bit3为指定突发访问的类型,bit4~bit6为CAS值,bit7和bit8为运行模式
//bit9为指定的写突发模式,bit10和bit11位保留位
temp=(uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | //设置突发长度:1(可以是1/2/4/8)
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | //设置突发类型:连续(可以是连续/交错)
SDRAM_MODEREG_CAS_LATENCY_2 | //设置CAS值:3(可以是2/3)
SDRAM_MODEREG_OPERATING_MODE_STANDARD | //设置操作模式:0,标准模式
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; //设置突发写模式:1,单点访问
SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_LOAD_MODE,1,temp); //设置SDRAM的模式寄存器
//刷新频率计数器(以SDCLK频率计数),计算方法:
//COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
//我们使用的SDRAM刷新周期为64ms,SDCLK=240/2=120Mhz,行数为8192(2^13).
//所以,COUNT=64*1000*120/8192-20=918
HAL_SDRAM_ProgramRefreshRate(&hsdram1,918);
}
/* 这个不行*/
//u16 ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR)));
uint16_t framebuf[1280][800] __attribute__((section (".sdram_data")));
/* STM32H743IITX_FLASH.ld 文件添加代码*/
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
SDRAM (xrw) : ORIGIN = 0xc0000000, LENGTH = 4M //添加此行
}
/* 末尾添加该代码 */
.sdram_data (NOLOAD) :
{
. = ALIGN(4);
_sdram_data_begin = .;
*(.sdram_data)
*(.sdram_data*)
. = ALIGN(4);
_sdram_data_end = .;
} >SDRAM
2. 跟着正点原子视频一步步移植LVGL,最开始是要建立目录,我照猫画虎在CubeIDE工程根目录下新建了Middlewares/LVGL/GUI/lvgl,然后放入LVGL相关文件并开始改代码,做到最后要添加定时器了,跑到ioc文件设置好并点击更新代码后,整个Middlewares目录及之下的文件居然全部删除了,之前算是白忙活了。。。后来查帖子才知道Middlewares应该是CubeIDE在添加好Freertos这样的中间件后会自动生成的目录,如果是自己新建的同名目录,在刷新代码后CubeIDE因为会发现其中没有中间件而自动删除。
3. 在全部移植好后,运行demo发现一直花屏。又是各种查帖子各种尝试调整(期间以为是LVGL8.4这个版本有问题,其实不是),发现在打点这一步如果不用正点原子代码中的LCD_Color_Fill函数,而是直接用lvgl模板中的for循环打点方法是没问题的。而LCD_Color_Fill最主要是用了DMA2D。于是一行一行地研究这个函数,最后发现了一个坑,代码中使能DMA2D时钟的是:RCC->AHB1ENR|=1<<23;
虽然这是正点原子H743教程的代码,但其实这句并不适用H743,看了STM32官方手册发现其实应该是:RCC->AHB3ENR|=1<<4;才对
DMA2D时钟没有正常开启当然会花屏。。。
不过即使调好了,发现还是会小花(如下图)。然后又是各种查原因,怎么也查不到。最后又瞎点到了MPU这个菜单,试着关掉了自己之前手欠开启的CPU I-Cache和D-Cache之后总算正常了。后来专门仔细看了正点原子关于MPU的讲解:
在使能 D Cache 之后, SRAM 里面的数据有可能会被缓存在 Cache 里面,此时如果有 DMA 之类的外设访问这个 SRAM 里面的数据,就有可能和 Cache 里面数据不同步, 导致数据出错,为了防止这种问题,保证数据的一致性,我们设置了 D Cache 的强制透写功能 (Write Through),这样 CPU 每次操作 Cache 里面的数据,同时也会更新到 SRAM 里面,保证D Cache 和 SRAM 里面数据一致。
我在添加了D-Cache强制透写语句SCB->CACR |= 1<<2;后发现仍然会有少量花屏现象,所以干脆直接关掉,然后就正常了。
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
/*虽然官方注释是most simple but also the slowest,但确实好用*/
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_DrawPoint(x, y, color_p->full);
color_p++;
}
}
}
//LCD_Color_Fill(area->x1,area->y1,area->x2,area->y2,(uint16_t *)color_p);
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
4. 除了以上问题,还有比较头疼的是头文件路径问题。我的工程目录结构如下,在项目头文件属性里设置好了各个文件夹的路径。最后在main文件里引用头文件的时候其他目录下的头文件都能直接引用,唯独TOUCH目录下的文件死活引用不了,必须设置绝对路径才行。到现在还没有找到原因,还请大神们指教(另外不知道TOUCH文件夹上的这个小扳手是啥意思)。
跟着正点原子的视频学习STM32有一阵子了,虽然其间碰到了各种莫名其妙抓狂的问题,但是也有不少收获。学习总归是伴随着痛苦的,尤其是编程,怪不得说程序猿头发少。幸好自己只是业余爱好,不靠这个吃饭,但也希望能继续坚持下去吧~