PCF8591 AD/DA转换基于51

Posted 了解2号

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PCF8591 AD/DA转换基于51相关的知识,希望对你有一定的参考价值。

#include <reg52.h>
#include<intrins.h>    //内部有_nop_();
//IIC模拟时序实现
//注意:SCL为高电平时变化SDA数据是起始或者终止信号;所以若不是起始或者终止信号,需要在SCL为低电平时变化SDA数据

sbit SDA = P2^0;
sbit SCL = P2^1;
sbit LED = P2^3;
sbit wei = P2^6;
sbit duan = P2^7;

unsigned char num = 0;

unsigned char TEMP=0,flag=0;
unsigned char code table[] = "i get value: --" ;


unsigned char code duan_table[] = 0x3F, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71;






//void delay(unsigned char t)
//
//    unsigned char i,j;
//    for(i = t; i > 0; i --)
//    for(j = 110; j > 0; j --);
//
void T1_Init()

    EA = 1;//打开总中断
//    ET1 = 1;//T1定时器中断打开    ,不屏蔽掉串口会一直发 ,像flag==1里出不来了
    TMOD = 0x20;//8位初值自动重装
    TH1 = 0xfd;
    TL1 = 0xfd;
    TR1 = 1; //启动定时器
    



void SCON_Init()

    SM0 = 0;
    SM1 = 1;//10bit

    REN = 1;//允许串行口接收数据
    ES = 1;






void delay3us()

    _nop_();    
    _nop_();
    _nop_();


void delayms(unsigned int t)

     unsigned int i,j;
    for(i = t; i > 0; i --)
    
        for(j = 250; j > 0; j --);
    


 
void start()

        
    SDA = 1;//scl=1写在SDA=1后面
    delay3us();
    delay3us();
    SCL = 1;////scl=1写在SDA=1后面,若是不这么做的话,万一SDA=1之前SDA是0就变成命令信号了
    delay3us();//SCL = 1需延时至少4.7us
    delay3us();
    
    SDA = 0;
    delay3us();//SDA = 0;SCL = 1需延时至少4us
    delay3us();

    SCL = 0;//释放SCL
    delay3us();


void stop()

     
     SDA = 0;
     delay3us();
     delay3us();

     SCL = 1;
     delay3us();//SCL = 1;SDA = 0需要至少延时4.7us
     delay3us();

     SDA = 1;
     delay3us();//SCL = 1;SDA = 1需要至少延时4.7us
     delay3us();

//     SDA = 0;    //这一句如果写了就是在SCL=1时变化了SDA

  







 
void ack()

    SCL = 0;
    SDA = 1;
    delay3us();

    SDA = 0;  //SDA先清零,让SCL = 1,等最少4us,看SDA数据线是否被接收设备拉低了,拉低表示接收设备应答了
    delay3us();//如果不写上面的SDA = 0;万一是SDA = 1,下面SCL = 1,之后等待SDA被接收设备拉低,会出现,SCL=1时SDA拉低了,变成了起始信号了

    SCL = 1; //SCL=1,等接收设备拉低SDA表示应答
    delay3us();
    delay3us();

    SCL = 0;//释放总线
    delay3us();
    delay3us();
    SDA = 1;//[可写可不写该句X]必须要写,这里不明白



void noack()

    SCL = 0;
    SDA = 0;
    delay3us();

    SDA = 1;
    delay3us();

    SCL = 1;
    delay3us();
    delay3us();

    SCL = 0;//释放总线
    delay3us();
    delay3us();
    SDA = 0;//可写可不写
  







//字节写入
void write_byte(unsigned char byte)

    unsigned char tmp_i,tmp_byte;    
    tmp_byte = byte;

    //启动信号发出后,便发出控制字,看start函数里SDA,SCL最后状态下笔
    for(tmp_i = 0; tmp_i < 8; tmp_i ++)
    
        SCL = 0;//START函数里末尾也是SCL=0,应该可以不用写,但是循环所以建议写

        //scl=0 时可以变化SDA数据
        tmp_byte <<= 1;//高位先移,移出数据会进入PSW寄存器CY内
//        delay3us();
        SDA = CY;
        delay3us();

        SCL = 1;//数据停止变化,并保持一段时间
        delay3us();
        delay3us();


    
    SCL = 0;//释放总线
    
    delay3us();

//字节读取
unsigned char read_byte()

    unsigned char i,byte;
    for(i = 0; i < 8; i ++)
    
        SCL = 0;  //应答之后读取数据,看ack内部最后SDA,SCL状态
        byte <<= 1;//高位先移出,高位就先移入。ack函数内部,SCL是低电平的,这里循环所以再写一下
