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 |
02 |
#include |
03 |
#include |
04 |
#include |
05 |
#include |
06 |
07 |
#define |
08 |
#define |
09 |
10 |
#define |
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 |
02 |
#include |
03 |
04 |
05 |
//ADC1采样初始化 |
06 |
//独立工作模式 |
07 |
//参数说明: |
08 |
// |
09 |
// |
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 |
// |
58 |
// |
59 |
u16 |
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 |
2 |
3 |
#define |
4 |
#define |
5 |
#define |
6 |
7 |
void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP);
|
8 |
u16 |
库函数操作
main.c
001 |
#include |
002 |
#include |
003 |
004 |
005 |
#define |
006 |
#define |
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 |
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 |