CAN(Controller Area Network)是 ISO 国际标准化的串行通信协议。早期源于汽车行业对安全性、舒适性、低成本等要求,各种各样的电子控制系统被开发了出来。各系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。
为"减少线束的数量,通过多个LAN进行大量数据的高速通信"的需要,1986 年德国电气商Bosch 公司开发出面向汽车的CAN 通信协议。此后CAN 通过 ISO11898 及 ISO11519 进行了标准化。
CAN 总线的工作原理
CAN 总线使用串行数据传输方式,且总线协议支持多主控制器。当CAN 总线上的一个节点(站)发送数据时,它以报文形式广播给网络中所有节点。
每组报文开头的11位字符为标识符,定义了报文的优先级,这种报文格式称为面向内容的编址方案。在同一系统中标识符是唯一的,不可能有两个站发送具有相同标识符的报文。当几个站同时竞争总线读取时,这种配置十分重要。
当一个站要向其它站发送数据时,该站的CPU 将要发送的数据和自己的标识符传送给本站的CAN 芯片,并处于准备状态;当它收到总线分配时,转为发送报文状态。
CAN 芯片将数据根据协议组织成一定的报文格式发出,这时网上的其它站处于接收状态。每个处于接收状态的站对接收到的报文进行检测,判断这些报文是否是发给自己的,以确定是否接收它。
CAN 的主要特点
1、 数据通信没有主从之分,任意一个节点可以向任何其他(一个或多个)节点发起数据通信,靠各个节点信息优先级先后顺序来决定通信次序。
2、 支持时间触发通信功能, 发送报文的优先级可软件配置。多个节点同时发起通信时,优先级低的避让优先级高的,不会对通信线路造成拥塞。
3、 CAN 是一种多主总线,通信介质可以是双绞线、同轴电缆或光导纤维。通信距离最远可达10KM(速率低于5Kbps),速率可达到1Mbps(通信距离小于40M)。
4、 CAN 总线采用了多主竞争式总线结构,具有多主站运行和分散仲裁的串行总线以及广播通信的特点。
5、 FIFO(First Input First Output),即先进先出队列,溢出处理方式可配置......
CAN总线的拓扑结构
CAN应用领域
CAN总线适用于大数据量短距离通信或者长距离小数据量,实时性要求比较高,适合多主多从或者各个节点平等的现场中使用。目前被广泛应用于工业自动化、船舶、医疗设备、工业设备等行业领域。
现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。
最后,来贴上我的驱动代码:
#ifndef __BSP_CAN_H__#define __BSP_CAN_H__ #include "can.h" #include "gpio.h" // 主频Pclk 与 波特率定义 #define CAN_CHIP_TJA1043 #define CAN_CONFIG_48MHZ_500K #define EVB_CAN_RX_PORT GpioPortB #define EVB_CAN_RX_PIN GpioPin8 #define EVB_CAN_TX_PORT GpioPortB #define EVB_CAN_TX_PIN GpioPin9 #define EVB_CAN_STB_PORT GpioPortA #define EVB_CAN_STB_PIN GpioPin3 /*IO_STB*/ #define CAN_ID_BACKLIGHT 0X3b3 /*背光*/ #define CAN_ID_ACC 0X263 /* ACC */ #define CAN_ID_GAIN 0X445 /*电子刹车增益*/ #define CAN_ID_KEEPALIVE 0X5C0 /*KEEPALIVE*/ #define CAN_ID_BRAKE_X 0X7d /*脚踩刹车深度*/ #define CAN_ID_BRAKE_SIGNAL 0X242 /*刹车信号*/ typedef struct stc_can { boolean_t RxFlag; boolean_t RxFlag_Break; /*接收到刹车深度数据*/ boolean_t BusErrorFlag; boolean_t CANSendFlag; boolean_t CAN_keepalive_SendFlag; boolean_t brake_signal; uint16_t brake_value; int Rx_cnt; /* 中断空闲30s倒计时进入低功耗*/ uint32_t brake_cnt; //刹车长踩8秒 uint32_t key_cnt; //按键长按计时 }stc_can_t; typedef enum ACC_Status { Off = 0u, On = 1u, }en_ACC_Status; extern stc_can_t APP_CAN; extern en_ACC_Status ACC_Status; extern stc_can_txframe_t TxFrame_Gain; extern stc_can_txframe_t TxFrame_Keepalive; void BSP_Can_Init(void); void CAN_send_Frame(stc_can_txframe_t *TxFrame, uint8_t frame_len); void TJA1043_Goto_Sleep(void); #endif
复制代码C文件:
#include "bsp_can.h"#include "bsp_led.h" #include "bsp_timer.h" stc_can_rxframe_t stcRxFrame; stc_can_txframe_t TxFrame_Gain; stc_can_txframe_t TxFrame_Keepalive; stc_can_t APP_CAN; en_ACC_Status ACC_Status = Off; extern void PWM_duty_adjust(uint16_t adc_value, uint16_t MAX_Value); static void Can_Gpio_Init(void) { stc_gpio_cfg_t stcGpioCfg; Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); ///< 端口方向配置->输入 stcGpioCfg.enDir = GpioDirIn; ///< 端口驱动能力配置->高驱动能力 stcGpioCfg.enDrv = GpioDrvL; ///< 端口上下拉配置->无 stcGpioCfg.enPu = GpioPuDisable; stcGpioCfg.enPd = GpioPdDisable; ///< 端口开漏输出配置->开漏输出关闭 stcGpioCfg.enOD = GpioOdDisable; ///< 端口输入/输出值寄存器总线控制模式配置->AHB stcGpioCfg.enCtrlMode = GpioAHB; Gpio_Init(EVB_CAN_RX_PORT, EVB_CAN_RX_PIN, &stcGpioCfg); stcGpioCfg.enDir = GpioDirOut; Gpio_Init(EVB_CAN_TX_PORT, EVB_CAN_TX_PIN, &stcGpioCfg); Gpio_Init(EVB_CAN_STB_PORT, EVB_CAN_STB_PIN, &stcGpioCfg); ///<CAN RX\TX复用功能配置 Gpio_SetAfMode(EVB_CAN_RX_PORT, EVB_CAN_RX_PIN, GpioAf3); Gpio_SetAfMode(EVB_CAN_TX_PORT, EVB_CAN_TX_PIN, GpioAf5); ///<STB 低-PHY有效 Gpio_ClrIO(EVB_CAN_STB_PORT, EVB_CAN_STB_PIN); } #ifdef CAN_CHIP_TJA1043 /* TJA1043 刚上电是出于激活状态的,如果CAN总线没有数据,30s左右会休眠 3个IO需要配置: IO_ERR 检测总线出错标志(可以忽略) IO_EN 芯片使能 IO_INH 使能外部LDO +5V IO_STB_N standby模式使能 */ static void TJA1043_Gpio_Init(void) { stc_gpio_cfg_t stcGpioCfg; Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); ///< 端口方向配置->输出 stcGpioCfg.enDir = GpioDirOut; ///< 端口驱动能力配置->高驱动能力 stcGpioCfg.enDrv = GpioDrvH; ///< 端口上下拉配置->无 stcGpioCfg.enPu = GpioPuDisable; stcGpioCfg.enPd = GpioPdDisable; ///< 端口开漏输出配置->开漏输出关闭 stcGpioCfg.enOD = GpioOdDisable; ///< 端口输入/输出值寄存器总线控制模式配置->AHB stcGpioCfg.enCtrlMode = GpioAHB; Gpio_Init(GpioPortA, GpioPin3, &stcGpioCfg); // STB_N Gpio_Init(GpioPortB, GpioPin3, &stcGpioCfg); //IO_INH Gpio_Init(GpioPortB, GpioPin7, &stcGpioCfg); // IO_EN Gpio_ClrIO(GpioPortB, GpioPin3); /* PB3 IO_INH*/ delay1ms(10); Gpio_SetIO(EVB_CAN_STB_PORT, EVB_CAN_STB_PIN); Gpio_SetIO(GpioPortB, GpioPin7);// IO_EN } void TJA1043_Goto_Sleep(void) { Gpio_ClrIO(EVB_CAN_STB_PORT, EVB_CAN_STB_PIN); Gpio_SetIO(GpioPortB, GpioPin7);// IO_EN } #endif void BSP_Can_Init(void) { stc_can_init_config_t stcCanInitCfg; stc_can_filter_t stcFilter; Can_Gpio_Init(); #ifdef CAN_CHIP_TJA1043 TJA1043_Gpio_Init(); #endif Sysctrl_SetPeripheralGate(SysctrlPeripheralCan, TRUE); //<<CAN 波特率配置 // 手册page884.exp: bt = (PRESC+1)((SEG_1+2) + (SEG_2+1))*(1/8M) = 1us (1000k) #ifdef CAN_CONFIG_48MHZ_500K stcCanInitCfg.stcCanBt.PRESC = 6-1; stcCanInitCfg.stcCanBt.SEG_1 = 9-2; stcCanInitCfg.stcCanBt.SEG_2 = 7-1; stcCanInitCfg.stcCanBt.SJW = 3-1; // stcCanInitCfg.stcCanBt.PRESC = 8-1; // stcCanInitCfg.stcCanBt.SEG_1 = 7-2; // stcCanInitCfg.stcCanBt.SEG_2 = 5-1; // stcCanInitCfg.stcCanBt.SJW = 5-1; #endif stcCanInitCfg.stcWarningLimit.CanErrorWarningLimitVal = 10; stcCanInitCfg.stcWarningLimit.CanWarningLimitVal = 16-1; stcCanInitCfg.enCanRxBufAll = CanRxNormal; stcCanInitCfg.enCanRxBufMode = CanRxBufNotStored; stcCanInitCfg.enCanSTBMode = CanSTBFifoMode; CAN_Init(&stcCanInitCfg); //<<CAN 滤波器配置 /*u32MASK = 0x1FFFFFFF is all received*/ stcFilter.enAcfFormat = CanStdFrames; stcFilter.enFilterSel = CanFilterSel1; stcFilter.u32CODE = CAN_ID_BACKLIGHT; stcFilter.u32MASK = 0x1FFFFFFF& (~CAN_ID_BACKLIGHT); CAN_FilterConfig(&stcFilter, TRUE); stcFilter.enFilterSel = CanFilterSel2; stcFilter.u32CODE = CAN_ID_ACC; stcFilter.u32MASK = 0x1FFFFFFF& (~CAN_ID_ACC); CAN_FilterConfig(&stcFilter, TRUE); stcFilter.enFilterSel = CanFilterSel3; stcFilter.u32CODE = CAN_ID_BRAKE_X; stcFilter.u32MASK = 0x1FFFFFFF& (~CAN_ID_BRAKE_X); CAN_FilterConfig(&stcFilter, TRUE); stcFilter.enFilterSel = CanFilterSel4; stcFilter.u32CODE = CAN_ID_BRAKE_SIGNAL; stcFilter.u32MASK = 0x1FFFFFFF& (~CAN_ID_BRAKE_SIGNAL); CAN_FilterConfig(&stcFilter, TRUE); CAN_IrqCmd(CanRxIrqEn, TRUE); /* 接收中断使能 */ CAN_IrqCmd(CanBusErrorIrqEn, TRUE); /*总线错误中断使能*/ EnableNvic(CAN_IRQn, IrqLevel0, TRUE); } /******************************************************************************************************** **函数信息 :void CAN_send_Frame() **功能描述 :CAN发送一帧报文 **输入参数 :stcTxFrame **输出参数 :None ** 备注 目前只考虑标准帧 ********************************************************************************************************/ void CAN_send_Frame(stc_can_txframe_t *TxFrame, uint8_t frame_len) { TxFrame->Control_f.DLC = frame_len; TxFrame->Control_f.IDE = 0; /*标准帧 or 扩展帧*/ TxFrame->Control_f.RTR = 0; /*数据帧 or 远程帧*/ TxFrame->enBufferSel = (en_can_buffer_sel_t)0U; /*使用主缓冲器*/ CAN_SetFrame(TxFrame); /*数据塞进fifo缓冲器*/ CAN_TransmitCmd(CanPTBTxCmd); } void Can_IRQHandler(void) { if(TRUE == CAN_IrqFlgGet(CanBusErrorIrqFlg)) /*当总线短路时,进入此中断*/ { CAN_IrqFlgClr(CanBusErrorIrqFlg); APP_CAN.BusErrorFlag = TRUE; // M0P_CAN->CFG_STAT = 0x00; /* 清除由硬件置位的Reset 标志位,必须在中断外部执行*/ } if(TRUE == CAN_IrqFlgGet(CanRxIrqFlg)) { CAN_IrqFlgClr(CanRxIrqFlg); CAN_Receive(&stcRxFrame); APP_CAN.RxFlag = TRUE; APP_CAN.Rx_cnt = 0; /*总线上发送周期约500ms*/ if( CAN_ID_BACKLIGHT == stcRxFrame.StdID ) { /* led off 3b3 40 48 00 12 10 05 80 02 led on 3b3 44 48 10 12 10 05 80 02 */ if(stcRxFrame.Data[0] & 0x04) { LED_ON; } else { LED_OFF; } } /* XX 00 00 20 52 FF FB 00 XX:02 (ON) 00(OFF) */ if (CAN_ID_ACC == stcRxFrame.StdID) { if(stcRxFrame.Data[0]&0x02) { ACC_Status = On; } else { ACC_Status = Off; /* ACC off 后延时30s进入休眠*/ } } /* ID:7D(刹车深度) //20ms一次 XX YY 00 00 00 00 00 00 XX:刹车深度高位 YY:刹车深度低位 XX<<8+YY = 0000 表示无刹车,最大到0F00(超级用力) */ if (CAN_ID_BRAKE_X == stcRxFrame.StdID) /* 刹车深度,最后与滑动变阻器比较,谁力度大则执行pwm输出*/ { APP_CAN.brake_value = (stcRxFrame.Data[0]<<8) + stcRxFrame.Data[1]; // 按等比例换算,再和adc_value 比较,再执行 APP_CAN.RxFlag_Break = TRUE; } /* ID:242(刹车信号) //40ms一次 00 00 xx 00 00 00 00 00 xx:80(有踩刹车) 00(无踩刹车) */ if (CAN_ID_BRAKE_SIGNAL == stcRxFrame.StdID) { if( stcRxFrame.Data[2]&0x80) { APP_CAN.brake_signal = TRUE; } else { APP_CAN.brake_signal = FALSE; } } } }
复制代码