/* p13_2.c Acquire data from ADC by DMA * * The program sets up the timer TIM3 channel 2 to trigger ADC1 to * convert the analog input channel 0. The output of the ADC1 is transferred * to the buffer in memory by DMA. Once the buffer is full, the DMA is * stopped. That data are converted to decimal ASCII numbers and sent * to USART2 to be displayed on the console. * * A global variable, done, is used by the DMA transfer complete interrupt * handler to signal the other part of the program that a buffer full of * data conversion is done. * * This program was tested with Keil uVision v5.24a with DFP v2.11.0 */ #include "stm32F0xx.h" #include "stdio.h" #define ADCBUFSIZE 64 void USART2_init(void); /* Initialize UART pins, Baudrate */ void DMA1_init(void); /* Initialize DMA1 controller */ void DMA1_channel1_setup(unsigned int src, unsigned int dst, int len); /* set up a DMA transfer for ADC1 */ void TIM3_init(void); /* initialize TIM3 */ void ADC1_init(void); /* setup ADC */ int done = 1; char adcbuf[ADCBUFSIZE]; /* buffer to receive DMA data transfers from ADC conversion results */ char uartbuf[ADCBUFSIZE * 5 + 1]; /* buffer to hold ASCII numbers for display */ int main(void) { int i; char* p; USART2_init(); DMA1_init(); TIM3_init(); ADC1_init(); while (1) { done = 0; /* clear done flag */ /* start a DMA of ADC data transfer */ DMA1_channel1_setup((unsigned int)adcbuf, (unsigned int)&(ADC1->DR), ADCBUFSIZE); while (done == 0); /* wait for ADC DMA transfer complete */ /* convert the ADC data into decimal ASCII numbers for display */ p = uartbuf; for (i = 0; i < ADCBUFSIZE; i++) { sprintf(p, "%3d ", adcbuf[i]); p += 4; } *p = '\n'; p++; /* send the ADC numbers through USART2 to the console terminal */ for (i = 0; i < p - uartbuf; i++) { while (!(USART2->ISR & 0x40)); USART2->TDR = uartbuf[i]; } } } /* Initialize ADC ADC1 is configured to do 8-bit data conversion and triggered by the rising edge of timer TIM3 channel 2 output. */ void ADC1_init(void) { RCC->AHBENR |= 0x00020000; /* enable GPIOA clock */ GPIOA->MODER |= 3; /* PA0 analog */ RCC->APB2ENR |= 0x00000200; /* enable ADC clock */ ADC1->CFGR1 = 0x000004D0; /* exten rising edge, extsel 3 = tim3, 8-bit conversion , Discontinuous mode */ ADC1->CFGR2 = 0x00001000; /* Asynchronous clock mode */ ADC1->CHSELR = 0x00000001; /* select channel 0 */ ADC1->CR |= 1; /* enable ADC1 */ } /* Initialize TIM3 Timer TIM3 channel 2 is configured to generate PWM at 1 kHz. The output of the timer signal is used to trigger ADC conversion. */ void TIM3_init(void) { RCC->AHBENR |= 0x00020000; /* enable GPIOA clock */ GPIOA->AFR[0] |= 0x10000000; /* PA7 pin for TIM3 channel 2 */ GPIOA->MODER &= ~0x0000C000; GPIOA->MODER |= 0x00008000; /* setup TIM3 */ RCC->APB1ENR |= 0x00000002; /* enable TIM3 clock */ TIM3->PSC = 80 - 1; /* divided by 80 */ TIM3->ARR = 100 - 1; /* divided by 100, sample at 1 kHz */ TIM3->CNT = 0; TIM3->CCMR1 = 0x6000; /* PWM mode */ TIM3->CCER = 0x0010; /* enable PWM Ch2 */ TIM3->CCR2 = 50 - 1; /* pulse width 1/2 of the period */ TIM3->CR2 = 0x0050; /* OC2REF signal is used as trigger output (TRGO)? */ TIM3->CR1 = 0x0000; /* disable ADC */ } /* Initialize DMA1 controller * DMA1 controller's clock is enabled and also the DMA interrupt is * enabled in NVIC. */ void DMA1_init(void) { RCC->AHBENR |= 0x00000001; /* DMA1 controller clock enable */ SYSCFG->CFGR1 &= ~0x00000100; /* remap DMA to use DMA channel 1 */ DMA1->IFCR = 0x0000000F; /* clear all interrupt flags of Stream 0 */ NVIC_EnableIRQ(DMA1_Channel1_IRQn); /* DMA interrupt enable at NVIC */ } /* Set up a DMA transfer for ADC * The ADC1 is connected to DMA1 channel 1. This function sets up the * peripheral register address, memory address, number of transfers, * data size, transfer direction, and DMA interrupts are enabled. * At the end, the DMA controller is enabled, the ADC conversion * complete is used to trigger DMA data transfer and the timer * used to trigger ADC is enabled. */ void DMA1_channel1_setup(unsigned int src, unsigned int dst, int len) { DMA1_Channel1->CCR &= ~1; /* disable DMA1 channel 1 */ while (DMA1_Channel1->CCR & 1); /* wait until DMA1 channel 1 is disabled */ DMA1->IFCR = 0x000F; /* clear all interrupt flags of channel 1 */ DMA1_Channel1->CPAR = dst; DMA1_Channel1->CMAR = src; DMA1_Channel1->CNDTR = len; DMA1_Channel1->CCR = 0x00000080; /* data size byte, mem incr, peripheral-to-mem */ DMA1_Channel1->CCR |= 0x0000000A; /* enable interrupts DMA_IT_TC | DMA_IT_TE */ DMA1_Channel1->CCR |= 0x00000001; /* enable DMA1 Channel 1 */ ADC1->CFGR1 |= 0x0001; /* enable ADC conversion complete DMA data transfer */ TIM3->CR1 |= 1; /* enable timer3 */ ADC1->CR |= 4; /* start ADC */ } /* DMA1 channel 1 interrupt handler This function handles the interrupts from DMA1 controller channel 1. The error interrupts have a placeholder for error handling code. If the interrupt is from DMA data transfer complete, the DMA controller is disabled, the interrupt flags are cleared, the ADC conversion complete DMA trigger is turned off and the timer that triggers ADC conversion is turned off too. */ void DMA1_Channel1_IRQHandler(void) { if (DMA1->ISR & 0x0008) /* if an error occurred */ while (1); /* substitute this by error handling */ DMA1->IFCR = 0x0000000F; /* clear all interrupt flags of Channel 1 */ DMA1_Channel1->CCR &= ~0x2; /* disable DMA1 Channel 1 TCIE */ ADC1->CFGR1 &= ~0x0001; /* disable ADC conversion complete DMA */ TIM3->CR1 &= ~1; /* disable timer3 */ done = 1; } /* Initialize UART pins, Baud rate The USART2 is configured to send output to pin PA2 at 9600 Baud. This is used to display the ADC conversion results on the console terminal. */ void USART2_init(void) { RCC->AHBENR |= 0x00020000; /* enable GPIOA clock */ RCC->APB1ENR |= 0x00020000; /* enable USART2 clock */ /* Configure PA2 for USART2_TX */ GPIOA->AFR[0] &= ~0x0F00; GPIOA->AFR[0] |= 0x0100; /* alt7 for USART2 */ GPIOA->MODER &= ~0x0030; GPIOA->MODER |= 0x0020; /* enable alternate function for PA2 */ USART2->BRR = 0x0341; /* 9600 baud @ 8 MHz */ USART2->CR1 = 0x0008; /* enable Tx, 8-bit data */ USART2->CR2 = 0x0000; /* 1 stop bit */ USART2->CR3 = 0x0000; /* no flow control */ USART2->CR1 |= 0x0001; /* enable USART2 */ }