STM32 CAN 通信(一)
STM32 CAN 配置
STM32 CAN 控制器需要配置的参数比较多,本文根据 SAE J1939 协议数据单元 对 STM32 CAN 进行配置。
CAN 的 GPIO 配置
/**********************************************************************************
* 函数名:CAN_GPIO_Config
* 描述 :CAN 的 GPIO 配置,PA11 上拉输入,PA12 推挽输出
* 输入 :无
* 输出 :无
* 返回 :无
* 调用 :内部调用
* 说明 :
**********************************************************************************/
static void CAN_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//外设时钟设置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
//RX PA11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//TX PA12
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);
}
CAN 的 NVIC 配置
/**********************************************************************************
* 函数名:CAN_NVIC_Config
* 描述 :CAN 的 NVIC 配置
* 输入 :无
* 输出 :无
* 返回 :无
* 调用 :内部调用
* 说明 :CAN 的中断由发送中断接收FIFO中断和错误中断构成。
1、发送中断由三个发送邮箱任意一个为空构成。
2、接收中断分为 FIFO0 和 FIFO1 的中断,接收 FIFO 收到新的报文
或报文溢出触发。
**********************************************************************************/
static void CAN_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //使能 CAN 接收中断
}
CAN 的 Mode 配置
/**********************************************************************************
* 函数名:CAN_Mode_Config
* 描述 :CAN 的 Mode 配置
* 输入 :无
* 输出 :无
* 返回 :无
* 调用 :内部调用
* 说明 :bxCAN有3个主要的工作模式:初始化、正常和睡眠模式。
**********************************************************************************/
static void CAN_Mode_Config(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
CAN_InitStructure.CAN_TTCM = DISABLE; //关闭时间触发通信模式使能
CAN_InitStructure.CAN_ABOM = ENABLE; //自动离线管理
CAN_InitStructure.CAN_AWUM = ENABLE; //自动唤醒模式
CAN_InitStructure.CAN_NART = DISABLE; //禁止报文自动重传 DISABLE = 自动重传
CAN_InitStructure.CAN_RFLM = DISABLE; //接收 FIFO 锁定模式 DISABLE = 溢出时新报文会覆盖原有报文
CAN_InitStructure.CAN_TXFP = DISABLE; //发送 FIFO 优先级 DISABLE = 优先级取决于报文标识符
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //正常工作模式
CAN_InitStructure.CAN_SJW = CAN_SJW_2tq; //重新同步跳跃宽度为2个时间单位
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq; //时间段1 占用6个时间单元
CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; //时间段2 占用3个时间单元
CAN_InitStructure.CAN_Prescaler = 30; //波特率分频器 定义了时间单元的长度 36/(1+6+3)/30 = 0.12Mbps
CAN_Init(CAN1,&CAN_InitStructure);
}
CAN波特率配置
波特率 = APB1Clock/(1+CAN_BS1+CAN_BS2)/CAN_Prescaler
(PS:CAN_SJW 表示重新同步跳跃宽度,不参与波特率的计算,其值可以编程为1到4个时间单元,表示该总线对波特率范围的容差度。公式中1表示同步段固定为一个单位时间。)
尽可能地把采样点设置为 CAN 组织 CiA 推荐的值:
CiA | 波特率 |
---|---|
75% | >800K |
80% | >500K |
87.5% | <=500K |
CAN 波特率与传输距离
标准波特率可以参考如下对应关系:
速率(Kbps) | 1000 | 500 | 250 | 125 | 100 | 50 | 20 | 10 |
---|---|---|---|---|---|---|---|---|
距离(m) | 40 | 130 | 270 | 530 | 620 | 1300 | 3300 | 6700 |
其它波特率也可以使shu用该公式:50/波特率(KBbps)10000.8 = 距离(m)。
CAN 的 Filter 配置
/**********************************************************************************
* 函数名:CAN_Filter_Config
* 描述 :CAN 的 Filter 配置
* 输入 :无
* 输出 :无
* 返回 :无
* 调用 :内部调用
* 说明 :(过滤器)配置,id 中目标地址为本机地址,接收时标识符完全相同才保存数据
1: 在各种过滤器模式下,CAN ID与寄存器相应位置一定要匹配;
2:在屏蔽方式下,屏蔽码寄存器某位为1表示接收到的CAN ID对应的位必须对验
证码寄存器对应的位相同。
**********************************************************************************/
static void CAN_Filter_Config(void)
{
CAN_FilterInitTypeDef CAN_FilterInitStructure;
//过滤器组0,只接受广播信息(PF = 240 - 255)
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //工作在标识符屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //过滤器位宽为单个32位
CAN_FilterInitStructure.CAN_FilterIdHigh = (((u32)0xf000<<3)&0xffff0000)>>16; //要过滤的ID高位
CAN_FilterInitStructure.CAN_FilterIdLow = ((u32)0xf000<<3)&0xffff; //要过滤的ID低位
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0007; //过滤器高16位 (MaskId << 3) >>16 左移3位--寄存器低3位为 保留位、RTR、IDE
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x1f00; //过滤器低16位
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FilterFIFO0; //过滤器被关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
//过滤器组1,只接受全局地址(CAN_GLOBAL_ADDRESS)
CAN_FilterInitStructure.CAN_FilterNumber = 1;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //工作在标识符屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //过滤器位宽为单个32位
CAN_FilterInitStructure.CAN_FilterIdHigh = (((u32)CAN_Address<<11)&0xffff0000)>>16; //要过滤的ID高位
CAN_FilterInitStructure.CAN_FilterIdLow = ((u32)CAN_Address<<11)&0xffff; //要过滤的ID低位 CAN_Address
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0007; //过滤器高16位
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x1f00; //过滤器低16位
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FilterFIFO0; //过滤器被关联到FIFO0,即通过过滤器的消息存储到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
}
/**********************************************************************************
* 函数名:Port_SetAddressFilter
* 描述 :CAN 的 Filter 配置
* 输入 :Address 要过滤的地址
* 输出 :无
* 返回 :无
* 调用 :内部调用
* 说明 :随着程序的运行,将改变滤波器2,来适应程序逻辑。
**********************************************************************************/
void Port_SetAddressFilter(u8 Address)
{
CAN_FilterInitTypeDef CAN_FilterInitStructure;
//过滤器组2,只接受全局地址(CAN_GLOBAL_ADDRESS)
CAN_FilterInitStructure.CAN_FilterNumber = 2;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //工作在标识符屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //过滤器位宽为单个32位
CAN_FilterInitStructure.CAN_FilterIdHigh = (((u32)Address<<11)&0xffff0000)>>16; //要过滤的ID高位
CAN_FilterInitStructure.CAN_FilterIdLow = ((u32)Address<<11)&0xffff; //要过滤的ID低位
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0007; //过滤器高16位
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x1f00; //过滤器低16位
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FilterFIFO0; //过滤器被关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
}
SAE J1939 只用扩展帧格式全面定义了标准化通信。
SAE J1939 协议数据单元结构
PDU | 优先权 | 保留位 | 数据页 | PDU格式 | PDU特定域 | 源地址 | 数据域 |
---|---|---|---|---|---|---|---|
域 | P | R | DP | PF | PS | SA | DATA |
位 | 3 | 1 | 1 | 8 | 8 | 8 | 0-64 |
定义:P 是优先级,R 是保留位,DP 是数据页,PF 是 PDU 格式,PS 是特定 PDU,SA 是源地址
PS : 目标地址(即接收机地址)
SA : 源地址(即发送机地址)
STM32 CAN ID过滤器
根据 SAE J1939 协议数据单元结构采用位宽为32位的屏蔽模式。
CAN_FilterIdHigh 和 CAN_FilterIdLow 为要过滤的 ID 高位与低位。
CAN_FilterMaskIdHigh 和 CAN_FilterMaskIdLow 为屏蔽位的高位与低位。
比如,我们要配置为只接收与本机源地址一致的数据
发送机 SA = 0x01
接收机 SA = 0x02
则接收机的过滤器配置如下:
PDU | 优先权 | 保留位 | 数据页 | PDU格式 | PDU特定域 | 源地址 | 数据域 |
---|---|---|---|---|---|---|---|
FilterId | 000 | 0 | 0 | 00000000 | 00000010 | 00000000 | DATA |
MaskId | 000 | 0 | 0 | 00000000 | 11111111 | 00000000 | 0- 64 |
FilterId = (0x02 << 8) << 3;
FilterMaskId = (0xff << 8) << 3;
左移3位寄存器低3位为:保留位、RTR、IDE
接收到的帧 ID 与 MaskId 中为1的位所对应的 FilterId 必须完全相同才将接收到的数据存入 FIFO 中。在这里即接收到的 ID 帧的PDU特定域必须为00000010,即接收机的源地址 0x02。