• STM32 + RTThread + UGUI


    一、概述

    • 开发板:STM32F103C8T6
    • 显示器:ST7735S
    • RT-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 驱动,所以这里我们只能根据标准的驱动进行编写了

    1. 驱动函数结构体

      /* 驱动函数实现的结构体 */
      #ifdef RT_USING_DEVICE_OPS
      const static struct rt_device_ops lcd_ops =
      {
      drv_lcd_init,
      RT_NULL,
      RT_NULL,
      RT_NULL,
      RT_NULL,
      drv_lcd_control
      };
      #endif
    2. 注册 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;
      }
    3. 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;
      }
    4. LCD 驱动功能实现
      剩下的就比较简单了,只需要参考 LCD 提供的案例程序进行更改就好了,主要有实现如下

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

    四、UGUI 介绍

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

    2. 获取 UGUI
      github:https://github.com/xidongxu/ugui

    3. 文件目录

    4. 使用介绍

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

    五、UGUI 移植

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

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

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

    4. 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 out
    rt_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 inversion
    lcd_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); //VCOM
    lcd_write_data(0x1A);
    lcd_write_cmd(0x36); //MX, MY, RGB mode
    lcd_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 mode
    lcd_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_OPS
    const 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 */
  • 相关阅读:
    Linux之IFS间隔符、C编程、Makefile工程文件、gdb调试、宏定义、预处理、assert和调试用特殊的宏
    AX6000路由器更改算号器密码为自定义密码的方法
    RCNN学习笔记-ResNeXt
    2-硼酸三苯胺,CAS号:934169-37-6
    Kafka原理剖析之「位点提交」
    linux根据指定的文件名杀死进程,再定时重启任务
    java---并查集算法_食物链(每日一道算法2022.8.17)
    Go语言中的面向对象编程(OOP)
    【图像融合】基于matlab多尺度奇异值分解图像融合【含Matlab源码 2040期】
    Mysql 分布式序列算法
  • 原文地址:https://www.cnblogs.com/jzcn/p/17362173.html