导图社区 20. IIC总线
添加文件到工程中:和在USER中添加main.c文件一样,只不过换成HARDWARE中添加。如果找不到创建的文件的话,改一下文件类型就OK。
编辑于2022-10-27 08:23:03 陕西20. IIC总线
IIC串行总线的组成及工作原理
单主机
PPT
数据位的有效规定
时钟线
数据线
数据传送格式
总共有9位 = 8位字节长度+1位应答位
应答位的作用
主机对从机发送新数据
如果从机没有对主机寻址信号应答,则
则它必须将数据线置1,而由主机产生一个种植信号,已结束总线的数据传送
若从机对主机进行了应答
从机对主机发送数据
数据帧格式
主机发送信号
PPT

起始信号 + 从机的地址(7位)+传送方向+应答位+数据1+应答位+数据2+应答位+……+数据n+应答位+终止信号结束
主机接收信号
PPT

传送过程中,要改变传送方向
PPT

总线的寻址
PPT

主机发送地址时,发生了什么
总线上的每个从机都将发送的7个位地址码与自己的地址码进行比较, 若相同,则认为自己正在被主机寻址, 然后根据R/T位,确认自己是发送器or接收器
从机的地址
总线数据传送的模拟
原因:
51单片机没有带I方C总线接口,所以只能利用软件实现I2C信号的传输
PPT

通过SCL和 SDA 高低电平的组合,并且高低电平的持续时间必须大于规定值,进行模拟
模拟起始信号的时序组合规定
模拟终止信号的时序组合规定
模拟应答信号的时序组合规定
模拟非应答信号的时序组合规定
I^2C总线器件的扩展
扩展电路
PPT

串行E^2PROM典型产品
AT24C系列
掉电不消失
写入过程
地址
固定部分为:1010
不固定部分——管脚确定:A2、A1、A0
写入过程
PPT

读出过程
PPT

AT24Cxx芯片
特点
PPT

概述
PPT

引脚说明
PPT

A0、A1、A2引脚的作用
提供期间的地址
每个器件规定了可以使用拿几个引脚。 若器件可以使用三个引脚,则总共可以使用8个相同的该器件。 若器件可以使用两个引脚,则……4个相同的该器件。 若…… 一个引脚, 则……2个相同的该期间。
总线时序
PPT
只要延时大于4.7μm就可以
AT24C02驱动程序的编写
目的
使用四个独立按键,控制单片机和AT24C02之间的读写
代码模块
准备工作
变量声明(u8,u16)
独立按键声明
38译码器的声明
数码管段选数据,显示数字字符的数组
延时函数
IIC
创建并添加I2C的".h"".c"文件
创建“.c" ".h"文件
在HARDWARE中创建".c"".h"后缀文件,
添加文件到工程中
和在USER中添加main.c文件一样,只不过换成HARDWARE中添加。 如果找不到创建的文件的话,改一下文件类型就OK
编写“.h”文件
".h"文件固定格式
目的:防止多次引用头文件
代码
#ifndef _I2C.H_ #define _I2C.H_ #endif
声明引脚
子主题
声明函数
写入函数
读取函数
编写“I2C.c”文件
时序总结参考地址
IIC基本传输格式
参考文章/图片来源地址
延时函数
通过“单片机小精灵”软件,生成误差小于1us的延时函数代码
软件地址:https://pan.baidu.com/s/1qFjQsPDybhWA3piD7tZiIg?_at_=1666744033107 提取码:uxzk 连接由:https://blog.csdn.net/weixin_42880082/article/details/121732198 文章提供
起始信号函数
时序图

代码
解释
 
源码
void I2C_Start() { /*起始信号*/ /*拉高时钟和数据线*/ SCL = 1; delay5us(); SDA = 1; delay5us(); /*拉低数据线*/ /*SCL = 1; delay5us();*/ /*上面这句代码可省略不写,因为SCL本身就是要维持高电平,写不写代码作用都一样*/ SDA = 0; delay5us(); /*拉低时钟线,为后面发送数据做准备*/ SCL = 0; }
终止信号函数
时序图

