更新了视觉上位机和CAN多机通信的文档

This commit is contained in:
NeoZeng 2022-11-29 23:11:37 +08:00
parent b75a848ef1
commit fc37baed23
7 changed files with 276 additions and 93 deletions

BIN
assets/CANcomm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -5,35 +5,91 @@
> TODO:
>
> 1. 增加数据长度可变的协议支持
> 2. 简化接收流程
> 1. 对`CANCommGet()`进行修改,使得其可以返回数据是否更新的相关信息。
## 总览和封装说明
CAN comm是用于CAN多机通信的模块。你不需要关心实现的协议**只需要传入你想发送/接收的结构体的长度**(通过`sizeof()`获取即可。单帧发送长度目前限制为60字节如果需要加长可以在`can_comm.h`中修改`CAN_COMM_MAX_BUFFSIZE`的定义值。注意CAN的负载以及使用此模块的发送频率否则可能造成堵塞。
## 代码结构
.h中放置的是数据定义和外部接口以及协议的定义和宏.c中包含一些私有函数。
## 外部接口
```c
CANCommInstance *CANCommInit(CANComm_Init_Config_s* comm_config);
void CANCommSend(CANCommInstance *instance, uint8_t *data);
void *CANCommGet(CANCommInstance *instance);
```
第一个函数将会初始化一个CANComm实例返回其指针。使用CANComm进行通信的应用应该保存返回的指针。初始化需要传入一个初始化结构体。请在应用初始化的时候调用该函数。推荐的结构体配置方式如下
```c
/* 初始化结构体的定义 */
typedef struct
{
can_instance_config_s can_config;
uint8_t send_data_len; //发送数据长度
uint8_t recv_data_len; //接收数据长度,两者请用sizeof获取
} CANComm_Init_Config_s;
CANComm_Init_Config_s cconfig = {
.can_config = {
.can_handle=&hcan1,
.tx_id=0x02,
.rx_id=0x03},
.send_data_len = sizeof(your_data_structure),
.recv_data_len = sizeof(recv_data_structure)
};
```
第二个函数将会通过CAN发送一帧数据。这里的“帧”不是CAN的一帧报文而是你使用的数据。在CANComm初始化的时候使用sizeof获得你要发送的数据的长度。调用此函数时将你的数据结构或数据cast成`u8`类型的指针传入即可。==**特别注意,你的结构体需要使用预编译指令`#pragma pack(1)`以及`#pragma pack()`包裹**==,从而进行字节压缩而不进行字节对齐,如:
```c
#pragma pack(1)
typedef struct
{
uint8_t aa;
float bb;
double cc;
uint16_t dd;
} some_struct;
#pragma pack()
```
只有这样这个结构体才不会进行padding扩充字节实现字节对齐。你可以尝试一下如果不使用pack处理上面的结构体将会占据18个字节以上的空间开启pack后会降低至15。更多关于字节对齐的内容自行查询。
> 后期可能更新字节对齐和内存访问相关的教程。
`CANCommGet()`是获取来自CANComm接收到的数据的接口返回值为一个void类型指针你需要通过**强制类型转换**将其变为你设定的接收类型指针,然后就可以访问其数据了。
## 私有函数和变量
```c
static CANCommInstance *can_comm_instance[MX_CAN_COMM_COUNT] = {NULL};
static uint8_t idx;
static void CANCommResetRx(CANCommInstance *ins);
static void CANCommRxCallback(can_instance *_instance);
```
第一个指针数组保存所有的can comm实例从而在callback中区分不同实例。idx是初始化使用的索引用于确定每个can comm实例指针存放的位置。
`CANCommResetRx()`在`CANCommRxCallback()`中多次被调用,用于清空接收缓冲区。
`CANCommRxCallback()`是CAN comm初始化can实例时的回调函数用于can接收中断进行协议解析。
## 接收解析流程
CAN comm的通信协议如下
| 帧头 | 数据长度 | 数据 | crc8校验 | 帧尾 |
| ----------- | -------- | ---------------- | -------- | ----------- |
| 's'0x73 | 0~255 | 最大60可修改 | 校验码 | 'e'0x65 |
| 1-byte | 1-byte | n-byte | 1-byte | 1-byte |
接收的流程见代码注释。
流程图如下:![未命名文件](../../assets/CANcomm.png)

View File

@ -14,7 +14,7 @@
#include "seasky_protocol.h"
/* use usart1 as vision communication*/
Vision_Recv_s recv_data;
static Vision_Recv_s recv_data;
// @todo:由于后续需要进行IMU-Cam的硬件触发采集控制,因此可能需要将发送设置为定时任务,或由IMU采集完成产生的中断唤醒的任务,
// 使得时间戳对齐. 因此,在send_data中设定其他的标志位数据,让ins_task填充姿态值.
// static Vision_Send_s send_data;
@ -62,8 +62,3 @@ void VisionSend(Vision_Send_s *send)
get_protocol_send_data(0x02, flag_register, &send->yaw, 3, send_buff, &tx_len);
USARTSend(vision_usart_instance, send_buff, tx_len);
}
Vision_Recv_s *VisionGetcmd()
{
return &recv_data;
}

