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*/ } }
参考: