最近在做的一个项目需要多个电机相互通讯工作,但是电调ID有限,所以不得不再用CAN2,但是配置好以后,居然连数据都发不出去,终于在今天解决了这个问题,但是debug的过程是如此心酸。
目录
前言
双CAN配置问题
1.can1正常工作,can2不能正常工作
硬件
测试
2.硬件没问题,配置也没问题,can2还是不工作
配置(F4标准库)
can.c
main.c
can_receive.c
如果是单独用can2,那么开启时钟的时候,也要把can1开启。
“在can的控制器中,存储访问控制器是由can1控制的,当使用can2的时候,can2要访问存储访问控制器时,必须通过can1才能访问,所以使用can2的时候,can1为主机,can2为从机。所以使用can2时,时钟使能的宏定义是将can1和can2的的时钟都开启的。”
先使能CAN1的时钟,再使能CAN2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);//使能CAN2时钟
如果是分别单独配置的,或者同时配置的,查找其他原因。
一般来说,代码配置好了,发现不工作,70%问题就是硬件哪里有问题。
先用万用表蜂鸣器看一下线的通讯情况;
没问题就看一下CAN_L与CAN_H是不是对的;
配置排查可以通过CAN的模式 CAN_Mode_LoopBack 来测试一下收发,如何可以收到数据说明配置没问题(波特率除外)
这个时候看一下通讯频率对不对,大疆M3508是1M的,那就得按照1M配好,不然回环有收发正常模式也不通讯。
如果频率配对没问题,回环也正常收发,那么就可以重点去检测硬件了。
这个时候也不要头疼,按照我的经验步骤来(血泪教训)
1.使用调试工具检查CAN总线上的数据是否正确发送
如果连收发都没有,你的配置有检查了没有问题,那么极有可能是引脚复用了!!!!
比如说我们配置的是PB5 | PB6,工程里还有其他文件,可以按住Ctrl+F,搜一下RCC_AHB1Periph_GPIOB,看看哪些外设用了PB5或者PB6
如果没有调试工具检查CAN总线,就单独写一个只用CAN通讯的工程,下到单片机里面,看看是否正常通讯,如果单独写的可以工作,你现在的工作工作不了,那极大概率是引脚复用了。
下面附上我的双can配置
#include "can.h"
#include "stm32f4xx.h"
//PD0 CAN1_Rx、PD1 CAN1_Tx
uint8_t CAN1_mode_init(uint8_t tsjw, uint8_t tbs2, uint8_t tbs1, uint16_t brp, uint8_t mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_CAN1);
//查询中文手册中 CAN主控制寄存器 (CAN_MCR) 有详细解释
CAN_InitStructure.CAN_TTCM = DISABLE; //非时间触发模式
CAN_InitStructure.CAN_ABOM = ENABLE; //硬件自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //睡眠模式通过软件唤醒
CAN_InitStructure.CAN_NART = DISABLE; //报文自动重发
CAN_InitStructure.CAN_RFLM = DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP = DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode = mode; //配置can的工作模式
CAN_InitStructure.CAN_SJW = tsjw; //重新同步跳跃宽度
CAN_InitStructure.CAN_BS1 = tbs1; //时间段1占用单元数
CAN_InitStructure.CAN_BS2 = tbs2; //时间段2占用单元数
CAN_InitStructure.CAN_Prescaler = brp; //分频系数
CAN_Init(CAN1, &CAN_InitStructure);
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关联到fifo
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = CAN1_NVIC;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
//PB5 CAN2_Rx、 PB6 CAN2_Tx
uint8_t CAN2_mode_init(uint8_t tsjw, uint8_t tbs2, uint8_t tbs1, uint16_t brp, uint8_t mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_CAN2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_CAN2);
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = ENABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = mode;
CAN_InitStructure.CAN_SJW = tsjw;
CAN_InitStructure.CAN_BS1 = tbs1;
CAN_InitStructure.CAN_BS2 = tbs2;
CAN_InitStructure.CAN_Prescaler = brp;
CAN_Init(CAN2, &CAN_InitStructure);
CAN_FilterInitStructure.CAN_FilterNumber = 14;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
CAN_ITConfig(CAN2, CAN_IT_FMP0, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = CAN2_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = CAN2_NVIC;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
#include "stm32f4xx.h"
#include "main.h"
#include "delay.h"
#include "can.h"
#include "CAN_receive.h"
extern CanRxMsg rx1_message,rx2_message;
int main(void)
{
BSP_Init();
while(1)
{
CAN_CMD_Fric(0,100,0,100);
if(can2_ok){
printf("%d,%d,%d,%d,%d\n",rx2_message.StdId,rx2_message.Data[0],rx2_message.Data[1]);
can2_ok = 0;
}
if(can1_ok){
printf("%d,%d,%d,%d,%d\n",rx1_message.StdId,rx1_message.Data[0],rx1_message.Data[1]);
can1_ok = 0;
}
}
}
void BSP_Init(void)
{
//中断组4
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//CAN通信初始化
CAN1_mode_init(CAN_SJW_1tq, CAN_BS2_2tq, CAN_BS1_6tq, 5, CAN_Mode_Normal);
CAN2_mode_init(CAN_SJW_1tq, CAN_BS2_2tq, CAN_BS1_6tq, 5, CAN_Mode_Normal);//CAN_Mode_LoopBack回环模式
}
#include "CAN_Receive.h"
#include "stm32f4xx.h"
//全局变量
int8_t can1_ok,can2_ok;
CanRxMsg rx1_message,rx2_message;
//can1中断
void CAN1_RX0_IRQHandler(void)
{
if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)
{
CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
//接收到的数据存在rx1_message中,转移即可
CAN_Receive(CAN1, CAN_FIFO0, &rx1_message);
//添加其他处理
can1_ok=1;
}
}
//can2中断
void CAN2_RX0_IRQHandler(void)
{
if (CAN_GetITStatus(CAN2, CAN_IT_FMP0) != RESET)
{
CAN_ClearITPendingBit(CAN2, CAN_IT_FMP0);
//接收到的数据存在rx1_message中,转移即可
CAN_Receive(CAN2, CAN_FIFO0, &rx2_message);
//添加其他处理
can2_ok=1;
}
}
//can2发送控制命令
void CAN_CMD_Fric(int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4)
{
CanTxMsg TxMessage;
TxMessage.StdId = 0x200; //根据设备ID自己更改
TxMessage.IDE = CAN_ID_STD;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.DLC = 0x08; //数据长度8位
TxMessage.Data[0] = motor1 >> 8;
TxMessage.Data[1] = motor1;
TxMessage.Data[2] = motor2 >> 8;
TxMessage.Data[3] = motor2;
TxMessage.Data[4] = motor3 >> 8;
TxMessage.Data[5] = motor3;
TxMessage.Data[6] = motor4 >> 8;
TxMessage.Data[7] = motor4;
CAN_Transmit( CAN2, &TxMessage );
// 发送失败检查
uint8_t transmit_status = CAN_Transmit(CAN2, &TxMessage);
if (transmit_status != CAN_TxStatus_Ok) {
// 发送失败,处理错误
// 可以在此处添加错误处理逻辑
printf("cuowucuowu\n");
}
}
//can1发送电机控制命令
void CAN_CMD_CHASSIS(int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4)
{
CanTxMsg TxMessage;
TxMessage.StdId = 0x200;
TxMessage.IDE = CAN_ID_STD;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.DLC = 0x08;
TxMessage.Data[0] = motor1 >> 8;
TxMessage.Data[1] = motor1;
TxMessage.Data[2] = motor2 >> 8;
TxMessage.Data[3] = motor2;
TxMessage.Data[4] = motor3 >> 8;
TxMessage.Data[5] = motor3;
TxMessage.Data[6] = motor4 >> 8;
TxMessage.Data[7] = motor4;
CAN_Transmit( CAN1, &TxMessage);
}
这套代码采用中断接收,可以用来移植双can,将配置和接受发送分开了,主函数演示了如何发送接收。
有用就点个赞吧~
如果还有其他问题欢迎留言讨论。