定时器串口接收空闲等待

1、定义需要用到的变量

typedef struct {
    u8 RxPacket[256];  //数据接收缓存区 :接收到的数据存放在这里
    u8 pRxPacket;      //数组下标       :每接收一个数据,下标加一  
    u8 RxFlag;         //接受完成标志位 :当数据接收完时置位,否则清零
    u8 RxState;        //数据接收状态机 :什么状态接收什么数据,滤掉不要的数据,对数据初步处理
    u8 RxTimer;        //数据接收定时器 :软件定时器,基于定时器的定时器。接收数据就打开,接收完成        
                        //              就关闭
    u8 RxCount;        //数据接收计数器 :数据接收定时器一打开就开始数数,
                       //               接收到数据就重新数即清零,数到定的时间还不清零就认为这一 
                       //               包数据接收完了
}reseive_t;


enum {
    Close = 0,//
    Open = 1
};

2、定时器中断处理

定时器时间取决于 波特率:即1秒能接收的字节数

如 115200 波特率 1 秒是 115200个字节

而发来的每个字节的数据有 1 起始位, 1 停止位,8 数据位,即 10位

因此接收每个字节所需要的时间为 10/115200 秒

1 秒等于 1000 000 微秒,则接收每个字节需要 1000 000 * 10/115200 微秒,约86微秒

因此每过来一个数据便打开定时器开始计时,并且每来一个数据重新开始计时

若没有数据过来,就一直计时,直到超过某个时间(一定要大于接收每个字节的时间)

便判定这一包数据接收完毕(一个数据包的数据每个字节的发送时间不会差太多的)

我这里使用的波特率为115200,不妨设 这个时间 为100微秒,超过100微秒则认为这一包数据接收完毕

基于定时器的定时器  :单片机的定时器每10us进入中断,我的“数据接收定时器”要数10个数才能进去执行内容

进入定时器中断之后,首先判断我的“数据接收定时器”打开了没(在接收中断中打开,且有数据过来才能打开)

如果打开了,说明有数据来了,那么我就开始计数,不妨假设计数超过了10,那么判定数据接收完毕

关闭该“数据接收定时器”,计数值清零,给   数据缓存区数组【数组下标】 = '\0',设为字符串到这结束

把数组下标清零  注:先给 数据缓存区末位='\0'再清零,数组也要清空,但是不在这里清,我们要把数据处理完再清

接收完成标志位置位,告诉单片坤数据接收完成

if(reseive.RxTimer == Open)//如果接收定时器打开了
{
	reseive.RxCount++;//开始计数
	if(reseive.RxCount >= RxMaxTime_100us)//超过100us认为数据接收完成
	{
		reseive.RxTimer = Close;//关闭“数据接收定时器”
		reseive.RxCount = 0;//计数值清零
		reseive.RxPacket[reseive.pRxPacket] = '\0';
		reseive.pRxPacket = 0;//数组下表清零
		reseive.RxFlag = 1;//接收完成标志位置位,接收完成
	}
}

3、串口中断处理

这里我是把发过来的数据照单全收,并没有用到 reseive.Status 这个变量

先对结构体初始化,全部清零

数据过来进入中断,定义一个变量接收数据

如果用状态机的话可以先判断数据是不是想要的,比如帧头、回车换行、帧尾什么的

是的话进入下一个状态接收数据,不是的话就放弃接收,清空数组、复位数组下标重新接收什么的,当然这里没有用到

那么接着往下看,数据来了就打开 “数据接收计时器” 开始计时

把数据接收到数组里面,数组下标自增,以便保存下一个数据

最后把计数值清零,因为对于连续接收的数据我们认为他们是在同一个数据包,

并认为发过来的每个字节间隔时间超过100us便不在同一个数据包,至于为啥这么认为,

以及不是同一个数据包怎么办、我已经在定时器中断那里阐述过了

所以给他清零,告诉单片坤是同一个数据包。

reseive_t reseive = {{0},0,0,0,0,0};
void USART1_IRQHandler()
{
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE) != RESET)
	{
		u8 RxData = USART_ReceiveData(USART1);
		
		reseive.RxTimer = Open;//打开定时计数器
		reseive.RxPacket[reseive.pRxPacket] = RxData;//开始接收
		reseive.pRxPacket++;
		reseive.RxCount = 0; //接收到一字节数据,计数值清零
	}

	USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}

4、主函数处理

在主函数里对接收的数据进行处理,若有其他需求可把数据处理封装成函数,塞到其他地方

首先要判断数据接收完了没,即数据 接收完成标志位 有没有置位,至于有没有置位是在定时器中断里操作的

若是接收完成标志位置位了,就把接收到的一串玩意打印出来

因为我是对发来的数据照单全收的,所以没有进一步处理

处理思路就是判断这个数据是不是自己想要的

比如 if( strcmp( 数据名,"字符串" ) == 0) strcmp函数是字符串匹配,如果数组里的字符串与你给的字符串一毛一样

那么就是我要接收的数据了,据此进行下一步操作。如若使用,记得加上<string.h>头文件。当然了我这里没有这么做

我这里是把接收的字符串全部打印出来,看看是不是我发的数据

接下来,既然对数据处理完了,当然要把接收完成标志位清零了啦,顺手也把数组也清空一下~~~~~~~~~~~~~~~~

if(reseive.RxFlag == 1)
{
	printf("%s\n", reseive.RxPacket);

    reseive.RxFlag = 0;//接收完成标志位清零
	memset(reseive.RxPacket, 0, sizeof(reseive.RxPacket));//清空数组              
                                                          //这个也需要<string.h>头文件
}

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Modbus串口接收程序可以采用两种方式:串口中断和DMA。对于没有DMA外设的单片机,只能使用串口中断方式。每当接收到一个字节的数据,串口会产生中断,但这样会频繁打断主程序的运行,可能导致数据丢失和主循环的实时性受影响。\[1\]而对于具有DMA的单片机,可以使用DMA方式,只有在接收到一帧数据后才会产生中断,这样可以减少中断的频率,不会频繁打断主程序的运行,提高了实时性。\[2\] 在串口中断方式中,每次接收到一个字节的数据都会重新启动定时器,因为无法确定串口数据的长度。当接收完成一帧数据后,会产生T35超时中断。为了提高主程序的运行效率,可以使用串口空闲中断来取代T35超时中断,如果芯片没有空闲中断,可以使用定时器资源来模拟空闲中断。\[3\] 在DMA方式中,数据的接收由DMA控制,当接收到一帧数据后才会产生中断,不会频繁打断主程序的运行。这种方式不需要重新启动定时器或使用空闲中断来判断一帧数据的结束。\[2\] 综上所述,Modbus串口接收程序可以根据具体的硬件资源选择合适的方式,串口中断方式适用于没有DMA外设的单片机,而DMA方式适用于具有DMA外设的单片机。 #### 引用[.reference_title] - *1* *2* *3* [Modbus-RTU状态机接收数据帧的方式(串口 or DMA)](https://blog.csdn.net/armwind/article/details/124873883)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值