OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 8787|回复: 21

我也说一下STM32下串口DMA的任意长度接收发送的思路

[复制链接]

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
发表于 2016-12-20 11:14:51 | 显示全部楼层 |阅读模式
1金钱
我也不算是真正的单片机开发人员,只是项目需要,单片机程序也一直在优化,优化最多的串口通信,尽可能快的传输大数据,以前直接用中断的方式,稳定,但传输稍大的数据时,需要的时间就久了,特别是需要传输扫描枪扫到的图片时,就会等待很久。后来改成了DMA,也只有DMA是最快了。好了,进入正题

1.百度,是最好的老师,可以查到很多前辈积累的经验和方法,我也是参考了他们的
2.DMA任意字符的接收,很多是使用串口的USART_IT_IDLE空闲中断,没错, 我也使用它了。
3.还有使用DMA双缓冲区,当时研究时发现双缓冲转换不是很方便,就没用了。
4.使用DMA的DMA_IT_TC中断,当接收满时就触发中断,这个我也需要用到
5.有前辈使用DMA与环形缓冲区结合,这个方法很好,不过我有点看不懂,对于手上的项目改动太大,就没继续深研
6.我使用了环型缓冲区,可以做到数据与业务分离,很方便
7.我上了RTX系统,多任务,很方便,也比较危险,所以线程同步要做好

好了,我要说下我的思路
我使用的单片机是STM32F205,RTX系统,关于串口和DMA配置忽略吧,百度都能查到

接收数据:
前面说到要用到的两个中断,串口的空闲和DMA的接收完成中断,两个相结合后就可以实现任意长度了(说是任意长度,其实接收的长度当然要小于分配的环型缓冲区)。我使用了环型缓冲区用来接收数据,这样就可以做到业务逻辑的分离了,接收的只管接收,处理的只管处理

//串口1中断处理
void USART1_IRQHandler(void)
{
    uint8_t ID = 0;                //串口序号
    uint16_t Size = 0;


    if ( USART_GetITStatus( USART1, USART_IT_IDLE ) != RESET )           //串口空闲中断 用于DMA       

    {
        USART_ClearITPendingBit( USART1, USART_IT_IDLE );  //清除空闲中断
        USART_ReceiveData( USART1);  //读DR,只有读过一次,才能真正清除标志
        USART_ClearFlag( USART1, USART_FLAG_IDLE );  //读SR其实就是清除标志
        DMA_Cmd(DMA2_Stream2, DISABLE);  //先停止DMA才行设置缓冲区大小
        
        Size = DMA_USART_RecieveBuffer_Len - DMA_GetCurrDataCounter( DMA2_Stream2 );                        //获得DMA当前收到的字节数,因为DMA中断只有接收长度满时才会触发,所以要把剩余数据读出来 DMA2 Stream2-Channel4
        if(Size > 0)  //如果有剩余数据
            FIFO_PutBuff( &USART1_FIFO_Receive, DMA_USART1_Recieve_Buff, Size );            //将收到的DMA缓冲复制进串口输入FIFO  FIFO_PutBuff()函数是环型缓冲区写入Buff的用到的,USART1_FIFO_Receive 环型缓冲区结构,  DMA_USART1_Recieve_Buff DMA接收缓冲区
        USART_DMA_Status[ID].USART_IS_IDLE = 1;  //自定义标识 串口空闲标识
        USART_DMA_Status[ID].DMA_ReceiveOK = 1;  //自定义标识 DMA接收完成标识

        DMA_SetCurrDataCounter( DMA2_Stream2, DMA_USART_RecieveBuffer_Len );   //重新设置DMA的读取缓冲区长度   DMA_USART1_Recieve_Buff[DMA_USART_RecieveBuffer_Len]
        DMA_Cmd(DMA2_Stream2, ENABLE);  //开启DMA       

    }


//DMA中断处理
void DMA2_Stream2_IRQHandler(void)
{
    uint8_t ID = 0;  //串口序号

    if( DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2) != RESET )
    {
        DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);

        //重新设置DMA坊取缓冲区长度时会触发该中断,所以当有该标志时,不处理该条中断。
       //在实际使用中,发现经常接收的数据有出入,跟踪后发现在串口中断USART_IT_IDLE重新设置DMA缓冲长度时,会触发该中断,所以加入了自定义标识USART_DMA_Status[ID].USART_IS_IDLE,为1时,说明已经空闲了,不再将数据写入环型缓冲区
        if( USART_DMA_Status[ID].USART_IS_IDLE != 1 )
        {
              FIFO_PutBuff( &USART1_FIFO_Receive, DMA_USART1_Recieve_Buff, DMA_USART_RecieveBuffer_Len );    //将DMA缓冲区数据写入环型缓冲区
              USART_DMA_Status[ID].DMA_ReceiveOK = 1;    //DMA接收完成
        }
               
        USART_DMA_Status[ID].USART_IS_IDLE = 0;    //重置串口空闲中断
       
    }

}

