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.rhkb.cn/news/104609.html

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

相关文章

XXX程序 详细说明

用于记录理解PC程序的程序逻辑 1、程序的作用 根据原作者的说明&#xff08;文件说明.txt&#xff09;&#xff0c;该程序 (PC.py) 的主要作用是提取某一个文件夹中的某个设备 (通过config中的信息看出来是Ag_T_8) 产生的日志文件&#xff0c;然后提取其中某些需要的数据&…

内网渗透神器CobaltStrike之权限提升(七)

Uac绕过 常见uac攻击模块 UAC-DLL UAC-DLL攻击模块允许攻击者从低权限的本地管理员账户获得更高的权限。这种攻击利用UAC的漏洞&#xff0c;将ArtifactKit生成的恶意DLL复制到需要特权的位置。 适用于Windows7和Windows8及更高版本的未修补版本 Uac-token-duplication 此攻…

Web3和去中心化:互联网的下一个演化阶段

文章目录 Web3和去中心化的定义Web3&#xff1a;去中心化&#xff1a; 为什么Web3和去中心化如此重要&#xff1f;数据隐私和安全&#xff1a;去中心化的创新&#xff1a;去除中间商&#xff1a; Web3和去中心化的应用领域去中心化金融&#xff08;DeFi&#xff09;&#xff1a…

Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)

文章目录 前言一、引入Modbus模块二、Modbus设备的连接三、各寄存器数据的读取四、各寄存器数据的写入五、示例完整代码总结 前言 本文主要讲述了使用Qt的Modbus模块来进行ModbusTcp的通信&#xff0c;实现对PLC的线圈寄存器和保持寄存器的读写&#xff0c;基于TCP/IP的Modbus…

设计模式大白话——命令模式

命令模式 一、概述二、经典举例三、代码示例&#xff08;Go&#xff09;四、总结 一、概述 ​ 顾名思义&#xff0c;命令模式其实和现实生活中直接下命令的动作类似&#xff0c;怎么理解这个命令是理解命令模式的关键&#xff01;&#xff01;&#xff01;直接说结论是很不负责…

MindManager

MindManager 简介下载安装crack 简介 MindManager是一款由Mindjet公司开发的思维导图软件。思维导图是一种图形化的方法&#xff0c;用于在一个中心主题周围组织和呈现各种相关思想、想法和信息。MindManager允许用户创建、编辑和共享思维导图&#xff0c;以帮助他们更好地组织…

【Unity3D赛车游戏】【四】在Unity中添加阿克曼转向,下压力,质心会让汽车更稳定

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

SMC状态机 讲解2 从模型到SMC

SMC状态机 讲解2 从模型到SMC 1、实例化有限状态机&#xff08;FSM)2、简单转换 Simple Transition3、外部环回转换 External Loopback Transition4、内部环回转换 Internal Loopback Transition5、转换动作6、转换Guard7、转换参数8、Entry 和 Exit动作9、Push 转换10、Pop转换…

鼠标拖拽盒子移动

目录 需求思路代码页面展示【补充】纯js实现 需求 浮动的盒子添加鼠标拖拽功能 思路 给需要拖动的盒子添加鼠标按下事件鼠标按下后获取鼠标点击位置与盒子边缘的距离给 document 添加鼠标移动事件鼠标移动过程中&#xff0c;将盒子的位置进行重新定位侦听 document 鼠标弹起&a…

【1-3章】Spark编程基础(Python版)

课程资源&#xff1a;&#xff08;林子雨&#xff09;Spark编程基础(Python版)_哔哩哔哩_bilibili 第1章 大数据技术概述&#xff08;8节&#xff09; 第三次信息化浪潮&#xff1a;以物联网、云计算、大数据为标志 &#xff08;一&#xff09;大数据 大数据时代到来的原因…

Docker环境安装elasticsearch和kibana

一、安装elasticsearch 创建es-network&#xff0c;让es、kibana在同一个网段&#xff1a; docker network create --driverbridge --subnet192.168.1.10/24 es-network运行elasticsearch docker run -d \ --name elasticsearch \ # 容器名 --hostname elasticsearch # 主机…

【开发笔记】ubuntu部署指定版本的前后端运行环境(npm nodejs mysql)

目录 1 背景2 环境要求3 部署流程3.1 npm的安装3.2 nodejs的安装3.3 MySQL的安装 4 可能的问题 1 背景 在远程服务器上的Ubuntu系统中&#xff0c;部署指定版本的前后端项目的运行环境 2 环境要求 npm 9.5.1Nodejs v18.16.1MySQL 8.0.33 3 部署流程 3.1 npm的安装 通过安…

Vue3新特性

认识vue3 1. Vue2 选项式 API vs Vue3 组合式API <script> export default {data(){return {count:0}},methods:{addCount(){this.count}} } </script><script setup> import { ref } from vue const count ref(0) const addCount ()> count.value &l…

安装docker服务及docker基本操作

一、docker安装&#xff08;yum安装&#xff09; 基于centos7 1.添加docker-ce 源信息 安装依赖包&#xff08;yum-utils 提供了 yum-config-manager &#xff0c;并且 device mapper 存储驱动程序需要device-mapper-persistent-data 和 lvm2&#xff09; yum install yum-…

​山东省图书馆典藏《乡村振兴战略下传统村落文化旅游设计》鲁图中大许少辉博士八一新书

​山东省图书馆《乡村振兴战略下传统村落文化旅游设计》鲁图中大许少辉博士八一新书

SpringCloud全家通新人入门手册

一、架构图 二、springCloud全家桶组件库 三、Spring Cloud 实战项目全景规划 四、技术选型 第一阶段&#xff1a;搭建基础的微服务功能&#xff0c;实现微服务之间的通信&#xff1b; 1、服务治理&#xff1a;服务治理的重点是搭建基础的跨服务调用功能。我会把用户服务、优…

LeetCode108. 将有序数组转换为二叉搜索树

108. 将有序数组转换为二叉搜索树 一、题目 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1&#x…

PDF怎么批量加密?掌握这招事半功倍

PDF文件是一种广泛使用的文档格式&#xff0c;而加密可以有效地保护PDF文件的安全性。当需要批量加密PDF文件时&#xff0c;以下是一些方法及注意事项。 PDF批量加密的方法 相信很多小伙伴平时都是直接在PDF阅读器中对文档进行加密&#xff0c;但是这样只能每次对当前打开的文…

Android JNI系列详解之CMake编译工具的使用

一、CMake工具的介绍 如图所示&#xff0c;CMake工具的主要作用是&#xff0c;将C/C编写的native源文件编译打包生成库文件&#xff08;包含动态库或者静态库文件&#xff09;&#xff0c;集成到Android中使用。 二、CMake编译工具的使用 使用主要是配置两个文件&#xff1a;CM…

0103水平分片-jdbc-shardingsphere-中间件

文章目录 1 准备服务器1.1 创建server-order0容器1.2 创建server-order1容器 2、基本水平分片2.1、基本配置2.2、数据源配置2.3、标椎分片表配置2.4、行表达式2.5、分片算法配置2.6、分布式序列算法 3、多表关联3.1、创建关联表3.2、创建实体类3.3、创建Mapper3.4、配置关联表3…