1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! UART = "Universal Aynchronous Receiver Transceiver"
//!
//! This module contains data transfer functions that allow easy access to the robot's serial UART
//! connection. Receiving messages is asynchronous (using interrupts) and uses ringbuffers for
//! intermediate storage of the received messages.
use super::port::{RX, TX};
use crate::{
avr::{
bitmasks::{RXC, RXCIE, RXEN, TXCIE, TXEN, UCSZ, UDRE, URSEL},
registers::{UBRRH, UBRRL, UCSRA, UCSRB, UCSRC, UDR},
},
Pin, Register,
};
/// Module that implements `Serial::write` and formatting behavior for types.
mod serial_writable;
pub use serial_writable::*;
use avr_config::CPU_FREQUENCY_HZ;
/// Define constants for RP6 baudrates.
pub const BAUD_LOW: u32 = 38400; // Low speed: 38.400 Baud
pub const UBRR_BAUD_LOW: u32 = (CPU_FREQUENCY_HZ / (16 * BAUD_LOW)) - 1;
pub const BAUD_HIGH: u32 = 500000; // High speed: 500.000 Baud
pub const UBRR_BAUD_HIGH: u32 = (CPU_FREQUENCY_HZ / (16 * BAUD_HIGH)) - 1;
/// Struct managing all access to the robot's serial port connection
pub struct Serial;
impl Serial {
/// Initialize the serial connection on pins `RX` and `TX`.
pub fn init() {
RX::set_input();
TX::set_low();
TX::set_output();
// UART:
Self::set_baudrate_low();
UCSRA::write(0x00);
UCSRC::write(URSEL | UCSZ);
UCSRB::write(TXEN | RXEN | RXCIE);
}
/// Enable the USART_RXC interrupt
#[allow(non_snake_case)]
pub fn enable_USART_RXC_interrupt() {
UCSRB::set_mask_raw(RXCIE);
}
/// Enable the USART_TXC interrupt
#[allow(non_snake_case)]
pub fn enable_USART_TXC_interrupt() {
UCSRB::set_mask_raw(TXCIE);
}
/// Disable the USART_RXC interrupt
#[allow(non_snake_case)]
pub fn disable_USART_RXC_interrupt() {
UCSRB::unset_mask_raw(RXCIE);
}
/// Disable the USART_TXC interrupt
#[allow(non_snake_case)]
pub fn disable_USART_TXC_interrupt() {
UCSRB::unset_mask_raw(TXCIE);
}
/// Configure serial connection to low baudrate `UBRR_BAUD_LOW`.
pub fn set_baudrate_low() {
UBRRH::write((UBRR_BAUD_LOW >> 8) as u8);
UBRRL::write(UBRR_BAUD_LOW as u8);
}
/// Configure serial connection to high baudrate `UBRR_BAUD_HIGH`.
pub fn set_baudrate_high() {
UBRRH::write((UBRR_BAUD_HIGH >> 8) as u8);
UBRRL::write(UBRR_BAUD_HIGH as u8);
}
/// Reads a single raw byte from the `Serial` connection. Blocks until the processor is ready
/// to receive the next byte, i.e., the corresponding bit `UDRE` is set in `UCSRA`.
#[inline(always)]
pub fn read_raw() -> u8 {
UCSRA::wait_until_mask_set_raw(RXC);
UDR::read()
}
/// Writes a single raw byte to the `Serial` connection. Blocks until the processor is ready to
/// send the next byte, i.e., the corresponding bit `UDRE` is set in `UCSRA`.
#[inline(always)]
pub fn write_raw(b: u8) {
UCSRA::wait_until_mask_set_raw(UDRE);
UDR::write(b);
}
/*
/// Tries to write a single raw byte to the `Serial` connection. If the processor is not ready
/// to send, i.e., the corresponding bit `UDRE` is not set in `UCSRA`, returns with an `Error`.
#[inline(always)]
fn try_write_raw(b: u8) -> Result<(), Error> {
if UCSRA::is_mask_set_raw(UDRE) {
Ok(UDR::write(b))
} else {
Error()
}
}
*/
/// Write something to the `Serial` connection. By default, supports `&str`, `char`, and basic
/// number types (in decimal notation).
pub fn write<T: SerialWritable>(value: T) {
value.write_to_serial();
}
/*
/// Write a number formatted as binary to the `Serial` connection.
pub fn write_bin<T: SerialWritableBinary>(value: T) {
value.write_to_serial_as_bin();
}
*/
/// Write a number formatted as decimal to the `Serial` connection.
pub fn write_dec<T: SerialWritableDecimal>(value: T) {
value.write_to_serial_as_dec();
}
/*
/// Write a number formatted as exponential to the `Serial` connection.
pub fn write_exp<T: SerialWritableExponential>(value: T) {
value.write_to_serial_as_exp();
}
*/
/// Write a number formatted as hexadecimal to the `Serial` connection.
pub fn write_hex<T: SerialWritableHexadecimal>(value: T) {
value.write_to_serial_as_hex();
}
/*
/// Write a number formatted as octal to the `Serial` connection.
pub fn write_oct<T: SerialWritableOctal>(value: T) {
value.write_to_serial_as_oct();
}
*/
/// Write a `'\n'` (newline character) to the serial connection.
pub fn new_line() {
Self::write('\n');
}
}
/// Convenience macro that allows to write multiple (formatted) `Serial::write` statements as a
/// single call. Currently supported formatters are `dec` and `hex` for numbers.
///
/// Example:
/// ```rust
/// let mut counter = 0;
/// loop {
/// println!(
/// "Counter:",
/// counter => dec,
/// "(DEC) | ",
/// counter => hex,
/// "(HEX)"
/// );
/// counter += 1;
/// }
/// ```
#[macro_export]
macro_rules! print {
($($writable: expr $(=> $format: tt)?),* $(,)?) => {
$($crate::print!(@write $writable $(=> $format)?);)*
};
(@write $writable: expr => dec) => {
Serial::write_dec($writable);
};
(@write $writable: expr => hex) => {
Serial::write_hex($writable);
};
(@write $writable: expr) => {
Serial::write($writable);
};
}
/// Convenience macro that allows to use the `print!` macro and append a newline character.
#[macro_export]
macro_rules! println {
($($writable: expr $(=> $format: tt)?),* $(,)?) => {
$crate::print!($($writable $(=> $format)?, )*);
Serial::new_line();
};
}