IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 关于iic协议和对AT24C02进行读写数据的理解和代码解读 -> 正文阅读

[嵌入式]关于iic协议和对AT24C02进行读写数据的理解和代码解读

认识IIC协议

本文是基于正点原子的第二十九章 IIC实验编写的。包括stm32f407和msp432e401y的代码展示,编写目的是方面自己理解,以下纯属自己的理解。

IIC协议是一种通过利用两条线,然后依次传输8位的数据的协议,所以说明他是传二进制数据的,我们可以利用接受到的二进制数据转化成我们需要的数字和字符,这里的意思就是比如MPU6050(陀螺仪)、vl53lxx(激光测距)这样我们从模块中获取传感器的数据,所以熟练使用这种协议是很有必要的。

IIC协议其实并不复杂,你就可以理解成它就是有两条线,我们通过规定这两条线的高低电平不同状态下的含义,然后就可以通过电平传输数据,类似这样的协议甚至你可以自己去定协议。

IIC协议主要分为8个识别信号组成,我们也称之为时序信号,他们分别是:开始信号、停止信号、结束信号、等待信号、ACK应答信号、no ACK应答信号、写信号、读信号。其中,结束信号我们几乎可以不用,因为我们在使用的时候每次只需要开启,结束的时候停止即可。

①开始信号:准备开始传输数据。
②停止信号:停止传输数据。
③等待信号:我们读写完每一次数据,都要等待反馈告诉我们从机接收到没有,其实接受的就是④⑤两个状态。
④ACK应答信号⑤no ACK应答信号:给主机的信号,告诉他现在我的状态怎么样,有没有接收到你的数据。
这个个地方可以参考:https://blog.csdn.net/shenhuxi_yu/article/details/65935697(第五点),其实意思就是告诉我读取的设备我还要不要继续读取。
⑥写信号:写入信号。此时单片机为发送端。
⑦读信号:读取信号。此时单片机为接收端。

如果没有理解没关系,因为IIC是底层协议,我们在写底层的时候,只需要初始化好,写好时序就行,基本上可以完整移植代码,真正应用层是24cxx等这样的操作文件。

IIC协议软件模拟方法

上面我们也说了,IIC底层是固定的,包括两部分,一个是初始化,一个是时序。

管脚初始化

我们只有两根线,一根线是SCL(信号线),一根是SDA(数据线)。所以使能两个管脚,我们就要办使能就行了,输出上拉。(为什么要上拉,因为对于某些单片机,可能性能不过,由于IIC对于管脚电平电话很敏感,如果你电平转不过来,那根本用不了,所以设置为上拉,那何为上拉,建议百度一下,其实就是在管脚并路上面串一个电阻,电阻另一头接VCC,这样在需要VCC电压的时候可以快速跳转)

STM32的

GPIO_InitTypeDef  GPIO_InitStructure;//GPIO_InitTypeDef  是初始化管脚需要参数的结构体
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//真正初始化管脚在这里

msp432

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);		//使能gpiok
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);		//使能gpiok	
GPIOPinTypeGPIOOutput(GPIO_PORTM_BASE, GPIO_PIN_0);	//设为输出
GPIOPinTypeGPIOOutput(GPIO_PORTG_BASE, GPIO_PIN_0);	//设为输出
GPIOM->PUR |= GPIO_PIN_0;GPIOG->PUR |= GPIO_PIN_0;	//上拉

在完成管脚初始化之后,我们还需要方便代码的编写,那什么是方便代码的编写呢?其实就是,我们在时序里面,经常要对电平拉高拉低,我们不可能每次都写gpioset();或者GPIOPinWrite();这样去操作电平,因为看起来很麻烦,所以我们需要在IIC的h文件中,进行相关宏定义,方便编写,这也是很多移植代码里面,常见的操作(下次出一期讲讲这个MPU6050移植到msp432上面的文章)。

//IO方向设置
#define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}	
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} 
//IO操作函数	 
#define IIC_SCL    PBout(8) //SCL
#define IIC_SDA    PBout(9) //SDA	 
#define READ_SDA   PBin(9)  //输入SDA 

可能很多金针菇就要问了,这都啥意思。

IIC_SCL和IIC_SDA其实设置scl和sda的输出电平情况,这要写,
我们只要比如IIC_SCL=0就是低电平,=1就是高电平,至于是怎么操作的,自己研究去。

金针菇们又要问了,为什么需要将SDA设为输入,因为你读的时候是读电平,当然是输入。READ_SDA相当于读SDA的电平

设置SDA是输入输出的在//IO方向设置
那至于怎么操作的,就是寄存器的操作,自己去理解去吧。

大家可以看看在msp432里面我们是如何实现的:
//IO方向设置
#define SDA_IN()  GPIOPinTypeGPIOInput(GPIO_PORTG_BASE, GPIO_PIN_0)	
#define SDA_OUT() GPIOPinTypeGPIOOutput(GPIO_PORTG_BASE, GPIO_PIN_0) 
//IO操作函数	 
#define IIC_SCL    HWREG(GPIO_PORTM_BASE + (0x00000000 + (GPIO_PIN_0 << 2))) //SCL	PM0
#define IIC_SDA    HWREG(GPIO_PORTG_BASE + (0x00000000 + (GPIO_PIN_0 << 2))) //SDA	 PG0
#define READ_SDA   GPIOPinRead(GPIO_PORTG_BASE, GPIO_PIN_0)  //输入SDA
我们可以看到代码非常的简单,除了HWREG,其实HWREG还有另一个也是H开头为了叫啥了,长一点的,都是对寄存器的操作(在官方SDK里面的timers和pwm都有),这个函数其实就是对寄存器进行操作,实现拉高或者拉低,实现IIC_SCL=?就能操作。

那金针菇又问了,为什么你都是gpio0,这个其实由于不是高电平都是1,可以看看
GPIO_PIN_?的宏定义,我们在移植mpu6050的时候讲讲。

时序

好了,我们如果将前面都弄完,其实这个时序,你都可以直接移植到任何单片机。那我们是时序呢?其实时序就是我们这个信号是,电平要怎么变化,然后他就属于这个信号,比如起始信号,是SCL高电平时,SDA由高到底,就代表起始信号,ok,其他的就不说,因为都是固定的。
在这里插入图片描述
理解了上面,代码就不是问题,我们这里举一个例子来说明一下,就拿起始信号

void IIC_Start(void)
{
	SDA_OUT(); //SDA设为输出,因为我们告诉从机,我们要开始传输拉
	IIC_SDA=1;//拉高,因为他要由高变低
	IIC_SCL=1;//拉高,一般SCL在高电平的时候都是指示信号,只有低电平的时候是传输数据
	delay_us(4);//小延迟
 	IIC_SDA=0;//拉低,可以看看上面那个时序介绍
	delay_us(4);//小延迟
	IIC_SCL=0;//我马上要拉低,其实这里是什么都行,但是我们一般要看下一步一般是什么操作,避免奇奇怪怪的电平传过去
	//那为什么要拉低,因为我们开始的时候,之后都是传输数据的,所以可以看看IIC_SCL=1这句话后面注释
}	  

这里msp432就不介绍了,一模一样,但是IIC_SDA和IIC_SCL高电平不一定是1,这个可以在NRF24L01移植的时候仔细说说。

至此,底层IIC就写完了。

可能金针菇们又要问了,那硬件呢?咋弄,其实你理解了软件模拟的思想,就可以了,关于硬件使用信手沾来,你可以问硬件IIC,好多接口函数,比如什么什么中断,FIFO啥的,这个你仔细看就明白了,中断什么其实就是某个地方的标志位,他单独弄出来。

最后就是建议还是用硬件IIC,因为避免占用MCU资源,速率也更快,接口函数也更多,使用也更多样化;软件模拟在移植的时候比较方便。

AT24C02

简介

其实AT24C02就是IIC通讯的一个存储单元硬件而已,往里面存东西的而已。下面简单介绍一下AT24C02。

首先AT24C02的意思是,是2k位,也就是21024位。

存储大小计算

