开篇先说一句废话····
本旺名字叫萨摩耶,,Please 叫我旺财,,,哈哈,招财进宝嘛!
IIC总线
- 啥是IIC总线
IIC(Inter-Integrated Circuit) 总线是由 PHILIPS 公司开发的两线式串行总线, 用于连接微控制器及其外围设备。 是微电子通信控制领域广泛采用的一种总线标准。
IIC总线有两根双向信号线,一根是:SDA数据线;一根是:SCL时钟线。数据线用来表示数据,时钟线用与数据收发同步。 - 常用总线的术语
主机:启动数据传输并且产生时钟信号的设备
从机:被主机寻址的器件
多主机: 同时有多于一个主机尝试控制总线但不破坏传输
主模式: 用 I2CNDAT 支持自动字节计数的模式;I2CRM,I2CSTT,I2CSTP控制数据的接收和发送
从模式: 发送和接收操作都是由 I2C 模块自动控制的
仲裁: 是一个在有多个主机同时尝试控制总线但只允许其中一个控制总线并使传输不被破坏的过程
同步: 两个或多个器件同步时钟信号的过程
发送器: 发送数据到总线的器件
接收器: 从总线接收数据的器件 - 起始、终止、应答信号、数据有效性规定
数据有效性
其实就是,要想SDA数据线变化,SCL时钟线必须为低;想要SDA数据线稳定,SCL时钟线必须为高
起始、终止信号
起始信号就是,当SCL时钟线为高的时候,SDA数据线由高–>低(下降沿)
void Start(){
SDA=1;
Delayus();
SCL=1;
Delayus();
SDA=0;
Delayus();
}
终止信号就是,当SCL时钟线为高的时候,SDA数据线由低–>高(上升沿)
void Stop(){
SDA=0;
Delayus();
SCL=1;
Delayus();
SDA=1;
Delayus();
}
应答信号
应答信号看的上图的后面应答’0’和非应答‘1’那部分
如果收到应答‘0’表示数据传输成功,产生应答,那如果没有产生应答也不能一直在那等应答,所以得有一个超时判断,如果一段时候后还没有收到应答,则退出即可。
void Respons(){
unsigned char i=0;
SCL=1;
Delayus();
while((SDA==1)&&(i<250))
i++;
Delayus();
SCL=0;
Delayus();
}
写一个字节数据(单片机–>器件)
单片机给器件发送一个字节的时候,需要一位一位发出去,这里要引入一个寄存器PSW中的CY位,让它来存放最高位,然后再把CY赋给SDA发送出去,循环八次将字节发送出去。
void WriteByte(unsigned char dat){
unsigned char i,temp;
temp=dat;
for(i=0;i<8;i++){
temp<<=1;
SCL=0;
Delayus();
SDA=CY;
Delayus();
SCL=1;
Delayus();
}
SCL=0;
Delayus();
SDA=1;
Delayus();
}
注意:函数结束的时候,SCL拉低,SDA拉高,就是释放总线,让总线处于空闲状态
读一个字节数据(器件–>单片机)
单片机在接受一个字节的时候,需要一位一位接受进来,这里声明一个临时变量接受这八位字节,最后把这个临时变量返回出去
unsigned char ReadByte(){
unsigned char i,date;
SCL=0;
Delayus();
SDA=1;
Delayus();
for(i=0;i<8;i++){
SCL=1;
Delayus();
date=(date<<1)|SDA;
Delayus();
SCL=0;
Delayus();
}
return date;
}
注意:函数开始的时候,SCL拉低,SDA拉高,就是释放总线,让总线处于空闲状态,为接下来读字节准备
这就是单片机模拟IIC总线基础的成分吧。
EEPROM AT24C02
AT24C02
- 采用具有IIC总线接口的串行EEPROM器件
- 可解决掉电数据保存问题
- 数据可保存100年,擦写次数可达10万次以上
具体内容可阅读AT24C02数据手册
原理图如下:
SCL时钟线接在P2^1;SDA数据线接在P2 ^0,其他更细节的接法参考数据手册。这种接法也决定了该器件的地址(1010–>a,剩余四位,取决于读/写(1/0))
既然AT24C02是模拟IIC总线通信,那么之前写的IIC总线起始、终止、应答,写字节,读字节可作为基础函数
阅读数据手册,发现,除了之前说的,还有几个时序图:
注意:SDA变化,SCL为低;SDA不变,SCL为高
- 字节写入方式
根据上面的时序图,可写出一个字节写入函数,
void Write(unsigned char address,unsigned char date){
Start();
WriteByte(0xa0);
Respons();
WriteByte(address);
Respons();
WriteByte(date);
Respons();
Stop();
}
- 字节读出方式
同样,可以写出一个字节读出函数
unsigned char Read(unsigned char address){
unsigned char date;
Start();
WriteByte(0xa0);
Respons();
WriteByte(address);
Respons();
Start();
WriteByte(0xa1);
Respons();
date=ReadByte();
Stop();
return date;
}
上面写的这些函数,可以存放在一个c文件中,方便以后使用
数码管定时器
项目内容其实就是,用数码管写个计时器,然后每次冷启动单片机的时候能接着上次关闭时的数据接着计时。
//main.c
#include "reg52.h"
#include "i2c.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA =P2^2;
sbit LSB =P2^3;
sbit LSC =P2^4;
u8 smgduan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
u8 k,num,s,g;
void Delay(u16 i) //11.0592MHz 1ms
{
while(i--){
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
//³õʼ»¯
void Init(){
//AT24C02
SCL=1;
Delayus();
SDA=1;
Delayus();
//定时器0
TMOD=0x01;
TH0=(65535-45872)/256;
TL0=(65535-45872)%256;
ET0=1;
EA=1;
TR0=1;
}
//数码管显示函数
void ShowSmg(u8 shi,u8 ge){
//ʮλ
LSA=1;LSB=1;LSC=1;
P0=smgduan[shi];
Delay(1);
P0=0x00;
//¸öλ
LSA=0;LSB=1;LSC=1;
P0=smgduan[ge];
Delay(1);
P0=0x00;
}
void main(){
Init();
num=Read(3);
if(num>100)
num=0;
while(1){
s=num/10;
g=num%10;
ShowSmg(s,g);
}
}
void Timer() interrupt 1{
TH0=(65535-45872)/256;
TL0=(65535-45872)%256;
k++;
if(k==20){
k=0;
num++;
if(num==100)
num=0;
Write(3,num);
}
}
//i2.c
#include "i2c.h"
void Delayus(){ //10us
unsigned char i;
i = 2;
while (--i);
}
//起始信号
void Start(){
SDA=1;
Delayus();
SCL=1;
Delayus();
SDA=0;
Delayus();
}
//终止信号
void Stop(){
SDA=0;
Delayus();
SCL=1;
Delayus();
SDA=1;
Delayus();
}
//应答信号
void Respons(){
unsigned char i=0;
SCL=1;
Delayus();
while((SDA==1)&&(i<250))
i++;
Delayus();
SCL=0;
Delayus();
}
//写一个字节数据
void WriteByte(unsigned char dat){
unsigned char i,temp;
temp=dat;
for(i=0;i<8;i++){
temp<<=1;
SCL=0;
Delayus();
SDA=CY;
Delayus();
SCL=1;
Delayus();
}
SCL=0;
Delayus();
SDA=1;
Delayus();
}
//读一个字节数据
unsigned char ReadByte(){
unsigned char i,date;
SCL=0;
Delayus();
SDA=1;
Delayus();
for(i=0;i<8;i++){
SCL=1;
Delayus();
date=(date<<1)|SDA;
Delayus();
SCL=0;
Delayus();
}
return date;
}
//写入字节方式
void Write(unsigned char address,unsigned char date){
Start();
WriteByte(0xa0);
Respons();
WriteByte(address);
Respons();
WriteByte(date);
Respons();
Stop();
}
//指定地址读操作
unsigned char Read(unsigned char address){
unsigned char date;
Start();
WriteByte(0xa0);
Respons();
WriteByte(address);
Respons();
Start();
WriteByte(0xa1);
Respons();
date=ReadByte();
Stop();
return date;
}
//i2c.h
#ifndef _I2C_H_
#define _I2C_H_
#include "reg52.h"
sbit SDA=P2^0;
sbit SCL=P2^1;
void Delayus();
void Start();
void Stop();
void Respons();
void WriteByte(unsigned char dat);
unsigned char ReadByte();
void Write(unsigned char address,unsigned char date);
unsigned char Read(unsigned char address);
#endif
总结
学习这篇内容收获最大的我觉得是看数据手册,也就是在数据手册中仿照视频寻找对于编写程序有用的内容,比如:管脚定义,操作时序图等。我觉得有必要等单片机学完之后专门找一些时序图阅读一下。