ADC independent DMA (2)

2016. 8. 14. 14:19 from MCU/ARM

ADC DMA


 STM32은 AVR과 다르게 conversion trigger 설정만 되어 있으면 ADC conversion이 자동으로 이루어진다. 자세한 것은 모르겠지만 ADC conversion trigger는 interrupt 우선순위가 매우 높은 것 같다. 실험 해본 것은 DMA 완료 인터럽트를 enable 시켜놓고 DMA interrupt routine 안에서 무한 반복문을 실행 시킨 후에 ADC conversion이 이루어지는지 해봤는데 결과는 interrupt routine 안에 서도 ADC conversion은 계속 이루어졌다.

 또한 AVR과 다른 점이 ADC converted value를 가져오는 방식이다. 라이브러리에 있는 ADC_GetConversionValue(), 

ADC_GetDualModeConversionValue() 그리고 ADC_GetInjectedConversionValue()함수를 이용하여 ADC converted value를 가져올 수도 있지만 DMA(Direct Memory Access)기능을 이용하여 ADC peripheral에 있는 값을 memory로 바로 가져올 수 있다. 아래서 보겠지만 DMA 설정을 enable 시켜 놓으면 conversion 후에 DMA request가 발생하여 자동으로 converted value가 destination location으로 저장된다. 즉, 다른 interrupt들에 방해 받지 않고 ADC_DR 값을 옮김으로써 data 손실이 없어진다.




ADC independent DMA 예제


-------------------------------------- my header --------------------------------------

#ifndef __MYDEFINE_H__

#define __MYDEFINE_H__


#include "stdio.h"

#include "stm32f10x_rcc.h"

#include "stm32f10x_usart.h"

#include "stm32f10x_gpio.h"

#include "stm32f10x_dma.h"

#include "stm32f10x_adc.h"


/* RCC header */

void RCC_Configuration();


/* GPIO header */

#define GPIO_LED1_PIN GPIO_Pin_9

#define GPIO_LED GPIOB

#define LED_GPIO_Clk RCC_APB2Periph_GPIOB

void test_led();

void test_led_togle();

void GPIO_Configuration();



/* USART header */

#define USART_GPIO_Clk RCC_APB2Periph_GPIOA

#define GPIO_USART GPIOA

#define GPIO_USART_Rx_Pin GPIO_Pin_10

#define GPIO_USART_Tx_Pin GPIO_Pin_9

void USART1_Init();

void SerialPutChar (uint8_t c);

void Serial_PutString(uint8_t *s);

uint8_t USART_GetCharacter(USART_TypeDef* usart_p);

int fputc(int ch, FILE *f);



/* ADC header */

#define ADC_GPIO_Clk RCC_APB2Periph_GPIOA

#define ADC_Clk RCC_APB2Periph_ADC1

#define GPIO_ADC GPIOA

#define GPIO_ADC_IN0 GPIO_Pin_0

void ADC_Configuration();



/* NVIC header */

//void NVIC_Configuration(void);



/* DMA header */

#define ADC1_DR_Address    ((uint32_t)0x4001244C)

#define DMA_Clk RCC_AHBPeriph_DMA1

void DMA_Configuration();



#endif



-------------------------------------- main.c --------------------------------------

#include "mydefine.h"


static void delay_int_count(volatile unsigned int nTime){     

  for(;nTime>0;nTime--);         

}

void delay_1_second(void){

  delay_int_count(806596);

}

void delay_100_milli_second(void){

  delay_int_count(80660);

}


extern __IO uint16_t ADCConvertedValue;


void main()

{

  /* RCC configuration */

  RCC_Configuration();

  /* GPIO configuration */

  GPIO_Configuration();

  /* NVIC configuration */

//  NVIC_Configuration();

  /* DMA configuration */

  DMA_Configuration();

  /* ADC configuration */

  ADC_Configuration();

  /* USART configuration */

  USART1_Init();

    

  

  while(1)

  {

    printf("ADCConvertedValue : %d \r\n", ADCConvertedValue);

  }

}


-------------------------------------- RCC.c --------------------------------------

#include "mydefine.h"

void RCC_Configuration()
{
  /* ADCCLK = PCLK2/6 */
  RCC_ADCCLKConfig(RCC_PCLK2_Div6); 
  
  /* Enable led GPIO clock enable */
  RCC_APB2PeriphClockCmd(LED_GPIO_Clk, ENABLE);
  
  /*  USART clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  RCC_APB2PeriphClockCmd(USART_GPIO_Clk, ENABLE);
  
  /* Enable ADC1 clock */
  RCC_APB2PeriphClockCmd(ADC_GPIO_Clk | ADC_Clk, ENABLE);
  
  /* Enable DMA1 clock */
  RCC_AHBPeriphClockCmd(DMA_Clk, ENABLE);
}


-------------------------------------- GPIO.c --------------------------------------

#include "mydefine.h"

#include "stm32f10x_gpio.h"


void GPIO_Configuration()

{

  GPIO_InitTypeDef GPIO_InitStructure;

   

  /* Configure gpio as output : LED1 */

  GPIO_InitStructure.GPIO_Pin = GPIO_LED1_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_Init(GPIO_LED, &GPIO_InitStructure);

  

  /* Configure USARTx_Tx as alteranate function push-pull */

  GPIO_InitStructure.GPIO_Pin = GPIO_USART_Tx_Pin;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_Init(GPIO_USART, &GPIO_InitStructure);

  

  /* Configure USARTx_Rx as input floating */

  GPIO_InitStructure.GPIO_Pin = GPIO_USART_Rx_Pin;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

  GPIO_Init(GPIO_USART, &GPIO_InitStructure);

  

  /* Configure PA.00 (ADC Channel0) as analog input -------------------------*/

  GPIO_InitStructure.GPIO_Pin = GPIO_ADC_IN0;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

  GPIO_Init(GPIO_ADC, &GPIO_InitStructure);

}


