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) { 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报文,我都可以接收。