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

 

posted @ 2023-02-10 15:53  辛河  阅读(1033)  评论(0)    收藏  举报