12.2 DAC驱动实现 F429带有两个DAC,分别是DAC1和DAC2,我们这里使用了DAC1,驱动中还需要用到TIM6和DMA,方便我们配置不同的的频率,占空比和幅值。
12.2.1 第1步:引脚配置和DAC配置 配置代码如下,使用的PA4引脚做输出: - /*
- *********************************************************************************************************
- * 函 数 名: bsp_InitDAC1
- * 功能说明: 配置PA4/DAC1
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void bsp_InitDAC1(void)
- {
- /* 配置GPIO */
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
-
- /* 配置DAC引脚为模拟模式 PA4 / DAC_OUT1 */
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- }
-
- /* DAC通道1配置 */
- {
- DAC_InitTypeDef DAC_InitStructure;
-
- /* 使能DAC时钟 */
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
-
- DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; /* 选择软件触发, 软件修改DAC数据寄存器 */
- DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
- DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
- //DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
- DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
- DAC_Init(DAC_Channel_1, &DAC_InitStructure);
- DAC_Cmd(DAC_Channel_1, ENABLE);
- }
- }
复制代码特别注意。程序中关闭了DAC输出缓冲,即DAC参数成员DAC_InitStructure.DAC_OutputBuffer。关于DAC的缓冲问题,看前面12.1小节说明即可。
12.2.2 第2步:DAC的定时器触发和DMA配置 DAC的定时器触发和DMA配置如下: - /*
- *********************************************************************************************************
- * 函 数 名: dac1_InitForDMA
- * 功能说明: 配置PA4 为DAC_OUT1, 启用DMA2
- * 形 参: _BufAddr : DMA数据缓冲区地址
- * _Count : 缓冲区样本个数
- * _DacFreq : DAC样本更新频率
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void dac1_InitForDMA(uint32_t _BufAddr, uint32_t _Count, uint32_t _DacFreq)
- {
- uint16_t usPeriod;
- uint16_t usPrescaler;
- uint32_t uiTIMxCLK;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
-
- DMA_Cmd(DMA1_Stream5, DISABLE);
- DAC_DMACmd(DAC_Channel_1, DISABLE);
- TIM_Cmd(TIM6, DISABLE);
-
-
- /* TIM6配置 */
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
-
- uiTIMxCLK = SystemCoreClock / 2;
-
- if (_DacFreq < 100)
- {
- usPrescaler = 10000 - 1; /* 分频比 = 10000 */
- usPeriod = (uiTIMxCLK / 10000) / _DacFreq - 1; /* 自动重装的值 */
- }
- else if (_DacFreq < 3000)
- {
- usPrescaler = 100 - 1; /* 分频比 = 100 */
- usPeriod = (uiTIMxCLK / 100) / _DacFreq - 1; /* 自动重装的值 */
- }
- else /* 大于4K的频率,无需分频 */
- {
- usPrescaler = 0; /* 分频比 = 1 */
- usPeriod = uiTIMxCLK / _DacFreq - 1; /* 自动重装的值 */
- }
-
- TIM_TimeBaseStructure.TIM_Period = usPeriod;
- TIM_TimeBaseStructure.TIM_Prescaler = usPrescaler;
- TIM_TimeBaseStructure.TIM_ClockDivision = 0;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0000; /* TIM1 和 TIM8 必须设置 */
-
- TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
-
- /* 选择TIM6做DAC的触发时钟 */
- TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
- }
-
- /* DAC通道1配置 */
- {
- DAC_InitTypeDef DAC_InitStructure;
-
- /* 使能DAC时钟 */
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
-
- DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
- DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
- DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
- //DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
- DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
- DAC_Init(DAC_Channel_1, &DAC_InitStructure);
- DAC_Cmd(DAC_Channel_1, ENABLE);
- }
-
- /* DMA1_Stream5配置 */
- {
- DMA_InitTypeDef DMA_InitStructure;
-
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
-
- /* 配置DMA1 Stream 5 channel 7用于DAC1 */
- DMA_InitStructure.DMA_Channel = DMA_Channel_7;
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1;
- DMA_InitStructure.DMA_Memory0BaseAddr = _BufAddr;
- DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
- DMA_InitStructure.DMA_BufferSize = _Count;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;
- DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
- DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
- DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
- DMA_Init(DMA1_Stream5, &DMA_InitStructure);
- DMA_Cmd(DMA1_Stream5, ENABLE);
-
- /* 使能DAC通道1的DMA */
- DAC_DMACmd(DAC_Channel_1, ENABLE);
- }
-
- /* 使能定时器 */
- TIM_Cmd(TIM6, ENABLE);
- }
复制代码通过这个函数可以方便的设置DAC的输出波形频率。计算方法是: 配置的定时器触发频率 / DMA的缓冲个数 = 输出波形频率 其中DMA缓冲数据的个数就是输出波形一个周期的采样点数。程序中统一将其配置为128个点代表一个周期的波形,大家实际应用中配置的点数不要太少,否则波形不够漂亮。比如我们要出10KHz的波形,这个函数的配置就是:dac1_InitForDMA((uint32_t)&g_Wave1,128, 10000 * 128); 数组g_Wave1里面是128个波形采样点。 关于这个驱动代码,要注意TIM6的配置。F429的定时器从TIM1到TIM14的主频如下: - /*
- ********************************************************************************
- system_stm32f4xx.c 文件中 void SetSysClock(void) 函数对时钟的配置如下:
-
- HCLK = SYSCLK / 1 (AHB1Periph)
- PCLK2 = HCLK / 2 (APB2Periph)
- PCLK1 = HCLK / 4 (APB1Periph)
-
- 因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = PCLK1 x 2 = SystemCoreClock / 2;
- 因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = PCLK2 x 2 = SystemCoreClock;
-
- APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14
- APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
-
- TIM 更新周期是 = TIMCLK / (TIM_Period + 1)/(TIM_Prescaler + 1)
- ********************************************************************************
- */
复制代码由此可知,TIM6的主频是SystemCoreClock / 2。当主频是168MHz时,TIM6的时钟就是84MHz,TIM6更新周期 = TIM6CLK / (TIM_Period + 1)/(TIM_Prescaler+ 1),其中 TIM_Period就是定时器结构体成员TIM_TimeBaseStructure.TIM_Period。 TIM_Prescaler就是定时器结构体成员TIM_TimeBaseStructure.TIM_Prescaler。 另外还有非常重要的一点,TIM6是16位定时器,这两个参范围是0-65535,切不要超过65535。正是因为这个原因,程序中对不同的输出频率做了范围区分。
12.2.3 第3步:正弦波输出配置 正弦波的输出配置如下: - /*
- *********************************************************************************************************
- * 函 数 名: dac1_SetSinWave
- * 功能说明: DAC1输出正弦波
- * 形 参: _vpp : 幅度 0-4095;
- * _freq : 频率
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void dac1_SetSinWave(uint16_t _vpp, uint32_t _freq)
- {
- uint32_t i;
- uint32_t dac;
-
- TIM_Cmd(TIM6, DISABLE);
-
- /* 调整正弦波幅度 */
- for (i = 0; i < 128; i++)
- {
- dac = (g_SineWave128[i] * _vpp) / 4095;
- if (dac > 4095)
- {
- dac = 4095;
- }
- g_Wave1[i] = dac;
- }
-
- dac1_InitForDMA((uint32_t)&g_Wave1, 128, _freq * 128);
- }
复制代码正弦波输出128个采样点代表一个周期,同时程序里面增加了一个幅值设置功能,范围0到4095。实际DAC输出的波形频率由前面第2步函数dac1_InitForDMA实现。比如我们要实现频率10KHz,幅值4095正弦波,那么配置就是:dac1_SetSinWave(4095, 10000)。
12.2.4 第4步:方波输出配置 方波的输出配置如下: - /*
- *********************************************************************************************************
- * 函 数 名: dac1_SetRectWave
- * 功能说明: DAC1输出方波
- * 形 参: _low : 低电平时DAC,
- * _high : 高电平时DAC
- * _freq : 频率 Hz
- * _duty : 占空比 2% - 98%, 调节步数 1%
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void dac1_SetRectWave(uint16_t _low, uint16_t _high, uint32_t _freq, uint16_t _duty)
- {
- uint16_t i;
- TIM_Cmd(TIM6, DISABLE);
-
- for (i = 0; i < (_duty * 128) / 100; i++)
- {
- g_Wave1[i] = _high;
- }
- for (; i < 128; i++)
- {
- g_Wave1[i] = _low;
- }
-
- dac1_InitForDMA((uint32_t)&g_Wave1, 128, _freq * 128);
- }
复制代码方波也是输出128个采样点代表一个周期,同时支持幅值和占空比的配置,其中占空比可以配置2%到98%,直接填数值2到98就可以了。实际DAC输出的波形频率由前面第2步函数dac1_InitForDMA实现。比如我们要实现频率10KHz,幅值4095,占空比50%的方波,那么配置就是: dac1_SetRectWave(0, 4095, 10000, 50)。
12.2.5 第5步:三角波输出配置 三角波的输出配置如下: - /*
- *********************************************************************************************************
- * 函 数 名: dac1_SetTriWave
- * 功能说明: DAC1输出三角波
- * 形 参: _low : 低电平时DAC,
- * _high : 高电平时DAC
- * _freq : 频率 Hz
- * _duty : 占空比
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void dac1_SetTriWave(uint16_t _low, uint16_t _high, uint32_t _freq, uint16_t _duty)
- {
- uint32_t i;
- uint16_t dac;
- uint16_t m;
-
- TIM_Cmd(TIM6, DISABLE);
-
- /* 构造三角波数组,128个样本,从 _low 到 _high */
- m = (_duty * 128) / 100;
-
- if (m == 0)
- {
- m = 1;
- }
-
- if (m > 127)
- {
- m = 127;
- }
- for (i = 0; i < m; i++)
- {
- dac = _low + ((_high - _low) * i) / m;
- g_Wave1[i] = dac;
- }
- for (; i < 128; i++)
- {
- dac = _high - ((_high - _low) * (i - m)) / (128 - m);
- g_Wave1[i] = dac;
- }
-
- dac1_InitForDMA((uint32_t)&g_Wave1, 128, _freq * 128);
- }
复制代码三角波也是输出128个采样点代表一个周期,同时支持幅值和占空比的配置,其中占空比可以配置0%到100%,不过程序中对0%和100%做了一个特殊处理。实际DAC输出的波形频率由前面第2步函数dac1_InitForDMA实现。比如我们要实现频率10KHz,幅值4095,占空比50%的三角波,那么配置就是:dac1_SetTriWave (0, 4095, 10000, 50)。
|