Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

In the main characteristics of DAC, it can be seen that DAC has waveform generator inside.

Main features of DAC

  • Two DAC converters: each converter corresponds to one output channel
  • 8 – or 12-bit monotonic output
  • Left-aligned or right-aligned data in 12-bit mode
  • Synchronous update function
  • Noise waveform generation
  • Triangular waveform generation
  • Dual DAC channels convert simultaneously or separately
  • Each channel has DMA capability
  • Externally triggered conversion
  • Input reference voltage V REF+

But there are only triangle waves and noise waves, so how do you make sine waves? Although the DAC has no sinusoidal wave function, we can form a sinusoidal array and then send the values in the sinusoidal array in turn through the timer, so that the output voltage value will change sinusoidal law.

Below directly through the code to demonstrate how to output sine wave

#include "dac_sin.h"

#define DAC_DHR12RD_Address      0x40007420


uc16 Sine12bit[32] = {2047.2447.2831.3185.3498.3750.3939.4056.4095.4056.3939.3750.3495.3185.2831.2447.2047.1647.1263.909.599.344.155.38.0.38.155.344.599.909.1263.1647
                     };

u32 DualSine12bit[32];
u8 Idx = 0;

// Use DMA to generate sine waves on PA4 and PA5
void DAC_SIN_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    DAC_InitTypeDef  DAC_InitStructure;
    TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
    DMA_InitTypeDef  DMA_InitStructure;
    // Set the clock
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
    / / GPIO Settings
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // Advanced timer Settings
    TIM_TimeBaseStructure.TIM_Period = 0x19;
    TIM_TimeBaseStructure.TIM_Prescaler = 0x00;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0x00;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x00;
    TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);

    TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);
    / / DAC Settings
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_T8_TRGO;					/ / TIM8 trigger
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);

    DAC_Init(DAC_Channel_2, &DAC_InitStructure);

    for(Idx = 0; Idx < 32; Idx++)
    {
        DualSine12bit[Idx] = (Sine12bit[Idx] << 16) + (Sine12bit[Idx]);
    }
    / / DMA Settings
    DMA_DeInit(DMA2_Channel4);

    DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_Address;			//DMA peripheral base address
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&DualSine12bit;				//DMA memory base address
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;						// Data transfer direction, read from memory
    DMA_InitStructure.DMA_BufferSize = 32;									// Size of the DMA cache for the DMA channel
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		// The peripheral address register remains unchanged
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;					// Memory address register increment
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // The data width is 32 bits
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;			// The data width is 32 bits
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;							// Work in cyclic mode
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;						//DMA channel X has a high priority
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;							//DMA channel x non-memory to memory mode
    DMA_Init(DMA2_Channel4, &DMA_InitStructure);

    DMA_Cmd(DMA2_Channel4, ENABLE);				// Enable DMA2 channel 4

    DAC_Cmd(DAC_Channel_1, ENABLE);				// Enable DAC channel 1
    DAC_Cmd(DAC_Channel_2, ENABLE);				// Enable DAC channel 2

    DAC_DMACmd(DAC_Channel_2, ENABLE);			// Enable DMA for the DAC

    TIM_Cmd(TIM8, ENABLE);						// Enable the timer
}
Copy the code

The data in the Sine12bit array is sinusoidal. Copy the data in the array into a table and observe.

You can see that the data in the array forms a standard sine wave.

When the data is available, the DAC is initialized. Here, timer is still used to trigger DAC, and timing 8 is used to trigger DAC. The generation mode of waveform is set to none, because the internal waveform generator is not used here.

Then the DMA mode is used for data transmission. Of course, a loop can be set up through the program to put the values of the data into the DAC register in turn, but this takes up resources of the single chip computer. In order to save resources, the single chip computer can realize this function itself.

Set DMA’s peripheral address to the ADDRESS of the DAC register and DMA’s memory address to the array of sine wave data, so that after each interrupt of timer 8, DMA automatically takes one data from the array and sets it to the REGISTER of the DAC. The DMA function allows the data from the array to be placed in the DAC’s data hold register in turn.

Finally, after the timer, DAC and DMA functions are enabled, the two output ports of DAC PA4 and PA5 will automatically output sinusoidal waveform with regular changes.