今日配置下CAN通信,顺带理解下CAN 的一些基础知识
目录
CAN基础知识:
CAN控制器
CAN收发器
CAN总线上的0和1
CAN通信示波器信号直观感受:
CAN盒与MCU的连接:
Ti 例程代码注意点:
代码贴出:
#include "my_can.h"
主函数:
测试效果:
CAN BUSOFF
CAN基础知识:
CAN控制器
CAN控制器内部结构还是挺复杂的,一般现在CAN控制器都是与处理器集成在一起。
其实对于编程的人来说,无非也就是包含一些控制、状态、配置等寄存器。
比如我们看到有些STM32芯片带有CAN,也就是说CAN控制器已经集成在STM32芯片中了,我们只需要编程操作其中的寄存器即可。
F28379D里面有CAN芯片:
CAN收发器
将CAN收发引脚(CAN_TX和CAN_RX)的TTL信号转换成CAN总线的电平信号。
CAN总线上的0和1
CAN总线为「两线」「差分」信号,用隐形代表逻辑1,显性代表逻辑0。
CAN通信示波器信号直观感受:
CAN的波形差不多是这样的,这里由于Ti例程的时钟影响,说不清是什么波特率的CAN,但波形是正确的,只是频率未知:
加了逻辑分析的效果如下:
CAN盒与MCU的连接:
CAN盒子
CAN-H 接 CAN-H
CAN-L 接 CAN-L
GND 接 GND
设置普通CAN通信,波特率:
正常通信 就能看到 如图所示:
右下角会有接收成功与错误计数,一般都是波特率不匹配,总线电平不对会错误帧:
Ti 例程代码注意点:
Ti的CAN例程,时钟频率不是很对,这会导致没法通信成功
主要是device.h这俩块需要注意对比清楚,必要时示波器分析:
我的程序在使用时发现需要将他的频率计算/2才正常:
代码贴出:
#include "my_can.h"
#ifndef _MY_CAN_H_ #define _MY_CAN_H_ #include "driverlib.h" #include "device.h" #include "board.h" #include "c2000ware_libraries.h" #define GPIO_CFG_CANRXB GPIO_17_CANRXB // "pinConfig" for CANB RX #define GPIO_CFG_CANTXB GPIO_12_CANTXB // "pinConfig" for CANB TX #define MSG_DATA_LENGTH 8 #define MSG_DATARX_LENGTH 0 #define TX_MSG_OBJ_ID 1 // 发送邮箱 #define RX_MSG_OBJ_ID 2 // 接收邮箱 #define CAN_ID 0x1 // 收发ID都是 0x1 extern uint16_t txMsgData[8]; extern uint16_t txMsgData2[8]; extern uint16_t rxMsgData[8]; void MY_can_bsp_init(); __interrupt void canB_ISR(void);// 接收中断函数 #endif#include "my_can.h" uint16_t txMsgData[8]; uint16_t txMsgData2[8]; uint16_t rxMsgData[8]; uint16_t rxMsgCount = 0; void MY_can_bsp_init() { txMsgData[0] = 0x55;txMsgData[1] = 0xff;txMsgData[2] = 0x55;txMsgData[3] = 0xff; txMsgData[4] = 0x55;txMsgData[5] = 0xff;txMsgData[6] = 0x55;txMsgData[7] = 0xff; txMsgData2[0] = 0x12;txMsgData2[1] = 0x34;txMsgData2[2] = 0x56;txMsgData2[3] = 0x78; txMsgData2[4] = 0x21;txMsgData2[5] = 0x43;txMsgData2[6] = 0x65;txMsgData2[7] = 0x87; GPIO_setPinConfig(GPIO_CFG_CANRXB); // CANB RX GPIO_setPinConfig(GPIO_CFG_CANTXB); // CANB TX // Initialize the CAN controllers CAN_initModule(CANB_BASE); // Set up the CAN bus bit rate to 500 kbps // 500k 20 // 250k 20 / 16 // 1000k 10 CAN_setBitRate(CANB_BASE, DEVICE_SYSCLK_FREQ/2,500000, 20); // CANB //CAN_enableTestMode(CANB_BASE, CAN_TEST_EXL); // Initialize the transmit message object used for sending CAN messages. // Message Object Parameters: // CAN Module: A // Message Object ID Number: 1 // Message Identifier: 0x01 // Message Frame: Standard // Message Type: Transmit // Message ID Mask: 0x0 // Message Object Flags: None // Message Data Length: 4 Bytes // CAN_setupMessageObject(CANB_BASE, TX_MSG_OBJ_ID, CAN_ID, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_TX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_DATA_LENGTH); // 配置接收邮箱 2 // Message Data Length: "Don't care" for a Receive mailbox CAN_setupMessageObject(CANB_BASE, RX_MSG_OBJ_ID, CAN_ID, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_USE_ID_FILTER | CAN_MSG_OBJ_RX_INT_ENABLE, MSG_DATA_LENGTH); // 注册 CANB 中断 //CAN_enableInterrupt(CANB_BASE, CAN_INT_IE0 | CAN_INT_ERROR | CAN_INT_STATUS); // CANB CAN_enableInterrupt(CANB_BASE, CAN_INT_IE0 | CAN_INT_ERROR |CAN_INT_STATUS); // CANB Interrupt_register(INT_CANB0, &canB_ISR); Interrupt_enable(INT_CANB0); CAN_enableGlobalInterrupt(CANB_BASE, CAN_GLOBAL_INT_CANINT0); // Start CANB module operations CAN_startModule(CANB_BASE); } uint32_t status; // 接收中断函数 __interrupt void canB_ISR(void) { int i=0; uint32_t errReg; status = CAN_getInterruptCause(CANB_BASE); if(status == CAN_INT_INT0ID_STATUS) { errReg = CAN_getStatus(CANB_BASE); if((errReg & 0x8000U) != 0U) // BUSOFF触发 { // 软复位CAN退出BUSOFF CAN_initModule(CANB_BASE); CAN_setBitRate(CANB_BASE, DEVICE_SYSCLK_FREQ/2,500000, 20); CAN_startModule(CANB_BASE); } } else if(status == TX_MSG_OBJ_ID) { CAN_clearInterruptStatus(CANB_BASE, TX_MSG_OBJ_ID); //errorFlag = 0; } else if(status == RX_MSG_OBJ_ID) { CAN_readMessage(CANB_BASE, RX_MSG_OBJ_ID, rxMsgData); CAN_clearInterruptStatus(CANB_BASE, RX_MSG_OBJ_ID); for(i=0;i<8;i++) { txMsgData[i] = rxMsgData[i]; } rxMsgCount++; //errorFlag = 0; } CAN_clearGlobalInterruptStatus(CANB_BASE, CAN_GLOBAL_INT_CANINT0); Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9); }主函数:
#include "driverlib.h" #include "device.h" #include "board.h" #include "c2000ware_libraries.h" #include "my_can.h" #define delay_s(x) SysCtl_delay(((((long double)(x)) / (2.0L /(long double)DEVICE_SYSCLK_FREQ)) - 9.0L) / 5.0L) #define delay_ms(x) SysCtl_delay(((((long double)(x)) / (2000.0L /(long double)DEVICE_SYSCLK_FREQ)) - 9.0L) / 5.0L) #define delay_us(x) SysCtl_delay(((((long double)(x)) / (2000000.0L /(long double)DEVICE_SYSCLK_FREQ)) - 9.0L) / 5.0L) void main(void) { Device_init(); Device_initGPIO(); Interrupt_initModule(); Interrupt_initVectorTable(); EINT; ERTM; Board_init(); //GPIO11 用于呼吸灯,指示程序有没有卡住 GPIO_setPinConfig(GPIO_11_GPIO11); GPIO_setPadConfig(11, GPIO_PIN_TYPE_STD); GPIO_setQualificationMode(11, GPIO_QUAL_SYNC); GPIO_setDirectionMode(11, GPIO_DIR_MODE_OUT); GPIO_setControllerCore(11, GPIO_CORE_CPU1); //C2000Ware_libraries_init(); MY_can_bsp_init(); Interrupt_enableMaster(); //开启总中断 while(1) { CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID, MSG_DATA_LENGTH, txMsgData); // Poll TxOk bit in CAN_ES register to check completion of transmission //while(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_TXOK)) != CAN_ES_TXOK){} delay_ms(10); CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID, MSG_DATA_LENGTH, txMsgData2); // Poll TxOk bit in CAN_ES register to check completion of transmission //while(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_TXOK)) != CAN_ES_TXOK){} GPIO_togglePin(11); delay_ms(1000); } }测试效果:
我的程序很简单,DSP每隔约1s先后发送俩条信息
其中第一条可以被接收中断替换掉
如果接收中断收到新信息,那DSP发送的第一条就被替换为收到的信息
CAN BUSOFF
CAN 控制器里面有两个错误计数器:发送错误 / 接收错误 计数器
错一次 +1 正常 -1
≥ 128→BUSOFF(直接断开总线)
我的代码有软启恢复CAN的笨办法,因为我不知道为何通信老是进CAN BUSOFF: