热度 1||
不善于写文字的我准备记录每一个项目中遇到的一些问题,这对我来说的确是一个不小的挑战,决定自己每天拿出一个小时来记录下这些小问题。
今天准备探讨一下Uart,这个太常见了,前几天看一个文章说做完流水灯就该做有关Uart的实验了!用的多不代表高级,只能说明它简单易用易上手,但带来的问题也是显而易见了,对复杂应用常用就显得无能无力了。自己之前思考Uart时想起一个问题,网络应用有协议栈,Zigbee组网有自己的协议栈,之前自己也有看过Micro CAN Open(MCO)协议栈,为什么Uart没有自己的一个协议呢?看过STM32的固件库,对UART进行了相关的封装,在其之上能不能进行BSP的封装呢,我觉得应该是可以的!
使用Uart操作的,一般都要有其协议,简单的协议无非是:
报头 + (设备标识符)【可选】 + 数据段 + 报尾【校验】
为了使今天的问题变得简单一些,我们只考虑接收部分的封装。使用过NXP的arm7,Uart模块内部就有一个16字节的FIFO,有过方案就是隔2ms去读一次,在速率不是很高的情况下完全够用,但现在自己看来就是不够看了,完全没有考虑到数据的完整性问题;今天说的接收方案都是在开发板上目前使用的,考虑接收数据需要完整的问题。
一:二维数组的接收——这种方案来自于清华那本很有名的《数据结构》,每次中断接收到数据检查是不是报头,是的话存入第一列中,再次检查到报头,存入第二列中,列数存满回归第一列,处理部分是对每一列的数据进行处理,处理完进行下一列的处理。
接收部分的结构体:
#define REC_QUEUE_NUM 4 //接收队列数目
#define REC_ELEM_LEN 300 //接收数组长度
#define OVER_TIMER 200 //超时200毫秒
typedef struct
{
INT8U Elem[REC_QUEUE_NUM][ REC_ELEM_LEN]; //队列元素
INT8U QueueFront; //队列头
INT8U QueueRear; //队列尾
INT8U QueueNum; //队列数目
INT8U OverTime; //超时时间
INT8U FindFlag; //找到字头
INT8U ElemLen; //正在接受队列已接收字节数目
INT16U Head; //字头
INT16U Tail; //字尾
}RecStruct;
extern RecStruct RecType;
接受完一帧数据,然后处理一下,操作系统中处理起来很方便。
二:环形队列的接收处理——网上把这种称为Ring,本人觉得就是一个大水池子,来了放进去,用的时候取一下(频率高一点,不然池子满了就溢出了),看过老外写的关于这方面代码,感觉还是很棒的。
typedef struct
{
__IO uint32_t rx_head; /*!< UART Rx ring buffer head index */
__IO uint32_t rx_tail; /*!< UART Rx ring buffer tail index */
} UART_RING_BUFFER;
#define __UART1_RX_BUF_MASK (RX_RING_BUFSIZE)
/* Check buf is empty */
#define __USART1_RX_BUF_IS_EMPTY(head,tail) ((head%__UART1_RX_BUF_MASK)==(tail%__UART1_RX_BUF_MASK))
uint32_t UsartRead(uint8_t *rxbuf, uint32_t buflen)
{
uint8_t *data = (uint8_t *) rxbuf;
uint32_t bytes = 0;
/* Temporarily lock out UART receive interrupts during this read so the UART receive interrupt won't cause problems with the index values */
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
/* Loop until receive buffer ring is empty or until max_bytes expires */
while ((buflen > 0) && (!(__USART1_RX_BUF_IS_EMPTY(Usart1.rx_head, Usart1.rx_tail))))
{
/* Read data from ring buffer into user buffer */
*data = Usart1_rx[Usart1.rx_tail];
data++;
/* Update tail pointer */
__USART1_RX_BUF_INCR(Usart1.rx_tail);
/* Increment data count and decrement buffer size count */
bytes++;
if(buflen > 0)
buflen--;
}
}
这种方式配合DMA最好,接受中断后存入RING中,调用UsartRead函数实现读取数据,配合操作系统某个任务负责读取数据即可。
也见过国内的关于环形队列的处理,但是写起来的代码要多许多,判断条件加的比较多,没有上述代码的简洁。
讨论一下Uart的数据接收的意义在于我们思考如何实现一个项目,针对实时需要交互的系统,环形队列应该是很好的选择了,那在其上我们是不是可以实现一个协议(姑且不成协议栈,一个协议栈要包含许多的协议),我想每家公司都会有自己的Uart通信协议标准。只是在此方面,我们还需要总结,目前的这种方式是否可以作为小范围的使用而不出现问题,还需要进一步的验证和大规模数据的验证。
下一步的计划是能够使用ETH,网络这东西一直没有时间去弄,想抽时间好好看一看,据说STM32Cube和以前用的C8051F的Config2很类似,想弄一下看看。
上次和同事聊天,他说到开发有了几年最后发现用什么单片机其实没那么重要,到了一定阶段,心中已经模糊了单片机的概念,或许这就是一种境界,就像高手不一定要有一把宝刀一样,一节树枝、一把扫帚就可以化腐朽为神奇。这也是今天想说的,很多工具的东西到了一定阶段就不需要注意的那么重要了,其实那些真正让我们关心的是算法,这些是我们需要思考的精髓。