业务逻辑处理:
//项目在通信中,使用了定长命令头协议方式,这样能更好的处理数据,可以知道这个命令是做什么的,以及这个命令将要接收的数据的长度

typedef struct
{
       
        uint8_t        Cmd;                                                        //命令标识
        uint8_t        CmdType;                                        //命令类型
        uint32_t        DataLen;                                        //发送数据长度,用于下次发送的数据长度
        uint32_t        Param1;                                                //参数1
        uint32_t        Param2;                                                //参数2
        uint32_t        Param3;                                                //参数3
        uint32_t        Param4;                                                //参数4
        uint8_t         LRC;                                                        //校验码 除最后一位LRC码外所有字节的XOR运算
       
} TCMDHeader;                                        //串口 命令头 结构体



void Query_USART1(void)

        uint16_t BuffSize        = FIFO_GetSize( &USART1_FIFO_Receive );                        //串口1缓冲区可读字节数

        if( BuffSize > 0 )                 //如果串口1有数据
        {
                ReadSize = FIFO_ReadByte( &USART1_FIFO_Receive, (uint8_t *) &USART_Receive_CMD, sizeof( TCMDHeader ), 50 );                //读取命令头 超时50ms



                if( ReadSize != sizeof( TCMDHeader ) )                 //如果读到的大小与待读大小不一致,则退出
                {
                        return;
                }

                if( Check_LRC( (uint8_t *) &USART_Receive_CMD, ReadSize) )                        //命令头LRC校验成功
                {

                                //这里通过USART_Receive_CMD命令来区分命令的类型和处理
                               //......
                }
        }




以上就是接收的部分,我的项目中两块单片机使用串口进行大数据传输,单片机1把采集的图片通过串口发送到单片机2上,单片机2首先接收命令头,通过命令头知道下次待接收的数据大小,这样单片机1只管发数据,单片机2处理命令头后循环处理环型缓冲的数据,边接收边通过网络接口透传到上位机上,直到数据接收完成。单片机1的发送也是通过DMA方式发送,当然也是任意长度的发送。

下面我就说说DMA如何发送任意长度

发送数据:
思路是这样的:发送也有一个环型缓冲区,发送的时候把数据填入缓冲区中,直到填满,如果未开启DMA发送中断,则开启,开启后,调用函数只管往缓冲区填数据,DMA的发送完成中断会判断缓冲区是否为空,空则发送完成,不空,则继续调用DMA发送

//DMA的底层发送函数,每调用一次发送一个包 SendFIFO是发送的环型缓冲区,结构与接收的一致
//DMAy_Streamx DMA通道号
//因为函数都是做成通用性的,不同的参数使用不同的串口和DMA流
void USART_DMA_Send( TFIFO* SendFIFO, DMA_Stream_TypeDef* DMAy_Streamx )
{
       
        uint16_t BuffSize = 0;
        uint16_t ReadSize = 0;
       
        int ID = USART_Get_DMA_USARTIndex_FromStreamX( DMAy_Streamx );        //通过DMAy_Streamx获得串口序列号 0:USART1 1:USART2...
        uint8_t* Buffer = USART_Get_DMA_Buffer_FromStreamX( DMAy_Streamx );    //获得DMA的发送缓冲区  如:if( DMAStreamX == DMA2_Stream7 )        return DMA_USART1_Send_Buff;        //USART1 TX
       
        if( SendFIFO == NULL || DMAy_Streamx == NULL || ID == -1 || Buffer == NULL ) return;
       
        //DMA发送标识,如果DMA未开启发送,则开启DMA发送。
       //第一次调用发送时 USART_DMA_Status[ID].DMA_IsSending = 0, 开启DMA发送后 USART_DMA_Status[ID].DMA_IsSending = 1 ,之后的发送将不在此处理剩余的数据,而是在发送完成中断中继续发送,如果发送后再置USART_DMA_Status[ID].DMA_IsSending = 0
        if( USART_DMA_Status[ID].DMA_IsSending == 0 )
        {

                        BuffSize = FIFO_GetSize( SendFIFO );    //获得发送缓冲区大小
       
                        if(  BuffSize == 0 ) return;    //为空则退出       
       
                        if( BuffSize > DMA_USART_SendBuffer_Len ) BuffSize = DMA_USART_SendBuffer_Len;    //如果发送大小大于DMA发送缓冲,则大小取DMA发送缓冲区大小
                       
                        if( FIFO_GetBuff( SendFIFO, BuffSize, Buffer, &ReadSize) != 1 ) return;            /复制发送数据到DMA缓冲区中  DMA_USART_Send_Buff[ID]:串口数对应的DMA缓冲区

                        USART_DMA_Status[ID].DMA_IsSending = 1;    //设置发送标识为1

                        DMA_Cmd( DMAy_Streamx, DISABLE );    //停止发送
                        DMA_SetCurrDataCounter( DMAy_Streamx, ReadSize );    //设置DMA发送缓冲区的大小为待发送大小       
                        DMA_Cmd( DMAy_Streamx, ENABLE );    //开启发送
               
        }
       
}


