본문 바로가기
Robot/Device Control

[STM32F10x-StdPeriph] 4. SmartRobot Board 실습 - Analog Input (ADC)

by lifeseed 2013. 11. 18.
0. 들어가기 전에

   이번 강좌에서는 각종 센서로 부터 Analog 입력을 받기위해 사용되어지는 ADC 컨트롤을 해볼까 합니다.

이번 강좌의 목적은 ADC에 대한 이해 보다는 STM32F10x에서 제공하는 ADC를 1채널만 동작시켜 센서로 부터 Analog값을 읽어오는 과정을 확인하는 것입니다. 

예제 코드는 본 게시글에 첨부되었으며, Base Code는 http://lifeseed.tistory.com/78 게시물의 첨부파일인 srbd_bsp_printf_ex.zip 파일 입니다.

ADC 결과 값을 확인을 위하여 지난시간에 실습했던 UART입출력을 위한 초기 설정을 UART_Configuration 라는 함수명으로 선언한 후 main함수의 시작부분에 호출함으로써 Lb_printf를 사용가능하도록 하였습니다.

해당 코드는 첨부파일의 main.c 에 추가되었습니다.

 예제에 사용되는 센서는 조도센서 A906012 이며, 본카페에서 공구된 Education Shield를 이용하였으며, SmartRobot Board의 J8-1 번핀인 PA7 (ADC1_7 channel) 에 연결되어 있습니다. (CD00161566_product_spec.pdf 참조)

그럼 이제 시작해 보겠습니다.

 

 

srbd_bsp_ex_adc.zip::  작업 결과 파일

 

1. 코드 작성 및 컴파일
 

void setup(void)
{
 ADC_InitTypeDef ADC_InitStructure;
 GPIO_InitTypeDef GPIO_InitStructure;

 

 // RCC Configuration
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );

 

 // Potentio meter  (volume control)
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
 GPIO_Init(GPIOA, &GPIO_InitStructure);

 

 /* ADC1 configuration ------------------------------------------------------*/
 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
 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 channels configuration */
 ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1 , ADC_SampleTime_55Cycles5);

 

/* Enable ADC1 */
 ADC_Cmd(ADC1, ENABLE);

 

 /* Enable ADC1 reset calibration register */
 /* Check the end of ADC1 reset calibration register */
 ADC_ResetCalibration(ADC1);
 while(ADC_GetResetCalibrationStatus(ADC1));


 /* Start ADC1 calibration */
 /* Check the end of ADC1 calibration */
 ADC_StartCalibration(ADC1);
 while(ADC_GetCalibrationStatus(ADC1));

 

 Lb_printf("Start Application : %s\r\n", "ADC Test");
}

 

void loop(void)
{
 char c;
 int AD_value;
 if(GetKey(&c)==1)
 {
  switch(c)
  {
  case '0':

   /* Start ADC1 Software Conversion & Read Data */
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);
   while(ADC_GetFlagStatus(ADC1, 0x2)==RESET);
   AD_value = ADC_GetConversionValue(ADC1);


   Lb_printf("ADC : %d, %d\r\n", AD_value, AD_value*100/4095);
   break;
  case '1':
   Lb_printf("pressed 1\r\n");
   break;
  default:
   Lb_printf("%c", c);
   break;
  }
 }
}

 
arch/main.c 의 setup 및 loop함수에 위와 같이 코딩하고 cs-make를 통하여 Build합니다.

 

컴파일 에러가 날 경우 adc library를 include해야 합니다.

1) arch/include/stm32f10x_conf.h 의 27라인의 주석을 풀어줍니다.

 

/*  #include "stm32f10x_adc.h" */ 

=>

#include "stm32f10x_adc.h" 

 

 

2) arch/STM32F10x_StdPeriph_Driver/src/Makefile.inc 의 7번째 라인의 주석을 풀어 줍니다.

 

#STM32F10X_DRV_SRC  += stm32f10x_adc.c

=>

STM32F10X_DRV_SRC  += stm32f10x_adc.c

  

 

그리고 다시 빌드를 하면  

stm32f103cb.bin 파일이 생성됩니다.
 arm-none-eabi-objcopy -O binary stm32f103cb.elf stm32f103cb.bin
arm-none-eabi-objdump -h -S -C stm32f103cb.elf > stm32f103cb.lss
arm-none-eabi-nm -n stm32f103cb.elf > stm32f103cb.sym
arm-none-eabi-size -A stm32f103cb.elf
stm32f103cb.elf  :
section            size        addr
.text             10696   134234112
.data                40   536870912
.heap              3072   536870952
.stack_dummy       1024   536870952
.ARM.attributes      41           0
.comment             48           0
.debug_info       16622           0
.debug_abbrev      3816           0
.debug_aranges      296           0
.debug_line        5871           0
.debug_str         5912           0
.debug_frame       2816           0
.debug_loc        14956           0
.debug_ranges       416           0
Total             65626
 
2. Download 및 실행
SW3을 누른채로 Reset하여 부트모드로 진입후 1번항목을 선택하고 TeraTerm등의 Terminal 창에서 Y-Modem으로 빌드된 stm32f103cb.bin을 전송합니다.
자세한 다운로드 방법은 http://lifeseed.tistory.com/73 의 3번 항목을 참조하시면 됩니다.
(저는 Teraterm V4.79를 사용하고 있습니다.)

 ================== Main Menu ============================

  Download Image To Flash ------- 1

  Upload Image From Flash ------- 2

  Execute ------------------------------ 3

==========================================================

Waiting for the file to be sent ... (press 'a' to abort)
C

 Programming Completed Successfully!
--------------------------------
 Name: stm32f103cb.bin
 Size: 10736      Bytes
-------------------

다운로드가 완료되면 Reset버튼을 눌러 코드를 실행합니다.
Terminal 창에 키보드를 입력하면 입력한 문자가 출력됨을 확인 할 수 있습니다.


3. Code 설명
1) ADC1 Clock 인가
 

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );


RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
- STM32F10x의 ADC1 port에 Clock을 인가해 실제 ADC1이 동작하도록 합니다. 모든 Peri. 들은 사용전에 반드시 Clock을 인가해 주어야 합니다.

 

2) GPIOA Pin7번을 Analog Input 모드로 설정 .

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
 GPIO_Init(GPIOA, &GPIO_InitStructure);


3) ADC1 초기화 

 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
 ADC_InitStructure.ADC_NbrOfChannel = 1;

 ADC_Init(ADC1, &ADC_InitStructure);

- STM32F10x manual에서 설명하는 가장 간단한 Single Conversion 모드 설정입니다.
 
4) ADC1 Channel 및 Sampling Time 설정 

 ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1 , ADC_SampleTime_55Cycles5);

- ADC1의 7번 channel을 이용하며, 55.5 cycle 로 설정합니다.
 
5) ADC1 Channel Calibration

 ADC_Cmd(ADC1, ENABLE);

 ADC_ResetCalibration(ADC1);
 while(ADC_GetResetCalibrationStatus(ADC1));


 ADC_StartCalibration(ADC1);
 while(ADC_GetCalibrationStatus(ADC1));

- ADC1을 Enable하고 Calibration을 수행합니다.
 
6) Start Conversion & Get Data  

  ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  
while(ADC_GetFlagStatus(ADC1, 0x2)==RESET);
  AD_value = ADC_GetConversionValue(ADC1);

- Conversion을 수행하고 End of Conversion(EOC) Status가 활성화 되면 데이터를 가지고 옵니다.
 
7) Arduino code와의 비교
Arduino는 Analog를 위해 6채널이 할당되어 있으며, 기본적으로 초기화가 되어 있습니다.
따라서 6)의 Start Conversion & Get Data 부분의 코드를 analogRead라는 함수로 구현한다면 동일한 결과를 얻을 수 있습니다.
단 analogRead의 결과 값은 0 ~ 1023의 값을 가지므로, 16비트로 구성된 ADC 값은 value*1023/4095 로 표현하면 됩니다..
 
cf) Ardiono Code 

#define ADC_CHANNEL  7 

 

void setup(void)
{
 Serial.begin(115200);          //  setup serial

}

 

void loop(void)
{

 int value;

 value = analogRead(ADC_CHANNEL);

 Serial.println(value);             // debug value 

}

 

 

 

 

 
- 위 아두이노 예제는 직접 돌려 보지 못해 결과가 다소 상이할 수 있습니다. 기본적인 컨셉 정도로만 이해 해주시면 될 듯합니다.
 
 
4. Register Control
1) ADCBase Address
ADC는 1 ~ 3까지 총 3개의 Port를 제공하며 각각의 Base Address는 다음과 같습니다.

 ADC1 Base Address : 0x40012400

 ADC2 Base Address : 0x40012800

 ADC3 Base Address : 0x40013C00

 
cf) ADC는 모두 APB2 Bus에 연결되어 있어 RCC_APB2PeriphClockCmd 함수를 이용하여 클럭을 Enable 시켜 주게 됩니다.
 
2) Register Description
위 예제에서 사용된 Single Conversion 모드를 구현하기 위해서는 다음과 같은 Register를 사용하게 됩니다.

 

 

  

- ADC_SR  ::Status Register로 1번 bit를 통하여 End of Conversion을 확인할 수 있습니다.
- ADC_CR1 :: 여기서는 Single 모드로 Independent mode, 그리고 Scan mode를 사용하므로 0x00000000으로 설정하게 됩니다.
- ADC_CR2 :: Software Start Conversion을 이용하므로 EXTSEL 은 0b111 그리고 ADC Enable을 위해 ADON은 0b1 나머지는 모두 0으로 설정합니다. 이후 Conversion 수행시 SWSTART bit와 EXTTRIG bit를 1로 설정합니다.
- ADC_SMPR2 : Sampling Time을 0~9번 채널은 SMPR2를 이용하며, 7번 채널에 해당하는 23:21 Bit를 55.5 cycle에 해당하는 0b101 로 설정합니다.
- ADC_SQR1 :: 사용하는 채널이 1개이므로 L[3:0] 을 0x1 로설정합니다.
- ADC_SQR3 :: 7번채널 하나를 이용하므로 SQ1[4:0]의 필드를 0x7로 설정합니다.
- ADC_DR :: Conversion된 Data값이 기록되는 Register로 Single Mode일때는 [15:0] bit의 값을 이용합니다. 
 
cf) 첨부 예제 파일은 UART를 통하여 0번을 입력받으면 Software Conversion을 수행하여 ADC 값을 읽어오도록 구현하였습니다.

 

 

ADC는 External Triger를 이용하거나, DMA를 이용하면 여러가지 방법으로 Conversion된 값을 읽어올 수 있습니다.
이에 대한 자세한 방법은 기회가 되면 추가적으로 정리하여 게시하도록 하겠습니다. 
 

[참고자료]


[1] CD00171190 STM32F1xx Reference Manual
[2] CD00161566 STM32F1xx Product Spec
[3] http://arduino.cc/en/Reference/AnalogRead