|
1mtnug4hj2n64031318805.gif (3.61 KB, 下載次數(shù): 1)
下載附件
保存到相冊
1mtnug4hj2n64031318805.gif
2024-9-21 05:28 上傳
關(guān)注、星標公眾號,直達精彩內(nèi)容
1、本節(jié)內(nèi)容介紹1.1、HAL庫硬件SPI DMA在cubemx中的配置及注意事項;1.2、HAL庫SPI DMA詳解與結(jié)構(gòu)介紹;1.3、使用SPI DMA驅(qū)動LCD顯示屏并移植LVGL V8.3源碼地址:https://gitee.com/MR_Wyf/hal-cubemx-rt-thread/tree/hal_rttNano_st7789_menu/
或者關(guān)注公眾號,后臺回復(fù)“SPI DMA”,獲取本章節(jié)源碼
2、HAL庫SPI DMA在CUBEMX中的配置2.1、配置界面配置非常簡單,只需要選擇SPI1的TX配置為DMA模式即可,選擇正常模式即可,不需要循環(huán)模式,否則LVGL可能會顯示異常
l4ef5olgqj264031318905.png (135.93 KB, 下載次數(shù): 0)
下載附件
保存到相冊
l4ef5olgqj264031318905.png
2024-9-21 05:28 上傳
2.2、SPI DMA代碼詳解先來看上面配置生成的代碼,主要就是DMA的模式配置參數(shù)
/* SPI1 DMA Init */
/* SPI1_TX Init */
hdma_spi1_tx.Instance = DMA1_Channel3;
hdma_spi1_tx.Init.Request = DMA_REQUEST_1;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(spiHandle,hdmatx,hdma_spi1_tx);
繼續(xù)來看下SPI DMA的接口:
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
uint16_t Size);
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);
主要就是發(fā)送、接收,以及接收暫停、接受恢復(fù)、接收停止功能函數(shù),本次驅(qū)動LCD主要用到的是DMA發(fā)送函數(shù),主要有3個參數(shù)
/**
* @brief Transmit an amount of data in non-blocking mode with DMA.
* @param hspi pointer to a SPI_HandleTypeDef structure that contains
* the configuration information for SPI module.
* @param pData pointer to data buffer
* @param Size amount of data to be sent
* @retval HAL status
*/
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
2.3 LCD驅(qū)動函數(shù)改造上一章節(jié)中我們使用的是SPI驅(qū)動的LCD,并沒有加入DMA,本章節(jié)咱們對驅(qū)動函數(shù)進行改造,加入DMA驅(qū)動,只需要把我們的SPI發(fā)送函數(shù)改為DMA發(fā)送函數(shù)即可,以下幾個函數(shù)同理改動
// ST7789寫函數(shù)
static HAL_StatusTypeDef lcd_st7789_write(int is_cmd, uint8_t data)
{
uint8_t pData[2] = { 0 };
assert_param(NULL != hspi_lcd);
pData[0] = data;
if (is_cmd)
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
// return HAL_SPI_Transmit(hspi_lcd, pData, 1, HAL_MAX_DELAY);
return HAL_SPI_Transmit_DMA(hspi_lcd, pData, 1);
}
/********************************************************************
*
* LcdWriteReg
*
* Function description:
* Sets display register
*/
void lcd_st7789_write_reg(uint8_t Data)
{
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET);
// HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
HAL_SPI_Transmit_DMA(&hspi1, &Data, 1);
}
/********************************************************************
*
* LcdWriteData
*
* Function description:
* Writes a value to a display register
*/
void lcd_st7789_write_data(uint8_t Data)
{
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
HAL_SPI_Transmit_DMA(&hspi1, &Data, 1);
//HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
}
/********************************************************************
*
* lcd_st7789_write_data_multiple
*
* Function description:
* Writes multiple values to a display register.
*/
extern uint8_t g_spi_dma_tc;
void lcd_st7789_write_data_multiple(uint8_t *pData, int NumItems)
{
if (g_spi_dma_tc) {
g_spi_dma_tc = 0;
HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
//HAL_SPI_Transmit(&hspi1, pData, NumItems, 10);
HAL_SPI_Transmit_DMA(&hspi1, pData, NumItems);
}
}
3、移植LVGL V8.3本次移植的是lvgl V8.3,源碼在lvgl官方github上就可以下載到,想下載的兄弟小手動一動,不想下載的也沒關(guān)系,小飛哥會把源碼開源,直接拿過去就行了
至于LVGL的移植,就不再贅述了,相信網(wǎng)上有成堆的教程,小飛哥也不再廢話浪費大家伙時間了,直接下載源碼即可
主要強調(diào)幾個移植的點:
1、周期調(diào)用lvgl tick接口,提供lvgl“心跳”在定時器3回調(diào)函數(shù)中調(diào)用lv_tick_inc(1),為LVGL提供心跳,周期10ms足夠
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/**timer for lvgl,period 1ms*/
if (htim->Instance == TIM3)
{
lv_tick_inc(1);
}
if (htim->Instance == TIM15)
{
// if(RT_EOK==rt_sem_take(sem_uart_rec,RT_WAITING_NO))
// {
if (embedded_get_uart_rec_flag())
{
/*100ms*/
if (embedded_get_uart_timeout_cnt() > 9)
{
// lv_tick_inc(1);
embedded_set_uart_rec_flag(RT_FALSE);
rt_sem_release(sem_uart_timeout);
}
}
// }
}
}
2、lvgl初始化配置,使用“雙緩存”void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 2) */
static lv_disp_draw_buf_t draw_buf_dsc_2;
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 3) also set disp_drv.full_refresh = 1 below*/
static lv_disp_draw_buf_t draw_buf_dsc_3;
static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
MY_DISP_VER_RES * MY_DISP_VER_RES); /*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 up the functions to access to your display*/
/*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;
disp_drv_p = &disp_drv;
/*Required for Example 3)*/
// disp_drv.full_refresh = 1;
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
// disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
刷新函數(shù):
/*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)
{
// /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
// int32_t x;
// int32_t y;
// for (y = area->y1; y y2; y++)
// {
// for (x = area->x1; x x2; x++)
// {
// /*Put a pixel to the display. For example:*/
// /*put_px(x, y, *color_p)*/
// lcd_st7789_write_pixel(x, y, color_p->full);
// color_p++;
// }
// }
// int32_t y;
// lcd_st7789_set_addr_win(area->x1, area->y1, area->x2, area->y2); // 指定填充區(qū)域
// // 一行一行 DMA
// for (y = area->y1; y y2; y++)
// {
// lcd_st7789_write_data_multiple((uint8_t *)color_p, (uint16_t)(area->x2 - area->x1 + 1) * 2);
// color_p += (area->x2 - area->x1 + 1);
// }
unsigned int size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2;
lcd_st7789_set_addr_win(area->x1, area->y1, area->x2, area->y2); // 指定填充區(qū)域
lcd_st7789_write_data_multiple((uint8_t *)color_p, size);
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
// lv_disp_flush_ready(disp_drv);
}
3、移植后的目錄如下 |
|