esp32c3 运行 Rust(五) —— 封装 I2C 操作
上篇已经通过 I2C 点亮 SSD1306。让我们封装 SSD1306 操作方法。首先封装 I2C 操作。
环境
-
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 |
创建项目
使用如下命令创建 ssd1306 项目,并修改项目配置
cargo generate --git https://github.com/esp-rs/esp-idf-template cargo
创建细节参见 esp32c3 运行 Rust(〇) 中 创建项目 HelloWorld。
-
安装相关依赖
在
Cargo.toml
中添加相关依赖- embedded-hal 嵌入式系统的硬件抽象层
- esp-idf-hal ESP32[-XX] + ESP-IDF 的
embedded-hal
实现 - anyhow
# ... [dependencies] # ... esp-idf-sys = { version = "0.31.1", features = ["binstart"] } embedded-hal = "1.0.0-alpha.8" # 添加这行 esp-idf-hal = "0.36.0" # 添加这行 anyhow = "1.0.57" # 添加这行 # ...
-
创建
i2c_adapter.rs
定义错误类型
pub enum SSD1306Error { BusWriteErr, OutOfBoundsErr, } pub type SSD1306Result = Result<(), SSD1306Error>;
在
main.rs
中引入mod i2c_adapter; //...
-
定义 I2C 相关 trait
pub trait I2CInterface { /// 写入命令 fn send_cmd(&mut self, cmd: &[u8]) -> SSD1306Result; /// 写入数据 fn send_data(&mut self, buf: &[u8]) -> SSD1306Result; }
-
定义 I2C 接口
pub struct I2CAdapter<I2C> { i2c: I2C, addr: u8, data_prefix: u8, } impl<I2C> I2CAdapter<I2C> where I2C: embedded_hal::i2c::blocking::I2c, { pub fn new(i2c: I2C, addr: u8, data_prefix: u8) -> Self { Self { i2c, addr, data_prefix } } }
-
实现
I2CInterface
traitimpl<I2C> I2CInterface for I2CAdapter<I2C> where I2C: embedded_hal::i2c::blocking::I2c, { fn send_cmd(&mut self, cmd: &[u8]) -> SSD1306Result { let mut buf: [u8; 8] = [0; 0]; buf[1..=cmd.len()].copy_from_slice(&cmd); self.i2c.write(self.addr, &buf).map_err(|_| SSD1306Error::BusWriteErr) } fn send_data(&mut self, data: &[u8]) -> SSD1306Result { if data.is_empty() { return Ok(()); } let mut buf = [0u8; 17]; buf[0] = self.data_prefix; data.chunks(16).try_for_each(|c| { let len = c.len(); buf[1..=len].copy_from_slice(c); self.i2c.write(self.addr, &buf) }).map_err(|_| SSD1306Error::BusWriteErr) } }
-
在入口文件中使用
在
main.rs
中添加如下代码mod i2c_adapter; use std::{ thread, time::Duration, }; use embedded_hal::blocking::i2c::Write; use esp_idf_hal::{i2c, peripherals::Peripherals, prelude::*}; use i2c_adapter::{ I2CInterface, I2CAdapter }; use anyhow; const SSD1306_ADDRESS: u8 = 0x3c; fn main() -> anyhow::Result<()> { //... let peripherals = Peripherals::take().unwrap(); let sda = peripherals.pins.gpio4; let scl = peripherals.pins.gpio5; let i2c = peripherals.i2c0; let config = <i2c::config::MasterConfig as Default>::default().baudrate(100.kHz().into()); let mut i2c = i2c::Master::<i2c::I2C0, _, _>::new(i2c, i2c::MasterPins{sda, scl}, config)?; let i2c = I2CAdapter::new(i2c, SSD1306_ADDRESS, 0x40); i2c.send_cmd(&[0xae]).unwrap(); // 关闭显示 i2c.send_cmd(&[0x00]).unwrap(); // Set lower column address i2c.send_cmd(&[0x10]).unwrap(); // Set higher column address i2c.send_cmd(&[0x40]).unwrap(); // Set display start line i2c.send_cmd(&[0xB0]).unwrap(); // Set page address i2c.send_cmd(&[0x81]).unwrap(); // contrast control i2c.send_cmd(&[0x7f]).unwrap(); // default contrast is 0x7f i2c.send_cmd(&[0xa1]).unwrap(); // Set segment remap i2c.send_cmd(&[0xa6]).unwrap(); // 设置显示模式为正常显示 i2c.send_cmd(&[0xa8]).unwrap(); // Multiplex ratio i2c.send_cmd(&[0x3f]).unwrap(); // Duty = 1/64 i2c.send_cmd(&[0xc8]).unwrap(); // Use remapped COM scan direction i2c.send_cmd(&[0xd3]).unwrap(); // Set display offset i2c.send_cmd(&[0x00]).unwrap(); // No offset i2c.send_cmd(&[0xd5]).unwrap(); // Set display clock division i2c.send_cmd(&[0x80]).unwrap(); // divide ratio i2c.send_cmd(&[0xd9]).unwrap(); // Set pre-charge period i2c.send_cmd(&[0xf1]).unwrap(); i2c.send_cmd(&[0xda]).unwrap(); // Set COM pins i2c.send_cmd(&[0x12]).unwrap(); i2c.send_cmd(&[0xdb]).unwrap(); // Set vcomh deselect level i2c.send_cmd(&[0x40]).unwrap(); i2c.send_cmd(&[0x8d]).unwrap(); // Set charge pump state i2c.send_cmd(&[0x14]).unwrap(); // charge pump enabled i2c.send_cmd(&[0xaf]).unwrap(); // 打开显示 i2c.send_cmd(&[0x20, 0x00]).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(); }
至此已经完成了 I2C 的封装,下篇将进行 SSD1306 的操作的封装。