stm32h7xx-hal 0.13.1

Hardware Abstraction Layer implementation for STM32H7 series microcontrollers
Documentation
//! Example that transmits serial data using the DMA
//!
//! The first part of the example transmits 10 bytes over serial.
//!
//! The maximum transfer length for DMA1/DMA2 is limited to 65_535 items by
//! hardware. The second part of this example demonstrates splitting a transfer
//! into chunks and using the `next_transfer_with` method to start each part of
//! the transfer.

#![allow(clippy::transmute_ptr_to_ptr)]
#![deny(warnings)]
#![no_main]
#![no_std]

use core::{mem, mem::MaybeUninit};

use cortex_m_rt::entry;
#[macro_use]
mod utilities;
use stm32h7xx_hal::{pac, prelude::*};

use stm32h7xx_hal::dma::{
    dma::{DmaConfig, StreamsTuple},
    MemoryToPeripheral, Transfer,
};

use log::info;

// DMA1/DMA2 cannot interact with our stack. Instead, buffers for use with the
// DMA must be placed somewhere that DMA1/DMA2 can access. In this case we use
// AXI SRAM.
//
// The runtime does not initialise these SRAM banks
#[link_section = ".axisram.buffers"]
static mut SHORT_BUFFER: MaybeUninit<[u8; 10]> = MaybeUninit::uninit();

#[link_section = ".axisram.buffers"]
static mut LONG_BUFFER: MaybeUninit<[u32; 0x8000]> = MaybeUninit::uninit();

#[entry]
fn main() -> ! {
    utilities::logger::init();
    let dp = pac::Peripherals::take().unwrap();

    // Constrain and Freeze power
    info!("Setup PWR...                  ");
    let pwr = dp.PWR.constrain();
    let pwrcfg = example_power!(pwr).freeze();

    // Constrain and Freeze clock
    info!("Setup RCC...                  ");
    let rcc = dp.RCC.constrain();
    let ccdr = rcc
        .sys_ck(200.MHz())
        .pll1_q_ck(200.MHz())
        .freeze(pwrcfg, &dp.SYSCFG);

    // Acquire the GPIOC peripheral. This also enables the clock for
    // GPIOC in the RCC register.
    let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);

    let tx = gpioc.pc10.into_alternate();
    let rx = gpioc.pc11.into_alternate();

    info!("");
    info!("stm32h7xx-hal example - Serial DMA");
    info!("");

    // Configure the serial peripheral.
    let serial = dp
        .USART3
        .serial(
            (tx, rx),
            1_000_000.bps(),
            ccdr.peripheral.USART3,
            &ccdr.clocks,
        )
        .unwrap();

    let (tx, _rx) = serial.split();

    // Initialise the source buffer, without taking any references to
    // uninitialised memory
    let short_buffer: &'static mut [u8; 10] = {
        let buf: &mut [MaybeUninit<u8>; 10] =
            unsafe { mem::transmute(&mut SHORT_BUFFER) };

        for (i, value) in buf.iter_mut().enumerate() {
            unsafe {
                value.as_mut_ptr().write(i as u8 + 96); // 0x60, 0x61, 0x62...
            }
        }
        unsafe { mem::transmute(buf) }
    };
    // view u32 buffer as u8. Endianess is undefined (little-endian on STM32H7)
    let long_buffer: &'static mut [u8; 0x2_0010] = {
        let buf: &mut [MaybeUninit<u32>; 0x8004] =
            unsafe { mem::transmute(&mut LONG_BUFFER) };

        for (i, value) in buf.iter_mut().enumerate() {
            unsafe {
                value.as_mut_ptr().write(i as u32);
            }
        }
        unsafe { mem::transmute(buf) }
    };

    // Setup the DMA transfer on stream 0
    //
    // We need to specify the direction with a type annotation, since DMA
    // transfers both to and from the UART are possible
    let streams = StreamsTuple::new(dp.DMA1, ccdr.peripheral.DMA1);

    let config = DmaConfig::default().memory_increment(true);

    let mut transfer: Transfer<_, _, MemoryToPeripheral, _, _> =
        Transfer::init(streams.0, tx, &mut short_buffer[..], None, config);

    transfer.start(|serial| {
        // This closure runs right after enabling the stream

        // Enable DMA Tx buffer by setting the DMAT bit in the USART_CR3
        // register
        serial.enable_dma_tx();
    });

    // Wait for transfer to complete
    while !transfer.get_transfer_complete_flag() {}

    // Disable DMA Tx once complete (or before)

    info!("Continuing with chunked transfer!");

    // Split the long buffer into chunks: Hardware supports 65_535 max.
    //
    // The last chunk will be length 16, compared to all the others which will
    // be length 32_768.
    for mut chunk in &mut long_buffer.chunks_mut(32_768) {
        // Using `next_transfer_with`
        let _current = transfer
            .next_transfer_with(|mut old, current, remaining| {
                // Check that we really did complete the current transfer
                assert_eq!(remaining, 0);

                mem::swap(&mut old, &mut chunk);

                (old, current)
            })
            .unwrap();

        // Using `next_transfer`: this is equivalent to the above (except the
        // assert) but less flexible
        //transfer.next_transfer(chunk).unwrap();

        // Wait for transfer to complete
        while !transfer.get_transfer_complete_flag() {}
    }

    transfer.pause(|serial| {
        // At this point, the DMA transfer is done, but the data is still in the
        // UART output FIFO. Wait for it to complete
        while !serial.is_txe() {}
    });

    info!("Chunked transfer complete!");

    let (_stream, _serial, _, _) = transfer.free();

    // We could re-use the stream or serial here

    loop {
        cortex_m::asm::nop()
    }
}