找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2007|回复: 10
收起左侧

用STC单片机和PCF8591进行数模转换数据并用LCD显示时遇到的问题

[复制链接]
ID:508775 发表于 2019-4-17 09:02 | 显示全部楼层 |阅读模式
想用PCF8591进行AD转换并将转换的数传入到单片机中,显示在LCD1602液晶屏上,但是并不能得到数据,显示也不能进行,显示情况即为在初始位置不断闪烁。本人大学生,想用单片机做一个散射式浊度计,因此需要将检测的光强显示在液晶上,但无奈因为之前并未接触过单片机,所以一直找不到头绪到底错在了哪里,还请各位大神指教。下面附上代码,代码也是我东拼西凑的,因为最近比较着急这个事,请问大家对如何写有什么好建议吗,是一边模仿一边学习还是如何呢。
#ifndef _I2C_H_
#define _I2C_H_

extern bit ack;
//起动总线函数
extern void Start_I2c();
//结束总线函数  
extern void Stop_I2c();
//应答子函数
extern void Ack_I2c(bit a);
//字节数据发送函数
extern void  SendByte(unsigned char  c);
//有子地址发送多字节数据函数               
extern bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no) ;
//无子地址发送多字节数据函数   
extern bit ISendStrExt(unsigned char sla,unsigned char *s,unsigned char no);
//无子地址读字节数据函数               
extern unsigned char RcvByte();



#endif

/*************************此部分为I2C总线的驱动程序*************************************/

#include <reg52.h>
#include <intrins.h>
#include <I2C.H>

#define  NOP()   _nop_()   /* 定义空指令 */
#define  _Nop()  _nop_()   /*定义空指令*/


sbit     SCL=P2^1;       //I2C  时钟
sbit     SDA=P2^0;       //I2C  数据
bit ack;                 /*应答标志位*/


/*******************************************************************
                     起动总线函数               
函数原型: void  Start_I2c();  
功能:     启动I2C总线,即发送I2C起始条件.  
********************************************************************/
void Start_I2c()
{
  SDA=1;         /*发送起始条件的数据信号*/
  _Nop();
  SCL=1;
  _Nop();        /*起始条件建立时间大于4.7us,延时*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();   
  SDA=0;         /*发送起始信号*/
  _Nop();        /* 起始条件锁定时间大于4μs*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();      
  SCL=0;       /*钳住I2C总线,准备发送或接收数据 */
  _Nop();
  _Nop();
}

/*******************************************************************
                      结束总线函数               
函数原型: void  Stop_I2c();  
功能:     结束I2C总线,即发送I2C结束条件.  
********************************************************************/
void Stop_I2c()
{
  SDA=0;      /*发送结束条件的数据信号*/
  _Nop();       /*发送结束条件的时钟信号*/
  SCL=1;      /*结束条件建立时间大于4μs*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  SDA=1;      /*发送I2C总线结束信号*/
  _Nop();
  _Nop();
  _Nop();
  _Nop();
}

/*******************************************************************
                 字节数据发送函数               
函数原型: void  SendByte(UCHAR c);
功能:     将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
          此状态位进行操作.(不应答或非应答都使ack=0)     
           发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void  SendByte(unsigned char  c)
{
        unsigned char  BitCnt;

        for(BitCnt=0;BitCnt<8;BitCnt++)  /*要传送的数据长度为8位*/
        {
                if((c<<BitCnt)&0x80)SDA=1;   /*判断发送位*/
                else  SDA=0;               
                        _Nop();
                SCL=1;               /*置时钟线为高,通知被控器开始接收数据位*/
                        _Nop();
                        _Nop();             /*保证时钟高电平周期大于4μs*/
                        _Nop();
                        _Nop();
                        _Nop();         
                SCL=0;
    }

                _Nop();
                _Nop();
    SDA=1;                /*8位发送完后释放数据线,准备接收应答位*/
                _Nop();
                _Nop();   
    SCL=1;
                _Nop();
                _Nop();
                _Nop();
    if(SDA==1)ack=0;     
       else ack=1;        /*判断是否接收到应答信号*/
    SCL=0;
                _Nop();
                _Nop();
}

/*******************************************************************
                 字节数据接收函数               
函数原型: UCHAR  RcvByte();
功能:        用来接收从器件传来的数据,并判断总线错误(不发应答信号),
          发完后请用应答函数应答从机。  
********************************************************************/   
unsigned char   RcvByte()
{
  unsigned char  retc;
  unsigned char  BitCnt;

  retc=0;
  SDA=1;                     /*置数据线为输入方式*/
  for(BitCnt=0;BitCnt<8;BitCnt++)
      {
        _Nop();           
        SCL=0;                  /*置时钟线为低,准备接收数据位*/
        _Nop();
        _Nop();                 /*时钟低电平周期大于4.7μs*/
        _Nop();
        _Nop();
        _Nop();
        SCL=1;                  /*置时钟线为高使数据线上数据有效*/
        _Nop();
        _Nop();
        retc=retc<<1;
        if(SDA==1)retc=retc+1;  /*读数据位,接收的数据位放入retc中 */
        _Nop();
        _Nop();
      }
  SCL=0;   
  _Nop();
  _Nop();
  return(retc);
}

/********************************************************************
                     应答子函数
函数原型:  void Ack_I2c(bit a);
功能:      主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
********************************************************************/
void Ack_I2c(bit a)
{

  if(a==0)SDA=0;              /*在此发出应答或非应答信号 */
  else SDA=1;
  _Nop();
  _Nop();
  _Nop();      
  SCL=1;
  _Nop();
  _Nop();                    /*时钟低电平周期大于4μs*/
  _Nop();
  _Nop();
  _Nop();  
  SCL=0;                     /*清时钟线,钳住I2C总线以便继续接收*/
  _Nop();
  _Nop();   
}



#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS

/**********************************
包含头文件
**********************************/
#include<reg52.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint unsigned int
#endif

/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;

/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c);   //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/        
void LcdWriteData(uchar dat)        ;
/*LCD1602初始化子程序*/               
void LcdInit();                                                  

#endif


#include"lcd.h"

