[联盛德 W803-Pico 试用] LVGL 驱动 SSD1306OLED 屏幕

发布于 2025-04-01 00: 20: 38

1. 硬件介绍

1. 1 W803-Pico

w803_pico. png
W803-Pico 主控为联盛德 W803 芯片, 支持无线 WiFi (IEEE802. 11 b/g/n 协议) , 蓝牙 BT/BLE4. 2 协议. 芯片内置高性能 32 位处理器, 主频达 240MHz.
内置 2MB Flash 以及 288KB RAM.

MCU 特性

集成 32 位 XT804 处理器, 工作频率 240MHz, 内置 DSP, 浮点运算单元与安全引擎
内置 2MB Flash, 288KB RAM
集成 PSRAM 接口, 支持最高 64M bit 外置 PSRAM 存储器
集成 10 路 Touch Sensor 触控接口
集成 5 路 UART 高速接口
集成 2 路 12 比特 ADC, 最高采样率 1KHz
集成 1 个高速 从 SPI 接口, 支持最高 50MHz
集成 1 个 SDIO_HOST 接口, 支持 SDIO2. 0, SDHC, MMC4. 2
集成 1 个 SDIO_DEVICE, 支持 SDIO2. 0, 最高工作频率 200Mbps
集成 1 个 I2C 控制器
集成 GPIO 控制器, 最多支持 20 个 GPIO
集成 5 路 PWM 接口
集成 1 路 Duplex I2S 控制器

1. 2 SSD1306 OLED 0. 96 屏幕

OLED. png

  • 分辨率: 128x64 像素
  • 显示颜色: 单色 (白色/蓝色)
  • 接口类型: I2C/SPI (本教程使用 I2C 接口)
  • 工作电压: 3. 3V-5V

管脚定义

管脚名称 功能说明
VCC 3. 3V 电源输入
GND 地线
SCL I2C 时钟线
SDA I2C 数据线

2. 硬件连接

电路连接. png
W803-Pico ↔ SSD1306 接线表:

W803-Pico 引脚 SSD1306 引脚
3. 3V VCC
GND GND
PA1 SCL
PA4 SDA

3. 工程配置

打开 vscode, 打开 wm_iot_sdk 文件夹, wm_iot_sdk2. x 下载地址

工程的搭建和环境配置请参照官网介绍安装测试 W803-Pico 入门指南

3. 1 创建工程副本

在 VSCODE 内终端输入:
cp . \examples\peripheral\i2c -r . \examples\peripheral\i2c_lvgl_ssd1306oled

3. 2 menuconfig 配置

SDK menuconfig 配置
SDK 配置. png

4. 驱动代码实现

4. 1 SSD1306 初始化

void SSD1306_init (void) 
{
    m_font_offset = 2; 
    
    // 初始化 I2C 设备
    ssd1306_i2c_device = wm_drv_i2c_init (I2C_CONTROLLER_NAME) ; 
    if  (ssd1306_i2c_device == NULL)  {
        printf ("SSD1306 I2C init error\r\n") ; 
        return; 
    }
    
    // 配置 I2C 设备
    ssd1306_i2c_config. addr = SSD1306_I2C_ADDR; 
    ssd1306_i2c_config. speed_hz = SSD1306_I2C_SPEED; 
 
    SSD1306_sendCommand (0xAE) ;             // display off
    SSD1306_sendCommand (0xA6) ;             // Set Normal Display  (default) 
    SSD1306_sendCommand (0xAE) ;             // DISPLAYOFF
    SSD1306_sendCommand (0xD5) ;             // SETDISPLAYCLOCKDIV
    SSD1306_sendCommand (0x80) ;             // the suggested ratio 0x80
    SSD1306_sendCommand (0xA8) ;             // SSD1306_SETMULTIPLEX
    SSD1306_sendCommand (0x3F) ; 
    SSD1306_sendCommand (0xD3) ;             // SETDISPLAYOFFSET
    SSD1306_sendCommand (0x0) ;              // no offset
    SSD1306_sendCommand (0x40|0x0) ;         // SETSTARTLINE
    SSD1306_sendCommand (0x8D) ;             // CHARGEPUMP
    SSD1306_sendCommand (0x14) ; 
    SSD1306_sendCommand (0x20) ;             // MEMORYMODE
    SSD1306_sendCommand (0x00) ;             // 0x0 act like ks0108
    SSD1306_sendCommand (0xA1) ;             // SEGREMAP   Mirror screen horizontally  (A0) 
    SSD1306_sendCommand (0xC8) ;             // COMSCANDEC Rotate screen vertically  (C0) 
    SSD1306_sendCommand (0xDA) ;             // 0xDA
    SSD1306_sendCommand (0x12) ;             // COMSCANDEC
    SSD1306_sendCommand (0x81) ;             // SETCONTRAST
    SSD1306_sendCommand (0xCF) ;             //
    SSD1306_sendCommand (0xd9) ;             // SETPRECHARGE 
    SSD1306_sendCommand (0xF1) ;  
    SSD1306_sendCommand (0xDB) ;             // SETVCOMDETECT                
    SSD1306_sendCommand (0x40) ; 
    SSD1306_sendCommand (0xA4) ;             // DISPLAYALLON_RESUME        
    SSD1306_sendCommand (0xA6) ;             // NORMALDISPLAY             
    SSD1306_clearDisplay () ; 
    SSD1306_sendCommand (0x2E) ;             // Stop scroll
    SSD1306_sendCommand (0x20) ;             // Set Memory Addressing Mode
    SSD1306_sendCommand (0x00) ;             // Set Memory Addressing Mode ab Horizontal addressing mode
    SSD1306_setFont (font8x8) ; 
}

SSD1306 OLED 其他接口

void SSD1306_sendCommand (unsigned char command) 
{
    uint8_t sub_addr = SSD1306_Command_Mode; 
    
    if  (ssd1306_i2c_device == NULL)  {
        printf ("SSD1306 I2C device not initialized\r\n") ; 
        return; 
    }
    
    wm_drv_i2c_write (ssd1306_i2c_device,  &ssd1306_i2c_config,  &sub_addr,  1,  &command,  1) ; 
}
 
void SSD1306_setBrightness (unsigned char Brightness) 
{
   SSD1306_sendCommand (SSD1306_Set_Brightness_Cmd) ; 
   SSD1306_sendCommand (Brightness) ; 
}
 
 
void SSD1306_setHorizontalMode () 
{
    addressingMode = HORIZONTAL_MODE; 
    SSD1306_sendCommand (0x20) ;                       // set addressing mode
    SSD1306_sendCommand (0x00) ;                       // set horizontal addressing mode
}
 
