14

单片机很好玩9,ADC介绍,使用电脑测量电压值

 4 years ago
source link: https://blog.popkx.com/mcu-is-fun-9-adc-introduction-using-computer-to-measure-voltage-value/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

单片机很好玩9,ADC介绍,使用电脑测量电压值

发表于 2019-01-15 19:01:33   |   已被 访问: 585 次   |   分类于:   单片机   |   暂无评论

在第 7 节,我们讨论了借助于单片机和传感器,电脑也能获取环境参数,例如室内的温度和湿度等信息。

不过不知道大家注意到没有,环境的温湿度应该是无时无刻都在变化的,而我们使用单片机采集的温湿度值却是离散的(大约1秒个温度值),这其实就是将模拟信息数字化的过程。

cf687cb6b00da190421ff776892dd062.png

相当一部分单片机都带有 ADC 外设,ADC 的功能就是将模拟信息数字化。恰好我使用的这款 51 单片机就有 ADC 功能,本节将介绍该模块。目的是让我们的电脑具备测量电压的能力。
%E7%94%B5%E8%84%91%E6%B5%8B%E7%94%B5%E5%8E%8BADC.gif

ADC 的全称是 Analog-to-Digital Converter,即“模拟到数字转换器”,它可以将连续不断变化的模拟信号转换为离散的数字信号,供计算机进一步处理。

将模拟信号数字化之后,才能使用计算机处理之,因为计算机本身就是数字电路组成的运算机器。

其实说将模拟信号“转换”为离散信号并不合适,更恰当的说法应该是 ADC 从模拟信号中取出“一部分”信息,请看下面右图的黑点即为 ADC 采集的数字信号。

cf687cb6b00da190421ff776892dd062.png

这么看来,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 模块结构如下图所示:

57c8ebedf91a9bcb05fe8b2f142414e1.png

可以看出,最终得到的数字信号其实是经过逐次比较的来的。下图是 ADC 相关寄存器的信息:
d0138c1a15ca86d3ad82cff595378d0a.png

所以,在 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 转换结果寄存器格式如下:

5ac569e24aa87dc30908372478573f9a.png

当 AUXR1.1/ADRJ = 1 时,ADC 转换结果寄存器格式如下:
a988833bdf415b3e4f03654bcdfeaa31.png

这么看来,获取一次 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);
    }
}

使用电脑测量电压

15f529fe215ff19d3e9e17237b607836.png

如上图,为了方便测试,将可变电阻和定电阻串联,将单片机的 P10 口与中间相连,即可在电脑端的串口调试助手得到电压信息:
4fb2e7f7656140c561b115b6225f750a.png

使用电压表测量该点的电压值,发现的确在 3.05V 附近:
34a9deb8eb00181f812ab0ea37124dbd.png

现在调节可变电阻,发现串口传来的电压值也随之改变:
%E7%94%B5%E8%84%91%E6%B5%8B%E7%94%B5%E5%8E%8BADC.gif

至此就实现了使用电脑测量电压,相信大家也应该明白了 ADC 的功能。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK