单片机补充案例--I2C和AD使用PCF8591

举报
zhangrelay 发表于 2021/07/15 01:33:35 2021/07/15
【摘要】 效果如下所示,AD转换结果用数码管显示: 上电位器:从大变小!(2.49-1.64-0.42-0.33)另一个不变(0.01附近) 下电位器:从小变大!(0.01-0.93-1.26-1.97-2.12)另一个不变(2.49) 源程序是keil,转为Linux_SDCC,如下: adtest.c #include <8052.h> unsigne...

效果如下所示,AD转换结果用数码管显示:

上电位器:从大变小!(2.49-1.64-0.42-0.33)另一个不变(0.01附近)

下电位器:从小变大!(0.01-0.93-1.26-1.97-2.12)另一个不变(2.49)

源程序是keil,转为Linux_SDCC,如下:

adtest.c


      #include <8052.h>
      unsigned char flag1s = 1;  //1s定时标志
      unsigned char T0RH = 0; //T0重载值的高字节
      unsigned char T0RL = 0; //T0重载值的低字节
      unsigned char LedBuff[4] ={0xFF,0xFF,0xFF,0xFF}; //显示缓冲区
      unsigned __code char smgcode[]={
     	0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
     	0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
      	};
      #define LSA P1_5 //LED位选译码地址引脚A
      #define LSB P1_6 //LED位选译码地址引脚B
      #define LSC P1_7 //LED位选译码地址引脚C
      #define SCL P3_7
      #define SDA P3_6
      void I2cDelay()
      {
       ;
      }
      void Timer0() __interrupt 1;
      void ConfigTimer0(unsigned int ms);
      void ValueToBuff(unsigned char val);
      /*******************************************************************************
      * 函数名 : I2cStart()
      * 函数功能 : 起始信号:在SCL时钟信号在高电平期间SDA信号产生一个下降沿
      * 输入 : 无
      * 输出 : 无
      * 备注 : 起始之后SDA和SCL都为0
      *******************************************************************************/
      void I2cStart()
      {
      	SDA = 1;
      	SCL = 1;
      	I2cDelay();
      	SDA = 0;		//先拉低SDA
      	I2cDelay();
      	SCL = 0;		//再拉低SCL 
      	I2cDelay();
      }
      /*******************************************************************************
      * 函数名 : I2cStop()
      * 函数功能 : 终止信号:在SCL时钟信号高电平期间SDA信号产生一个上升沿
      * 输入 : 无
      * 输出 : 无
      * 备注 : 结束之后保持SDA和SCL都为1;表示总线空闲
      *******************************************************************************/
      void I2cStop()
      {
      	SDA = 0;
      	SCL = 0;
      	I2cDelay();
      	SCL = 1; //先拉高SCL
      	I2cDelay();
      	SDA = 1;	 	//再拉高SDA
      	I2cDelay();
      }
      /*******************************************************************************
      * 函数名 : I2cWriteByte(unsigned char dat)
      * 函数功能 : 通过I2C发送一个字节。在SCL时钟信号高电平期间,保持发送信号SDA保持稳定
      * 输入 : dat
      * 输出 : 从机的应答值
      * 备注 : 发送完一个字节SCL=0,SDA=1
      *******************************************************************************/
      unsigned char I2cWriteByte(unsigned char dat)
      {
     	unsigned char i = 0;
     	unsigned char ack;
     	for(i = 0;i < 8;i++)	//要发送8位,从最高位开始
      	{
      		SDA = dat >> 7;	//起始信号之后SCL=0,所以可以直接改变SDA信号
      		dat = dat << 1;
      		I2cDelay();
      		SCL = 1;	//拉高SCL
      		I2cDelay();
      		SCL = 0;	//再拉低SCL,完成一个位周期
      		I2cDelay();
      	}
      	SDA = 1;		//8位数据发送完以后主机释放SDA,以检测从机应答
      	I2cDelay();
      	SCL = 1;		//拉高SCL
      	ack = SDA;		//读取此时的SDA值,即为从机的应答值
      	I2cDelay();
      	SCL = 0;		//再拉低SCL完成应答位,并保持住总线
      	return ack;		//应答位取反以符合逻辑习惯:0=不存在
      //或忙或失败,1=存在且空闲或写入成功
      }
      /*******************************************************************************
      * 函数名 : I2cReadByte()
      * 函数功能 : 使用I2c读取一个字节
      * 输入 : ack,1:发送无应答信号,0:发送应答信号
      * 输出 : dat
      * 备注 : 接收完一个字节SCL=0,SDA=1.
      *******************************************************************************/
      unsigned char I2cReadByte(unsigned char ack)
      {
     	unsigned char i = 0,dat = 0;
      	SDA = 1; //起始和发送一个字节之后SCL都是0
      	I2cDelay();
     	for(i = 0;i < 8;i++)	//从高位到地位接收8位
      	{
      		SCL = 1;
      		I2cDelay();
      		dat <<= 1;
      		dat |= SDA;
      		I2cDelay();
      		SCL = 0;
      		I2cDelay();
      	}
      	SDA = ack;		//8位数据发送完以后,发送应答或非应答信号
      	I2cDelay();
      	SCL = 1;		//拉高SCL
      	I2cDelay();
      	SCL = 0;		//再拉低SCL完成应答或非应答位,并保持住总线
     	return dat;
      }
      /*A/D转换程序*/
      unsigned char GetADCValue(unsigned char chn)
      {
     	unsigned char val;
      	I2cStart();
     	if(I2cWriteByte(0x48<<1)!=0) 	//寻址PCF8591,若未应答,则停止操作并返回0
      	{
      		I2cStop();
     		return 0;
      	}
      	I2cWriteByte(0x40 | chn);	//写控制字节,选择转换通道
      	I2cStart();
      	I2cWriteByte(0x48<<1 | 0x01);	//寻址PCF8591,指定后续为读操作
      	I2cReadByte(0);			//先空读一个字节,提供采样转换时间
      	val = I2cReadByte(1);		//读取刚刚转换的值 
      	I2cStop();
     	return val;
      }
      void main()
      {
      unsigned char val;  	//AD转换后的数字量
     	unsigned char channel = 0;
      	EA = 1; //开总中断
       ConfigTimer0(2); //配置T0定时2ms
      while (1)
       {
      if (flag1s)
       {
       flag1s = 0; //显示通道0的电压
       val = GetADCValue(channel++);   //获取ADC通道0的转换值
       ValueToBuff(val); //转为字符串格式的电压值
       channel %= 2; //保证channel为0或1 
       }
       }
      }
      /* 取出数字量的百十个位,保存到显示缓冲区 */
      void ValueToBuff(unsigned char val)
      {
      	val = (val*250)/255;		//val放大100倍
      	LedBuff[0] = smgcode[(val%10)];	//取个位数字
      	LedBuff[1] = smgcode[(val/10)%10];//取十位数字
      	LedBuff[2] = smgcode[(val/100)]; //取百位数字
      	LedBuff[2] &= 0x7F;		//小数点设置在百位,再缩小100倍
      }
      /* 配置并启动T0,ms:T0定时时间 */
      void ConfigTimer0(unsigned int ms)
      {
      unsigned long tmp; //临时变量
       tmp = 11059200 / 12; //定时器计数频率
       tmp = (tmp * ms) / 1000; //计算所需的计数值
       tmp = 65536 - tmp; //计算定时器重载值
       tmp = tmp + 18; //补偿中断响应延时造成的误差
       T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
       T0RL = (unsigned char)tmp;
       TMOD &= 0xF0; //清零T0的控制位
       TMOD |= 0x01; //配置T0为模式1
       TH0 = T0RH; //加载T0重载值
       TL0 = T0RL;
       ET0 = 1; //使能T0中断
       TR0 = 1; //启动T0
      }
      /* 数码管动态扫描刷新函数,需在定时中断中调用 */
      void LedScan()
      {
      static unsigned char i = 0; //动态扫描的索引
       P0 = 0xFF; //显示消隐
      switch (i)
       {
      case 0: LSC=0; LSB=0; LSA=1; i++; P0=LedBuff[0]; break;
      case 1: LSC=0; LSB=1; LSA=0; i++; P0=LedBuff[1]; break;
      case 2: LSC=0; LSB=1; LSA=1; i++; P0=LedBuff[2]; break;
      case 3: LSC=1; LSB=0; LSA=0; i=0; P0=LedBuff[3]; break;
      default: break;
       }
      }
      /* T0中断服务函数,执行LED动态显示和1s计时 */
      void Timer0() __interrupt 1
      {
      static unsigned int tmr1s = 0;
       TH0 = T0RH;  	//重新加载重载值
       TL0 = T0RL;
       tmr1s++;
      if (tmr1s >= 500)  	//定时1s,2us计了500次
       {
       tmr1s = 0;
       flag1s = 1;
       }
       LedScan();
      }
  
 

接下来两步即可:

  1. sdcc -mmcs51 adtest.c
  2. stcgal -P stc89 adtest.ihx

附keil程序,注意对比差异性!

main.c


      #include <reg52.h>
      #include "PCF8591.h"
      bit flag1s = 1; //1s定时标志
      unsigned char T0RH = 0;  	//T0重载值的高字节
      unsigned char T0RL = 0;  	//T0重载值的低字节
      unsigned char LedBuff[4] ={0xFF,0xFF,0xFF,0xFF};//显示缓冲区
      unsigned char code smgcode[]={
     	0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
     	0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
      	};
      sbit LSA = P1^5; //LED位选译码地址引脚A
      sbit LSB = P1^6; //LED位选译码地址引脚B
      sbit LSC = P1^7; //LED位选译码地址引脚C
      void ConfigTimer0(unsigned int ms);
      void ValueToBuff(unsigned char val);
      void main()
      {
      unsigned char val;  	//AD转换后的数字量
     	unsigned char channel = 0;
      	EA = 1; //开总中断
       ConfigTimer0(2);  	//配置T0定时2ms
      while (1)
       {
      if (flag1s)
       {
       flag1s = 0;
      //显示通道0的电压
       val = GetADCValue(channel++);   //获取ADC通道0的转换值
       ValueToBuff(val); //转为字符串格式的电压值
      			channel %= 2;			//保证channel为0或1 
       }
       }
      }
      /* 取出数字量的百十个位,保存到显示缓冲区 */
      void ValueToBuff(unsigned char val)
      {
      	val = (val*250)/255;			//val放大100倍
      	LedBuff[0] = smgcode[(val%10)];	//取个位数字
      	LedBuff[1] = smgcode[(val/10)%10];//取十位数字
      	LedBuff[2] = smgcode[(val/100)]; // 取百位数字
      	LedBuff[2] &= 0x7F; //小数点设置在百位,再缩小100倍
      }
      /* 配置并启动T0,ms:T0定时时间 */
      void ConfigTimer0(unsigned int ms)
      {
      unsigned long tmp; //临时变量
       tmp = 11059200 / 12; //定时器计数频率
       tmp = (tmp * ms) / 1000; //计算所需的计数值
       tmp = 65536 - tmp; //计算定时器重载值
       tmp = tmp + 18; //补偿中断响应延时造成的误差
       T0RH = (unsigned char)(tmp>>8);  	//定时器重载值拆分为高低字节
       T0RL = (unsigned char)tmp;
       TMOD &= 0xF0; //清零T0的控制位
       TMOD |= 0x01; //配置T0为模式1
       TH0 = T0RH; //加载T0重载值
       TL0 = T0RL;
       ET0 = 1; //使能T0中断
       TR0 = 1; //启动T0
      }
      /* 数码管动态扫描刷新函数,需在定时中断中调用 */
      void LedScan()
      {
      static unsigned char i = 0; //动态扫描的索引
       P0 = 0xFF; //显示消隐
      switch (i)
       {
      case 0: LSC=0; LSB=0; LSA=1; i++; P0=LedBuff[0]; break;
      case 1: LSC=0; LSB=1; LSA=0; i++; P0=LedBuff[1]; break;
      case 2: LSC=0; LSB=1; LSA=1; i++; P0=LedBuff[2]; break;
      case 3: LSC=1; LSB=0; LSA=0; i=0; P0=LedBuff[3]; break;
      default: break;
       }
      }
      /* T0中断服务函数,执行LED动态显示和1s计时 */
      void InterruptTimer0() interrupt 1
      {
      static unsigned int tmr1s = 0;
       TH0 = T0RH; //重新加载重载值
       TL0 = T0RL;
       tmr1s++;
      if (tmr1s >= 500)  	//定时1s,2us计了500次
       {
       tmr1s = 0;
       flag1s = 1;
       }
       LedScan();
      }
  
 

PCF8591.h


      #ifndef __PCF8591_H_
      #define __PCF8591_H_
      unsigned char GetADCValue(unsigned char chn);
      #endif
  
 

PCF8591.c


      #include "PCF8591.h"
      #include "i2c.h"
      /*A/D转换程序*/
      unsigned char GetADCValue(unsigned char chn)
      {
     	unsigned char val;
      	I2cStart();
     	if(!I2cWriteByte(0x48<<1)) //寻址PCF8591,若未应答,则停止操作并返回0
      	{
      		I2cStop();
     		return 0;
      	}
      	I2cWriteByte(0x40 | chn);			//写控制字节,选择转换通道
      	I2cStart();
      	I2cWriteByte(0x48<<1 | 0x01);		//寻址PCF8591,指定后续为读操作
      	I2cReadByte(0); //先空读一个字节,提供采样转换时间
      	val = I2cReadByte(1); //读取刚刚转换的值 
      	I2cStop();
     	return val;
      }
  
 

I2C.h


      #ifndef __I2C_H_
      #define __I2C_H_
      #include <reg52.h>
      sbit SCL = P3^7;
      sbit SDA = P3^6;
      void I2cStart();
      void I2cStop();
      bit I2cWriteByte(unsigned char dat);
      unsigned char I2cReadByte(bit ACK);
      #endif
  
 

I2C.c


      #include"i2c.h"
      #include <intrins.h>
      #define I2cDelay() {_nop_();_nop_();_nop_();_nop_();}
      /*******************************************************************************
      * 函数名 : I2cStart()
      * 函数功能 : 起始信号:在SCL时钟信号在高电平期间SDA信号产生一个下降沿
      * 输入 : 无
      * 输出 : 无
      * 备注 : 起始之后SDA和SCL都为0
      *******************************************************************************/
      void I2cStart()
      {
      	SDA = 1;
      	SCL = 1;
      	I2cDelay();
      	SDA = 0;		//先拉低SDA
      	I2cDelay();
      	SCL = 0;		//再拉低SCL 
      	I2cDelay();
      }
      /*******************************************************************************
      * 函数名 : I2cStop()
      * 函数功能 : 终止信号:在SCL时钟信号高电平期间SDA信号产生一个上升沿
      * 输入 : 无
      * 输出 : 无
      * 备注 : 结束之后保持SDA和SCL都为1;表示总线空闲
      *******************************************************************************/
      void I2cStop()
      {
      	SDA = 0;
      	SCL = 0;
      	I2cDelay();
      	SCL = 1; //先拉高SCL
      	I2cDelay();
      	SDA = 1;	 	//再拉高SDA
      	I2cDelay();
      }
      /*******************************************************************************
      * 函数名 : I2cWriteByte(unsigned char dat)
      * 函数功能 : 通过I2C发送一个字节。在SCL时钟信号高电平期间,保持发送信号SDA保持稳定
      * 输入 : dat
      * 输出 : 从机的应答值
      * 备注 : 发送完一个字节SCL=0,SDA=1
      *******************************************************************************/
      bit I2cWriteByte(unsigned char dat)
      {
     	unsigned char i = 0;
      	bit ack;
     	for(i = 0;i < 8;i++)	//要发送8位,从最高位开始
      	{
      		SDA = dat >> 7;	//起始信号之后SCL=0,所以可以直接改变SDA信号
      		dat = dat << 1;
      		I2cDelay();
      		SCL = 1;			//拉高SCL
      		I2cDelay();
      		SCL = 0;			//再拉低SCL,完成一个位周期
      		I2cDelay();
      	}
      	SDA = 1; //8位数据发送完以后主机释放SDA,以检测从机应答
      	I2cDelay();
      	SCL = 1; //拉高SCL
      	ack = SDA; //读取此时的SDA值,即为从机的应答值
      	I2cDelay();
      	SCL = 0; //再拉低SCL完成应答位,并保持住总线
      	return ~ack;			//应答位取反以符合逻辑习惯:0=不存在
      //或忙或失败,1=存在且空闲或写入成功
      }
      /*******************************************************************************
      * 函数名 : I2cReadByte()
      * 函数功能 : 使用I2c读取一个字节
      * 输入 : ack,1:发送无应答信号,0:发送应答信号
      * 输出 : dat
      * 备注 : 接收完一个字节SCL=0,SDA=1.
      *******************************************************************************/
      unsigned char I2cReadByte(bit ack)
      {
     	unsigned char i = 0,dat = 0;
      	SDA = 1; //起始和发送一个字节之后SCL都是0
      	I2cDelay();
     	for(i = 0;i < 8;i++)		//从高位到地位接收8位
      	{
      		SCL = 1;
      		I2cDelay();
      		dat <<= 1;
      		dat |= SDA;
      		I2cDelay();
      		SCL = 0;
      		I2cDelay();
      	}
      	SDA = ack; //8位数据发送完以后,发送应答或非应答信号
      	I2cDelay();
      	SCL = 1; //拉高SCL
      	I2cDelay();
      	SCL = 0; //再拉低SCL完成应答或非应答位,并保持住总线
     	return dat;
      }
  
 

 

 

文章来源: zhangrelay.blog.csdn.net,作者:zhangrelay,版权归原作者所有,如需转载,请联系作者。

原文链接:zhangrelay.blog.csdn.net/article/details/110352069

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。