STM32 CAN环回模式

Posted

技术标签:

【中文标题】STM32 CAN环回模式【英文标题】:STM32 CAN loop back mode 【发布时间】:2020-08-06 03:20:58 【问题描述】:

我开始在 STM32F103xx MCU 中基本使用 HAL 驱动程序来实现 CAN 外设的环回模式。根据单片机的用户手册,当传输完成且数据在CAN总线上可用时,硬件置位CAN_TSR寄存器的TME、RQCP和TXOK位,表示对应的邮箱已为空,传输成功。

当我调试以下程序时,CAN_TSR 寄存器中没有发生上述更改。我只能看到 TME 位被设置,这意味着提供了一个邮箱用于传输但从未传输。

我的这段代码导致程序中出现不定式循环:

while(HAL_CAN_IsTxMessagePending(&hcan, TxMailbox));

你能解释一下我的问题是什么,我应该怎么做。感谢您的想法。

这是完整的代码:

#include "main.h"
CAN_HandleTypeDef hcan;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_CAN_Init(void);

/* USER CODE BEGIN PFP */
void Can_TX(void);
/* USER CODE END PFP */

int main(void)

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN_Init();

  /* USER CODE BEGIN 2 */
  LCD1602_Begin4BIT(RS_GPIO_Port, RS_Pin, E_Pin, D4_GPIO_Port, D4_Pin, D5_Pin, D6_Pin, D7_Pin);
  LCD1602_print("Sending...");
  Can_TX();
  /* USER CODE END 2 */

  /* Infinite loop */
  while (1)
  
    for (int i = 0; i <= 1000; i++);
  


void SystemClock_Config(void)

  RCC_OscInitTypeDef RCC_OscInitStruct = 0;
  RCC_ClkInitTypeDef RCC_ClkInitStruct = 0;
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  
    Error_Handler();
  
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  
    Error_Handler();
  


static void MX_CAN_Init(void)

  hcan.Instance = CAN1;
  hcan.Init.Prescaler = 1;
  hcan.Init.Mode = CAN_MODE_LOOPBACK;
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan.Init.TimeSeg1 = CAN_BS1_13TQ;
  hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
  hcan.Init.TimeTriggeredMode = DISABLE;
  hcan.Init.AutoBusOff = DISABLE;
  hcan.Init.AutoWakeUp = DISABLE;
  hcan.Init.AutoRetransmission = ENABLE;
  hcan.Init.ReceiveFifoLocked = DISABLE;
  hcan.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  
    Error_Handler();
  


static void MX_GPIO_Init(void)

  GPIO_InitTypeDef GPIO_InitStruct = 0;
  __HAL_RCC_GPIOA_CLK_ENABLE();
  HAL_GPIO_WritePin(GPIOA, RS_Pin|E_Pin|D4_Pin|D5_Pin|D6_Pin|D7_Pin, GPIO_PIN_RESET);
  GPIO_InitStruct.Pin = RS_Pin|E_Pin|D4_Pin|D5_Pin|D6_Pin|D7_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);


void Can_TX(void)

  CAN_TxHeaderTypeDef TxHeader;
  uint8_t our_message[5] = 'H','E','L','L','O';
  uint32_t TxMailbox;
  TxHeader.DLC = 1;
  TxHeader.StdId = 0x65D;
  TxHeader.IDE = CAN_ID_STD;
  TxHeader.RTR = CAN_RTR_DATA;
  HAL_CAN_AddTxMessage(&hcan, &TxHeader, our_message, &TxMailbox);
  if(HAL_CAN_AddTxMessage(&hcan, &TxHeader, our_message, &TxMailbox) != HAL_OK)
  
    Error_Handler();
  
  while(HAL_CAN_IsTxMessagePending(&hcan, TxMailbox));
  LCD1602_print("Message is sent");


void Error_Handler(void)  

【问题讨论】:

联系您的供应商,希望他们不会向您收取 $$$ :) 【参考方案1】:

我可能有其他有用的信息,关于正确的位计时设置如何解决我的问题,并让我的 tx 消息正确地离开邮箱。

我想用环回功能测试我的 CAN 外设,但是消息卡在邮箱中。

仔细阅读文档后,我首先发现我将HAL_CAN_Start() 忘记为suggested 被HelpingHand 忘记了,而且确实,标志 INRQ 仍然打开。 (由HAL_CAN_Init设置。)

修改后,我在接收端没有看到任何消息,并且不确定它是否正确发送。查了一下原因,我注意到RQCPx设置为1,表示无论成功或错误,传输请求都已完成,TXOKx为0,TERRx为1,表示传输错误。 CAN_ESR(错误状态寄存器)中的 LEC(最后一个错误代码)位被设置为 101,或者根据文档,位显性错误。

我不是很明白这是什么意思,欢迎澄清,以便更好地了解如何使邮件正确离开邮箱,但偶然发现this thread from st community,我明白使用随机定时设置不会有很好的结果. (现在看来很明显了)。

所以使用this CAN Bit Time Calculation,我找到了正确的设置。为此,我需要我的APB1 peripheral clocks 频率和所需的波特率(对我来说是 24MHz 和 125kbps)。我使用了工具推荐的首选sample pointSJW 值,分别是87.5% 和1。然后生成下表: https://i.stack.imgur.com/ENshf.png

