今日配置下CAN通信,顺带理解下CAN 的一些基础知识

目录
[Ti 例程代码注意点:](#Ti 例程代码注意点:)
[CAN BUSOFF](#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-HCAN-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);
}
主函数:
cpp
#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:












