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烧写文件。
使用官方下载工具烧写。如下图: