My setup:
- The signal is connected to the analog input of the STM32F767.
- ADC sampling rate should be at least 192kHz because input signal frequency is between 1Hz and 40kHz.
- Also STM32 connected to the client via ETH port (UDP protocol).
My question:
- How to setup an asynchronous reading from the ADC using DMA?
Will be nice to have an interrupt on half of buffer ready. And transfer ready samples to the client via eth while another half of buffer will be prepared.
Already tried:
Following code reads and transfers the samples in the main loop.
It is not fast enough, and cycle time is not a constant.
Full example on GitHub.
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#![allow(non_snake_case)]
use defmt::*;
use heapless::Vec;
use embassy_executor::Spawner;
use embassy_net::udp::UdpSocket;
use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, PacketMetadata};
use embassy_time::{Duration, Timer, Delay};
use embassy_stm32::adc::{Adc, SampleTime};
use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::{interrupt, Config};
// use embedded_io::asynch::Write;
use rand_core::RngCore;
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};
// T, uc QSIZE
// 976.563 1 024
// 488.281 2 048
// 244.141 4 096
// 122.070 8 192
// 61.035 16 384
// 30.518 32 768
// 15.259 65 536
// 7.629 131 072
// 3.815 262 144
// 1.907 524 288
const udpPort: u16 = 15180;
const SYN: u8 = 22;
const EOT: u8 = 4;
const ADC_READ_DELAY: Duration = Duration::from_micros(61);
const QSIZE: usize = 512;
const QSIZE_DOUBLE: usize = QSIZE * 2;
macro_rules! singleton {
($val:expr) => {{
type T = impl Sized;
static STATIC_CELL: StaticCell<T> = StaticCell::new();
let (x,) = STATIC_CELL.init(($val,));
x
}};
}
type Device = Ethernet<'static, ETH, GenericSMI>;
#[embassy_executor::task]
async fn net_task(stack: &'static Stack<Device>) -> ! {
stack.run().await
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
info!("[main] enter");
let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200));
let dp = embassy_stm32::init(config);
let mut adcPin = dp.PA3;
let mut adc = Adc::new(dp.ADC1, &mut Delay);
adc.set_sample_time(SampleTime::Cycles480);
// let mut vrefint_channel = adc.enable_vrefint();
// Generate random seed.
let mut rng = Rng::new(dp.RNG);
let mut seed = [0; 8];
rng.fill_bytes(&mut seed);
let seed = u64::from_le_bytes(seed);
let eth_int = interrupt::take!(ETH);
let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
let device = Ethernet::new(
singleton!(PacketQueue::<16, 16>::new()),
dp.ETH,
eth_int,
dp.PA1,
dp.PA2,
dp.PC1,
dp.PA7,
dp.PC4,
dp.PC5,
dp.PG13,
dp.PB13,
dp.PG11,
GenericSMI,
mac_addr,
0,
);
// let config = embassy_net::Config::Dhcp(Default::default());
let localIp = Ipv4Address::new(192, 168, 120, 173);
let config = embassy_net::Config::Static(embassy_net::StaticConfig {
address: Ipv4Cidr::new(localIp, 24),
dns_servers: Vec::new(),
gateway: Some(Ipv4Address::new(192, 168, 120, 1)),
});
// Init network stack
let stack = &*singleton!(
Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)
);
// Launch network task
unwrap!(spawner.spawn(net_task(&stack)));
info!("Network task initialized");
// Then we can use it!
let mut rx_meta = [PacketMetadata::EMPTY; 16];
let mut rx_buffer = [0; QSIZE_DOUBLE];
let mut tx_meta = [PacketMetadata::EMPTY; 16];
let mut tx_buffer = [0; QSIZE_DOUBLE];
let mut bufDouble = [0; QSIZE_DOUBLE];
loop {
let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);
info!("UDP bind on {}:{}...", localIp, udpPort);
let r = socket.bind(udpPort);
info!("UDP bind result: {:?}", r);
if let Err(e) = r {
info!("UDP bind error: {:?}", e);
continue;
}
info!("UDP server ready!");
// let mut dataBuf = [0_u16; QSIZE];
// for i in 0..QSIZE {
// dataBuf[i] = (i as u16) * 8u16;
// }
// let mut bufDouble = [0_u8; QSIZE_DOUBLE];
// let mut j = 0;
// for (i, item) in dataBuf.iter().enumerate() {
// let bytes = item.to_be_bytes();
// j = i * 2;
// bufDouble[j] = bytes[0];
// bufDouble[j + 1] = bytes[1];
// }
loop {
info!("waiting handshake message...");
let (_n, remoteAddr) = socket.recv_from(&mut bufDouble).await.unwrap();
info!("received message from {:?}: {:?}", remoteAddr, bufDouble);
if handshakeReceived(&bufDouble) {
info!("received handshake from {:?}", remoteAddr);
loop {
for i in (0..QSIZE_DOUBLE).step_by(2) {
let measured = adc.read(&mut adcPin);
let bytes = measured.to_be_bytes();
bufDouble[i] = bytes[0];
bufDouble[i + 1] = bytes[1];
// Timer::after(ADC_READ_DELAY).await;
// info!("measured: {}", measured);
}
if socket.is_open() {
let r = socket.send_to(&bufDouble, remoteAddr).await;
if let Err(e) = r {
info!("write error: {:?}", e);
break;
}
} else {
info!("socket is not open");
break;
}
// Timer::after(Duration::from_millis(1000)).await;
}
} else {
info!("received wrong handshake from({:?}): {:?}", remoteAddr, bufDouble);
}
}
}
}
/// return true if handshake received
fn handshakeReceived(buf: & [u8; QSIZE_DOUBLE]) -> bool {
buf[0] == SYN && buf[1] == EOT
}
// icrementing index up to QSIZE, then return it to 0
// fn incrementLoop(index: usize) -> usize {
// (index + 1) % QSIZE
// }