赞
踩
最近用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分析仪的软件界面以及通讯结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。