Lab 8

Overview

In this lab you will reading the voltage on your board using the STM32 A2D converter. Initially you will be printing this value to the terminal. Next you will utilize the D2A converter to synthesis a sine wave and output it to the speaker. The samples of the waveform will be output at a rate controlled by Timer 2. A DMA channel will be used to move samples from memory to the DAC register. Finally, you utilize the A2D value to control the output rate of the sine wave.

You will be creating the following driver modules:

The code for this lab is based very closely on the peripheral example code located at:

Initial Setup

A2D [ds_A2D.c/h]

We will be reading ADC1 Channel 10 which is on pin PF4.

void ds_a2d_init(void):

RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div2);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);
ADC_VoltageRegulatorCmd(ADC1, ENABLE);
ds_delay_uS(10);
ADC_CommonStructInit(&ADC_CommonInitStructure);
ADC_CommonInitStructure.ADC_Mode=ADC_Mode_Independent;                                                      
ADC_CommonInitStructure.ADC_Clock = ADC_Clock_AsynClkMode;                    
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;             
ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_OneShot;                  
ADC_CommonInitStructure.ADC_TwoSamplingDelay = 0;          
ADC_CommonInit(ADC1, &ADC_CommonInitStructure);
ADC_StructInit(&ADC_InitStructure);   
ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; 
ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0;         
ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;   
ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable;  
ADC_InitStructure.ADC_NbrOfRegChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_7Cycles5);
ADC_Cmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));
Start the first conversion
ADC_StartConversion(ADC1); 

The ADC is now ready to read values. The values will be converted into a 12-bit number.

void ds_read_adc(void):

This function will poll the A2D converter for an available conversion. Once ready, the function returns the converted 12-bit value.

uint16_t ds_read_adc(void) {
  while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
  return(ADC_GetConversionValue(ADC1));
}

Incorporate this function into a while loop and use it to output the voltage from your lab system's potentiometer on the console. Vary the voltage and confirm proper functionality.

TIMER2

This timer will be used to control the rate at which the DAC outputs samples. We will need a function to initialize the timer along with a function that we will use later to vary the period.

void ds_timer2_init(void):

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;   
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
TIM_TimeBaseStructure.TIM_Period = 0xFF;          
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;       
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;    
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);

void ds_timer2_setperiod(uint16_t period):

This function will be used to update the period based on the converted potentiometer voltage. The function will keep a copy of the time structure similar to what was used in the initialization. The new period value can then be added to the structure and the timebase updated.

static TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;       
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;    
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
if (period < 0x1F) period = 0x1F;
period = period << 4;
TIM_TimeBaseStructure.TIM_Period = period;          
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

DAC/DMA

This driver defines the data arrays used to generate the signals. It also initializes the DAC pins, the DAC channels, along with setting up DMA channels that loop through the samples and output them without software intervention.

The DACs are driven by Timer 2. After a sample is output, the DAC triggers a DMA that moves the next sample from the particular data array to the DAC's output register.

Define Data

#define DAC_DHR12R2_ADDRESS  0x40007414
#define DAC_DHR8R1_ADDRESS 0x40007410

Data Arrays

const uint16_t 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};    
const uint8_t Escalator8bit[6] = {0x0, 0x33, 0x66, 0x99, 0xCC, 0xFF};

void ds_dac_channel_setup(void):

	DAC_DeInit(); 
	DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;
	DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
	DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits11_0;
	DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
	DAC_Init(DAC_Channel_2, &DAC_InitStructure);
	DAC_Cmd(DAC_Channel_2, ENABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R2_ADDRESS;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Sine12bit;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_BufferSize = 32;
	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);
	DAC_DMACmd(DAC_Channel_2, ENABLE);
	DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
	DAC_Init(DAC_Channel_1, &DAC_InitStructure);
	DMA_DeInit(DMA2_Channel4);
	DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_ADDRESS;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Escalator8bit;
	DMA_InitStructure.DMA_BufferSize = 6;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_Init(DMA2_Channel4, &DMA_InitStructure);
	DMA_Cmd(DMA2_Channel4, ENABLE);
	DAC_Cmd(DAC_Channel_1, ENABLE);
	DAC_DMACmd(DAC_Channel_1, ENABLE);

void ds_dac_init(void):

GPIO_InitTypeDef GPIO_InitStructure;
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);	
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
   GPIO_StructInit(&GPIO_InitStructure);  
   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4|GPIO_Pin_5;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   ds_dac_channel_setup();

Assignment

What to Turn in:

This assignment is due by midnight on 3/28. At midnight, your AI will pull the current state of your repository to review your code. In the following lab session, you will be asked to demonstrate your solution. Independently, you will also write a lab report describing in approximately 300 words or less the following:

You will turn in this lab report in the oncourse assignments section.