View File

@ -7,6 +7,78 @@
#define VISION_RECV_SIZE 36u
#define VISION_SEND_SIZE 36u
typedef enum
{
NO_FIRE = 0,
AUTO_FIRE = 1,
AUTO_AIM = 2
} Fire_Mode_e;
typedef enum
{
NO_TARGET = 0,
TARGET_CONVERGING = 1,
READY_TO_FIRE = 2
} Target_State_e;
typedef enum
{
NO_TARGET_NUM = 0,
HERO1 = 1,
ENGINEER2 = 2,
INFANTRY3 = 3,
INFANTRY4 = 4,
INFANTRY5 = 5,
OUTPOST = 6,
SENTRY = 7,
BASE = 8
} Target_Type_e;
typedef struct
{
Fire_Mode_e fire_mode;
Target_State_e target_state;
Target_Type_e target_type;
float pitch;
float yaw;
} Vision_Recv_s;
typedef enum
{
BLUE = 0,
RED = 1
} Enemy_Color_e;
typedef enum
{
MODE_AIM = 0,
MODE_SMALL_BUFF = 1,
MODE_BIG_BUFF = 2
} Work_Mode_e;
typedef enum
{
BIG_AMU_10 = 10,
SMALL_AMU_15 = 15,
BIG_AMU_16 = 16,
SMALL_AMU_18 = 18,
SMALL_AMU_30 = 30,
} Bullet_Speed_e;
typedef struct
{
Enemy_Color_e enemy_color;
Work_Mode_e work_mode;
Bullet_Speed_e bullet_speed;
float yaw;
float pitch;
float roll;
// uint32_t time_stamp; // @todo 用于和相机的时间戳对齐
} Vision_Send_s;
/**
* @brief
*

View File

@ -1 +1,134 @@
# master_process
<p align='right'>neozng1@hnu.edu.cn</p>
> TODO:
>
> 1. 补全标志位解析和发送设置的代码
> 2. 增加发送给视觉数据的时间戳用于数据对齐
## 总览和封装说明
模块包含了和视觉通信的初始化、向上位机发送信息的接口和模块的串口的回调处理。接口的定义统一可以方便的替换成其他通信方式如CAN。
## 代码结构
.h文件内包括了外部接口和与**视觉上位机通信的数据结构定义**以及模块对应的宏。c文件内为私有函数和外部接口的定义。
本模块主要是对协议解析的处理和协议发送的封装,实际内容不多。协议相关内容都在`seasky_protocol.h`中。
## 类型定义
和视觉通信所必须的标志位和数据。包括开火模式,目标状态,目标类型,接收/发送数据结构体。
```c
typedef enum
{
NO_FIRE = 0,
AUTO_FIRE = 1,
AUTO_AIM = 2
} Fire_Mode_e;
typedef enum
{
NO_TARGET = 0,
TARGET_CONVERGING = 1,
READY_TO_FIRE = 2
} Target_State_e;
typedef enum
{
NO_TARGET_NUM = 0,
HERO1 = 1,
ENGINEER2 = 2,
INFANTRY3 = 3,
INFANTRY4 = 4,
INFANTRY5 = 5,
OUTPOST = 6,
SENTRY = 7,
BASE = 8
} Target_Type_e;
typedef struct
{
Fire_Mode_e fire_mode;
Target_State_e target_state;
Target_Type_e target_type;
float pitch;
float yaw;
} Vision_Recv_s;
typedef enum
{
BLUE = 0,
RED = 1
} Enemy_Color_e;
typedef enum
{
MODE_AIM = 0,
MODE_SMALL_BUFF = 1,
MODE_BIG_BUFF = 2
} Work_Mode_e;
typedef enum
{
BIG_AMU_10 = 10,
SMALL_AMU_15 = 15,
BIG_AMU_16 = 16,
SMALL_AMU_18 = 18,
SMALL_AMU_30 = 30,
} Bullet_Speed_e;
typedef struct
{
Enemy_Color_e enemy_color;
Work_Mode_e work_mode;
Bullet_Speed_e bullet_speed;
float yaw;
float pitch;
float roll;
// uint32_t time_stamp; // @todo 用于和相机的时间戳对齐
} Vision_Send_s;
```
## 外部接口
```c
Vision_Recv_s *VisionInit(UART_HandleTypeDef *_handle);
void VisionSend(Vision_Send_s *send);
```
给`VisionInit()`传入串口handle将初始化一个视觉通信模块返回值是接收数据的结构体指针。拥有视觉模块的应用应该在初始化中调用此函数并保存返回值的指针。
调用`VisionSend`并传入填好发送数据的结构体,会通过底层的通信模块向视觉发送一帧报文。
## 私有函数和变量
```c
static Vision_Recv_s recv_data;
static usart_instance *vision_usart_instance;
static void DecodeVision()
{
static uint16_t flag_register;
get_protocol_info(vision_usart_instance->recv_buff, &flag_register, (uint8_t*)&recv_data.pitch);
// TODO: code to resolve flag_register;
}
```
第一个是保存接收数据的结构体,其指针将会在初始化的时候返回给拥有者。目前最多只能配置一个视觉模块。
第二个是该模块拥有的串口实例指针用于调度其底层的发送和接收。如果要换成CAN/SPI等替换成对应实例并修改初始化和发送的实现即可。
`DecodeVision()`是解析视觉接收数据的回调函数,会在串口接收回调中被调用。如果修改通信协议,只需要更改
`get_protocol_info()`

View File

@ -104,10 +104,10 @@ uint16_t get_protocol_info(uint8_t *rx_buf, // 接收到的原始数据
uint16_t *flags_register, // 接收数据的16位寄存器地址
uint8_t *rx_data) // 接收的float数据存储地址
{
// 放在静态区,避免反复申请栈上空间
static protocol_rm_struct pro;
static uint16_t date_length;
volatile size_t s = sizeof(pro);
volatile size_t aa = sizeof(Vision_Recv_s);
if (protocol_heade_Check(&pro, rx_buf))
{
date_length = OFFSET_BYTE + pro.header.data_length;

View File

@ -5,81 +5,8 @@
#include <stdint-gcc.h>
#define PROTOCOL_CMD_ID 0XA5
#define OFFSET_BYTE 8 // 出数据段外,其他部分所占字节数
typedef enum
{
NO_FIRE = 0,
AUTO_FIRE = 1,
AUTO_AIM = 2
} Fire_Mode_e;
typedef enum
{
NO_TARGET = 0,
TARGET_CONVERGING = 1,
READY_TO_FIRE = 2
} Target_State_e;
typedef enum
{
NO_TARGET_NUM = 0,
HERO1 = 1,
ENGINEER2 = 2,
INFANTRY3 = 3,
INFANTRY4 = 4,
INFANTRY5 = 5,
OUTPOST = 6,
SENTRY = 7,
BASE = 8
} Target_Type_e;
typedef struct
{
Fire_Mode_e fire_mode;
Target_State_e target_state;
Target_Type_e target_type;
float pitch;
float yaw;
} Vision_Recv_s;
typedef enum
{
BLUE = 0,
RED = 1
} Enemy_Color_e;
typedef enum
{
MODE_AIM = 0,
MODE_SMALL_BUFF = 1,
MODE_BIG_BUFF = 2
} Work_Mode_e;
typedef enum
{
BIG_AMU_10 = 10,
SMALL_AMU_15 = 15,
BIG_AMU_16 = 16,
SMALL_AMU_18 = 18,
SMALL_AMU_30 = 30,
} Bullet_Speed_e;
typedef struct
{
Enemy_Color_e enemy_color;
Work_Mode_e work_mode;
Bullet_Speed_e bullet_speed;
float yaw;
float pitch;
float roll;
// uint32_t time_stamp; // @todo 用于和相机的时间戳对齐
} Vision_Send_s;
typedef struct
{
struct