CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。 在北美和西欧,CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线
bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B。它的设计 目标是,以小的CPU负荷来高效处理大量收到的报文。它也支持报文发送的优先级要求(优先 级特性可软件配置)。 对于安全紧要的应用,bxCAN提供所有支持时间触发通信模式所需的硬件功能。
● 支持CAN协议2.0A和2.0B主动模式
● 波特率高可达1兆位/秒
● 支持时间触发通信功能 发送
● 3个发送邮箱
● 发送报文的优先级特性可软件配置
● 记录发送SOF时刻的时间戳 接收
● 3级深度的2个接收FIFO
● 可变的过滤器组: ─ 在互联型产品中,CAN1和CAN2分享28个过滤器组 ─ 其它STM32F103xx系列产品中有14个过滤器组
● 标识符列表
● FIFO溢出处理方式可配置
● 记录接收SOF时刻的时间戳 时间触发通信模式
● 禁止自动重传模式
● 16位自由运行定时器
● 可在后2个数据字节发送时间戳 管理
● 中断可屏蔽
● 邮箱占用单独1块地址空间,便于提高软件效率 双CAN
● CAN1:是主bxCAN,它负责管理在从bxCAN和512字节的SRAM存储器之间的通信
● CAN2:是从bxCAN,它不能直接访问SRAM存储器
can.c (从机部分)
#include "can.h"
/**
*@function CAN初始化
*@param
* tsjw: 重新同步跳跃时间单元,范围:CAN_SJW_1tq~ CAN_SJW_4tq
* tbs2: 时间段2的时间单元,范围:CAN_BS2_1tq~CAN_BS2_8tq;
* tbs1: 时间段1的时间单元,范围:CAN_BS1_1tq ~CAN_BS1_16tq
* brp : 波特率分频器,范围:1~1024; tq=(brp)*tpclk1
* 波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
* mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
* Fpclk1的时钟在初始化时设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
* 则波特率:36M/((8+9+1)*4)=500Kbps
*@return 0,初始化成功;
* 其他,初始化失败;
*/
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);/* 使能PORTA时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);/* 使能CAN1时钟 */
/* CAN IO口设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /* 复用推挽 */
GPIO_Init(GPIOA, &GPIO_InitStructure); /* PA12----CAN_TX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /* 上拉输入 */
GPIO_Init(GPIOA, &GPIO_InitStructure); /* PA11----CAN_RX */
/* CAN单元设置 */
CAN_InitStructure.CAN_TTCM=DISABLE;
CAN_InitStructure.CAN_ABOM=DISABLE;
CAN_InitStructure.CAN_AWUM=DISABLE;
CAN_InitStructure.CAN_NART=ENABLE;
CAN_InitStructure.CAN_RFLM=DISABLE;
CAN_InitStructure.CAN_TXFP=DISABLE;
CAN_InitStructure.CAN_Mode= mode;
/* 波特率设置 */
CAN_InitStructure.CAN_SJW=tsjw;
CAN_InitStructure.CAN_BS1=tbs1;
CAN_InitStructure.CAN_BS2=tbs2;
CAN_InitStructure.CAN_Prescaler=brp;
CAN_Init(CAN1, &CAN_InitStructure); /* 初始化CAN1 */
CAN_FilterInitStructure.CAN_FilterNumber=0; /* 过滤器0 */
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; /* 屏蔽位宽模式 */
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; /* 32位宽 */
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0020; /* 从机的32位ID */
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0020; /* 32位MASK */
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0; /* 过滤器关联到FIFO0 */
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; /* 激活过滤器 */
CAN_FilterInit(&CAN_FilterInitStructure); /* 过滤器初始化 */
#if CAN_RX0_INT_ENABLE /* 如果使能RX0中断 */
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); /* FIFO消息挂号中断允许 */
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 主优先级为1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* 次优先级为0 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
#if CAN_RX0_INT_ENABLE /* 如果使能RX0中断 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
/**
*@function can发送一组数据
*固定格式:ID,标准帧,数据帧
*@param
* len:数据长度(max 8)
* msg:数据指针,最大8个字节
*@return
* 0----成功
* 其他----失败
*/
u8 Can_Send_Msg(u8* msg,u8 len,u32 ID)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=(ID >> 21); /* 标准标识符 */
TxMessage.ExtId=((ID >> 3)&0x3ffff); /* 设置扩展标识符 */
TxMessage.IDE=CAN_Id_Standard; /* 标准帧 */
TxMessage.RTR=CAN_RTR_Data; /* 数据帧 */
TxMessage.DLC=len; /* 要发送的数据长度 */
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; /* 等待数据发送 */
if(i>=0XFFF)return 1;
return 0;
}
/**
*@function CAN接收数据查询
*@param
* buf:数据缓存区
*@return
* 0----无数据
* 其他----接收到的数据长度
*/
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; /* 判断是否接收到数据 */
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); /* 读取数据 */
for(i=0;i<8;i++)
{
buf[i]=RxMessage.Data[i];
}
return RxMessage.DLC;
}
can.h(从机部分)
#ifndef __CAN_H
#define __CAN_H
#include "sys.h"
#define CAN_RX0_INT_ENABLE 0
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode);
u8 Can_Send_Msg(u8* msg,u8 len,u32 ID);
u8 Can_Receive_Msg(u8 *buf);
#endif
main.c(从机部分)
#include "can.h"
#include "sys.h"
u8 rec_buf[8] = {0};
int main(void)
{
int key = 0;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal);
while(1)
{
key = Can_Receive_Msg(rec_buf);
if(key!=0)
{
for(int i = 0;i<8;++i)
{
println(rec_buf[i]);
}
}
}
}
can.c (主机部分)
#include "can.h"
u8 can_buf[8] = {0};
/**
*@function CAN初始化
*@param
* tsjw: 重新同步跳跃时间单元,范围:CAN_SJW_1tq~ CAN_SJW_4tq
* tbs2: 时间段2的时间单元,范围:CAN_BS2_1tq~CAN_BS2_8tq;
* tbs1: 时间段1的时间单元,范围:CAN_BS1_1tq ~CAN_BS1_16tq
* brp : 波特率分频器,范围:1~1024; tq=(brp)*tpclk1
* 波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
* mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
* Fpclk1的时钟在初始化时设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
* 则波特率:36M/((8+9+1)*4)=500Kbps
*@return 0,初始化成功;
* 其他,初始化失败;
*/
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);/* 使能PORTA时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);/* 使能CAN1时钟 */
/* CAN IO口设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /* 复用推挽 */
GPIO_Init(GPIOA, &GPIO_InitStructure); /* PA12----CAN_TX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /* 上拉输入 */
GPIO_Init(GPIOA, &GPIO_InitStructure); /* PA11----CAN_RX */
/* CAN单元设置 */
CAN_InitStructure.CAN_TTCM=DISABLE;
CAN_InitStructure.CAN_ABOM=DISABLE;
CAN_InitStructure.CAN_AWUM=DISABLE;
CAN_InitStructure.CAN_NART=ENABLE;
CAN_InitStructure.CAN_RFLM=DISABLE;
CAN_InitStructure.CAN_TXFP=DISABLE;
CAN_InitStructure.CAN_Mode= mode;
/* 波特率设置 */
CAN_InitStructure.CAN_SJW=tsjw;
CAN_InitStructure.CAN_BS1=tbs1;
CAN_InitStructure.CAN_BS2=tbs2;
CAN_InitStructure.CAN_Prescaler=brp;
CAN_Init(CAN1, &CAN_InitStructure); /* 初始化CAN1 */
CAN_FilterInitStructure.CAN_FilterNumber=0; /* 过滤器0 */
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; /* 屏蔽位宽模式 */
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; /* 32位宽 */
CAN_FilterInitStructure.CAN_FilterIdHigh=0x2000; /* 主机的32位ID*/
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x2000; /* 32位MASK */
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0; /* 过滤器关联到FIFO0 */
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; /* 激活过滤器 */
CAN_FilterInit(&CAN_FilterInitStructure); /* 过滤器初始化 */
#if CAN_RX0_INT_ENABLE /* 如果使能RX0中断 */
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); /* FIFO消息挂号中断允许 */
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 主优先级为1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* 次优先级为0 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
#if CAN_RX0_INT_ENABLE /* 如果使能RX0中断 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
/**
*@function can发送一组数据
*固定格式:ID,标准帧,数据帧
*@param
* len:数据长度(max 8)
* msg:数据指针,最大8个字节
*@return
* 0----成功
* 其他----失败
*/
u8 Can_Send_Msg(u8* msg,u8 len,u32 ID)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=(ID >> 21); /* 标准标识符 */
TxMessage.ExtId=((ID >> 3)&0x3ffff); /* 设置扩展标识符 */
TxMessage.IDE=CAN_Id_Standard; /* 标准帧 */
TxMessage.RTR=CAN_RTR_Data; /* 数据帧 */
TxMessage.DLC=len; /* 要发送的数据长度 */
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; /* 等待数据发送 */
if(i>=0XFFF)return 1;
return 0;
}
/**
*@function CAN接收数据查询
*@param
* buf:数据缓存区
*@return
* 0----无数据
* 其他----接收到的数据长度
*/
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; /* 判断是否接收到数据 */
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); /* 读取数据 */
for(i=0;i<8;i++)
{
buf[i]=RxMessage.Data[i];
}
return RxMessage.DLC;
}
can.h (主机部分)
#ifndef __CAN_H
#define __CAN_H
#include "sys.h"
#define CAN_RX0_INT_ENABLE 0
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode);
u8 Can_Send_Msg(u8* msg,u8 len,u32 ID);
u8 Can_Receive_Msg(u8 *buf);
#endif
main.c(主机部分)
#include "can.h"
#include "sys.h"
extern u8 can_buf[];
int main(void)
{
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal);
while(1)
{
Can_Send_Msg(can_buf,8,0x00200000);
}
}
STM32内部集成了CAN总线控制器,CAN控制器用于实现CAN总线的协议底层以及数据链路层,用于生成CAN帧并以二进制码流的方式发送,在此过程中进行位填充、添加CRC校验、应答检测等操作;将接收到的二进制码流进行解析并接收,在此过程中进行收发比对、去位填充、执行CRC校验等操作。此外还需要进行冲突判断、错误处理等诸多任务。
但是,单单有CAN总线控制器是不足够的,由于CAN是使用差分信号进行通信的,那么我们还需要一种东西,来将CAN控制器生成的二进制码流转换成差分信号,同时将接收到的差分信号转换成二进制码流。这便是CAN总线收发器的作用。
原子的开发板上有些已经有了CAN总线收发器了,如果是自己做的最小系统板,那就需要外加一个CAN收发器,比如TJA1050。
为了保证CAN总线信号的稳定,在绘制PCBLayout的时候记得要走差分线。
TJA1050的外部电路图,如果对CAN总线的电气要求不是很严苛的话,可以参考原子开发板的原理图。只不过原子只使用了最简单并联的120Ω电阻的方式(为什么是120Ω,因为电缆的特性阻抗为120Ω,为了模拟无限远的传输线),我在绘制自己的pcb原理图时采用的是分裂式终端加上共模扼流圈的方式:
其中两个0R电阻只是当跳线帽使用而已。
当然,如果你仅仅只是做实验而已,也可以不使用CAN总线的主从通信模式(正常模式),STM32的CAN总线还有一个环回模式可以供实验使用,环回模式就不需要有两块STM32。