ADC实验

STM32 拥有 1~3 个 ADC( STM32F101/102 系列只有 1 个 ADC),这些 ADC 能够独立使用,也可使用双重模式(提升采样率)。 STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换能够单次、连续、扫描或间断模式执行。 ADC 的结果能够左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性容许应用程序检测输入电压是否超出用户定义的高/低阀值。html


STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道至关于你正常运行的程序,而注入通道呢,就至关于中断。在你程序正常执行的时候,中断是能够打断你的执行的。同这个相似,注入通道的转换能够打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。ide


经过一个形象的例子能够说明: 假如你在家里的院子内放了 5 个温度探头,室内放了3个温度探头; 你须要时刻监视室外温度便可,但偶尔你想看看室内的温度;所以你可使用规则通道组循环扫描室外的 5 个探头并显示 AD 转换结果,当你想看室内温度时,经过一个按钮启动注入转换组(3 个室内探头)并暂时显示室内温度,当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。从系统设计上,测量并显示室内温度的过程当中断了测量并显示室外温度的过程,但程序设计上能够在初始化阶段分别设置好不一样的转换组,系统运行中没必要再变动循环转换的配置,从而达到两个任务互不干扰和快速切换的结果。能够设想一下,若是没有规则组和注入组的划分,当你按下按钮后,须要重新配置 AD 循环扫描的通道,而后在释放按钮后需再次配置 AD 循环扫描的通道。函数

但本节只用到规则通道,由于是单次转换模式。。大概能够理解为咱们如今只测量一个地方的电压值。。字体

配置ADC步骤以下:es5

1) 开启 PA 口和 ADC1 时钟,设置 PA1 为模拟输入。
spa

STM32F103RCT6 的 ADC 通道 1 在 PA1 上,因此,咱们先要使能 PORTA 的时钟,而后设置 PA1 为模拟输入。 使能 GPIOA 和 ADC 时钟用 RCC_APB2PeriphClockCmd 函数,设置 PA1的输入方式,使用 GPIO_Init 函数便可。这里咱们列出 STM32 的 ADC 通道与 GPIO 对应表:.net


2)复位 ADC1,同时设置 ADC1 分频因子。
3)初始化 ADC1 参数,设置 ADC1 的工做模式以及规则序列的相关信息。
4)使能 ADC 并校准。
5)读取 ADC 值。

配置ADC的文件adc.c


[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到个人代码片
  1. #include "adc.h"  
  2. void Adc_Init(void)  
  3. {  
  4.     ADC_InitTypeDef ADC_ist;  
  5.     GPIO_InitTypeDef GPIO_ist;  
  6.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE );  
  7.     //72M/6=12,ADC 最大时间不能超过 14M  
  8.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置 ADC分频因子6  
  9.     //PA1 做为模拟通道输入引脚  
  10.     GPIO_ist.GPIO_Pin=GPIO_Pin_1;  
  11.     GPIO_ist.GPIO_Mode=GPIO_Mode_AIN;//模拟输入  
  12.     GPIO_Init(GPIOA,&GPIO_ist);  
  13.       
  14.     ADC_DeInit(ADC1);//复位 ADC1,将外设 ADC1 的所有寄存器重设为缺省值  
  15.     ADC_ist.ADC_ModeADC_Mode_Independent;//ADC 独立模式  
  16.     ADC_ist.ADC_ScanConvMode=DISABLE;//单通道模式  
  17.     ADC_ist.ADC_ContinuousConvMode=DISABLE;//单次转换模式  
  18.     //转换由软件而不是外部触发启动  
  19.     ADC_ist.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;  
  20.     ADC_ist.ADC_DataAlign=ADC_DataAlign_Right;//ADC 数据右对齐  
  21.     ADC_ist.ADC_NbrOfChannel=1;//顺序进行规则转换的 ADC 通道的数目  
  22.     ADC_Init(ADC1,&ADC_ist);  
  23.       
  24.     ADC_Cmd(ADC1,ENABLE);//使能指定的 ADC1  
  25.     ADC_ResetCalibration(ADC1);//开启复位校准  
  26.     while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准结束  
  27.     ADC_StartCalibration(ADC1);//开启 AD 校准  
  28.     while(ADC_GetCalibrationStatus(ADC1));//等待校准结束  
  29. }  
  30. //得到 ADC 值  
  31. //ch:通道值 0~3  
  32. u16 Get_Adc(u8 ch)  
  33. {  
  34.     //设置指定 ADC 的规则组通道设置它们的转化顺序和采样时间  
  35.     ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5);   
  36.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的软件转换功能  
  37.     while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//等待转换结束  
  38.     return ADC_GetConversionValue(ADC1);//返回最近一次 ADC1 规则组的转换结果  
  39. }  
  40. u16 Get_Adc_Average(u8 ch,u8 times)  
  41. {  
  42.     u32 tem_val=0;  
  43.     u8 i;  
  44.     for(i=0;i<times;i++)  
  45.     {  
  46.         tem_val+=Get_Adc(ch);  
  47.         delay_ms(5);  
  48.     }  
  49.     return tem_val/times;  
  50. }  


