最近在重构自己的平衡车代码,里面需要用到MPU6050的DMP,从中读取四元数进行欧拉角解算,但是看着软件IIC的代码实在是很变扭,因为之前不会C++,所以如果需要调用多个IIC设备,那么使用的时候就需要重复的去进行软件IIC底层代码的初始化,非常的麻烦,而且需要调整各个引脚,在学习过C++之后,发现类实在是太好用了,那么我就在想能不能通过类把软件IIC的底层进行封装,实现和arduino一样的编程效果,使用的时候只需要放入软件IIC的SCL和SDA对应的GPIO即可。
1.环境
软件环境:keil CubeMX
硬件环境:STM32F103C8T6 MPU6050
使用引脚:PB8–>SCL PB9–>SDA
2.注意事项
软件IIC,顾名思义通过操作IO口的输入和输出,即使得IO口高低电平变换输出模拟数据发送,读取IO口高低电平实现数据接收,关于IIC通讯协议的介绍,本篇文章就不赘述了,这里要说的有以下几点:
a)实现IIC输出高低电平有以下几种方式:
HAL库:
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
标准库:
GPIO_SetBits(GPIOB,GPIO_Pin_9);
GPIO_ResetBits(GPIOB,GPIO_Pin_9);
寄存器:
寄存器有很多
正点原子封装好的,可以以操作51单片机IO口的方式去操作STM32IO输入或者 输出:
PBout(9) = 1;
PBout(9) = 0; //相信大家正点原子用的应该是最多的
直接操作寄存器:
GPIOB -> BSRR = GPIO_PIN_9; //输出高电平
GPIOB-> BRR = GPIO_PIN_9; //输出低电平
这里的效率对比,从慢往快说:
HAL库 、标准库、仿51 、 直接操作寄存器
从我个人角度来说,我喜欢使用最后一种,直接操作寄存器,效率最高,执行最快,玩单片机玩的熟的都喜欢
追求极致的效率,个人习惯看不见底层代码很难受,所以喜欢直接对寄存器进行操作。
b)IO口方向设置:(相信大家在移植软件IIC的时候这应该是最头痛的一件事情)
#define SDA_IN() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<4;}
#define SDA_OUT() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=3<<4;}
相信大家对这个已经深恶痛绝,每次需要更换IO口去实现的时候,都需要重新算,这里咱们就不多说关于STM
IO寄存器映射问题了,简单说一下这个IO口怎么映射,和让哪一位归0,如果使用的IO口是8-15,那么就是就是
高八位即对应CRH,反之0-7对应CRL,CRx &= 0XFFFFFFFF代表要操作哪一位,如果是GPIO10,那么高八位
从右想左第一个是8,第三个就是10,此时就应该是10,移动多少位,用(10-8)*4,可以计算出来是8,为什么要
*4,因为STM32是32位的,高低八位,每位各对应4个,即每个IO口对应4所处高低八位中的32位中的四位。
之所以要说这件事情,是因为之前本人的博客是关于[F4情况下软件IIC的使用](https://blog.csdn.net/weixin_44080304/article/details/123480999?spm=1001.2014.3001.5502),
那个时候在做项目,时间比较着急,所以就直接用HAL库上了,当然功能上是并不影响,因为传感器的通讯协议
和功能都比较简单对通讯的要求并不高,而且F4的主频也比较快并没有什么大的影响。
在重构自己平衡车代码的时候发现,软件IIC驱动MPU6050的时候,不设定IO口方向,我可以读取到6050发送的角速度和加速度信息,但是无法正常使用DMP模式,即无法直接读取出来四元数,经过查看单片机说明书和其他资料,发现是因为IO口方向设置问题,所以趁着这次封装,就一鼓作气全部搞定。
3软件IIC类
其中最重要的就是:
#define setbit(x,y) x|=(1<<y) //将X的第Y位置1
#define clrbit(x,y) x&=~(1<<y) //将X的第Y位清0
这段代码的作用是用于设置IO口方向的,最重要也是最容易忽视的一部分
#ifndef _SOFT_I2C1_H__
#define _SOFT_I2C1_H__
#include "header.h"
#define setbit(x,y) x|=(1<<y) //将X的第Y位置1
#define clrbit(x,y) x&=~(1<<y) //将X的第Y位清0
class SOFTIIC{
private:
GPIO_TypeDef *SCL_GPIOx;
uint16_t SCL_PIN;
GPIO_TypeDef *SDA_GPIOx;
uint16_t SDA_PIN;
uint32_t sda_io_num; //对应的SDA引脚位移之后的数字
uint8_t sda_move; // 输出需要移动的位数
uint8_t temp;
private:
void SDA_IN_SET(void); //IO方向设置,将IO设置为输入
void SDA_OUT_SET(void); //IO方向设置,将IO设置为输出50Mhz
void I2C_SCL_LOW(void); //SCL总线输出低电平
void I2C_SCL_HIGH(void); //SCL总线输出高电平
void I2C_SDA_LOW(void); //SCL总线输出低电平
void I2C_SDA_HIGH(void); //SCL总线输出高电平
public:
SOFTIIC(){}; //构造函数
SOFTIIC(GPIO_TypeDef *scl_GPIOx, uint16_t scl_GPIO_Pin,
GPIO_TypeDef *sda_GPIOx, uint16_t sda_GPIO_Pin) //重载构造函数
{
this->SCL_GPIOx = scl_GPIOx;
this->SCL_PIN = scl_GPIO_Pin;
this->SDA_GPIOx = sda_GPIOx;
this->SDA_PIN = sda_GPIO_Pin;
}
~SOFTIIC(){}; //析构函数
public:
void I2C_Soft_Init(void);
void I2C_Soft_Delay(void);
int I2C_Soft_Start(void);
void I2C_Soft_Stop(void);
void I2C_Soft_Ack(void);
void I2C_Soft_NoAck(void);
int I2C_Soft_WaitAck(void); //返回:=1有ACK,=0无ACK
void I2C_Soft_SendByte(u8 SendByte);
u8 I2C_Soft_ReadByte(void);
int I2C_Soft_Single_Read(unsigned char I2C_Addr,unsigned char addr);
u8 IICwriteBits(u8 dev,u8 reg,u8 bitStart,u8 length,u8 data);
/**************************实现函数********************************************
*函数原型: u8 IICreadByte(u8 dev, u8 reg, u8 *data)
*功 能: 读取指定设备 指定寄存器的一个值
输入 dev 目标设备地址
reg 寄存器地址
*data 读出的数据将要存放的地址
返回 1
*******************************************************************************/
u8 IICreadByte(u8 dev, u8 reg, u8 *data);
/**************************实现函数********************************************
*函数原型: u8 IICwriteBit(u8 dev, u8 reg, u8 bitNum, u8 data)
*功 能: 读 修改 写 指定设备 指定寄存器一个字节 中的1个位
输入 dev 目标设备地址
reg 寄存器地址
bitNum 要修改目标字节的bitNum位
data 为0 时,目标位将被清0 否则将被置位
返回 成功 为1
失败为0
*******************************************************************************/
u8 IICwriteBit(u8 dev, u8 reg, u8 bitNum, u8 data);
/**************************实现函数********************************************
*函数原型: bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data)
*功 能:
*******************************************************************************/
int i2cWrite(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *data);
/**************************实现函数********************************************
*函数原型: u8 IIC_Read_Byte(unsigned char ack)
*功 能: //读1个字节,ack=1时,发送ACK,ack=0,发送nACK
*******************************************************************************/
u8 I2C_Soft_ReadByte(unsigned char ack); //数据从高位到低位//
/**************************实现函数********************************************
*函数原型: u8 IICreadBytes(u8 dev, u8 reg, u8 length, u8 *data)
*功 能: 读取指定设备 指定寄存器的 length个值
输入 dev 目标设备地址
reg 寄存器地址
length 要读的字节数
*data 读出的数据将要存放的指针
返回 读出来的字节数量
*******************************************************************************/
u8 IICreadBytes(u8 dev, u8 reg, u8 length, u8 *data);
/**************************实现函数********************************************
*函数原型: u8 IICwriteBytes(u8 dev, u8 reg, u8 length, u8* data)
*功 能: 将多个字节写入指定设备 指定寄存器
输入 dev 目标设备地址
reg 寄存器地址
length 要写的字节数
*data 将要写的数据的首地址
返回 返回是否成功
*******************************************************************************/
u8 IICwriteBytes(u8 dev, u8 reg, u8 length, u8* data);
/**************************实现函数********************************************
*函数原型: unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data)
*功 能: 写入指定设备 指定寄存器一个字节
输入 dev 目标设备地址
reg 寄存器地址
data 将要写入的字节
返回 1
*******************************************************************************/
unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data);
/**************************实现函数********************************************
*函数原型: bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data)
*功 能:
*******************************************************************************/
int i2cRead(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf);
};
#endif
具体实现:
#include "softiic.h"
//模拟IIC初始化
void SOFTIIC::I2C_Soft_Init(void)
{
uint8_t clear;
switch(SDA_PIN)
{
case GPIO_PIN_0:
temp = 0;
break;
case GPIO_PIN_1:
temp = 1;
break;
case GPIO_PIN_2:
temp = 2;
break;
case GPIO_PIN_3:
temp = 3;
break;
case GPIO_PIN_4:
temp = 4;
break;
case GPIO_PIN_5:
temp = 5;
break;
case GPIO_PIN_6:
temp = 6;
break;
case GPIO_PIN_7:
temp = 7;
break;
case GPIO_PIN_8:
temp = 8;
break;
case GPIO_PIN_9:
temp = 9;
break;
case GPIO_PIN_10:
temp = 10;
break;
case GPIO_PIN_11:
temp = 11;
break;
case GPIO_PIN_12:
temp = 12;
break;
case GPIO_PIN_13:
temp = 13;
break;
case GPIO_PIN_14:
temp = 14;
break;
case GPIO_PIN_15:
temp = 15;
break;
default:
break;
}
if(temp <= 7)
{
clear = temp;
}
else if(temp > 7)
{
clear = temp-8;
}
sda_move = clear*4;
sda_io_num = (uint32_t)4294967295;
clrbit(sda_io_num,(clear*4));
clrbit(sda_io_num,(clear*4+1));
clrbit(sda_io_num,(clear*4+2));
clrbit(sda_io_num,(clear*4+3));
}
void SOFTIIC::SDA_IN_SET(void) //IO口输入设置
{
if(temp <= 7)
{
SDA_GPIOx->CRL &= sda_io_num;
SDA_GPIOx->CRL |= 8<<sda_move;
}
else if(temp >7)
{
SDA_GPIOx->CRH &= sda_io_num;
SDA_GPIOx->CRH |= 8<<sda_move;
}
}
void SOFTIIC::SDA_OUT_SET(void) // IO口输出设置
{
if(temp <= 7)
{
SDA_GPIOx->CRL &= sda_io_num;
SDA_GPIOx->CRL |= 3<<sda_move;
}
else if(temp >7)
{
SDA_GPIOx->CRH &= sda_io_num;
SDA_GPIOx->CRH |= 3<<sda_move;
}
}
void SOFTIIC::I2C_SCL_LOW(void) //SCL总线输出低电平
{
SCL_GPIOx->BRR = SCL_PIN;
}
void SOFTIIC::I2C_SCL_HIGH(void) //SCL总线输出高电平
{
SCL_GPIOx->BSRR = SCL_PIN;
}
void SOFTIIC::I2C_SDA_LOW(void) //SCL总线输出低电平
{
SDA_GPIOx->BRR = SDA_PIN;
}
void SOFTIIC::I2C_SDA_HIGH(void) //SCL总线输出高电平
{
SDA_GPIOx->BSRR = SDA_PIN;
}
/*软件延迟*/
void SOFTIIC::I2C_Soft_Delay(void)
{
delay_us(1);
}
/*开始信号,SCL为高时,SDA由高电平变为低电平*/
int SOFTIIC::I2C_Soft_Start(void)
{
//sda线输出
SDA_OUT_SET();
I2C_SDA_HIGH();
if(!(SDA_GPIOx->IDR & SDA_PIN))return 0;
I2C_SCL_HIGH();
delay_us(1);
I2C_SDA_LOW();//START:when CLK is high,DATA change form high to low
if((SDA_GPIOx->IDR & SDA_PIN))return 0;
delay_us(1);
I2C_SCL_LOW();//钳住I2C总线,准备发送或接收数据
return 1;
}
/*停止信号,SCL为高时,SDA由低电平变为高电平*/
void SOFTIIC::I2C_Soft_Stop(void)
{
//sda线输出
SDA_OUT_SET();
I2C_SCL_LOW();
I2C_SDA_LOW();//STOP:when CLK is high DATA change form low to high
delay_us(1);
I2C_SCL_HIGH();
I2C_SDA_HIGH();//发送I2C总线结束信号
delay_us(1);
}
/*应答信号,在第九个时钟脉冲来临前将SDA拉低,并在SCL=1期间,SDA稳定为低*/
void SOFTIIC::I2C_Soft_Ack(void)
{
I2C_SCL_LOW();
SDA_OUT_SET();
I2C_SDA_LOW();
delay_us(1);
I2C_SCL_HIGH();
delay_us(1);
I2C_SCL_LOW();
}
/*不产生应答信号,第9个时钟脉冲来临前将SDA拉低,并在SCL=1期间,SDA稳定为高*/
void SOFTIIC::I2C_Soft_NoAck(void)
{
I2C_SCL_LOW();
SDA_OUT_SET();
I2C_SDA_HIGH();
delay_us(1);
I2C_SCL_HIGH();
delay_us(1);
I2C_SCL_LOW();
}
/*等待应答*/
int SOFTIIC::I2C_Soft_WaitAck(void) //返回为:=1有ACK,=0无ACK
{
u8 ucErrTime=0;
SDA_IN_SET();
I2C_SDA_HIGH();
delay_us(1);
I2C_SCL_HIGH();
delay_us(1);
while((SDA_GPIOx->IDR & SDA_PIN))
{
ucErrTime++;
if(ucErrTime>50)
{
I2C_Soft_Stop();
return 0;
}
delay_us(1);
}
I2C_SCL_LOW();//时钟输出0
return 1;
}
/*字节发送函数*/
void SOFTIIC::I2C_Soft_SendByte(u8 SendByte)
{
u8 t;
SDA_OUT_SET();
I2C_SCL_LOW();//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((SendByte&0x80)>>7 == 1){
I2C_SDA_HIGH();
}
else{
I2C_SDA_LOW();
}
SendByte<<=1;
delay_us(1);
I2C_SCL_HIGH();
delay_us(1);
I2C_SCL_LOW();
delay_us(1);
}
}
/**************************实现函数********************************************
*函数原型: bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data)
*功 能:
*******************************************************************************/
int SOFTIIC::i2cWrite(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *data)
{
int i;
if (!I2C_Soft_Start())
return 1;
I2C_Soft_SendByte(addr << 1 );
if (!I2C_Soft_WaitAck()) {
I2C_Soft_Stop();
return 1;
}
I2C_Soft_SendByte(reg);
I2C_Soft_WaitAck();
for (i = 0; i < len; i++) {
I2C_Soft_SendByte(data[i]);
if (!I2C_Soft_WaitAck()) {
I2C_Soft_Stop();
return 0;
}
}
I2C_Soft_Stop();
return 0;
}
/**************************实现函数********************************************
*函数原型: bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data)
*功 能:
*******************************************************************************/
int SOFTIIC::i2cRead(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
if (!I2C_Soft_Start())
return 1;
I2C_Soft_SendByte(addr << 1);
if (!I2C_Soft_WaitAck()) {
I2C_Soft_Stop();
return 1;
}
I2C_Soft_SendByte(reg);
I2C_Soft_WaitAck();
I2C_Soft_Start();
I2C_Soft_SendByte((addr << 1)+1);
I2C_Soft_WaitAck();
while (len) {
if (len == 1)
*buf = I2C_Soft_ReadByte(0);
else
*buf = I2C_Soft_ReadByte(1);
buf++;
len--;
}
I2C_Soft_Stop();
return 0;
}
///**************************实现函数********************************************
//*函数原型: void IIC_Send_Byte(u8 txd)
//*功 能: IIC发送一个字节
//*******************************************************************************/
u8 SOFTIIC::I2C_Soft_ReadByte(unsigned char ack) //数据从高位到低位//
{
unsigned char i,receive=0;
SDA_IN_SET();
for(i=0;i<8;i++ )
{
I2C_SCL_LOW();
delay_us(2);
I2C_SCL_HIGH();
receive<<=1;
if((SDA_GPIOx->IDR & SDA_PIN))receive++;
delay_us(2);
}
if (ack)
I2C_Soft_Ack(); //发送ACK
else
I2C_Soft_NoAck();//发送nACK
return receive;
}
/**************************实现函数********************************************
*函数原型: unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr)
*功 能: 读取指定设备 指定寄存器的一个值
输入 I2C_Addr 目标设备地址
addr 寄存器地址
返回 读出来的值
*******************************************************************************/
int SOFTIIC::I2C_Soft_Single_Read(unsigned char I2C_Addr,unsigned char addr)
{
unsigned char res=0;
I2C_Soft_Start();
I2C_Soft_SendByte(I2C_Addr); //发送写命令
res++;
I2C_Soft_WaitAck();
I2C_Soft_SendByte(addr); res++; //发送地址
I2C_Soft_WaitAck();
I2C_Soft_Start();
I2C_Soft_SendByte(I2C_Addr+1); res++; //进入接收模式
I2C_Soft_WaitAck();
res=I2C_Soft_ReadByte(0);
I2C_Soft_Stop();//产生一个停止条件
return res;
}
/**************************实现函数********************************************
*函数原型: u8 IICreadBytes(u8 dev, u8 reg, u8 length, u8 *data)
*功 能: 读取指定设备 指定寄存器的 length个值
输入 dev 目标设备地址
reg 寄存器地址
length 要读的字节数
*data 读出的数据将要存放的指针
返回 读出来的字节数量
*******************************************************************************/
u8 SOFTIIC::IICreadBytes(u8 dev, u8 reg, u8 length, u8 *data)
{
u8 count = 0;
I2C_Soft_Start();
I2C_Soft_SendByte(dev); //发送写命令
I2C_Soft_WaitAck();
I2C_Soft_SendByte(reg); //发送地址
I2C_Soft_WaitAck();
I2C_Soft_Start();
I2C_Soft_SendByte(dev+1); //进入接收模式
I2C_Soft_WaitAck();
for(count=0;count<length;count++){
if(count!=length-1)data[count]=I2C_Soft_ReadByte(1); //带ACK的读数据
else data[count]=I2C_Soft_ReadByte(0); //最后一个字节NACK
}
I2C_Soft_Stop();//产生一个停止条件
return count;
}
/**************************实现函数********************************************
*函数原型: u8 IICwriteBytes(u8 dev, u8 reg, u8 length, u8* data)
*功 能: 将多个字节写入指定设备 指定寄存器
输入 dev 目标设备地址
reg 寄存器地址
length 要写的字节数
*data 将要写的数据的首地址
返回 返回是否成功
*******************************************************************************/
u8 SOFTIIC::IICwriteBytes(u8 dev, u8 reg, u8 length, u8* data){
u8 count = 0;
I2C_Soft_Start();
I2C_Soft_SendByte(dev); //发送写命令
I2C_Soft_WaitAck();
I2C_Soft_SendByte(reg); //发送地址
I2C_Soft_WaitAck();
for(count=0;count<length;count++){
I2C_Soft_SendByte(data[count]);
I2C_Soft_WaitAck();
}
I2C_Soft_Stop();//产生一个停止条件
return 1; //status == 0;
}
/**************************实现函数********************************************
*函数原型: u8 IICreadByte(u8 dev, u8 reg, u8 *data)
*功 能: 读取指定设备 指定寄存器的一个值 IIC读取一位的数值,输出要存放的地址
输入 dev 目标设备地址
reg 寄存器地址
*data 读出的数据将要存放的地址
返回 1
*******************************************************************************/
u8 SOFTIIC::IICreadByte(u8 dev, u8 reg, u8 *data){
*data=I2C_Soft_Single_Read(dev, reg);
return 1;
}
/**************************实现函数********************************************
*函数原型: unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data)
*功 能: 写入指定设备 指定寄存器一个字节
输入 dev 目标设备地址
reg 寄存器地址
data 将要写入的字节
返回 1
*******************************************************************************/
unsigned char SOFTIIC::IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data){
return IICwriteBytes(dev, reg, 1, &data);
}
/**************************实现函数********************************************
*函数原型: u8 IICwriteBits(u8 dev,u8 reg,u8 bitStart,u8 length,u8 data)
*功 能: 读 修改 写 指定设备 指定寄存器一个字节 中的多个位 多字节写入
输入 dev 目标设备地址
reg 寄存器地址
bitStart 目标字节的起始位
length 位长度
data 存放改变目标字节位的值
返回 成功 为1
失败为0
*******************************************************************************/
u8 SOFTIIC::IICwriteBits(u8 dev,u8 reg,u8 bitStart,u8 length,u8 data)
{
u8 b;
if (IICreadByte(dev, reg, &b) != 0) {
u8 mask = (0xFF << (bitStart + 1)) | 0xFF >> ((8 - bitStart) + length - 1);
data <<= (8 - length);
data >>= (7 - bitStart);
b &= mask;
b |= data;
return IICwriteByte(dev, reg, b);
} else {
return 0;
}
}
/**************************实现函数********************************************
*函数原型: u8 IICwriteBit(u8 dev, u8 reg, u8 bitNum, u8 data)
*功 能: 读 修改 写 指定设备 指定寄存器一个字节 中的1个位
输入 dev 目标设备地址
reg 寄存器地址
bitNum 要修改目标字节的bitNum位
data 为0 时,目标位将被清0 否则将被置位
返回 成功 为1
失败为0
*******************************************************************************/
u8 SOFTIIC::IICwriteBit(u8 dev, u8 reg, u8 bitNum, u8 data){
u8 b;
IICreadByte(dev, reg, &b);
b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
return IICwriteByte(dev, reg, b);
}
首先声明其中对于IO口初始化部分和输入输出设置的函数绝对本人原创,这其中需要重点说的一部分:
此部分的作用就是确认IO口输入输出方向设定的时候,应当进行如何移位,以及移几位,如果使用一定要认真查看此部分代码。
//模拟IIC初始化
void SOFTIIC::I2C_Soft_Init(void)
{
uint8_t clear;
switch(SDA_PIN)
{
case GPIO_PIN_0:
temp = 0;
break;
case GPIO_PIN_1:
temp = 1;
break;
case GPIO_PIN_2:
temp = 2;
break;
case GPIO_PIN_3:
temp = 3;
break;
case GPIO_PIN_4:
temp = 4;
break;
case GPIO_PIN_5:
temp = 5;
break;
case GPIO_PIN_6:
temp = 6;
break;
case GPIO_PIN_7:
temp = 7;
break;
case GPIO_PIN_8:
temp = 8;
break;
case GPIO_PIN_9:
temp = 9;
break;
case GPIO_PIN_10:
temp = 10;
break;
case GPIO_PIN_11:
temp = 11;
break;
case GPIO_PIN_12:
temp = 12;
break;
case GPIO_PIN_13:
temp = 13;
break;
case GPIO_PIN_14:
temp = 14;
break;
case GPIO_PIN_15:
temp = 15;
break;
default:
break;
}
if(temp <= 7)
{
clear = temp;
}
else if(temp > 7)
{
clear = temp-8;
}
sda_move = clear*4;
sda_io_num = (uint32_t)4294967295;
clrbit(sda_io_num,(clear*4));
clrbit(sda_io_num,(clear*4+1));
clrbit(sda_io_num,(clear*4+2));
clrbit(sda_io_num,(clear*4+3));
}
4使用方式:
构造软件IIC类
SOFTIIC softiic1(GPIOB,GPIO_PIN_8,GPIOB,GPIO_PIN_9); // 前两个对应SCL引脚,后两个对应SDA引脚
如果需要用很多,那么根据自己需要进行构造即可
SOFTIIC softiic2(GPIOB,GPIO_PIN_10,GPIOB,GPIO_PIN_11);
SOFTIIC softiic3(GPIOA,GPIO_PIN_4,GPIOA,GPIO_PIN_5);
调用上,在需要使用软件IIC通讯的传感器,底层通讯协议上直接使用,这里的示例使用的是MPU6050的部分初始化文章来源:https://www.toymoban.com/news/detail-442879.html
//MPU6050初始化,传入参数:采样率,低通滤波频率
void MPU6050::Mpu6050_Init(void)
{
//设置时钟源
softiic1.IICwriteBits(MPU6050_ADDRESS, MPU_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, MPU6050_CLOCK_PLL_YGYRO);
//设定陀螺仪采样的范围 陀螺仪最大量程 +-2000度每秒
softiic1.IICwriteBits(MPU6050_ADDRESS, MPU_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, MPU6050_GYRO_FS_1000);
//加速度度最大量程 +-2G
softiic1.IICwriteBits(MPU6050_ADDRESS, MPU_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH,MPU6050_ACCEL_FS_2);
//设定工作模式 0工作 1睡眠 进入工作状态
softiic1.IICwriteBit(MPU6050_ADDRESS,MPU_RA_PWR_MGMT_1,MPU6050_PWR1_SLEEP_BIT,0);
//设置 MPU6050 是否为AUX I2C线的主机 0不让MPU6050 控制AUXI2C
softiic1.IICwriteBit(MPU6050_ADDRESS,MPU_RA_USER_CTRL,MPU6050_USERCTRL_I2C_MST_EN_BIT,0);
//设置 MPU6050 是否为AUX I2C线的主机 主控制器的I2C与 MPU6050的AUXI2C 直通控制器可以直接访问HMC5883L
softiic1.IICwriteBit(MPU6050_ADDRESS, MPU_RA_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT,0);
/* DMP模式初始化 */
DMP_Init();
}
5效果:
理论上适用于任何IIC通讯的传感器或者说设备,执行效率上达到最高,因为直接面向寄存器进行操作,虽然底层IO初始化使用的是HAL库,但是这并不会影响到以上代码的执行效率,个人通过以上代码是成功的可以对MPU6050的DMP模式进行初始化,并且能够正常使用。以上代码对应的是STM32F1系列单片机的寄存器,如果往其它系列的32中进行移植,需要自己修改。
后续可能会更新软件串口、软件SPI等IO口可以模拟的通讯协议的底层代码封装。文章来源地址https://www.toymoban.com/news/detail-442879.html
到了这里,关于使用C++编写STM32软件IIC的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!