STM32设置为I2C从机模式(HAL库版本)转

news/2025/12/9 1:29:54/文章来源:https://www.cnblogs.com/ctmd/p/19324110

1、初始化I2C配置
注:除了最后的HAL_I2C_EnableListen_IT()函数,其他代码都可以用STM32CubeMX自动生成
参考代码:

 1 static void MX_I2C1_Init(void)
 2 {
 3   hi2c1.Instance = I2C1;                                // 配置I2C1                   
 4   hi2c1.Init.ClockSpeed = 100000;                       // 时钟频率:100k                            
 5   hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;               // 占空比:1/2                                    
 6   hi2c1.Init.OwnAddress1 = 0x80;                        // 本机地址:0x80(若作为从设备则是从机地址)                           
 7   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;  // 地址模式:7位                                                 
 8   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 禁止双地址                                                  
 9   hi2c1.Init.OwnAddress2 = 0;                           // 第二地址                        
10   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 禁止广播                                                  
11   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;     // 禁止时钟拉伸                                              
12   if (HAL_I2C_Init(&hi2c1) != HAL_OK)    // I2C1初始化                                                  
13   {                                                      
14     Error_Handler();                                                      
15   }                                                      
16   HAL_I2C_EnableListen_IT(&hi2c1);       // 使能I2C1的侦听中断  
17 }

 

2、初始化I2C引脚和中断
参考代码:
注:这个代码可以用STM32CubeMX自动生成

 1 void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
 2 {
 3   GPIO_InitTypeDef GPIO_InitStruct = {0};
 4   if(hi2c->Instance==I2C1)
 5   {
 6     // 配置GPIO
 7     __HAL_RCC_GPIOB_CLK_ENABLE();   
 8     GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
 9     GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
10     GPIO_InitStruct.Pull = GPIO_NOPULL;
11     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
12     GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
13     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
14 
15     // 配置I2C中断
16     /* Peripheral clock enable */
17     __HAL_RCC_I2C1_CLK_ENABLE();
18     /* I2C1 interrupt Init */
19     HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);  // 事件中断(必须有)
20     HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
21     HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0);  // 错误中断(非必须)
22     HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
23   }
24 }

 

3、配置I2C中断服务函数
参考代码:
注:这个代码可以用STM32CubeMX自动生成

 1 // I2C1事件中断服务函数(必须有)
 2 void I2C1_EV_IRQHandler(void)
 3 {
 4   HAL_I2C_EV_IRQHandler(&hi2c1);
 5 }
 6 
 7 // I2C1错误中断服务函数(非必须)
 8 void I2C1_ER_IRQHandler(void)
 9 {
10   HAL_I2C_ER_IRQHandler(&hi2c1);
11 }

 

4、配置I2C从机回调处理函数
参考代码:

 1 static uint8_t ram[256];             // 模拟I2C从机数据寄存器(主机读写的数据都放在这块内存)
 2 uint8_t offset;                      // 从机寄存器当前偏移地址
 3 static uint8_t first_byte_state = 1; // 是否收到第1个字节,也就是偏移地址(0:已收到,1:没有收到)
 4 
 5 // 侦听完成回调函数(完成一次完整的i2c通信以后会进入该函数)
 6 void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
 7 {
 8   // 完成一次通信,清除状态
 9   first_byte_state = 1;
10   offset = 0;
11   HAL_I2C_EnableListen_IT(hi2c); // slave is ready again
12 }
13 
14 // I2C设备地址回调函数(地址匹配上以后会进入该函数)
15 void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
16 {
17   if(TransferDirection == I2C_DIRECTION_TRANSMIT) 
18   {// 主机发送,从机接收
19     if(first_byte_state) 
20     {// 准备接收第1个字节数据
21       HAL_I2C_Slave_Seq_Receive_IT(hi2c, &offset, 1, I2C_NEXT_FRAME);  // 每次第1个数据均为偏移地址
22     } 
23   } 
24   else 
25   {// 主机接收,从机发送
26     HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);  // 打开中断并把ram[]里面对应的数据发送给主机
27   }
28 }
29 
30 // I2C数据接收回调函数(在I2C完成一次接收时会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
31 void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
32 {
33   if(first_byte_state) 
34   {// 收到的第1个字节数据(偏移地址)
35     first_byte_state = 0;
36   } 
37   else 
38   {// 收到的第N个字节数据
39     offset++;  // 每收到一个数据,偏移+1
40   }
41   // 打开I2C中断接收,下一个收到的数据将存放到ram[offset]
42   HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME);  // 接收数据存到ram[]里面对应的位置
43 }
44 
45 // I2C数据发送回调函数(在I2C完成一次发送后会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
46 void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
47 {
48   offset++;  // 每发送一个数据,偏移+1
49   HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME);  // 打开中断并把ram[]里面对应的数据发送给主机
50 }

 

5、测试程序
其实和上面讲解的代码是一样的,只是初始化时先把ram[]赋初值。
参考测试代码:

 1 #include "stm32f1xx_hal.h"
 2 
 3 static uint8_t ram[256];             // 模拟I2C从机数据寄存器(主机读写的数据都放在这块内存)
 4 uint8_t offset;                      // 从机寄存器当前偏移地址
 5 static uint8_t first_byte_state = 1; // 是否收到第1个字节,也就是偏移地址(0:已收到,1:没有收到)
 6 
 7 // 侦听完成回调函数(完成一次完整的i2c通信以后会进入该函数)
 8 void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
 9 {
10   // 完成一次通信,清除状态
11   first_byte_state = 1;
12   offset = 0;
13   HAL_I2C_EnableListen_IT(hi2c); // slave is ready again
14 }
15 
16 // I2C设备地址回调函数(地址匹配上以后会进入该函数)
17 void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
18 {
19   if(TransferDirection == I2C_DIRECTION_TRANSMIT) 
20   {// 主机发送,从机接收
21     if(first_byte_state) 
22     {// 准备接收第1个字节数据
23       HAL_I2C_Slave_Seq_Receive_IT(hi2c, &offset, 1, I2C_NEXT_FRAME);  // 每次第1个数据均为偏移地址
24     } 
25   } 
26   else 
27   {// 主机接收,从机发送
28     HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], 1, I2C_NEXT_FRAME);  // 打开中断并把ram[]里面对应的数据发送给主机
29   }
30 }
31 
32 // I2C数据接收回调函数(在I2C完成一次接收时会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
33 void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
34 {
35   if(first_byte_state) 
36   {// 收到的第1个字节数据(偏移地址)
37     first_byte_state = 0;
38   } 
39   else 
40   {// 收到的第N个字节数据
41     offset++;  // 每收到一个数据,偏移+1
42   }
43   // 打开I2C中断接收,下一个收到的数据将存放到ram[offset]
44   HAL_I2C_Slave_Seq_Receive_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME);  // 接收数据存到ram[]里面对应的位置
45 }
46 
47 // I2C数据发送回调函数(在I2C完成一次发送后会关闭中断并调用该函数,因此在处理完成后需要手动重新打开中断)
48 void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
49 {
50   offset++;  // 每发送一个数据,偏移+1
51   HAL_I2C_Slave_Seq_Transmit_IT(hi2c, &ram[offset], sizeof(ram), I2C_NEXT_FRAME);  // 打开中断并把ram[]里面对应的数据发送给主机
52 }
53 
54 // 测试用例:初始化把ram设置为从0到255的数
55 void i2c_test(void)
56 {
57   for (uint16_t i = 0; i < 256; i++)
58   {
59     ram[i] = i;
60   }
61 }

 

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

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

相关文章

手竿什么品牌质量好?选购指南:中国手竿十大品牌,公认好用的手竿

手竿作为淡水垂钓的核心装备,其质量好坏直接决定垂钓体验。判断一款手竿是否优质,关键看材质、调性、钓重、工艺四大维度。材质上,30-40T碳布是性价比黄金区间,过高的T数易导致竿体过脆,反而不适合野钓;调性方面…

STM32设置为I2C从机模式(转)

测试例程:定义一个256字节的buffer用来存放I2C从机的数据,默认赋初值0-255,然后通过中断的方式实现I2C数据读写。示例代码如下:1 #include "stm32f10x.h"2 #include "stm32f10x_gpio.h"3 #inc…

Linux Mint在更新内核后出现网卡未识别的问题

