单片机---ESP8266移植lvgl(一)

前言

最近看着这个LVGL真是挺喜欢,能做出来各种花哨的界面,就像我们各大车企造车一样,性能的提升确实很难,有时候快个0.1秒,都需要多年的累计,但是外观不一样了,改改图纸,立马就性能了,虽然动力拉跨,但是颜值不输,毕竟这个社会,颜值还是硬道理啊。

单片机---ESP8266移植lvgl(一)_第1张图片

lvgl核心移植

搜了一下,貌似只有arduino版本的,没有c语言版本的,那不行啊,我不允许
单片机---ESP8266移植lvgl(一)_第2张图片

不过我发现了ESP32版本的C语言的,其实lvgl在里面只是作为一个组件(component),所以自然就可以将ESP32版本中的lvgl拷贝过来,放在我们ESP8266版本下,

单片机---ESP8266移植lvgl(一)_第3张图片
之所以用这个,就是因为ESP32版本,将kconfig给添加好了,我们可以通过配置面板,进行参数配置,并且Cmaklists.txt也都给写好了,而且最重要的就是它将Kconfig翻译为了一个内部的配置文件,所以我们不再需要像之前移植那样,修改lv_conf.h了。这是很关键的一个修改。单片机---ESP8266移植lvgl(一)_第4张图片

然后我们就可以通过配置面板,来进行lvgl的配置了
单片机---ESP8266移植lvgl(一)_第5张图片
这样lvgl的库,就很容易的编译进来了。注意先把主题关闭,否则有个编译的问题,这里还没有解决
单片机---ESP8266移植lvgl(一)_第6张图片
关闭第一个主题

在这里插入图片描述
否则会报错关于Meter和Char的一个错误。反正也不用主题,先关了吧
单片机---ESP8266移植lvgl(一)_第7张图片

lvgl平台对接

ESP32提供了对接层的代码,这里我没有移植,因为涉及到了显示模块和输入模块,ESP32已经支持了很多显示屏幕,并且可以通过配置进行选择。这部分移植起来有些麻烦,毕竟我这里就一个屏幕,其他的后续再说吧。
单片机---ESP8266移植lvgl(一)_第8张图片

我这里打算自己写一个适配层,也很简单,将标准版里面的
在这里插入图片描述
直接复制到自己的工程主路径中,修改一下名字
单片机---ESP8266移植lvgl(一)_第9张图片
然后修改一下里面的,用第一个例子就可以

void lv_port_disp_init(void)
{
……
 /* 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*/

#if 0
    /* 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 * LV_VER_RES_MAX);   /*Initialize the display buffer*/

……
}
   
#endif

然后再添加 一个绘图刷新就可以了

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
	ILI9341_Draw(area->x1,area->y1,area->x2,area->y2,(unsigned short*) color_p);
    lv_disp_flush_ready(disp_drv);
}

GPIO模拟SPI驱动ili9341

这个ESP8266的开发板并没有SPI接口,那么只能用GPIO模拟一个了。
单片机---ESP8266移植lvgl(一)_第10张图片

GPIO的初始化就不说了,CLK MOSI RES和DC四根线都初始化为输出。
下面主要说一下GPIO模拟SPI,根据这个时序图,上面写错了一个标志,MOSI
单片机---ESP8266移植lvgl(一)_第11张图片)
在CPHA=0和CPOL=0的时候,我们输出是在上升沿的时候输出数据,所以我们用GPIO模拟的时候,可以采用下面的函数

static void SPIWriteByte(unsigned char uc_Bt)
{
    int i = 0;

	/* Clock Polarity is 0 and Clock Phase is 0 */
	gpio_set_level(LCD_SPI_CLK,0);
	for(i = 7; i >= 0; i--)
	{
		gpio_set_level(LCD_SPI_CLK,0);
	    ILI9341_delay_us(10);
	    if(uc_Bt & (1 << i))
	    {
			gpio_set_level(LCD_SPI_MOSI,1);
	    }
	    else
	    {
			gpio_set_level(LCD_SPI_MOSI,0);
	    }
		
		gpio_set_level(LCD_SPI_CLK,1);
	    ILI9341_delay_us(10);
	}
	gpio_set_level(LCD_SPI_CLK,0);
	
}

其他部分的代码,就简单了,标准的ili9341的使用。这里有个左右横屏的问题,搜了全网,都是一个样子,结果发现颜色不正确。问题就出在一个地方
单片机---ESP8266移植lvgl(一)_第12张图片
全网都是这个版本,搬来搬去的。
在这里插入图片描述
难道就没发现颜色错了吗
单片机---ESP8266移植lvgl(一)_第13张图片

例子

还是用了我在HLKW801上的例子,把那个时钟界面移过来了

