

好处:便捷;
缺点:双方必须时钟精确,否则对不齐了。
在IIC总线上,每个从机都有其唯一的默认设备地址【这个设备地址由谁规定的呢?】。发送数据时采用广播形式,数据最开始带着从机地址,其他设备会选择性失聪。


两总线电平处于拉高状态:

主机在时钟线上产生恒定的时钟脉冲

在时钟线某个高电平时,数据线下拉;随机主机会在时钟线上产生一个恒定的时钟脉冲信号。


主机在低电平时钟时,设置数据;从机在高时钟时,读取数据。



每当发送器传输完一个字节(8 bit)的数据之后,发送端会等待一定的时间,等接收方的应答信号。接收端通过拉低SDA数据线,给发送端发送一个应答信号,以提醒发送端我这边已经接受完成,数据可以继续传输,接下来,发送端就可以继续发送数据了。如此反复,直到发送完所有数据。

即每一帧的数据传送与应答数据 共有9bit
值得注意的是,IIC通信中,主机发送的第一个字节往往是某个从机在总线上的地址:




参考来源:
IIC详解,包括原理、过程,最后一步步教你实现IIC_iic协议-CSDN博客
然后主机会在时钟线处于高电平时,将数据线拉高,通信终止信号。整段通信完成。

起始信号和终止信号都是由主机发送的。在起始信号产生之后,总线就处于被占用的状态,在终止信号产生之后,总线就处于空闲状态。
注意:每次数据传送总是由主机产生的终止信号来结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
代码实现-学习视频推荐
- 头文件
-
- myiic.h
-
- #ifndef __MYIIC_H
- #define __MYIIC_H
- #include "sys.h"
-
- //IO方向设置
- //CRH,CRL 端口输入输出模式寄存器。CRH为8-15引脚,CRL为0-7引脚
- #define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}//PC11引脚为上拉/下拉输入
- #define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}//PC11为推挽输出
-
- //IO操作函数
- #define IIC_SCL PCout(12) //SCL
- #define IIC_SDA PCout(11) //SDA
- #define READ_SDA PCin(11) //输入SDA
-
- //IIC所有操作函数
- void IIC_Init(void); //初始化IIC的IO口
- void IIC_Start(void); //发送IIC开始信号
- void IIC_Stop(void); //发送IIC停止信号
- void IIC_Send_Byte(u8 txd); //IIC发送一个字节
- u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
- u8 IIC_Wait_Ack(void); //IIC等待ACK信号
- void IIC_Ack(void); //IIC发送ACK信号
- void IIC_NAck(void); //IIC不发送ACK信号
-
- //一下两个函数实际并未被定义
- void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
- u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
- #endif
-
- .c文件
-
- #include "myiic.h"
- #include "delay.h"
-
-
- //初始化IIC
- //硬件I2C必须开漏输出
- //模拟I2C则为推挽输出(方便输出高低电平?)
- void IIC_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- //RCC->APB2ENR|=1<<4;//先使能外设IO PORTC时钟
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE );
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
-
- IIC_SCL=1;//PCout(12)
- IIC_SDA=1;//PCout(11)
-
- }
- //产生IIC起始信号
- //SCL为高时,SDA拉低,准备开始传数据。(记得再拉低SCL,这个是为什么?)
- //SCL为高时,SDA拉高,结束传输数据。
- //传输数据时,SCL为高时数据有效
- void IIC_Start(void)
- {
- SDA_OUT(); //sda线输出
- IIC_SDA=1; //SDA = 1,SCL = 1 空闲状态
- IIC_SCL=1;
- delay_us(4);
- IIC_SDA=0;//START:when CLK is high,DATA change form high to low
- delay_us(4);
- IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
- }
- //产生IIC停止信号
- void IIC_Stop(void)
- {
- SDA_OUT();//sda线输出
- IIC_SCL=0;
- IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
- delay_us(4);
- IIC_SCL=1;
- IIC_SDA=1;//发送I2C总线结束信号
- delay_us(4);
- }
- //等待应答信号到来
- //返回值:1,接收应答失败
- // 0,接收应答成功
- u8 IIC_Wait_Ack(void)
- {
- u8 ucErrTime=0;
- SDA_IN(); //SDA设置为输入
- IIC_SDA=1;delay_us(1);
- IIC_SCL=1;delay_us(1); //设置数据有效
- while(READ_SDA)//READ_SDA为0时表示接收到应答
- {
- ucErrTime++;
- if(ucErrTime>250)
- {
- IIC_Stop();
- return 1;
- }
- }
- IIC_SCL=0;//时钟输出0
- return 0;
- }
- //产生ACK应答
- void IIC_Ack(void)
- {
- IIC_SCL=0;
- SDA_OUT();
- IIC_SDA=0;
- delay_us(2);
- IIC_SCL=1;
- delay_us(2);
- IIC_SCL=0;
- }
- //不产生ACK应答
- void IIC_NAck(void)
- {
- IIC_SCL=0;
- SDA_OUT();
- IIC_SDA=1;
- delay_us(2);
- IIC_SCL=1;
- delay_us(2);
- IIC_SCL=0;
- }
- //IIC发送一个字节
- //返回从机有无应答
- //1,有应答
- //0,无应答
- void IIC_Send_Byte(u8 txd)
- {
- u8 t;
- SDA_OUT();
- IIC_SCL=0;//拉低时钟开始数据传输
- for(t=0;t<8;t++)
- {
- IIC_SDA=(txd&0x80)>>7;//先发高位
- txd<<=1;
- delay_us(2); //对TEA5767这三个延时都是必须的
- IIC_SCL=1;
- delay_us(2);
- IIC_SCL=0;
- delay_us(2);
- }
- }
- //读1个字节,ack=1时,发送ACK,ack=0,发送nACK
- u8 IIC_Read_Byte(unsigned char ack)
- {
- unsigned char i,receive=0;
- SDA_IN();//SDA设置为输入
- for(i=0;i<8;i++ )
- {
- IIC_SCL=0;
- delay_us(2);
- IIC_SCL=1;
- receive<<=1;
- if(READ_SDA)receive++;
- delay_us(1);
- }
- if (!ack)
- IIC_NAck();//发送nACK
- else
- IIC_Ack(); //发送ACK
- return receive;
- }
- ————————————————
- 版权声明:本文为CSDN博主「碑 一」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
- 原文链接:https://blog.csdn.net/dedell/article/details/96628955
stm32无人机:利用上面基础模拟IIC代码把数据从mpu6050传感器数据读取出来(未完成)
- *stm32 HAL库 实现和使用IIC总线*
-
- #include "stm32xxxxx.h"
- #include "stm32xxxxx_hal.h"
-
- I2C_HandleTypeDef hi2c1;
-
- void SystemClock_Config()
- {
- // 配置系统时钟
- // ...
- }
-
- void GPIO_Configuration()
- {
- // 配置GPIO引脚
- // ...
- }
-
- void I2C_Configuration()
- {
- // 配置I2C外设
- hi2c1.Instance = I2C1;
- hi2c1.Init.Timing = 0x00303D5B; // 根据具体系统时钟和I2C时钟频率调整这个值
- hi2c1.Init.OwnAddress1 = 0;
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
- hi2c1.Init.OwnAddress2 = 0;
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
-
- // 初始化I2C外设
- HAL_I2C_Init(&hi2c1);
- }
-
- int main(void)
- {
- uint8_t sendData[] = {0x01, 0x02, 0x03};
- uint8_t receiveData[3];
-
- // 初始化系统时钟
- SystemClock_Config();
-
- // 配置GPIO引脚
- GPIO_Configuration();
-
- // 配置I2C外设
- I2C_Configuration();
-
- // 发送数据
- HAL_I2C_Master_Transmit(&hi2c1, DeviceAddress, sendData, sizeof(sendData), HAL_MAX_DELAY);
-
- // 延时等待数据发送完成
- HAL_Delay(1); // 根据实际情况调整延时时间
-
- // 接收数据
- HAL_I2C_Master_Receive(&hi2c1, DeviceAddress, receiveData, sizeof(receiveData), HAL_MAX_DELAY);
-
- while (1)
- {
- // 主程序逻辑
- }
- }
参考:
【物联网】I2C(IIC)通信协议详解与应用_iic通信_嵌入式小白—小黑的博客-CSDN博客
前几节用到的串口以及串口调试助手 - 从单片机发送数据到pc上,显示出来


直接看“传感器数据手册”

重点关注:设备从机地址 0X71

1、为什么选择IIC通信,和SPI UART 的区别,哪个更快?
2、IIC最大速率
3、IIC协议,帧格式,收发数据的时序流程
4、介绍一下IIC
5、调试IIC过程中遇到的问题
6、iic寻址,主机从机如何对接的