I2C和SI2C复习

I2C和SI2C复习 一、I2C发送1.电路结构线与由于串口只能一对一通信所以引入I2C。7位地址0000000——1111111也就是从0——127去掉一些特殊的地址仍然能够同时和100多个从机通信。SCL:主---从SDA主---从从---主开漏输出就是为了实现逻辑线与功能2.I2C通信协议2.1整体流程2.2起始位和停止位2.3寻址阶段主机发送地址0x78,左移一位给读写那一位留出空间读为1写为0。主机发送完要等待从机回应2.4发送数据阶段发送方发完接收方应答3.模块使用3.1波特率3.2速度模式3.3占空比3.4 I2C初始化这里需要特别注意I2C使用之前一定要施加并释放复位信号类似于手机的重启4.代码4.1 I2C内部结构总框图4.2发送函数介绍4.3发送函数编写#1、首先等待总线空闲因为并不能保证SCL和SDA都处于高电位空闲#2、发送起始位并等待起始位发送完成#3、寻址阶段先清除AF标志位因为后面要查看此标志位再发送地址等待从机回应ADDR和AF清除ADDR标志位。这里发送地址那里要注意清除ADDR的方式也要注意#4、发送数据阶段发送之前一定要先查看上一次发送时数据结束后是否接收AF同时确保数据寄存器为空再发送。发送之后发送完同样检查是否收到回应并且查看数据寄存器和移位寄存器为空BTF#5、发送停止位二、I2C接收1.代码1.1接收函数介绍注意发送前等待总线空闲是为了争夺 I2C 总线控制权接收数据时控制权已经属于当前主机只是在读取 SDA 电平因此不需要等待总线空闲。不过注意如果是“一次新的读操作”比如读取传感器也要先等空闲因为你还是要先发送地址。只有连续接收数据阶段才不用等。2.接收函数编写#1、发送起始位等待发送完成#2、寻址阶段先清除AF 再发送地址 等待从机回应#3、接收数据讲接收数据之前先了解ACK和NAK这块与发送数据不同。特别需要注意的是他们只作用于正在被接受的字节停止位作用于正在被接收的字节之后#3.1、size 1时由于清除ADDR之后就开始接收数据了所以清除完ADDR字节正处于接收状态从而必须马上发送NAKACK 0并且发送停止位。当字节完全被接受到移位寄存器中会自动被送到接受数据寄存器中所以等待接收数据寄存器为非空状态就可以读取数据了#3.2、size 2时第一个字节被读取完之后第二个字节自动进入移位寄存器。#3.3、size n时ACK1 是开关不是按钮。表示的是“从现在开始我每收到一个字节都自动回 ACK。”三、SI2C1.SI2C初始化因为要实现线与功能所以都是开漏模式2.SI2C发送一个字节2.1先建立几个函数方便后续对SCL和SDA操作void SCL_Write(uint8_t Level) { if(Level 1) { GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET); } else { GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); } } void SDA_Write(uint8_t Level) { if(Level 1) { GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET); } else { GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET); } } uint8_t SDA_Read(void) { if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1) Bit_SET) { return 1; } else { return 0; } } //延迟微妙 void Delay_us(uint32_t us) { uint32_t n us*8; for(uint32_t i 0;i n; i);//一次for循环1/8 us }2.2发送起始位和停止位函数起始位因为一开始SCL和SDA都是高电位空闲所以直接给SDA置低电位就可以停止位想要让SDA出现上升沿但无法确定SDA的状态。所以先让SCL和SDA都置0随后SCL置1再SDA置1void SendStart(void) { //假设SCL和SDA都是空闲——高电平 SDA_Write(0); Delay_us(1); } //SDA未知所以先把SDA置低位才有上升沿 void SendStop(void) { //都置低位 SCL_Write(0); SDA_Write(0); Delay_us(1); //SCL置高位 SCL_Write(1); Delay_us(1); //SDA置高位 SDA_Write(1); Delay_us(1); }2.3发送一个字节发送一个bit位重点理解SCL 0发送方准备发送数据。SCL 1接收方读取数据。这里SCL先置0是因为SCL 1时改变SDA容易误认为起始位和停止位所以SCL 0时改变SDA。发送一个字节代码特别注意一个字节到一个bite 是如何转换的。uint8_t SendByte(uint8_t Byte) { //发送一个字节从高位向低位发 for(int8_t i 7;i 0; i--) { //发送一个bite SCL_Write(0); if((Byte (0x01i)) 0) { SDA_Write(0); } else { SDA_Write(1); } Delay_us(1); SCL_Write(1); Delay_us(1); } //读取ACK或NAK SCL_Write(0);//SCL拉低此时允许SDA改变 SDA_Write(1);//主机释放SDA让从机控制 Delay_us(1); SCL_Write(1);//第9个时钟拉高此时SDA上的应答有效 Delay_us(1); return SDA_Read();//返回从机的ACK或NAK }读取ACK和NAK也需要注意核心是主机释放SDA从机拉低SDA回应。主机想要释放SDA必须先SCL置0然后SDA置1紧接着SCL置1读取SDA获得从机回应的ACK或NAK3.SI2C读取一个字节3.1读取一个biteSCL_Write(0); // SCL拉低进入数据准备阶段 SDA_Write(1);// 主机释放SDA让从机控制SDA并放入要发送的0或1 Delay_us(10);// 等待一段时间让SDA上的数据稳定 SCL_Write(1); // SCL拉高进入数据读取阶段 Delay_us(10);// 等待信号稳定保证读取的数据可靠 bit SDA_Read();// 读取SDA电平得到从机发送的1个bit0或13.2读取一个字节参数ACK表示主机想不想继续接收数据1代表ACK继续0代表NAK不继续但是对于I2C的SDA来说0代表继续1代表停止uint8_t ReceiveByte(uint8_t ACK) { uint8_t Byte 0; for(int8_t i 7;i 0; i--) { //读取一个bite SCL_Write(0);//SCL拉低允许SDA改变 SDA_Write(1);//主机释放SDA让从机控制 Delay_us(1); SCL_Write(1);//第1-8个时钟SCL拉高SDA上的数据有效 Delay_us(1); //读取数据 if(SDA_Read() ! 0) { Byte | 0x01i;//给Byte的第i位赋1 } } //主机回复ACK/NAK SCL_Write(0);//SCL拉低允许SDA改变 SDA_Write(!ACK);//ACK1主机回应对于SDA取反SDA_Write(0) Delay_us(1); SCL_Write(1);//第9 个时钟SCL拉高从机读取ACK/NAK Delay_us(1); return Byte; }4.SI2C发送数据/** * brief 软件I2C向从机连续发送多个字节数据 * param Addr: I2C从设备7位地址 * param TData: 待发送数据缓冲区指针 * param Size: 需要发送的数据字节长度 * retval int8_t 0:发送成功 -1:发送从机地址无应答 -2:发送数据时无应答 */ int8_t My_SI2C_SendBytes(uint8_t Addr,uint8_t *TData,uint16_t Size) { //1.发送I2C起始信号 SendStart(); //2.发送从机地址写命令(Addr0xFE 最低位清0代表写操作) if(SendByte(Addr 0xfe) ! 0) //SendByte返回非0表示从机无应答 { SendStop(); //发送停止位结束本次通信 return -1; //寻址失败返回错误码‑1 } //3.循环逐个发送缓冲区中的所有数据字节 for(uint16_t i 0;i Size; i) { if(SendByte(TData[i]) ! 0)//发送单字节检测从机应答 { SendStop(); //出错则立即结束通信 return -2; //发送数据失败返回错误码‑2 } } //4.全部数据发送完成发送I2C停止信号释放总线 SendStop(); return 0; //发送成功 }5.SI2C接收数据/** * brief 软件I2C从从机连续读取多个字节数据 * param Addr: I2C从设备7位地址 * param RData: 接收数据存放缓冲区指针 * param Size: 需要读取的数据字节长度 * retval int8_t 0:读取成功 -1:发送读地址无应答 */ int8_t My_SI2C_ReceiveBytes(uint8_t Addr,uint8_t *RData,uint16_t Size) { //1.发送I2C起始信号 SendStart(); //2.发送从机地址读命令(Addr | 0x01 最低位置1代表读操作) if(SendByte(Addr | 0x01) !0) //SendByte返回非0表示从机无应答 { SendStop(); //发送停止位结束本次通信 return -1; //寻址失败返回错误码‑1 } //3.接收前Size‑1个字节读完每一字节主机回复ACK(继续读) for(uint16_t i 0;i Size-1; i) { RData[i] ReceiveByte(1);//参数1主机发送应答ACK通知从机继续发数据 } //读取最后1个字节主机回复NACK告诉从机不再读取数据 RData[Size-1] ReceiveByte(0);//参数0主机发送非应答NACK //4.全部数据接收完成发送I2C停止信号释放总线 SendStop(); return 0; //读取成功 }