大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“【STC15系列】SYK-0806-A2S1 工业自动化控制之【17-EEPROM实现数据掉电保存】”,
这一篇中,笔者将会给大家介绍“ADC的应用”。
SYK-0806-A2S1 控制板预留了两路 0-5V 电压的AD采集接口,直接采用了官方手册上提供的参考设计:
这种直接用单片机IO口采集的方式,电路也比较简单。
AD基准电压也没有另外用专用的基准电源芯片,直接用芯片电源作为AD基准,
当然,这样做误差肯定是会有点的,但是做一般应用的话,应该是足够了,
对精度要求高一点的应用场合,还是建议用个专门的基准电源芯片会好点,或者选择带更高AD精度的单片机。
接下来就是代码部分,笔者还是用官方提供的库函数的方式来编写的,采用的是AD查询的方式,
我们从原理图上可以看到AD采集使用的P10和P11两个引脚:
接下来,我们便需要将这两个引脚的模式配置成AD口的模式,即高阻输入模式,这部分主要在bsp_gpio.h和bsp_gpio.c中:
- /****************** ADC GPIO ******************/
- //ADC0 - ADC1
-
- #define ADC0_GPIO_PIN GPIO_Pin_1
- #define ADC0_GPIO GPIO_P1
-
- #define ADC1_GPIO_PIN GPIO_Pin_0
- #define ADC1_GPIO GPIO_P1
复制代码
- GPIO_InitTypeDef GPIO_InitStructure; //结构定义
-
- //P10-11口设置为高阻输入
- GPIO_InitStructure.Pin = ADC0_GPIO_PIN; //指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7, 或操作
- GPIO_InitStructure.Mode = GPIO_HighZ; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(ADC0_GPIO,&GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.Pin = ADC1_GPIO_PIN; //指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7, 或操作
- GPIO_InitStructure.Mode = GPIO_HighZ; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
- GPIO_Inilize(ADC1_GPIO,&GPIO_InitStructure);//初始化
复制代码
接下来,就是在app.c文件中定义一些跟adc相关的变量:
- #define Timer0_Fre 500UL //timer0中断频率 500*2Hz
-
- u8 data adc_count = 0;//ADC采集次数
- u16 data adc_msec = 0;//ADC采样计时
-
- u16 data ADC0_VALUE = 0;//ADC0的AD值
- u16 data ADC1_VALUE = 0;//ADC1的AD值
-
- u16 data ADC0_Voltage = 0;//ADC0的电压值
- u16 data ADC1_Voltage = 0;//ADC1的电压值
-
- u32 data ADC0_VALUE_TEMP = 0;
- u32 data ADC1_VALUE_TEMP = 0;
复制代码
然后就是app_init()函数,对定时器0、串口、GPIO、ADC相关参数进行配置:
- /********************* APP初始化 ***********************/
- void app_init(void)
- {
- Timer0_config(Timer0_Fre); //定时器0配置
-
- UART_config(); //UART初始化
- GPIO_Config(); //GPIO初始化
- ADC_config(); //ADC初始化
-
- TI = 1;
- EA = 1;//开启总中断
-
- printf("This is ADC test Program!\r\n");//串口打印结果
- }
复制代码
前面几个功能的配置之前有说过很多次了,主要是看下ADC的配置,这个是在bsp_adc.c文件中:
- /**************** ADC配置函数 *****************/
- /*P11 ADC0
- /*P10 ADC1
- /**********************************************/
- void ADC_config(void)
- {
- ADC_InitTypeDef ADC_InitStructure; //结构定义
-
- ADC_InitStructure.ADC_Px = ADC_P11|ADC_P10; //设置要做ADC的IO, ADC_P10 ~ ADC_P17(或操作),ADC_P1_All
- ADC_InitStructure.ADC_Speed = ADC_90T; //ADC速度 ADC_90T,ADC_180T,ADC_360T,ADC_540T
- ADC_InitStructure.ADC_Power = ENABLE; //ADC功率允许/关闭 ENABLE,DISABLE
- ADC_InitStructure.ADC_AdjResult = ADC_RES_H2L8; //ADC结果调整, ADC_RES_H2L8,ADC_RES_H8L2
- ADC_InitStructure.ADC_Polity = PolityLow; //优先级设置 PolityHigh,PolityLow
- ADC_InitStructure.ADC_Interrupt = DISABLE; //中断允许 ENABLE,DISABLE
- ADC_Inilize(&ADC_InitStructure); //初始化
-
- ADC_PowerControl(ENABLE); //单独的ADC电源操作函数, ENABLE或DISABLE
- }
复制代码
这里主要要注意一下ADC结果的对齐方式,即ADC_InitStructure.ADC_AdjResult这个参数的配置,
这个参数实际上就是配置CKK0_DIV寄存器的ADRJ位,用于A/D转行结果寄存器的数据格式调整控制。
从手册中我们可以得知:
在ADC_config()配置中,ADC_InitStructure.ADC_AdjResult = ADC_RES_H2L8,也就是配置的ADRJ位为“1”,
即:10位A/D转换结果的高2位存放在ADC_RES的低2位中,低8位存放在ADC_RESL中。
- #define ADC_RES_H2L8 1
- #define ADC_RES_H8L2 0
复制代码
Timer0中断函数为ADC定时采集提供了一个1ms的定时基准:
- /********************* Timer0中断函数************************/
- void timer0_int (void) interrupt TIMER0_VECTOR //频率可变
- {
- adc_msec ++;
- }
复制代码
我们按照100ms采集一次AD值,每采集10次求一次平均值的方式,尽量让ADC抖动的不要那么厉害:
- /********************* APP运行 ***********************/
- void app_run(void)
- {
- if(adc_msec>=100) //每隔100ms采集一次AD数据
- {
- adc_msec = 0;
-
- ADC0_VALUE_TEMP += Get_ADC10bitResult(1);//通道1-P11
- ADC1_VALUE_TEMP += Get_ADC10bitResult(0);//通道0-P10
-
- if(++adc_count>=10)//采集10次数据,求平均值
- {
- ADC0_VALUE = ADC0_VALUE_TEMP/10; //求出ADC0的AD平均值
- ADC1_VALUE = ADC1_VALUE_TEMP/10; //求出ADC1的AD平均值
-
- //将得到的AD值换算成电压值(单位:mV)
- ADC0_Voltage = (ADC0_VALUE*5000UL)/1024; //求出ADC0的电压值,单位mV
- ADC1_Voltage = (ADC1_VALUE*5000UL)/1024; //求出ADC1的电压值,单位mV
-
- ADC0_VALUE_TEMP = 0;
- ADC1_VALUE_TEMP = 0;
-
- adc_count = 0;
-
- printf("ADC0_VALUE = %u\r\n",ADC0_VALUE); //ADC0的AD平均值
- printf("ADC1_VALUE = %u\r\n",ADC1_VALUE); //ADC1的AD平均值
- printf("ADC0_Voltage = %u mV\r\n",ADC0_Voltage);//ADC0的电压值,单位mV
- printf("ADC1_Voltage = %u mV\r\n",ADC1_Voltage);//ADC1的电压值,单位mV
-
- printf("**********************\r\n");//打印分隔符
- }
- }
- }
-
复制代码
笔者这里只是用了一个简单的平均值计算方法,实际应用中,为了使得采集到的电压更加平稳,
还可以先采集多个数据,然后使用冒泡排序算法,去掉一些最大值和最小值,然后再求平均值,
即所谓的“中值求平均算法”(可能这个说法不是很准确,大概就是这么个意思)。感兴趣的小伙伴们可以自己动手尝试一下。
按照如下方式接线(将5V电压直接接到ADC1接口上),进行测试:
程序下载成功后,我们可以看到执行结果,串口会打印如下信息:
好了,关于本讲的内容就简单介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,
至此,SYK-0806-A2S1控制的基本应用就介绍完成了,感谢大家的支持!
本章附件:
【STC15系列】SYK-0806-A2S1- 18-内部AD采集外部电压.rar
(85.98 KB, 下载次数: 3)
|