lv_area_t *area, lv_color_t *color_p)
{
ST7735_WriteAddrWindow(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);
// Indicate you are ready with the flushing
lv_disp_flush_ready(disp);
}
对应的 ST7735_WriteAddrWindow() 函数实现, 因为来源是16bit, SPI接口是8bit, 每一次调用分别写入两次
void ST7735_WriteAddrWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *data)
{
uint32_t tmp, i;
if (x1 > x2)
{
tmp = x1; x1 = x2; x2 = tmp;
}
if (y1 > y2)
{
tmp = y1; y1 = y2; y2 = tmp;
}
tmp = (x2 - x1 + 1) * (y2 - y1 + 1);
ST7735_CS_LOW;
ST7735_SetAddrWindow(x1, y1, x2, y2);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
for(i = 0; i < tmp; i ++)
{
SPI_I2S_SendData(SPI1, (uint8_t)(*data >> 8));
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
SPI_I2S_SendData(SPI1, (uint8_t)(*data & 0xFF));
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
data++;
}
ST7735_CS_HIGH;
}
5. 创建图像缓冲, 初始化LVGL
最小为1/10个屏幕尺寸所需数据大小
static lv_disp_draw_buf_t draw_buf;
// Declare a buffer for 1/10 screen size
static lv_color_t buf1[ST7735_WIDTH * ST7735_HEIGHT / 10];
// Descriptor of a display driver
static lv_disp_drv_t disp_drv;
在 main() 中进行初始化, 注意这部分官网给代码里的类型不太对, 这部分的代码已经修改
lv_init();
// Initialize the display buffer.
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, ST7735_WIDTH * ST7735_HEIGHT / 10);
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.flush_cb = my_disp_flush; /*Set your driver function*/
disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/
disp_drv.hor_res = ST7735_WIDTH; /*Set the horizontal resolution of the display*/
disp_drv.ver_res = ST7735_HEIGHT; /*Set the vertical resolution of the display*/
lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
6. 主循环添加 lv_timer_handler()
因为这个 ST7735 没有触屏功能, 所以输入读取函数就省了. 在 main() while(1)主循环中加上 lv_timer_handler()
while (1)
{
lv_timer_handler();
Delay_Ms(10);
}
6. 执行示例
经过以上的设置, LVGL就已经集成到项目中了, 可以运行LVGL自带的一些例子查看控件的显示效果
文字标签, 居中和滚动的效果
lv_example_label_1();
按钮效果
lv_example_btn_1();
源代码
以上LVGL整合示例的完整源代码已经提交到 GitHub: https://github.com/IOsetting/air32f103-template/tree/master/Examples/NonFreeRTOS/SPI/ST7735_LVGL
修改为 DMA 输出
上面的例子是使用 SPI_I2S_SendData()
函数传输图像数据的, 可以修改为 DMA 传输, 因为传输方式的变化, 外设初始化和图像更新要做对应的调整, 这里没有使用中断.
1. 外设调整
GPIO 不变, 启用 SPI 的 DMA
在 APP_SPI_Config()
中启用SPI1 DMA
/* Enable SPI1 DMA TX request */
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
开启 DMA 时钟
void APP_DMA_Configuration(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}
因为DMA每次传输的数量在变化, 所以DMA的初始化在图像输出的方法里
2. 修改图像更新方法
图像更新方法的变化比较大, 这里需要根据输入的坐标, 计算实际的数据长度, 并对DMA进行初始化, 然后启动传输, 等待完成后关闭DMA
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint16_t len;
DMA_InitTypeDef initStructure;
len = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2;
ST7735_CS_LOW;
ST7735_SetAddrWindow(area->x1, area->y1, area->x2, area->y2);
/* DMA1 Channel3 (triggered by SPI1 Tx event) Config */
DMA_DeInit(DMA1_Channel3);
initStructure.DMA_BufferSize = len;
initStructure.DMA_M2M = DMA_M2M_Disable;
initStructure.DMA_DIR = DMA_DIR_PeripheralDST;
initStructure.DMA_MemoryBaseAddr = (uint32_t)color_p;
initStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_By