基于51单片机的PCF8591电压测量

基于51单片机的PCF8591电压测量

这个笔记记录好久了。因为想突破ADS1115在STC89下的使用,但一直没有太清晰的眉目和充裕的时间。不知道为什么各种资料里,ADS1115很少有介绍差分的,而都是单端的。这几天忙时也无暇琢磨,于是轮到了PCF8591。

STC89系列芯片不能直接测电压,也就是这个系列的芯片没有AD/DA转换的本领。想测量电压,就必须得借助PCF8591来帮忙。这一点上STC89跟ATTiny没法比,实际ATTiny13/85都可以直接AD转换。不过STC后期的STC8/12/15系列都自带ADC引脚,可以直接使用的,价格可能也要贵一些。

在STC89系列下使用PCF8591本质就是IIC读写,然后转换。但是说起来,实现起来也不容易。

接线图:

代码很混乱,不拆分,也不依赖外部其他文件,但是对PCF8591的使用流程则一目了然:

#include<reg52.h>
#include<intrins.h>
#include <stdio.h>
#include <stdlib.h>

#define uchar unsigned char
#define uint unsigned int
uchar dat[8];
uchar code char_temp[8] = {'\r', '\n'};

sbit SCL = P2 ^ 0;
sbit SDA = P2 ^ 1;

void IIC_Start(void)
{
    SDA = 1;
    SCL = 1;
    _nop_();
    _nop_();
    SDA = 0;
    _nop_();
    _nop_();
    SCL = 0;
}
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    _nop_();
    _nop_();
    SDA = 1;
}
void IIC_Ack(unsigned char ackbit)
{
    if(ackbit)
    {
        SDA = 0;
    }
    else
    {
        SDA = 1;
    }

_nop_();
    _nop_();
    SCL = 1;
    _nop_();
    _nop_();
    SCL = 0;
    SDA = 1;
    _nop_();
    _nop_();
}

bit IIC_WaitAck(void)
{
    SDA = 1;
    _nop_();
    _nop_();
    SCL = 1;
    _nop_();
    _nop_();

if(SDA)
    {
        SCL = 0;
        IIC_Stop();
        return 0;
    }
    else
    {
        SCL = 0;
        return 1;
    }
}

void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

for(i = 0; i < 8; i++)
    {
        if(byt & 0x80)
        {
            SDA = 1;
        }
        else
        {
            SDA = 0;
        }

_nop_();
        _nop_();
        SCL = 1;
        byt <<= 1;
        _nop_();
        _nop_();
        SCL = 0;
    }

}

unsigned char IIC_RecByte(void)
{
    unsigned char da;
    unsigned char i;

for(i = 0; i < 8; i++)
    {
        SCL = 1;
        _nop_();
        _nop_();
        da <<= 1;

if(SDA)
        da |= 0x01;
        SCL = 0;
        _nop_();
        _nop_();
    }
    return da;
}

void init_pcf8591(void)
{
    IIC_Start();
    IIC_SendByte(0x90);    //PCF8591里的地址
    IIC_WaitAck();
    IIC_SendByte(0x00);  //选择ADC通道,0x00电位器,0x01光敏,0x02热敏
    IIC_WaitAck();
    IIC_Stop();
}

//接收PCF8591转换过的采样电压值
unsigned char adc_pcf8591(void)
{
    unsigned char temp;
    IIC_Start();
    IIC_SendByte(0x91);
    IIC_WaitAck();
    temp = IIC_RecByte();
    IIC_Ack(0);
    IIC_Stop();
    return temp;
}

/**
 * 串口初始化函数
 * 波特率为9600
 */
void UartConfigurationInit()
{
    TMOD = 0x20; //设置定时器1工作方式为方式2
    TH1 = 0xfd;    //波特率9600
    TL1 = 0xfd;
    TR1 = 1;    //启动定时器1
    SM0 = 0;
    SM1 = 1;    //串口方式1
    REN = 1;    //允许接收
    PCON = 0x00; //关倍频
    ES = 1;     //开串口中断
    EA = 1;     //开总中断
}

/**
 * 延时函数
 * 延时count毫秒
 */

void delay(uint count)
{
    uint cycle;
    while(count)
    {
        cycle = 120;
        while(cycle > 0) cycle--;
        count--;
    }
}

/**
 * 字符发送函数
 */
void PostChar(uchar character)
{
    SBUF = character; //发送单个字符
    while(!TI);
    TI = 0; //发送完成标志
}

/**
 * 字符串发送函数
 * 通过调用字符发送函数来实现
 */
void  PostString(uchar *p)
{
    while(*p)      //若指针指向的地址为空,则跳出循环
    {
        PostChar(*p); //指针第一次默认指向首地址
        delay(20);  //延时,作用为提高发送准确度
        p++;
    }
}

void main()
{
    uint adNum;
    float value;
    UartConfigurationInit(); //初始化串口

while(1)
    {
        init_pcf8591();
        PostString(char_temp); //发送字符串
        adNum = adc_pcf8591();
        value = adNum * 0.01953; //转为电压值
        adNum = value * 100;    //保留两位小数
        dat[0] = adNum / 1000 + '0'; //加上'0'是表示数字转换成字符
        dat[1] = adNum % 1000 / 100 + '0';
        dat[2] = '.';
        dat[3] = adNum % 100 / 10 + '0';
        dat[4] = adNum % 10 + '0';
        dat[5] = 'V';
        PostString(dat); //发送字符串
        delay(1000);
    }
}

(0)

相关推荐