除了显示驱动,其余全部测试通过
This commit is contained in:
@@ -0,0 +1,473 @@
|
||||
#include "st7789v2.h"
|
||||
#include "app_config.h"
|
||||
#include "asm/gpio.h"
|
||||
#include "device/device.h"
|
||||
#include "system/init.h"
|
||||
#include "app_log.h"
|
||||
|
||||
#define LCD_SPI_NAME "spi2"
|
||||
|
||||
#define LCD_RES_PIN IO_PORTA_01
|
||||
#define LCD_CS_PIN IO_PORTA_02
|
||||
#define LCD_DC_PIN IO_PORTA_05
|
||||
#define LCD_BL_PIN IO_PORTA_07
|
||||
|
||||
/* PA6 是 TE 引脚,但当前版本暂未启用 TE 同步 */
|
||||
|
||||
#define LCD_WIDTH 240
|
||||
#define LCD_HEIGHT 320
|
||||
#define LCD_PIXEL_BYTES 3
|
||||
#define LCD_LINE_BYTES (LCD_WIDTH * LCD_PIXEL_BYTES)
|
||||
|
||||
#define LCD_CMD_SWRESET 0x01
|
||||
#define LCD_CMD_SLPIN 0x10
|
||||
#define LCD_CMD_SLPOUT 0x11
|
||||
#define LCD_CMD_INVON 0x21
|
||||
#define LCD_CMD_DISPOFF 0x28
|
||||
#define LCD_CMD_DISPON 0x29
|
||||
#define LCD_CMD_CASET 0x2A
|
||||
#define LCD_CMD_RASET 0x2B
|
||||
#define LCD_CMD_RAMWR 0x2C
|
||||
|
||||
static void *lcd_spi_hdl;
|
||||
static u8 lcd_line_buf[LCD_LINE_BYTES];
|
||||
static OS_MUTEX lcd_mutex;
|
||||
|
||||
struct lcd_init_entry {
|
||||
u8 cmd;
|
||||
u8 len;
|
||||
u8 data[14];
|
||||
};
|
||||
|
||||
/*
|
||||
* B7、BB、C0、C3、C4、E0、E1 与具体玻璃面板有关。
|
||||
* 这里保留原工程参数,量产前必须与模组厂初始化表核对。
|
||||
*
|
||||
* D6/A1 未列入公开 ST7789V2 命令表,因此不放入该序列。
|
||||
*/
|
||||
static const struct lcd_init_entry lcd_init_seq[] = {
|
||||
{0x36, 1, {0x00}},
|
||||
{0x3A, 1, {0x06}}, /* RGB666 */
|
||||
|
||||
{0xB2, 5, {0x0C, 0x0C, 0x00, 0x33, 0x33}},
|
||||
{0xB7, 1, {0x46}},
|
||||
{0xBB, 1, {0x1B}},
|
||||
{0xC0, 1, {0x2C}},
|
||||
{0xC2, 1, {0x01}},
|
||||
{0xC3, 1, {0x0F}},
|
||||
{0xC4, 1, {0x20}},
|
||||
{0xC6, 1, {0x0F}},
|
||||
{0xD0, 2, {0xA4, 0xA1}},
|
||||
|
||||
{0xE0, 14, {
|
||||
0xD0, 0x06, 0x0B, 0x09, 0x08, 0x30, 0x30,
|
||||
0x5B, 0x4B, 0x18, 0x14, 0x14, 0x2B, 0x31
|
||||
}},
|
||||
|
||||
{0xE1, 14, {
|
||||
0xD0, 0x05, 0x0A, 0x0A, 0x07, 0x28, 0x32,
|
||||
0x2C, 0x49, 0x18, 0x13, 0x13, 0x2C, 0x33
|
||||
}},
|
||||
|
||||
/*
|
||||
* Display inversion:面板行列驱动反相。
|
||||
* 它不是简单的软件颜色取反。
|
||||
*/
|
||||
{LCD_CMD_INVON, 0, {0x00}},
|
||||
};
|
||||
|
||||
static int st7789v2_mutex_init(void)
|
||||
{
|
||||
return os_mutex_create(&lcd_mutex);
|
||||
}
|
||||
early_initcall(st7789v2_mutex_init);
|
||||
|
||||
static int lcd_lock(void)
|
||||
{
|
||||
if (!os_mutex_valid(&lcd_mutex)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return os_mutex_pend(&lcd_mutex, 0);
|
||||
}
|
||||
|
||||
static void lcd_unlock(void)
|
||||
{
|
||||
os_mutex_post(&lcd_mutex);
|
||||
}
|
||||
|
||||
static void lcd_delay_ms(u32 ms)
|
||||
{
|
||||
u32 ticks;
|
||||
|
||||
ticks = (ms * OS_TICKS_PER_SEC + 999U) / 1000U;
|
||||
if (ticks == 0) {
|
||||
ticks = 1;
|
||||
}
|
||||
|
||||
os_time_dly(ticks);
|
||||
}
|
||||
|
||||
static void lcd_gpio_output(u32 pin, int value)
|
||||
{
|
||||
gpio_direction_output(pin, value);
|
||||
}
|
||||
|
||||
static void lcd_cs(int value)
|
||||
{
|
||||
lcd_gpio_output(LCD_CS_PIN, value);
|
||||
}
|
||||
|
||||
static void lcd_dc(int value)
|
||||
{
|
||||
lcd_gpio_output(LCD_DC_PIN, value);
|
||||
}
|
||||
|
||||
static void lcd_reset(int value)
|
||||
{
|
||||
lcd_gpio_output(LCD_RES_PIN, value);
|
||||
}
|
||||
|
||||
static void lcd_backlight_on(void)
|
||||
{
|
||||
lcd_gpio_output(LCD_BL_PIN, 1);
|
||||
}
|
||||
|
||||
static void lcd_backlight_off(void)
|
||||
{
|
||||
lcd_gpio_output(LCD_BL_PIN, 0);
|
||||
}
|
||||
|
||||
static int lcd_spi_write_locked(const void *buf, u32 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!lcd_spi_hdl) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 本 SDK 的 SPI dev_write() 使用 0 表示成功,
|
||||
* 非 0 表示失败。
|
||||
*/
|
||||
ret = dev_write(lcd_spi_hdl, (void *)buf, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lcd_write_cmd_raw_locked(u8 cmd)
|
||||
{
|
||||
lcd_dc(0);
|
||||
return lcd_spi_write_locked(&cmd, 1);
|
||||
}
|
||||
|
||||
static int lcd_write_data_raw_locked(const void *data, u32 len)
|
||||
{
|
||||
lcd_dc(1);
|
||||
return lcd_spi_write_locked(data, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* 一个完整命令事务:
|
||||
* CS 低 -> 命令 -> 可选参数 -> CS 高
|
||||
*/
|
||||
static int lcd_send_command_locked(u8 cmd, const u8 *data, u32 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lcd_cs(0);
|
||||
|
||||
ret = lcd_write_cmd_raw_locked(cmd);
|
||||
if (ret == 0 && len != 0) {
|
||||
ret = lcd_write_data_raw_locked(data, len);
|
||||
}
|
||||
|
||||
lcd_cs(1);
|
||||
|
||||
if (ret != 0) {
|
||||
APP_LOG("st7789v2: command 0x%02x failed, ret=%d\n",
|
||||
cmd, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lcd_hardware_reset_locked(void)
|
||||
{
|
||||
lcd_cs(1);
|
||||
lcd_dc(1);
|
||||
|
||||
lcd_reset(1);
|
||||
lcd_delay_ms(10);
|
||||
|
||||
lcd_reset(0);
|
||||
lcd_delay_ms(20);
|
||||
|
||||
lcd_reset(1);
|
||||
lcd_delay_ms(120);
|
||||
}
|
||||
|
||||
static int lcd_controller_init_locked(void)
|
||||
{
|
||||
int ret;
|
||||
u32 i;
|
||||
|
||||
lcd_hardware_reset_locked();
|
||||
|
||||
ret = lcd_send_command_locked(LCD_CMD_SLPOUT, NULL, 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
lcd_delay_ms(120);
|
||||
|
||||
for (i = 0;
|
||||
i < sizeof(lcd_init_seq) / sizeof(lcd_init_seq[0]);
|
||||
i++) {
|
||||
ret = lcd_send_command_locked(
|
||||
lcd_init_seq[i].cmd,
|
||||
lcd_init_seq[i].data,
|
||||
lcd_init_seq[i].len);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = lcd_send_command_locked(LCD_CMD_DISPON, NULL, 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
lcd_delay_ms(20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 调用本函数前 CS 必须已经拉低。
|
||||
*/
|
||||
static int lcd_set_window_raw_locked(u16 x1, u16 y1, u16 x2, u16 y2)
|
||||
{
|
||||
int ret;
|
||||
u8 data[4];
|
||||
|
||||
data[0] = x1 >> 8;
|
||||
data[1] = x1 & 0xff;
|
||||
data[2] = x2 >> 8;
|
||||
data[3] = x2 & 0xff;
|
||||
|
||||
ret = lcd_write_cmd_raw_locked(LCD_CMD_CASET);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lcd_write_data_raw_locked(data, sizeof(data));
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
data[0] = y1 >> 8;
|
||||
data[1] = y1 & 0xff;
|
||||
data[2] = y2 >> 8;
|
||||
data[3] = y2 & 0xff;
|
||||
|
||||
ret = lcd_write_cmd_raw_locked(LCD_CMD_RASET);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lcd_write_data_raw_locked(data, sizeof(data));
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return lcd_write_cmd_raw_locked(LCD_CMD_RAMWR);
|
||||
}
|
||||
|
||||
static int lcd_fill_rgb_locked(u8 red, u8 green, u8 blue)
|
||||
{
|
||||
int ret;
|
||||
u32 x;
|
||||
u32 y;
|
||||
|
||||
if (!lcd_spi_hdl) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* COLMOD=0x06 时为 RGB666。
|
||||
* 每个颜色分量只有高 6 bit 有效。
|
||||
*/
|
||||
red &= 0xfc;
|
||||
green &= 0xfc;
|
||||
blue &= 0xfc;
|
||||
|
||||
for (x = 0; x < LCD_WIDTH; x++) {
|
||||
lcd_line_buf[x * 3 + 0] = red;
|
||||
lcd_line_buf[x * 3 + 1] = green;
|
||||
lcd_line_buf[x * 3 + 2] = blue;
|
||||
}
|
||||
|
||||
/*
|
||||
* 窗口设置和所有像素属于同一个 SPI 事务。
|
||||
* 任意错误发生时,也必须恢复 CS 高电平。
|
||||
*/
|
||||
lcd_cs(0);
|
||||
|
||||
ret = lcd_set_window_raw_locked(
|
||||
0,
|
||||
0,
|
||||
LCD_WIDTH - 1,
|
||||
LCD_HEIGHT - 1);
|
||||
|
||||
if (ret == 0) {
|
||||
lcd_dc(1);
|
||||
|
||||
for (y = 0; y < LCD_HEIGHT; y++) {
|
||||
ret = lcd_spi_write_locked(
|
||||
lcd_line_buf,
|
||||
sizeof(lcd_line_buf));
|
||||
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lcd_cs(1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int st7789v2_open(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lcd_lock();
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (lcd_spi_hdl) {
|
||||
lcd_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
lcd_backlight_off();
|
||||
lcd_cs(1);
|
||||
lcd_dc(1);
|
||||
lcd_reset(1);
|
||||
|
||||
lcd_spi_hdl = dev_open(LCD_SPI_NAME, NULL);
|
||||
if (!lcd_spi_hdl) {
|
||||
APP_LOG("st7789v2: dev_open(%s) failed\n",
|
||||
LCD_SPI_NAME);
|
||||
|
||||
lcd_unlock();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = lcd_controller_init_locked();
|
||||
if (ret != 0) {
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* 背光打开前先清成黑色,避免显示未初始化显存。
|
||||
* 1 MHz 下该操作约需要 1.84 秒。
|
||||
*/
|
||||
ret = lcd_fill_rgb_locked(0, 0, 0);
|
||||
if (ret != 0) {
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
lcd_backlight_on();
|
||||
lcd_unlock();
|
||||
|
||||
APP_LOG("st7789v2: open success\n");
|
||||
return 0;
|
||||
|
||||
open_failed:
|
||||
lcd_backlight_off();
|
||||
lcd_cs(1);
|
||||
|
||||
dev_close(lcd_spi_hdl);
|
||||
lcd_spi_hdl = NULL;
|
||||
|
||||
lcd_unlock();
|
||||
|
||||
APP_LOG("st7789v2: initialization failed, ret=%d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int st7789v2_close(void)
|
||||
{
|
||||
int ret;
|
||||
int tmp;
|
||||
|
||||
ret = lcd_lock();
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
lcd_backlight_off();
|
||||
|
||||
if (!lcd_spi_hdl) {
|
||||
lcd_cs(1);
|
||||
lcd_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = lcd_send_command_locked(LCD_CMD_DISPOFF, NULL, 0);
|
||||
lcd_delay_ms(20);
|
||||
|
||||
tmp = lcd_send_command_locked(LCD_CMD_SLPIN, NULL, 0);
|
||||
if (ret == 0 && tmp != 0) {
|
||||
ret = tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* 使用与 SDK 官方 SleepIn 类似的保守等待时间。
|
||||
*/
|
||||
lcd_delay_ms(120);
|
||||
lcd_cs(1);
|
||||
|
||||
tmp = dev_close(lcd_spi_hdl);
|
||||
lcd_spi_hdl = NULL;
|
||||
|
||||
if (ret == 0 && tmp != 0) {
|
||||
ret = tmp;
|
||||
}
|
||||
|
||||
lcd_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int st7789v2_fill_rgb(u8 red, u8 green, u8 blue)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lcd_lock();
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lcd_fill_rgb_locked(red, green, blue);
|
||||
|
||||
lcd_unlock();
|
||||
|
||||
if (ret != 0) {
|
||||
APP_LOG("st7789v2: fill failed, ret=%d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef ST7789V2_H
|
||||
#define ST7789V2_H
|
||||
|
||||
#include "system/includes.h"
|
||||
|
||||
int st7789v2_open(void);
|
||||
int st7789v2_close(void);
|
||||
int st7789v2_fill_rgb(u8 red, u8 green, u8 blue);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "st7789v2.h"
|
||||
#include "app_log.h"
|
||||
#include "system/includes.h"
|
||||
|
||||
|
||||
static u8 lcd_test_task_created;
|
||||
|
||||
static void lcd_test_color(const char *name, u8 r, u8 g, u8 b)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = st7789v2_fill_rgb(r, g, b);
|
||||
if (ret != 0) {
|
||||
APP_LOG("lcd test: fill %s failed, ret=%d\n",
|
||||
name, ret);
|
||||
} else {
|
||||
APP_LOG("lcd test: %s\n",
|
||||
name);
|
||||
}
|
||||
|
||||
os_time_dly(100);
|
||||
}
|
||||
|
||||
static void st7789v2_test_task(void *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (1) {
|
||||
ret = st7789v2_open();
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
APP_LOG("lcd test: open failed, ret=%d; retrying\n",
|
||||
ret);
|
||||
|
||||
os_time_dly(100);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
lcd_test_color("red", 255, 0, 0);
|
||||
lcd_test_color("green", 0, 255, 0);
|
||||
lcd_test_color("blue", 0, 0, 255);
|
||||
lcd_test_color("white", 255, 255, 255);
|
||||
lcd_test_color("black", 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int st7789v2_test_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (lcd_test_task_created) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = os_task_create(
|
||||
st7789v2_test_task,
|
||||
NULL,
|
||||
20,
|
||||
1000,
|
||||
0,
|
||||
"st7789v2_test");
|
||||
|
||||
if (ret == 0) {
|
||||
lcd_test_task_created = 1;
|
||||
} else {
|
||||
APP_LOG("lcd test: task create failed, ret=%d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef ST7789V2_TEST_H
|
||||
#define ST7789V2_TEST_H
|
||||
|
||||
int st7789v2_test_init(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user