The STM32F103VCT6 features two 12-bit DACs. The conversion speed of the DAC has not been confirmed, but some online sources suggest a frequency of 1MHz, which means 1us. The ADC conversion time at a working frequency of 56MHz is 1us, and at 72MHz it is 1.17us. If there is a symmetrical relationship between AD and DA, then it is likely to be the same as the ADC time. (This is for analysis purposes only!)
For me, the DAC has two uses: outputting waveforms and outputting fixed voltages. Let’s first discuss the configuration for the former.
The first parameter: trigger mode, DAC_InitStructure.DAC_Trigger. There are a total of eight selectable external trigger sources. Six are timer triggers: TIM2, TIM4, TIM5, TIM6, TIM7, and TIM8. The other two are: EXTI line 9 and software trigger. If using timer triggers, you will also need to write the corresponding timer function, which is not very complex and is similar to the way timers are written. For example: DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; // Select Timer 6 as the external trigger source
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 0x85;
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
}
Output signal frequency calculation: Assuming we output a sawtooth waveform with 6 points, its frequency would be: (72MHz/(0x85+1))*6=89.552kHz. Note: If the prescaler is not 0, the clock must be divided by (prescaler+1).
The second parameter: DAC_InitStructure.DAC_WaveGeneration. As the name suggests: waveform generator. The STM32 integrates two amplitude-adjustable waveform generators that can produce triangle waves and noise waves. If we use a custom buffer, we do not need to configure this parameter or set it to DAC_WaveGeneration_None, but to avoid others mistakenly thinking you forgot to configure this parameter, it’s better to write it down, developing good habits.
The third parameter: DAC_InitStructure.DAC_OutputBuffer, which determines whether to use the output buffer. The output buffer’s function is primarily to reduce output impedance, allowing the STM32’s DAC to drive loads directly without external operational amplifiers. It is generally set to DAC_OutputBuffer_Disable, i.e., disabling the external buffer. This is for safety; if the driving capability is not strong enough (I haven’t tried it @_@), debugging time will increase.
That’s all for parameter configuration, making it one of the simplest peripheral configurations in STM32!
Generally speaking, when outputting waveforms with the DAC, the data transmitted is relatively large, so using DMA transmission can save CPU overhead. Therefore, after calling DAC_DMACmd(DAC_Channel_1, ENABLE); do not forget to configure the DMA function. A detailed configuration explanation will not be included here; I will just paste the code:
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA2_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R1_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&FIR_OutPutValueTab;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 200;
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_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel3, &DMA_InitStructure);
DMA_Cmd(DMA2_Channel3, ENABLE);
DMA_ITConfig(DMA2_Channel3, DMA_IT_TC, ENABLE);
}
Note the line of code written in red: when we transmit multiple data, it needs to be set for continuous conversion! The rest will not be elaborated.
Next, let’s talk about configuring the DAC for fixed voltage.
The configuration for fixed voltage output on the STM32’s DAC is similar to the waveform output configuration, with the difference being that it requires an additional function call: DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE); to output a fixed level.
The specific configuration is as follows:
void DAC_VOLTAGE_Configuration(void)
{
DAC_InitTypeDef DAC_InitStructure;
DAC_DeInit();
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
//
DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
}
After configuration is complete, calling DAC_SetChannel1Data(DAC_Align_12b_R, 4000); will send data!! Extremely convenient. Compared to 5615, 0832 and similar devices, this is much simpler! There are two points to note: 1. The first parameter of this function DAC_Align_12b_R cannot be rewritten using the method for setting ADC peripheral addresses, as this parameter does not carry the base address, and rewriting the address will include the base address, which will be incorrect! (I once got stuck here! It seems that one should not have preconceptions; otherwise, problems will inevitably arise.) 2. Each time you change the data sent, you must call the following two functions:
DAC_SetChannel1Data(DAC_Align_12b_R, 4000);
DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
As for why, my understanding is that: because changing the data in the DAC does not immediately write to the register, it requires an update, and since it is not updated through a timer trigger, it can only be updated by calling the function.