RT-thread(9)基于RT-thread的环境,通过DMA_UART方式实现modbus-rtu 多寄存器读和写操作
cubeMx配置串口4
1、GPIO配置为上拉模式。输出高电平
2、使用DMA传输中断接收通信包
3、注意开串口4的中断,应用串口空闲中断实现不定长通信包接收。
4、通过RT-thread的内核互斥信号,防止多个线程同时操作同一个串口外设。
通过RT-thread的内核事件信号,实现中断通知线程完成串口发送或接收操作。
5、先做从机程序,在做主机程序,借助从机调主机程序代码。
6、本例使用的是STM32G070RB,串口1供 rt_kprintf()使用。
7、本例演示插图使用串口4通过USB转串口模块与PC机连接,在PC机上使用串口调试助手。MCU作为从机使用。

通信协议
/*读一个或多个REG量 0x03 */
/*主机(PC机)发出报文(8BYTES):
站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE)*/
/*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES):
站号(BYTE1),功能号(BYTE1),读来报文数据区的字节数(BYTE1), REGS数据(读来的数量*[HBYTE+LBYTE]),CRC16检验码(LBYTE+HBYTE)。*/
/*写一个或多个REG量 0x10*/
/*主机发送帧(9BYTES +2BYTES*写REG的数量):
站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE)*/
/*从机返回帧(6BYTES):
站号(1BYTE),功能号(1BYTE), 寄存器地址(HBYTE+LBYTE),写的REG数量(HBYTE+LBYTE),CRC校验码(LBYTE+HBYTE))*/

