基于STM32CUBEMX生成的CAN的收发——学习笔记

学习完就要做笔记要总结,趁这个没有忘记,加上这几天搞模型设计搞到头晕,写一写博客吧,各路大佬将就看一下哈

1.硬件

我用的是STM32103C8T6的板子,用的是最下系统板,然后外接了一个TJA1050,害,上图
基于STM32CUBEMX生成的CAN的收发——学习笔记_第1张图片
做实验的话是够用的了

2.生成工程

因为之前用原子的例程写了一个,但是对于我这种懒人来说,还是STM32CUBEMX比较方便,后期开发也容易一点,下面是生成工程的一些步骤,由于是笔记,所以写得比较详细,如果比较熟练的话就可以直接跳过生成工程这个部分。
首先是芯片的选型
基于STM32CUBEMX生成的CAN的收发——学习笔记_第2张图片
然后是相应的功能配置基于STM32CUBEMX生成的CAN的收发——学习笔记_第3张图片
在这里勾选发送和接收
基于STM32CUBEMX生成的CAN的收发——学习笔记_第4张图片
然后就是配置参数,
基于STM32CUBEMX生成的CAN的收发——学习笔记_第5张图片
这里要选哦,至于为啥,待会再讲
然后就是RCC
基于STM32CUBEMX生成的CAN的收发——学习笔记_第6张图片
以及SYS~
基于STM32CUBEMX生成的CAN的收发——学习笔记_第7张图片
因为这里想要把CAN收发的数据通过串口转发出去,所以就使能一下串口,选一下异步通信以及波特率115200bit/s
基于STM32CUBEMX生成的CAN的收发——学习笔记_第8张图片
然后就是配置时钟u,这里就是用外部时钟
基于STM32CUBEMX生成的CAN的收发——学习笔记_第9张图片
注意看红色箭头喔,这里的时钟频率是36M喔,再回想一下刚刚上面说的为什么CAN的参数要那么配置呢?下面是某一位关于CAN的波特率的设置,可以进去看,我就不献丑了

CSDN某大佬对CAN的波特率、采样的讲解

总结下来就是:波特率=peripheral clocks/prescaler/(TQ1+TQ2+SJW)
这里就是36M/4/(9+8+1)=500kps(这个速率也是一般汽车上一般电子产品的报文传输速率,一般对实时性要求不是很高,深入的我就没研究了)
基于STM32CUBEMX生成的CAN的收发——学习笔记_第10张图片
然后生成工程,刚生成的工程里面都是一些初始化,但是要实现CAN的收发功能还是需要用户自定义的,所以我们要在生成的工程里面添加收发的定义,当然,还有串口的定义

3.CAN的发送

首先我们先看stm32f1xx_hal_can.h
基于STM32CUBEMX生成的CAN的收发——学习笔记_第11张图片
这里面是一些实现CAN收发功能的一些声明,这个图可能有点小,下面是程序块


/** @addtogroup CAN_Exported_Functions_Group3 Control functions
 *  @brief    Control functions
 * @{
 */

/* Control functions **********************************************************/
HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan);
HAL_StatusTypeDef HAL_CAN_Stop(CAN_HandleTypeDef *hcan);
HAL_StatusTypeDef HAL_CAN_RequestSleep(CAN_HandleTypeDef *hcan);
HAL_StatusTypeDef HAL_CAN_WakeUp(CAN_HandleTypeDef *hcan);
uint32_t HAL_CAN_IsSleepActive(CAN_HandleTypeDef *hcan);
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox);
HAL_StatusTypeDef HAL_CAN_AbortTxRequest(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);
uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan);
uint32_t HAL_CAN_IsTxMessagePending(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);
uint32_t HAL_CAN_GetTxTimestamp(CAN_HandleTypeDef *hcan, uint32_t TxMailbox);
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan, uint32_t RxFifo);

这个是代码块,然后主要用到里面的收发函数
打开main.c文件,在man函数里面加上以下代码块,这个是定义一个输出的数组,以及CAN报文的一些帧结构

  /* USER CODE BEGIN 1 */
	CAN_TxHeaderTypeDef Can_Tx;
	uint8_t tdata[8] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
	uint32_t pTxMailbox = 0;
 	Can_Tx.StdId = 0x123;
	Can_Tx.ExtId = 0x123;
	Can_Tx.IDE = 0;
	Can_Tx.RTR = 0;
	Can_Tx.DLC = 8;

然后在while循环之前加入以下初始化

 /* USER CODE BEGIN 2 */
	HAL_UART_MspInit(&huart1);
	MX_CAN_Init();
	HAL_CAN_MspInit(&hcan);
	HAL_CAN_Start(&hcan);
  /* USER CODE END 2 */

这个是串口、CAN的初始化函数,第一个是串口的,第二个是CAN的,第三个是CAN引脚的,初始化之后就是启动CAN了。
然后在while循环里面加入以下代码

HAL_CAN_AddTxMessage(&hcan,&Can_Tx,tdata,&pTxMailbox);

这个是在上面提到过的,是CAN发送报文的函数,这里直接调用。然后接上CAN分析仪,有设备嘛,就不用发串口啦,都一样都一样
基于STM32CUBEMX生成的CAN的收发——学习笔记_第12张图片
这个就是收到的CAN报文,如果对CAN报文有兴趣的,也可以看下面大佬对CAN报文的讲解
某大佬的CAN报文讲解

CAN分析仪能正常收到板子发上来的报文,并且能分析其结构,说明发送成功!

4.CAN的接收

说完CAN的发送,接下来的就是CAN的接收,而CAN接收不像CAN的发送那样子直接一条指令就可能发了,敲重点敲重点,CAN总线协议是有邮箱机制的,就是收到的报文是需要放到邮箱里,但是要怎么用邮箱呢??**就需要配置过滤器!!配置过滤器!!配置过滤器!!!**一开始我没搞明白过滤器机制的时候是看得懵懵的,都不知道是在干嘛,后来研究了才理解是干啥的,不懂的请移步到大佬对CAN的讲解那里学习学习一下。所以在接收的时候一定要配置过滤器,看到很多博客都是有过滤器的,但是没有说过滤器的重要性,一开始还试了不用过滤器看能不能接收,结果板子是不能接收到任何数据的。好了,进入正题
重新打开stm32f1xx_hal_can.h文件,在刚刚的代码块上面
基于STM32CUBEMX生成的CAN的收发——学习笔记_第13张图片


/* Configuration functions ****************************************************/
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig);

这个是过滤器的声明函数,我们就需要用到这个来配置过滤器
在can.c中加入以下代码

/* USER CODE BEGIN 0 */

CAN_FilterTypeDef  CAN_FilterInitStructure;
CAN_HandleTypeDef hcan1;

/* USER CODE END 0 */

这个是句柄的定义。然后在MX_CAN_Init()里面加入过滤器的配置

void MX_CAN_Init(void)
{

  hcan.Instance = CAN1;
  hcan.Init.Prescaler = 4;
  hcan.Init.Mode = CAN_MODE_NORMAL;
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan.Init.TimeSeg1 = CAN_BS1_9TQ;
  hcan.Init.TimeSeg2 = CAN_BS2_8TQ;
  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();
  }
	CAN_FilterInitStructure.FilterActivation = ENABLE;//使能过滤器
	CAN_FilterInitStructure.FilterBank = 1;//指定过滤器为1
	CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK;//指定过滤器为标识符屏蔽位模式
	CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT;//过滤器位宽为32位
	CAN_FilterInitStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0;//设定了指向过滤器的FIFO
	
	CAN_FilterInitStructure.FilterIdHigh =0x0000 ;//要过滤的ID高位 
  	CAN_FilterInitStructure.FilterIdLow = 0x0000;//要过滤的ID低位 
	CAN_FilterInitStructure.FilterMaskIdHigh = 0x0000;//过滤器屏蔽标识符的高16位值
  	CAN_FilterInitStructure.FilterMaskIdLow = 0x0000; //过滤器屏蔽标识符的低16位值
	
	HAL_CAN_ConfigFilter(&hcan,&CAN_FilterInitStructure);
}

整个Init函数就在这里了,配置过滤器的话也是从stm32f1xx_hal_can.h里面找相关的定义

/**
  * @brief  CAN filter configuration structure definition
  */
