# Chhol-CMD-STC8-ECBM **Repository Path**: lee8871/chhol-cmd-stc8-ecbm ## Basic Information - **Project Name**: Chhol-CMD-STC8-ECBM - **Description**: 斥候模块命令端口通信程序库,基于STC8单片机。 - **Primary Language**: C/C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-09-17 - **Last Updated**: 2024-07-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CC9D - demo说明 # 程序说明 这个程序提供了基础的斥候模块CMD串口通信协议库函数。CC9D会实现包头包尾发送,拆装转义数据,添加CRC等操作,将整体的包呈递给上层程序。 和斥候模块相关的库函数保存在 ./CC9D/ 库函数的测试程序保存在 ./Chhol-CMD-STC8-ECBM/ecbm_cc9d_test/ 这个测试程序使用了两个斥候模块,和电进行通信 ![image](assets/image-20220917203352-ddnoa0d.png)​ ![78FD6B96FCC9B46A0B73CE37B9633EE2](assets/78FD6B96FCC9B46A0B73CE37B9633EE2-20220917202558-hw4gx1o.png)​ # 发包 ## 发包程序 程序启动后,会每隔5秒向`0xeff1`节点发送一个包: ```c void sendPkg(){ cc9d_startSend(0x20,0x22,0xeff1); cc9d_sendData('1'); cc9d_sendData('2'); cc9d_sendData('3'); cc9d_sendData('4'); //Big endding cc9d_sendData((uint8)(test_num>>8)); cc9d_sendData((uint8)test_num); cc9d_finishSend(); test_num++; } ``` 程序使用这三个函数来完成发包: ```c //外部程序想要发送包时,使用此函数开始一次发送 void cc9d_startSend( uint8 from_port, uint8 to_port, uint16 remote_addr); //外部程序开始一次发送后,调用此函数多次,以发送后续数据 void cc9d_sendData(byte b); //外部程序发出了全部想要的数据后,调用此函数完成发送。 void cc9d_finishSend(); ``` 程序首先调用cc9d_startSend启动一次发包,随后使用cc9d_sendData依次填充后续数据,最后使用cc9d_finishSend结束发包。 这样我们就可以在0xeff1节点看到这样的数据: ![image](assets/image-20220917202021-1zcyccb.png) ## 在多线程中使用 如果发包可能多线程进行(例如需要在中断函数中进行发包),则必须对发包函数添加额外的线程锁进行保护。 ## 外部调用函数 CC9D发包函数会调用这个程序,将一个byte写入串口的操作,这个函数必须由用户完成。 ```c //此模块需要发送1byte时,会调用此函数。 extern uint8 _cc9d_sendByte(uint8 b); ``` ## 命名规则 在CC9D库中,函数命名的结构为 *库名称_函数名称*,如果是需要外部实现供本程序调用的函数,则吗,命名为 _*库名称_函数名称* ‍ # 收包 ## 接收数据 当串口收到1byte数据时,需要调用cc9d_recvbyte,将数据交给CC9D程序。 ```c //外部程序收到1Byte数据时,调用此函数将数据交给解码器。 void cc9d_recvbyte(byte b);//解码器收到一个包后,会调用此函数进行包处理 extern uint8 _cc9d_receivePkg(byte xdata* buf, uint8 recv_len); ``` CC9D程序会按顺序整理这些数据,一旦从这些数据中接收到一个完整的包,就会调用`_cc9d_receivePkg`,在主程序中实现这个函数,即可完成收包 ```c uint8 _cc9d_receivePkg(byte xdata* buf, uint8 recv_len){ sCc9dPkg xdata* pkg = (sCc9dPkg xdata*)buf; P25=!P25; if(pkg->remote_addr == 0xeff1){ P26=!P26; } if( (pkg->to_port == 0x30) &&(recv_len = 4+2)){ //Big endding test_num = *((uint16 xdata*)&(pkg->datas[0])); P27=!P27; } return 0; } ``` 可以使用 pkg->remote_addr 、pkg->to_port 来获取包的地址和端口号,recv_len表示包的整体长度,长度包括地址和端口号,数据部分的长度为recv_len-4 。 # 循环队列 通常不建议在串口程序中进行包解析,因此我在串口中断中将数据转移到了循环队列中,收到9D后则在主函数中进行数据解析: ```c //串口接收完成中断,会将接收到的数据保存到队列中,如果遇到0x9d也就是包结束标记,则说明可能收到了一个包 void uart2_receive_callback(void){ uint8 b = S2BUF; ByteQueue_enqueue(&recv_queue,S2BUF); if(b == 0x9d){ flag_cc9d_receive ++; } } ``` 同样的,发包时也会将数据存入循环队列,随后依次转义给串口进行发出: ```c //创建一个循环队列,用来保存发送序列 #define QUEUE_SIZE 128 byte xdata send_queue_buf[QUEUE_SIZE]; sByteQueue send_queue = { 0,0,QUEUE_SIZE-1,send_queue_buf }; uint8 is_sending = 0;//串口正在工作的标记 //发送序列函数,如果串口正在工作就将数据存入缓存。否则将数据发往 uint8 _cc9d_sendByte(uint8 b){ EA = 0; if(is_sending == 0){ UART2_SET_REG_S2BUF(b); is_sending = 1; }else{ ByteQueue_enqueue(&send_queue,b); } EA = 1; return 0; } //串口发送完成中断,从循环队列中取出下一个数进行发送,如果没有后续数要发送,则停止发送 void uart2_send_callback(void){ int16 b = ByteQueue_dequeue(&send_queue); if(b!=-1){ UART2_SET_REG_S2BUF(b); } else { is_sending = 0; } } ``` # 特别感谢 感谢ECBM的作者,有关此程序的详细说明请移步 :https://gitee.com/jackchio/ecbm_library ‍