You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 15:58:09