目录
前言
CVSK协议是我近几年来玩各种模块,各类厂家的协议规范中,精简借鉴而来,其稳定性尚待校验。但功能还是可以百分百保证没问题,好的废话不多说我们这就开始进入主题——CVSK协议栈的协议定义及代码编写等介绍。
压缩包密码:your_k
一、资料下载
- 百度网盘:链接:https://pan.baidu.com/s/1FP6BwrnznkmilJolmGgr7w?pwd=l360
提取码:l360
- CSDN:文章置顶位置
二、CVSK协议介绍
- 名字由来,由于这个协议是根据各类模块厂家的通信协议改进,借鉴而来所以前缀是CV即ctrl c,ctrl v的意思。而后缀SK则是stack的意思。
故笔者的协议帧头帧尾就是这四个字母的ascii即C — 0x43, V —0x56, S—0x53, K—0x48。 - 协议定义:
由以上截图可简明看出,一条CVSK协议由帧头、协议长、数据长、设备型号、功能字、指令字、保留位(不一定有)、数据、校验字加帧尾构成。
帧头帧尾 —— 笔者在上面说明了就不再复述;
协议长 —— 表示的是除数据长度以外,一条协议的长度;
数据长 —— 顾名思义就是数据位的长度;
设备型号 —— 由两个字节构成,用于区分不同的设备控制,前一个字节标志一类的设备型 号,后面一个代表同一设备型号不同的设备ID(主要考虑到在某一场景下可能有不止一个的同一型号的设备需要控制);
功能字 —— 某一指定功能的一个大类;
指令字 —— 某一指定功能大类下的一个指令;
保留位 —— 用于未来有可能的拓展,由协议长度来决定有无;
数据 —— 传输的数据;
校验字 —— 将除去帧尾及校验字的其他所有数据相加;
- 协议定义举例——初代空气净化器
这个笔者暂时还没想好怎么解释,你们先看,有问题评论区提出。我会根据你们问题来解释,或者各位自行参悟我不做解释😂。
三、工程目录添加及代码编写
- 工程目录添加
添加这些文件
添加头文件 - 代码编写——cvsk_port.c
驱动层:void cvsk_message_copy(uint8_t *message, uint16_t len) 此函数对应放置在串口驱动的空闲中断函数中如下#include "cvsk.h" /* cvsk 转存数据 --- 驱动层 */ csk_msg_buf_t csk_msg_buf = {0}; void cvsk_message_copy(uint8_t *message, uint16_t len) { csk_msg_buf.len = (uint8_t)len; for (uint8_t i = 0; i < csk_msg_buf.len; i++) { csk_msg_buf.buf[i] = message[i]; } } /* 控制字、命令字查表 --- 解析层 */ void csk_look_up_table(csk_msg_buf_t *message_data, const cvsk_fuc_table_t *fuc_table, uint16_t table_size) { for (uint16_t i = 0; i < table_size; i++) // 遍历表 { if (fuc_table[i].function == message_data->buf[CSK_FUNCTION] && // 判断功能字 fuc_table[i].command == message_data->buf[CSK_COMMAND]) // 判断指令字 { fuc_table[i].fun(message_data); // 执行查到的函数 } } } /* 设备型号查寻 --- 解析层 */ void csk_look_up_device_type(csk_msg_buf_t *message_data) { uint16_t equipment_num = message_data->buf[CSK_DEVICE_TYPE_H] << 8 | message_data->buf[CSK_DEVICE_TYPE_L]; switch (equipment_num) { case AIR_CLEARN_FIRST_GENERATION: csk_look_up_table(message_data, fgac_fun_table, fgac_infor.table_size); break; default: printf("device type error !"); } } /* 帧头帧尾及校验和校对 --- 解析层 */ void csk_receive_process(csk_msg_buf_t *message_data) /* 测试底板消息接收处理 */ { /* 判断接收帧头是否为协议帧头 */ if ((TP_HEAD >> 8) != message_data->buf[CSK_HEAD_H] || (uint8_t)TP_HEAD != message_data->buf[CSK_HEAD_L]) { printf("message receive error data_head ! \n"); return; } if ((TP_TAIL >> 8) != message_data->buf[message_data->len - 2] || (uint8_t)TP_TAIL != message_data->buf[message_data->len - 1]) { printf("message receive error data_tail ! \n"); return; } /* 判断校验和是否为正确 */ if (message_data->buf[message_data->len - 3] != check_sum_u8(message_data->buf, message_data->len - 3)) { printf("message receive error data_check ! \n"); return; } csk_look_up_device_type(message_data); }
这个意思是每当串口接收到数据并触发串口中断后就将串口的缓存数据搬运到下图红色方框中定义的csk_msg_buf缓存中
帧头帧尾及校验和校对 --- 解析层:此处会对驱动层中转运得到的缓存数据csk_msg_buf进行解析,也就是判断帧头、尾及校验和。
如果正确则会执行csk_look_up_device_type(message_data);函数也就是下一步,设备型号查询(解析层)。在这一步是通过一个switch语句来查找对应的设备型号对应指令中的第五个到第六个字节,接着如果设备型号对应的正确,就会执行下图红框中的函数
这个函数传入了驱动层的消息数据,对应的设备函数表和此表大小。进入这个函数后就到了控制字、命令字查表(解析层),在这里有个for循环遍历整个表来查找对应的控制字和命令字,在这里查的表如下图所示。
到此解析层结束
接下来就是执行层,我们在查表函数中会执行表中的函数如下图
由于不同的设备会对应不同的函数,因此笔者专门创建一个文件夹来放置对应的函数
如下图
由于这部分由于硬件设备还在未来计划中所以目前只能通过printf函数来演示效果,至于ota等功能,还得待我学完QT来制作上位机才能够进行延时(有生之年吧我也不知到什么时候用空来做😂)。
在这里就是写对应的cvsk_first_generation_air_cleaner.c文件了
到这也就先告一段落了,因为完成这里还需要对flash进行相关操作来配置,以及硬件支持。如果没有像我一样复杂的操作其实就已经大致完成(后续有空继续完善,最近事太多了加班)。#include "cvsk_protocol/cvsk_first_generation_air_cleaner.h" fgac_infor_t fgac_infor; const cvsk_fuc_table_t fgac_fun_table[] = { {0x01, 0x01, fgac_switch_power_control_request}, {0x02, 0x01, fgac_firmware_version_query}, // {0x04, 0x09, }, }; void fgac_infor_init(void) { // if (fgac_initial_power_on_flag) // { fgac_infor.table_size = sizeof(fgac_fun_table) / sizeof(cvsk_fuc_table_t); for (uint16_t i = 0; i < 6; i++) { fgac_infor.firmaware_version[6 - i] = (uint8_t)(FGAC_FIRMAWARE_VERSION >> (8 * i)); } // } // else // { // ; // } } void fgac_switch_power_control_request(csk_msg_buf_t *message_data) { uint8_t send_data[] = {0x43, 0x56, 0x0B, 0x01, 0xA1, 0x01, 0x01, 0x01, 0xAA, 0xFF, 0x53, 0x4B}; // printf("fgac_switch_power_control_query ! \n"); /* 设置对应状态 */ fgac_infor.state = (fgac_state_t)message_data->buf[CSK_DATA]; send_data[CSK_DATA] = fgac_infor.state; /* 对应指令操作 */ if (message_data->buf[CSK_DATA] == FGAC_POWER_OFF) { // printf("fgac_power_off \n"); } else if (message_data->buf[CSK_DATA] == FGAC_POWER_ON) { // printf("fgac_power_on \n"); } else if (message_data->buf[CSK_DATA] == FGAC_RESET) { // printf("fgac_reset \n"); } else { // printf("fgac_standby \n"); } /* 回复上位机 */ send_data[sizeof(send_data) - 3] = check_sum_u8(send_data, sizeof(send_data) - 3); bsp_usart0_tx(send_data, sizeof(send_data)); } void fgac_firmware_version_query(void) { uint8_t send_data[] = {0x43, 0x56, 0x0B, 0x06, 0xA1, 0x01, 0x02, 0x01, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xFF, 0x53, 0x4B}; // printf("fgac_firmware_version_query ! \n"); /* 回复数据赋值 */ for (uint16_t i = 0; i < 6; i++) { send_data[CSK_DATA + i] = fgac_infor.firmaware_version[i]; } /* 回复上位机 */ send_data[sizeof(send_data) - 3] = check_sum_u8(send_data, sizeof(send_data) - 3); bsp_usart0_tx(send_data, sizeof(send_data)); }
- 完整代码:
见资料下载中的工程里。
四、CVSK协议测试(效果展示)
- 准备工作,串口指令导入:
至于设置串口为8N1,波特率115200,等操作不再细讲 - 指令测试
总结
各位今天先到这,有空在更,有点摸😂(2024-3-6)
好的目前先更到这,后续在修改,有问题敬请提出,没写多少次教程,若有问题还望多多指点(2024-3-7)