首页 > 快讯 > 正文

STM32 + RTThread + UGUI|新要闻

2023-04-28 16:44:04 来源:博客园
一、概述开发板:STM32F103C8T6显示器:ST7735SRT-Thread:5.0.0

玩过 GUI 的小伙伴都知道,界面的显示是一个个像素点组合起来的,那么直接构建出来炫酷的 GUI 还是相对比较困难的,所以我们一般都会使用一些 GUI 库来实现,比如 LVGL、QT、UGUI等,这样对于驱动开发的人员来说就相对比较简单了,


(相关资料图)

图形库应用的核心思想只需要提供一帧的缓冲区,我们只需要不断的将缓冲区的数据写入到 LCD 中即可,而缓冲区的内容由图形库实现,需要注意的是这个缓冲的创建方式,有的图形库会自己创建缓冲区,我们只需要负责刷新 LCD 的显示即可,而有的图形库是由驱动提供缓冲区,图形库负责写入。

二、RT-Thread 移植

移植 RT-Thread 不是此文章的重点,可以参考一下我之前的笔记,或者直接使用 RT-Thread Studio、STM32CubeMX等工具直接生成,这里我就不过多介绍了

三、LCD 驱动

使用过 RT-Thread 的小伙伴,都知道 RT-Thread 目前还不能直接使用工具生成我们想要的 LCD 驱动,所以这里我们只能根据标准的驱动进行编写了

驱动函数结构体

/* 驱动函数实现的结构体 */#ifdef RT_USING_DEVICE_OPSconst static struct rt_device_ops lcd_ops ={    drv_lcd_init,    RT_NULL,    RT_NULL,    RT_NULL,    RT_NULL,    drv_lcd_control};#endif

注册 LCD 设备

