【GD32450I-EVAL】板载SDRAM支持LittleVGL
[复制链接]
此前的程序都是只支持内部RAM,内部RAM对于480*272的屏幕来说终究还是小了点,为了支持整个屏幕,必须开辟480*272*4字节的空间,总共510K,板载芯片尾号IK,只有256K,就算是最大的那个II系列,也只有512K,其余的程序分配2K根本就不够,单论堆栈空间都需要8K。
(一)外部SDRAM驱动
GD系列单片机SDRAM的驱动总线叫做EXMC,这个外设还同时支持各种nand flash nor falsh
地址示意:
SDRAM设备支持两个,起始地址分别是0xC0000000和0xD0000000,一个设备竟然有256Mb,一开始觉得这个配置是不是会空余很大的空白地址,后来看了下程序,发现整个256Mb都被使用了才发现。
这颗SDRAM型号是 MT48LC16M16A2P-6AIT ,里面有个16M的字样,一开始理所当然地以为是16M,后来看代码发现不太对,搜了一下才发现是一颗镁光的256M的内存!
驱动代码方面没什么值得注意的,因为配置完EXMC之后,就跟操作内部存储的差距不大了,屏蔽了底层,不需要调用什么函数。
驱动代码可以直接引用例程EXMC里面的SDRAM工程代码。
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0003)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_TIMEOUT ((uint32_t)0x0000FFFF)
void exmc_synchronous_dynamic_ram_init(uint32_t sdram_device)
{
exmc_sdram_parameter_struct sdram_init_struct;
exmc_sdram_timing_parameter_struct sdram_timing_init_struct;
exmc_sdram_command_parameter_struct sdram_command_init_struct;
uint32_t command_content = 0, bank_select;
uint32_t timeout = SDRAM_TIMEOUT;
rcu_periph_clock_enable(RCU_EXMC);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_GPIOH);
gpio_af_set(GPIOC, GPIO_AF_12, GPIO_PIN_2 | GPIO_PIN_5);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2 | GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2 | GPIO_PIN_5);
gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 |
GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 |
GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 |
GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_af_set(GPIOE, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 |
GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 |
GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 |
GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_af_set(GPIOF, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 |
GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_af_set(GPIOG, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 |
GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 |
GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 |
GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15);
gpio_af_set(GPIOH, GPIO_AF_12, GPIO_PIN_5);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_5);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
if(EXMC_SDRAM_DEVICE0 == sdram_device)
{
bank_select = EXMC_SDRAM_DEVICE0_SELECT;
}
else
{
bank_select = EXMC_SDRAM_DEVICE1_SELECT;
}
sdram_timing_init_struct.load_mode_register_delay = 2;
sdram_timing_init_struct.exit_selfrefresh_delay = 7;
sdram_timing_init_struct.row_address_select_delay = 5;
sdram_timing_init_struct.auto_refresh_delay = 6;
sdram_timing_init_struct.write_recovery_delay = 2;
sdram_timing_init_struct.row_precharge_delay = 2;
sdram_timing_init_struct.row_to_column_delay = 2;
sdram_init_struct.sdram_device = sdram_device;
sdram_init_struct.column_address_width = EXMC_SDRAM_COW_ADDRESS_9;
sdram_init_struct.row_address_width = EXMC_SDRAM_ROW_ADDRESS_13;
sdram_init_struct.data_width = EXMC_SDRAM_DATABUS_WIDTH_16B;
sdram_init_struct.internal_bank_number = EXMC_SDRAM_4_INTER_BANK;
sdram_init_struct.cas_latency = EXMC_CAS_LATENCY_3_SDCLK;
sdram_init_struct.write_protection = DISABLE;
sdram_init_struct.sdclock_config = EXMC_SDCLK_PERIODS_2_HCLK;
sdram_init_struct.brust_read_switch = ENABLE;
sdram_init_struct.pipeline_read_delay = EXMC_PIPELINE_DELAY_1_HCLK;
sdram_init_struct.timing = &sdram_timing_init_struct;
exmc_sdram_init(&sdram_init_struct);
sdram_command_init_struct.command = EXMC_SDRAM_CLOCK_ENABLE;
sdram_command_init_struct.bank_select = bank_select;
sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
sdram_command_init_struct.mode_register_content = 0;
while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0))
{
timeout--;
}
exmc_sdram_command_config(&sdram_command_init_struct);
delay_1ms(10);
sdram_command_init_struct.command = EXMC_SDRAM_PRECHARGE_ALL;
sdram_command_init_struct.bank_select = bank_select;
sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
sdram_command_init_struct.mode_register_content = 0;
timeout = SDRAM_TIMEOUT;
while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0))
{
timeout--;
}
exmc_sdram_command_config(&sdram_command_init_struct);
sdram_command_init_struct.command = EXMC_SDRAM_AUTO_REFRESH;
sdram_command_init_struct.bank_select = bank_select;
sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_8_SDCLK;
sdram_command_init_struct.mode_register_content = 0;
timeout = SDRAM_TIMEOUT;
while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0))
{
timeout--;
}
exmc_sdram_command_config(&sdram_command_init_struct);
command_content = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
sdram_command_init_struct.command = EXMC_SDRAM_LOAD_MODE_REGISTER;
sdram_command_init_struct.bank_select = bank_select;
sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
sdram_command_init_struct.mode_register_content = command_content;
timeout = SDRAM_TIMEOUT;
while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0))
{
timeout--;
}
exmc_sdram_command_config(&sdram_command_init_struct);
exmc_sdram_refresh_count_set(761);
timeout = SDRAM_TIMEOUT;
while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0))
{
timeout--;
}
}
(二)将缓冲放到SDRAM中
LittleVGL 和 TLI配置的缓冲区都要修改。
1-配置缓冲地址
定义SDRAM中的地址,初始化中使用了是SDRAM设备0,对应地址为 0xC0000000
定义地址:
#define EXT_BUFFER0_ADDR 0xC0000000
#define EXT_BUFFER1_ADDR (EXT_BUFFER0_ADDR + LCD_WIDTH * LCD_HEIGHT * 2)
*2是因为每个像素包含一个RGB565数据,5+6+5是16bit即两个8字节。
2-TLI 缓冲区
把TLI初始化中的缓冲位定向到SDRAM的地址中:
tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)EXT_BUFFER0_ADDR;
tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)EXT_BUFFER1_ADDR;
删去layer_window_leftpos、layer_window_rightpos、layer_window_toppos、layer_window_bottompos的偏移:
tli_layer_init_struct.layer_window_leftpos = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH;
tli_layer_init_struct.layer_window_rightpos = (LCD_WIDTH + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
tli_layer_init_struct.layer_window_toppos = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
tli_layer_init_struct.layer_window_bottompos = (LCD_HEIGHT + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
3-LittleVGL 缓冲
lv_port_disp_init 函数里,首先要在一开始调用EXMC的初始化函数,并且开辟内存:
exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
delay_ms(1);
memset((void *)EXT_BUFFER0_ADDR, 0, LCD_WIDTH * LCD_HEIGHT * LCD_FB_BYTE_PER_PIXEL*2);
memset((void *)EXT_BUFFER1_ADDR, 0, LCD_WIDTH * LCD_HEIGHT * LCD_FB_BYTE_PER_PIXEL*2);
重新定向缓冲区:
static lv_disp_buf_t disp_buf;
lv_disp_buf_init(&disp_buf, EXT_BUFFER0_ADDR, EXT_BUFFER1_ADDR, LCD_WIDTH * LCD_HEIGHT * LCD_FB_BYTE_PER_PIXEL);
disp_drv.buffer = &disp_buf;
4-重新配置触摸范围
DEMO_ReadTouch 函数中,把以下两行注释掉:
// touch_x -= 20;
// touch_y -= 30;
(三)显示效果
对比之前的局部显示效果:
代码:
源码查看本人gitee开源代码:
|