Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add uart sample #3

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rp2040_pac as pac;

mod pll;
mod resets;
mod uart;

#[link_section = ".boot2"]
#[used]
Expand All @@ -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,
Expand All @@ -52,7 +63,8 @@ fn init(
| resets::SPI1
| resets::UART0
| resets::UART1
| resets::USBCTRL),
| resets::USBCTRL
| resets::IO_BANK0),
);

// xosc 12 mhz
Expand Down Expand Up @@ -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]
Expand All @@ -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 {
Expand Down
116 changes: 116 additions & 0 deletions src/uart.rs
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
* 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<T: Instance> {
inner: T,
clk_base: u32,
}

impl<T: Instance> UART<T> {
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<Target = pac::uart0::RegisterBlock> {}
impl Instance for pac::UART0 {}
impl Instance for pac::UART1 {}