//任意字符发送,主要是使用该函数发送,该函数主要是向发送环型缓冲区填入数据,并触发开启第一次DMA发送,之后的发送将在发送中断完成后发送
uint16_t USART_DMA_PostBuff( USART_TypeDef* USARTx, uint8_t *Buff, uint16_t BuffSize )
{
       
        uint16_t Free = 0;
        uint16_t SendSize = 0;
       
        TFIFO* SendFIFO = USART_GetSendFIFOName( USARTx );    //获得串口对应的FIFO环型缓冲区
        DMA_Stream_TypeDef* DMAy_Streamx = USART_Get_DMAStreamX(USARTx, 1);    //取得对应串口的DMA流通道 0:为RX 1:为TX
       
        if( SendFIFO == NULL || DMAy_Streamx == NULL )        return SendSize;
       
        //进入循环发送,直到所有的数据都填入环型缓冲区为止
        while( SendSize < BuffSize )
        {
                        Free = FIFO_GetFree( SendFIFO );    //获得串口发送环型缓冲区空闲大小
               
                        if( Free > 0 )    //如果有空闲
                        {
               
                                if( Free > BuffSize )    //如果空闲大小大于待发送大小,则直接把待发送数据压入发送缓冲区
                                {
                                        FIFO_PutBuff( SendFIFO, Buff, BuffSize);    //压入发送缓冲区
                                        SendSize = BuffSize;    //发送数据大小
                                       
                                        USART_DMA_Send( SendFIFO, DMAy_Streamx );    //发送DMA数据
                                }
                                else
                                {
                                       
                                        if( BuffSize - SendSize < Free ) Free = BuffSize - SendSize;
                                       
                                        FIFO_PutBuff( SendFIFO, (uint8_t *) (Buff + SendSize), Free);        //压入发送缓冲区
                                        SendSize += Free;
                                       
                                        USART_DMA_Send( SendFIFO, DMAy_Streamx );     //发送DMA数据

                                }
                               
                        }

                        Delay_ms(1);    //延时,多任务下让出时间片

        }
       
        return SendSize;
       
}



/*
*函数名称:DMA2_Stream7_IRQHandler
*函数功能:USART1-TX DMA2 Stream7-Channel4 响应中断
*发送完成后触发该中断,并继续发送未完成的数据
*/
void DMA2_Stream7_IRQHandler(void)
{
       
        uint8_t ID = 0;                //串口序号
       
        uint16_t BuffSize = 0;
        uint16_t ReadSize = 0;       
       
        if( DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7) != RESET )
        {
               
                DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);    //清除中断标志
               
               
                BuffSize = FIFO_GetSize( &USART1_FIFO_Send );    //获得发送缓冲区大小
               
                if( BuffSize > 0 )
                {
                       
                        if( BuffSize > DMA_USART_SendBuffer_Len ) BuffSize = DMA_USART_SendBuffer_Len;        //如果发送大小大于DMA发送缓冲,则大小取DMA发送缓冲区大小
                       
                        if( FIFO_GetBuff( &USART1_FIFO_Send, BuffSize, DMA_USART1_Send_Buff, &ReadSize) != 1 )            //如果发送环型缓冲区已经为空,则退出发送,否则继续发送
                        {
                                USART_DMA_Status[ID].DMA_SendOK = 1;    //发送完成
                                USART_DMA_Status[ID].DMA_IsSending = 0;    //重置发送标识,下次发送需要开启DMA发送
                               
                                return;
                        }
               
                        DMA_Cmd( DMA2_Stream7, DISABLE );    //停止发送
                        DMA_SetCurrDataCounter( DMA2_Stream7, ReadSize );    //设置DMA发送缓冲区的大小为待发送大小       
                        DMA_Cmd( DMA2_Stream7, ENABLE );

                        USART_DMA_Status[ID].DMA_SendOK = 0;    //未发送完成
                        USART_DMA_Status[ID].DMA_IsSending = 1;    //正在发送
                       
                }
                else
                {
                        USART_DMA_Status[ID].DMA_SendOK = 1;    //发送完成
                        USART_DMA_Status[ID].DMA_IsSending = 0;    //已发送完成
                }
               

        }
       
}





