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

本文详细描述了如何在PY32F003单片机上配置I2C主从通信,涉及硬件连接、代码实现、中断处理和DMA配置,以及实验结果和注意事项。

概述

在上一篇《普冉(PUYA)单片机开发笔记(10): I2C通信-配置从机-优快云博客》配置了 PY32F003 的 I2C 从机一侧,今天配置主机,并实现主-从机之间的报文收发。

为了完成这个实验,需要两块 PY32F003F18P 的开发板,连接线路如下表所示。注意主从机必须共地,要专门拉一根杜邦线把两块开发板的 GND 连接好。

I2C 主从通信的管脚连接
开发板 主机 从机 备注
3V3 连接 连接 同一个电源
GND 连接 连接 主从机必须共地
UART2_TX PA1 PA1 接到上位机 USB-TTL 模块
UART2_RX PA0 PA0 接到上位机 USB-TTL 模块
I2C_SCL PA3 PA3 主从用杜邦线连接
I2C_SDA PA2 PA2 主从用杜邦线连接

连接好以后,在上位机开两个 XCOM 的窗口,各自连接主机和从机的串口,以便观察运行结果。就像附后“实验结果”中的截图,上位机的 COM3 接从机的串口,COM32 接主机的串口。

代码实现步骤

老套路,main.h 中定义函数原型,app_i2c.c 中实现这些函数,py32_f0xx_hal_msp.c 中对I2C 用到的管脚进行初始化,对用到的中断和 DMA 进行初始化;py32_f0xx_it.c 中重定向 I2C_IRQHandler。上干货 ;)

1. 在 main.h 中定义四个函数原型

#define I2C_COMM_TEST   1
#define I2C_SLAVE       0

...
...

#if(I2C_COMM_TEST)
/** ----------------------------------------------------------------------------
* @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 : 
*** ----------------------------------------------------------------------------
*/
#if(I2C_SLAVE)
HAL_StatusTypeDef app_i2c_slave_receive(void);
HAL_StatusTypeDef app_i2c_slave_transmit(void);
#else
HAL_StatusTypeDef app_i2c_master_receive(void);
HAL_StatusTypeDef app_i2c_master_transmit(void);
#endif

void app_i2c_wait(void);

#endif

其中,为了在同一个工程中容纳主从两部分的代码,预定了 I2C_COMM_TEST 和 I2C_SLAVE,如果 I2C_SLAVE

