在目前比较流行的几种串行扩展总线中,IIC总线以其严格的规范和众多带IIC接口的外围器件而获得广泛的应用。 IIC总线是PHILIPS公司推出的芯片间串行传输总线。它以1根串行数据线(SDA)和1根串行时钟线(SCL)实现了全双工的同步数据传输。随着IIC总线研究的深入,它已经广泛应用于视/音频领域、IC卡行业和一些家电产品中,在智能仪器、仪表和工业测控领域也越来越多地得到应用。
1 IIC总线硬件结构
IIC串行总线有两根信号线:一根双向的数据线SDA;另一根是时钟线SCL。所有接到IIC总线上的设备的串行数据都接到总线的SDA线,各设备的时钟线SCL接到总线的SCL。
总线对设备接口电路的制造工艺和电平都没有特殊的要求(NMOS、CMOS都可以兼容)。数据传送率按IIC总线可高达每秒十万位,高速方式可高达每秒四十万位。
总线的运行(数据传输)由主控器控制。主控器启动数据的传送(发出启动信号),发出时钟信号,传送结束时发出停止信号,通常主控器是微处理器。被主控器寻访的设备都称为从机。为了进行通讯,每个接到IIC总线的设备都有一个唯一的地址,以便于主控器寻访。
2 IIC总线时序
在IIC总线传输过程中,将两种特定的情况定义为开始和停止条件(如图1):当SCL保持“高”,SDA由“高”变为“低”时为开始条件;SCL保持“高”,SDA由“低”变为“高”是为停止条件。开始和停止条件由主控器产生。使用硬件接口可以很容易地检测开始和停止条件,没有这种接口的微机必须以每时钟周期至少两次对SDA取样以检测这种变化。
图1 总线开始/停止
SDA线上的数据在时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。
输出到SDA线上的每个字节必须是8位,每次传输的字节不受限制,每个字节必须有一个应答为ACK。如果一接收器件在完成其他功能(如一内部中断)前不能接收另一数据的完整字节时,它可以保持时钟线SCL为低,以促使发送器进入等待状态,当接收器械准备好接受数据的其它字节并释放时钟SCL后,数据传输继续进行。IIC数据总线传送时序如图2。
图2 总线数据传送时序
数据传送具有应答是必须的。与应答对应的时钟脉冲由主控器产生,发送器在应答期间必须下拉SDA线。当寻址的被控器件不能应答时,数据保持为高,接着主控器产生停止条件终止传输。在传输的过程中,当用到主控接收器的情况下,主控接收器必须发出一数据结束信号给被控发送器,被控发送器必须释放数据线,以允许主控器产生停止条件。合法的数据传输格式如图3所示:
超始位 被控接收器地址 R/W 应答位 数据 应答位 、、、、 停止位
图3
IIC总线在开始条件后的首字节决定哪个被控器将被主控器选择,例外的是“通用访问”地址,它可以寻址所有期间。当主控器输出一地址时,系统中的每一器件都将开始条件后的前七位地址和自己地址比较。如果相同,该器件认为自己被主控器寻址,而作为被控接收器或被控发送器则取决于R/W位。
3 IIC总线特点
由上面的介绍可以看出IIC总线的特点主要表现在以下几个方面: (1) 硬件结构上具有相同的硬件接口界面。IIC总线系统中,任何一个IIC总线接口的外围器件,不论其功能差别有多大,都是通过串行数据线(SDA)和串行时钟线(SCL)连接到IIC总线上。这一特点给用户在设计应用系统中带来了极大的便利性。用户不必理解每个IIC总线接口器件的功能如何,只要将器件的SDA和SCL引脚连到IIC总线上,然后对该器件模块进行独立的电路设计,从而简化了系统设计的复杂性,提高了系统抗干扰的能力,符合EMC (Electromagnetic Compatibility)设计原则。 (2) 总线接口器件地址具有很大的独立性。在单主系统中,每个IIC接口芯片具有惟一的器件地址,由于不能发出串行时钟信号而只能作为从器件使用。各器件之间互不干扰,相互之间不能进行通信,各个器件可以单独供电。FPGA与IIC器件之间的通信是通过独一无二的器件地址来实现的。 (3) 软件 *** 作的一致性。由于任何器件通过IIC总线与DSP进行数据传送的方式是基本一样的,这就决定了IIC总线软件编写的一致性。 (4) PHILIPS公司在推出IIC总线的同时,也为IIC总线制订了严格的规范,如:接口的电气特性、信号时序、信号传输的定义等。规范的严密性,结构的独立性和硬、软件接口界面的一致性,极大地方便了IIC总线设计的模块化和规范化。
本文来自CSDN博客,转载请标明出处:>
这是程序设计者偷懒的结果,因为大多数正常情况下不会出现应答为1的情况。程序没有写出错处理,出错处理也就是把结果向上一级函数返回,IIC_single_byte_write定义为 bit型,返回IIC_Tack的值。
正常情况下 程序设计合理,电路器件正常,基本不会出错。程序设计不合理调试不会通过,电路器件不正常,程序也没办法,只是知道错了而已。解决不了问题,所以很多时候程序员都会舍去这部分出错处理程序。
//
// GY-29 ADXL345 IIC测试程序
// 使用单片机STC89C51
// 晶振:110592M
// 显示:LCD1602
// 编译环境 Keil uVision2
// 参考宏晶网站24c04通信程序
// 时间:2011年3月1日
// QQ:531389319
//
#include <REG51H>
#include <mathh> //Keil library
#include <stdioh> //Keil library
#include <INTRINSH>
#define uchar unsigned char
#define uint unsigned int
#define DataPort P0 //LCD1602数据端口
sbit SCL=P1^0; //IIC时钟引脚定义
sbit SDA=P1^1; //IIC数据引脚定义
sbit LCM_RS=P2^0; //LCD1602命令端口
sbit LCM_RW=P2^1; //LCD1602命令端口
sbit LCM_EN=P2^2; //LCD1602命令端口
#define SlaveAddress 0xA6 //定义器件在IIC总线中的从地址,根据ALT ADDRESS地址引脚不同修改
//ALT ADDRESS引脚接地时地址为0xA6,接电源时地址为0x3A
typedef unsigned char BYTE;
typedef unsigned short WORD;
BYTE BUF[8]; //接收数据缓存区
uchar ge,shi,bai,qian,wan; //显示变量
int dis_data; //变量
int data_xyz[3];
void delay(unsigned int k);
void InitLcd(); //初始化lcd1602
void Init_ADXL345(void); //初始化ADXL345
void WriteDataLCM(uchar dataW);
void WriteCommandLCM(uchar CMD,uchar Attribc);
void DisplayOneChar(uchar X,uchar Y,uchar DData);
void conversion(uint temp_data);
void Single_Write_ADXL345(uchar REG_Address,uchar REG_data); //单个写入数据
uchar Single_Read_ADXL345(uchar REG_Address); //单个读取内部寄存器数据
void Multiple_Read_ADXL345(); //连续的读取内部寄存器数据
//------------------------------------
void Delay5us();
void Delay5ms();
void ADXL345_Start();
void ADXL345_Stop();
void ADXL345_SendACK(bit ack);
bit ADXL345_RecvACK();
void ADXL345_SendByte(BYTE dat);
BYTE ADXL345_RecvByte();
void ADXL345_ReadPage();
void ADXL345_WritePage();
//-----------------------------------
//
void conversion(uint temp_data)
{
wan=temp_data/10000+0x30 ;
temp_data=temp_data%10000; //取余运算
qian=temp_data/1000+0x30 ;
temp_data=temp_data%1000; //取余运算
bai=temp_data/100+0x30 ;
temp_data=temp_data%100; //取余运算
shi=temp_data/10+0x30 ;
temp_data=temp_data%10; //取余运算
ge=temp_data+0x30;
}
//
void delay(unsigned int k)
{
unsigned int i,j;
for(i=0;i<k;i++)
{
for(j=0;j<121;j++)
{;}}
}
//
void WaitForEnable(void)
{
DataPort=0xff;
LCM_RS=0;LCM_RW=1;_nop_();
LCM_EN=1;_nop_();_nop_();
while(DataPort&0x80);
LCM_EN=0;
}
//
void WriteCommandLCM(uchar CMD,uchar Attribc)
{
if(Attribc)WaitForEnable();
LCM_RS=0;LCM_RW=0;_nop_();
DataPort=CMD;_nop_();
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}
//
void WriteDataLCM(uchar dataW)
{
WaitForEnable();
LCM_RS=1;LCM_RW=0;_nop_();
DataPort=dataW;_nop_();
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}
//
void InitLcd()
{
WriteCommandLCM(0x38,1);
WriteCommandLCM(0x08,1);
WriteCommandLCM(0x01,1);
WriteCommandLCM(0x06,1);
WriteCommandLCM(0x0c,1);
}
//
void DisplayOneChar(uchar X,uchar Y,uchar DData)
{
Y&=1;
X&=15;
if(Y)X|=0x40;
X|=0x80;
WriteCommandLCM(X,0);
WriteDataLCM(DData);
}
/
延时5微秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数,注意时钟过快时需要修改
当改用1T的MCU时,请调整此延时函数
/
void Delay5us()
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
/
延时5毫秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数
当改用1T的MCU时,请调整此延时函数
/
void Delay5ms()
{
WORD n = 560;
while (n--);
}
/
起始信号
/
void ADXL345_Start()
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
/
停止信号
/
void ADXL345_Stop()
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
/
发送应答信号
入口参数:ack (0:ACK 1:NAK)
/
void ADXL345_SendACK(bit ack)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
/
接收应答信号
/
bit ADXL345_RecvACK()
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
/
向IIC总线发送一个字节数据
/
void ADXL345_SendByte(BYTE dat)
{
BYTE i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
ADXL345_RecvACK();
}
/
从IIC总线接收一个字节数据
/
BYTE ADXL345_RecvByte()
{
BYTE i;
BYTE dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
//单字节写入
void Single_Write_ADXL345(uchar REG_Address,uchar REG_data)
{
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf22页
ADXL345_SendByte(REG_data); //内部寄存器数据,请参考中文pdf22页
ADXL345_Stop(); //发送停止信号
}
//单字节读取
uchar Single_Read_ADXL345(uchar REG_Address)
{ uchar REG_data;
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(REG_Address); //发送存储单元地址,从0开始
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=ADXL345_RecvByte(); //读出寄存器数据
ADXL345_SendACK(1);
ADXL345_Stop(); //停止信号
return REG_data;
}
//
//
//连续读出ADXL345内部加速度数据,地址范围0x32~0x37
//
//
void Multiple_read_ADXL345(void)
{ uchar i;
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(0x32); //发送存储单元地址,从0x32开始
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress+1); //发送设备地址+读信号
for (i=0; i<6; i++) //连续读取6个地址数据,存储中BUF
{
BUF[i] = ADXL345_RecvByte(); //BUF[0]存储0x32地址中的数据
if (i == 5)
{
ADXL345_SendACK(1); //最后一个数据需要回NOACK
}
else
{
ADXL345_SendACK(0); //回应ACK
}
}
ADXL345_Stop(); //停止信号
Delay5ms();
}
//
//初始化ADXL345,根据需要请参考pdf进行修改
void Init_ADXL345()
{
Single_Write_ADXL345(0x31,0x0B); //测量范围,正负16g,13位模式
Single_Write_ADXL345(0x2C,0x08); //速率设定为125 参考pdf13页
Single_Write_ADXL345(0x2D,0x08); //选择电源模式 参考pdf24页
Single_Write_ADXL345(0x2E,0x80); //使能 DATA_READY 中断
Single_Write_ADXL345(0x1E,0x00); //X 偏移量 根据测试传感器的状态写入pdf29页
Single_Write_ADXL345(0x1F,0x00); //Y 偏移量 根据测试传感器的状态写入pdf29页
Single_Write_ADXL345(0x20,0x05); //Z 偏移量 根据测试传感器的状态写入pdf29页
}
//
//显示x轴
void display_x()
{ float temp;
dis_data=(BUF[1]<<8)+BUF[0]; //合成数据
if(dis_data<0){
dis_data=-dis_data;
DisplayOneChar(2,0,'-'); //显示正负符号位
}
else DisplayOneChar(2,0,' '); //显示空格
temp=(float)dis_data39; //计算数据和显示,查考ADXL345快速入门第4页
conversion(temp); //转换出显示需要的数据
DisplayOneChar(0,0,'X'); //第0行,第0列 显示X
DisplayOneChar(1,0,':');
DisplayOneChar(3,0,qian);
DisplayOneChar(4,0,'');
DisplayOneChar(5,0,bai);
DisplayOneChar(6,0,shi);
DisplayOneChar(7,0,'g');
}
//
//显示y轴
void display_y()
{ float temp;
dis_data=(BUF[3]<<8)+BUF[2]; //合成数据
if(dis_data<0){
dis_data=-dis_data;
DisplayOneChar(2,1,'-'); //显示正负符号位
}
else DisplayOneChar(2,1,' '); //显示空格
temp=(float)dis_data39; //计算数据和显示,查考ADXL345快速入门第4页
conversion(temp); //转换出显示需要的数据
DisplayOneChar(0,1,'Y'); //第1行,第0列 显示y
DisplayOneChar(1,1,':');
DisplayOneChar(3,1,qian);
DisplayOneChar(4,1,'');
DisplayOneChar(5,1,bai);
DisplayOneChar(6,1,shi);
DisplayOneChar(7,1,'g');
}
//
//显示z轴
void display_z()
{ float temp;
dis_data=(BUF[5]<<8)+BUF[4]; //合成数据
if(dis_data<0){
dis_data=-dis_data;
DisplayOneChar(10,1,'-'); //显示负符号位
}
else DisplayOneChar(10,1,' '); //显示空格
temp=(float)dis_data39; //计算数据和显示,查考ADXL345快速入门第4页
conversion(temp); //转换出显示需要的数据
/
DisplayOneChar(10,0,'Z'); //第0行,第10列 显示Z
DisplayOneChar(11,0,':');
DisplayOneChar(11,1,qian);
DisplayOneChar(12,1,'');
DisplayOneChar(13,1,bai);
DisplayOneChar(14,1,shi);
DisplayOneChar(15,1,'g');
/
}
//
//主程序
//
void main()
{
uchar devid;
float Roll,Pitch,Q,T,K;
delay(500); //上电延时
InitLcd(); //液晶初始化ADXL345
Init_ADXL345(); //初始化ADXL345
devid=Single_Read_ADXL345(0X00);//读出的数据为0XE5,表示正确
while(1) //循环
{
Init_ADXL345(); //初始化ADXL345
Multiple_Read_ADXL345(); //连续读出数据,存储在BUF中
data_xyz[0]=(BUF[1]<<8)+BUF[0]; //合成数据
data_xyz[1]=(BUF[3]<<8)+BUF[2]; //合成数据
data_xyz[2]=(BUF[5]<<8)+BUF[4]; //合成数据
//分别是加速度X,Y,Z的原始数据,10位的
Q=(float)data_xyz[0]39;
T=(float)data_xyz[1]39;
K=(float)data_xyz[2]39;
Q=-Q;
Roll=(float)(((atan2(K,Q)180)/314159265)+180); //X轴角度值
Pitch=(float)(((atan2(K,T)180)/314159265)+180); //Y轴角度值
conversion(Roll); //转换出显示需要的数据X轴,或者Y轴
DisplayOneChar(9,1,'A');
DisplayOneChar(10,1,':');
DisplayOneChar(11,1,bai);
DisplayOneChar(12,1,shi);
DisplayOneChar(13,1,ge);
delay(200); //延时
}
}
unsigned char A_D(unsigned char Ch)
{
unsigned char i,dat;
CS=1; //一个转换周期开始
CLK=0; //为第一个脉冲作准备
CS=0; //CS置0,片选有效
DIO=1; //DIO置1,规定的起始信号
CLK=1; //第一个脉冲
CLK=0; //第一个脉冲的下降沿,此前DIO必须是高电平
DIO=1; //DIO置1, 通道选择信号
CLK=1; //第二个脉冲,第2、3个脉冲下沉之前,DI必须跟别输入两位数据用于选择通道,这里选通道CH0
CLK=0; //第二个脉冲下降沿
DIO=Ch; //DI置0,选择通道0
CLK=1; //第三个脉冲
CLK=0; //第三个脉冲下降沿
DIO=1; //第三个脉冲下沉之后,输入端DIO失去作用,应置1
CLK=1; //第四个脉冲
for(i=0;i<8;i++) //高位在前
{
CLK=1; //第四个脉冲
CLK=0;
dat<<=1; //将下面储存的低位数据向右移
dat|=(unsigned char)DIO; //将输出数据DIO通过或运算储存在dat最低位
}
CS=1; //片选无效
return dat; //将读书的数据返回
}
只要单片机的端口和你的一样只要复制下来用keil编译就可实验
一路差分输入用数码管显示 p0段p1位没用锁存器。不一样话显示部分可自己编译 ,其他无需改动
#include <reg52h>
#include <intrinsh>
#define AddWr 0x90 //写数据地址
#define AddRd 0x91 //读数据地址
#define _Nop() _nop_() //定义空指令
bit ack; //应答标志位
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
#define CtrlPort P1
sbit SDA=P2^5;
sbit SCL=P2^4;
unsigned char code DuanMa[10]={0xc0,0xf9,0xa4,0xb0, 0x99,0x92,0x82,0xf8,0x80,0x90};// 显示段码值0~9
unsigned char code WeiMa[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//分别对应相应的数码管点亮,即位码
unsigned char TempData[8]; //存储显示值的全局变量
extern bit ack;
unsigned char ReadADC(unsigned char Chl);
bit WriteDAC(unsigned char dat);
/------------------------------------------------
主程序
------------------------------------------------/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/------------------------------------------------
mS延时函数,含有输入参数 unsigned char t,无返回值
unsigned char 是定义无符号字符变量,其值的范围是
0~255 这里使用晶振12M,精确延时请使用汇编
------------------------------------------------/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
void Display(unsigned char FirstBit,unsigned char Num)
{
static unsigned char i=0;
DataPort=0; //清空数据,防止有交替重影
CtrlPort=WeiMa[i+FirstBit]; //取位码
DataPort=TempData[i]; //取显示数据,段码
i++;
if(i==Num)
i=0;
}
/------------------------------------------------
定时器初始化子程序
------------------------------------------------/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
//TH0=0x00; //给定初值
//TL0=0x00;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
/------------------------------------------------
定时器中断子程序
------------------------------------------------/
void Timer0_isr(void) interrupt 1
{
TH0=(65536-2000)/256; //重新赋值 2ms
TL0=(65536-2000)%256;
Display(0,3);
}
void Start_I2c()
{
SDA=1; //发送起始条件的数据信号
_Nop();
SCL=1;
_Nop(); //起始条件建立时间大于47us,延时
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; //发送起始信号
_Nop(); //起始条件锁定时间大于4μ
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0; //钳住I2C总线,准备发送或接收数据
_Nop();
_Nop();
}
/------------------------------------------------
结束总线
------------------------------------------------/
void Stop_I2c()
{
SDA=0; //发送结束条件的数据信号
_Nop(); //发送结束条件的时钟信号
SCL=1; //结束条件建立时间大于4μ
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1; //发送I2C总线结束信号
_Nop();
_Nop();
_Nop();
_Nop();
}
/----------------------------------------------------------------
字节数据传送函数
函数原型: void SendByte(unsigned char 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μ
_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();
}
/----------------------------------------------------------------
字节数据传送函数
函数原型: unsigned char 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(); //时钟低电平周期大于47us
_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(void);
----------------------------------------------------------------/
/void Ack_I2c(void)
{
SDA=0;
_Nop();
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop(); //时钟低电平周期大于4μ
_Nop();
_Nop();
_Nop();
SCL=0; //清时钟线,钳住I2C总线以便继续接收
_Nop();
_Nop();
}/
/----------------------------------------------------------------
非应答子函数
原型: void NoAck_I2c(void);
----------------------------------------------------------------/
void NoAck_I2c(void)
{
SDA=1;
_Nop();
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop(); //时钟低电平周期大于4μ
_Nop();
_Nop();
_Nop();
SCL=0; //清时钟线,钳住I2C总线以便继续接收
_Nop();
_Nop();
}
main()
{
unsigned char num=0;
Init_Timer0();
while (1) //主循环
{
num=ReadADC(0);
TempData[2]=DuanMa[num/100];
TempData[1]=DuanMa[(num%100)/10];
TempData[0]=DuanMa[(num%100)%10];
//主循环中添加其他需要一直工作的程序
DelayMs(100);
}
}
/------------------------------------------------
读AD转值程序
输入参数 Chl 表示需要转换的通道,范围从0-3
返回值范围0-255
------------------------------------------------/
unsigned char ReadADC(unsigned char Chl)
{
unsigned char Val;
Start_I2c(); //启动总线
SendByte(AddWr); //发送器件地址
if(ack==0)return(0);
SendByte(0x62|Chl); //发送器件子地址 0x62控制字节可自行更改,可参考资料的图五 部分更改《8591中文资料》
if(ack==0)return(0);
Start_I2c();
SendByte(AddWr+1);
if(ack==0)return(0);
Val=RcvByte();
NoAck_I2c(); //发送非应位
Stop_I2c(); //结束总线
return(Val);
}
要求用51单片机做一个风扇温控检测系统,其功能大概就是:当温度达到设定值时候,风扇电机开始转动,以达到散热的目的,题目本身没有什么意思,有意思的是想要用尽可能少的元器件来完成这项工作,要求使用两个数码管,其他没说,所以我就加了3个按键,大体思路是这样的,开机以后,正常显示温度,按下SET键位时候,进温度设置,两个按键+和-设置数值。再按一次SET保存并退出。
最有意思的在于用了两个标志位,这两个标志位简直没把我绕晕。现在完成了,发出来大家一起看看,不在于代码多好,在于一种解决问题的方法。我把文件发上来了,大家一起学习交流。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
TIM截图20181216211410jpg TIM截图20181216211656jpg TIM截图20181216211828jpg
单片机源程序如下:
代码:
#include "reg52h"
#include <intrinsh>
#define _Nop() _nop_() //定义空指令
/-----------------------------------------------
IO口定义
-----------------------------------------------/
sbit LED_R = P1^6; //红灯
sbit BEEP = P2^1; //蜂鸣器
sbit MOTOR = P2^2; //电机
sbit KEY_ADD = P2^5; //加
sbit KEY_DEC = P2^4; //减
sbit KEY_SET = P2^3; //确认
sbit ONE = P2^6; //个位位选
sbit TEN = P2^7; //十位位选
sbit DQ=P1^7; //DS18B20数据
sbit SDA=P1^1; //模拟I2C数据传送位
sbit SCL=P1^0; //模拟I2C时钟控制位
/-----------------------------------------------
定义变量
-----------------------------------------------/
unsigned char Set_Flag=0; //
unsigned char Set_Value=40; //设定值
unsigned char Tempature = 0; //温度
unsigned char num=40; //数字
unsigned char table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //数码管段选
unsigned char Data=0;
char Temp[12];
bit ack; //应答标志位
/-----------------------------------------------
延时函数
-----------------------------------------------/
void Delay_us(unsigned char t)
{
while(t--);
}
void Delay_ms(unsigned char t)
{
int i,j;
for(i=t;i>0;i--)
for(j=125;j>0;j--);
}
void delay_18B20(unsigned int i)//微秒级延时
{
while(i--);
}
/-----------------------------------------------
DS18B20初始化
-----------------------------------------------/
void ds1820rst(void) /ds1820复位/
{
unsigned char x=0;
DQ = 1; //DQ复位
delay_18B20(5); //延时
DQ = 0; //DQ拉低
delay_18B20(100); //精确延时大于480us
DQ = 1; //拉高
delay_18B20(40);
}
unsigned char ds1820rd(void)//从18b20读取数据
{
unsigned char i=0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{
DQ = 0; //给脉冲信号
dat>>=1;
DQ = 1; //给脉冲信号
if(DQ) dat|=0x80;
delay_18B20(10);
}
return(dat);
}
void ds1820wr(unsigned char wdata)/写数据/
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = wdata&0x01;
delay_18B20(25);
DQ = 1;
wdata>>=1;
}
}
//读取DS18B20温度值
//入口参数:无
//返回值:温度值
unsigned int read_18b20(void)/读取温度值并转换/
{
unsigned char a,b;
unsigned char tem;
ds1820rst();
ds1820wr(0xcc);//跳过读序列号/
ds1820wr(0x44);//启动温度转换/
ds1820rst();
ds1820wr(0xcc);//跳过读序列号/
ds1820wr(0xbe);//读取温度/
a=ds1820rd();
b=ds1820rd();
tem=((b<<4)|(a>>4)); //将高低字节组合成字
//Delay_ms(50);
return tem; //完成温度转换
}
/-----------------------------------------------
数码管
-----------------------------------------------/
//共阴数码管
//动态扫描
void display(unsigned char Data)
{
//P3 = 0;
ONE = 1; //打开个位位选
TEN = 0;
P3 = table[Data/10];//送个位
Delay_ms(5);//延时
P3 = 0; //防止重影
//Delay_us(5);
TEN = 1;
ONE = 0;
P3 = table[Data%10];
Delay_ms(5);
P3 = 0;
//Delay_us(5);
}
/-----------------------------------------------
按键处理函数
-----------------------------------------------/
char S_F=0;
unsigned char Key_Value(void)
{
if(!KEY_ADD || !KEY_DEC||!KEY_SET) // 检测是否有任意按键按下
{
Delay_ms(20);
Set_Flag = 1; //切换到显示设置值
if(!KEY_ADD) //如果检测到低电平,说明按键按下
{
Delay_ms(10); //延时去抖,一般10-20ms
if(!KEY_ADD) //再次确认按键是否按下,没有按下则退出
{
while(!KEY_ADD);//如果确认按下按键等待按键释放,没有释放则一直等待
{
if(num<99) //加 *** 作
num++;
}
}
}
if(!KEY_DEC) //如果检测到低电平,说明按键按下
{
Delay_ms(10); //延时去抖,一般10-20ms
if(!KEY_DEC) //再次确认按键是否按下,没有按下则退出
{
while(!KEY_DEC);//如果确认按下按键等待按键释放,没有释放则一直等待
{
if(num>0) //减 *** 作
num--;
}
}
}
if(!KEY_SET) //如果检测到低电平,说明按键按下
{
Delay_ms(10); //延时去抖,一般10-20ms
if(!KEY_SET) //再次确认按键是否按下,没有按下则退出
{
//Set_Flag = 0;
while(!KEY_SET);//如果确认按下按键等待按键释放,没有释放则一直等待
{
//将当前值作为设定值
//Set_Flag = 0; //切换到显示温度值
}
if(S_F)
Set_Flag=0;
}
}
}
Set_Value = num;
return (num);
}
/-----------------------------------------------
24C02初始化
-----------------------------------------------/
/------------------------------------------------
启动总线
------------------------------------------------/
void Start_I2c()
{
SDA=1; //发送起始条件的数据信号
_Nop();
SCL=1;
_Nop(); //起始条件建立时间大于47us,延时
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; //发送起始信号
_Nop(); //起始条件锁定时间大于4μ
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0; //钳住I2C总线,准备发送或接收数据
_Nop();
_Nop();
}
/------------------------------------------------
结束总线
------------------------------------------------/
void Stop_I2c()
{
SDA=0; //发送结束条件的数据信号
_Nop(); //发送结束条件的时钟信号
SCL=1; //结束条件建立时间大于4μ
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1; //发送I2C总线结束信号
_Nop();
_Nop();
_Nop();
_Nop();
}
以上就是关于IIC总线的C51程序中的个别指令的问题全部的内容,包括:IIC总线的C51程序中的个别指令的问题、51单片机 *** 作24c08,IIC应答时,以下程序怎样反应出来是为0和为1呢、求用51单片机控制ADXL345测量角度的程序,通过ADXL345传感器,用51单片机控制,测量倾角的程序!等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)