代码
解释图

源码
void I2C_Stop() { /*停止信号*/ /拉高时钟线,拉低数据线*/ SCL = 1; delay5us(); SDA = 0; delay5us(); /*拉高时钟线*/ SDA = 1; delay5us(); }
发送字节函数
需要有一个返回值来判断数据是否发送成功。
函数头要用 char类型
移位传送
需要用for循环
先传送最高位
SDA = dat>>7;
将要传输的8位数据,右移七位,将最高位赋给SDA
然后将次高位变成最高位
dat = dat<<1;//为下一次传输做准备
根据时序图最大要至少延时4.7us, 且每一位SDA信号变化结束之后, 都要将SCL调制高电平,让SDA电平稳定发送
 
delay_5us();//为了满足时序图的延时要求 SCL = 1;//保持数据稳定传输 delay_5us(); SCL = 0;//为下一次传输位数据做准备 delay_5us();//为了满足时序图的延时要求
释放时钟线和数据线
SDA=1; delay_5us(); SCL=1;delay_5us():
等待应答ACK/NACK
ACK: acknowledgement 应答 NACK: Negtive ACKnowledgement 非应答
判断SDA数据线的状态
从时序图中可以看出 若从机应答,SDA会变成低电平 若从机非应答,SDA会变成高电平

