当前位置: 首页 > news >正文

10:IIC通信

1:IIC通信

I2C总线(Inter IC BUS) 是由Philips公司开发的一种通用数据总线,应用广泛,下面是一些指标参数: 

  • 两根通信线:SCL(Serial Clock,串行时钟线)、SDA(Serial Data,串行数据线)。
  • 同步,半双工。
  • 主从机通信过程中,会带数据应答。
  • 支持总线挂载多设备(一主多从、多主多从)。“多主多从”模式更复杂,下面没有特殊说明,默认都是“一主多从”模式。
  • 高位先行。

那同步时序和异步时序的优缺点各是什么呢?

  • 同步时序:优点是对时间要求不严格,可以软件手动翻转电平实现通信,所以可以大幅降低单片机对外围硬件电路的依赖,在一些低端单片机没有硬件资源的情况下,很容易使用软件来模拟时序;缺点就是多一个时钟线。
  • 异步时序:优点是少一根时钟线,节省资源;缺点是对时间要求严格,对硬件电路的依赖严重。

一个通信协议,就必须在硬件和软件上都做出规定 

  • 硬件上规定电路如何连接、端口的输入输出模式等
  • 软件上规定通信时序、字节如何传输、高位/低位先行等

 

硬件电路规定所有I2C设备的SCL连在一起,SDA连在一起。设备的SCL和SDA均要配置成 开漏输出模式。SCL和SDA各添加一个 上拉电阻,阻值一般为4.7KΩ左右。
总结:为防止总线没协调好,导致电源短路,采用 外置弱上拉电阻+开漏输出 的电路结构。

  • CPU:主机,对总线控制权力大。任何时候,都是主机对SCL完全控制。空闲状态下,主机可以主动发起对SDA的控制。
  • 被控ICx:从机,对总线控制权力小。任何时刻,从机都只能被动读取SCL线。只有在从机发送数据、或者从机发送应答位时,从机才短暂的具有对SDA的控制权。
  • 从机设备地址:为了保证通信正常,每个从机设备都具有一个唯一的地址。在I2C协议标准中,从机设备地址分为7位地址、10位地址,其中7位较为简单且应用更广泛。不同的I2C模块出厂时,厂商都会为其分配唯一的7位地址,具体可以在芯片手册中查询,如MPU6050的地址是110_1000,AT24C02的地址是101_0000等,其中器件地址的最后几位是可以在电路中改变的。总线上都是不同模块一般不会有地址冲突,若总线上有相同的模块就需要外界电路来相应的器件地址。
  • 通信引脚结构:输入线都很正常。输出线则采用开漏输出,引脚下拉是“强下拉”,引脚上拉则处于“浮空状态”。于是所有设备都只能输出低电平,为了避免高电平造成的引脚浮空,就需要在总线外面设置上拉电阻(弱上拉)。采用这个模式的好处:
  1.     完全杜绝了电源短路的情况,保证了电路安全。
    2.避免了引脚模式的频繁切换。开漏+弱上拉的模式,同时兼具了输入和输出的功能。要想输出就直接操纵总线;要想输入就直接输出高电平,然后读取总线数据,无需专门将GPIO切换成输入模式。
    3.此模式可以实现“线与”功能。只有总线上所有设备都输出高电平,总线才是高电平;否则只要有一个设备输出低电平,总线就是低电平。I2C可以利用这个特征,执行多主机模式下的时钟同步(所以SCL也采用此模式)和总线仲裁。

下面介绍I2C通信的软件规定-----时序结构(一主多从) 

下面的这些操作的主语都是“主机”。

  • 起始条件:SCL高电平期间,SDA从高电平切换到低电平。
  • 终止条件:SCL高电平期间,SDA从低电平切换到高电平。
  • 发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。
  • 接收一个字节:主机在接收之前释放SDA,只控制SCL变化。SCL低电平期间,从机将数据位依次放到SDA线上(高位先行,且一般贴着SCL下降沿变化);SCL高电平期间,主机读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节。
  • 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。若从机没有收到主机的应答,就会完全释放SDA的控制权,回到待机模式。
    接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)。

 2: MPU6050简介

MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角(欧拉角),常应用于平衡车、飞行器等需要检测自身姿态的场景。以飞机为例,欧拉角就是飞机机身相对于初始3个轴的夹角——俯仰(Pitch)、滚转(Roll)、偏航(Yaw)。但是单一的加速度计、陀螺仪、磁力计都不能获得精确且稳定的欧拉角,只有综合多种传感器进行数据融合、取长补短,才能获得精确且稳定的欧拉角。常见的数据融合算法,一般有互补滤波、卡尔曼滤波等(惯性导航领域——姿态解算)。 下面给出MPU6050芯片的一些参数:
 

3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度。本质上就是弹簧测力计:F = m a F=maF=ma。测量的值是合加速度,由于系统运动时有运动加速度的影响,所以只有在静态时才表示系统的姿态。也就是说,加速度计静态稳定,而动态不稳定。
3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度。若想得到角度,只需要对角速度进行积分即可。陀螺仪的缺点是,物体静止时,会由于噪声导致角速度无法完全归零,于是就会导致积分得到的角度产生缓慢的漂移,积分时间越长漂移越明显,但当物体运动时就可以忽略漂移的大小。也就是说,陀螺仪动态稳定,而静态不稳定。
3轴磁场传感器(选修):测量X、Y、Z轴的磁场强度,某些芯片会有此功能。
气压传感器(选修):测量气压大小,一般气压值反映高度信息(海拔高度),某些芯片会有此功能。
注:于是姿态解算的大题思路就是:将“加速度计”、“陀螺仪”进行互补滤波,融合得到静态和动态都稳定的姿态角。
注:上图所示的机械结构只是便于理解,实际MPU6050芯片中不存在图示的机械结构。

16位ADC采集传感器的模拟信号,量化范围:-32768~32767(有符号数)。
加速度计满量程选择:±2、±4、±8、±16(g,1g=9.8m/s2)。
陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)。
可配置的数字低通滤波器,使输出数据更加平缓。
可配置的时钟源、可配置的采样分频。时钟源经过分频后,为内部的ADC提供转换时钟
I2C从机地址(手册可以查看到):MPU6050的设备号固定为0x68(某些批次可能为0x98),所以可以用于验证I2C读出协议是否正常。对于出厂地址为0x68的芯片来说,调整AD0引脚电压就可以改变其从机地址:1101000(AD0=0)、1101001(AD0=1)。

 

实验1:软件读写MPU6050

 

通过I2C通信协议,对MPU6050内部的寄存器进行读写。写入到配置寄存器,就可以对这个外挂模块进行配置。读出数据寄存器,就可以获取外挂模块的数据。最终将读出的数据显示在OLED屏幕上。要求显示:设备的ID号、加速度传感器的三个输出数据、陀螺仪传感器的三个输出数据(角速度)。

注:串口是异步通信,时序严格,所以一般不用软件模拟;I2C等同步通信,对时序要求不严格,可以很方便的进行软件模拟。
注:软件模拟时,SCL和SDA都是普通的GPIO口,所以可以随便接。
注:SCL和SDA都应该有一个上拉电阻,在模块的电路中已经设计了这个上拉电阻,所以可以跳线直连GPIO口。
 

 main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "MPU6050.h"int main(void){MPU6050_DataStruct SensorData;//存放6轴传感器数据OLED_Init();   //OLED初始化 MPU6050_Init();//MPU6050初始化//显示一些基本信息OLED_ShowString(1,1,"ID:");OLED_ShowHexNum(1,4,MPU6050_GetID(),2);
//    OLED_ShowString(1,1,"Acce:  | Gyro:");OLED_ShowString(2,1,"       |");OLED_ShowString(3,1,"       |");OLED_ShowString(4,1,"       |");//不断获取传感器值并显示while(1){MPU6050_GetData(&SensorData);//原始数值
//        OLED_ShowSignedNum(2,1,SensorData.Acce_X,5);
//        OLED_ShowSignedNum(3,1,SensorData.Acce_Y,5);
//        OLED_ShowSignedNum(4,1,SensorData.Acce_Z,5);
//        OLED_ShowSignedNum(2,9,SensorData.Gyro_X,5);
//        OLED_ShowSignedNum(3,9,SensorData.Gyro_Y,5);
//        OLED_ShowSignedNum(4,9,SensorData.Gyro_Z,5);//转换成小数的数值OLED_ShowFloat(2,1,(float)SensorData.Acce_X/32768*20,3,1);OLED_ShowFloat(3,1,(float)SensorData.Acce_Y/32768*20,3,1);OLED_ShowFloat(4,1,(float)SensorData.Acce_Z/32768*20,3,1);OLED_ShowFloat(2,9,(float)SensorData.Gyro_X/32768*500,3,1);OLED_ShowFloat(3,9,(float)SensorData.Gyro_Y/32768*500,3,1);OLED_ShowFloat(4,9,(float)SensorData.Gyro_Z/32768*500,3,1);};
}

I2C_User.h

#ifndef __I2C_USER_H
#define __I2C_USER_Hvoid  I2C_User_Init(void);
void I2C_User_Start(void);
void I2C_User_Stop(void);
void I2C_User_SendByte(uint8_t send_byte);
uint8_t I2C_User_RecvByte(void);
void I2C_User_SendAck(uint8_t AckBit);
uint8_t I2C_User_RecvAck(void);#endif

 I2C_User.c

