STM32 ADC Multi Channel Using DMA - FabLabSeoul/WingProject GitHub Wiki
STM32 ADC Multi Channel Using DMA
์๋ ๋ก๊ทธ ์ ๋ ฅ์ ๋๊ฐ ์ด์์ ๋ฐ์์ผ ํ ๋ ADC ์ฑ๋์ ์ด์ฉํ ์ ์๋ค. STM32 Value line discovery ๋ณด๋๋ ADC1, ADC2 ๋๊ฐ์ A/D ์ปจ๋ฒํฐ๊ฐ ์์ง๋ง, ์ฑ๋์ ์ฌ์ฉํ๋ฉด ADC1 ํ๋๋ก ๋ ๊ฐ ์ด์์ ์๋ ๋ก๊ทธ ๋ฐ์ดํ๋ฅผ ๋ฐ์ ์ ์๋ค. ๋ฌธ์ ๋ ADC1์์ ์๋ ๋ก๊ทธ๋ฅผ ๋์งํธ ์ ๋ณด๋ก ๋ฐ๊พผ ํ, ADC1->DR ๋ ์ง์คํฐ์ ์ ์ฅ๋๋๋ฐ, ๋ ๊ฐ์ด์์ ์๋ ๋ก๊ทธ ์ ๋ ฅ์ ๋์์ ํฌํธ๋ก๋ถํฐ ๋ฐ๊ฒ๋๋ฉด, ADC1->DR ์ ๋ณด๊ฐ ๋ฎ์ด์์์ง๊ธฐ ๋๋ฌธ์, ๊ธฐ์กด ๋ฐฉ์๋๋ก ์ฌ์ฉํด์๋ ์๋๋ค. ์ด ๋ ์ฌ์ฉํ๋ ๊ฒ์ด DMA(Direct Memory access Controller) ๋ค. ํฌํธ๋ก ์๋ ๋ก๊ทธ ์ ๋ณด๋ฅผ ๋ฐ์ ํ, ADC1 ์ปจ๋ฒํฐ๋ฅผ ๊ฑฐ์ณ ๋ณํ๋ ์ ๋ณด๋ฅผ ์ง์ ๋ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๊ฒ ํ๋ ๊ธฐ๋ฅ์ด๋ค. 2๊ฐ์ ์๋ ๋ก๊ทธ ์ ๋ ฅ์ ๋ฐ๊ณ ์ถ๋ค๋ฉด, ๋ ๊ฐ์ ์ ๋ณด๋ฅผ ๋ฐ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง๋ค๊ณ , ํด๋น ๋ฉ๋ชจ๋ฆฌ๋ฅผ ADC ์ฑ๋์ ์ฐ๊ฒฐ์์ผ์ฃผ๊ธฐ๋ง ํ๋ฉด๋๋ค. ์ ์ฉํ๊ณ , ๊ฐ๋จํ ๊ธฐ๋ฅ์ด๊ธฐ ๋๋ฌธ์ ์์ฃผ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์ด๋ค. ๊ฐ๋จํ ๊ฐ๋ ์ http://egloos.zum.com/recipes/v/5152867 ์ฌ์ดํธ๋ฅผ ์ฐธ์กฐํ์.
์์ผ๋ก ์ค๋ช ํ MultiChannel ADC using DMA ์์ ๋ STM32\stm32vldiscovery_package\Project\Examples\ADC_Channel2 ํด๋์ ์๋ค.
์ด ์์ ๋ ํฌํธ PC1, PC2 ๋ก๋ถํฐ ์๋ ๋ก๊ทธ ์ ํธ๋ฅผ ๋ฐ์, ๊ฐ๊ฐ์ ํฌํธ์ ์ ์์ด 2V ์ด์์ด๋๋ฉด, PC9, PC8 LED๋ฅผ ์ผ๊ฒํ๋ค. ์ด ์์ ๋ฅผ ์คํํด ๋ณด๋ ค๋ฉด, ๊ฐ๋ณ์ ํญ ๋ ๊ฐ๊ฐ ์์ด์ผ ํ๋ค. ์์ธํ ํ๋ก ์ค๊ณ๋ ๊ฐ์ ์์์ ํ๊ธธ ๋ฐ๋๋ค.
์๋ ๋ก๊ทธ ์ ๋ ฅ ์ด๊ธฐํ
PC1, PC2 ๋ฅผ ์๋ ๋ก๊ทธ ์ ๋ ฅ์ผ๋ก ๋ฐ๋๋ค. ๊ตณ์ด PC ํฌํธ๋ฅผ ์ด ์ด์ ๋, ํฌํธ์ ADC1 ๊ฐ์ ์ฑ๋์ ์ฐ๊ฒฐํ๊ธฐ ์ํด์๋ค. ADC1๋ ์ฑ๋๋ง๋ค ์ ํด์ง ํฌํธ๊ฐ ์๋ค. ์ ํด์ง ์ฑ๋ ํฌํธ ํ ์ด๋ธ์ ๋ค์๊ณผ ๊ฐ๋ค. ADC1_IN0 ์ Channel_0 ์ ๋งํ๋ค.
Channel | Port |
---|---|
ADC1_IN0 | PA0 |
ADC1_IN1 | PA1 |
ADC1_IN2 | PA2 |
ADC1_IN3 | PA3 |
ADC1_IN4 | PA4 |
ADC1_IN5 | PA5 |
ADC1_IN6 | PA6 |
ADC1_IN7 | PA7 |
ADC1_IN8 | PB0 |
ADC1_IN9 | PB1 |
ADC1_IN10 | PC0 |
ADC1_IN11 | PC1 |
ADC1_IN12 | PC2 |
ADC1_IN13 | PC3 |
ADC1_IN14 | PC4 |
ADC1_IN15 | PC5 |
PC1, PC2 ์ ์๋ ๋ก๊ทธ ์ ๋ ฅ์ ๋ฐ๊ฒ ํ๋ ์ค์ ์ฝ๋๋ค. GPIOC ์ ํด๋ญ์ ์ค์ ํ๋ ์ฝ๋๋ ๋ค๋ฅธ ๊ณณ์ ์๊ธฐ ๋๋ฌธ์, ์ฌ๊ธฐ์๋ ์๋ตํ๋ค.
GPIO_InitTypeDef myGPIO;
myGPIO.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; //set to pin 1, 2
myGPIO.GPIO_Mode = GPIO_Mode_AIN; //set as analog input
GPIO_Init(GPIOC, &myGPIO); //set to C1, C2
PC8,PC9 LED ์ค์
PC8, PC9 ํฌํธ๋ฅผ ์ถ๋ ฅ์ผ๋ก ์ค์ ํ๋ค. PC8,9๋ STM32 Value line discovery ๋ณด๋ ์์ LED 3,4๋ฒ๊ณผ ์ฐ๊ฒฐ๋์ด ์๋ค.
//Configure other I/O pins
void GPIO_config(void)
{
GPIO_InitTypeDef myGPIO;
// initialize LED C8, 9
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_StructInit(&myGPIO);
myGPIO.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
myGPIO.GPIO_Mode = GPIO_Mode_Out_PP;
myGPIO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &myGPIO);
}
ADC ์ค์
์ฌ๊ธฐ๊น์ง๋ ๊ธฐ์กด ADC ์ค์ ๊ณผ ํฌ๊ฒ ๋ค๋ฅด์ง ์๋ค.
- ADC_NbrOfChannel : Number of Channel ์ ์ฑ๋์ ์๋ค. ๋ ๊ฐ์ ์ฑ๋์ ์ฌ์ฉํ ๊ฒ์ด๋ 2 ๋ค.
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //clock for ADC (max 14MHz, 72/6=12MHz)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //enable ADC clock
//configure ADC1 parameters
ADC_InitTypeDef myADC;
myADC.ADC_Mode = ADC_Mode_Independent;
myADC.ADC_ScanConvMode = ENABLE;
myADC.ADC_ContinuousConvMode = ENABLE;
myADC.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
myADC.ADC_DataAlign = ADC_DataAlign_Right;
myADC.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &myADC);
DMA ์ค์
DMA ์ ๊ด๋ จ๋ ๋ด์ฉ์ ๋งค๋ด์ผ์ ์ฐธ์กฐํ์. DMA1 ๋ ADC, DAC์ ๋ง์ฐฌ๊ฐ์ง๋ก ํด๋ญ์ ์ค์ ํด์ค์ผ ํ๋ค.
- DMA_PeripheralBaseAddr ๋ณ์๋ฅผ ํตํด ์ด๋ค ๋ ์ง์คํฐ์ ์ ๋ณด๋ฅผ ๋ฐ์ ์ง ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ์ค์ ํ๋ค.
- DMA_MemoryBaseAddr ๋ณ์๋ฅผ ํตํด ์ค์ ๋ก ์ ์ฅ๋ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ์ค์ ํ๋ค.
- DMA_BufferSize ๋ณ์์ ์ ์ฅํ ๋ฉ๋ชจ๋ฆฌ ํฌ๊ธฐ๋ฅผ ์ค์ ํ๋ค. ๋ง์ฝ uint32 ๋๊ฐ๋ฅผ ๋ฐ๋๋ค๋ฉด, 2๋ฅผ ๋ฃ์ผ๋ฉด ๋๋ค.
- DMA_MemoryDataSize ๋ณ์์ ์ ์ฅํ ๋ฐ์ดํ ํฌ๊ธฐ๋ฅผ ์ค์ ํ๋ค. ํ์ฌ uint32 ํ์ผ๋ก ๋ฐ๊ธฐ ๋๋ฌธ์ 32๋นํธ ์ฌ์ด์ฆ์ Word๋ก ์ด๊ธฐํ ํ๋ค. 16๋นํธ ๋ฐ์ดํ๋ฅผ ๋ฐ์ ๊ฒ์ด๋ผ๋ฉด HalfWord ๋ก ์ค์ ํ๋ค.
- DMA_MemoryInc ๋ณ์์ DMA_MemoryInc_Enable ๋ก ์ค์ ํด์ผ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ์ฆ๊ฐ์ํค๋ฉด์ ๋ค์ ๋ฉ๋ชจ๋ฆฌ์ ์ ๋ณด๋ฅผ ์ฐ๊ฒ ๋๋ค.
- DMA_Mode ๋ณ์์๋ DMA_Mode_Circular ๋ก ์ค์ ํ๋ค. ์ด๋ ๊ฒ๋๋ฉด ์ ํด์ง ํฌ๊ธฐ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ฐ์ดํ๋ฅผ ๋ค ์ฐ๋ฉด, ์ฒ์์ผ๋ก ๋์๊ฐ์ ๊ณ์ ์ฐ๊ฒ ๋๋ค. ๋งค ๋ฃจํ ๋ง๋ค ์ ๋ณด๋ฅผ ๊ฐฑ์ ํด์ผ ํ๋ค๋ฉด, ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ์ธ ์ ์๋, ์ด ์ต์ ์ผ๋ก ์ค์ ํ๋๊ฒ ์ข๋ค.
__IO uint32_t ADC_DualConvertedValueTab[2]; // ์ ์ญ ๋ณ์
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// DMA1 channel1 configuration ----------------------------------------------
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_DualConvertedValueTab;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 32bit
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // 32bit
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 Channel ์ค์
ADC1์ ์ฑ๋์ ์ฐ๊ฒฐํ๋ค. ADC_Channel_11์ PC1๊ณผ ์ฐ๊ฒฐ๋๊ณ , ADC_Channel_12๋ PC2์ ์ฐ๊ฒฐ๋๋ค. ์ฆ, ํฌํธ PC1์ผ๋ก๋ถํฐ ๋ฐ์ ์๋ ๋ก๊ทธ ์ ๋ณด๋ Channel_11 ์ ํตํด ๋์งํธ๋ก ๋ณํ๋์ด DMA๋ฅผ ํตํด, ์ต์ข ์ ์ผ๋ก ADC_DualConvertedValueTab[0] ์์น์ ์ ์ฅ๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก ํฌํธ PC2๋ก๋ถํฐ ๋ฐ์ ์๋ ๋ก๊ทธ ์ ๋ณด๋ Channel_12๋ฅผ ํตํด ๋์งํธ๋ก ๋ณํ๋์ด DMA๋ฅผ ํตํด, ์ต์ข ์ ์ผ๋ก ADC_DualConvertedValueTab[1] ์์น์ ์ ์ฅ๋๋ค.
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5); //PC1 as Input
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 2, ADC_SampleTime_55Cycles5); //PC2 as Input
ADC_DMACmd(ADC1, ENABLE);
ADC1 ์ด๊ธฐํ ๊ทธ๋ฆฌ๊ณ , ๋ณํ ์์
์ด ์ฝ๋์ ๋ด์ฉ์ STM32 ADC์ ์์ผ๋ ์ฐธ์กฐํ์.
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
์ ์ฒด ์์ ์ฝ๋
์ค๋ช ์ ์ฝ๊ฒ ํ๊ธฐ์ํด ์์ ๋ณด์ฌ์ค ์ฝ๋๋ ์ค์ ์์ ์ฝ๋์๋ ์กฐ๊ธ ๋ค๋ฅด๋ค. ์ต์ข ์์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
์ด ์์ ๋ ํฌํธ PC1, PC2 ๋ก๋ถํฐ ์๋ ๋ก๊ทธ ์ ํธ๋ฅผ ๋ฐ์, ๊ฐ๊ฐ์ ํฌํธ์ ์ ์์ด 2V ์ด์์ด๋๋ฉด, PC9, PC8 LED๋ฅผ ์ผ๊ฒํ๋ค. ์ด ์์ ๋ฅผ ์คํํด ๋ณด๋ ค๋ฉด, ๊ฐ๋ณ์ ํญ ๋ ๊ฐ๊ฐ ์์ด์ผ ํ๋ค.
//
// ADC Channel 2 Example
//
#include "stm32f10x.h"
#include "STM32vldiscovery.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
double x = 0, y = 0;
GPIO_InitTypeDef myGPIO;
ADC_InitTypeDef myADC;
DMA_InitTypeDef DMA_InitStructure;
__IO uint32_t ADC_DualConvertedValueTab[2];
//Configure other I/O pins
void GPIO_config(void)
{
// initialize LED C8, 9
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_StructInit(&myGPIO);
myGPIO.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
myGPIO.GPIO_Mode = GPIO_Mode_Out_PP;
myGPIO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &myGPIO);
}
void adc_config()
{
// PC1, PC2๋ฅผ analog input ๋ชจ๋๋ก ์ค์ ํ๋ค.
myGPIO.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; //set to pin 1, 2
myGPIO.GPIO_Mode = GPIO_Mode_AIN; //set as analog input
GPIO_Init(GPIOC, &myGPIO); //set to C1, C2
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //clock for ADC (max 14MHz, 72/6=12MHz)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //enable ADC clock
// DMA1 channel1 configuration ----------------------------------------------
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_DualConvertedValueTab;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 32bit
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // 32bit
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);
//configure ADC1 parameters
myADC.ADC_Mode = ADC_Mode_Independent;
myADC.ADC_ScanConvMode = ENABLE;
myADC.ADC_ContinuousConvMode = ENABLE;
myADC.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
myADC.ADC_DataAlign = ADC_DataAlign_Right;
myADC.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &myADC);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5); //PC1 as Input
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 2, ADC_SampleTime_55Cycles5); //PC2 as Input
ADC_DMACmd(ADC1, ENABLE);
//enable
ADC_Cmd(ADC1, ENABLE);
//Calibrate ADC *optional?
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
int main(void)
{
GPIO_config(); //configure pins
adc_config(); //configure ADC
while(1)
{
x = ADC_DualConvertedValueTab[0] * 3.3/4095; //get analog value and convert to volts, 12bit ADC
if(x > 2)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_SET);//turn LED on 1
}
else
{
GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_RESET);//turn LED off 0
}
y = ADC_DualConvertedValueTab[1] * 3.3/4095; //get analog value and convert to volts, 12bit ADC
if(y > 2)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);//turn LED on 1
}
else
{
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);//turn LED off 0
}
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/