RT-thread(11) cubeMX+RT-Thread nano +Keil环境下,WIFI8266模块+2串口实现modbus协议通信

main.c 部分

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/*
    Arm Cortex-M0+ MCU with 128 Kbytes of Flash memory, 
    36 Kbytes RAM, 64 MHz CPU, 4x USART, timers, ADC, 
    comm. I/F, 2-3.6
*/
#include <rtthread.h>
#include <com_rtth_app.h>//#include <rth_modbus_usart.h>
/* USER CODE END Includes */

。。。。

  /* USER CODE BEGIN WHILE */
  global_regs_mux_setup();/*初始化内核变量,首先启动*/
  rtth_uart3_regs_setup();/*通过uart3通信,修改从机机寄存器值*/
  rtth_uart4_regs_setup();/*通过uart4通信,修改本机寄存器值*/
  rtth_wifi_regs_setup();/*通过WIFI通信,修改本机寄存器值*/
  while (1)
  {    
    /* USER CODE END WHILE */

串口1  用来做rtthread内核中 rt_kprintf ( )函数使用

参考:

RT-thread(2)RT-Thread 控制台与重载void rt_hw_console_output()函数   

https://www.cnblogs.com/excellentHellen/articles/16963025.html

串口2 同wifi模块使用,AT指令操作,WiFi模块作为热点组网,作为TCP-Server 通信。MCU外设没有使用DMA。

串口3 作为USART,通信,modbus协议中的主机。MCU外设使用DMA。

串口4作为USART,通信,,modbus协议中的从机。MCU外设使用DMA。

在 stm32g0xx_it.c文中,对中断函数修改如下:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32g0xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 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 "stm32g0xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <modbus_uart3_app.h>
#include <modbus_uart4_app.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* 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 -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern TIM_HandleTypeDef htim6;
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_usart3_tx;
extern DMA_HandleTypeDef hdma_usart4_rx;
extern DMA_HandleTypeDef hdma_usart4_tx;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;
extern UART_HandleTypeDef huart4;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M0+ Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVC_IRQn 0 */

  /* USER CODE END SVC_IRQn 0 */
  /* USER CODE BEGIN SVC_IRQn 1 */

  /* USER CODE END SVC_IRQn 1 */
}

/******************************************************************************/
/* STM32G0xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32g0xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles DMA1 channel 1 interrupt.
  */
void DMA1_Channel1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */

  /* USER CODE END DMA1_Channel1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart4_rx);
  /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */

  /* USER CODE END DMA1_Channel1_IRQn 1 */
}

/**
  * @brief This function handles DMA1 channel 2 and channel 3 interrupts.
  */
void DMA1_Channel2_3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 */

  /* USER CODE END DMA1_Channel2_3_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart4_tx);
  HAL_DMA_IRQHandler(&hdma_usart3_rx);
  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 */

  /* USER CODE END DMA1_Channel2_3_IRQn 1 */
}

/**
  * @brief This function handles DMA1 channel 4, channel 5, channel 6, channel 7 and DMAMUX1 interrupts.
  */
void DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Ch4_7_DMAMUX1_OVR_IRQn 0 */

  /* USER CODE END DMA1_Ch4_7_DMAMUX1_OVR_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart3_tx);
  /* USER CODE BEGIN DMA1_Ch4_7_DMAMUX1_OVR_IRQn 1 */

  /* USER CODE END DMA1_Ch4_7_DMAMUX1_OVR_IRQn 1 */
}

/**
  * @brief This function handles TIM6 global interrupt.
  */
void TIM6_IRQHandler(void)
{
  /* USER CODE BEGIN TIM6_IRQn 0 */

  /* USER CODE END TIM6_IRQn 0 */
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_IRQn 1 */

  /* USER CODE END TIM6_IRQn 1 */
}

/**
  * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

/**
  * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26.
  */
__weak void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

/**
  * @brief This function handles USART3 and USART4 interrupts.
  */
void USART3_4_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_4_IRQn 0 */
    Usart3_Receive_IDLE(&huart3);
  Usart4_Receive_IDLE(&huart3);
  /* USER CODE END USART3_4_IRQn 0 */
 // HAL_UART_IRQHandler(&huart3);
 // HAL_UART_IRQHandler(&huart4);
  /* USER CODE BEGIN USART3_4_IRQn 1 */

  /* USER CODE END USART3_4_IRQn 1 */
}

/* USER CODE BEGIN 1 */
/*
***********************************************
* 函 数 名:  HAL_UART3_RxCpltCallback
* 功能说明:  重载中断函数回调函数,处理接收数据事件,
             并发送rth内核邮件modbus_rx_mb给线程
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    HAL_UART3_RxCpltCallback(huart);
    HAL_UART4_RxCpltCallback(huart);
}
/*
***********************************************
* 函 数 名:  HAL_UART3_TxCpltCallback
* 功能说明:  重载中断函数回调函数,处理发送数据事件,
             并发送rth内核邮件modbus_tx_mb给线程
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  HAL_UART3_TxCpltCallback(huart);
    HAL_UART4_TxCpltCallback(huart);
}
/* USER CODE END 1 */
View Code

modbus协议程序

modbus_protocol.h

#ifndef MODBUS_PROTOCOL_H
#define MODBUS_PROTOCOL_H 
#include "stm32g0xx_hal.h"
//通信帧 最大字节数
#define MODBUS_FRAME_MAX_LEN  256+9

/*通信包类型*/
typedef struct  {
 uint8_t buf[MODBUS_FRAME_MAX_LEN];  /*字节指针*/
 uint16_t n_bytes;  /*字节数量*/
}modbus_frame_def;
//extern volatile modbus_frame_def modbus_frame;/*MODBUS帧*/
/*通信包类型*/
typedef struct  {
 uint16_t* p_regsList;//指针,指向寄存器数组
 uint16_t regsListLen;//寄存器数组元素个数    
 uint16_t regAdd;   /*操作的REG地址*/
 uint16_t regsNum;  /*操作的REG数量*/
 uint16_t slaveAdd; /*从机地址*/    
 uint16_t funCode;  /*0x10 与 0x03*/
 uint16_t newDataFlag; /*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/ 
}modbus_packet_def;
//extern volatile modbus_packet_def modbus_packet;/*MODBUS寄存器组*/
//extern   modbus_frame_def modbus_frame;  /*MODBUS 帧*/
//extern   modbus_packet_def modbus_packet;/*MODBUS寄存器组*/
/*测试帧
//01 03 00 01 00 02 95 cb
uint8_t s0x03[]={0x01,0x03,0x00,0x01,0x00,0x02,0x95,0xcb};
//01 03 04 12 2e 04 e8 9d cc
uint8_t r0x03[]={0x01,0x03,0x04,0x12,0x2e,0x04,0xe8,0x9d,0xcc};

//01 10 00 00 00 03 06 00 01 00 02 00 03 3a 81
uint8_t s0x10[]={0x01,0x10,0x00,0x00,0x00,0x03,0x06,0x00,0x01,0x00,0x02,0x00,0x03,0x3a,0x81};
//01 10 00 00 00 03  80 08
uint8_t r0x10[]={0x01,0x10,0x00,0x00,0x00,,0x03,0x80,0x08};
*/
void modbus_rtu_protocol_test(void);

uint8_t modbus_rtu_master_packet(modbus_packet_def* p_packet,modbus_frame_def* p_frame);/*主机打包成帧,放入p_array指向的数组*/
uint8_t modbus_rtu_master_unpack(modbus_packet_def* p_packet,modbus_frame_def* p_frame);/*主机将p_array指向数据,按帧解开*/
uint8_t modbus_rtu_slave_packet( modbus_packet_def* p_packet,modbus_frame_def* p_frame); /*从机打包成帧,放入p_array指向的数组*/
uint8_t modbus_rtu_slave_unpack( modbus_packet_def* p_packet,modbus_frame_def* p_frame); /*从机将p_array指向数据,按帧解开*/
/*校验算法*/
/*CRC-16/MODBUS       x16+x15+x2+1*/
/*返回CRC值,16bits*/
uint16_t Modbus_Rtu_CRC(uint8_t  *puchMsg, uint16_t usDataLen );//Modbus校验码计算函数,低字节在前,高字节在后
/*返回非零值,校验正确*/
uint8_t Modbus_Rtu_CRCCheck(uint8_t* pBuf,uint16_t dwLen);//Modbus校验函数
#endif
View Code

modbus_protocol.c

#include <modbus_protocol.h>
#include <rtthread.h> /*TR-Thread库*/

/*读一个或多个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))*/


/*
***********************************************
* 函 数 名: modbus_rtu_master_packet
* 功能说明: modbus-rtu 协议主机功能
            作为主机,组合询问帧  
* 形    参: p_packet 通信数据
             p_frame  已接受的帧
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t modbus_rtu_master_packet(modbus_packet_def* p_packet,modbus_frame_def* p_frame)/*主机发送帧*/
{
    uint8_t add;
    uint8_t funcode;
    uint16_t regsAdd;
    uint16_t regsNum;
    uint16_t index;
    uint16_t uint16_temp;  
    uint16_t* p_uint16_t; 
    /*输入访问从机及其寄存器*/    
    add    =p_packet->slaveAdd;/*从机地址*/
    regsAdd=p_packet->regAdd;  /*寄存器起始地址*/    
    regsNum=p_packet->regsNum; /*寄存器起始地址*/    
    funcode=p_packet->funCode; /*功能号*/    

    if(funcode==0x03)
    {
        /*组合发送通信包*/ /*读一个或多个REG量   0x03*/
        //主机(PC机)发出报文(8BYTES): 
        //站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE)        //添加通信包头    
        
        //添加通信包头    
        p_frame->buf[0] =add;//通信站号
        p_frame->buf[1] =funcode;  //功能号
        p_frame->buf[2] =regsAdd/256;  //寄存器地址
        p_frame->buf[3] =regsAdd%256;  //寄存器地址
        p_frame->buf[4] =regsNum/256;  //写的REG数量
        p_frame->buf[5] =regsNum%256;  //写的REG数量            
        //无添加寄存器内容
        //添加CRC校验码
        p_frame->n_bytes = 6;//用于计算CRC的字节数
        uint16_temp=Modbus_Rtu_CRC((uint8_t*)p_frame->buf, p_frame->n_bytes);
        p_frame->buf[p_frame->n_bytes] =   (uint16_temp>>8);  //CRC校验码低BYTES
      p_frame->buf[p_frame->n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES
        p_frame->n_bytes= p_frame->n_bytes+2;//发送字节数    
        return 0;
    }        
    if(funcode==0x10)
    {
        /*组合发送通信包*/ /*写一个或多个REG量  0x10*/
        //主机发送帧(9BYTES +2BYTES*写REG的数量):
        //站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE)
                
        /*检测p_packet是否正确*/
        if(p_packet->p_regsList ==0x0000){
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 1;//没有指向寄存器组错误
        }
        if(p_packet->regsListLen <regsNum+regsAdd){        
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 2;//超过寄存器组范围    
        }
        //添加通信包头    
        p_frame->buf[0] =add;//通信站号
        p_frame->buf[1] =funcode;  //功能号
        p_frame->buf[2] =regsAdd/256;  //寄存器地址
        p_frame->buf[3] =regsAdd%256;  //寄存器地址
        p_frame->buf[4] =regsNum/256;  //写的REG数量
        p_frame->buf[5] =regsNum%256;  //写的REG数量    
        p_frame->buf[6] =regsNum*2;    //写的REG字节数    
        // 向通信包添加寄存器值    
        p_uint16_t=p_packet->p_regsList;//取指针
        p_uint16_t = p_uint16_t+regsAdd;//移动指针,指向访问的首个REG    
    rt_enter_critical();/* 进入临界段,禁止线程切换 */        
        for(index=0;index<regsNum;index++)           //寄存器值
        { /*各线程公用变量读写*/
                uint16_temp=*p_uint16_t;/*MODBUS寄存器*/
            
                p_frame->buf[7+index*2]   = (uint16_temp/256);    // High byte
                p_frame->buf[7+index*2+1] = (uint16_temp%256);    // Low byte
              p_uint16_t++;//移动指针,指向下一个REG
        }
        rt_exit_critical();/* 退出临界段,允许线程切换 */
        //添加CRC校验码
        p_frame->n_bytes = 7+2*regsNum;//用于计算CRC的字节数
        uint16_temp=Modbus_Rtu_CRC((uint8_t*)p_frame->buf, p_frame->n_bytes);
        p_frame->buf[p_frame->n_bytes] =   (uint16_temp>>8);  //CRC校验码低BYTES
        p_frame->buf[p_frame->n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES
        p_frame->n_bytes= p_frame->n_bytes+2;//发送字节数    
        return 0;
    }
    return 0;
}
/*
***********************************************
* 函 数 名: modbus_rtu_master_unpack
* 功能说明: modbus-rtu 协议主机功能
            作为主机,解析应答帧 
* 形    参: p_packet 通信数据
             p_frame  已接受的帧
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t modbus_rtu_master_unpack(modbus_packet_def* p_packet,modbus_frame_def* p_frame)/*主机将p_array指向数据,按帧解开*/
{
  uint16_t regsAdd;
    uint16_t regsNum;
    uint16_t index;
    uint16_t uint16_temp;
  uint16_t* p_uint16_t; 
    //检测站号
    if(    p_frame->buf[0] != p_packet->slaveAdd)//从机地址检测;
        return 1;
  //检测功能号    
    if((p_frame->buf[1] != 0x03)&&(p_frame->buf[1] != 0x10))
        return 2;
    
    //功能类型 0x03通信包处理 解析从机应答通信包
    /*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES): 
        站号(BYTE1),功能号(BYTE1),读来报文数据区(REGS)的字节数(BYTE1), REGS数据(读来的数量*[HBYTE+LBYTE]),CRC16检验码(LBYTE+HBYTE)。*/    
  if(p_frame->buf[1] == 0x03)
    {    
        regsAdd =p_packet->regAdd  ; /*寄存器起始地址*/    
        regsNum =p_packet->regsNum  ;/*寄存器起始地址*/
        /*检测p_packet是否正确*/
        if(p_packet->p_regsList ==0x0000){
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 1;//没有指向寄存器组错误
        }
        if(p_packet->regsListLen <regsNum+regsAdd){        
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 2;//超过寄存器组范围    
        }        
        /*解析通信包*/
        //检测帧长度
        if(p_frame->n_bytes != regsNum*2+5)
           return 3;    
        // 报文中读来报文数据区(REGS)的字节数(BYTE1)检测
        if(regsNum*2!=p_frame->buf[2])
             return 4;
        // 获得寄存器值            
        p_packet->newDataFlag =0xFF00;/*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/
        p_uint16_t=p_packet->p_regsList;//取指针
        p_uint16_t = p_uint16_t+regsAdd;//移动指针,指向访问的首个REG
        
        rt_enter_critical();/* 进入临界段,禁止线程切换 */
        for(index=0;index<regsNum;index++)           //寄存器值
        { /*各线程公用变量读写*/
            uint16_temp=p_frame->buf[3+index*2]*2 +p_frame->buf[3+index*2+1];
      *p_uint16_t= uint16_temp;    //赋寄存器值    
            p_uint16_t++;//移动指针,指向下一个REG
        }
        rt_exit_critical();/* 退出临界段,允许线程切换 */
    } 
    
    //功能类型 0x10通信包处理     
    /*写一个或多个REG量  0x10*/
  /*从机返回帧(8BYTES):
        站号(1BYTE),功能号(1BYTE),  寄存器地址(HBYTE+LBYTE),写的REG数量(HBYTE+LBYTE),CRC校验码(LBYTE+HBYTE))*/
  if(p_frame->buf[1] == 0x10)
    {
        //检测通信包长度
        if(p_frame->n_bytes != 8)
           return 3;            
        // 计算寄存器地址和数量
        regsAdd = p_frame->buf[2];
        regsAdd =regsAdd<<8;
        regsAdd =regsAdd+p_frame->buf[3];
        regsNum = p_frame->buf[4];
        regsNum =regsNum<<8;
        regsNum =regsNum+p_frame->buf[5];
        // 检查寄存器地址和数量
      if(regsAdd !=p_packet->regAdd)/*寄存器起始地址*/    
                    return 4;
        if(regsNum !=p_packet->regsNum)/*寄存器数量*/
                    return 5;
    }         
    return 0;    
}
/*
***********************************************
* 函 数 名: modbus_rtu_slave_packet
* 功能说明: modbus-rtu 协议主机功能
            作为从机,组合应答帧 
* 形    参: p_packet 通信数据
             p_frame  已接受的帧
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t modbus_rtu_slave_packet(modbus_packet_def* p_packet,modbus_frame_def* p_frame) /*从机打包成帧,放入p_array指向的数组*/
{
    uint8_t add;
    uint8_t funcode;
    uint16_t regsAdd;
    uint16_t regsNum;
    uint16_t index;
    uint16_t uint16_temp;
  uint16_t* p_uint16_t; 
    
    add    =p_packet->slaveAdd;/*从机地址*/
    regsAdd=p_packet->regAdd;  /*寄存器起始地址*/    
    regsNum=p_packet->regsNum; /*寄存器起始地址*/    
    funcode=p_packet->funCode; /*功能号*/
    
    if(funcode==0x03)
    {
    //功能类型 0x03通信包处理,作为从机
    /*读一个或多个REG量  0x03 */
    /*从机(MCU) 应答报文(2BYTES*读REG的数量 +5BYTES): 
        站号(BYTE1),功能号(BYTE1),读来报文数据区的字节数(BYTE1), REGS数据(读来的数量*[HBYTE+LBYTE]),CRC16检验码(LBYTE+HBYTE)。*/    
        
        /*检测p_packet是否正确*/
        if(p_packet->p_regsList ==0x0000){
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 1;//没有指向寄存器组错误
        }
        if(p_packet->regsListLen <regsNum+regsAdd){        
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 2;//超过寄存器组范围    
        }        
        //添加通信包头    
        p_frame->buf[0] =add;//通信站号
        p_frame->buf[1] =funcode;  //功能号
        p_frame->buf[2] =regsNum*2;  //读来报文数据区的字节数(BYTE1)        
        //添加寄存器内容
        p_uint16_t=p_packet->p_regsList;//取指针
        p_uint16_t = p_uint16_t+regsAdd;//移动指针,指向访问的首个REG    
        
        rt_enter_critical();/* 进入临界段,禁止线程切换 */
        for(index=0;index<regsNum;index++)           //寄存器值
        { /*各线程公用变量读写*/
                uint16_temp= *p_uint16_t; /*MODBUS寄存器值*/
                p_frame->buf[3+index*2]   = (uint16_temp/256);    // High byte
                p_frame->buf[3+index*2+1] = (uint16_temp%256);    // Low byte
                p_uint16_t++;//移动指针,指向下一个REG
        }        
        rt_exit_critical();/* 退出临界段,允许线程切换 */
        //添加CRC校验码
        p_frame->n_bytes = 3+regsNum*2;//用于计算CRC的字节数
        uint16_temp=Modbus_Rtu_CRC((uint8_t*)p_frame->buf, p_frame->n_bytes);
        p_frame->buf[p_frame->n_bytes] =   (uint16_temp>>8);  //CRC校验码低BYTES
      p_frame->buf[p_frame->n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES
        p_frame->n_bytes= p_frame->n_bytes+2;//发送字节数    
        return 0;
    }        
    
    if(funcode==0x10)
    {
        /*组合发送通信包*/ /*写一个或多个REG量  0x10*/
        /*从机返回帧(8BYTES):
            站号(1BYTE),功能号(1BYTE),  寄存器地址(HBYTE+LBYTE),写的REG数量(HBYTE+LBYTE),CRC校验码(LBYTE+HBYTE))*/

        //添加通信包头    
        p_frame->buf[0] =add;//通信站号
        p_frame->buf[1] =funcode;  //功能号
        p_frame->buf[2] =regsAdd/256;  //寄存器地址
        p_frame->buf[3] =regsAdd%256;  //寄存器地址
        p_frame->buf[4] =regsNum/256;  //写的REG数量
        p_frame->buf[5] =regsNum%256;  //写的REG数量    
        // 无添加寄存器值    
        //添加CRC校验码
        p_frame->n_bytes = 6;//用于计算CRC的字节数
        uint16_temp=Modbus_Rtu_CRC((uint8_t*)p_frame->buf, p_frame->n_bytes);
        p_frame->buf[p_frame->n_bytes] =   (uint16_temp>>8);  //CRC校验码低BYTES
        p_frame->buf[p_frame->n_bytes+1] = (uint16_temp&0x00FF); //CRC校验码高BYTES
        p_frame->n_bytes= p_frame->n_bytes+2;//发送字节数    
        return 0;
    }
            
    return 0;
}
/*
***********************************************
* 函 数 名: modbus_rtu_slave_unpack
* 功能说明: modbus-rtu 协议主机功能
            作为从机,解析应答帧 
* 形    参: p_packet 通信数据
             p_frame  已接受的帧
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t modbus_rtu_slave_unpack(modbus_packet_def* p_packet,modbus_frame_def* p_frame) /*从机将p_array指向数据,按帧解开*/
{
    uint8_t  add;
    uint8_t  funcode;
    uint16_t regsAdd;
    uint16_t regsNum;
    uint16_t index;
    uint16_t uint16_temp;  
    uint16_t* p_uint16_t; 
    //检测站号
//    if(    p_frame->buf[0] != p_packet->slaveAdd)//从机地址检测;
//        return 1;
  //检测功能号    
    if((p_frame->buf[1] != 0x03)&&(p_frame->buf[1] != 0x10))
        return 2;
    //功能类型 0x03通信包处理,作为从机
    /*读一个或多个REG量  0x03 */
    /*主机(PC机)发出报文(8BYTES): 
        站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 读的REG数量(HBYTE+LBYTE) CRC16检验码(LBYTE+HBYTE)*/    
  if(p_frame->buf[1] == 0x03)
    {
        add     =p_frame->buf[0];//从机地址
        funcode =p_frame->buf[1];//功能号
        /*解析通信包*/
        //检测通信包长度
        if(p_frame->n_bytes != 8)
           return 3;            
        // 根据接收的通信包,计算寄存器地址和数量
        regsAdd = p_frame->buf[2];
        regsAdd =regsAdd<<8;
        regsAdd =regsAdd+p_frame->buf[3];
        regsNum = p_frame->buf[4];
        regsNum =regsNum<<8;
        regsNum =regsNum+p_frame->buf[5];
        // 记录PACKET特征
        p_packet->slaveAdd =add;      /*从机地址*/
        p_packet->regAdd   =regsAdd;  /*寄存器起始地址*/    
        p_packet->regsNum  =regsNum;  /*寄存器数量*/    
        p_packet->funCode  =funcode;  /*功能号*/        
    }
    
    //功能类型 0x10通信包处理,作为从机     
    /*写一个或多个REG量  0x10*/
    /*主机发送帧(9BYTES +2BYTES*写REG的数量):
        站号(1BYTE),功能号(1BYTE),开始地址(HBYTE+LBYTE), 写的数量(HBYTE+LBYTE),字节数(1BYTES),寄存器值序列([HBYTE+LBYTE]*写的数量),CRC校验码(LBYTE+HBYTE)*/
  if(p_frame->buf[1] == 0x10)
    {
        add     =p_frame->buf[0];//从机地址
        funcode =p_frame->buf[1];//功能号
        /*解析通信包*/
        //检测通信包长度
        if(p_frame->n_bytes != 9+p_frame->buf[6])               
            return 3;    
        // 根据接收的通信包,计算寄存器地址和数量
        regsAdd = p_frame->buf[2];
        regsAdd =regsAdd<<8;
        regsAdd =regsAdd+p_frame->buf[3];
        regsNum = p_frame->buf[4];
        regsNum =regsNum<<8;
        regsNum =regsNum+p_frame->buf[5];
        /*检测p_packet是否正确*/
        if(p_packet->p_regsList ==0x0000){
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 1;//没有指向寄存器组错误
        }
        if(p_packet->regsListLen <regsNum+regsAdd){        
            rt_kprintf("error@ %s %s:%d\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            return 2;//超过寄存器组范围    
        }        
        // 获得寄存器值            
        p_packet->newDataFlag =0xFF00;/*0xFF00:寄存器值已经改变,其他:寄存器值没有发生变化*/
        p_uint16_t=p_packet->p_regsList;//取指针
        p_uint16_t = p_uint16_t+regsAdd;//移动指针,指向访问的首个REG    
        
        rt_enter_critical();/* 进入临界段,禁止线程切换 */
        for(index=0;index<regsNum;index++)           //寄存器值
        {/*各线程公用变量读写*/
            uint16_temp=p_frame->buf[7+index*2]*2 +p_frame->buf[7+index*2+1];
      *p_uint16_t = uint16_temp;//写寄存器值
            p_uint16_t++;//移动指针,指向下一个REG
        }        
        rt_exit_critical();/* 退出临界段,允许线程切换 */
    // 记录PACKET特征
        p_packet->slaveAdd =add;      /*从机地址*/
        p_packet->regAdd   =regsAdd;  /*寄存器起始地址*/    
        p_packet->regsNum  =regsNum;  /*寄存器数量*/    
        p_packet->funCode  =funcode;  /*功能号*/        
    }    
    
    return 0;
}

/*校验算法                        */
/*CRC-16/MODBUS       x16+x15+x2+1*/
const uint8_t modbus_rtu_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 modbus_rtu_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_Rtu_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 ^ modbus_rtu_auchCRCHi[uIndex] ; 
        uchCRCHi = modbus_rtu_auchCRCLo[uIndex] ; 
    } 
    //return (uchCRCHi << 8 | uchCRCLo) ; 
    return (uchCRCHi  | uchCRCLo << 8) ; //  XINJE   低字节在前,高字节在后
} 
/**
  * @brief  Modbus_Rtu_CRC校验函数
  * @para   字节数组指针,数组长度
  * @retval 1 正确,0 错误
  */
uint8_t Modbus_Rtu_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_Rtu_CRC(pBuf,dwLen-2);
        
        crcframe = pBuf[dwLen-2]*256+  pBuf[dwLen-1];
        if(crcframe ==crc)
            bret = 0x01;
    }
    return(bret);
};
View Code

应用线程程序  com_rtth_app

com_rtth_app.h

#ifndef COM_RTTH_APP_H
#define COM_RTTH_APP_H
#include "stm32g0xx_hal.h"
// 串口2<-WIFI <-手机APP
// 串口4->RS485->LORA模块
// 串口3->RS485->电机、GPS模块
// 本机管理从机数量
#define TERMINAL_MAX_NUM 16 /*从机最大数量*/
// 从机最多128个寄存器
#define TERMINAL_REGS_NUM 57
// 在本机REGS组内,从机寄存器开始地址
#define TERMINAL_REGS_START_ADD 64
// 本机寄存器个数  前TERMINAL_REGS_START_ADD寄存器放本机参数,之后放从机参数
#define HOST_REGS_NUM   (TERMINAL_REGS_START_ADD +TERMINAL_MAX_NUM*TERMINAL_REGS_NUM )

typedef struct
{
    //当前操作从机ID号
    volatile    uint8_t Terminal_Index;// terminalsAdd[]的下标
    //影响从机寄存器的开始地址
    volatile    uint16_t terminal_regAdd;//必须按在从机内地址
    //影响寄存器的数量
    volatile    uint16_t terminal_regsNum;
    //MODBUS通信功能号    
    volatile    uint16_t funCode;  /*0x10 与 0x03*/
    //管理的从机数量,不得多于TERMINAL_MAX_NUM    
    volatile    uint8_t terminalsNum;
    // 从机地址表    
  volatile    uint8_t terminalsAdd[TERMINAL_MAX_NUM];
    //如果从机寄存器组的值发生变化,相应标志置1,依次为0号机,1号机,2号机。。。符合terminalsAdd[]的下标
    //从机寄存器值发生变化,将发送信息至从机,相应标志位清零
    volatile uint32_t changedBitFlag;    
} com_global_type;
/*全局变量*/
extern com_global_type com_global;
/*本机寄存器组,各寄存器用途见通信点表*/
extern volatile uint16_t hostRegs[HOST_REGS_NUM];/*本机寄存器组*/
extern volatile uint16_t terminalRegs[TERMINAL_REGS_NUM];/*从机寄存器组*/
/*互斥函数*/

uint8_t take_global_regs(uint32_t timeout);/*使用互斥量,取得全局变量global_regs使用权 */
uint8_t release_global_regs(void);/*使用互斥量,放弃全局变量global_regs使用权 */
/*线程启动函数*/
uint8_t global_regs_mux_setup(void);/*初始化内核变量,首先启动*/
void rtth_wifi_regs_setup(void); /*通过WIFI 通信,MODBUS通信*/
void rtth_uart3_regs_setup(void);/*通过uart3通信,MODBUS通信*/
void rtth_uart4_regs_setup(void);/*通过uart4通信,MODBUS通信*/
#endif
View Code

com_rtth_app.c

#include <com_rtth_app.h>
#include <rtthread.h>
#include <modbus_protocol.h>
#include <modbus_wifi_app.h>
#include <modbus_uart3_app.h>
#include <modbus_uart4_app.h>

/*全局变量*/
com_global_type com_global;
/*本机寄存器组,各寄存器用途见通信点表*/
volatile uint16_t hostRegs[HOST_REGS_NUM];/*本机寄存器组*/
volatile uint16_t terminalRegs[TERMINAL_REGS_NUM];/*从机寄存器组*/

/* RT-Thread 定义线程控制块指针 */
static rt_thread_t wifi_regs_thread  = RT_NULL;
static rt_thread_t uart3_regs_thread = RT_NULL;
static rt_thread_t uart4_regs_thread = RT_NULL;
/* 定义互斥量控制块 */
static rt_mutex_t global_regs_mux = RT_NULL;   // 防止多个线程同时使用全局变量masterGlobal
/*
***********************************************
* 函 数 名: rtth_uart3_regs_entry
* 功能说明: 线程程序,用来uart3通信,modbus_rtu帧
*           作为MODBUS主机,访问电机控制器、GPS等  
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
static   modbus_frame_def  uart3_frame;  /*MODBUS 帧*/
static   modbus_packet_def uart3_packet;/*MODBUS寄存器组*/
uint8_t rtth_exchange_regs(modbus_packet_def* p_packet,com_global_type* p_global);/*交换从机terminalRegs[]与主机hostRegs[]内的数据*/
static  void rtth_uart3_regs_entry(void* parameter)/*通过uart3通信,修改寄存器值*/
{
    /* RT-Thread 线程入口函数声明 */
    uint8_t ret;
    ret =0;
  ret = modbus_usart3_setup();/*初始化USART3模块及内核变量*/
    uart3_packet.p_regsList =(uint16_t*)(&terminalRegs[0]);//寄存器组首地址
    uart3_packet.regsListLen = sizeof(terminalRegs)/2;    
    
    while(1)
    {
    /*交换从机terminalRegs[]与主机hostRegs[]内的数据*/
        while(take_global_regs(1000)){};/*使用互斥量,取得全局变量global_regs使用权 */
        rtth_exchange_regs(&uart3_packet,&com_global);/*调用前设定全局变量com_global,首先指定功能码,0x10时,指定寄存器地址和数量、从机序号*/
                                             
//        uart3_packet.funCode =0x10;
//        uart3_packet.regAdd  =0;
//        uart3_packet.regsNum =3;
//    uart3_packet.slaveAdd =0x01;        
        /*组合询问帧*/    
    modbus_rtu_master_packet(&uart3_packet,&uart3_frame);/*解析询问帧*/    
    release_global_regs();/*使用互斥量,放弃全局变量global_regs使用权 */        
        /*通过usart3发询问帧*/
      ret=modbus_usart3_send(&uart3_frame,1000);           /*阻塞型,通过串口发送数据*/ 
        /*通过usart3收应答帧*/    
      ret=modbus_usart3_receive(&uart3_frame,1000);        /*阻塞型,通过串口收数据*/        
        /*解析应答帧*/        
        while(take_global_regs(1000)){};/*使用互斥量,取得全局变量global_regs使用权 */    
    modbus_rtu_master_unpack(&uart3_packet,&uart3_frame);/*解析应答帧*/         
    /*交换从机terminalRegs[]与主机hostRegs[]内的数据*/
        rtth_exchange_regs(&uart3_packet,&com_global);/*调用前设定全局变量com_global,首先指定功能码,0x10时,指定寄存器地址和数量、从机序号*/
    release_global_regs();/*使用互斥量,放弃全局变量global_regs使用权 */    
    }    
}
/*
***********************************************
* 函 数 名:  rtth_exchange_regs
* 功能说明:  因REGS地址不同,特殊交换从机terminalRegs[]与主机间hostRegs[]内的数据
* 形    参:
            p_packet:指向terminalRegs[]
            p_global:指向hostRegs[]
* 返 回 值: 无
* 全局变量:调用前设定全局变量com_global,首先指定功能码,0x10时,指定寄存器地址和数量、从机序号
************************************************
*/
uint8_t rtth_exchange_regs(modbus_packet_def* p_packet,com_global_type* p_global)/*交换从机terminalRegs[]与主机hostRegs[]内的数据*/
{    
    uint8_t ret;
    ret =0;
    uint16_t i;
    uint16_t index;
    uint16_t regsNum;
    uint16_t regAdd;
    uint32_t temp32;
  
    //0x10 *写一个或多个REG量  0x10*/
  if(p_global->funCode ==0x10)
    {//p_global=>p_packet
   p_packet->funCode  = 0x10;//MODBUS功能号
     p_global->funCode  = 0x10; //MODBUS功能号
        
     p_packet->slaveAdd = p_global->terminalsAdd[p_global->Terminal_Index];//从机站号,即通信地址
        
     if(p_global->terminal_regAdd<TERMINAL_REGS_START_ADD+p_global->Terminal_Index *TERMINAL_REGS_NUM)     
         return 1;//地址范围检查
     if(p_global->terminal_regAdd>=TERMINAL_REGS_START_ADD+(p_global->Terminal_Index+1)*TERMINAL_REGS_NUM)     
         return 1;//地址范围检查
     
   p_packet->regAdd   = p_global->terminal_regAdd-TERMINAL_REGS_START_ADD  
                            -p_global->Terminal_Index *TERMINAL_REGS_NUM;    //从机内寄存器地址
     p_packet->regsNum  = p_global->terminal_regsNum;                      //从机内寄存器数量
     regsNum = p_packet->regsNum;
     for(index=0;index<regsNum;index++)//寄存器赋值操作
     {
         terminalRegs[p_packet->regAdd+index] = hostRegs[p_global->terminal_regAdd+index];
     }        
    }
    //0x03 /*读一个或多个REG量  0x03 */
  if(p_packet->funCode ==0x03)
    {//p_packet=>p_global                
        p_global->funCode  =0x03; //MODBUS功能号
    p_packet->funCode  =0x03; //MODBUS功能号
        p_global->Terminal_Index =p_global->terminalsNum+1;        
        for(index=0;index<p_global->terminalsNum;index++)
        {
            if(p_packet->slaveAdd == p_global->terminalsAdd[index])
                p_global->Terminal_Index =index;//当前操作从机ID号
        }
        if(p_global->Terminal_Index>=p_global->terminalsNum)//检查当前操作从机ID号
            return 2;//超出管理的从机站号范围
        
        if(p_packet->regAdd>=TERMINAL_REGS_NUM)    return 3;//超出从机REGS数量范围
        p_global->terminal_regAdd = TERMINAL_REGS_START_ADD+(p_global->Terminal_Index)*TERMINAL_REGS_NUM;
        p_global->terminal_regAdd +=p_packet->regAdd;//寄存器地址
        p_global->terminal_regsNum  =p_packet->regsNum;//寄存器数量
        for(index=0;index<regsNum;index++)//寄存器赋值操作
        {
            hostRegs[p_global->terminal_regAdd+index]=terminalRegs[p_packet->regAdd+index];
        }            
    }        
    return 0;
}
/*
***********************************************
* 函 数 名:  rtth_uart3_regs_setup
* 功能说明:  初始化,用来uart3通信,modbus_rtu帧
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
 void rtth_uart3_regs_setup(void)/*通过uart3通信,修改寄存器值*/
 {
    /*创建线程控制块,并赋给一个该类型指针变量f*/
    uart3_regs_thread= /* 线程控制块指针 */
    rt_thread_create( "uart3_regs",  /* 线程名字 */
                                            rtth_uart3_regs_entry, /* 线程入口函数 */
                                            RT_NULL, /* 线程入口函数参数 */
                                            512, /* 线程栈大小 */
                                            2, /* 线程的优先级 */
                                            200); /* 线程时间片 */
    /* 启动线程,开启调度 */
    if (uart3_regs_thread != RT_NULL){
      rt_thread_startup(uart3_regs_thread); 
            return;
        }
    rt_kprintf("error@ %s %s:%d\r\n",
                   __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码        
 }
/*
***********************************************
* 函 数 名:  rtth_wifi_regs_entry
* 功能说明:  线程程序,用来wifi通信,tcp_server连接,作为modbus从机首发帧
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
static   modbus_frame_def  wifi_frame;  /*MODBUS 帧*/
static   modbus_packet_def wifi_packet;/*MODBUS寄存器组*/
static  void rtth_wifi_regs_entry(void* parameter)/*通过WIFI通信,修改寄存器值*/
{
    /* RT-Thread 线程入口函数声明 */
    uint8_t ret;
    ret =0;
  ret = modbus_wifi_setup();/*初始化WIFI模块及内核变量*/
    wifi_packet.p_regsList =(uint16_t*)(&hostRegs[0]);//寄存器组首地址
    wifi_packet.regsListLen = sizeof(hostRegs)/2;    
    
    while(1)
    {
     /*通过wifi收询问帧*/
     ret=modbus_wifi_receive(&wifi_frame,1000);/*阻塞型,通过WIFI串口接收数据*/      
     if(ret !=0) continue;//重新接收数据
        /*解析询问帧*/
     while(take_global_regs(1000)){};/*使用互斥量,取得全局变量global_regs使用权 */
   ret=modbus_rtu_slave_unpack(&wifi_packet,&wifi_frame);/*按帧解析*/    
   release_global_regs();/*使用互斥量,放弃全局变量global_regs使用权 */
     if(ret !=0) continue;//重新接收数据        
        /*通信处理*/
//   ret = rtth_exchange_regs(&wifi_packet,&masterGlobal);     

        /*组合应答帧*/    
     while(take_global_regs(1000)){};/*使用互斥量,取得全局变量global_regs使用权 */
   ret=modbus_rtu_slave_packet(&wifi_packet,&wifi_frame);/*按帧组合*/        
   release_global_regs();/*使用互斥量,放弃全局变量global_regs使用权 */
     if(ret !=0) continue;//重新接收数据        
        /*通过wifi发应答帧*/
     ret=modbus_wifi_send(&wifi_frame,1000);/*阻塞型,通过WIFI串口发送数据*/    
   rt_thread_delay(50); /* 延时 500 个 tick, 让出CPU */          
    }
}
/*
***********************************************
* 函 数 名:  rtth_wifi_regs_setup
* 功能说明:  初始化,用来wifi通信,tcp_server连接,传送modbus_rtu帧
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
* TCP SERVER的IP及端口见 <wifi_8266_driver.h>
* 按照此参数设置 TCP&UDP测试工具
*调用本函数,启动线程后,通信测试:
*TCP&UDP测试工具发:01 10 00 00 00 03 06 00 01 00 02 00 03 3a 81
*TCP&UDP测试工具收:01 10 00 00 00 03 80 08
*TCP&UDP测试工具发:01 03 00 00 00 06 c5 c8
*TCP&UDP测试工具收:01 03 0C 00 01 00 02 00 03 00 00 00 00 00 00 BD EC 
*/
 void rtth_wifi_regs_setup(void)/*通过WIFI通信,修改本机寄存器值*/
 {
    /*创建线程控制块,并赋给一个该类型指针变量f*/
    wifi_regs_thread = /* 线程控制块指针 */
    rt_thread_create( "wifi_regs",  /* 线程名字 */
                                            rtth_wifi_regs_entry, /* 线程入口函数 */
                                            RT_NULL, /* 线程入口函数参数 */
                                            512, /* 线程栈大小 */
                                            2, /* 线程的优先级 */
                                            200); /* 线程时间片 */
    /* 启动线程,开启调度 */
    if (wifi_regs_thread != RT_NULL){
      rt_thread_startup(wifi_regs_thread);
            return;
        }
    rt_kprintf("error@ %s %s:%d\r\n",
                   __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码        
 }

 /*
***********************************************
* 函 数 名: rtth_uart4_regs_entry
* 功能说明:  线程程序,用来uart4通信,作为modbus从机收发帧
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
static   modbus_frame_def  uart4_frame;  /*MODBUS 帧*/
static   modbus_packet_def uart4_packet;/*MODBUS寄存器组*/
static  void rtth_uart4_regs_entry(void* parameter)/*通过uart4通信,修改寄存器值*/
{
    /* RT-Thread 线程入口函数声明 */
    uint8_t ret;
    ret =0;
  ret = modbus_usart4_setup();/*初始化USART4模块及内核变量*/
    uart4_packet.p_regsList =(uint16_t*)(&hostRegs[0]);//寄存器组首地址
    uart4_packet.regsListLen = sizeof(hostRegs)/2;
    
    while(1)
    {
        /*通过usart4收询问帧*/
     ret=modbus_usart4_receive(&uart4_frame,1000);/*阻塞型,通过串口收数据*/        
     if(ret !=0) continue;//重新接收数据
        /*解析询问帧*/    
   while(take_global_regs(1000)){};/*使用互斥量,取得全局变量global_regs使用权 */
   ret=modbus_rtu_slave_unpack(&uart4_packet,&uart4_frame);/*解析询问帧*/        
   release_global_regs();/*使用互斥量,放弃全局变量global_regs使用权 */
     if(ret !=0) continue;//重新接收数据        
        /*通信处理*/
    //if(ret !=0) continue;//重新接收数据
        /*组合应答帧*/            
   while(take_global_regs(1000)){};/*使用互斥量,取得全局变量global_regs使用权 */
   ret=modbus_rtu_slave_packet(&uart4_packet,&uart4_frame);/*组合应答帧*/        
   release_global_regs();/*使用互斥量,放弃全局变量global_regs使用权 */
     if(ret !=0) continue;//重新接收数据        
        /*通过usart4应答帧*/
     ret=modbus_usart4_send(&uart4_frame,1000);/*阻塞型,通过串口发送数据*/
   rt_thread_delay(50); /* 延时 500 个 tick, 让出CPU */         
    }        
}
/*
***********************************************
* 函 数 名:  rtth_uart4_regs_setup
* 功能说明:  初始化,用来uart4通信,modbus_rtu帧
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*调用本函数,启动线程后,通信测试:
*串口助手发:01 10 00 00 00 03 06 00 01 00 02 00 03 3a 81
*串口助手收:01 10 00 00 00 03 80 08
*串口助手发:01 03 00 00 00 06 c5 c8
*串口助手收:01 03 0C 00 01 00 02 00 03 00 00 00 00 00 00 BD EC 
*/
 void rtth_uart4_regs_setup(void)/*通过uart4通信,修改本机寄存器值*/
 { 
      /*初始化MODBUS_RTU内核变量*/
       modbus_usart4_setup();                    
    /*创建线程控制块,并赋给一个该类型指针变量f*/
    uart4_regs_thread= /* 线程控制块指针 */
    rt_thread_create( "uart4_regs",  /* 线程名字 */
                                            rtth_uart4_regs_entry, /* 线程入口函数 */
                                            RT_NULL, /* 线程入口函数参数 */
                                            512, /* 线程栈大小 */
                                            2, /* 线程的优先级 */
                                            200); /* 线程时间片 */
    /* 启动线程,开启调度 */
    if (uart4_regs_thread != RT_NULL){             
            rt_thread_startup(uart4_regs_thread);
            return;
        }
    rt_kprintf("error@ %s %s:%d\r\n",
                   __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码        
 }
 
 /*
***********************************************
* 函 数 名: global_regs_mux_setup
* 功能说明:  初始化global_regs内核变量
* 形    参:无
* 返 回 值: 0,正常; 非0,错误
* 全局变量:modbus_usart_mux,modbus_uart_it_event
************************************************
*/
uint8_t global_regs_mux_setup(void) /*初始化内核变量*/
{
    /*创建一个互斥信号量*/
    global_regs_mux = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO);
    if (global_regs_mux == RT_NULL){
        rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码    
        return 1;
    }        
return 0;
}
/*
***********************************************
* 函 数 名: take_global_regs
* 功能说明: 使用内核互斥量,取得全局变量global_regs使用权 
* 形    参:timeout 超时,单位ticks
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t take_global_regs(uint32_t timeout)   /*使用互斥量,取得全局变量global_regs使用权 */
{        rt_err_t uwRet = RT_EOK;
    uwRet=rt_mutex_take(global_regs_mux, /* 获取互斥量 */
                              timeout); /* 等待时间:等timeout个ticks */ 
      if(uwRet != RT_EOK ) return 1;
return 0;
}
/*
***********************************************
* 函 数 名: release_global_regs
* 功能说明: 使用内核互斥量,放弃全局变量global_regs使用权 
* 形    参:无
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:
* 特殊说明:
************************************************
*/
uint8_t release_global_regs(void)/*使用互斥量,放弃全局变量global_regs使用权 */
{    
    rt_mutex_release( global_regs_mux ); //释放互斥量
  return 0;
}

 
View Code

wifi模块与串口通信的BSP程序

 wifi8266_uart_bsp.h

/**
 ****************************************************************************************************
 * @file        wifi8266_uart_bsp.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的硬件驱动函数
 *
 ****************************************************************************************************
 */

#ifndef __WIFI8266_UART_BSP_H
#define __WIFI8266_UART_BSP_H

//#include "./SYSTEM/sys/sys.h"
#include "stm32g0xx_hal.h"
/**
 *是否使用RT—thread操作系统
 * WIFI_USING_RT_THREAD_OS用于定义系统文件夹是否支持OS
 * 0,不支持OS
 * 1,支持OS
 */
#define  WIFI_USING_RT_THREAD_OS 1
/* 如果使用os,则包括下面的头文件即可. */
#if WIFI_USING_RT_THREAD_OS
#include <rtthread.h> /* os 使用 */
#endif

/* 引脚定义 */
#define WIFI8266_UART_TX_GPIO_PORT           GPIOD
#define WIFI8266_UART_TX_GPIO_PIN            GPIO_PIN_5
#define WIFI8266_UART_TX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PC口时钟使能 */

#define WIFI8266_UART_RX_GPIO_PORT           GPIOD
#define WIFI8266_UART_RX_GPIO_PIN            GPIO_PIN_6
#define WIFI8266_UART_RX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PD口时钟使能 */

#define WIFI8266_UART_INTERFACE              USART2
#define WIFI8266_UART_IRQn                   USART2_IRQn
#define WIFI8266_UART_IRQHandler             USART2_IRQHandler

#define WIFI8266_UART_CLK_ENABLE()           do{ __HAL_RCC_UART5_CLK_ENABLE(); }while(0) /* UART5时钟使能 */

/* UART收发缓冲大小 */
#define WIFI8266_UART_RX_BUF_SIZE            256
#define WIFI8266_UART_TX_BUF_SIZE            256

#ifdef WIFI_USING_RT_THREAD_OS
/*防止多个线程访问WIFI,调用wifi接口函数时使用*/
void wifi8266_lock(void);   //获取互斥量,得到WIFI
void wifi8266_unlock(void); //释放互斥量,失去WIFI
/*用于接收帧处理函数与接收中断通信*/
uint8_t wifi8266_take_sem(rt_int32_t timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
#endif
/* 操作函数 */
void wifi_delay_ms(uint16_t n);                  /*等待WIFI8266应答的延时函数*/
void wifi8266_uart_printf(char *fmt, ...);       /* WIFI8266 UART printf */
void wifi8266_uart_sendData(uint8_t *p_frame,uint16_t len);/* 向WIFI8266 UART发送8位数据组*/
void wifi8266_uart_rx_restart(void);             /* WIFI8266 UART重新开始接收数据 */
uint8_t *wifi8266_uart_rx_get_frame(void);       /* 获取WIFI8266 UART接收到的一帧数据 */
uint16_t wifi8266_uart_rx_get_frame_len(void);   /* 获取WIFI8266 UART接收到的一帧数据的长度 */
void wifi8266_uart_init(uint32_t baudrate);      /* WIFI8266 UART初始化 */

#endif
View Code

wifi8266_uart_bsp.c

/**
 ****************************************************************************************************
 * @file        wifi8266_uart_bsp.c
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的硬件驱动函数
 *
 ****************************************************************************************************
 */
#include <wifi8266_uart_bsp.h>

#include "usart.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>


static UART_HandleTypeDef g_uart_handle;                    /* WIFI8266 UART */

static struct
{
    uint8_t buf[WIFI8266_UART_RX_BUF_SIZE];                 /* 帧接收缓冲 */
    struct
    {
        uint16_t len    : 15;                               /* 帧接收长度,sta[14:0] */
        uint16_t finsh  : 1;                                /* 帧接收完成标志,sta[15] */
    } sta;                                                  /* 帧状态信息 */
} g_uart_rx_frame = {0};                                    /* WIFI8266 UART接收帧缓冲信息结构体 */


static uint8_t g_uart_tx_buf[WIFI8266_UART_TX_BUF_SIZE]; /* WIFI8266 UART发送缓冲 */

#if WIFI_USING_RT_THREAD_OS

/* 定义信号量*/
rt_sem_t WIFI8266_RX_IT_sem = RT_NULL;
/* 定义互斥量控制块 */
static rt_mutex_t WIFI8266_usart_mux = RT_NULL;   // 防止多个线程同时使用WIFI及相应的串口外设


#endif

/**
 * @brief       等待WIFI8266应答的延时函数
 * @param       无
 * @retval      无
 */
//static 
void delay_one_ms(void)/*等待WIFI8266应答的延时函数*/
{
    uint16_t i;
    /* 
         下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
        循环次数为10时,SCL频率 = 205KHz 
        循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
         循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
    */
    for (i = 0; i < 1366; i++);
}
//__weak 
void wifi_delay_ms(uint16_t n)
{
      uint16_t i;
        for (i = 0; i < n; i++)
           delay_one_ms();
}
/**
 * @brief       WIFI8266 UART printf
 * @param       fmt: 待打印的数据
 * @retval      无
 */
void wifi8266_uart_printf(char *fmt, ...)
{
    va_list ap;//定义一具VA_LIST型的变量,包括 #include <stdarg.h>
    uint16_t len;
    
    va_start(ap, fmt);//用VA_START宏初始化变量刚定义的VA_LIST变量
    vsprintf((char *)g_uart_tx_buf, fmt, ap);//具VA_LIST型的变量
    va_end(ap);//最后用VA_END宏结束可变参数的获取
    
    len = strlen((const char *)g_uart_tx_buf);
    HAL_UART_Transmit(&g_uart_handle, g_uart_tx_buf, len, HAL_MAX_DELAY);
}
/**
* @brief        向WIFI8266 UART发送8位数据组
 * @param       p_frame: 首个BYTE地址,len:字节数
 * @retval      无
 */
void wifi8266_uart_sendData(uint8_t *p_frame,uint16_t len)// 向WIFI8266 UART发送8位数据组
{
    HAL_UART_Transmit(&g_uart_handle, p_frame, len, HAL_MAX_DELAY);
}
/**
 * @brief       WIFI8266 UART重新开始接收数据
 * @param       无
 * @retval      无
 */
void wifi8266_uart_rx_restart(void)
{
      //memset( g_uart_rx_frame.buf,0,sizeof(g_uart_rx_frame.buf));//清零
    g_uart_rx_frame.sta.len     = 0;
    g_uart_rx_frame.sta.finsh   = 0;
}

/**
 * @brief       获取WIFI8266 UART接收到的一帧数据
 * @param       无
 * @retval      NULL: 未接收到一帧数据
 *              其他: 接收到的一帧数据
 */
uint8_t *wifi8266_uart_rx_get_frame(void)
{
    if (g_uart_rx_frame.sta.finsh == 1)
    {
        g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = '\0';//末尾加字符串结束符
        return g_uart_rx_frame.buf;
    }
    else
    {
        return NULL;
    }
}

/**
 * @brief       获取WIFI8266 UART接收到的一帧数据的长度
 * @param       无
 * @retval      0   : 未接收到一帧数据
 *              其他: 接收到的一帧数据的长度
 */
uint16_t wifi8266_uart_rx_get_frame_len(void)
{
    if (g_uart_rx_frame.sta.finsh == 1)
    {
        return g_uart_rx_frame.sta.len;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief       WIFI8266 UART初始化
 * @param       baudrate: UART通讯波特率
 * @retval      无
 */
void wifi8266_uart_init(uint32_t baudrate)
{
    g_uart_handle.Instance          = WIFI8266_UART_INTERFACE;   /* WIFI8266 UART */
    g_uart_handle.Init.BaudRate     = baudrate;                     /* 波特率 */
    g_uart_handle.Init.WordLength   = UART_WORDLENGTH_8B;           /* 数据位 */
    g_uart_handle.Init.StopBits     = UART_STOPBITS_1;              /* 停止位 */
    g_uart_handle.Init.Parity       = UART_PARITY_NONE;             /* 校验位 */
    g_uart_handle.Init.Mode         = UART_MODE_TX_RX;              /* 收发模式 */
    g_uart_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;          /* 无硬件流控 */
    g_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16;         /* 过采样 */
    HAL_UART_Init(&g_uart_handle);                                  /* 使能WIFI8266 UART
                                                                     * HAL_UART_Init()会调用函数HAL_UART_MspInit()
                                                                     * 该函数定义在文件usart.c中
                                                                     */
     __HAL_UART_ENABLE_IT(&g_uart_handle, UART_IT_IDLE);// 开空闲中断
     __HAL_UART_ENABLE_IT(&g_uart_handle, UART_IT_RXNE);// 开接收非空中断    
    
    #if WIFI_USING_RT_THREAD_OS
    /* 创建一个互斥信号*/  
        WIFI8266_usart_mux = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO);
    if (WIFI8266_usart_mux == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The mux for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
    /* 创建一个信号*/
        WIFI8266_RX_IT_sem = rt_sem_create("binary_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (WIFI8266_RX_IT_sem == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
        
    #endif
}
/**
 * @brief       WIFI8266_UART互斥锁
 * @param       baudrate: UART通讯波特率
 * @retval      无
 */
#if WIFI_USING_RT_THREAD_OS
void wifi8266_lock(void)
{
   rt_mutex_take( WIFI8266_usart_mux,RT_WAITING_FOREVER); //获取互斥量
}
void wifi8266_unlock(void)
{
   rt_mutex_release( WIFI8266_usart_mux ); //释放互斥量 
}    
#endif

/**
 * @brief       线程调用的应用函数与串口通信中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
#if WIFI_USING_RT_THREAD_OS
uint8_t wifi8266_take_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //WIFI8266_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(WIFI8266_RX_IT_sem, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d, WIFI8266_RX_IT_sem:time out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,WIFI8266_RX_IT_sem:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t wifi8266_release_sem(void)
{
    rt_sem_release(WIFI8266_RX_IT_sem);//放出信号量
    return 0;
}
#endif
/**
 * @brief       WIFI8266 UART中断回调函数
 * @param       无
 * @retval      无
 */
void WIFI8266_UART_IRQHandler(void)
{
    uint8_t tmp;
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
    {
        __HAL_UART_CLEAR_OREFLAG(&g_uart_handle);                           /* 清除接收过载错误中断标志 */
        (void)g_uart_handle.Instance->ISR;//SR;                                   /* 先读SR寄存器,再读DR寄存器 */
        (void)g_uart_handle.Instance->RDR;//DR;
    }
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_RXNE) != RESET)       /* UART接收中断 */
    {
        HAL_UART_Receive(&g_uart_handle, &tmp, 1, HAL_MAX_DELAY);           /* UART接收数据 */
        
        if (g_uart_rx_frame.sta.len < (WIFI8266_UART_RX_BUF_SIZE - 1))   /* 判断UART接收缓冲是否溢出
                                                                             * 留出一位给结束符'\0'
                                                                             */
        {
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
        else                                                                /* UART接收缓冲溢出 */
        {
            g_uart_rx_frame.sta.len = 0;                                    /* 覆盖之前收到的数据 */
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
            g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
        }
    }
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_IDLE) != RESET)       /* UART总线空闲中断 */
    {
        g_uart_rx_frame.sta.finsh = 1;                                      /* 标记帧接收完成 */
        
        __HAL_UART_CLEAR_IDLEFLAG(&g_uart_handle);                          /* 清除UART总线空闲中断 */
            
            #if WIFI_USING_RT_THREAD_OS
            wifi8266_release_sem();                                                     /* 发送信号,通知帧接收完成 */
            #endif
    }
}
View Code

wifi模块driver用AT指令操作WIFI程序

 wifi_8266_app.h

/**
 ****************************************************************************************************
 * @file        wifi_8266_driver.h
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的应用函数
 *
 ****************************************************************************************************
 */

#ifndef __WIFI_8266_DRIVER_H
#define __WIFI_8266_DRIVER_H

#include "stm32g0xx_hal.h"
#include <wifi8266_uart_bsp.h>
/*网络设置*/
#define WIFI_SSID          "REGAIN_001" /*WIFI热点名称*/
#define WIFI_PWD           "1234567899" /*WIFI热点密码*/
#define WIFI_AP_TCP_SERVER_IP      "192.168.8.1" /*设置WIFI服务器的IP*/
#define WIFI_AP_TCP_SERVER_PORT    "8181"        /*设置WIFI服务器端口*/

/* 引脚定义 */
#define WIFI8266_RST_GPIO_PORT           GPIOC
#define WIFI8266_RST_GPIO_PIN            GPIO_PIN_4
#define WIFI8266_RST_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)   /* PC口时钟使能 */

/* IO操作 */
#define WIFI8266_RST(x)                  do{ x ?                                                                                     \
                                                HAL_GPIO_WritePin(WIFI8266_RST_GPIO_PORT, WIFI8266_RST_GPIO_PIN, GPIO_PIN_SET) :  \
                                                HAL_GPIO_WritePin(WIFI8266_RST_GPIO_PORT, WIFI8266_RST_GPIO_PIN, GPIO_PIN_RESET); \
                                            }while(0)

/* 错误代码 */
#define WIFI8266_EOK         0   /* 没有错误 */
#define WIFI8266_ERROR       1   /* 通用错误 */
#define WIFI8266_ETIMEOUT    2   /* 超时错误 */
#define WIFI8266_EINVAL      3   /* 参数错误 */
                                                                                        
/* TCP客户端与服务器间通信使用,本机WIFI模块作为服务器 */                                                                                        
typedef  struct
{
  char  data[WIFI8266_UART_RX_BUF_SIZE];/* 帧接收的数据 */
    uint16_t linkNo;                         /* 收到网络连接的ID号*/
    uint16_t len;                            /* 数据长度 */
} TCP_FRAME_TYPE;
extern volatile TCP_FRAME_TYPE g_uart_tcp_frame;
                                                                                        

/* 操作函数 */
uint8_t wifi8266_init(uint32_t baudrate);                                /* WIFI8266初始化 */
void wifi8266_hw_reset(void);                                            /* WIFI8266硬件复位 */
uint8_t wifi8266_send_at_cmd(char *cmd, char *ack, uint32_t timeout);    /* WIFI8266发送AT指令 */                                                                                    
uint8_t wifi8266_restore(void);                                          /* WIFI8266恢复出厂设置 */
uint8_t wifi8266_at_test(void);                                          /* WIFI8266 AT指令测试 */
uint8_t wifi8266_sw_reset(void);                                         /* WIFI8266软件复位 */
uint8_t wifi8266_ate_config(uint8_t cfg);                                /* WIFI8266设置回显模式 */                                                                                    
uint8_t wifi8266_set_mode(uint8_t mode);                                 /* 设置WIFI8266工作模式 1 STA,2 AP,3AP+STA*/    
/*WIFI的IP地址*/                                                                                        
uint8_t wifi8266_cipap_set(char *ip);                                    /*AT+CIPAP   WIFI模块AP模式的IP设置*/
uint8_t wifi8266_get_ip(char *buf);                                      /* WIFI8266获取热点分配的IP地址 */
/*WIFI组网*/
uint8_t wifi8266_join_ap(char *ssid, char *pwd);                         /* WIFI8266连接外部WIFI热点 */
uint8_t wifi8266_set_ap(char *ssid, char *pwd,uint8_t channel,           /*设置WIFI8266模块成WIFI热点,及其账户密码等信息*/
                          uint8_t encryptionMethod, uint8_t  maxConnection,
                        uint8_t broadCast);        
/*WIFI模块作为TCP Server通信*/
uint8_t wifi8266_cipmux_set(uint8_t setMux);                             /*AT+CIPMUX  WIFI模块AP模式多连接*/
uint8_t wifi8266_servicer_set(uint8_t Enable,char *port);               /*AT+CIPMUX=1 ,AT+CIPSERVER指令,将WIFI模块设置为TCP服务器*/

uint8_t wifi8266_connect_tcp_server(char *server_ip, char *server_port); /* WIFI8266连接外部TCP服务器 */
uint8_t wifi8266_tcp_server_receive(uint32_t timeout);                   /* WIFI8266连接外部TCP服务器 +IPD*/
uint8_t wifi8266_servicer_get_data(void);                                /*解析通信帧,得到客户端发来的信息*/
uint8_t wifi8266_tcp_server_sendcmd( uint16_t linkNo,uint16_t len);      /*向客户端发送数据帧的AT指令及客户端链接号,数据字节数*/
uint8_t wifi8266_servicer_sendData(void);                                /*向客户端发送数据帧*/

/*WIFI模块透传*/
uint8_t wifi8266_enter_unvarnished(void);                                /* WIFI8266进入透传 */
void wifi8266_exit_unvarnished(void);                                    /* WIFI8266退出透传 */

uint8_t wifi8266_connect_atkcld(char *id, char *pwd);                    /* WIFI8266连接原子云服务器 */
uint8_t wifi8266_disconnect_atkcld(void);                                /* WIFI8266断开原子云服务器连接 */

#endif
View Code

wifi_8266_app.c

#include <wifi_8266_driver.h>
/**
 ****************************************************************************************************
 * @file        wifi_8266_app.c
 * @author      
 * @version     V1.0
 * @date        2023-02-22
 * @brief       
 * @license    
 ****************************************************************************************************
 * @attention
 *        AT指令操作WIFI模块的应用函数
 *
 ****************************************************************************************************
 */
#include <wifi_8266_driver.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> //字符串和数值间转换

/*TCP帧信息*/
volatile TCP_FRAME_TYPE g_uart_tcp_frame={0};
/**
 * @brief       WIFI8266硬件初始化
 * @param       无
 * @retval      无
 * @attention   WIFI模块复位引脚,设置GPIO引脚控制复位
 */
static void wifi8266_hw_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    WIFI8266_RST_GPIO_CLK_ENABLE();
    
    gpio_init_struct.Pin = WIFI8266_RST_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(WIFI8266_RST_GPIO_PORT, &gpio_init_struct);
}

/**
 * @brief       WIFI8266硬件复位
 * @param       无
 * @retval      无
 */
/*来自WIFI模块的应答*/
void wifi8266_hw_reset(void)
{
    WIFI8266_RST(0);
    wifi_delay_ms(100);
    WIFI8266_RST(1);
    wifi_delay_ms(500);
}

/**
 * @brief       WIFI8266发送AT指令
 * @param       cmd    : 待发送的AT指令
 *              ack    : 等待的响应
 *              timeout: 等待超时时间
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 */
uint8_t wifi8266_send_at_cmd(char *cmd, char *ack, uint32_t timeout)
{
    uint8_t *ret = NULL;
      uint8_t ubRet;
    
    wifi8266_uart_rx_restart();
      wifi8266_uart_printf("%s\r\n", cmd); //发送AT指令
    
    if ((ack == NULL) || (timeout == 0))
    { 

        return WIFI8266_EOK;
    }
    else
    {
             #ifndef WIFI_USING_RT_THREAD_OS
        while (timeout > 0)//延时循环查询WIFI回传通信帧,判断是否接收
        {
            ret = wifi8266_uart_rx_get_frame();
            if (ret != NULL)
            {
                if (strstr((const char *)ret, ack) != NULL)//在ret字符串中查找 ack字符串
                                {                                
                    return WIFI8266_EOK;
                }
                else
                {
                    wifi8266_uart_rx_restart();//清零RX缓冲
                }
            }
            timeout--;
            wifi_delay_ms(1);
        }    
            #endif
            #ifdef WIFI_USING_RT_THREAD_OS        
            receive:
            //ubRet = wifi8266_wait_event(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            ubRet = wifi8266_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            if( ubRet ==0){//从通信帧中查找ack
                ret = wifi8266_uart_rx_get_frame();
                if (ret != NULL)
                {
                        if (strstr((const char *)ret, ack) != NULL)//在ret字符串中查找 ack字符串
                        {                                
                                return WIFI8266_EOK;//从通信帧中找到ack
                        }
                        else
                        {            
                              wifi8266_uart_rx_restart();//清零RX缓冲
                                goto receive;//从通信帧中没有找到ack,再次接收新的帧
                        }
                }    
        else{// ret没有字符串
                    wifi8266_uart_rx_restart();//清零RX缓冲
                    return  WIFI8266_ERROR;
                }                    
            }
            if(ubRet ==1){
                /*超时,没有接收数据*/
                return WIFI8266_ETIMEOUT;        
            }
            if(ubRet ==2){
              /*错误,没有接收数据*/
              return WIFI8266_ERROR;            
            }
            #endif
            return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266的AT指令
 * @param       cmd    : 待发送的AT指令
 *              ack    : 等待的响应
 *              timeout: 等待超时时间
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 */
uint8_t wifi8266_get_cmd_echo( char *ack, uint32_t timeout)
{
    uint8_t *ret = NULL;
      uint8_t ubRet;
    
    wifi8266_uart_rx_restart();
    if ((ack == NULL) || (timeout == 0))
    { 

        return WIFI8266_EOK;
    }
    else
    {
             #ifndef WIFI_USING_RT_THREAD_OS
        while (timeout > 0)//延时循环查询WIFI回传通信帧,判断是否接收
        {
            ret = wifi8266_uart_rx_get_frame();
            if (ret != NULL)
            {
                if (strstr((const char *)ret, ack) != NULL)//在ret字符串中查找 ack字符串
                                {                                
                    return WIFI8266_EOK;
                }
                else
                {
                    wifi8266_uart_rx_restart();//清零RX缓冲
                }
            }
            timeout--;
            wifi_delay_ms(1);
        }    
            #endif
            #ifdef WIFI_USING_RT_THREAD_OS        
            receive:
            //ubRet = wifi8266_wait_event(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            ubRet = wifi8266_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
            if( ubRet ==0){//从通信帧中查找ack
                ret = wifi8266_uart_rx_get_frame();
                if (ret != NULL)
                {
                        if (strstr((const char *)ret, ack) != NULL)//在ret字符串中查找 ack字符串
                        {                                
                                return WIFI8266_EOK;//从通信帧中找到ack
                        }
                        else
                        {            
                              wifi8266_uart_rx_restart();//清零RX缓冲
                                goto receive;//从通信帧中没有找到ack,再次接收新的帧
                        }
                }    
        else{// ret没有字符串
                    wifi8266_uart_rx_restart();//清零RX缓冲
                    return  WIFI8266_ERROR;
                }                    
            }
            if(ubRet ==1){
                /*超时,没有接收数据*/
                return WIFI8266_ETIMEOUT;        
            }
            if(ubRet ==2){
              /*错误,没有接收数据*/
              return WIFI8266_ERROR;            
            }
            #endif
            return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266初始化
 * @param       baudrate: WIFI8266 UART通讯波特率
 * @retval      WIFI8266_EOK  : WIFI8266初始化成功,函数执行成功
 *              WIFI8266_ERROR: WIFI8266初始化失败,函数执行失败
 */
uint8_t wifi8266_init(uint32_t baudrate)
{
    wifi8266_hw_init();                          /* WIFI8266硬件初始化 */
    wifi8266_hw_reset();                         /* WIFI8266硬件复位 */
    wifi8266_uart_init(baudrate);                /* WIFI8266 UART初始化 */
    if (wifi8266_at_test() != WIFI8266_EOK)   /* WIFI8266 AT指令测试 */
    {
        return WIFI8266_ERROR;
    }
    
    return WIFI8266_EOK;
}

/**
 * @brief       WIFI8266恢复出厂设置
 * @param       无
 * @retval      WIFI8266_EOK  : 恢复出场设置成功
 *              WIFI8266_ERROR: 恢复出场设置失败
 */
/*来自WIFI模块的应答*/
//AT+RESTORE

//OK

// ...

//ready
uint8_t wifi8266_restore(void)
{
    uint8_t ret;
    
    ret = wifi8266_send_at_cmd("AT+RESTORE", "ready", 30000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266 AT指令测试
 * @param       无
 * @retval      WIFI8266_EOK  : AT指令测试成功
 *              WIFI8266_ERROR: AT指令测试失败
 */
/*来自WIFI模块的应答*/
//AT

//OK
uint8_t wifi8266_at_test(void)
{
    uint8_t ret;
    uint8_t i;
    
    for (i=0; i<10; i++)
    {
        ret = wifi8266_send_at_cmd("AT", "OK", 500);
        if (ret == WIFI8266_EOK)
        {
            return WIFI8266_EOK;
        }
    }
    
    return WIFI8266_ERROR;
}

/**
 * @brief       设置WIFI8266工作模式
 * @param       mode: 1,Station模式
 *                    2,AP模式
 *                    3,AP+Station模式
 * @retval      WIFI8266_EOK   : 工作模式设置成功
 *              WIFI8266_ERROR : 工作模式设置失败
 *              WIFI8266_EINVAL: mode参数错误,工作模式设置失败
 */
/*来自WIFI模块的应答*/
//AT+CWMODE=1

//OK
uint8_t wifi8266_set_mode(uint8_t mode)
{
    uint8_t ret;
    
    switch (mode)
    {
        case 1:
        {
            ret = wifi8266_send_at_cmd("AT+CWMODE_DEF=1", "OK", 500);    /* Station模式 */
            break;
        }
        case 2:
        {
            ret = wifi8266_send_at_cmd("AT+CWMODE_DEF=2", "OK", 500);    /* AP模式 */
            break;
        }
        case 3:
        {
            ret = wifi8266_send_at_cmd("AT+CWMODE_DEF=3", "OK", 500);    /* AP+Station模式 */
            break;
        }
        default:
        {
            return WIFI8266_EINVAL;
        }
    }
    
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266软件复位
 * @param       无
 * @retval      WIFI8266_EOK  : 软件复位成功
 *              WIFI8266_ERROR: 软件复位失败
 *//*来自WIFI模块的应答*/
//AT+RST

//OK

// ets Jan  8 2013,rst cause:2, boot mode:(3,7)
// ...
//ready
uint8_t wifi8266_sw_reset(void)
{
    uint8_t ret;
    
    ret = wifi8266_send_at_cmd("AT+RST", "ready", 10000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266设置回显模式
 * @param       cfg: 0,关闭回显
 *                   1,打开回显
 * @retval      WIFI8266_EOK  : 设置回显模式成功
 *              WIFI8266_ERROR: 设置回显模式失败
 * @specifi     回显,在WIFI模块返回应答的时候,首先返回发给它的指令。关闭时,直接返回应答。
 *//*来自WIFI模块的应答*/
//ATE1

//OK
uint8_t wifi8266_ate_config(uint8_t cfg)
{
    uint8_t ret;
    
    switch (cfg)
    {
        case 0:
        {
            ret = wifi8266_send_at_cmd("ATE0", "OK", 500);   /* 关闭回显 */
            break;
        }
        case 1:
        {
            ret = wifi8266_send_at_cmd("ATE1", "OK", 500);   /* 打开回显 */
            break;
        }
        default:
        {
            return WIFI8266_EINVAL;
        }
    }
    
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266连接WIFI
 * @param       ssid: WIFI名称
 *              pwd : WIFI密码
 * @retval      WIFI8266_EOK  : WIFI连接成功
 *              WIFI8266_ERROR: WIFI连接失败
 */
/*来自WIFI模块的应答*/
uint8_t wifi8266_join_ap(char *ssid, char *pwd)
{
    uint8_t ret;
    char cmd[64];
    
    sprintf(cmd, "AT+CWJAP_DEF=\"%s\",\"%s\"", ssid, pwd);//组合AT指令字符串
    ret = wifi8266_send_at_cmd(cmd, "WIFI GOT IP", 10000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266设置WIFI热点的账户密码等信息
 * @param       ssid:             WIFI名称
 *              pwd :             WIFI密码
 *              channel:          无线信道
 *              encryptionMethod:加密方法
 *              maxConnection:   最多连接数,不超过4
 *              broadCast:       广播名称,0或1
 * @retval      WIFI8266_EOK  : WIFI连接成功
 *              WIFI8266_ERROR: WIFI连接失败
 *              wifi8266_set_ap("REGAIN_001","1234567899",10,4,1,0);
 */
/*来自WIFI模块的应答*/
//AT+CWSAP="REGAIN_001","1234567899",10,4,1,0

//OK
uint8_t wifi8266_set_ap(char *ssid, char *pwd,uint8_t channel, 
                          uint8_t encryptionMethod,uint8_t  maxConnection,
                        uint8_t broadCast)//WIFI8266设置WIFI热点的账户密码等信息
{
    uint8_t ret;
    char cmd[128];
    
    sprintf(cmd, "AT+CWSAP_DEF=\"%s\",\"%s\",%d,%d,%d,%d", 
              ssid, pwd,channel,encryptionMethod,maxConnection,broadCast);//组合AT指令字符串
    ret = wifi8266_send_at_cmd(cmd, "OK", 8000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266获取IP地址
 * @param       buf: IP地址,需要16字节内存空间
 * @retval      WIFI8266_EOK  : 获取IP地址成功
 *              WIFI8266_ERROR: 获取IP地址失败
 */
/*来自WIFI模块的应答*/
//AT+CIFSR
//+CIFSR:APIP,"192.168.4.1"
//+CIFSR:APMAC,"32:83:98:82:4f:34"

//OK
uint8_t wifi8266_get_ip(char *buf)
{
    uint8_t ret;
    char *p_start;
    char *p_end;
    
    ret = wifi8266_send_at_cmd("AT+CIFSR", "OK", 500);
    if (ret != WIFI8266_EOK)
    {
        return WIFI8266_ERROR;
    }
    
    p_start = strstr((const char *)wifi8266_uart_rx_get_frame(), "\"");
    p_end = strstr(p_start + 1, "\"");
    *p_end = '\0';
    sprintf(buf, "%s", p_start + 1);
    
    return WIFI8266_EOK;
}

/**
 * @brief       WIFI8266作为客户端,连接TCP服务器
 * @param       server_ip  : TCP服务器IP地址
 *              server_port: TCP服务器端口号
 * @retval      WIFI8266_EOK  : 连接TCP服务器成功
 *              WIFI8266_ERROR: 连接TCP服务器失败
 */
uint8_t wifi8266_connect_tcp_server(char *server_ip, char *server_port)
{
    uint8_t ret;
    char cmd[64];
    
    sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s", server_ip, server_port);
    ret = wifi8266_send_at_cmd(cmd, "CONNECT", 5000);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       作为客户端,WIFI8266进入透传
 * @param       无
 * @retval      WIFI8266_EOK  : 进入透传成功
 *              WIFI8266_ERROR: 进入透传失败
 *@attention    CIPMUX and CIPSERVER must be 0
 */
uint8_t wifi8266_enter_unvarnished(void)
{
    uint8_t ret;
    
    ret  = wifi8266_send_at_cmd("AT+CIPMODE=1", "OK", 500);
    ret += wifi8266_send_at_cmd("AT+CIPSEND", ">", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}

/**
 * @brief       WIFI8266退出透传
 * @param       无
 * @retval      无
 */
void wifi8266_exit_unvarnished(void)
{
    wifi8266_uart_printf("+++");
}
/**
 * @brief       AT+CIPMUX  多连接
 * @param       setMux 0,1
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPMUX=1

//OK
uint8_t wifi8266_cipmux_set(uint8_t setMux)/*AT+CIPMUX  多连接*/
{
    uint8_t ret;
    char cmd[64];    
    sprintf(cmd, "AT+CIPMUX=%d", setMux);
    
    ret = wifi8266_send_at_cmd(cmd, "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       AT+CIPAP  AP模式的IP设置
 * @param       setMux 0,1
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPAP="192.168.5.1"

//OK
uint8_t wifi8266_cipap_set(char *ip)/*AT+CIPAP  AP模式的IP设置*/
{
    uint8_t ret;
    char cmd[64];    
    sprintf(cmd, "AT+CIPAP=\"%s\"", ip);
    
    ret = wifi8266_send_at_cmd(cmd, "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       AT+CIPMUX=1 ,AT+CIPSERVER指令,将 WIFI模块设置为TCP服务器
 * @param       Enable 0,1 ;port 端口号字符串
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPSERVER=1,8080

//OK
uint8_t wifi8266_servicer_set(uint8_t Enable,char *port)/*AT+CIPMUX=1 ,AT+CIPSERVER指令,将WIFI模块设置为TCP服务器*/
{
    uint8_t ret;
    char cmd[64];   
    ret =0;    
      ret  = wifi8266_send_at_cmd("AT+CIPMUX=1","OK",500);//多接入模式
    sprintf(cmd, "AT+CIPSERVER=%d,%s", Enable,port);//服务器开启及端口设置    
    ret += wifi8266_send_at_cmd(cmd, "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       AT+CIPSERVER=0  关闭TCP服务器,注意需要重启WIFI模块
 * @param       无
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的应答*/
//AT+CIPSERVER=0

//OK
uint8_t wifi8266_servicer_reset(void)/*AT+CIPSERVER=0  关闭 WIFI模块TCP服务器,注意需要重启WIFI模块*/
{
    uint8_t ret;
    ret = wifi8266_send_at_cmd("AT+CIPSERVER=0", "OK", 500);
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
/**
 * @brief       WIFI8266作为TCP服务器,启动向客户端发送帧
 *               AT指令 AT+CIPSEND
 * @param       linkNo :收到客户端网络连接的ID号
 *              len;   :数据长度 
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 * @global      g_uart_tcp_frame  
 *              数据帧的 客户端链接号linkNo,长度len,及数据帧data
 */
uint8_t wifi8266_tcp_server_sendcmd( uint16_t linkNo,uint16_t len)
{
    //uint8_t *ret = NULL;
      uint8_t ubRet;
    char cmd[128];
    
    sprintf(cmd, "AT+CIPSEND=%d,%d", linkNo, len);//组合AT指令字符串
    ubRet = wifi8266_send_at_cmd(cmd, ">", 8000);    
      return ubRet;
}
/**
* @brief       向远端发送数据
 * @param       无
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 * @global      g_uart_tcp_frame  
 *              数据帧的 客户端链接号linkNo,长度len,及数据帧data
 */
//>              // 提示符>出现后,串口发送字节

//busy s...

//Recv 4 bytes

//SEND OK
uint8_t wifi8266_servicer_sendData(void)/*向客户端发送数据帧*/
{
    uint8_t ret;
      wifi8266_uart_sendData((uint8_t*)g_uart_tcp_frame.data,g_uart_tcp_frame.len);//发送8位数据组    
      ret = wifi8266_get_cmd_echo( "SEND OK", 1000);//检测是否发送成功
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }        
}

/**
 * @brief       WIFI8266作为TCP服务器,接收+IPD
 * @param       timeout: 等待超时时间
 * @retval      WIFI8266_EOK     : 函数执行成功
 *              WIFI8266_ETIMEOUT: 等待期望应答超时,函数执行失败
 */
/*来自WIFI模块的通信帧*/
//+IPD,0,3:ACK  //接客户端0发来的3BYTES帧,是 ACK
uint8_t wifi8266_tcp_server_receive(uint32_t timeout) /* WIFI8266连接外部TCP服务器 +IPD*/
{
        uint8_t *ret = NULL;
        uint8_t ubRet;

        wifi8266_uart_rx_restart();
        #ifdef WIFI_USING_RT_THREAD_OS        
        receive:
        //ubRet = wifi8266_wait_event(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况
        ubRet = wifi8266_take_sem(timeout);//已接收帧,但串口没有接收到新的字节。等待这种情况

        if( ubRet ==0){//从通信帧中查找ack
            ret = wifi8266_uart_rx_get_frame();
            if (ret != NULL)
            {
                    if (strstr((const char *)ret, "+IPD") != NULL)//在ret字符串中查找+IPD字符串
                    {                                
                            return WIFI8266_EOK;//从通信帧中找到+IPD
                    }
                    else
                    {            
                            wifi8266_uart_rx_restart();//清零RX缓冲
                            goto receive;//从通信帧中没有找到ack,再次接收新的帧
                    }
            }    
            else{// ret没有字符串
                wifi8266_uart_rx_restart();//清零RX缓冲
                return  WIFI8266_ERROR;
            }                    
        }
        if(ubRet ==1){
            /*超时,没有接收数据*/
            return WIFI8266_ETIMEOUT;        
        }
        if(ubRet ==2){
            /*错误,没有接收数据*/
            return WIFI8266_ERROR;            
        }
        #endif
        return WIFI8266_ERROR;    

}
/**
 * @brief       取出接收远端发来的数据
 * @param       无
 * @retval      WIFI8266_EOK  : 成功
 *              WIFI8266_ERROR: 失败
 */
/*来自WIFI模块的通信帧*/
//+IPD,0,3:ACK  //接客户端0发来的3BYTES帧,是 ACK
uint8_t frame_buf[WIFI8266_UART_RX_BUF_SIZE];/* 帧接收缓冲 */
uint8_t wifi8266_servicer_get_data(void)/*解析通信帧,得到客户端发来的信息*/
{
    uint8_t ret;
      char *p_ipd;
    char *p_start;
    char *p_end;
      char *p_data;
      char buf[3];
      ret = WIFI8266_EOK;
      /*查找"+IPD"*/
      p_ipd = strstr((const char *)wifi8266_uart_rx_get_frame(), "+IPD");
      sprintf((char*)frame_buf, "%s", p_ipd);//+IPD及以后的字符数据
    
      /*取帧内len部分*/      
      p_start = strstr((const char *)frame_buf, ",");    //+IPD以后的第1个","
    p_start = strstr(p_start + 1, ",");                //+IPD以后的第2个","
      p_end   = strstr(p_start + 1, ":");                  //+IPD以后的第1个":"
      *p_end = '\0';//字符串结束符号
      if(p_end-p_start>sizeof(buf)+1) return WIFI8266_ERROR;
        sprintf(buf, "%s", p_start + 1);    
      g_uart_tcp_frame.len = atoi(buf);//字符串转整数
      /*取帧内link No部分*/    
      p_start = strstr((const char *)frame_buf, ",");//+IPD以后的第1个","        
      p_end   = strstr(p_start + 1, ",");//+IPD以后的第2个","
      *p_end = '\0';//字符串结束符号    
      if(p_end-p_start>sizeof(buf)+1) return WIFI8266_ERROR;
        sprintf(buf, "%s", p_start + 1);
    g_uart_tcp_frame.linkNo= atoi(buf);//字符串转整数    
    
      /*取帧内data部分*/ 
      p_ipd = strstr((const char *)wifi8266_uart_rx_get_frame(), "+IPD");//查找"+IPD"    
      p_start = strstr(p_ipd+1, ":");//查找开头
        
        if(g_uart_tcp_frame.len <1)/*长度检测*/
            return WIFI8266_ERROR;
        p_end   = p_start+1 + g_uart_tcp_frame.len-1;//计算结尾
        p_data = (char*)g_uart_tcp_frame.data;
        do{/*数据=>g_uart_tcp_frame*/
            p_start++;
            *p_data =*p_start;//数据
            p_data++;            
        }while(p_start<p_end);

    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
View Code

wifi模块APP接口程序

 modbus_wifi_app.h

#ifndef  MODBUS_WIFI_APP_H
#define  MODBUS_WIFI_APP_H
#include "stm32g0xx_hal.h"
#include <modbus_protocol.h>

/*内部函数*/
uint8_t wifi_tcp_server_check(void);/*检查WIFI模块配置是否正确*/
/*接口函数*/
uint8_t modbus_wifi_setup(void);/*初始化WIFI模块及内核变量*/
uint8_t modbus_wifi_send(modbus_frame_def* p_frame,uint32_t timeout);   /*阻塞型,通过串口发送数据*/
uint8_t modbus_wifi_receive(modbus_frame_def* p_frame,uint32_t timeout);/*阻塞型,通过串口接收数据*/
#endif
View Code

 modbus_wifi_app.c

#include <modbus_wifi_app.h>
#include <rtthread.h>
#include <wifi8266_uart_bsp.h>// 与WIFI模块链接的串口程序
#include <wifi_8266_driver.h> // AT 指令操作WIFI模块
#include <string.h>
#include <stdio.h>
#include <stdlib.h> //字符串和数值间转换

/**
 * @brief       初始化WIFI作为TCP SERVER,初始化串口、信号量、互斥量
 * @param       无
 * @retval      0正常,其他错误
 * @attention   WIFI模块复位引脚,设置GPIO引脚控制复位
 */
uint8_t modbus_wifi_setup(void)/*初始化MODBUS_RTU内核变量,启动MODBUS_RTU内核线程*/
{
    wifi8266_uart_init(115200);//初始化串口、信号量、互斥量
    
     wifi8266_lock();   //获取互斥量,得到WIFI使用权
     while(1){
        if(WIFI8266_EOK !=         wifi8266_at_test()){
        /* WIFI8266 AT指令测试 */        
        rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码          
        rt_thread_delay(100);
        continue;
        }
        /*组网参数设置*/
        if(WIFI8266_EOK !=wifi_tcp_server_check())/*检查WIFI模块配置是否正确*/
        {
            if(WIFI8266_EOK != wifi8266_cipap_set(WIFI_AP_TCP_SERVER_IP)){ 
            /*WIFI模块的IP设置*/    
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            rt_thread_delay(100);
            continue;
            }        
            if(WIFI8266_EOK != wifi8266_set_mode(2)){
            /* 设置WIFI8266工作模式 sta+ap */        
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            rt_thread_delay(100);
            continue;
            }    
            if(WIFI8266_EOK != wifi8266_set_ap(WIFI_SSID,WIFI_PWD,10,4,1,0)){ 
            /* WIFI8266软件复位,新设置的工作模式生效*/            
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码          
            rt_thread_delay(100);
            continue;
            }    
            if(WIFI8266_EOK != wifi8266_sw_reset()){ 
            /* WIFI8266软件复位,新设置的工作模式生效*/            
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            rt_thread_delay(100);
            continue;
            }
        }
        else
        {
            if(WIFI8266_EOK != wifi8266_sw_reset()){ 
            /* WIFI8266软件复位,新设置的工作模式生效*/            
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
            rt_thread_delay(100);
            continue;
            }        
        }
            
        /*网络链接参数设置*/                        
        if(WIFI8266_EOK != wifi8266_servicer_set(1,WIFI_AP_TCP_SERVER_PORT)){ 
        /*AT+CIPMUX=1 ,AT+CIPSERVER指令,将WIFI模块设置为TCP服务器*/
        rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
        rt_thread_delay(100);
        continue;
        } 
        break;    
    }
  wifi8266_unlock(); //释放互斥量,失去WIFI使用权            
    return 0;
}
/**
* @brief       作为TCP SERVER,通过wifi发送数据帧
 * @param       p_frame 帧地址
 *              timeout 阻塞最长时长 单位 ticks
 * @retval      0正常,其他错误
 * @attention  
 */
uint8_t modbus_wifi_send(modbus_frame_def* p_frame,uint32_t timeout)/*阻塞型,通过WIFI发送数据*/
{
    wifi8266_lock();   //获取互斥量,得到WIFI使用权
    g_uart_tcp_frame.len = p_frame->n_bytes;//发送数据的字节
    /*填写modbus帧*/
    memcpy((char*)g_uart_tcp_frame.data,p_frame->buf, g_uart_tcp_frame.len);    
    /*向客户端发送命令*/         
    if(WIFI8266_EOK != wifi8266_tcp_server_sendcmd( g_uart_tcp_frame.linkNo,g_uart_tcp_frame.len)){
        rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);    
        wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
        return 1;
    }; 
    /*向客户端发送数据帧*/   
    if(WIFI8266_EOK != wifi8266_servicer_sendData()){
        rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);
        wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
        return 2;
    };     
    wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
 return 0;    
}
/**
 * @brief       作为TCP SERVER,通过wifi接收数据,放入p_frame
 * @param       p_frame 帧地址
 *              timeout 阻塞最长时长 单位 ticks
 * @retval      0正常,其他错误
 * @attention   
 */
uint8_t modbus_wifi_receive(modbus_frame_def* p_frame,uint32_t timeout)/*阻塞型,通过WIFI接收数据*/
{        
    wifi8266_lock();   //获取互斥量,得到WIFI使用权
    if(WIFI8266_EOK !=wifi8266_tcp_server_receive(timeout)){     
    /* WIFI8266作为服务器,接收+IPD开始的通信帧*/
        wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
        return 1;
    }
    /*解析通信帧,得到客户端发来的信息*/   
    if(WIFI8266_EOK !=wifi8266_servicer_get_data()){
        rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);
        wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
        return 2;
    }; 
    if( 0==        g_uart_tcp_frame.len) {             
        wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
        return 3;        
    }
    /*提取modbus帧*/
    memcpy(p_frame->buf, (char*)g_uart_tcp_frame.data,g_uart_tcp_frame.len);
    p_frame->n_bytes = g_uart_tcp_frame.len;
    wifi8266_unlock(); //释放互斥量,失去WIFI使用权    
    return 0;        
}

/*内部函数*/
/**
 * @brief       检查WIFI模块配置是否正确
 * @param       无
 * @retval      0正常,其他错误
 * @attention   
 */
uint8_t wifi_tcp_server_check(void)/*检查WIFI模块配置是否正确*/
{
    uint8_t ret; 
          
      ret  = wifi8266_send_at_cmd("AT+CWMODE?","2",500);                 //AP模式?
      ret += wifi8266_send_at_cmd("AT+CWSAP?",WIFI_SSID,500);            //热点名称?
    ret += wifi8266_send_at_cmd("AT+CIFSR",WIFI_AP_TCP_SERVER_IP,500); //IP地址?
    if (ret == WIFI8266_EOK)
    {
        return WIFI8266_EOK;
    }
    else
    {
        return WIFI8266_ERROR;
    }
}
View Code

串口3 作为USART,通信,modbus协议中的主机   cubeMx配置 DMA+uart

modbus_uart3_app.h

#ifndef  MODBUS_USART3_APP_H
#define  MODBUS_USART3_APP_H
#include "stm32g0xx_hal.h"
#include <modbus_protocol.h>
#include "usart.h"

#define MODBUS_USART3_HANDLE  huart3
/*内部函数*/
//uint8_t take_modbus_usart4(uint32_t timeout); /*使用互斥量,占用串口*/
//uint8_t release_modbus_usart4(void); /*使用互斥量,释放串口*/
void Usart3_Receive_IDLE(UART_HandleTypeDef *huart);// 串口空闲中断处理函数,此函数应添加到中断处理函数void USART4_IRQHandler(void)中
void HAL_UART3_TxCpltCallback(UART_HandleTypeDef *huart);//在重载中断回调函数中调用
void HAL_UART3_RxCpltCallback(UART_HandleTypeDef *huart);//在重载中断回调函数中调用
/*接口函数*/
uint8_t modbus_usart3_setup(void);                     /*初始化MODBUS_RTU内核变量,启动MODBUS_RTU内核线程*/
uint8_t modbus_usart3_send(modbus_frame_def* p_frame,uint32_t timeout);/*阻塞型,通过串口发送数据*/
uint8_t modbus_usart3_receive(modbus_frame_def* p_frame,uint32_t timeout);/*阻塞型,通过串口接收数据*/
#endif
View Code

modbus_uart3_app.c

#include <modbus_uart3_app.h>
#include <rtthread.h> /*TR-Thread库*/
/*全局变量*/
static modbus_frame_def* p_usart3_frame;/*指针,中断函数与接口函数传递参数*/

static rt_mutex_t modbus_usart3_mux = RT_NULL;    /* 定义互斥量控制块 */
/* 定义信号量*/
static rt_sem_t UART3_RX_END_SEM = RT_NULL;// RX中断接收结束信号
static rt_sem_t UART3_TX_END_SEM = RT_NULL;// RX中断接收结束信号
/*内部函数*/
/*
 * @brief       线程调用的应用函数与串口通信RX中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
uint8_t uart3_take_RX_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //WIFI8266_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(UART3_RX_END_SEM, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d,UART4_RX_END_SEMtime out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,UART4_RX_END_SEM:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t uart3_release_RX_sem(void)
{
    rt_sem_release(UART3_RX_END_SEM);//放出信号量
    return 0;
}
/*
 * @brief       线程调用的应用函数与串口通信TX中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
uint8_t uart3_take_TX_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //WIFI8266_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(UART3_TX_END_SEM, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d, UART4_TX_END_SEM:time out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,UART4_TX_END_SEM:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t uart3_release_TX_sem(void)
{
    rt_sem_release(UART3_TX_END_SEM);//放出信号量
    return 0;
}
/*
***********************************************
* 函 数 名: take_modbus_usart
* 功能说明: 使用内核互斥量,取得串口使用权 
* 形    参:timeout 超时,单位ticks
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t take_modbus_usart3(uint32_t timeout)   /*使用互斥量,占用串口*/
{        rt_err_t uwRet = RT_EOK;
    uwRet=rt_mutex_take(modbus_usart3_mux, /* 获取互斥量 */
                              timeout); /* 等待时间:等timeout个ticks */ 
      if(uwRet != RT_EOK ) return 1;
return 0;
}
/*
***********************************************
* 函 数 名: release_modbus_usart
* 功能说明: 使用内核互斥量,放弃串口使用权 
* 形    参:无
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t release_modbus_usart3(void)/*使用互斥量,释放串口*/
{    
    rt_mutex_release( modbus_usart3_mux ); //释放互斥量
  return 0;
}

/*DMA USART 中断处理函数*/
/*
***********************************************
* 函 数 名:  Usart_Receive_IDLE()
* 功能说明:  空闲中断服务子程序,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况
* 形    参: UART_HandleTypeDef *huart
* 返 回 值:  无
* 全局变量:
* 特殊说明:此子程序应添加到中断处理函数void USART?_IRQHandler(void)中。
            使能此中断 __HAL_UART_ENABLE_IT(&MODBUS_USART3_HANDLE, UART_IT_IDLE);
************************************************
*/
void Usart3_Receive_IDLE(UART_HandleTypeDef *huart)  // 串口空闲中断处理函数,此函数应添加到中断处理函数void USART4_IRQHandler(void)中
{  
  uint32_t tmpFlg = 0;
  uint32_t temp;
    
    if (huart != &MODBUS_USART3_HANDLE) return;
    
        if (__HAL_UART_GET_FLAG(&MODBUS_USART3_HANDLE, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
    {
        __HAL_UART_CLEAR_OREFLAG(&MODBUS_USART3_HANDLE);                           /* 清除接收过载错误中断标志 */
        (void)MODBUS_USART3_HANDLE.Instance->ISR;//SR;                                   /* 先读SR寄存器,再读DR寄存器 */
        (void)MODBUS_USART3_HANDLE.Instance->RDR;//DR;
    }      
    if (huart != &MODBUS_USART3_HANDLE) return;
    
  tmpFlg =__HAL_UART_GET_FLAG(&MODBUS_USART3_HANDLE,UART_FLAG_IDLE); //获取IDLE标志位
 
    
  if((tmpFlg != RESET))//idle标志被置位
  {         
    __HAL_UART_CLEAR_IDLEFLAG(&MODBUS_USART3_HANDLE);//清除标志位
//        temp = MODBUS_USART3_HANDLE.Instance->ISR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
//        temp = MODBUS_USART3_HANDLE.Instance->RDR; //读取数据寄存器中的数据                
        //这两句和上面那句等效
    HAL_UART_DMAStop(&MODBUS_USART3_HANDLE); // 停止DMA
    //temp  =  __HAL_DMA_GET_COUNTER(&MODBUS_DMA_USART_RX_HANDLE) ;// 获取DMA中未传输的数据个数,函数参数在 usart.h中定义  
        temp  = MODBUS_USART3_HANDLE.hdmarx->Instance->CNDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数,
        //这句和上面那句等效        
    /*修改mail_msg_rx.n_bytes为接收字节数*/
        p_usart3_frame->n_bytes=  sizeof(p_usart3_frame->buf) - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数  
                //__HAL_UART_DISABLE_IT(&MODBUS_USART3_HANDLE, UART_IT_IDLE);// 开空闲中断
    HAL_UART_AbortReceive_IT(&MODBUS_USART3_HANDLE);//停止并清除中断允许标志        
        rt_kprintf("RX3 bytes %d! \r\n",p_usart3_frame->n_bytes);
      /* 发送一个信号 UART4_RX_END */    
    uart3_release_RX_sem();        
        rt_schedule();// 调度,使得 高优先级的从机线程执行!
    }
}
/*
***********************************************
* 函 数 名:  HAL_UART3_RxCpltCallback
* 功能说明:  重载中断函数回调函数,处理接收数据事件,
             并发送rth内核邮件modbus_rx_mb给线程
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
void HAL_UART3_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart == &MODBUS_USART3_HANDLE)
  {
        /*修改mail_msg_rx.n_bytes为接收字节数*/
        p_usart3_frame->n_bytes =sizeof(p_usart3_frame->buf);// 接受完成,置接收字节数    
        HAL_UART_DMAStop(&MODBUS_USART3_HANDLE); // 停止DMA    
    HAL_UART_AbortReceive_IT(&MODBUS_USART3_HANDLE);//停止并清除中断允许标志        
    /* 发送一个信号 UART3_RX_END */    
    uart3_release_RX_sem();    
        rt_schedule();// 调度,使得 高优先级的从机线程执行!
  }
}
/*
***********************************************
* 函 数 名:  HAL_UART3_TxCpltCallback
* 功能说明:  重载中断函数回调函数,处理发送数据事件,
             并发送rth内核邮件modbus_tx_mb给线程
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
void HAL_UART3_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart == &MODBUS_USART3_HANDLE)
  {
    HAL_UART_DMAStop(&MODBUS_USART3_HANDLE); // 停止DMA
  HAL_UART_AbortReceive_IT(&MODBUS_USART3_HANDLE);    //停止并清除中断允许标志         
    /* 发送一个信号 UART3_TX_END */    
  uart3_release_TX_sem();        
    rt_schedule();// 调度,使得 高优先级的从机线程执行!    
    }  
}

/*接口函数*/
/*
***********************************************
* 函 数 名:  modbus_usart_setup
* 功能说明:  初始化内核变量
* 形    参:无
* 返 回 值: 0,正常; 非0,错误
* 全局变量:modbus_usart_mux,modbus_uart_it_event
************************************************
*/
uint8_t modbus_usart3_setup(void) /*初始化内核变量*/
{
    /*创建一个互斥信号量*/
    modbus_usart3_mux = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO);
    if (modbus_usart3_mux == RT_NULL)
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码            
    /* 创建一个信号*/
        UART3_RX_END_SEM  = rt_sem_create("RX3_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (UART3_RX_END_SEM == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
    /* 创建一个信号*/        
        UART3_TX_END_SEM  = rt_sem_create("TX3_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (UART3_TX_END_SEM == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
return 0;
}
/*
***********************************************
* 函 数 名:  modbus_usart_send
* 功能说明:  阻塞型,通过串口发送数据
* 形    参: 
*           timeout 超时,单位ticks
*           p_frame 指针,指向发送数据
* 返 回 值: 0,正常; 非0,错误
* 全局变量:
************************************************
*/
uint8_t modbus_usart3_send   (modbus_frame_def* p_frame,uint32_t timeout)/*阻塞型,通过串口发送数据*/
{
    uint8_t ret;
    /* 发送通信包 */        
    take_modbus_usart3(timeout);   /*使用互斥量,占用串口*/
    /*独占DMA-USART外设,启动DMA-USART发送通信包*/        
    HAL_UART_AbortTransmit_IT(&MODBUS_USART3_HANDLE);//停止并清除中断允许标志        
    if (HAL_UART_Transmit_DMA(&MODBUS_USART3_HANDLE, (uint8_t*)p_frame->buf, p_frame->n_bytes) != HAL_OK)
    {/*发送帧*/
    rt_kprintf("%s %s:%d HAL_UART_Transmit_DMA() error!\r\n", __FILE__, __FUNCTION__, __LINE__);
    return 1;
    }    
    ret = uart3_take_TX_sem(timeout);/*使用信号,串口发送结束*/
    release_modbus_usart3();/*使用互斥量,释放串口*/
    return ret;
}
/*
***********************************************
* 函 数 名:  modbus_usart_receive
* 功能说明:  阻塞型,通过串口接收数据
* 形    参: 
*           timeout 超时,单位ticks
*           p_frame 指针,指向接收数据
* 返 回 值: 0,正常; 非0,错误
* 全局变量:
************************************************
*/
uint8_t modbus_usart3_receive(modbus_frame_def* p_frame,uint32_t timeout)/*阻塞型,通过串口接收数据*/
{
    uint8_t ret;
    static uint16_t len;
    /* 发送通信包 */        
    take_modbus_usart3(timeout);   /*使用互斥量,占用串口*/
    
    p_usart3_frame =p_frame;//传指针,接收串口中断使用
    len =sizeof(p_frame->buf);
    HAL_UART_AbortReceive_IT(&MODBUS_USART3_HANDLE);//停止并清除中断允许标志        
    if (HAL_UART_Receive_DMA(&MODBUS_USART3_HANDLE, (uint8_t*)p_frame->buf, sizeof(p_frame->buf)) != HAL_OK)
    {/*发送帧*/
    rt_kprintf("%s %s:%d HAL_UART_Receive_DMA() error!\r\n", __FILE__, __FUNCTION__, __LINE__);
    return 1;
    }        
    __HAL_UART_ENABLE_IT(&MODBUS_USART3_HANDLE, UART_IT_IDLE);// 开空闲中断,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况。
    
    ret = uart3_take_RX_sem(timeout);/*使用信号,串口发送结束*/
    release_modbus_usart3();/*使用互斥量,释放串口*/
    return ret;
}
View Code

串口4作为USART,通信,,modbus协议中的从机   cubeMx配置 DMA+uart

modbus_uart4_app.h

#ifndef  MODBUS_USART4_APP_H
#define  MODBUS_USART4_APP_H
#include "stm32g0xx_hal.h"
#include <modbus_protocol.h>
#include "usart.h"

#define MODBUS_USART4_HANDLE  huart4
/*内部函数*/
//uint8_t take_modbus_usart4(uint32_t timeout); /*使用互斥量,占用串口*/
//uint8_t release_modbus_usart4(void); /*使用互斥量,释放串口*/
void Usart4_Receive_IDLE(UART_HandleTypeDef *huart);// 串口空闲中断处理函数,此函数应添加到中断处理函数void USART4_IRQHandler(void)中
void HAL_UART4_TxCpltCallback(UART_HandleTypeDef *huart);//在重载中断回调函数中调用
void HAL_UART4_RxCpltCallback(UART_HandleTypeDef *huart);//在重载中断回调函数中调用
/*接口函数*/
uint8_t modbus_usart4_setup(void);                     /*初始化MODBUS_RTU内核变量,启动MODBUS_RTU内核线程*/
uint8_t modbus_usart4_send(modbus_frame_def* p_frame,uint32_t timeout);/*阻塞型,通过串口发送数据*/
uint8_t modbus_usart4_receive(modbus_frame_def* p_frame,uint32_t timeout);/*阻塞型,通过串口接收数据*/
#endif
View Code

modbus_uart4_app.c

#include <modbus_uart4_app.h>
#include <rtthread.h> /*TR-Thread库*/
/*全局变量*/
static modbus_frame_def* p_usart4_frame;/*指针,中断函数与接口函数传递参数*/

static rt_mutex_t modbus_usart4_mux = RT_NULL;    /* 定义互斥量控制块 */
/* 定义信号量*/
static rt_sem_t UART4_RX_END_SEM = RT_NULL;// RX中断接收结束信号
static rt_sem_t UART4_TX_END_SEM = RT_NULL;// RX中断接收结束信号
/*内部函数*/
/*
 * @brief       线程调用的应用函数与串口通信RX中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
uint8_t uart4_take_RX_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //WIFI8266_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(UART4_RX_END_SEM, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d,UART4_RX_END_SEMtime out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,UART4_RX_END_SEM:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t uart4_release_RX_sem(void)
{
    rt_sem_release(UART4_RX_END_SEM);//放出信号量
    return 0;
}
/*
 * @brief       线程调用的应用函数与串口通信TX中断同步信号
 * @param       timeout: 延时多少ticks
 * @retval      无
 */
uint8_t uart4_take_TX_sem(rt_int32_t timeout)
{
  rt_err_t uwRet = RT_EOK;
    //WIFI8266_RX_IT_sem->value =0;//信号值清零
    uwRet = rt_sem_take(UART4_TX_END_SEM, timeout);//等待信号量
    if(-RT_ETIMEOUT == uwRet){
            rt_kprintf("error@ %s %s:%d, UART4_TX_END_SEM:time out!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 1;//超时
    }         
    if(-RT_ERROR  == uwRet){
            rt_kprintf("error@ %s %s:%d,UART4_TX_END_SEM:ERROR!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
      return 2;//错误
    }              
    /* 如果信号接收完成并且正确 */
      return 0;
}
uint8_t uart4_release_TX_sem(void)
{
    rt_sem_release(UART4_TX_END_SEM);//放出信号量
    return 0;
}
/*
***********************************************
* 函 数 名: take_modbus_usart
* 功能说明: 使用内核互斥量,取得串口使用权 
* 形    参:timeout 超时,单位ticks
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t take_modbus_usart4(uint32_t timeout)   /*使用互斥量,占用串口*/
{        rt_err_t uwRet = RT_EOK;
    uwRet=rt_mutex_take(modbus_usart4_mux, /* 获取互斥量 */
                              timeout); /* 等待时间:等timeout个ticks */ 
      if(uwRet != RT_EOK ) return 1;
return 0;
}
/*
***********************************************
* 函 数 名: release_modbus_usart
* 功能说明: 使用内核互斥量,放弃串口使用权 
* 形    参:无
* 返 回 值: 0,正常; 非0,错误; 
* 全局变量:modbus_msg_rx  已经接收的通信包,modbus_regs[MODBUS_REGS_NUM] MODBUS寄存器
* 特殊说明:
************************************************
*/
uint8_t release_modbus_usart4(void)/*使用互斥量,释放串口*/
{    
    rt_mutex_release( modbus_usart4_mux ); //释放互斥量
  return 0;
}

/*DMA USART 中断处理函数*/
/*
***********************************************
* 函 数 名:  Usart_Receive_IDLE()
* 功能说明:  空闲中断服务子程序,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况
* 形    参: UART_HandleTypeDef *huart
* 返 回 值:  无
* 全局变量:
* 特殊说明:此子程序应添加到中断处理函数void USART?_IRQHandler(void)中。
            使能此中断 __HAL_UART_ENABLE_IT(&MODBUS_USART4_HANDLE, UART_IT_IDLE);
************************************************
*/
void Usart4_Receive_IDLE(UART_HandleTypeDef *huart)  // 串口空闲中断处理函数,此函数应添加到中断处理函数void USART4_IRQHandler(void)中
{  
  uint32_t tmpFlg = 0;
  uint32_t temp;
        
    if (huart != &MODBUS_USART4_HANDLE) return;
    
        if (__HAL_UART_GET_FLAG(&MODBUS_USART4_HANDLE, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
    {
        __HAL_UART_CLEAR_OREFLAG(&MODBUS_USART4_HANDLE);                           /* 清除接收过载错误中断标志 */
        (void)MODBUS_USART4_HANDLE.Instance->ISR;//SR;                                   /* 先读SR寄存器,再读DR寄存器 */
        (void)MODBUS_USART4_HANDLE.Instance->RDR;//DR;
    }
    
  tmpFlg =__HAL_UART_GET_FLAG(&MODBUS_USART4_HANDLE,UART_FLAG_IDLE); //获取IDLE标志位
 
  if((tmpFlg != RESET))//idle标志被置位
  {         
    __HAL_UART_CLEAR_IDLEFLAG(&MODBUS_USART4_HANDLE);//清除标志位
//        temp = MODBUS_USART4_HANDLE.Instance->ISR;  //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
//        temp = MODBUS_USART4_HANDLE.Instance->RDR; //读取数据寄存器中的数据                
        //这两句和上面那句等效
    HAL_UART_DMAStop(&MODBUS_USART4_HANDLE); // 停止DMA
    //temp  =  __HAL_DMA_GET_COUNTER(&MODBUS_DMA_USART_RX_HANDLE) ;// 获取DMA中未传输的数据个数,函数参数在 usart.h中定义  
        temp  = MODBUS_USART4_HANDLE.hdmarx->Instance->CNDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数,
        //这句和上面那句等效        
    /*修改mail_msg_rx.n_bytes为接收字节数*/
        p_usart4_frame->n_bytes=  sizeof(p_usart4_frame->buf) - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数  
                //__HAL_UART_DISABLE_IT(&MODBUS_USART4_HANDLE, UART_IT_IDLE);// 开空闲中断
    HAL_UART_AbortReceive_IT(&MODBUS_USART4_HANDLE);//停止并清除中断允许标志        
        rt_kprintf("RX4 bytes %d! \r\n",p_usart4_frame->n_bytes);
      /* 发送一个信号 UART4_RX_END */    
    uart4_release_RX_sem();        
        rt_schedule();// 调度,使得 高优先级的从机线程执行!
    }
}
/*
***********************************************
* 函 数 名:  HAL_UART4_RxCpltCallback
* 功能说明:  重载中断函数回调函数,处理接收数据事件,
             并发送rth内核邮件modbus_rx_mb给线程
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
void HAL_UART4_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart == &MODBUS_USART4_HANDLE)
  {
        /*修改mail_msg_rx.n_bytes为接收字节数*/
        p_usart4_frame->n_bytes =sizeof(p_usart4_frame->buf);// 接受完成,置接收字节数    
        HAL_UART_DMAStop(&MODBUS_USART4_HANDLE); // 停止DMA    
    HAL_UART_AbortReceive_IT(&MODBUS_USART4_HANDLE);//停止并清除中断允许标志        
    /* 发送一个信号 UART4_RX_END */    
    uart4_release_RX_sem();    
        rt_schedule();// 调度,使得 高优先级的从机线程执行!
  }
}
/*
***********************************************
* 函 数 名:  HAL_UART_TxCpltCallback
* 功能说明:  重载中断函数回调函数,处理发送数据事件,
             并发送rth内核邮件modbus_tx_mb给线程
* 形    参:无
* 返 回 值: 无
* 全局变量:
************************************************
*/
void HAL_UART4_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart == &MODBUS_USART4_HANDLE)
  {
    HAL_UART_DMAStop(&MODBUS_USART4_HANDLE); // 停止DMA
  HAL_UART_AbortReceive_IT(&MODBUS_USART4_HANDLE);    //停止并清除中断允许标志         
    /* 发送一个信号 UART4_TX_END */    
  uart4_release_TX_sem();        
    rt_schedule();// 调度,使得 高优先级的从机线程执行!    
    }  
}

/*接口函数*/
/*
***********************************************
* 函 数 名:  modbus_usart_setup
* 功能说明:  初始化内核变量
* 形    参:无
* 返 回 值: 0,正常; 非0,错误
* 全局变量:modbus_usart_mux,modbus_uart_it_event
************************************************
*/
uint8_t modbus_usart4_setup(void) /*初始化内核变量*/
{
    /*创建一个互斥信号量*/
    modbus_usart4_mux = rt_mutex_create("usart4_mux",RT_IPC_FLAG_PRIO);
    if (modbus_usart4_mux == RT_NULL)
            rt_kprintf("%s %s:%d error!\r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码            
    /* 创建一个信号*/
        UART4_RX_END_SEM  = rt_sem_create("RX4_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (UART4_RX_END_SEM == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
    /* 创建一个信号*/        
        UART4_TX_END_SEM  = rt_sem_create("TX4_sem",
                                                                                0,
                                                                                RT_IPC_FLAG_FIFO);
    if (UART4_TX_END_SEM == RT_NULL)
            rt_kprintf("error@ %s %s:%d,The semphore for WIFI can‘t be created!r\n", __FILE__, __FUNCTION__, __LINE__);//当前行所在文件名、函数名、第**行代码
return 0;
}
/*
***********************************************
* 函 数 名:  modbus_usart_send
* 功能说明:  阻塞型,通过串口发送数据
* 形    参: 
*           timeout 超时,单位ticks
*           p_frame 指针,指向发送数据
* 返 回 值: 0,正常; 非0,错误
* 全局变量:
************************************************
*/
uint8_t modbus_usart4_send   (modbus_frame_def* p_frame,uint32_t timeout)/*阻塞型,通过串口发送数据*/
{
    uint8_t ret;
    /* 发送通信包 */        
    take_modbus_usart4(timeout);   /*使用互斥量,占用串口*/
    /*独占DMA-USART外设,启动DMA-USART发送通信包*/        
    HAL_UART_AbortTransmit_IT(&MODBUS_USART4_HANDLE);//停止并清除中断允许标志        
    if (HAL_UART_Transmit_DMA(&MODBUS_USART4_HANDLE, (uint8_t*)p_frame->buf, p_frame->n_bytes) != HAL_OK)
    {/*发送帧*/
    rt_kprintf("%s %s:%d HAL_UART_Transmit_DMA() error!\r\n", __FILE__, __FUNCTION__, __LINE__);
    return 1;
    }    
    ret = uart4_take_TX_sem(timeout);/*使用信号,等待串口发送结束*/
    release_modbus_usart4();/*使用互斥量,释放串口*/
    return ret;
}
/*
***********************************************
* 函 数 名:  modbus_usart_receive
* 功能说明:  阻塞型,通过串口接收数据
* 形    参: 
*           timeout 超时,单位ticks
*           p_frame 指针,指向接收数据
* 返 回 值: 0,正常; 非0,错误
* 全局变量:
************************************************
*/
uint8_t modbus_usart4_receive(modbus_frame_def* p_frame,uint32_t timeout)/*阻塞型,通过串口接收数据*/
{
    uint8_t ret;
    /* 发送通信包 */        
    take_modbus_usart4(timeout);   /*使用互斥量,占用串口*/
    
    p_usart4_frame =p_frame;////传指针,接收串口中断使用
    
    HAL_UART_AbortReceive_IT(&MODBUS_USART4_HANDLE);//停止并清除中断允许标志        
    if (HAL_UART_Receive_DMA(&MODBUS_USART4_HANDLE, (uint8_t*)p_frame->buf, sizeof(p_frame->buf)) != HAL_OK)
    {/*接收帧*/
    rt_kprintf("%s %s:%d HAL_UART_Receive_DMA() error!\r\n", __FILE__, __FUNCTION__, __LINE__);
    return 1;
    }        
    __HAL_UART_ENABLE_IT(&MODBUS_USART4_HANDLE, UART_IT_IDLE);// 开空闲中断,处理接收字节没有达到sizeof(mail_msg_rx.buf)字节数情况。
    
    ret = uart4_take_RX_sem(timeout);/*使用信号,等待串口发送结束*/
    release_modbus_usart4();/*使用互斥量,释放串口*/
    return ret;
}
View Code

 

注意:

没有修改好,中断函数,调用在modbus_uart3_app.c等文件中定义的中断处理APP函数,将发生如下错误。

 

posted @ 2023-02-28 12:53  辛河  阅读(161)  评论(0编辑  收藏  举报