买了几块 夏普的1.26寸 memory lcd 型号为 LS013B7DH07,这种屏幕显示有点像墨水屏,只是白色不是那纯有点偏银灰的感觉,比普通墨水屏刷新快很多,在不刷新的情况下墨水屏显示更好,需要频繁刷新的情况下memory lcd 是更好的选择。
这个屏的驱动方式与常见的一根cd命令数据线来控制传输命令或数据的方式不同,它是在cs拉高后开始的前几个固定时钟周期内为命令,命令后再跟着传数据。常用的两个就是清屏与整屏刷新,下面就引脚定义与这两个操作记录一下。
引脚如上图, vdda 、vdd 接3.3v, vss、vssa 接地,sclk 时钟信号、si 即 mosi 串口数据,scs 为片选信号拉高有效,disp 为开启显示 拉高显示。
这里说一下串口数据信号采集的简单原理就是如下这个时序图,scs即片置为有效后(具体是选拉高或拉低有效要看具体设备,大部分是拉低有效,这里这个是拉高有效),sclk 在上升沿或下降沿(具体设备而论,有的是上升沿,有的是下降沿,这里是上升沿)是会采集一下si的信号是高还是低,这样就得到一个bit 的值,就是这样连续的采集信号进行数据的传输。
这是传输一个字节的实现方法,通常我们不需要具体实现这个方法,一般可以直接使用spi 外设及程序中对应的类型来处理单个或多个字节的连续传输。
另外在主机端我们需要注意串口传输数据时是高位在前还是低位在前,要能够与其手册定义的格式对应上,如下的代码分别是低位先传与高位先传的区别,具体如何传输看如何处理数据更合适,如使用spi外设需要注意设置其顺序。
//低位先传
fn send_byte(clk: &mut Output, mosi: &mut Output, in_data :u8) {
let mut data: u8 = in_data;
let mut temp: u8 = 0;
for i in 0..8 {
temp = data & 0x01;
if temp == 1 {
mosi.set_high();
} else {
mosi.set_low();
}
clk.set_high();
clk.set_low();
data = data >> 1;
}
}
//如下传输时要低位先传
const DISP_MODE_LSBF:u8 = 0b00000010; // L display mode;
const UPDATE_MODE_LSBF:u8 = 0b00000011; // H memory mode;
//高位先传
fn send_byte(clk: &mut Output, mosi: &mut Output, in_data: u8) {
let mut data = in_data;
for _ in 0..8 {
let temp = data & 0x80;
if temp == 0x80 {
mosi.set_high();
} else {
mosi.set_low();
}
clk.set_high();
clk.set_low();
data = data << 1;
}
}
//如下传输时要高位先传
const DISP_MODE_MSBF:u8 = 0b01000000; // L display mode;
const UPDATE_MODE_MSBF:u8 = 0b11000000; // H memory mode;
这个图内描述了清屏与多行刷新的完整通信周期。
清屏时只发送前16个clk 即两个字节,第三个bit 为高代表清屏。
多行刷新为前16个clk 为命令总分,后8 个clk 为行地址从1开始,后跟144个clk 为这一行的数据,这样一行的数据就处理完了。
开始下一行时先加8个dmy clk 再加 8个clk 为行地址,后跟144 个clk ,这样一直到最后一行,最后一行结束后加16个dmy clk 。
dmy clk 不关心 si 的值 建议为低。
如下是关键程序:
//spi 的参数,频率高于这个值会不稳定,bit_order 设为 LSBFirst
let mut spi_bus = Spi::new(peripherals.SPI2, 1500_u32.kHz(), SpiMode::Mode0)
.with_bit_order(SpiBitOrder::LSBFirst,SpiBitOrder::LSBFirst)
.with_sck(clk)
.with_mosi(mosi);
//如下传输时要低位先传
const DISP_MODE:u8 = 0b00000010; // L display mode;
const UPDATE_MODE:u8 = 0b00000011; // H memory mode;
pub fn clear(&mut self, spi: &mut SPI){
self.cs.set_high();
spi.write( &[0b00000110]);
spi.write( &[0b00000000]);
self.cs.set_low();
}
//刷新
pub fn update_frame(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> {
self.cs.set_high();
spi.write(&[UPDATE_MODE]);
let row_bytes:u32 = self.width() / 8;
for i in 0..self.height() {
spi.write(&[(i+1) as u8]);
for j in 0.. row_bytes{
let index = i*18+j;
let mut temp = data[index as usize];
temp = !temp;
spi.write(&[temp]);
}
spi.write(&[0x00]);
}
spi.write(&[0x00]);
spi.write(&[0x00]);
self.cs.set_low();
Ok(())
}
另外这个屏幕的排线有点特殊普通的fpc 0.3mm 11pin 的座子插不进去,因为排线两侧有两个突出,我是直接用小刀把这两个给切了,对着排线金属针脚插进去的,倒可以用,但不是很方便。
如下的突出: