S32K148_CAN驅動(裸機開發)

2022-07-17 18:04:03

hello,大家好。今天我又來啦,今天記錄一下S32K148-CAN裸機驅動編寫,有錯誤地方歡迎大家指正。

CAN的傳送接收在S32K148中主要有三種方式,一種是郵箱機制(mailbox),一種FIFO方式,還有一種DMA方式。郵箱方式較為簡單,也比較好理解。我的想法是先搞懂一種實現方式,再去慢慢研究其他方法。

郵箱機制主要涉及到一下暫存器:

 

 還有一個RAMn[n]暫存器。

具體每個暫存器的功能資料手冊有詳細介紹,我就不逐個敘述了。直接根據我的demo程式來作簡要介紹。

CAN的初始化主要包括:

1)PCC時鐘使能

2)CAN傳送引腳和接收引腳的複用設定

3)CAN模組程式使能,同時進入凍結模式

4)設定時鐘,波特率,MB清0,接收MB的掩碼設定,接收MB的code設定

5)接收或傳送報文中斷使能

#define MSG_BUF_SIZE        4      /* Msg Buffer Size. (CAN 2.0AB: 2 hdr +  2 data= 4 words)1MB = 4words */
#define MB_FIFO_IDX_TX      16u   /* MB for transmitting CAN message*/
#define MB_FIFO_IDX_RX      6u    /* MB for transmitting CAN message*/
#define MB_FIFO_NUM         6u    /* MB0~MB5 for FIFO engine*/
void can2_init(void)
{
    PCC->PCCn[PCC_PORTB_INDEX] |= 1<<30;
    /*PIN MUX
     * PTB12 CAN2 RX
     * PTB13 CAN2 TX*/
    PORTB->PCR[12] |= 1<<10;
    PORTB->PCR[13] |= 1<<10;
    /*CAN2 init*/
    /*PCC FOR CAN2*/
    PCC->PCCn[PCC_FlexCAN2_INDEX] |= 1<<30;
    /*CAN2 cfg*/
    CAN2->MCR |= 1<<31;         //mdis=1,disable module
    CAN2->CTRL1 &=~(1<<13);     //CLKSRC=0,Clock Source = SOSCDIV2
    CAN2->MCR &=~(1<<31);       //mdis=0,ENable module

    //等待進入凍結模式
    while( (CAN2->MCR & (1<<24))>>24 == 0 );

    CAN2->CTRL1 = 0x01DB0006;                   //250k,rjw=11,psg1=011,psg2=011,propsg=110,clk=0,presdiv=1,設定時鐘and波特率
    for(uint8_t i=24; i<128; i++ )
    {                                           /* CAN2: clear 32 msg bufs x 4 words/msg buf = 128 words */
        CAN2->RAMn[i] = 0;                      /* Clear msg buf word */
    }
    for(uint8_t i=0; i<32; i++ )
    {                                              /* In FRZ mode, init CAN2 16 msg buf filters */
        CAN2->RXIMR[i] = 0xFFFFFFFF;              /* Check all ID bits for incoming messages */
    }

    CAN2->RXMGMASK = 0x00000000;                   /* Global acceptance mask:  all ID bits DON'T CARE receive all ID message*/


    CAN2->RAMn[ 0*MSG_BUF_SIZE + 0] = 0x04000000;     /* Msg Buf 4, word 0: Enable for reception     */
                                                     /* EDL,BRS,ESI=0: CANFD not used                 */
                                                     /* CODE=4: MB set to RX inactive                 */
                                                     /* IDE=0: Standard ID                             */
                                                     /* SRR, RTR, TIME STAMP = 0: not applicable     */
    //CAN2->RAMn[ 0*MSG_BUF_SIZE + 0] = 0x04200000;     /* Msg Buf 4, word 0: Enable for reception     */
                                                     /* EDL,BRS,ESI=0: CANFD not used                 */
                                                     /* CODE=4: MB set to RX inactive                 */
                                                     /* IDE=1: extend ID                             */
                                                     /* SRR, RTR, TIME STAMP = 0: not applicable     */


    CAN2->MCR = (0x0000001f) | (1<<17);             /*SRXDIS=1,MAXMB=32*/

     CAN2->MCR &= ~CAN_MCR_HALT_MASK;                    /* Negate HALT bit */
    /*傳送和接收message中斷使能*/
     CAN2->IMASK1 |= (1<<16)|(1<<0);
     /*bus off中斷使能*/
     CAN2->CTRL1 |= (1<<15);
    while ((CAN2->MCR && CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT);/* Good practice: wait for FRZACK to clear (not in freeze mode) */
    while ((CAN2->MCR && CAN_MCR_NOTRDY_MASK) >> CAN_MCR_NOTRDY_SHIFT);/* Good practice: wait for NOTRDY to clear (module ready) */
    //註冊接收中斷向量
    S32_NVIC_EnableIRQ(CAN2_ORed_0_15_MB_IRQn);
    S32_NVIC_EnableIRQ(CAN2_ORed_16_31_MB_IRQn);
    //註冊bus off中斷向量
    S32_NVIC_EnableIRQ(CAN2_ORed_IRQn);
}

CAN初始化過程中,需要注意地方在於接收掩碼如何設定(原則:0表示不關心,1表示關心),其次設定好接收MB的選擇同時使能相應MB的接收中斷。這裡面CAN傳送很簡單,主要針對CAN報文接收問題。

傳送函數,注意標準幀和擴充套件幀的設定

void can2_send_8Byte(uint8_t data[], uint8_t datalength, uint32_t id, uint8_t id_mode)
{
    uint8_t i;
    uint8_t data_tem[datalength];
    for(i=0;i<datalength;i++)
    {
        data_tem[i] = data[i];
    }
    //CAN2->IFLAG1 |= 0xFFFF0000; //clear all transmit flag
    //wait bus idle
//    while( (CAN2->ESR1 & (1<<7))>>7 == 0 );
    //data input
    CAN2->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 2] = data_tem[0]<<24 | data_tem[1]<<16 | data_tem[2]<<8 | data_tem[3];
    CAN2->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 3] = data_tem[4]<<24 | data_tem[5]<<16 | data_tem[6]<<8 | data_tem[7];
    //ID cfg
    if(id_mode == StandardID)
    {
        CAN2->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 1] = id<<18;
        CAN2->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 0] = 0x0C480000;   //CODE=1100,SRR=1,IDE=0,RTR=0,DLC=1000
    }else
    {
        CAN2->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 1] = id;
        CAN2->RAMn[MB_FIFO_IDX_TX*MSG_BUF_SIZE + 0] = 0x0C680000;   //CODE=1100,SRR=1,IDE=1,RTR=0,DLC=1000
    }
}

接收函數

void can2_receive_8Byte(uint8_t id_mode)
{
    can2_RecMessage.length = (CAN2->RAMn[0*MSG_BUF_SIZE + 0] >> 16) & 0xF;
    if(id_mode == StandardID)
    {
        can2_RecMessage.id = (CAN2->RAMn[0*MSG_BUF_SIZE + 1] >> 18) & 0x7FF;
    }else
    {
        can2_RecMessage.id = (CAN2->RAMn[0*MSG_BUF_SIZE + 1]) & 0x1FFFFFFF;
    }
    can2_RecMessage.data[0] = (CAN2->RAMn[0*MSG_BUF_SIZE + 2]) >> 24;
    can2_RecMessage.data[1] = ((CAN2->RAMn[0*MSG_BUF_SIZE + 2]) >> 16) & 0xFF;
    can2_RecMessage.data[2] = ((CAN2->RAMn[0*MSG_BUF_SIZE + 2]) >> 8) & 0xFF;
    can2_RecMessage.data[3] = (CAN2->RAMn[0*MSG_BUF_SIZE + 2]) & 0xFF;
    can2_RecMessage.data[4] = (CAN2->RAMn[0*MSG_BUF_SIZE + 3]) >> 24;
    can2_RecMessage.data[5] = ((CAN2->RAMn[0*MSG_BUF_SIZE + 3]) >> 16) & 0xFF;
    can2_RecMessage.data[6] = ((CAN2->RAMn[0*MSG_BUF_SIZE + 3]) >> 8) & 0xFF;
    can2_RecMessage.data[7] = (CAN2->RAMn[0*MSG_BUF_SIZE + 3]) & 0xFF;
}

這個時候還不能接收,因為我需要把這個接收函數放到中斷函數中,因為我設定的接收MB為0,所以接收中斷函數是CAN2_ORed_0_15_MB_IRQHandler();

void CAN2_ORed_0_15_MB_IRQHandler(void)
{
        gpio_reverse(PTe, 22);
        can2_receive_8Byte(StandardID);
        CAN2->IFLAG1 |= 0x0000FFFF;//清除標誌位
}

每次接收到報文後,進入中斷,在接收中斷函數裡讀取報文ID和DATA。注意在中斷函數裡清除相應的MB標誌位,即可進行下一次接收中斷。

本次我設定的接收全域性掩碼設定都為0x0,且設定的標準幀。所以對於所有的標準幀ID報文,我都可以接收。