typedef struct
{
  uint32_t FilterIdHigh;          /*!< Specifies the filter identification number (MSBs for a 32-bit
                                       configuration, first one for a 16-bit configuration).
                                       This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */

  uint32_t FilterIdLow;           /*!< Specifies the filter identification number (LSBs for a 32-bit
                                       configuration, second one for a 16-bit configuration).
                                       This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */

  uint32_t FilterMaskIdHigh;      /*!< Specifies the filter mask number or identification number,
                                       according to the mode (MSBs for a 32-bit configuration,
                                       first one for a 16-bit configuration).
                                       This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */

  uint32_t FilterMaskIdLow;       /*!< Specifies the filter mask number or identification number,
                                       according to the mode (LSBs for a 32-bit configuration,
                                       second one for a 16-bit configuration).
                                       This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */

  uint32_t FilterFIFOAssignment;  /*!< Specifies the FIFO (0 or 1U) which will be assigned to the filter.
                                       This parameter can be a value of @ref CAN_filter_FIFO */

  uint32_t FilterBank;            /*!< Specifies the filter bank which will be initialized.
                                       For single CAN instance(14 dedicated filter banks),
                                       this parameter must be a number between Min_Data = 0 and Max_Data = 13.
                                       For dual CAN instances(28 filter banks shared),
                                       this parameter must be a number between Min_Data = 0 and Max_Data = 27. */

  uint32_t FilterMode;            /*!< Specifies the filter mode to be initialized.
                                       This parameter can be a value of @ref CAN_filter_mode */

  uint32_t FilterScale;           /*!< Specifies the filter scale.
                                       This parameter can be a value of @ref CAN_filter_scale */

  uint32_t FilterActivation;      /*!< Enable or disable the filter.
                                       This parameter can be a value of @ref CAN_filter_activation */

  uint32_t SlaveStartFilterBank;  /*!< Select the start filter bank for the slave CAN instance.
                                       For single CAN instances, this parameter is meaningless.
                                       For dual CAN instances, all filter banks with lower index are assigned to master
                                       CAN instance, whereas all filter banks with greater index are assigned to slave
                                       CAN instance.
                                       This parameter must be a number between Min_Data = 0 and Max_Data = 27. */

} CAN_FilterTypeDef;

这里是过滤器里面的一些定义,具体怎么用可以看下英文说明
在can.c里面配置完成之后,在main.c文件里面加入相关的定义,下面是整个主函数

int main(void)
{
  /* USER CODE BEGIN 1 */
	CAN_TxHeaderTypeDef Can_Tx;
	CAN_RxHeaderTypeDef Can_Rx;
	uint8_t tdata[8] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
	uint8_t adata[8] = {0};
	uint32_t pTxMailbox = 0;

	
  Can_Tx.StdId = 0x123;
	Can_Tx.ExtId = 0x123;
	Can_Tx.IDE = 0;
	Can_Tx.RTR = 0;
	Can_Tx.DLC = 8;
	
	Can_Rx.StdId = 0x321;
	Can_Rx.ExtId = 0x321;
	Can_Rx.IDE = 0;
	Can_Rx.RTR = 0;
	Can_Rx.DLC = 8;
	//定义相关的输出数组和存放接收报文的数组,以及接收帧ID和发送帧ID,如果要接收所有的帧,那么就把
	//接收的帧ID改成形参,传递一下变量就可以了,因为是实验所有写在这里,也可以自己加工一下写个子函
	//数直接调用。
  /* 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 */
	HAL_UART_MspInit(&huart1);
	
	MX_CAN_Init();
	
	HAL_CAN_MspInit(&hcan);
	
	HAL_CAN_Start(&hcan);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_CAN_AddTxMessage(&hcan,&Can_Tx,tdata,&pTxMailbox);
		HAL_CAN_GetRxMessage(&hcan,CAN_RX_FIFO0,&Can_Rx,adata);
		//这个就是接收CAN报文的指令,如果没有过滤器,单单这一条是不工作的
		HAL_UART_Transmit(&huart1,adata,8,1000);
		printf("RX the CAN data: %02X%02X%02X%02X%02X%02X%02X%02X\r\n",adata[0],adata[1],adata[2],adata[3],adata[4],adata[5],adata[6],adata[7]);
		HAL_Delay(500);
		//把接收到的报文通过串口转发到调试助手
  }
  /* USER CODE END 3 */
}

那么下面看实验结果
基于STM32CUBEMX生成的CAN的收发——学习笔记_第14张图片
左边是分析仪的报文分析框,很明显是发送成功的,右上角是通过上位机向板子发送报文,然后经过板子之后通过串口向电脑转发出来,好的,实验成功,那么实验结束!
噢对了,由于STM32CUBEMX生成的工程是不能printf的,所以需要重定向,那么打开usart.c文件,加入以下代码


/* USER CODE BEGIN 0 */
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (uint8_t) ch;      
	return ch;
}


/* USER CODE END 0 */

这个是从原子的例程那里扣出来的,有兴趣的也可以去研究一下,反正我能用就好。。。。
然后打开usart.h文件,毕竟要有头文件嘛

/* USER CODE BEGIN Includes */
#include 
#include 
#include "string.h"
/* USER CODE END Includes */

把这几个头文件包含进去先,好了现在就可以printf啦。写完收工~

	如果有什么错误或者什么不对的地方,还烦请大佬们不吝指正!

你可能感兴趣的:(STM32)