代码:(以下代码按照主机模式启动线程。做从机时,修改启动的线程)
rth_modbus_usart.h 部分
#ifndef RTH_MODBUS_USART_H #define RTH_MODBUS_USART_H //#error "TODO 1: 根据MCU芯片修改." #include "stm32g0xx_hal.h" #define MODBUS_MSG_MAX_LEN 256+9 // 最多128个寄存器 #define MODBUS_REGS_NUM 127 /*通信包类型*/ typedef struct { uint8_t buf[MODBUS_MSG_MAX_LEN]; /*字节指针*/ uint16_t n_bytes; /*字节数量*/ }modbus_msg_def; //extern volatile modbus_msg_def modbus_msg_rx;/*收到的通信包*/ //extern volatile modbus_msg_def modbus_msg_tx;/*发送的通信包*/ /*通信包类型*/ typedef struct { uint16_t regs[MODBUS_REGS_NUM];/*MODBUS寄存器*/ uint16_t regAdd; /*操作的REG地址*/ uint16_t regsNum; /*操作的REG数量*/ uint16_t slaveAdd; /*从机地址*/ uint16_t funCode; /*0x10 与 0x03*/ uint16_t newDataFlag; /*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/ // uint16_t actOk; /*完成一次通信(包括收和发,CRC校验等)标志*/ }modbus_regs_def; extern volatile modbus_regs_def modbus_regs;/*MODBUS寄存器组*/ //初始化MODBUS_RTU内核变量,启动MODBUS_RTU内核线程 int8_t modbus_usart_init(void); //工作在从机模式,测试程序 void modbus_slave_test(void); //工作在主机模式,测试程序 void modbus_master_test(void); // 串口空闲中断处理函数,此函数应添加到中断处理函数void USART2_IRQHandler(void)中 void UsartReceive_IDLE(UART_HandleTypeDef *huart); //从机,解析接收到的主机发来的通信包,生成应答通信包,并发送给主机 uint8_t modbus_rtu_slave_packet(void); //主机发送功能号0x10或0x03通信包,并得到应答通信包及处理 写一个或多个REG量 站号(1BYTE),功能号(1BYTE),开始地址(2BYTES), 读的数量(2BYTES) uint8_t modbus_rtu_master_com(uint8_t add,uint8_t funcode,uint16_t regsAdd,uint16_t regsNum); //主机解析接收的信息包 uint8_t modbus_rtu_master_rx_packet(void); //组成命令通信包,将发送给从机 uint8_t modbus_rtu_master_tx_packet(void); //从芯片唯一标识,可作为从机站号。 uint8_t modbus_rtu_sation_id(void); /*读一个或多个REG量 0x03 */ /*主机(PC机)发出报文(8BYTES): 站号(1BYTE),功能号(1BYTE),开始地址(2BYTES), 读的数量(2BYTES) CRC16检验码(2BYTES)*/ /*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES): 站号(BYTE1),功能号(BYTE1),读来报文数据区的字节数(BYTE1), 数据(读来的数量*2BYTES),CRC16检验码。*/ /*写一个或多个REG量 0x10*/ /*主机发送帧(9BYTES +2BYTES*写REG的数量): 站号(1BYTE),功能号(1BYTE),开始地址(2BYTES), 写的数量(2BYTES),字节数(1BYTES),寄存器值序列[(2BYTES)*写的数量],CRC校验码(2BYTES)*/ /*从机返回帧(6BYTES): 站号(1BYTE),功能号(1BYTE), 寄存器地址(2BYTES),写的REG数量(2BYTES),CRC校验码(2BYTES)*/ /* 测试数据包*/ //主机发出(HEX格式): //电机控制命令 /*校验算法 */ /*CRC-16/MODBUS x16+x15+x2+1*/ /*返回CRC值,16bits*/ uint16_t Modbus_CRC(uint8_t *puchMsg, uint16_t usDataLen );//Modbus校验码计算函数,低字节在前,高字节在后 /*返回非零值,校验正确*/ uint8_t Modbus_CRCCheck(uint8_t* pBuf,uint16_t dwLen);//Modbus校验函数 #endif
rth_modbus_usart.c 部分
/** ****************************************************************************** * @file : modbus_usart_app.c * @brief : DMA-USART通信程序功能 * DMA-USART通信中断,RT-THREAD内核变量与线程 ****************************************************************************** * @attention * * Copyright (c) 2022 SHIJUNHAI * 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 <rth_modbus_usart.h> #include <rtthread.h> /*TR-Thread库*/ #include "usart.h" #include "stdio.h" #include "string.h" #include "main.h" //访问全局变量 #define MODBUS_USART_HANDLE huart4 #define MODBUS_DMA_USART_RX_HANDLE hdma_usart4_rx #define MODBUS_DMA_USART_TX_HANDLE hdma_usart4_tx /**/ extern DMA_HandleTypeDef hdma_usart4_rx;// 在usart.c文件中定义 extern DMA_HandleTypeDef hdma_usart4_tx;// 在usart.c文件中定义 static rt_thread_t modbus_slave_thread = RT_NULL; /*从机处理收->发通信包*/ static rt_thread_t modbus_master_thread = RT_NULL;/*主机处理发->收通信包*/ /* 定义互斥量控制块 */ static rt_mutex_t modbus_usart_mux = RT_NULL; /*收到的通信包*/ __align(2) static modbus_msg_def modbus_msg_rx={0};/*收到的通信包*/ /*发送的通信包*/ __align(2) static modbus_msg_def modbus_msg_tx={0};/*发送的通信包*/ /*MODBUS寄存器组*/ volatile modbus_regs_def modbus_regs;/*MODBUS寄存器组*/ /* 定义事件控制块(句柄) 及代码*/ static rt_event_t UART_TX_RX_IT_event = RT_NULL;// 中断向通信线程发送的事件句柄 #define UART_TX_IT_EVENT (0x0001 << 0) //设置事件掩码的位0 RX中断接收结束事件 #define UART_RX_IT_EVENT (0x0001 << 1) //设置事件掩码的位1 TX中断发送结束事件 static rt_event_t MODBUS_THREAD_event = RT_NULL;// 通信线程与应用线程间的事件句柄 #define START_SLAVE_THREAD (0x0001 << 0) //设置事件掩码的位0 发送启动从机线程工作的事件 #define START_MASTER_THREAD (0x0001 << 1) //设置事件掩码的位1 发送启动主机线程工作的事件 #define END_SLAVE_THREAD_EVENT (0x0001 << 2)//设置事件掩码的位2 发送从机线程工作结束事件 #define END_MASTER_THREAD_EVENT (0x0001 << 3)//设置事件掩码的位3 发送主机线程工作结束事件 /* RT-Thread 线程入口函数声明 */ static void modbus_master_thread_entry(void* parameter);/*处理发邮件*/ static void modbus_slave_thread_entry(void* parameter);/*处理收邮件*/ /* *********************************************** * 函 数 名: modbus_slave_test * 功能说明: 启动MODBUS通信线程,作为从机,测试 * 形 参:无 * 返 回 值: 无 * 全局变量: ************************************************ *从机接收到正确命令通信包,发回应答通信包给主机。 *如果没有接收到正确命令通信包,不发送任何通信包。 *变量modbus_regs.newDataFlag标志寄存器值是否变化。 */ void modbus_slave_test(void)// { uint16_t index; rt_err_t uwRet; rt_uint32_t EventID; /* 启动从机线程,开启调度 */ if (modbus_slave_thread != RT_NULL) rt_thread_startup(modbus_slave_thread); rt_schedule(); /* 测试线程功能*/ for(index=0;index<MODBUS_REGS_NUM;index++) { /*付给寄存器测试值*/ modbus_regs.regs[index]=index+1; } /*设置本机地址*/ modbus_regs.slaveAdd =modbus_rtu_sation_id();//设定本从机地址(站号) while(1) { /**作为从机,通信一次,获取数据**/ /* 为了触发通信,发送一个事件 START_SLAVE_THREAD */ rt_event_send( MODBUS_THREAD_event,START_SLAVE_THREAD); rt_schedule();// 调度,使得 高优先级的从机线程执行! /*等待触发事件,确定是否完成一次通信*/ uwRet=rt_event_recv(MODBUS_THREAD_event, /* 事件对象句柄 */ END_SLAVE_THREAD_EVENT,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 300,/* 指定超时事件, 100 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_kprintf("%s %s:%d waiting END_SLAVE_THREAD_EVENT,time out!\r\n", __FILE__, __FUNCTION__, __LINE__); continue;//超时,触发事件,重新接收数据 } if(-RT_ERROR == uwRet){ rt_kprintf("%s %s:%d rt_event_recv() error!\r\n", __FILE__, __FUNCTION__, __LINE__); continue;//错误,触发事件,重新接收数据 } if (EventID == ( END_SLAVE_THREAD_EVENT ) )/* 如果事件接收完成并且正确 */ rt_kprintf("%s %s:%d END_SLAVE_THREAD_EVENT event happens!\r\n", __FILE__, __FUNCTION__, __LINE__); /**作为从机,对得到通信数据处理**/ /*检测有寄存器值被修改*/ if(modbus_regs.newDataFlag !=0xFF00){/*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/ rt_thread_delay(50); /* 延时 50 个 tick, 让出CPU */ continue;/*去重新触发通信*/ } /*逐行打印寄存器值*/ rt_enter_critical();/* 进入临界段,访问全局变量 */ modbus_regs.newDataFlag =0x0000; /*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/ for(index=0;index<modbus_regs.regsNum;index++) { /*逐行打印寄存器值*/ rt_kprintf("NO.%dREG:%d \r\n",(index+modbus_regs.regAdd),(modbus_regs.regs[index+modbus_regs.regAdd])); } rt_exit_critical(); /* 退出临界段 */ } } /* *********************************************** * 函 数 名: modbus_master_test * 功能说明: 启动MODBUS通信线程,作为主机,测试0x03通信,测试0x10通信 * 形 参:无 * 返 回 值: 无 * 全局变量: ************************************************ * 主机调用阻塞型函数modbus_rtu_master_com(),完成一次通信。 */ void modbus_master_test(void) { uint16_t index; rt_err_t uwRet; rt_uint32_t EventID; /* 启动主机线程,开启调度 */ if (modbus_master_thread != RT_NULL) rt_thread_startup(modbus_master_thread); while(1) { /*测试 0x10通信函数*/ for(index=0;index<MODBUS_REGS_NUM;index++) { /*付给寄存器测试值*/ modbus_regs.regs[index]=index+1; } if(modbus_rtu_master_com(0x01,0x10,0,10)==0)//作为主机,发送命令 { rt_kprintf("%s %s:%d 向从机的多个寄存器写,完成!\r\n", __FILE__, __FUNCTION__, __LINE__); } rt_thread_delay(100); if(modbus_rtu_master_com(0x01,0x03,0,10)==0)//作为主机,发送命令 { rt_kprintf("%s %s:%d 读从机的多个寄存器,完成!\r\n", __FILE__, __FUNCTION__, __LINE__); } rt_thread_delay(100); } } /* *********************************************** * 函 数 名: modbus_usart_init * 功能说明: 建立RT-thread内核对象,启动MODBUS通信线程 * 主机,启动modbus_master_thread * 从机,启动modbus_slave_thread * 根据使用,注释掉启动线程代码。 * 形 参:无 * 返 回 值: 无 * 全局变量: ************************************************ */ int8_t modbus_usart_init(void) { /*创建一个互斥信号量*/ modbus_usart_mux = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO); if (modbus_usart_mux != RT_NULL) rt_kprintf("The mux for usart has been created!\n\n"); /* 创建一个事件*/ UART_TX_RX_IT_event = rt_event_create("usart_IT_event", /* 事件标志组名字*/ RT_IPC_FLAG_PRIO); /* 事件模式FIFO(0x00)*/ /* 创建一个事件*/ MODBUS_THREAD_event = rt_event_create("usart_thread_event", /* 事件标志组名字*/ RT_IPC_FLAG_PRIO); /* 事件模式FIFO(0x00)*/ // if (modbus_mrx_event != RT_NULL) // rt_kprintf("事件创建成功!\n\n"); /*创建线程控制块,并赋给一个该类型指针变量f*/ modbus_slave_thread = /* 线程控制块指针 */ rt_thread_create("modbus_slave_thread", /* 线程名字 */ modbus_slave_thread_entry, /* 线程入口函数 */ RT_NULL, /* 线程入口函数参数 */ 512, /* 线程栈大小 */ 3, /* 线程的优先级 */ 20); /* 线程时间片 */ /* 启动从机线程,开启调度 */ // if (modbus_slave_thread != RT_NULL) // rt_thread_startup(modbus_slave_thread); // else // return -1; /*创建线程控制块,并赋给一个该类型指针变量f*/ modbus_master_thread = /* 线程控制块指针 */ rt_thread_create("modbus_master_thread ", /* 线程名字 */ modbus_master_thread_entry, /* 线程入口函数 */ RT_NULL, /* 线程入口函数参数 */ 512, /* 线程栈大小 */ 1, /* 线程的优先级 */ 200); /* 线程时间片 */ /* 启动主机线程,开启调度 */ // if (modbus_master_thread != RT_NULL) // rt_thread_startup(modbus_master_thread); // else // return -1; return 0; } /* *********************************************** * 函 数 名: modbus_master_thread_entry * 功能说明: 主机模式,接收通信包,并解析 发送通信包由其他应用程序完成 * 形 参:无 * 返 回 值: 无 * 全局变量: ************************************************ */ static void modbus_master_thread_entry(void* parameter)/*处理发通信包*/ { rt_err_t uwRet = RT_EOK; rt_uint32_t EventID; uint16_t crc; uint16_t index; while(1) { master_thread_start: /**等待触发事件,启动通信一次**/ uwRet=rt_event_recv(MODBUS_THREAD_event, /* 事件对象句柄 */ START_MASTER_THREAD,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 300,/* 指定超时事件, 100 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_kprintf("%s %s:%d waiting START_MASTER_THREAD,time out!\r\n", __FILE__, __FUNCTION__, __LINE__); continue;//超时,重新接收数据 } if(-RT_ERROR == uwRet){ rt_kprintf("%s %s:%d rt_event_recv() error!\r\n", __FILE__, __FUNCTION__, __LINE__); continue;//错误,重新接收数据 } if (EventID == ( START_MASTER_THREAD ) )/* 如果事件接收完成并且正确 */ rt_kprintf("%s %s:%d START_MASTER_THREAD event happens!\r\n", __FILE__, __FUNCTION__, __LINE__); /**向从机发送通信包**/ master_thread_send: if( modbus_rtu_master_tx_packet()!=0)//组成命令通信包,将发送给从机 { rt_kprintf("%s %s:%d tx packet error!\r\n", __FILE__, __FUNCTION__, __LINE__); } /* 发送通信包 */ rt_mutex_take(modbus_usart_mux, /* 获取互斥量 */ RT_WAITING_FOREVER); /* 等待时间:一直等 */ /*独占DMA-USART外设,启动DMA-USART发送通信包*/ HAL_UART_AbortTransmit_IT(&MODBUS_USART_HANDLE);//停止并清除中断允许标志 if (HAL_UART_Transmit_DMA(&MODBUS_USART_HANDLE, (uint8_t*)modbus_msg_tx.buf, modbus_msg_tx.n_bytes) != HAL_OK) { rt_kprintf("%s %s:%d HAL_UART_Transmit_DMA() error!\r\n", __FILE__, __FUNCTION__, __LINE__); goto master_thread_start; } /* 等待接event内核消息,确认发送 */ uwRet=rt_event_recv( UART_TX_RX_IT_event, /* 事件对象句柄 */ UART_TX_IT_EVENT,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 300,/* 指定超时事件, 300 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Master_thread TX,time out\n",uwRet); goto master_thread_start; } if(-RT_ERROR == uwRet){ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Master_thread TX ,error\n",uwRet); goto master_thread_start; } /* 如果接收完成并且正确 */ /* 发送一个事件 START_MASTER_THREAD,通知主机线程,开始接收应答通信包 */ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf("Master_thread TX success \r\n"); //rt_kprintf("%s %s:%d Master_thread TX success", __FILE__, __FUNCTION__, __LINE__); /**接收从机发来的应答通信包**/ master_thread_receive: /*清零接收modbus_msg_rx*/ modbus_msg_rx.n_bytes =0;//字节数为0 for(index=0;index<MODBUS_MSG_MAX_LEN;index++) modbus_msg_rx.buf[index]=0; /*取得串口外设*/ uwRet = rt_mutex_take(modbus_usart_mux, /* 获取互斥量 */ 100); /* 等待时间:100ms */ if(RT_ETIMEOUT==uwRet) goto master_thread_receive;//没有得到互斥信号,重新开始 /* 0 启动DMA-UART接收,接收从机的应答通信包 */ HAL_UART_AbortReceive_IT(&MODBUS_USART_HANDLE);//停止并清除中断允许标志 HAL_UART_Receive_DMA(&MODBUS_USART_HANDLE, (uint8_t*)modbus_msg_rx.buf, sizeof(modbus_msg_rx.buf)); __HAL_UART_ENABLE_IT(&MODBUS_USART_HANDLE, UART_IT_IDLE);// 开空闲中断,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况。 /* 1 等待接event内核消息,确认接收到数据 */ uwRet=rt_event_recv(UART_TX_RX_IT_event, /* 事件对象句柄 */ UART_RX_IT_EVENT,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 300,/* 指定超时事件, 300 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Master_thread RX,time out\n",uwRet); continue;//超时,等待线程触发事件 } if(-RT_ERROR == uwRet){ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Master_thread RX ,error\n",uwRet); continue;//超时,等待线程触发事件 } rt_mutex_release( modbus_usart_mux ); //释放互斥量 if (EventID == (UART_RX_IT_EVENT)) /* 如果接收完成并且正确 */ rt_kprintf("Master_thread RX success \r\n"); /* 2 接收字节数不为零检测*/ if(modbus_msg_rx.n_bytes ==0) { rt_kprintf("Master_thread RX types zero! \r\n"); continue;//重新接收通信包 } /* 3 CRC校验 */ crc = Modbus_CRC((uint8_t*)modbus_msg_rx.buf, modbus_msg_rx.n_bytes); if(crc !=0x0000)//CRC校验错误 { rt_kprintf ( "master_RX CRC error!\n"); continue;//重新等待触发事件 } else { rt_kprintf ( "master_RX CRC pass!\n"); } /* 4 对从机的应答通信包解析,更新寄存器 */ if( modbus_rtu_master_rx_packet()!=0) { rt_kprintf ( "Packet error!\n"); continue;//重新等待触发事件 } else rt_kprintf ( "Packet pass!\n"); /** 发送一个事件 END_MASTER_THREAD_EVENT,通知应用线程完成一次通信 **/ rt_event_send( MODBUS_THREAD_event,END_MASTER_THREAD_EVENT); rt_schedule();// 调度,使得 高优先级的从机线程执行! rt_thread_delay(50); /* 延时 50 个 tick, 让出CPU */ } } /* *********************************************** * 函 数 名: modbus_slave_thread_entry * 功能说明: 从机模式,检验通信包,处理接收来的通信包,发应答通信包 * 形 参:无 * 返 回 值: 无 * 全局变量: * 特殊说明: * 0x03操作时,事先,MODBUS寄存器modbus_regs应更新为最新数据 * 0x10操作时,事后,MODBUS寄存器modbus_regs存放的是上位机发来的寄存器数据 ************************************************ */ static void modbus_slave_thread_entry(void* parameter)/*处理收通信包*/ { rt_err_t uwRet = RT_EOK; rt_uint32_t EventID; uint16_t crc; uint16_t index; HAL_StatusTypeDef Hal_Status; /*开始接收数据*/ //在串口初始化后加入 modbus_msg_rx.n_bytes =0;//字节数为0 for(index=0;index<MODBUS_MSG_MAX_LEN;index++) modbus_msg_rx.buf[index]=0; while(1) { waitingStartEvent: /* 等待一个事件START_SLAVE_THREAD,通知应用线程开始一次通信 */ uwRet=rt_event_recv(MODBUS_THREAD_event, /* 事件对象句柄 */ START_SLAVE_THREAD,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 300,/* 指定超时事件, 100 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_kprintf("%s %s:%d waiting START_SLAVE_THREAD,time out!\r\n", __FILE__, __FUNCTION__, __LINE__); continue;//超时,重新接收事件信号START_SLAVE_THREAD } if(-RT_ERROR == uwRet){ rt_kprintf("%s %s:%d rt_event_recv() error!\r\n", __FILE__, __FUNCTION__, __LINE__); continue;//错误,重新接收事件信号START_SLAVE_THREAD } if (EventID == ( START_SLAVE_THREAD ) )/* 如果事件信号接收完成并且正确 */ rt_kprintf("%s %s:%d START_SLAVE_THREAD event happens!\r\n", __FILE__, __FUNCTION__, __LINE__); receive:/* 1 开始接收数据*/ modbus_msg_rx.n_bytes =0;//字节数为0 for(index=0;index<MODBUS_MSG_MAX_LEN;index++) modbus_msg_rx.buf[index]=0; uwRet = rt_mutex_take(modbus_usart_mux, /* 获取互斥量 */ 100); /* 等待时间:100ms */ if(RT_ETIMEOUT==uwRet) goto receive;//没有得到互斥信号,重新开始 /* 2 启动DMA-UART接收,接收从机的应答通信包 */ HAL_UART_AbortReceive_IT(&MODBUS_USART_HANDLE);//停止并清除中断允许标志 HAL_UART_Receive_DMA(&MODBUS_USART_HANDLE, (uint8_t*)modbus_msg_rx.buf, sizeof(modbus_msg_rx.buf)); __HAL_UART_ENABLE_IT(&MODBUS_USART_HANDLE, UART_IT_IDLE);// 开空闲中断,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况。 /* 3 等待接event内核消息,确认接收到数据 */ uwRet=rt_event_recv(UART_TX_RX_IT_event, /* 事件对象句柄 */ UART_RX_IT_EVENT,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 300,/* 指定超时事件, 100 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Slave_thread RX,time out\n",uwRet); continue;//超时,重新接收数据 } if(-RT_ERROR == uwRet){ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Slave_thread RX ,error\n",uwRet); continue;//错误,重新接收数据 } rt_mutex_release( modbus_usart_mux ); //释放互斥量 if (EventID == (UART_RX_IT_EVENT)) /* 如果接收完成并且正确 */ rt_kprintf("Slave_thread RX success \r\n"); /* 4 CRC校验 */ crc = Modbus_CRC((uint8_t*)modbus_msg_rx.buf, modbus_msg_rx.n_bytes); if(crc !=0x0000)//CRC校验错误 { for(index=0;index<modbus_msg_rx.n_bytes;index++) rt_kprintf ( "%02X ",modbus_msg_rx.buf[index]); rt_kprintf ( "CRC error!\n"); goto receive;//重新接收通信包 } /* 5 接收通信包解析,更新寄存器,组发送通信包 */ if( modbus_rtu_slave_packet()!=0) { rt_kprintf ( "Packet length error!\n"); goto receive;//重新接收通信包 } /* 6 发送应答通信包 */ if(modbus_msg_tx.n_bytes==0) goto receive;//发送字节数不能为0,重新接收通信包 rt_mutex_take(modbus_usart_mux, /* 获取互斥量 */ RT_WAITING_FOREVER); /* 等待时间:一直等 */ /*独占DMA-USART外设,启动DMA-USART发送通信包*/ HAL_UART_AbortTransmit_IT(&MODBUS_USART_HANDLE);//停止并清除中断允许标志 Hal_Status =HAL_UART_Transmit_DMA(&MODBUS_USART_HANDLE, (uint8_t*)modbus_msg_tx.buf, modbus_msg_tx.n_bytes); if (Hal_Status != HAL_OK) { rt_kprintf("%s %s:%d error! HAL_UART_Transmit()\r\n", __FILE__, __FUNCTION__, __LINE__); } /* 7 等待发送完成,等待中断回调函数发event内核消息,确认发送 */ uwRet=rt_event_recv( UART_TX_RX_IT_event, /* 事件对象句柄 */ UART_TX_IT_EVENT,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 1000,/* 指定超时事件, 1000 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ /* 发送超时*/ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Master_thread TX,time out\n",uwRet); goto receive; } if(-RT_ERROR == uwRet){ /* 接收事件故障*/ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf ( "Master_thread TX ,error\n",uwRet); goto receive; } if (EventID == (UART_TX_IT_EVENT)) /* 如果接收完成并且正确 */ rt_mutex_release( modbus_usart_mux ); //释放互斥量 rt_kprintf("Master_thread TX success \r\n"); /* 发送一个事件 END_SLAVE_THREAD_EVENT,通知应用线程完成一次通信 */ rt_event_send( MODBUS_THREAD_event,END_SLAVE_THREAD_EVENT); rt_schedule();// 调度,使得 高优先级的从机线程执行! rt_thread_delay(10); /* 延时 50 个 tick, 让出CPU */ goto waitingStartEvent;// 等待新的事件信号 } } /* *********************************************** * 函 数 名: modbus_rtu_slave_packet * 功能说明: 作为从机,接收的通信包解析,组合将发送的通信包 * 形 参:无 * 返 回 值: 0,正常; 1,站号错误;2,功能号错误;3通信包长度错误 * 全局变量:modbus_msg_rx 已经收到的通信包,modbus_msg_tx 即将发送的通信包 * 特殊说明: * 0x03操作时,事先,MODBUS寄存器modbus_regs应更新为最新数据 * 0x10操作时,事后,MODBUS寄存器modbus_regs存放的是上位机发来的寄存器数据 ************************************************ */ uint8_t modbus_rtu_slave_packet(void) { uint16_t regsAdd; uint16_t regsNum; uint16_t index; uint16_t uint16_temp; //检测站号 if( modbus_msg_rx.buf[0] != modbus_regs.slaveAdd)//通信站号; return 1; //检测功能号 if((modbus_msg_rx.buf[1] != 0x03)&&(modbus_msg_rx.buf[1] != 0x10)) return 2; //功能类型 0x03通信包处理,作为从机 /*读一个或多个REG量 0x03 */ /*主机(PC机)发出报文(8BYTES): 站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE)*/ /*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES): 站号(BYTE1),功能号(BYTE1),读来报文数据区的字节数(BYTE1), REGS数据(读来的数量*[HBYTE+LBYTE]),CRC16检验码(LBYTE+HBYTE)。*/ if(modbus_msg_rx.buf[1] == 0x03) { modbus_regs.funCode =0x03;//功能号 /*解析通信包*/ //检测通信包长度 if(modbus_msg_rx.n_bytes != 8) return 3; // 根据接收的通信包,计算寄存器地址和数量 regsAdd = modbus_msg_rx.buf[2]; regsAdd =regsAdd<<8; regsAdd =regsAdd+modbus_msg_rx.buf[3]; regsNum = modbus_msg_rx.buf[4]; regsNum =regsNum<<8; regsNum =regsNum+modbus_msg_rx.buf[5]; /*组合应答通信包*/ modbus_msg_tx.buf[0] =modbus_msg_rx.buf[0]; //取通信站号 modbus_msg_tx.buf[1] =modbus_msg_rx.buf[1]; //功能号 modbus_msg_tx.buf[2] =((regsNum<<1)&0xFF); //读来报文数据区的字节数 /*记录访问从机及其寄存器*/ //modbus_regs.slaveAdd = add;/*从机地址*/ modbus_regs.regAdd = regsAdd;/*寄存器起始地址*/ modbus_regs.regsNum = regsNum;/*寄存器起始地址*/ // 向通信包添加寄存器值 rt_enter_critical();/* 进入临界段,访问全局变量 */ for(index=0;index<regsNum;index++) //寄存器值 { uint16_temp=modbus_regs.regs[index+regsAdd];/*MODBUS寄存器*/ modbus_msg_tx.buf[3+index*2] = (uint16_temp/256); // High byte modbus_msg_tx.buf[3+index*2+1] = (uint16_temp%256); // Low byte } rt_exit_critical(); /* 退出临界段 */ //添加CRC校验码 modbus_msg_tx.n_bytes = 3+2*regsNum; uint16_temp=Modbus_CRC((uint8_t*)modbus_msg_tx.buf, modbus_msg_tx.n_bytes); modbus_msg_tx.buf[modbus_msg_tx.n_bytes] = (uint16_temp>>8); //CRC校验码低BYTES modbus_msg_tx.buf[modbus_msg_tx.n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES modbus_msg_tx.n_bytes= modbus_msg_tx.n_bytes+2;//发送字节数 } //功能类型 0x10通信包处理,作为从机 /*写一个或多个REG量 0x10*/ /*主机发送帧(9BYTES +2BYTES*写REG的数量): 站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE)*/ /*从机返回帧(8BYTES): 站号(1BYTE),功能号(1BYTE), 寄存器地址(HBYTE+LBYTE),写的REG数量(HBYTE+LBYTE),CRC校验码(LBYTE+HBYTE))*/ if(modbus_msg_rx.buf[1] == 0x10) { modbus_regs.funCode =0x10;//功能号 //检测通信包长度 if(modbus_msg_rx.n_bytes != 9+modbus_msg_rx.buf[6]) return 3; // 计算寄存器地址和数量 regsAdd = modbus_msg_rx.buf[2]; regsAdd =regsAdd<<8; regsAdd =regsAdd+modbus_msg_rx.buf[3]; regsNum = modbus_msg_rx.buf[4]; regsNum =regsNum<<8; regsNum =regsNum+modbus_msg_rx.buf[5]; /*记录访问从机及其寄存器*/ if(MODBUS_MSG_MAX_LEN<regsAdd + regsNum){ rt_kprintf("%s %s:%d Out the scope of regs!\r\n", __FILE__, __FUNCTION__, __LINE__); return 1; } /*访问寄存器范围超过1->MODBUS_MSG_MAX_LEN*/ //modbus_regs.slaveAdd = add;/*从机地址*/ modbus_regs.regAdd = regsAdd;/*寄存器起始地址*/ modbus_regs.regsNum = regsNum;/*寄存器起始地址*/ modbus_regs.funCode =0x10;//功能号 // 保存寄存器值 rt_enter_critical();/* 进入临界段,访问全局变量 */ for(index=0;index<regsNum;index++) //寄存器值 { uint16_temp=modbus_msg_rx.buf[6+index*2]; uint16_temp=uint16_temp<<8;// High byte uint16_temp=uint16_temp+modbus_msg_rx.buf[6+index*2+1];//Plus Low byte modbus_regs.regs[index+regsAdd]/*MODBUS寄存器*/=uint16_temp;//存寄存器值 } modbus_regs.newDataFlag =0xFF00;/*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/ rt_exit_critical(); /* 退出临界段 */ /*组合应答通信包*/ modbus_msg_tx.buf[0] =modbus_msg_rx.buf[0]; //获取通信站号 modbus_msg_tx.buf[1] =modbus_msg_rx.buf[1]; //功能号 modbus_msg_tx.buf[2] =modbus_msg_rx.buf[2]; //寄存器地址 modbus_msg_tx.buf[3] =modbus_msg_rx.buf[3]; //寄存器地址 modbus_msg_tx.buf[4] =modbus_msg_rx.buf[4]; //写的REG数量 modbus_msg_tx.buf[5] =modbus_msg_rx.buf[5]; //写的REG数量 //添加CRC校验码 modbus_msg_tx.n_bytes = 6; uint16_temp=Modbus_CRC((uint8_t*)modbus_msg_tx.buf, modbus_msg_tx.n_bytes); modbus_msg_tx.buf[modbus_msg_tx.n_bytes] = (uint16_temp>>8); //CRC校验码低BYTES modbus_msg_tx.buf[modbus_msg_tx.n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES modbus_msg_tx.n_bytes= modbus_msg_tx.n_bytes+2;//发送字节数 } return 0; } /* *********************************************** * 函 数 名: modbus_rtu_master_rx_packet * 功能说明: modbus-rtu 协议主机功能 作为主机,对应答通信包处理程序 * 形 参:无 * 返 回 值: 0,正常; 非0,错误; * 全局变量:modbus_msg_rx 已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器 * 特殊说明: * 0x03操作时,事后MODBUS寄存器modbus_regs存放的是下位机发来的寄存器数据 * 0x10操作时,事先MODBUS寄存器modbus_regs存放的是上位机向从机发去的数据 ************************************************ */ uint8_t modbus_rtu_master_rx_packet(void) { uint16_t regsAdd; uint16_t regsNum; uint16_t index; uint16_t uint16_temp; //检测站号 if( modbus_msg_rx.buf[0] != modbus_regs.slaveAdd)//从机地址检测; return 1; //检测功能号 if((modbus_msg_rx.buf[1] != 0x03)&&(modbus_msg_rx.buf[1] != 0x10)) return 2; //功能类型 0x03通信包处理 解析从机应答通信包 /*读一个或多个REG量 0x03 */ /*主机(PC机)发出报文(8BYTES): 站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE)*/ /*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES): 站号(BYTE1),功能号(BYTE1),读来报文数据区(REGS)的字节数(BYTE1), REGS数据(读来的数量*[HBYTE+LBYTE]),CRC16检验码(LBYTE+HBYTE)。*/ if(modbus_msg_rx.buf[1] == 0x03) { regsAdd =modbus_regs.regAdd ;/*寄存器起始地址*/ regsNum =modbus_regs.regsNum ;/*寄存器起始地址*/ /*解析通信包*/ //检测通信包长度 if(modbus_msg_rx.n_bytes != regsNum*2+5) return 3; if(regsNum*2!=modbus_msg_rx.buf[2])// 报文中读来报文数据区(REGS)的字节数(BYTE1)检测 return 4; // 获得寄存器值 rt_enter_critical();/* 进入临界段,访问全局变量 */ modbus_regs.newDataFlag =0xFF00;/*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/ for(index=0;index<regsNum;index++) //寄存器值 { uint16_temp=modbus_msg_rx.buf[3+index*2]*2 +modbus_msg_rx.buf[3+index*2+1]; modbus_regs.regs[index+regsAdd] = uint16_temp; } rt_exit_critical(); /* 退出临界段 */ } //功能类型 0x10通信包处理 /*写一个或多个REG量 0x10*/ /*主机发送帧(9BYTES +2BYTES*写REG的数量): 站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE)*/ /*从机返回帧(8BYTES): 站号(1BYTE),功能号(1BYTE), 寄存器地址(HBYTE+LBYTE),写的REG数量(HBYTE+LBYTE),CRC校验码(LBYTE+HBYTE))*/ if(modbus_msg_rx.buf[1] == 0x10) { //检测通信包长度 if(modbus_msg_rx.n_bytes != 8) return 3; // 计算寄存器地址和数量 regsAdd = modbus_msg_rx.buf[2]; regsAdd =regsAdd<<8; regsAdd =regsAdd+modbus_msg_rx.buf[3]; regsNum = modbus_msg_rx.buf[4]; regsNum =regsNum<<8; regsNum =regsNum+modbus_msg_rx.buf[5]; // 检查寄存器地址和数量 if(regsAdd !=modbus_regs.regAdd)/*寄存器起始地址*/ return 4; if(regsNum !=modbus_regs.regsNum)/*寄存器起始地址*/ return 5; } return 0; } /* *********************************************** * 函 数 名: modbus_rtu_master_tx_packet * 功能说明: 组成命令通信包,将发送给从机 * 0x10写一个或多个REG量 * 0x03读一个或多个REG量 * 形 参: * 返 回 值: 0,正常; 1,站号错误;2,功能号错误;3通信包长度错误 * 全局变量:modbus_msg_tx 即将发送的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器 * 特殊说明: * 0x03操作时,事后MODBUS寄存器modbus_regs存放的是下位机发来的寄存器数据 * 0x10操作时,事先MODBUS寄存器modbus_regs存放的是上位机向从机发去的数据 ************************************************ */ uint8_t modbus_rtu_master_tx_packet(void)//组成命令通信包,将发送给从机 { uint8_t add; uint8_t funcode; uint16_t regsAdd; uint16_t regsNum; uint16_t index; uint16_t uint16_temp; /*输入访问从机及其寄存器*/ add=modbus_regs.slaveAdd;/*从机地址*/ regsAdd=modbus_regs.regAdd;/*寄存器起始地址*/ regsNum=modbus_regs.regsNum;/*寄存器起始地址*/ funcode=modbus_regs.funCode;/*功能号*/ if(funcode==0x10) { /*组合发送通信包*/ /*写一个或多个REG量 0x10*/ //主机发送帧(9BYTES +2BYTES*写REG的数量): //站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE) //添加通信包头 modbus_msg_tx.buf[0] =add;//通信站号 modbus_msg_tx.buf[1] =funcode; //功能号 modbus_msg_tx.buf[2] =regsAdd/256; //寄存器地址 modbus_msg_tx.buf[3] =regsAdd%256; //寄存器地址 modbus_msg_tx.buf[4] =regsNum/256; //写的REG数量 modbus_msg_tx.buf[5] =regsNum%256; //写的REG数量 modbus_msg_tx.buf[6] =regsNum*2; //写的REG字节数 // 向通信包添加寄存器值 rt_enter_critical();/* 进入临界段,访问全局变量 */ for(index=0;index<regsNum;index++) //寄存器值 { uint16_temp=modbus_regs.regs[index+regsAdd];/*MODBUS寄存器*/ modbus_msg_tx.buf[7+index*2] = (uint16_temp/256); // High byte modbus_msg_tx.buf[7+index*2+1] = (uint16_temp%256); // Low byte } rt_exit_critical(); /* 退出临界段 */ //添加CRC校验码 modbus_msg_tx.n_bytes = 7+2*regsNum;//用于计算CRC的字节数 uint16_temp=Modbus_CRC((uint8_t*)modbus_msg_tx.buf, modbus_msg_tx.n_bytes); modbus_msg_tx.buf[modbus_msg_tx.n_bytes] = (uint16_temp>>8); //CRC校验码低BYTES modbus_msg_tx.buf[modbus_msg_tx.n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES modbus_msg_tx.n_bytes= modbus_msg_tx.n_bytes+2;//发送字节数 return 0; } if(funcode==0x03) { /*组合发送通信包*/ /*读一个或多个REG量 0x03*/ //主机(PC机)发出报文(8BYTES): //站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE) //添加通信包头 modbus_msg_tx.buf[0] =add;//通信站号 modbus_msg_tx.buf[1] =funcode; //功能号 modbus_msg_tx.buf[2] =regsAdd/256; //寄存器地址 modbus_msg_tx.buf[3] =regsAdd%256; //寄存器地址 modbus_msg_tx.buf[4] =regsNum/256; //写的REG数量 modbus_msg_tx.buf[5] =regsNum%256; //写的REG数量 //无添加寄存器内容 //添加CRC校验码 modbus_msg_tx.n_bytes = 6;//用于计算CRC的字节数 uint16_temp=Modbus_CRC((uint8_t*)modbus_msg_tx.buf, modbus_msg_tx.n_bytes); modbus_msg_tx.buf[modbus_msg_tx.n_bytes] = (uint16_temp>>8); //CRC校验码低BYTES modbus_msg_tx.buf[modbus_msg_tx.n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES modbus_msg_tx.n_bytes= modbus_msg_tx.n_bytes+2;//发送字节数 return 0; } return 1; } /* *********************************************** * 函 数 名: modbus_rtu_master_com(阻塞型函数) * 功能说明: 主机发送功能号0x10通信包 或 0x03通信包 写一个或多个REG量 站号(1BYTE) * 形 参:站号(1BYTE),功能号(1BYTE),开始地址(2BYTES), 读的数量(2BYTES) * 返 回 值: 0,正常; 1,站号错误;2,功能号错误;3通信包长度错误 * 全局变量:modbus_msg_tx 即将发送的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器 * 特殊说明: * 0x03操作时,事后MODBUS寄存器modbus_regs存放的是下位机发来的寄存器数据 * 0x10操作时,事先MODBUS寄存器modbus_regs存放的是上位机向从机发去的数据 ************************************************ */ uint8_t modbus_rtu_master_com(uint8_t add,uint8_t funcode,uint16_t regsAdd,uint16_t regsNum) { uint16_t uint16_temp; uint16_t index; rt_uint32_t EventID; rt_err_t uwRet = RT_EOK; /*写入访问的从机及其寄存器*/ modbus_regs.slaveAdd = add;/*从机地址*/ modbus_regs.regAdd = regsAdd;/*寄存器起始地址*/ modbus_regs.regsNum = regsNum;/*寄存器起始地址*/ modbus_regs.funCode = funcode;/*功能号*/ /* 为了触发通信,发送一个事件 START_SLAVE_THREAD */ rt_event_send( MODBUS_THREAD_event,START_MASTER_THREAD); rt_schedule();// 调度,使得 高优先级的从机线程执行! /*等待触发事件,确定是否完成一次通信*/ uwRet=rt_event_recv(MODBUS_THREAD_event, /* 事件对象句柄 */ END_MASTER_THREAD_EVENT,/* 接收线程感兴趣的事件 */ RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,/* 接收选项 */ 1000,/* 指定超时事件, 300 ticks */ &EventID); /* 指向接收到的事件 */ if(-RT_ETIMEOUT == uwRet){ rt_kprintf("%s %s:%d waiting END_MASTER_THREAD_EVENT,time out!\r\n", __FILE__, __FUNCTION__, __LINE__); return 1;//超时,触发事件,重新接收数据 } if(-RT_ERROR == uwRet){ rt_kprintf("%s %s:%d rt_event_recv() error!\r\n", __FILE__, __FUNCTION__, __LINE__); return 2;//错误,触发事件,重新接收数据 } if (EventID == ( END_SLAVE_THREAD_EVENT ) )/* 如果事件接收完成并且正确 */ rt_kprintf("%s %s:%d END_MASTER_THREAD_EVENT event happens!\r\n", __FILE__, __FUNCTION__, __LINE__); return 0; } /* *********************************************** * 函 数 名: UsartReceive_IDLE * 功能说明: 空闲中断服务子程序,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况 * 形 参:无 * 返 回 值: 无 * 全局变量: * 特殊说明:此子程序应添加到中断处理函数void USART?_IRQHandler(void)中。 使能此中断 __HAL_UART_ENABLE_IT(&MODBUS_USART_HANDLE, UART_IT_IDLE); ************************************************ */ void UsartReceive_IDLE(UART_HandleTypeDef *huart) // 串口空闲中断处理函数,此函数应添加到中断处理函数void USART3_4_IRQHandler(void)中 { uint32_t tmpFlg = 0; uint32_t temp; tmpFlg =__HAL_UART_GET_FLAG(&MODBUS_USART_HANDLE,UART_FLAG_IDLE); //获取IDLE标志位 if((tmpFlg != RESET))//idle标志被置位 { __HAL_UART_CLEAR_IDLEFLAG(&MODBUS_USART_HANDLE);//清除标志位 // temp = MODBUS_USART_HANDLE.Instance->ISR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能 // temp = MODBUS_USART_HANDLE.Instance->RDR; //读取数据寄存器中的数据 //这两句和上面那句等效 HAL_UART_DMAStop(&MODBUS_USART_HANDLE); // 停止DMA //temp = __HAL_DMA_GET_COUNTER(&MODBUS_DMA_USART_RX_HANDLE) ;// 获取DMA中未传输的数据个数,函数参数在 usart.h中定义 temp = MODBUS_USART_HANDLE.hdmarx->Instance->CNDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数, //这句和上面那句等效 /*修改mail_msg_rx.n_bytes为接收字节数*/ modbus_msg_rx.n_bytes= sizeof(modbus_msg_rx.buf) - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数 //__HAL_UART_DISABLE_IT(&MODBUS_USART_HANDLE, UART_IT_IDLE);// 开空闲中断 HAL_UART_AbortReceive_IT(&MODBUS_USART_HANDLE);//停止并清除中断允许标志 rt_kprintf("RX bytes %d! \r\n",modbus_msg_rx.n_bytes); /* 发送一个事件 UART_RX_IT_EVENT */ rt_event_send( UART_TX_RX_IT_event,UART_RX_IT_EVENT); rt_schedule();// 调度,使得 高优先级的从机线程执行! } } /* *********************************************** * 函 数 名: HAL_UART_RxCpltCallback * 功能说明: 重载中断函数回调函数,处理接收数据事件, 并发送rth内核邮件modbus_rx_mb给线程 * 形 参:无 * 返 回 值: 无 * 全局变量: ************************************************ */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &MODBUS_USART_HANDLE) { /*修改mail_msg_rx.n_bytes为接收字节数*/ modbus_msg_rx.n_bytes =sizeof(modbus_msg_rx.buf);// 接受完成,置接收字节数 HAL_UART_DMAStop(&MODBUS_USART_HANDLE); // 停止DMA HAL_UART_AbortReceive_IT(&MODBUS_USART_HANDLE);//停止并清除中断允许标志 /* 发送一个事件 UART_RX_IT_EVENT */ rt_event_send( UART_TX_RX_IT_event,UART_RX_IT_EVENT); } } /* *********************************************** * 函 数 名: HAL_UART_TxCpltCallback * 功能说明: 重载中断函数回调函数,处理发送数据事件, 并发送rth内核邮件modbus_tx_mb给线程 * 形 参:无 * 返 回 值: 无 * 全局变量: ************************************************ */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &MODBUS_USART_HANDLE) { /* 发送一个事件 UART_TX_IT_EVENT */ rt_event_send( UART_TX_RX_IT_event,UART_TX_IT_EVENT); } HAL_UART_DMAStop(&MODBUS_USART_HANDLE); // 停止DMA HAL_UART_AbortReceive_IT(&MODBUS_USART_HANDLE); //停止并清除中断允许标志 } /*读一个或多个REG量 0x03 */ /*主机(PC机)发出报文(8BYTES): 站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE)*/ /*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES): 站号(BYTE1),功能号(BYTE1),读来报文数据区的字节数(BYTE1), REGS数据(读来的数量*[HBYTE+LBYTE]),CRC16检验码(LBYTE+HBYTE)。*/ /*写一个或多个REG量 0x10*/ /*主机发送帧(9BYTES +2BYTES*写REG的数量): 站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE)*/ /*从机返回帧(6BYTES): 站号(1BYTE),功能号(1BYTE), 寄存器地址(HBYTE+LBYTE),写的REG数量(HBYTE+LBYTE),CRC校验码(LBYTE+HBYTE))*/ /*校验算法 */ /*CRC-16/MODBUS x16+x15+x2+1*/ const uint8_t auchCRCHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 } ; const uint8_t auchCRCLo[] = { 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 }; /** * @brief Modbus校验码计算函数 * @para 字节数组指针,数组长度 * @retval 16bits校验码,低字节在前,高字节在后 */ uint16_t Modbus_CRC( uint8_t *puchMsg, uint16_t usDataLen ) //Modbus校验码计算函数,低字节在前,高字节在后 { uint8_t uchCRCHi = 0xFF ; /* 高字节初始化值 */ uint8_t uchCRCLo = 0xFF ; /* 低字节初始化值 */ uint8_t uIndex ; while (usDataLen--) { uIndex = uchCRCLo ^ (*puchMsg++) ; uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex] ; uchCRCHi = auchCRCLo[uIndex] ; } //return (uchCRCHi << 8 | uchCRCLo) ; return (uchCRCHi | uchCRCLo << 8) ; // XINJE 低字节在前,高字节在后 } /** * @brief Modbus_CRC校验函数 * @para 字节数组指针,数组长度 * @retval 1 正确,0 错误 */ uint8_t Modbus_CRCCheck(uint8_t* pBuf,uint16_t dwLen)//Modbus校验函数 { static uint16_t crc =0;// My_Com_CRC(pBuf,dwLen-2); static uint16_t crcframe; uint8_t bret; bret =0x00; if(dwLen>2) { crc = Modbus_CRC(pBuf,dwLen-2); crcframe = pBuf[dwLen-2]*256+ pBuf[dwLen-1]; if(crcframe ==crc) bret = 0x01; } return(bret); }; uint8_t modbus_rtu_sation_id(void)//从芯片唯一标识,获取通信站号。 { uint8_t retval; uint32_t chipId[3] = {0}; // HAL库读取方式 chipId[0]=HAL_GetUIDw0(); chipId[1]=HAL_GetUIDw1(); chipId[2]=HAL_GetUIDw2(); retval =(uint8_t) (chipId[2]&0x007F); if(retval==0) retval++; retval =0x01;//调试使用 return retval; //———————————————— //版权声明:本文为CSDN博主「Leung_ManWah」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 //原文链接:https://blog.csdn.net/qq_36347513/article/details/126061779 }
主程序部分
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * 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. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "dma.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <rtthread.h> #include <rth_modbus_usart.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ uint8_t retval; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init();// RT-THREAD 控制台使用,使用rt_kprintf()打印信息 MX_USART4_UART_Init();// RT-THREAD MODBUS使用,DMA模式 //MX_TIM6_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ modbus_usart_init();//初始化MODBUS_RTU内核变量,启动MODBUS_RTU内核线程 //HAL_TIM_Base_Start_IT(&htim6);//启动定时器TIM6 //工作在从机模式,测试程序 modbus_slave_test();//在应用线程中调用 //工作在主机模式,测试程序 //modbus_master_test();//在应用线程中调用 while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ rt_thread_delay(1000); //rt_kprintf("%s %s:%d here!\r\n", __FILE__, __FUNCTION__, __LINE__); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1; RCC_OscInitStruct.PLL.PLLN = 8; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* 功能: 重载函数 INPUT: 无 OUTPUT:无 GLOBAL: */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { HAL_NVIC_DisableIRQ(EXTI4_15_IRQn);//关EXTI4_15_IRQn中断 if(htim->Instance == TIM6) //处理TIM6间隔定时中断 { } HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);//开EXTI4_15_IRQn中断 } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */
修改中断函数 stm32g0xx_it.c
/** * @brief This function handles USART3 and USART4 interrupts. */ void USART3_4_IRQHandler(void) { /* USER CODE BEGIN USART3_4_IRQn 0 */ UsartReceive_IDLE( &huart4); // 串口空闲中断处理函数,此函数应添加到中断处理函数void USART3_4_IRQHandler(void)中 /* USER CODE END USART3_4_IRQn 0 */ HAL_UART_IRQHandler(&huart4); /* USER CODE BEGIN USART3_4_IRQn 1 */ /* USER CODE END USART3_4_IRQn 1 */ }
RT-thread配置文件rtconfig.h
/* RT-Thread config file */ #ifndef __RTTHREAD_CFG_H__ #define __RTTHREAD_CFG_H__ // <<< Use Configuration Wizard in Context Menu >>> // <h>Basic Configuration // <o>Maximal level of thread priority <8-256> // <i>Default: 32 #define RT_THREAD_PRIORITY_MAX 32 // <o>OS tick per second // <i>Default: 1000 (1ms) #define RT_TICK_PER_SECOND 1000 // <o>Alignment size for CPU architecture data access // <i>Default: 4 #define RT_ALIGN_SIZE 4 // <o>the max length of object name<2-16> // <i>Default: 8 #define RT_NAME_MAX 8 // <c1>Using RT-Thread components initialization // <i>Using RT-Thread components initialization //#define RT_USING_COMPONENTS_INIT // </c> #define RT_USING_USER_MAIN // <o>the stack size of main thread<1-4086> // <i>Default: 512 #define RT_MAIN_THREAD_STACK_SIZE 256 // </h> // <h>Debug Configuration // <c1>enable kernel debug configuration // <i>Default: enable kernel debug configuration //#define RT_DEBUG // </c> // <o>enable components initialization debug configuration<0-1> // <i>Default: 0 #define RT_DEBUG_INIT 0 // <c1>thread stack over flow detect // <i> Diable Thread stack over flow detect //#define RT_USING_OVERFLOW_CHECK // </c> // </h> // <h>Hook Configuration // <c1>using hook // <i>using hook //#define RT_USING_HOOK // </c> // <c1>using idle hook // <i>using idle hook //#define RT_USING_IDLE_HOOK // </c> // </h> // <e>Software timers Configuration // <i> Enables user timers #define RT_USING_TIMER_SOFT 0 #if RT_USING_TIMER_SOFT == 0 #undef RT_USING_TIMER_SOFT #endif // <o>The priority level of timer thread <0-31> // <i>Default: 4 #define RT_TIMER_THREAD_PRIO 4 // <o>The stack size of timer thread <0-8192> // <i>Default: 512 #define RT_TIMER_THREAD_STACK_SIZE 512 // </e> // <h>IPC(Inter-process communication) Configuration // <c1>Using Semaphore // <i>Using Semaphore #define RT_USING_SEMAPHORE // </c> // <c1>Using Mutex // <i>Using Mutex #define RT_USING_MUTEX // </c> // <c1>Using Event // <i>Using Event #define RT_USING_EVENT // </c> // <c1>Using MailBox // <i>Using MailBox #define RT_USING_MAILBOX // </c> // <c1>Using Message Queue // <i>Using Message Queue //#define RT_USING_MESSAGEQUEUE // </c> // </h> // <h>Memory Management Configuration // <c1>Memory Pool Management // <i>Memory Pool Management //#define RT_USING_MEMPOOL // </c> // <c1>Dynamic Heap Management(Algorithm: small memory ) // <i>Dynamic Heap Management #define RT_USING_HEAP #define RT_USING_SMALL_MEM // </c> // <c1>using tiny size of memory // <i>using tiny size of memory //#define RT_USING_TINY_SIZE // </c> // </h> // <h>Console Configuration // <c1>Using console // <i>Using console #define RT_USING_CONSOLE // </c> // <o>the buffer size of console <1-1024> // <i>the buffer size of console // <i>Default: 128 (128Byte) #define RT_CONSOLEBUF_SIZE 128 // </h> // <h>FinSH Configuration // <c1>include finsh config // <i>Select this choice if you using FinSH //#include "finsh_config.h" // </c> // </h> // <h>Device Configuration // <c1>using device framework // <i>using device framework //#define RT_USING_DEVICE // </c> // </h> // <<< end of configuration section >>> #endif
RT-thread配置文件board.c
/* * Copyright (c) 2006-2019, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-05-24 the first version */ #include <rthw.h> #include <rtthread.h> #include "usart.h" #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) /* * Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP * the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes */ #define RT_HEAP_SIZE (15*1024) static rt_uint8_t rt_heap[RT_HEAP_SIZE]; RT_WEAK void *rt_heap_begin_get(void) { return rt_heap; } RT_WEAK void *rt_heap_end_get(void) { return rt_heap + RT_HEAP_SIZE; } #endif void rt_os_tick_callback(void) { rt_interrupt_enter(); rt_tick_increase(); rt_interrupt_leave(); } /* cortex-m 架构使用 SysTick_Handler() */ void SysTick_Handler() { rt_os_tick_callback(); } /** * This function will initial your board. */ void rt_hw_board_init(void) { //#error "TODO 1: OS Tick Configuration." /* * TODO 1: OS Tick Configuration * Enable the hardware timer and call the rt_os_tick_callback function * periodically with the frequency RT_TICK_PER_SECOND. */ /* Call components board initial (use INIT_BOARD_EXPORT()) */ #ifdef RT_USING_COMPONENTS_INIT rt_components_board_init(); #endif #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get()); #endif } #ifdef RT_USING_CONSOLE static int uart_init(void) { //#error "TODO 2: Enable the hardware uart and config baudrate." /*串口初始化有cubemx生成的函数在main.c完成*/ return 0; } INIT_BOARD_EXPORT(uart_init); void rt_hw_console_output(const char *str) { //#error "TODO 3: Output the string 'str' through the uart." /* 进入临界段 */ rt_enter_critical(); /* 直到字符串结束 */ while (*str!='\0') { /* 换行 */ if (*str=='\n') { HAL_UART_Transmit( &huart1,(uint8_t *)'\r',1,1000); } HAL_UART_Transmit( &huart1,(uint8_t *)(str++),1,1000); } /* 退出临界段 */ rt_exit_critical(); } #endif
测试:
读取多个寄存器值(测试通信包:01 03 00 00 00 40 44 3a)

写多个寄存器(通信包:
01 10 00 00 00 19 32 00 03 FF FF F0 00 FF 00 00 64 01 FF 00 64 0F 40 00 14 06 40 01 00 01 00 01 00 FF FF F0 00 FF 00 00 64 01 FF 00 64 0F 40 00 14 06 40 01 00 01 00 01 00 AD 45


浙公网安备 33010602011771号