/*******************************************************************************
* 函 数 名         : Lcd1602_Delay1ms
* 函数功能                   : 延时函数,延时1ms
* 输    入         : c
* 输    出         : 无
* 说    名         : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //误差 0us
{
    uchar a,b;
        for (; c>0; c--)
        {
                 for (b=199;b>0;b--)
                 {
                          for(a=1;a>0;a--);
                 }      
        }
            
}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能                   : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
#ifndef         LCD1602_4PINS         //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com)          //写入命令
{
        LCD1602_E = 0;     //使能
        LCD1602_RS = 0;           //选择发送命令
        LCD1602_RW = 0;           //选择写入
        
        LCD1602_DATAPINS = com;     //放入命令
        Lcd1602_Delay1ms(1);                //等待数据稳定

        LCD1602_E = 1;                  //写入时序
        Lcd1602_Delay1ms(5);          //保持时间
        LCD1602_E = 0;
}
#else
void LcdWriteCom(uchar com)          //写入命令
{
        LCD1602_E = 0;         //使能清零
        LCD1602_RS = 0;         //选择写入命令
        LCD1602_RW = 0;         //选择写入

        LCD1602_DATAPINS = com;        //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;         //写入时序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;

//        Lcd1602_Delay1ms(1);
        LCD1602_DATAPINS = com << 4; //发送低四位
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;         //写入时序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能                   : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/                  
#ifndef         LCD1602_4PINS                  
void LcdWriteData(uchar dat)                        //写入数据
{
        LCD1602_E = 0;        //使能清零
        LCD1602_RS = 1;        //选择输入数据
        LCD1602_RW = 0;        //选择写入

        LCD1602_DATAPINS = dat; //写入数据
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;   //写入时序
        Lcd1602_Delay1ms(5);   //保持时间
        LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)                        //写入数据
{
        LCD1602_E = 0;          //使能清零
        LCD1602_RS = 1;          //选择写入数据
        LCD1602_RW = 0;          //选择写入

        LCD1602_DATAPINS = dat;        //由于4位的接线是接到P0口的高四位,所以传送高四位不用改
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;          //写入时序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;

        LCD1602_DATAPINS = dat << 4; //写入低四位
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;          //写入时序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能                 : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/                  
#ifndef                LCD1602_4PINS
void LcdInit()                                                  //LCD初始化子程序
{
         LcdWriteCom(0x38);  //开显示
        LcdWriteCom(0x0c);  //开显示不显示光标
        LcdWriteCom(0x06);  //写一个指针加1
        LcdWriteCom(0x01);  //清屏
        LcdWriteCom(0x80);  //设置数据指针起点
}
#else
void LcdInit()                                                  //LCD初始化子程序
{
        LcdWriteCom(0x32);         //将8位总线转为4位总线
        LcdWriteCom(0x28);         //在四位线下的初始化
        LcdWriteCom(0x0c);  //开显示不显示光标
        LcdWriteCom(0x06);  //写一个指针加1
        LcdWriteCom(0x01);  //清屏
        LcdWriteCom(0x80);  //设置数据指针起点
}
#endif



#include "reg52.h"
#include "lcd.h"
#include "I2C.h"
typedef unsigned int u16;
typedef unsigned char u8;
#define READADDR 0x91;
#define WRITEADDR 0x90;

u8 hehe[5]="nihao";




void delay(u16 i)
{
        while(i--);
}

void Pcf8591SendByte(uchar channel)
{   
    Start_I2c();   
    SendByte(0x91);        
    SendByte(0x40|channel);     
    Stop_I2c();
}

u8 Pcf8591ReadByte()
{
    uchar num;
    Start_I2c();
    SendByte(0x90);      //???????
    num = RcvByte();          //????
    Stop_I2c();                  //????
    return num;
}



void main()
{
        u16 i,dat;
        u8 dat;
        Pcf8591SendByte(0x40);
        dat=Pcf8591ReadByte();
        LcdInit();
        LcdWriteCom(0x80);
        LcdWriteData(dat);
        

        
}


还请各位大神指教,有偿也行的,是真的想了解到底为什么错了,错在哪里,我有什么做的不对的地方请各位大神指教。

全部资料51hei下载地址:
LCD显示AD转换结果.rar (17.08 KB, 下载次数: 13)
回复

使用道具 举报

ID:508775 发表于 2019-4-17 09:03 | 显示全部楼层
请各位大佬指教啊。
回复

使用道具 举报

ID:213173 发表于 2019-4-17 18:14 | 显示全部楼层
892494590 发表于 2019-4-17 09:03
请各位大佬指教啊。

何不使用STC15系列有内置AD转换的MCU,只要外接一个光敏电阻采样即可。
回复

使用道具 举报

ID:158375 发表于 2019-4-18 13:37 | 显示全部楼层
一次不要写太多程序,可以分开写程序调试;
例如,不加PCF8591,先把单片机和显示弄出来,你想显示什么,就能显示什么,就是很大的提高啦。
然后再说PCF8591数模转换问题以及显示计算等等。。。
回复

使用道具 举报

ID:508775 发表于 2019-4-18 17:28 | 显示全部楼层
wulin 发表于 2019-4-17 18:14
何不使用STC15系列有内置AD转换的MCU,只要外接一个光敏电阻采样即可。

因为我之前并未接触过单片机这些,因为这边有个小项目最近才接触,并不是懂很多,您的建议我会去看看的,谢谢您了。
回复

使用道具 举报

ID:508775 发表于 2019-4-18 17:30 | 显示全部楼层
笨笨兔 发表于 2019-4-18 13:37
一次不要写太多程序,可以分开写程序调试;
例如,不加PCF8591,先把单片机和显示弄出来,你想显示什么,就 ...

嗯,您说的对,想请问您对串口通信了解吗。
回复

使用道具 举报

ID:508775 发表于 2019-4-18 17:33 | 显示全部楼层
wulin 发表于 2019-4-17 18:14
何不使用STC15系列有内置AD转换的MCU,只要外接一个光敏电阻采样即可。

我们做的项目也不是很高端,如果是STC15系列,您有什么推荐呢,我们这边是学生,经费有限。
回复

使用道具 举报

ID:508775 发表于 2019-4-18 17:43 | 显示全部楼层
wulin 发表于 2019-4-17 18:14
何不使用STC15系列有内置AD转换的MCU,只要外接一个光敏电阻采样即可。

我们要做一个散射式浊度计,但是传感器不入水,所以对光的收集之类的要求比较高,就没用光敏电阻,直接买的照度计模块。
回复

使用道具 举报

ID:508775 发表于 2019-4-18 17:43 | 显示全部楼层
谢谢各位的回答,小白在这里受教了哈哈。
回复

使用道具 举报

ID:213173 发表于 2019-4-18 21:42 | 显示全部楼层
892494590 发表于 2019-4-18 17:43
我们要做一个散射式浊度计,但是传感器不入水,所以对光的收集之类的要求比较高,就没用光敏电阻,直接买 ...

PCF8591照度模块上面一样还是用的光敏电阻,不过也不贵。用STC15W408AS加光敏电阻就更省钱。
无标题.jpg

回复

使用道具 举报

ID:158375 发表于 2019-4-19 08:44 | 显示全部楼层
892494590 发表于 2019-4-18 17:30
嗯,您说的对,想请问您对串口通信了解吗。

对串口了解一点,有什么要说的就直接说好了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表