static void clock_date_task_callback(lv_timer_t *timer)
{
    static const char *week_day[7] = { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" };
    static struct tm *time_info;
	
	int year = 2000;
	int month = 1;
	int day = 1;
	int weekday = CaculateWeekDay(year,month,day);
	int hour =1;
	int minutes = 1;
	int second = 1;
 
	if (timer != NULL && timer->user_data != NULL)
	{
		lv_clock_t * clock = (lv_clock_t *)(timer->user_data);
		if (clock->time_label != NULL)
		{
			lv_label_set_text_fmt(clock->time_label, "%02d:%02d:%02d", hour, minutes, second);
			lv_obj_align_to(clock->time_label, lv_obj_get_parent(clock->time_label), LV_ALIGN_CENTER, 0, 0);
		}
 
		if (clock->date_label != NULL)
		{
			lv_label_set_text_fmt(clock->date_label, "%d-%02d-%02d", year, month, day);
			lv_obj_align_to(clock->date_label, lv_obj_get_parent(clock->date_label), LV_ALIGN_TOP_MID, 2, 0);
		}
 
		 if (clock->weekday_label != NULL)
		 {
			lv_label_set_text_fmt(clock->weekday_label, "%s", week_day[weekday]);
			lv_obj_align_to(clock->weekday_label, lv_obj_get_parent(clock->weekday_label), LV_ALIGN_BOTTOM_MID, -2, 0);
		 }
	}

}

void lvgl_clock_start()
{

    static lv_style_t date_time_clock_style; // 最外层对象的样式
    lv_style_reset(&date_time_clock_style); // 重置样式
    lv_style_init(&date_time_clock_style); // 初始化样式  
    lv_style_set_radius(&date_time_clock_style, 0); // 设置样式圆角,去掉圆角
    lv_style_set_bg_opa(&date_time_clock_style, LV_OPA_100); // 设置样式背景透明度,完全不透
    lv_style_set_border_width(&date_time_clock_style, 0); // 设置样式边框宽度
    lv_style_set_bg_color(&date_time_clock_style, lv_color_black()); // 设置样式背景颜色,黑色
    lv_style_set_pad_left(&date_time_clock_style, 1); // 设置样式左边padding填充宽度
    lv_style_set_pad_right(&date_time_clock_style, 1); // 设置样式右边padding填充宽度
    lv_style_set_pad_top(&date_time_clock_style, 0); // 设置样式顶部padding填充宽度
    lv_style_set_pad_bottom(&date_time_clock_style, 0); // 设置样式底部padding填充宽度
 
    static lv_style_t time_style; // 时间对象样式
    lv_style_reset(&time_style);
    lv_style_init(&time_style);
    lv_style_set_bg_opa(&time_style, LV_OPA_60);
    lv_style_set_border_width(&time_style, 0);
    lv_style_set_radius(&time_style, 5);
    lv_style_set_bg_color(&time_style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_pad_left(&time_style, 0);
    lv_style_set_pad_right(&time_style, 0);
    lv_style_set_pad_top(&time_style, 0);
    lv_style_set_pad_bottom(&time_style, 0);
 
    static lv_style_t date_style; // 日期对象样式
    lv_style_reset(&date_style);
    lv_style_init(&date_style);
    lv_style_set_bg_opa(&date_style, LV_OPA_60);
    lv_style_set_border_width(&date_style, 0);
    lv_style_set_radius(&date_style, 5);
    lv_style_set_bg_color(&date_style, lv_palette_main(LV_PALETTE_BLUE));
    lv_style_set_pad_left(&date_style, 0);
    lv_style_set_pad_right(&date_style, 0);
 
	/* Time font */
    static lv_style_t time_label_style; // 时间标签样式
    lv_style_reset(&time_label_style); // 重置样式
    lv_style_init(&time_label_style); // 初始化样式
    lv_style_set_text_color(&time_label_style , lv_color_white()); // 设置标签样式文本颜色
    lv_style_set_text_font(&time_label_style, &lv_font_montserrat_32); // 设置字体风格
    lv_style_set_text_opa(&time_label_style, LV_OPA_COVER); // 设置字体透明度
    lv_style_set_bg_opa(&time_label_style, LV_OPA_0); // 设置样式背景透明度
 
	/* Date font */
    static lv_style_t date_label_style; // 日期标签样式
    lv_style_reset(&date_label_style);
    lv_style_init(&date_label_style);
    lv_style_set_text_opa(&date_label_style, LV_OPA_COVER);
    lv_style_set_bg_opa(&date_label_style, LV_OPA_0);
    lv_style_set_text_color(&date_label_style , lv_color_white());
    lv_style_set_text_font(&date_label_style, &lv_font_montserrat_16);
 
	/* Week font */
    static lv_style_t week_lable_style; // 日期标签样式
    lv_style_reset(&week_lable_style);
    lv_style_init(&week_lable_style);
    lv_style_set_text_opa(&week_lable_style, LV_OPA_COVER);
    lv_style_set_bg_opa(&week_lable_style, LV_OPA_0);
    lv_style_set_text_color(&week_lable_style, lv_color_white());
    lv_style_set_text_font(&week_lable_style, &lv_font_montserrat_16);
 
	/* Time & Date */
	LV_IMG_DECLARE(bg_pic);
    lv_obj_t * time_date_obj = lv_img_create(lv_scr_act()); // 基于屏幕创建时间日期对象
    lv_img_set_src(time_date_obj, &bg_pic);
    
	
    /*Time display*/
    lv_obj_t *time_obj = lv_obj_create(time_date_obj); // 基于time_date_obj对象创建时间对象
    if (time_obj == NULL)
    {
        printf("[%s:%d] time_obj create failed\n", __FUNCTION__, __LINE__);
        return;
    }
 
    lv_obj_set_size(time_obj, 158, 100); // 设置对象大小
    lv_obj_align_to(time_obj, time_date_obj, LV_ALIGN_LEFT_MID, 0, 0); // 设置time_obj对象基于time_date_obj对象左边中间对齐
    lv_obj_add_style(time_obj, &time_style, LV_STATE_DEFAULT);  // 给time_obj对象添加样式
 
    static lv_clock_t lv_clock = { 0 };
 
    lv_clock.time_label = lv_label_create(time_obj); // 基于time_obj对象创建时间显示标签对象 lv_clock.time_label
    if (lv_clock.time_label == NULL)
    {
        printf("[%s:%d] time_label create failed\n", __FUNCTION__, __LINE__);
        return ;
    }
 
    lv_obj_add_style(lv_clock.time_label, &time_label_style, LV_STATE_DEFAULT); // 给对象 lv_clock.time_label添加样式
 
    /*Date display*/
    lv_obj_t *date_obj = lv_obj_create(time_date_obj); // 基于time_date_obj对象创建date_obj对象
    if (date_obj == NULL)
    {
        printf("[%s:%d] date_obj create failed\n", __FUNCTION__, __LINE__);
        return ;
    }
    lv_obj_set_size(date_obj, 158, 100); // 设置对象大小
    lv_obj_align_to(date_obj, time_date_obj, LV_ALIGN_RIGHT_MID, 0, 0); //设置date_obj对象基于time_date_obj对象右边中部对齐
    lv_obj_add_style(date_obj, &date_style, LV_STATE_DEFAULT); // 给date_obj对象添加样式
 
    lv_clock.date_label = lv_label_create(date_obj); // 基于date_obj对象创建lv_clock.date_label日期显示对象
    if (lv_clock.date_label == NULL)
    {
        printf("[%s:%d] date_label create failed\n", __FUNCTION__, __LINE__);
        return ;
    }
    lv_obj_add_style(lv_clock.date_label, &date_label_style, LV_STATE_DEFAULT); // 给lv_clock.date_label对象添加样式
 
    /*Week display*/
    lv_clock.weekday_label = lv_label_create(date_obj); // 基于date_obj对象创建星期显示lv_clock.weekday_label对象
    if (lv_clock.weekday_label == NULL)
    {
        printf("[%s:%d] weekday_label create failed\n", __FUNCTION__, __LINE__);
        return;
    }
    lv_obj_add_style(lv_clock.weekday_label, &week_lable_style, LV_STATE_DEFAULT); // 给对象lv_clock.weekday_label添加样式
 
    // 设置时间标签lv_clock.time_label对象基于父对象居中对齐
    lv_obj_align_to(lv_clock.time_label, lv_obj_get_parent(lv_clock.time_label), LV_ALIGN_CENTER, 0, 0);
     // 设置时间标签lv_clock.date_label对象基于父对象顶部中间对齐
    lv_obj_align_to(lv_clock.date_label, lv_obj_get_parent(lv_clock.date_label), LV_ALIGN_TOP_MID, 2, 0);
    // 设置时间标签lv_clock.weekday_label对象基于父对象底部中间对齐
    lv_obj_align_to(lv_clock.weekday_label, lv_obj_get_parent(lv_clock.weekday_label), LV_ALIGN_BOTTOM_MID, -2, 0);
 
    lv_timer_t* task_timer = lv_timer_create(clock_date_task_callback, 2000, (void *)&lv_clock); // 创建定时任务,200ms刷新一次
    if (task_timer == NULL)
    {
        printf("[%s:%d] lv_timer_create failed\n", __FUNCTION__, __LINE__);
    }
}

主函数
单片机---ESP8266移植lvgl(一)_第14张图片
显示效果如下
单片机---ESP8266移植lvgl(一)_第15张图片

代码下载

上述全部内容的代码,已经打包,可以通过下面链接下载。注意我的是安信可的IDE1.5版本。在我的win11上编译,那真是奇慢无比。回头我再换个电脑试试吧。

《》下载地址《》

结束语

最近这些天的新闻,也没有什么正能量的事,老痰的酸菜,油价迈上新台阶,一个亿的罚款又让一堆作品进了百度云盘珍藏目录,
单片机---ESP8266移植lvgl(一)_第16张图片
直到昨晚才有一个好消息,不过希望那些老师护士家政妇搜查官什么的,不要受伤。
单片机---ESP8266移植lvgl(一)_第17张图片

你可能感兴趣的:(单片机,IOT,单片机,ESP8266,lvgl,ili9341)