然后填充位时序参数的 STM32CubeIDE 字段非常简单: https://i.stack.imgur.com/V1q61.png

还有,TXOKx 是 1,TERRx 是 0。

作为奖励,这里有一小段代码可以检索我编写的LEC。它不打算用于功能齐全的应用程序,它只是用于测试。

//adds a message to transmit and obtains "allocated" TxMailbox
HAL_CAN_AddTxMessage(hcan, &TxHeader, TxData, &TxMailbox);

//waiting for message to leave
while(HAL_CAN_IsTxMessagePending(&hcan1, TxMailbox));

//waiting for transmission request to be completed by checking RQCPx
while( !(hcan->Instance->TSR & ( 0x1 << (7 * ( TxMailbox - 1 )))));

//checking if there is an error at TERRx, may be done with TXOKx as well (i think)
if ((hcan->Instance->TSR & ( 0x8 << (7 * ( TxMailbox - 1 )))))
    //error is described in ESR at LEC last error code
    uint8_t error_code = ( hcan->Instance->ESR & 0x70 ) >> 4;
    //000: No Error
    //001: Stuff Error
    //010: Form Error
    //011: Acknowledgment Error
    //100: Bit recessive Error
    //101: Bit dominant Error
    //110: CRC Error
    //111: Set by software
    while (1);

我希望我的回答不是主题过多,并且符合 Stack Overflow 政策的可接受的第一贡献。

【讨论】:

感谢您的详细分析,它为我节省了大量时间和痛苦。【参考方案2】:

[...] 当传输完全完成并且数据在 CAN 总线中可用时,CAN_TSR 寄存器的 TME、RQCP 和 TXOK 位由硬件设置,表明相应的邮箱已为空,并且传输成功。

当我调试以下程序时,CAN_TSR 寄存器中没有发生上述任何更改。

STM32F103xx 的 CAN 外设(“bxCAN”)包含三个 TX FIFO。 因此,需要检查三个 ( TMEx, RQCPx, TXOKx ) 实例 - 只是为了确保。

我只能看到 TME 位被设置,这意味着提供了一个邮箱用于传输但从未传输。

CAN_TSR中TME位的解释似乎有误: 正如您可以在 sec 中验证的那样。 24.9.2(第 677-8 页) STM32F103 reference manual, TME 位是“当邮箱没有等待传输请求时由硬件设置”。 因此,如果发现这些位 set,则传输似乎已经结束。

如果传输仍处于挂起状态,则可能是由于错误情况而卡住了。 寄存器CAN_ESR 中的错误计数器(REC/TEC)呢?

如果没有接收器在监听,CAN 发送器将进入错误模式,因为接收器的确认位没有到达发送器。 代码示例没有显示任何 CAN 引脚的启用 GPIO 指令。 环回模式应该没问题,但我自己从未尝试过。

传输可能处于挂起状态,因为 bxCAN 组件仍处于其初始化模式,请参见第 2 节。 24.4.1(第 657 页)在参考手册中。 然后,软件必须触发转换到正常模式。 我不确定HAL_CAN_Init() 是否包含此内容。

变量TxMailbox 从未在用户代码中初始化。 如果它是由函数HAL_CAN_AddTxMessage() 分配的,这很好。 如果没有,HAL_CAN_IsTxMessagePending() 将轮询错误的邮箱地址(可能永远)。

函数HAL_CAN_AddTxMessage()被调用了两次(一次丢弃它的返回值,第二次检查它。 这可能会导致其他问题(多 TX),但我看不出与当前的无 TX 问题有关。

DLC 设置为 1,但消息长度为 5 个字节。 当传输开始工作时,它只会传输一个'H',但'E','L','L','O'会丢失。


更新:我检查了 documentation of STM32CubeF1 HAL。 第 9.2 节(第 92 页)描述了如何通过 ST 的 HAL 驱动程序初始化 CAN:

    运行__HAL_RCC_CANx_CLK_ENABLE()

    我在代码示例中没有看到这一点。待查...

    使用 HAL_CAN_Init() 初始化 CAN 外设。

    这是在代码示例中完成的。好的。

    使用HAL_CAN_ConfigFilter() 配置接收过滤器。

    这是缺失的,但我认为这不是 TX 不起作用的原因。

    使用HAL_CAN_Start()启动CAN模块。

    我认为这是缺少的函数调用。我没有检查它的代码,但我猜这个函数也处理了INRQ/SLEEP 标志。 这是问题根源的最佳候选!

    影响HAL_CAN_AddTxMessage() 和其他人。

    但这符合代码示例。好的。

【讨论】:

【参考方案3】:

我遇到了同样的问题。您必须在CAN1_x 函数中添加HAL_CAN_Start(&amp;hcan);

【讨论】:

以上是关于STM32 CAN环回模式的主要内容,如果未能解决你的问题,请参考以下文章

CAN 1 RX 在环回模式下工作,但不在正常模式下

stm32 can可以接收数据,但却无法发送数据,怎么回事

STM32F405 bxCan不离开初始化模式

stm32f405和stm32f407的区别

stm32的can发送时序不准

STM32 HAL CAN TX 邮箱