diff --git a/.vscode/launch.json b/.vscode/launch.json index 130ad1f0c000ab9d02cc1deeb69508ef50d8cc66..dd5712aaa74f417d8f1129ab96732ac4423bdb3a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -66,6 +66,21 @@ ], "cwd": "${workspaceRoot}" }, + { + "type": "gdb", + "request": "attach", + "name": "Debug usart_clk", + "gdbpath": "/usr/bin/arm-none-eabi-gdb", + "target": ":3333", + "remote": true, + "autorun": [ + "monitor reset init", + "monitor arm semihosting enable", + "file ./target/thumbv7em-none-eabihf/debug/examples/usart_clk", + "load" + ], + "cwd": "${workspaceRoot}" + }, { "type": "gdb", "request": "attach", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5b92a69c579dceef58622eaa08c90cc7b0fadb73..3291c4dd235f68026e468efbb1cb141f8b6d1e5a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -35,6 +35,18 @@ "$rustc" ] }, + { + "taskName": "xargo build --example usart_clk", + "type": "shell", + "command": "xargo build --example usart_clk", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$rustc" + ] + }, { "taskName": "xargo build --example itm", "type": "shell", @@ -50,10 +62,6 @@ "problemMatcher": [ "$rustc" ], - "group": { - "kind": "build", - "isDefault": true - } }, { "type": "shell", diff --git a/examples/usart_clk.rs b/examples/usart_clk.rs new file mode 100644 index 0000000000000000000000000000000000000000..a0c1efce54e266e282e59e5bd42dd611bb84a8c1 --- /dev/null +++ b/examples/usart_clk.rs @@ -0,0 +1,220 @@ +//! Test the USART1 instance +//! +//! Connect the TX and RX pins to run this test +//! +//! PA2 (TX), PA3(RX) on the MCU is connected to the pin header CN3 +//#![deny(unsafe_code)] +#![deny(warnings)] +#![allow(warnings)] +#![feature(proc_macro)] +#![no_std] + +extern crate cortex_m_rtfm as rtfm; +extern crate cortex_m_semihosting as semihosting; +extern crate hal; +#[macro_use] +extern crate nb; +#[macro_use] +extern crate nucleo_64; +extern crate stm32f40x; +use hal::usart::USART; + +use nucleo_64::time::Hertz; + +use rtfm::app; +use core::ptr; +use nucleo_64::apb1; + +const BAUD_RATE: Hertz = Hertz(115_200); + +app! { + device: nucleo_64::stm32f40x, +} + +use stm32f40x::USART2; + +fn serial_init(p: &init::Peripherals) { + // RM0368 6.3.9 + // enable clock to GPIOA, USART2 + p.RCC.ahb1enr.modify(|_, w| w.gpioaen().enable()); + + // enable clock to + p.RCC.apb1enr.modify(|_, w| w.usart2en().bit(true)); + + // PA2 = TX, PA3 = RX + // p.AFIO.mapr.modify(|_, w| w.usart2_remap().clear_bit()); + + + // RM0368 8.4.1 + // set output mode for GPIOA + // PA2 = TX (output mode), PA3 = RX (input mode) + p.GPIOA.moder.modify(|_, w| { + w.moder2() + .variant(stm32f40x::gpioa::moder::MODER15W::ALTERNATEMODE) + .moder3() + .variant(stm32f40x::gpioa::moder::MODER15W::ALTERNATEMODE) + }); + + // we don't care about the speed register atm + // DM00102166 + // AF7, Table 9 + // PA2 and PA3 is connected to USART2 TX and RX respectively + p.GPIOA.afrl.modify(|_, w| w.afrl2().af7().afrl3().af7()); + + // 8N1, stop bit + p.USART2.cr2.write(|w| unsafe { w.stop().bits(0b00) }); + + // baud rate + // let brr :<U::Ticks>= BAUD_RATE.into(); + let brr: apb1::Ticks = BAUD_RATE.invert().into(); + let brr = brr.0; + assert!(brr >= 16, "impossible baud rate"); + + p.USART2.brr.write(|w| unsafe { w.bits(brr) }); + + // disable hardware flow control + // enable DMA TX and RX transfers + p.USART2.cr3.write(|w| { + w.rtse() /* Ready To Send disable */ + .clear_bit() + .ctse() /* Clear To Send disable */ + .clear_bit() + .dmat() /* Enable DMA Transmit */ + .set_bit() + .dmar() /* Enable DMA Receive */ + .set_bit() + }); + + // enable TX, RX; disable parity checking + p.USART2.cr1.write(|w| { + w.ue() /* Usart Enable */ + .set_bit() + .re() /* Receiver Enable */ + .set_bit() + .te() /* Transmitter Enable */ + .set_bit() + .m() /* Word Length, 8 */ + .clear_bit() + .pce() /* Parity Control Enable */ + .clear_bit() + .rxneie() /* Reception Interrupt Enable */ + .clear_bit() + }); +} + +fn clk_init(p: &init::Peripherals) { + // setting up the flash memory latency + // RM0368 8.4.1 (register), 3.4 Table 6 + // we assume 3.3 volt operation, thus 2 cycles for 84mHz + p.FLASH.acr.modify(|_, w| unsafe { w.latency().bits(2) }); + + p.FLASH.acr.modify(|_, w| unsafe { w.latency().bits(2) }); + println!("Init! {:x}", p.FLASH.acr.read().latency().bits()); + + + p.RCC + .cfgr + .modify(|_, w| w.sw0().clear_bit().sw1().clear_bit()); //Switch to HSI + p.RCC.cfgr.modify(|_, w| unsafe { w.ppre1().bits(4) }); //Configure apb1 prescaler = 2 + p.RCC.apb1enr.modify(|_, w| w.pwren().set_bit()); + p.RCC.cr.write(|w| w.pllon().clear_bit()); + + //Enable PLL + // PP PLLN PLLM + // 0b0000 0000 0000 00 01 0 101010000 010000 + // RM0368 6.3.2 + p.RCC + .pllcfgr + .write(|w| unsafe { w.bits(0b00000000000000010101010000010000) }); //Configure PLL + + p.RCC.cr.modify(|_, w| w.pllon().set_bit()); //Enable PLL + + while p.RCC.cr.read().pllrdy().bit_is_clear() {} + + p.RCC.cfgr.modify(|_, w| w.sw0().clear_bit()); //Switch to PLL + p.RCC.cfgr.modify(|_, w| w.sw1().set_bit()); //Switch to PLL + p.RCC.apb2enr.modify(|_, w| w.syscfgen().set_bit()); + + p.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); //Enable GPIOA clock + p.RCC.ahb1enr.modify(|_, w| w.gpioben().set_bit()); //Enable GPIOB clock + + //USART::send(p.USART2, "\n\n\nUSART initialized\n\r"); +} + +#[inline(never)] +fn init(p: init::Peripherals) { + println!("Init!"); + clk_init(&p); + //serial_init(&p); + USART::initialize(p.GPIOA, p.RCC, p.USART2); + + USART::send(p.USART2, "\n\n\nUSART \n\r"); + + loop { + let b = block!(read(p.USART2)).unwrap(); + write(p.USART2, b).unwrap(); + } +} + +// Specialized `Result` type +pub type Result<T> = ::core::result::Result<T, nb::Error<Error>>; + +/// An error +#[derive(Debug)] +pub enum Error { + /// De-synchronization, excessive noise or a break character detected + Framing, + /// Noise detected in the received frame + Noise, + /// RX buffer overrun + Overrun, + #[doc(hidden)] _Extensible, +} + +fn write(usart2: &USART2, byte: u8) -> Result<()> { + let sr = usart2.sr.read(); + + if sr.ore().bit_is_set() { + Err(nb::Error::Other(Error::Overrun)) + } else if sr.nf().bit_is_set() { + Err(nb::Error::Other(Error::Noise)) + } else if sr.fe().bit_is_set() { + Err(nb::Error::Other(Error::Framing)) + } else if sr.txe().bit_is_set() { + // NOTE(write_volatile) see NOTE in the `read` method + unsafe { ptr::write_volatile(&usart2.dr as *const _ as *mut u8, byte) } + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } +} + +fn read(usart2: &USART2) -> Result<u8> { + let sr = usart2.sr.read(); + + if sr.ore().bit_is_set() { + Err(nb::Error::Other(Error::Overrun)) + } else if sr.nf().bit_is_set() { + Err(nb::Error::Other(Error::Noise)) + } else if sr.fe().bit_is_set() { + Err(nb::Error::Other(Error::Framing)) + } else if sr.rxne().bit_is_set() { + // NOTE(read_volatile) the register is 9 bits big but we'll only + // work with the first 8 bits + Ok(unsafe { + ptr::read_volatile(&usart2.dr as *const _ as *const u8) + }) + } else { + Err(nb::Error::WouldBlock) + } +} + +fn idle() -> ! { + // Will never be hit, as + rtfm::bkpt(); + + // Sleep + loop { + rtfm::wfi(); + } +}