乐于分享
好东西不私藏

CCP协议代码实现—源码获取

CCP协议代码实现—源码获取

大家好,我是左工,经过多天的努力,我们终于完成了CCP的基本操作和基本命令学习。从本篇开始我们将学习CCP协议的代码实现。再次强调一点基本命令的学习虽然枯燥,但这是CCP协议实现的基础,希望大家能反复揣摩直到完全理解。对于本文需要的协议栈,可以关注公众号,加左工微信,给左工发送“CCP源码”即可获取上述协议栈代码。

CCP的代码结构

CCP协议栈的代码比较简单,只有六个文件如下所示
这六个文件的作用分别如下所示:

ccp_core.c

协议栈核心实现

ccp_core.h

协议栈核心实现头文件

ccp_daq.c

DAQ管理实现

ccp_types.h

数据类型定义

ccp_interface_tc.c

芯片平台适配接口

ccp_interface_tc.h
芯片平台适配接口头文件
其中前四个文件是不需要修改的,只需要根据芯片平台将“ccp_interface_tc.c”这个文件中给出的提示将相关函数给补齐就行了。如下所示就是ccp_interface_tc.c文件中关于函数tc_ccp_can_send()的描述。
/** * @brief CCP的CAN报文发送接口 * @param handle CCP句柄 * @param buffer 数据缓冲区 * @param length 数据长度 * @param odt_index odt索引 * @return 错误码 *  * 用户需要在此函数中实现CAN消息发送: * - 配置发送消息对象 * - 填充CAN数据 * - 启动消息发送 */uint8_t tc_ccp_can_send(ccp_handle_t *handle, uint8_t *buffer, uint8_t length, uint8_t odt_index){    /* TODO: 用户需要实现CAN消息发送 */    /* 示例实现:     * 1. 选择合适的发送消息对象     * 2. 配置消息对象的CAN ID和数据长度     * 3. 将数据复制到消息对象的数据寄存器     * 4. 设置发送请求位     * 5. 等待发送完成或检查发送状态     */    return CCP_OK;}
ccp_interface_tc.c文件中需要补充的函数也不多,只有如下8个函数。每一函数都有详细的解释和定义。左工后续也会教大家如何编写接口文件。
/* CCP的CAN报文发送接口*/uint8_ttc_ccp_can_send(ccp_handle_t *handle, uint8_t *buffer, uint8_t length, uint8_t odt_index);uint8_ttc_ccp_can_tx_callback(ccp_handle_t *handle);/* 内存读写接口 */uint8_ttc_memory_read(uint32_t address, uint8_t *data, uint32_t size);uint8_ttc_memory_write(uint32_t address, uint8_t *data, uint32_t size);/* 标定页管理 */uint8_ttc_cal_page_init(void);uint8_ttc_cal_page_switch(uint32_t page_address);uint32_ttc_cal_page_get_active(void);/* 定时器 */uint32_ttc_timer_get_tick(void);
对于其他文件的代码,大家不用担心,也有详细的注释说明,如下为ccp_core.c文件中的部分代码,大家可以根据注释非常清楚的读懂代码。
/* CCP协议栈处理 */uint8_tccp_process(ccp_handle_t *handle, uint8_t *data, uint8_t length){    ccp_packet_t packet;    ccp_response_t response;    uint8_t result;    if (!handle || !data || length < 8) {        return CCP_ERR_INVALID_PARAM;    }    /* 解析数据包 */    packet.command = data[0];    packet.counter = data[1];    memcpy(packet.data, &data[2], sizeof(packet.data));    /* 处理命令 */    result = ccp_process_command(handle, &packet, &response);
各位可以关注公众号,加左工微信,给左工发送“CCP源码”即可获取上述协议栈代码。
CCP的代码集成
我们已经有了协议栈代码了,那么如何集成呢?首先请大家回顾前面的文章搭建英飞凌Aurix系列TC334芯片开源免费开发工具链,将开发环境搭建起来。然后按照文章中的方法,创建一个工程。并找到工程所在文件夹将下载的协议栈代码文件直接复制到工程的跟目录下面即可。
然后在AurixDev编译环境中右键单击工程名,再点击“Refresh”即完成了协议栈代码的集成。
CCP的代码调用
协议栈代码的调用主要在Cup0_Main.c这个文件中进行,首先是引入头文件。
#include"drive_tc.h"//底层驱动#include"ccp_core.h"//CCP实现
中“drive_tc.h”文件主要作用是驱动CAN模块和GTP定时器模块,大家可以参考文章Aurix英飞凌TC334芯片CAN模块配置Aurix英飞凌TC334芯片GPT模块定时器配置完成底层驱动的编写。后续左工也会单独讲解如何编写驱动文件。
然后就是建立CCP模块相关的变量。
/* 全局CCP句柄 */ccp_handle_t g_ccp_handle;/* 设备信息 */static ccp_device_info_t g_device_info = {    .version_major = CCP_VERSION_MAJOR,    .version_minor = CCP_VERSION_MINOR,    .resource_availability = CCP_PROTECT_CAL | CCP_PROTECT_DAQ | CCP_PROTECT_PGM,    .protection_status = CCP_PROTECT_CAL | CCP_PROTECT_DAQ | CCP_PROTECT_PGM,    .device_name = "TC_CCP_DEMO"};
观测量和标定量的定义也被放在了这个文件中了。
/*观测量定义*/#pragma section "app.meas_var"volatile uint32 Meas_SysCount = 0ul;  //观测量volatile uint32 Meas_Output = 0ul;   //观测量#pragma section /*标定量定义*/#pragma section "app.cali_var"const volatile uint32 Cali_Proportion = 0ul;  //标定参数1const volatile uint32 Cali_Offset = 0ul;  //标定参数2#pragma section 
这四个变量是不是很眼熟,没错就是文章CCP基本操作流程与效果展示使用的四个变量。我们这里再把这四个变量的用法重复一遍:
  1. 名为“Meas_SysCount”的变量,这个变量每隔0.5秒,自增1,增长到20的时候,就会重新归零。
  2. 名为“Meas_Output”的变量,这个变量的值为:
Meas_Output = Cali_Proportion * 16 + Cali_Offset。
其中两个变量有前缀“Meas”这是测量量(Measure)的的简写。两外两个变量的前缀是”Cali“这是标定量(Calibration)的简写。
再下一步就是要在core0_main()函数中完成初始化和局部变量定义。
GPTimer_init();CAN_init();ccp_init(&g_ccp_handle,&g_device_info);  //初始化ccpuint8 buf[8] = {0};uint8 len = 0;
最后就是在while(1)循环中完成CCP指令的周期性处理即可。
 while(1)    {        /*CCP接收处理*/        if((rx_flag == 1) && (rx_id == 0x010))        {            rx_flag = 0/*清除接收标志*/            len = rx_len;/*从通用接收缓冲区复制消息到ccp处理缓冲区*/            for(uint32 cnt = 0; (cnt < len) && (cnt < 8); cnt ++)            {                buf[cnt] = rx_buf[cnt];            }            ccp_process(&g_ccp_handle,buf,len);/*处理CCP命令*/        }        /*时基处理,1ms未到达则跳过后续代码*/        if(TimeOut_1ms_Flag == 0)        {            continue;        }        TimeOut_1ms_Flag = 0;        TimeCount_1ms += 1;        ccp_daq_timer_handler(&g_ccp_handle);        /*CCP_DAQ处理,1ms触发一次*/        /*App定时任务*/        if(TimeCount_1ms % 500 == 0)  //500ms        {            Meas_SysCount += 1;  //每0.5秒自增1            Meas_Output = Cali_Proportion * 16 + Cali_Offset;  //CCP标定与观测演示        }        /*每10s重置时基*/        if(TimeCount_1ms >= 10000)  //10s        {            TimeCount_1ms = 0;          }    }
我们可以看出来在上面的调用逻辑下,CCP指令的处理是发生在收到CAN报文触发中断之后,而DAQ相关指令的处理是1ms处理一次。
现在我们所有的集成工作已经结束了,Cup0_Main.c文件的所有代码如下所示。完成这些工作,应该是能顺利完成编译了。
#include"drive_tc.h"//底层驱动#include"ccp_core.h"//CCP实现IFX_ALIGN(4) IfxCpu_syncEvent g_cpuSyncEvent = 0;/************************CCP模块相关变量************************//* 全局CCP句柄 */ccp_handle_t g_ccp_handle;/* 设备信息 */static ccp_device_info_t g_device_info = {    .version_major = CCP_VERSION_MAJOR,    .version_minor = CCP_VERSION_MINOR,    .resource_availability = CCP_PROTECT_CAL | CCP_PROTECT_DAQ | CCP_PROTECT_PGM,    .protection_status = CCP_PROTECT_CAL | CCP_PROTECT_DAQ | CCP_PROTECT_PGM,    .device_name = "TC_CCP_DEMO"};/*观测量定义*/#pragma section "app.meas_var"volatile uint32 Meas_SysCount = 0ul;  //观测量,每0.5秒自增1volatile uint32 Meas_Output = 0ul;   //观测量,公式:Meas_Output = Cali_Proportion * 16 + Cali_Offset#pragma section /*标定量定义*/#pragma section "app.cali_var"const volatile uint32 Cali_Proportion = 0ul;  //标定参数1const volatile uint32 Cali_Offset = 0ul;  //标定参数2#pragma section /************************主函数************************/voidcore0_main(void){    IfxCpu_enableInterrupts();    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!     * Enable the watchdogs and service them periodically if it is required     */    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());    /* Wait for CPU sync event */    IfxCpu_emitEvent(&g_cpuSyncEvent);    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);    GPTimer_init();    CAN_init();    ccp_init(&g_ccp_handle,&g_device_info);  //初始化ccp    uint8 buf[8] = {0};    uint8 len = 0;    while(1)    {        /*CCP接收处理*/        if((rx_flag == 1) && (rx_id == 0x010))        {            rx_flag = 0/*清除接收标志*/            len = rx_len;/*从通用接收缓冲区复制消息到ccp处理缓冲区*/            for(uint32 cnt = 0; (cnt < len) && (cnt < 8); cnt ++)            {                buf[cnt] = rx_buf[cnt];            }            ccp_process(&g_ccp_handle,buf,len);/*处理CCP命令*/        }        /*时基处理,1ms未到达则跳过后续代码*/        if(TimeOut_1ms_Flag == 0)        {            continue;        }        TimeOut_1ms_Flag = 0;        TimeCount_1ms += 1;        ccp_daq_timer_handler(&g_ccp_handle);        /*CCP_DAQ处理,1ms触发一次*/        /*App定时任务*/        if(TimeCount_1ms % 500 == 0)  //500ms        {            Meas_SysCount += 1;  //每0.5秒自增1            Meas_Output = Cali_Proportion * 16 + Cali_Offset;  //CCP标定与观测演示        }        /*每10s重置时基*/        if(TimeCount_1ms >= 10000)  //10s        {            TimeCount_1ms = 0;          }    }}

总结

今天我们讲解了CCP协议栈代码结构和集成方法。在今天的描述中我们粗略的引入了如何定义观测量和标定量。我们用到了如下几行代码,这几行代码是什么意思呢,关键字“#pragma”是如何将这四个变量定义在指定的地址上的,如何与.lsl文件进行互动的?下一次左工将回答这些问题。

/*观测量定义*/#pragma section "app.meas_var"volatile uint32 Meas_SysCount = 0ul;  //观测量volatile uint32 Meas_Output = 0ul;   //观测量#pragma section /*标定量定义*/#pragma section "app.cali_var"const volatile uint32 Cali_Proportion = 0ul;  //标定参数1const volatile uint32 Cali_Offset = 0ul;  //标定参数2#pragma section 
再次提醒各位可以关注公众号,加左工微信,给左工发送“CCP源码”即可获取上述协议栈代码。请敬请收藏关注,不迷路。