adc.h
[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到个人代码片
  1. #ifndef _ADC_H  
  2. #define _ADC_H  
  3. #include "sys.h"  
  4. #include "delay.h"  
  5. void Adc_Init(void);  
  6. u16 Get_Adc(u8 ch);  
  7. u16 Get_Adc_Average(u8 ch,u8 times);  
  8. #endif  

主函数
[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到个人代码片
  1. #include "led.h"  
  2. #include "delay.h"  
  3. #include "sys.h"  
  4. #include "usart.h"  
  5. #include "lcd.h"  
  6. #include "adc.h"  
  7. void init()  
  8. {  
  9.     delay_init();            //延时函数初始化      
  10.     uart_init(9600);        //串口初始化为9600  
  11.     LED_Init();             //初始化与LED链接的硬件接口  
  12.     LCD_Init();  
  13.     Adc_Init();  
  14.     POINT_COLOR=RED;//设置字体为红色  
  15.     LCD_ShowString(60,40,200,24,24,"ADC Test ^-^");  
  16.     LCD_ShowString(60,70,200,16,16,"Medium difficulty");  
  17.     LCD_ShowString(60,90,200,16,16,"2015/1/24");  
  18.     LCD_ShowString(60,110,200,16,16,"By--Mr yh");  
  19.     //显示提示信息  
  20.     POINT_COLOR=BLUE;//设置字体为蓝色  
  21.     LCD_ShowString(60,130,200,16,16,"ADC_CH0_VAL:");   
  22.     LCD_ShowString(60,150,200,16,16,"ADC_CH0_VOL:0.000V");  
  23. }  
  24. int main(void)  
  25. {  
  26.     u16 adcnum;  
  27.     float tem;  
  28.     init();  
  29.     while(1)  
  30.     {  
  31.         adcnum=Get_Adc_Average(ADC_Channel_1,10);  
  32.         LCD_ShowxNum(156,130,adcnum,4,16,0);//显示ADC的值  
  33.         tem=(float)adcnum*(3.3/4096);  
  34.         adcnum=tem;  
  35.         LCD_ShowxNum(156,150,adcnum,1,16,0);//显示电压值的整数位  
  36.         tem-=adcnum;  
  37.         tem*=1000;  
  38.         LCD_ShowxNum(172,150,tem,3,16,0x80);//显示ADC的值的小数位  
  39.         LED0=!LED0;  
  40.         delay_ms(250);  
  41.     }  
  42. }  


得到了ADC的值以后。。 再转换成电压值的公式就看不懂了。。orz

不过有一个地方须要注意 LCD_ShowxNum()的用法设计

再次翻出它的源码code

[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到个人代码片
  1. //显示数字,高位为0,仍是显示  
  2. //x,y:起点坐标  
  3. //num:数值(0~999999999);     
  4. //len:长度(即要显示的位数)  
  5. //size:字体大小  
  6. //mode:  
  7. //[7]:0,不填充;1,填充0.  
  8. //[6:1]:保留  
  9. //[0]:0,非叠加显示;1,叠加显示.  
  10. void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)  
  11. {    
  12.     u8 t,temp;  
  13.     u8 enshow=0;                             
  14.     for(t=0;t<len;t++)  
  15.     {  
  16.         temp=(num/LCD_Pow(10,len-t-1))%10;  
  17.         if(enshow==0&&t<(len-1))  
  18.         {  
  19.             if(temp==0)  
  20.             {  
  21.                 if(mode&0X80)LCD_ShowChar(x+(size/2)*t,y,'0',size,mode&0X01);    
  22.                 else LCD_ShowChar(x+(size/2)*t,y,' ',size,mode&0X01);    
  23.                 continue;  
  24.             }else enshow=1;   
  25.                
  26.         }  
  27.         LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,mode&0X01);   
  28.     }  
  29. }   
看到最后一个参数的说明, mode 是一个8位的变量,第7位为0表明不填充,1表明填充。

一开始对填充这个概念没什么理解,因而将两种结果(填充和不填充)烧进去看了一下,发现显示0.001 的时候,若是选不填充,它会显示0.  1(点和1之间有2个空格),若是是填充就会显示0.001 (正常显示)因此我对填充的理解是:假如一个数6, 你想显示006,那么须要设置数的长度为3,填充模式orm