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燒寫文件。
使用官方下載工具燒寫。如下圖: