最近用STM32F407最小电路板测试CAN通讯遇到点小问题:回环模式测试没有问题的基础上,两块相同的最小系统板之间也可以通讯。但把其中一块板子换成USB转CAN分析仪时(或者其他板子),怎么也调不通。
思考良久,硬件测试正常,没找到问题。逼得没办法,将CANH和CANL接到滤波器(此时在两块STM32F407最小系统板之间正常收发信息),测试波特率,才找到根源:不知什么时候,将外部时钟的系统频率由336MHz调成了360MHz,结果就导致CAN一直发送失败...
详情如下:
CAN.h代码:
#ifndef __CAN1_H
#define __CAN1_H
#include "stm32f4xx.h" // Device header
void CAN1_Init(void);
void CAN1_SetMsg(CanTxMsg* Send_CAN1_Msg);
void CAN1_Send(CanTxMsg* TxMessage);
void CAN1_RX0_IRQHandler(void);
#endif
CAN.c代码
#include "CAN1.h"
// CAN1_RX PA11
// CAN1_TX PA12 APB1总线 42MHz
extern CanRxMsg Recv_CAN1_Msg;
uint8_t t = 6;
void CAN1_Init(void)
{
//开启CAN1和GPIO时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
//引脚复用为CAN1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1);
//配置GPIO引脚
GPIO_InitTypeDef GPIO_InitStructure;
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_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//CAN1初始化配置
CAN_InitTypeDef CAN_InitStructure;
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
//CAN最大频率为1MHz。APB1频率为42MHz,波特率 = 42M/预分频/(1+BS1+BS2)
//在本案例中,波特率 = 42M/6/(1+4+2) = 1M
//如果想让波特率 = 500k,可以设置预分频为12,或者增加BS1和BS2的值
CAN_InitStructure.CAN_Prescaler = 12; //预分频,APB1总线42MHz
CAN_InitStructure.CAN_BS1 = CAN_BS1_4tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //CAN模式选择
// CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;
CAN_InitStructure.CAN_ABOM = ENABLE; //自动离线
CAN_InitStructure.CAN_AWUM = ENABLE; //自动唤醒
CAN_InitStructure.CAN_NART = DISABLE; //禁止自动重发送,消息只发送一次
CAN_InitStructure.CAN_RFLM = DISABLE; //FIFO满了后,覆盖前一条
CAN_InitStructure.CAN_TTCM = DISABLE; //时间戳。不需要时间戳
CAN_InitStructure.CAN_TXFP = DISABLE; //发送FIFO优先级。按照请求的时间,不是标识符
CAN_Init(CAN1,&CAN_InitStructure);
//CAN过滤器配置
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //是否激活筛选器
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
//参考CAN 发送邮箱标识符寄存器 (CAN_TIxR),最低三位是功能码,所以需要左移3bit
CAN_FilterInitStructure.CAN_FilterIdHigh = ((((uint32_t)0x199 << 3 )| CAN_ID_STD | CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterIdLow = (((uint32_t)0x199 << 3 )| CAN_ID_STD | CAN_RTR_DATA)&0xFFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //工作在掩码模式
CAN_FilterInitStructure.CAN_FilterNumber = 8; //设置筛选器的编号,0~13
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //筛选器的尺寸
CAN_FilterInit(&CAN_FilterInitStructure);
//配置CAN中断
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);
//中断配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
//设置要发送的数据
void CAN1_SetMsg(CanTxMsg* Send_CAN1_Msg)
{
if(t>9)
t = 6;
Send_CAN1_Msg->ExtId = 0; //此处采用标准报文格式,因此不用给ExtId赋值
Send_CAN1_Msg->IDE = CAN_ID_STD;
Send_CAN1_Msg->RTR = CAN_RTR_DATA;
Send_CAN1_Msg->StdId = 0x199; //设置标准ID为0x1314
Send_CAN1_Msg->DLC = 8;
for(uint8_t i = 0;i<8;i++)
{
Send_CAN1_Msg->Data[i] = t+i;
}
t++;
}
void CAN1_RX0_IRQHandler(void)
{
if(CAN_GetITStatus(CAN1,CAN_IT_FMP0) == SET)
{
//接收数据后,系统会自动清除中断标志位。
//如果没有接收函数,中断标志位一直保持为1,导致程序卡死
CAN_Receive(CAN1,CAN_FIFO0,&Recv_CAN1_Msg);
}
//不用单独清除标志位。CAN_ClearITPendingBit函数没有CAN_IT_FMP0这个变量
// CAN_ClearITPendingBit(CAN1,CAN_IT_FMP0);
}
main.c代码
#include "stm32f4xx.h" // Device header
#include "Delay.h"
#include "CAN1.h"
CanTxMsg Send_CAN1_Msg;
CanRxMsg Recv_CAN1_Msg;
uint8_t Mail_Box = 0;
int main(void)
{
//NVIC为内核中断,函数在misc.c和misc.h文件中
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
CAN1_Init();
CAN1_RX0_IRQHandler();
while(1)
{
//设置要发送的数据
CAN1_SetMsg(&Send_CAN1_Msg);
//获取发送的邮箱号。如果没有空的邮箱,返回CAN_TxStatus_NoMailBox
Mail_Box = CAN_Transmit(CAN1,&Send_CAN1_Msg);
if(Mail_Box != CAN_TxStatus_NoMailBox)
{
//检测发送状态,一直等到发送成功
while(CAN_TransmitStatus(CAN1,Mail_Box) != CAN_TxStatus_Ok);
//此处延时是为了让CAN收发器将收到的数据转换为电平,传送到总线
delay_ms(1000);
}
else
return 0;
//此处延时只是单纯防止发送过快
delay_ms(1000);
}
}
起初,代码运行时出现的问题:每次运行,都会卡在
while(CAN_TransmitStatus(CAN1,Mail_Box) != CAN_TxStatus_Ok);这一行,经检测,CAN_TransmitStatus(CAN1,Mail_Box)函数的返回值是CAN_TxStatus_Pending,也就是发送不成功
于是怀疑是代码有问题。调成回环模式测试,正常,用两个相同的板子测试,也是正常的
但每次接到USB转CAN分析仪就卡住,差点以为是分析仪坏掉了。于是用STM32F107板子写了个代码测试,也会卡住。猜测是波特率对不上,导致发送错误,于是用示波器测试频率(方法:将CANH接到示波器探头,将CANL接到示波器地端。按自动检测按钮。注意需要把程序中的两个延时注释掉),果然和计算数值不对。按照上面的计算,波特率应该是500kHz,实测是541.6KHz。
查stm32f4xx.h文件,HSE_VALUE数值和实际晶振相同
于是逐个检查system_stm32f4xx.c文件中的各个参数,最终发现,PLL_N的数值不知什么时候被改成了360
(检查方法,双击选中,右键,点击Go To Definition Of "PLL_N",调到PLL_N定义处)
一时大意,搞得这么麻烦,吸取教训了...
(以上代码亲测有效,供大家参考。芯片是stm32f407zgt6,外部晶振8MHz)
整体接线
USB转CAN分析仪,通电后,红灯亮起,如果通讯成功,绿灯+红灯同时亮,如上图(红灯小,拍不出来)
USB转CAN分析仪的软件界面以及通讯结果: