esp32c3 运行 Rust(四)

444 阅读5分钟

esp32c3 运行 Rust(四)

通过 I2C 点亮 SSD1306。

环境

  • OS: Ubuntu 20.04.4 LTS

  • Rust: rustc 1.62.0-nightly

  • IDE: VsCode

  • ESP32C3: ESP-C3-13-Kit 开发板

    esp32c3.png

    GPIOtitle
    GPIO5SDA
    GPIO6SCL
  • SSD1306: 0.96 寸 OLED 模块

    ssd1306.png

创建项目

使用如下命令创建 ssd1306 项目,并修改项目配置

cargo generate --git https://github.com/esp-rs/esp-idf-template cargo

创建细节参见 esp32c3 运行 Rust(〇)创建项目 HelloWorld

点亮 OLED

  1. 安装相关依赖

    Cargo.toml 中添加相关依赖

    # ...
    [dependencies]
    # ...
    esp-idf-sys = { version = "0.31.1", features = ["binstart"] }
    embedded-hal = "0.2.7" # 添加这行
    esp-idf-hal = "0.36.0" # 添加这行
    anyhow = "1.0.57" #  添加这行
    # ...
    
  2. 导入依赖

    use std::{
        thread,
        time::Duration,
    };
    
    use embedded_hal::blocking::i2c::Write;
    use esp_idf_hal::{i2c, peripherals::Peripherals, prelude::*};
    
    use anyhow;
    
  3. 定义 OLED 地址

    //...
    const SSD1306_ADDRESS: u8 = 0x3c;
    //...
    
  4. 设置 I2C 引脚

    //...
    fn main() -> anyhow::Result<()> {
        //...
        let peripherals = Peripherals::take().unwrap();
        let sda = peripherals.pins.gpio5;
        let scl = peripherals.pins.gpio6;
        //...
    }
    
  5. 设置 I2C

    //...
    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)?;
    //...
    
  6. 初始化 SSD1306

    //...
    i2c.write(SSD1306_ADDRESS, &[0, 0xae])?; // 关闭显示
    i2c.write(SSD1306_ADDRESS, &[0, 0x00])?; // Set lower column address
    i2c.write(SSD1306_ADDRESS, &[0, 0x10])?; // Set higher column address
    i2c.write(SSD1306_ADDRESS, &[0, 0x40])?; // Set display start line
    i2c.write(SSD1306_ADDRESS, &[0, 0xB0])?; // Set page address
    i2c.write(SSD1306_ADDRESS, &[0, 0x81])?; // contrast control
    i2c.write(SSD1306_ADDRESS, &[0, 0x7f])?; // default contrast is 0x7f
    i2c.write(SSD1306_ADDRESS, &[0, 0xa1])?; // Set segment remap
    i2c.write(SSD1306_ADDRESS, &[0, 0xa6])?; // 设置显示模式为正常显示
    i2c.write(SSD1306_ADDRESS, &[0, 0xa8])?; // Multiplex ratio
    i2c.write(SSD1306_ADDRESS, &[0, 0x3f])?; // Duty = 1/64
    i2c.write(SSD1306_ADDRESS, &[0, 0xc8])?; // Use remapped COM scan direction
    i2c.write(SSD1306_ADDRESS, &[0, 0xd3])?; // Set display offset
    i2c.write(SSD1306_ADDRESS, &[0, 0x00])?; // No offset
    i2c.write(SSD1306_ADDRESS, &[0, 0xd5])?; // Set display clock division
    i2c.write(SSD1306_ADDRESS, &[0, 0x80])?; // divide ratio
    i2c.write(SSD1306_ADDRESS, &[0, 0xd9])?; // Set pre-charge period
    i2c.write(SSD1306_ADDRESS, &[0, 0xf1])?;
    i2c.write(SSD1306_ADDRESS, &[0, 0xda])?; // Set COM pins
    i2c.write(SSD1306_ADDRESS, &[0, 0x12])?;
    i2c.write(SSD1306_ADDRESS, &[0, 0xdb])?; // Set vcomh deselect level
    i2c.write(SSD1306_ADDRESS, &[0, 0x40])?;
    i2c.write(SSD1306_ADDRESS, &[0, 0x8d])?; // Set charge pump state
    i2c.write(SSD1306_ADDRESS, &[0, 0x14])?; // charge pump enabled
    i2c.write(SSD1306_ADDRESS, &[0, 0xaf])?; // 打开显示
    i2c.write(SSD1306_ADDRESS, &[0, 0x20, 0x00])?; // 设置显示模式
    //...
    
  7. 清屏

    //...
    println!("clear display");
    let mut empty = vec![0x40];
    empty.extend_from_slice(&[0u8; 1024]);
    i2c.write(SSD1306_ADDRESS, &empty)?;
    //...
    
  8. 显示内容

    //...
    i2c.write(SSD1306_ADDRESS, &[0x40, 
        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, // !
    ])?;
    //...
    
  9. 编译并上传

    连接开发板,使用如下命令编译并上传

    cargo espflash --release --monitor /dev/ttyUSB0
    