终于发完了,并整理了备注,程序可能比较复杂,该方法也稳定运行了一段时间,暂时没发现问题


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

26

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
215
金钱
215
注册时间
2012-12-17
在线时间
77 小时
发表于 2016-12-20 14:18:45 | 显示全部楼层
回复

使用道具 举报

1

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
171
金钱
171
注册时间
2016-12-20
在线时间
40 小时
发表于 2016-12-20 14:53:10 | 显示全部楼层
这种思路不错!
回复

使用道具 举报

21

主题

71

帖子

0

精华

初级会员

Rank: 2

积分
167
金钱
167
注册时间
2013-12-1
在线时间
88 小时
发表于 2016-12-20 15:10:32 | 显示全部楼层
本帖最后由 ssis909 于 2016-12-20 15:17 编辑

不错不错

回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-23 18:02:56 | 显示全部楼层
楼主 FIFO_PutBuff( &USART1_FIFO_Receive, DMA_USART1_Recieve_Buff, Size );  这个函数你是怎么定义的, USART_DMA_Status[ID].DMA_SendOK = 1;    //发送完成
                                USART_DMA_Status[ID].DMA_IsSending = 0; 这两个标志就自己随便定义一个就可以了吧
回复

使用道具 举报

10

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
142
金钱
142
注册时间
2014-7-19
在线时间
20 小时
发表于 2016-12-23 18:20:30 | 显示全部楼层
帮顶  这个思路不粗
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-23 19:06:55 | 显示全部楼层
楼主可否将工程贴出来参考一下
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2016-12-25 21:36:44 | 显示全部楼层
谢谢分享
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
 楼主| 发表于 2016-12-26 08:51:16 | 显示全部楼层
wdxYHC 发表于 2016-12-23 18:02
楼主 FIFO_PutBuff( &USART1_FIFO_Receive, DMA_USART1_Recieve_Buff, Size );  这个函数你是怎么定义的,  ...

是的,自定义的,用来作标识的
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
 楼主| 发表于 2016-12-26 08:52:37 | 显示全部楼层
wdxYHC 发表于 2016-12-23 19:06
楼主可否将工程贴出来参考一下

这个没办法贴,在用项目,里面的涉及面太广,所以还是断章取义吧
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-26 09:08:02 | 显示全部楼层
lanxix 发表于 2016-12-26 08:52
这个没办法贴,在用项目,里面的涉及面太广,所以还是断章取义吧

楼主这个 FIFO_PutBuff( &USART1_FIFO_Receive, DMA_USART1_Recieve_Buff, Size );            //将收到的DMA缓冲复制进串口输入FIFO  FIFO_PutBuff()函数是环型缓冲区写入Buff的用到的,USART1_FIFO_Receive 环型缓冲区结构
这个环形缓冲区不是很明白,这个FIFO_PutBuff函数以及USART1_FIFO_Receive 环型缓冲区结构您是如何定义的可否将这部分分享一下
回复

使用道具 举报

5

主题

277

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1522
金钱
1522
注册时间
2014-5-16
在线时间
217 小时
发表于 2016-12-26 09:09:44 | 显示全部楼层
用IDLE中断作为串口接收完成的判据一般情况下是可以的,但是如果发送方的字节传输延时大于1个字节,那就可能收不到完整数据帧,有一定的风险,我的做法是轮询DMA长度寄存器.
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-26 15:35:27 | 显示全部楼层
楼主FIFO_PutBuff函数以及USART1_FIFO_Receive 环型缓冲区结构您是如何定义的可否将这部分分享一下
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-26 17:55:21 | 显示全部楼层

原子哥,楼主FIFO_PutBuff函数以及USART1_FIFO_Receive 环型缓冲区结构您是否有好的理解,可否指点一二
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2016-12-27 22:24:38 | 显示全部楼层
楼主分享下工程代码吧
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
 楼主| 发表于 2016-12-27 22:54:27 来自手机 | 显示全部楼层
wdxYHC 发表于 2016-12-26 15:35
楼主FIFO_PutBuff函数以及USART1_FIFO_Receive 环型缓冲区结构您是如何定义的可否将这部分分享一下

USART1_FIFO_Receive  确实是一个结构体  它包含分配内存的地址  环形缓冲区用到的头跟尾指针等   然后我在做一些通用函数  就可以实现多个操作了   其实我也是抄网上的fifo  只是把它改成通用式而已
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
 楼主| 发表于 2016-12-27 23:02:54 来自手机 | 显示全部楼层
zmingwang 发表于 2016-12-26 09:09
用IDLE中断作为串口接收完成的判据一般情况下是可以的,但是如果发送方的字节传输延时大于1个字节,那就可能 ...

如果延时的话   下次如果还能接受到这个字节  那就没问题  只要不是丢包   数据帧的完整性不能光依赖底层传输   我的做法是使用fifo  命令头  读取fifo特定的字节数超时设置  命令头还包含数据体的长度  还有数据的有效性判断    有了这些保障   数据就尽可能的减少错误了
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-28 15:06:53 | 显示全部楼层
楼主,如果是接收的数据每一包中间会有大于1个字节的延时,并且每一包数据都是不确定的,
没有一个标识来表示是一包数据的开头与结尾,那么每次遇到这个延时总线空闲就会进入中断将数据发出去,
这样数据就不完整,是否有方法可以解决?(如果在数据接收完后加入一个延时,比如20MS,如果还有数据
就继续接收,没有数据进来就发出去,那么这个延时是否应该是在进入中断前,这样的延时改放在程序的什么地方比较合适)
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
 楼主| 发表于 2016-12-28 17:17:41 | 显示全部楼层
本帖最后由 lanxix 于 2016-12-28 17:18 编辑
wdxYHC 发表于 2016-12-28 15:06
楼主,如果是接收的数据每一包中间会有大于1个字节的延时,并且每一包数据都是不确定的,
没有一个标识来 ...

我使用的方法接收发送与业务逻辑分离,中间层是FIFO,接收只管保存到FIFO, 发送也只管从FIFO取。

我在发送之前,都会有一个定长的命令头,告诉对方我要发送的类型和长度,这样,对方每次开始接收的时候都会先接收这个定长的命令,通过命令头来判断下一步操作。

当然,你的说的数据包延时是会存在,所以我有个超时读取FIFO的函数,如:FIFO_ReadByte( &USART1_FIFO_Receive, (uint8_t *) &USART_Receive_CMD, sizeof( TCMDHeader ), 50 );  最后的50就是如果接收的大小没到达sizeof( TCMDHeader )命令头的长度,则最大等待50ms,这样就很好处理超时的问题了,如果等待时间过长,肯定硬件或软件有问题了。
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-28 18:05:07 | 显示全部楼层
lanxix 发表于 2016-12-28 17:17
我使用的方法接收发送与业务逻辑分离,中间层是FIFO,接收只管保存到FIFO, 发送也只管从FIFO取。

我 ...

这种情况还是在你自己可以提前告知需要接收的字节大小,如果接收到的数据没有达到你定义的长度那么就会有个延时,但是如果接收的数据时外部来的,你无法判断它的大小,这个时候如果还是要加一个延时判断是否接收完数据,那么是应该在进入中断前就加延时,判断如果已经没有数据了再进中断吗?
回复

使用道具 举报

2

主题

15

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2016-12-23
在线时间
7 小时
发表于 2016-12-28 19:58:52 | 显示全部楼层
串口空闲中断,当检测到空闲帧就进中断,我可不可以在进中断前先延时20ms,如果还有数据就继续接收,没有了再进中断,请问这个怎么实现(可以人为地去控制这个空闲中断吗?)
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-12-20
在线时间
4 小时
 楼主| 发表于 2016-12-29 00:42:58 来自手机 | 显示全部楼层
wdxYHC 发表于 2016-12-28 19:58
串口空闲中断,当检测到空闲帧就进中断,我可不可以在进中断前先延时20ms,如果还有数据就继续接收,没有了 ...

中断不能加任何有延时的代码,不能做占用时间久的事   所以数据的处理代码一般不是写在中断  而是在轮询中   串口空闲就表示时间范围内没数据了,其实你现在的情况是对单片机不熟  多百度下,多看别人的思路就好了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-21 02:55

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表