软件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)可以正确生成。这里的过程可以分为以下几个步骤:
-
释放 SDA:在拉低 SCL 之前,首先确保数据线 SDA 处于高电平。这意味着当前没有数据传输,并且 SDA 线处于空闲状态。
-
释放 SCL:在确保 SDA 线高电平后,接下来释放 SCL 线。此时,SDA 和 SCL 都应该是高电平,表明总线处于空闲状态。
-
拉低 SDA:现在可以安全地将 SDA 拉低,表示开始一个新的数据传输。
-
拉低 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 位的值,通用的操作步骤如下:
- 创建掩码:
mask = 1 << n,其中n是你想提取的位的索引。 - 按位与:
result = data & mask。 - 移位(可选):
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_tuint8_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); //配置寄存器,配置DLPFMPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器,选择满量程为±2000°/sMPU6050_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);}
}
相关文章:
软件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_PinGPIO_Pin_10 | GPIO_Pin_…...
登录时用户名密码加密传输(包含前后端代码)
页面输入用户名密码登录过程中,如果没有对用户名密码进行加密处理,可能会导致传输过程中数据被窃取,就算使用https协议,在浏览器控制台的Request Payload中也是能直接看到传输的明文,安全感是否还是不足。 大致流程&a…...
ai聊天对话页面-uniapp
流式传输打字机效果,只支持uniapp内使用 ,下载地址 https://download.csdn.net/download/qq_54123885/89899859...
虚拟滚动列表如何实现?
highlight: a11y-dark 虚拟滚动列表,虚拟滚动的关键在于只渲染当前视口内可见的数据项,而不是一次性渲染所有数据项。这可以显著提高性能,尤其是在处理大量数据时。 以下是一个完整的虚拟滚动列表的示例代码: <!DOCTYPE htm…...
07_Linux网络配置与管理:命令与工具指南
本系列文章导航:01_Linux基础操作CentOS7学习笔记-CSDN博客 文章目录 网络配置与管理:命令与工具指南1. ping命令2. ifconfig命令3. ip命令4. route命令5. ip route命令6. nslookup命令7. nmcli命令8. nmtui命令9. RHEL7修改网卡名1. 修改网络(会话)配置…...
首个统一生成和判别任务的条件生成模型框架BiGR:专注于增强生成和表示能力,可执行视觉生成、辨别、编辑等任务
BiGR是一种新型的图像生成模型,它可以生成高质量的图像,同时还能有效地提取图像特征。该方法是通过将图像转换为一系列的二进制代码来工作,这些代码就像是图像的“压缩版”。在训练时会遮住一些代码,然后让模型学习如何根据剩下的…...
【Java知识】Java进阶-服务发现机制SPI
文章目录 SPI概述SPI 工作原理 ServiceLoader代码展示简化的 ServiceLoader 类关键点解释使用示例1. 定义服务接口2. 实现服务提供者3. 配置文件4. 加载服务提供者 总结 SPI使用场景1. 数据库驱动2. 日志框架3. 图像处理4. 加密算法5. 插件系统6. 缓存机制示例代码1. 定义服务接…...
多模态技术的协同表现:从文本生成、语音合成到口型同步综合测评
本文是针对多模态对话系统核心技术栈的使用效果和网络测评整理。 测评内容基于用户体验,侧重于从使用者角度出发,讨论实际操作中的体验感受,如技术的易用性、输出效果如文本的连贯性、语音的自然度、口型同步的准确性等。不涉及具体算法架构…...
Java最全面试题->Java主流框架->Srping面试题
Spring面试题 下边是我自己整理的面试题,基本已经很全面了,想要的可以私信我,我会不定期去更新思维导图 哪里不会点哪里 谈谈你对 Spring 的理解? Spring 是一个开源框架,为简化企业级应用开发而生。Spring 可以是使简单的 JavaBean 实现以前只有 EJB 才能实现的功能。…...
参编国家标准需要注意的事项有哪些?
1. 项目相关性: • 选择与自身企业产品、业务或专业领域紧密相关的国家标准进行参编。这样不仅能确保企业在标准制定过程中发挥自身的优势和专长,使参编工作更有实际意义和价值,也有利于企业将标准更好地应用于自身的生产经营活动,…...
【Dash】feffery_antd_components 按钮组件的应用
一、feffery_antd_componenet 中的 AntdFloatButton 和 AntdFloatButtonGroup AntdFloatButton 和 AntdFloatButtonGroup 是两个用于创建悬浮按钮和悬浮按钮组的组件。 AntdFloatButton 是单个悬浮按钮组件,它提供了多种属性来定义按钮的外观及行为。AntdFloatBut…...
01 springboot-整合日志(logback-config.xml)
logback-config.xml 是一个用于配置 Logback 日志框架的 XML 文件,通常位于项目的 classpath 下的根目录或者 src/main/resources 目录下。 Logback 提供了丰富的配置选项,可以满足各种不同的日志需求。需要根据具体情况进行配置。 项目创建࿰…...
Java最全面试题->计算机基础面试题->计算机网络面试题
计算机网络 下边是我自己整理的面试题,基本已经很全面了,想要的可以私信我,我会不定期去更新思维导图 哪里不会点哪里 1.说一下TCP/IP四层模型 TCP/IP协议是美国国防部高级计划研究局为实现ARPANET互联网而开发的。 网络接口层ÿ…...
VSCode编译器改为中文
1. 通过快捷键设置中文 打开命令面板:按住键盘上的CtrlShiftP组合键,打开命令面板。 输入并设置语言:在命令面板中输入Configure Display Language。 点击Configure Display Language选项。 在弹出的语言选择列表中,选择zh-cn…...
前端开发设计模式——状态模式
目录 一、状态模式的定义和特点 二、状态模式的结构与原理 1.结构: 2.原理: 三、状态模式的实现方式 四、状态模式的使用场景 1.按钮的不同状态: 2.页面加载状态: 3.用户登录状态: 五、状态模式的优点 1.提…...
特种作业操作烟花爆竹试题分享
1.(单选题)职业卫生研究的是人类从事各种职业劳动过程中的( )。 A.健康问题 B.环境问题 C.卫生问题 答案:C 2.(单选题)安全生产事关人民群众的( )安全,事关改革发展和…...
实现prometheus+grafana的监控部署
直接贴部署用的文件信息了 kubectl label node xxx monitoringtrue 创建命名空间 kubectl create ns monitoring 部署operator kubectl apply -f operator-rbac.yml kubectl apply -f operator-dp.yml kubectl apply -f operator-crd.yml # 定义node-export kubectl app…...
确保Spring Boot定时任务只执行一次方案
在Spring Boot项目中,确保定时任务只执行一次是一个常见的需求。这种需求可以通过多种方式来实现,以下是一些常见的方法,它们各具特点,可以根据项目的实际需求来选择最合适的方法。 1. 使用Scheduled注解并设置极大延迟 一种简单…...
【Python数据可视化】利用Matplotlib绘制美丽图表!
【Python数据可视化】利用Matplotlib绘制美丽图表! 数据可视化是数据分析过程中的重要步骤,它能直观地展示数据的趋势、分布和相关性,帮助我们做出明智的决策。在 Python 中,Matplotlib 是最常用的可视化库之一,它功能…...
【最新通知】2024年Cisco思科认证CCNA详解
CCNA现在涵盖安全性、自动化和可编程性。该计划拥有一项涵盖IT职业基础知识的认证,包括一门考试和一门培训课程,助您做好准备。 CCNA培训课程和考试最近面向最新技术和工作岗位进行了重新调整,为您提供了向任何方向发展事业所需的基础。CCNA认…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
