STM32F407 CAN1初始化校验、波特率验证及代码修正求助
STM32F407 CAN1发送问题排查与修正
一、波特率验证(当前代码错误)
STM32 CAN波特率计算公式为:
波特率 = APB1时钟频率 / ((TS1 + TS2 + 1) × PRESCALER)
假设你的SysClockConfig()配置系统时钟为168MHz,APB1总线时钟为42MHz(默认预分频系数为4)。代入当前代码参数:
- PRESCALER=6,TS1=8,TS2=7
- 计算结果:
42000000 / ((8+7+1)×6) = 437500 b/s
显然与目标500000b/s不符,这是接收端无法接收数据的核心原因之一。
二、初始化代码核心问题
GPIO复用配置错误
CAN1_RX对应PD0、CAN1_TX对应PD1,这两个引脚属于引脚0-7范围,应使用GPIOD->AFR[0](AFR[0]对应引脚0-7,AFR[1]对应引脚8-15)。当前代码将两个引脚的复用配置都写到AFR[1],导致引脚复用映射失败,CAN信号无法正常输出。波特率参数不匹配
如上述计算,当前参数无法得到500000b/s的波特率,与接收端波特率不匹配导致通信失败。
三、寄存器分步配置说明
1. RCC时钟使能
- 使能CAN1时钟:
RCC->APB1ENR |= (1<<25)(CAN1挂载在APB1总线) - 使能GPIOD时钟:
RCC->AHB1ENR |= (1<<3)(PD0/PD1属于GPIOD)
2. GPIO引脚配置
PD0(CAN1_RX)和PD1(CAN1_TX)需配置为复用功能模式,并设置复用映射为AF9:
- PD1(TX):
- MODER设置为复用模式:
GPIOD->MODER |= (2<<(1×2))(引脚1的MODER位为2-3位) - AFR[0]设置AF9:
GPIOD->AFR[0] |= (9<<(1×4))(引脚1的AFR位为4-7位)
- MODER设置为复用模式:
- PD0(RX):
- MODER设置为复用模式:
GPIOD->MODER |= (2<<(0×2))(引脚0的MODER位为0-1位) - AFR[0]设置AF9:
GPIOD->AFR[0] |= (9<<(0×4))(引脚0的AFR位为0-3位)
- MODER设置为复用模式:
3. CAN控制器初始化
- 进入初始化模式:
CAN1->MCR |= CAN_MCR_INRQ,等待硬件响应:while((CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) - 配置发送FIFO优先级:
CAN1->MCR |= CAN_MCR_TXFP(按发送请求顺序排序优先级) - 退出初始化模式:
CAN1->MCR &= ~CAN_MCR_INRQ,等待进入正常模式:while((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)
4. 波特率配置(目标500000b/s)
以APB1时钟42MHz为例,选择参数:
- PRESCALER=6,TS1=11,TS2=2
- 验证计算:
42000000 / ((11+2+1)×6) = 500000 b/s
寄存器配置: - 重置BTR:
CAN1->BTR = 0 - 设置预分频器:
CAN1->BTR |= (PRESCALER - 1)(BRP为0基数值) - 设置TS1:
CAN1->BTR |= ((TS1 - 1) << 16)(TS1字段占16-19位,0基数值) - 设置TS2:
CAN1->BTR |= ((TS2 - 1) << 20)(TS2字段占20-22位,0基数值)
5. CAN发送流程
- 等待空闲发送邮箱:
while ((CAN1->TSR & CAN_TSR_TME0) == 0) - 配置标准ID:
CAN1->sTxMailBox[0].TIR = (CAN_ID << 3)(IDE位默认0,表示标准ID) - 配置数据长度:
CAN1->sTxMailBox[0].TDTR = CAN_DLC - 加载数据到邮箱:直接赋值TDLR寄存器
- 请求发送:
CAN1->sTxMailBox[0].TIR |= CAN_TI0R_TXRQ
四、修正后的完整代码
#include "stm32f407xx.h" #include "RCCconfig.h" // 波特率参数(APB1=42MHz时,对应500000b/s) #define PRESCALER 6 #define TIME_QUANTA_SEGMENT_1 11 #define TIME_QUANTA_SEGMENT_2 2 // CAN配置 #define CAN_ID 0x123 // 标准ID(11位) #define CAN_DLC 2 // 数据长度 static uint8_t data[CAN_DLC] = {0}; void CAN1_Init(void) { // 1. 使能时钟 RCC->APB1ENR |= (1 << 25); // CAN1时钟使能 RCC->AHB1ENR |= (1 << 3); // GPIOD时钟使能 // 2. 配置GPIO引脚复用 // CAN1_TX PD1 GPIOD->MODER |= (2 << (1 * 2)); // 设置为复用功能模式 GPIOD->AFR[0] |= (9 << (1 * 4)); // 复用为AF9 // CAN1_RX PD0 GPIOD->MODER |= (2 << (0 * 2)); // 设置为复用功能模式 GPIOD->AFR[0] |= (9 << (0 * 4)); // 复用为AF9 // 3. CAN控制器初始化 CAN1->MCR |= CAN_MCR_INRQ; // 请求进入初始化模式 while((CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK); // 等待进入初始化模式 CAN1->MCR |= CAN_MCR_TXFP; // 发送FIFO按请求顺序优先级 CAN1->MCR &= ~CAN_MCR_INRQ; // 请求退出初始化模式 while((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK); // 等待进入正常模式 // 4. 配置波特率为500000b/s CAN1->BTR = 0; // 重置BTR寄存器 CAN1->BTR |= (PRESCALER - 1); // 设置预分频器(BRP=5) CAN1->BTR |= ((TIME_QUANTA_SEGMENT_1 - 1) << 16); // TS1=10(对应实际11个时间量子) CAN1->BTR |= ((TIME_QUANTA_SEGMENT_2 - 1) << 20); // TS2=1(对应实际2个时间量子) } void CAN1_Transmit(uint8_t data[CAN_DLC]) { // 等待空闲发送邮箱0 while ((CAN1->TSR & CAN_TSR_TME0) == 0); // 配置ID和数据长度 CAN1->sTxMailBox[0].TIR = (CAN_ID << 3); // 标准ID,IDE位默认0 CAN1->sTxMailBox[0].TDTR = CAN_DLC; // 直接加载数据到邮箱 CAN1->sTxMailBox[0].TDLR = (data[1] << 8) | data[0]; // 请求发送 CAN1->sTxMailBox[0].TIR |= CAN_TI0R_TXRQ; } int main(void) { SysClockConfig(); CAN1_Init(); // 初始化发送数据 data[0] = 0x65; data[1] = 0xAA; while (1) { CAN1_Transmit(data); // 可选:添加延时避免频繁发送 // for(uint32_t i=0; i<1000000; i++); } }
内容的提问来源于stack exchange,提问作者Shubham Samal
相关产品推荐
相关产品推荐