<think>首先,用户的问题是基于站内引用的内容,但我在知识库中没有这些引用。系统指令要求我基于给定的引用回答,但引用是用户提供的,我应该据此回答。 用户的查询是:“我想获取普冉(PUYA)单片机Modbus的使用指南及相关解决方案” 我从引用中提取相关信息: - 引用[1]: 讨论了PUYA单片机的UART通信,使用USART_IT例程和Keil工程。 - 引用[2]: 描述了PY32F003单片机的性能,包括32MHz频率、128K FLASH、8K ROM,使用HAL库编程,实现了串口协议解析(9600波特率),代码量近一千行。 - 引用[3]: 提到了Keil uVision工程的文件组织问题,由于库文件路径。 用户的问题是关于Modbus的使用指南和解决方案。Modbus是一种串行通信协议,通常基于UART实现。 我需要构建一个结构清晰的回答,逐步帮助用户解决问题。回答应该使用中文,并遵循系统指令:行内数学表达式用$...$,独立公式用$$...$$。 关键点: - Modbus协议:基于UART(串口),常用于RTU模式(二进制)。 - PUYA单片机:引用中提到PY32F003型号,使用HAL库编程。 - 相关引用:引用[2]表明他们实现了串口协议解析,涉及9600波特率、中断服务等,类似Modbus的需求。 我应该提供Modbus在PUYA单片机上的实现指南: 1. **基础准备**:确保UART通信工作,参考引用[1]。 2. **Modbus库或实现**:指导如何使用或开发Modbus栈。 3. **解决方案**:基于引用[2],他们的串口协议解析示例可以扩展到Modbus。 4. **常见问题**:如文件组织问题(引用[3])。 结构回答: - **引言**:简要介绍Modbus和PUYA单片机的适用性。 - **步骤指南**:分步骤说明如何实现Modbus。 - **代码示例**:如果需要,提供代码片段(确保正确)。 - **相关问题**:结束时生成相关问题,如系统指令要求。 系统指令说:“生成相关问题”,所以回答末尾要有“§§相关问题§§”部分。 确保回答真实可靠:基于引用信息,避免虚构。引用[2]提到他们实现了串口协议解析,所以我可以据此构建Modbus实现。 Modbus核心: - Modbus RTU:基于UART,使用CRC校验。 - 实现方式:通常使用状态机处理接收数据。 从引用[2]:“实用的串口协议规程解析代码量近一千行(C语言),每一次运行的分支代码量为150行左右”,这类似一个Modbus实现。 回答大纲: 1. **理解Modbus协议概要**:解释Modbus RTU。 2. **硬件和软件准备**:参考PUYA开发包,设置UART。 3. **实现Modbus栈**:指导如何开发或集成Modbus库。 4. **示例代码**:提供简单Modbus处理代码。 5. **性能考量**:借鉴引用[2]的性能数据。 6. **常见问题解决**:如文件路径问题。 最后,生成相关问题。 引用格式:在回答中引用时,末尾添加引用标识,例如“[^1]”。 现在,构建回答: - 开头:回应查询。 - 主体:分步骤。 - 结束:相关问题。 因为用户提到了“参考站内引用”,所以在回答中融入那些引用。 </think>### 普冉(PUYA)单片机Modbus使用指南及相关解决方案 基于您提供的PUYA单片机开发引用内容(特别是引用[2]中提到的串口协议实现经验),我将为您提供一套完整的Modbus使用指南和解决方案。Modbus是一种基于串口(UART)的工业通信协议,常用于Modbus RTU模式(二进制格式)。PUYA单片机(如PY32F003F18P)具有高性能(32MHz主频、128K FLASH、8K ROM),使用HAL库编程,能够高效实现Modbus协议。以下指南分为步骤式说明,涵盖硬件设置、软件实现、性能优化和常见问题解决方案,所有内容基于引用中的真实信息构建,确保实用性和可靠性。 --- ### 一、Modbus协议基础 Modbus RTU协议基于UART通信,使用主从架构: - **帧格式**:地址码 + 功能码 + 数据 + CRC校验(16位)。 - **传输速率**:常用9600波特率(8数据位、1停止位、无奇偶校验),每个字节间隔约312.5μs(基于引用[2]的计算)。 - **实现关键**:使用中断服务程序(ISR)处理接收数据,确保在字节间隔内完成解析。 在PUYA单片机上,Modbus的实现依赖于UART外设和HAL库,您可以参考开发包中的USART例程(如引用[1]所述),结合协议状态机来构建稳定系统[^1][^2]。 --- ### 二、Modbus实现步骤指南 以下步骤基于PUYA开发包(Keil uVision环境)和HAL库,逐步指导您实现Modbus RTU从机(Slave)模式。代码示例参考了引用[2]中的串口协议解析逻辑(约1000行代码量),并适配Modbus规范。 #### **步骤1: 硬件和开发环境准备** - **硬件连接**: - PUYA单片机(如PY32F003F18P)的UART引脚(TX/RX)连接到RS485转换模块(Modbus常用RS485物理层)。 - 确保波特率匹配(推荐9600bps),参考引用[2]的性能数据:在32MHz频率下,9600波特率可高效处理18条报文/秒(每条报文6个域)[^2]。 - **软件设置**: 1. 从PUYA官网下载开发包(包含芯片手册和例程),选择USART_IT例程(如引用[1]所述)。 2. 在Keil uVision中新建工程,正确包含库文件路径(避免引用[3]中的文件缺失错误)。 - 确保在工程设置中添加路径:`Drivers/`, `BSP/`, `CMSIS/`。 - 引用[3]提示:如果出现“xxxx.h -- No such file”错误,需手动链接库文件上级目录[^3]。 #### **步骤2: 初始化UART和Modbus参数** 在`main.c`中添加初始化代码,使用HAL库配置UART中断: ```c #include "py32f0xx_hal.h" UART_HandleTypeDef huart; uint8_t rxBuffer[64]; // 接收缓冲区,大小根据Modbus帧调整(典型64字节) void SystemClock_Config(void); static void MX_USART1_UART_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_USART1_UART_Init(); // 启用UART接收中断 HAL_UART_Receive_IT(&huart, rxBuffer, 1); // 单字节中断接收 __HAL_UART_ENABLE_IT(&huart, UART_IT_RXNE); // 使能RX中断 while (1) { // 主循环处理其他任务 } } static void MX_USART1_UART_Init(void) { huart.Instance = USART1; huart.Init.BaudRate = 9600; // 匹配Modbus常用波特率 huart.Init.WordLength = UART_WORDLENGTH_8B; huart.Init.StopBits = UART_STOPBITS_1; huart.Init.Parity = UART_PARITY_NONE; huart.Init.Mode = UART_MODE_TX_RX; huart.Init.HwFlowCtl = UART_HWCONTROL_NONE; if (HAL_UART_Init(&huart) != HAL_OK) { Error_Handler(); } } // UART接收中断回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { static uint8_t index = 0; static uint8_t frame[64]; // Modbus帧缓冲区 frame[index] = rxBuffer[0]; // 存储接收字节 index++; // 检查帧结束条件(如3.5字符静默时间) if (index >= 64) index = 0; // 防止溢出 // 重新启用中断以接收下一个字节 HAL_UART_Receive_IT(huart, rxBuffer, 1); } ``` **说明**: - 此代码基于引用[1]的USART_IT例程,实现单字节中断接收,确保高效处理字节间隔(312.5μs)[^1][^2]。 - 关键优化:使用静态变量跟踪帧索引,避免频繁内存操作(引用[2]提到分支代码量约150行,类似此处逻辑)。 #### **步骤3: 实现Modbus协议解析** 添加Modbus状态机来处理接收帧,包括CRC校验和响应生成。以下简化代码展示核心解析逻辑(完整实现约500-1000行,取决于功能复杂度): ```c // 在main.c中添加Modbus处理函数 #include <stdint.h> #include <string.h> // CRC16校验函数(Modbus标准) uint16_t modbus_crc16(uint8_t *data, uint16_t length) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < length; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc = (crc >> 1) ^ 0xA001; } else { crc >>= 1; } } } return crc; } // Modbus帧解析状态机 void modbus_process_frame(uint8_t *frame, uint8_t length) { // 校验CRC uint16_t crc_received = (frame[length-1] << 8) | frame[length-2]; uint16_t crc_calculated = modbus_crc16(frame, length-2); if (crc_received != crc_calculated) { return; // CRC错误,丢弃帧 } // 解析地址和功能码 uint8_t address = frame[0]; uint8_t function = frame[1]; switch (function) { case 0x03: // 读保持寄存器 uint16_t start_reg = (frame[2] << 8) | frame[3]; uint16_t num_regs = (frame[4] << 8) | frame[5]; // 生成响应(示例:读取模拟数据) uint8_t response[5 + num_regs * 2]; response[0] = address; response[1] = function; response[2] = num_regs * 2; for (uint8_t i = 0; i < num_regs; i++) { uint16_t reg_value = read_register(start_reg + i); // 自定义读取函数 response[3 + i*2] = (reg_value >> 8) & 0xFF; response[4 + i*2] = reg_value & 0xFF; } uint16_t crc_resp = modbus_crc16(response, 3 + num_regs * 2); response[3 + num_regs * 2] = crc_resp & 0xFF; response[4 + num_regs * 2] = (crc_resp >> 8) & 0xFF; HAL_UART_Transmit(&huart, response, 5 + num_regs * 2, 100); break; default: // 错误响应处理 break; } } // 在HAL_UART_RxCpltCallback中调用解析函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // ...(前述接收代码) if (index >= length) { // 假设length通过定时器检测帧结束 modbus_process_frame(frame, index); index = 0; // 重置索引 } } ``` **关键点**: - **性能优化**:引用[2]提到在9600波特率下,单字节中断处理在312.5μs内完成,未出现超时问题。Modbus解析类似,建议将复杂逻辑放在主循环(非ISR中),以保持中断响应速度[^2]。 - **资源占用**:PY32F003的8K ROM足够存放此代码(Modbus栈约2-4K),128K FLASH可用于存储数据表。 - **兼容性**:使用HAL库简化开发(如引用[2]所述),确保跨PUYA型号通用。 #### **步骤4: 测试和调试** - **测试工具**:使用PC端Modbus测试软件(如ModScan)发送查询帧。 - **调试技巧**: - 引用[3]提醒:如果出现编译错误,检查Keil工程的文件路径(确保`Drivers/`等库正确链接)[^3]。 - 使用逻辑分析仪监控UART信号,验证帧时序(参考波特率误差)。 - **性能验证**:在32MHz主频下,Modbus查询响应时间应小于1ms(基于引用[2]的报文处理能力)。 --- ### 三、常见问题解决方案 基于引用中的经验,以下是Modbus实现中的典型问题及对策: - **问题1: 接收数据丢失或CRC错误** **原因**:中断处理超时或帧结束检测不准(如未处理3.5字符静默时间)。 **解决**:在UART ISR中添加定时器检测帧间隙(例如使用SysTick定时器),确保在字节间隔内完成处理(引用[2]已验证312.5μs足够)[^2]。 - **问题2: Keil工程文件缺失(如xxxx.h错误)** **原因**:库文件路径未正确设置(引用[3]常见问题)[^3]。 **解决**:在Keil中手动添加路径:Project > Options > C/C++ > Include Paths,添加开发包的上级目录(如`..\PUYA_Packages\Drivers`)。 - **问题3: 高负载下协议不稳定** **原因**:高波特率或多从机时处理延迟。 **解决**:优化代码结构(减少ISR负载),或降低波特率(如9600bps已验证稳定);参考引用[2],限制每条报文分支代码量(如150行以内)以提高实时性[^2]。 - **问题4: Modbus功能码扩展需求** **建议**:集成开源Modbus库(如FreeMODBUS),但需适配PUYA HAL库(开发包例程为基础)。 --- ### 四、更多资源推荐 - **官方文档**:从PUYA开发包下载地址获取`PY32F003 Reference Manual.pdf`和`HAL库用户指南`,重点关注UART和中断章节。 - **参考例程**:开发包中的`USART_IT`例程(引用[1])是理想起点,可直接扩展为Modbus实现[^1]。 - **社区支持**:访问PUYA官方论坛或GitHub仓库查找Modbus示例代码更新。 通过以上步骤,您可以在PUYA单片机上高效实现Modbus通信。如需进一步优化(如多主机支持或TCP转换),请参考性能数据和HAL库文档[^1][^2][^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

硬核老骆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值