(一)概述
GD32F4系列是我知道的国内最早的M4F内核单片机了,另外的几个系列诸如F3、E1都是不带硬件FPU的M4内核,在一些运算的场景上劣势相当明显,当然,F4最终还是在今年被新推出的M33内核E5系列给摩擦了,后者超高精度定时器和TMU数学加速功能简直让人垂涎,连NXP的POWERQUAD都被不支持的arctan运算都给加速了!
但E5强大的数学能力并不足以让他处处压制F4系列,F4系列有一项重头戏功能——TLI借口,能够直驱RGB接口!而F450更是F4系列其中其中的最强者,主频高达200M,比E5系列还要高20M。
(二)TLI
TLI外设除了提供一般的RGB888接口还,还支持双显示层功能,能够通过DMA发送。
另外,直驱过RGB和MCU的一定能够感受到,RGB有一个重大的劣势,就是无法单独刷新某一块区域,这导致在设置DMA缓冲区时必须要一个完整的480*272*2(如果是使用RGB565 16位色)缓冲区,而双缓冲更是需要480*272*2*2,算下来足足有510K,目前F4作为兆易创新SRAM最大的片子也就512K,本芯片则是只有256K,只要程序有一点大小SRAM开销就放不下哪怕一个缓冲,如果不使用外部SDRAM的前提下根本不可能驱动一个最基本的480*272屏幕。
TLI的特性给了一个选择,可以设置填充空缺的位置,而使用一个较小的分辨率和较小的内存开销,这在开发测试阶段很意义。
(三)TLI设置
1-时钟源设置
TLI的时钟完全是由一个独立的PLL(PLLSAI)锁相环输出的,与系统时钟来源不同。值得一提的是I2S也是另一个独立的PLL时钟源(PLLI2S),总共三个PLL锁相环,让音频应用时钟也可以更准确。
TLI的时钟从(PLLSAI)锁相环输出:
通过rcu_pllsai_config设置锁相环参数,rcu_osci_on使能锁相环,rcu_tli_clock_div_config使能锁相环后的分频参数
/* configure PLLSAI to generate TLI clock */
if(ERROR == rcu_pllsai_config(192, 2, 3)){
while(1);
}
rcu_tli_clock_div_config(RCU_PLLSAIR_DIV8);
rcu_osci_on(RCU_PLLSAI_CK);
if(ERROR == rcu_osci_stab_wait(RCU_PLLSAI_CK)){
while(1);
}
2-使能本身设备时钟
rcu_periph_clock_enable(RCU_TLI);
3-使能GPIO
void tli_gpio_config(void)
{
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOH);
rcu_periph_clock_enable(RCU_GPIOI);
rcu_periph_clock_enable(RCU_GPIOG);
/* configure HSYNC(PI10), VSYNC(PI9), PCLK(PG7) */
/* configure LCD_R7(PG6), LCD_R6(PH12), LCD_R5(PH11), LCD_R4(PH10), LCD_R3(PH9),LCD_R2(PH8),
LCD_R1(PH3), LCD_R0(PH2), LCD_G7(PI2), LCD_G6(PI1), LCD_G5(PI0), LCD_G4(PH15),
LCD_G3(PH14), LCD_G2(PH13),LCD_G1(PE6), LCD_G0(PE5),LCD_B7(PI7), LCD_B6(PI6),
LCD_B5(PI5), LCD_B4(PI4), LCD_B3(PG11),LCD_B2(PG10), LCD_B1(PG12), LCD_B0(PE4) */
/* configure TLI pins AF function */
gpio_af_set(GPIOE,GPIO_AF_14,GPIO_PIN_5);
gpio_af_set(GPIOE,GPIO_AF_14,GPIO_PIN_6);
gpio_af_set(GPIOE,GPIO_AF_14,GPIO_PIN_4);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_2);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_3);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_8);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_9);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_10);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_11);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_12);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_13);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_14);
gpio_af_set(GPIOH,GPIO_AF_14,GPIO_PIN_15);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_0);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_1);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_2);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_4);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_5);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_6);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_7);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_9);
gpio_af_set(GPIOI,GPIO_AF_14,GPIO_PIN_10);
gpio_af_set(GPIOG,GPIO_AF_14,GPIO_PIN_6);
gpio_af_set(GPIOG,GPIO_AF_14,GPIO_PIN_7);
gpio_af_set(GPIOG,GPIO_AF_14,GPIO_PIN_10);
gpio_af_set(GPIOG,GPIO_AF_14,GPIO_PIN_11);
gpio_af_set(GPIOG,GPIO_AF_14,GPIO_PIN_12);
/* configure TLI GPIO */
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_200MHZ,GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2|GPIO_PIN_3|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(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_200MHZ,GPIO_PIN_2|GPIO_PIN_3|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(GPIOI, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4
|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_9|GPIO_PIN_10);
gpio_output_options_set(GPIOI, GPIO_OTYPE_PP, GPIO_OSPEED_200MHZ,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4
|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_9|GPIO_PIN_10);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP,GPIO_OSPEED_200MHZ, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12);
/* LCD PWM BackLight(PB15) */
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_15);
gpio_bit_set(GPIOB,GPIO_PIN_15);
}
4-TLI初始化
设置HS、VS、DE几个引脚空闲状态高低电平状态
/* configure TLI parameter struct */
tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI;
配置前后空闲时序,可以通过bpsz实现局部范围内的图像显示
/* LCD display timing configuration */
tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;
tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;
tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;
tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;
tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1;
tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1;
tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1;
tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1;
设置背景颜色
/* configure LCD background R,G,B values */
tli_init_struct.backcolor_red = 0xFF;
tli_init_struct.backcolor_green = 0xFF;
tli_init_struct.backcolor_blue = 0xFF;
tli_init(&tli_init_struct);
5-TLI单一层显示
通过层的POS实现单个层在局部范围内显示,比如下面这个例子就是指显示X:20->160; Y:30->90
配置这些参数对应的是设置TLI_LxHPOS和TLI_LxVPOS寄存器实现,外部像素由TLI_LxDC决定,对应的就是下面的layer_default_xx字段
/* TLI window size configuration */
tli_layer_init_struct.layer_window_leftpos = 20 + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH;
tli_layer_init_struct.layer_window_rightpos = (20 + 140 + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
tli_layer_init_struct.layer_window_toppos = 30 + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
tli_layer_init_struct.layer_window_bottompos = (30 + 60 + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
设置像素格式RGB565
/* TLI window pixel format configuration */
tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
设置层的透明程度
/* TLI window specified alpha configuration */
tli_layer_init_struct.layer_sa = 255;
设置层混合模式【图】
/* TLI window blend configuration */
tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
设置该层显示范围外的颜色
/* TLI layer default alpha R,G,B value configuration */
tli_layer_init_struct.layer_default_alpha = 0;
tli_layer_init_struct.layer_default_blue = 0xFF;
tli_layer_init_struct.layer_default_green = 0xFF;
tli_layer_init_struct.layer_default_red = 0xFF;
设置缓冲区及缓冲区数据组织形式,每行多少数据、多少行。
/* TLI layer frame buffer base address configuration */
tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)&gImage_0;
tli_layer_init_struct.layer_frame_line_length = ((140 * 2) + 3);
tli_layer_init_struct.layer_frame_buf_stride_offset = (140 * 2);
tli_layer_init_struct.layer_frame_total_line_number = 60;
tli_layer_init(LAYER1, &tli_layer_init_struct);
6-开防抖动
tli_dither_config(TLI_DITHER_ENABLE);
可以看到,虽然数据手册里说了内部有DMA发送数据,但实际配置的时候是不需要额外配置DMA的,
tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)&gImage_0;
这句实际上是把缓冲区基地址传进了一个寄存器就完事了
(四)TLI测试
例程中实现了两个层叠加显示,我就不再重复了,关键是要实现两个层轮流单独显示
1-使能层显示
tli_layer_enable(LAYER0);
tli_reload_config(TLI_REQUEST_RELOAD_EN);
tli_enable();
2-在500ms周期轮流显示两个图案
static __IO uint32_t timingdelaylocal = 0U;
if(timingdelaylocal){
if(timingdelaylocal < 500U){
gd_eval_led_on(LED1);
tli_layer_disable(LAYER1);
tli_layer_enable(LAYER0);
tli_reload_config(TLI_REQUEST_RELOAD_EN);
}else{
gd_eval_led_off(LED1);
tli_layer_disable(LAYER0);
tli_layer_enable(LAYER1);
tli_reload_config(TLI_REQUEST_RELOAD_EN);
}
timingdelaylocal--;
}else{
timingdelaylocal = 1000U;
}
(五)效果