S32K CAN收发标准数据帧配置(纯C 不用SDK带的驱动)

由于S32K144 CAN功能众多,协议上既支持CAN FD,又支持传统CAN 2.0,存储消息既支持FIFO机制,又支持邮箱机制,还支持DMA,可配置can消息缓存数量及分类众多,比较建议先直接用一种的简单方式,再由简入繁,不论是看文档,还是调试都会容易得多。

本文主要讲解如何用纯C配置S32K144 CAN收发功能(FIFO中断接收),如何配置比特率,可以接收任意CAN ID,也可以指定过滤CAN ID。使用了SDK RTM3.0.0的中断管理文件interrupt_manager.h/c, 来管理CPU中断(响应)。

运行环境

MCU: S32K144 LQFP100, IDE: S32DS for ARM 2018.R1

SDK: None, PCBA: 要求使用外部晶振8M(如果不是需要自己配置bit rate)

CAN收发器:NXP TJA1044(使用如TJA1043等低功耗方案时,需要在发送CAN报文前配置好CAN收发器相关引脚,以便能正常收发报文,否则MCU CAN模块ESR/ECR会报错)

配置说明

CAN引脚:PTC2 - CAN0_RX, PTC3 - CAN0_TX

CAN bit rate = 500kbps

CAN发送: MB8 (中断);

CAN接收FIFO中断(MB0~5), CAN ID过滤表(MB6~7,FIFO默认选择);

1.时钟配置(略)

关于如何配置bus clock,可以参见我写的另一篇文章 S32K144之时钟配置 , 里面也包含了bus clock 80M, OSC 8M, 如何验证以及端口配置的讲解。

2.CAN Port配置

根据板子实际使用CAN引脚决定。我手上板子实际使用PTC2, PTC3作为CAN收发功能。

PTC2&PTC3 IO Map截图

  

/**
 * CAN Port Initialization
 */
void Port_CanInit(void)
{
    // Enable clock to PORTC
    if(PCC->PCCn[PCC_PORTC_INDEX] & PCC_PCCn_PR_MASK)
    {
        PCC->PCCn[PCC_PORTC_INDEX] |= PCC_PCCn_CGC(1); // Clock enabled to PORTC
    }

    // Set MUX
    // PTC2 - CAN0_RX
    PORT_CAN->PCR[PORT_CAN_RX_IDX] = PORT_PCR_MUX(0b00000011); // Port C2: MUX = ALT3, CAN0_RX
    // PTC3 - CAN0_TX
    PORT_CAN->PCR[PORT_CAN_TX_IDX] = PORT_PCR_MUX(0b00000011); // Port C3: MUX = ALT3, CAN0_TX
}

3.CAN0配置

1)bit rate

初始化,时钟选择8M OSC时钟(时钟源是8M外部晶振),CAN 比特率bit rate = 500kbps 

1 CAN Bit Time = (1 + (PROPSEG + PSEG1 + 2) + (PSEG2 + 1)) * Tq

而Tq=1/8M = 1/8 us,不妨取PROPSEG = 6, PSEG1 = 3, PSEG2 = 3。这里PROPSEG ,PSEG1 ,PSEG2 只是其中一组可行参数,具体如何配置还要看车厂要求,车厂一般都会有自己的CAN节点通讯规范来规定CAN底层参数的。

传统CAN 2.0帧1bit time组成示意图

2)CAN0初始化

使用FIFO中断接收,1MB(message buffer)占用4word,每个word对应4byte。其中前面2个word表示长度、时间戳、IDE、RTR、CAN ID等信息,后2个word表示CAN数据段8byte。

FIFO接收缓存:默认MB0~5,读取数据接口:MB0(下图红框)。M6~7默认是CAN ID过滤表元素0~1,MB8~37是可配置ID过滤表元素2~127。

中断机制:FIFO接收缓存MB0~5,当有任意一个CAN成功接收后,会产生事件标志CAN_IFLAG1[BUF5I];

当接收到从4个到5个时,会产生事件标志CAN_IFLAG1[BUF6I];

当接收满5个后,还接收到新CAN消息,会产生事件标志CAN_IFLAG1[BUF7I],表示发生溢出;

另外,RM提到,使用FIFO时,要使用单独ID过滤mask,而不能用global mask。

MB缓存大小有关宏定义

#define MSG_BUF_SIZE        4u    /* 1 Msg Buffer Size : CAN0->RAMn[i] use 4word(1word=4byte). 4word = 2word header, 2word data*/
#define MB_FIFO_NUM         6u    /* MB Size used for FIFO engine: MB0~5 */
#define MB_FIFO_IDX_TX      8u    /* MB for transmitting CAN message*/
#define MB_FIFO_IDX_RX      6u    /* MB for receiving CAN message*/

CAN0初始化,主要步骤参考S32K RM CAN初始化章节:FlexCAN initialization sequence

/**
 * CAN configuration, set baud rate = 500Kbps
 *
 * @note CAN Baud rate calculation
 * Tq = (PRESDIV + 1) / f_canclk = 1 / 8M
 *
 * 1 CAN Bit Time = (1 + (PROPSEG + PSEG1 + 2) + (PSEG2 + 1)) * Tq
 * = (1 + (6 + 3 + 2) + (3 + 1) * 1/8M
 * = 16 * 1/8M = 2us
 *
 * Baud Rate = 1/2us = 500Kbps
 *
 * configure 1MB = 8 CAN->RAMn = 32byte
 * MB6 ~ FIFO Rx
 * MB8 ~ Tx
 */
void Can0_Init()
{
    PCC->PCCn[PCC_FlexCAN0_INDEX] |= PCC_PCCn_CGC(1); // Clock enabled to CAN0 module

    // Step0: Go to Freeze mode
    // Step0-1: After reset, disable can module
    CAN_DisableModule(CAN0); // Disable module before selecting clock

    // Step0-2: Select clock source
    CAN0->CTRL1 &= ~CAN_CTRL1_CLKSRC_MASK; // CLKSRC = 0: Clock source = the oscillator clock. (SOSCDIV2_CLK = 8MHz)

    // Step0-3: Enable can module, then flexCAN automatically enters Freeze mode
    CAN_EnableModule(CAN0); // Enable module

    // Step0-4: Check whether current mode is Freeze mode
    CAN_EnableFreezeMode(CAN0);
    while(!((CAN0->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT)){} // Wait for FRZACK = 1, to ensure next operation in Freeze mode
//    while(!(CAN0->MCR & CAN_MCR_NOTRDY_MASK)){} // Wait for NOTRDY = 1, to ensure next operation in Disable mode/Stop mode/Freeze mode.

    // Step1: Initialize MCR
    CAN0->MCR |=
            CAN_MCR_IRMQ_MASK // a: IRMQ=1, enable the individual filtering per MB and reception queue features
            | CAN_MCR_WRNEN_MASK // b: WRNEN=1, enable the warning interrupts
            | CAN_MCR_SRXDIS_MASK // c: SRXDIS=1, disable frame self reception
            | CAN_MCR_RFEN_MASK // d: RFEN=1, Enable the Rx FIFO, MBs 0 to 5 cannot be used for normal reception and transmission(they have been uesed for the FIFO engine)
            // e: DMA=0, dont use DMA
            // f: PNET_EN=0, dont use pretended networking
            | CAN_MCR_AEN_MASK// g: AEN=1, use Tx Abort mechanism
            // h: PRIOEN=0, dont use Local Priority feature
            | CAN_MCR_IDAM(0)// IDAM=0, ID Acceptance Mode=Format A: One full ID (standard and extended) per ID filter table element.
            | CAN_MCR_MAXMB(32) // MAXMB = Rx FIFO + ID filter table space(CTRL2[REFN]), default=16
            ;

    // Step2: Initialize CTRL1 or CBT
    // The CAN bit variables in CTRL1 and in CBT are stored in the same register.
    // Configure for CAN bit rate = 500 Kbps, 16 time quanta for 1 bit
    CAN0->CTRL1 = CAN_CTRL1_PRESDIV(0) // Tq = fcanclk / prediv = 8MHz / 1 = 8MHz
            | CAN_CTRL1_RJW(0b11) // RJW: since Phase_Seg2 >=4, RJW+1=4 so RJW=3.
            | CAN_CTRL1_PSEG1(0b011) // Phase Segment 1 = PSEG1 + 1
            | CAN_CTRL1_PSEG2(0b011) // Phase Segment 2 = PSEG2 + 1
            | CAN_CTRL1_PROPSEG(0b110) // Propagation Segment = PROPSEG + 1
            | CAN_CTRL1_SMP(1);
            // LBUF=0, Buffer with highest priority is transmitted first.(MCR[LPRIOEN] + LBUF => transmit priority)

    // Step3: Initialize MB
    // MB & Rx Individual Mask registers are not affected by reset, so they are not initialized automatically.
    // payload=8, MB0~5 used for FIFO engine(contains message received but not read)
    // CAN0: clear 32 message buffer x 4 words/msg, buf = 128 words
    // CAN0 contains 32MBs
    for(int i = 32; i < CAN_RAMn_COUNT; i ++)
    {
        CAN0->RAMn[i] = 0;
    }

    // Step4: Initialize RXIMRn(Rx Individual Mask registers)
    // In FRZ mode, init CAN0 16 individual msg buf filters . there are total 32MBs for CAN0
    for(int i = 0; i < 32; i ++)
    {
        CAN0->RXIMR[i] = 0xFFFFFFFF; // check all ID bits for incoming messages
//        CAN0->RXIMR[i] = 0; // dont care every bit
    }

//    CAN0->RXMGMASK = 0xFFFFFFFF; // Global acceptance mask. When MCR[IRMQ] set, it has no effect; When MCR[IRMQ] cleared, it will mask the mailbox filter bits. check all ID bits

//    CAN0->FDCTRL &= ~CAN_FDCTRL_MBDSR0_MASK; // MBDSR0 = 0, Selects 8 bytes per message buffer. 32 MBs with 8 bytes payload

    // enable interrupt
    CAN0->CTRL1 |= CAN_CTRL1_RWRNMSK_MASK; // enable Rx warning interrupt
    CAN0->IMASK1 |= CAN_IMASK1_BUF31TO0M(1 << MB_FIFO_IDX_RX); /* Buffer MB i Mask: Open FIFO receives interrupt */
    CAN0->IMASK1 |= CAN_IMASK1_BUF31TO0M(1 << MB_FIFO_IDX_TX); /* MB8 interrupt mask*/
    CAN0->IMASK1 |= CAN_IMASK1_BUF31TO0M(1 << 5);

    // set the ID Table, assuming CTRL2[RFFN=0]
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 0] = (0x55 &0x7FF) << 19; // ID filter table element 0
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 1] = (0x56 &0x7FF) << 19; // element 1
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 2] = (0x57 &0x7FF) << 19; // element 2
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 3] = (0x58 &0x7FF) << 19; // element 3

    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 4] = (0x59 &0x7FF) << 19; // element 4
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 5] = (0x5A &0x7FF) << 19; // element 5
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 6] = (0x5B &0x7FF) << 19; // element 6
    CAN0->RAMn[ MB_FIFO_NUM*MSG_BUF_SIZE + 7] = (0x5C &0x7FF) << 19; // element 7

    // Step7: nagate HALT
    CAN0->MCR &= ~CAN_MCR_HALT_MASK;       /* Negate HALT bit */

    CAN_DisableFreezeMode(CAN0);
    while((CAN0->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT) {}  // Wait for FRZACK = 0

    while((CAN0->MCR & CAN_MCR_NOTRDY_MASK) >> CAN_MCR_NOTRDY_SHIFT) {} // Wait for NOTRDY = 0, FlexCAN module is either in Normal mode, Listen-Only mode, or Loop-Back mode.

    INT_SYS_ClearPending(CAN0_ORed_0_15_MB_IRQn);
    INT_SYS_EnableIRQ(CAN0_ORed_0_15_MB_IRQn);
    INT_SYS_SetPriority(CAN0_ORed_0_15_MB_IRQn, 0xA0);
}

3)CAN0发送

将can id,8byte数据,数据长度写入硬件缓存MB,准备发送

/**
 * Transmit message once using MB8
 * @param id CAN id
 * @param [in] pData data to be sent
 * @param length length of data to be sent, unit: byte
 */
void Can0_Tx(uint32_t canId, uint8_t *pData, uint8_t length)
{
    if(!pData)
        return;

    /*MB8 word1*/
    CAN0->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 1] = ((canId & 0x7FF) << 18u); // std id

    /*MB8 word2*/
    CAN0->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 2] =
            CAN_RAMn_DATA_BYTE_0(pData[0])
            | CAN_RAMn_DATA_BYTE_1(pData[1])
            | CAN_RAMn_DATA_BYTE_2(pData[2])
            | CAN_RAMn_DATA_BYTE_3(pData[3]);
    /*MB8 word3*/
    CAN0->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 3] =
            CAN_RAMn_DATA_BYTE_0(pData[4])
            | CAN_RAMn_DATA_BYTE_1(pData[5])
            | CAN_RAMn_DATA_BYTE_2(pData[6])
            | CAN_RAMn_DATA_BYTE_3(pData[7]);

    /* MB0 word 0: */
    /* EDL,BRS,ESI=0: CANFD not used */
    /* CODE=0xC: Activate msg buf to transmit */
    /* IDE=0: Standard ID */
    /* SRR=1 Tx frame (not req'd for std ID) */
    /* RTR = 0: data, not remote tx request frame*/
    /* DLC = 8 bytes */
    uint32_t code = 0b1100;
    CAN0->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 0] = CAN_WMBn_CS_CODE(code)
            | CAN_WMBn_CS_SRR(1)
            | CAN_WMBn_CS_RTR(0)
            | CAN_WMBn_CS_DLC(length);

//    while(!(CAN0->IFLAG1 & (1 << 8u))){} // Wait for CAN0 MB8 flag // 相当于等待发送完毕, 这里没必要, 如需要在发送完毕后做些什么, 可以放到中断里面
//    CAN0->IFLAG1 |= 1 << 8u; // Clear CAN0 8 flag without clearing other.
}

发送完成中断处理(发送完成后捕捉中断,可用于继续进行某些动作,比如从发送列表中继续发送下一帧)

uint32_t txCouner = 0;
void CAN0_ORed_0_15_MB_IRQHandler()
{
    if(CAN0->IFLAG1 & (1 << MB_FIFO_IDX_TX))
    {
        CAN0->IFLAG1 |= 1 << MB_FIFO_IDX_TX; /* Clear CAN0 MB8 flag */
        txCouner ++;
    }
}

4)CAN0中断接收

注意要在中断处理函数之前,就enable相应的中断

typedef struct
{
    uint32_t id;
    uint32_t dlc;
    uint8_t data[8];
    uint32_t timestamp;
}CAN_StdFrame, *CAN_StdFramePtr;

volatile static CAN_StdFrame rxStdFrame = {0};
uint32_t txCouner = 0; uint32_t rxCounter = 0; void CAN0_ORed_0_15_MB_IRQHandler() { // Tx completed interrupt if(CAN0->IFLAG1 & (1 << MB_FIFO_IDX_TX)) { CAN0->IFLAG1 |= 1 << MB_FIFO_IDX_TX; /* Clear CAN0 MB8 flag */ txCouner ++; } // Rx warning interrupt - MB0~5 full(unread message increase to 5 from 4) if(CAN0->IFLAG1 & (1 << MB_FIFO_IDX_RX)) { CAN0->IFLAG1 |= 1 << MB_FIFO_IDX_RX; /* Clear CAN0 MB6 flag */ } // when received CAN message by FIFO (MB0~5), interrupt flag set by IFLAG5(not IFLAG0~4) if(CAN0->IFLAG1 & (1 << 5)) { // read can message first rxStdFrame.id = (CAN0->RAMn[1] >> 18) & 0x7FF; rxStdFrame.dlc = (CAN0->RAMn[0] >> 16) & 0xF; rxStdFrame.timestamp = (CAN0->RAMn[0] ) & 0xFF; rxStdFrame.data[0] = (CAN0->RAMn[2] >> 24) & 0xFF; rxStdFrame.data[1] = (CAN0->RAMn[2] >> 16) & 0xFF; rxStdFrame.data[2] = (CAN0->RAMn[2] >> 8) & 0xFF; rxStdFrame.data[3] = (CAN0->RAMn[2]) & 0xFF; rxStdFrame.data[4] = (CAN0->RAMn[3] >> 24) & 0xFF; rxStdFrame.data[5] = (CAN0->RAMn[3] >> 16) & 0xFF; rxStdFrame.data[6] = (CAN0->RAMn[3] >> 8) & 0xFF; rxStdFrame.data[7] = (CAN0->RAMn[3]) & 0xFF; rxCounter ++; // then clear MB5 interrupt flag CAN0->IFLAG1 |= 1 << 5; /* Clear CAN0 MB5 flag*/ } // Rx overflow - MB0~5 full if(CAN0->IFLAG1 & (1 << 7)) { CAN0->IFLAG1 |= 1 << 7; /* Clear CAN0 MB6 flag*/ } }

参考: 

S32K系列S32K144学习笔记——CAN

原文地址:https://www.cnblogs.com/fortunely/p/12755948.html