Linux Mint在更新内核后出现网卡未识别的问题前段时间在U盘中安装了一个Linux Mint,但后来因为更新了一下内核版本导致无线网卡无法识别(network UNCLAIMED),问了deepseek和chatgpt后(进入Live模式给新内核的Linu…

jittor和torch的爱恨情仇

左jittor右torch execute --- forward concat --- cat zeros_like --- empty_like jt.array --- torch.tensor greater --- gt 需要注意的 unique在 Jittor 中只有 return_inverse=True 时 return_counts=True 才有效…

FeatherNotes

https://github.com/tsujan/FeatherNotes

XHORSE XSGA80EN Universal Smart Key 5pcs/lot – Ideal for European American Car Owners Mechanics

Solving the Smart Key Replacement Headache: Introducing the XHORSE XSGA80EN Universal Smart Key Problem: The High Cost and Compatibility Nightmare of Smart Key Replacement For European and American car…

P1036 选数

点击查看代码 #include<bits/stdc++.h> using namespace std;int n,m; int a[25]; long long ans;bool prime(int x) {if(x==1||x!=2&&x%2==0) return false;for(int i=3;i<=x/i;i++) if(x%i==0) ret…

GIT-01 Fuel Injection Drivebox Injector Tester: Universal Plugs for All Injectors + Frequency Lock

The Pain of Fuel Injector Diagnostics: A Common Challenge for Mechanics and Car Owners Fuel injectors are the heart of modern engines, responsible for delivering precise fuel to combustion chambers. Wh…

虚拟机 VMware Tools 工具安装失败/缺失的问题

开源的VMware Tools工具下载方法 VMware Tools 有多种获取方式,以下是主要方法: 1. 通过 VMware 产品直接安装(推荐) 这是最简单的方法,在安装 VMware 虚拟机时:自动安装:VMware Workstation/Fusion 在新建虚拟…

AgileConfig-1.11.0 发布:增强的权限管理

AgileConfig 作为一个轻量级配置中心,我一直强调其“轻量”的概念。因此,权限管理这一块一直比较薄弱,甚至可以说形同虚设。然而,在实际项目实施中,用户对于权限管理的需求非常强烈,因为某些配置(如数据库连接串…

Windows 10 终止服务,趁微软官方下载通道还没有关闭,现在教大家如何用电脑浏览器下载。

微软下载通道虽然还在,但是桌面浏览器访问,默认是看不到的。但是使用手机浏览器还是可以看的到的。https://www.microsoft.com/zh-cn/software-download/windows10   那么第一种方式:可以使用手机浏览器访问上面链…

CSAPP 存储器层次结构

CSAPP 存储器层次结构目录存储器层次结构存储技术RAMDRAM的读写内存模块ROM主存的访问磁盘的存储磁盘的构造磁盘的存储磁盘的读写逻辑磁盘块 存储器层次结构 存储器系统是一个具有不同容量,成本和访问时间的存储设备的…

WPS免费版(会员功能永久可用)

WPS免费版(会员功能永久可用)WPS电信内部版是一款针对特定部门定制的办公软件版本,内置了永不到期的授权码,实现永久免费使用,无需订阅即可解锁完整功能该版本在兼容性、功能性和部署效率上均有显著优化,以下是其…

P1030 求先序序列

点击查看代码 #include<bits/stdc++.h> using namespace std;string in,post;void preorder(int inl,int inr,int postl,int postr) {if(inl>inr||postl>postr) return;//找到根节点并输出char root=post[…

2025年真空滚揉机厂家口碑排行:本地用户真实体验分享,市场上专业的滚揉机品牌优质企业盘点及核心优势详细解读

在肉制品加工行业,真空滚揉机是提升产品口感、色泽和出品率的关键设备。随着市场对高品质加工设备需求的持续增长,生产厂家的技术实力、产品稳定性及售后服务,成为下游用户选择的核心考量。本文基于公开市场数据、用…

第49天(中等题 数据结构)

打卡第四十九天 2道中等题题目:思路:代码: class Solution { public:long long shiftDistance(string s, string t, vector<int>& nextCost, vector<int>& previousCost) {long long dis[26][2…

谷歌反重力 Google Antigravity 常见问题

谷歌反重力 Google Antigravity 常见问题Google Antigravity 怎么设置中文?Google Antigravity 无法登录的解决方法Google Antigravity 怎么添加全局规则和项目规则?

windows11同时安装mysql8和mysql5.7数据库

一 官网下载压缩包 解压后创建my.ini文件编辑my.ini内容 mysql8[mysqld] # 设置3306端口 port=3306 # 设置MySQL的安装目录 basedir=E:\ProgramFiles\MySQL\mysql-8.0.43-winx64 # 设置MySQL的数据存放目录 datadir=E:…

2025年午餐肉罐头机优质生产商口碑榜发布,行业内罐头机产品博锐发展迅速,实力雄厚

随着预制食品与方便食品市场的持续扩张,午餐肉罐头作为经典品类,其生产线核心——灌装设备的技术水平与稳定性,日益成为生产企业关注的焦点。高效、精准、卫生的灌装设备,是保障产品品质、提升生产效率、满足大规模…