可能金针菇们又问了,怎么算了,还有那些存储空间单位怎么回事,下面可以看一下存储空间的单位换算:
1kB=1024B(1k Byte = 1024Byte)(1千字节)
1B=8b(1Byte=8bit)(1字节为8个位)

那金针菇又问了,那为什么是2*k位,因为这里的k是kb,小b,也就是bit。

所以AT24C02大小是21024位,那我们当然是一个个字节存放啦,所以有21024/8=256个字节。

金针菇又问了,我知道这个干吗,因为IIC一次传输8位,也就是8个bit,所以每一次是读取一个字节,也就是我们有256个字节读取,可以看一下下面的图:
在这里插入图片描述

工作方式

接着就是我们AT24C02工作方式了,首先介绍一下AT24C02上面有三个管脚:A0、A1、A2,那是用来干什么的呢?是用来规定地址的,比如我们AT24C02这三个管脚都是用来固定地址,他们都接地了,所以我们访问的时候是传输1010 000x(1010是固定的,000就是A2、A1、A0,你也别问为什么是1010,厂家规定的,他总的有个位吧?,因为他是8位呀,总得传点什么),那什么是x,x就是你是读是写,1是读,0是写。(如果看不懂地址作用,那就一直看下去,下面讲工作流程有讲)

那我们每次只能读取一个字节的数据,所以,我们只要输入我们想要读写的地址就可以完成相关操作。

那聪明点的金针菇又问了,那他比256要多呢?ok,但是我们换个说法,是比2k多。你可能就猜到了,没错,都是2k、2k的存储,如果大于2k,我们就要利用地址(地址作用不明白往下看),2k的A0-A2是固定的,如果我们大于2k就要利用起来(2k一个区),比如4k,那我们只固定A2、A1(具体忘了哪个,就那个意思)A0可变,这样A0就可以决定你访问哪一个区(两个2
k的区,一个去256个字节)。(看不懂的金针菇多看几篇)

流程(代码)

那说完一些基础知识,那就开始实操了。有那么几个函数:
①u8 AT24CXX_Check(void)

②u8 AT24CXX_ReadOneByte(u16 ReadAddr);
③void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);

④void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
⑤void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)

⑥void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
⑦u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)

那聪明的金针菇又要问了,怎么三个读写呀?下面来具体解释一下代码:

首先是AT24CXX_Check,这个函数其实就是检测AT24C02存不存在,那怎么检测的呢?其实就是,他在255字节地方写了个0X55进去,然后读取看看有没有,你其实也可以写在其他字节、其他符号(比如0xaa啥的),主要写了一个你不常用的地方,然后每次使用前读取看看对不对,读取到,ok,那就行了。

如何是②和③,是底层的读写函数,正如其名称,一个读写一个字节。这里解释一下IIC_Wait_Ack();这里每次发送指令都要IIC_Wait_Ack是因为确认发送的指令他收到没。另外就是,为什么有EE_TYPE>AT24C16,其实是为了判断你的存储大小是不是超过16k,所以所以正点的代码其实兼容了其他大小了,那究竟怎么做到的呢?我们想想,16k=8*2k,所以我们A0-A3已经用满了,需要其他方式,至于大于16k怎么用就没研究了,反正都差不多。

IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));
这句话兼容2-16k的,我们在读写第一步都是确认我们读写哪个区,对于AT24C02,后面都是0,只读写0XA0,因为他只有一个区,比如AT24C04,4个,两个256的区,那他的区就是0XA0和0XA1,后面的地址操作其实就是超过256,我们就是第一个去,就是1,就是0XA1,其他同理。(注意WriteAddr是16位)

④⑤和⑥⑦可能金针菇们就不理解了,怎么由两个读写函数。你可能发现④⑤在main没有用,其他这种函数你自己写就行,你想实现什么功能,就怎么写,注释也很清楚了,这里要注意一点,读写时从高位开始,因为比如我定义一个u32的temp,我读一次,是不是temp = (u8)data啊,然后temp<<=8,所以他第一次读的数据,最后会变成高8位。

基本上知识就那么多,总结一下。IIC传输速度慢,一次只能传8位数据,但是在现实生产中还是很常见的。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-16 11:54:30  更:2021-08-16 11:56:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:46:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码