Compare commits
4 Commits
v0.1.1b
...
92e27ad076
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92e27ad076 | ||
|
|
93c43dee11 | ||
|
|
096fe5e2b9 | ||
|
|
fef7de2045 |
@@ -9,10 +9,12 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
|||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use dma_gpio::software_uart::{
|
use dma_gpio::software_uart::{
|
||||||
dma_timer::{init_tim6_for_uart, init_tim7_for_uart},
|
dma_timer::{init_tim6_for_uart, init_tim7_for_uart},
|
||||||
gpio_dma_uart_tx::{write_uart_frames_to_pipe, UartConfig, Parity, StopBits},
|
uart_emulation::{Parity, StopBits, UartConfig},
|
||||||
runtime::{rx_dma_task, tx_dma_task},
|
gpio_dma_uart_tx::{write_uart_frames_to_ring, TIM6_UP_REQ},
|
||||||
|
gpio_dma_uart_rx::rx_dma_task,
|
||||||
debug::dump_tim6_regs,
|
debug::dump_tim6_regs,
|
||||||
};
|
};
|
||||||
|
use embassy_stm32::dma::{TransferOptions, WritableRingBuffer};
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@@ -21,13 +23,16 @@ const BAUD: u32 = 115_200;
|
|||||||
const TX_PIN_BIT: u8 = 2; // PA2
|
const TX_PIN_BIT: u8 = 2; // PA2
|
||||||
const TX_OVERSAMPLE: u16 = 1;
|
const TX_OVERSAMPLE: u16 = 1;
|
||||||
const RX_OVERSAMPLE: u16 = 16;
|
const RX_OVERSAMPLE: u16 = 16;
|
||||||
const PIPE_TX_SIZE: usize = 256;
|
|
||||||
const PIPE_RX_SIZE: usize = 256;
|
|
||||||
const RX_RING_BYTES: usize = 4096;
|
const RX_RING_BYTES: usize = 4096;
|
||||||
|
const TX_RING_BYTES: usize = 4096;
|
||||||
|
|
||||||
|
// Nemoze by generic, v taskoch treba manualne zmenit
|
||||||
|
// Compiler upozorni, takze ostava takto
|
||||||
|
const PIPE_RX_SIZE: usize = 256;
|
||||||
|
|
||||||
static PIPE_TX: Pipe<CriticalSectionRawMutex, PIPE_TX_SIZE> = Pipe::new();
|
|
||||||
static PIPE_RX: Pipe<CriticalSectionRawMutex, PIPE_RX_SIZE> = Pipe::new();
|
static PIPE_RX: Pipe<CriticalSectionRawMutex, PIPE_RX_SIZE> = Pipe::new();
|
||||||
static RX_RING: StaticCell<[u8; RX_RING_BYTES]> = StaticCell::new();
|
static RX_RING: StaticCell<[u8; RX_RING_BYTES]> = StaticCell::new();
|
||||||
|
static TX_RING: StaticCell<[u32; TX_RING_BYTES]> = StaticCell::new();
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
@@ -39,14 +44,35 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE);
|
init_tim6_for_uart(p.TIM6, BAUD, TX_OVERSAMPLE);
|
||||||
init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE);
|
init_tim7_for_uart(p.TIM7, BAUD, RX_OVERSAMPLE);
|
||||||
|
|
||||||
dump_tim6_regs();
|
dump_tim6_regs();
|
||||||
|
|
||||||
// Safe one-time init from StaticCell
|
// Safe one-time init from StaticCell
|
||||||
let ring: &mut [u8; RX_RING_BYTES] = RX_RING.init([0; RX_RING_BYTES]);
|
let rx_ring: &mut [u8; RX_RING_BYTES] = RX_RING.init([0; RX_RING_BYTES]);
|
||||||
|
let tx_ring_mem: &mut [u32; TX_RING_BYTES] =
|
||||||
|
TX_RING.init([0; TX_RING_BYTES]);
|
||||||
|
|
||||||
// Spawn tasks
|
// Spawn tasks
|
||||||
spawner.spawn(tx_dma_task(p.GPDMA1_CH0, &PIPE_TX).unwrap());
|
spawner.spawn(rx_dma_task(p.GPDMA1_CH1, &PIPE_RX, rx_ring).unwrap());
|
||||||
spawner.spawn(rx_dma_task(p.GPDMA1_CH1, &PIPE_RX, ring).unwrap());
|
|
||||||
|
// Create and start the TX DMA ring in main.
|
||||||
|
let bsrr_ptr = embassy_stm32::pac::GPIOA.bsrr().as_ptr() as *mut u32;
|
||||||
|
let mut tx_opts = TransferOptions::default();
|
||||||
|
tx_opts.half_transfer_ir = true;
|
||||||
|
tx_opts.complete_transfer_ir = true;
|
||||||
|
|
||||||
|
// SAFETY: tx_ring_mem is exclusive, bsrr_ptr points to GPIOA BSRR, paced by TIM6.
|
||||||
|
let mut tx_ring = unsafe {
|
||||||
|
WritableRingBuffer::new(
|
||||||
|
p.GPDMA1_CH0,
|
||||||
|
TIM6_UP_REQ,
|
||||||
|
bsrr_ptr,
|
||||||
|
tx_ring_mem,
|
||||||
|
tx_opts,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
tx_ring.start();
|
||||||
|
info!("TX DMA ring started");
|
||||||
|
|
||||||
let uart_cfg = UartConfig {
|
let uart_cfg = UartConfig {
|
||||||
data_bits: 8,
|
data_bits: 8,
|
||||||
@@ -55,7 +81,13 @@ async fn main(spawner: Spawner) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
write_uart_frames_to_pipe(&PIPE_TX, TX_PIN_BIT, b"Hello marshmallow\r\n", &uart_cfg).await;
|
write_uart_frames_to_ring(
|
||||||
|
&mut tx_ring,
|
||||||
|
TX_PIN_BIT,
|
||||||
|
b"Hello marshmallow\r\n",
|
||||||
|
&uart_cfg,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
Timer::after(Duration::from_secs(2)).await;
|
Timer::after(Duration::from_secs(2)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,39 @@
|
|||||||
// src/gpio_dma_uart_rx.rs
|
// src/software_uart/runtime.rs
|
||||||
|
use embassy_executor::task;
|
||||||
use embassy_stm32::{
|
use embassy_stm32::{
|
||||||
dma::{Request, Transfer, TransferOptions},
|
dma::Request,
|
||||||
peripherals::GPDMA1_CH1,
|
peripherals::GPDMA1_CH1,
|
||||||
Peri,
|
Peri,
|
||||||
};
|
};
|
||||||
|
use embassy_stm32::dma::{
|
||||||
|
ReadableRingBuffer as DmaRingRx,
|
||||||
|
TransferOptions,
|
||||||
|
};
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||||
|
|
||||||
// RM0456 tabulka 137
|
// datasheet tabulka 137
|
||||||
pub const TIM7_UP_REQ: Request = 5;
|
pub const TIM7_UP_REQ: Request = 5;
|
||||||
|
|
||||||
pub struct GpioDmaRx<'d, const N: usize> {
|
/// RX DMA task: reads GPIO samples paced by TIM7 and fills PIPE_RX
|
||||||
ch: Peri<'d, GPDMA1_CH1>,
|
#[task]
|
||||||
pin_bit: u8,
|
pub async fn rx_dma_task(
|
||||||
buf: &'d mut [u32; N],
|
ch: Peri<'static, GPDMA1_CH1>,
|
||||||
opts: TransferOptions,
|
pipe_rx: &'static Pipe<CriticalSectionRawMutex, 256>,
|
||||||
pipe_rx: &'d Pipe<CriticalSectionRawMutex, 256>,
|
ring: &'static mut [u8],
|
||||||
}
|
) {
|
||||||
|
let gpioa_idr = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8;
|
||||||
|
|
||||||
impl<'d, const N: usize> GpioDmaRx<'d, N> {
|
let mut opts = TransferOptions::default();
|
||||||
pub fn new(
|
opts.half_transfer_ir = true;
|
||||||
ch: Peri<'d, GPDMA1_CH1>,
|
opts.complete_transfer_ir = true;
|
||||||
pin_bit: u8,
|
|
||||||
buf: &'d mut [u32; N],
|
|
||||||
pipe_rx: &'d Pipe<CriticalSectionRawMutex, 256>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
ch,
|
|
||||||
pin_bit,
|
|
||||||
buf,
|
|
||||||
opts: TransferOptions::default(),
|
|
||||||
pipe_rx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&mut self) -> ! {
|
// SAFETY: ring is exclusive to this task
|
||||||
loop {
|
let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) };
|
||||||
let gpioa_idr_addr = embassy_stm32::pac::GPIOA.as_ptr() as *mut u32;
|
rx.start();
|
||||||
|
|
||||||
unsafe {
|
let mut chunk = [0u8; 256];
|
||||||
Transfer::new_read(
|
loop {
|
||||||
self.ch.reborrow(),
|
let _ = rx.read_exact(&mut chunk).await;
|
||||||
TIM7_UP_REQ,
|
pipe_rx.write(&chunk).await;
|
||||||
gpioa_idr_addr,
|
|
||||||
&mut self.buf[..],
|
|
||||||
self.opts,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
for &word in self.buf.iter() {
|
|
||||||
let bit_high = ((word >> self.pin_bit) & 1) as u8;
|
|
||||||
self.pipe_rx.write(&[bit_high]).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,146 +1,14 @@
|
|||||||
// src/gpio_dma_uart.rs
|
// src/software_uart/gpio_dma_uart_tx.rs
|
||||||
use embassy_stm32::{
|
use embassy_stm32::dma::Request;
|
||||||
dma::{Request, Transfer, TransferOptions},
|
use embassy_stm32::dma::WritableRingBuffer;
|
||||||
peripherals::GPDMA1_CH0,
|
use crate::software_uart::uart_emulation::{UartConfig, encode_uart_byte_cfg};
|
||||||
Peri,
|
|
||||||
};
|
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
|
||||||
|
|
||||||
// kapitola 17.4.11 - 2 casovace pre 2 DMA
|
// kapitola 17.4.11 - 2 casovace pre 2 DMA
|
||||||
pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687 STM32U5xx datasheet
|
pub const TIM6_UP_REQ: Request = 4; // Table 137: tim6_upd_dma, strana 687 STM32U5xx datasheet
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
/// Push UART frames into the DMA-backed TX ring
|
||||||
pub enum Parity {
|
pub async fn write_uart_frames_to_ring(
|
||||||
None,
|
ring: &mut WritableRingBuffer<'static, u32>,
|
||||||
Even,
|
|
||||||
Odd,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum StopBits {
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct UartConfig {
|
|
||||||
pub data_bits: u8, // 5..=8 bitov strana 16 TI_uart
|
|
||||||
pub parity: Parity,
|
|
||||||
pub stop_bits: StopBits,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UartConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
data_bits: 8,
|
|
||||||
parity: Parity::None,
|
|
||||||
stop_bits: StopBits::One,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GpioDmaBsrrTx<'d> {
|
|
||||||
ch: Peri<'d, GPDMA1_CH0>,
|
|
||||||
bsrr: *mut u32,
|
|
||||||
opts: TransferOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> GpioDmaBsrrTx<'d> {
|
|
||||||
// Constructor. Hides the raw register pointer internally.
|
|
||||||
pub fn new(ch: Peri<'d, GPDMA1_CH0>) -> Self {
|
|
||||||
let bsrr = embassy_stm32::pac::GPIOA.bsrr().as_ptr() as *mut u32;
|
|
||||||
Self {
|
|
||||||
ch,
|
|
||||||
bsrr,
|
|
||||||
opts: TransferOptions::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safe API: perform one timer-paced DMA write of a single 32-bit BSRR word.
|
|
||||||
pub async fn write_word(&mut self, word: u32) {
|
|
||||||
let buf = [word];
|
|
||||||
// Safety: bsrr is a valid 32-bit aligned register, buf lives until DMA completes,
|
|
||||||
// request selects TIM6_UP, which paces one beat per update.
|
|
||||||
unsafe {
|
|
||||||
Transfer::new_write(
|
|
||||||
self.ch.reborrow(),
|
|
||||||
TIM6_UP_REQ,
|
|
||||||
&buf,
|
|
||||||
self.bsrr,
|
|
||||||
self.opts,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build up to 12 BSRR words for one UART frame on a given GPIO bit.
|
|
||||||
// Format: 1 START (low), N data (LSB first), optional PARITY, STOP(1/2 -> here 1 or 2 ticks).
|
|
||||||
// BSRR je safe atomic write only shortcut
|
|
||||||
pub fn encode_uart_byte_cfg(
|
|
||||||
pin_bit: u8,
|
|
||||||
data: u8,
|
|
||||||
cfg: &UartConfig,
|
|
||||||
out: &mut [u32; 12],
|
|
||||||
) -> usize {
|
|
||||||
// Dokumentacia strana 636 13.4.7
|
|
||||||
// set bit - HIGH, reset bit - LOW (BSRR)
|
|
||||||
let set_high = |bit: u8| -> u32 { 1u32 << bit };
|
|
||||||
let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) };
|
|
||||||
|
|
||||||
let mut idx = 0usize;
|
|
||||||
|
|
||||||
// START bit (LOW)
|
|
||||||
out[idx] = set_low(pin_bit);
|
|
||||||
idx += 1;
|
|
||||||
|
|
||||||
// Data bits, LSB first (5..=8)
|
|
||||||
let nbits = cfg.data_bits.clamp(5, 8);
|
|
||||||
for i in 0..nbits {
|
|
||||||
let one = ((data >> i) & 1) != 0;
|
|
||||||
out[idx] = if one { set_high(pin_bit) } else { set_low(pin_bit) };
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional parity
|
|
||||||
match cfg.parity {
|
|
||||||
Parity::None => {}
|
|
||||||
Parity::Even | Parity::Odd => {
|
|
||||||
// Count ones
|
|
||||||
let mask: u8 = if nbits == 8 { 0xFF } else { (1u16 << nbits) as u8 - 1 };
|
|
||||||
let ones = (data & mask).count_ones() & 1; // 0=even, 1=odd
|
|
||||||
let par_bit_is_one = match cfg.parity {
|
|
||||||
Parity::Even => ones == 1, // make total ones even
|
|
||||||
Parity::Odd => ones == 0, // make total ones odd
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
out[idx] = if par_bit_is_one {
|
|
||||||
set_high(pin_bit)
|
|
||||||
} else {
|
|
||||||
set_low(pin_bit)
|
|
||||||
};
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// STOP bits (HIGH)
|
|
||||||
// - STB=0 => 1 stop bit
|
|
||||||
// - STB=1 => 2 stop bits
|
|
||||||
let stop_ticks = match cfg.stop_bits {
|
|
||||||
StopBits::One => 1usize,
|
|
||||||
StopBits::Two => 2usize,
|
|
||||||
};
|
|
||||||
for _ in 0..stop_ticks {
|
|
||||||
out[idx] = set_high(pin_bit);
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
idx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push UART frames for a whole byte slice into a Pipe.
|
|
||||||
pub async fn write_uart_frames_to_pipe<const N: usize>(
|
|
||||||
pipe: &Pipe<CriticalSectionRawMutex, N>,
|
|
||||||
pin_bit: u8,
|
pin_bit: u8,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
cfg: &UartConfig,
|
cfg: &UartConfig,
|
||||||
@@ -148,21 +16,8 @@ pub async fn write_uart_frames_to_pipe<const N: usize>(
|
|||||||
for &b in bytes {
|
for &b in bytes {
|
||||||
let mut frame = [0u32; 12];
|
let mut frame = [0u32; 12];
|
||||||
let used = encode_uart_byte_cfg(pin_bit, b, cfg, &mut frame);
|
let used = encode_uart_byte_cfg(pin_bit, b, cfg, &mut frame);
|
||||||
for w in &frame[..used] {
|
|
||||||
pipe.write(&w.to_le_bytes()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: emit a BREAK (line LOW for 'bits' bit-times).
|
// Will wait until all words are written
|
||||||
pub async fn write_break_to_pipe<const N: usize>(
|
ring.write_exact(&frame[..used]).await.unwrap();
|
||||||
pipe: &Pipe<CriticalSectionRawMutex, N>,
|
|
||||||
pin_bit: u8,
|
|
||||||
bits: usize,
|
|
||||||
) {
|
|
||||||
let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) };
|
|
||||||
let word = set_low(pin_bit);
|
|
||||||
for _ in 0..bits {
|
|
||||||
pipe.write(&word.to_le_bytes()).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
pub mod gpio_dma_uart_tx;
|
pub mod gpio_dma_uart_tx;
|
||||||
pub mod gpio_dma_uart_rx;
|
pub mod gpio_dma_uart_rx;
|
||||||
pub mod dma_timer;
|
pub mod dma_timer;
|
||||||
pub mod runtime;
|
pub mod uart_emulation;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
|
|
||||||
pub use gpio_dma_uart_tx::*;
|
pub use gpio_dma_uart_tx::*;
|
||||||
pub use gpio_dma_uart_rx::*;
|
pub use gpio_dma_uart_rx::*;
|
||||||
pub use dma_timer::*;
|
pub use dma_timer::*;
|
||||||
pub use runtime::*;
|
pub use uart_emulation::*;
|
||||||
pub use debug::*;
|
pub use debug::*;
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
// src/software_uart/runtime.rs
|
|
||||||
use defmt::{info, warn};
|
|
||||||
use embassy_executor::task;
|
|
||||||
use embassy_stm32::{
|
|
||||||
dma::{ReadableRingBuffer as DmaRingRx, TransferOptions},
|
|
||||||
peripherals::{GPDMA1_CH0, GPDMA1_CH1},
|
|
||||||
Peri,
|
|
||||||
};
|
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
|
||||||
use embassy_time::Duration;
|
|
||||||
use crate::software_uart::{
|
|
||||||
gpio_dma_uart_rx::TIM7_UP_REQ,
|
|
||||||
gpio_dma_uart_tx::GpioDmaBsrrTx,
|
|
||||||
debug::{dump_dma_ch0_regs, dump_tim6_regs},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// RX DMA task: reads GPIO samples paced by TIM7 and fills PIPE_RX
|
|
||||||
#[task]
|
|
||||||
pub async fn rx_dma_task(
|
|
||||||
ch: Peri<'static, GPDMA1_CH1>,
|
|
||||||
pipe_rx: &'static Pipe<CriticalSectionRawMutex, 256>,
|
|
||||||
ring: &'static mut [u8],
|
|
||||||
) {
|
|
||||||
let gpioa_idr = embassy_stm32::pac::GPIOA.idr().as_ptr() as *mut u8;
|
|
||||||
|
|
||||||
let mut opts = TransferOptions::default();
|
|
||||||
opts.half_transfer_ir = true;
|
|
||||||
opts.complete_transfer_ir = true;
|
|
||||||
|
|
||||||
// SAFETY: ring is exclusive to this task
|
|
||||||
let mut rx = unsafe { DmaRingRx::new(ch, TIM7_UP_REQ, gpioa_idr, ring, opts) };
|
|
||||||
rx.start();
|
|
||||||
|
|
||||||
let mut chunk = [0u8; 256];
|
|
||||||
loop {
|
|
||||||
let _ = rx.read_exact(&mut chunk).await;
|
|
||||||
pipe_rx.write(&chunk).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TX DMA task: dequeues prebuilt frames from PIPE_TX and writes to GPIOA.BSRR
|
|
||||||
#[task]
|
|
||||||
pub async fn tx_dma_task(
|
|
||||||
ch: Peri<'static, GPDMA1_CH0>,
|
|
||||||
pipe_tx: &'static Pipe<CriticalSectionRawMutex, 256>,
|
|
||||||
) {
|
|
||||||
let mut tx = GpioDmaBsrrTx::new(ch);
|
|
||||||
info!("DMA TX task started");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut b = [0u8; 4];
|
|
||||||
let n = pipe_tx.read(&mut b).await;
|
|
||||||
if n != 4 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let w = u32::from_le_bytes(b);
|
|
||||||
info!("DMA write 0x{:08X} -> GPIOA.BSRR", w);
|
|
||||||
|
|
||||||
match embassy_time::with_timeout(Duration::from_millis(20), tx.write_word(w)).await {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(_) => {
|
|
||||||
warn!("DMA timeout: no TIM6 request");
|
|
||||||
dump_tim6_regs();
|
|
||||||
dump_dma_ch0_regs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
89
dma_gpio/src/software_uart/uart_emulation.rs
Normal file
89
dma_gpio/src/software_uart/uart_emulation.rs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// src/software_uart/uart_emulation.rs
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Parity {
|
||||||
|
None,
|
||||||
|
Even,
|
||||||
|
Odd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum StopBits {
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct UartConfig {
|
||||||
|
pub data_bits: u8,
|
||||||
|
pub parity: Parity,
|
||||||
|
pub stop_bits: StopBits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UartConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
data_bits: 8,
|
||||||
|
parity: Parity::None,
|
||||||
|
stop_bits: StopBits::One,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes one byte into a sequence of GPIO BSRR words
|
||||||
|
pub fn encode_uart_byte_cfg(
|
||||||
|
pin_bit: u8,
|
||||||
|
data: u8,
|
||||||
|
cfg: &UartConfig,
|
||||||
|
out: &mut [u32; 12],
|
||||||
|
) -> usize {
|
||||||
|
// GPIOx_BSRR register str. 636 kap. 13.4.7
|
||||||
|
let set_high = |bit: u8| -> u32 { 1u32 << bit };
|
||||||
|
let set_low = |bit: u8| -> u32 { 1u32 << (bit as u32 + 16) };
|
||||||
|
|
||||||
|
let mut idx = 0usize;
|
||||||
|
|
||||||
|
// START bit (LOW)
|
||||||
|
out[idx] = set_low(pin_bit);
|
||||||
|
idx += 1;
|
||||||
|
|
||||||
|
// Data bits, LSB-first
|
||||||
|
let nbits = cfg.data_bits.clamp(5, 8);
|
||||||
|
for i in 0..nbits {
|
||||||
|
let one = ((data >> i) & 1) != 0;
|
||||||
|
out[idx] = if one { set_high(pin_bit) } else { set_low(pin_bit) };
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parity
|
||||||
|
match cfg.parity {
|
||||||
|
Parity::None => {}
|
||||||
|
Parity::Even | Parity::Odd => {
|
||||||
|
let mask: u8 = if nbits == 8 { 0xFF } else { (1u16 << nbits) as u8 - 1 };
|
||||||
|
let ones = (data & mask).count_ones() & 1;
|
||||||
|
let par_bit_is_one = match cfg.parity {
|
||||||
|
Parity::Even => ones == 1,
|
||||||
|
Parity::Odd => ones == 0,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
out[idx] = if par_bit_is_one {
|
||||||
|
set_high(pin_bit)
|
||||||
|
} else {
|
||||||
|
set_low(pin_bit)
|
||||||
|
};
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STOP bits (HIGH)
|
||||||
|
let stop_ticks = match cfg.stop_bits {
|
||||||
|
StopBits::One => 1usize,
|
||||||
|
StopBits::Two => 2usize,
|
||||||
|
};
|
||||||
|
for _ in 0..stop_ticks {
|
||||||
|
out[idx] = set_high(pin_bit);
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user