普冉(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;                       // I2C
    I2cHandle.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 Configuration
    PA3 : I2C1_SCL
    PA2 : 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;                  //复用为I2C
    HAL_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_TX
    HAL_SYSCFG_DMA_Req(0xA00);                                  //DMA2_MAP选择为IIC_RX

    /* Configure the DMA handler for Transmission process */
    HdmaCh1.Instance                 = DMA1_Channel1;           // 选择DMA通道1
    HdmaCh1.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通道1
    HdmaCh2.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_RX

    HAL_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 做通信实验。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

时序预测 | Python实现XGBoost电力需求预测

时序预测 | Python实现XGBoost电力需求预测 目录 时序预测 | Python实现XGBoost电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较…

linux redis-cluster ipv6方式

配置文件,具体字段的含义,可以参考其他文档。 1.单个文件的配置信息 redis_36380.conf requirepass Paas_2024port 36380tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised nopidfile /data/paas/apps/aicache-redis/redis_36380.p…

MyBatis Plus 大数据量查询优化

大数据量操作的场景大致如下: 数据迁移 数据导出 批量处理数据 在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理。但有些情况不需要分页的方式查询数据或分很大一页查询数据时,如果一下子将数…

阿里5年经验之谈 —— 浅谈自动化测试方法!

导读 在当今快节奏的软件开发环境中,高质量的代码交付至关重要。而针对经过多次迭代,主要功能趋向稳定的产品,大量传统的重复性手动测试方法已经无法满足高效、快速的需求。为了提高测试效率保证产品质量,本文通过产品实践应用&a…

LeetCode(66)二叉树的最大深度【二叉树】【简单】

目录 1.题目2.答案3.提交结果截图 链接: 二叉树的最大深度 1.题目 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7]…

DS考研真题总结——客观题(1)

开始整理真题中的客观小题,至于和算法有关的大题统一最后整理~ 定义背诵:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效…

C语言----文件操作(二)

在上一篇文章中我们简单介绍了在C语言中文件是什么以及文件的打开和关闭操作,在实际工作中,我们不仅仅是要打开和关闭文件,二是需要对文件进行增删改写。本文将详细介绍如果对文件进行安全读写。 一,以字符形式读写文件&#xff…

JVM学习之JVM概述

JVM的整体结构 Hotspot VM是目前市面上高性能虚拟机代表作之一 它采用解释器与即时编译器并存的架构 在今天,Java程序的运行性能已经达到了可以和C/C程序一较高下的地步 Java代码执行流程 具体图为 JVM架构模型 Java编译器输入的指令流基本上是一种基于 栈的指令…

关于负载和驱动能力的问题总结

这两天重新接触到了驱动能力这个说法,之前也听过,但是一直不理解是怎么回事儿,也就没有深究,现在想来,这里面还是有点门道的。 驱动能力,说的是什么呢?应该就是带载能力,而带载能力&…

Linux 中的网站服务管理

目录 1.安装服务 2.启动服务 3.停止服务 4.重启服务 5.开机自启 6.案例 1.安装服务 网址服务程序 yum insatll httpd -y 查看所有服务 systemctl list-unit-files 2.启动服务 systemctl start httpd 查看服务进程,确认是否启动 ps -ef|grep httpd 3.停止…

Windows Linux - 关于IP地址看这一篇就够了

目录 🥙1 IP地址简介 🥙2 IP地址分类 🥪分类方式1 🥪分类方式2 🥪特殊的IP地址 - 本机IP地址 🥙3 域名:便捷的记录IP地址 🥙4 常用命令 🥙5 查看网络IP和网关 &…

内网穿透工具,如何保障安全远程访问?

内网穿透工具是一种常见的技术手段,用于在没有公网IP的情况下将本地局域网服务映射至外网。这种工具的使用极大地方便了开发人员和网络管理员,使得他们能够快速建立起本地服务与外部网络之间的通信渠道。然而,在享受高效快捷的同时&#xff0…

windows电脑半夜突然睡眠自动唤醒的问题查找与治理

遇见几次了,半夜起来上厕所,发现休眠的电脑居然自己开了,还得跑过去把电脑再休眠,很烦。昨天晚上居然自动唤醒两次,忍无可忍了,于是开始查找原因。 查询原因如下,解决方面也在后面。 固件 S3 计…

深度学习记录--矩阵维数

如何识别矩阵的维数 如下图 矩阵的行列数容易在前向和后向传播过程中弄错,故写这篇文章来提醒易错点 顺便起到日后查表改错的作用 本文仅作本人查询参考(摘自吴恩达深度学习笔记)

Python学习路线 - Python语言基础入门 - 数据容器

Python学习路线 - Python语言基础入门 - 数据容器 数据容器入门为什么学习数据容器数据容器 数据容器:list(列表)列表的定义嵌套列表的定义列表的下标索引列表的下标(索引)列表的下标(索引) - 反向嵌套列表的下标(索引) 列表的常用操作列表的常用操作(方法)列表的查…

关联规则 关联规则概述

关联规则概述 关联规则 (Association Rules) 反映一个事物与其他事物之间的相互依存性和关联性。如果两个或者多个事物之间存在一定的关联关系,那么,其中一个事物就能够通过其他事物预测到。 关联规则可以看作是一种IF-THEN关系。假设商品A被客户购买&…

【TB作品】基于单片机的机械通风控制系统,实时温度和二氧化碳浓度

硬件: (1)51系列单片机,拟采用STC89C52RC; (2)DS18B20温度传感器; (3)二氧化碳浓度传感器:https://item.taobao.com/item.htm?spma21n57.1.0.0.1…

Unity升级到2022版本后,打开Spine会卡住

1)Unity升级到2022版本后,打开Spine会卡住 2)iPhone在同时播放多个音效的时候会压低某些音源的音量 3)在Y77手机上出现IMGSRV:GetMainShaderConstantBufferBaseAddress: Unsupported 4)UE4打包后在部分安卓机型出现“花…

函数栈帧的创建和销毁(编程底层原理)

本篇的内容格外的难写,里面包含了许多的专业术语名和汇编指令等晦涩难懂的东西,既不利于讲解,也不利于读者的理解。但我会尽力去讲述出里面的底层逻辑,帮助大家去理解里面的过程,理解编程的底层原理可以为我们后续更为…

NLP项目实战02:英文文本识别

简介: 欢迎来到本篇文章!今天我们将讨论一个新的自然语言处理任务——英文短文识别。具体而言,即通过分析输入的英文文本来判断其是比较消极的还是比较积极的。 展示: 1、项目界面 如下所示是项目启动后用户使用使用界面 2、布…