Hi there, I am attempting to leverage USART3 on the STM32F3 Discovery board to communicate with an Adafruit FONA GSM board using simple serial communications. I have not been able to get USART3 to work - at least from what I can tell (i.e. even just direct connected it to my laptop with a terminal program). I am using the stm32f3xx_hal crate.
Was able to get things to work (at least using a terminal app on my laptop) communicating between my laptop and STM32F3 over USART1 which is over the USB ST-LINK hardware interface. This validated that my coding strategy at least for USART1. I have since converted the code to leverage USART3 along with updating the DMA channel numbers and other things to enable USART3 versus USART1. However, I have not been able to get this type of test working with USART3. I'm wondering if I might be needing to configure something that was not necessary when I leveraged USART1 over the USB ST-Link interface. I was hopeful that by updating the appropriate pins, (i.e. pc10, pc11), dma channels (C2, C3) and changing everything from USART1 to USART3 would magically work .
Here's the code (thank you @ kalkyl for the help!) . BTW, I felt this belongs in Embedded versus RTIC since the RTIC part of the design is working just fine.
//! main.rs
#![deny(unsafe_code)]
#![no_main]
#![no_std]
#[rtic::app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [WWDG])]
mod app {
use stm32f3xx_hal::{
dma::{
dma1::{C2, C3},
Channel, Event, Transfer,
},
pac::USART3,
prelude::*,
serial::{Rx, Tx},
};
use panic_semihosting as _;
const BUF_SIZE: usize = 12;
pub enum TxTransfer {
Running(Transfer<&'static mut [u8; BUF_SIZE], C2, Tx<USART3>>),
Idle(&'static mut [u8; BUF_SIZE], C2, Tx<USART3>),
}
#[shared]
struct Shared {
#[lock_free]
send: Option<TxTransfer>,
}
#[local]
struct Local {
recv: Option<Transfer<&'static mut [u8; BUF_SIZE], C3, Rx<USART3>>>,
gpioe: stm32f3xx_hal::pac::GPIOE,
}
#[init(local = [tx_buf: [u8; BUF_SIZE] = [0; BUF_SIZE], rx_buf: [u8; BUF_SIZE] = [0; BUF_SIZE]])]
fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
ctx.device.RCC.ahbenr.modify(|_, w| w.dma1en().enabled());
ctx.device.RCC.ahbenr.modify(|_, w| w.iopeen().set_bit());
let mut rcc = ctx.device.RCC.constrain();
let mut flash = ctx.device.FLASH.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
// for serial port ops
let mut gpioc = ctx.device.GPIOC.split(&mut rcc.ahb);
// for relay ops
let gpioe = ctx.device.GPIOE;
gpioe.moder.modify(|_, w| {
w.moder8().output();
w.moder9().output();
w.moder10().output();
w.moder11().output();
w.moder12().output();
w.moder13().output();
w.moder14().output();
w.moder15().output()
});
// set the led to on
gpioe.odr.modify(|_, w| {
w.odr15().set_bit()
});
// setup for serial communications
let pins = (
gpioc.pc10.into_af7(&mut gpioc.moder, &mut gpioc.afrh),
gpioc.pc11.into_af7(&mut gpioc.moder, &mut gpioc.afrh),
);
let usart3 = stm32f3xx_hal::serial::Serial::usart3(
ctx.device.USART3,
pins,
9_600.bps(),
clocks,
&mut rcc.apb1,
);
let (tx, rx) = usart3.split();
let mut dma1 = ctx.device.DMA1.split(&mut rcc.ahb);
dma1.ch4.listen(Event::TransferComplete);
dma1.ch5.listen(Event::TransferComplete);
(
Shared {
send: Some(TxTransfer::Idle(ctx.local.tx_buf, dma1.ch2, tx)),
},
Local {
recv: Some(rx.read_exact(ctx.local.rx_buf, dma1.ch3)),
gpioe,
},
init::Monotonics(),
)
}
#[idle]
fn idle(_: idle::Context) -> ! {
loop {}
}
// Triggers on RX transfer completed
#[task(binds = DMA1_CH5, local = [recv,gpioe], priority = 2)]
fn on_rx(ctx: on_rx::Context) {
let (rx_buf, rx_channel, rx) = ctx.local.recv.take().unwrap().wait();
echo::spawn(*rx_buf).ok();
// set the led to off
ctx.local.gpioe.odr.modify(|r, w| {
//w.odr15().clear_bit()
let led = r.odr15().bit();
if led {
w.odr15().clear_bit()
} else {
w.odr15().set_bit()
}
});
ctx.local.recv.replace(rx.read_exact(rx_buf, rx_channel));
}
#[task(shared = [send], priority = 1, capacity = 4)]
fn echo(ctx: echo::Context, data: [u8; BUF_SIZE]) {
//defmt::info!("Received {:?}", data);
let send = ctx.shared.send;
let (tx_buf, tx_channel, tx) = match send.take().unwrap() {
TxTransfer::Idle(buf, ch, tx) => (buf, ch, tx),
TxTransfer::Running(transfer) => transfer.wait(),
};
tx_buf.copy_from_slice(&data[..]);
send.replace(TxTransfer::Running(tx.write_all(tx_buf, tx_channel)));
}
// Triggers on TX transfer completed
#[task(binds = DMA1_CH4, shared = [send], priority = 1)]
fn on_tx(ctx: on_tx::Context) {
let send = ctx.shared.send;
let (tx_buf, tx_channel, tx) = match send.take().unwrap() {
TxTransfer::Idle(buf, ch, tx) => (buf, ch, tx),
TxTransfer::Running(transfer) => transfer.wait(),
};
//defmt::info!("Sent {:?}", tx_buf);
send.replace(TxTransfer::Idle(tx_buf, tx_channel, tx));
}
}
And here's a picture of my makeshift lab setup (haha):
I'm wondering:
- Is there something special that I might have to do with the hal configuration to use USART3 (i.e anything special with the clock config that I did not have to do with USART1)?
- Does the HAL even support USART3?
- Is my design/thought process all wrong?
Thank you for reading my long post!
Matt