DSP28377S_CAN通信

CAN通信的由来

为适应“减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需要,1986 年德国电气商博世公司开发出面向汽车的CAN 通信协议。CAN属于现场总线的范畴,它是一种有效支持分布式控制或实时控制的串行通信网络。

CAN通信格式

CAN通信共有5种,分别为数据帧、遥控帧、错误帧、过载帧、帧间隔。数据帧格式由下图所示,分为标准格式和拓展格式,笔者目前仅使用到标准数据帧,使用其中的64bits数据段进行CAN节点间的数据交互,以ID号区分数据类型。注意ACK!实际波形中彼己电平高度不一
在这里插入图片描述

CAN通信配置

①初始化CAN的映射GPIO,使用TI封装好的函数初始化CAN,选择CAN时钟源,设置波特率,使能CAN中断触发源,开启CAN;

void InitCana(void)
{
    InitCanaGpio();

    //
    // Initialize the CAN controller
    //
    CANInit(CANA_BASE);

    //
    // Setup CAN to be clocked off the M3/Master subsystem clock
    //
    CANClkSourceSelect(CANA_BASE, 0);

    //
    // Set up the bit rate for the CAN bus.  This function sets up the CAN
    // bus timing for a nominal configuration.  You can achieve more control
    // over the CAN bus timing by using the function CANBitTimingSet() instead
    // of this one, if needed.
    // In this example, the CAN bus is set to 500 kHz.  In the function below,
    // the call to SysCtlClockGet() is used to determine the clock rate that
    // is used for clocking the CAN peripheral.  This can be replaced with a
    // fixed value if you know the value of the system clock, saving the extra
    // function call.  For some parts, the CAN peripheral is clocked by a fixed
    // 8 MHz regardless of the system clock in which case the call to
    // SysCtlClockGet() should be replaced with 8000000.  Consult the data
    // sheet for more information about CAN peripheral clocking.
    //
    CANBitRateSet(CANA_BASE, 200000000, 200000);    // 波特率200kbps

    //
    // Enable interrupts on the CAN peripheral.  This example uses static
    // allocation of interrupt handlers which means the name of the handler
    // is in the vector table of startup code.  If you want to use dynamic
    // allocation of the vector table, then you must also call CANIntRegister()
    // here.
    //
    CANIntEnable(CANA_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    //
    // Enable the CAN for operation.
    //
    CANEnable(CANA_BASE);

    //
    // Enable CAN Global Interrupt line0
    //
    CANGlobalIntEnable(CANA_BASE, CAN_GLB_INT_CANINT0);
}

②确定收发缓存数组、ID号;

//  CAN通信
unsigned char ucRXMsgData1[8];   // CAN接受数据
unsigned char ucTXMsgData2[8];   // CAN发送数据
unsigned char ucTXMsgData3[8];   // CAN发送数据
unsigned char ucTXMsgData4[8];   // CAN发送数据
unsigned char ucTXMsgData5[8];   // CAN发送数据
unsigned char ucTXMsgData6[8];   // CAN发送数据

tCANMsgObject sRXCANMessage1 = {RX_MSG_OBJ_ID1, 0, 2, 8, ucRXMsgData1};    // CAN接收结构体  MSG_OBJ_RX_INT_ENABLE = 2
tCANMsgObject sTXCANMessage2 = {TX_MSG_OBJ_ID2, 0, 1, 8, ucTXMsgData2};    // CAN发送结构体  MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage3 = {TX_MSG_OBJ_ID3, 0, 1, 8, ucTXMsgData3};    // CAN发送结构体  MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage4 = {TX_MSG_OBJ_ID4, 0, 1, 8, ucTXMsgData4};    // CAN发送结构体  MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage5 = {TX_MSG_OBJ_ID5, 0, 1, 8, ucTXMsgData5};    // CAN发送结构体  MSG_OBJ_TX_INT_ENABLE = 1
tCANMsgObject sTXCANMessage6 = {TX_MSG_OBJ_ID6, 0, 1, 8, ucTXMsgData6};    // CAN发送结构体  MSG_OBJ_TX_INT_ENABLE = 1

③根据自己的通信机制,封装好CAN通信收发函数;

void CANA_TX(void)
{

    unsigned int T_switch = 0;  // 选择数据发送

    CANA_TX_FRAME_CNT = CANA_TX_FRAME_CNT + 1;      // 帧计数自增
//    CANA_TX_FRAME_CNT = CANA_TX_FRAME_CNT & 0xFF;   // 帧计数达到255后清零,循环计数

    T_switch = CANA_TX_FRAME_CNT % 5;

    switch(T_switch)
    {
        case 0:
            ucTXMsgData2[0] = CAN_CNT_delta & 0xFF;    // 帧计数
            ucTXMsgData2[1] = 0; // ...此处略去
            ucTXMsgData2[2] = 0; // ...此处略去
            ucTXMsgData2[3] = 0; // ...此处略去
            ucTXMsgData2[4] = 0; // ...此处略去
            ucTXMsgData2[5] = 0; // ...此处略去
            ucTXMsgData2[6] = 0; // ...此处略去
            ucTXMsgData2[7] = 0; // ...此处略去
            CanaMessageSet(TX_MSG_OBJ_ID2, &sTXCANMessage2, MSG_OBJ_TYPE_TX);
            break;

        case 1:
            ucTXMsgData3[0] = CAN_CNT_delta & 0xFF;    // 帧计数
            ucTXMsgData3[1] = 0; // ...此处略去
            ucTXMsgData3[2] = 0; // ...此处略去
            ucTXMsgData3[3] = 0; // ...此处略去
            ucTXMsgData3[4] = 0; // ...此处略去
            ucTXMsgData3[5] = 0; // ...此处略去
            ucTXMsgData3[6] = // ...此处略去
            ucTXMsgData3[7] = // ...此处略去
            CanaMessageSet(TX_MSG_OBJ_ID3, &sTXCANMessage3, MSG_OBJ_TYPE_TX);
            break;

        case 2:
            ucTXMsgData4[0] = CAN_CNT_delta & 0xFF;    // 帧计数
            ucTXMsgData4[1] = // ...此处略去
            ucTXMsgData4[2] = // ...此处略去
            ucTXMsgData4[3] = // ...此处略去
            ucTXMsgData4[4] = // ...此处略去
            ucTXMsgData4[5] = // ...此处略去
            ucTXMsgData4[6] = // ...此处略去
            ucTXMsgData4[7] = // ...此处略去
            CanaMessageSet(TX_MSG_OBJ_ID4, &sTXCANMessage4, MSG_OBJ_TYPE_TX);
            break;

        case 3:
            ucTXMsgData5[0] = CAN_CNT_delta & 0xFF;    // 帧计数
            ucTXMsgData5[1] = // ...此处略去
            ucTXMsgData5[2] = // ...此处略去
            ucTXMsgData5[3] = // ...此处略去
            ucTXMsgData5[4] = // ...此处略去
            ucTXMsgData5[5] = // ...此处略去
            ucTXMsgData5[6] = // ...此处略去
            ucTXMsgData5[7] = // ...此处略去
            CanaMessageSet(TX_MSG_OBJ_ID5, &sTXCANMessage5, MSG_OBJ_TYPE_TX);
            break;

        case 4:
            ucTXMsgData6[0] = CAN_CNT_delta & 0xFF;    // 帧计数
            ucTXMsgData6[1] = // ...此处略去
            ucTXMsgData6[2] = // ...此处略去
            ucTXMsgData6[3] = // ...此处略去
            ucTXMsgData6[4] = // ...此处略去
            ucTXMsgData6[5] = // ...此处略去
            ucTXMsgData6[6] = // ...此处略去
            ucTXMsgData6[7] = // ...此处略去
            CanaMessageSet(TX_MSG_OBJ_ID6, &sTXCANMessage6, MSG_OBJ_TYPE_TX);
            CANA_TX_Active_Flag = 0;
            break;

        default:
            break;
    }

    if(CANA_TX_FRAME_CNT >= 254)
    {
        CANA_TX_FRAME_CNT = 0;
    }
}
void CANA_RX(void)
{
    if((CANA_errorFlag == 0) && (CANA_RX_Flag == 1))
    {
        RX_FRAME_CANA.CNT = (Uint16)ucRXMsgData1[0]; // 字节1
        RX_FRAME_CANA.x= (Uint16)(((ucRXMsgData1[1] & 0xF0) >> 4) & 0x0F);   // 字节2H
        RX_FRAME_CANA.xx = (Uint16)ucRXMsgData1[1] & 0x0F;          // 字节2L
        RX_FRAME_CANA.xxx= (Uint16)(((ucRXMsgData1[2] & 0xF0) >> 4) & 0x0F);    // 字节3H
        RX_FRAME_CANA.xxxx= (Uint16)ucRXMsgData1[2] & 0x0F;               // 字节3L
        RX_FRAME_CANA.xxxxx= ((Uint16)(ucRXMsgData1[3] & 0xFF)) * 0.2;      // 字节4
        RX_FRAME_CANA.xxxxxx= ((Uint16)(ucRXMsgData1[4] & 0xFF)) * 196.08;  // 字节5
        RX_FRAME_CANA.xxxxxxx= ((int)(((ucRXMsgData1[5] & 0xFF) << 8 ) | (ucRXMsgData1[6] & 0xFF))) * 0.02;    // 字节6 字节7

    }
}

④使用中断服务函数,传输接收发送结构体;

interrupt void CANA0_ISR(void)
{
    /************************************************************
    Description:CANA0中断服务程序
       用于检测中断产生原因,发送接收上位机数据
    ************************************************************/
    uint32_t status;

    //
    // Read the CAN-A interrupt status to find the cause of the interrupt
    //
    status = CANIntStatus(CANA_BASE, CAN_INT_STS_CAUSE);

    //
    // If the cause is a controller status interrupt, then get the status
    //
    if(status == CAN_INT_INT0ID_STATUS)
    {
        //
        // Read the controller status.  This will return a field of status
        // error bits that can indicate various errors.  Error processing
        // is not done in this example for simplicity.  Refer to the
        // API documentation for details about the error status bits.
        // The act of reading this status will clear the interrupt.
        //
        status = CANStatusGet(CANA_BASE, CAN_STS_CONTROL);

        //
        // Check to see if an error occurred.
        //
        if(((status  & ~(CAN_ES_TXOK | CAN_ES_RXOK)) != 7) &&
                   ((status  & ~(CAN_ES_TXOK | CAN_ES_RXOK)) != 0))
        {
            //
            // Set a flag to indicate some errors may have occurred.
            //
            CANA_errorFlag = 1;
        }
    }
    //
    // Check if the cause is the CAN-A receive message object 1
    //
    else if(status == RX_MSG_OBJ_ID1)
    {
        //
        // Get the received message
        //
        CANMessageGet(CANA_BASE, RX_MSG_OBJ_ID1, &sRXCANMessage1, true);

        //
        // Getting to this point means that the RX interrupt occurred on
        // message object 1, and the message RX is complete.  Clear the
        // message object interrupt.
        //
        CANIntClear(CANA_BASE, RX_MSG_OBJ_ID1);

        //
        // Since the message was received, clear any error flags.
        //
        CANA_errorFlag = 0;

        CANA_RX_Flag = 1;
        CANA_TX_Active_Flag = 1;
        Timer_CANA_TX_1ms = 0;
    }
    //
    // Check if the cause is the CAN-A send message object 1
    //
    else if(status == TX_MSG_OBJ_ID2)
    {
        //
        // Getting to this point means that the TX interrupt occurred on
        // message object 1, and the message TX is complete.  Clear the
        // message object interrupt.
        //
        CANIntClear(CANA_BASE, TX_MSG_OBJ_ID2);

        //
        // Since the message was sent, clear any error flags.
        //
        CANA_errorFlag = 0;
    }
    //
    // Check if the cause is the CAN-A send message object 1
    //
    else if(status == TX_MSG_OBJ_ID3)
    {
        //
        // Getting to this point means that the TX interrupt occurred on
        // message object 1, and the message TX is complete.  Clear the
        // message object interrupt.
        //
        CANIntClear(CANA_BASE, TX_MSG_OBJ_ID3);

        //
        // Since the message was sent, clear any error flags.
        //
        CANA_errorFlag = 0;
    }
    //
    // Check if the cause is the CAN-A send message object 1
    //
    else if(status == TX_MSG_OBJ_ID4)
    {
        //
        // Getting to this point means that the TX interrupt occurred on
        // message object 1, and the message TX is complete.  Clear the
        // message object interrupt.
        //
        CANIntClear(CANA_BASE, TX_MSG_OBJ_ID4);

        //
        // Since the message was sent, clear any error flags.
        //
        CANA_errorFlag = 0;
    }
    //
    // Check if the cause is the CAN-A send message object 1
    //
    else if(status == TX_MSG_OBJ_ID5)
    {
        //
        // Getting to this point means that the TX interrupt occurred on
        // message object 1, and the message TX is complete.  Clear the
        // message object interrupt.
        //
        CANIntClear(CANA_BASE, TX_MSG_OBJ_ID5);

        //
        // Since the message was sent, clear any error flags.
        //
        CANA_errorFlag = 0;
    }
    //
    // Check if the cause is the CAN-A send message object 1
    //
    else if(status == TX_MSG_OBJ_ID6)
    {
        //
        // Getting to this point means that the TX interrupt occurred on
        // message object 1, and the message TX is complete.  Clear the
        // message object interrupt.
        //
        CANIntClear(CANA_BASE, TX_MSG_OBJ_ID6);

        //
        // Since the message was sent, clear any error flags.
        //
        CANA_errorFlag = 0;
    }
    //
    // If something unexpected caused the interrupt, this would handle it.
    //
    else
    {
        //
        // Spurious interrupt handling can go here.
        //
    }

    //
    // Clear the global interrupt flag for the CAN interrupt line
    //
    CANGlobalIntClear(CANA_BASE, CAN_GLB_INT_CANINT0);

    //
    // Acknowledge this interrupt located in group 9
    //
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;

}

实验验证

采用上位机(PC)发1帧,下位机(DSP)应答5帧的方式,实现遥控与遥测,要想实现更为精准的定时,则可以采取其他的方式,比如TT-CAN等。上位机发送间隔6ms,下位机应答间隔1ms。上位机数据帧ID(00000000001),下位机数据帧ID(00000000010、00000000011、00000000100、00000000101、00000000110)。使用ZLG的示波器,因为他自带CAN通信解码功能,使用起来非常方便。
上位机遥控帧:
在这里插入图片描述
下位机遥测帧①
在这里插入图片描述
下位机遥测帧⑤
在这里插入图片描述

结束语

笔者对于CAN的使用,仅停留在数据帧这一简单的帧种类上,以后若是有项目需要,则再补充学习其他的帧种类。TI对于CAN的支持比较到位,我们可以直接调用相应的函数,即可实现功能。当然规则越明细,开发人员对其标准化程度会越高,但使用灵活度、自由度变差。

参考资料目录

《TMS320F2837xS Delfino Microcontrollers Datasheet》Memory章节
《TMS320F2837xS Delfino Microcontrollers Technical Reference Manual》CAN章节
RENESAS应用手册《CAN入门书》
C2000Ware有关CAN的所有例程

  • 9
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值