Add shared waveform processing, generation and firmware image support

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent 2ad29e7ffd
commit cc22c803d1
36 changed files with 2591 additions and 61 deletions

View File

@@ -0,0 +1,53 @@
#include "set_wave_f407.h"
#include "stm32f4xx.h"
static void stop(void *context) {
unsigned limit=100000; (void)context;
RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_DMA1EN;
RCC->APB1ENR|=RCC_APB1ENR_DACEN|RCC_APB1ENR_TIM6EN;
(void)RCC->APB1ENR;
TIM6->CR1=0;
DAC->CR&=~(DAC_CR_DMAEN1|DAC_CR_TEN1);
DMA1_Stream5->CR&=~DMA_SxCR_EN;
while((DMA1_Stream5->CR&DMA_SxCR_EN)&&--limit) {}
/* Leave other DAC channel untouched. Channel 1 outputs zero on stop. */
DAC->DHR12R1=0;
DMA1->HIFCR=0xF40u;
DAC->SR=DAC_SR_DMAUDR1;
}
static int start(void *context,const uint16_t *samples,size_t count,uint32_t rate) {
set_wave_f407 *c=(set_wave_f407 *)context;
uint32_t ticks,psc,arr; uintptr_t address=(uintptr_t)samples;
if(!c||!rate||rate>SET_WAVE_MAX_RATE||count<2||count>SET_WAVE_MAX||c->timer_hz%rate
||address<0x20000000u||address+count*2>0x20020000u) return 1;
ticks=c->timer_hz/rate;
for(psc=1;psc<=65536;++psc) if(ticks%psc==0&&ticks/psc<=65536) break;
if(psc>65536||ticks<psc) return 1;
arr=ticks/psc;
stop(context);
if(DMA1_Stream5->CR&DMA_SxCR_EN) return 1;
GPIOA->MODER|=3u<<8;
GPIOA->PUPDR&=~(3u<<8);
TIM6->PSC=psc-1;TIM6->ARR=arr-1;TIM6->CNT=0;
TIM6->CR2=0;TIM6->EGR=TIM_EGR_UG;TIM6->SR=0;
TIM6->CR2=TIM_CR2_MMS_1; /* update -> TRGO */
DMA1_Stream5->PAR=(uint32_t)&DAC->DHR12R1;
DMA1_Stream5->M0AR=(uint32_t)samples;DMA1_Stream5->NDTR=(uint32_t)count;
DMA1_Stream5->FCR=0;
DMA1_Stream5->CR=(7u<<25)|DMA_SxCR_DIR_0|DMA_SxCR_MINC|DMA_SxCR_CIRC
|DMA_SxCR_PSIZE_0|DMA_SxCR_MSIZE_0|DMA_SxCR_PL_1;
/* Prime the previous period's last sample; DMA supplies the next value
* after each trigger. Cyclic order is last,0,1,... with one priming tick. */
DAC->CR&=~0xFFFFu;
DAC->DHR12R1=samples[count-1];
DAC->CR|=DAC_CR_EN1|DAC_CR_TEN1|DAC_CR_DMAEN1; /* TSEL1=0: TIM6 */
DMA1_Stream5->CR|=DMA_SxCR_EN;
TIM6->CR1=TIM_CR1_CEN;
return 0;
}
set_wave_port set_wave_f407_port(set_wave_f407 *context) {
set_wave_port port={context,start,stop};return port;
}
int set_wave_f407_fault(void) {
return (DMA1->HISR&(DMA_HISR_TEIF5|DMA_HISR_DMEIF5|DMA_HISR_FEIF5))
||(DAC->SR&DAC_SR_DMAUDR1);
}

View File

