当前位置: 代码迷 >> 综合 >> 单片机 MQTT 读取数据问题(串口通信)
  详细解决方案

单片机 MQTT 读取数据问题(串口通信)

热度:10   发布时间:2023-11-22 22:03:59.0

单片机 MQTT 读取数据问题(串口通信)

问题描述

  1. 使用stm32F10x单片机串口接收数据时,接收中断只能按字节接收。将接收到的数据放入数据队列中。
  2. 这种接收中断按字节接收数据的方式,无法像Linux一样利用接收超时机制判断无后续帧。(接收到一段数据后,不会立即退出接收,而是等超时时间到表示本轮接收完毕)
  3. mqtt 读取到数据队列中有数据,就开始解析数据,然而此时有可能串口并没有将一帧完整的数据帧读取完毕,所以此时解析数据会出现解析失败问题。

解决方法

解决此问题的方法,可以参考Linux的思路。具体实现利用接收完成标志和定时器实现。具体步骤如下:

  1. 配置定时器,此定时器用来判断超时,定义两个函数,TIM_Start()和TIM_Stop()。用来启动和关闭定时器。启动定时器里面有两个动作,一个是重新装载自动重载值,让定时器重新计数。另一个动作就是启动动作。
  2. 定义一个接收状态标志,进入接收中断后就复位此标志,在定时器中断中置位此标志(进入定时器中断就表示已经接收超时)。
  3. 在mqtt读取数据时,不是先看数据队列中有无数据,而是看接收状态标志是否置位。如果是置位状态,则现在是一帧完整的数据帧。

程序

mqtt 通信处理函数

#include "appComDeal.h"#define TIMER_NAME TIM4 //定时器名称
#define AUTORELOAD_VAL 1000-1 //自动装载值,100msByteQueue_t	usart2_recvQueue = Queue_Init_Value;
ByteQueue_t	usart2_sendQueue = Queue_Init_Value;/******************************************************************* * funcname: TIM_START * author: Mr-TJW * para: Autoreload 重装载值 * function: 启动定时器 * return: ********************************************************************/
void TIM_START(void)
{
    TIM_SetAutoreload(TIMER_NAME, AUTORELOAD_VAL);TIM_Cmd(TIMER_NAME, ENABLE);
}/******************************************************************* * funcname: Timer_Start * author: Mr-TJW * para: * function: 关闭定时器 * return: ********************************************************************/
void TIM_Stop(void)
{
    TIM_Cmd(TIMER_NAME, DISABLE);
}//定时器4初始化
//arr:自动重装值。
//psc:时钟预分频数
//TIM4_Int_Init(9,7199); //10Khz 的计数频率,计数到 10 为 1ms
//TIM4_Int_Init(10000-1,7199); //10Khz 的计数频率,计数到 10000 为 1s
void TIM4_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500msTIM_TimeBaseStructure.TIM_Prescaler = psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位TIM_ITConfig(  //使能或者失能指定的TIM中断TIM4, //TIM2TIM_IT_Update  |  //TIM 中断源TIM_IT_Trigger,   //TIM 触发中断源 ENABLE  //使能);NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);  //TIM_Cmd(TIM4, ENABLE); //使能TIMx外设 
}/******************************************************************* * funcname: TIM4_IRQHandler * author: Mr-TJW * para: * function: 串口4接收中断,用作判断接收完一轮数据后,是否还有后续帧,若超时时间到,置位接收完成标志 * return: ********************************************************************/
void TIM4_IRQHandler(void)   //TIM4中断
{
    	if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 {
       TIM_Stop();usart2_recvQueue.flag = 1;  //超时时间到,没后续帧过来,此时认为本轮数据接收完毕TIM_ClearITPendingBit(TIM4, TIM_IT_Update);  //清除TIMx的中断待处理位:TIM 中断源 }
}/******************************************************************* * funcname: usart2_init * author: Mr-TJW * para: * function: 串口2初始化 * return: ********************************************************************/
void usart2_init(u32 bound)
{
    	//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟//USART1_TX GPIOA2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2//USART1_RX GPIOA3初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器//USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式USART_Init(USART2, &USART_InitStructure); 		//初始化串口1USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);	//开启串口接受中断USART_Cmd(USART2, ENABLE);                    	//使能串口1//收发队列初始化queueInit(&usart2_recvQueue, USART2_QUEUE_LEN);queueInit(&usart2_sendQueue, USART2_QUEUE_LEN);
}/******************************************************************* * funcname: usart2_sendData * author: Mr-TJW * para: * function: 串口2数据发送,将发送队列中数据发送出去 * return: ********************************************************************/
void usart2_sendData()
{
    	char sendFlag = 0;if(usart2_sendQueue.count){
    	sendFlag = 1;printf("\r\nusart2_sendData, time = %d, bytes = %d\r\n", g_systemRunTime_s, usart2_sendQueue.count);		}else{
    return;}while(usart2_sendQueue.count){
    	printf("%02X ",usart2_sendQueue.buf[usart2_sendQueue.head]);USART_SendData(USART2, usart2_sendQueue.buf[usart2_sendQueue.head]);dropData(&usart2_sendQueue);}if(sendFlag) printf("\r\n\r\n");
}/******************************************************************* * funcname: usart2_recvData * author: Mr-TJW * para: buf-接收缓冲区 * function: 串口2接收数据,将接收队列中数据放入buf * return: 接收数据长度 ********************************************************************/
u16 usart2_recvData(u8* _buf)
{
    	u16 index = 0;if(usart2_recvQueue.count > 0){
    	printf("\r\nusart2_recvData, time = %d, bytes = %d:\r\n", g_systemRunTime_s, usart2_recvQueue.count);while(usart2_recvQueue.count){
    	printf("%02X", usart2_recvQueue.buf[usart2_recvQueue.head]);_buf[index++] = usart2_recvQueue.buf[usart2_recvQueue.head];dropData(&usart2_recvQueue);}printf("\r\n\r\n");}return index;
}void USART2_IRQHandler(void) 				//串口1中断服务程序
{
    u8 Res;				if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾){
       TIM_Stop();Res =USART_ReceiveData(USART2); //读取接收到的数据usart2_recvQueue.flag = 0;  //接收中putData(&usart2_recvQueue, Res);TIM_START();} 
} /******************************************************************* * funcname: app_cpm_deal * author: Mr-TJW * para: bound-波特率 * function: * return: ********************************************************************/
void app_com_init(u32 bound)
{
       usart2_init(bound);TIM4_Int_Init(AUTORELOAD_VAL,7199); //定时器4初始化,用作数据接收中断,等超时时间到,还没后续帧过来标识接收完毕
}

mqtt接收端代码程序

/******************************************************************* * funcname: Mqtt_msgRecvDeal * author: Mr-TJW * para: * function: 消息处理 * return: ********************************************************************/
void Mqtt_msgRecvDeal(void)
{
    	int   msgType;        /* 消息类型 */if(g_pRecvFromComQueue->flag){
    	if(g_pRecvFromComQueue->count){
    printf("\r\nmqtt recv %d bytes, time = %d\r\n", g_pRecvFromComQueue->count, g_systemRunTime_s);printHex(&g_pRecvFromComQueue->buf[g_pRecvFromComQueue->head], g_pRecvFromComQueue->count);msgType = MQTTPacket_read(m_recvBuf, g_pRecvFromComQueue->len, readBytesFromQueue);	// transport_getdataif ((msgType >= CONNACK) &&(msgType <= DISCONNECT)){
    Mqtt_msgParseDeal();}else{
    	printf("MQTTPacket_read return error code %d\r\n", msgType);}}}
}