(软件03)单片机串口处理思路,超时接收的方法

这篇具有很好参考价值的文章主要介绍了(软件03)单片机串口处理思路,超时接收的方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文目录

  •     软件学习前言
  •     代码思路
  •     实操练习

软件学习前言

        最近写了两篇硬件分享文章,要做的一个通过485串口接收指令,从而控制电机转速的内容。里面涉及到了串口的处理,于是便想写一下关于串口处理的相关经验分享,串口也是非常重要的,不管是printf打印log信息,还是涉及到协议通信部分,都是嵌入式里面必不可少的知识点。

        相关配套的硬件思路请参考我之前的硬件篇文章:

        (硬件02)按键+电位器+485控制的电机调速电路实战,上篇https://blog.csdn.net/BEXZJ/article/details/134784629

        (硬件03)按键+电位器+485控制的电机调速电路实战,下篇https://blog.csdn.net/BEXZJ/article/details/134791467

        接下来就开始正式地介绍串口的收发处理了,本篇我们要讲的是,串口的超时接收处理。

        老规矩,一张封面图进入今天的分享。

单片机串口超时分包,嵌入式软硬件知识分享,单片机,嵌入式硬件,物联网,mcu,软件工程,arm开发,stm32

代码思路

        1.串口理解

        说起刚学串口那会,听到的一个顺口溜叫“9681N”,让我印象很深刻,学习完串口知识之后,就理解它的意思了。

单片机串口超时分包,嵌入式软硬件知识分享,单片机,嵌入式硬件,物联网,mcu,软件工程,arm开发,stm32

        96:代表9600波特率,英文bps,bit per second,就是每秒钟可以发9600位数据,我们知道一个字节是8位的,那么就是9600/8=1200个字节。常见的波特率有9600、115200。

        8:代表数据是8位为一个字节的,其中也可以选择7位的标准ASCII码(0-127)数据,8位的是拓展的ASCII码(0-255)数据。

        1:代表1个停止位,这个与通讯双方进行约定好即可,可选1.5、2位。

        N:代表None,是不进行校验的意思,这个与通讯双方进行约定好即可,可选奇校验、偶检验等。

        2.串口的初始化

        使用单片机对应的库函数,将串口硬件初始化为上述我们理解后的串口格式配置。再配置串口中断,以及在中断服务函数的相关处理。

        3.串口的超时接收

        串口中断收到一个字节的数据,将其存入BUFF数组,并且将数组的位置往后一位以用来存储下一个字节数据,重置一下接收超时的时间,标记我们已经开始接收了,最后清中断标记位。

        在接收完最后一字节数据后。接收时间便不会重置,于是我们可以想到,根据开始接收的标记,结合重置时间的非0自减,当重置时间减少到0时,我们就认为这一帧数据我们已经接收完了,可以对其进行处理了,并且让接收数据的位置回到第一位,其他的标记也重置一下。

        4.数据处理

        根据接收完的标记,我们可以进行数据处理,处理完,再将接收完的标记取消了,便可以开始下一轮的串口接收和处理了。

        5.思路总结

        串口有接收字节时,给它一个等待时间,如20ms(刚刚我们算出9600波特率是1秒钟传1200个字节,那么1ms可以传1.2个字节,我们只需要等1ms就可以了,我们之前设置的是1ms的中断时基处理,为了保险,我们取20ms作为等待时间,再说了,两条报文间只间隔20ms是很少见的,基本不会出现连包情况),最后一个字节接收完了后,再等20ms时间,就判读为接收完成,标记数据帧有效,可以处理数据。

实操练习

        1.头文件的宏定义、结构体、函数申明。

        头文件zj_public.h

#include <at32f4xx.h>
#include <string.h>

#include "at32f4xx_usart.h"


//串口
#define BSP_UART1_TX_GPIO_PIN      		GPIO_Pins_6
#define BSP_UART1_TX_GPIO_AF1_Source	GPIO_PinsSource6
#define BSP_UART1_TX_GPIO_PORT   	  	GPIOB
#define BSP_UART1_RX_GPIO_PIN      		GPIO_Pins_7
#define BSP_UART1_RX_GPIO_AF1_Source	GPIO_PinsSource7
#define BSP_UART1_RX_GPIO_PORT   	  	GPIOB

#define BSP_485_USART   	  			USART1
#define BSP_485_USART_IRQHandler 		USART1_IRQHandler
#define BSP_485_USART_BANDRATE 			115200

#define BSP_485_USART_IRQN  		    USART1_IRQn
#define BSP_485_USART_IRQN_LEVEL  		2
#define BSP_485_USART_IRQN_HANDLER  	USART1_IRQHandler


//时基定时器
#define BSP_TIME_BASE_TIMER  			TMR6
#define BSP_TIME_BASE_TIMER_ARR  		10-1
#define BSP_TIME_BASE_TIMER_PSC  		7200-1
#define BSP_TIME_BASE_IRQN  			TMR6_GLOBAL_IRQn
#define BSP_TIME_BASE_IRQN_LEVEL  		0
#define BSP_TIME_BASE_IRQN_HANDLER  	TMR6_GLOBAL_IRQHandler


//LED灯
#define BSP_LED_RUN_GPIO_PIN      		GPIO_Pins_10
#define BSP_LED_RUN_GPIO_PORT     		GPIOA
#define BSP_LED_RUN_ON      			GPIO_ResetBits(BSP_LED_RUN_GPIO_PORT, BSP_LED_RUN_GPIO_PIN)
#define BSP_LED_RUN_OFF      			GPIO_SetBits(BSP_LED_RUN_GPIO_PORT, BSP_LED_RUN_GPIO_PIN)




//串口数据缓存长度
#define	UART_DATA_BUFF_LEN 				512

extern volatile uint64_t gTimeBase;

typedef struct
{
	uint8_t rx_buff[UART_DATA_BUFF_LEN];
	uint16_t rx_index;
	uint8_t rx_finish_flag;
	uint8_t rx_overtime_flag;
	uint16_t rx_overtime_ms;
	uint16_t rx_len;
	
	uint8_t tx_buff[UART_DATA_BUFF_LEN];
	uint16_t tx_len;
	
}UART_INFO;
extern UART_INFO zj_485_uart;

void zj_app_timebase_process(void);
void zj_app_timebase_1ms_process(void);

void zj_app_uart_process(void);
void zj_app_uart_10ms_process(void);
void zj_app_uart_send(uint8_t *pBuf,uint8_t mLen);


硬件配置函数zj_bsp.c,配置时钟、GPIO复用、串口配置、串口中断、时基配置(软件01篇有介绍)

#include "zj_public.h"

volatile uint64_t gTimeBase = 0;


void RCC_Configuration(void)
{   
    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOB, ENABLE);
	
    RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_USART1, ENABLE);//串口 
	RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_TMR6, ENABLE);//时基
	
	RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_SYSCFGCOMP, ENABLE);//IO复用
}

void GPIO_Configuration(void)
{
    GPIO_InitType GPIO_InitStructure;
    GPIO_StructInit(&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;

	//UART1 管脚配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
	GPIO_InitStructure.GPIO_Pins = BSP_UART1_TX_GPIO_PIN;
    GPIO_Init(BSP_UART1_TX_GPIO_PORT, &GPIO_InitStructure); 
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Pull = GPIO_Pull_PU;
	GPIO_InitStructure.GPIO_Pins = BSP_UART1_RX_GPIO_PIN;
    GPIO_Init(BSP_UART1_RX_GPIO_PORT, &GPIO_InitStructure); 
	
	//UART1 管脚复用
    GPIO_PinAFConfig(BSP_UART1_TX_GPIO_PORT, BSP_UART1_TX_GPIO_AF1_Source, GPIO_AF_0);
    GPIO_PinAFConfig(BSP_UART1_RX_GPIO_PORT, BSP_UART1_RX_GPIO_AF1_Source, GPIO_AF_0);
	
	//灯
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
	GPIO_InitStructure.GPIO_Pins = BSP_LED_RUN_GPIO_PIN;
    GPIO_Init(BSP_LED_RUN_GPIO_PORT, &GPIO_InitStructure); 
    BSP_LED_RUN_ON;
}

void NVIC_Configuration(void)
{
    NVIC_InitType NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

	// 0 TIMER6_TIMEBASE 
    NVIC_InitStructure.NVIC_IRQChannel = BSP_TIME_BASE_IRQN;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = BSP_TIME_BASE_IRQN_LEVEL;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

	// 1 USART1_485 
    NVIC_InitStructure.NVIC_IRQChannel = BSP_485_USART_IRQN;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = BSP_485_USART_IRQN_LEVEL;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void EXTI_Configuration(void)
{

}

void TIMER_BASE_Configuration(TMR_Type * mTimer ,u16 mArr, u16 mPsc)
{
    TMR_TimerBaseInitType TIM_TimeBaseStructure;

    //定时器初始化
    TMR_Reset(mTimer);
    TIM_TimeBaseStructure.TMR_Period = mArr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 
    TIM_TimeBaseStructure.TMR_DIV = mPsc; //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TMR_ClockDivision = TMR_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TMR_CounterMode = TMR_CounterDIR_Up; //TIM向上计数模式
    TMR_TimeBaseInit(mTimer, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
	
    TMR_ClearITPendingBit(mTimer, TMR_INT_Overflow);
    TMR_INTConfig(mTimer, TMR_INT_Overflow, ENABLE);
    TMR_Cmd(mTimer, ENABLE); //使能TIMx 
}

void BSP_TIME_BASE_IRQN_HANDLER(void)
{
  if (TMR_GetINTStatus(BSP_TIME_BASE_TIMER, TMR_INT_Overflow) != RESET)//是更新中断
  {
    TMR_ClearITPendingBit(BSP_TIME_BASE_TIMER, TMR_INT_Overflow); //清除TIM更新中断标志 
		
	zj_app_timebase_1ms_process();
  }
}

void USART_Configuration(USART_Type *USARTx, uint32_t nBandRate)
{
    USART_InitType USART_InitStructure;
 
    USART_InitStructure.USART_BaudRate = nBandRate; //波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位
    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(USARTx, &USART_InitStructure);
	
	 //使能串口1接收中断
    USART_INTConfig(USARTx, USART_INT_RDNE, ENABLE);	
    //使能串口
    USART_Cmd(USARTx, ENABLE);
}

void BSP_485_USART_IRQN_HANDLER(void)
{
  if (USART_GetITStatus(BSP_485_USART, USART_INT_RDNE) != RESET)
  {
    USART_ClearFlag(BSP_485_USART, USART_FLAG_RDNE);
		
	zj_485_uart.rx_overtime_flag = TRUE;
	zj_485_uart.rx_overtime_ms = 20;
		
	zj_485_uart.rx_buff[zj_485_uart.rx_index++] = USART_ReceiveData(BSP_485_USART);
		
	if(zj_485_uart.rx_index >= UART_DATA_BUFF_LEN)
	zj_485_uart.rx_index = 0;
  }
}


void zj_bsp_config(void)
{
  SystemCoreClockUpdate();

  RCC_Configuration();      
  NVIC_Configuration();
  GPIO_Configuration();
  EXTI_Configuration();

  USART_Configuration(BSP_485_USART,BSP_485_USART_BANDRATE);
  TIMER_BASE_Configuration(BSP_TIME_BASE_TIMER,BSP_TIME_BASE_TIMER_ARR,BSP_TIME_BASE_TIMER_PSC);
}

时基函数处理

#include "zj_public.h"
 
 
uint64_t TimeBaseMs=0,TimeBase10ms=0,TimeBase50ms=0,TimeBase100ms=0,TimeBase500ms=0,TimeBase1000ms=0,TimeBase5000ms=0,TimeBase6000ms=0,TimeBase10000ms=0,TimeBase60000ms=0;
 
static uint64_t Time_GetTimeMs(void)
{
    return gTimeBase;
}
 
static void Process(void)
{
    zj_app_uart_process(); //串口接收处理
}
 
void zj_app_timebase_1ms_process(void)
{
	gTimeBase++;

	if(zj_485_uart.rx_overtime_ms)//串口接收超时时间处理,非零自减
	  zj_485_uart.rx_overtime_ms--; 
}
	
static void TimeProcess_10MS(void)
{
    zj_app_uart_10ms_process();//串口数据帧10ms处理
}
static void TimeProcess_50MS(void)
{
 
}
static void TimeProcess_100MS(void)
{
 
}
static void TimeProcess_500MS(void)
{
}                  
static void TimeProcess_1000MS(void)
{
}
static void TimeProcess_5000MS(void)
{   
	static uint8_t sToggleFlag = 0;
 
	if(sToggleFlag)
	{
		sToggleFlag = FALSE;
		
		BSP_LED_RUN_ON;
	}
	else
	{
		sToggleFlag = TRUE;
	
		BSP_LED_RUN_OFF;
	}
}
static void TimeProcess_10000MS(void)
{   
}
static void TimeProcess_60000MS(void)
{   
}
 

void zj_app_timebase_process(void)
{
	
  Process();
  TimeBaseMs=Time_GetTimeMs();
 
  if(((TimeBaseMs-TimeBase10ms))>9)//10ms
  {
    TimeBase10ms+=10;
    TimeProcess_10MS();
  }
  if(((TimeBaseMs-TimeBase50ms))>49)//50ms
  {
    TimeBase50ms+=50;
    TimeProcess_50MS();
  }
  if(((TimeBaseMs-TimeBase100ms))>99)//100ms
  {
    TimeBase100ms+=100;
    TimeProcess_100MS();
  }
  if(((TimeBaseMs-TimeBase500ms))>499)//500ms
  {
    TimeBase500ms+=500;
    TimeProcess_500MS();
  }
  if(((TimeBaseMs-TimeBase1000ms))>999)//1s
  {
    TimeBase1000ms+=1000;
    TimeProcess_1000MS();
  }
  if(((TimeBaseMs-TimeBase5000ms))>4999)//5s
  {
    TimeBase5000ms+=5000;
    TimeProcess_5000MS();
  }
  if(((TimeBaseMs-TimeBase10000ms))>9999)//10s
  {
    TimeBase10000ms+=10000;
		TimeProcess_10000MS();
  }
  if(((TimeBaseMs-TimeBase60000ms))>59999)//60s
  {
    TimeBase60000ms+=60000;
		TimeProcess_60000MS();
  }
}

应用函数zj_app_uart.c

#include "zj_public.h"


void zj_app_uart_send(uint8_t *pBuf,uint8_t mLen)
{
	while(mLen--)
	{
		USART_SendData(BSP_485_USART,*pBuf++); //库函数,串口发送单个字节数据
		while(USART_GetFlagStatus(BSP_485_USART, USART_FLAG_TRAC) == RESET); 
	}
}

void zj_app_uart_process(void)
{
	if((zj_485_uart.rx_overtime_flag == TRUE) && (zj_485_uart.rx_overtime_ms==0))
	{
		//rx_overtime_flag=TRUE :有超时接收,rx_overtime_ms=0 :最后一个字节接收完成

		zj_485_uart.rx_len =  zj_485_uart.rx_index; //接收到的长度
		zj_485_uart.rx_index = 0;                   //接收BUFF回到首位,为下次接收做准备
		zj_485_uart.rx_overtime_flag = FALSE;       //重置超时接收标记,为下次接收做准备
		
		zj_485_uart.rx_finish_flag = TRUE;          //标记接收完成
	}
}


void zj_app_uart_10ms_process(void)
{
	if(zj_485_uart.rx_finish_flag) //根据接收完成标记处理数据
	{
	  zj_485_uart.rx_finish_flag = FALSE;//重置开始接收标记,为下次处理数据做准备

      zj_app_uart_send(zj_485_uart.rx_buff,zj_485_uart.rx_len); //示例用,收到什么数据发什么数据回去
	}
}

主函数main.c

#include "zj_public.h"
     
int main(void)
{  
  zj_bsp_config();
     
  while(1)
  {
   zj_app_timebase_process();
  } 	
}

        如此,串口的超时接收处理已经介绍完成了,具体的串口应用肯定不是接收什么发送什么回去,比如说一些私有协议解析,要解析报文头、报文尾、数据长度、校验方式等,这些都是比较复杂的,但都是在zj_app_uart_10ms_process(void);中处理就行,通过状态机的方式处理,先判断接收数组中的第一个字节,再跳转到接收数组中的第二个字节....最后判断完最后的数据,对整个接收数字进行memset(&buff,0,buff_len)重置处理。

        预告一下,下一篇软件篇将带大家实操练习一下485 Modbus的项目,包括0x03读多个寄存器指令、0x06写单个寄存器指令、0x10写多个寄存器指令,以及遇到一些关键数据怎么保存到flash中去,如从机地址。。。


小弟感谢大家的关注!

      (利他之心,原创分享)
 文章来源地址https://www.toymoban.com/news/detail-789819.html

到了这里,关于(软件03)单片机串口处理思路,超时接收的方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (软件02)单片机按键处理,区分短按与长按

    本文目录     本篇前言     代码思路     实操练习 本篇前言         今天接着上篇与大家继续分享软件方面关于按键事件的处理,上篇软件01篇已提到整个软件框架时基的处理,其中提到了关于按键的处理,这篇将具体地介绍按键处理的思路与实例。         话不

    2024年02月03日
    浏览(37)
  • 学习笔记|串口通信的基础知识|同步/异步|RS232|常见的串口软件的参数|STC32G单片机视频开发教程(冲哥)|第二十集:串口通信基础

    百度百科:串口通信的概念 什么是通信? 例如U盘和电脑,我们电脑需要往U盘存东西,而U盘上只有四个触点,除去一个电源一个地,只剩下两个引脚了。此时我们坑定不能像点亮LED那样单纯的给他两个引脚上输出个高低电平就能写数据了对吧。总不至于输出一个高电平就能

    2024年02月07日
    浏览(65)
  • 【单片机】51单片机串口的收发实验,串口程序

    这段代码是使用C语言编写的用于8051单片机的串口通信程序。它实现了以下功能: 引入必要的头文件,包括reg52.h、intrins.h、string.h、stdio.h和stdlib.h。 定义了常量FSOC和BAUD,分别表示系统时钟频率和波特率。 定义了一个发送数据的函数send,该函数将数据发送到串口,等待数据

    2024年02月14日
    浏览(45)
  • 【51单片机系列】proteus仿真单片机的串口通信

    本文参考:https://zhuanlan.zhihu.com/p/425809292。 在proteus之外使用串口软件和单片机通信。通过在proteus设计一个单片机接收PC发送的数据,并将接收的数据发送出去,利用软件【Configure Virtual Serial Port Driver】创建一对虚拟串口,利用软件【串口助手】向单片机发送数据。 proteus仿真

    2024年01月17日
    浏览(52)
  • AT89C51单片机实现单片机串口互动(中断方式,单片机--单片机,应答)

     说一下功能:客户机发送0x01到服务机 2服务单片机应答0xf2到客户机 3客户机接收到0xf2,发送信息153432这6个数字到服务机 4client发送完信息后发送0xaa结束通信 5server接收到0xaa后回复0xaa结束通信,从此老死不相往来 看代码: 服务端代码:    

    2024年02月13日
    浏览(57)
  • 51单片机串口的应用(单片机和电脑互发数据)

    现在来详细看一下寄存器,我们直接查看单片机手册。 SCON寄存器 先来说说SCON寄存器。 前一节我们提过,我们一般使用串口用的是模式1,即8位UART,这样我们就用不到校验位。从手册中可以看到,寄存器SCON中的SM0和SM1配置成01即可。 SM2寄存器明显用不到,因为我们没有用模

    2024年02月07日
    浏览(58)
  • 单片机的串口通信

            今天,完整地总结一下普中科技的单片机的串口通信的硬件与编程,记录一下以后如果需要也比较方便捡起来。         单片机的串口部分的电路图。开发板上集成了 1 个串口通信电路,是 USB 转串口模块,它既可下载程序也可实现串口通信功能。         对这

    2024年02月11日
    浏览(44)
  • 51单片机串口使用

    今天将为大家讲解51单片机的串口原理及代码编写。 51单片机串口是一种通信接口,它可以将51单片机与外部设备连接起来,实现数据的双向传输。51单片机串口的原理是,51单片机的串口接口由两个管脚组成,一个是RXD,另一个是TXD,RXD用于接收数据,TXD用于发送数据。当5

    2024年02月05日
    浏览(49)
  • 51单片机串口

    1.1串口接线方式 RXD:数据输入引脚,数据接受;STC89系列对应P3.0口,上官一号有单独引出 TXD:数据发送引脚,数据发送;STC89系列对应P3.1口,上官一号有单独引出 接线方式 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMY5yTZI-1690308835514)(C:Use

    2024年02月15日
    浏览(40)
  • 单片机串口通信程序

    本文总结了两种比较简单的关于串口发送接收的程序,以下是步骤: 定义数据: 首先要串口初始化:(以9600波特率为例) 串口中断:  在定时器0扫描里加入,或者主函数while(1)里加入: (其中++URX_tt=(1000/波特率),数要接近,否则会有小小干扰。) 接下来关于串口通信

    2024年02月10日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包