音乐网站制作课程报告,下载 app,网站备案时 首页,网站文字变白色代码怎么做1#xff1a;IIC通信 I2C总线#xff08;Inter IC BUS#xff09; 是由Philips公司开发的一种通用数据总线#xff0c;应用广泛#xff0c;下面是一些指标参数#xff1a; 两根通信线#xff1a;SCL#xff08;Serial Clock#xff0c;串行时钟线#xff09;、SDAIIC通信 I2C总线Inter IC BUS 是由Philips公司开发的一种通用数据总线应用广泛下面是一些指标参数 两根通信线SCLSerial Clock串行时钟线、SDASerial Data串行数据线。同步半双工。主从机通信过程中会带数据应答。支持总线挂载多设备一主多从、多主多从。“多主多从”模式更复杂下面没有特殊说明默认都是“一主多从”模式。高位先行。 那同步时序和异步时序的优缺点各是什么呢 同步时序优点是对时间要求不严格可以软件手动翻转电平实现通信所以可以大幅降低单片机对外围硬件电路的依赖在一些低端单片机没有硬件资源的情况下很容易使用软件来模拟时序缺点就是多一个时钟线。异步时序优点是少一根时钟线节省资源缺点是对时间要求严格对硬件电路的依赖严重。 一个通信协议就必须在硬件和软件上都做出规定 硬件上规定电路如何连接、端口的输入输出模式等软件上规定通信时序、字节如何传输、高位/低位先行等 硬件电路规定所有I2C设备的SCL连在一起SDA连在一起。设备的SCL和SDA均要配置成 开漏输出模式。SCL和SDA各添加一个 上拉电阻阻值一般为4.7KΩ左右。 总结为防止总线没协调好导致电源短路采用 外置弱上拉电阻开漏输出 的电路结构。 CPU主机对总线控制权力大。任何时候都是主机对SCL完全控制。空闲状态下主机可以主动发起对SDA的控制。被控ICx从机对总线控制权力小。任何时刻从机都只能被动读取SCL线。只有在从机发送数据、或者从机发送应答位时从机才短暂的具有对SDA的控制权。从机设备地址为了保证通信正常每个从机设备都具有一个唯一的地址。在I2C协议标准中从机设备地址分为7位地址、10位地址其中7位较为简单且应用更广泛。不同的I2C模块出厂时厂商都会为其分配唯一的7位地址具体可以在芯片手册中查询如MPU6050的地址是110_1000AT24C02的地址是101_0000等其中器件地址的最后几位是可以在电路中改变的。总线上都是不同模块一般不会有地址冲突若总线上有相同的模块就需要外界电路来相应的器件地址。通信引脚结构输入线都很正常。输出线则采用开漏输出引脚下拉是“强下拉”引脚上拉则处于“浮空状态”。于是所有设备都只能输出低电平为了避免高电平造成的引脚浮空就需要在总线外面设置上拉电阻弱上拉。采用这个模式的好处 完全杜绝了电源短路的情况保证了电路安全。 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 FmaFma。测量的值是合加速度由于系统运动时有运动加速度的影响所以只有在静态时才表示系统的姿态。也就是说加速度计静态稳定而动态不稳定。 3轴陀螺仪传感器Gyroscope测量X、Y、Z轴的角速度。若想得到角度只需要对角速度进行积分即可。陀螺仪的缺点是物体静止时会由于噪声导致角速度无法完全归零于是就会导致积分得到的角度产生缓慢的漂移积分时间越长漂移越明显但当物体运动时就可以忽略漂移的大小。也就是说陀螺仪动态稳定而静态不稳定。 3轴磁场传感器选修测量X、Y、Z轴的磁场强度某些芯片会有此功能。 气压传感器选修测量气压大小一般气压值反映高度信息海拔高度某些芯片会有此功能。 注于是姿态解算的大题思路就是将“加速度计”、“陀螺仪”进行互补滤波融合得到静态和动态都稳定的姿态角。 注上图所示的机械结构只是便于理解实际MPU6050芯片中不存在图示的机械结构。 16位ADC采集传感器的模拟信号量化范围-32768~32767有符号数。 加速度计满量程选择±2、±4、±8、±16g1g9.8m/s2。 陀螺仪满量程选择 ±250、±500、±1000、±2000°/sec。 可配置的数字低通滤波器使输出数据更加平缓。 可配置的时钟源、可配置的采样分频。时钟源经过分频后为内部的ADC提供转换时钟 I2C从机地址手册可以查看到MPU6050的设备号固定为0x68某些批次可能为0x98所以可以用于验证I2C读出协议是否正常。对于出厂地址为0x68的芯片来说调整AD0引脚电压就可以改变其从机地址1101000AD00、1101001AD01。 实验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.hint 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 BitValue0;BitValue GPIO_ReadInputDataBit(I2C_User_SDA_Port,I2C_User_SDA);Delay_us(3);// 延迟一位I2C最大通信速率400kHz-2.5usreturn 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 i0;for(i0;i8;i){//改变SDAI2C_User_W_SDA((0x80i) send_byte);//拉高SCLI2C_User_W_SCL(1);//拉低SCLI2C_User_W_SCL(0);}
}//4. 接收一个字节
uint8_t I2C_User_RecvByte(void){uint8_t recv_byte0x00;uint8_t i0;//主机释放总线I2C_User_W_SDA(1);//接收数据for(i0;i8;i){// 拉高SCLI2C_User_W_SCL(1);// 读取数据if(I2C_User_R_SDA()1){recv_byte | (0x80i);}// 拉低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 AckBit0;//主机释放总线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 rData0x00;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_H8) | 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_H8) | 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_H8) | 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_H8) | 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_H8) | 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_H8) | 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_H8) | 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。兼容SMBusSystem Management Bus协议。SMBus是基于I2C总线改进而来的主要用于电源管理系统中。本节用不到。STM32F103C8T6 硬件I2C资源I2C1、I2C2。 SDA部分 发送数据过程首先将数据写入“数据寄存器DR”当没有移位时DR中的数据就被转运到“数据移位寄存器”中并同时置状态寄存器的TXE位为1发送寄存器空。此时就可以继续向DR中写入数据。接收数据流程将数据一位一位的写入“数据移位寄存器”当一个字节的数据收齐后将会将数据整体从“数据移位寄存器”转运到“数据寄存器DR”并同时置标志位RXNE接收寄存器非空。此时就可以将数据从DR中读出。比较器和地址寄存器用不到从机模式使用。由于stm32的I2C是基于可变多主机模型设计的不进行通信时默认为“从机”于是“自身地址寄存器”就用于存放从机地址可以由用户指定。“双地址寄存器”存储的也是从机地址于是stm32就可以同时响应两个从机地址。数据校验模块用不到当发送一个多字节的数据帧时“帧错误校验计算”可以硬件自动执行CRC校验计算得到一个字节的校验位附加在数据帧的最后。同样的当接收的校验字节和CRC计算结果不匹配时就会置校验错误标志位。评价整个数据收发流程类似于“串口通信”只不过串口是全双工通信收发电路独立I2C是半双工通信收发共用一个电路。 SCL部分 时钟控制控制SCL线具体细节无需了解。时钟控制寄存器CCR控制“时钟控制”电路执行相应的功能。控制逻辑电路写入“控制寄存器”可以对整个电路进行控制读取“状态寄存器”可以得知整个电路的工作状态。中断内部某些事情比较紧急时可以申请中断。DMA请求与响应在进行很多字节的收发时可以配合DMA来提高效率。 初始化I2C外设时注意四部分 RCC时钟现实需要初始化GPIO、I2C两个外设的时钟。GPIO外设都要配置成复用开漏输出模式。“复用”是交给片上外设来控制“开漏输出”是I2C协议要求的端口配置。I2C外设老套路了先定义结构体再调用一个I2C_Init即可。开关控制使能I2C外设。注上图简化成“一主多从模型”所以SCL只有时钟输出。“多主机模型”时SCL也会有输入。