是一种异步半双工的通讯协议,只有CAN_High与CAN_Low两条信号线。
有两种连接形式:闭环总线(高速)和开环总线(远距离)
他使用的是一种差分信号来传输电信号
所谓差分信号就是两条信号线电平之差,来传输电信号1/0,
该方式优点是抗干扰能力强、可以远距离传输,因为当信号线过长的时候电阻累积过大,使得传输到的电信号不准确,而差分信号是使用两条信号线电平差,两条信号线只要长度相似信号误差都不会很大。
电位差为0时为逻辑1,电位差>=2.0时为逻辑0。
协议层内规定了一个数据位有四个段,分别为:SS段、PTS段、PBS1段、PBS2段
1Tq为1个时基单元
SS段:是同步段,若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。SS 段的大小固定为 1Tq。
PTS段:是传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS 段的大小可以为 1~8Tq。
PBS1段:PBS1 译为相位缓冲段,补偿SS段的误差,它的时间长度在重新同步的时候可以加长,如下图相位超前,既ss段比预计的时间超期一个时间基准,可以通过PBS1段校准。
PBS2段:这是另一个相位缓冲段,它的时间长度在重新同步时可以缩短,补偿整个数据对齐。
当节点1与节点2同时发送报文时,总线电平会有优先级,按照ID优先级选择其1
当节点1或节点2发现自身电平和总线电平不同时会停止发送报文,总线显性电平为0,有1和0时总线电平取0
CRC 段:为了保证报文的正确传输,CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的CRC 码跟接收到的 CRC 码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC 部分的计算一般由 CAN 控制器硬件完成,出错时的处理则由软件控制最大重发数。在 CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK段间隔起来。
ACK 段:ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界定符间隔开。
stm32f103c8芯片中带有CAN的控制器,但不带CAN的收发器,所以如果想用最小系统板进行CAN外设实验,需要自己连接CAN的收发器。
有一下五个主要部分
寄存器的31位30位分别控制两个模式
31位:是否静默
30位:是否回环
23-20位:TS2,表示TSS2占用多少个时间单元,TSS2=Tq*(TS2+1)
19-15位:TS1,与上同理
25-24位:SJW,定义每次可活动增减TS1与TS2数据位的最大值(调整时序时最大增减的时间单元个数)
9-0位:BRP,波特率分频器Tq=(BRP+1)*tPCLK
每个CAN有两个接收FIFO邮箱,每个邮箱有3级
两个FIFO接收邮箱公用28个筛选器组(f103是14个、f105是28个)
一个筛选器有两个寄存器,在上图的掩码映射中,写入1就代表ID寄存器中的这一位需要筛选,如果全写入0,即不筛选。
#define GPIO_CAN_RX_Pin GPIO_Pin_8
#define GPIO_CAN_TX_Pin GPIO_Pin_9
/****************************************************************************
*@*名称 : hal_CAN_GPIO_Config
*@*功能 : CAN的引脚口(PB8R/9T)
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct_CAN_TX;
GPIO_InitTypeDef GPIO_InitStruct_CAN_RX;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap1_CAN1,ENABLE);
GPIO_InitStruct_CAN_TX.GPIO_Pin = GPIO_CAN_TX_Pin ;
GPIO_InitStruct_CAN_TX.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct_CAN_TX.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct_CAN_TX);
GPIO_InitStruct_CAN_RX.GPIO_Pin = GPIO_CAN_RX_Pin ;
GPIO_InitStruct_CAN_RX.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct_CAN_RX.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct_CAN_RX);
}
这里需要提一下,因为核心板没有CAN的接收器,如果要验证功能,要使用回环模式
/****************************************************************************
*@*名称 : hal_CAN_Mode_Config
*@*功能 : 定义CAN模块的模式
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_Mode_Config(void)
{
CAN_InitTypeDef GAN_InitStruct_Mode;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
/*CAN寄存器初始化*/
CAN_DeInit(CAN1);
CAN_StructInit(&GAN_InitStruct_Mode);
GAN_InitStruct_Mode.CAN_Mode = CAN_Mode_Normal; //普通工作模式,CAN_Mode_LoopBack为回环模式
GAN_InitStruct_Mode.CAN_SJW = CAN_SJW_1tq; //可活动时间单元1tq
GAN_InitStruct_Mode.CAN_BS1 = CAN_BS1_5tq;
GAN_InitStruct_Mode.CAN_BS2 = CAN_BS2_3tq;
/*CAN单元初始化*/
GAN_InitStruct_Mode.CAN_TTCM=DISABLE; //MCR-TTCM 关闭时间触发通信模式使能
GAN_InitStruct_Mode.CAN_ABOM=ENABLE; //MCR-ABOM 自动离线管理
GAN_InitStruct_Mode.CAN_AWUM=ENABLE; //MCR-AWUM 使用自动唤醒模式
GAN_InitStruct_Mode.CAN_NART=DISABLE; //MCR-NART 禁止报文自动重传 DISABLE-自动重传
GAN_InitStruct_Mode.CAN_RFLM=DISABLE; //MCR-RFLM 接收FIFO 锁定模式 DISABLE-溢出时新报文会覆盖原有报文
GAN_InitStruct_Mode.CAN_TXFP=DISABLE; //MCR-TXFP 发送FIFO优先级 DISABLE-优先级取决于报文标示符
/* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB1 = 36 MHz) */
GAN_InitStruct_Mode.CAN_Prescaler =4; BTR-BRP 波特率分频器 定义了时间单元的时间长度 36/(1+5+3)/4=1 Mbps
CAN_Init(CAN1,&GAN_InitStruct_Mode)
}
/****************************************************************************
*@*名称 : hal_CAN_Filter_Config
*@*功能 : 初始化CAN模块的过滤器
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_Filter_Config(void)
{
CAN_FilterInitTypeDef GAN_InitStruct_Filter0;
CAN_FilterInitTypeDef GAN_InitStruct_Filter1;
GAN_InitStruct_Filter0.CAN_FilterActivation = ENABLE; //使能筛选器
GAN_InitStruct_Filter0.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0 ; //筛选器被关联到FIFO0
GAN_InitStruct_Filter0.CAN_FilterIdHigh = (((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data))>>16;
GAN_InitStruct_Filter0.CAN_FilterIdLow = ((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data);
GAN_InitStruct_Filter0.CAN_FilterMaskIdHigh = 0xFFFF; //筛选器高16位每位必须匹配
GAN_InitStruct_Filter0.CAN_FilterMaskIdLow= 0xFF00; //筛选器低16位每位必须匹配
GAN_InitStruct_Filter0.CAN_FilterMode = CAN_FilterMode_IdMask; //工作在掩码模式
GAN_InitStruct_Filter0.CAN_FilterNumber = 0;
GAN_InitStruct_Filter0.CAN_FilterScale = CAN_FilterScale_32bit; //筛选器位宽为单个32位。
CAN_FilterInit(&GAN_InitStruct_Filter0);
/*CAN通信中断使能*/
GAN_InitStruct_Filter1.CAN_FilterActivation = ENABLE; //使能筛选器
GAN_InitStruct_Filter1.CAN_FilterFIFOAssignment = CAN_Filter_FIFO1 ; //筛选器被关联到FIFO1
GAN_InitStruct_Filter1.CAN_FilterIdHigh = ((CAN_FIFO1_ID<<3)|CAN_Id_Extended|CAN_RTR_Data)>>16;
GAN_InitStruct_Filter1.CAN_FilterIdLow = ((CAN_FIFO1_ID<<3)|CAN_Id_Extended|CAN_RTR_Data);
GAN_InitStruct_Filter1.CAN_FilterMaskIdHigh = 0xFFFF; //筛选器高16位每位必须匹配
GAN_InitStruct_Filter1.CAN_FilterMaskIdLow= 0xFF00; //筛选器低16位每位必须匹配
GAN_InitStruct_Filter1.CAN_FilterMode = CAN_FilterMode_IdMask; //工作在掩码模式
GAN_InitStruct_Filter1.CAN_FilterNumber = 2;
GAN_InitStruct_Filter1.CAN_FilterScale = CAN_FilterScale_32bit; //筛选器位宽为单个32位。
CAN_FilterInit(&GAN_InitStruct_Filter1);
/*CAN通信中断使能*/
CAN_ITConfig(CAN1, CAN_IT_FMP1, ENABLE);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
}
这里我打开了两个接收FIFO
打开两个接收FIFO时需要注意:
该筛选器关联到的FIFO:GAN_InitStruct_Filter1.CAN_FilterFIFOAssignment
筛选器编号:GAN_InitStruct_Filter1.CAN_FilterNumber
这里不要配置好了一个,另一个就直接复制结构体导致错误了。
筛选器掩码配置要注意:因为低3位是配置RTR的标志位,高29位才是掩码ID配置,所以一开始将掩码ID左移3位,然后在或上IDE 位标志“宏 CAN_ID_EXT”以及RTR 位标
志“宏 CAN_RTR_DATA”然后再赋值入筛选器的高八位和低八位
GAN_InitStruct_Filter0.CAN_FilterIdHigh = (((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data))>>16;
GAN_InitStruct_Filter0.CAN_FilterIdLow = ((CAN_TargetID<<3)|CAN_Id_Extended|CAN_RTR_Data);
GAN_InitStruct_Filter0.CAN_FilterMaskIdHigh = 0xFFFF; //筛选器高16位每位必须匹配
GAN_InitStruct_Filter0.CAN_FilterMaskIdLow= 0xFF00; //筛选器低16位每位必须匹配
/****************************************************************************
*@*名称 : hal_CAN_NVIC_Config
*@*功能 : 初始化CAN模块的中断
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
static void hal_CAN_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/*中断设置*/
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //CAN1 RX0中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/*中断设置*/
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX1_IRQn; //CAN1 RX0中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
这里要注意,想要打开两个FIFO的中断是有区别的
FIFO0的中断通道名称是:USB_LP_CAN1_RX0_IRQn
FIFO1的中断通道名称是:CAN1_RX1_IRQn
FIFO0的中断函数名称是:void USB_LP_CAN1_RX0_IRQHandler(void)
FIFO1的中断函数名称是:void CAN1_RX1_IRQHandler(void)
/****************************************************************************
*@*名称 : CAN_SetMsg
*@*功能 : 配置CAN的发送信箱进行发送
*@*形参 : CanTxMsg型的结构体地址,发送的拓展ID,发送的数据存放地址
*@*返回值 : 无
****************************************************************************/
void CAN_SetMsg(CanTxMsg *TxMessage,uint32_t EXT_ID ,uint8_t* Data)
{
uint8_t ubCounter = 0;
//TxMessage.StdId=0x00;
TxMessage->ExtId=EXT_ID; //使用的扩展ID
TxMessage->IDE=CAN_ID_EXT; //扩展模式
TxMessage->RTR=CAN_RTR_DATA; //发送的是数据
TxMessage->DLC=8; //数据长度为8字节
/*设置要发送的数据0-7*/
for (ubCounter = 0; ubCounter < 8; ubCounter++)
{
TxMessage->Data[ubCounter] = Data[ubCounter];
}
CAN_Transmit(CAN1, TxMessage);//将TMIDxR_TxRQ标志位置1,发送CAN发送邮箱的报文发送出去
}
/****************************************************************************
*@*名称 : USB_LP_CAN1_RX0_IRQHandler
*@*功能 : CAN模块的FIFO0接收中断函数
*@*形参 : 无
*@*返回值 : 无
****************************************************************************/
void USB_LP_CAN1_RX0_IRQHandler(void)
{
/*从邮箱中读出报文*/
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
/* 比较ID是否为0x1111 */
if((RxMessage.ExtId >= 0x1110) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )
{
CAN_flag = 1; //接收成功
}
else
{
CAN_flag = 0; //接收失败
}
}
在以上函数中我打开了两个接收FIFO,希望能一次性接收6个CAN的报文,但在实际实验中,连续发送6个报文,只能接收3个成功。
查了好久的原因还是没有解决该问题,如果有大佬知道为什么,希望能帮我解惑。