三种串口接受不定长数据方法详解
串口通信中接收数据时延迟处理与缓存处理的解决方案(C#)
串口通信中接收数据时延迟处理与缓存处理的解决方案(C#)利用串口进行通信,当发送方(A)将数据写入串口后,通过无线或有线方式将数据传送给接收方(B),B通过调用串口读方法comm.read(参数)即可将数据读出。
原理十分简单,但最近在利用串口处理SM-42无线传输时,数据总是一段一段的传到B,并不能在comm_DataReceived方法中单纯使用read方法将数据接收完全。
我知道用缓存机制,但由于经验少(正在实习),到网上找了找大牛们的方法,并结合自己的理解,发现有两种方法可以处理。
方法一:comm_DataReceived(Comm控件的数据接收方法,当有数据来临时会触发)会创建一个线程(悲哀,因为之前不知道它另辟线程,所以自己编写了一个线程处理函数),因此当串口在等待数据时,不影响主窗体或主线程的操作。
所以当数据到来时,可以通过Thread.Sleep(100)让接收函数休息100毫秒,这100毫秒做什么用呢?就是让所有的数据都到达B时再读取,这样就逃避了分批到达的问题。
很明显,这是在糊弄。
因为万一100毫秒都不够呢?所以,方法二更合适。
代码1private void comm_DataReceived(object sender, EventArgs e)2{3 Thread.Sleep(100); //等待100毫秒4int nReviceBytesNum =comm.BytesToRead; ///收到的字节数。
5byte[] ReadBuf = new byte[nReviceBytesNum]; ///定义接收字节数组6 comm.Read(ReadBuf, 0, nReviceBytesNum); ///接收数据7}方法二:使用缓存机制完成。
首先通过定义一个成员变量List<byte> buffer = newList<byte>(4096);用来存放所有的数据,在接收函数里,通过buffer.AddRange()方法不断地将接收到的数据加入到buffer中,并同时对buffer中的数据进行检验,如果达到一定的长度并且校验结果正确(校验方法在发送方和接收方一致),再进行处理。
STM32L051C8T6HALDMAIDLE串口不定长接收遇到的问题
STM32L051C8T6HALDMAIDLE串⼝不定长接收遇到的问题DMA中断,串⼝不定长接收,⽆法连续发送第⼆个printf写2个printf只能发送第⼀个,第⼆个需要延时5S左右才能发送。
⽆法把串⼝状态置为READY初始化的时候 Main.c要使能串⼝中断 DMA``HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);HAL_UART_Receive_DMA(&huart1, artDMA_rxBuf, USART1_MAX_RECV_LEN);__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);``HAL 串⼝库.c添加串⼝发送完毕状态/**@brief DMA UART transmit process complete callback.@param hdma DMA handle.@retval None*/static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma){UART_HandleTypeDef *huart = (UART_HandleTypeDef *)(hdma->Parent);/* DMA Normal mode */if (HAL_IS_BIT_CLR(hdma->Instance->CCR, DMA_CCR_CIRC)){huart->TxXferCount = 0U;/* Disable the DMA transfer for transmit request by resetting the DMAT bitin the UART CR3 register */CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);/* Enable the UART Transmit Complete Interrupt */SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);/* Check if a transmit process is ongoing or not */if(huart->gState == HAL_UART_STATE_BUSY_TX_RX){huart->gState = HAL_UART_STATE_BUSY_RX;}else{huart->gState = HAL_UART_STATE_READY;}}/* DMA Circular mode */else{if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered Tx complete callback*/huart->TxCpltCallback(huart);else/*Call legacy weak Tx complete callback*/HAL_UART_TxCpltCallback(huart);endif /* USE_HAL_UART_REGISTER_CALLBACKS */}}。
STM32串口教程(DMA方式)
在使用STM32的串口接收数据的时候,我们常常会使用接收中断的方式来接收数据,常用的是RXNE。
这里分享另一种接收数据的方式——IDLE中断(PS:本文的例子运行在STM32F103ZET6上)。
一、IDLE中断什么时候发生?
IDLE就是串口收到一帧数据后,发生的中断。
什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。
二、RXNE中断和IDLE中断的区别?
当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。
比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
三、IDLE中断如何配置?
IDLE中断由USART_CR1 寄存器进行配置:
对于STM32F103ZET6来说,配置USART_CR1寄存器bit5为1则打开RXNE 中断,配置USART_CR1寄存器bit4为1则打开IDLE中断。
这是状态寄存器,当串口接收到一个字节数据时,bit5就会自动变成1,当接收完一帧数据后,bit4就会变成1.需要注意的是,在中断函数里面,需要把对应的位清0,否则会影响下一次数据的接收。
对于RXNE中断,对USART_DR的读操作可以将该位清零:
对于IDLE中断,由软件序列清除该位(先读USART_SR,然后读USART_DR):
四、USART+DMA+IDLE接收不定长数据例程
1、USART初始化
2、中断服务函数
3、主函数
4、运行结果。
Stm32使用串口空闲中断,基于队列来接收不定长、不定时数据
Stm32使用串口空闲中断,基于队列来接收不定长、不定时数据串口持续地接收不定长、不定时的数据,把每一帧数据缓存下来且灵活地利用内存空间,下面提供一种方式供参考。
原理是利用串口空闲中断和DMA,每当对方发来一帧完整的数据后,串口接收开始空闲,触发中断,在中断处理中新建一个接收队列节点,把DMA缓存的数据copy到接收队列里。
当需要的时候就从接收队列里提出数据。
定期清理队列防止堆空间溢出。
话不多说,上代码。
定义数据结构:/*USART接收队列*/typedef struct _USART_REC_Queue{u16 index; //序号char *buf; //链接的字符串struct _USART_REC_Queue* next; //链接到下一个节点}USART_REC_Queue;声明全局变量:#define USART3_REC_len 320 //单次最大接收数extern u8 USART3_REC_buf[USART3_REC_len]; //用于DMA的临时数据中转extern u16 USART3_REC_counter; //接收计数器extern USART_REC_Queue* USART3_REC_Queue_head; //接收队列固定头节点extern USART_REC_Queue* USART3_REC_Queue_tail; //始终指向最后一个节点准备阶段:在启动汇编文件里,把堆空间改大,防止接收一点点数据就内存溢出。
Heap_Size EQU 0x00004000 //默认200字节,改大实例化全局变量:u8 USART3_REC_buf[320] = {0};u16 USART3_REC_counter = 0;USART_REC_Queue* USART3_REC_Queue_head = NULL;USART_REC_Queue* USART3_REC_Queue_tail = NULL;初始化各个硬件,使能了串口接收空闲中断,串口接收DMA,为接收队列头节点分配内存空间:void USART3_Init(u32 BaudRate){//初始化参数结构体GPIO_InitTypeDef GPIO_InitStruct; //IOUSART_InitTypeDef USART_InitStruct; //串口NVIC_InitTypeDef NVIC_InitStruct; //中断控制DMA_InitTypeDef DMA_InitStruct; //DMA/*全局指针初始化*/USART3_REC_Queue_head = USART_REC_Queue_Creat(); //构建串口3接收队列头节点USART3_REC_Queue_tail = USART3_REC_Queue_head; //构建串口3接收队列尾节点//RCC使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //IO时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //串口3时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA时钟//PB11 USART1_TXDGPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);//PB10 USART1_RXDGPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStruct);//内嵌向量中断控制器初始化NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级1NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;//子优先级1NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能IRQ 通道NVIC_Init(&NVIC_InitStruct);//USART初始化USART_ART_BaudRate = BaudRate;//波特率一般9600USART_ART_WordLength = USART_WordLength_8b;//字节数据格式8位USART_ART_StopBits = USART_StopBits_1;//一个停止位USART_ART_Parity = USART_Parity_No;//无奇偶字节校验USART_ART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式USART_Init(USART3, &USART_InitStruct);//初始化USART//USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//使能接收中断USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);//使能总线空闲中断USART_Cmd(USART3, ENABLE);//使能串口DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&USART3->DR); //读取哪一个寄存器DMA_InitStruct.DMA_MemoryBaseAddr = (u32)(&USART3_REC_buf); //读取到的数据的存放地址DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址DMA_InitStruct.DMA_BufferSize = USART3_REC_len;//数据存放区大小DMA_InitStruct.DMA_PeripheralInc =DMA_PeripheralInc_Disable; //外设寄存器地址是否偏移DMA_InitStruct.DMA_MemoryInc =DMA_MemoryInc_Enable; //数据存放地址是否偏移DMA_InitStruct.DMA_PeripheralDataSize =DMA_MemoryDataSize_Byte; //外设数据宽度8位DMA_InitStruct.DMA_MemoryDataSize =DMA_MemoryDataSize_Byte; //定义存储器数据宽度8位DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //正常操作模式DMA_InitStruct.DMA_Priority = DMA_Priority_High;//通道优先级DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //是否开启存储器到存储器模式DMA_Init(DMA1_Channel3, &DMA_InitStruct); //写入设置到DMA1通道DMA_Cmd(DMA1_Channel3, ENABLE); //使能DMA1通道USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); //注意不要忘了使能串口的DMA功能}串口中断处理(核心):void USART3_IRQHandler(void){if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET){char *buf_new; //新字符串USART_REC_Queue* queue_new; //新队列节点u16 len;USART3->DR; //读取数据。
串口发送和接收数据的一般方法
串口发送和接收数据的一般方法串口通信是一种用于在计算机或嵌入式系统之间传输数据的常用通信方式。
它使用串行连接,并遵循一定的通信协议。
在串口通信中,通常涉及到发送和接收数据的步骤。
下面是串口发送和接收数据的一般方法的详细解释。
1.打开串口:在发送和接收数据之前,需要首先打开串口连接。
打开串口可以通过相应的串口库函数实现。
常用的串口库函数有SerialPort in C/C++和pyserial in Python。
这些库函数提供了用于打开和控制串口的功能。
2.配置串口参数:打开串口后,需要配置一些串口参数,例如波特率、数据位、停止位和校验位等。
这些参数的配置通常由串口库函数提供的设置函数完成。
根据实际需求,可以选择不同的参数配置。
3.发送数据:发送数据是通过调用串口库函数提供的发送函数实现的。
发送函数通常需要传入一个数据缓冲区和要发送的数据长度作为参数。
在发送数据之前,需要将要发送的数据存储到数据缓冲区中。
发送函数会将数据从缓冲区发送到串口。
4.接收数据:接收数据是通过调用串口库函数提供的接收函数实现的。
接收函数通常需要传入一个数据缓冲区和要接收的数据长度作为参数。
在接收数据之前,需要定义一个足够大的缓冲区来存储接收到的数据。
接收函数会将数据从串口读取并存储到缓冲区中。
5.数据处理:接收到的数据可以进行进一步的处理。
例如,可以将数据解析为具体的信息,或者根据接收到的数据执行特定的操作。
数据处理的方法取决于应用需求。
6.关闭串口:在数据的发送和接收任务完成之后,应该关闭串口连接。
关闭串口可以通过调用串口库函数提供的关闭函数实现。
关闭串口将释放相关的资源。
需要注意的是,在进行串口通信时,要确保发送和接收端的串口参数配置一致。
否则,可能导致通信失败或数据解析错误。
上述是关于串口发送和接收数据的一般方法的基本介绍。
具体的实现方法和细节会因为不同的编程语言和串口库函数而有所差异。
因此,在实际应用中可以根据具体情况选择适合的编程语言和库函数,以实现串口通信。
stm32 HAL库 串口DMA接收不定长度数据及粘包处理
串口接收不定长度数据及数据粘包解析的实现1、如何让串口接收不定长度数据想让Stm32 串口接收不定长度数据,这就需要我们开启串口空闲中断(IDLE)方式,所谓串口空闲中断指的是stm32的数据总线在接收数据的过程中,如果总线在接收一个字节所需要的时间内没有再接收到数据,单片机就会判定此时数据已经接收完成了,这时单片机会自动触发空闲中断IDLE标志位,引发空闲中断,我们只需要进入中断取数据就可以了。
使用IDLE空闲中断我们就可以用串口接收任意长度的数据了。
2、串口接收不定长度数据的实现思路我们实现串口接收不定长度数据的思路是:首先我们要定义一个接收数据的缓冲区,一般用数组接收数据,在串口初始化时要开启串口的空闲中断和接收中断。
然后在有中断产生时,我们需要在串口中断函数里判断是空闲中断还是正常接收一个字节数据引起的接收中断,如果是正常接收字节的中断,那么我们需要把接收到的这个字节数据存放到缓冲数组中,如果是IDLE空闲中断,表示串口数据已经接收完成了,我们需要在IDEL中断处理函数中设置一个数据接收完成标志位表示已经完整的接收到一帧数据了,如:RecFlag=1;3、数据粘包解析的实现思路数据粘包是多个数据包发送时由于线路延时,或者发送端发送多个数据包的时间延时很小,导致几个数据包几乎同时到达接收端(数据包到达接收端的时间间隔小于一个字节时间),这样单片机接收数据时就会将这几个数据包当做一帧数据来接收存放。
那么我们如何将这几个数据包合成的一帧数据拆解成几个数据包呢?其实,实现的方法也很简单,这就需要我们在发送端和接收端的数据格式上做一个统一的约定,约定了统一的数据发送格式在发送数据时就严格按照这个格式来发送。
一般来说约定的格式我们要明确规定数据头和数据长度。
然后我们再根据定义的数据头是什么数据,在这一帧数据中逐个去判断当前数据是不是数据头,如果是就说明这个是一个小数据包的开始位置,在根据数据长度就可以解析出一个数据包了。
VB串口数据接收方式
1、在OnComm 事件中接收数据:这种方式能充分MSCOMM控件的特性。
OnComm 事件还可以检查和处理通讯错误;可以通过检查CommEvent 属性的值来查询事件和错误;对于不定长数据以及对数据进行处理比较复杂的情况,此法不是很方便。
Private Sub MSComm_OnComm ()Select Case mEvent' 错误Case comEventBreak ' 收到Break。
Case comEventCDTO ' CD (RLSD) 超时。
Case comEventCTSTO ' CTS Timeout。
Case comEventDSRTO ' DSR Timeout。
Case comEventFrame ' Framing ErrorCase comEventOverrun '数据丢失。
Case comEventRxOver'接收缓冲区溢出。
Case comEventRxParity' Parity 错误。
Case comEventTxFull '传输缓冲区已满。
Case comEventDCB '获取DCB] 时意外错误' 事件Case comEvCD ' CD 线状态变化。
Case comEvCTS ' CTS 线状态变化。
Case comEvDSR ' DSR 线状态变化。
Case comEvRing ' Ring Indicator 变化。
Case comEvReceive ' 收到RThreshold # of chars.Case comEvSend ' 传输缓冲区有Sthreshold 个字符'Case comEvEof ' 输入数据流中发现EOF 字符End SelectEnd Sub2.轮循法采集数据:A、定时器轮循法对于数据包方式收发数据以及不需即时响应情况,用轮循法更好些。
串口接收字符串技巧
串口接收字符串技巧串口接收字符串,就像是在一个神秘的信息通道口守着,等着一串串神秘的字符像小蚂蚁排队一样一个接一个地进来。
这可不是个简单事儿,里面的门道可多啦。
咱先说说这个串口的设置。
串口就像一个小管家,你得告诉它一些规则,它才能好好地接收字符串。
比如说波特率,这就好比是小蚂蚁们走路的速度。
如果波特率设置错了,那就像小蚂蚁们不是按照正常速度走,有的走得太快,有的走得太慢,那接收到的字符串肯定是乱七八糟的。
我就曾经遇到过这个问题,当时怎么都接收不对字符串,就像在跟一个外星人对话,完全不明白对方在说啥。
后来才发现是波特率这个小调皮鬼捣的乱。
再说说数据位和停止位。
这两个东西啊,就像是小蚂蚁队伍的编排规则。
数据位决定了每次过来的字符的长度,停止位就像是队伍末尾的小旗帜,表示这个字符已经传输完了。
要是这两个没设置好,就像小蚂蚁队伍没有排整齐,东倒西歪的,接收的字符串也会出错。
这就好比你在数一群排得歪歪扭扭的小蚂蚁,数着数着就乱套了。
接收缓冲区也很重要。
这个缓冲区就像是一个小仓库,用来暂时存放接收到的字符串。
要是这个小仓库太小了,就像一个小盒子装不了太多小蚂蚁,有些字符串就可能会丢失。
我有次做一个小项目,没注意这个缓冲区的大小,结果就像一个粗心的孩子丢了自己心爱的玩具一样,好多字符串都没接收到,可把我急坏了。
在接收字符串的时候,还得注意字符的编码格式。
这就像是小蚂蚁们身上的标记。
不同的编码格式下,同样的字符看起来可能是不一样的。
如果不搞清楚这个,就像认错了小蚂蚁身上的标记,把原本是“朋友”的小蚂蚁当成了“敌人”。
比如说ASCII码和UTF - 8编码,它们就像两种不同的语言,如果搞混了,接收到的字符串解读出来可能就是一堆乱码,就像看天书一样,完全不知道是什么意思。
那怎么知道接收到的字符串是完整的呢?这就像等小蚂蚁们全部排好队一样。
有时候,我们可以通过特定的结束标志来判断。
就像小蚂蚁队伍最后有一个特殊的小蚂蚁表示队伍结束了。
串口通讯方法的三种实现
串口基本信息用一台电脑实验串口自发自收,实验前要将串口(以9针为例)的发送引脚(2脚)和接受引脚(3脚)短接。
三线连接:适用于计算机之间尤其是PC机和单片机之间的数据通信。
其连接信号对为(TxD,RxD)、(RxD,TxD)、(SG,SG)。
即发送数据TxD端和接受数据RxD端交叉连接,信号地SG对应连接。
七线交叉连接:适用于同型号的计算机之间的连接,如PC机间的数据通信。
其连接信号对为:(TxD,RxD)、(RxD,TxD)、(SG,SG)、(RTS,CTS)、(CTS,RTS)、(DSR.DTR)、(DTR,DSR)。
其中,TxD、RxD、SG与前面信号的含义相同,RTS为请求发送,CTS为准许发送,DSR为数据装置准备好,DTR为数据终端准备好。
在本地连接的微机系统中,RTS、CTS、DTR、DSR用作硬件联络控制信号。
目前使用的串口连接线有DB9和DB25两种连接器,用户可以国家使用的具体机器选择相应的连接器。
一个串口通讯类在/network/serialport.shtml。
PC机的RS-232接口的电平标准是-12V标示“1”,和+12V表示“0”,有些单片机的信号电平时TTL 型,即大于2.4v表示“1”,小于0.5v表示“0”,因此采用RS-232总线进行异步通信是,发送端和接受端要有一个电平转换接口。
串口通讯方法的三种实现串口是计算机上一种非常通用的设备通信协议。
大多数计算机包含两个基于RS232的串口。
串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS一232口。
同时,串口通信协议也可以用于获取远程采集设备的数据。
串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线、控制线等,按位进行传输数据的一种通讯方式。
串口通信方便易行,应用广泛。
在Windows应用程序的开发中,我们常常需要面临与外围数据源设备通信的问题。
串口读取数据的方法
串口读取数据的方法1.打开串口:首先需要打开串口,通过设备文件或串口号来指定要打开的串口。
```c++#include <stdio.h>#include <fcntl.h>#include <termios.h>int openSerialPort(const char* portName)int fd = open(portName, O_RDWR , O_NOCTTY);if (fd < 0)printf("Failed to open serial port\n");return -1;}//配置串口参数struct termios options;tcgetattr(fd, &options);cfsetispeed(&options, B9600);cfsetospeed(&options, B9600);options.c_cflag ,= (CLOCAL , CREAD);tcsetattr(fd, TCSANOW, &options);return fd;}```2.读取串口数据:打开串口之后,可以通过读取文件描述符来读取串口数据。
```c++int readSerialData(int fd, unsigned char* buffer, int bufferSize)int bytesRead = read(fd, buffer, bufferSize);if (bytesRead < 0)printf("Failed to read serial data\n");}return bytesRead;}```3.解析串口数据:读取到的数据可能是原始的字节流,需要根据具体的协议和数据格式进行解析。
```c++void parseData(unsigned char* buffer, int bufferSize)//解析数据的逻辑}```4.循环读取数据:可以使用循环来不断读取串口数据,并进行解析和处理。
STM32 HAL库USART中断接收不定长数据——空闲中断法
STM32 HAL库USART中断接收不定长数据——空闲中断法STM32cubeMX软件配置好串口中断,导出工程并打开,定义串口接收缓冲区和接收长度的全局变量:uint8_t RX_data[1000];uint16_t RX_len;在main函数的初始化中开启IDLE中断并首次打开中断接收函数://开启IDLE中断__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//中断接收函数,这里设置最大接收长度为1000HAL_UART_Receive_IT(&huart1, (uint8_t*)RX_data, 1000);添加IDLE中断处理函数:void UsartReceive_IDLE(UART_HandleTypeDef *huart){__HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_IDLEF); //清除中断RX_len = 1000 - huart1.RxXferCount; //计算接收数据长度HAL_UART_AbortReceive_IT(huart); //终止接收// 用户数据处理,如将接收到的内容重新发送// HAL_UART_Transmit_IT(&huart1, (uint8_t*)RX_data, RX_len);HAL_UART_Receive_IT(&huart1, (uint8_t*)RX_data, 1000); //接收完数据后再次打开中断接收函数}打开stm32fXxx_it.c文件(X视具体芯片系列),在USART1_IRQHandler()函数中添加IDLE中断服务:if(__HAL_UART_GET_IT(&huart1,UART_IT_IDLE) != RESET) //判断是否为IDLE中断{UsartReceive_IDLE(&huart1); //调用IDLE中断处理函数}。
STM32基于HAL库串口空闲中断接收不定长数据
STM32基于HAL库串⼝空闲中断接收不定长数据⼀、前⾔最近在使⽤STM32的HAL库的时候,发现竟然没有集成IDLE中断处理,本⾝写的HAL库处理逻辑就挺繁琐,效率⼜不⾼,还缺胳膊少腿的。
平时项⽬中的串⼝接收数据都是不定长的,⽽IDLE中断在这⼀块作⽤是⾮常⼤的,可以⼤⼤简化数据接收过程的判断。
本⽂将介绍基于HAL库IDLE中断接收不定长数据。
⼆、代码实现⾸先串⼝的初始化⼯作,在初始化过程中,我们需要开启两个中断,⼀个是UART_IT_RXNE接收中断,此中断是没接收到⼀个字节的数据接收产⽣⼀次中断,另⼀个是UART_IT_IDLE空闲中断,也就是我们今天的主⾓。
每帧数据发送完成就会有空闲时期,⼀帧数据接收完成就会产⽣空闲中断。
这⾥我们不使⽤ HAL_UART_Receive_IT()函数来初始化了,因为我们不⽤HAL库的那⼀套,直接进⾏中断开启。
void USART1_Init(uint32_t Bound){UART1_HandleStructure.Instance = USART1;UART1_HandleStructure.Init.BaudRate = Bound;UART1_HandleStructure.Init.WordLength = UART_WORDLENGTH_8B;UART1_HandleStructure.Init.StopBits = UART_STOPBITS_1;UART1_HandleStructure.Init.Parity = UART_PARITY_NONE;UART1_HandleStructure.Init.Mode = UART_MODE_TX_RX;UART1_HandleStructure.Init.HwFlowCtl = UART_HWCONTROL_NONE;HAL_UART_Init(&UART1_HandleStructure);HAL_NVIC_EnableIRQ(USART1_IRQn);HAL_NVIC_SetPriority(USART1_IRQn,3,3);__HAL_UART_ENABLE_IT(&UART1_HandleStructure,UART_IT_RXNE);//接收中断__HAL_UART_ENABLE_IT(&UART1_HandleStructure,UART_IT_IDLE);//空闲中断}下来是写我们的中断服务函数,我们直接在USART1_IRQHandler()⾥写我们的处理逻辑,不需要再调⽤HAL_UART_IRQHandler()函数。
串口设备数据的接收和处理
串口设备数据的接收和处理介绍串口是一种常用的数据通信接口,用于在电脑和外部设备之间进行数据传输。
串口设备通常用于与各种外部设备进行通信,如传感器、控制器、打印机等。
本文将介绍如何在计算机上通过串口接收和处理数据。
硬件准备在进行串口数据接收和处理之前,我们需要准备一些硬件设备:1.计算机:计算机上需要有串口接口,通常是RS-232或USB串口。
2.串口线:用于连接计算机和外部设备,传输串口数据。
3.外部设备:如传感器、控制器等,通过串口与计算机进行通信。
软件设置在开始接收和处理串口数据之前,我们需要进行一些软件设置。
1.安装串口驱动程序:如果你的计算机上没有安装串口驱动程序,你需要先安装相应的驱动程序。
可以从设备制造商的网站上下载并安装最新的驱动程序。
2.串口工具:为了方便调试和测试串口通信,我们可以使用串口调试工具。
常用的串口调试工具有TeraTerm、PuTTY等。
数据接收接收串口数据是一个基本的操作,可以通过以下步骤进行:1.打开串口:通过串口调试工具或编程语言提供的串口API,打开与外部设备连接的串口。
2.配置串口参数:设置串口的波特率、数据位、校验位、停止位等参数,确保与外部设备的设置一致。
3.循环接收数据:通过串口API提供的函数,循环接收串口数据,并将数据保存到缓冲区中。
4.解析数据:根据数据格式,解析接收到的数据。
可以根据数据包长度、特定字符、起始字符等方式进行解析。
下面是一个示例代码,用于示范数据接收的过程(使用Python语言):import serial# 打开串口ser = serial.Serial('COM1', 9600)while True:# 接收一行数据(以\\r\结尾)data = ser.readline()# 处理接收到的数据# TODO: 解析数据# 关闭串口ser.close()数据处理一旦从串口接收到数据,我们就可以进行进一步的数据处理。
串口数据接收防丢包的方法
串口数据接收防丢包的方法
串口数据接收防丢包的方法主要包括以下几种:
1. 缓冲区处理:在接收数据时,使用一个缓冲区来存储接收到的数据。
当新的数据到达时,将其添加到缓冲区的尾部,并检查缓冲区中的数据是否完整。
如果数据不完整,则等待更多数据到达。
当数据完整时,处理缓冲区中的数据。
2. 超时检测:设定一个超时时间,如果在超时时间内没有接收到新的数据,则认为数据丢失。
在这种情况下,可以选择重发丢失的数据,或者跳过丢失的数据继续处理后续的数据。
3. 校验和:在发送数据时,计算数据的校验和,并将校验和一起发送。
在接收数据时,重新计算接收到的数据的校验和,并与发送的校验和进行比较。
如果校验和不匹配,则认为数据丢失或损坏。
4. 流控制:通过流控制机制,控制数据的发送和接收速度。
例如,当接收速度较慢时,可以暂停发送数据,等待接收速度加快后再继续发送。
5. 重传机制:如果检测到数据丢失,可以重新发送丢失的数据。
为了防止无限重传导致系统瘫痪,可以设定重传次数限制和重传时间限制。
6. 数据分包与合包:对于大块数据,可以采用分包的方式进行发送。
在接收端,将接收到的分包数据进行合包处理,还原成完整的数据。
7. 提高硬件质量:选择高质量的串口硬件,可以提高数据的传输稳定性和可靠性,从而减少数据丢失的可能性。
以上方法可以根据实际需求进行选择和组合使用,以达到最佳的防丢包效果。
STM32HAL库使用中断实现串口接收不定长数据
STM32HAL库使用中断实现串口接收不定长数据要在STM32HAL库中实现串口接收不定长数据超过1200字,您可以使用中断来接收。
以下是一个简单的示例代码,演示了如何设置串口接收中断,并处理超过1200个字的数据:```c#include "stm32xxxx.h" // 根据您的STM32型号进行包含适当的头文件#define RX_BUFFER_SIZE 2000 // 定义接收缓冲区的大小//定义接收缓冲区和相关变量uint8_t rxBuffer[RX_BUFFER_SIZE];volatile uint16_t rxIndex = 0;volatile uint8_t rxDataAvailable = 0;//初始化串口void UART_Ini//串口GPIO引脚配置//串口时钟配置//串口配置//使能接收中断__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);//中断处理函数void USART1_IRQHandler(void)if ((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)) rxBuffer[rxIndex++] = huart1.Instance->DR;if (rxIndex >= RX_BUFFER_SIZE)//接收超过1200字,进行处理//在这里添加您的处理代码//重置接收缓冲区和相关变量rxIndex = 0;rxDataAvailable = 0;} else//未达到1200字,继续接收rxDataAvailable = 1;}}int main(void)//初始化硬件//...//初始化串口UART_Init(;while (1)if (rxDataAvailable)//执行接收数据处理//在这里添加您的处理代码//重置接收缓冲区和相关变量rxIndex = 0;rxDataAvailable = 0;}//执行其他任务//...}```在上述代码中,我们首先定义了一个接收缓冲区 `rxBuffer`,并使用 `rxIndex` 变量来跟踪接收到的字节数。
VB三种读取串口数据的方式
3三种读取串口数据的方式目前通用的串口通讯的软件实现方式有3种,本文都进行详细的介绍,它们各有自身的优缺点,读者在编程时可根据具体的情况选择合适的方式。
3.1利用Mscomm控件VB提供的这个通信控件“隐藏”了大部分串口通信的底层运行过程,程序员只需编写少量的代码就可以完成软件的开发过程。
在通信数据量不大,通信要求不是很高的情况下建议采取此方式。
利用Mscomm控件实现通信最需要掌握的就是它的几个主要属性,下面选取其中重要的进行介绍,其余的可以参考相关资料。
[3](1)Settings属性:以字符串的形式设置并返回波特率、奇偶校验位、数据位、停止位。
这个属性很重要,针对不同的终端设备需要根据设备的具体情况进行调整(比如日本的设备不同于美国的设备,通常会采用奇校验)。
(2)InputMode属性:设置接收数据的类型,0为文本格式,1为二进制格式。
(3)Input属性:读取并删除接收缓冲区中的数据流。
(4)Output属性:向发送缓冲区传送一数据流。
(5)Rthreshold属性:该属性为一阀值,它确定当接收缓冲区内的字节个数达到或超过该值后就产生代码为ComEvReceive的OnComm事件。
(6)Handshaking属性:设置和返回握手协议,即计算机内部CPU与串口之间的通讯协议,保证在缓冲区过载时数据不会丢失。
这个属性在保证数据传输的正确性方面有很大的作用,共有四个选项,分别表示:①无流控制;②软件流控制;③硬件流控制;④软硬件流控制。
采用硬件流控时,要求串口之间和电缆支持硬件握手,在自己制作串口通信线时,有关硬件握手的线RTS、CTS、DSR、DTR要连接正确。
在正确设置这些属性的基础上,剩下的就是打开串口,通过串口发送及接受数据了。
本文后续章节利用一个实例详细讲解了这些属性的设置及具体代码。
3.2直接调用Win32API通信函数直接调用Windows API函数,可以清楚地理解串口通信的机制,根据需要灵活地配置串口的各种参数和属性,而且直接调用低层API函数,通信效率比较高,但付出的代价就是程序较复杂,编程周期长,适合于大型通信程序及通讯质量要求较高的场合。
STM32HAL库使用中断实现串口接收不定长数据
STM32HAL库使⽤中断实现串⼝接收不定长数据 以前⽤DMA实现接收不定长数据,DMA的⽅法接收串⼝助⼿的数据,全部没问题,不过如果接收模块返回的数据,⽽这些数据如果包含回车换⾏的话就会停⽌接收,例如接收:AT\r\nOK\r\n,就只能接收到AT\r,导致没有接收完成,具体原因还没搞懂,有了解的,希望可以告知⼀下,DMA不定长接收⽅法传输门:。
好了,不多说了,现在进⼊正⽂。
⾸先建⽴⼀个STM32Cumebx的⼯程,打开串⼝中断,完成配置,具体的配置流程就不细说了,没什么难度就只是打开串⼝跟中断⽽已。
⽣成⼯程代码后,先定义好⼀些变量://串⼝4中断接收定义#define MAX_RECV_LEN 1024 //设定可以接收的最⼤字节uint8_t msg_buff[MAX_RECV_LEN] = {0}; //接收缓存区uint8_t * msg = msg_buff; //定义⼀个指针指向接收缓存区int flag = 0; //接收完成标志int len_u4=0; //数据长度记录 接着重写串⼝接收回调函数/*重写串⼝接收回调函数*/void HAL_UART_RxCpltCallback(UART_HandleTypeDef*UartHandle){uint8_t ret = HAL_OK;msg++;len_u4++;//数据长度计数if( msg == msg_buff + MAX_RECV_LEN){msg = msg_buff;}do{ret = HAL_UART_Receive_IT(UartHandle,(uint8_t *)msg,1);}while(ret != HAL_OK);if(*(msg-1) == '\n') //接收以\n为结尾字符,则表⽰接收完成{flag = 1;}} 最后在main函数⾥⾯编写接收后的逻辑,注意要在while(1){ }前打开串⼝接收中断int main(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration----------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init(); MX_DMA_Init(); MX_USART3_UART_Init();MX_UART4_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE *///⾃⼰添加代码部分:while前打开串⼝中断接收HAL_UART_Receive_IT(&huart4, (uint8_t *)msg, 1); //开启第⼀次中断while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///======⾃⼰添加代码部分=========if (flag == 1){printf("msg_buff = %s ;len = %d\r\n",msg_buff,len_u4); HAL_Delay(100); //加延时,保证接收到数据过长的时候,等待数据存⼊缓存区发送HAL_UART_Transmit(&huart3,msg_buff, len_u4,100); //将串⼝4接收到的数据通过串⼝3传出memset(msg_buff, 0, sizeof(msg_buff)); //清空缓存区// 指向接收缓存的头部msg = msg_buff;(&huart4)->pRxBuffPtr = msg;flag = 0;len_u4=0;//每次数据长度清0}HAL_Delay(10);}//==============================/* USER CODE END 3 */} 运⾏结果如下,效果正确 谈谈串⼝RS232跟RS485:这两个串⼝除了逻辑电平不同外,还有传输距离也不同,如果对速度要求不⾼,传输距离要⽐较远的就⽤RS485⽐较好,虽然RS485是个半双⼯,但是抑制共模⼲扰能⼒⽐较强,不过这些只是对于硬件层⾯的,对于软件层⾯来说他们的本质都是串⼝,在STM32Cubemx中,都是只是配置为串⼝,按照串⼝的编程来处理即可。
VB串口数据接收方式
1、在OnComm 事件中接收数据:这种方式能充分MSCOMM控件的特性。
OnComm 事件还可以检查和处理通讯错误;可以通过检查CommEvent 属性的值来查询事件和错误;对于不定长数据以及对数据进行处理比较复杂的情况,此法不是很方便。
Private Sub MSComm_OnComm ()Select Case mEvent' 错误Case comEventBreak ' 收到Break。
Case comEventCDTO ' CD (RLSD) 超时。
Case comEventCTSTO ' CTS Timeout。
Case comEventDSRTO ' DSR Timeout。
Case comEventFrame ' Framing ErrorCase comEventOverrun '数据丢失。
Case comEventRxOver'接收缓冲区溢出。
Case comEventRxParity' Parity 错误。
Case comEventTxFull '传输缓冲区已满。
Case comEventDCB '获取DCB] 时意外错误' 事件Case comEvCD ' CD 线状态变化。
Case comEvCTS ' CTS 线状态变化。
Case comEvDSR ' DSR 线状态变化。
Case comEvRing ' Ring Indicator 变化。
Case comEvReceive ' 收到RThreshold # of chars.Case comEvSend ' 传输缓冲区有Sthreshold 个字符'Case comEvEof ' 输入数据流中发现EOF 字符End SelectEnd Sub2.轮循法采集数据:A、定时器轮循法对于数据包方式收发数据以及不需即时响应情况,用轮循法更好些。
stm32HAL库串口DMA接收不定长度数据及粘包处理
stm32HAL库串⼝DMA接收不定长度数据及粘包处理串⼝接收不定长度数据及数据粘包解析的实现1、如何让串⼝接收不定长度数据想让Stm32 串⼝接收不定长度数据,这就需要我们开启串⼝空闲中断(IDLE)⽅式,所谓串⼝空闲中断指的是stm32的数据总线在接收数据的过程中,如果总线在接收⼀个字节所需要的时间内没有再接收到数据,单⽚机就会判定此时数据已经接收完成了,这时单⽚机会⾃动触发空闲中断IDLE标志位,引发空闲中断,我们只需要进⼊中断取数据就可以了。
使⽤IDLE空闲中断我们就可以⽤串⼝接收任意长度的数据了。
2、串⼝接收不定长度数据的实现思路我们实现串⼝接收不定长度数据的思路是:⾸先我们要定义⼀个接收数据的缓冲区,⼀般⽤数组接收数据,在串⼝初始化时要开启串⼝的空闲中断和接收中断。
然后在有中断产⽣时,我们需要在串⼝中断函数⾥判断是空闲中断还是正常接收⼀个字节数据引起的接收中断,如果是正常接收字节的中断,那么我们需要把接收到的这个字节数据存放到缓冲数组中,如果是IDLE空闲中断,表⽰串⼝数据已经接收完成了,我们需要在IDEL中断处理函数中设置⼀个数据接收完成标志位表⽰已经完整的接收到⼀帧数据了,如:RecFlag=1;3、数据粘包解析的实现思路数据粘包是多个数据包发送时由于线路延时,或者发送端发送多个数据包的时间延时很⼩,导致⼏个数据包⼏乎同时到达接收端(数据包到达接收端的时间间隔⼩于⼀个字节时间),这样单⽚机接收数据时就会将这⼏个数据包当做⼀帧数据来接收存放。
那么我们如何将这⼏个数据包合成的⼀帧数据拆解成⼏个数据包呢?其实,实现的⽅法也很简单,这就需要我们在发送端和接收端的数据格式上做⼀个统⼀的约定,约定了统⼀的数据发送格式在发送数据时就严格按照这个格式来发送。
⼀般来说约定的格式我们要明确规定数据头和数据长度。
然后我们再根据定义的数据头是什么数据,在这⼀帧数据中逐个去判断当前数据是不是数据头,如果是就说明这个是⼀个⼩数据包的开始位置,在根据数据长度就可以解析出⼀个数据包了。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
三种串口接受不定长数据方法详解
方法1:串口接受数据,定时器来判断超时是否接受数据完成。
方法2:DMA接受+IDLE中断
实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。
那么初始化完成之后,当外部给单片机发送数据的时候,假设这帧数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。
当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理。
应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。
关键代码分析:
void uart_init(u32 bound);
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);
#endif
usart.C
//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENAB LE); //使能USART1,GPIOA时钟。