STM32F756VGH6 CAN发送邮箱寄存器写入无响应问题求助
STM32 CAN发送邮箱无法写入问题排查
问题背景
基于寄存器编程实现STM32与ESP32的CAN通信,配置为1Mbps波特率、11位标识符、1字节数据长度,双方使用TJA1050收发器。CAN_Setup寄存器配置符合预期,但执行CAN_transmit时,确认邮箱0"空闲"后无法写入TIR、TDTR、TDLR寄存器,TX线输出错误数据。
核心排查点与修复方案
1. 发送邮箱空闲判断条件完全错误
代码中等待邮箱空闲的逻辑错误:
while (!(CAN1->TSR & (1 << 26))); // 错误:等待发送成功标志,而非邮箱空闲
TSR的bit26是TXOK0,仅表示邮箱0上一次发送成功,并非邮箱空闲可写入。正确判断位是TME0(TSR的bit0),该位为1时表示邮箱0空,可写入数据。
修复代码:
while (!(CAN1->TSR & (1 << 0))); // 等待邮箱0空闲(TME0置位)
2. 发送寄存器赋值未清零,残留旧数据干扰
当前代码对TDTR和TDLR使用|=赋值,若邮箱存在残留数据,会导致新旧数据叠加(比如DLC位被旧值覆盖)。需先清零对应寄存器再赋值:
修复CAN_transmit函数:
void CAN_transmit (uint8_t data){ while (!(CAN1->TSR & (1 << 0))); // 等待邮箱0空闲 // 清零寄存器,避免残留数据干扰 CAN1->sTxMailBox[0].TIR = 0; CAN1->sTxMailBox[0].TDTR = 0; CAN1->sTxMailBox[0].TDLR = 0; CAN1->sTxMailBox[0].TIR &= ~ (1 << 2); // 选择标准标识符 CAN1->sTxMailBox[0].TIR &= ~ (1 << 1); // 设置为数据帧 CAN1->sTxMailBox[0].TIR |= (0b10101010101 << 21); // 写入11位标识符 CAN1->sTxMailBox[0].TDTR = 0x01; // 设置1字节数据长度 CAN1->sTxMailBox[0].TDLR = data; // 写入数据字节 CAN1->sTxMailBox[0].TIR |= (1 << 0); // 启动发送 }
3. CAN波特率配置错误,导致通信时序异常
当前配置的波特率并非目标1Mbps:
- APB1时钟为50MHz,BRP预分频值为
0b0000000100(对应预分频系数5),CAN内核时钟=50MHz/5=10MHz - TS1=0b0110(7个时间量子)、TS2=0b011(4个时间量子),总时间量子数=12,波特率≈833kbps,与目标不符
调整为1Mbps配置(50MHz APB1时钟):
CAN1->BTR &= ~((0x3FF << 0) | (0xF << 16) | (0x7 << 20) | (0x3 <<24)); CAN1->BTR |= (4 << 0); // BRP=4 → 预分频系数5,CAN时钟=10MHz CAN1->BTR |= (8 << 16); // TS1=8 → 9个时间量子 CAN1->BTR |= (0 << 20); // TS2=0 →1个时间量子 CAN1->BTR &= ~(0x3 <<24); // SJW=1个时间量子
4. 接收函数错误操作只读寄存器
CAN接收邮箱的sFIFOMailBox[0].RIR是只读寄存器,直接修改会导致硬件异常。接收标识符过滤需通过CAN过滤器配置实现,而非修改接收邮箱寄存器。
在CAN_Setup中添加过滤器配置:
// 过滤器配置:接收指定11位ID的帧 CAN1->FMR |= (1 << 0); // 进入过滤器初始化模式 CAN1->FA1R &= ~(1 << 0); // 禁用过滤器0 CAN1->FS1R |= (1 << 0); // 过滤器0为32位宽模式 CAN1->FM1R &= ~(1 << 0); // 过滤器0为掩码模式 CAN1->sFilterRegister[0].FR1 = 0b11001100110 << 21; // 目标接收ID CAN1->sFilterRegister[0].FR2 = 0x1FFFFFFF; // 全匹配掩码 CAN1->FFA1R &= ~(1 << 0); // 过滤器关联到FIFO0 CAN1->FA1R |= (1 << 0); // 启用过滤器0 CAN1->FMR &= ~(1 << 0); // 退出过滤器初始化模式
修改CAN_receive函数:
uint8_t CAN_receive (void){ uint8_t data; while ((CAN1->RF0R & 0x03) == 0); // 等待FIFO0有有效数据 data = CAN1->sFIFOMailBox[0].RDLR & 0xFF; // 提取数据字节 CAN1->RF0R |= (1 << 5); // 释放FIFO0邮箱 return data; }
5. 语法错误:main函数缺少分号
main函数中CAN_transmit(0x55)后缺少分号,修复为:
while(1){ delay_ms(500); CAN_transmit(0x55); }
完整修复后代码
#include "main.h" void Core_Clock_Setup (void){ RCC->CR |= RCC_CR_HSEON; //Set the clock source to external crystal/resonator (HSE) while (!(RCC->CR & RCC_CR_HSEON)); //Wait until clock gets stable RCC->APB1ENR |= RCC_APB1ENR_PWREN; //Enable power interface clock PWR->CR1 &= ~(1U << 14); PWR->CR1 &= ~(1U << 15); //Set internal voltage regulator to is reset value (scale 1) FLASH->ACR &= ~FLASH_ACR_ARTEN; //Disable ART accelerator FLASH->ACR &= ~FLASH_ACR_ARTRST; //Reset ART accelerator FLASH->ACR |= FLASH_ACR_PRFTEN; //Enable prefetch FLASH->ACR |= FLASH_ACR_LATENCY_6WS; //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock) //@ 25 MHz crystal, 200 MHz core clock configuration down below RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4); RCC->CFGR |= (0 << 4); //Core clock division by 1 (core clock is not devided) RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz) RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz) RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0; //PLLP Setting corresponding PLL prescalers (division by 2) RCC->PLLCFGR &= ~((1 << 6) - 1); RCC->PLLCFGR |= (16 & ((1 << 6) - 1)); //PLLM Setting corresponding PLL prescalers (division by 16) RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6); RCC->PLLCFGR |= (256 << 6); //PLLN Setting corresponding PLL prescalers ( multiplication by 256) RCC->CR |= RCC_CR_PLLON; //Enable PLL while (!(RCC->CR & RCC_CR_PLLRDY)); //Wait until PLL gets stable RCC->CFGR |= RCC_CFGR_SW_PLL; //PLL is set to be core clock while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source } void GPIO_Setup(void){ RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E GPIOE->MODER |= (1 << 8); //PE4 configured as digital output GPIOE->OTYPER &= ~(1 << 4); //PE4 configured as push-pull GPIOE->OSPEEDR |= (0b11 << 8); //PE4 very high speed } void Timer_Setup(void){ RCC->DCKCFGR1 |= (1 << 24); //TIMxCLK = 4xPCLKx RCC->APB1ENR |= (1 << 4); //Enable Timer 6 clock TIM6->PSC = 99; //100MHz timer clock → 1MHz tick TIM6->ARR = 0xFFFF; //Auto reload max value TIM6->CR1 |= (1 << 0); //Enable Timer 6 counter while(!(TIM6->SR & (1<<0))); //Wait until timer update bit is set } void delay_us (uint16_t us){ TIM6->CNT = 0; //Reset counter while (TIM6->CNT < us); //Wait until counter reaches desired value } void delay_ms (uint16_t ms){ for(uint16_t i = 0; i<ms; i++) { TIM6->CNT = 0; //Reset counter while (TIM6->CNT < 1000); //Wait until counter reaches 1ms } } void CAN_Setup (void){ //PD0 CAN_RX_1, PD1 CAN_TX_1 RCC->APB1ENR |= (1 << 25); //CAN 1 clock enabled RCC->AHB1ENR |= (1 << 3); //Enable clock for GPIO bank D delay_ms(1); GPIOD->AFR[0] |= (0b1001 << 0); //PD0 set as alternate function AF9 (CAN_RX_1) GPIOD->AFR[0] |= (0b1001 << 4); //PD1 set as alternate function AF9 (CAN_TX_1) GPIOD->MODER |= (0b10 << 0); //PD0 set as alternate function GPIOD->MODER |= (0b10 << 2); //PD1 set as alternate function GPIOD->OSPEEDR |= (0b11 << 0); //PD0 very high GPIO speed GPIOD->OSPEEDR |= (0b11 << 2); //PD1 very high GPIO speed //CAN core initialization CAN1->MCR &= ~ (1 << 1); //Exit sleep mode CAN1->MCR |= (1 << 0); //Enter initialization mode while (!(CAN1->MSR & (1 << 0))); //Wait until CAN enters initialization mode CAN1->MCR |= (1 << 2); //Priority driven by request order CAN1->MCR |= (1 << 4); //Non-automatic retransmission CAN1->MCR &= ~ (1 << 16); //Disable debug freeze //1Mbps波特率配置(50MHz APB1时钟) CAN1->BTR &= ~((0x3FF << 0) | (0xF << 16) | (0x7 << 20) | (0x3 <<24)); CAN1->BTR |= (4 << 0); //BRP=4 → 预分频系数5,CAN时钟=50MHz/5=10MHz CAN1->BTR |= (8 << 16); //TS1=8 → 9个时间量子 CAN1->BTR |= (0 << 20); //TS2=0 →1个时间量子 CAN1->BTR &= ~(0x3 <<24); //SJW=1个时间量子 //过滤器配置:接收指定11位ID的帧 CAN1->FMR |= (1 << 0); //进入过滤器初始化模式 CAN1->FA1R &= ~(1 << 0); //禁用过滤器0 CAN1->FS1R |= (1 << 0); //过滤器0为32位宽模式 CAN1->FM1R &= ~(1 << 0); //过滤器0为掩码模式 CAN1->sFilterRegister[0].FR1 = 0b11001100110 << 21; //目标接收ID CAN1->sFilterRegister[0].FR2 = 0x1FFFFFFF; //全匹配掩码 CAN1->FFA1R &= ~(1 << 0); //过滤器关联到FIFO0 CAN1->FA1R |= (1 << 0); //启用过滤器0 CAN1->FMR &= ~(1 << 0); //退出过滤器初始化模式 CAN1->MCR &= ~ (1 << 0); //Exit initialization mode while (CAN1->MSR & (1 << 0)); //Wait until CAN exits initialization mode } void CAN_transmit (uint8_t data){ while (!(CAN1->TSR & (1 << 0))); //等待邮箱0空闲(TME0置位) //清零寄存器,避免残留数据干扰 CAN1->sTxMailBox[0].TIR = 0; CAN1->sTxMailBox[0].TDTR = 0; CAN1->sTxMailBox[0].TDLR = 0; CAN1->sTxMailBox[0].TIR &= ~ (1 << 2); //选择标准标识符 CAN1->sTxMailBox[0].TIR &= ~ (1 << 1); //设置为数据帧 CAN1->sTxMailBox[0].TIR |= (0b10101010101 << 21); //写入11位发送ID CAN1->sTxMailBox[0].TDTR = 0x01; //设置1字节数据长度 CAN1->sTxMailBox[0].TDLR = data; //写入数据字节 CAN1->sTxMailBox[0].TIR |= (1 << 0); //启动发送 } uint8_t CAN_receive (void){ uint8_t data; while ((CAN1->RF0R & 0x03) == 0); //等待FIFO0有有效数据 data = CAN1->sFIFOMailBox[0].RDLR & 0xFF; //提取数据字节 CAN1->RF0R |= (1 << 5); //释放FIFO0邮箱 return data; } int main (void){ Core_Clock_Setup(); Timer_Setup(); GPIO_Setup(); CAN_Setup(); while(1){ delay_ms(500); CAN_transmit(0x55); } }
内容的提问来源于stack exchange,提问作者Vilius Žalėnas
相关产品推荐
相关产品推荐

