이제 생성된 인스턴스들이 어떻게 사용되는지 살펴보겠습니다!
//! Blinks an LED
#![no_std]
#![no_main]
extern crate cortex_m;
#[macro_use]
extern crate cortex_m_rt as rt;
extern crate cortex_m_semihosting as sh;
extern crate panic_semihosting;
extern crate stm32l4xx_hal as hal;
// #[macro_use(block)]
// extern crate nb;
use crate::hal::delay::Delay;
use crate::hal::prelude::*;
use crate::rt::entry;
use crate::rt::ExceptionFrame;
use crate::sh::hio;
use core::fmt::Write;
#[entry]
fn main() -> ! {
let mut hstdout = hio::hstdout().unwrap();
writeln!(hstdout, "Hello, world!").unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
let dp = hal::stm32::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain(); // .constrain();
let mut rcc = dp.RCC.constrain();
let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
// Try a different clock configuration
let clocks = rcc.cfgr.hclk(8.MHz()).freeze(&mut flash.acr, &mut pwr);
// let clocks = rcc.cfgr
// .sysclk(64.MHz())
// .pclk1(32.MHz())
// .freeze(&mut flash.acr);
// let mut gpioc = dp.GPIOC.split(&mut rcc.ahb2);
// let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.afrh);
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
let mut led = gpiob
.pb3
.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
let mut timer = Delay::new(cp.SYST, clocks);
loop {
// block!(timer.wait()).unwrap();
timer.delay_ms(1000_u32);
led.set_high();
// block!(timer.wait()).unwrap();
timer.delay_ms(1000_u32);
led.set_low();
}
}
#[exception]
unsafe fn HardFault(ef: &ExceptionFrame) -> ! {
panic!("{:#?}", ef);
}
let clocks = rcc.cfgr.hclk(8.MHz()).freeze(&mut flash.acr, &mut pwr);
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
let mut led = gpiob
.pb3
.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
let mut timer = Delay::new(cp.SYST, clocks);
위 코드를 통해 flash, rcc, pwr이 다른 peripheral을 초기화하거나 어플리케이션에서 특정 기능을 사용하기 위해 생성한 인스턴스라는 것을 알 수 있습니다.
rcc : reset and clock
pwr : power
let clocks = rcc.cfgr.hclk(8.MHz()).freeze(&mut flash.acr, &mut pwr);
clock을 사용하기 위해 rcc의 config 레지스터에 접근해서 AHB 버스의 clock 속도를 설정하고 있습니다. 여기서 파라미터로 8.MHz()를 사용하고 있는데, 이는 Rate<u32, NOM, DENOM> 자료형의 데이터를 반환하여 버스 속도를 설정할 수 있도록 합니다.
fuqit 크레이트에 RateExtU32, RateExtU64 트레이트가 각각 u32, u64에 대해서 구현이 되어 있습니다. 프로젝트 내 Cargo.toml에 의존성 정의가 되어 있습니다! 여기에는 fugit = "0.3.5"로 되어 있네요.
다음으로 freeze()는 cfgr에 public 메소드로 정의되어 있습니다. 클럭 설정을 고정시키고 Clocks를 반환합니다.
인자로 ACR과 Pwr을 받아서 사용하고 있는데 내부적으로 ACR은 flash의 latency 관련 설정을 위해서, Pwr은 lse 관련 설정을 위해서 사용되고 있네요, 내부 구현 분석은 생략하겠습니다.
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
전에 constrain()과 잠깐 언급된 split()을 통해 GPIOB의 Parts를 반환 받아 사용하고 있고, 인자로는 AHB 버스를 받아 해당 peripheral을 제어할 때 사용할 버스를 지정합니다.
let mut led = gpiob
.pb3
.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
led는 GPIOB3을 push-pull 출력 방식으로 설정했습니다. 인자로는 GPIOB의 레지스터를 넘겨주어 push-pull 방식으로 동작할 수 있도록 설정합니다.
let mut timer = Delay::new(cp.SYST, clocks);
Delay기능을 사용할 인스턴스를 생성하고, 인자로 시스템 타이머와 위에서 생성한 clocks를 넘겨줍니다.
loop {
// block!(timer.wait()).unwrap();
timer.delay_ms(1000_u32);
led.set_high();
// block!(timer.wait()).unwrap();
timer.delay_ms(1000_u32);
led.set_low();
}
그나마 익숙한 구간입니다 ㅎㅎ loop문에서는 단순하게 led를 켜고 끄는 행위를 반복하고 있습니다.
#[exception]
unsafe fn HardFault(ef: &ExceptionFrame) -> ! {
panic!("{:#?}", ef);
}
cortex_m_rt 크레이트에서 제공하는 예외 기능을 다룰 수 있는 #[exception] 속성입니다. 특징으로는 해당 핸들러는 소프트웨어에 호출될 수 없고 하드웨어에 의해 호출된다는 점이 있습니다.
여기서는 HardFault를 정의하고 있고, 예외 핸들러의 이름은 cortex_m_rt 크레이트에 정의되어 있는대로 사용해야 합니다. 하드웨어가 호출하는 부분이기 때문에 레지스터에 저장되어 있는 벡터 테이블에 맞도록 사용해야하는 것 같습니다.
각 쓰임에 맞도록 핸들러를 사용하면 될 것 같습니다.
현재 코드에 있는 HardFault 핸들러에는 panic을 호출하여 무한 loop에 빠지도록 처리가 되어 있네요!
간단한 예제이지만 rust로 쓰여있다보니 오래걸리고 어려웠습니다만 많이 배운 것 같아서 좋습니다. blink 예제 분석 마치도록 하겠습니다!
잘못된 내용이 있다면 알려주시면 감사하겠습니다 ~~