UART 接收数据较大时,怎么接收



  • 串口的FIFO只有32字节,大于这个字节就接收不到,还有很大可能会使系统不能继续运行,我是用uart1_read_for_hci去读的,怎么解决



  • 可以常用中断形式

    0_1617014013446_20210329183203.png



  • 0_1617017943902_28ab8243-93d1-44b7-8144-99e3dc1a0b13-image.png
    0_1617018480714_3efc096b-3255-423b-b9c2-2892cf3e70fc-image.png
    比我开始的方法好点了,可还是只能接收到前32个字节,我发送的内容长度是41个字节的
    AA 29 00 00 00 AA D4 C1 41 38 38 38 38 38 00 00 00 00 4E 00 04 C4 02 00 00 FC 01 00 00 52 04 01 02 6C 02 00 00 01 00 8E BB



  • uart就这样,你看发送那端可以分开两次发不,或者用别的串口协议传输数据



  • 你是用jlink调试的



  • @鱼的记忆 发送那端是别人的设备控制不了,是用Jlink调试的,打印口被用来传输数据了



  • 这个有点麻烦,这个只能富瑞坤内部人员,看可以解决不,分两次接收,我只会这种



  • 如果可以控制发送端,分包发送,两次接收,应该是没问题。谢谢你的帮忙



  • 找到解决办法了,接收中断改为半满,这样就有足够时间读取数据,不要在中断中打断点查看数据,这样也会导致接收不完整。具体做法如下
    串口初始化
    0_1617068878547_94a9dcdc-df40-4ba2-b1c1-24672afe74ca-image.png
    中断执行内容还是上面的图



  • 我不建议用芯片带FIFO来做接收中断,我从来都是自己做1个串口字节接收缓冲区,1个串口包处理缓冲区。这种结构对于1个叠包也基本够用了。
    attribute((weak)) attribute((section("ram_code"))) void uart0_isr_ram(void)
    {
    uint8_t int_id;
    uint8_t rx_byte;

    int_id = uart0_reg->u3.iir.int_id;
    
    if(int_id == 0x04 || int_id == 0x0c )   /* Receiver data available or Character time-out indication */
    {
        rx_byte = uart0_reg->u1.data;
        uart0_info.packet_timeout = 2;
    	if(1 == uart0_info.rxd_flg)
    	{
    		uart0_info.rxd_buf[uart0_info.rxd_ctr++] = rx_byte; //
    		if(uart0_info.rxd_ctr == uart0_info.rxd_len)
    		{   //收齐 处理包
    			uart0_info.rxd_ctr = 0;
    			uart0_info.rxd_flg = 0;
    			uart0_info.rxd_cmd_len = uart0_info.rxd_len; 
    			memcpy(uart0_info.rxd_cmd_buf, uart0_info.rxd_buf, uart0_info.rxd_len); 
    			evt.event_id = 0;            //串口0 事件
    			os_msg_post(uart01_task_id,  &evt);
    		}
    		else if(3==uart0_info.rxd_ctr)
    		{
    		    uart0_info.rxd_len = rx_byte + 4;//后面字节包的长度
    		}
    		else ;
    	}
    	else //== 0
    	{
    		if(0xA7 == rx_byte) //协议包头
    		{
    			uart0_info.rxd_flg = 1; //开启接收
    			uart0_info.rxd_ctr = 1; //接收字节计数
    			uart0_info.rxd_buf[0] = rx_byte;
    		}			
    	}		
    }
    if(int_id == 0x06)
    {
        volatile uint32_t line_status = uart0_reg->lsr;
    }
    if(int_id == 0x02)
    {	
       if(uart0_info.txd_ctr < uart0_info.txd_len[uart0_info.txd_busy_index]) //判断当前包是否发送完毕
       {   
    	   uart0_reg->u1.data     = uart0_info.txd_buf[uart0_info.txd_busy_index][uart0_info.txd_ctr++];
       }
       else //发完一包 休息或继续下一包
       {
    		uart0_info.txd_len[uart0_info.txd_busy_index] = 0;     //清除发送包长度 表示空的 可以重新组装			
    		if(uart0_info.txd_len[uart0_info.txd_emptypp_index]>0) //有包要发 则继续
    		{
    			if(0==uart0_info.txd_busy_index) {  
    				uart0_info.txd_busy_index    = 1;
    				uart0_info.txd_emptypp_index = 0;
    			}
    			else {  	
    				uart0_info.txd_busy_index    = 0; 	
    			    uart0_info.txd_emptypp_index = 1;
    			}			 				                           
    			uart0_info.txd_ctr  = 1;
    			uart0_reg->u1.data  = 0xA7; // 
    		}
    		else
    		{
    			uart0_reg->u2.ier.eti  = 0;   //关闭发送中断 避免这个MCU 产生发送空中断,STC51则不必 关不掉(因为51发送跟接收中断开关共用,而你不可能关闭接收中断)
    			uart0_info.tx_busy = IDLE;
    		}		   
       }
    }
    

    }



  • 修改接收中断处理函数,另外收到第一个byte开启硬件定时器做超时处理,收到下一个byte后reload定时器初值保证不产生中断,在间隔一定时间不收到产生超时中断,这个时间要以当前波特率下的的一byte传输数据留下余量。即使用超时成帧机制,再处理一帧数据



  • @赢娶姗姗 挺好的,值得学习学习,感谢



  • @zj4068 现在的使用和这个方法类似



  • 自己多想方法,原厂只是demo而已,我把发送函数也做了处理。目前demo是使用无中断发送。好处比较简单,不好的地方数据超多,会比较占用时间。使用填好发送缓冲我数据后,发送第一个字节启动tx串口中断,后续在发送中断中完成,发完后关闭发送中断。相对高效,如果原厂可以开放dma会更高效。



  • @zj4068 没错的 发送中断方式 比查询方式 会更少阻塞CPU一些。 DMA有的话更好, 中断发送方式更加通用,8位机基本没DMA,有的32位也没有串口发送 DMA呢