//本文件 定义I2C的6个基本的时序单元,供其他模块调用
#include "stm32f10x.h"                  // Device header
#include "Delay.h"//引脚操作的封装和改名,方便移植——移植时仅需修改本部分即可
//
//定义I2C通信的两个引脚-PB10为SCL、PB11为SDA
#define I2C_User_SCL_Port GPIOB
#define I2C_User_SDA_Port GPIOB
#define I2C_User_SCL GPIO_Pin_10
#define I2C_User_SDA GPIO_Pin_11
//#define I2C_User_SCL_High GPIO_SetBits  (I2C_User_SCL_Port, I2C_User_SCL)
//#define I2C_User_SCL_Low  GPIO_ResetBits(I2C_User_SCL_Port, I2C_User_SCL)
//#define I2C_User_SDA_High GPIO_SetBits  (I2C_User_SDA_Port, I2C_User_SDA)
//#define I2C_User_SDA_Low  GPIO_ResetBits(I2C_User_SDA_Port, I2C_User_SDA)
//写SCL的操作
void I2C_User_W_SCL(uint8_t BitValue){GPIO_WriteBit(I2C_User_SCL_Port,I2C_User_SCL,(BitAction)BitValue);Delay_us(3);// 延迟一位(I2C最大通信速率400kHz-2.5us)
}//写SDA的操作
void I2C_User_W_SDA(uint8_t BitValue){GPIO_WriteBit(I2C_User_SDA_Port,I2C_User_SDA,(BitAction)BitValue);Delay_us(3);// 延迟一位(I2C最大通信速率400kHz-2.5us)
}//读SDA的操作
uint8_t I2C_User_R_SDA(void){uint8_t BitValue=0;BitValue = GPIO_ReadInputDataBit(I2C_User_SDA_Port,I2C_User_SDA);Delay_us(3);// 延迟一位(I2C最大通信速率400kHz-2.5us)return BitValue;
}//初始化两个GPIO口
void  I2C_User_Init(void){// 开启APB2-GPIOB的外设时钟RCCRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);// 初始化PA的输出端口:定义结构体及参数GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = I2C_User_SCL | I2C_User_SDA;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出GPIO_Init(GPIOB, &GPIO_InitStructure);// 默认输出为高电平-释放总线GPIO_SetBits(GPIOB, I2C_User_SCL | I2C_User_SDA);
}
//// 下面是I2C的六个基本时序-移植时无需修改
// 注:除了终止条件,SCL以高电平结束;其他时序都以SCL低电平结束
//
//1. 发送起始位
void I2C_User_Start(void){// 调整SCL、SDA输出均为高电平-保险起见,先将SDA拉高I2C_User_W_SDA(1);I2C_User_W_SCL(1);// SDA输出下降沿I2C_User_W_SDA(0);// 拉低SCLI2C_User_W_SCL(0);
}//2. 发送终止位
void I2C_User_Stop(void){// 调整SCL、SDA输出均为低电平I2C_User_W_SCL(0);I2C_User_W_SDA(0);// 拉高SCLI2C_User_W_SCL(1);// 拉高SDAI2C_User_W_SDA(1);  
}//3. 发送一个字节
void I2C_User_SendByte(uint8_t send_byte){uint8_t i=0;for(i=0;i<8;i++){//改变SDAI2C_User_W_SDA((0x80>>i) & send_byte);//拉高SCLI2C_User_W_SCL(1);//拉低SCLI2C_User_W_SCL(0);}
}//4. 接收一个字节
uint8_t I2C_User_RecvByte(void){uint8_t recv_byte=0x00;uint8_t i=0;//主机释放总线I2C_User_W_SDA(1);//接收数据for(i=0;i<8;i++){// 拉高SCLI2C_User_W_SCL(1);// 读取数据if(I2C_User_R_SDA()==1){recv_byte |= (0x80>>i);}// 拉低SCLI2C_User_W_SCL(0);}return recv_byte;
}//5. 发送应答位
void I2C_User_SendAck(uint8_t AckBit){// 将应答位放在SDA上I2C_User_W_SDA(AckBit);// 拉高SCLI2C_User_W_SCL(1);// 拉低SCLI2C_User_W_SCL(0);
}//6. 接收应答位
uint8_t I2C_User_RecvAck(void){uint8_t AckBit=0;//主机释放总线I2C_User_W_SDA(1);// 拉高SCLI2C_User_W_SCL(1);// 读取应答信号AckBit = I2C_User_R_SDA();// 拉低SCLI2C_User_W_SCL(0);return AckBit;
}
//

  MPU6050.h

#ifndef __MPU6050_H
#define __MPU6050_H//定义MPU6050的6轴数据结构体
typedef struct{int16_t Gyro_X;int16_t Gyro_Y;int16_t Gyro_Z;int16_t Acce_X;int16_t Acce_Y;int16_t Acce_Z;int16_t Temp;
}MPU6050_DataStruct;//外部可调用函数
void MPU6050_WriteReg(uint8_t RegAddr, uint8_t wData);
uint8_t MPU6050_ReadReg(uint8_t RegAddr);
void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(MPU6050_DataStruct* MPU6050_Data);#endif

MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "I2C_User.h"
#include "MPU6050.h"//定义从机地址、寄存器地址
#define MPU6050_ADDRESS           0xD0
#define MPU6050_REG_SMPLRT_DIV    0x19
#define MPU6050_REG_CONFIG        0x1A
#define MPU6050_REG_GYRO_CONFIG   0x1B
#define MPU6050_REG_ACCEL_CONFIG  0x1C#define MPU6050_REG_ACCEL_XOUT_H  0x3B
#define MPU6050_REG_ACCEL_XOUT_L  0x3C
#define MPU6050_REG_ACCEL_YOUT_H  0x3D
#define MPU6050_REG_ACCEL_YOUT_L  0x3E
#define MPU6050_REG_ACCEL_ZOUT_H  0x3F
#define MPU6050_REG_ACCEL_ZOUT_L  0x40
#define MPU6050_REG_TEMP_OUT_H    0x41
#define MPU6050_REG_TEMP_OUT_L    0x42
#define MPU6050_REG_GYRO_XOUT_H   0x43
#define MPU6050_REG_GYRO_XOUT_L   0x44
#define MPU6050_REG_GYRO_YOUT_H   0x45
#define MPU6050_REG_GYRO_YOUT_L   0x46
#define MPU6050_REG_GYRO_ZOUT_H   0x47
#define MPU6050_REG_GYRO_ZOUT_L   0x48#define MPU6050_REG_PWR_MGMT_1    0x6B
#define MPU6050_REG_PWR_MGMT_2    0x6C
#define MPU6050_REG_WHO_AM_I      0x75//MPU6050指定地址写寄存器
void MPU6050_WriteReg(uint8_t RegAddr, uint8_t wData){I2C_User_Start();I2C_User_SendByte(MPU6050_ADDRESS);I2C_User_RecvAck();//暂时不对应答位进行处理I2C_User_SendByte(RegAddr);I2C_User_RecvAck();I2C_User_SendByte(wData);I2C_User_RecvAck();I2C_User_Stop();
}//MPU6050指定地址读寄存器
uint8_t MPU6050_ReadReg(uint8_t RegAddr){uint8_t rData=0x00;I2C_User_Start();I2C_User_SendByte(MPU6050_ADDRESS);I2C_User_RecvAck();//暂时不对应答位进行处理I2C_User_SendByte(RegAddr);I2C_User_RecvAck();I2C_User_Start();I2C_User_SendByte(MPU6050_ADDRESS | 0x01);I2C_User_RecvAck();//暂时不对应答位进行处理rData = I2C_User_RecvByte();I2C_User_SendAck(1);I2C_User_Stop();return rData;
}//MPU6050初始化
void MPU6050_Init(void){//I2C初始化I2C_User_Init();//不复位、解除睡眠、不开启循环模式、温度传感器失能、选择陀螺仪x轴时钟MPU6050_WriteReg(MPU6050_REG_PWR_MGMT_1,0x01);//没有开启循环模式MPU6050_WriteReg(MPU6050_REG_PWR_MGMT_2,0x00);//采样率10分频MPU6050_WriteReg(MPU6050_REG_SMPLRT_DIV,0x09);//不使用外部同步、DLPF设置等级6MPU6050_WriteReg(MPU6050_REG_CONFIG,0x06);//陀螺仪:自测失能、满量程±500°/s-000_01_000MPU6050_WriteReg(MPU6050_REG_GYRO_CONFIG,0x08);//加速度计:自测失能、满量程±2g、失能运动检测-000_00_000MPU6050_WriteReg(MPU6050_REG_ACCEL_CONFIG,0x00);
}//获取MPU6050的ID号
uint8_t MPU6050_GetID(void){return MPU6050_ReadReg(MPU6050_REG_WHO_AM_I);
}//获取MPU6050的传感器数据
void MPU6050_GetData(MPU6050_DataStruct* MPU6050_Data){uint16_t sensor_byte_L, sensor_byte_H;//获取加速度计数据sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_ACCEL_XOUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_ACCEL_XOUT_L);MPU6050_Data->Acce_X = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_ACCEL_YOUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_ACCEL_YOUT_L);MPU6050_Data->Acce_Y = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_ACCEL_ZOUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_ACCEL_ZOUT_L);MPU6050_Data->Acce_Z = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);//获取陀螺仪数据sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_GYRO_XOUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_GYRO_XOUT_L);MPU6050_Data->Gyro_X = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_GYRO_YOUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_GYRO_YOUT_L);MPU6050_Data->Gyro_Y = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_GYRO_ZOUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_GYRO_ZOUT_L);MPU6050_Data->Gyro_Z = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);//获取温度传感器数据sensor_byte_H = MPU6050_ReadReg(MPU6050_REG_TEMP_OUT_H);sensor_byte_L = MPU6050_ReadReg(MPU6050_REG_TEMP_OUT_L);MPU6050_Data->Temp = (int16_t)((sensor_byte_H<<8) | sensor_byte_L);
}

IIC外设简介

总的来说,由于串口是异步时序,对时序要求极其严格,所以几乎不会软件模拟串口,基本上一边倒的采用硬件实现串口通信。而对于I2C通信来说,尽管用硬件电路可以减轻CPU负担,但是硬件I2C总线数量有限、引脚固定等,也有很多限制;而由于I2C是同步时序,软件模拟I2C足够简单且灵活,所以还是有许多场合都使用软件I2C进行通信。若只是简单应用I2C读写数据,使用软件模拟更加灵活;若对时序要求高,可以使用硬件I2C。本节介绍硬件实现I2C的原理。

STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担,还兼具可以实现完整的多主机通信、时序波形规整、通信速率快等优点,功能非常强大。具体参数如下:

  • 支持多主机模型(固定多主机/可变多主机)。stm32是按照“可变多主机”的模式设置硬件电路的,也就是说,需要自己声明自己是主机。
  • 支持7位/10位地址模式。10位地址发送两字节寻址,高5位固定为11110(这种开头不会在7位地址中出现),低10位作为地址(第一个字节的最低位还是R/W)。
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)。只要不超过最大频率,多少都可以。
  • 支持DMA。多字节传输时可提高传输效率,如指定地址 读/写 多字节。若只有几个字节也没必要配置DMA。
  • 兼容SMBus(System Management Bus)协议。SMBus是基于I2C总线改进而来的,主要用于电源管理系统中。本节用不到。
  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2。
     

 

