普冉(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 做通信实验。

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

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

相关文章

C++ 运算符重载

目录 前言 算术运算符重载 加号运算符 位运算符重载 左移运算符 自增自减运算符重载 前置自增运算符 后置自增运算符 赋值运算符重载 等号赋值运算符重 关系运算符重载 相等 不等 函数调用运算符重载 总结 前言 在C中,运算符重载是一种强大的特性…

C语言经典错误总结(三)

一.指针与数组理解 我们都知道定义一个数组然后对其进行各种想要的操作,但是你真的能够区分那些是对数组的操作,那些是通过指针实现的吗? 例如;arr[1]10;这个是纯粹对数组操作实现的吗? 答案肯定不是,实际上我们定义…

目标检测YOLO实战应用案例100讲-自动驾驶复杂场景下目标检测(续)

目录 3.2 YOLOv5框架的分析 3.3改进算法的基本思想 3.4改进聚类算法 3.5重构损失函数模型和NMS算法<

HiveSql语法优化四 :Bucket Map Join和Sort Merge Bucket Map Join优化

Bucket Map Join 之前的map join适用场景是大表join小表的情况&#xff0c;但是两张表都相对较大&#xff0c;若采用普通的Map Join算法&#xff0c;则Map端需要较多的内存来缓存数据&#xff0c;当然可以选择为Map段分配更多的内存&#xff0c;来保证任务运行成功。但是&#…

数据可视化---柱状图

import matplotlib.pyplot as plt import numpy as npdef plot_bar_chart(data, labels, colorsNone, title"Bar Chart", xlabel"X-Axis", ylabel"Y-Axis"):"""绘制柱状图&#xff0c;并在柱子上显示数量和比例。:param data: 包…

pytest之allure测试报告03:allure动态自定义报告

1、测试用例模块中引入allure&#xff1a;import allure 2、yaml文件中定义添加title、story的值&#xff1a; 3、测试用例中读取调用。eg:allure.dynamic.title() 4、运行报告查看&#xff1a;成功动态展示yaml文件中配置的story、title

共同编辑文档功能实现(websocket)

目录 前言 websocket封装 wangeditor下载 共同编辑文档代码实现 HTML样式部分 JS部分 css部分 前言 功能&#xff1a;实现文档共同编辑功能&#xff0c;可以实时接收到其他人的信息 思路&#xff1a;先调用接口获取相应的数据进行渲染&#xff0c;然后通过webSocket建…

当 Sealos 遇上区块链

当 Sealos 遇上区块链 拿着区块链技术不一定是去发币&#xff0c;很多业务系统也适合用这些技术&#xff0c;比如做个统一支付系统&#xff0c;积分系统等&#xff0c;可以做为一家公司的金融基础设施&#xff0c;或支付中台。拿链的技术去做有很多好处&#xff1a; 高可用&a…

【图的应用一:最小生成树】- 用 C 语言实现普里姆算法

目录 一、最小生成树 二、普里姆算法的构造过程 三、普里姆算法的实现 一、最小生成树 假设要在 n 个城市之间建立通信联络网&#xff0c;则连通 n 个城市只需要 n - 1 条线路。这时&#xff0c;自然会考虑这样一个问题&#xff0c;如何在最节省经费的前提下建立这个通信…

AUTOSAR汽车电子嵌入式编程精讲300篇-车载CAN总线网关的网络安全协议设计(续)

目录 3.2 网关过滤功能模块设计 3.3 车载网关ID安全等级分类 3.3.1 ID安全等级划分标准

Ansible运行临时命令

Ansible服务的强大之处在于只需要一条命令&#xff0c;便可以操控成千上万台的主机节点&#xff0c;而ansible命令便是最得力的工具之一。前文提到&#xff0c;Ansible服务实际上只是一个框架&#xff0c;能够完成工作的是模块化功能代码。Ansible的常用模块大致有20多个&#…

机器学习之无监督学习

聚类&#xff1a;发掘纵向结构的某种模式信息&#xff0c;某些x属于相同的分布或者类别 特征学习&#xff1a;发掘横向结构的某种模式信息&#xff0c;每一行都可以看成是一种属性或特征 密度估计&#xff1a;发掘底层数据分布&#xff0c;x都是从某个未知分布p(x)采出来的&a…

代码随想录 474. 一和零

题目 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度&#xff0c;该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素&#xff0c;集合 x 是集合 y 的 子集 。 示例 1&#xff1a; 输入&#xff1a;strs [“10…

vue实现悬浮窗拖动的自定义指令

首先在自己的项目根目录下建一个 src --> config --> drag.js 然后在main.js中全局引入 //鼠标拖动 import drag from /config/drag; Vue.use(drag); drag.js文件相关代码 import Vue from vue; //使用Vue.directive()定义一个全局指令 //1.参数一&#xff1a;指令的…

(四)STM32 操作 GPIO 点亮 LED灯 / GPIO工作模式

目录 1. STM32 工程模板中的工程目录介绍 2. GPIO 简介 3. GPIO 框图剖析 1&#xff09;保护二极管及上、下拉电阻 2&#xff09; P-MOS 管和 N-MOS 管 3&#xff09;输出数据寄存器 3.1&#xff09;ODR 端口输出数据寄存器 3.2&#xff09;BSRR 端口位设置/清除寄存器 4&a…

实验六 排序相关典型算法实现

一 实验目的 1&#xff0e;熟悉并掌握各种排序方法的设计思路。 2&#xff0e;掌握各种具体排序算法在计算机上的实现。 3&#xff0e;掌握各种排序方法的性能比较。 二 实验内容及要求 实验内容&#xff1a; 1. 编程实现如下功能&#xff1a; &#xff08;1&#xff09…

CGAL的手柄和循环器

1、手柄 CGAL中的大多数数据结构在其用户界面中使用Handles 的概念来引用它们存储的元素。这个概念描述了有时被称为琐碎迭代器的东西。Handle类似于指向对象的指针&#xff0c;提供解引用运算符*&#xff08;&#xff09;和成员访问运算符->&#xff08;&#xff09;&#…

【Java代码审计】目录穿越篇

【Java代码审计】目录穿越篇 1.Java中的目录穿越2.目录穿越漏洞审计3.Java中目录穿越漏洞修复 1.Java中的目录穿越 目录穿越漏洞产生的本质是路径可控&#xff0c;一旦涉及文件的读取问题便会涉及java.io.File类&#xff0c;因此在审计这类漏洞时可以优先查找java.io.File引用…

最强Pose模型RTMO开源 | 基于YOLO架构再设计,9MB+9ms性能完爆YOLO-Pose

实时多人在图像中的姿态估计面临着在速度和精度之间实现平衡的重大挑战。尽管两阶段的上下文方法在图像中人数增加时会减慢速度&#xff0c;但现有的单阶段方法往往无法同时实现高精度和实时性能。 本文介绍了RTMO&#xff0c;这是一个单阶段姿态估计框架&#xff0c;通过在YOL…

超文本传送协议HTTP

目录 HTTP简介&#xff1a; URL的格式&#xff1a; HTTP协议的特点&#xff1a; HTTP/1.0协议&#xff1a; HTTP/1.1协议&#xff1a; HTTP/2: HTTP代理服务器&#xff1a; HTTP的报文结构&#xff1a; 请求报文的特点&#xff1a; 响应报文的特点&#xff1a; Cook…