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如何设置从机模式就介绍到这里,如果你们有什么问题,欢迎评论区留言。

需要完整源码工程的同学可以自行下载:源码下载地址

如果这篇文章能够帮到你,就…懂的。
请添加图片描述

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

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

相关文章

Claude 2 国内镜像站

Claudeai是什么&#xff1f; Claude 2被称为ChatGPT最强劲的竞争对手&#xff0c;支持100K上下文对话&#xff0c;并且可以同时和5个文档进行对话&#xff0c;不过国内目前无法正常实用的&#xff0c;而claudeai是一个Claude 2 国内镜像站&#xff0c;并且免翻可用&#xff0…

实验三 HBase1.2.6安装及配置

系列文章目录 文章目录 系列文章目录前言一、HBase1.2.6的安装二、HBase1.2.6的配置2.1 单机模式配置2.2 伪分布式模式配置 总结参考 前言 在安装HBase1.2.6之前&#xff0c;需要安装好hadoop2.7.6。 本篇文章参考&#xff1a;HBase2.2.2安装和编程实践指南 一、HBase1.2.6的安…

Android---- 一个完整的小项目(消防app)

前言&#xff1a; 针对不同群体的需求&#xff0c;想着应该拓展写方向。医疗app很受大家喜欢&#xff0c;就打算顺手写个消防app&#xff0c;里面基础框架还是挺简洁 规整的。登陆注册和本地数据库写的便于大家理解。是广大学子的毕设首选啊&#xff01; 此app主要为了传递 消防…

代码随想录打卡—day24—【回溯】— 基础最新8.20+8.22

1 理论基础 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯算法——回溯和递归是相辅相成的。回溯法的效率&#xff0c;回溯法其实就是暴力查找&#xff0c;并不是什么高效的算法。回溯法解决的问题都可以抽象为树形结构&#xff08;N叉树&#xff09; 1.1…

redis 7高级篇1 redis的单线程与多线程

一 redis单线程与多线程 1.1 redis单线程&多线程 1.redis的单线程 redis单线程主要是指Redis的网络IO和键值对读写是由一个线程来完成的&#xff0c;Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理…

【第三阶段】kotlin语言的内置函数let

