Skip to content
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

Merged
merged 19 commits into from
Apr 2, 2020
Merged

Framing serial #94

merged 19 commits into from
Apr 2, 2020

Conversation

korken89
Copy link
Collaborator

@korken89 korken89 commented Mar 18, 2020

Work in progress PR for framing serial with DMA.

PR for framing serial with character match using DMA.

@korken89
Copy link
Collaborator Author

korken89 commented Mar 20, 2020

@MabezDev / @MathiasKoch I have implemented the framing based DMA / UART transfers that utilize character match.

Some things I want to do:

  • Move from slice operation to raw operations, the compiler cannot remove all panicing branches based on slice access.
  • Use the input array as if it was MaybeUninit so uninitialized data can be used. No good reason to set data to a value when the DMA later will overwrite it.

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
    }
};

@korken89
Copy link
Collaborator Author

korken89 commented Mar 24, 2020

The new version addressing my thoughts have been pushed, here is a new example program.
The enticing feature here is that this is without copies, a buffer is ever copied while received.

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();
    }
};

@korken89
Copy link
Collaborator Author

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.

@korken89 korken89 changed the title [WIP] Framing serial Framing serial Mar 25, 2020
@MathiasKoch
Copy link
Collaborator

Whats with the nothing: ()?

Other than that, i can't wait to test this out!

@korken89
Copy link
Collaborator Author

Ah right, for got to remove that. Fixed now.
It's for keeping a stable interface while testing :)

@korken89
Copy link
Collaborator Author

I added receiver timeout support and made some documentation improvements

Copy link
Member

@thalesfragoso thalesfragoso left a 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.

src/dma.rs Outdated Show resolved Hide resolved
src/dma.rs Outdated Show resolved Hide resolved
src/dma.rs Show resolved Hide resolved
src/dma.rs Outdated Show resolved Hide resolved
src/dma.rs Show resolved Hide resolved
src/dma.rs Show resolved Hide resolved
src/dma.rs Outdated Show resolved Hide resolved
src/serial.rs Outdated Show resolved Hide resolved
src/serial.rs Outdated Show resolved Hide resolved
src/serial.rs Outdated Show resolved Hide resolved
@MabezDev
Copy link
Member

This is looking really great! Not seeing any real issues, other than the ones kindly reported, thank you @thalesfragoso!

@korken89
Copy link
Collaborator Author

Thanks for the comment @thalesfragoso, I have updated accordingly!

@korken89
Copy link
Collaborator Author

korken89 commented Mar 30, 2020

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. :)

Copy link
Member

@thalesfragoso thalesfragoso left a 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

@korken89
Copy link
Collaborator Author

@thalesfragoso Interesting approach! I have to give a deeper look to that.

I did a final commit to this PR.

  1. Changed name to DMAFrame, the frame has no connection to the serial so the name was misleading.
  2. Added a write and commit method, some serialization frameworks wants an &mut [u8] which now is possible.

src/dma.rs Outdated Show resolved Hide resolved
@thalesfragoso
Copy link
Member

@thalesfragoso Interesting approach! I have to give a deeper look to that.

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.

@MathiasKoch
Copy link
Collaborator

Works beautifully here, with both the frame reader and receiver timeout for receiving AT commands! Thanks!

@korken89
Copy link
Collaborator Author

korken89 commented Apr 2, 2020

Thanks for the review @thalesfragoso, I have updated the code!
I think this is ready for merge now. :)

@MathiasKoch Awesome, glad to hear that you are already getting use from it!

@korken89
Copy link
Collaborator Author

korken89 commented Apr 2, 2020

@MabezDev Whenever you feel ready, this should now be ready for merge.

@MabezDev
Copy link
Member

MabezDev commented Apr 2, 2020

@korken89 I was just writing up a comment asking if this was good to go 😂. This looks really great, thanks for the contribution!

@MabezDev MabezDev merged commit a3931e2 into stm32-rs:master Apr 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants