stm32 AD模数转换[操作寄存器+库函数]

时间:2021-01-31 16:35:26
stm32f103最少有2个AD模数转换器,每个ADC都有18个通道,可以测量16个外部和2个内部模拟量。最大转换频率为1Mhz,也就是转换时间为1us(在 ADCCLK = 14Mhz,采样周期为1.5个时钟周期时)。最大时钟超过14Mhz,将导致ADC转换准确度降低。stm32的ADC是12位精度的。
 
stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。本例只使用规则通道实现独立模式的中断采样,这里不再赘述两种通道区别。
 
stm32的ADC可以由外部事件触发(例如定时器捕获,EXTI线)和软件触发(即在配置相关寄存器时,直接开启采样)。
 

本例实现AD采样PB0口,使用串口输出PB0口电压值。PB0口接变阻器以改变调整电压。
效果如下:
                                     ADValue = 1.39v
                                     ADValue = 1.38v
                                     ADValue = 1.40v
                                     ADValue = 1.38v
                                     ADValue = 1.39v
 

直接操作寄存器
 
首先需要配置ADC的时钟分频值,在RCC->CFGR的[15:14]位:
  • 00:PCLK2 2分频后作为ADC时钟         01:PCLK2 4分频后作为ADC时钟
  • 10:PCLK2 6分频后作为ADC时钟         11:PCLK2 8分频后作为ADC时钟
设定各通道的采样时间ADCx->SMPR,该寄存器给每个通道3位来选择8种采样周期:
  • 000:1.5周期               100:41.5周期
  • 001:7.5周期               101:55.5周期
  • 010:13.5周期             110:71.5周期
  • 011:28.5周期             111:239.5周期
采样时间算法为: (采样周期+12.5)/分频后的时钟
 
ADC采样得到的只是一个相对值,将 转换值/4096*参考电压 即可得到采样电压 这里的4096是因为stm32的adc为12位精度,表示参考电压时即为 2^12=4096
 
代码如下:  (system.h 和 stm32f10x_it.h 等相关代码参照  stm32 直接操作寄存器开发环境配置
User/main.c
01 #include
<stm32f10x_lib.h>    
02 #include
"system.h"
03 #include
"usart.h"
04 #include
"adc.h"
05 #include
"stdio.h" 
06  
07 #define
LED1 PAout(4)
08 #define
LED2 PAout(5)
09  
10 #define
VREF 3.3         //参考电压
11 void Gpio_Init(void);
12  
13 int main(void)
14 {                
15     u16 ADValue;
16     float temp;
17  
18     Rcc_Init(9);             //系统时钟设置
19     Usart1_Init(72,9600);   //设置串口时钟和波特率
20  
21     Adc1_Init(8,7);   //使用8通道采样,采样时间系数为7(111),据手册可得采样时间为 (239.5+12.5)/12= 21 (us)
22     Gpio_Init();
23  
24     while(1){
25          
26         ADValue = Get_Adc(ADC_1,8);
27         temp = (float)VREF*(ADValue/4096);     //ADC精度为12位精度,即达到 VREF电压时为 2^12 = 4096
28  
29         printf("\r\n ADValue = %.2fv\r\n",temp);
30  
31         LED2 = !LED2;
32  
33         delay(100000);   //延时100ms
34  
35     }      
36 }
37  
38  
39 void Gpio_Init(void)
40 {
41     RCC->APB2ENR|=1<<2;    //使能PORTA时钟    
42     RCC->APB2ENR|=1<<3;    //使能PORTB时钟    
43                
44  
45     GPIOA->CRL&=0xFF0FFFF0;
46     GPIOA->CRL|=0xFF3FFFF0; // PA0设置为模拟输入,PA4推挽输出
47  
48     GPIOB->CRL&=0xFFFFFFF0;
49     GPIOB->CRL|=0xFFFFFFF0; // PB0设置为模拟输入
50  
51      
52     //USART1 串口I/O设置
53  
54     GPIOA -> CRH&=0xFFFFF00F;   //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
55     GPIOA -> CRH|=0x000008B0;     
56 }
Library/src/adc.c
01 #include
<stm32f10x_lib.h>        
02 #include
"adc.h"
03  
04  
05 //ADC1采样初始化
06 //独立工作模式
07 //参数说明:
08 //         
ADC_CH_x    选择使用通道(0~17),目前暂支持0~15通道
09 //         
ADC_CH_SMP  设定采样周期(0~7)
10 //采样周期算法:
11  
12 void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP)
13 {
14     RCC -> APB2ENR |= 1<<9;        //开启ADC1时钟
15     RCC -> APB2RSTR |= 1<<9;       //复位ADC1
16     RCC -> APB2RSTR &= ~(1<<9);    //ADC1复位结束
17  
18     RCC -> CFGR &= ~(3<<14);       //分频因子清零
19     RCC -> CFGR |= 2<<14;          //设定分频因数为2,PCLK2 6分频后作为ADC时钟
20  
21     ADC1 -> CR1 &= 0xF0FFFF;     //工作模式清零
22     ADC1 ->  CR1 |= 0<<16;         //设定为独立模式
23     ADC1 -> CR1 &= ~(1<<8);            //非扫描工作模式
24     ADC1 -> CR2 &= ~(1<<1);            //关闭连续转换
25  
26     ADC1 -> CR2 &= ~(7<<17);       //清除规则通道启动事件
27     ADC1 -> CR2 |= 7<<17;          //设定规则通道启动事件为软件启动(SWSTART)
28  
29     ADC1 -> CR2 |= 1<<20;          //使用外部事件触发 SWSTART
30     ADC1 -> CR2 &= ~(1<<11);       //设置对齐模式为右对齐
31  
32     ADC1 -> SQR1 &= ~(0xF<<20);        //清零规则序列的数量
33     ADC1 -> SQR1 |= 15<<20;            //设置规则序列的数量为16
34  
35     ADC1 -> SMPR2 &= 0x00000000; //清零通道采样时间
36     ADC1 -> SMPR1 &= 0xFF000000;
37  
38     if(ADC_CH_x <= 9 ){
39         ADC1 -> SMPR2 |= 7<<(ADC_CH_x*3);          //设置通道x采样时间,提高采样时间可以提高采样精度
40     }
41  
42     if(ADC_CH_x > 9 ){
43         ADC1 -> SMPR1 |= 7<<((ADC_CH_x-10)*3);    
44     }
45      
46  
47     ADC1 -> CR2 |= 1<<0;           //开启AD转换
48     ADC1 -> CR2 |= 1<<3;           //使能复位校准,由硬件清零
49     while((ADC1 -> CR2)& (1<<3));  //等待校准结束
50     ADC1 -> CR2 |= 1<<2;           //开启AD校准,由硬件清零
51     while((ADC1 -> CR2)& (1<<2));  //等待校准结束
52  
53 }
54  
55 //取得数模转换的值
56 //参数说明:(参数定义于adc.h)
57 //      
ADC_x  (0~3),选择数模转换器
58 //      
ADC_CH_x    (0~15),选择通道
59 u16
Get_Adc(u8 ADC_x,u8 ADC_CH_x)
60 {
61     u16 data = 0;
62  
63     switch(ADC_x)  
64     {
65         case 1 : {
66  
67             ADC1 -> SQR3 &= 0xFFFFFFE0;          //清除通道选择
68             ADC1 -> SQR3 |= ADC_CH_x;                //选择通道
69             ADC1 -> CR2  |= 1<<22;             //开启AD转换
70             while(!(ADC1 -> SR & 1<<1));           //等待转换结束
71  
72             data = ADC1->DR;
73             break;
74         }
75         case 2 : {break;}
76         case 3 : {break;}
77     }
78  
79     return data;
80 }
Library/inc/adc.h
1 #include
<stm32f10x_lib.h>   
2  
3 #define 
ADC_1 0x01
4 #define 
ADC_2 0x02
5 #define 
ADC_3 0x03
6  
7 void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP);
8 u16
Get_Adc(u8 ADC_x,u8 ADC_CH_x);
 
 
库函数操作
 
main.c
001 #include
"stm32f10x.h"
002 #include
"stdio.h"
003  
004  
005 #define 
PRINTF_ON  1
006 #define 
VREF       3.3        // 参考电压
007  
008  
009 void RCC_Configuration(void);
010 void GPIO_Configuration(void);
011 void USART_Configuration(void);
012 void ADC_Configuration(void);
013  
014  
015 int main(void)
016 {
017     float ADValue = 0.00;
018     u32 delayTime = 0;
019  
020     RCC_Configuration();
021     GPIO_Configuration();
022     USART_Configuration();
023     ADC_Configuration();
024  
025     while(1)
026     {
027         if(delayTime++ >=2000000)
028         {
029             delayTime = 0;
030             ADValue = VREF*ADC_GetConversionValue(ADC1)/0x0fff;
031             printf("\r\n ADValue = %.2fv\r\n",ADValue);
032          
033         }
034     }
035 }
036  
037    
038 void GPIO_Configuration(void)
039 {
040     GPIO_InitTypeDef GPIO_InitStructure;                                                                                    
041     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
042     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;          
043     GPIO_Init(GPIOA , &GPIO_InitStructure);
044  
045  
046     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
047     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
048     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        
049     GPIO_Init(GPIOA , &GPIO_InitStructure);
050  
051     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
052     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;          
053     GPIO_Init(GPIOA , &GPIO_InitStructure);
054 }
055  
056 void ADC_Configuration(void)
057 {
058     ADC_InitTypeDef ADC_InitStructure; 
059  
060     RCC_ADCCLKConfig(RCC_PCLK2_Div4);   //配置ADC时钟分频
061  
062     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
063     ADC_InitStructure.ADC_ScanConvMode = ENABLE;
064     ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
065     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
066     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
067     ADC_InitStructure.ADC_NbrOfChannel = 1;
068     ADC_Init(ADC1,&ADC_InitStructure);
069      
070     ADC_RegularChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_55Cycles5);
071     ADC_Cmd(ADC1,ENABLE);
072     ADC_ResetCalibration(ADC1);
073     while(ADC_GetResetCalibrationStatus(ADC1));
074     ADC_StartCalibration(ADC1);
075     while(ADC_GetCalibrationStatus(ADC1));
076     ADC_SoftwareStartConvCmd(ADC1,ENABLE);
077  
078  
079 }
080  
081  
082 void RCC_Configuration(void)
083 {
084     /* 定义枚举类型变量 HSEStartUpStatus */
085     ErrorStatus HSEStartUpStatus;
086  
087     /* 复位系统时钟设置*/
088     RCC_DeInit();
089     /* 开启HSE*/
090     RCC_HSEConfig(RCC_HSE_ON);
091     /* 等待HSE起振并稳定*/
092     HSEStartUpStatus = RCC_WaitForHSEStartUp();
093     /* 判断HSE起是否振成功,是则进入if()内部 */
094     if(HSEStartUpStatus == SUCCESS)
095     {
096         /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
097         RCC_HCLKConfig(RCC_SYSCLK_Div1);
098         /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
099         RCC_PCLK2Config(RCC_HCLK_Div1);
100         /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
101         RCC_PCLK1Config(RCC_HCLK_Div2);
102         /* 设置FLASH延时周期数为2 */
103         FLASH_SetLatency(FLASH_Latency_2);
104         /* 使能FLASH预取缓存 */
105         FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
106         /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
107         RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
108         /* 使能PLL */
109         RCC_PLLCmd(ENABLE);
110         /* 等待PLL输出稳定 */
111         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
112         /* 选择SYSCLK时钟源为PLL */
113         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
114         /* 等待PLL成为SYSCLK时钟源 */
115         while(RCC_GetSYSCLKSource() != 0x08);
116     }
117     /* 打开APB2总线上的GPIOA时钟*/
118     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOB|RCC_APB2Periph_ADC1, ENABLE);
119  
120     //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
121          
122 }
123  
124   
125 void USART_Configuration(void)
126 {
127     USART_InitTypeDef USART_InitStructure;
128     USART_ClockInitTypeDef USART_ClockInitStructure;
129  
130     USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
131     USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
132     USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                     
133     USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
134     USART_ClockInit(USART1 , &USART_ClockInitStructure);
135  
136     USART_InitStructure.USART_BaudRate = 9600;
137     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
138     USART_InitStructure.USART_StopBits = USART_StopBits_1;
139     USART_InitStructure.USART_Parity = USART_Parity_No;
140     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
141     USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
142     USART_Init(USART1,&USART_InitStructure);
143  
144     USART_Cmd(USART1,ENABLE);
145 }
146  
147  
148 #if 
PRINTF_ON
149  
150 int fputc(int ch,FILE *f)
151 {
152     USART_SendData(USART1,(u8) ch);
153     while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
154     return ch;
155 }
156  
157 #endif