SDA部分:

  • 发送数据过程:首先将数据写入“数据寄存器DR”,当没有移位时,DR中的数据就被转运到“数据移位寄存器”中,并同时置状态寄存器的TXE位为1(发送寄存器空)。此时就可以继续向DR中写入数据。
  • 接收数据流程:将数据一位一位的写入“数据移位寄存器”,当一个字节的数据收齐后,将会将数据整体从“数据移位寄存器”转运到“数据寄存器DR”,并同时置标志位RXNE(接收寄存器非空)。此时就可以将数据从DR中读出。
  • 比较器和地址寄存器(用不到):从机模式使用。由于stm32的I2C是基于可变多主机模型设计的,不进行通信时默认为“从机”,于是“自身地址寄存器”就用于存放从机地址,可以由用户指定。“双地址寄存器”存储的也是从机地址,于是stm32就可以同时响应两个从机地址。
  • 数据校验模块(用不到):当发送一个多字节的数据帧时,“帧错误校验计算”可以硬件自动执行CRC校验计算,得到一个字节的校验位附加在数据帧的最后。同样的,当接收的校验字节和CRC计算结果不匹配时,就会置校验错误标志位。
  • 评价:整个数据收发流程类似于“串口通信”,只不过串口是全双工通信,收发电路独立;I2C是半双工通信,收发共用一个电路。
  1. SCL部分:
  • 时钟控制:控制SCL线,具体细节无需了解。
  • 时钟控制寄存器CCR:控制“时钟控制”电路执行相应的功能。
  • 控制逻辑电路:写入“控制寄存器”,可以对整个电路进行控制;读取“状态寄存器”,可以得知整个电路的工作状态。
  • 中断:内部某些事情比较紧急时,可以申请中断。
  • DMA请求与响应:在进行很多字节的收发时,可以配合DMA来提高效率。

 

初始化I2C外设时注意四部分:

  • RCC时钟:现实需要初始化GPIO、I2C两个外设的时钟。
  • GPIO外设:都要配置成复用开漏输出模式。“复用”是交给片上外设来控制,“开漏输出”是I2C协议要求的端口配置。
  • I2C外设:老套路了,先定义结构体,再调用一个I2C_Init即可。
  • 开关控制:使能I2C外设。
  • 注:上图简化成“一主多从模型”,所以SCL只有时钟输出。“多主机模型”时,SCL也会有输入。

 

相关文章:

10:IIC通信

1&#xff1a;IIC通信 I2C总线&#xff08;Inter IC BUS&#xff09; 是由Philips公司开发的一种通用数据总线&#xff0c;应用广泛&#xff0c;下面是一些指标参数&#xff1a; 两根通信线&#xff1a;SCL&#xff08;Serial Clock&#xff0c;串行时钟线&#xff09;、SDA&a…...

互联网上门洗衣洗鞋小程序优势有哪些?

互联网洗鞋店小程序相较于传统洗鞋方式&#xff0c;具有以下优势&#xff1b; 1. 便捷性&#xff1a;用户只需通过手机即可随时随地下单并查询&#xff0c;省去了许多不必要的时间和精力。学生们无需走出宿舍或校园&#xff0c;就能轻松预约洗鞋并取件。 2. 精准定位&#xff1…...

Java中如何优雅地根治null值引起的Bug问题

1. Java对象为null会引发的问题 NullPointerException&#xff1a;当你尝试调用或访问一个null对象的属性或方法时&#xff0c;Java会抛出NullPointerException异常。例如&#xff0c;如果你有一个名为person的变量&#xff0c;它被设置为null&#xff0c;然后你尝试调用perso…...

C# WPF上位机开发(子窗口通知父窗口更新进度)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 这两天在编写代码的时候&#xff0c;正好遇到一个棘手的问题&#xff0c;解决之后感觉挺有意义的&#xff0c;所以先用blog记录一下&#xff0c;后…...

XUbuntu22.04之跨平台容器格式工具:MKVToolNix(二百零三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…...

vue中的生命周期和VueComponent实例对象

生命周期 生命周期又叫生命周期钩子&#xff0c;生命周期函数 生命周期是&#xff0c;Vue在关键的时刻帮我们调用的一些特殊名字的函数 生命周期的this指向vm或者组件实例对象 mounted会将初始化的Dom挂载到页面上 <template><div class"hello"><…...

Hooked协议掀起WEB3新浪潮

随着区块链技术和加密货币的兴起&#xff0c;币圈已经成为全球范围内的一个热门领域。在这个充满机遇与挑战的行业中&#xff0c;Hook机制正逐渐成为一种重要的技术手段&#xff0c;为投资者、开发者以及相关机构提供了更多的选择和可能性。本文将详细介绍币圈中的Hook机制&…...

【图文教程】windows 下 MongoDB 介绍下载安装配置

文章目录 介绍MySQL 之间的区别和适用场景差异数据模型&#xff1a;查询语言&#xff1a;可扩展性&#xff1a;数据一致性&#xff1a; 下载安装环境变量配置 介绍 MongoDB 是一种开源的、面向文档的 NoSQL 数据库管理系统。它使用灵活的文档模型来存储数据&#xff0c;这意味…...

算法复杂度-BigO表示法

1.时间复杂度--大O表示法 算法的渐进时间复杂度&#xff0c;T(n)O(f(n)) T&#xff08;n&#xff09;表示算法的渐进时间复杂度 f&#xff08;n&#xff09;表示代码执行的次数 O&#xff08;&#xff09;表示正比例关系 2.常用的时间复杂度量级 3.举例 &#xff08;1&am…...

测试理论知识五:功能测试、系统测试、验收测试、安装测试、测试的计划与控制

模块测试的目的是发现程序模块与其接口规格说明之间的不一致。 功能测试的目的是为了证明程序未能符合其外部规格说明。 系统测试的目的是为了证明软件产品与其初始目标不一致。 1. 功能测试 功能测试是一个试图发现程序与其外部规格说明之间存在不一致的过程。功能测试通…...

太阳能爆闪警示灯

适用场所&#xff1a; 适用于高压线,塔吊,路政,船舶,种植,塔机,航海航道等场所起警示作用。 产品特点&#xff1a; 光控无开关&#xff0c;白天不闪&#xff0c;昏暗环境自动闪烁&#xff0c;无需手动操作&#xff0c;省时省事; 采用红色LED作光源&#xff0c;亮度高&#…...

怎么为pdf文件添加水印?

怎么为pdf文件添加水印&#xff1f;PDF是一种很好用的文件格式&#xff0c;这种格式能够很有效的保护我们的文件&#xff0c;但有时可能还会被破解&#xff0c;这种时候在PDF上添加水印就是比较好的方法。 综上所述&#xff0c;PDF是保密性很强的文件&#xff0c;但添加水印能够…...

基于ssm医药信息管理系统论文

基于SSM的医药信息管理系统的设计与实现 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前相关行业对于医药信息的管理和控制&#xff0c;采用人工登记的方式保存相关数据&#xff…...

Ceph存储体系架构?

Ceph体系架构主要由RADOS和RADOS GW和RBD以及CephFS构成。 RADOS&#xff08;Reliable, Autonomic Distributed Object Store&#xff09;是Ceph的底层核心&#xff0c;RADOS本身也是分布式存储系统&#xff0c;CEPH所有的存储功能都是基于RADOS实现。RADOS由两个组件组成&…...

详解现实世界资产(RWAs)

区块链中的现实世界资产&#xff08;RWAs&#xff09;是代表实际和传统金融资产的数字通证&#xff0c;如货币、大宗商品、股票和债券。 实际世界资产&#xff08;RWA&#xff09;的通证化是区块链行业中最大的市场机会之一&#xff0c;潜在市场规模可达数万万亿美元。理论上&…...

Windows漏洞利用开发——利用ROP绕过DEP保护

实验6 Windows漏洞利用开发 6.1实验名称 Windows漏洞利用开发 6.2实验目的 学习windows漏洞利用开发&#xff0c;使用kali linux相关工具对windows内目标程序进行漏洞利用 6.3实验步骤及内容 第三阶段&#xff1a;利用ROP绕过DEP保护 了解DEP保护理解构造ROP链从而绕过DEP…...

合并两个有序链表算法(leetcode第21题)

题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a;输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a;输入&#xff1a;l1 [], l2 [] 输…...

二维码初体验 com.google.zxing 实现续 - web api封装

文章目录 一、概述二、最终效果三、源码结构四、完整代码 一、概述 在 二维码初体验 com.google.zxing 实现 我们实现了二维码的生成&#xff0c;但是大部分情况下&#xff0c;二维码的相关功能是作为API接口来提供服务的。 我们下面便演示在springboot、Knife4j下封装api接口…...

Hadoop入门学习笔记——四、MapReduce的框架配置和YARN的部署

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 四、MapReduce的框架配置和YARN的部署4.1. 配置MapReduce…...

list集合

List集合 List集合的概述 有序集合&#xff08;也称之为序列&#xff09;&#xff0c;用户可以精确的控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素&#xff0c;并搜索列表中的元素 与 Set 集合不同&#xff0c;列表通常允许重复的元素 List 集合的特点 有…...

Vue3学习(后端开发)

目录 一、安装Node.js 二、创建Vue3工程 三、用VSCode打开 四、源代码目录src 五、入门案例——手写src 六、测试案例 七、ref和reactive的区别 一、安装Node.js 下载20.10.0 LTS版本 https://nodejs.org/en 使用node命令检验安装是否成功 node 二、创建Vue3工程 在…...

爬虫字典生成工具,CeWL使用教程

爬虫字典生成工具,CeWL使用教程 1.工具概述2.参数解析3.使用实例1.工具概述 CeWL 是一个 ruby 应用程序,它将给定的 URL 爬到指定的深度,可以选择跟随外部链接,并返回一个单词列表,然后可用于密码破解者 Cewl 是黑客武器库中的强大工具,因为它允许创建有针对性的单词列…...

消息队列之关于如何实现延时队列

一、延时队列的应用 1.1 什么是延时队列&#xff1f; 顾名思义&#xff1a;首先它要具有队列的特性&#xff0c;再给它附加一个延迟消费队列消息的功能&#xff0c;也就是说可以指定队列中的消息在哪个时间点被消费。 延时队列在项目中的应用还是比较多的&#xff0c;尤其像…...

Linux Shell 002-基础知识

Linux Shell 002-基础知识 本节关键字&#xff1a;Linux、Bash Shell、基础知识、Bash特性 相关指令&#xff1a;bash、rm、cp、touch、date 基础知识 什么是Shell脚本 简单概括&#xff1a;将需要执行的命令保存到文本中&#xff0c;按照顺序执行。 准备描述&#xff1a;sh…...

前缀和+单调双队列+贪心:LeetCode2945:找到最大非递减数组的长度

本文涉及知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 单调双队列 贪心 题目 给你一个下标从 0 开始的整数数组 nums 。 你可以执行任意次操作。每次操作中&#xff0c;你需要选择一个 子数组 &#xff0c;并将这个子数组用它所…...

【微服务】springboot整合kafka-stream使用详解

目录 一、前言 二、kafka stream概述 2.1 什么是kafka stream 2.2 为什么需要kafka stream 2.2.1 对接成本低 2.2.2 节省资源 2.2.3 使用简单 2.3 kafka stream特点 2.4 kafka stream中的一些概念 2.5 Kafka Stream应用场景 三、环境准备 3.1 搭建zk 3.1.1 自定义d…...

什么是动态代理?

目录 一、为什么需要代理&#xff1f; 二、代理长什么样&#xff1f; 三、Java通过什么来保证代理的样子&#xff1f; 四、动态代理实现案例 五、动态代理在SpringBoot中的应用 导入依赖 数据库表设计 OperateLogEntity实体类 OperateLog枚举 RecordLog注解 上下文相…...

【OAuth2】:赋予用户控制权的安全通行证--原理篇

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于OAuth2的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是OAuth? 二.为什么要用OAuth?…...

【K8s】2# 使用kuboard管理K8s集群(kuboard安装)

文章目录 安装 Kuboard v3部署计划 安装登录测试 安装 Kuboard v3 部署计划 在正式安装 kuboard v3 之前&#xff0c;需做好一个简单的部署计划的设计&#xff0c;在本例中&#xff0c;各组件之间的连接方式&#xff0c;如下图所示&#xff1a; 假设用户通过 http://外网IP:80…...

爬虫是什么?起什么作用?

【爬虫】 如果把互联网比作一张大的蜘蛛网&#xff0c;数据便是放于蜘蛛网的各个节点&#xff0c;而爬虫就是一只小蜘蛛&#xff0c;沿着网络抓取自己得猎物&#xff08;数据&#xff09;。这种解释可能更容易理解&#xff0c;官网的&#xff0c;就是下面这个。 爬虫是一种自动…...

代码随想录27期|Python|Day24|回溯法|理论基础|77.组合

图片来自代码随想录 回溯法题目目录 理论基础 定义 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯是递归的副产品&#xff0c;只要有递归就会有回溯。回溯函数也就是递归函数&#xff0c;指的都是一个函数。 基本问题 组合问题&#xff08;无序&…...

mysql(49) : 大数据按分区导出数据

代码 import com.alibaba.gts.flm.base.util.Mysql8Instance;import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.u…...

阿里云ECS配置IPv6后,如果无法访问该服务器上的网站,可检查如下配置

1、域名解析到这个IPv6地址,同一个子域名可以同时解析到IPv4和IPv6两个地址&#xff0c;这样就可以给网站配置ip4和ipv6双栈&#xff1b; 2、在安全组规则开通端口可访问&#xff0c;设定端口后注意授权对象要特殊设置“源:::/0” 3、到服务器nginx配置处&#xff0c;增加端口…...

基于SSM的双减后初小教育课外学习生活活动平台的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

HTTP前端请求

目录 HTTP 请求1.请求组成2.请求方式与数据格式get 请求示例post 请求示例json 请求示例multipart 请求示例数据格式小结 3.表单3.1.作用与语法3.2.常见的表单项 4.session 原理5.jwt 原理 HTTP 请求 1.请求组成 请求由三部分组成 请求行请求头请求体 可以用 telnet 程序测…...

前端性能优化二十四:花裤衩模板第三方库打包

(1). 工作原理: ①. externals配置在所创建bundle时:a. 会依赖于用户环境(consumers environment)中的依赖,防止将某些import的包(package)打包到bundle中b. 在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)②. webpack会检测这些组件是否在externals中注…...

多维时序 | MATLAB实现BiTCN-Multihead-Attention多头注意力机制多变量时间序列预测

多维时序 | MATLAB实现BiTCN-Multihead-Attention多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现BiTCN-Multihead-Attention多头注意力机制多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现BiTCN-Multihea…...

Qt的简单游戏实现提供完整代码

文章目录 1 项目简介2 项目基本配置2.1 创建项目2.2 添加资源 3 主场景3.1 设置游戏主场景配置3.2 设置背景图片3.3 创建开始按钮3.4 开始按钮跳跃特效实现3.5 创建选择关卡场景3.6 点击开始按钮进入选择关卡场景 4 选择关卡场景4.1场景基本设置4.2 背景设置4.3 创建返回按钮4.…...

SpringMVC之文件的下载

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 SpringMVC之文件的下载 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、文件下载实现…...

计算机组成原理第6章-(算术运算)【下】

移位运算 对于有符号数的移位称为算术移位,对于无符号数的移位称为逻辑移位。 算术移位规则【极其重要】 对于正数的算术移位,且不管是何种机器数【原码、反码、补码】,移位后出现的空位全部填0。 而对于负数的算术移位,机器数不同,移位后的规则也不同。 对于负数的原…...

【开题报告】基于微信小程序的校园资讯平台的设计与实现

