STM32--MPU6050与I2C外设

news2024/11/23 15:45:18

文章目录

  • 前言
  • MPU6050
    • 参数
    • 电路
    • MPU6050框图
  • IIC外设
    • 框图
  • IIC的基本结构
  • 软件IIC实现MPU6050
  • 硬件IIC实现MPU6050

前言

在51单片机专栏中,用过I2C通信来进行实现AT24C02的数据存储;
里面介绍的是利用程序的编程来实现I2C的时序,进而实现AT24C02与单片机之间的关系连接;
本章将介绍使用I2C的硬件外设来实现I2C通信,和介绍MPU6050,利用I2C通信实现STM32对MPU6050的控制.

I2C通信软件实现程序链接入口

MPU6050

MPU6050是一种集成三轴陀螺仪和三轴加速度计的六轴运动处理组件。
可以用来感知物体的旋转和加速度运动,并提供相应的测量数据

在这里插入图片描述

MPU6050采用微机电系统(MEMS)技术,通过测量微小的力和振动来检测物体的运动。其内置的三轴陀螺仪可以测量绕X、Y、Z轴的角速度,而三轴加速度计可以测量物体在X、Y、Z轴上的加速度。通过结合两者的数据,可以获得更准确的运动信息。

MPU6050还具有一个可扩展的数字运动处理器(DMP),可以实现更复杂的运动处理功能。DMP可以通过主要的I2C或SPI端口输出完整的九轴运动融合数据,当连接到三轴磁强计时,可以获得更全面的运动信息。

MPU6050广泛应用于飞行器、机器人、游戏控制器等领域,可以提供准确的姿态感知和运动跟踪功能。它的集成设计减少了封装空间和组合陀螺仪与加速度计时间轴不匹配的问题,使其在各种应用中具有较高的可靠性和性能。

参数

角速度全格感测范围:±250、±500、±1000、±2000°/sec(dps)
可追踪快速和慢速动作

加速度全格感测范围:±2g、±4g、±8g、±16g

产品传输可透过最高至400kHz的IIC

16位ADC采集传感器的模拟信号,量化范围:-32768~32767

I2C从机地址:
1101000(AD0=0) 0x68
1101001(AD0=1)0x69

这里的地址是没有融入读写地址位的,如果融入读写地址位,那么将通过左移的方式,变成11010000,0xD0;11010001,0xD1。

电路

在这里插入图片描述
最右边的是MPU6050的芯片,左下角是8针的排针,左上角是一个低压差线性稳压器。

芯片包括时钟、IIC通信引脚、供电、帧同步等,本章有很多是用不到的。

左下角VCC和GND是引脚供电;SCL和SDA是IIC通信的引脚,在芯片处,SCL和SDA已经内置了两个4.7k的上拉电阻,在接线时,可以直接连接到GPIO口。
XCL和XDA是主机的IIC引脚,目的是为了拓展芯片功能,可拓展磁力计等功能。
AD0是从机地址的最低位,接低电平时,7位从机地址为1101000,接高电平时,7位从机地址就是1101001.这里默认连接上了弱下拉到低电平,引脚悬空时,即为低电平。倘若想变为高电平,可以直接连接VCC,强上拉为高电平。
INT是中断输出引脚,可配置芯片内部一些事件,来触发中断引脚的输出。

左上角LD0是供电的逻辑,MPU6050芯片的VCC供电为2.375~3.46V,属于3.3V的供电设备,为了扩大供电范围,设计者在电路中加入3.3V的稳压器,输入端电压VCC_5V可以在3.3V到5V之间,经过稳压器后即可输出3.3V的电压,一旦3.3V端有电压,指示灯就会亮起。

MPU6050框图

在这里插入图片描述
该图即为芯片的内部结构。

左上角是时钟系统,有时钟输入脚和时钟输出脚。

灰色部分就是芯片的传感器,分别是XYZ轴的加速度计,XYZ轴的陀螺仪。且还内置一个温度传感器,本质上传感器就是可变电阻,所以通过ADC模数转换,就可以产生数据放在数据寄存器中。我们只需要读取寄存器中的数据即可得到测量值。

最左边是用来验证芯片的好坏的,当启动自测后,芯片内部会模拟一个外力施加在传感器上,该数据一般给的比平时大一些。可以用使能自测,读取数据,再使能自测,读取数据,最后数据差称为数据响应,只要数据响应在规定范围内,则表明芯片没问题。

ChargePump为电荷泵,这是一种升压电路,会连接一个电容。
电荷泵与电容并联时,可为电容充电,串联时,那么电荷泵和电容一起为芯片供电,提供比原来大的电压。

右边是通信接口和一些寄存器。
中断状态寄存器,可以控制内部的某些事件到中断引脚的输出;
FIFO是先入先出寄存器,可以对数据流进行缓冲;
配置寄存器,可以对内部电路进行配置。
传感器寄存器,也就是数据寄存器,存储各传感器的数据。
工厂校准,内部的传感器都进行了校准;
数字运动处理器(DMP),这是芯片内部自带的一个姿态解算的硬件算法。

然后就是IIC通信的接口,链接着STM32,下面还有一些是主机的IIC通信接口。通过(SIBM)寄存器选择。

IIC外设

STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担

支持多主机模型

支持7位/10位地址模式

支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)

支持DMA

兼容SMBus协议

STM32F103C8T6 硬件I2C资源:I2C1、I2C2

框图

在这里插入图片描述
SDA接收数据和发送数据;
对于要发送的数据,会从数据寄存器转移到数据移位寄存器中,数据移位寄存器再通过引脚串行发送数据位;
对于要接收的数据,也会先放到数据移位寄存器中,数据寄存器再从移位寄存器中取数据,这样做的目的是为了更好的缓存数据,防止有些数据会丢失。
数据寄存器可以通过写入控制寄存器对应位进行操作。

比较器和地址寄存器是从机模式使用的,STM32的IIC是基于可变多主机模型设计的,当STM32不通信时,可作为从机,可被别人召唤,这时就需要一个地址。
PEC是一个数据校验模块,当发送一个多数据帧时,硬件可以自动执行CRC校验计算,CRC是一种校验算法,会根据前面这些数据,进行各种数据运算,会得到一个字节的校验位,加在数据帧后面;STM32也可以自动对数据帧进行判断,如果数据在传输过程中出错,CRC算法通不过,硬件就会置校验错误标志位。

SCL连接着时钟控制。
SMBALEART是用于SMBus模式的,不使用该模式接口不能使用。

IIC的基本结构

在这里插入图片描述
数据控制器,控制对数据的发送和接收;
时钟控制器,控制对时钟的流动;
再接上GPIO口,最后把开关控制启用,就能实现IIC通信。

软件IIC实现MPU6050

OLED代码链接入口

连接方式:
在这里插入图片描述

利用OLED屏幕显示MPU6050陀螺仪和加速度各轴数据;

IIC.h

#ifndef __I2C_H__
#define __I2C_H__

void I2C_INIT();
void I2C_Start();
void I2C_Stop();
void I2C_SendByte(uint8_t Byte);
uint8_t I2C_ReceiveByte();
void I2C_SendAck(uint8_t AckBit);
uint8_t I2C_ReceiveAck();

#endif

IIC.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

void I2C_W_SCL(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
    Delay_us(10); //延迟是让函数有时间反应
}

void I2C_W_SDA(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
    Delay_us(10); //延迟是让函数有时间反应
}

uint8_t I2C_R_SDA()
{
    uint8_t BitValue;
    BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
    Delay_us(10);
    return BitValue;
}

void I2C_INIT()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏弱上拉输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}

void I2C_Start()
{
    I2C_W_SDA(1);
    I2C_W_SCL(1);
    I2C_W_SDA(0);
    I2C_W_SCL(0);
}

void I2C_Stop()
{
    I2C_W_SDA(0);
    I2C_W_SCL(1);
    I2C_W_SDA(1);
}

void I2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for(i=0;i<8;i++)
    {
        I2C_W_SDA(Byte&(0x80>>i));
        //高电平锁存,从机读取
        I2C_W_SCL(1);
        I2C_W_SCL(0);
    }
}

uint8_t I2C_ReceiveByte()
{
    uint8_t i,Byte=0x00;
    I2C_W_SDA(1); //主机释放SDA
    for(i=0;i<8;i++)
    {
        I2C_W_SCL(1); //SCL高电平期间主机读取从机发送的数据
        if(I2C_R_SDA()==1){Byte|=(0x80>>i);}
        I2C_W_SCL(0);
    }
    return Byte;
}

void I2C_SendAck(uint8_t AckBit)
{
    I2C_W_SDA(AckBit);
    //高电平锁存,从机读取
    I2C_W_SCL(1);
    I2C_W_SCL(0);
}

uint8_t I2C_ReceiveAck()
{
    uint8_t AckBit;
    I2C_W_SDA(1); //主机释放SDA
    I2C_W_SCL(1); //SCL高电平期间主机读取从机发送的数据
    AckBit=I2C_R_SDA(); 
    I2C_W_SCL(0);
    return AckBit;
}

这部分实现的是IIC的六个主要部分的代码,与51单片机上的时序基本一致;在STM32上,是利用GPIO口来实现高低电平的输入输出。

输出模式用开漏输出,当输出低电平时,电路会强下拉,变为低电平;
当输出为高电平时,为弱上拉,一旦有下拉电平输入,就会变成低电平。

该模式不止可以实现输出,也可以实现对IIC的接收,当输入为1时,在SCL高电平时STM32会读取;当输入为0时,在SCL高电平STM32会读取.

MPU6050_tag.h

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75

#endif

MPU6050.h

#ifndef __MPU6050_H__
#define __MPU6050_H__


void MPU6050_Init();
uint8_t MPU6050_GetID();
void MPU6050_GetData(int16_t* AccX,int16_t* AccY,int16_t* AccZ,int16_t* GyroX,int16_t* GyroY,int16_t* GyroZ);

#endif

MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "I2C.h"
#include "MPU6050_rag.h"

#define MPU6050_ADDRESS 0xD0

void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)
{
    I2C_Start();
    I2C_SendByte(MPU6050_ADDRESS);
    I2C_ReceiveAck();
    I2C_SendByte(RegAddress);
    I2C_ReceiveAck();
    I2C_SendByte(Data);
    I2C_ReceiveAck();
    I2C_Stop();
    
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
    uint8_t Data;
    
    I2C_Start();
    I2C_SendByte(MPU6050_ADDRESS);
    I2C_ReceiveAck();
    I2C_SendByte(RegAddress);
    I2C_ReceiveAck();
    
    I2C_Start();
    I2C_SendByte(MPU6050_ADDRESS|0x01);
    I2C_ReceiveAck();
    Data=I2C_ReceiveByte();
    I2C_SendAck(1); //主机不应答,主机收回主动权,让从机停止发送数据字节
    I2C_Stop();
    
    return Data;
    
    
}

void MPU6050_Init()
{
   I2C_INIT();
    MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01); //解除睡眠,选择陀螺仪时钟
    MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x00); //6个轴均不待机
    MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09); //采样分频为10
    MPU6050_WriteReg(MPU6050_CONFIG, 0x06);  //滤波参数给最大
    MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //最大陀螺仪量程
    MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //最大加速度量程

}

uint8_t MPU6050_GetID()
{
    return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(int16_t* AccX,int16_t* AccY,int16_t* AccZ,int16_t* GyroX,int16_t* GyroY,int16_t* GyroZ)
{
    uint8_t DataH,DataL;
    
    DataH=MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
    DataL=MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
    *AccX=(DataH<<8)|DataL;
    
    DataH=MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
    DataL=MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
    *AccY=(DataH<<8)|DataL;
    
    DataH=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
    DataL=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
    *AccZ=(DataH<<8)|DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
    
    

}

第一个代码块是对MPU6050一些寄存器地址的宏定义,主要有采样分频寄存器、陀螺仪配置寄存器、加速度配置寄存器、加速度数据寄存器、陀螺仪数据寄存器、状态寄存器1/2、地址寄存器。

对于MPU6050的读写,采用了IIC通信时序实现
写时序:在这里插入图片描述
读时序:
在这里插入图片描述
数据存储器为16位存储;

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"

uint8_t ID;
int16_t AX,AY,AZ,GX,GY,GZ;

int main()
{
	OLED_Init();
    MPU6050_Init();
    
    OLED_ShowString(1,1,"ID:");
    ID=MPU6050_GetID();
    OLED_ShowHexNum(1,4,ID,2);
    
    while(1)
    {
        MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
        OLED_ShowSignedNum(2,1,AX,5);
        OLED_ShowSignedNum(3,1,AY,5);
        OLED_ShowSignedNum(4,1,AZ,5);
        OLED_ShowSignedNum(2,8,GX,5);
        OLED_ShowSignedNum(3,8,GY,5);
        OLED_ShowSignedNum(4,8,GZ,5);
        
    }
}

硬件IIC实现MPU6050

连接方式与软件的保持一致。由于是硬件外设,需要注意引脚有没有支持该IIC功能。
在这里插入图片描述
对于硬件外设,我们只需要对软件实现部分的IIC通信进行修改。
MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "MPU6050_rag.h"

#define MPU6050_ADDRESS 0xD0

void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
	{
		Timeout --;
		if (Timeout == 0)
		{
			break;
		}
	}
}

void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)
{
    
    
    I2C_GenerateSTART(I2C2,ENABLE); //S
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //EV5
    
    I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6
    
    I2C_SendData(I2C2,RegAddress);
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8
    
    I2C_SendData(I2C2,Data);
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2
    
    I2C_GenerateSTOP(I2C2,ENABLE);
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
    uint8_t Data;
    
    I2C_GenerateSTART(I2C2,ENABLE); //S
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //EV5
    
    I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); 
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6
    
    I2C_SendData(I2C2,RegAddress); 
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);
    I2C_GenerateSTART(I2C2,ENABLE); //S
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //EV5
    
    I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver);
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //EV6
    
    //EV7_1
    I2C_AcknowledgeConfig(I2C2,DISABLE);//应答位禁用    
    I2C_GenerateSTOP(I2C2,ENABLE); //P
    MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED); //EV7
    
    Data=I2C_ReceiveData(I2C2);
    
    I2C_AcknowledgeConfig(I2C2,ENABLE); //应答位启用
    
    return Data;
    
    
}

void MPU6050_Init()
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD; //复用开漏输出
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; //启用应答位
    I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; //确认地址模式
    I2C_InitStructure.I2C_ClockSpeed=50000; //时钟频率
    I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2; //占空比
    I2C_InitStructure.I2C_Mode=I2C_Mode_I2C; //模式选择
    I2C_InitStructure.I2C_OwnAddress1=0x00;
    I2C_Init(I2C2,&I2C_InitStructure);
    
    I2C_Cmd(I2C2,ENABLE);
    
    MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01); //解除睡眠,选择陀螺仪时钟
    MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x00); //6个轴均不待机
    MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09); //采样分频为10
    MPU6050_WriteReg(MPU6050_CONFIG, 0x06);  //滤波参数给最大
    MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //最大陀螺仪量程
    MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //最大加速度量程

}

uint8_t MPU6050_GetID()
{
    return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(int16_t* AccX,int16_t* AccY,int16_t* AccZ,int16_t* GyroX,int16_t* GyroY,int16_t* GyroZ)
{
    uint8_t DataH,DataL;
    
    DataH=MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
    DataL=MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
    *AccX=(DataH<<8)|DataL;
    
    DataH=MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
    DataL=MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
    *AccY=(DataH<<8)|DataL;
    
    DataH=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
    DataL=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
    *AccZ=(DataH<<8)|DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
    
    

}

这里的初始化,GPIO引脚需要用到复用模式,因为IIC外设是片上外设;
接收数据和发送数据要根据STM32的要求,
在这里插入图片描述
根据响应事件来确定事件的产生效果,所以会在每条条件后执行响应事件。
对于所给的库函数,有些事件(EVX)没有提供,一些可以省略,一些需要对程序进行一定的整改。
如上面的EV8_1,确保数据寄存器和移位寄存器为空,在我们一开始调用时,就为空,所以可以对它进行忽略。
在这里插入图片描述
接收数据的EV7_1,描述到,设置应答位为无应答,和Stop请求;对于连续接收的数据,需要在最后一个数据之前进行EV7_1响应,在进行EV7_1响应时,因为最后一个数据会先放到移位寄存器中,最后第二个会在数据寄存器中。需要提前STOP请求,表示,接收结束。
而在程序中,我们只是对一个数据进行接收,并没有连续接收数据,但道理一样,需要提前STOP请求。而在库函数中刚好没有对应的函数,需要自己禁用ACK和STOP请求。

上软下硬:
在这里插入图片描述

这时软件和硬件的波形对比,会发现,在应答位硬件会更快应答,只要到到SCL下降沿和SDA上升沿,就会产生应答。这就是硬件的优势;

在软件中,由于是同步时序,对于时间没有严格要求,只要在对应时间完成对应的电平操作即可,所以IIC通信才可以实现软件编程。而软件编程相对来说,也比较容易理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/914147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于android studio 几个简单的问题说明

自信是成功的第一步。——爱迪生 1. android studio 如何运行不同项目是否要更换不同的sdk 和 gradle 2.编译Gradle总是错误为什么 3.如何清理android studio 的缓存 4. 关于android Studio中的build 下面的rebuild project

『SEQ日志』在 .NET Core 中轻松集成 NLog 和 SEQ ,打造轻量级的分布式日志系统

&#x1f4e3;读完这篇文章里你能收获到 如何在Docker中部署 SEQ&#xff1a;介绍了如何创建和运行 SEQ 容器&#xff0c;给出了详细的执行操作如何使用 NLog 接入 .NET Core 应用程序的日志&#xff1a;详细介绍了 NLog 和 NLog.Seq 来配置和记录日志的步骤日志记录示例&…

判断三角形

int main() {int a 0;int b 0;int c 0;scanf("%d%d%d", &a, &b, &c);if ((ab>c)&&(ac>b)&&(bc>a)){if (a b && b c){printf("等边三角形\n");}else if ((a b && b ! c) || (a c && c…

企业的降本增益,从源头抓起——精细化管理每一条销售线索

存量经济时代&#xff0c;降本增效成为企业增长的重点抓手&#xff0c;也是企业自身数字化建设重要目标。 如何能让销售更珍惜线索&#xff1f;如何能让线索产生更快的转化&#xff1f;如何能让对的线索给到对的销售&#xff1f;如何能依托线索转化结果找到更有效的获客渠道&a…

【踩坑日记】STM32 USART 串口与 FreeRTOS 冲突

文章目录 问题描述问题出现的环境问题解决过程第一步第二步第三步第四步第五步第六步第七步第八步 后续验证一些思考类似的问题后记 问题描述 笔者使用 FreeRTOS 创建了两个任务&#xff0c;使两颗 LED 以不同频率闪烁&#xff0c;但是在加入串口 USART 部分代码后&#xff0c…

Shader 用到的数学函数总结及用法

在写shader相关的程序的时候往往需要用到 OpenGL内置的数学库&#xff0c;下面列举几个常用的函数用法和效果&#xff0c;下面的所有代码都是运行在shadertoy在线工具上的&#xff0c;推荐一个好用的数学工具desmosDesmos 目录&#xff1a; abs(x)step(edge,x)dot(uv1,uv2) …

MySQL双主架构、主从架构

为什么要对数据库做优化&#xff1f; MySQL官方说法&#xff1a; 单表2000万数据就达到瓶颈了。所以为了保证查询效率&#xff0c;要让每张表的大小得到控制。 MySQL主主架构 主数据库都负责增删改查。 比如有1000W的数据&#xff0c;有两个主数据库&#xff0c;就将数据分流给…

古巴勒索软件-CVE-2023-27532工具利用

黑莓捕获古巴勒索软件&#xff0c;其中一项工具有关CVE漏洞工具利用&#xff0c;利用有关CVE-2023-27532编号的漏洞利用工具进行投放。 MD5 9a3349f062130201a3dc16e78d5ab05d SHA-1 96e0a6a2aa12ed572fea9a1a858c903356bce1ab SHA-256 cf87a44c575d391df668123b05c207eef0…

代码随想录 (四)字符串

1.反转字符串 class Solution { public:void reverseString(vector<char>& s) {for (int i 0, j s.size() - 1; i < s.size()/2; i, j--) {swap(s[i],s[j]);}} };2.反转字符串II 直接用reverse&#xff08;是左闭右开的&#xff09; std::reverse 是 C 标准库中…

【2023深圳杯数学建模A题思路模型与代码分享】

2023深圳杯数学建模A题 A题 影响城市居民身体健康的因素分析解题思路第一问第二问第三问第四问 技术文档第一问完整代码写在最后 A题 影响城市居民身体健康的因素分析 以心脑血管疾病、糖尿病、恶性肿瘤以及慢性阻塞性肺病为代表的慢性非传染性疾病&#xff08;以下简称慢性病…

TCP性能机制

延迟应答 为什么有延迟应答 发送方如果长时间没有收到ACK应答&#xff0c;则会触发超时重传机制&#xff0c;重新发送数据包。但如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小&#xff0c;发送方一次只能发少量数据&#xff0c;效率较低。 举个例子理解一…

高性能服务器NodejsExpress快速构建Web

目录 1 Express基本使用1.1 托管静态资源 2 Express 路由3 Express 中间件3.1 全局中间件3.2 局部中间件3.3 中间件分类 4 CORS 跨域资源共享4.1 cors 中间件解决跨域4.2 CORS4.3 CORS 常见响应头4.4 CORS 请求分类 1 Express基本使用 官网传送门(opens new window) 基于 Node.…

简单计算器的实现(含转移表实现)

文章目录 计算器的一般实现使⽤函数指针数组的实现&#xff08;转移表&#xff09; 计算器的一般实现 通过函数的调用&#xff0c;实现加减乘除 # define _CRT_SECURE_NO_WARNINGS#include<stdio.h>int Add(int x, int y) {return x y; }int Sub(int x, int y) {retur…

计算机中找不到msvcp140.dll丢失怎么弄?程序员都是这样解决的

msvcp140.dll是Microsoft Visual C 2015 Redistributable Package的一部分&#xff0c;它是一个运行时库文件&#xff0c;提供了许多C标准库函数的实现。如果缺少这个文件&#xff0c;可能会导致一些程序无法正常运行&#xff0c;例如一些游戏、应用程序等。为了解决这个问题&a…

【Unity】如何制作小地图

我们为什么要制作小地图呢&#xff1f; 原因很简单&#xff1a; 导航和定位&#xff1a;小地图可以显示玩家当前位置以及周围环境的概览。这使得玩家能够更好地导航和定位自己在游戏中的位置&#xff0c;找到目标或避开障碍物。场景了解&#xff1a;通过小地图&#xff0c;玩…

Presto 介绍

目录 Presto 介绍Presto 架构Coordinator协调器节点发现服务&#xff08;discovery service&#xff09;工作节点基于连接器的架构查询执行模型查询优化JVM 配置Presto Web UIPresto 内存管理Presto 任务并发性Presto 工作节点调度Presto 网络数据交换Presto 资源组 Presto 介绍…

公司电脑文件数据加密系统

电脑文件透明加密软件是一种可以对文件进行加密的软件&#xff0c;这类软件能够在用户无感知的情况下对文件进行加密&#xff0c;使得文件的安全性得到保障。 PC访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 智能透明加…

Java抽象类详解

抽象类 抽象类的概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类。比如&#xff1a; 说…

C语言刷题(13)

第一题 第二题 第三题 第四题 第五题 第六题 第七题 注意 1.nsqrt(n)&#xff0c;sqrt本身不会将n开根 2.初始化已经令sumn了&#xff0c;故相加的个数为m-1次

机器学习在大数据分析中的应用

文章目录 机器学习在大数据分析中的原理机器学习在大数据分析中的应用示例预测销售趋势客户细分和个性化营销 机器学习在大数据分析中的前景和挑战前景挑战 总结 &#x1f389;欢迎来到AIGC人工智能专栏~探索机器学习在大数据分析中的应用 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&…