-
Notifications
You must be signed in to change notification settings - Fork 106
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
Framing serial #94
Framing serial #94
Conversation
@MabezDev / @MathiasKoch I have implemented the framing based DMA / UART transfers that utilize character match. Some things I want to do:
Feedback is welcome! Here is an example program: #![no_main]
#![no_std]
use cortex_m::asm;
use cortex_m_semihosting::hprintln;
use hal::{
dma,
prelude::*,
rcc::{ClockSecuritySystem, CrystalBypass, MsiFreq},
serial::{self, Config, Serial},
};
use heapless::{
pool,
pool::singleton::{Box, Pool},
};
use panic_semihosting as _;
use rtfm::app;
use stm32l4xx_hal as hal;
pool!(
A: [u8; 8]
);
#[app(device = stm32l4xx_hal::stm32, peripherals = true)]
const APP: () = {
struct Resources {
rx: serial::Rx<hal::stm32::USART2>,
frame_reader: dma::FrameReader<Box<A>, dma::dma1::C6>,
}
#[init]
fn init(cx: init::Context) -> init::LateResources {
static mut MEMORY: [u8; 1024] = [0; 1024];
A::grow(MEMORY);
let dp = cx.device;
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
let clocks = rcc
.cfgr
.lse(CrystalBypass::Disable, ClockSecuritySystem::Disable)
.msi(MsiFreq::RANGE4M)
.sysclk(80.mhz())
.freeze(&mut flash.acr, &mut pwr);
let tx2 = gpioa.pa2.into_af7(&mut gpioa.moder, &mut gpioa.afrl);
let rx2 = gpioa.pa3.into_af7(&mut gpioa.moder, &mut gpioa.afrl);
// TRY using a different USART peripheral here
let mut serial = Serial::usart2(
dp.USART2,
(tx2, rx2),
Config::default()
.baudrate(115_200.bps())
.character_match(b'a'),
clocks,
&mut rcc.apb1r1,
);
serial.listen(serial::Event::CharacterMatch);
let (mut tx, serial_rx) = serial.split();
let channels = dp.DMA1.split(&mut rcc.ahb1);
let mut dma_ch6 = channels.6;
dma_ch6.listen(dma::Event::TransferComplete);
let dma_buf = A::alloc().unwrap().init([0; 8]);
let fr = serial_rx.frame_read(dma_ch6, dma_buf);
let sent = b'X';
tx.write(sent).ok();
init::LateResources {
rx: serial_rx,
frame_reader: fr,
}
}
#[idle]
fn idle(_: idle::Context) -> ! {
hprintln!("IDLE").ok();
loop {
asm::nop();
}
}
#[task(binds = USART2, resources = [rx, frame_reader], priority = 3)]
fn serial_isr(cx: serial_isr::Context) {
// Check for character match
if cx.resources.rx.is_character_match(true) {
let dma_buf = A::alloc().unwrap().init([0; 8]);
let (buf, len) = cx.resources.frame_reader.character_match_interrupt(dma_buf);
hprintln!("CM: {}", unsafe {
core::str::from_utf8_unchecked(&buf[0..len])
})
.ok();
// NB: Memory is return to the pool here when it goes out of scope
}
}
#[task(binds = DMA1_CH6, resources = [rx, frame_reader], priority = 3)]
fn serial_dma_isr(cx: serial_dma_isr::Context) {
let dma_buf = A::alloc().unwrap().init([0; 8]);
let (buf, len) = cx
.resources
.frame_reader
.transfer_complete_interrupt(dma_buf);
hprintln!("DMA: {}", unsafe {
core::str::from_utf8_unchecked(&buf[0..len])
})
.ok();
// NB: Memory is return to the pool here when it goes out of scope
}
}; |
The new version addressing my thoughts have been pushed, here is a new example program. Now to fix sending of frames. :) #![no_main]
#![no_std]
use cortex_m::asm;
use cortex_m_rt::{self};
use cortex_m_semihosting::hprintln;
use hal::{
dma::{self, consts, FrameReader, SerialDMAFrame},
prelude::*,
rcc::{ClockSecuritySystem, CrystalBypass, MsiFreq},
serial::{self, Config, Serial},
};
use heapless::{
pool,
pool::singleton::{Box, Pool},
};
use panic_semihosting as _;
use rtfm::app;
use stm32l4xx_hal as hal;
pool!(
A: SerialDMAFrame<consts::U8>
);
#[app(device = stm32l4xx_hal::stm32, peripherals = true)]
const APP: () = {
struct Resources {
rx: serial::Rx<hal::stm32::USART2>,
frame_reader: FrameReader<Box<A>, dma::dma1::C6, consts::U8>,
nothing: (),
}
#[init]
fn init(cx: init::Context) -> init::LateResources {
static mut MEMORY: [u8; 1024] = [0; 1024];
// increase the capacity of the pool
A::grow(MEMORY);
let dp = cx.device;
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
let clocks = rcc
.cfgr
.lse(CrystalBypass::Disable, ClockSecuritySystem::Disable)
.msi(MsiFreq::RANGE4M)
.sysclk(80.mhz())
.freeze(&mut flash.acr, &mut pwr);
let tx2 = gpioa.pa2.into_af7(&mut gpioa.moder, &mut gpioa.afrl);
let rx2 = gpioa.pa3.into_af7(&mut gpioa.moder, &mut gpioa.afrl);
// TRY using a different USART peripheral here
let mut serial = Serial::usart2(
dp.USART2,
(tx2, rx2),
Config::default()
.baudrate(115_200.bps())
.character_match(b'a'),
clocks,
&mut rcc.apb1r1,
);
serial.listen(serial::Event::CharacterMatch);
let (mut tx, serial_rx) = serial.split();
let channels = dp.DMA1.split(&mut rcc.ahb1);
let mut dma_ch6 = channels.6;
dma_ch6.listen(dma::Event::TransferComplete);
let dma_buf = A::alloc().unwrap().init(SerialDMAFrame::new());
let fr = serial_rx.frame_read(dma_ch6, dma_buf);
let sent = b'X';
tx.write(sent).ok();
init::LateResources {
rx: serial_rx,
frame_reader: fr,
nothing: (),
}
}
#[idle]
fn idle(_: idle::Context) -> ! {
hprintln!("IDLE").ok();
loop {
asm::nop();
}
}
#[task(binds = USART2, resources = [rx, frame_reader], priority = 3)]
fn serial_isr(cx: serial_isr::Context) {
hprintln!("UISR").ok();
// Check for character match
if cx.resources.rx.is_character_match(true) {
let dma_buf = A::alloc().unwrap().init(SerialDMAFrame::new());
let buf = cx.resources.frame_reader.character_match_interrupt(dma_buf);
hprintln!("CM: {}", unsafe {
core::str::from_utf8_unchecked(buf.read())
})
.ok();
}
}
#[task(binds = DMA1_CH6, resources = [rx, frame_reader], priority = 3)]
fn serial_dma_isr(cx: serial_dma_isr::Context) {
hprintln!("TX").ok();
let dma_buf = A::alloc().unwrap().init(SerialDMAFrame::new());
let buf = cx
.resources
.frame_reader
.transfer_complete_interrupt(dma_buf);
hprintln!("DMA: {}", unsafe {
core::str::from_utf8_unchecked(buf.read())
})
.ok();
}
}; |
I have now pushed so sending and receiving is working and I added an example for this using RTFM. I think this is ready for review now. |
Whats with the Other than that, i can't wait to test this out! |
Ah right, for got to remove that. Fixed now. |
I added receiver timeout support and made some documentation improvements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @korken89 , just left some suggestions.
This is looking really great! Not seeing any real issues, other than the ones kindly reported, thank you @thalesfragoso! |
Thanks for the comment @thalesfragoso, I have updated accordingly! |
The changes to the compiler fences did not produce a different binary in my case, but better with weaker ones if it is OK and to not have missed a case. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work !!
If you wanna see some maybe "over the top" type checking I'm trying to do with a DMA peripheral:
stm32-rs/stm32f4xx-hal#88
It isn't finished or tested, I'm not even sure if it's that good of an idea, but it's an experiment, heh
@thalesfragoso Interesting approach! I have to give a deeper look to that. I did a final commit to this PR.
|
I changed a lot of things in that initial PR, so don't look much at the module docs, I still have to change that. |
Works beautifully here, with both the frame reader and receiver timeout for receiving AT commands! Thanks! |
Thanks for the review @thalesfragoso, I have updated the code! @MathiasKoch Awesome, glad to hear that you are already getting use from it! |
@MabezDev Whenever you feel ready, this should now be ready for merge. |
@korken89 I was just writing up a comment asking if this was good to go 😂. This looks really great, thanks for the contribution! |
Work in progress PR for framing serial with DMA.PR for framing serial with character match using DMA.