void SSD1306_clearDisplay () 
{
    unsigned char i,  j; 
    SSD1306_sendCommand (SSD1306_Display_Off_Cmd) ;      // display off
    for (j=0;  j "8;  j++) 
    {    
        SSD1306_setTextXY (j,  0) ;     
        {
            for (i=0;  i "16;  i++)   // clear all columns
            {
                SSD1306_putChar (' ') ;     
            }
        }
    }
    SSD1306_sendCommand (SSD1306_Display_On_Cmd) ;      // display on
    SSD1306_setTextXY (0,  0) ;     
}
 
void SSD1306_sendData (unsigned char Data) 
{
    uint8_t sub_addr = SSD1306_Data_Mode; 
    
    if  (ssd1306_i2c_device == NULL)  {
        printf ("SSD1306 I2C device not initialized\r\n") ; 
        return; 
    }
    
    wm_drv_i2c_write (ssd1306_i2c_device,  &ssd1306_i2c_config,  &sub_addr,  1,  &Data,  1) ; 
}

4. 2 LVGL 适配修改

修改 wm_lv_port_disp. c

#include "wm_lv_port_disp. h"
#include  "stdbool. h" 
 
#include "wm_error. h"
#include "freertos/FreeRTOS. h"
#include "freertos/semphr. h"
 
#define LOG_TAG "lvgl_port_oled"
#include "wm_log. h"
 
/*********************
 *      DEFINES
 *********************/
#ifdef MY_DISP_HOR_RES
#undef MY_DISP_HOR_RES
#endif
#ifdef MY_DISP_VER_RES
#undef MY_DISP_VER_RES
#endif
#define MY_DISP_HOR_RES 128   // SSD1306 OLED 水平分辨率
#define MY_DISP_VER_RES 64    // SSD1306 OLED 垂直分辨率
 
/**********************
 *      TYPEDEFS
 **********************/
typedef struct {
    SemaphoreHandle_t lvgl_sem;  /*semaphore to control the timing of next buffer filling*/
} wm_lvgl_ctx_t; 
 
/**********************
 *  STATIC PROTOTYPES
 **********************/
static void disp_init (void) ; 
static void disp_flush (lv_disp_drv_t *disp_drv,  const lv_area_t *area,  lv_color_t *color_p) ; 
 
/**********************
 *  STATIC VARIABLES
 **********************/
static wm_lvgl_ctx_t wm_lvgl_ctx = { 0 }; 
 
#define LVGL_PORT_BUFF_SIZE  (MY_DISP_HOR_RES * MY_DISP_VER_RES)  // 1/8 screen resolution
static lv_color_t lvgl_draw_buff1[LVGL_PORT_BUFF_SIZE]; 
static lv_color_t lvgl_draw_buff2[LVGL_PORT_BUFF_SIZE]; 
 
/**********************
 *   GLOBAL FUNCTIONS
 **********************/
void lv_port_disp_init (void) 
{
    /*-------------------------
     * Initialize your display
     * -----------------------*/
    disp_init () ; 
    printf ("CONFIG_LV_COLOR_DEPTH = %d\n", CONFIG_LV_COLOR_DEPTH) ; 
    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/
    static lv_disp_draw_buf_t draw_buf_dsc; 
    lv_disp_draw_buf_init (&draw_buf_dsc,  lvgl_draw_buff1,  lvgl_draw_buff2, 
                          LVGL_PORT_BUFF_SIZE) ;  /*Initialize the display buffer*/
 
    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/
    static lv_disp_drv_t disp_drv;  /*Descriptor of a display driver*/
    lv_disp_drv_init (&disp_drv) ;    /*Basic initialization*/
 
    /*Set the resolution of the display*/
    disp_drv. hor_res = MY_DISP_HOR_RES; 
    disp_drv. ver_res = MY_DISP_VER_RES; 
 
    /*Used to copy the buffer's content to the display*/
    disp_drv. flush_cb = disp_flush; 
    /*Set a display buffer*/
    disp_drv. draw_buf = &draw_buf_dsc; 
    disp_drv. full_refresh = 1; 
    /*Finally register the driver*/
    lv_disp_drv_register (&disp_drv) ; 
        
    wm_log_info ("LVGL with SSD1306 OLED display initialized") ; 
}
/**********************
 *   STATIC FUNCTIONS
 **********************/
extern void SSD1306_sendCommand (unsigned char command) ; 
extern void SSD1306_setHorizontalMode () ; 
extern void SSD1306_sendData (unsigned char Data) ; 
/*Initialize your display and the required peripherals. */
static void disp_init (void) 
{
    // 创建 LVGL 使用的信号量
    wm_lvgl_ctx. lvgl_sem = xSemaphoreCreateCounting (1,  0) ; 
    wm_log_info ("SSD1306 OLED display initialized") ; 
}
volatile bool disp_flush_enabled = true; 
/* Enable updating the screen  (the flushing process)  when disp_flush ()  is called by LVGL
 */
void disp_enable_update (void) 
{
    disp_flush_enabled = true; 
}
/* Disable updating the screen  (the flushing process)  when disp_flush ()  is called by LVGL
 */
void disp_disable_update (void) 
{
    disp_flush_enabled = false; 
}
/*Flush the content of the internal buffer the specific area on the display
 *You can use DMA or any hardware acceleration to do this operation in the background but
 *'lv_disp_flush_ready () ' has to be called when finished. */
static void disp_flush (lv_disp_drv_t *disp_drv,  const lv_area_t *area,  lv_color_t *color_p) 
{
    // 如果禁用了显示更新,  直接返回
    if  (! disp_flush_enabled)  {
        lv_disp_flush_ready (disp_drv) ; 
        return; 
    }
    
    // 计算要更新的区域的宽度和高度
    uint16_t width = area-" x2 - area-" x1 + 1; 
    uint16_t height = area-" y2 - area-" y1 + 1; 
    
    // 由于 SSD1306 是单色显示屏,  我们需要将 LVGL 的彩色数据转换为单色
    // 这里假设 lv_color_t 是 16 位色彩
    uint8_t page_start = area-" y1 / 8; 
    uint8_t page_end =  (area-" y2 + 7)  / 8; 
    
    // 设置水平模式更容易按区域更新
    SSD1306_setHorizontalMode () ; 
    
    // 遍历每一页  (每页 8 个像素行)  
    for  (uint8_t page = page_start;  page  " page_end;  page++)  {
        // 设置 OLED 显示的起始地址
        SSD1306_sendCommand (0xB0 + page) ;   // 设置页地址
        SSD1306_sendCommand (0x00 +  (area-" x1 & 0x0F) ) ;   // 设置低位列地址
        SSD1306_sendCommand (0x10 +  ( (area-" x1 " "  4)  & 0x0F) ) ;   // 设置高位列地址
        
        // 处理这一页的每一列
        for  (uint16_t col = 0;  col  " width;  col++)  {
            uint8_t data = 0; 
            
            // 组合 8 个垂直像素到一个字节
            for  (uint8_t bit = 0;  bit  " 8;  bit++)  {
                uint16_t y = page * 8 + bit; 
                if  (y " = area-" y1 && y  "= area-" y2)  {
                    uint16_t index =  (y - area-" y1)  * width + col; 
                    // 将 LVGL 的颜色值转换为单色  (简单阈值处理)  
                    if  (color_p[index]. full "  0)  {
                        data |=  (1  " " bit) ; 
                    }
                }
            }
            
            // 发送数据到 OLED
            SSD1306_sendData (~data) ; 
        }
    }
    
    // 通知 LVGL 显示刷新完成
    xSemaphoreGive (wm_lvgl_ctx. lvgl_sem) ; 
    
    /*IMPORTANT! ! ! 
     *Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready (disp_drv) ; 
}

修改 lvgl_user_config. h 如下

/**
 * @file lvgl_user_config. h
 * Include all user config setting for LVGL related headers
 */
 
#ifndef LVGL_USER_CONFIG_H
#define LVGL_USER_CONFIG_H
 
#include "wm_drv_tft_lcd_cfg. h"
 
#ifdef __cplusplus
extern "C" {
#endif
 
/*********************
 *      DEFINES
 *********************/
 
/*Indicate the exact LCD device name as defined in the device table and wm_drv_tft_lcd_cfg. h*/
//#define WM_LVGL_LCD_MODULE_NAME WM_CFG_TFT_LCD_DEVICE_NAME
 
/* actual screen width,  it must equals to the x-resolution in the wm_drv_tft_lcd_cfg. h if no rotation */
//#define MY_DISP_HOR_RES          ( (WM_CFG_TFT_LCD_ROTATION % 2)  ?  WM_CFG_TFT_LCD_Y_RESOLUTION :  WM_CFG_TFT_LCD_X_RESOLUTION) 
 
/* actual screen height,  it must equals to the y-resolution in the wm_drv_tft_lcd_cfg. h if no rotation */
//#define MY_DISP_VER_RES          ( (WM_CFG_TFT_LCD_ROTATION % 2)  ?  WM_CFG_TFT_LCD_X_RESOLUTION :  WM_CFG_TFT_LCD_Y_RESOLUTION) 
 
/**********************
 *      TYPEDEFS
 **********************/
 
/**********************
 * GLOBAL PROTOTYPES
 **********************/
 
#ifdef __cplusplus
} /*extern "C"*/
#endif
 
#endif /*LVGL_USER_CONFIG_H*/

主函数测试代码

/*
 * W803 平台 SSD1306 OLED 驱动测试程序
 */
#include  "stdio. h" 
#include "W803_SSD1306. h"
#include "wmsdk_config. h"
#include "freertos/FreeRTOS. h"
#include "freertos/task. h"
#include "wm_drv_timer. h"
#include "wm_drv_gpio. h"
#include "wm_drv_sdh_spi. h"
#include "wm_utils. h"
 
#include "lvgl. h"
#include "wm_lv_port_disp. h"
 
#define LOG_TAG "lvgl_example"
#include "wm_log. h"
 
#include "wm_log. h"
 
#define WM_LVGL_TASK_STACK              (1024) 
#define WM_LVGL_TASK_PRIO               (configMAX_PRIORITIES - 8) 
 
/* the controller device name which used by LCD,  it must same as the device name defined in device table*/
#define LCD_SPI_CONTROLLER_DEVICE_NAME "sdspi"
 
/* LVGL tick period uint */
#define LV_TICK_PERIOD_MS               (1) 
void wm_lv_create_tick (void) ; 
static void wm_lv_task_entry (void *arg) ; 
int main (void) 
{
    xTaskCreate (wm_lv_task_entry,  "wm_lv_task",  WM_LVGL_TASK_STACK,  NULL,  WM_LVGL_TASK_PRIO,  NULL) ; 
    return 0; 
}
 
// 创建一个简单的 LVGL 界面
static void create_demo_ui (void) 
{
    // 创建标签
    //lv_obj_t *label = lv_label_create (lv_scr_act () ) ; 
    //lv_label_set_text (label,  "W803 SSD1306") ; 
    //lv_obj_align (label,  LV_ALIGN_TOP_MID,  0,  5) ; 
    
    // 创建按钮
    lv_obj_t *btn1 = lv_btn_create (lv_scr_act () ) ; 
    lv_obj_set_pos (btn1,  24,  0) ; 
    lv_obj_set_size (btn1,  100,  20) ; 
    
    // 简化按钮样式以适应单色显示
    //lv_obj_set_style_radius (btn,  0,  0) ;                // 移除圆角
    lv_obj_set_style_shadow_width (btn1,  0,  0) ;          // 移除阴影
    lv_obj_set_style_bg_grad_dir (btn1,  LV_GRAD_DIR_NONE,  0) ;  // 移除渐变
    
    // 设置高对比度颜色
    lv_obj_set_style_bg_color (btn1,  lv_color_black () ,  0) ;     // 背景设为黑色
    lv_obj_set_style_border_color (btn1,  lv_color_white () ,  0) ;  // 边框设为白色
    lv_obj_set_style_border_width (btn1,  1,  0) ;                // 确保边框可见
    
    // 在按钮上创建标签
    lv_obj_t *btn1_label = lv_label_create (btn1) ; 
    lv_label_set_text (btn1_label,  "Button1") ; 
    lv_obj_center (btn1_label) ; 
    lv_obj_set_style_text_font (btn1_label,  &lv_font_montserrat_12,  0) ;  // 设置为 16 号字体
    lv_obj_set_style_text_color (btn1_label,  lv_color_white () ,  0) ;  // 确保文本为白色
    
    // 创建按钮
    lv_obj_t *btn2 = lv_btn_create (lv_scr_act () ) ; 
    lv_obj_set_pos (btn2,  24,  32) ; 
    lv_obj_set_size (btn2,  100,  20) ; 
    
    // 简化按钮样式以适应单色显示
    lv_obj_set_style_radius (btn2,  0,  0) ;                // 移除圆角
    lv_obj_set_style_shadow_width (btn2,  0,  0) ;          // 移除阴影
    lv_obj_set_style_bg_grad_dir (btn2,  LV_GRAD_DIR_NONE,  0) ;  // 移除渐变
    
    // 设置高对比度颜色
    lv_obj_set_style_bg_color (btn2,  lv_color_white () ,  0) ;     // 背景设为黑色
    lv_obj_set_style_border_color (btn2,  lv_color_black () ,  0) ;  // 边框设为白色
    lv_obj_set_style_border_width (btn2,  1,  0) ;                // 确保边框可见
    
    // 在按钮上创建标签
    lv_obj_t *btn2_label = lv_label_create (btn2) ; 
    lv_label_set_text (btn2_label,  "Button2") ; 
    lv_obj_set_style_text_font (btn2_label,  &lv_font_montserrat_16,  0) ;  // 设置为 16 号字体
    lv_obj_center (btn2_label) ; 
    lv_obj_set_style_text_color (btn2_label,  lv_color_black () ,  0) ;  // 确保文本为白色
}
 
#if LV_USE_LOG
void wm_lv_log_cb (const char *buf) 
{
    wm_log_info ("%s",  buf) ; 
}
#endif
 
static void wm_lv_task_entry (void *arg) 
{
    //wm_lcd_init () ; 
    printf ("wm_lv_task_entry\n") ; 
     // 初始化 SSD1306
     SSD1306_init () ; 
     // 清屏并设置亮度
     SSD1306_clearDisplay () ; 
     SSD1306_setBrightness (255) ;  // 最大亮度
     printf ("SSD1306_init\n") ; 
#if LV_USE_LOG
    lv_log_register_print_cb (wm_lv_log_cb) ; 
#endif
 
    printf ("lv_init\n") ; 
    lv_init () ; 
    printf ("wm_lv_create_tick\n") ; 
    wm_lv_create_tick () ; 
    printf ("lv_port_disp_init\n") ; 
    lv_port_disp_init () ; 
 
/* Enable one demo scenario in lv_confs. h at a time to prevent conflicts */
#if LV_USE_DEMO_BENCHMARK
    lv_demo_benchmark_set_finished_cb (&on_benchmark_finished) ; 
 
    lv_demo_benchmark_set_max_speed (true) ; 
 
    lv_demo_benchmark () ; 
#endif
 
#if LV_USE_DEMO_STRESS
    lv_demo_stress () ; 
#endif
 
#if LV_USE_DEMO_MUSIC
    lv_demo_music () ; 
#endif
 
#if LV_USE_DEMO_WIDGETS
    lv_demo_widgets () ; 
#endif
    printf ("create_demo_ui\n") ; 
    create_demo_ui () ; 
    while  (1)  {
        lv_task_handler () ; 
        vTaskDelay (pdMS_TO_TICKS (10) ) ; 
    }
    /* deinit once loop break */
    vTaskDelete (NULL) ; 
}
 
/* LVGL Timer Porting */
static void wm_lv_tick_timer_irq (void *arg) 
{
    lv_tick_inc (LV_TICK_PERIOD_MS) ; 
}
 
 
void wm_lv_create_tick (void) 
{
    int err                      = 0; 
    wm_device_t *timer0_dev      = NULL; 
    wm_drv_timer_cfg_t timer_cfg = { 0 }; 
 
    if  (NULL ==  (timer0_dev = wm_drv_timer_init ("timer0") ) )  {
        wm_log_error ("timer init failed") ; 
    }
 
    if  (WM_ERR_SUCCESS ! =  (err = wm_drv_timer_register_callback (timer0_dev,  wm_lv_tick_timer_irq,  timer0_dev) ) )  {
        wm_log_error ("timer register err") ; 
    }
 
    timer_cfg. unit        = WM_HAL_TIMER_UNIT_MS; 
    timer_cfg. auto_reload = true; 
    timer_cfg. period      = LV_TICK_PERIOD_MS; 
    wm_drv_timer_start (timer0_dev,  timer_cfg) ; 
}
 
/* LVGL Task Related */
#if LV_USE_DEMO_BENCHMARK
static void on_benchmark_finished (void) 
{
    disp_enable_update () ; 
}
#endif

5, 编译与烧录

5. 1 编译

选择 mian. c 后鼠标右击-" WM_IOD_SDK-" build, 编译完成后可在 build 目录下生成 fls 烧写文件.

5. 2 烧写

使用官方下载工具烧写. 如下图:

工具烧写. jpg

6, 运行效果

lvgl 驱动 OLED 显示按钮. png

0 条评论

发布
问题