串口驱动,这是在每个单片机中可以说是必备接口。可以说大部分产品中都会使用,更有甚者一个产品中用到8个串口。这样一个高效的驱动是决定您产品优劣的关键因素。本文主要针对STM32F4XX系列芯片做的一个驱动接口层。以减少您在开发项目时驱动方面所花费时间,以及为程序达到高效的处理为目的。
从51,pic到现在的STM32,个人感觉STM32这方面做的非常突出,丰富的使用模式,强大的引脚映射功能,强大的处理能力等,给我留下的深刻的印象。
关于串口的使用方式,个人总结出以下三种:
1) 中断接收,状态查询发送:这种方式在单片机时代用的比较多,那时候大部分芯片的处理速度不够快,工业控制接口中大部分使用的是9600波特率,程序简单也就对串口发送数据所用时间要求不高。
2) 中断接收,中断发送:这种方式一般会在对高效性要求较高,或软件实时性高的产品中使用。这种方式的好外在于完全释放了CPU在发送开始到结束这一时间CPU控制权。
3) 中断接口,DMA发送:这种方式的使用场合和模式2相同,效率也相差不多。但这种方式确实比模式2CPU的占用时间会更少。
举个例子来说明以上三种模式。假如要把一堆物品从A点运到B点通过传送带,在这个传送过程中传送带上一次只能运一个物品,模式一:把物品放到传送带A点上,然后等待物品由A点被传到B点,这时再放一个物品到传送带上,以此往复所有物品传送过去。模式二:把物品放到A点的传送带上然后就去忙别的事情,看到物品快被传到B点,马上回来再放一个物品到传送带上。很明显这种方式比模式一多了很多空余时间。模式三:这种模式就牛了,在把物品放到A点之前,直接去找了一个“闲杂人等”过来,把物品交给他由他去发,我们需要再发送时,先问下她是否以发完,如果发完了就把新的物品交给他,如果没发完就等一小回再来找他。哈哈,这样咱们就有更多的时间去忙更多的事情了,也不用一回跑来一回跑去。把跑路的时间给节约出来了。
以上三种模式,很明显得出那种模式发送数据效率最高。我们下面说提供的程序就是使用模式三。
说完发送,再说说接收方式。大家一定发现数据接收都是采用中断方式,是的 本人使用过DMA方式进行过多次测试,在使用方面确实没有中断接收灵活。主要有以下两种情况,1,DMA接收数据的主要判断依据1是接收器满中断,这种情况在实际中很少用,除非您的数据是定长。这种方式同时还会存在一些安全隐患,假如噪声原因多接收到一个字节,那之后数据时序就会错位。2,DMA总线空闲中断,这种方式除非是半双工情况下使用。在全双工时被受到发送完成总线空闲的干扰。所以在数据接收方式上主要使用中断。
在数据接收中断方面还分二种方式,
方式1:顺序接收,在接收到第一个数据时就触发超时定时器,每接收到一个字节时就清一次定时器,都到一组数据接收完毕,定时器会因为触发超时中断。在超时中断中来判断一组数据被接收。这种方式一般会用到实时性的一些协议中,比如MODBUS。
方式2:队列接收,申请一个缓冲区收尾相接,接收到数据时载入队列之中,用户只要定时的去队列中读数据,来使用这些数据。这种方式是window,linux的驱动主要接收方式。他的优点就在于在使用数据时无需关闭中断。也就不用怛心在处理上一组数据时了来新的数据会破坏上组数据内容。在方式1中需要考虑在处理数据时暂时性的关下中断。
以下程序则主要是使用到接收使用方式2,发送数据使用模式3的DMA发送,本驱动程序为可裁切,覆盖串口1-8,通过宏裁切。下面提供了一些接口,这里对接口做一个大概的说明。
打开串口
void BSP_UartOpen(uint8_t COM, uint32_t baud, uint8_t data, uint8_t stop, uint8_t parity);
关闭串口
void BSP_UartClose(uint8_t COM);
向串口中写数据
uint32_t BSP_UartWrite(uint8_t COM, uint8_t *buffter, uint32_t len);
从串口中读数据
uint32_t BSP_UartRead(uint8_t COM, uint8_t *buffter, uint32_t len);
查询串口发送忙碌状态
uint32_t BSP_UartTxIdleState(uint8_t COM);
这个接口主要用在,向串口写数据后,在进行下一次写数据之前需要进行查询。
具体接口的使用方法,函数都有详细的说明。
1 /*
2 ********************************************************************************
3 *
4 * Queue.c
5 *
6 * File : Queue.c
7 * Version : V1.0
8 * Author : whq
9 * Mode : Thumb2
10 * Toolchain :
11 * Description : 队列操作
12 *
13 * History :
14 * Date : 2013.07.22
15 *******************************************************************************/
16
17 #include <string.h>
18
19 #include "Queue.h"
20 /*******************************************************************************
21 * Function Name : uint32_t QUEUE_PacketCreate(QUEUE8_TYPE *pQ8, uint8_t *pBuf, uint32_t lenSize)
22 * Description : 队列建立
23 * Input : pQ8: 队列
24 pBuf: 队列缓冲区地址
25 bufSize:队列缓冲区大小
26 * Output :
27 * Other :
28 * Date : 2013.08.29
29 *******************************************************************************/
30 uint32_t QUEUE_PacketCreate(QUEUE8_TYPE *pQ8, uint8_t *pBuf, uint32_t bufSize)
31 {
32 ASSERT_PARAM(pQ8);
33 ASSERT_PARAM(pBuf);
34 ASSERT_PARAM(bufSize);
35
36 pQ8->bufSize = bufSize;
37 pQ8->pBuf = pBuf;
38 pQ8->pStart = pBuf;
39 pQ8->pEnd = pBuf;
40
41 return 0;
42 }
43
44 /*******************************************************************************
45 * Function Name : uint32_t QUEUE_PacketIn(QUEUE8_TYPE *pQ8, uint8_t *pData, uint32_t len)
46 * Description : 数据载入队列
47 * Input : pQ8: 队列
48 pData: 要进队列的数据
49 len: 数据长度
50 * Output :
51 * Other :
52 * Date : 2013.08.29
53 *******************************************************************************/
54 uint32_t QUEUE_PacketIn(QUEUE8_TYPE *pQ8, uint8_t *pData, uint32_t len)
55 {
56 uint32_t dataLen = len;
57
58 ASSERT_PARAM(pData);
59 ASSERT_PARAM(pQ8);
60 ASSERT_PARAM(pQ8->pStart);
61 ASSERT_PARAM(pQ8->pEnd);
62
63 while (dataLen--)
64 {
65 *pQ8->pEnd++ = *pData++;
66
67 if (pQ8->pEnd >= pQ8->pBuf + pQ8->bufSize) //指针指向栈尾
68 {
69 pQ8->pEnd = pQ8->pBuf;
70 }
71
72 if (pQ8->pEnd == pQ8->pStart) //缓冲区填满 覆盖最早的数据
73 {
74 pQ8->pStart++;
75 if (pQ8->pStart >= pQ8->pBuf + pQ8->bufSize)
76 {
77 pQ8->pStart = pQ8->pBuf;
78 }
79 }
80 }
81
82 return len;
83 }
84
85 /*******************************************************************************
86 * Function Name : uint32_t QUEUE_PacketOut(QUEUE8_TYPE *pQ8, uint8_t *pData, uint32_t dataLen)
87 * Description : 队列中取数据
88 * Input : pQ8: 队列
89 pData: 缓冲区
90 dataLen:缓冲区大小
91 * Output :
92 * Other :
93 * Date : 2013.08.29
94 *******************************************************************************/
95 uint32_t QUEUE_PacketOut(QUEUE8_TYPE *pQ8, uint8_t *pData, uint32_t dataLen)
96 {
97 uint32_t index = 0;
98
99 ASSERT_PARAM(pData);
100 ASSERT_PARAM(pQ8);
101 ASSERT_PARAM(pQ8->pStart);
102 ASSERT_PARAM(pQ8->pEnd);
103
104 while ((pQ8->pStart != pQ8->pEnd) && (index < dataLen) && (index < pQ8->bufSize))
105 {
106 pData[index++] = *pQ8->pStart++;
107 if (pQ8->pStart >= pQ8->pBuf + pQ8->bufSize)
108 {
109 pQ8->pStart = pQ8->pBuf;
110 }
111 }
112
113 return index;
114 }
115
116 /*******************************************************************************
117 * Function Name : uint32_t QUEUE_PacketSplit(QUEUE8_TYPE *pQ8, uint8_t startChar, uint8_t endChar, uint8_t *pData, uint32_t dataLen)
118 * Description : 以起始符和结束符取队列中的数据 (取出的数据 包括起始符 和分隔符)
119 * Input : pQ8: 队列
120 startChar: 起始符
121 endChar: 结束符
122 pData: 缓冲区
123 dataLen: 缓冲区大小
124 * Output :
125 * Other :
126 * Date : 2013.08.29
127 *******************************************************************************/
128 uint32_t QUEUE_PacketSplit(QUEUE8_TYPE *pQ8, uint8_t startChar, uint8_t endChar, uint8_t *pData, uint32_t dataLen)
129 {
130 int32_t count;
131 int32_t index;
132 volatile uint8_t *pStart;
133 volatile uint8_t *pEnd;
134
135 ASSERT_PARAM(pData);
136 ASSERT_PARAM(pQ8);
137 ASSERT_PARAM(pQ8->pStart);
138 ASSERT_PARAM(pQ8->pEnd);
139
140 pStart = pQ8->pStart;
141 count = pQ8->bufSize;
142
143 while ((pStart != pQ8->pEnd) && count--) //查找起始字符
144 {
145 if (startChar == *pStart) break;
146 if (++pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
147 }
148
149 if (pStart == pQ8->pEnd) return 0; //未找到起始符
150 if (count == -1) return 0;
151 pEnd = pStart;
152 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
153
154 while ((pEnd != pQ8->pEnd) && count--) //查找结束字符
155 {
156 if (endChar == *pEnd) break;
157 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
158 }
159
160 if (pEnd == pQ8->pEnd) return 0; //未找结束符
161 if (count == -1) return 0;
162 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
163
164 count = pQ8->bufSize - count;
165 index = 0;
166 //获取从起始字符到结束字符的数据
167 while ((pStart != pEnd) && (index < dataLen) && (index < pQ8->bufSize) && count--)
168 {
169 pData[index++] = *pStart++;
170 if (pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
171 }
172
173 pQ8->pStart = pEnd;
174 return index;
175 }
176
177 /*******************************************************************************
178 * Function Name : uint32_t QUEUE_PacketCharSplit(QUEUE8_TYPE *pQ8, uint8_t splitChar, uint8_t *pData, uint32_t dataLen)
179 * Description : 提取首尾双分隔符内的数据(包括分隔符)
180 * Input : pQ8: 队列
181 startChar: 起始符
182 endChar: 结束符
183 pData: 缓冲区
184 dataLen: 缓冲区大小
185 * Output :
186 * Other :
187 * Date : 2013.08.30
188 *******************************************************************************/
189 uint32_t QUEUE_PacketDoubleByteSplit(QUEUE8_TYPE *pQ8, uint8_t splitChar, uint8_t *pData, uint32_t dataLen)
190 {
191 int32_t count;
192 int32_t index;
193 volatile uint8_t *pStart;
194 volatile uint8_t *pEnd;
195
196 ASSERT_PARAM(pData);
197 ASSERT_PARAM(pQ8);
198 ASSERT_PARAM(pQ8->pStart);
199 ASSERT_PARAM(pQ8->pEnd);
200
201 pStart = pQ8->pStart;
202 count = pQ8->bufSize;
203
204 while ((pStart != pQ8->pEnd) && count--) //查找起始字符
205 {
206 if (splitChar == *pStart) break;
207 if (++pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
208 }
209
210 if (pStart == pQ8->pEnd) return 0; //未找到起始符
211 if (count == -1) return 0;
212 pEnd = pStart;
213 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
214
215 while ((pEnd != pQ8->pEnd) && count--) //查找结束字符
216 {
217 if (splitChar == *pEnd) break;
218 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
219 }
220
221 if (pEnd == pQ8->pEnd) return 0; //未找结束符
222 if (count == -1) return 0;
223 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
224
225 count = pQ8->bufSize - count;
226 index = 0;
227 //获取从起始字符到结束字符的数据
228 while ((pStart != pEnd) && (index < dataLen) && (index < pQ8->bufSize) && count--)
229 {
230 pData[index++] = *pStart++;
231 if (pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
232 }
233
234 //如果取出的数据只包括分隔符,有可能是上次结束符和下次起始符,因此放弃上次结束符。
235 if (index <= 2)
236 {
237 index = 0;
238 if (--pStart < pQ8->pBuf) pStart = pQ8->pBuf + pQ8->bufSize - 1;
239 }
240
241 pQ8->pStart = pStart;
242 return index;
243 }
244
245 /*******************************************************************************
246 * Function Name : uint32_t QUEUE_PacketCharSplit(QUEUE8_TYPE *pQ8, uint8_t splitChar, uint8_t *pData, uint32_t dataLen)
247 * Description : 提取单结束分隔符的数据 (包括分隔符)
248 * Input :
249 * Output :
250 * Other :
251 * Date : 2013.10.20
252 *******************************************************************************/
253 uint32_t QUEUE_PacketCharSplit(QUEUE8_TYPE *pQ8, uint8_t splitChar, uint8_t *pData, uint32_t dataLen)
254 {
255 int32_t count;
256 int32_t index;
257 volatile uint8_t *pStart;
258 volatile uint8_t *pEnd;
259
260 ASSERT_PARAM(pData);
261 ASSERT_PARAM(pQ8);
262 ASSERT_PARAM(pQ8->pStart);
263 ASSERT_PARAM(pQ8->pEnd);
264
265 pStart = pQ8->pStart;
266 count = pQ8->bufSize;
267
268 while ((pStart != pQ8->pEnd) && count--) //查找起始字符
269 {
270 if (splitChar == *pStart) break;
271 if (++pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
272 }
273
274 if (pStart == pQ8->pEnd) return 0; //未找到起始符
275 if (count == -1) return 0;
276 pEnd = pStart;
277 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
278
279 pStart = pQ8->pStart;
280 count = pQ8->bufSize;
281 index = 0;
282 while ((pStart != pEnd) && (index < dataLen) && count--) //查找起始字符
283 {
284 pData[index++] = *pStart;
285 if (++pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
286 }
287
288 pQ8->pStart = pStart;
289 return index;
290 }
291
292 /*******************************************************************************
293 * Function Name :QUEUE_PacketDoubleCharSplit
294 * Description :提取双结束分隔符的数据 (包括分隔符)
295 * Input :QUEUE8_TYPE * pQ8
296 * Input :uint8_t splitChar1
297 * Input :uint8_t splitChar2
298 * Input :uint8_t * pData
299 * Input :uint32_t dataLen
300 * Output :uint32_t
301 * Other :
302 * Date :2014/03/27
303 *******************************************************************************/
304 uint32_t QUEUE_PacketDoubleCharSplit(QUEUE8_TYPE *pQ8, uint8_t splitChar1, uint8_t splitChar2, uint8_t *pData, uint32_t dataLen)
305 {
306 int32_t count;
307 int32_t index;
308 volatile uint8_t *pStart;
309 volatile uint8_t *pEnd;
310 uint8_t lastChar = 0;
311
312 ASSERT_PARAM(pData);
313 ASSERT_PARAM(pQ8);
314 ASSERT_PARAM(pQ8->pStart);
315 ASSERT_PARAM(pQ8->pEnd);
316
317 pStart = pQ8->pStart;
318 count = pQ8->bufSize;
319
320 while ((pStart != pQ8->pEnd) && count--) //查找起始字符
321 {
322 if ((splitChar1 == lastChar) && (splitChar2 == *pStart)) break;
323
324 lastChar = *pStart;
325
326 if (++pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
327 }
328
329 if (pStart == pQ8->pEnd) return 0; //未找到起始符
330 if (count == -1) return 0;
331 pEnd = pStart;
332 if (++pEnd >= pQ8->pBuf + pQ8->bufSize) pEnd = pQ8->pBuf;
333
334 pStart = pQ8->pStart;
335 count = pQ8->bufSize;
336 index = 0;
337 while ((pStart != pEnd) && (index < dataLen) && count--) //查找起始字符
338 {
339 pData[index++] = *pStart;
340 if (++pStart >= pQ8->pBuf + pQ8->bufSize) pStart = pQ8->pBuf;
341 }
342
343 pQ8->pStart = pStart;
344 return index;
345 }
346
347
348 /*******************************************************************************
349 * Function Name : void ASSERT_FAILED(uint8_t* file, uint32_t line)
350 * Description : 异常
351 * Input :
352 * Output :
353 * Other :
354 * Date : 2013.08.29
355 *******************************************************************************/
356 void ASSERT_FAILED(uint8_t* file, uint32_t line)
357 {
358 uint8_t flg = 1;
359
360 while (flg);
361 }
Queue.c