void test_led()

{

  GPIO_LED->BRR |= GPIO_LED1_PIN;

}

    


void test_led_togle()

{

  GPIO_LED->BRR |= GPIO_LED1_PIN;

  GPIO_LED->BSRR |= GPIO_LED1_PIN;  

}



-------------------------------------- DMA.c --------------------------------------

#include "mydefine.h"



__IO uint16_t ADCConvertedValue;


void DMA_Configuration()

{

  DMA_InitTypeDef DMA_InitStructure;

  

  /* DMA1 channel1 configuration ----------------------------------------------*/

  DMA_DeInit(DMA1_Channel1);

  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;

  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

  DMA_InitStructure.DMA_BufferSize = 1;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;

  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(DMA1_Channel1, &DMA_InitStructure);

  

  /* Enable DMA1 channel1 */

  DMA_Cmd(DMA1_Channel1, ENABLE);

}



-------------------------------------- ADC.c --------------------------------------

#include "mydefine.h"


void ADC_Configuration()

{

  ADC_InitTypeDef  ADC_InitStructure;

  

  /* ADC1 configuration ------------------------------------------------------*/

  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

  ADC_InitStructure.ADC_ScanConvMode = ENABLE;

  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

  ADC_InitStructure.ADC_NbrOfChannel = 1;

  ADC_Init(ADC1, &ADC_InitStructure);


  /* ADC1 regular channel 0 configuration */ 

  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);


  /* Enable ADC1 DMA */

  ADC_DMACmd(ADC1, ENABLE);

  

  /* Enable ADC1 */

  ADC_Cmd(ADC1, ENABLE);


  /* Enable ADC1 reset calibration register */   

  ADC_ResetCalibration(ADC1);

  /* Check the end of ADC1 reset calibration register */

  while(ADC_GetResetCalibrationStatus(ADC1));


  /* Start ADC1 calibration */

  ADC_StartCalibration(ADC1);

  /* Check the end of ADC1 calibration */

  while(ADC_GetCalibrationStatus(ADC1));

     

  /* Start ADC1 Software Conversion */ 

  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

}


ADC1 configuration



 위 비트는 ADC Dual mode 사용을 결정한다. Dual mode에는 두 가지 ADC를 통해서 다양한 모드를 만들 수 있는데 이것은 다음에 보도록 한다. 지금 우리는 single mode를 사용할 것이기 때문에 'DUALMOD[3:0]' 에 0000 - independent mode를 선택한다. 




 위 비트는 Scan mode를 결정한다. 사실 single mode에 channel 한 개를 쓰기 때문에 scan mode은 아무 의미 없다. scan mode는 ADC_SQRx register를 통해서 group 지어진 것을 scan 하여 자동적으로 conversion하도록 하는 것이다.




 위 비트는 Continuous conversion을 결정한다. Single conversion과 반대 개념으로 써 CONT bit가 1이고 , SCAN mode가 Disable 이면 channel conversion 이후에 다음 channel 이 자동으로 conversion 된다. SCAN mode가 Enable 이면 group 전체를 conversion한 이후에 다시 group conversion이 이루어진다.




 위 비트는 external trigger 종류를 무엇으로 할지 결정해준다. ADC main feature(1)에서 설명했지만 STM32 ADC는 무조건 external trigger가 있고 external trigger를 결정하므로써 trigger가 발동된다. 우리는 software적으로 trigger를 발생시킬 것이기 때문에 'EXTSEL[2:0]' 에 (111) - SWSTART 를 선택한다.




 위 비트는 Data alignment를 하위 비트에서 부터 할지 상위 비트에서부터 할지 결정한다. 별 내용 없다. 대부분 오른쪽에서 부터 할당함.




 위 비트는 Regular channel 총 몇개 conversion하는지 결정한다. 우리는 channel 한개만 사용하므로 'L[3:0]' 에 (0000) - 1 conversion을 선택한다.




ADC channel configuration


 이 register는 ADC channel 우선순위를 결정한다. 라이브러리에 있는 ADC_RegularChannelConfig() 함수를 통해서 아주 쉽게 ADC channel 우선순위를 결정할 수 있다.




ADC DMA


ADC_CR2 register에 8번 비트인데 이 비트는 ADC DMA 사용을 결정한다. DMA 요청은 end of conversion of regular channel 에서 발생한다. 




ADC ENABLE


 위 ADC_CR2 register에 0번 비트인데 ADC on/off 를 결정한다. 




ADC calibration


 

 위 ADC_CR2 register에 2,3번 비트인데 calibration on/off를 결정한다. calibration은 ADC 정확성을 증가시켜주는데 ADC power on(ADON set) 시키고 나서 calibration을 한번은 꼭 해주는 것이 좋다.




ADC external trigger


 위 ADC_CR2 register에 20번, external trigger on/off 결정한다.


---------------------------------------------------------------------------------------------


'MCU > ARM' 카테고리의 다른 글

TIM frequency, cycle 결정 방법  (0) 2017.04.07
ADC main feature(1)  (0) 2016.08.13
SPI 통신 (3)  (0) 2016.08.11
SPI 통신 (2)  (0) 2016.08.11
SPI 통신 (1)  (0) 2016.08.11
Posted by 나무길 :