【得捷电子Follow me第4期】入门任务之二-液晶驱动
[复制链接]
本帖最后由 qinyunti 于 2024-2-15 22:15 编辑
二.液晶驱动
2.1参考
1.adafruit-sharp-memory-display-breakout.pdf
2.LS013B4DN04-3V_FPC-204284.pdf
2.2接线
电源只需要3.3V接VIN,接GND。
信号使用3个输出引脚接CS,DI,CLK
2.3显存映射
分辨率144x168,注意这里不要搞反了否则显示会不对。
V垂直方向对应Line地址144x168就是168.
H水平方向对应P,一个字节对应8列,144列需要144/8个字节。
需要显存大小是144*168/8字节。
Line地址如下映射,注意对应二进制从1开始,以下示例是96行,我们这里实际是168行:
2.4时序
操作参考1文档的6.5章节。
两个重点参数帧率1~60HZ,时钟频率最大1MHz。
其他重点时序参数
tsSCS最小6uS SCS建立时间
thSCS最小2uS SCS保持时间
tsSI最小380nS 数据建立时间
thSI最小440nS 数据保持时间
twSCLKL 最低450nS 时钟低时间
twSCLKL 最低450nS 时钟高时间
SCS高有效,SCLK低时准备数据,上升沿设备采样数据。
关注以下操作,显示单行,多行和清除显示,只需要这几个操作即可,前者将存在控制器的显存刷新到液晶显示,后者清除显示为白色。
2.4.1显示单行6-5-1 Data update mode (1 line)
2.4.2显示多行6-5-2 Data Update Mode (Multiple Lines)
2.4.3清除显示6-5-4 All Clear Mode
2.5驱动
2.5.1 数据结构和接口
定义基本的IO操作接口
typedef void (*ls013_io_cs_set)(uint8_t level); /**< CS输出接口 */
typedef void (*ls013_io_di_set)(uint8_t level); /**< DI输出接口 */
typedef void (*ls013_io_clk_set)(uint8_t level); /**< SCK输出接口 */
typedef void (*ls013_io_delay)(uint32_t ns); /**< 延时接口 */
typedef void (*ls013_io_init)(void); /**< 初始化 */
typedef void (*ls013_io_deinit)(void); /**< 解除初始化 */
/** \struct ls013_dev_st
* 设备接口结构体
*/
typedef struct
{
ls013_io_cs_set cs_set; /**< CS输出接口 */
ls013_io_di_set di_set; /**< DI输出接口 */
ls013_io_clk_set clk_set; /**< SCK输出接口 */
ls013_io_delay delay; /**< 延时接口 */
ls013_io_init init; /**< 初始化 */
ls013_io_deinit deinit; /**< 解除初始化 */
} ls013_dev_st;
定义对外接口
/**
* \fn ls013_dis
* 显示一帧
* \param[in] dev \ref ls013_dev_st 设备指针
* \param[in] buffer 帧缓存
* \param[in] h 水平像素点
* \param[in] v 垂直像素点
*/
void ls013_dis(ls013_dev_st* dev, uint8_t* buffer, uint8_t h, uint8_t v);
/**
* \fn ls013_clr
* 清除显示
* \param[in] dev \ref ls013_dev_st 设备指针
*/
void ls013_clr(ls013_dev_st* dev);
/**
* \fn ls013_init
* 初始化
* \param[in] dev \ref ls013_dev_st 设备指针
*/
void ls013_init(ls013_dev_st* dev);
/**
* \fn ls013_deinit
* 解除初始化
* \param[in] dev \ref ls013_dev_st 设备指针
*/
void ls013_deinit(ls013_dev_st* dev);
定时参数宏
/**
* 时序宏定义
* tsSCS最小6uS SCS建立时间
* thSCS最小2uS SCS保持时间
* tsSI最小380nS 数据建立时间
* thSI最小440nS 数据保持时间
* twSCLKL 最低450nS 时钟低时间
* twSCLKL 最低450nS 时钟高时间
* twSCSL 最低2uS SCS低时间
* twSCSH 最低24/136uS SCS高时间
*/
#define TSSCS 6000
#define THSCS 2000
#define TSSI 380
#define THSI 440
#define TWSCLKL 450
#define TWSCLKH 450
#define TWSCSL 2000
#define TWSCSH 24000
/**
* 命令宏定义
* M2-M1-M0的组合
*/
#define CMD_ALL_CLR 0x04 /* M2 */
#define CMD_NO_CLR 0x00
#define CMD_VCOM_H 0x02 /* M1 */
#define CMD_VCOM_L 0x00
#define CMD_MODE_UPDATE 0x01 /* M0 */
#define CMD_MODE_DISPLAY 0x00
发送字节实现
/**
* \fn ls013_write_byte
* 发送一个字节
*/
static void ls013_write_byte(ls013_dev_st* dev, uint8_t val)
{
/* 默认SCLK为0 */
dev->clk_set(0);
for(int i=0; i<8; i++)
{
if((val & 0x01) != 0)
{
dev->di_set(1);
}
else
{
dev->di_set(0);
}
if(dev->delay != 0)
{
dev->delay(TWSCLKL); /* 足够数据建立时间 TWSCLKL>TSSI */
}
dev->clk_set(1); /* SCK上升沿触发对方采样 */
if(dev->delay != 0)
{
dev->delay(TWSCLKH); /* 足够数据保持时间 TWSCLKH>THSI */
}
/* 恢复SCLK为0 */
dev->clk_set(0);
val >>= 1; /* 准备下一位, 低位在前发送 */
}
}
2.5.2设置模式实现
/**
* \fn ls013_set_mode
* 设置m2-m1-m0
* \param[in] dev \ref ls013_dev_st 设备指针
* \param[in] mode m2-m1-m0的组合
*/
static void ls013_set_mode(ls013_dev_st* dev, uint8_t mode)
{
if(dev == 0)
{
return;
}
if((dev->clk_set == 0) || (dev->cs_set == 0) || (dev->di_set == 0))
{
return;
}
dev->cs_set(1); /* 拉高SCS */
if(dev->delay != 0)
{
dev->delay(TSSCS); /* 等待TSSCS */
}
ls013_write_byte(dev,mode); /* M2-M1-M0 */
ls013_write_byte(dev,0x00); /* dummy */
if(dev->delay != 0)
{
dev->delay(TSSCS); /* 等待THSCS */
}
dev->cs_set(0); /* 拉低SCS */
if(dev->delay != 0)
{
dev->delay(TWSCSL); /* 等待TWSCSL */
}
}
2.5.3单行显示
void ls013_dis_line(ls013_dev_st* dev, uint8_t* buffer, uint8_t h, uint8_t line)
{
uint32_t index = 0;
dev->cs_set(1); /* 拉高SCS */
if(dev->delay != 0)
{
dev->delay(TSSCS); /* 等待TSSCS */
}
ls013_write_byte(dev,CMD_NO_CLR | CMD_VCOM_L | CMD_MODE_UPDATE); /* M2-M1-M0 */
ls013_write_byte(dev,line+1); /* line地址,从1开始 */
for(int j=0; j<h/8; j++)
{
ls013_write_byte(dev,buffer[index++]); /* 数据 */
}
ls013_write_byte(dev,0); /* 最后16个CLK dummy */
ls013_write_byte(dev,0);
if(dev->delay != 0)
{
dev->delay(TSSCS); /* 等待THSCS */
}
dev->cs_set(0); /* 拉低SCS */
if(dev->delay != 0)
{
dev->delay(TWSCSL); /* 等待TWSCSL */
}
}
2.5.4多行显示
基于单行更新实现,或者直接使用多行更新。
void ls013_dis(ls013_dev_st* dev, uint8_t* buffer, uint8_t h, uint8_t v)
{
#if 1
uint8_t index = 0;
if(dev == 0)
{
return;
}
if((dev->clk_set == 0) || (dev->cs_set == 0) || (dev->di_set == 0))
{
return;
}
dev->cs_set(1); /* 拉高SCS */
if(dev->delay != 0)
{
dev->delay(TSSCS); /* 等待TSSCS */
}
for(int i=0; i<v; i++)
{
if(i==0)
{
ls013_write_byte(dev,CMD_NO_CLR | CMD_VCOM_L | CMD_MODE_UPDATE); /* M2-M1-M0 */
}
else
{
ls013_write_byte(dev,0);
}
ls013_write_byte(dev,i+1); /* line地址,从1开始 */
for(int j=0; j<h/8; j++)
{
ls013_write_byte(dev,buffer[index++]); /* 数据 */
}
}
ls013_write_byte(dev,0); /* 最后16个CLK dummy */
ls013_write_byte(dev,0);
if(dev->delay != 0)
{
dev->delay(TSSCS); /* 等待THSCS */
}
dev->cs_set(0); /* 拉低SCS */
if(dev->delay != 0)
{
dev->delay(TWSCSL); /* 等待TWSCSL */
}
//ls013_set_mode(dev, CMD_NO_CLR | CMD_VCOM_L | CMD_MODE_DISPLAY);
#else
for(int i=0; i<v; i++)
{
ls013_dis_line(dev, buffer+i*(h/8), h, i);
}
#endif
}
逻辑分析仪测试如下
2.5.5清除显示
void ls013_clr(ls013_dev_st* dev)
{
ls013_set_mode(dev, CMD_ALL_CLR | CMD_VCOM_L | CMD_MODE_DISPLAY);
}
逻辑分析测量如下
2.5.6初始化解除初始化
void ls013_init(ls013_dev_st* dev)
{
if(dev == 0)
{
return;
}
if(dev->init == 0)
{
return;
}
dev->init();
}
void ls013_deinit(ls013_dev_st* dev)
{
if(dev == 0)
{
return;
}
if(dev->deinit == 0)
{
return;
}
dev->deinit();
}
2.5.7 测试
实现ls013_lcd.h
#ifndef LS013_LCD_H
#define LS013_LCD_H
#ifdef __cplusplus
extern "C"{
#endif
#define LCD_H (uint8_t)144
#define LCD_V (uint8_t)168
void lcd_init(void);
void lcd_deinit(void);
void lcd_sync(void);
void lcd_fill(uint8_t val);
void lcd_set_pixel(uint8_t x, uint8_t y, uint8_t val);
uint8_t lcd_get_pixel(uint8_t x, uint8_t y);
#ifdef __cplusplus
}
#endif
#endif
实现ls013_lcd.c
#include "ls013.h"
#include "ls013_lcd.h"
#include "pico/stdlib.h"
#include <string.h>
uint8_t lcd_buffer[LCD_V*LCD_H/8];
void ls013_port_init(void)
{
gpio_init(2);
gpio_set_dir(2, GPIO_OUT);
gpio_put(2, 0);
gpio_init(3);
gpio_set_dir(3, GPIO_OUT);
gpio_put(3, 0);
gpio_init(4);
gpio_set_dir(4, GPIO_OUT);
gpio_put(4, 0);
}
void ls013_port_deinit(void)
{
}
void ls013_port_cs_set(uint8_t val)
{
if(val == 0)
{
gpio_put(2, 0);
}
else
{
gpio_put(2, 1);
}
}
void ls013_port_di_set(uint8_t val)
{
if(val == 0)
{
gpio_put(3, 0);
}
else
{
gpio_put(3, 1);
}
}
void ls013_port_clk_set(uint8_t val)
{
if(val == 0)
{
gpio_put(4, 0);
}
else
{
gpio_put(4, 1);
}
}
void ls013_port_delay(uint32_t ns)
{
sleep_us((ns+999)/1000);
}
ls013_dev_st ls013_dev =
{
.cs_set = ls013_port_cs_set,
.di_set = ls013_port_di_set,
.clk_set = ls013_port_clk_set,
.delay = ls013_port_delay,
.init = ls013_port_init,
.deinit = ls013_port_deinit,
};
void lcd_init(void)
{
memset(lcd_buffer,0,sizeof(lcd_buffer));
ls013_init(&ls013_dev);
ls013_clr(&ls013_dev);
}
void lcd_deinit(void)
{
ls013_deinit(&ls013_dev);
}
void lcd_sync(void)
{
ls013_dis(&ls013_dev, lcd_buffer, LCD_H, LCD_V);
}
void lcd_fill(uint8_t val)
{
memset(lcd_buffer,val,sizeof(lcd_buffer));
ls013_dis(&ls013_dev, lcd_buffer, LCD_H, LCD_V);
}
void lcd_set_pixel(uint8_t x, uint8_t y, uint8_t val)
{
uint8_t byte;
uint8_t offset;
byte = x>>3;
offset = x&0x07;
if(val != 0)
{
lcd_buffer[y*(LCD_H/8)+byte] |= ((uint8_t)1<<offset);
}
else
{
lcd_buffer[y*(LCD_H/8)+byte] &= ~((uint8_t)1<<offset);
}
}
uint8_t lcd_get_pixel(uint8_t x, uint8_t y)
{
uint8_t byte;
uint8_t offset;
byte = x>>3;
offset = x&0x07;
return ((lcd_buffer[y*(LCD_H/8)+byte] & ((uint8_t)1<<offset)) == 0) ? 0 : 1;
}
测试代码如下,黑白刷屏
lcd_init();
while (true) {
for(int i=0; i<LCD_H; i++)
{
for(int j=0; j<LCD_V; j++)
{
lcd_set_pixel(i,j,1);
}
}
lcd_sync();
//lcd_fill(0xFF);
sleep_ms(1000);
for(int i=0; i<LCD_H; i++)
{
for(int j=0; j<LCD_V; j++)
{
lcd_set_pixel(i,j,0);
}
}
lcd_sync();
//lcd_fill(0);
sleep_ms(1000);
}
|