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> |
10 | #define VREF 3.3 //參考電壓 |
11 | void Gpio_Init( void ); |
19 | Usart1_Init(72,9600); //設定序列槽時鐘和波特率 |
21 | Adc1_Init(8,7); //使用8通道采樣,采樣時間系數為7(111),據手冊可得采樣時間為 (239.5+12.5)/12= 21 (us) |
26 | ADValue = Get_Adc(ADC_1,8); |
27 | temp = ( float )VREF*(ADValue/4096); //ADC精度為12位精度,即達到 VREF電壓時為 2^12 = 4096 |
29 | printf ( "\r\n ADValue = %.2fv\r\n" ,temp); |
33 | delay(100000); //延時100ms |
39 | void Gpio_Init( void ) |
41 | RCC->APB2ENR|=1<<2; //使能PORTA時鐘 |
42 | RCC->APB2ENR|=1<<3; //使能PORTB時鐘 |
45 | GPIOA->CRL&=0xFF0FFFF0; |
46 | GPIOA->CRL|=0xFF3FFFF0; // PA0設定為模拟輸入,PA4推挽輸出 |
48 | GPIOB->CRL&=0xFFFFFFF0; |
49 | GPIOB->CRL|=0xFFFFFFF0; // PB0設定為模拟輸入 |
54 | GPIOA -> CRH&=0xFFFFF00F; //設定USART1 的Tx(PA.9)為第二功能推挽,50MHz;Rx(PA.10)為浮空輸入 |
55 | GPIOA -> CRH|=0x000008B0; |
Library/src/adc.c
01 | #include <stm32f10x_lib.h> |
08 | // ADC_CH_x 選擇使用通道(0~17),目前暫支援0~15通道 |
09 | // ADC_CH_SMP 設定采樣周期(0~7) |
12 | void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP) |
14 | RCC -> APB2ENR |= 1<<9; //開啟ADC1時鐘 |
15 | RCC -> APB2RSTR |= 1<<9; //複位ADC1 |
16 | RCC -> APB2RSTR &= ~(1<<9); //ADC1複位結束 |
18 | RCC -> CFGR &= ~(3<<14); //分頻因子清零 |
19 | RCC -> CFGR |= 2<<14; //設定分頻因數為2,PCLK2 6分頻後作為ADC時鐘 |
21 | ADC1 -> CR1 &= 0xF0FFFF; //工作模式清零 |
22 | ADC1 -> CR1 |= 0<<16; //設定為獨立模式 |
23 | ADC1 -> CR1 &= ~(1<<8); //非掃描工作模式 |
24 | ADC1 -> CR2 &= ~(1<<1); //關閉連續轉換 |
26 | ADC1 -> CR2 &= ~(7<<17); //清除規則通道啟動事件 |
27 | ADC1 -> CR2 |= 7<<17; //設定規則通道啟動事件為軟體啟動(SWSTART) |
29 | ADC1 -> CR2 |= 1<<20; //使用外部事件觸發 SWSTART |
30 | ADC1 -> CR2 &= ~(1<<11); //設定對齊模式為右對齊 |
32 | ADC1 -> SQR1 &= ~(0xF<<20); //清零規則序列的數量 |
33 | ADC1 -> SQR1 |= 15<<20; //設定規則序列的數量為16 |
35 | ADC1 -> SMPR2 &= 0x00000000; //清零通道采樣時間 |
36 | ADC1 -> SMPR1 &= 0xFF000000; |
39 | ADC1 -> SMPR2 |= 7<<(ADC_CH_x*3); //設定通道x采樣時間,提高采樣時間可以提高采樣精度 |
43 | ADC1 -> SMPR1 |= 7<<((ADC_CH_x-10)*3); |
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)); //等待校準結束 |
57 | // ADC_x (0~3),選擇數模轉換器 |
58 | // ADC_CH_x (0~15),選擇通道 |
59 | u16 Get_Adc(u8 ADC_x,u8 ADC_CH_x) |
67 | ADC1 -> SQR3 &= 0xFFFFFFE0; //清除通道選擇 |
68 | ADC1 -> SQR3 |= ADC_CH_x; //選擇通道 |
69 | ADC1 -> CR2 |= 1<<22; //開啟AD轉換 |
70 | while (!(ADC1 -> SR & 1<<1)); //等待轉換結束 |
Library/inc/adc.h
1 | #include <stm32f10x_lib.h> |
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 view source print ?
001 | #include "stm32f10x.h" |
006 | #define VREF 3.3 // 參考電壓 |
009 | void RCC_Configuration( void ); |
010 | void GPIO_Configuration( void ); |
011 | void USART_Configuration( void ); |
012 | void ADC_Configuration( void ); |
017 | float ADValue = 0.00; |
021 | GPIO_Configuration(); |
022 | USART_Configuration(); |
027 | if (delayTime++ >=2000000) |
030 | ADValue = VREF*ADC_GetConversionValue(ADC1)/0x0fff; |
031 | printf ( "\r\n ADValue = %.2fv\r\n" ,ADValue); |
038 | void GPIO_Configuration( void ) |
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); |
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); |
051 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; |
052 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; |
053 | GPIO_Init(GPIOA , &GPIO_InitStructure); |
056 | void ADC_Configuration( void ) |
058 | ADC_InitTypeDef ADC_InitStructure; |
060 | RCC_ADCCLKConfig(RCC_PCLK2_Div4); //配置ADC時鐘分頻 |
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); |
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); |
082 | void RCC_Configuration( void ) |
085 | ErrorStatus HSEStartUpStatus; |
090 | RCC_HSEConfig(RCC_HSE_ON); |
092 | HSEStartUpStatus = RCC_WaitForHSEStartUp(); |
094 | if (HSEStartUpStatus == SUCCESS) |
097 | RCC_HCLKConfig(RCC_SYSCLK_Div1); |
099 | RCC_PCLK2Config(RCC_HCLK_Div1); |
101 | RCC_PCLK1Config(RCC_HCLK_Div2); |
103 | FLASH_SetLatency(FLASH_Latency_2); |
105 | FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); |
107 | RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); |
111 | while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); |
113 | RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); |
115 | while (RCC_GetSYSCLKSource() != 0x08); |
118 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOB|RCC_APB2Periph_ADC1, ENABLE); |
120 | //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); |
125 | void USART_Configuration( void ) |
127 | USART_InitTypeDef USART_InitStructure; |
128 | USART_ClockInitTypeDef USART_ClockInitStructure; |
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); |
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); |
144 | USART_Cmd(USART1,ENABLE); |
150 | int fputc ( int ch, FILE *f) |
152 | USART_SendData(USART1,(u8) ch); |
153 | while (USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET); |