int drv_lcd_hw_init(void){    rt_err_t result = RT_EOK;    rt_uint32_t lcd_buff_size = lcd_buff_size = LCD_HEIGHT * LCD_WIDTH * 2;        /* 创建LCD设备对象 */    struct rt_device *device = &_lcd.lcd_dev;    memset(&_lcd, 0x00, sizeof(_lcd));    LOG_D("drv_lcd_hw_init!\n");        /* 初始化lcd_lock信号量 */    result = rt_sem_init(&_lcd.lcd_lock, "lcd_lock", 0, RT_IPC_FLAG_FIFO);    if (result != RT_EOK)    {        LOG_E("init semaphore failed!\n");        result = -RT_ENOMEM;        goto __exit;    }        /* 设置 LCD 设备信息 */    _lcd.lcd_info.height = LCD_HEIGHT;    _lcd.lcd_info.width = LCD_WIDTH;    _lcd.lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;    _lcd.lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565;     // 图像的格式(RGB:565)        /* LCD 显示缓冲区,大小为显示一帧图像所需空间 */    _lcd.lcd_info.smem_len = lcd_buff_size;    _lcd.lcd_info.framebuffer = rt_malloc(lcd_buff_size);    if (_lcd.lcd_info.framebuffer == RT_NULL)    {        LOG_E("init frame buffer failed!\n");        result = -RT_ENOMEM;        goto __exit;    }    /* 将缓冲区初始化为 0xFF */    memset(_lcd.lcd_info.framebuffer, 0xFF, lcd_buff_size);#ifdef RT_USING_DEVICE_OPS    device->ops     = &lcd_ops;#else    device->init    = drv_lcd_init;    device->control = drv_lcd_control;#endif    /* 注册 LCD 设备 */    rt_device_register(device, "lcd", RT_DEVICE_FLAG_RDWR);    __exit:    if (result != RT_EOK)    {        rt_sem_detach(&_lcd.lcd_lock);        if (_lcd.lcd_info.framebuffer)        {            rt_free(_lcd.lcd_info.framebuffer);        }    }    return result;    }

LCD 控制函数的实现

static rt_err_t drv_lcd_control(struct rt_device *device, int cmd, void *args){    // struct drv_lcd_device *lcd = LCD_DEVICE(device);    LOG_D("drv_lcd_control cmd is: %d\n", cmd);    switch (cmd)    {        case RTGRAPHIC_CTRL_RECT_UPDATE:        {            rt_sem_take(&_lcd.lcd_lock, RT_TICK_PER_SECOND / 20);            /* 刷新缓冲区 */            rt_sem_release(&_lcd.lcd_lock);        }        break;                case RTGRAPHIC_CTRL_POWERON:        {            /* LCD 退出睡眠模式 */        }        break;                case RTGRAPHIC_CTRL_POWEROFF:        {            /* LCD 进入睡眠模式 */        }        break;                case RTGRAPHIC_CTRL_GET_INFO:        {            /* 获取 LCD 参数 */            memcpy(args, &_lcd.lcd_info, sizeof(_lcd.lcd_info));        }        break;            default:            return -RT_EINVAL;    }    return RT_EOK;}

LCD 驱动功能实现剩下的就比较简单了,只需要参考 LCD 提供的案例程序进行更改就好了,主要有实现如下

drv_lcd_init:完成 LCD 的复位、初始化、首次清屏工作drv_lcd_control:完成 LCD 显示区域的刷新、屏幕参数的返回、亮屏和息屏等工作注意:具体实现参考后面的程序源码,相对比较简单,这里就不过多介绍了四、UGUI 介绍

介绍µGUI 是一个用于嵌入式系统的免费开源图形库。 它独立于平台,可以轻松移植到几乎任何微控制器系统。 只要显示器能够显示图形,μGUI 就不受某种显示技术的限制。 因此,支持LCD、TFT、E-Paper、LED或OLED等显示技术。 整个模块由两个文件组成:ugui.c 和 ugui.h。注意:这里的介绍我直接引用了作者的描述

获取 UGUIgithub:https://github.com/xidongxu/ugui

文件目录

使用介绍

移植:我们主要实现 ugui_port.c,这里下载时已经提供了案例,所以我只需要在其中进行简单的修改使用:使用相对比较简单,直接参考 “µGUI v0.3.pdf” 文档即可,直接没有难度五、UGUI 移植

初始化直接在 ugui_port.c 文件中使用 INIT_COMPONENT_EXPORT(ugui_port_init)进行自动初始化,如下图所示:

lcd_open 函数这里不要做任何更改,从函数中可以看出 LCD 相关的参数获取,如下图所示:

lcd_draw_pixel 函数主要功能是在缓冲区中写入一个像素点的颜色,如下图所示:

ugui_port_thread_entry 函数这是线程的入口函数,主要目的是定期将缓冲区的数据写入到 LCD 中,下图所示:

注意:从以上步奏可以看出,移植 UGUI 时不需要更改任何参数,只需要在初始化时调用 ugui_port_init函数即可。

六、程序源码drv_lcd_st7735s.h
/** * @file drv_lcd_st7735s.h * */#ifndef __DRV_LCD_ST7735S_H__#define __DRV_LCD_ST7735S_H__#include #define LCD_HEIGHT                      20      // LCD 高像素#define LCD_WIDTH                       128      // LCD 宽像素#define LCD_BITS_PER_PIXEL              16      // 像素点的数据宽度#define LCD_CS_PIN_TYPE                 GPIOA               // CS 引脚所在的组#define LCD_CS_PIN                      GPIO_PIN_4          // 引脚编号#define LCD_BCK_PIN                     GET_PIN(B, 1)       // 背光引脚#define LCD_DC_PIN                      GET_PIN(B, 8)       // 数据引脚#define LCD_RES_PIN                     GET_PIN(B, 9)       // 复位引脚#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#define GRAY175          0XAD75#define GRAY151          0X94B2#define GRAY187          0XBDD7#define GRAY240          0XF79E#endif  /* __DRV_LCD_ST7735S_H__ */
drv_lcd_st7735s.c
/***************************************************************文件名 : drv_lcd_st7735s.c作者 : jiaozhu版本 : V1.0描述 : st7735s 显示驱动其他 : 无日志 : 初版 V1.0 2023/04/28***************************************************************/#include #include #ifdef BSP_USING_LCD#include "drv_spi.h"#include #include "drv_lcd_st7735s.h"//#define DRV_DEBUG#define LOG_TAG             "drv.lcd"#include static struct rt_spi_device *spi_dev_lcd;struct drv_lcd_device{    struct rt_device lcd_dev;    struct rt_device_graphic_info lcd_info;    struct rt_semaphore lcd_lock;};struct drv_lcd_device _lcd;/** * @brief  LCD 命令写入,写入时数据引脚为低电平 * * @param  cmd 命令 * @retval 返回执行结果 */static rt_err_t lcd_write_cmd(const rt_uint8_t cmd){    rt_size_t len;    rt_pin_write(LCD_DC_PIN, PIN_LOW);    len = rt_spi_send(spi_dev_lcd, &cmd, 1);    if (len != 1)    {        LOG_I("lcd_write_cmd error. %d", len);        return -RT_ERROR;    }    else    {        return RT_EOK;    }}/** * @brief  LCD 数据写入,写入时数据引脚为高电平 * * @param  cmd 命令 * @retval 返回执行结果 */static rt_err_t lcd_write_data(const rt_uint8_t data){    rt_size_t len;    rt_pin_write(LCD_DC_PIN, PIN_HIGH);    len = rt_spi_send(spi_dev_lcd, &data, 1);    if (len != 1)    {        LOG_I("lcd_write_data error. %d", len);        return -RT_ERROR;    }    else    {        return RT_EOK;    }}/** * @brief  LCD 板级初始化 * * @param  None * @retval int 初始化结果 */static int lcd_dev_init(void){    lcd_write_cmd(0x11); //Sleep outrt_thread_delay(12); //Delay 12ms//------------------------------------ST7735S Frame Rate-----------------------------------------//lcd_write_cmd(0xB1);lcd_write_data(0x05);lcd_write_data(0x3C);lcd_write_data(0x3C);lcd_write_cmd(0xB2);lcd_write_data(0x05);lcd_write_data(0x3C);lcd_write_data(0x3C);lcd_write_cmd(0xB3);lcd_write_data(0x05);lcd_write_data(0x3C);lcd_write_data(0x3C);lcd_write_data(0x05);lcd_write_data(0x3C);lcd_write_data(0x3C);//------------------------------------End ST7735S Frame Rate-----------------------------------------//lcd_write_cmd(0xB4); //Dot inversionlcd_write_data(0x03);lcd_write_cmd(0xC0);lcd_write_data(0x28);lcd_write_data(0x08);lcd_write_data(0x04);lcd_write_cmd(0xC1);lcd_write_data(0XC0);lcd_write_cmd(0xC2);lcd_write_data(0x0D);lcd_write_data(0x00);lcd_write_cmd(0xC3);lcd_write_data(0x8D);lcd_write_data(0x2A);lcd_write_cmd(0xC4);lcd_write_data(0x8D);lcd_write_data(0xEE);//---------------------------------End ST7735S Power Sequence-------------------------------------//lcd_write_cmd(0xC5); //VCOMlcd_write_data(0x1A);lcd_write_cmd(0x36); //MX, MY, RGB modelcd_write_data(0xC0);//------------------------------------ST7735S Gamma Sequence-----------------------------------------//lcd_write_cmd(0xE0);lcd_write_data(0x04);lcd_write_data(0x22);lcd_write_data(0x07);lcd_write_data(0x0A);lcd_write_data(0x2E);lcd_write_data(0x30);lcd_write_data(0x25);lcd_write_data(0x2A);lcd_write_data(0x28);lcd_write_data(0x26);lcd_write_data(0x2E);lcd_write_data(0x3A);lcd_write_data(0x00);lcd_write_data(0x01);lcd_write_data(0x03);lcd_write_data(0x13);lcd_write_cmd(0xE1);lcd_write_data(0x04);lcd_write_data(0x16);lcd_write_data(0x06);lcd_write_data(0x0D);lcd_write_data(0x2D);lcd_write_data(0x26);lcd_write_data(0x23);lcd_write_data(0x27);lcd_write_data(0x27);lcd_write_data(0x25);lcd_write_data(0x2D);lcd_write_data(0x3B);lcd_write_data(0x00);lcd_write_data(0x01);lcd_write_data(0x04);lcd_write_data(0x13);//------------------------------------End ST7735S Gamma Sequence-----------------------------------------//lcd_write_cmd(0x3A); //65k modelcd_write_data(0x05);lcd_write_cmd(0x29); //Display on    return RT_EOK;}/** * @brief  初始化 LCD 所需的引脚,并通过引脚复位 LCD * * @param  None * @retval None */static void lcd_gpio_init(void){    /* 配置引脚模式 */    rt_pin_mode(LCD_DC_PIN, PIN_MODE_OUTPUT);    rt_pin_mode(LCD_RES_PIN, PIN_MODE_OUTPUT);    rt_pin_mode(LCD_BCK_PIN, PIN_MODE_OUTPUT);/* 通过引脚复位 LCD */rt_pin_write(LCD_BCK_PIN, PIN_LOW);    rt_pin_write(LCD_RES_PIN, PIN_LOW);    rt_thread_mdelay(12);    rt_pin_write(LCD_RES_PIN, PIN_HIGH);       /* 复位后延时一段时间,确保屏幕正常工作 */    rt_thread_mdelay(12);}/** * @brief  初始化 LCD 所需的 SPI 外设 * * @param  None * @retval int 操作结果 */static int lcd_spi_init(void){    /* 配置 SPI 端口,并指定 CS 引脚为 PA4 */    __HAL_RCC_GPIOA_CLK_ENABLE();    rt_hw_spi_device_attach("spi1", "spi10", LCD_CS_PIN_TYPE, LCD_CS_PIN);        /* 查找设备 */    spi_dev_lcd = (struct rt_spi_device *)rt_device_find("spi10");    if(RT_NULL == spi_dev_lcd)    {        LOG_E("Unable to find SPI device required for LCD");        return RT_ERROR;    }    /* 配置 SPI */    struct rt_spi_configuration cfg;    cfg.data_width = 8;    cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;    cfg.max_hz = 42 * 1000 * 1000; /* 42M,SPI max 42MHz,lcd 4-wire spi */    spi_dev_lcd->bus ->owner = spi_dev_lcd;    rt_spi_configure(spi_dev_lcd, &cfg);    return RT_EOK;}  /** * @brief  设置需要绘图的区域 * * @param   x1      start of x position * @param   y1      start of y position * @param   x2      end of x position * @param   y2      end of y position * @retval None */static void lcd_draw_area_set(rt_uint16_t x1, rt_uint16_t y1, rt_uint16_t x2, rt_uint16_t y2){    lcd_write_cmd(0x2a);    lcd_write_data(x1 >> 8);    lcd_write_data(x1);    lcd_write_data(x2 >> 8);    lcd_write_data(x2);    lcd_write_cmd(0x2b);    lcd_write_data(y1 >> 8);    lcd_write_data(y1);    lcd_write_data(y2 >> 8);    lcd_write_data(y2);    lcd_write_cmd(0x2C);}/** * @brief  LCD 清屏,将整个屏幕设定为指定颜色 * * @param  color 清空的颜色 * @retval None */static void lcd_clear_screen(rt_uint16_t color){    rt_uint16_t i, j;        rt_uint8_t data[2] = {0};    data[0] = (color >> 8) & 0xFF;    data[1] = color & 0xFF;        /* 设置整个屏幕区域 */    lcd_draw_area_set(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);        /* 这里直接通过 SPI 发送数据,所以需要单独将数据引脚拉高 */    rt_pin_write(LCD_DC_PIN, PIN_HIGH);        if (_lcd.lcd_info.framebuffer != RT_NULL)    {        /* 重置缓冲区 */        // memset(_lcd.lcd_info.framebuffer, color, _lcd.lcd_info.smem_len);                for (j = 0; j < _lcd.lcd_info.smem_len / 2; j++)        {            _lcd.lcd_info.framebuffer[j * 2] =  data[0] ;            _lcd.lcd_info.framebuffer[j * 2 + 1] = data[1];        }                rt_spi_send(spi_dev_lcd, _lcd.lcd_info.framebuffer, _lcd.lcd_info.smem_len);    }    else    {        for (i = 0; i < LCD_HEIGHT; i++)        {            for (j = 0; j < LCD_WIDTH; j++)            {                rt_spi_send(spi_dev_lcd, data, 2);            }        }    }}/** * @brief  点亮 LED 屏幕 * @param  None * @retval None */static void lcd_display_on(void){    rt_pin_write(LCD_BCK_PIN, PIN_HIGH);}/** * @brief  熄灭 LED 屏幕 * @param  None * @retval None */static void lcd_display_off(void){    rt_pin_write(LCD_BCK_PIN, PIN_LOW);}/** * @brief  液晶显示器进入最小功耗模式,背光关闭 * @param  None * @retval None */static void lcd_enter_sleep(void){    rt_pin_write(LCD_BCK_PIN, PIN_LOW);    rt_thread_mdelay(5);    lcd_write_cmd(0x10);}/** * @brief  液晶显示器关闭睡眠模式,背光灯打开 * @param  None * @retval None */static void lcd_exit_sleep(void){    rt_pin_write(LCD_BCK_PIN, PIN_HIGH);    rt_thread_mdelay(5);    lcd_write_cmd(0x11);    rt_thread_mdelay(120);}/** * @brief  设置光标位置 * @param  Xpos 横坐标 * @param  Ypos 纵坐标 * @retval None */// static void lcd_cursor_set(rt_uint16_t Xpos, rt_uint16_t Ypos)// {//     lcd_write_cmd(0x2A); // lcd_write_data(Xpos>>8); // lcd_write_data(Xpos&0XFF); // lcd_write_cmd(0x2B); // lcd_write_data(Ypos>>8); // lcd_write_data(Ypos&0XFF);// }/** * @brief  LCD 驱动初始化 * @param  device LCD 设备结构体 * @retval None */static rt_err_t drv_lcd_init(struct rt_device *device){    LOG_D("drv_lcd_init!\n");        if (lcd_spi_init() != RT_EOK)    {        return -RT_EINVAL;    }        lcd_gpio_init();        if (lcd_dev_init() != RT_EOK)    {        return -RT_EINVAL;    }        /* 清屏 */    lcd_clear_screen(WHITE);        /* 初始化完成后,点亮屏幕 */    rt_pin_write(LCD_BCK_PIN, PIN_HIGH);        return RT_EOK;}/** * @brief  LCD 驱动的操作函数 * @param  device LCD 设备结构体 * @param  cmd 操作命令 * @param  args 传入的参数 * @retval None */static rt_err_t drv_lcd_control(struct rt_device *device, int cmd, void *args){    // struct drv_lcd_device *lcd = LCD_DEVICE(device);    LOG_D("drv_lcd_control cmd is: %d\n", cmd);    switch (cmd)    {        case RTGRAPHIC_CTRL_RECT_UPDATE:        {            rt_sem_take(&_lcd.lcd_lock, RT_TICK_PER_SECOND / 20);            /* 刷新缓冲区 */            if (_lcd.lcd_info.framebuffer)            {                /* 设置整个屏幕区域 */                lcd_draw_area_set(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);                /* 这里直接通过 SPI 发送数据,所以需要单独将数据引脚拉高 */                rt_pin_write(LCD_DC_PIN, PIN_HIGH);                rt_spi_send(spi_dev_lcd, _lcd.lcd_info.framebuffer, _lcd.lcd_info.smem_len);            }                        /* 释放锁信号 */            rt_sem_release(&_lcd.lcd_lock);        }        break;                case RTGRAPHIC_CTRL_POWERON:        {            /* LCD 退出睡眠模式 */            lcd_display_on();            lcd_exit_sleep();        }        break;                case RTGRAPHIC_CTRL_POWEROFF:        {            /* LCD 进入睡眠模式 */            lcd_display_off();            lcd_enter_sleep();        }        break;                case RTGRAPHIC_CTRL_GET_INFO:        {            /* 获取 LCD 参数 */            memcpy(args, &_lcd.lcd_info, sizeof(_lcd.lcd_info));        }        break;                    default:            return -RT_EINVAL;    }    return RT_EOK;}/* 驱动函数实现的结构体 */#ifdef RT_USING_DEVICE_OPSconst static struct rt_device_ops lcd_ops ={    drv_lcd_init,    RT_NULL,    RT_NULL,    RT_NULL,    RT_NULL,    drv_lcd_control};#endif/** * @brief  LCD 设备注册 * * @param  None * @retval int 注册结果 */int drv_lcd_hw_init(void){    rt_err_t result = RT_EOK;    rt_uint32_t lcd_buff_size = lcd_buff_size = LCD_HEIGHT * LCD_WIDTH * 2;        /* 创建LCD设备对象 */    struct rt_device *device = &_lcd.lcd_dev;    memset(&_lcd, 0x00, sizeof(_lcd));    LOG_D("drv_lcd_hw_init!\n");        /* 初始化lcd_lock信号量 */    result = rt_sem_init(&_lcd.lcd_lock, "lcd_lock", 0, RT_IPC_FLAG_FIFO);    if (result != RT_EOK)    {        LOG_E("init semaphore failed!\n");        result = -RT_ENOMEM;        goto __exit;    }        /* 设置 LCD 设备信息 */    _lcd.lcd_info.height = LCD_HEIGHT;    _lcd.lcd_info.width = LCD_WIDTH;    _lcd.lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;    _lcd.lcd_info.pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565;     // 图像的格式(RGB:565)        /* LCD 显示缓冲区,大小为显示一帧图像所需空间 */    _lcd.lcd_info.smem_len = lcd_buff_size;    _lcd.lcd_info.framebuffer = rt_malloc(lcd_buff_size);    if (_lcd.lcd_info.framebuffer == RT_NULL)    {        LOG_E("init frame buffer failed!\n");        result = -RT_ENOMEM;        goto __exit;    }    /* 将缓冲区初始化为 0xFF */    memset(_lcd.lcd_info.framebuffer, 0xFF, lcd_buff_size);#ifdef RT_USING_DEVICE_OPS    device->ops     = &lcd_ops;#else    device->init    = drv_lcd_init;    device->control = drv_lcd_control;#endif    /* 注册 LCD 设备 */    rt_device_register(device, "lcd", RT_DEVICE_FLAG_RDWR);    __exit:    if (result != RT_EOK)    {        rt_sem_detach(&_lcd.lcd_lock);        if (_lcd.lcd_info.framebuffer)        {            rt_free(_lcd.lcd_info.framebuffer);        }    }    return result;    }INIT_DEVICE_EXPORT(drv_lcd_hw_init);#endif /* BSP_USING_LCD */

标签:

上一篇:【环球新视野】有奖征集 云阳航拍大赛启动
下一篇:最后一页
相关阅读
猜你喜欢
精彩推送
社科