#include "init.h" // Жестко заданный адрес регистра DAC DHR12R1 (12-bit right aligned, channel 1) // Это надежнее, чем использовать &DAC->DHR12R1 #define DAC_DHR12R1_ADDR 0x40007408 void GPIO_Ini(void) { GPIO_InitTypeDef gpio; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); gpio.GPIO_Pin = GPIO_Pin_4; // PA4 - выход ЦАП 1 gpio.GPIO_Mode = GPIO_Mode_AN; // Обязательно аналоговый режим! gpio.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &gpio); } void TIM_Ini(void) { TIM_TimeBaseInitTypeDef tim; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructInit(&tim); // Обнуляем структуру tim.TIM_Prescaler = 0; tim.TIM_CounterMode = TIM_CounterMode_Up; // Чуть увеличим период для стабильности (частота ~42 кГц) // 84МГц / 2000 = 42кГц. Синус 100 точек = 420 Гц. tim.TIM_Period = 2000 - 1; tim.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &tim); // Триггер по обновлению таймера (TRGO) TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); TIM_Cmd(TIM2, ENABLE); } void DAC_Ini(void) { DAC_InitTypeDef dac; RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); // Важно: настраиваем структуру dac.DAC_Trigger = DAC_Trigger_T2_TRGO; // Работаем по таймеру 2 dac.DAC_WaveGeneration = DAC_WaveGeneration_None; // Своя волна, не встроенный шум dac.DAC_OutputBuffer = DAC_OutputBuffer_Enable; // Буфер для раскачки выхода DAC_Init(DAC_Channel_1, &dac); // Включаем канал DAC_Cmd(DAC_Channel_1, ENABLE); // Разрешаем ЦАПу просить данные у DMA DAC_DMACmd(DAC_Channel_1, ENABLE); } void DMA_Ini(uint16_t *buffer, uint32_t size) { DMA_InitTypeDef dma; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // Отключаем поток перед настройкой (на всякий случай) DMA_Cmd(DMA1_Stream5, DISABLE); DMA_DeInit(DMA1_Stream5); // !! Очищаем структуру дефолтными значениями, чтобы убрать "мусор" !! DMA_StructInit(&dma); dma.DMA_Channel = DMA_Channel_7; // Канал 7 для DAC1 dma.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDR; // Куда класть (регистр ЦАП) dma.DMA_Memory0BaseAddr = (uint32_t)buffer; // Откуда брать (массив) dma.DMA_DIR = DMA_DIR_MemoryToPeripheral; // Направление: Память -> Периферия dma.DMA_BufferSize = size; // Сколько точек dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Адрес ЦАП не меняем dma.DMA_MemoryInc = DMA_MemoryInc_Enable; // Адрес массива увеличиваем // Важно: размеры данных dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 бит (регистр) dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16 бит (массив uint16_t) dma.DMA_Mode = DMA_Mode_Circular; // Зацикливаем (бесконечное воспроизведение) dma.DMA_Priority = DMA_Priority_High; dma.DMA_FIFOMode = DMA_FIFOMode_Disable; // Прямой режим (Direct) DMA_Init(DMA1_Stream5, &dma); // Запускаем DMA DMA_Cmd(DMA1_Stream5, ENABLE); }