1.使用普通方法对集合的第一个元素相加 fun main() {//使用普通方法对集合的第一个元素相加var list listOf(1,2,3,4,5)var value1list.first()var resultvalue1value1println(result) }执行结果 2.使用let内置函数对集合的第一个元素相加 package Stage3fun main() {//使用…

Android进阶之路 - 去除EditText内边距

正如题名&#xff0c;在Android中的EditText是自带内边距的&#xff0c;常规而言设置背景为null即可&#xff0c;但是因为使用了并不熟悉的声明式框架&#xff0c;本是几分钟解决的事儿&#xff0c;却花费了小半天~ 简单的需求&#xff0c;相关blog Android进阶之路 - 去除Edi…

探索智能文字识别:技术、应用与发展前景

探索智能文字识别&#xff1a;技术、应用与发展前景 前言一张图全览大赛作品解读随心记你不对我对小结 智能文字识别体系化解读图像预处理文字定位和分割文字区域识别图像校正字体识别和匹配结果后处理小结 如何应对复杂场景下挑战复杂场景应对方法小结 人才时代对人才要求合合…

MyBatis快速入门以及环境搭建和CRUD的实现

目录 前言 一、MyBatis简介 1.MyBatis是什么 2.MyBatis的特点 3.mybatis的作用 4.MyBatis的应用场景 5.MyBatis优缺点 二、相关概念 1.ORM概述 2.常见的ORM框架 3.什么是持久层框架 三、MyBatis的工作原理 1.框架交互 2.工作原理 ​编辑 四、MyBatis环境搭建 1…

Linux Kernel 4.12 或将新增优化分析工具

到 7 月初&#xff0c;Linux Kernel 4.12 预计将为修复所有安全漏洞而奠定基础&#xff0c;另外新增的是一个分析工具&#xff0c;对于开发者优化启动时间时会有所帮助。 新的「个别任务统一模型」&#xff08;Per-Task Consistency Model&#xff09;为主要核心实时修补&#…

软件开发之低代码平台实践

一、低代码、零代码、纯代码定义 低代码开发平台使企业在不编写大量代码的情况下快速创建复杂应用程序。与传统的纯代码开发相比&#xff0c;低代码开发能够大大减少开发周期&#xff0c;并降低技术门槛&#xff0c;使得开发过程更加高效。而零代码开发更进一步简化了开发过程&…

C++信息学奥赛1121:计算矩阵边缘元素之和

题解&#xff1a;i0 or j0 or in-1 or jm-1 or in-1 or jm-1 代码&#xff1a; #include<iostream> // 包含输入输出流库 #include<cmath> // 包含数学函数库 using namespace std; // 使用标准命名空间int main() {int n,m;cin>>n>>m; // 输入…

【Java从0到1学习】10 Java常用类汇总

1. System类 System类对读者来说并不陌生&#xff0c;因为在之前所学知识中&#xff0c;需要打印结果时&#xff0c;使用的都是“System.out.println();”语句&#xff0c;这句代码中就使用了System类。System类定义了一些与系统相关的属性和方法&#xff0c;它所提供的属性和…

基础论文学习(2)——DETR

目标检测 DETR&#xff1a;End-to-End Detection with Transformer detr是facebook提出的引入transformer到目标检测领域的算法&#xff0c;效果很好&#xff0c;做法也很简单&#xff0c;相较于RCNN和YOLO系列算法&#xff0c;避免了Proposal/AnchorNMS的复杂流程。 1. detr…

Faster RCNN网络数据流总结

前言 在学习Faster RCNN时&#xff0c;看了许多别人写的博客。看了以后&#xff0c;对Faster RCNN整理有了一个大概的了解&#xff0c;但是对训练时网络内部的数据流还不是很清楚&#xff0c;所以在结合这个版本的faster rcnn代码情况下&#xff0c;对网络数据流进行总结。以便…

负载均衡下的webshell

文章目录 1.场景描述2.在蚁剑里添加 Shell3.因为负载均衡而出现的问题4.问题解决方案4.1 方案14.2 方案24.3 方案3 1.场景描述 当前手里有一个以docker部署的Tomcat负载均衡环境。主机对外ip和端口为192.168.100.130:18080 我们假设其为一个真实的业务系统&#xff0c;存在一…

连接pgsql数据库 sslmode sslrootcert sslkey sslcert 参数的作用

sslmode 参数的作用 sslmode 参数用于指定数据库连接时使用的 SSL 加密模式。SSL&#xff08;Secure Sockets Layer&#xff09;是一种加密协议&#xff0c;用于保护数据在客户端和服务器之间的传输过程&#xff0c;以增加数据传输的安全性。sslmode 参数可以设置不同的值&…

阿拉伯国家-中国经贸合作创新中心揭牌仪式在阿联酋迪拜举行

当地时间8月16日&#xff0c;阿拉伯国家-中国经贸合作创新中心揭牌仪式在迪拜举行。中心的成立将为 “一带一路”中国与沿线各国提供科技交流和经贸合作的高质量平台。中国国际科技促进会军民科技产业委员会主任林志东代表中国国际科技促进会宣读了阿拉伯国家-中国经贸合作创新…

innovus如何设置size only

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 给instance设置size only属性命令如下: dbset [dbGet top.inst.name aa/bb -p] .dontTouch sizeOk 给一个module设置size only需要foreach循环一下: foreach inst [dbGet top.…

灰鸽子远程控制病毒实验

一、实验目的&#xff1a; 1、掌握经典远控木马的原理 2、掌握“灰鸽子”木马的使用方法 二、预备知识&#xff1a; “灰鸽子”是现在网络上非常流行的一种木马&#xff0c;由两部分组成&#xff0c;一是控制端&#xff08;主程序&#xff09;&#xff0c;一是服务端&…