1】CAN总线为什么要有两个120Ω的终端电阻?
高频信号传输时,信号波长先对传输线较短,信号在传输线终端会形成放射波,干扰原信号,所以需要在传输线末端加终端电阻,使信号到达传输线末端后不放射。对于低频信号则不用。
2】CAN总线远距离通讯接120Ω的终端电阻,有时候通讯不上?
CAN的终端要加120Ω,那是短距离的阻抗匹配结果,如果在长距离上还是加120Ω是会对信号有极大危害的,因为CAN收发器的驱动能力是一定的,传了5公里后,回路线阻抗(CAN_H+CAN_L)将近250Ω,如果加上120Ω电阻,那么终端接收节点的收发器所能收到的电压只有1V左右。这样的的电压幅值的抗干扰能力极差的,通讯呈现为时好时坏的现象。故在远距离通讯中,终端电阻建议加到300Ω-500Ω左右,可以保证信号幅值在1.2V之上。
3】CAN2.0B 协议帧格式
标准帧
字节 1为帧信息。第 7位( FF )表示帧格式,在标准帧中 FF =0;第 6位( RTRRTR)表 示帧的类型, RTR=0 表示为数据帧, RTR=1 表示为远程帧; DLC 表示在数据帧时实际的数据长度。 最高只能为8位即0x00XX 0111
字节 2、3为报文识别码, ,11 位有效。最高只能位0x07FF 即最大只能为2047
字节 4~11 为数据帧的实际数据,远程帧无效。只能发8个字节数据帧
扩展帧
字节 1为帧信息。第 7位( FF )表示帧格式,在扩展帧中 FF = 1;第 6位( RTRRTR) 表示帧的类型, RTR=0 表示为数据帧, RTR=1 表示为远程帧; DLC 表示在数据帧时实际的数据长度。
字节 2~5为报文识别码,其高 29 位有效。
字节 6~13 为数据帧的实际数据,远程帧无效。
标准帧与扩展帧得区别在于扩展帧的ID比标准帧多了18位。
CAN发送程序
扩展帧发送
一、
u8 CAN1_Send_Msg(u8* msg, u8 len)//**扩展帧配置为远程帧**
{
u8 mbox;
u16 i = 0;
CanTxMsg TxMessage;
TxMessage.ExtId = 0xFAFFF2; // 设置扩展标示符(29位)
TxMessage.RTR = CAN_RTR_REMOTE;//该帧为远程帧
TxMessage.IDE = CAN_ID_EXT; //帧为扩展帧
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;
}
CAN1_Send_Msg(Can_txbuffer, 8);
注意:扩展帧配置为远程帧,远程帧不带数据,只有ID号,可以有数据长度,但是无数据。
**
二、
u8 CAN1_Send_Msg(u8* msg, u8 len)**扩展帧配置为数据帧**
{
u8 mbox;
u16 i = 0;
CanTxMsg TxMessage;
TxMessage.ExtId = 0xFAFFF2; // 设置扩展标示符(29位)
TxMessage.RTR = CAN_RTR_DATA;//发送帧类型为数据帧
TxMessage.IDE = CAN_ID_EXT; //帧为扩展帧
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;
}
注意:扩展帧配置为数据帧的只需要配置 TxMessage.RTR = CAN_RTR_DATA;
标志帧发送
三、
标准帧数据帧
u8 CAN1_Send_Msg(u8* msg, u8 len)
{
u8 mbox;
u16 i = 0;
CanTxMsg TxMessage;
TxMessage.StdId = 0xEA; // 标准标识符为0
TxMessage.RTR = CAN_RTR_DATA;//发送帧类型为数据帧
TxMessage.IDE = CAN_ID_STD;//该帧为标志帧格式
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;
}
四、标志帧远程帧
u8 CAN1_Send_Msg(u8* msg, u8 len)
{
u8 mbox;
u16 i = 0;
CanTxMsg TxMessage;
TxMessage.StdId = 0xEA; // 标准标识符为0
TxMessage.RTR = CAN_RTR_REMOTE;//发送帧类型为远程帧
TxMessage.IDE = CAN_ID_STD;//该帧为标志帧格式
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;
}
总结:标准帧与扩展帧的配置TxMessage.IDE 相关,帧的ID由TxMessage.StdId与TxMessage.ExtId 控制,stdid为标准帧ID,EXtid为扩展帧的ID。TxMessage.RTR控制该帧的类型使数据帧还是远程帧,CAN_RTR_REMOTE为远程帧,CAN_RTR_DATA为数据帧。
CANJ接收程序
u8 can_rxIDE=0xff;//帧类型:标准帧/扩展帧
u8 can_rxRTR=0xff;//帧形式:数据帧/远程帧
u8 can_rxLEN=0;//数据帧长度
u8 can_rxbuf[8]={0};//数据缓存
u32 can_rxID=0;//帧ID
#define CAN1_RX0_INT_ENABLE 1 //0,不使能;1,使能.
#if CAN1_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
if(CAN_GetITStatus(CAN1,CAN_IT_FMP0)==SET)
{
CAN_Receive(CAN1, 0, &RxMessage);
memcpy(can_rxbuf,&RxMessage.Data[0],RxMessage.DLC);
can_rxIDE=RxMessage.IDE;//0为标准帧 0x04为扩展帧
can_rxRTR=RxMessage.RTR;//0为数据帧 0x02为远程帧
if(can_rxIDE)
can_rxID=RxMessage.ExtId;
else can_rxID=RxMessage.StdId;
}
CAN_ClearITPendingBit(CAN1,CAN_IT_FMP0);
}
#endif
u8 CAN1_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 CAN1_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
//使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA11,PA12
//引脚复用映射配置
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1); //GPIOA11复用为CAN1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1); //GPIOA12复用为CAN1
//CAN单元设置
CAN_InitStructure.CAN_TTCM = DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM = DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
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; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2 = tbs2; //Tbs2范围CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler = brp; //分频系数(Fdiv)为brp+1
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 = 0x0000; 32位ID
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; //32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
#if CAN1_RX0_INT_ENABLE
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
CAN过滤器
只针对标准CAN ID只接收标准的CAN ID以及指定的CAN ID ,只接收StdIdArray[10]的标准ID
u8 CAN1_Mode_Init(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode)
{
uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,
0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; //定义一组标准CAN ID
uint16_t mask,num,tmp,i;
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN1_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
//使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA11,PA12
//引脚复用映射配置
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1); //GPIOA11复用为CAN1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1); //GPIOA12复用为CAN1
//CAN单元设置
CAN_InitStructure.CAN_TTCM = DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM = DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
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; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2 = tbs2; //Tbs2范围CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler = brp; //分频系数(Fdiv)为brp+1
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; //16位
CAN_FilterInitStructure.CAN_FilterIdHigh = (StdIdArray[0]<<5); 32位ID
CAN_FilterInitStructure.CAN_FilterIdLow = 0;
mask =0x7ff; //下面开始计算屏蔽码
num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
for(i =0; i<num; i++) //屏蔽码位StdIdArray[]数组中所有成员的同或结果
{
tmp =StdIdArray[i] ^ (~StdIdArray[0]); //所有数组成员与第0个成员进行同或操作
mask &=tmp;
}
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (mask<<5); //32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0|0x02;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
#if CAN1_RX0_INT_ENABLE
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
16位宽的列表模式 将需要接收的CANID写如下图的四个寄存器,则只能接收定义的4个标准ID且只能是数据帧
uint32_t StdId1 =0x123; //这里采用4个标准CAN ID作为例子
uint32_t StdId2 =0x124;
uint32_t StdId3 =0x125;
uint32_t StdId4 =0x126;
//配置过滤器
CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit; //16位
CAN_FilterInitStructure.CAN_FilterIdHigh = (StdId1<<5); 32位ID
CAN_FilterInitStructure.CAN_FilterIdLow = (StdId2<<5);
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = (StdId3<<5); //32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow = (StdId4<<5);
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化