【天空星GD32F407VET6移植FreeRTOS+LVGL 硬件SPI+DMA 3.5寸触摸屏ILI9488踩坑记 】

概要

基于官方的飞书文档中的1.13-立创开发板-3.5寸ILI9488彩色触摸屏幕软硬件SPI二合一移植成功案例修改

新手学习中,遇到不合理的地方请大佬们多多指教!
实现了FreeRTOS + LVGL使用硬件SPI+DMA 刷屏,顺便移植了easylogger方便调试打印。基础的移植就不多赘述了,网上一堆案例可以参考,我直接贴上我调试中遇到的好几个踩坑点。

FreeRTOS任务

void led_task(void)
{
	while(1)
	{
		// 配置PB2引脚输出高电平
		gpio_bit_write(GPIOB,GPIO_PIN_2,SET);
		vTaskDelay(pdMS_TO_TICKS(500)); // 延时 500 毫秒
		// 配置PB2引脚输出低电平
		gpio_bit_write(GPIOB,GPIO_PIN_2,RESET);
		vTaskDelay(pdMS_TO_TICKS(500)); 
	}
	
	vTaskDelete(NULL);
}	


void lcd_task(void)
{
	TickType_t xLastWakeTime;
	const TickType_t xPeriod = pdMS_TO_TICKS( 5 );
	
	xLastWakeTime = xTaskGetTickCount();  
	
	lv_init();
	lv_port_disp_init(); 
	lv_port_indev_init();	
	
	lv_demo_widgets();
	
	while(1)
	{
		/* 调用系统延时函数,周期性阻塞5ms */    
		vTaskDelayUntil( &xLastWakeTime,xPeriod );
		lv_task_handler();
	}
	
	vTaskDelete(NULL);
}	

SPI初始化

踩坑点1:例程使用SPI1时,LVGL运行一段时间后会直接卡死。

SPI0和SPI1时钟不一样

如果使用SPI1,配置SPI为2分频的时候,LVGL运行一段时间后会直接卡死在当前画面中,只有配置成4分频才能正常工作,但是帧率低了很多。debug调试发现是卡死在了SPIv_WriteData函数中的while循环中里。

  /* 配置 SPI 参数 */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;  	// 传输模式全双工
    spi_init_struct.device_mode          = SPI_MASTER;   				// 配置为主机
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;			// 8位数据
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;		// 极性高相位2
    spi_init_struct.nss                  = SPI_NSS_SOFT;  				// 软件cs
    spi_init_struct.prescale             = SPI_PSC_2;					// 2分频
    spi_init_struct.endian               = SPI_ENDIAN_MSB;				// 高位在前	
    spi_init(PORT_SPI, &spi_init_struct);
void  SPIv_WriteData(u8 Data)
{
    while(RESET == spi_i2s_flag_get(PORT_SPI, SPI_FLAG_TBE));
    spi_i2s_data_transmit(PORT_SPI, Data);
    while(RESET == spi_i2s_flag_get(PORT_SPI, SPI_FLAG_RBNE)); //会在此处卡死
    spi_i2s_data_receive(PORT_SPI);  
}
  1. 修改 startup_gd32f407_427.s 中 Stack_Size 和 Heap_Size的大小,仍然会卡死;
  2. 修改 lv_conf.h 中的 LV_MEM_SIZE 大小,无作用;
  3. 增加或修改RBNE为while(SET == spi_i2s_flag_get(PORT_SPI, SPI_FLAG_TRANS)),无作用;
  4. 增加超时退出或者删除掉此句,显示不正常,直接白屏;
  5. 后来翻了翻gd32的手册,发现SPI0和SPI1最大速度不同,尝试使用SPI0的二分频后,就没发生过卡死了。推测是SPI1二分频后速度太快导致发送异常?但是APB1是42MHz,二分频为21MHz,没超过官方文档所说的最大30MHz,反倒是APB2是84MHz,二分频就42MHz远远超过最大频率了,反而没出错,这里就百思不得其解了。
    在这里插入图片描述

LCD颜色填充函数

踩坑点2:ILI9488使用SPI时,只有三字节的RGB666可以使用!

买屏幕的时候没具体了解屏幕的驱动方式,导致后面修改填充函数的时候踩了大坑,刷屏的时候要进行格式转换,导致LVGL帧率低了巨多。

一开始找资料发现,初始化中修改0x3A寄存器可以改成16BIT传输。但是试了一下发现不起作用,后面才知道3/4线SPI驱动屏幕时是不能使用16BIT,使用并口驱动修改才有用。

/* Interface Pixel Format (3Ah) */
LCD_WR_REG(0x3A);
LCD_WR_DATA(0x66); //0X55 16BIT/PIXEL

在这里插入图片描述
于是只能强行使用18bit然后转换一下格式才能正常显示,这一来一回浪费了太多时间跟内存,白白损失太多性能了,所以非常不推荐使用ILI94XX系列的驱动芯片使用SPI驱动,太坑了!! 如果是16BIT的话,可以完美适配LVGL,直接利用uint16_t指针进行dma发送,完全不需要格式转换。

uint8_t SPI_TX_BUFF[X_MAX_PIXEL * 10 * 3];  //宽480,10行

void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,uint16_t* color)
{
	u16 width=ex-sx+1; 	
	u16 height=ey-sy+1;		
	LCD_SetWindows(sx,sy,ex,ey);

	u32 pixelNum = width*height;

	uint32_t j = 0;
	for(uint32_t i = 0; i < pixelNum; i++) 
	{
		SPI_TX_BUFF[j++] = ((*color>>8)&0xF8);//RED
		SPI_TX_BUFF[j++] = ((*color>>3)&0xFC);//GREEN
		SPI_TX_BUFF[j++] = (*color<<3);//BLUE

		color++;
	}

	LCD_CS_CLR; 
	LCD_RS_SET; 

	#if !USE_SPI_DMA
	
	for(int i = 0;i < j;i++)
	{
		SPIv_WriteData(buf[i]);
	}	
	
	LCD_CS_SET;
	#else
 
	// 配置发送DMA通道 
	dma_channel_disable(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH);
	dma_flag_clear(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, DMA_FLAG_FTF);	
	dma_memory_address_config(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, DMA_MEMORY_0,(uint32_t)SPI_TX_BUFF);
	dma_transfer_number_config(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, j);	
	dma_channel_enable(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH);	
	#endif
}


其他函数


#define BUFFER_ROW    10 
static lv_disp_drv_t * disp_drv_temp = NULL;

void SPI_DMAInit(void)
{
	//DMA初始化
	rcu_periph_clock_enable(RCU_DMA_SPI);  
    
	//DMA发送
    dma_single_data_parameter_struct dma_init_SPI_TX={0};

	dma_deinit(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH);      /* SPI0_Tx --> DMA1 Channel3 */
    dma_init_SPI_TX.periph_addr = (uint32_t)(&SPI_DATA(PORT_SPI));  	// 配置外设地址为SPI数据寄存器地址
    dma_init_SPI_TX.periph_inc  = DMA_PERIPH_INCREASE_DISABLE;   	// 禁止外设地址增量
    dma_init_SPI_TX.memory0_addr= NULL;           					// 配置存储器地址
    dma_init_SPI_TX.memory_inc  = DMA_MEMORY_INCREASE_ENABLE;    	// 允许存储器地址增量
    dma_init_SPI_TX.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;  	// 配置外设宽度为8位
    dma_init_SPI_TX.direction   = DMA_MEMORY_TO_PERIPH;            // 配置传输方向为存储器到外设	
    dma_init_SPI_TX.number      = 0;                  	 			// 配置传输数据个数
    dma_init_SPI_TX.priority    = DMA_PRIORITY_ULTRA_HIGH;         // 配置DMA优先级
    dma_init_SPI_TX.circular_mode = DMA_CIRCULAR_MODE_DISABLE; 	// 禁止循环模式
	
    dma_single_data_mode_init(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, &dma_init_SPI_TX);	// 初始化DMA0的DMA_CH4通道
    dma_channel_subperipheral_select(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, DMA_SUBPERI3);
	dma_channel_enable(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH);	

	spi_dma_enable(PORT_SPI,SPI_DMA_TRANSMIT);	

	//DMA中断
	/* 使能DMA通道中断 */
	nvic_irq_enable(DMA1_Channel3_IRQn, 0, 0); 
	/* 使能DMA中断 */
	dma_interrupt_enable(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, DMA_CHXCTL_FTFIE);  // 使能传输完成中断
}


