esp32c3 运行 Rust(六)
上篇已经封装 I2C 操作,让我们开始封装 SSD1306 相关操作指令。
环境
-
OS: Ubuntu 20.04.4 LTS
-
Rust: rustc 1.62.0-nightly
-
IDE: VsCode
-
ESP32C3: ESP-C3-13-Kit 开发板
-
SSD1306: 0.96 寸 OLED 模块
使用到的 GPIO
| GPIO | title |
|---|---|
| GPIO4 | SDA |
| GPIO5 | SCL |
创建项目
使用上篇创建的项目工程。
-
创建
command.rs文件定义命令枚举
pub enum Command { //************************** // 充电泵命令表 ChangePump(bool), //************************** // 基本命令 /// 对比度 Contrast(u8), /// 打开/关闭正显示屏,忽略内存中的数据 OnAll(bool), /// 反转显示 Invert(bool), /// 开启/关闭显示 DisplayOn(bool), //************************** // 滚动命令 /// 水平滚动 HScrollSetup(HScrollDirection, Page, Page, ScrollStep), /// 垂直滚动 VScrollSetup(VHScrollDirection, Page, Page, ScrollStep, u8), /// 开启/关闭滚动 ScrollEnable(bool), /// 设置重置滚动区域 ScrollArea(u8, u8), //************************** // 地址设置命令 /// 设置起始低地址,仅耶模式下可用 LowerColStart(u8), /// 设置起始高地址,仅耶模式下可用 UpperColStart(u8), /// 设置内存地址模式 AddressMode(AddrMode), /// 设置列地址,仅在 AddrMode::Horizontal/AddrMode::Vertical 可用 ColumAddr(u8, u8), /// 设置页地址,仅在 AddrMode::Horizontal/AddrMode::Vertical 可用 PageAddr(Page, Page), /// 设置起始页,仅在 AddrMode::Page 可用 PageStart(Page), //************************** // 硬件配置(分辨率和布局) /// 显示起始行 StartLine(u8), /// 设置区段重绘 SegmentRemap(bool), /// 设置重复率 Multiplex(u8), /// 设置COM输出扫描方向 ComOutputScanDirection(bool), /// 设置显示偏移量 DisplayOffset(u8), /// 设置COM引脚硬件配置 ComPinConfig(bool, bool), //************************** /// 计时和驱动方案设置命令 DisplayClockDivide(u8, u8), /// 设置预充电周期 PreChargePeriod(u8, u8), /// 调整 VCOMH 的输出 VcomhDeselect(VcomhLevel), /// 空操作 Nop, }定义滚动命令相关参数枚举
-
水平滚动方向
pub enum HScrollDirection { LeftToRight = 0, RightToLeft = 1, } -
垂直+水平滚动
pub enum VHScrollDirection { /// 垂直向右滚动 VRight = 0b01, /// 垂直向左滚动 VLeft = 0b10, } -
滚动帧率
pub enum ScrollStep { Frame2 = 0b111, Frame3 = 0b100, Frame4 = 0b101, Frame5 = 0b000, Frame25 = 0b110, Frame64 = 0b001, Frame128 = 0b010, Frame256 = 0b011, }
定义页地址枚举
pub enum Page { Page0 = 0b000, Page1 = 0b001, Page2 = 0b010, Page3 = 0b011, Page4 = 0b100, Page5 = 0b101, Page6 = 0b110, Page7 = 0b111, }定义地址模式枚举
#[derive(Debug, Clone, Copy, PartialEq)] pub enum AddrMode { Horizontal = 0b00, Vertical = 0b01, Page = 0b10, }VCOMH 的输出级别
pub enum VcomhLevel { V65 = 0b000, V77 = 0b010, V83 = 0b011, Auto = 0b100, } -
-
实现将命令写入 SSD1306
impl Command { pub fn send<I2C>(self, iface: &mut I2C) -> SSD1306Result where I2C: I2CInterface { let (data, len) = match self { Self::ChangePump(pump) => ([0x8D, 0x10 | ((pump as u8) << 2), 0, 0, 0, 0, 0], 2), Self::Contrast(val) => ([0x81, val, 0, 0, 0, 0, 0], 2), Self::OnAll(all) => ([0xa4 | (all as u8), 0, 0, 0, 0, 0, 0], 1), Self::Invert(inverse) => ([0xa6 | (inverse as u8), 0, 0, 0, 0, 0, 0], 1), Self::DisplayOn(on) => ([0xae | (on as u8), 0, 0, 0, 0, 0, 0], 1), Self::HScrollSetup(direction, start, end, step) => ([ 0x26 | (direction as u8), 0, start as u8, step as u8, end as u8, 0, 0xff, ], 7), Self::VScrollSetup(direction, start, end, step, offset) => ([ 0x28 | (direction as u8), 0, start as u8, step as u8, end as u8, offset, 0, ], 6), Self::ScrollEnable(enable) =>([0x2e|(enable as u8), 0, 0, 0, 0, 0, 0], 1), Self::ScrollArea(top, bottom) => ([0xa3, top, bottom, 0, 0, 0, 0], 3), Self::LowerColStart(addr) => ([0xf & addr, 0, 0, 0, 0, 0, 0], 1), Self::UpperColStart(addr) => ([0x10 | (0xf & addr), 0, 0, 0, 0, 0, 0], 1), Self::AddressMode(mode) => ([0x20, mode as u8, 0, 0, 0, 0, 0], 2), Self::ColumAddr(start, end) => ([0x21, start, end, 0, 0, 0, 0], 3), Self::PageAddr(start, end) => ([0x22, start as u8, end as u8, 0, 0, 0, 0], 3), Self::PageStart(page) => ([0xb0 | (page as u8), 0, 0, 0, 0, 0, 0], 1), Self::StartLine(line) => ([0x40 | (0x3f & line), 0, 0, 0, 0, 0, 0], 1), Self::SegmentRemap(remap) => ([0xA0 | (remap as u8), 0, 0, 0, 0, 0, 0], 1), Self::Multiplex(ratio) => ([0xa8, ratio, 0, 0, 0, 0, 0], 2), Self::ComOutputScanDirection(rev) => ([0xc0|((rev as u8) << 3), 0, 0, 0, 0, 0, 0], 1), Self::DisplayOffset(offset) => ([0xd3, offset, 0, 0, 0, 0, 0], 2), Self::ComPinConfig(alt, lr) => ([0xda, 0x2 | ((alt as u8) << 4) | ((lr as u8) << 5), 0, 0, 0, 0, 0], 2), Self::DisplayClockDivide(fosc, div) => ([0xd5, (((0xf & fosc) << 4) | (0xf & div)), 0, 0, 0, 0, 0], 2), Self::PreChargePeriod(p1, p2) => ([0xd9, (((0xf & p1) << 4) | (0xf & p2)), 0, 0, 0, 0, 0], 2), Self::VcomhDeselect(level) => ([0xdb, (level as u8) << 4, 0, 0, 0, 0, 0], 2), Self::Nop => ([0xe3, 0, 0, 0, 0, 0, 0], 1), }; iface.send_cmd(&data[..len]).map_err(|_| SSD1306Error::BusWriteErr) } } -
测试命令
修改上篇的
main.rs//... mod command; use command::Command; //... fn main() -> anyhow::Result<()> { //... let mut i2c = I2CAdapter::new(i2c, SSD1306_ADDRESS, 0x40); Command::DisplayOn(false).send(&mut i2c).unwrap(); // 关闭显示 Command::DisplayClockDivide(0x8, 0x0).send(&mut i2c).unwrap(); Command::Multiplex(63).send(&mut i2c).unwrap(); Command::DisplayOffset(0).send(&mut i2c).unwrap(); Command::StartLine(0).send(&mut i2c).unwrap(); Command::ChangePump(true).send(&mut i2c).unwrap(); Command::AddressMode(self.addr_mode).send(&mut i2c).unwrap(); Command::ComPinConfig(true, false).send(&mut i2c).unwrap(); // 旋转 Command::SegmentRemap(true).send(&mut i2c).unwrap(); Command::ComOutputScanDirection(true).send(&mut i2c).unwrap(); // 亮度 Command::PreChargePeriod(1, 0x2).send(&mut i2c).unwrap(); Command::Contrast(0x5f).send(&mut i2c).unwrap(); Command::VcomhDeselect(VcomhLevel::Auto).send(&mut i2c).unwrap(); Command::OnAll(false).send(&mut i2c).unwrap(); Command::Invert(false).send(&mut i2c).unwrap(); Command::ScrollEnable(false).send(&mut i2c).unwrap(); Command::DisplayOn(true).send(&mut i2c).unwrap(); // 清屏 i2c.send_data(&[0u8; 1024]).unwrap(); // 写入数据 i2c.send_data(&[ 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x00, // H 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00, // e 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x00, 0x00, // l 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x00, 0x00, // l 0x00, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, // o 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, 0x00, // W 0x00, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, // o 0x00, 0x00, 0x7C, 0x08, 0x04, 0x00, 0x00, 0x00, // r 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x00, 0x00, // l 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, 0x00, 0x00, // d 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, // ! ]).unwrap(); loop { thread::sleep(Duration::from_millis(1000)); } } -
编译并上传
连接开发板,使用如下命令编译并上传
cargo espflash --release --monitor /dev/ttyUSB0
至此已经完成了 SSD1306 命令的封装,下篇将进行 SSD1306 的初始化的封装。