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ピン1と有機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_SCL | 7 SCL |
D14 (CN5) | I2C1_SDA | 8 SDA_in |
D14 (CN5) | I2C1_SDA | 9 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 で書き込む。
User manual STM32 Nucleo-64 boards (MB1136) - UM1724 ↩︎