当前位置: 首页 > news >正文

普冉(PUYA)单片机开发笔记(10): I2C通信-配置从机

概述

I2C 常用在某些型号的传感器和 MCU 的连接,速率要求不高,距离很短,使用简便。

I2C的通信基础知识请参见《基础通信协议之 IIC详细讲解 - 知乎》。

PY32F003 可以复用出一个 I2C 接口(PA3:SCL,PA2:SDA),可以和 DMA 配合完成 I2C 的主从通信。厂家的数据手册对 I2C 接口简述如下图。

要完成 I2C 的通信实验需要两个 MCU。这里现尝试着配置好 I2C 的从机,下一篇再配置 I2C 的主机,并完成两者的通信实验。

代码实现的步骤

1. 在 py32f0xx_hal_conf.h 文件中增加对 I2C 的引用

在 Exported constances 一节中将 #define HAL_I2C_MODULE_ENABLED 的注释打开,打开对 I2C 功能的引用函数。打开后的代码节选如下。

/* Exported constants --------------------------------------------------------*//* ########################## Module Selection ############################## */
/*** @brief This is the list of modules to be used in the HAL driver */
#define HAL_MODULE_ENABLED  
#define HAL_RCC_MODULE_ENABLED
#define HAL_ADC_MODULE_ENABLED   
//#define HAL_CRC_MODULE_ENABLED   
//#define HAL_COMP_MODULE_ENABLED  
#define HAL_FLASH_MODULE_ENABLED   
#define HAL_GPIO_MODULE_ENABLED    
//#define HAL_IWDG_MODULE_ENABLED  
//#define HAL_WWDG_MODULE_ENABLED 
#define HAL_TIM_MODULE_ENABLED 
#define HAL_DMA_MODULE_ENABLED
#define HAL_LPTIM_MODULE_ENABLED  
#define HAL_PWR_MODULE_ENABLED
#define HAL_I2C_MODULE_ENABLED 
#define HAL_UART_MODULE_ENABLED 
//#define HAL_SPI_MODULE_ENABLED  
//#define HAL_RTC_MODULE_ENABLED   
//#define HAL_LED_MODULE_ENABLED 
//#define HAL_EXTI_MODULE_ENABLED
#define HAL_CORTEX_MODULE_ENABLED  
/* ########################## Oscillator Values adaptation ####################*/

2. 在 main.h 中添加 I2C 相关的函数声明

/** ----------------------------------------------------------------------------
* @name   : HAL_StatusTypeDef app_i2c_init(void);
* @brief  : i2c 初始化
* @param  : 
* @retval : HAL_OK: 写入成功; 其它: 错误
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才可继续操作
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef app_i2c_init(void);/** ----------------------------------------------------------------------------
* @brief  : i2c 测试使用的三个函数, 接收/发送/等待
* @param  : 
* @retval : 
* @remark : 
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef app_i2c_receive(void);
HAL_StatusTypeDef app_i2c_transmit(void);
void app_i2c_wait(void);

3. 在 app_i2c.c 中实现这些函数

在 Application/User 组中增加一个 app_i2c.c 文件。

/********************************************************************************* @file    app_i2c.c* @brief   I2C functions.******************************************************************************* @copyright** Copyright (c) 2023 CuteModem Intelligence.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/#include "main.h"#define EXDATA_LEN    15                 // 数据长度
#define I2C_ADDRESS   0xA0               // 本机地址0xA0
#define I2C_SPEEDBPS  100000             // 通讯速度100K
#define I2C_DUTYCYCLE I2C_DUTYCYCLE_16_9 // 占空比I2C_HandleTypeDef I2cHandle;
uint8_t mI2cTxBuf[EXDATA_LEN] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
uint8_t mI2cRxBuf[EXDATA_LEN] = {0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0};HAL_StatusTypeDef app_i2c_init(void)
{HAL_StatusTypeDef cfg_res = HAL_OK;I2cHandle.Instance             = I2C;                       // I2CI2cHandle.Init.ClockSpeed      = I2C_SPEEDBPS;              // I2C 通讯速度I2cHandle.Init.DutyCycle       = I2C_DUTYCYCLE;             // I2C 占空比I2cHandle.Init.OwnAddress1     = I2C_ADDRESS;               // I2C 地址I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;   // 禁止广播呼叫I2cHandle.Init.NoStretchMode   = I2C_NOSTRETCH_DISABLE;     // 允许时钟延长cfg_res = HAL_I2C_Init(&I2cHandle);                         //I2C初始化if (cfg_res != HAL_OK) return cfg_res;return cfg_res;
}HAL_StatusTypeDef app_i2c_receive(void)
{/*I2C从机中断方式接收*/while (HAL_I2C_Slave_Receive_IT(&I2cHandle, (uint8_t *)mI2cRxBuf, EXDATA_LEN) != HAL_OK){Error_Handler();}return HAL_OK;
}HAL_StatusTypeDef app_i2c_transmit(void)
{/*I2C从机中断方式发送*/while (HAL_I2C_Slave_Transmit_IT(&I2cHandle, (uint8_t *)mI2cTxBuf, EXDATA_LEN) != HAL_OK){Error_Handler();}return HAL_OK;
}void app_i2c_wait(void)
{/* 判断当前I2C状态, 等待I2C状态就绪 */while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY);
}

首先定义了4个常量。

  • 交换数据的长度 EXDATA_LEN(Exchange Data Length)
  • 本机的 I2C 地址为 0xA0
  • I2C 的通信速率定为 100Kbps
  • 高速模式下 I2C 总线的占空比为 9/16

接着定义了 mI2CTxBuf 和  mI2CRxBuf 两个缓冲区变量,长度为 EXDATA_LEN

初始化函数很简单,这里设置了“禁止广播呼叫”

接收、发送函数都使用了中断式模式,并使用了等待方式,一直到发送/接收完毕才会返回

等待函数就是循环读 I2C 的 State 标志,直到 I2C 空闲为止

4. 在 py32f0xx_hal_msp.c 增加 i2c 的外设初始化

static DMA_HandleTypeDef HdmaCh1;
static DMA_HandleTypeDef HdmaCh2;...
.../*** -----------------------------------------------------------------------* @name   : void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)* @brief  : 初始化I2C相关MSP* @param  : [in] *hi2c, I2C handler pointer* @retval : void* @remark :* -----------------------------------------------------------------------
*/
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_SYSCFG_CLK_ENABLE();                              //SYSCFG时钟使能__HAL_RCC_DMA_CLK_ENABLE();                                 //DMA时钟使能__HAL_RCC_I2C_CLK_ENABLE();                                 //I2C时钟使能__HAL_RCC_GPIOA_CLK_ENABLE();                               //GPIOA时钟使能/**I2C GPIO ConfigurationPA3 : I2C1_SCLPA2 : I2C1_SDA*/GPIO_InitStruct.Pin       = GPIO_PIN_3 | GPIO_PIN_2;GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;                //推挽方式GPIO_InitStruct.Pull      = GPIO_PULLUP;                    //上拉GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF12_I2C;                  //复用为I2CHAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                     //GPIO初始化/*复位I2C*/__HAL_RCC_I2C_FORCE_RESET();__HAL_RCC_I2C_RELEASE_RESET();/* I2C1 interrupt Init */HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0);                     //中断优先级设置HAL_NVIC_EnableIRQ(I2C1_IRQn);                              //使能I2C中断//DMA配置HAL_SYSCFG_DMA_Req(9);                                      //DMA1_MAP选择为IIC_TXHAL_SYSCFG_DMA_Req(0xA00);                                  //DMA2_MAP选择为IIC_RX/* Configure the DMA handler for Transmission process */HdmaCh1.Instance                 = DMA1_Channel1;           // 选择DMA通道1HdmaCh1.Init.Direction           = DMA_MEMORY_TO_PERIPH;    // 方向为从存储器到外设HdmaCh1.Init.PeriphInc           = DMA_PINC_DISABLE;        // 禁止外设地址增量HdmaCh1.Init.MemInc              = DMA_MINC_ENABLE;         // 使能存储器地址增量HdmaCh1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;     // 外设数据宽度为8位HdmaCh1.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;     // 存储器数据宽度位8位HdmaCh1.Init.Mode                = DMA_NORMAL;              // 禁止循环模式HdmaCh1.Init.Priority            = DMA_PRIORITY_VERY_HIGH;  // 通道优先级为"很高"HAL_DMA_Init(&HdmaCh1);                                     // 初始化DMA通道1__HAL_LINKDMA(hi2c, hdmatx, HdmaCh1);                       // DMA1 关联 IIC_TX/* Configure the DMA handler for Transmission process */HdmaCh2.Instance                 = DMA1_Channel2;           // 选择DMA通道1HdmaCh2.Init.Direction           = DMA_PERIPH_TO_MEMORY;    // 方向为从外设到存储HdmaCh2.Init.PeriphInc           = DMA_PINC_DISABLE;        // 禁止外设地址增量HdmaCh2.Init.MemInc              = DMA_MINC_ENABLE;         // 使能存储器地址增量HdmaCh2.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;     // 外设数据宽度为8位HdmaCh2.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;     // 存储器数据宽度位8位HdmaCh2.Init.Mode                = DMA_NORMAL;              // 禁止循环模式HdmaCh2.Init.Priority            = DMA_PRIORITY_HIGH;       // 通道优先级为高HAL_DMA_Init(&HdmaCh2);                                     // 初始化DMA通道1__HAL_LINKDMA(hi2c, hdmarx, HdmaCh2);                       // DMA1 关联 IIC_RXHAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 1);             // 中断优先级设置HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);                     // 使能DMA通道1中断HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0, 1);           // 中断优先级设置HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);                   // 使能DMA通道2_3中断
}

指定 PA3 管脚为 I2C_SCL, PA2 管脚为 I2C_SDA。

为 I2C 分配高优先级

使用  HAL_SYSCFG_DMA_Req() 把 DMA1 的通道1和通道2分别映射到 I2C 的 TX 和 RX

配置 DMA1 的两个通道,注意收发通道的方向,TX 是内存->外设,RX是外设->内存;

    HdmaChx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    HdmaChx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;

是因为收发的都是 uint8_t 型的数据,是BYTE(字节)型的,也是因为PY32F003 的 I2C DMA 的收发缓冲区是单字节的。

本实验中着重考察 I2C 的通信,所以把 HdmaChx.Init.Priority 设置得都比较高,应用中不一定设置那么高的优先级,这个要看应用的需求。

5. 在 py32f0xx_hal_it.c 中增加对 I2C 和 DMA 的中断服务程序

#include "main.h"
#include "py32f0xx_it.h"extern UART_HandleTypeDef UartHandle;
extern TIM_HandleTypeDef  htim16;
extern TIM_HandleTypeDef  htim1;
extern ADC_HandleTypeDef  hadcdma;
/* Add for I2C functionalities */
extern I2C_HandleTypeDef I2cHandle;...
...void DMA1_Channel1_IRQHandler(void)
{HAL_DMA_IRQHandler(I2cHandle.hdmatx);
}void DMA1_Channel2_3_IRQHandler(void)
{HAL_DMA_IRQHandler(I2cHandle.hdmarx);
}void I2C1_IRQHandler(void)
{HAL_I2C_EV_IRQHandler(&I2cHandle);HAL_I2C_ER_IRQHandler(&I2cHandle);
}...
...

注意 DMA1_Channel1 是为 TX 服务的,因此执行的是 HAL_DMA_IRQHandler(I2CHandler.hdmatx),注意是hdma"t"x;DMA2_Channl2_3 是为 RX 服务的,因此执行的是 HAL_DMA_IRQHandler(I2CHandle.hdmarx),注意是hdma“r”x。

不要忘记了 I2C_IRQHander() 中把 EV 和 ER 两个都放进去。

编译和运行

按照上述步骤,编译没有告警和错误;F8 下载到开发板。由于还没有主机通信,只能看到串口的输出,并且串口收发正常。LED 灯没有闪烁,说明正在等待和主机完成通信。

今天先到这里啦,等主机(Master)板子的代码写好,两台 MCU 做通信实验。

相关文章:

普冉(PUYA)单片机开发笔记(10): I2C通信-配置从机

