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 */
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
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); };
应用线程程序 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
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; }
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
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 } }
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
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; } }
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
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; } }
串口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
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; }
串口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
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; }
注意:
没有修改好,中断函数,调用在modbus_uart3_app.c等文件中定义的中断处理APP函数,将发生如下错误。