在第 7 节,我们讨论了借助于单片机和传感器,电脑也能获取环境参数,例如室内的温度和湿度等信息。
不过不知道大家注意到没有,环境的温湿度应该是无时无刻都在变化的,而我们使用单片机采集的温湿度值却是离散的(大约1秒个温度值),这其实就是将模拟信息数字化的过程。
相当一部分单片机都带有 ADC 外设,ADC 的功能就是将模拟信息数字化。恰好我使用的这款 51 单片机就有 ADC 功能,本节将介绍该模块。目的是让我们的电脑具备测量电压的能力。
ADC 的全称是 Analog-to-Digital Converter,即“模拟到数字转换器”,它可以将连续不断变化的模拟信号转换为离散的数字信号,供计算机进一步处理。
将模拟信号数字化之后,才能使用计算机处理之,因为计算机本身就是数字电路组成的运算机器。
其实说将模拟信号“转换”为离散信号并不合适,更恰当的说法应该是 ADC 从模拟信号中取出“一部分”信息,请看下面右图的黑点即为 ADC 采集的数字信号。
这么看来,ADC的重要参数有两个:采样频率和精度。采样频率决定了 ADC 从模拟信号中取数据的“密集”程度,一般来说肯定越密集越好,因为这样更能还原信号的特性。精度则决定了取数据的时的精确性。
以我的 51 单片机为例,它有 8 路 10 位的 ADC,采样频率为 250K/s。所以它能从每秒的模拟信号中取出 25 万个数字信号,也就相当于在坐标系中用 25 万个点描绘出 1 秒的信号。
精度为 10 位,也就是说它利用 1~1024(2的十次方)的数字表示信号,我的 51 单片机 ADC 的参考电压信号为 5V,所以它能够表示的最小电压为 5V/1024 约为 5mV。
C语言编程单片机,实现ADC采样
现在知道了什么是 ADC,怎么使用它呢?请继续往下看。我使用的这款 51 单片机自带的 ADC 模块结构如下图所示:
可以看出,最终得到的数字信号其实是经过逐次比较的来的。下图是 ADC 相关寄存器的信息:
所以,在 keil4 中可以写出如下C语言代码:
sfr ADC_CONTR = 0xbc;
sfr ADC_RES = 0xbd; // 高 8 位结果
sfr ADC_LOW2 = 0xbe; // 低 2 位结果
sfr P1ASF = 0x9d; //
我的这款 51 单片机的 ADC 转换通道与 P1 口复用,上电复位后 P1 口为弱上拉型 IO 口,我们可以通过 C语言编程设置这 8 路的任意一路做 ADC 转换。
void adc_init()
{
P1ASF = 0xff; // 8 个通道都开
ADC_RES = 0;
ADC_CONTR = ADC_POWER|ADC_SPEEDLL;
delay_about_100ms(2);
}
上面的C语言代码中,我们将 P1ASF 赋值为 0xff,表示 P1 的 8 个 IO 口都可以作为 ADC 采样口。然后延时一段时间,等待 ADC 模块初始化。
因为我使用的这款 51 单片机是一个 8 位单片机,传送 10 位的 ADC 值需要两次,当 AUXR1.1/ADRJ = 0 时,ADC 转换结果寄存器格式如下:
当 AUXR1.1/ADRJ = 1 时,ADC 转换结果寄存器格式如下:
这么看来,获取一次 ADC 的采样值高 8 位的 C语言代码可以如下写:
// 获取高 8 位的 adc 值
BYTE get_adc_h8bit(BYTE ch)
{
ADC_CONTR = ADC_POWER|ADC_SPEEDLL|ADC_START|ch;
_nop_();_nop_();_nop_();_nop_(); // 等待转换完成
while(!(ADC_CONTR & ADC_FLAG));
ADC_CONTR &= ~ADC_FLAG;
return ADC_RES;
}
然后将之与余下 2 位 ADC 值组合一下,就得到了一次完整的ADC采样值:
// 获取 10 位 adc
WORD get_adc_res(BYTE ch)
{
WORD res = 0;
res = get_adc_h8bit(ch);
res <<= 2;
res |= ADC_LOW2;
return res;
}
使用电脑测量电压值
上面一小部分介绍了单片机的 ADC 模块使用方法,结合之前介绍的单片机的串口 printf,我们已经能够把外界的电压值转换为 1~1024 之间的数值并传送到电脑了,但是如何将之转换为电压值呢?
其实很简单,我的这款单片机 ADC 模块的参考电压为 5V,假设 ADC 采集的数值为 n,那么对应的电压值为:
U = n*5V / 1024
如此一来,C语言控制程序可以如下写:
void main()
{
init_uart(9600);
adc_init();
while(1){
delay_about_100ms(2);
printf("adc: %0.2f\r\n", 5.0*((float)get_adc_res(0))/1024.0);
}
}
使用电脑测量电压
如上图,为了方便测试,将可变电阻和定电阻串联,将单片机的 P10 口与中间相连,即可在电脑端的串口调试助手得到电压信息:
使用电压表测量该点的电压值,发现的确在 3.05V 附近:
现在调节可变电阻,发现串口传来的电压值也随之改变:
至此就实现了使用电脑测量电压,相信大家也应该明白了 ADC 的功能。