1.选题背景与意义 随着移动互联网的快速发展&#xff0c;微信成为了人们日常生活中不可或缺的工具之一。在校园生活中&#xff0c;学生们对于校园资讯的获取和交流需求也越来越高。然而&#xff0c;传统的校园资讯发布方式存在信息不及时、传播范围有限等问题&#xff0c;无法…...

VUE前端导出文件之file-saver插件

VUE前端导出文件之file-saver插件 安装 npm install file-saver --save # 如使用TS开发&#xff0c;可安装file-saver的TypeScript类型定义 npm install types/file-saver --save-dev如果需要保存大于 blob 大小限制的非常大的文件&#xff0c;或者没有 足够的 RAM&#xff0…...

【Earth Engine】协同Sentinel-1/2使用随机森林回归实现高分辨率相对财富(贫困)制图

目录 1 简介与摘要2 思路3 效果预览4 代码思路5 完整代码6 后记 1 简介与摘要 最近在做一些课题&#xff0c;需要使用Sentinel-1/2进行机器学习制图。 然后想着总结一下相关数据和方法&#xff0c;就花半小时写了个代码。 然后再花半小时写下这篇博客记录一下。 因为基于多次拍…...

C++ 检测 是不是 com组件 的办法 已解决

在日常开发中&#xff0c;遇到动态库和 com组件库的调用 无法区分。检测是否com组件的办法 在头部文件&#xff0c;引入文件 如果能编译成功说明是 com组件&#xff0c;至于动态库如何引入&#xff0c;还在观察中 最简单办法 regsvr32 TerraExplorerX.dll 是com 组件 regs…...

linux buffer的回写的触发链路

mark_buffer_dirty中除了会标记dirty到buffer_head->state、page.flag、folio->mapping->i_pages外&#xff0c;还会调用inode所在文件系统的dirty方法&#xff08;inode->i_sb->s_op->dirty_inode&#xff09;。然后为inode创建一个它所在memory group的wri…...

Lambda表达式超详解

目录 背景 Lambda表达式的用法 函数式接口 Lambda表达式的基本使用 语法精简 变量捕获 匿名内部类 匿名内部类中的变量捕获 Lambda的变量捕获 Lambda表达式在类集中的使用 Collection接口 List接口 Map接口 总结 背景 Lambda表达式是Java SE 8中的一个重要的新特性.…...

西门子博途与菲尼克斯无线蓝牙模块通讯

菲尼克斯无线蓝牙模块 正常运行时,可以使用基站控制字0发送00E0(得到错误代码命令) 正常运行时,可以使用基站控制字0发送00E0(得到错误代码命令)得到各个无线I/O是否连 接的信号(状态字IN word 1的第2、6、10位) 小车1连接状态 小车2连接状态 小车3连接状态 1#小车自…...

vue2 之 实现pdf电子签章

一、前情提要 1. 需求 仿照e签宝&#xff0c;实现pdf电子签章 > 拿到pdf链接&#xff0c;移动章的位置&#xff0c;获取章的坐标 技术 : 使用fabric pdfjs-dist vuedraggable 2. 借鉴 一位大佬的代码仓亏 : 地址 一位大佬写的文章 &#xff1a;地址 3. 优化 在大佬的代码…...

什么是MVC?MVC框架的优势和特点

目录 一、什么是MVC 二、MVC模式的组成部分和工作原理 1、模型&#xff08;Model&#xff09; 2、视图&#xff08;View&#xff09; 3、控制器&#xff08;Controller&#xff09; 三、MVC模式的工作过程如下&#xff1a; 用户发送请求&#xff0c;请求由控制器处理。 …...

主从复制mysql-replication | Replication故障排除

主从复制mysql-replication 准备环境 #防火墙 selinux systemctl stop firewalld --now &&setenforce 0 #修改主机名&#xff1a;hostnamectl set-hostname 名字 tip&#xff1a;vim /etc/sysconfig/network-scripts/ifcfg-ens33 BOOTPRTOTstatic IPADDR192.168.100.…...