USART 使用 ringbuffer 实现无阻塞的读写
在使用串口的使用中,由于速率比较低,因此数据的收发都比较占用资源。尤其是数据的输入,因为在程序的执行过程中无法预知到底何时才有数据过来,采用中断的方式去实现接收也有弊端,当需要解析帧协议时需要不断的去判断是否有足够一帧的数据,而且在发送过程中也无法实现无阻塞。从而浪费了大量的 CPU 资源。这里采用一种 ringbuffer 的方式去实现无阻塞的收发,发送数据时只需要数据写入 buffer 即可,不需要等待完全发送完毕才退出。接收数据时,可以先判断缓存中是否有足够的数据,再去取出缓存的数据。而且在没有数据的时候,即使调用 getchar,也不会阻塞。因此使用起来比较方便。代码的实现比较简单,很容易就能移植到其他的平台。
usart 初始化与接口函数
1 |
|
与平台无关的 ringbuffer 文件
ringbuffer.h
1 |
|
ringbuffer.c1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
void RingBufferInit(tRingBuffer *rb, void (*callback)(void))
{
rb->Read = 0;
rb->Write = 0;
rb->Overrun = 0;
rb->CallBack = callback;
}
int RingBufferSize(tRingBuffer *rb)
{
return sizeof(rb->Buffer);
}
int RingBufferFillLevel(tRingBuffer *rb)
{
return (rb->Write - rb->Read + RingBufferSize(rb)) % RingBufferSize(rb);
}
void RingBufferPut(tRingBuffer *rb, unsigned char c, int block)
{
if (block)
{
while (RingBufferFillLevel(rb) + 1 == RingBufferSize(rb));
}
else
{
if (RingBufferFillLevel(rb) + 1 == RingBufferSize(rb))
{
rb->Overrun++;
return;
}
}
rb->Buffer[rb->Write] = c;
if (rb->Write + 1 == RingBufferSize(rb))
{
rb->Write = 0;
}
else
{
rb->Write++;
}
if (rb->CallBack)
{
rb->CallBack();
}
}
void RingBufferPutBlock(tRingBuffer *rb, unsigned char *data, int dataLen, int block)
{
if (block)
{
while (RingBufferFillLevel(rb) + dataLen >= RingBufferSize(rb));
}
else
{
if (RingBufferFillLevel(rb) + dataLen >= RingBufferSize(rb))
{
rb->Overrun += dataLen;
if (rb->CallBack)
{
rb->CallBack();
}
return;
}
}
int free1 = RingBufferSize(rb) - rb->Write;
if (dataLen <= free1)
{
memcpy(rb->Buffer + rb->Write, data, dataLen);
if (rb->Write + dataLen == RingBufferSize(rb))
{
rb->Write = 0;
}
else
{
rb->Write += dataLen;
}
}
else
{
memcpy(rb->Buffer + rb->Write, data, free1);
int len2 = dataLen - free1;
memcpy(rb->Buffer, data + free1, len2);
rb->Write = len2;
}
if (rb->CallBack)
{
rb->CallBack();
}
}
int RingBufferGet(tRingBuffer *rb)
{
if (rb->Read == rb->Write)
{
return -1;
}
else
{
unsigned char c = rb->Buffer[rb->Read];
if (rb->Read + 1 == RingBufferSize(rb))
{
rb->Read = 0;
}
else
{
rb->Read++;
}
return c;
}
}
int RingBufferPeek(tRingBuffer *rb)
{
if (rb->Read == rb->Write)
{
return -1;
}
else
{
int c = rb->Buffer[rb->Read];
return c;
}
}
使用 :
1、调用 debug_usart_init() 初始化串口和收发 ringbuffer
2、调用 get_char() 函数读取一个字节的数据, 该函数不会阻塞, 如果返回 -1 表示缓存中没有数据
3、调用 print() 发送数据, 该函数不会等到发送完成才返回, 只是将数据写入 buffer, 后面会自己去开启发
送中断函数完成发送。如果确实需要等到发送完成, 可以调用 usart_flush 函数。
————————————————
原文链接:https://blog.csdn.net/zhou0842/article/details/53574199