STM32设置为I2C从机模式(HAL库版本)
STM32设置为I2C从机模式(HAL库版本)
目录
- STM32设置为I2C从机模式(HAL库版本)
- 前言
- 1 硬件连接
- 2 软件编程
- 2.1 步骤分解
- 2.2 测试用例
- 3 运行测试
- 3.1 I2C连续写入
- 3.2 I2C连续读取
- 3.3 I2C单次读写测试
- 4 总结
前言
我之前出过一篇关于STM32设置为I2C从机的博客,现在应粉丝要求,出一篇HAL库版本的I2C从机编程。
基于官方库版本的可以看下我之前发的文章:STM32设置为I2C从机模式
1 硬件连接
测试芯片:STM32F103RCT6
测试方法:用一个USB转I2C的工具接到STM32的I2C引脚上,通过上位机工具进行读写操作。如果没有这个工具,也可以用另外一组I2C作为主机或者其他设备测试通讯,同时也可以借助示波器或者逻辑分析仪来辅助调试。
硬件连接:
STM32这边使用硬件I2C1(PB6、PB7),并外接上拉电阻。


本次测试中使用的USB转I2C的工具如下图所示:

2 软件编程
2.1 步骤分解
1、初始化I2C配置
注:除了最后的HAL_I2C_EnableListen_IT()函数,其他代码都可以用STM32CubeMX自动生成
参考代码:
static void MX_I2C1_Init(void)
{hi2c1.Instance = I2C1; // 配置I2C1 hi2c1.Init.ClockSpeed = 100000; // 时钟频率:100k hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比:1/2 hi2c1.Init.OwnAddress1 = 0x80; // 本机地址:0x80(若作为从设备则是从机地址) hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 地址模式:7位 hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 禁止双地址 hi2c1.Init.OwnAddress2 = 0; // 第二地址 hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 禁止广播 hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 禁止时钟拉伸 if (HAL_I2C_Init(&hi2c1) != HAL_OK) // I2C1初始化 { Error_Handler(); } HAL_I2C_EnableListen_IT(&hi2c1); // 使能I2C1的侦听中断
}
2、初始化I2C引脚和中断
参考代码:
注:这个代码可以用STM32CubeMX自动生成
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(hi2c->Instance==I2C1){// 配置GPIO__HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);// 配置I2C中断/* Peripheral clock enable */__HAL_RCC_I2C1_CLK_ENABLE();/* I2C1 interrupt Init */HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0); // 事件中断(必须有)HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0); // 错误中断(非必须)HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);}
}
3、配置I2C中断服务函数
参考代码:
注:这个代码可以用STM32CubeMX自动生成
// I2C1事件中断服务函数(必须有)
void I2C1_EV_IRQHandler(void)
{HAL_I2C_EV_IRQHandler(&hi2c1);
}// I2C1错误中断服务函数(非必须)
void I2C1_ER_IRQHandler(void)
{HAL_I2C_ER_IRQHandler(&hi2c1);
}
4、配置I2C从机回调处理函数
参考代码:
static uint8_t ram[256]; // 模拟I2C从机数据寄存器(主机读写的数据都放在这块内存)
uint8_t offset; // 从机寄存器当前偏移地址
static uint8_t first_byte_state = 1; // 是否收到第1个字节,也就是偏移地址(0:已收到,1:没有收到)// 侦听完成回调函数(完成一次完整的i2c通信以后会进入该函数)
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{// 完成一次通信,清除状态first_byte_state = 1;offset = 0;HAL_I2C_EnableListen_IT(hi2c); // slave is ready again
}// I2C设备地址回调函数(地址匹配上以后会进入该函数)
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{if(TransferDirection == I2C_DIRECTION_TRANSMIT) {// 主机发送,从机接收if(first_byte_state) {// 准备接收第1个字节数据HAL_I2C_Slave_Seq_Receive_IT(hi2c, &offset, 1, I2C_NEXT_FRAME); // 每次第1个数据均为偏移地址} } else {// 主机接收,从机发送HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME); // 打开中断并把ram[]里面对应的数据发送给主机}
}// I2C数据接收回调函数(在I2C完成一次接收时会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{if(first_byte_state) {// 收到的第1个字节数据(偏移地址)first_byte_state = 0;} else {// 收到的第N个字节数据offset++; // 每收到一个数据,偏移+1}// 打开I2C中断接收,下一个收到的数据将存放到ram[offset]HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME); // 接收数据存到ram[]里面对应的位置
}// I2C数据发送回调函数(在I2C完成一次发送后会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{offset++; // 每发送一个数据,偏移+1HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME); // 打开中断并把ram[]里面对应的数据发送给主机
}
2.2 测试用例
1、测试方法
使用USB转I2C的工具接入到MCU的I2C上面,然后使用上位机工具进行读写操作,最后通过串口把I2C通讯过程中的几个重要节点打印出来,验证结果是否正确。
2、测试程序
其实和上面讲解的代码是一样的,只是初始化时先把ram[]赋初值。
参考测试代码:
#include "stm32f1xx_hal.h"static uint8_t ram[256]; // 模拟I2C从机数据寄存器(主机读写的数据都放在这块内存)
uint8_t offset; // 从机寄存器当前偏移地址
static uint8_t first_byte_state = 1; // 是否收到第1个字节,也就是偏移地址(0:已收到,1:没有收到)// 侦听完成回调函数(完成一次完整的i2c通信以后会进入该函数)
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{// 完成一次通信,清除状态first_byte_state = 1;offset = 0;HAL_I2C_EnableListen_IT(hi2c); // slave is ready again
}// I2C设备地址回调函数(地址匹配上以后会进入该函数)
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{if(TransferDirection == I2C_DIRECTION_TRANSMIT) {// 主机发送,从机接收if(first_byte_state) {// 准备接收第1个字节数据HAL_I2C_Slave_Seq_Receive_IT(hi2c, &offset, 1, I2C_NEXT_FRAME); // 每次第1个数据均为偏移地址} } else {// 主机接收,从机发送HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME); // 打开中断并把ram[]里面对应的数据发送给主机}
}// I2C数据接收回调函数(在I2C完成一次接收时会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{if(first_byte_state) {// 收到的第1个字节数据(偏移地址)first_byte_state = 0;} else {// 收到的第N个字节数据offset++; // 每收到一个数据,偏移+1}// 打开I2C中断接收,下一个收到的数据将存放到ram[offset]HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME); // 接收数据存到ram[]里面对应的位置
}// I2C数据发送回调函数(在I2C完成一次发送后会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{offset++; // 每发送一个数据,偏移+1HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME); // 打开中断并把ram[]里面对应的数据发送给主机
}// 测试用例:初始化把ram设置为从0到255的数
void i2c_test(void)
{for (uint16_t i = 0; i < 256; i++){ram[i] = i;}
}
3 运行测试
3.1 I2C连续写入
通过上位机工具写入:

通过逻辑分析仪抓取波形:

3.2 I2C连续读取
通过上位机工具连续读取256字节:

通过逻辑分析仪抓取波形:


3.3 I2C单次读写测试
通过上位机工具读取原值,再写入新值,最后再读取新值:

通过逻辑分析仪抓取波形:

4 总结
通过上位机工具的测试以及逻辑分析仪的解析,STM32的硬件I2C从机通信正常且稳定,读写速度测试了100k和400k,没有发现问题,至此测试完成。
好了,关于STM32如何设置从机模式就介绍到这里,如果你们有什么问题,欢迎评论区留言。
需要完整源码工程的同学可以自行下载:源码下载地址
如果这篇文章能够帮到你,就…懂的。

相关文章:
STM32设置为I2C从机模式(HAL库版本)
STM32设置为I2C从机模式(HAL库版本) 目录 STM32设置为I2C从机模式(HAL库版本)前言1 硬件连接2 软件编程2.1 步骤分解2.2 测试用例 3 运行测试3.1 I2C连续写入3.2 I2C连续读取3.3 I2C单次读写测试 4 总结 前言 我之前出过一篇关于…...
牛客网Verilog刷题 | 入门特别版本
文章目录 1、 VL1 输出12、VL2 wire连线3、 VL3 多wire连接4、VL4 反相器5、VL5 与门6、VL6 NOR 门7、VL7 XOR 门8、VL8 逻辑运算10、VL10 逻辑运算211、VL11 多位信号12、VL12 信号顺序调整13、VL13 位运算与逻辑运算14、VL14 对信号按位操作15、VL15 信号级联合并16、VL16 信…...
ROS通信机制之话题(Topics)的发布与订阅以及自定义消息的实现
我们知道在ROS中,由很多互不相干的节点组成了一个复杂的系统,单个的节点看起来是没起什么作用,但是节点之间进行了通信之后,相互之间能够交互信息和数据的时候,就变得很有意思了。 节点之间进行通信的一个常用方法就是…...
容灾设备系统组成,容灾备份系统组成包括哪些
随着信息技术的快速发展,企业对数据的需求越来越大,数据已经成为企业的核心财产。但是,数据安全性和完整性面临巨大挑战。在这种环境下,容灾备份系统应运而生,成为保证企业数据安全的关键因素。下面我们就详细介绍容灾…...
腾讯云服务器租用价格表_一年、1个月和1小时报价明细
腾讯云服务器租用费用表:轻量应用服务器2核2G4M带宽112元一年,540元三年、2核4G5M带宽218元一年,2核4G5M带宽756元三年、云服务器CVM S5实例2核2G配置280.8元一年、GPU服务器GN10Xp实例145元7天,腾讯云服务器网长期更新腾讯云轻量…...
【java安全】JNDI注入概述
文章目录 【java安全】JNDI注入概述什么是JNDI?JDNI的结构InitialContext - 上下文Reference - 引用 JNDI注入JNDI & RMI利用版本:JNDI注入使用Reference 【java安全】JNDI注入概述 什么是JNDI? JNDI(Java Naming and Directory Interf…...
零基础如何使用IDEA启动前后端分离中的前端项目(Vue)?
一、在IDEA中配置vue插件 点击File-->Settings-->Plugins-->搜索vue.js插件进行安装,下面的图中我已经安装好了 二、搭建node.js环境 安装node.js 可以去官网下载:安装过程就很简单,直接下一步就行 测试是否安装成功:要…...
laravel实现AMQP(rabbitmq)生产者以及消费者
基于php-amqplib/php-amqplib组件适配laravel框架的amqp封装库 支持便捷可配置的队列工作模式 官网详情 在此基础上可支持延迟消息、死信队列等机制。 环境要求: PHP版本: ^7.3|^8.0 需要开启的扩展: socket 其他: 如果需要实现延迟任务需要安装对应版本的ra…...
LeetCode——二叉树篇(九)
刷题顺序及思路来源于代码随想录,网站地址:https://programmercarl.com 目录 669. 修剪二叉搜索树 108. 将有序数组转换为二叉搜索树 538. 把二叉搜索树转换为累加树 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root ,同时给定最小边界…...
uniapp scroll-view横向滚动无效,scroll-view子元素flex布局不生效
要素排查: 1.scroll-x属性需要开启,官方类型是Boolean,实际字符串也行。 2scroll-view标签需要给予一个固定宽度,可以是百分百也可以是固定宽度或者100vw。 3.子元素需要设置display: inline-block(行内块元素&#x…...
无涯教程-进程 - 简介
进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问的介质呢?进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。另外,系统空间是“公共场所”,各进…...
HTML番外篇(四)-HTML5新增元素-CSS常见函数-理解浏览器前缀-BFC
一、HTML5新增元素 1.HTML5语义化元素 在HMTL5之前,我们的网站分布层级通常包括哪些部分呢? header、nav、main、footer ◼ 但是这样做有一个弊端: 我们往往过多的使用div, 通过id或class来区分元素;对于浏览器来说这些元素不…...
机器学习之Adam(Adaptive Moment Estimation)自适应学习率
Adam(Adaptive Moment Estimation)是一种常用的优化算法,特别适用于训练神经网络和深度学习模型。它是一种自适应学习率的优化算法,可以根据不同参数的梯度信息来动态调整学习率,以提高训练的效率和稳定性。 Adam算法…...
深入理解Linux权限管理:保护系统安全的重要措施
Linux操作系统以其稳定性、可靠性和灵活性而受到广泛使用。其中一个关键特性是其强大的权限管理系统,它可以保护系统资源和用户数据的安全性。本文将深入探讨Linux权限管理的概念、原则和实践,帮助您理解如何正确配置和管理权限,以确保系统的…...
kafka复习:(20):消费者拦截器的使用
一、定义消费者拦截器(只消费含"sister"的消息) package com.cisdi.dsp.modules.metaAnalysis.rest;import org.apache.kafka.clients.consumer.ConsumerInterceptor; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.…...
水库大坝安全监测的主要内容包括哪些?
在水库大坝的实时监测中,主要任务是通过无线传感网络监测各个监测点的水位、水压、渗流、流量、扬压力等数据,并在计算机上用数据模式或图形模式进行实时反映,以掌握整个水库大坝的各项变化情况。大坝安全监测系统能实现全天候远程自动监测&a…...
Cadence软件屏幕显示问题
问题 就是今天打开Cadence软件想导出网表看一下,发现没有显示确定按钮什么的,那个窗口也是无语,不能移动,缩放也只能左右缩放,还不能缩小什么的,真的醉了,后面就是调整窗口的分辨率。 因为我最…...
访问服务器快慢的因素
我们在租用服务器的过程中,可能在访问速度方面,会受到某些因素影响,如果您要进行此项业务,进行一些简单的了解 是非常的有必要的,下面壹基比小鑫带大家一起去做个具体的探讨吧。 对于服务器不太了解的都认为࿰…...
vue(element ui安装)
目录 一,element ui安装二,main.js三,使用element ui最后 一,element ui安装 先在盘服中找到你创建的node的位置 如有不懂根据可以看看上一章安装node 然后在终端找到 进入这个位置之后就可以安装了 输入npm i element-ui -S这个…...
基于FPGA视频接口之HDMI2.0编/解码
简介 为什么要特别说明HDMI的版本,是因为HDMI的版本众多,代表的HDMI速度同样不同,当前版本在HDMI2.1速度达到48Gbps,可以传输4K及以上图像,但我们当前还停留在1080P@60部分,且使用的芯片和硬件结构有很大差别,故将HDMI分为两个部分说明1080@60以下分辨率和4K以上分辨率(…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
