[Raw-OS] uart 接收发送新玩法

jorya_txj   2014-1-5 20:11 楼主
很多时候,用户发送具体消息的时候,也需要发送一个消息长度。比如在网卡中断中接收到了一包数据,发送消息给外面任务的时候,也需要告诉外面的任务消息的长度是多少。使用之前的queue模块做需要一点小技巧,但是使用queue_size模块做的话,显的很直接,直接可以发送出去。
带有消息长度的消息机制有一种很有用的用法。假设有一个串口接收中断来了,接收数据的话可以采用信号量+fifo 或者queue+块内存分配去做,但是这两种多多少少有些缺点。信号量+fifo的方式处理的话,fifo内部是关了cpu全局中断去操作的,如果数据量比较大的话,这个会严重影响实时性。queue +block内存区分配的话,如果中断接收每次来的数据都不同的话,会严重浪费内存。
有没有经济实用的不关中断,又节约内存的方式操作呢。答案是肯定的。当串口接收中断来的时候,用户需要自己维护一个简单的fifo缓冲区,但是这个缓冲区不需要关中断,写到头了重新回到头去写就好。当接收到数据填好缓冲区的时候,把缓冲区的这次数据起始地址以及数据长度通过raw_queue_size_end_post 发送出去。同时外面有一个任务调用raw_queue_size_receive接收数据。具体的示例如下:
staticRAW_U32 recv_temp_buffer[12]; (1)
staticRAW_U32 *uart_p; (2)
staticRAW_U32 uart_data_length (3)
void uart_int_recevie_handle(void)
{
RAW_U32 length_temp;
RAW_U32 uart_recv_length;
RAW_U8 *p_temp;
uart_recv_length = 读串口数据的长度
length_temp = uart_recv_length;
if (raw_queue_size_full_check()) {
/*do something thing and return*/
return;
}
/*if isr receive data is faster than task data process, just ignore the data*/
if (uart_data_length >= (12 * 4)) {
/*do something thing and return*/
return;
}
if ((uart_p + (uart_recv_length >> 2))> = (recv_temp_buffer + 12)) {
uart_p = recv_temp_buffer;
}
p_temp = (RAW_U8 *)uart_p;
while (length_temp--) {
*p_temp++ = 串口数据;
}
queue_size_end_post(queue_size, uart_p, uart_recv_length);
if(uart_recv_length & 3) {
uart_data_length += 4u - (uart_recv_length& 3u)+ uart_recv_length;
uart_p = uart_p + (uart_recv_length>> 2) + 1;
}
else {
uart_p = uart_p + (uart_recv_length>> 2)
uart_data_length += uart_recv_length;
}
}
RAW_U32process_buffer[10];
void task_uart_receive()
{
RAW_U8 receive_size;
void * msg_ptr;
while (1) {
raw_queue_size_receive(&q_size,RAW_WAIT_FOREVER, &msg_ptr,
&receive_size);
raw_memcpy(process_buffer,msg_ptr, receive_size);
RAW_CPU_DISABLE();
if(receive_size & 3) {
uart_data_length = uart_data_length - (4u- (receive_size & 3u) + receive_size);
}
else {
uart_data_length -= receive_size;
}
RAW_CPU_ENABLE();
/*processsreceived data, processed time must be smaller than interrupt frequecy*/
}
}
(1)处用户自己维护了一个缓冲区,缓冲区的数据长度是48个字节。
(2)处代码的uart_p用来记录缓冲区的写位置。
(3)处代码维护一个长度,如果长度超过了缓冲区的长度就丢弃数据,返回,保证不会去覆盖缓冲区的数据。
以上的操作避免了临界区的缓冲区的操作,又节省了内存开销。而且数据执行效率上比起fifo也要高,因为少了一次copy动作。
本帖最后由 jorya_txj 于 2014-1-5 20:18 编辑

回复评论 (11)

不错,学习了
点赞  2014-1-5 20:59
自己建一个FIFO,直接把串口中断的数据压入FIFO,任务中再读FIFO。FIFO满了的时候再发个信号量或者事件通知任务,比如像STM32那种,还可以利用UART的IDLE中断来通知任务该读FIFO了
点赞  2014-1-5 23:51
点赞  2014-1-6 09:05
楼上的想法是可行的,只是自己建的fifo, 难免要关中断,数据量一大,实时性无法忍受,这个就是放弃的原因。因为中断模型不单单是uart的,其它各模块比如i2c, spi 等模块都是共通的,打造0关中断的fifo操作,是很有意义的。 本帖最后由 jorya_txj 于 2014-1-6 09:22 编辑
点赞  2014-1-6 09:20
学习了
点赞  2014-1-6 10:35
不错,学习了        
点赞  2014-1-13 15:27
太难懂了,要理解得把os先通读啊。 本帖最后由 nos001 于 2014-4-28 15:12 编辑
点赞  2014-4-27 02:51
上面的只是一种设计思路,和os没任何关系。
点赞  2014-4-27 11:38
为什么不用 DMA 呢。。。启用DMA后,串口接收数据可以直接传送到 FIFO 里面 ,都可以不经过中断处理 本帖最后由 谷子木 于 2014-5-3 22:48 编辑
点赞  2014-5-3 22:42
dma 数据传到内存后,什么时候结束,还是需要中断去通知的。只是中断里面不需要去搬数据了。有一些单片机的串口是没有dma的支持的。
点赞  2014-5-4 10:49
很受启发,结合着K60的例子和视频看了一下,
if ((uart_p + (uart_recv_length >> 2))> = (recv_temp_buffer + 12))   {

       uart_p = recv_temp_buffer;
    }

这句代码能否解释一下,uart_p是一个指针,uart_p + uart_rec_length >> 2是? 在K60 的代码中 uart_recv_length是1,它右移了2位。。。。。不是很懂,然后让它和rece_tem_buffer+12,相比较,后者是缓冲区的尾地址吧,前者能解释下吗,这句代码的意思是,写到了尾部,那就把缓冲区地址赋给uart_p

点赞  2014-6-22 19:14
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复