IIC总线的C51程序中的个别指令的问题

IIC总线的C51程序中的个别指令的问题,第1张

在目前比较流行的几种串行扩展总线中,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单片机控制,测量倾角的程序!等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9838944.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-02
下一篇 2023-05-02

发表评论

登录后才能评论

评论列表(0条)

保存