diff --git a/src/main.rs b/src/main.rs index 998c2b7..bfe5d73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,8 @@ mod si5153; #[app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [SPI3])] mod app { + use core::mem; + use embedded_hal::blocking::spi::transfer; use num::Complex; use stm32f4xx_hal::{ @@ -32,13 +34,17 @@ mod app { }, Adc, }, - dma::{self, config::DmaConfig, PeripheralToMemory, Stream0, StreamsTuple, Transfer}, + dma::{ + self, config::DmaConfig, PeripheralToMemory, Stream0, Stream7, StreamsTuple, Transfer, + }, gpio::{self, gpioa, gpioc, Analog, Output, PushPull}, i2c::{self, I2c}, - pac::{ADC1, DMA2, I2C1, SPI1, TIM2, TIM4}, + pac::{ADC1, DMA1, DMA2, I2C1, SPI1, TIM2, TIM4}, prelude::*, spi::{self, Spi}, - timer::{self, Channel, Channel1, Channel3, ChannelBuilder, CounterHz, Event, PwmHz}, + timer::{ + self, Channel, Channel1, Channel3, ChannelBuilder, CounterHz, Event, PwmHz, CCR, CCR3, + }, }; use cortex_m::{asm, singleton}; @@ -67,15 +73,25 @@ mod app { //mic_in: gpio::Pin<'A', 4, Analog>, i_in: gpio::Pin<'A', 1, Analog>, q_in: gpio::Pin<'A', 0, Analog>, - phase: f32, i_offset: f32, q_offset: f32, audio_pwm: AudioPwm, usb_filter: filters::FirFilter<63>, - transfer: + adc_transfer: Transfer, 0, Adc, PeripheralToMemory, &'static mut [u16; 256]>, iq_buffer: Option<&'static mut [u16; 256]>, + pwm_transfer: Transfer< + Stream7, + 2, + CCR, + stm32f4xx_hal::dma::MemoryToPeripheral, + &'static mut [u16; 256], + >, + audio_buffer: Option<&'static mut [u16; 256]>, + phase: f32, + audio_max_duty: u16, + disp_led: gpioa::PA10>, disp_cs: gpioa::PA15>, disp: ST7735, gpio::Pin<'A', 12, Output>, gpio::Pin<'A', 11, Output>>, @@ -198,17 +214,47 @@ mod app { .memory_increment(true) .double_buffer(false); - let mut transfer = + let mut adc_transfer = Transfer::init_peripheral_to_memory(dma2.0, adc1, iq_buff1, None, config); - transfer.start(|_| {}); + adc_transfer.start(|_| {}); - defmt::info!("DMA Setup done"); + defmt::info!("ADC DMA Setup done"); + + let ccr3_tim4: CCR3 = unsafe { mem::transmute_copy(&cx.device.TIM4) }; let audio_out = Channel3::new(gpiob.pb8); - let mut audio_pwm = cx.device.TIM4.pwm_hz(audio_out, 192.kHz(), &clocks); + let mut audio_pwm = cx.device.TIM4.pwm_hz(audio_out, 8.kHz(), &clocks); audio_pwm.enable(Channel::C3); - audio_pwm.set_duty(Channel::C3, 0u16); + audio_pwm.set_duty(Channel::C3, audio_pwm.get_max_duty() / 2); + let audio_max_duty = audio_pwm.get_max_duty(); + + defmt::info!("Max duty: {}", audio_pwm.get_max_duty()); + + unsafe { + (*TIM4::ptr()).dier.modify(|_, w| { + w.tde().set_bit(); // enable DMA trigger + w.cc3de().set_bit(); // dma on Update + w + }); + }; + + let audio_buff1 = singleton!(: [u16; 256] = [100; 256]).unwrap(); + let audio_buff2 = singleton!(: [u16; 256] = [100; 256]).unwrap(); + + let dma1 = StreamsTuple::new(cx.device.DMA1); + + let config = DmaConfig::default() + .transfer_complete_interrupt(true) + .memory_increment(true) + .double_buffer(false); + + let mut pwm_transfer = + Transfer::init_memory_to_peripheral(dma1.7, ccr3_tim4, audio_buff1, None, config); + + pwm_transfer.start(|_| {}); + + defmt::info!("PWM DMA Setup done"); let mut rx_en = gpioa.pa7.into_push_pull_output(); rx_en.set_high(); @@ -226,14 +272,18 @@ mod app { //mic_in, i_in, q_in, - phase: 0.0, i_offset: 2048.0, q_offset: 2048.0, audio_pwm, usb_filter: filters::usb_firfilter(), - transfer, + adc_transfer, iq_buffer: Some(iq_buff2), + pwm_transfer, + audio_buffer: Some(audio_buff2), + phase: 0.0, + audio_max_duty, + disp_led, disp_cs, disp, @@ -246,7 +296,7 @@ mod app { #[task(priority = 0, local = [disp, col_pos, max_mag])] async fn update_display(cx: update_display::Context, col: [Complex; 128]) { for samp in col { - let mag = (samp.re.pow(2) + samp.im.pow(2)) as f32; + let mag = (samp.re.pow(2) + samp.im.pow(2)) / 5.0 as f32; *cx.local.max_mag = if mag > *cx.local.max_mag { mag } else { @@ -274,15 +324,15 @@ mod app { defmt::info!("Position is {}", cx.local.col_pos); } - #[task(binds = DMA2_STREAM0, local = [transfer, iq_buffer, board_led, i_offset, q_offset, usb_filter])] + #[task(binds = DMA2_STREAM0, local = [adc_transfer, iq_buffer, board_led, i_offset, q_offset, usb_filter])] fn dma2_stream0(cx: dma2_stream0::Context) { let (buffer, _) = cx .local - .transfer + .adc_transfer .next_transfer(cx.local.iq_buffer.take().unwrap()) .unwrap(); - defmt::info!("Transfer complete"); + defmt::info!("ADC transfer complete"); cx.local.board_led.toggle(); let mut samples = [Complex::::default(); 128]; @@ -296,13 +346,14 @@ mod app { let i_sample = (i_raw as f32) - *cx.local.i_offset; let q_sample = (q_raw as f32) - *cx.local.q_offset; - samples[idx] = Complex::new(i_sample as f32 / 4096.0, q_sample as f32 / 4096.0); + samples[idx] = Complex::new(q_sample as f32 / 4096.0, i_sample as f32 / 4096.0); } let mut fft_input = [Complex::::default(); 128]; + for idx in 0..samples.len() / 2 { - let _filtered = cx.local.usb_filter.compute(samples[idx]); - fft_input[idx] = samples[idx]; + let filtered = cx.local.usb_filter.compute(samples[idx]); + fft_input[idx] = filtered; } let spectrum = cfft_128(&mut fft_input); @@ -311,4 +362,29 @@ mod app { *cx.local.iq_buffer = Some(buffer); } + + #[task(binds = DMA1_STREAM7, local = [pwm_transfer, audio_buffer, phase, audio_max_duty])] + fn dma1_stream7(cx: dma1_stream7::Context) { + let (mut buffer, _) = cx + .local + .pwm_transfer + .next_transfer(cx.local.audio_buffer.take().unwrap()) + .unwrap(); + + defmt::info!("PWM transfer complete"); + + let phase_inc = core::f32::consts::PI * 2.0 / 8000.0 * 440.0; + + for i in 0..buffer.len() { + *cx.local.phase += phase_inc; + if *cx.local.phase > 2.0 * core::f32::consts::PI { + *cx.local.phase -= 2.0 * core::f32::consts::PI; + } + + buffer[i] = + ((cx.local.phase.sin() + 1.0) * (*cx.local.audio_max_duty as f32) / 4.0) as u16; + } + + *cx.local.audio_buffer = Some(buffer); + } }