完整代码

use std::{thread, time::Duration};

use embedded_hal::i2c::blocking::I2c;
use esp_idf_hal::{i2c, peripherals::Peripherals, prelude::*};

use anyhow;

const SSD1306_ADDRESS: u8 = 0x3c;

fn main() -> anyhow::Result<()> {
  esp_idf_sys::link_patches();
  let peripherals = Peripherals::take().unwrap();
  let sda = peripherals.pins.gpio5;
  let scl = peripherals.pins.gpio6;
  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)?;

  println!("init SSD1306");
  i2c.write(SSD1306_ADDRESS, &[0, 0xae])?; // 关闭显示
  i2c.write(SSD1306_ADDRESS, &[0, 0x00])?; // Set lower column address
  i2c.write(SSD1306_ADDRESS, &[0, 0x10])?; // Set higher column address
  i2c.write(SSD1306_ADDRESS, &[0, 0x40])?; // Set display start line
  i2c.write(SSD1306_ADDRESS, &[0, 0xB0])?; // Set page address
  i2c.write(SSD1306_ADDRESS, &[0, 0x81])?; // contrast control
  i2c.write(SSD1306_ADDRESS, &[0, 0x7f])?; // default contrast is 0x7f
  i2c.write(SSD1306_ADDRESS, &[0, 0xa1])?; // Set segment remap
  i2c.write(SSD1306_ADDRESS, &[0, 0xa6])?; // 设置显示模式为正常显示
  i2c.write(SSD1306_ADDRESS, &[0, 0xa8])?; // Multiplex ratio
  i2c.write(SSD1306_ADDRESS, &[0, 0x3f])?; // Duty = 1/64
  i2c.write(SSD1306_ADDRESS, &[0, 0xc8])?; // Use remapped COM scan direction
  i2c.write(SSD1306_ADDRESS, &[0, 0xd3])?; // Set display offset
  i2c.write(SSD1306_ADDRESS, &[0, 0x00])?; // No offset
  i2c.write(SSD1306_ADDRESS, &[0, 0xd5])?; // Set display clock division
  i2c.write(SSD1306_ADDRESS, &[0, 0x80])?; // divide ratio
  i2c.write(SSD1306_ADDRESS, &[0, 0xd9])?; // Set pre-charge period
  i2c.write(SSD1306_ADDRESS, &[0, 0xf1])?;
  i2c.write(SSD1306_ADDRESS, &[0, 0xda])?; // Set COM pins
  i2c.write(SSD1306_ADDRESS, &[0, 0x12])?;
  i2c.write(SSD1306_ADDRESS, &[0, 0xdb])?; // Set vcomh deselect level
  i2c.write(SSD1306_ADDRESS, &[0, 0x40])?;
  i2c.write(SSD1306_ADDRESS, &[0, 0x8d])?; // Set charge pump state
  i2c.write(SSD1306_ADDRESS, &[0, 0x14])?; // charge pump enabled
  i2c.write(SSD1306_ADDRESS, &[0, 0xaf])?; // 打开显示
  i2c.write(SSD1306_ADDRESS, &[0, 0x20, 0x00])?; // 设置显示模式
    
  println!("clear display");
  let mut empty = vec![0x40];
  empty.extend_from_slice(&[0u8; 1024]);
  i2c.write(SSD1306_ADDRESS, &empty)?;

  println!("Hello world!");
  i2c.write(
    SSD1306_ADDRESS,
    &[ 0x40,
      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, // !
    ],
  )?;
  thread::sleep(Duration::from_millis(1000));
  loop {
    thread::sleep(Duration::from_millis(1000));
  }
}

参考

  1. Embedded Rust on Espressif
  2. embedded-hal
  3. esp-idf-hal