//        delay3us();
        byte |= SDA; //scl = 0从SDA获取数据,这里处理可能需要延时,防止时间太短
        delay3us();

        SCL = 1;   //数据停止变化,并保持一段时间
        delay3us();
        delay3us();
//        byte |= SDA;  //SCL为高并延时了至少4.7us,SDA稳定了,再处理数据亦可

    

    SCL = 0;//释放总线
    delay3us();
    return byte;

//unsigned char read_byte()
//
//    unsigned char i,byte;
//    for(i = 0; i < 8; i ++)
//    
//        SCL = 1;   //数据停止变化,并保持一段时间
//        delay3us();
//        delay3us();
//        byte <<= 1;//高位先移出,高位就先移入。ack函数内部,SCL是低电平的,这里循环所以再写一下
//        byte |= SDA;  //SCL为高并延时了至少4.7us,SDA稳定了,再处理数据亦可
//
//        SCL = 0;  //应答之后读取数据,看ack内部最后SDA,SCL状态
//        delay3us();
////        byte |= SDA; //scl = 0从SDA获取数据,这里处理可能需要延时,防止时间太短
////        delay3us();
//
//        
//    

//    SCL = 0;//释放总线
//    
//    delay3us();
//    return byte;
//








//单字节写入数据到指定地址
void write_data(unsigned char add,unsigned char val)

    start();
    write_byte(0xae);
    ack();

    write_byte(add);
    ack();

    write_byte(val);
    ack();

    stop();

//单字节读取
unsigned char read_data(unsigned char add)

    unsigned char val=0;
    start();
    write_byte(0xae);
    ack();

    write_byte(add);
    ack();
    start();
    write_byte(0xaf);
    ack();

    val = read_byte();
    noack();

    stop();

    return val;
 

//将数字值转换成模拟量输出函数
void DAC_convert(unsigned char val)

     start();
     write_byte(0x90);
     ack();

     write_byte(0x40);
     ack();

     write_byte(val);
     ack();

     stop();
//     write_byte(0x01);
//     ack();



//模数转换函数
unsigned char ADC_convert()

    
    unsigned char temp_val;


    start();
     write_byte(0x90);
     ack();

     write_byte(0x00);
     ack();




    start();
    write_byte(0x91);
    ack();

    temp_val = read_byte();
    ack();

    temp_val = read_byte();
    noack();

    stop();
    return temp_val;



void display()

    
    unsigned char numv=0;
    numv =     ADC_convert();

    wei = 1;
    P0 = 0xfe;
    wei = 0;

    duan = 1;
    P0 = duan_table[numv/100];

    duan = 0;

    delayms(1);

    P0 = 0XFF;

    wei = 1;
    P0 = 0xfd;
    wei = 0;

    duan = 1;
    P0 = duan_table[(numv%100)/10];

    duan = 0;

    delayms(1);

    P0 = 0XFF;

    wei = 1;
    P0 = 0xfb;
    wei = 0;

    duan = 1;
    P0 = duan_table[(numv%100)%10];

    duan = 0;

    delayms(1);

    P0 = 0XFF;






void main()

    

    T1_Init();
    SCON_Init();
    while(1)
    
    
        display();
        
        if(flag == 1)
        
            ES = 0;
            for(num = 0; num < 15; num ++)
            
                SBUF = table[num];
                while(!TI);
                TI = 0;
            
            SBUF = ADC_convert();
            while(!TI);
            TI = 0;

            flag = 0;
            ES = 1;
            
             
        
    
        
        




void usart1() interrupt 4

    RI = 0;
//    TEMP = SBUF;//接收到数据存储到TEMP中
    flag = 1;
    

AD采样时序(类似):

并将转换发送到串口进行观察

 

Arduino和C51开发光敏传感器

技术:51单片机、Arduino、光敏传感器、PCF8591、AD/DA转换
 

概述

本文介绍了如何接收传感器的模拟信号和如何使用PCF8591 AD/DA转换模块对光敏传感器的模拟信号进行转换。讲述了51单片机和Arduino如何读取模拟信号,并通过串口实时显示出来。

详细

一、光敏传感器

技术图片光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。光敏传感器中最简单的电子器件是光敏电阻,它能感应光线的明暗变化,输出微弱的电信号,通过简单电子线路放大处理,可以控制LED灯具的自动开关。

 

模块使用说明

 

  • 光敏电阻模块对环境光线最敏感,一般用来检测周围环境的光线的亮度,触发单片机或继电器模块等;

  • 模块在环境光线亮度达不到设定阈值时,DO 端输出高电平,当外界环境光线亮度超过设定阈值时,DO端输出低电平;

  • DO 输出端可以与单片机直接相连,通过单片机来检测高低电平,由此来检测环境的光线亮度改变;

  • 小板模拟量输出 AO 可以和 AD 模块相连,通过 AD 转换,可以获得环境光强更精准的数值.

