我正在为Atmel的XMEGA设备实施一个小型Zigbee库。 Zigbee无线电使用内部USART与MCU通信。
当我开始编写库时,我使用固定数组的简单方法以及接收数据的中断。一旦我有完整的命令(我知道我有一个完整的命令,因为Zigbee说明帧长度 - 减去最后的起始分隔符和校验和),我在中断服务例程中设置一个标志并将数组复制到另一个数组中我的主要()。
复制完数组后,另一个例程 ZBProcessFrame 接管并解析框架并采取相应的操作。
这种方法的潜在问题是,虽然我正在复制数组,但另一条消息可能会改变这个共享变量。
通过在线阅读,似乎我可以在复制数组时关闭我的中断,或者我应该使用循环缓冲区,因为这样我就可以完全避免复制数组了。我已经成功实现了一个32字节的循环缓冲区,但现在我的问题是,如何判断实际数据的开始位置以及自起始分隔符以来已经有多少字节。我的ISR只有以下内容:
ISR(Receiver Interrupt)
{
ring->add(USART_Data);
}
我应该在这里检查起始分隔符并设置一个标志,这里有一个有效的命令吗?然后main()可以连续查看该标志,如果提出,则表示存在有效命令。
这是一种有效的方法还是我应该寻找替代方案?
答案 0 :(得分:4)
循环缓冲区是一个很好的设计。我会让ISR尽可能简单,就像你拥有它一样。如果你的main
是一个超级循环,那么我会添加一个新例程的调用,如ZBReceiveFrame
,它从环形缓冲区中读取下一个可用字节(直到所有可用字节)并处理它在国家机器。例如,第一个状态期望帧起始分隔符,并在接收到它时前进到下一个状态。下一个状态接收并解释帧长度。下一个状态接收帧体,最终状态验证帧CRC(所有仅仅是示例)。状态都可以使用switch语句在ZBReceiveFrame
内实现。 ZBReceiveFrame
中使用的状态变量应该是静态的,以便从一次调用到下一次调用时记住状态。当最终状态标识有效帧时,它会设置标记以提示main
调用ZBProcessFrame
。 ZBReceiveFrame
处理一个或所有可用字符是否取决于main
超级循环中其他时间的关键时间。
这种技术创造了loose coupling,这是可取的。 ISR仅负责将字节接收到环形缓冲区中,并且不知道帧是什么。 ZBReceiveFrame
只知道如何从环形缓冲区读取并分隔帧但不知道如何解释数据。 ZBProcessFrame
负责解释帧内的数据,但不知道环形缓冲区。
答案 1 :(得分:1)
您的循环缓冲区需要具有读写位置。 WR指向下一个空白空间,RD指向最早的接收字节。
但这意味着您需要同时操作这两个变量:RD应该永远不会超过WR。由于您不能通过单个指令执行此操作,因此您需要在读取或写入任何一个时禁用中断。
我通常这样做:
具有从中断例程使用的putByte
函数。 ISR没有做任何其他事情。
拥有主程序中使用的getByte
函数。此功能将暂时禁用中断。如果缓冲区为空,则返回特殊值(-1
或类似值。
在每个主循环迭代中,将可用字节从循环缓冲区复制到主缓冲区。在循环缓冲区为空或主缓冲区已满后检查接收的数据。如果没有足够的字节,请在下一个主循环迭代中复制更多。否则,处理框架。
ISR中的时间应该短(仅将字节复制到循环缓冲区),时间阻塞ISR应该短(读取循环缓冲区时没有错误检查,只复制到主缓冲区)