Files
templates/c/set-protocol/ports/stm32g474-periph/wave_hal.c

117 lines
8.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Аппаратный исполнитель генератора: таблица uint16_t в SRAM -> DMA1 -> DAC1/PA4.
* TIM6 задаёт частоту выборок аппаратными событиями TRGO; CPU не отправляет каждый отсчёт.
* Модуль владеет только DAC channel 1, TIM6 и DMA1 channel 1 и DMAMUX request DAC1_CHANNEL1.
*/
/* HAL backend owned by this board; the shared protocol remains hardware independent. */
#include "wave_hal.h"
#include "stm32g4xx_hal.h"
static DAC_HandleTypeDef dac;
static DMA_HandleTypeDef dma;
static TIM_HandleTypeDef timer;
static uint8_t initialized;
/* Callback остановки set_wave_port. context не используется: имеется один статический комплект HAL handles.
* Сначала останавливаем источник триггеров, затем DMA/DAC. После первого запуска держим PA4 на нулевом коде;
* до инициализации функция ничего не делает. Второй канал DAC не перенастраивается. */
static void stop(void *context)
{
(void)context;
if (!initialized) return;
(void)HAL_TIM_Base_Stop(&timer);
(void)HAL_DAC_Stop_DMA(&dac,DAC_CHANNEL_1);
/* Keep channel 1 driving zero after stop; channel 2 is untouched. */
{
DAC_ChannelConfTypeDef config={0};
/* G4 при AHB=160 МГц требует HFSEL; выход направляем на внешний PA4. */
config.DAC_HighFrequency=DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
config.DAC_ConnectOnChipPeripheral=DAC_CHIPCONNECT_EXTERNAL;
config.DAC_UserTrimming=DAC_TRIMMING_FACTORY;
config.DAC_Trigger=DAC_TRIGGER_NONE; config.DAC_OutputBuffer=DAC_OUTPUTBUFFER_ENABLE;
(void)HAL_DAC_ConfigChannel(&dac,&config,DAC_CHANNEL_1);
(void)HAL_DAC_SetValue(&dac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,0);
(void)HAL_DAC_Start(&dac,DAC_CHANNEL_1);
}
}
/* Callback запуска: samples — таблица кодов DAC, count — число отсчётов, rate — выборок/с.
* Таблица должна оставаться действительной и неизменной до stop(). Возвращает 0 при успехе, 1 при ошибке.
* Проверки формы сигнала выполняет общий генератор; здесь проверяется аппаратная выполнимость. */
static int start(void *context,const uint16_t *samples,size_t count,uint32_t rate)
{
GPIO_InitTypeDef gpio={0};
DAC_ChannelConfTypeDef config={0};
/* G4 при AHB=160 МГц требует HFSEL; выход направляем на внешний PA4. */
config.DAC_HighFrequency=DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
config.DAC_ConnectOnChipPeripheral=DAC_CHIPCONNECT_EXTERNAL;
config.DAC_UserTrimming=DAC_TRIMMING_FACTORY;
TIM_MasterConfigTypeDef master={0};
/* При делителе APB1=2 таймер получает удвоенную частоту PCLK1, то есть 160 МГц.
* Эта формула соответствует дереву тактирования из board.c. */
uint32_t ticks,psc,clock=HAL_RCC_GetPCLK1Freq()*2u;
uintptr_t address=(uintptr_t)samples;
(void)context;
/* Разрешены 2..SET_WAVE_MAX отсчётов, частота до 1 МГц и только точное деление частоты таймера.
* Для DMA используем SRAM1/2: буфер должен лежать в 0x20000000..0x20017FFF; CCM оставляем архиву. */
if (!rate || rate>SET_WAVE_MAX_RATE || count<2 || count>SET_WAVE_MAX || clock%rate ||
address<0x20000000u || address+count*2>0x20018000u) return 1;
ticks=clock/rate;
/* Подбираем целые (PSC+1) и (ARR+1), оба не больше 65536.
* Частота выборок = timer_clock / ((PSC+1)*(ARR+1)); в регистры HAL передаёт значения минус один. */
for (psc=1;psc<=65536;++psc) if (ticks%psc==0 && ticks/psc<=65536) break;
if (psc>65536 || ticks<psc) return 1;
stop(context);
__HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMAMUX1_CLK_ENABLE(); __HAL_RCC_DAC1_CLK_ENABLE(); __HAL_RCC_TIM6_CLK_ENABLE();
gpio.Pin=GPIO_PIN_4; gpio.Mode=GPIO_MODE_ANALOG; gpio.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&gpio);
timer.Instance=TIM6; timer.Init.Prescaler=psc-1; timer.Init.Period=ticks/psc-1;
timer.Init.CounterMode=TIM_COUNTERMODE_UP; timer.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE;
/* Remove TRGO before Base_Init generates its prescaler update event. */
master.MasterOutputTrigger=TIM_TRGO_RESET;
master.MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&timer,&master)!=HAL_OK ||
HAL_TIM_Base_Init(&timer)!=HAL_OK) return 1;
__HAL_TIM_SET_COUNTER(&timer,0); __HAL_TIM_CLEAR_FLAG(&timer,TIM_FLAG_UPDATE);
master.MasterOutputTrigger=TIM_TRGO_UPDATE;
if (HAL_TIMEx_MasterConfigSynchronization(&timer,&master)!=HAL_OK) return 1;
/* DMA пишет полуслова в фиксированный регистр DAC, последовательно читая таблицу.
* Circular автоматически возвращает адрес к началу; высокий приоритет уменьшает риск underrun. */
dma.Instance=DMA1_Channel1; dma.Init.Request=DMA_REQUEST_DAC1_CHANNEL1;
dma.Init.Direction=DMA_MEMORY_TO_PERIPH; dma.Init.PeriphInc=DMA_PINC_DISABLE;
dma.Init.MemInc=DMA_MINC_ENABLE; dma.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;
dma.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; dma.Init.Mode=DMA_CIRCULAR;
dma.Init.Priority=DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&dma)!=HAL_OK) return 1;
/* Связываем DAC с DMA handle: HAL использует эту связь при запуске и остановке передачи. */
dac.Instance=DAC1; __HAL_LINKDMA(&dac,DMA_Handle1,dma);
if (HAL_DAC_Init(&dac)!=HAL_OK) return 1;
initialized=1;
config.DAC_Trigger=DAC_TRIGGER_T6_TRGO; config.DAC_OutputBuffer=DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&dac,&config,DAC_CHANNEL_1)!=HAL_OK) goto fail;
__HAL_DAC_CLEAR_FLAG(&dac,DAC_FLAG_DMAUDR1);
/* DMA fills the next sample after each trigger, preserving cyclic phase. */
/* Первым триггером выводится заранее записанный последний отсчёт; далее идут 0,1,... из DMA.
* Таймер запускается последним, когда DAC и DMA уже готовы. Приведение к uint32_t* требуется API HAL,
* но размер реальной передачи задаётся DMA как HALFWORD (16 бит). */
if (HAL_DAC_SetValue(&dac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,samples[count-1])!=HAL_OK ||
HAL_DAC_Start_DMA(&dac,DAC_CHANNEL_1,(uint32_t *)samples,(uint32_t)count,DAC_ALIGN_12B_R)!=HAL_OK ||
HAL_TIM_Base_Start(&timer)!=HAL_OK) goto fail;
return 0;
/* Если запуск после инициализации DAC не завершился, возвращаем выход к нулю и сообщаем ошибку. */
fail:
stop(context); return 1;
}
/* Возвращает таблицу callback-функций для общего генератора; само оборудование ещё не запускается. */
set_wave_port wave_hal_port(void)
{
set_wave_port port={0,start,stop}; return port;
}
/* Опрос ошибки канала DMA и underrun DAC. У G4 нет F4 stream FIFO/direct-mode flags.
* NVIC-прерывания DMA/DAC здесь не включены: флаги проверяет основной цикл, остановку выполняет wave_output_poll(). */
int wave_hal_fault(void)
{
return initialized && (__HAL_DMA_GET_FLAG(&dma,DMA_FLAG_TE1) ||
__HAL_DAC_GET_FLAG(&dac,DAC_FLAG_DMAUDR1));
}