文章目录[隐藏]
一、展示成果
博客上传图片限制在5M内,视频转 GIF 帧率压缩太严重了,还得再次压缩才小于5M,效果完全失真了
小伙伴们有什么好的工具或方法解决吗gif 图上应该看不出什么区别。但是ESP32 用60MHz的 spi 驱动跑 lvgl,其卡顿比 stm32 spi 驱动跑 lvgl 明显好很多,,毕竟stm32 spi 也达不到 60MHz嘛
- 主控:ESP32
- 开发工具:esp- idf-v4.3
- LCD 4.3寸 ILI9488
- 温度传感器:K型热电偶+MAX6675
- GUI:little VGL v8.1.0
- 简单的跑个 little VGL 例程而已,下一步再搞点好玩的
ESP32
60MHz SPI 驱动 little VGL v8.1 效果
STM32
SPI驱动 little VGL v7.8效果
二、LCD驱动调试
老样子,直接上代码
// 头文件
#ifndef __SLIM_LCD_H__
#define __SLIM_LCD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl/lvgl.h"
//*************************************************************************************************
// 引脚描述
// CS --> 15
// SCK --> 14
// SDO --> 12
// SDI --> 13
// RESET -->
// DC/RS -->
// LED -->
// T_CLK -->
// T_CS -->
// T_DIN -->
// T_DO -->
// T_IRQ -->
//*************************************************************************************************
#ifdef CONFIG_IDF_TARGET_ESP32
#define LCD_SPI HSPI_HOST
#define GPIO_HANDSHAKE 2
#define PIN_LCD_MISO 12
#define PIN_LCD_MOSI 13
#define PIN_LCD_CLK 14
#define PIN_LCD_CS 15
#define PIN_LCD_RST 25
#define PIN_LCD_DC 26
#define PIN_LCD_LED 27
#endif
//LCD parameter
typedef struct{
uint16_t width; //LCD width
uint16_t height; //LCD height
uint16_t id; //LCD ID
uint8_t dir; //0:vertical / 1:Horizontal
uint16_t wramcmd;//start write GRAM command
uint16_t setxcmd;//setting x axis
uint16_t setycmd;//setting y axis
}_lcd_dev;
extern _lcd_dev lcddev;
#define USE_HORIZONTAL 1 //0:0 / 1:90 / 2:180 / 3:270
#define USER_BUFF_LEN 1440 //3*480
//画笔颜色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
//GUI颜色
#define DARKBLUE 0X01CF //深蓝色
#define LIGHTBLUE 0X7D7C //浅蓝色
#define GRAYBLUE 0X5458 //灰蓝色
//以上三色为 PANEL颜色
#define LIGHTGREEN 0X841F //灰绿色
#define LIGHTGRAY 0XEF5B //浅灰色(PANNEL)
#define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色)
//brush color and background color
#define POINT_COLOR 0x0000
#define BACK_COLOR 0xFFFF
enum{
REG = 0,
DATA,
};
enum{
OFF = 0,
ON,
};
//==================================================================================
//LCD size
#define LCD_W 320
#define LCD_H 480
void LCD_Init(void);
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos);
void LCD_Refresh(uint16_t Color);
void LCD_Flush(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd, uint16_t *buff);
void LCD_Disp_Flush(const lv_area_t * area, lv_color_t * color_p);
#endif
/**
*************************************************************************************************
* @file : slim_lcd.c
* @author : slim
* @version : V0.0.1
* @date : 2021.07.11
* @brief : LCD moduler for ILI9488 IC driver
*************************************************************************************************
*/
#include "slim_lcd.h"
#define RGB_R(n) (n>>8)&0xF8
#define RGB_G(n) (n>>3)&0xFC
#define RGB_B(n) n<<3
_lcd_dev lcddev;
static spi_device_handle_t spi_handle;
uint8_t GRAM_BUFF[USER_BUFF_LEN] = {0};
//===========================================================================
// 静态函数声明
static void _spi_init(void);
static void _lcd_spi_pre_transfer_callback(spi_transaction_t *trans);
static void _lcd_cmd(uint8_t reg);
static void _lcd_data(uint8_t *buff, uint16_t len);
static void _byte_read(uint8_t *buff, uint16_t len);
static uint32_t _lcd_get_id(void);
static void _lcd_init(void);
static void _lcd_led_set(uint8_t state);
static void _lcd_direction(uint8_t direction);
static void _lcd_write_data_16bit(uint16_t rgb_data);
static void _lcd_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd);
static void _lcd_clear(uint16_t color);
//============================================================================
// spi/lcd 初始化、读、写
//============================================================================
static void _spi_init(void){
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num = PIN_LCD_MISO,
.mosi_io_num = PIN_LCD_MOSI,
.sclk_io_num = PIN_LCD_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096, //非DMA:64byte,,DMA:4096Byte
.flags = SPICOMMON_BUSFLAG_MASTER, //初始化检查SPI主机模式是否成功
};
//Configuration for the SPI device on the other side of the bus
spi_device_interface_config_t devcfg = {
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
.clock_speed_hz = 60*1000000,
.duty_cycle_pos = 0, //50% duty cycle
.mode = 0,
.spics_io_num = PIN_LCD_CS,
.cs_ena_posttrans = 1, //Keep the CS low 3 cycles after transaction, to stop slave from missing the last bit when CS has less propagation delay than CLK
.queue_size = 3,
.pre_cb = _lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
};
//Initialize the SPI bus and add the device we want to send stuff to.
ret=spi_bus_initialize(LCD_SPI, &buscfg, SPI_DMA_CH1); //总线初始化
assert(ret==ESP_OK);
ret=spi_bus_add_device(LCD_SPI, &devcfg, &spi_handle);
assert(ret==ESP_OK);
}
static void _lcd_spi_pre_transfer_callback(spi_transaction_t *trans){
uint32_t dc = (uint32_t)trans->user;
gpio_set_level(PIN_LCD_DC, dc);
}
// LCD写寄存器
static void _lcd_cmd(uint8_t reg){
esp_err_t ret;
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.length = 8; //Command is 8 bits
trans.tx_buffer = ®
trans.user = (void*)0; //reg
ret=spi_device_polling_transmit(spi_handle, &trans); //Transmit!
assert(ret==ESP_OK);//Should have had no issues.
}
// LCD写数据
static void _lcd_data(uint8_t *buff, uint16_t len){
esp_err_t ret;
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.length = len*8;
trans.tx_buffer = buff;
trans.user = (void*)1; //data
ret=spi_device_polling_transmit(spi_handle, &trans); //Transmit!
assert(ret==ESP_OK);//Should have had no issues.
}
static void _byte_read(uint8_t *buff, uint16_t len){
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.length=len*8;
// trans.flags = SPI_TRANS_USE_RXDATA;
trans.tx_buffer = NULL;
trans.rx_buffer = buff;
trans.user = (void*)1;
esp_err_t ret = spi_device_polling_transmit(spi_handle, &trans);
assert( ret == ESP_OK );
}
// 读取LCD id
static uint32_t _lcd_get_id(void){
uint8_t tmp_id[4] = {0};
_lcd_cmd(0xD3);
_byte_read(tmp_id, 4);
return (tmp_id[0]<<24 | tmp_id[1]<<16 | tmp_id[2]<<8 | tmp_id[3]);
}
// 初始化LCD
static void _lcd_init(void){
_spi_init();
//Initialize non-SPI GPIOs
gpio_set_direction(PIN_LCD_DC, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_LCD_RST, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_LCD_LED, GPIO_MODE_OUTPUT);
//Reset the display
gpio_set_level(PIN_LCD_RST, 0);
vTaskDelay(100 / portTICK_RATE_MS);
gpio_set_level(PIN_LCD_RST, 1);
vTaskDelay(100 / portTICK_RATE_MS);
_lcd_cmd(0xF7);
_lcd_data((uint8_t[]){0xA9, 0x51, 0x2C, 0x82}, 4);
_lcd_cmd(0xC0);
_lcd_data((uint8_t[]){0x11, 0x09}, 2);
_lcd_cmd(0xC1);
_lcd_data((uint8_t[]){0x41}, 1);
_lcd_cmd(0xC1);
_lcd_data((uint8_t[]){0x41}, 1);
_lcd_cmd(0XC5);
_lcd_data((uint8_t[]){0x00, 0x0A, 0x80}, 3);
_lcd_cmd(0xB1);
_lcd_data((uint8_t[]){0xB0, 0x11}, 2);
_lcd_cmd(0xB4);
_lcd_data((uint8_t[]){0x02}, 1);
_lcd_cmd(0xB6);
_lcd_data((uint8_t[]){0x02, 0x42}, 2);
_lcd_cmd(0xB7);
_lcd_data((uint8_t[]){0xc6}, 1);
_lcd_cmd(0xBE);
_lcd_data((uint8_t[]){0x00, 0x04}, 2);
_lcd_cmd(0xE9);
_lcd_data((uint8_t[]){0x00}, 1);
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(0<<7)|(1<<6)|(1<<5)}, 1);
_lcd_cmd(0x3A);
_lcd_data((uint8_t[]){0x66}, 1);
_lcd_cmd(0xE0);
_lcd_data((uint8_t[]){0x00, 0x07, 0x10, 0x09, 0x17, 0x0B, 0x41, 0x89, 0x4B, 0x0A, 0x0C, 0x0E, 0x18, 0x1B, 0x0F}, 15);
_lcd_cmd(0XE1);
_lcd_data((uint8_t[]){0x00, 0x17, 0x1A, 0x04, 0x0E, 0x06, 0x2F, 0x45, 0x43, 0x02, 0x0A, 0x09, 0x32, 0x36, 0x0F}, 15);
_lcd_cmd(0x11);
vTaskDelay(120 / portTICK_RATE_MS);
_lcd_cmd(0x29);
printf("==== LCD ID: %X ====\n",_lcd_get_id());
_lcd_direction(USE_HORIZONTAL); //setting direction
_lcd_led_set(ON);
_lcd_clear(GREEN); //clear screen
}
// 背光灯设置
static void _lcd_led_set(uint8_t state){
if(state){
gpio_set_level(PIN_LCD_LED, 1);
}else{
gpio_set_level(PIN_LCD_LED, 0);
}
}
// lcd显示方向设置
static void _lcd_direction(uint8_t direction){
switch(direction){
case 0:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(0<<6)|(0<<7)}, 1);//BGR==1,MY==0,MX==0,MV==0
break;
case 1:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(0<<7)|(1<<6)|(1<<5)}, 1);//BGR==1,MY==1,MX==0,MV==1
break;
case 2:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(1<<6)|(1<<7)}, 1);//BGR==1,MY==0,MX==0,MV==0
break;
case 3:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(1<<7)|(1<<5)}, 1);//BGR==1,MY==1,MX==0,MV==1
break;
default:break;
}
}
// 写一个RGB像素点
static void _lcd_write_data_16bit(uint16_t rgb_data){
//18Bit
uint8_t rgb_r = (rgb_data>>8)&0xF8; //RED
uint8_t rgb_g = (rgb_data>>3)&0xFC; //GREEN
uint8_t rgb_b = rgb_data<<3; //BLUE
_lcd_data((uint8_t[]){rgb_r, rgb_g, rgb_b}, 3);
}
static void _lcd_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd){
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
_lcd_cmd(lcddev.setxcmd);
_lcd_data((uint8_t[]){xStar>>8, 0x00FF&xStar, xEnd>>8, 0x00FF&xEnd}, 4);
_lcd_cmd(lcddev.setycmd);
_lcd_data((uint8_t[]){yStar>>8, 0x00FF&yStar, yEnd>>8, 0x00FF&yEnd}, 4);
_lcd_cmd(lcddev.wramcmd); //start writing GRAM
}
//清屏
static void _lcd_clear(uint16_t color){
uint32_t i = 0;
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
_lcd_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
for(i=0;i<USER_BUFF_LEN;){
GRAM_BUFF[i] = RGB_R(color);
GRAM_BUFF[i+1] = RGB_G(color);
GRAM_BUFF[i+2] = RGB_B(color);
i += 3;
}
for(i=0;i<(lcddev.height*lcddev.width*3)/USER_BUFF_LEN;i++){
_lcd_data(GRAM_BUFF, USER_BUFF_LEN);
}
}
//============================================================================
// lcd 初始化、读、写
//============================================================================
void LCD_Init(void){
_lcd_init();
}
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos){
_lcd_SetWindows(Xpos,Ypos,Xpos+1,Ypos+1);
}
void LCD_Refresh(uint16_t color){
_lcd_clear(color);
}
void LCD_Disp_Flush(const lv_area_t * area, lv_color_t * color_p){
int32_t cnt = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 3;
uint16_t i = 0;
uint16_t index = 0;
uint16_t numTime = 0; //多少组GRAM_BUFF
uint16_t numSurplus = 0;//最后剩下的
if(cnt < 0){
printf("parameter setting error!!!\n");
return;
}
_lcd_SetWindows(area->x1, area->y1, area->x2, area->y2);
// printf("CNT %d\n\n\n",cnt);
if(cnt <= USER_BUFF_LEN){
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
for(i=0;i<cnt;){
GRAM_BUFF[i] = RGB_R(color_p->full);
GRAM_BUFF[i+1] = RGB_G(color_p->full);
GRAM_BUFF[i+2] = RGB_B(color_p->full);
i += 3;
color_p++;
}
_lcd_data(GRAM_BUFF, cnt);
}else{
numTime = cnt/USER_BUFF_LEN;
numSurplus = cnt%USER_BUFF_LEN;
printf("NumTime:%d NumSurplus:%d\n\n", numTime, numSurplus);
for(uint16_t j=0;j<numTime;j++){
// index = j*USER_BUFF_LEN;
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
for(i=0;i<USER_BUFF_LEN;){
GRAM_BUFF[i] = RGB_R(color_p->full);
GRAM_BUFF[i+1] = RGB_G(color_p->full);
GRAM_BUFF[i+2] = RGB_B(color_p->full);
i += 3;
color_p++;
}
_lcd_data(GRAM_BUFF, USER_BUFF_LEN);
}
if(numSurplus > 0){
// index = cnt-numSurplus;
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
for(i=0;i<=numSurplus;){
GRAM_BUFF[i] = RGB_R(color_p->full);
GRAM_BUFF[i+1] = RGB_G(color_p->full);
GRAM_BUFF[i+2] = RGB_B(color_p->full);
i += 3;
color_p++;
}
_lcd_data(GRAM_BUFF, numSurplus);
}
}
}
ESP32 的spi 驱动还是挺有意思的
ESP32共有 3 组SPI,这里用的是 HSPI,另一个 VSPI 用于驱动MAX6675 读取温度
ESP32 的 SPI 在传输前和传输完成时可设置回调
transaction_cb_t pre_cb; /**< Callback to be called before a transmission is started.
和
transaction_cb_t post_cb; /**< Callback to be called after a transmission has completed.
,这里就是用的在传输前设置 LCD D/C的 IO 来设置传输的是命令还是数据。
还有个妙用:就是当 SPI 接口不够用时,可以用该回调实现多个 IO 做软CS 片选来复用出多个 SPI。
三、LVGL v8移植
- 将下载的 lvgl 移动到 esp-idf-v4.3/components 下
- 设置 lvgl 目录下的 lvgl.h 和 lv_conf.h,按需配置即可
- 实现
lv_port_disp
、lv_port_indev
、lv_port_fs
port 为接口文件,其中lv_port_disp/lv_port_indev/lv_port_fs 分别对应 LCD 屏绘制,触摸、鼠标、编码器等输入设备,还有文件系统,这里只使能lv_port_disp
// lv_port_disp.c部分函数
void lv_port_disp_init(void){
disp_init();
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[LV_HOR_RES_MAX * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.hor_res = 480;
disp_drv.ver_res = 320;
disp_drv.flush_cb = disp_flush;
disp_drv.draw_buf = &draw_buf_dsc_1;
lv_disp_drv_register(&disp_drv);
}
static void disp_init(void){
LCD_Init();
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){
LCD_Disp_Flush(area, color_p);
lv_disp_flush_ready(disp_drv);
}
四、LVGL v8测试线程
// 创建 lvgl 相关的线程
......
xSta = xTaskCreate(Thread_lvgl, /* 任务函数指针 */
"LVGL Thread", /* 任务名:调试使用 */
15 * 1024, /* 栈深 */
NULL, /* 任务参数 */
2, /* 优先级. */
lvgl_task_handle); /* 任务 handle */
if(pdPASS == xSta){
// vTaskStartScheduler(); //不需要
printf("create lvgl thread successful!!!\n");
}else{
printf("create lvgl thread failed!!!\n");
return -1;
}
......
// lvgl线程
lv_tick_inc(50)
最好用定时器去实现周期调用
void Thread_lvgl(void *arg){
lv_init();
lv_port_disp_init(); // 显示器初始化
// lv_port_indev_init(); // 输入设备初始化(如果没有实现就注释掉)
// lv_port_fs_init(); // 文件系统设备初始化(如果没有实现就注释掉)
slim_index(); //小控件测试
while(1){
lv_tick_inc(50);
lv_task_handler();
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
//上测试控件
little VGL v8.1 跟 v7.8 的控件设置、style设置还是有很大不同的哈,从v7.8转过来的,还得先适应适应
static void slim_index(void){
lv_obj_t *scr = lv_scr_act(); //获取屏幕对象
//彩色标签
#if 1
static lv_style_t label_styles;
lv_style_init(&label_styles);
lv_style_set_anim_speed(&label_styles, 50);
lv_style_set_text_font(&label_styles, &lv_font_montserrat_20);
lv_obj_t * label1 = lv_label_create(scr);
lv_label_set_recolor(label1, true);
lv_label_set_long_mode(label1, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/
lv_obj_set_width(label1, 120);
// lv_obj_add_style(label1, LV_LABEL_PART_MAIN, &label_styles);
lv_label_set_text(label1, "#ff0000 Hello# #00ff00 world ! slim su.#");
lv_obj_align(label1, LV_ALIGN_TOP_LEFT, 20, 5);
lv_obj_add_style(label1, &label_styles, 0);
// lv_label_set_anim_speed(label1, 80);
#endif
//圆弧
#if 1
static lv_style_t style_arc1_bg, style_arc1_indic, style_arc1_knob;
lv_style_init(&style_arc1_bg);
lv_style_set_arc_width(&style_arc1_bg, 10);
lv_style_set_arc_color(&style_arc1_bg, lv_color_make(0, 255, 0));
lv_style_init(&style_arc1_indic);
lv_style_set_arc_width(&style_arc1_indic, 10);
lv_style_set_arc_color(&style_arc1_indic, lv_color_make(255, 0, 0));
lv_style_init(&style_arc1_knob);
lv_style_set_bg_color(&style_arc1_knob, lv_color_make(255, 0, 0));
lv_style_set_bg_opa(&style_arc1_knob, LV_OPA_0);
lv_obj_t *arc1 = lv_arc_create(scr);
lv_obj_set_size(arc1, 100, 100);
lv_obj_set_pos(arc1, 5, 50);
lv_arc_set_bg_angles(arc1, 0, 360);
lv_arc_set_angles(arc1, 0, 90);
lv_obj_add_style(arc1, &style_arc1_bg, LV_PART_MAIN);
lv_obj_add_style(arc1, &style_arc1_indic, LV_PART_INDICATOR);
lv_obj_add_style(arc1, &style_arc1_knob, LV_PART_KNOB);
static lv_style_t style_arc2_bg, style_arc2_indic, style_arc2_knob;
lv_style_init(&style_arc2_bg);
lv_style_set_arc_width(&style_arc2_bg, 15);
lv_style_set_arc_color(&style_arc2_bg, lv_color_make(0xC0, 0xC0, 0xC0));
lv_style_init(&style_arc2_indic);
lv_style_set_arc_width(&style_arc2_indic, 15);
lv_style_set_arc_color(&style_arc2_indic, lv_color_make(0x80, 0x00, 0x80));
lv_style_init(&style_arc2_knob);
lv_style_set_bg_color(&style_arc2_knob, lv_color_make(0x80, 0x00, 0x80));
lv_style_set_bg_opa(&style_arc2_knob, LV_OPA_0);
lv_obj_t *arc2 = lv_arc_create(scr);
lv_obj_set_size(arc2, 100, 100);
lv_obj_set_pos(arc2, 120, 50);
lv_arc_set_bg_angles(arc2, 0, 360);
lv_arc_set_angles(arc2, 90, 270);
lv_obj_add_style(arc2, &style_arc2_bg, LV_PART_MAIN);
lv_obj_add_style(arc2, &style_arc2_indic, LV_PART_INDICATOR);
lv_obj_add_style(arc2, &style_arc2_knob, LV_PART_KNOB);
lv_obj_t *label2 = lv_label_create(arc2); // 在Arc控件上创建一个标签
lv_obj_align(label2, LV_ALIGN_CENTER, 0, 0); // 对齐到Arc控件中心
lv_label_set_text(label2, "2"); // 设置标签文本
// lv_obj_set_event_cb(arc2, arc_event_handler);
#endif
//进度条
#if 1
static lv_style_t style_bar1_bg, style_bar1_indic, style_bar_label1;
lv_style_init(&style_bar_label1);
lv_style_set_text_font(&style_bar_label1, &lv_font_montserrat_18);
lv_style_init(&style_bar1_bg);
lv_style_set_bg_opa(&style_bar1_bg, LV_OPA_COVER);
lv_style_set_bg_color(&style_bar1_bg, lv_color_make(0xFF, 0xFF, 0x00));
lv_style_init(&style_bar1_indic);
lv_style_set_bg_opa(&style_bar1_indic, LV_OPA_COVER);
lv_style_set_bg_color(&style_bar1_indic, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_color(&style_bar1_indic, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_dir(&style_bar1_indic, LV_GRAD_DIR_VER);
bar1 = lv_bar_create(scr);
lv_obj_add_style(bar1, &style_bar1_bg, LV_PART_MAIN);
lv_obj_add_style(bar1, &style_bar1_indic, LV_PART_INDICATOR);
lv_obj_set_size(bar1, 20, 200);
lv_obj_align(bar1, LV_ALIGN_TOP_RIGHT, -30, 50);
lv_bar_set_range(bar1, 0, 100);
label_bar1 = lv_label_create(scr);
lv_obj_add_style(label_bar1, &style_bar_label1, LV_PART_MAIN);
lv_obj_align_to(label_bar1, bar1, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
lv_label_set_text(label_bar1, "80%");
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_temp);
lv_anim_set_time(&a, 8000);
lv_anim_set_playback_time(&a, 1000);
lv_anim_set_var(&a, bar1);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
#endif
//复选框
#if 1
static lv_style_t style_checkbox1;
lv_style_init(&style_checkbox1);
lv_style_set_text_font(&style_checkbox1, &lv_font_montserrat_18);
lv_style_set_bg_opa(&style_checkbox1, LV_OPA_100);
lv_style_set_bg_color(&style_checkbox1, lv_color_make(0xFF, 0x00, 0x00));
lv_obj_t *checkbox1 = lv_checkbox_create(scr);
lv_obj_set_width(checkbox1, 130);
lv_obj_set_pos(checkbox1, 250, 150);
lv_obj_add_style(checkbox1, &style_checkbox1, LV_PART_MAIN);
lv_checkbox_set_text(checkbox1, "CheckBox");
#endif
//滑块
#if 0
slider1 = lv_slider_create(scr, NULL);
lv_obj_set_size(slider1, 100, 10);
lv_slider_set_range(slider1, 0, 100);
lv_slider_set_anim_time(slider1, 200);
lv_bar_set_value(slider1, 20, LV_ANIM_ON);
lv_obj_align(slider1, scr, LV_ALIGN_IN_TOP_RIGHT, -20, 20);
// lv_obj_set_event_cb(slider1, arc_event_handler);
#endif
//按钮
#if 1
lv_obj_t *btn1 = lv_btn_create(scr);
lv_obj_set_pos(btn1, 250, 50);
lv_obj_set_size(btn1, 120, 40);
lv_obj_t *label_btn1 = lv_label_create(scr);
lv_obj_align_to(label_btn1, btn1, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label_btn1, "key1");
// lv_obj_set_event_cb(btn1, arc_event_handler);
btn2 = lv_btn_create(scr);
lv_obj_set_pos(btn2, 250, 220);
lv_obj_set_size(btn2, 80, 40);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
// lv_btn_set_checkable(btn2, true);
// lv_btn_set_state(btn2, true);
lv_obj_t *label_btn2 = lv_label_create(scr);
lv_obj_align_to(label_btn2, btn2, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label_btn2, "key2");
// lv_obj_set_event_cb(btn2, arc_event_handler);
#endif
//预加载
#if 1
static lv_style_t style_spinner1_bg, style_spinner1_indic;
lv_style_init(&style_spinner1_bg);
lv_style_set_bg_opa(&style_spinner1_bg, LV_OPA_100);
lv_style_set_arc_width(&style_spinner1_bg, 15);
lv_style_set_arc_color(&style_spinner1_bg, lv_color_make(0xFF, 0x00, 0xFF));
lv_style_init(&style_spinner1_indic);
lv_style_set_bg_opa(&style_spinner1_indic, LV_OPA_100);
lv_style_set_arc_width(&style_spinner1_indic, 15);
lv_style_set_arc_color(&style_spinner1_indic, lv_color_make(0x00, 0x00, 0x80));
lv_obj_t * spinner1 = lv_spinner_create(scr, 2000, 60);
lv_obj_set_size(spinner1, 80, 80);
lv_obj_set_pos(spinner1, 10, 180);
lv_obj_add_style(spinner1, &style_spinner1_bg, LV_PART_MAIN);
lv_obj_add_style(spinner1, &style_spinner1_indic, LV_PART_INDICATOR);
static lv_style_t style_spinner2_bg, style_spinner2_indic;
lv_style_init(&style_spinner2_bg);
lv_style_set_arc_width(&style_spinner2_bg, 0);
lv_style_init(&style_spinner2_indic);
lv_style_set_arc_width(&style_spinner2_indic, 8);
lv_style_set_arc_color(&style_spinner2_indic, lv_color_make(0x80, 0x00, 0x00));
lv_obj_t *spinner2 = lv_spinner_create(scr, 2000, 120);
lv_obj_set_size(spinner2, 60, 60);
lv_obj_set_pos(spinner2, 120, 180);
lv_obj_add_style(spinner2, &style_spinner2_bg, LV_PART_MAIN);
lv_obj_add_style(spinner2, &style_spinner2_indic, LV_PART_INDICATOR);
#endif
//线条
#if 1
static lv_style_t style_line1;
lv_style_init(&style_line1);
lv_style_set_line_width(&style_line1, 10);
lv_style_set_line_color(&style_line1, lv_color_make(255, 100, 100));
lv_style_set_line_rounded(&style_line1, true); //圆角
static lv_point_t line_points[] = {{180, 8},{460,40}};
lv_obj_t *line1 = lv_line_create(scr);
lv_obj_add_style(line1, &style_line1, LV_PART_MAIN);
lv_line_set_points(line1, line_points, 2);
lv_obj_set_pos(line1, 0, 0); //点的坐标以此为原点
#endif
//led
#if 1
static lv_style_t style_led1;
lv_style_init(&style_led1);
lv_style_set_bg_color(&style_led1, lv_color_make(0xff, 0xff, 0x00));
led1 = lv_led_create(scr);
lv_obj_set_pos(led1, 350, 215);
lv_obj_set_size(led1, 50, 50);
lv_led_off(led1);
lv_led_set_color(led1, lv_palette_main(LV_PALETTE_YELLOW));
#endif
//文本域
#if 1
txt1 = lv_textarea_create(scr);
lv_obj_set_size(txt1, 380, 30);
lv_obj_set_pos(txt1, 50, 280);
// lv_textarea_set_one_line(txt1, true);
lv_textarea_set_text(txt1, "MAX6675 detect Tempuration: --.-C");
#endif
}
//这里就是测试用的,实际项目不能这么写咯
static void set_temp(void * bar, int32_t temp){
char str[10] = {0};
float temp_val = 0;
lv_bar_set_value(bar, temp, LV_ANIM_ON);
if(0 == temp%10){
sprintf(str, "%d%%", temp);
lv_label_set_text(label_bar1, str);
}
if((led1 != NULL) && (0 == temp%50) && (txt1 != NULL)){
lv_led_toggle(led1);
max6675_get_temp(&temp_val);
sprintf(str, "%2.1fC", temp_val);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_add_text(txt1, str);
}
}
差不多就是这样咯
想玩下语音控制,小伙伴们有木有好的 ESP 离线语音开发方案 或 开发板推荐吖
版权声明:本文为CSDN博主「slimmm」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/slimmm/article/details/119975059
一、展示成果
博客上传图片限制在5M内,视频转 GIF 帧率压缩太严重了,还得再次压缩才小于5M,效果完全失真了
小伙伴们有什么好的工具或方法解决吗gif 图上应该看不出什么区别。但是ESP32 用60MHz的 spi 驱动跑 lvgl,其卡顿比 stm32 spi 驱动跑 lvgl 明显好很多,,毕竟stm32 spi 也达不到 60MHz嘛
- 主控:ESP32
- 开发工具:esp- idf-v4.3
- LCD 4.3寸 ILI9488
- 温度传感器:K型热电偶+MAX6675
- GUI:little VGL v8.1.0
- 简单的跑个 little VGL 例程而已,下一步再搞点好玩的
ESP32
60MHz SPI 驱动 little VGL v8.1 效果
STM32
SPI驱动 little VGL v7.8效果
二、LCD驱动调试
老样子,直接上代码
// 头文件
#ifndef __SLIM_LCD_H__
#define __SLIM_LCD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl/lvgl.h"
//*************************************************************************************************
// 引脚描述
// CS --> 15
// SCK --> 14
// SDO --> 12
// SDI --> 13
// RESET -->
// DC/RS -->
// LED -->
// T_CLK -->
// T_CS -->
// T_DIN -->
// T_DO -->
// T_IRQ -->
//*************************************************************************************************
#ifdef CONFIG_IDF_TARGET_ESP32
#define LCD_SPI HSPI_HOST
#define GPIO_HANDSHAKE 2
#define PIN_LCD_MISO 12
#define PIN_LCD_MOSI 13
#define PIN_LCD_CLK 14
#define PIN_LCD_CS 15
#define PIN_LCD_RST 25
#define PIN_LCD_DC 26
#define PIN_LCD_LED 27
#endif
//LCD parameter
typedef struct{
uint16_t width; //LCD width
uint16_t height; //LCD height
uint16_t id; //LCD ID
uint8_t dir; //0:vertical / 1:Horizontal
uint16_t wramcmd;//start write GRAM command
uint16_t setxcmd;//setting x axis
uint16_t setycmd;//setting y axis
}_lcd_dev;
extern _lcd_dev lcddev;
#define USE_HORIZONTAL 1 //0:0 / 1:90 / 2:180 / 3:270
#define USER_BUFF_LEN 1440 //3*480
//画笔颜色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
//GUI颜色
#define DARKBLUE 0X01CF //深蓝色
#define LIGHTBLUE 0X7D7C //浅蓝色
#define GRAYBLUE 0X5458 //灰蓝色
//以上三色为 PANEL颜色
#define LIGHTGREEN 0X841F //灰绿色
#define LIGHTGRAY 0XEF5B //浅灰色(PANNEL)
#define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色)
//brush color and background color
#define POINT_COLOR 0x0000
#define BACK_COLOR 0xFFFF
enum{
REG = 0,
DATA,
};
enum{
OFF = 0,
ON,
};
//==================================================================================
//LCD size
#define LCD_W 320
#define LCD_H 480
void LCD_Init(void);
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos);
void LCD_Refresh(uint16_t Color);
void LCD_Flush(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd, uint16_t *buff);
void LCD_Disp_Flush(const lv_area_t * area, lv_color_t * color_p);
#endif
/**
*************************************************************************************************
* @file : slim_lcd.c
* @author : slim
* @version : V0.0.1
* @date : 2021.07.11
* @brief : LCD moduler for ILI9488 IC driver
*************************************************************************************************
*/
#include "slim_lcd.h"
#define RGB_R(n) (n>>8)&0xF8
#define RGB_G(n) (n>>3)&0xFC
#define RGB_B(n) n<<3
_lcd_dev lcddev;
static spi_device_handle_t spi_handle;
uint8_t GRAM_BUFF[USER_BUFF_LEN] = {0};
//===========================================================================
// 静态函数声明
static void _spi_init(void);
static void _lcd_spi_pre_transfer_callback(spi_transaction_t *trans);
static void _lcd_cmd(uint8_t reg);
static void _lcd_data(uint8_t *buff, uint16_t len);
static void _byte_read(uint8_t *buff, uint16_t len);
static uint32_t _lcd_get_id(void);
static void _lcd_init(void);
static void _lcd_led_set(uint8_t state);
static void _lcd_direction(uint8_t direction);
static void _lcd_write_data_16bit(uint16_t rgb_data);
static void _lcd_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd);
static void _lcd_clear(uint16_t color);
//============================================================================
// spi/lcd 初始化、读、写
//============================================================================
static void _spi_init(void){
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num = PIN_LCD_MISO,
.mosi_io_num = PIN_LCD_MOSI,
.sclk_io_num = PIN_LCD_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096, //非DMA:64byte,,DMA:4096Byte
.flags = SPICOMMON_BUSFLAG_MASTER, //初始化检查SPI主机模式是否成功
};
//Configuration for the SPI device on the other side of the bus
spi_device_interface_config_t devcfg = {
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
.clock_speed_hz = 60*1000000,
.duty_cycle_pos = 0, //50% duty cycle
.mode = 0,
.spics_io_num = PIN_LCD_CS,
.cs_ena_posttrans = 1, //Keep the CS low 3 cycles after transaction, to stop slave from missing the last bit when CS has less propagation delay than CLK
.queue_size = 3,
.pre_cb = _lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
};
//Initialize the SPI bus and add the device we want to send stuff to.
ret=spi_bus_initialize(LCD_SPI, &buscfg, SPI_DMA_CH1); //总线初始化
assert(ret==ESP_OK);
ret=spi_bus_add_device(LCD_SPI, &devcfg, &spi_handle);
assert(ret==ESP_OK);
}
static void _lcd_spi_pre_transfer_callback(spi_transaction_t *trans){
uint32_t dc = (uint32_t)trans->user;
gpio_set_level(PIN_LCD_DC, dc);
}
// LCD写寄存器
static void _lcd_cmd(uint8_t reg){
esp_err_t ret;
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.length = 8; //Command is 8 bits
trans.tx_buffer = ®
trans.user = (void*)0; //reg
ret=spi_device_polling_transmit(spi_handle, &trans); //Transmit!
assert(ret==ESP_OK);//Should have had no issues.
}
// LCD写数据
static void _lcd_data(uint8_t *buff, uint16_t len){
esp_err_t ret;
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.length = len*8;
trans.tx_buffer = buff;
trans.user = (void*)1; //data
ret=spi_device_polling_transmit(spi_handle, &trans); //Transmit!
assert(ret==ESP_OK);//Should have had no issues.
}
static void _byte_read(uint8_t *buff, uint16_t len){
spi_transaction_t trans;
memset(&trans, 0, sizeof(trans));
trans.length=len*8;
// trans.flags = SPI_TRANS_USE_RXDATA;
trans.tx_buffer = NULL;
trans.rx_buffer = buff;
trans.user = (void*)1;
esp_err_t ret = spi_device_polling_transmit(spi_handle, &trans);
assert( ret == ESP_OK );
}
// 读取LCD id
static uint32_t _lcd_get_id(void){
uint8_t tmp_id[4] = {0};
_lcd_cmd(0xD3);
_byte_read(tmp_id, 4);
return (tmp_id[0]<<24 | tmp_id[1]<<16 | tmp_id[2]<<8 | tmp_id[3]);
}
// 初始化LCD
static void _lcd_init(void){
_spi_init();
//Initialize non-SPI GPIOs
gpio_set_direction(PIN_LCD_DC, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_LCD_RST, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_LCD_LED, GPIO_MODE_OUTPUT);
//Reset the display
gpio_set_level(PIN_LCD_RST, 0);
vTaskDelay(100 / portTICK_RATE_MS);
gpio_set_level(PIN_LCD_RST, 1);
vTaskDelay(100 / portTICK_RATE_MS);
_lcd_cmd(0xF7);
_lcd_data((uint8_t[]){0xA9, 0x51, 0x2C, 0x82}, 4);
_lcd_cmd(0xC0);
_lcd_data((uint8_t[]){0x11, 0x09}, 2);
_lcd_cmd(0xC1);
_lcd_data((uint8_t[]){0x41}, 1);
_lcd_cmd(0xC1);
_lcd_data((uint8_t[]){0x41}, 1);
_lcd_cmd(0XC5);
_lcd_data((uint8_t[]){0x00, 0x0A, 0x80}, 3);
_lcd_cmd(0xB1);
_lcd_data((uint8_t[]){0xB0, 0x11}, 2);
_lcd_cmd(0xB4);
_lcd_data((uint8_t[]){0x02}, 1);
_lcd_cmd(0xB6);
_lcd_data((uint8_t[]){0x02, 0x42}, 2);
_lcd_cmd(0xB7);
_lcd_data((uint8_t[]){0xc6}, 1);
_lcd_cmd(0xBE);
_lcd_data((uint8_t[]){0x00, 0x04}, 2);
_lcd_cmd(0xE9);
_lcd_data((uint8_t[]){0x00}, 1);
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(0<<7)|(1<<6)|(1<<5)}, 1);
_lcd_cmd(0x3A);
_lcd_data((uint8_t[]){0x66}, 1);
_lcd_cmd(0xE0);
_lcd_data((uint8_t[]){0x00, 0x07, 0x10, 0x09, 0x17, 0x0B, 0x41, 0x89, 0x4B, 0x0A, 0x0C, 0x0E, 0x18, 0x1B, 0x0F}, 15);
_lcd_cmd(0XE1);
_lcd_data((uint8_t[]){0x00, 0x17, 0x1A, 0x04, 0x0E, 0x06, 0x2F, 0x45, 0x43, 0x02, 0x0A, 0x09, 0x32, 0x36, 0x0F}, 15);
_lcd_cmd(0x11);
vTaskDelay(120 / portTICK_RATE_MS);
_lcd_cmd(0x29);
printf("==== LCD ID: %X ====\n",_lcd_get_id());
_lcd_direction(USE_HORIZONTAL); //setting direction
_lcd_led_set(ON);
_lcd_clear(GREEN); //clear screen
}
// 背光灯设置
static void _lcd_led_set(uint8_t state){
if(state){
gpio_set_level(PIN_LCD_LED, 1);
}else{
gpio_set_level(PIN_LCD_LED, 0);
}
}
// lcd显示方向设置
static void _lcd_direction(uint8_t direction){
switch(direction){
case 0:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(0<<6)|(0<<7)}, 1);//BGR==1,MY==0,MX==0,MV==0
break;
case 1:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(0<<7)|(1<<6)|(1<<5)}, 1);//BGR==1,MY==1,MX==0,MV==1
break;
case 2:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(1<<6)|(1<<7)}, 1);//BGR==1,MY==0,MX==0,MV==0
break;
case 3:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
_lcd_cmd(0x36);
_lcd_data((uint8_t[]){(1<<3)|(1<<7)|(1<<5)}, 1);//BGR==1,MY==1,MX==0,MV==1
break;
default:break;
}
}
// 写一个RGB像素点
static void _lcd_write_data_16bit(uint16_t rgb_data){
//18Bit
uint8_t rgb_r = (rgb_data>>8)&0xF8; //RED
uint8_t rgb_g = (rgb_data>>3)&0xFC; //GREEN
uint8_t rgb_b = rgb_data<<3; //BLUE
_lcd_data((uint8_t[]){rgb_r, rgb_g, rgb_b}, 3);
}
static void _lcd_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd){
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
_lcd_cmd(lcddev.setxcmd);
_lcd_data((uint8_t[]){xStar>>8, 0x00FF&xStar, xEnd>>8, 0x00FF&xEnd}, 4);
_lcd_cmd(lcddev.setycmd);
_lcd_data((uint8_t[]){yStar>>8, 0x00FF&yStar, yEnd>>8, 0x00FF&yEnd}, 4);
_lcd_cmd(lcddev.wramcmd); //start writing GRAM
}
//清屏
static void _lcd_clear(uint16_t color){
uint32_t i = 0;
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
_lcd_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
for(i=0;i<USER_BUFF_LEN;){
GRAM_BUFF[i] = RGB_R(color);
GRAM_BUFF[i+1] = RGB_G(color);
GRAM_BUFF[i+2] = RGB_B(color);
i += 3;
}
for(i=0;i<(lcddev.height*lcddev.width*3)/USER_BUFF_LEN;i++){
_lcd_data(GRAM_BUFF, USER_BUFF_LEN);
}
}
//============================================================================
// lcd 初始化、读、写
//============================================================================
void LCD_Init(void){
_lcd_init();
}
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos){
_lcd_SetWindows(Xpos,Ypos,Xpos+1,Ypos+1);
}
void LCD_Refresh(uint16_t color){
_lcd_clear(color);
}
void LCD_Disp_Flush(const lv_area_t * area, lv_color_t * color_p){
int32_t cnt = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 3;
uint16_t i = 0;
uint16_t index = 0;
uint16_t numTime = 0; //多少组GRAM_BUFF
uint16_t numSurplus = 0;//最后剩下的
if(cnt < 0){
printf("parameter setting error!!!\n");
return;
}
_lcd_SetWindows(area->x1, area->y1, area->x2, area->y2);
// printf("CNT %d\n\n\n",cnt);
if(cnt <= USER_BUFF_LEN){
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
for(i=0;i<cnt;){
GRAM_BUFF[i] = RGB_R(color_p->full);
GRAM_BUFF[i+1] = RGB_G(color_p->full);
GRAM_BUFF[i+2] = RGB_B(color_p->full);
i += 3;
color_p++;
}
_lcd_data(GRAM_BUFF, cnt);
}else{
numTime = cnt/USER_BUFF_LEN;
numSurplus = cnt%USER_BUFF_LEN;
printf("NumTime:%d NumSurplus:%d\n\n", numTime, numSurplus);
for(uint16_t j=0;j<numTime;j++){
// index = j*USER_BUFF_LEN;
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
for(i=0;i<USER_BUFF_LEN;){
GRAM_BUFF[i] = RGB_R(color_p->full);
GRAM_BUFF[i+1] = RGB_G(color_p->full);
GRAM_BUFF[i+2] = RGB_B(color_p->full);
i += 3;
color_p++;
}
_lcd_data(GRAM_BUFF, USER_BUFF_LEN);
}
if(numSurplus > 0){
// index = cnt-numSurplus;
memset(GRAM_BUFF, 0, USER_BUFF_LEN);
for(i=0;i<=numSurplus;){
GRAM_BUFF[i] = RGB_R(color_p->full);
GRAM_BUFF[i+1] = RGB_G(color_p->full);
GRAM_BUFF[i+2] = RGB_B(color_p->full);
i += 3;
color_p++;
}
_lcd_data(GRAM_BUFF, numSurplus);
}
}
}
ESP32 的spi 驱动还是挺有意思的
ESP32共有 3 组SPI,这里用的是 HSPI,另一个 VSPI 用于驱动MAX6675 读取温度
ESP32 的 SPI 在传输前和传输完成时可设置回调
transaction_cb_t pre_cb; /**< Callback to be called before a transmission is started.
和
transaction_cb_t post_cb; /**< Callback to be called after a transmission has completed.
,这里就是用的在传输前设置 LCD D/C的 IO 来设置传输的是命令还是数据。
还有个妙用:就是当 SPI 接口不够用时,可以用该回调实现多个 IO 做软CS 片选来复用出多个 SPI。
三、LVGL v8移植
- 将下载的 lvgl 移动到 esp-idf-v4.3/components 下
- 设置 lvgl 目录下的 lvgl.h 和 lv_conf.h,按需配置即可
- 实现
lv_port_disp
、lv_port_indev
、lv_port_fs
port 为接口文件,其中lv_port_disp/lv_port_indev/lv_port_fs 分别对应 LCD 屏绘制,触摸、鼠标、编码器等输入设备,还有文件系统,这里只使能lv_port_disp
// lv_port_disp.c部分函数
void lv_port_disp_init(void){
disp_init();
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[LV_HOR_RES_MAX * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.hor_res = 480;
disp_drv.ver_res = 320;
disp_drv.flush_cb = disp_flush;
disp_drv.draw_buf = &draw_buf_dsc_1;
lv_disp_drv_register(&disp_drv);
}
static void disp_init(void){
LCD_Init();
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){
LCD_Disp_Flush(area, color_p);
lv_disp_flush_ready(disp_drv);
}
四、LVGL v8测试线程
// 创建 lvgl 相关的线程
......
xSta = xTaskCreate(Thread_lvgl, /* 任务函数指针 */
"LVGL Thread", /* 任务名:调试使用 */
15 * 1024, /* 栈深 */
NULL, /* 任务参数 */
2, /* 优先级. */
lvgl_task_handle); /* 任务 handle */
if(pdPASS == xSta){
// vTaskStartScheduler(); //不需要
printf("create lvgl thread successful!!!\n");
}else{
printf("create lvgl thread failed!!!\n");
return -1;
}
......
// lvgl线程
lv_tick_inc(50)
最好用定时器去实现周期调用
void Thread_lvgl(void *arg){
lv_init();
lv_port_disp_init(); // 显示器初始化
// lv_port_indev_init(); // 输入设备初始化(如果没有实现就注释掉)
// lv_port_fs_init(); // 文件系统设备初始化(如果没有实现就注释掉)
slim_index(); //小控件测试
while(1){
lv_tick_inc(50);
lv_task_handler();
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
//上测试控件
little VGL v8.1 跟 v7.8 的控件设置、style设置还是有很大不同的哈,从v7.8转过来的,还得先适应适应
static void slim_index(void){
lv_obj_t *scr = lv_scr_act(); //获取屏幕对象
//彩色标签
#if 1
static lv_style_t label_styles;
lv_style_init(&label_styles);
lv_style_set_anim_speed(&label_styles, 50);
lv_style_set_text_font(&label_styles, &lv_font_montserrat_20);
lv_obj_t * label1 = lv_label_create(scr);
lv_label_set_recolor(label1, true);
lv_label_set_long_mode(label1, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/
lv_obj_set_width(label1, 120);
// lv_obj_add_style(label1, LV_LABEL_PART_MAIN, &label_styles);
lv_label_set_text(label1, "#ff0000 Hello# #00ff00 world ! slim su.#");
lv_obj_align(label1, LV_ALIGN_TOP_LEFT, 20, 5);
lv_obj_add_style(label1, &label_styles, 0);
// lv_label_set_anim_speed(label1, 80);
#endif
//圆弧
#if 1
static lv_style_t style_arc1_bg, style_arc1_indic, style_arc1_knob;
lv_style_init(&style_arc1_bg);
lv_style_set_arc_width(&style_arc1_bg, 10);
lv_style_set_arc_color(&style_arc1_bg, lv_color_make(0, 255, 0));
lv_style_init(&style_arc1_indic);
lv_style_set_arc_width(&style_arc1_indic, 10);
lv_style_set_arc_color(&style_arc1_indic, lv_color_make(255, 0, 0));
lv_style_init(&style_arc1_knob);
lv_style_set_bg_color(&style_arc1_knob, lv_color_make(255, 0, 0));
lv_style_set_bg_opa(&style_arc1_knob, LV_OPA_0);
lv_obj_t *arc1 = lv_arc_create(scr);
lv_obj_set_size(arc1, 100, 100);
lv_obj_set_pos(arc1, 5, 50);
lv_arc_set_bg_angles(arc1, 0, 360);
lv_arc_set_angles(arc1, 0, 90);
lv_obj_add_style(arc1, &style_arc1_bg, LV_PART_MAIN);
lv_obj_add_style(arc1, &style_arc1_indic, LV_PART_INDICATOR);
lv_obj_add_style(arc1, &style_arc1_knob, LV_PART_KNOB);
static lv_style_t style_arc2_bg, style_arc2_indic, style_arc2_knob;
lv_style_init(&style_arc2_bg);
lv_style_set_arc_width(&style_arc2_bg, 15);
lv_style_set_arc_color(&style_arc2_bg, lv_color_make(0xC0, 0xC0, 0xC0));
lv_style_init(&style_arc2_indic);
lv_style_set_arc_width(&style_arc2_indic, 15);
lv_style_set_arc_color(&style_arc2_indic, lv_color_make(0x80, 0x00, 0x80));
lv_style_init(&style_arc2_knob);
lv_style_set_bg_color(&style_arc2_knob, lv_color_make(0x80, 0x00, 0x80));
lv_style_set_bg_opa(&style_arc2_knob, LV_OPA_0);
lv_obj_t *arc2 = lv_arc_create(scr);
lv_obj_set_size(arc2, 100, 100);
lv_obj_set_pos(arc2, 120, 50);
lv_arc_set_bg_angles(arc2, 0, 360);
lv_arc_set_angles(arc2, 90, 270);
lv_obj_add_style(arc2, &style_arc2_bg, LV_PART_MAIN);
lv_obj_add_style(arc2, &style_arc2_indic, LV_PART_INDICATOR);
lv_obj_add_style(arc2, &style_arc2_knob, LV_PART_KNOB);
lv_obj_t *label2 = lv_label_create(arc2); // 在Arc控件上创建一个标签
lv_obj_align(label2, LV_ALIGN_CENTER, 0, 0); // 对齐到Arc控件中心
lv_label_set_text(label2, "2"); // 设置标签文本
// lv_obj_set_event_cb(arc2, arc_event_handler);
#endif
//进度条
#if 1
static lv_style_t style_bar1_bg, style_bar1_indic, style_bar_label1;
lv_style_init(&style_bar_label1);
lv_style_set_text_font(&style_bar_label1, &lv_font_montserrat_18);
lv_style_init(&style_bar1_bg);
lv_style_set_bg_opa(&style_bar1_bg, LV_OPA_COVER);
lv_style_set_bg_color(&style_bar1_bg, lv_color_make(0xFF, 0xFF, 0x00));
lv_style_init(&style_bar1_indic);
lv_style_set_bg_opa(&style_bar1_indic, LV_OPA_COVER);
lv_style_set_bg_color(&style_bar1_indic, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_grad_color(&style_bar1_indic, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_dir(&style_bar1_indic, LV_GRAD_DIR_VER);
bar1 = lv_bar_create(scr);
lv_obj_add_style(bar1, &style_bar1_bg, LV_PART_MAIN);
lv_obj_add_style(bar1, &style_bar1_indic, LV_PART_INDICATOR);
lv_obj_set_size(bar1, 20, 200);
lv_obj_align(bar1, LV_ALIGN_TOP_RIGHT, -30, 50);
lv_bar_set_range(bar1, 0, 100);
label_bar1 = lv_label_create(scr);
lv_obj_add_style(label_bar1, &style_bar_label1, LV_PART_MAIN);
lv_obj_align_to(label_bar1, bar1, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
lv_label_set_text(label_bar1, "80%");
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_temp);
lv_anim_set_time(&a, 8000);
lv_anim_set_playback_time(&a, 1000);
lv_anim_set_var(&a, bar1);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
#endif
//复选框
#if 1
static lv_style_t style_checkbox1;
lv_style_init(&style_checkbox1);
lv_style_set_text_font(&style_checkbox1, &lv_font_montserrat_18);
lv_style_set_bg_opa(&style_checkbox1, LV_OPA_100);
lv_style_set_bg_color(&style_checkbox1, lv_color_make(0xFF, 0x00, 0x00));
lv_obj_t *checkbox1 = lv_checkbox_create(scr);
lv_obj_set_width(checkbox1, 130);
lv_obj_set_pos(checkbox1, 250, 150);
lv_obj_add_style(checkbox1, &style_checkbox1, LV_PART_MAIN);
lv_checkbox_set_text(checkbox1, "CheckBox");
#endif
//滑块
#if 0
slider1 = lv_slider_create(scr, NULL);
lv_obj_set_size(slider1, 100, 10);
lv_slider_set_range(slider1, 0, 100);
lv_slider_set_anim_time(slider1, 200);
lv_bar_set_value(slider1, 20, LV_ANIM_ON);
lv_obj_align(slider1, scr, LV_ALIGN_IN_TOP_RIGHT, -20, 20);
// lv_obj_set_event_cb(slider1, arc_event_handler);
#endif
//按钮
#if 1
lv_obj_t *btn1 = lv_btn_create(scr);
lv_obj_set_pos(btn1, 250, 50);
lv_obj_set_size(btn1, 120, 40);
lv_obj_t *label_btn1 = lv_label_create(scr);
lv_obj_align_to(label_btn1, btn1, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label_btn1, "key1");
// lv_obj_set_event_cb(btn1, arc_event_handler);
btn2 = lv_btn_create(scr);
lv_obj_set_pos(btn2, 250, 220);
lv_obj_set_size(btn2, 80, 40);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
// lv_btn_set_checkable(btn2, true);
// lv_btn_set_state(btn2, true);
lv_obj_t *label_btn2 = lv_label_create(scr);
lv_obj_align_to(label_btn2, btn2, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label_btn2, "key2");
// lv_obj_set_event_cb(btn2, arc_event_handler);
#endif
//预加载
#if 1
static lv_style_t style_spinner1_bg, style_spinner1_indic;
lv_style_init(&style_spinner1_bg);
lv_style_set_bg_opa(&style_spinner1_bg, LV_OPA_100);
lv_style_set_arc_width(&style_spinner1_bg, 15);
lv_style_set_arc_color(&style_spinner1_bg, lv_color_make(0xFF, 0x00, 0xFF));
lv_style_init(&style_spinner1_indic);
lv_style_set_bg_opa(&style_spinner1_indic, LV_OPA_100);
lv_style_set_arc_width(&style_spinner1_indic, 15);
lv_style_set_arc_color(&style_spinner1_indic, lv_color_make(0x00, 0x00, 0x80));
lv_obj_t * spinner1 = lv_spinner_create(scr, 2000, 60);
lv_obj_set_size(spinner1, 80, 80);
lv_obj_set_pos(spinner1, 10, 180);
lv_obj_add_style(spinner1, &style_spinner1_bg, LV_PART_MAIN);
lv_obj_add_style(spinner1, &style_spinner1_indic, LV_PART_INDICATOR);
static lv_style_t style_spinner2_bg, style_spinner2_indic;
lv_style_init(&style_spinner2_bg);
lv_style_set_arc_width(&style_spinner2_bg, 0);
lv_style_init(&style_spinner2_indic);
lv_style_set_arc_width(&style_spinner2_indic, 8);
lv_style_set_arc_color(&style_spinner2_indic, lv_color_make(0x80, 0x00, 0x00));
lv_obj_t *spinner2 = lv_spinner_create(scr, 2000, 120);
lv_obj_set_size(spinner2, 60, 60);
lv_obj_set_pos(spinner2, 120, 180);
lv_obj_add_style(spinner2, &style_spinner2_bg, LV_PART_MAIN);
lv_obj_add_style(spinner2, &style_spinner2_indic, LV_PART_INDICATOR);
#endif
//线条
#if 1
static lv_style_t style_line1;
lv_style_init(&style_line1);
lv_style_set_line_width(&style_line1, 10);
lv_style_set_line_color(&style_line1, lv_color_make(255, 100, 100));
lv_style_set_line_rounded(&style_line1, true); //圆角
static lv_point_t line_points[] = {{180, 8},{460,40}};
lv_obj_t *line1 = lv_line_create(scr);
lv_obj_add_style(line1, &style_line1, LV_PART_MAIN);
lv_line_set_points(line1, line_points, 2);
lv_obj_set_pos(line1, 0, 0); //点的坐标以此为原点
#endif
//led
#if 1
static lv_style_t style_led1;
lv_style_init(&style_led1);
lv_style_set_bg_color(&style_led1, lv_color_make(0xff, 0xff, 0x00));
led1 = lv_led_create(scr);
lv_obj_set_pos(led1, 350, 215);
lv_obj_set_size(led1, 50, 50);
lv_led_off(led1);
lv_led_set_color(led1, lv_palette_main(LV_PALETTE_YELLOW));
#endif
//文本域
#if 1
txt1 = lv_textarea_create(scr);
lv_obj_set_size(txt1, 380, 30);
lv_obj_set_pos(txt1, 50, 280);
// lv_textarea_set_one_line(txt1, true);
lv_textarea_set_text(txt1, "MAX6675 detect Tempuration: --.-C");
#endif
}
//这里就是测试用的,实际项目不能这么写咯
static void set_temp(void * bar, int32_t temp){
char str[10] = {0};
float temp_val = 0;
lv_bar_set_value(bar, temp, LV_ANIM_ON);
if(0 == temp%10){
sprintf(str, "%d%%", temp);
lv_label_set_text(label_bar1, str);
}
if((led1 != NULL) && (0 == temp%50) && (txt1 != NULL)){
lv_led_toggle(led1);
max6675_get_temp(&temp_val);
sprintf(str, "%2.1fC", temp_val);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_del_char(txt1);
lv_textarea_add_text(txt1, str);
}
}
差不多就是这样咯
想玩下语音控制,小伙伴们有木有好的 ESP 离线语音开发方案 或 开发板推荐吖
版权声明:本文为CSDN博主「slimmm」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/slimmm/article/details/119975059
暂无评论