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

软件I2C的代码

I2C的函数 

GPIO的配置——scl和sda都配置为开漏输出

void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStruture;
	GPIO_InitStruture.GPIO_Mode= GPIO_Mode_Out_OD;
	GPIO_InitStruture.GPIO_Pin=GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStruture.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruture);
	
	GPIO_SetBits(GPIOA,GPIO_Pin_10 | GPIO_Pin_11);
}

封装读写函数

(BitAction)BitValue 是一种强制类型转换 它将 BitValue 转换为 BitAction 类型

void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);    
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}

uint8_t  MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue  = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
	return BitValue;
}

起始函数和终止函数

除了起始和终止,其他时刻只要SCL处于高电平,SDA都不允许有电平变化

为了确保 I2C 通信中的起始条件(START)和重复起始条件(REPEATED START)可以正确生成。这里的过程可以分为以下几个步骤:

  1. 释放 SDA:在拉低 SCL 之前,首先确保数据线 SDA 处于高电平。这意味着当前没有数据传输,并且 SDA 线处于空闲状态。

  2. 释放 SCL:在确保 SDA 线高电平后,接下来释放 SCL 线。此时,SDA 和 SCL 都应该是高电平,表明总线处于空闲状态。

  3. 拉低 SDA:现在可以安全地将 SDA 拉低,表示开始一个新的数据传输。

  4. 拉低 SCL:最后,拉低 SCL,这样就形成了一个起始条件(START)或重复起始条件(REPEATED START)。

担心先将SCL拉高 ,然后如果SDA是低再拉高产生终止条件 ,所以要先拉高SDA
这样这个start就可以兼容起始条件和重复起始条件了
永远记住在SCL低电平调整SDA,因为SCL在每次操作之后我们都会拉低。所以这里确定SCL为低电平,于是先释放SDA,避免当SDA为低电平时先释放SCL造成停止

void MyI2C_Start(void)
{	
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

发送和接收一个字节

实际上啊除了终止条件SCL以高电平结束,所有的单元我们都会保证SCL以低电平结束,这样方便各个单元的拼接

若要提取任意第 n 位的值,通用的操作步骤如下:

  1. 创建掩码mask = 1 << n,其中 n 是你想提取的位的索引。
  2. 按位与result = data & mask
  3. 移位(可选)bit_value = (result >> n)

示例:提取第 n 位的通用代码

uint8_t get_nth_bit(uint8_t data, uint8_t n) {
    uint8_t mask = 1 << n;     // 创建掩码
    uint8_t result = data & mask; // 按位与提取第 n 位
    return result >> n;        // 返回第 n 位的值(0 或 1)
}
#include <stdio.h>
#include <stdint.h>  // 添加头文件以使用 uint8_t

uint8_t get_nth_bit(uint8_t data, uint8_t n) {
    uint8_t mask = 1 << n;     // 创建掩码
    uint8_t result = data & mask; // 按位与提取第 n 位
    return result >> n;        // 返回第 n 位的值(0 或 1)
}

int main() {
    uint8_t data = 0b10101010;  // 数据:170 (二进制表示:10101010)
    int i = get_nth_bit(data, 3); // 取出 data 的第 3 位
    printf("%d\n", i);  // 使用 printf 输出结果
    return 0;
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for(i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t  Byte = 0x00;
	uint8_t i;
	MyI2C_W_SDA(1);
	for(i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);
		if(MyI2C_R_SDA() == 1)
		{
			Byte = Byte | (0x80 >> i);
		}
		MyI2C_W_SCL(0);
	}
	
	return Byte;
}

 发送应答和接收应答
 

发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}

I2C扫描总线上设备

​
​
void I2C_ScanBus(void) {
    uint8_t address;
    uint8_t ack;

    // 遍历所有可能的 I2C 7位地址 (0x00 - 0x7F)
    for (address = 0x00; address <= 0x7F; address++) {
        MyI2C_Start();  // 发送 I2C 起始信号

        // 发送地址,注意左移1位并加上0表示写操作
        MyI2C_SendByte(address << 1);

        ack = MyI2C_ReceiveAck();  // 检查是否有ACK应答

        if (ack == 0) {  // 如果接收到ACK,表示该地址有设备
            OLED_ShowHexNum(1, 1, address, 2);
        }

        MyI2C_Stop();  // 发送 I2C 停止信号
    }
}


​

​

mup6050的函数

指定地址写

对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)

#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)  
{
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(Data);
	MyI2C_ReceiveAck();
	MyI2C_Stop();
}

 

指定地址读

对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)

 

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	
	MyI2C_Start();						//I2C重复起始
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_ReceiveAck();					//接收应答
	Data = MyI2C_ReceiveByte();			//接收指定寄存器的数据
	MyI2C_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_Stop();						//I2C终止
	
	return Data;
}

 

 mpu6050的寄存器的配置

void MPU6050_Init()
{
	MyI2C_Init();
	MyI2C_Init();		
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);		//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);		//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);		//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);			//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);	//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);	//加速度计配置寄存器,选择满量程为±16g
}

 

 获取mpu6050寄存器的数据

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;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "MPU6050.h"
uint8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;
int main(void)
{
	OLED_Init();
	MPU6050_Init();
	
	while(1)
	{
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
		
		OLED_ShowSignedNum(2, 1, AX, 5);					//OLED显示数据
		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);
	}
}


http://www.kler.cn/news/359846.html

相关文章:

  • PROFIENT开发和Ethernet IP开发—嵌入式板卡:赋予通讯设备之间的神奇力量
  • 机器学习与深度学习1:神经网络概念
  • 高级java每日一道面试题-2024年10月19日-消息队列[RabbitMQ]-RabbitMQ中积压了大量的消息,如何处理?
  • 相同的树算法
  • 手机ip地址怎么切换城市
  • Zookeeper 快速入门到实战
  • VAS1802奇力线性芯片LED驱动芯片车规认证AEC-Q100
  • 深度学习-模型部署
  • python 爬虫 入门 四、线程,进程,协程
  • Go 切片详解
  • 智能安全配电装置在老旧建筑防火中的应用
  • 解决Git合并冲突:掌握版本控制的精髓
  • (计算机毕设)基于SpringBoot的就业平台开题报告
  • MATLAB | 绘图复刻(十八) | K-means 聚类分组热图
  • 【MySQL数据库】MySQL高级语句(SQL语句进阶版)
  • Hive优化:Hive的执行计划、分桶、MapJoin、数据倾斜
  • 大模型输出的outputs为什么要取[0](即outputs[0])
  • STM32G4系列MCU的启动项配置
  • CIM系统:智慧城市的数字基石
  • 鸿蒙--进度条通知