二、C51单片机串口显示光照强度

光敏传感器的AO引脚输出的是模拟信号,而51单片机内部没有AD/DA转换器,不能接收模拟信号,只能接收到数字信号,所以我们需要一个能够进行AD/DA转换的模块,这里我选择了PCF8591模块来进行AD转换。

技术图片PCF8591是一个单片集成、单独供电、低功耗、8-bit CMOS数据获取器件。PCF8591 具有 4 个模拟输入、1 个模拟输出和 1个串行 I2C 总线接口。PCF8591 的 3 个地址引脚 A0, A1 和 A2 可用于硬件地址编程,允许在同个 I2C 总线上接入8个PCF8591 器件,而无需额外的硬件。在 PCF8591 器件上输入输出的地址、控制和数据信号都是通过双线双向 I2C 总线以串行的方式进行传输。(简单的说:可以实现4路 模拟输入,1路模拟输出,具体功能看模块使用说明)

硬件连接: 技术图片

PCF8591的使用会涉及到I2C总线,程序中也要加入对I2C的操作,Pcf8591SendByte()来选择使用PCF8591 模块的哪一路输入(这里选择AIN0)

void Pcf8591SendByte(uchar channel)
{   
    I2C_Start();    //开始I2C总线
    I2C_SendByte(WRITEADDR);        //发送写器件地址
    I2C_SendByte(0x40|channel);     //发送控制寄存器
    I2C_Stop();
}
 
uchar Pcf8591ReadByte()    //读取值
{
    uchar num;
    I2C_Start();
    I2C_SendByte(READADDR);      //发送读器件地址
    num=I2C_ReadByte();          //读取数据
    I2C_Stop();                  //结束总线
    return num;
}
 
void main()
{
    uint adNum;
    float value;
    UsartInit(); 
    while(1)
    {    
        Pcf8591SendByte(0);      //使用通道0   (可选择通道0-3)光敏传感器A0连接PCF8591传感器的AIN0
        /*adNum一定是0到255之间的一个数,因为pcf8591是8位的AD/DA芯片,所以输出的范围为00000000
          到11111111,即0到255*/
        adNum=Pcf8591ReadByte();   //读出数值
        value = adNum;
        value=100.0 - value*100.0/255.0; //把光敏值转换为0-100的数值,这里255.0可更改
                                         //(根据实际测试value最暗值)
        printf("light:");        
        printf("%.2f
",value);    //打印数据到串口
        delay1s();
    }
}

打开串口调试助手,获取光照强度值:

技术图片

三、Arduino串口显示光照强度

Arduino内部有AD/DA转换器,所以接收模拟信号会变得非常容易操作。

硬件连接: 技术图片

代码如下:

#define light A5   //定义模拟口A5
float Intensity = 0;//光照度数值
 
void setup()  //初始化
{
  Serial.begin(9600);//设置波特率9600
}
 
void loop()//程序主体循环
{
  Intensity = analogRead(light);  //读取模拟口AD5的值,存入Intensity变量
  //Intensity = 100.0 - Intensity / 6.7;//VCC接3.3V时,Intensity最高为670,最低为0
  Intensity = 100.0 - Intensity / 10.23;//VCC接5V时,Intensity最高为1023,最低为0
  Serial.print("Intensity = ");  //串口输出"Intensity = "
  Serial.print(Intensity);       //串口输出Intensity变量的值
  Serial.print("%
");  
  delay(1000);     //延时1s
}

 

串口读取到的光照强度值如下图:

技术图片

四、总结

本文实现51单片机和Arduino串口显示光照强度,介绍了光敏传感器和PCF8591 AD/DA转换模块的使用方法,如果你对模拟信号和数字信号还不怎么了解,我建议你先去学下。学完了这个,建议去读取其他传感器的模拟信号,具体实现方法是一样的。

五、项目结构图

技术图片

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

以上是关于PCF8591 AD/DA转换基于51的主要内容,如果未能解决你的问题,请参考以下文章

基于51单片机怎么让pcf8591输出正弦波形

4.8 51单片机-PCF8591(ADC/DAC)转换芯片

4.8 51单片机-PCF8591(ADC/DAC)转换芯片

STC的51单片机自带有AD转换,和PCF8591T AD芯片相比的话,哪个更好一些?

51单片机 PCF8591模数与数模转换实例+Proteus仿真

51单片机AD转换pcf8591+数码管显示+Proteus仿真二