三种串口接受不定长数据方法详解
串口通信中接收数据时延迟处理与缓存处理的解决方案(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编码,它们就像两种不同的语言,如果搞混了,接收到的字符串解读出来可能就是一堆乱码,就像看天书一样,完全不知道是什么意思。
那怎么知道接收到的字符串是完整的呢?这就像等小蚂蚁们全部排好队一样。
有时候,我们可以通过特定的结束标志来判断。
就像小蚂蚁队伍最后有一个特殊的小蚂蚁表示队伍结束了。
- 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时钟。