stm32とI2C接続の有機ELディスプレイSO2002AでHello worldした。

stm32 nucleo board(NUCLEO-F302R8)で「有機ELキャラクタディスプレイモジュール 20×2行 黄色」にHello Worldを表示してみました。

stm32 nucleo board(NUCLEO-F302R8)で「有機ELキャラクタディスプレイモジュール 20×2行 黄色」にHello Worldを表示してみました。

今回は20文字2行のディスプレイSO2002Aを使うが, 同じシリーズに16文字2行のディスプレイSO1602Aがある。

確認していないがSO1602Aでもピンの差し替えで同じように使えると思う。

用意する物

  • STM32 Nucleo-64 development board with STM32F302R8 MCU
    NUCLEO-F302R8 - STMicroelectronics
  • 有機ELキャラクタディスプレイモジュール 20×2行 黄色 SO2002AWYB-UC-WB-U
    akizukidenshi.com/catalog/g/gP-08279/
    P-08279 秋月電子通商
  • ブレッドボードとワイヤ

接続

stm32と有機ELキャラクタディスプレイモジュールのインターフェースはI2Cで接続するので、 NUCLEO-F302R8のarduinoコネクタにあるハードウェアI2Cピンpinoutと有機ELキャラクタディスプレイモジュールのピンをこのように接続する。

I2Cバス SCLとSDA には 適当な値の抵抗でプルアップしておく事。ここでは手元にあった抵抗器 2.7kでプルアップした。

NUCLEO-F302R8機能SO2002A
GND (CN6)-1 VSS
3V3 (CN6)-2 VDD
GND (CN6)-3 /CS
3V3 (CN6)-4 SA0
--5 NC
--6 NC
D15 (CN5)I2C1_SCL7 SCL
D14 (CN5)I2C1_SDA8 SDA_in
D14 (CN5)I2C1_SDA9 SDA_out
--10-14 NC

接続

src/main.rs

今回はI2Cを400kHzで動かしてみた。

#![no_main]
#![no_std]

#[allow(unused_extern_crates)]
extern crate panic_halt;

extern crate cortex_m;
extern crate cortex_m_rt;
extern crate cortex_m_semihosting;

extern crate embedded_hal;
extern crate stm32f30x_hal as hal;

use hal::delay::Delay;
use hal::i2c::I2c;
use hal::prelude::*;
use hal::stm32f30x;

use cortex_m_rt::entry;

// 0 1 1 1 1 0 SA0 (SA0 pin is default Hi)
// 0 1 1 1 1 0 1
// 011 1101 = (2+1)*16 + (8+4+1)*1 = 0x3d
const OLED_ADDR: u8 = 0x3d;

const USER_CHARACTERS: [[u8; 8]; 7] = [
    // character code 0x00
    [
        0b00000000, // line 1
        0b00000000, // line 2
        0b00000000, // line 3
        0b00000000, // line 4
        0b00000000, // line 5
        0b00000000, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
    // character code 0x01
    [
        0b00000000, // line 1
        0b00000000, // line 2
        0b00000000, // line 3
        0b00000000, // line 4
        0b00000000, // line 5
        0b00011111, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
    // character code 0x02
    [
        0b00000000, // line 1
        0b00000000, // line 2
        0b00000000, // line 3
        0b00000000, // line 4
        0b00011111, // line 5
        0b00011111, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
    // character code 0x03
    [
        0b00000000, // line 1
        0b00000000, // line 2
        0b00000000, // line 3
        0b00011111, // line 4
        0b00011111, // line 5
        0b00011111, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
    // character code 0x04
    [
        0b00000000, // line 1
        0b00000000, // line 2
        0b00011111, // line 3
        0b00011111, // line 4
        0b00011111, // line 5
        0b00011111, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
    // character code 0x05
    [
        0b00000000, // line 1
        0b00011111, // line 2
        0b00011111, // line 3
        0b00011111, // line 4
        0b00011111, // line 5
        0b00011111, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
    // character code 0x06
    [
        0b00011111, // line 1
        0b00011111, // line 2
        0b00011111, // line 3
        0b00011111, // line 4
        0b00011111, // line 5
        0b00011111, // line 6
        0b00011111, // line 7
        0b00000000, // line 8
    ],
];

#[entry]
fn main() -> ! {
    let cp = cortex_m::Peripherals::take().unwrap();
    let p = stm32f30x::Peripherals::take().unwrap();

    let mut flash = p.FLASH.constrain();
    let mut rcc = p.RCC.constrain();

    let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(&mut flash.acr);

    let mut delay = Delay::new(cp.SYST, clocks);
    let mut gpiob = p.GPIOB.split(&mut rcc.ahb);

    // OLED display I2C interface
    let scl = gpiob.pb8.into_af4(&mut gpiob.moder, &mut gpiob.afrh);
    let sda = gpiob.pb9.into_af4(&mut gpiob.moder, &mut gpiob.afrh);
    let mut display = I2c::i2c1(p.I2C1, (scl, sda), 400.khz(), clocks, &mut rcc.apb1);

    // Wait 100ms until module control, recommended
    delay.delay_ms(100u8);

    // Set CGRAM
    for (i, ch) in USER_CHARACTERS.iter().enumerate() {
        let code = i as u8;
        display
            .write(OLED_ADDR, &[0x00u8, 0x40u8 | (code << 3) | 0])
            .unwrap();
        delay.delay_ms(1u8);
        for v in ch {
            display.write(OLED_ADDR, &[0x40u8, *v]).unwrap();
            delay.delay_ms(1u8);
        }
    }

    // Clear Display
    display.write(OLED_ADDR, &[0x00u8, 0x01u8]).unwrap();
    delay.delay_ms(20u8);
    // Return Home
    display.write(OLED_ADDR, &[0x00u8, 0x02u8]).unwrap();
    delay.delay_ms(2u8);
    // Display ON, Cursor OFF, Blink OFF
    display.write(OLED_ADDR, &[0x00u8, 0x0Cu8]).unwrap();
    delay.delay_ms(2u8);
    // Clear Display
    display.write(OLED_ADDR, &[0x00u8, 0x01u8]).unwrap();
    delay.delay_ms(20u8);
    // Function Set (N = 2, DH = 0, RE = 1, IS = 0)
    display.write(OLED_ADDR, &[0x00u8, 0x2Au8]).unwrap();
    delay.delay_ms(1u8);
    // OLED Characterization (SD = 1)
    display.write(OLED_ADDR, &[0x00u8, 0x79u8]).unwrap();
    delay.delay_ms(1u8);
    // contrast set
    display.write(OLED_ADDR, &[0x00u8, 0x81u8]).unwrap();
    delay.delay_ms(1u8);
    // Set Contrast Control
    display.write(OLED_ADDR, &[0x00u8, 0xFFu8]).unwrap();
    delay.delay_ms(1u8);
    // OLED Characterization (SD = 0)
    display.write(OLED_ADDR, &[0x00u8, 0x78u8]).unwrap();
    delay.delay_ms(2u8);
    // Function Set (N = 2, DH = 0, RE = 0, IS = 0)
    display.write(OLED_ADDR, &[0x00u8, 0x28u8]).unwrap();
    delay.delay_ms(1u8);
 
    //
    // Print to display.
    //
    let message = b"I2C OLED Yellow 20x2Hello World";

    const BUF_SIZE: usize = 1 + 20 * 02; // 1 byte and 20 chars * 02 lines
    let mut write_buf: [u8; BUF_SIZE] = [b' '; BUF_SIZE]; // clear to whitespace

    // first byte is set to D/C bit Hi
    write_buf[0] = 0x40u8;

    // after first bytes
    write_buf[1..=message.len()].copy_from_slice(message);

    // send to display.
    display.write(OLED_ADDR, &write_buf).unwrap();

    // display progress bar
    let progress_bar: [u8; 8] = [b' ', 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
    write_buf[32..32 + progress_bar.len()].copy_from_slice(&progress_bar);

    // Idle loop
    let mut i = 0;
    loop {
        write_buf[40] = progress_bar[i];
        i = i + 1 & 7;
        // send to display.
        display.write(OLED_ADDR, &write_buf).unwrap();
        delay.delay_ms(100_u16);
    }
}

GitHubリポジトリ

https://github.com/ak1211/hello-nucleo-so2002

ビルドと実行

WSL上のUbuntu CLI上で

$ cargo build --release  
$ cargo objcopy --release -- -O binary firmware.bin  

を実行してでできた firmware.bin を STM32CubeProgrammer で書き込む。


  1. User manual STM32 Nucleo-64 boards (MB1136) - UM1724