概述 I2C 常用在某些型号的传感器和 MCU 的连接,速率要求不高,距离很短,使用简便。 I2C的通信基础知识请参见《基础通信协议之 IIC详细讲解 - 知乎》。 PY32F003 可以复用出一个 I2C 接口(PA3:SCL,PA2&a…...

Idea maven打包时 报错 illegalArgumentException: Malformed \uxxxx encoding 解决方法

1 改变打包命令重新打包 在maven打包命令上加入 -e -X 2 找到报错类和方法 可以看到是 java.util.Properties#loadConvert类方法中有个throw new IllegalArgumentException( "Malformed \\uxxxx encoding."),在此打断点 3 以Debug方式重新运行maven…...

Qt中槽函数在那个线程执行的探索和思考

信号和槽是Qt的核心机制之一,通过该机制大大简化了开发者的开发难度。信号和槽属于观察者模式(本质上是回调函数的应用)。是函数就需要考虑其是在那个线程中执行,本文讨论的就是槽函数在那个线程中执行的问题。 目录 1. connect…...

C++ 类模板

目录 前言 类模板语法 类模板和函数模板的区别 类模板没有自动类型推导的使用方式 类模板在模板参数列表中可以有默认参数 类模板中成员函数创建时机 类模板对象做函数参数 指定传入的类型 参数模板化 整个类模板化 类模板与继承 类模板成员函数类外实现 类模板分…...

边缘计算系统设计与实践

随着科技的飞速发展,物联网和人工智能两大领域的不断突破,我们看到了一种新型的计算模型——边缘计算的崛起。这种计算模型在处理大规模数据、实现实时响应和降低延迟需求方面,展现出了巨大的潜力。本文将深入探讨边缘计算系统的设计原理和实…...

【Spark精讲】Spark存储原理

目录 类比HDFS的存储架构 Spark的存储架构 存储级别 RDD的持久化机制 RDD缓存的过程 Block淘汰和落盘 类比HDFS的存储架构 HDFS集群有两类节点以管理节点-工作节点模式运行,即一个NameNode(管理节点)和多个DataNode(工作节点)。 Namenode管理文件系统的命名空…...

贪心算法:买卖股票的最佳时机II 跳跃游戏 跳跃游戏II

122.买卖股票的最佳时机II 思路: 想要获得利润,至少要以两天为一个交易单元,因为两天才会有股价差。因此可以将最终利润进行分解,如prices[3] - prices[0] (prices[3] - prices[2]) (prices[2] - prices[1]) (prices[1] - pr…...

音频DAC,ADC,CODEC的选型分析,高性能立体声

想要让模拟信号和数字信号顺利“交往”,就需要一座像“鹊桥”一样的中介,将两种不同的语言转变成统一的语言,消除无语言障碍。这座鹊桥就是转换器芯片,也就是ADC芯片。ADC芯片的全称是Analog-to-Digital Converter, 即模拟数字转换…...

python 连接SQL server 请用pymssql连接,千万别用pyodbc

pymssql官方介绍文档 python 使用 pymssql连接 SQL server 代码示例: 安装pymssql包: pip install pymssql代码: import pymssqldef conn_sqlserver_demo():# 连接字符串示例(根据您的配置进行修改)conn Nonetry:co…...

IntelliJ IDEA 自带HTTP Client接口插件上传文件示例

如何使用IntelliJ IDEA自带的HTTP Client接口插件进行文件上传的示例。在这个示例中,我们将关注Controller代码、HTTP请求文件(xxx.http),以及文件的上传和处理。 Controller代码 首先,让我们看一下处理文件上传的Co…...

C++中的接口有什么用

2023年12月13日,周三上午 今天上午在适配器模式,我发现如果想真正理解适配器模式,就必须学会使用C中的接口,就必须明白为什么要在C中使用接口,所以重新学习了一下C中的接口 目录 C中的接口有什么用用代码说明“实现多…...

el-table合并相同数据的单元格

相同的数据合并单元格 <el-table :data"userList" :span-method"objectSpanMethod" border><el-table-column type"selection" width"50" align"center" /><el-table-column label"用户名称" a…...

Verilog Systemverilog define宏定义

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 文章前情预告一、define是个啥&#xff1f;二、为什么要使用define三、怎么使用define四、define的横向拓展五、define思想在生活中的体现!六、结论七、参考资料八、…...

51单片机应用从零开始(十一)·数组函数、指针函数

51单片机应用从零开始&#xff08;九&#xff09;数组-CSDN博客 51单片机应用从零开始&#xff08;十&#xff09;指针-CSDN博客 目录 1. 用数组作函数参数控制流水花样 2. 用指针作函数参数控制 P0 口 8 位 LED 流水点亮 1. 用数组作函数参数控制流水花样 要在51单片机中…...

【PostgreSQL】从零开始:(八)PostgreSQL-数据库PSQL元命令

元命令 postgres# \? General\bind [PARAM]... set query parameters\copyright show PostgreSQL usage and distribution terms\crosstabview [COLUMNS] execute query and display result in crosstab\errverbose show most recent error…...

02 使用Vite创建Vue3项目

概述 A Vue project is structured similarly to a lot of modern node-based apps and contains the following: A package.json fileA node_modules folder in the root of your projectVarious other configuration files are usually contained at the root level, such …...

Shell三剑客:sed(简介)

一、前言 Stream EDitor:流编辑 sed 是一种在线的、非交互式的编辑器&#xff0c;它一次处理一行内容。处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为“模式空间”(pattern space)&#xff0c;接着用sed命令处理缓冲区中的内容&#xff0c;处理完成后&…...

tp连接数据库

ThinkPHP内置了抽象数据库访问层&#xff0c;把不同的数据库操作封装起来&#xff0c;我们只需要使用公共的Db类进行操作&#xff0c;而无需针对不同的数据库写不同的代码和底层实现&#xff0c;Db类会自动调用相应的数据库驱动来处理。采用PDO方式&#xff0c;目前包含了Mysql…...

jmeter,断言:响应断言、Json断言

一、响应断言 接口A请求正常返回值如下&#xff1a; {"status": 10013, "message": "user sign timeout"} 在该接口下创建【响应断言】元件&#xff0c;配置如下&#xff1a; 若断言成功&#xff0c;则查看结果树的接口显示绿色&#xff0c;若…...

dockerfite创建镜像---INMP+wordpress

搭建dockerfile---lnmp 在192.168.10.201 使用 Docker 构建 LNMP 环境并运行 Wordpress 网站平台 [rootdocker1 opt]# mkdir nginx mysql php [rootdocker1 opt]# ls #分别拖入四个包&#xff1a; nginx-1.22.0.tar.gz mysql-boost-5.7.20.tar.gz php-7.1.10.tar.bz2 wor…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...