源码:
while (SDA){ /*产生应答,自然会跳过while循环*/ t++; /*防止从机一直不产生应答,然后卡死在这。所以需要设置强制跳出结构*/ if(t == 200){ return 0; /*若长时间不响应,则返回响应失败*/ } } return 1; return 1; /*返回响应成功*/
读取字节函数
函数头需要有返回类型,判断是否读取成功
SDA和SCL
参考文章
移位读取8位数据
源码
unsigned char I2C_read_byte() { unsigned char a = 0; dat = 0; /*移位读取8位数据*/ for(a; a < 7; a++){ /*准备接收一位数据: SDA保持不变,将其值赋给dat*/ SCL = 1; delay5us(); /*先接收一位数据,再移位,这样移动7次,最高位就从0→7了*/ dat |= SDA; delay5us(); dat <<= 1; /*SCL拉低,让SDA可以变化*/ SCL = 0; delay5us(); } return dat; }
返回读取到的数据,给数据变量
写入函数
A是什么? +0是写入, +1是读取
函数头
不返回值
变量:子主题
存储的地址,存储的数据
起始信号 函数;
发送 写入的器件地址;
发送 写入的首地址;
发送 要写入的数据;
停止信号 函数
读取数据函数
函数头
返回读到的
要读取的地址
起始信号 函数;
发送 写入的器件地址;
发送 读出的首地址;
起始信号 函数
子主题
发送 读取的器件地址
发送
发送 要读取的数据;
读取地址函数
停止信号 函数
按键函数
K1
保存显示的数据
K2
读取保存的数据
K3
对于显示的数据进行累加
K4
对保存的数据清零
显示函数
代码
i2c.h
代码
#ifndef _I2C_H_ #define _I2C_H_ #include "reg52.h" sbit SDA = P2^0; sbit SCL = P2^1; unsigned char AT24C02_read(unsigned char address); void AT24C02_write(unsigned char address, unsigned char dat); #endif
i2c.c
代码
#include "i2c.h" #include "reg52.h" void delay5us() { unsigned char a; for(a = 1; a > 0; a--); } void I2C_start() { /*起始信号*/ /*拉高时钟和数据线*/ SCL = 1; delay5us(); SDA = 1; delay5us(); /*拉低数据线*/ /*SCL = 1; delay5us();*/ /*上面这句代码可省略不写,因为SCL本身就是要维持高电平,写不写代码作用都一样*/ SDA = 0; delay5us(); /*拉低时钟线,为后面发送数据做准备*/ SCL = 0; } void I2C_stop() { /*停止信号*/ /*拉高时钟线,拉低数据线*/ SCL = 1; delay5us(); SDA = 0; delay5us(); /*拉高时钟线*/ SDA = 1; delay5us(); } unsigned char I2C_send_byte(unsigned char dat) { unsigned char i, t = 0; for (i = 0; i < 8;i++){ SDA = dat>>7; delay5us(); /*改变SDA 或 SCL 之后,后面紧跟延时函数*/ dat <<=1;/*为下一次传输最高位做准备*/ SCL = 1; delay5us(); /*保持数据稳定传输*/ SCL = 0; delay5us(); /*为下一次改变SDA数值进行传输做准备*/ SDA = 1; delay5us(); /*释放数据线*/ SCL = 1; delay5us(); /*释放时钟线*/ while (SDA){/*产生应答,自然会跳过while循环*/ t++; /*防止从机一直不产生应答,然后卡死在这。所以需要设置强制跳出结构*/ if(t == 200){ SCL = 0; delay5us(); return 0; /*若长时间不响应,则返回响应失败*/ } } SCL = 0; delay5us(); return 1; /*返回响应成功*/ } } unsigned char I2C_read_byte() { unsigned char a = 0, dat = 0; /*让数据线处于空闲状态??*/ SDA = 1; delay5us(); /*移位读取8位数据*/ for(a; a < 8; a++){ /*准备接收一位数据: SDA保持不变,将其值赋给dat*/ SCL = 1; delay5us(); /*先移位,再接受数据,这样移动8次,就刚好将没有装填的那一位移出去了*/ dat <<= 1; dat |= SDA; delay5us(); /*SCL拉低,让SDA可以变化*/ SCL = 0; delay5us(); } return dat; } void AT24C02_write(unsigned char address, unsigned char dat) { I2C_start(); I2C_send_byte(0xa0); /*发送器件地址(写命令)*/ I2C_send_byte(address); I2C_send_byte(dat); I2C_stop(); } unsigned char AT24C02_read(unsigned char address) { unsigned char num; I2C_start(); I2C_send_byte(0xa0); /*发送期末地址(写命令)*/ I2C_send_byte(address); /*转换方向*/ I2C_start(); I2C_send_byte(0xa1); /*发送器件地址(读命令)*/ num = I2C_read_byte(); I2C_stop(); return num; }
main.c
代码
#include "reg52.h" #include "i2c.h" typedef unsigned int u16; typedef unsigned char u8; sbit k1 = P3^1; sbit k2 = P3^0; sbit k3 = P3^2; sbit k4 = P3^3; sbit LSA = P2^2; sbit LSB = P2^3; sbit LSC = P2^4; u8 digiDuan[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; u8 number = 0; void delay(u16 i) { while(i--); } void keys() { if (k1 == 0){ delay(1000); if (k1 == 0){ AT24C02_write(0, 2); } while(!k1); } if (k2 == 0){ delay(1000); if (k2 == 0){ number = AT24C02_read(0); } while(!k2); } if (k3 == 0){ delay(1000); if (k3 == 0){ number++; if(number > 255){ number = 0; } } while(!k3); } if (k4 == 0){ delay(1000); if (k4 == 0){ number = 0; } while(!k4); } } void display_digital( u8 a) { u8 i; for (i = 0; i < 3; i++){ switch (i){ case 0: LSA = 0; LSB = 0; LSC = 0; P0 = digiDuan[a%10]; delay(100); P0 = 0X00; break; case 1: LSA = 1; LSB = 0; LSC = 0; P0 = digiDuan[a%100/10]; delay(100); P0 = 0X00; break; case 2: LSA = 0; LSB = 1; LSC = 0; P0 = digiDuan[a/100]; delay(100); P0 = 0X00; break; } } } void main() { while(1){ keys(); display_digital(number); } }
读取保存的数据一直是255, 不能正常存储+读取显示的数字
OLED