这一章节的内容比较简单,大家反复细读学习《手把手教你学51单片机》文档第17章就会了解透彻这部分的知识。
1.简要解析概念和代码
我们知道PCF8591有4个通道是可以用来读取输入的电压值的。
那么宋老师写的“unsigned char GetADCValue(unsigned char chn)”就是选择读取其中一个通道输入的测量电压是多少V,受基准源影响,测量范围是0V~2.5V,函数返回值代表读出的电压值为0.01V的n倍左右,也就是返回值为150时,测得的输入电压大概为1.5V。大于2.5V的输入电压,返回值最高为255,也就是测量范围只支持到最高为2.5V。这个概念叫做A/D。
D/A则是选择通道需要输出的电压值,范围同样是只能输出0V~2.5V之间,宋老师写的“void SetDACOut(unsigned char val)”,参数val就是决定输出多少V的电压,参数为100时,PCF8591的AOUT引脚输出电压就是1.0V。
函数“void ValueToString(unsigned char *str, unsigned char val)”也比较实用,
我们一起把这三个函数封装成单独的文件,创建好“adc.c”和“adc.h”文件。
2.adc.c的代码#include
#include
unsigned char GetADCValue(unsigned char chn)
{
unsigned char val;
I2CStart();
if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回0
{
I2CStop();
return 0;
}
I2CWrite(0x40|chn); //写入控制字节,选择转换通道
I2CStart();
I2CWrite((0x48<<1)|0x01); //寻址PCF8591,指定后续为读操作
I2CReadNAK_OR_ACK(0); //先空读一个字节,提供采样转换时间
val = I2CReadNAK_OR_ACK(1); //读取刚刚转换完的值
I2CStop();
return val;
}
void SetDACOut(unsigned char val)
{
I2CStart();
if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回
{
I2CStop();
return;
}
I2CWrite(0x40); //写入控制字节
I2CWrite(val); //写入DA值
I2CStop();
}
void ValueToString(unsigned char *str, unsigned char val)
{
//电压值=转换结果*2.5V/255,式中的25隐含了一位十进制小数
val = (val*25) / 255;
str[0] = (val/10) + '0'; //整数位字符
str[1] = '.'; //小数点
str[2] = (val%10) + '0'; //小数位字符
str[3] = 'V'; //电压单位
str[4] = '\0'; //结束符
}
3.adc.h的代码#ifndef __ADC_H__
#define __ADC_H__
unsigned char GetADCValue(unsigned char chn); //读取当前的ADC转换值,chn-ADC通道号0~3
void SetDACOut(unsigned char val); //设置DAC输出值,val-设定值
void ValueToString(unsigned char *str, unsigned char val); //ADC转换值转为实际电压值的字符串形式,str-字符串指针,val-AD转换值
#endif
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yYlNzMyE2YidzNwE2MkRGMzQGM5Y2YkdTNwEGM1ATOi9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
4.main.c测试代码#include
#include //详见第六章第8讲
#include //详见第八章第11讲
#include //详见第十一章第3讲
#include
u8 flag300ms=0;
void main()
{
u8 val,key,volt;
u8 str[10];
EA = 1;
KEY_Init(); //初始化按键模块
InitLcd1602(); //初始化液晶屏
TIM0_Init(10000,11); //定时10ms,11是微调使定时更精确
LcdShowStr(6, 0, "AIN3");
while (1)
{
//请将开发板底部中间那里的位置插针将第一个和最后一个跳线帽拔出,把AOUT引脚与AIN3引脚用杜邦线连接起来测试代码
if (flag300ms)
{
flag300ms = 0;
val = GetADCValue(3); //获取ADC通道3的转换值
ValueToString(str, val); //转为字符串格式的电压值
LcdShowStr(6, 1, str); //显示通道3的电压
}
//按键控制AOUT的输出电压值
key=KEY_Scan(0,500);
if(key==4)
{
if (volt<25)volt++;
SetDACOut(volt*255/25); //转换为AD输出值
}
if(key==12)
{
if(volt>0)volt--;
SetDACOut(volt*255/25); //转换为AD输出值
}
}
}
void TIM0_IRQHandler() interrupt 1
{
static u8 tmr300ms = 0;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
tmr300ms++;
if (tmr300ms >= 30) //定时300ms
{
tmr300ms = 0;
flag300ms = 1;
}
}
我们就是通过K4,K12来控制PCF8591的AOUT引脚输出不同的电压,每按一次按键电压改变0.1V,然后通过PCF8591的ADC通道3来检测所读到的电压值显示在液晶屏上,在测试之前我们还需在开发板上像如下一样用杜邦线连接起来
因为PCF8591的AOUT引脚输出电压有50mV的偏差,也就是有0.05V的偏差,所以我们通过按键把AOUT引脚输出电压调到最高时,液晶屏上看到的只是2.4V,也就是说由于偏差导致,PCF8591的ADC通道3读到的电压有可能是2.45V,然后液晶屏就只显示成2.4V而已。