445 lines
11 KiB
C
445 lines
11 KiB
C
#include "lcds.h"
|
||
#include "delay.h"
|
||
|
||
#define ST_DISP_ON 0xAF // 显示开
|
||
#define ST_DISP_OFF 0xAE // 显示关
|
||
#define ST_START_LINE 0x40 // 设置Start Line
|
||
#define ST_PAGE_ADDR 0xB0 // 设置Page Address
|
||
#define ST_COL_ADDRM 0x10 // 设置Column Address MSB
|
||
#define ST_COL_ADDRL 0x00 // 设置Column Address LSB
|
||
#define ST_DISP_NORMAL 0xA6 // 正常显示,1-显示
|
||
#define ST_DISP_REVERSE 0xA7 // 反向显示,0-显示
|
||
#define ST_DISP_ALL_OFF 0xA4 // 正常显示
|
||
#define ST_DISP_ALL_ON 0xA5 // 不管RAM内容,全部显示
|
||
#define ST_RESET 0xE2 // 软件复位
|
||
#define ST_BOOST 0x2F // 内部升压
|
||
#define ST_DISP_0 0xC4 // 0度正常显示
|
||
#define ST_DISP_180 0xC2 // 旋转180度显示
|
||
#define ST_SET_EV 0x81 // 对比度控制
|
||
|
||
/*
|
||
驱动使用的数据结构,不对外
|
||
*/
|
||
struct _cog_drv_data
|
||
{
|
||
uint8_t gram[8][COL_DOT_MAX_192];
|
||
|
||
/*刷新区域*/
|
||
uint16_t sx;
|
||
uint16_t ex;
|
||
uint16_t sy;
|
||
uint16_t ey;
|
||
uint16_t disx;
|
||
uint16_t disy;
|
||
};
|
||
|
||
/**
|
||
* @brief 写命令
|
||
* @param {uint8_t} cmd
|
||
* @return {*}
|
||
*/
|
||
static void write_cmd(spi_t *handle, uint8_t cmd)
|
||
{
|
||
handle->interface.u.lcd.write_cmd(handle, cmd);
|
||
}
|
||
|
||
static void write_data(spi_t *handle, uint8_t *data, uint16_t len)
|
||
{
|
||
handle->interface.u.lcd.write_data(handle, data, len);
|
||
}
|
||
/**
|
||
* @brief 设置起始页
|
||
* @param {uint8_t} page
|
||
* @return {*}
|
||
*/
|
||
static void start_page(spi_t *handle, uint8_t page)
|
||
{
|
||
page &= 0x07; // 定义低3 位
|
||
page |= ST_PAGE_ADDR; // 第一页
|
||
write_cmd(handle, page);
|
||
}
|
||
|
||
/**
|
||
* @brief 设置起始列
|
||
* @param {uint8_t} Column
|
||
* @return {*}
|
||
*/
|
||
static void start_column(spi_t *handle, uint8_t column)
|
||
{
|
||
uint8_t temp, cmd;
|
||
|
||
temp = column;
|
||
cmd = (temp & 0x0F); // 定义A3-A0;
|
||
cmd += ST_COL_ADDRL;
|
||
write_cmd(handle, cmd);
|
||
|
||
temp >>= 4;
|
||
cmd = (temp & 0x0F); // 定义A7-A4;
|
||
cmd += ST_COL_ADDRM;
|
||
write_cmd(handle, cmd);
|
||
}
|
||
|
||
/**
|
||
* @brief 设置起始行
|
||
* @param {uint8_t} line
|
||
* @return {*}
|
||
*/
|
||
static void start_line(spi_t *handle, uint8_t line)
|
||
{
|
||
line &= 0x3F; // 定义低6 位
|
||
line |= ST_START_LINE; // 第一行或
|
||
write_cmd(handle, line);
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化,包括硬件初始化和软件初始化
|
||
* @param {lcd_t} *lcd
|
||
* @return {*}
|
||
*/
|
||
static int32_t _init(lcd_t *lcd)
|
||
{
|
||
lcd->info.spi->gpios.rst->set(*lcd->info.spi->gpios.rst);
|
||
delay_tick(50);
|
||
lcd->info.spi->gpios.rst->reset(*lcd->info.spi->gpios.rst);
|
||
delay_tick(50);
|
||
lcd->info.spi->gpios.rst->set(*lcd->info.spi->gpios.rst);
|
||
delay_tick(50);
|
||
lcd->info.on = 0;
|
||
lcd->info.pri = (void *)osel_mem_alloc(sizeof(struct _cog_drv_data));
|
||
uint8_t *p = (uint8_t *)lcd->info.pri;
|
||
osel_memset(p, 0x00, (lcd->info.height / 8) * lcd->info.width);
|
||
|
||
write_cmd(lcd->info.spi, ST_RESET); // 软件复位
|
||
delay_ms(20);
|
||
write_cmd(lcd->info.spi, ST_BOOST); // 内部升压
|
||
delay_ms(20);
|
||
write_cmd(lcd->info.spi, ST_DISP_OFF); // display Off
|
||
lcd->info.on = 0;
|
||
write_cmd(lcd->info.spi, 0xa0);
|
||
delay_ms(20);
|
||
write_cmd(lcd->info.spi, ST_SET_EV);
|
||
write_cmd(lcd->info.spi, 0x79);
|
||
|
||
write_cmd(lcd->info.spi, 0xeb);
|
||
start_line(lcd->info.spi, 0); // display start line=0
|
||
|
||
write_cmd(lcd->info.spi, ST_DISP_NORMAL); // 正常显示,1-显示
|
||
write_cmd(lcd->info.spi, ST_DISP_ALL_OFF); // 正常显示
|
||
|
||
write_cmd(lcd->info.spi, ST_DISP_0); // 0度正常显示
|
||
lcd->info.scandir = 0;
|
||
|
||
// 应用层去清空并显示
|
||
// lcd->driver.color_fill(lcd, 0, lcd->info.width, 0, lcd->info.height, WHITE);
|
||
// lcd->driver.onoff(lcd, 1);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
*@brief refresh_gram
|
||
*@details: 刷新指定区域到屏幕上
|
||
坐标是横屏模式坐标
|
||
*@param[in] uint16_t sc
|
||
uint16_t ec
|
||
uint16_t sp
|
||
uint16_t ep
|
||
*@param[out] 无
|
||
*@retval: static
|
||
*/
|
||
static uint32_t refresh_gram(lcd_t *lcd, uint16_t sc, uint16_t ec, uint16_t sp, uint16_t ep)
|
||
{
|
||
struct _cog_drv_data *drvdata;
|
||
uint8_t i;
|
||
|
||
drvdata = (struct _cog_drv_data *)lcd->info.pri;
|
||
|
||
for (i = sp / 8; i <= ep / 8; i++)
|
||
{
|
||
// 设置页地址。每页是8行。一个画面的64行被分成8个页。我们平常所说的第1页,在LCD驱动IC里是第0页
|
||
start_page(lcd->info.spi, i);
|
||
// 设置列地址的高4位 和列地址的低4位
|
||
start_column(lcd->info.spi, sc);
|
||
|
||
write_data(lcd->info.spi, &(drvdata->gram[i][sc]), (ec - sc + 1));
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 画点
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint16_t} x
|
||
* @param {uint16_t} y
|
||
* @param {uint16_t} color
|
||
* @return {*}
|
||
*/
|
||
static int32_t _draw_point(lcd_t *lcd, uint16_t x, uint16_t y, uint16_t color)
|
||
{
|
||
uint16_t xtmp, ytmp;
|
||
uint16_t page, colum;
|
||
|
||
struct _cog_drv_data *drvdata;
|
||
|
||
drvdata = (struct _cog_drv_data *)lcd->info.pri;
|
||
|
||
if (x > lcd->info.width)
|
||
return -1;
|
||
if (y > lcd->info.height)
|
||
return -1;
|
||
|
||
if (lcd->info.dir == W_LCD)
|
||
{
|
||
xtmp = x;
|
||
ytmp = y;
|
||
}
|
||
else // 如果是竖屏,XY轴跟显存的映射要对调
|
||
{
|
||
xtmp = y;
|
||
ytmp = lcd->info.width - 1 - x;
|
||
}
|
||
|
||
page = ytmp / 8; // 页地址
|
||
colum = xtmp; // 列地址
|
||
|
||
if (color == BLACK)
|
||
{
|
||
drvdata->gram[page][colum] |= (0x01 << (ytmp % 8));
|
||
}
|
||
else
|
||
{
|
||
drvdata->gram[page][colum] &= ~(0x01 << (ytmp % 8));
|
||
}
|
||
|
||
// 设置页地址。每页是8行。一个画面的64行被分成8个页。我们平常所说的第1页,在LCD驱动IC里是第0页
|
||
start_page(lcd->info.spi, page);
|
||
// 设置列地址的高4位 和列地址的低4位
|
||
start_column(lcd->info.spi, colum);
|
||
|
||
write_data(lcd->info.spi, &drvdata->gram[page][colum], 1);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 将一块区域设定为某种颜色
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint16_t} sx
|
||
* @param {uint16_t} ex
|
||
* @param {uint16_t} sy
|
||
* @param {uint16_t} ey
|
||
* @param {uint16_t} color
|
||
* @return {*}
|
||
*/
|
||
static int32_t _color_fill(lcd_t *lcd, uint16_t sx, uint16_t ex, uint16_t sy, uint16_t ey, uint16_t color)
|
||
{
|
||
uint16_t i, j;
|
||
uint16_t xtmp, ytmp;
|
||
uint16_t page, colum;
|
||
|
||
struct _cog_drv_data *drvdata;
|
||
|
||
drvdata = (struct _cog_drv_data *)lcd->info.pri;
|
||
|
||
/*防止坐标溢出*/
|
||
if (sy >= lcd->info.height)
|
||
{
|
||
sy = lcd->info.height - 1;
|
||
}
|
||
if (sx >= lcd->info.width)
|
||
{
|
||
sx = lcd->info.width - 1;
|
||
}
|
||
|
||
if (ey >= lcd->info.height)
|
||
{
|
||
ey = lcd->info.height - 1;
|
||
}
|
||
if (ex >= lcd->info.width)
|
||
{
|
||
ex = lcd->info.width - 1;
|
||
}
|
||
|
||
for (j = sy; j <= ey; j++)
|
||
{
|
||
for (i = sx; i <= ex; i++)
|
||
{
|
||
|
||
if (lcd->info.dir == W_LCD)
|
||
{
|
||
xtmp = i;
|
||
ytmp = j;
|
||
}
|
||
else // 如果是竖屏,XY轴跟显存的映射要对调
|
||
{
|
||
xtmp = j;
|
||
ytmp = lcd->info.width - 1 - i;
|
||
}
|
||
|
||
page = ytmp / 8; // 页地址
|
||
colum = xtmp; // 列地址
|
||
|
||
if (color == BLACK)
|
||
{
|
||
drvdata->gram[page][colum] |= (0x01 << (ytmp % 8));
|
||
}
|
||
else
|
||
{
|
||
drvdata->gram[page][colum] &= ~(0x01 << (ytmp % 8));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
只刷新需要刷新的区域
|
||
坐标范围是横屏模式
|
||
*/
|
||
if (lcd->info.dir == W_LCD)
|
||
{
|
||
refresh_gram(lcd, sx, ex, sy, ey);
|
||
}
|
||
else
|
||
{
|
||
refresh_gram(lcd, sy, ey, lcd->info.width - ex - 1, lcd->info.width - sx - 1);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 填充矩形区域,这个函数是用来填充数据的
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint16_t} sx
|
||
* @param {uint16_t} ex
|
||
* @param {uint16_t} sy
|
||
* @param {uint16_t} ey
|
||
* @param {uint16_t} *color
|
||
* @return {*}
|
||
*/
|
||
static int32_t _fill(lcd_t *lcd, uint16_t x, uint16_t y, uint8_t font_width, uint8_t font_hight, uint8_t *buf)
|
||
{
|
||
uint8_t i, j, n;
|
||
n = y;
|
||
for (i = 0; i < font_hight / 8; i++)
|
||
{
|
||
start_page(lcd->info.spi, x);
|
||
for (j = 0; j < font_width; j++)
|
||
{
|
||
start_column(lcd->info.spi, y);
|
||
write_data(lcd->info.spi, &buf[j + i * font_width], 1);
|
||
y++;
|
||
}
|
||
x++;
|
||
y = n;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint16_t} sx
|
||
* @param {uint16_t} ex
|
||
* @param {uint16_t} sy
|
||
* @param {uint16_t} ey
|
||
* @return {*}
|
||
*/
|
||
static int32_t _prepare_display(lcd_t *lcd, uint16_t sx, uint16_t ex, uint16_t sy, uint16_t ey)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint16_t} *color
|
||
* @param {uint32_t} len
|
||
* @return {*}
|
||
*/
|
||
static void _flush(lcd_t *lcd)
|
||
{
|
||
}
|
||
|
||
/**
|
||
* @brief 显示或关闭
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint8_t} sta
|
||
* @return {*}
|
||
*/
|
||
static int32_t _onoff(lcd_t *lcd, uint8_t sta)
|
||
{
|
||
if (lcd->info.on != sta)
|
||
{
|
||
lcd->info.on = sta;
|
||
if (sta == 1)
|
||
{
|
||
write_cmd(lcd->info.spi, ST_DISP_ON); // display On
|
||
}
|
||
else
|
||
{
|
||
write_cmd(lcd->info.spi, ST_DISP_OFF); // display Off
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置显存扫描方向
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint8_t} scan_dir 0 = 0度扫描,1 = 180度扫描
|
||
* @return {*}
|
||
*/
|
||
void _set_dir(lcd_t *lcd, uint8_t scan_dir)
|
||
{
|
||
if (lcd->info.scandir != scan_dir)
|
||
{
|
||
lcd->info.scandir = scan_dir;
|
||
if (scan_dir == 0)
|
||
{
|
||
write_cmd(lcd->info.spi, ST_DISP_0); // 0度扫描
|
||
}
|
||
else
|
||
{
|
||
write_cmd(lcd->info.spi, ST_DISP_180); // 180度扫描
|
||
}
|
||
}
|
||
}
|
||
|
||
void _full_fill(lcd_t *lcd, uint16_t color)
|
||
{
|
||
uint16_t i;
|
||
uint8_t val;
|
||
val = color > 0 ? 0x00 : 0xff;
|
||
for (i = 0; i < lcd->info.height / 8; i++)
|
||
{
|
||
// 设置页地址。每页是8行。一个画面的64行被分成8个页。我们平常所说的第1页,在LCD驱动IC里是第0页
|
||
start_page(lcd->info.spi, i);
|
||
// 设置列地址的高4位 和列地址的低4位
|
||
start_column(lcd->info.spi, 0);
|
||
|
||
write_data(lcd->info.spi, &val, lcd->info.width);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
* @param {lcd_t} *lcd
|
||
* @param {uint8_t} sta
|
||
* @return {*}
|
||
*/
|
||
void _backlight(lcd_t *lcd, uint8_t sta) {}
|
||
|
||
void lcd_st7525_init(lcd_driver_t *driver)
|
||
{
|
||
driver->init = _init; // 已实现
|
||
driver->set_point = _draw_point; // 已实现
|
||
driver->color_fill = _color_fill; // 已实现
|
||
driver->fill = _fill; // 已实现
|
||
driver->prepare_display = _prepare_display;
|
||
driver->flush = _flush;
|
||
driver->onoff = _onoff; // 已实现
|
||
driver->set_dir = _set_dir;
|
||
driver->backlight = _backlight;
|
||
driver->full_fill = _full_fill;
|
||
}
|