- /* Setup the timer. */
- void app_tim_init(void)
- {
- NVIC_InitTypeDef NVIC_InitStruct;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
- RCC_ClocksTypeDef RCC_Clocks;
- RCC_GetClocksFreq(&RCC_Clocks);
- RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);
- TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
- TIM_TimeBaseInitStruct.TIM_Prescaler = (RCC_Clocks.PCLK2_Frequency / APP_TIM_UPDATE_STEP - 1);
- TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_COUNTERMODE_UP;
- TIM_TimeBaseInitStruct.TIM_Period = (APP_TIM_UPDATE_PERIOD - 1);
- TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
- TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
- TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
- TIM_ClearFlag(TIM1, TIM_IT_UPDATE);
- TIM_ITConfig(TIM1, TIM_IT_UPDATE, ENABLE);
- NVIC_InitStruct.NVIC_IRQChannel = TIM1_UP_IRQn;
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStruct);
- }
- void TIM1_UP_IRQHandler(void)
- {
- TIM_ClearITPendingBit(TIM1, TIM_IT_UPDATE);
- tmrTask_thread();
- }
在 main.c 文件中实现定时线程任务处理
这里对 tmrTask_thread() 函数进行完善。
- /* timer thread executes in constant intervals ********************************/
- void tmrTask_thread(void){
- INCREMENT_1MS(CO_timer1ms);
- if (CO->CANmodule[0]->CANnormal) {
- bool_t syncWas;
- /* Process Sync */
- syncWas = CO_process_SYNC(CO, TMR_TASK_INTERVAL);
- /* Read inputs */
- CO_process_RPDO(CO, syncWas);
- /* Further I/O or nonblocking application code may go here. */
- /* Write outputs */
- CO_process_TPDO(CO, syncWas, TMR_TASK_INTERVAL);
- /* verify timer overflow */
- if((TIM_GetITStatus(TIM1, TIM_IT_UPDATE) & TIM_IT_UPDATE) != 0u) {
- CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0u);
- TIM_ClearITPendingBit(TIM1, TIM_IT_UPDATE);
- }
- }
- }
在 main.c 文件中实现 FlexCAN 的中断服务函数
- /* CAN interrupt function *****************************************************/
- void FLEXCAN_IRQHandler(void)
- {
- FLEXCAN_TransferHandleIRQ(FLEXCAN, &FlexCAN_Handle);
- CO_CANinterrupt(CO->CANmodule[0]);
- __DSB();
- }
在 CO_driver.c 文件中实现FlexCAN模块配置
实现包括对 FlexCAN 相关的 GPIO引脚、时钟、CAN报文收发消息缓冲区的配置。
- void FlexCAN_Configure(uint32_t can_bitrate)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- NVIC_InitTypeDef NVIC_InitStruct;
- RCC_ClocksTypeDef RCC_Clocks;
- flexcan_config_t FlexCAN_ConfigStruct;
- flexcan_rx_mb_config_t FlexCAN_RxMB_ConfigStruct;
- RCC_GetClocksFreq(&RCC_Clocks);
- RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_FLEXCAN, ENABLE);
- RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
- GPIO_PinAFConfig(GPIOA, GPIO_PINSOURCE11, GPIO_AF_9);
- GPIO_PinAFConfig(GPIOA, GPIO_PINSOURCE12, GPIO_AF_9);
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_PIN_11;
- GPIO_InitStruct.GPIO_Speed = GPIO_SPEED_HIGH;
- GPIO_InitStruct.GPIO_Mode = GPIO_MODE_FLOATING;
- GPIO_Init(GPIOA, &GPIO_InitStruct);
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_PIN_12;
- GPIO_InitStruct.GPIO_Speed = GPIO_SPEED_HIGH;
- GPIO_InitStruct.GPIO_Mode = GPIO_MODE_AF_PP;
- GPIO_Init(GPIOA, &GPIO_InitStruct);
- NVIC_InitStruct.NVIC_IRQChannel = FLEXCAN_IRQn;
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStruct);
- FLEXCAN_GetDefaultConfig(&FlexCAN_ConfigStruct);
- FlexCAN_ConfigStruct.baudRate = can_bitrate*1000;
- FlexCAN_ConfigStruct.clkSrc = Enum_Flexcan_ClkSrc1;
- FlexCAN_ConfigStruct.enableLoopBack = false;
- FlexCAN_ConfigStruct.disableSelfReception = true;
- FlexCAN_ConfigStruct.enableIndividMask = true;
- #if 1 /* Baudrate calculate by automatically */
- FLEXCAN_CalculateImprovedTimingValues(FlexCAN_ConfigStruct.baudRate, RCC_Clocks.PCLK1_Frequency, &FlexCAN_ConfigStruct.timingConfig);
- #else /* You can modify the parameters yourself */
- FlexCAN_ConfigStruct.timingConfig.preDivider = 23;
- FlexCAN_ConfigStruct.timingConfig.propSeg = 6;
- FlexCAN_ConfigStruct.timingConfig.phaseSeg1 = 3;
- FlexCAN_ConfigStruct.timingConfig.phaseSeg2 = 3;
- FlexCAN_ConfigStruct.timingConfig.rJumpwidth = 3;
- #endif
- FLEXCAN_Init(FLEXCAN, &FlexCAN_ConfigStruct);
- /* Set Tx MB_2. */
- FLEXCAN_TxMbConfig(FLEXCAN, BOARD_FLEXCAN_TX_MB_CH, ENABLE);
- FLEXCAN_TransferCreateHandle(FLEXCAN, &FlexCAN_Handle, FlexCAN_Transfer_Callback, NULL);
- /* Set Rx MB_0. */
- FlexCAN_RxMB_ConfigStruct.id = FLEXCAN_ID_STD(0x222);
- FlexCAN_RxMB_ConfigStruct.format = Enum_Flexcan_FrameFormatStandard;
- FlexCAN_RxMB_ConfigStruct.type = Enum_Flexcan_FrameTypeData;
- FLEXCAN_RxMbConfig(FLEXCAN, BOARD_FLEXCAN_RX_MB_CH, &FlexCAN_RxMB_ConfigStruct, ENABLE);
- /* Set Rx Individual Mask. */
- FLEXCAN_SetRxIndividualMask(FLEXCAN, BOARD_FLEXCAN_RX_MB_CH, FLEXCAN_RX_MB_STD_MASK(0x000, 0, 0));
- FlexCAN_MB0_FrameStruct.length = (uint8_t)(8);
- FlexCAN_MB0_FrameStruct.type = (uint8_t)Enum_Flexcan_FrameTypeData;
- FlexCAN_MB0_FrameStruct.format = (uint8_t)Enum_Flexcan_FrameFormatStandard;
- FlexCAN_MB0_FrameStruct.id = FLEXCAN_ID_STD(0x222);
- FlexCAN_MB0_TransferStruct.mbIdx = BOARD_FLEXCAN_RX_MB_CH;
- FlexCAN_MB0_TransferStruct.frame = &FlexCAN_MB0_FrameStruct;
- FLEXCAN_TransferReceiveNonBlocking(FLEXCAN, &FlexCAN_Handle, &FlexCAN_MB0_TransferStruct);
- }
- /******************************************************************************/
- CO_ReturnError_t CO_CANmodule_init(
- CO_CANmodule_t *CANmodule,
- void *CANdriverState,
- CO_CANrx_t rxArray[],
- uint16_t rxSize,
- CO_CANtx_t txArray[],
- uint16_t txSize,
- uint16_t CANbitRate)
- {
- uint16_t i;
- /* verify arguments */
- if(CANmodule==NULL || rxArray==NULL || txArray==NULL){
- return CO_ERROR_ILLEGAL_ARGUMENT;
- }
- /* Configure object variables */
- CANmodule->CANdriverState = CANdriverState;
- CANmodule->rxArray = rxArray;
- CANmodule->rxSize = rxSize;
- CANmodule->txArray = txArray;
- CANmodule->txSize = txSize;
- CANmodule->CANnormal = false;
- CANmodule->useCANrxFilters = false;/* microcontroller dependent */
- CANmodule->bufferInhibitFlag = false;
- CANmodule->firstCANtxMessage = true;
- CANmodule->CANtxCount = 0U;
- CANmodule->errOld = 0U;
- CANmodule->em = NULL;
- for(i=0U; i<rxSize; i++){
- rxArray[i].ident = 0U;
- rxArray[i].mask = 0xFFFFU;
- rxArray[i].object = NULL;
- rxArray[i].pFunct = NULL;
- }
- for(i=0U; i<txSize; i++){
- txArray[i].bufferFull = false;
- }
- FlexCAN_Configure(CANbitRate);
- return CO_ERROR_NO;
- }
在 CO_driver.c 文件中实现FlexCAN的报文收发
对 flexcan_tx() 函数及 CO_CANinterrupt()函数的实现。
- /* Send a message frame. */
- bool flexcan_tx(CO_CANtx_t *buffer)
- {
- bool status = false;
- flexcan_frame_t FlexCAN_FrameStruct;
- flexcan_mb_transfer_t FlexCAN_MB_TransferStruct;
- if (!buffer->rtr)
- {
- FlexCAN_FrameStruct.type = (uint8_t)Enum_Flexcan_FrameTypeData; /* Data frame type. */
- }
- else
- {
- FlexCAN_FrameStruct.type = (uint8_t)Enum_Flexcan_FrameTypeRemote; /* Remote frame type. */
- }
- FlexCAN_FrameStruct.length = (uint8_t)buffer->DLC;
- FlexCAN_FrameStruct.format = (uint8_t)Enum_Flexcan_FrameFormatStandard;
- FlexCAN_FrameStruct.id = FLEXCAN_ID_STD(buffer->ident); /* Indicated ID number. */
- FlexCAN_FrameStruct.dataByte0 = buffer->data[0];
- FlexCAN_FrameStruct.dataByte1 = buffer->data[1];
- FlexCAN_FrameStruct.dataByte2 = buffer->data[2];
- FlexCAN_FrameStruct.dataByte3 = buffer->data[3];
- FlexCAN_FrameStruct.dataByte4 = buffer->data[4];
- FlexCAN_FrameStruct.dataByte5 = buffer->data[5];
- FlexCAN_FrameStruct.dataByte6 = buffer->data[6];
- FlexCAN_FrameStruct.dataByte7 = buffer->data[7];
- FlexCAN_MB_TransferStruct.mbIdx = 2;
- FlexCAN_MB_TransferStruct.frame = &FlexCAN_FrameStruct;
- if (Status_Flexcan_Success == FLEXCAN_TransferSendNonBlocking(FLEXCAN, &FlexCAN_Handle, &FlexCAN_MB_TransferStruct))
- {
- status = true;
- }
- return status;
- }
- /******************************************************************************/
- CO_ReturnError_t CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer){
- CO_ReturnError_t err = CO_ERROR_NO;
- /* Verify overflow */
- if(buffer->bufferFull){
- if(!CANmodule->firstCANtxMessage){
- /* don't set error, if bootup message is still on buffers */
- CO_errorReport((CO_EM_t*)CANmodule->em, CO_EM_CAN_TX_OVERFLOW, CO_EMC_CAN_OVERRUN, buffer->ident);
- }
- err = CO_ERROR_TX_OVERFLOW;
- }
- CO_LOCK_CAN_SEND();
- bool tx_mb_status = flexcan_tx(buffer);
- if(tx_mb_status == true){
- CANmodule->bufferInhibitFlag = buffer->syncFlag;
- }
- /* if no buffer is free, message will be sent by interrupt */
- else{
- buffer->bufferFull = true;
- CANmodule->CANtxCount++;
- }
- CO_UNLOCK_CAN_SEND();
- return err;
- }
- void CO_CANinterrupt(CO_CANmodule_t *CANmodule){
- uint32_t status = FLEXCAN->IFLAG1;
- if (0 != (status & (BOARD_FLEXCAN_RX_MB_STATUS)) || (FlexCAN_MB0_RxCompleteFlag))
- {
- /* receive interrupt */
- CO_CANrxMsg_t *rcvMsg; /* pointer to received message in CAN module */
- CO_CANrxMsg_t rcvMsgBuff;
- uint16_t index; /* index of received message */
- uint32_t rcvMsgIdent; /* identifier of the received message */
- CO_CANrx_t *buffer = NULL; /* receive message buffer from CO_CANmodule_t object. */
- bool_t msgMatched = false;
- /* get message from module here */
- rcvMsg = &rcvMsgBuff;
- rcvMsg->ident = (FlexCAN_MBTemp_FrameStruct.id>> CAN_ID_STD_SHIFT)&0x7FF;
- rcvMsg->DLC = FlexCAN_MBTemp_FrameStruct.length;
- rcvMsg->data[0] = FlexCAN_MBTemp_FrameStruct.dataByte0;
- rcvMsg->data[1] = FlexCAN_MBTemp_FrameStruct.dataByte1;
- rcvMsg->data[2] = FlexCAN_MBTemp_FrameStruct.dataByte2;
- rcvMsg->data[3] = FlexCAN_MBTemp_FrameStruct.dataByte3;
- rcvMsg->data[4] = FlexCAN_MBTemp_FrameStruct.dataByte4;
- rcvMsg->data[5] = FlexCAN_MBTemp_FrameStruct.dataByte5;
- rcvMsg->data[6] = FlexCAN_MBTemp_FrameStruct.dataByte6;
- rcvMsg->data[7] = FlexCAN_MBTemp_FrameStruct.dataByte7;
- rcvMsgIdent = rcvMsg->ident;
- FlexCAN_MB0_RxCompleteFlag = 0;
- /* CAN module filters are not used, message with any standard 11-bit identifier */
- /* has been received. Search rxArray form CANmodule for the same CAN-ID. */
- buffer = &CANmodule->rxArray[0];
- for(index = CANmodule->rxSize; index > 0U; index--){
- if(((rcvMsgIdent ^ buffer->ident) & buffer->mask) == 0U){
- msgMatched = true;
- break;
- }
- buffer++;
- }
- /* Call specific function, which will process the message */
- if(msgMatched && (buffer != NULL) && (buffer->pFunct != NULL)){
- buffer->pFunct(buffer->object, rcvMsg);
- }
- /* Clear interrupt flag */
- FLEXCAN_ClearMbStatusFlags(FLEXCAN, BOARD_FLEXCAN_RX_MB_STATUS);
- }
- else if (0 != (status & BOARD_FLEXCAN_TX_MB_STATUS))
- {
- /* Clear interrupt flag */
- FLEXCAN_ClearMbStatusFlags(FLEXCAN, BOARD_FLEXCAN_TX_MB_STATUS);
- /* First CAN message (bootup) was sent successfully */
- CANmodule->firstCANtxMessage = false;
- /* clear flag from previous message */
- CANmodule->bufferInhibitFlag = false;
- /* Are there any new messages waiting to be send */
- if(CANmodule->CANtxCount > 0U){
- uint16_t i; /* index of transmitting message */
- /* first buffer */
- CO_CANtx_t *buffer = &CANmodule->txArray[0];
- /* search through whole array of pointers to transmit message buffers. */
- for(i = CANmodule->txSize; i > 0U; i--){
- /* if message buffer is full, send it. */
- if(buffer->bufferFull){
- buffer->bufferFull = false;
- CANmodule->CANtxCount--;
- /* Copy message to CAN buffer */
- CANmodule->bufferInhibitFlag = buffer->syncFlag;
- CO_CANsend(CANmodule, buffer);
- break; /* exit for loop */
- }
- buffer++;
- }/* end of for loop */
- /* Clear counter if no more messages */
- if(i == 0U){
- CANmodule->CANtxCount = 0U;
- }
- }
- }
- else{
- /* some other interrupt reason */
- }
- }
在 CO_driver.c 文件中实现CAN总线错误检测
关于 CO_CANverifyErrors() 函数的实现。
- void CO_CANverifyErrors(CO_CANmodule_t *CANmodule){
- uint16_t rxErrors, txErrors, overflow;
- CO_EM_t* em = (CO_EM_t*)CANmodule->em;
- uint32_t err;
- /* get error counters from module. Id possible, function may use different way to
- * determine errors. */
- rxErrors = (uint16_t) ((FLEXCAN->ECR & CAN_ECR_RXERRCNT_MASK) >> CAN_ECR_RXERRCNT_SHIFT);
- txErrors = (uint16_t) ((FLEXCAN->ECR & CAN_ECR_TXERRCNT_MASK) >> CAN_ECR_TXERRCNT_SHIFT);
- overflow = (uint16_t) ((FLEXCAN->ESR1 & CAN_ESR1_ERROVR_MASK) >> CAN_ESR1_ERROVR_SHIFT);
- err = ((uint32_t)txErrors << 16) | ((uint32_t)rxErrors << 8) | overflow;
- if(CANmodule->errOld != err){
- CANmodule->errOld = err;
- if(txErrors >= 256U){ /* bus off */
- CO_errorReport(em, CO_EM_CAN_TX_BUS_OFF, CO_EMC_BUS_OFF_RECOVERED, err);
- }
- else{ /* not bus off */
- CO_errorReset(em, CO_EM_CAN_TX_BUS_OFF, err);
- if((rxErrors >= 96U) || (txErrors >= 96U)){ /* bus warning */
- CO_errorReport(em, CO_EM_CAN_BUS_WARNING, CO_EMC_NO_ERROR, err);
- }
- if(rxErrors >= 128U){ /* RX bus passive */
- CO_errorReport(em, CO_EM_CAN_RX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err);
- }
- else{
- CO_errorReset(em, CO_EM_CAN_RX_BUS_PASSIVE, err);
- }
- if(txErrors >= 128U){ /* TX bus passive */
- if(!CANmodule->firstCANtxMessage){
- CO_errorReport(em, CO_EM_CAN_TX_BUS_PASSIVE, CO_EMC_CAN_PASSIVE, err);
- }
- }
- else{
- bool_t isError = CO_isError(em, CO_EM_CAN_TX_BUS_PASSIVE);
- if(isError){
- CO_errorReset(em, CO_EM_CAN_TX_BUS_PASSIVE, err);
- CO_errorReset(em, CO_EM_CAN_TX_OVERFLOW, err);
- }
- }
- if((rxErrors < 96U) && (txErrors < 96U)){ /* no error */
- CO_errorReset(em, CO_EM_CAN_BUS_WARNING, err);
- }
- }
- if(overflow != 0U){ /* CAN RX bus overflow */
- CO_errorReport(em, CO_EM_CAN_RXB_OVERFLOW, CO_EMC_CAN_OVERRUN, err);
- }
- }
- }
至此,驱动代码适配完成。
板载验证
验证环境
使用搭载了MM32G5330 MCU的开发板Mini-G5330 ,以CANopen_Basic样例工程为例,将开发板上的CAN收发器与PCAN相连接,并将PCAN与PC机通过USB相连接,在PC端(基于Win10操作系统)使用PCAN-View上位机模拟CANopen主站,来通过CANopen协议与CANopen从站(即 MM32 MCU)进行通信,如图5所示。
图5
注:这里我们使用了PCAN-USB,并使用了配套上位机PCAN-View。
验证过程
上述环境搭建好后,将上述工程代码编译后刷写固件进MCU,将MCU上电并复位通过PC端上位机PCAN-View测试如下指令,观察CANopen节点其对指令的响应,来判断该CANopen节点是否处于正常运行状态。
节点上线:
MCU上电后,CANopen节点应成功启动并向网络发送上线报文。
CANopen节点上线向CAN网络发送CANopen节点上线报文,PC上位机将收到一条如下报文:
表2
之后该CANopen节点以 1000ms 的时间间隔向CAN网络发送节点心跳报文,上位机以1000ms的时间间隔收到如下报文:
表3
如图6所示。
图6
至此,可验证该CANopen节点设备成功启动并开始正常运行。
模式切换:
通过上位机发送NMT命令,验证节点能够正确响应Start、Stop和Pre-operation等模式切换指令。
将NODE-ID为0x0A的节点设置为 Stop 模式,上位机PCAN-View发送如下指令:
表4
如下图7所示,可接收到如下报文:
图7
将NODE-ID为0x0A的节点设置为 Start 模式,上位机PCAN-View发送如下指令:
表5
如下图8所示,可接收到如下报文:
图8
将NODE-ID为0x0A的节点设置为Pre-operation模式,上位机PCAN-View发送如下指令:
表6
如下图9所示,该节点进入Pre-operation模式,可接收到如下报文:
图9
将NODE-ID为0x0A节点复位,上位机PCAN-View发送如下指令:
表7
如下图10所示,该节点被复位:
图10
将NODE-ID为0x0A节点的通信层复位,上位机PCAN-View发送如下指令:
表8
如下图11所示,该节点通信层被复位,重新上线:
图11
心跳检测:
节点应周期性发送心跳报文,以表明其处于活跃状态。
获取NODE-ID为0x0A节点的心跳发送间隔时间,上位机PCAN-View发送如下指令:
表9
如下图12所示,返回该节点当前心跳发送间隔时间为1000(0x03E8)ms:
图12
设置NODE-ID为0x0A节点的心跳发送间隔时间为500(0x01F4)ms,上位机PCAN-View发送如下指令:
表10
如下图13所示,该节点当前心跳发送间隔时间变为500ms:
图13
总结
通过本文的介绍,我们了解了CANopen协议的基本概念,并基于MM32G5330的FlexCAN完成了CANopenNode协议栈的移植工作。通过板载验证,我们确认了移植后的协议栈能够正常工作,为后续的设备集成和通信提供了进一步开发的基础。同样的开发者可以根据实际应用需求使用灵动其他带有FlexCAN的MCU,参考本文的方法进行相应的移植和验证工作,以实现高效可靠的CANopen通信。