diff --git a/src/main.rs b/src/main.rs index e500874..4b05b6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use rp2040_pac as pac; mod pll; mod resets; +mod uart; #[link_section = ".boot2"] #[used] @@ -26,6 +27,16 @@ fn timestamp() -> u64 { n as u64 } +static GREETING: &str = "\n\r/ Hello fellow rustaceans! Now I talk to \\\r +\\ you from a Raspberry Pico board! /\r + -----------------------------------------\r + \\\r + \\\r + _~^~^~_\r + \\) / o o \\ (/\r + '_ - _'\r + / '-----' \\\n\n\n\n\r"; + fn init( resets: pac::RESETS, watchdog: pac::WATCHDOG, @@ -52,7 +63,8 @@ fn init( | resets::SPI1 | resets::UART0 | resets::UART1 - | resets::USBCTRL), + | resets::USBCTRL + | resets::IO_BANK0), ); // xosc 12 mhz @@ -84,6 +96,28 @@ fn init( pll::PLL::new(pll_sys).configure(1, 1500_000_000, 6, 2); pll::PLL::new(pll_usb).configure(1, 480_000_000, 5, 2); + + // Activate peripheral clock and take external oscillator as input + clocks.clk_peri_ctrl.write(|w| { + w.enable().set_bit(); + w.auxsrc().xosc_clksrc(); + w + }); +} + +fn uart_configure_alternate_functions(p: &pac::IO_BANK0) { + // todo funcsel in pac not implemented for generic access to gpio + // for UART, the funcsel is 2 for all pins, so calling uart0_tx + // on each gp does the trick but is very confusing + + // set GP0 to UART0_TX + p.gpio[0].gpio_ctrl.write(|w| w.funcsel().uart0_tx()); + // set GP1 to UART0_RX + p.gpio[1].gpio_ctrl.write(|w| w.funcsel().uart0_tx()); + // set GP4 to UART1_TX + p.gpio[4].gpio_ctrl.write(|w| w.funcsel().uart0_tx()); + // set GP5 to UART1_RX + p.gpio[5].gpio_ctrl.write(|w| w.funcsel().uart0_tx()); } #[entry] @@ -94,6 +128,17 @@ fn main() -> ! { init(p.RESETS, p.WATCHDOG, p.CLOCKS, p.XOSC, p.PLL_SYS, p.PLL_USB); + uart_configure_alternate_functions(&p.IO_BANK0); + // Peripheral clock is attached to XOSC + const PERI_CLK: u32 = 12_000_000; + let uart0 = uart::UART::new(p.UART0, PERI_CLK); + uart0.configure(115200); + let uart1 = uart::UART::new(p.UART1, PERI_CLK); + uart1.configure(115200); + + uart0.write_blocking(&GREETING.as_bytes()); + uart1.write_blocking(&GREETING.as_bytes()); + let led_pin = 25; loop { diff --git a/src/uart.rs b/src/uart.rs new file mode 100644 index 0000000..0cb5a4f --- /dev/null +++ b/src/uart.rs @@ -0,0 +1,116 @@ +/* +* Uart example in rust for raspberry pico +* +* Simple example to enable blocking write on uart0 +* and uart1 +* +* +* Copyright (c) Siemens AG, 2021 +* +* Authors: +* Dominik Tacke +* +* This work is licensed under the terms of the MIT. See +* the LICENSE-MIT file in the top-level directory. +* +* SPDX-License-Identifier: MIT +*/ + +use core::ops::Deref; + +use rp2040_pac as pac; + +pub struct UART { + inner: T, + clk_base: u32, +} + +impl UART { + pub fn new(inner: T, clk_base: u32) -> Self { + Self { inner, clk_base } + } + + pub fn configure(&self, baudrate: u32) -> u32 { + let u = &self.inner; + // Any LCR writes need to take place before enabling the UART + let baud = self.set_baudrate(baudrate); + self.set_format(8, 1); + + // Enable the UART, both TX and RX + u.uartcr + .write(|w| w.uarten().bit(true).rxe().bit(true).txe().bit(true)); + // Enable FIFOs + u.uartlcr_h + .modify(|r, w| unsafe { w.bits(r.bits()) }.fen().set_bit()); + + // Always enable DREQ signals -- no harm in this if DMA is not listening + u.uartdmacr + .write(|w| w.txdmae().set_bit().rxdmae().set_bit()); + + return baud; + } + + fn set_baudrate(&self, baudrate: u32) -> u32 { + let p = &self.inner; + let baud_rate_div = (8 * self.clk_base) / baudrate; + let mut baud_ibrd = baud_rate_div >> 7; + let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; + + if baud_ibrd == 0 { + baud_ibrd = 1; + baud_fbrd = 0; + } else if baud_ibrd >= 65535 { + baud_ibrd = 65535; + baud_fbrd = 0; + } + + // Load PL011's baud divisor registers + p.uartibrd.write(|w| unsafe { w.bits(baud_ibrd) }); + p.uartfbrd.write(|w| unsafe { w.bits(baud_fbrd) }); + + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here + let lcr_h = p.uartlcr_h.read().bits() | 0x01; + + p.uartlcr_h.write(|w| unsafe { w.bits(lcr_h) }); + + // See datasheet + return (4 * self.clk_base) / (64 * baud_ibrd + baud_fbrd); + } + + fn set_format(&self, data_bits: u8, stop_bit: u8) { + let p = &self.inner; + p.uartlcr_h.write(|w| unsafe { + w.wlen() + .bits(data_bits - 5) + .stp2() + .bit(stop_bit - 1 == 1) + .pen() + .bit(false) + .eps() + .bit(false) + }); + } + + pub fn write_blocking(&self, src: &[u8]) { + let p = &self.inner; + for byte in src { + loop { + if self.is_writable() { + break; + } + } + p.uartdr.write(|w| unsafe { w.bits(*byte as u32) }); + } + } + + fn is_writable(&self) -> bool { + let r = self.inner.uartfr.read(); + + return !r.txff().bit(); + } +} + +pub trait Instance: Deref {} +impl Instance for pac::UART0 {} +impl Instance for pac::UART1 {}