Skip to content
Snippets Groups Projects
Commit 3d697032 authored by Benjamin Koch's avatar Benjamin Koch
Browse files

add RS485 TX - sending continuously, for now

parent 7c2af45e
No related branches found
No related tags found
No related merge requests found
......@@ -72,7 +72,6 @@ async fn beeper_task(pwm_channel: embassy_rp::peripherals::PWM_CH3, beeper_pin:
#[embassy_executor::task]
async fn uart_task(this: RS485) {
info!("abc");
this.run_task().await;
}
......@@ -99,8 +98,8 @@ async fn main(spawner: Spawner) {
let sda = p.PIN_12;
let scl = p.PIN_13;
let _ws2811_red_in = Input::new(p.PIN_14, Pull::None);
let _tx_en = p.PIN_15;
let _tx = p.PIN_16;
let tx_en = p.PIN_15;
let tx = p.PIN_16;
let rx = p.PIN_17;
let _matrix_in2 = Input::new(p.PIN_18, Pull::Up);
let _reed3 = Input::new(p.PIN_19, Pull::Up);
......@@ -124,7 +123,7 @@ async fn main(spawner: Spawner) {
let mut uart_config = uart::Config::default();
uart_config.baudrate = 19200;
uart_config.parity = Parity::ParityEven;
let rs485 = RS485::new(p.UART0, rx, interrupt::take!(UART0_IRQ), p.DMA_CH1, uart_config, p.PIO0, p.DMA_CH0);
let rs485 = RS485::new(p.UART0, rx, tx, tx_en, interrupt::take!(UART0_IRQ), p.DMA_CH1, uart_config, p.PIO0, p.DMA_CH0, p.DMA_CH2);
unwrap!(spawner.spawn(uart_task(rs485)));
if false {
......
......@@ -19,7 +19,10 @@ use crate::dont_abort::DontAbort;
pub struct RS485 {
pio: peripherals::PIO0,
rx_pin: peripherals::PIN_17,
tx_pin: peripherals::PIN_16,
tx_en_pin: peripherals::PIN_15,
dma_channel: peripherals::DMA_CH0,
tx_dma_channel: peripherals::DMA_CH2,
rx: UartRx<'static, UART0, uart::Async>
}
......@@ -47,42 +50,42 @@ async fn debug_print_pio_addr(sm: pac::pio::StateMachine) {
impl RS485 {
pub fn new(
uart: UART0, rx: peripherals::PIN_17, rx_irq: UART0_IRQ, rx_dma_channel: peripherals::DMA_CH1, uart_config: uart::Config,
pio: peripherals::PIO0, dma_channel: peripherals::DMA_CH0
uart: UART0, rx_pin: peripherals::PIN_17, tx_pin: peripherals::PIN_16, tx_en_pin: peripherals::PIN_15,
rx_irq: UART0_IRQ, rx_dma_channel: peripherals::DMA_CH1, uart_config: uart::Config,
pio: peripherals::PIO0, dma_channel: peripherals::DMA_CH0, tx_dma_channel: peripherals::DMA_CH2
) -> RS485 {
// SAFETY: The auto-baud will only read from this pin and we will set it back to UART mode after initialising the PIO.
let rx_pin_for_autobaud = unsafe { rx.clone_unchecked() };
let rx_pin_for_autobaud = unsafe { rx_pin.clone_unchecked() };
let uart_rx = UartRx::new(
uart,
rx,
rx_pin,
rx_irq,
rx_dma_channel,
uart_config,
);
RS485 { pio, rx_pin: rx_pin_for_autobaud, dma_channel, rx: uart_rx }
RS485 { pio, rx_pin: rx_pin_for_autobaud, tx_pin, tx_en_pin, dma_channel, tx_dma_channel, rx: uart_rx }
}
pub async fn run_task(mut self: Self) {
let Pio {
mut common,
sm0: mut sm,
mut sm1,
mut irq0,
mut irq1,
..
} = Pio::new(self.pio);
sm.set_enable(false);
let prg_set_osr = pio_proc::pio_asm!(
".origin 0",
"pull",
"hang:",
"jmp hang",
);
let prg = pio_proc::pio_asm!(
".origin 0",
let prg_autobaud = pio_proc::pio_asm!(
// Send zero to RX FIFO to signal start of next measurement.
".wrap_target",
"timeout:",
......@@ -101,6 +104,7 @@ impl RS485 {
// We have a long 1, i.e. UART is idle. Wait for start bit.
"have_long_high:",
"irq set 1", // notify MCU in case they want to know about this (start of Modbus frame)
"wait 0 pin 0",
"nop [4]",
"jmp pin wait_idle", // abort if zero was just a glitch
......@@ -137,6 +141,41 @@ impl RS485 {
".wrap",
);
// This is based on the UART TX example in the datasheet but we also control the TX_EN pin
// and we have 9 data bits because we have parity.
/*
let prg_tx = pio_proc::pio_asm!(
".side_set 2 opt",
// An 8e1 UART transmit program.
// OUT pin 0 and side-set pin 1 are both mapped to UART TX pin.
"pull side 1", // disable transmitter, get number of bytes
"mov y, osr side 3", // copy to y reg, enable transmitter
"byteloop:",
"pull side 3 [6]", // Assert stop bit, or stall with line in idle state (6 instead of 7 because mov/jmp take one clk)
"set x, 8 side 2 [7]", // Preload bit counter, assert start bit for 8 clocks
"bitloop:", // This loop will run 9 times (8e1 UART, i.e. 8 data bits and one clock bit)
"out pins, 1", // Shift 1 bit from OSR to the first OUT pin
"jmp x-- bitloop [6]", // Each loop iteration is 8 cycles.
"jmp y-- byteloop side 3",
);
*/
// Let's use 4 cycles for each baud time because we need two bits of side-set.
let prg_tx = pio_proc::pio_asm!(
".origin 24",
".side_set 2 opt",
// An 8e1 UART transmit program.
// OUT pin 0 and side-set pin 1 are both mapped to UART TX pin.
"pull side 2", // disable transmitter, get number of bytes. Side: TX_EN=0, TX=1
"mov y, osr side 3", // copy to y reg, enable transmitter
"byteloop:",
"pull side 3 [2]", // Assert stop bit, or stall with line in idle state (6 instead of 7 because mov/jmp take one clk)
"set x, 8 side 1 [3]", // Preload bit counter, assert start bit for 8 clocks. Side: TX_EN=1, TX=0
"bitloop:", // This loop will run 9 times (8e1 UART, i.e. 8 data bits and one clock bit)
"out pins, 1", // Shift 1 bit from OSR to the first OUT pin
"jmp x-- bitloop [2]", // Each loop iteration is 8 cycles.
"jmp y-- byteloop side 3",
);
let relocated = RelocatedProgram::new(&prg_set_osr.program);
let mut cfg = Config::default();
let loaded_program = common.load_program(&relocated);
......@@ -162,14 +201,14 @@ impl RS485 {
// Timeout: Modbus wants 1.5 ms between frames so we make this a bit smaller. SM runs at 25 MHz
// but we need two clocks per loop.
let timeout_start_value = (SM_FREQ as f32 * 1.2e-3 / 2.) as u32;
let timeout_start_value = (SM_FREQ as f32 * 1.4e-3 / 2.) as u32;
sm.tx().push(timeout_start_value);
info!("timeout_start_value: {} = 0x{:08x}", timeout_start_value, timeout_start_value);
// switch to the real program and join FIFOs
unsafe { common.free_instr(loaded_program.used_memory); };
sm.set_enable(false);
let relocated = RelocatedProgram::new(&prg.program);
let relocated = RelocatedProgram::new(&prg_autobaud.program);
let loaded_program = common.load_program(&relocated);
cfg.use_program(&loaded_program, &[]);
cfg.fifo_join = FifoJoin::RxOnly;
......@@ -182,18 +221,56 @@ impl RS485 {
pin_io(&self.rx_pin).ctrl().write(|w| w.set_funcsel(embassy_rp::pac::io::vals::Gpio17ctrlFuncsel::UART0_RX.0));
}
info!("Program size for auto-baud: {}", prg.program.code.len());
info!("Program size for auto-baud: {}, for tx: {}", prg_autobaud.program.code.len(), prg_tx.program.code.len());
let relocated = RelocatedProgram::new(&prg_tx.program);
let mut cfg = Config::default();
let tx_pin_pio = common.make_pio_pin(self.tx_pin);
let tx_en_pin_pio = common.make_pio_pin(self.tx_en_pin);
let loaded_program = common.load_program(&relocated);
cfg.use_program(&loaded_program, &[&tx_en_pin_pio, &tx_pin_pio]);
const BAUD_RATE: u32 = 19200;
cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(76800 /* BAUD_RATE*4 */)).to_fixed();
cfg.shift_out = ShiftConfig {
auto_fill: false,
threshold: 32,
direction: ShiftDirection::Right,
};
cfg.set_out_pins(&[&tx_pin_pio]);
cfg.fifo_join = FifoJoin::TxOnly;
sm1.set_config(&cfg);
sm1.set_pins(gpio::Level::Low, &[&tx_en_pin_pio]);
sm1.set_pins(gpio::Level::High, &[&tx_pin_pio]);
sm1.set_pin_dirs(embassy_rp::pio::Direction::Out, &[&tx_en_pin_pio, &tx_pin_pio]);
sm1.set_enable(true);
let mut tx_data = [0, 'H' as u32, 'e' as u32, 'l' as u32, 'l' as u32, 'o' as u32, '\r' as u32, '\n' as u32];
tx_data[0] = (tx_data.len() - 1) as u32;
for i in 1..tx_data.len() {
let x = tx_data[i] & 0xff;
let mut parity = 0;
for j in 0..8 {
parity ^= x>>j;
}
tx_data[i] = x | ((parity&1) << 8);
}
info!("tx_data: {:?}", tx_data);
let mut dma_in_ref = self.dma_channel.into_ref();
let mut dma_tx_ref = self.tx_dma_channel.into_ref();
let mut din = [42u32; 9];
let mut bit_index = 0;
let mut rx_buf = [0; 1];
let mut rx_future = DontAbort::new(self.rx.read(&mut rx_buf));
let mut tx_future = DontAbort::new(sm1.tx().dma_push(dma_tx_ref.reborrow(), &tx_data));
loop {
let x = select4(
irq0.wait(),
sm.rx().dma_pull(dma_in_ref.reborrow(), &mut din),
debug_print_pio_addr(embassy_rp::pac::PIO0.sm(0)),
//debug_print_pio_addr(embassy_rp::pac::PIO0.sm(0)),
tx_future.continue_wait(),
rx_future.continue_wait(),
).await;
match x {
......@@ -279,6 +356,10 @@ impl RS485 {
}
}
},
Either4::Third(_) => {
drop(tx_future);
tx_future = DontAbort::new(sm1.tx().dma_push(dma_tx_ref.reborrow(), &tx_data));
},
Either4::Fourth(x) => {
drop(rx_future);
match x {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment