STM32-HAL库09-CAN通讯(loopback模式)

一、所用材料:

STM32F103C6T6最小系统板
STM32CUBEMX(HAL库软件)
MDK5
串口调试助手

二、所学内容:

初步学习如何使用STM32的CAN通讯功能,在本章节主要达到板内CAN通讯的效果,即32发送CAN信息再在CAN接收中断中用串口打印出来。

三、CUBEMX配置:

第一步:老三件套-RCC,SYS,时钟树配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第二步:串口配置

在这里插入图片描述

第三步:CAN配置

关键步骤:

  • 波特率设置:1M bit/s,具体配置如下图
  • TEST MODE - Loopback
  • 打开接收中断RX0
    在这里插入图片描述
    在这里插入图片描述

四、MDK5配置:

一、can.h头文件配置

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __CAN_H__
#define __CAN_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern CAN_HandleTypeDef hcan;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_CAN_Init(void);

/* USER CODE BEGIN Prototypes */
void CANFilter_Config(void);
void CAN1_Send_Test(void);
void CAN_Start_Init(void);
void CAN1_Send(uint8_t* cdata);
/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __CAN_H__ */


二、can.c源文件配置

/* Includes ------------------------------------------------------------------*/
#include "can.h"

/* USER CODE BEGIN 0 */
#include "stdio.h"
#include "usart.h"
#include "main.h"
static CAN_TxHeaderTypeDef TxMessage; //CAN发送的消息的消息头
static CAN_RxHeaderTypeDef RxMessage; //CAN接收的消息的消息头
/* USER CODE END 0 */

CAN_HandleTypeDef hcan;

/* CAN init function */
void MX_CAN_Init(void)
{
  hcan.Instance = CAN1;
  hcan.Init.Prescaler = 4;
  hcan.Init.Mode = CAN_MODE_LOOPBACK;
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan.Init.TimeSeg1 = CAN_BS1_5TQ;
  hcan.Init.TimeSeg2 = CAN_BS2_3TQ;
  hcan.Init.TimeTriggeredMode = DISABLE;
  hcan.Init.AutoBusOff = DISABLE;
  hcan.Init.AutoWakeUp = DISABLE;
  hcan.Init.AutoRetransmission = DISABLE;
  hcan.Init.ReceiveFifoLocked = DISABLE;
  hcan.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CAN_Init 2 */

  /* USER CODE END CAN_Init 2 */

}

void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(canHandle->Instance==CAN1)
  {
  /* USER CODE BEGIN CAN1_MspInit 0 */

  /* USER CODE END CAN1_MspInit 0 */
    /* CAN1 clock enable */
    __HAL_RCC_CAN1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**CAN GPIO Configuration
    PA11     ------> CAN_RX
    PA12     ------> CAN_TX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* CAN1 interrupt Init */
    HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
  /* USER CODE BEGIN CAN1_MspInit 1 */

  /* USER CODE END CAN1_MspInit 1 */
  }
}

void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{

  if(canHandle->Instance==CAN1)
  {
  /* USER CODE BEGIN CAN1_MspDeInit 0 */

  /* USER CODE END CAN1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_CAN1_CLK_DISABLE();

    /**CAN GPIO Configuration
    PA11     ------> CAN_RX
    PA12     ------> CAN_TX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);

    /* CAN1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
  /* USER CODE BEGIN CAN1_MspDeInit 1 */

  /* USER CODE END CAN1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
/*******************
接受信息过滤器
*******************/
 void CANFilter_Config(void)
{
    CAN_FilterTypeDef  sFilterConfig;
    
    sFilterConfig.FilterBank = 0;                       //CAN过滤器编号,范围0-27
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;   //CAN过滤器模式,掩码模式或列表模式
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;  //CAN过滤器尺度,16位或32位
    sFilterConfig.FilterIdHigh = 0x000 << 5;			//32位下,存储要过滤ID的高16位
    sFilterConfig.FilterIdLow = 0x0000;					//32位下,存储要过滤ID的低16位
    sFilterConfig.FilterMaskIdHigh = 0x0000;			//掩码模式下,存储的是掩码
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = 0;				//报文通过过滤器的匹配后,存储到哪个FIFO
    sFilterConfig.FilterActivation = ENABLE;    		//激活过滤器
    sFilterConfig.SlaveStartFilterBank = 0;
    
    if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK) 
		 {
        Error_Handler();
       }
	else{ printf("HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) is HAL_OK\r\n"); }
}
/*******************
中断接受
*******************/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{

    uint8_t  data[8];
    HAL_StatusTypeDef	status;
    status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
    if (HAL_OK == status)
	{    
		printf("--->Data Receieve!\r\n");
		printf("RxMessage.StdId is %#x\r\n",  RxMessage.StdId);
		printf("data[0] is 0x%02x\r\n", data[0]);
		printf("data[1] is 0x%02x\r\n", data[1]);
		printf("data[2] is 0x%02x\r\n", data[2]);
		printf("data[3] is 0x%02x\r\n", data[3]);
		printf("data[4] is 0x%02x\r\n", data[4]);
		printf("data[5] is 0x%02x\r\n", data[5]);
		printf("data[6] is 0x%02x\r\n", data[6]);
		printf("data[7] is 0x%02x\r\n", data[7]);		
		printf("<---\r\n");   
	    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);		
    }
}

/*******************
发送函数
*******************/
void CAN1_Send_Test(void)
{
    uint32_t TxMailbox;
    uint8_t data[4] = {0x01, 0x02, 0x03, 0x04};
    TxMessage.IDE = CAN_ID_STD;     //设置ID类型
	 TxMessage.StdId = 0x111;        //设置ID号
    TxMessage.RTR = CAN_RTR_DATA;   //设置传送数据帧
	 TxMessage.DLC = 4;              //设置数据长度
	if (HAL_CAN_AddTxMessage(&hcan, &TxMessage, data, &TxMailbox) != HAL_OK)
		{
        Error_Handler();
     }	
	// else{ printf("HAL_CAN_AddTxMessage(&hcan, &TxMessage, data, &TxMailbox) is HAL_OK\r\n"); }
}

void CAN1_Send(uint8_t* cdata)
{
    uint32_t TxMailbox;
    TxMessage.IDE = CAN_ID_STD;     //设置ID类型
	TxMessage.StdId = 0x111;        //设置ID号
    TxMessage.RTR = CAN_RTR_DATA;   //设置传送数据帧
	TxMessage.DLC = 8;              //设置数据长度	
	if (HAL_CAN_AddTxMessage(&hcan, &TxMessage, cdata, &TxMailbox) != HAL_OK)
	{
        Error_Handler();
    }		
}
/*******************
CAN启动函数
*******************/
void CAN_Start_Init(void)
{
  if (HAL_CAN_Start(&hcan) != HAL_OK) 
    {
        Error_Handler();
     }
  else{ printf("HAL_CAN_Start(&hcan) is HAL_OK\r\n"); }
    
    /* 3. Enable CAN RX Interrupt */
    if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) !=  HAL_OK) {
        Error_Handler();
    }
  else{ printf("HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) is HAL_OK\r\n"); }	
}
/* USER CODE END 1 */

三、main.c文件配置

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  CANFilter_Config();
  CAN_Start_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  CAN1_Send_Test();
	  HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、串口打印结果:

在这里插入图片描述

五、本文对应代码分享:

https://download.csdn.net/download/qq_45854134/88522530

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用 STM32-HAL 库读取编码器时,如果出现读取到负数的情况,可能会导致程序卡死的问题。这是因为 HAL 库默认使用的是 32 位定时器计数器,而编码器的计数器是一个 16 位的有符号整型,当计数器值达到 32767(0x7FFF)时,会变为负数,这会导致 HAL 库出现异常。 为了解决这个问题,可以使用以下两种方法之一: 1. 使用 16 位定时器计数器 可以通过修改定时器的初始化配置,将其设置为 16 位计数器模式。这样可以保证计数器值始终为 16 位有符号整型,不会出现负数的情况。例如,对于 TIM2 定时器,可以使用以下代码进行初始化: ```c TIM_HandleTypeDef htim2; // ... htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 65535; // 设置为 16 位计数器模式 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { // 初始化失败 } if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) { // 启动失败 } ``` 2. 处理负数值 如果无法使用 16 位定时器计数器,可以在程序中对读取到的负数值进行处理。例如,可以将计数器值加上一个足够大的值,使其变为正数。具体的处理方法可以根据实际情况进行调整。以下是一个示例代码: ```c int16_t count = 0; uint32_t offset = 0x10000; // 偏移量为 65536 while (1) { count = __HAL_TIM_GET_COUNTER(&htim2); if (count < 0) { count += offset; } // ... } ``` 以上两种方法都可以解决读取编码器时出现负数导致程序卡死的问题,具体选择哪种方法应根据实际情况进行判断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tony0925

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值