void LCD_GPIOInit(void)
{
	spi_parameter_struct spi_init_struct;
	
      /* 开启各引脚时钟 */
    rcu_periph_clock_enable(RCU_LCD_SCL);
    rcu_periph_clock_enable(RCU_LCD_SDA);
    rcu_periph_clock_enable(RCU_LCD_CS);
    rcu_periph_clock_enable(RCU_LCD_DC);
    rcu_periph_clock_enable(RCU_LCD_RES);
    rcu_periph_clock_enable(RCU_LCD_BLK);
	
      /* 使能SPI */      
    rcu_periph_clock_enable(RCU_SPI_HARDWARE); 

      /* 配置 SPI的SCK GPIO */
    gpio_af_set(PORT_LCD_SCL, LINE_AF_SPI, GPIO_LCD_SCL);
    gpio_mode_set(PORT_LCD_SCL, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_LCD_SCL);
    gpio_output_options_set(PORT_LCD_SCL, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_LCD_SCL);
    gpio_bit_set(PORT_LCD_SCL,GPIO_LCD_SCL);
	
      /* 配置 SPI的MOSI  GPIO */
    gpio_af_set(PORT_LCD_SDA, LINE_AF_SPI, GPIO_LCD_SDA);
    gpio_mode_set(PORT_LCD_SDA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_LCD_SDA);
    gpio_output_options_set(PORT_LCD_SDA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_LCD_SDA);
    gpio_bit_set(PORT_LCD_SDA, GPIO_LCD_SDA);         

       /* 配置DC */
    gpio_mode_set(PORT_LCD_DC,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,GPIO_LCD_DC);
    gpio_output_options_set(PORT_LCD_DC,GPIO_OTYPE_PP,GPIO_OSPEED_MAX,GPIO_LCD_DC);
    gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, SET);
	
       /*  配置RES */
    gpio_mode_set(PORT_LCD_RES,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,GPIO_LCD_RES);
    gpio_output_options_set(PORT_LCD_RES,GPIO_OTYPE_PP,GPIO_OSPEED_MAX,GPIO_LCD_RES);
    gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, SET);
	
       /* 配置BLK */
    gpio_mode_set(PORT_LCD_BLK, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_BLK);
    gpio_output_options_set(PORT_LCD_BLK, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_LCD_BLK);
    gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, SET);
	
       /*配置CS */
    gpio_mode_set(PORT_LCD_CS,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO_LCD_CS);
    gpio_output_options_set(PORT_LCD_CS,GPIO_OTYPE_PP,GPIO_OSPEED_MAX,GPIO_LCD_CS);
    gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, SET);

    /* 配置 SPI 参数 */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;  	// 传输模式全双工
    spi_init_struct.device_mode          = SPI_MASTER;   				// 配置为主机
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;			// 8位数据
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;		// 极性高相位2
    spi_init_struct.nss                  = SPI_NSS_SOFT;  				// 软件cs
    spi_init_struct.prescale             = SPI_PSC_2;					// 2分频
    spi_init_struct.endian               = SPI_ENDIAN_MSB;				// 高位在前	
    spi_init(PORT_SPI, &spi_init_struct);


    /* 使能 SPI */
    spi_enable(PORT_SPI);
      
	#if USE_SPI_DMA  
	SPI_DMAInit();
	#endif
}


void LCD_Init(void)
{  
	LCD_GPIOInit();//LCD GPIO初始化	
 	LCD_RESET(); //LCD 复位
//************* ILI9488初始化**********//	
	LCD_WR_REG(0XF7);
	LCD_WR_DATA(0xA9);
	LCD_WR_DATA(0x51);
	LCD_WR_DATA(0x2C);
	LCD_WR_DATA(0x82);

	/* Power Control 1 (C0h)  */
	LCD_WR_REG(0xC0);
	LCD_WR_DATA(0x11);
	LCD_WR_DATA(0x09);

	/* Power Control 2 (C1h) */
	LCD_WR_REG(0xC1);
	LCD_WR_DATA(0x41);

	/* VCOM Control (C5h)  */
	LCD_WR_REG(0XC5);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x0A);
	LCD_WR_DATA(0x80);

	/* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
	LCD_WR_REG(0xB1);
	LCD_WR_DATA(0xB0);
	LCD_WR_DATA(0x11);

	/* Display Inversion Control (B4h) */
	LCD_WR_REG(0xB4);
	LCD_WR_DATA(0x02); 

	/* Display Function Control (B6h)  */
	LCD_WR_REG(0xB6);
	LCD_WR_DATA(0x02);
	LCD_WR_DATA(0x42); //0x42 0X22

	/* Entry Mode Set (B7h)  */
	LCD_WR_REG(0xB7);
	LCD_WR_DATA(0xc6);

	/* HS Lanes Control (BEh) */
	LCD_WR_REG(0xBE);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x04);

	/* Set Image Function (E9h)  */
	LCD_WR_REG(0xE9);
	LCD_WR_DATA(0x00);


	LCD_WR_REG(0x36);
	//LCD_WR_DATA((1<<3)|(0<<7)|(1<<6)|(1<<5));
	LCD_WR_DATA( 0x08 );

	/* Interface Pixel Format (3Ah) */
	LCD_WR_REG(0x3A);
	LCD_WR_DATA(0x66); //0X55 16BIT/PIXEL

	/* PGAMCTRL (Positive Gamma Control) (E0h) */
	LCD_WR_REG(0xE0);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x07);
	LCD_WR_DATA(0x10);
	LCD_WR_DATA(0x09);
	LCD_WR_DATA(0x17);
	LCD_WR_DATA(0x0B);
	LCD_WR_DATA(0x41);
	LCD_WR_DATA(0x89);
	LCD_WR_DATA(0x4B);
	LCD_WR_DATA(0x0A);
	LCD_WR_DATA(0x0C);
	LCD_WR_DATA(0x0E);
	LCD_WR_DATA(0x18);
	LCD_WR_DATA(0x1B);
	LCD_WR_DATA(0x0F);

	/* NGAMCTRL (Negative Gamma Control) (E1h)  */
	LCD_WR_REG(0XE1);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x17);
	LCD_WR_DATA(0x1A);
	LCD_WR_DATA(0x04);
	LCD_WR_DATA(0x0E);
	LCD_WR_DATA(0x06);
	LCD_WR_DATA(0x2F);
	LCD_WR_DATA(0x45);
	LCD_WR_DATA(0x43);
	LCD_WR_DATA(0x02);
	LCD_WR_DATA(0x0A);
	LCD_WR_DATA(0x09);
	LCD_WR_DATA(0x32);
	LCD_WR_DATA(0x36);
	LCD_WR_DATA(0x0F);

	LCD_WR_REG(0x11);	//SLEEP OUT
	delay_ms(120);
	LCD_WR_REG(0x29);	//DISPLAY ON
	
	LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
	LCD_LED_SET; //点亮背光
	LCD_Clear(WHITE);//清全屏白色
}

void lv_port_disp_init(void)
{
    /*-------------------------
     * Initialize your display
     * -----------------------*/
    disp_init();
    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/
    /* Example for 2) */
    static lv_disp_draw_buf_t draw_buf_dsc_2;
    static lv_color_t buf_2_1[MY_DISP_HOR_RES * BUFFER_ROW];                        /*A buffer for 10 rows*/
    static lv_color_t buf_2_2[MY_DISP_HOR_RES * BUFFER_ROW];   
    lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * BUFFER_ROW);   /*Initialize the display buffer*/

    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

    static lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/


    /*Set the resolution of the display*/
    disp_drv.hor_res = MY_DISP_HOR_RES;
    disp_drv.ver_res = MY_DISP_VER_RES;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = disp_flush;

    /*Set a display buffer*/
    disp_drv.draw_buf = &draw_buf_dsc_2;

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
    /*You code here*/
	LCD_Init();   
}

/*Flush the content of the internal buffer the specific area on the display
 *You can use DMA or any hardware acceleration to do this operation in the background but
 *'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    if(disp_flush_enabled) {
        disp_drv_temp = disp_drv;
		LVGL_LCD_Fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_p);
	}
}

void SPI_DMA_IRQHandler(void){    
    /* 传输完成中断 */ 
    if(dma_interrupt_flag_get(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, DMA_INT_FLAG_FTF))
     {   
        dma_interrupt_flag_clear(RCU_SPI_DMA, RCU_SPI_DMA_TX_CH, DMA_INT_FLAG_FTF);// 数据传输完成  
        LCD_CS_SET;
        lv_disp_flush_ready(disp_drv_temp);
    }
}

最后的问题

跑LVGL的demo的时候,动态图表有62帧,但是进行滑动的时候直接掉到6-8帧,此时CPU占用会直接到100%,LED任务就卡住了,灯不闪烁。不知道还有没有优化的办法,以后再慢慢研究了,也欢迎各位大佬指教一下。

在这里插入图片描述

天空星GD32F407VET6移植FreeRTOS+LVGL

完整代码

github.com/Elichikaa/GD32F407VET6-FreeRTOS-LVGL-SPI-DMA

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,STMF407ZGT6和STM32F407VET6是基于ARM Cortex-M4内核的微控制器。在进行源码移植时,需要注意两者之间的硬件差异。 ADC采样出现问题可能是由于以下几个原因导致的: 1. 外设引脚配置不正确:确认ADC引脚配置正确连接,并且使能对应的GPIO外设。 2. ADC时钟配置不正确:配置ADC时钟时需要考虑不同芯片的时钟树结构和时钟配置寄存器。确保时钟配置正确,并且提供足够的时钟给ADC模块。 3. ADC初始化设置不正确:根据目标芯片的参考手册,确保ADC模块的初始化设置正确。注意参考电压、转换模式、采样时间等参数的配置。 4. ADC触发源设置不正确:确认ADC启动的触发源以及触发模式设置正确,例如定时器触发、软件触发或外部触发。 5. DMA配置不正确:如果使用DMA进行ADC数据转移,需要正确配置DMA通道和DMA传输模式。 如果在确认以上几个方面没有问题的情况下,仍然存在ADC采样问题,可以考虑以下步骤进行调试: 1. 使用调试器:通过串口调试器或仿真器,在代码执行过程中进行断点设置和变量查看,以确定问题出现的具体位置。 2. 输出调试信息:在ADC采样时,可以通过串口输出相关的调试信息,例如采样值、寄存器状态等,以便进一步分析问题。 3. 参考官方例程:查阅STMicroelectronics官方提供的开发板和芯片参考手册,并尝试运行官方的ADC采样示例代码,以验证硬件和软件配置的正确性。 4. 联系技术支持:如果以上步骤仍无法解决问题,可以通过STMicroelectronics的技术支持渠道,向他们报告问题并获取专业指导。 总之,在移植STM32F407ZGT6源码到STM32F407VET6时,需要仔细检查硬件和软件配置的差异,并针对性地进行调试和调整,以解决ADC采样问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值