W803-Pico 主控为联盛德 W803 芯片, 支持无线 WiFi (IEEE802. 11 b/g/n 协议) , 蓝牙 BT/BLE4. 2 协议. 芯片内置高性能 32 位处理器, 主频达 240MHz.
内置 2MB Flash 以及 288KB RAM.
集成 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 控制器
管脚名称 | 功能说明 |
---|---|
VCC | 3. 3V 电源输入 |
GND | 地线 |
SCL | I2C 时钟线 |
SDA | I2C 数据线 |
W803-Pico ↔ SSD1306 接线表:
W803-Pico 引脚 | SSD1306 引脚 |
---|---|
3. 3V | VCC |
GND | GND |
PA1 | SCL |
PA4 | SDA |
打开 vscode, 打开 wm_iot_sdk 文件夹, wm_iot_sdk2. x 下载地址
工程的搭建和环境配置请参照官网介绍安装测试 W803-Pico 入门指南
在 VSCODE 内终端输入:
cp . \examples\peripheral\i2c -r . \examples\peripheral\i2c_lvgl_ssd1306oled
SDK menuconfig 配置
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) ;
}
修改 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
选择 mian. c 后鼠标右击- WM_IOD_SDK- build, 编译完成后可在 build 目录下生成 fls 烧写文件.
使用官方下载工具烧写. 如下图: