Files
BC1C-firmware/app/by_frame.c

166 lines
5.6 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "by_frame.h"
#include "at32f403a_407.h"
#include "lwrb.h"
#include "by_crc16.h"
uint8_t frame_buffer_recv[(2 * (4 + BY_FRAME_DATA_NUM * sizeof(uint32_t))) + 1];
uint8_t frame_buffer_send[4 + BY_FRAME_DATA_NUM * sizeof(uint32_t)];
uint8_t frame_parse_busy;
lwrb_t lwrb_ctx;
void by_frame_init(void)
{
lwrb_init(&lwrb_ctx, frame_buffer_recv, sizeof(frame_buffer_recv)); // lwrb 最大元素数量为 buff 大小减一
}
void by_frame_send(uint8_t cmd, uint32_t *data_array)
{
uint16_t crc_cal = 0;
const uint8_t data_byte_num = BY_FRAME_DATA_NUM * sizeof(uint32_t);
frame_buffer_send[0] = BY_FRAME_HEAD;
frame_buffer_send[1] = cmd;
// 当传入数组不足时,会发生越界情况
memcpy(frame_buffer_send + 2, data_array, data_byte_num);
crc_cal = by_crc16_calculate(frame_buffer_send, 2 + data_byte_num);
// crc_cal = crc16_check(frame_buffer_send, 2 + data_byte_num);
frame_buffer_send[2 + data_byte_num] = (uint8_t)(crc_cal >> 8);
frame_buffer_send[3 + data_byte_num] = (uint8_t)(crc_cal);
for (uint8_t i = 0; i < 4 + data_byte_num; i++) {
while (RESET == usart_flag_get(BY_FRAME_UART_INDEX, USART_TDBE_FLAG))
;
usart_data_transmit(BY_FRAME_UART_INDEX, frame_buffer_send[i]);
}
}
/**
* @brief
*
* @param data_num
* @param data_array
* @todo 将其中写死的数据长度按照宏定义给出
*/
uint8_t by_frame_parse(uint8_t *cmd, uint32_t *data_array)
{
uint32_t len = lwrb_get_full(&lwrb_ctx); // 缓冲区大小
uint8_t status = 0; // 状态 0-未找到帧头 1-找到帧头 2-校验
uint16_t frame_start = 0; // 帧起始位置
uint8_t frame_buf[4 + BY_FRAME_DATA_NUM * sizeof(uint32_t)] = {0}; // 帧
uint8_t buf[(4 + BY_FRAME_DATA_NUM * sizeof(uint32_t)) * 2] = {0}; // 用于解析的数据块
const uint8_t data_byte_num = BY_FRAME_DATA_NUM * sizeof(uint32_t);
// if (len < 2 * (4 + data_byte_num)) { // FIXME 当传递相对值时会出现问题
// // 当前要求缓冲区满
// // (x) 缓冲区内长度小于帧长度,直接返回
// // 要是每次读的时候缓冲区内就只有前一帧的尾部和后一帧的头部,岂不是很尴尬
// // 是不是应该正确解析之后再把过的部分清空?但是是异步操作,实际上缓冲区内已经是新数据了
// // 可是直接读取 fifo 的话也是异步操作
// // 发的慢的话就很有可能有同步问题,导致一直解析不出来
// // 喵的,为啥不直接丢中断里解析算了
// // 目前的解决办法大概是缓冲区开两帧长的大小,然后一次性读完
// // 读取的时候不清除,等待新帧覆盖
// // 用 lwrb 的话就只能清除了
// return 1;
// }
lwrb_sz_t valid_num = 0;
lwrb_sz_t invalid_num = 0;
uint8_t frame_head = BY_FRAME_HEAD;
// lwrb_find(&lwrb_ctx, &frame_head, 1, 0, &invalid_num);
// lwrb_skip(&lwrb_ctx, invalid_num);
// // TODO 优化逻辑,先找 0xEF判断缓冲区里帧头后的长度足够则进入解析
// // 从环形缓冲区里读取数据,仅读取一个帧长
// lwrb_read(&lwrb_ctx, buf, 4 + BY_FRAME_DATA_NUM * sizeof(uint32_t));
// 如果没找到帧头,跳出
if (!lwrb_find(&lwrb_ctx, &frame_head, 1, 0, &invalid_num)) {
lwrb_skip(&lwrb_ctx, len);
return 1;
}
valid_num = len - invalid_num; // 从帧头开始,剩下的数据长度
lwrb_skip(&lwrb_ctx, invalid_num);
// 如果没有足够的数据,跳出
if (valid_num < 4 + data_byte_num) {
return 1;
}
lwrb_read(&lwrb_ctx, buf, 4 + data_byte_num);
// 递归解析有效帧
while (1) {
if (0 == status) // 没找到帧头
{
// 读到最后一个元素还没找到帧头
if (frame_start >= len - 2) {
return 1;
}
uint16_t temp = (buf[frame_start] | (buf[frame_start + 1] << 8));
frame_start++;
// 递归寻找帧头,现在只有一个帧头了,摆大烂不想改就这样了
if (BY_FRAME_HEAD == (uint8_t)(temp & 0xFF)) {
status = 1; // 找到了好耶
}
continue;
}
// 开始读数据
if (1 == status) {
// 剩下的数据不够组成一帧
if ((frame_start + 4 + data_byte_num - 1) > len) {
// 解析出错,缓冲区中没有有效帧
return 1;
} else {
// 复制到帧缓冲区,减一是因为之前多加了一次
memcpy(frame_buf, buf + frame_start - 1, 4 + data_byte_num);
status = 2;
}
continue;
}
if (2 == status) // 校验 CRC
{
uint16_t crc_cal = by_crc16_calculate(frame_buf, 2 + data_byte_num);
if ((frame_buf[2 + data_byte_num] << 8 | frame_buf[2 + data_byte_num + 1]) == crc_cal) {
// 解析成功了✌
// 复制数据
if (NULL != (frame_buf + 2)) {
*cmd = frame_buf[1];
memcpy(data_array, frame_buf + 2, data_byte_num);
}
return 0;
} else {
status = 0;
// 这样无法应对连续帧之间缺字节的的问题,但是减少了重新遍历寻找帧头的时间
// frame_start += (8 - 1);
// 从上一个帧头之后开始解析
frame_start += (2 - 1);
continue;
}
}
}
return 1;
}
void by_frame_parse_uart_handle(uint8_t data)
{
lwrb_write(&lwrb_ctx, &data, 1);
}
/**
* @brief 定时器回调,用于接收超时判断 1ms 调用一次
*
*/
void by_frame_parse_timer_handle(void)
{
}