/** * Аппаратный исполнитель генератора: таблица 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