单片机补充案例--I2C和AD使用PCF8591
【摘要】 效果如下所示,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();
-
}
接下来两步即可:
- sdcc -mmcs51 adtest.c
- 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)