@@ -0,0 +1,12 @@
#ifndef SET_WAVE_F407_H
#define SET_WAVE_F407_H
#include "../../include/set_wavegen.h"
/* Owns PA4, DAC channel 1, TIM6 and DMA1 stream 5/channel 7.
* timer_hz is TIM6 input clock (twice APB1 when APB1 prescaler !=1).
* Rate: 1..SET_WAVE_MAX_RATE (1 MS/s), exact divisors of timer_hz only.
* Context and sample buffer must outlive playback; buffer must be in SRAM. */
typedef struct { uint32_t timer_hz; } set_wave_f407;
set_wave_port set_wave_f407_port(set_wave_f407 *context);
/* Nonzero on DMA error or DAC underrun; caller stops state on failure. */
int set_wave_f407_fault(void);
#endif

View File

@@ -0,0 +1,37 @@
#include "set_wave_g474.h"
static void stop(void *context) {
set_wave_g474 *c=(set_wave_g474 *)context;
HAL_TIM_Base_Stop(c->timer);
HAL_DAC_Stop_DMA(c->dac,DAC_CHANNEL_1);
/* Disable triggering so zero is applied without another timer edge. */
CLEAR_BIT(c->dac->Instance->CR,DAC_CR_TEN1);
HAL_DAC_SetValue(c->dac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,0);
HAL_DAC_Start(c->dac,DAC_CHANNEL_1);
}
static int start(void *context,const uint16_t *samples,size_t count,uint32_t rate) {
set_wave_g474 *c=(set_wave_g474 *)context;
uint32_t ticks,psc; DMA_HandleTypeDef *dma;
if(!c||!c->dac||!c->timer||c->dac->Instance!=DAC1||c->timer->Instance!=TIM6
||!rate||rate>50000||count<2||count>SET_WAVE_MAX||c->timer_hz%rate) return 1;
dma=c->dac->DMA_Handle1;
if(!dma||dma->Init.Mode!=DMA_CIRCULAR||dma->Init.MemDataAlignment!=DMA_MDATAALIGN_HALFWORD
||dma->Init.PeriphDataAlignment!=DMA_PDATAALIGN_HALFWORD
||dma->Init.Request!=DMA_REQUEST_DAC1_CHANNEL1||dma->Init.MemInc!=DMA_MINC_ENABLE) return 1;
ticks=c->timer_hz/rate;
for(psc=1;psc<=65536;++psc) if(ticks%psc==0&&ticks/psc<=65536) break;
if(psc>65536||ticks<psc) return 1;
stop(context);
__HAL_TIM_SET_PRESCALER(c->timer,psc-1);
__HAL_TIM_SET_AUTORELOAD(c->timer,ticks/psc-1);
__HAL_TIM_SET_COUNTER(c->timer,0);
c->timer->Instance->EGR=TIM_EGR_UG;
__HAL_TIM_CLEAR_FLAG(c->timer,TIM_FLAG_UPDATE);
MODIFY_REG(c->dac->Instance->CR,DAC_CR_TSEL1|DAC_CR_TEN1,DAC_TRIGGER_T6_TRGO);
HAL_DAC_SetValue(c->dac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,samples[count-1]);
if(HAL_DAC_Start_DMA(c->dac,DAC_CHANNEL_1,(uint32_t *)samples,(uint32_t)count,DAC_ALIGN_12B_R)!=HAL_OK) return 1;
if(HAL_TIM_Base_Start(c->timer)!=HAL_OK) {stop(context);return 1;}
return 0;
}
set_wave_port set_wave_g474_port(set_wave_g474 *context) {
set_wave_port port={context,start,stop};return port;
}

View File

@@ -0,0 +1,10 @@
#ifndef SET_WAVE_G474_H
#define SET_WAVE_G474_H
#include "stm32g4xx_hal.h"
#include "../../include/set_wavegen.h"
/* CubeMX owns initialization: DAC1 channel1 PA4, TIM6 TRGO_UPDATE,
* DMA_REQUEST_DAC1_CHANNEL1, circular, halfword/halfword, memory increment.
* Supply TIM6 input clock, not APB bus clock. DMA buffer in SRAM. */
typedef struct { DAC_HandleTypeDef *dac; TIM_HandleTypeDef *timer; uint32_t timer_hz; } set_wave_g474;
set_wave_port set_wave_g474_port(set_wave_g474 *context);
#endif