IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32CubeMX与HAL库学习--简单的CAN回环测试 -> 正文阅读

[嵌入式]STM32CubeMX与HAL库学习--简单的CAN回环测试

STM32CubeMX与HAL库学习--简单的CAN回环测试

前言

本人小白,最近看了CAN协议与STM32的bxCAN外设的一些资料,简单地做个CAN回环测试练习一下。这只是一个初学者的简单练习记录,学习的话还是要看对应的教程啦。

使用到的工具及版本:
STM32CubeMX版本:6.3.0
HAL库:STM32CubeF4 Firmware Package V1.26.1
MDK-ARM:V5.32.0.0
开发板:野火的霸天虎开发板V2(主控芯片是STM32F407ZGT6)

大概的框图如下:
在这里插入图片描述

STM32CubeMX生成初始化代码

时钟、串口配置之类的略过。
关于CAN配置:
在这里插入图片描述

在NVIC那里打开CAN1 RX0中断允许,并设定一下优先级。

在MDK-ARM里编辑代码

CAN的设置尚未完成,CubeMX那里只是设置了模式,接下来设置筛选器。我配置的是列表模式,筛选了拓展ID 0x2233和标准ID 0。

/*
 * 函数名:CAN_Filter_Config
 * 描述  :CAN的过滤器 配置
 * 输入  :无
 * 输出  : 无
 * 调用  :内部调用
 */
static void CAN_Filter_Config(void)
{
	CAN_FilterTypeDef  CAN_FilterTypeDef;

	/*CAN筛选器初始化*/
	CAN_FilterTypeDef.FilterBank=0;						//筛选器组0
	CAN_FilterTypeDef.FilterMode=CAN_FILTERMODE_IDLIST;	//工作在列表模式
	CAN_FilterTypeDef.FilterScale=CAN_FILTERSCALE_32BIT;	//筛选器位宽为单个32位。
	/* 使能筛选器,按照标志的内容进行比对筛选,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */

	CAN_FilterTypeDef.FilterIdHigh= ((((uint32_t)0x2233<<3)|
										 CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;		//要筛选的ID高位 
	CAN_FilterTypeDef.FilterIdLow= (((uint32_t)0x2233<<3)|
									     CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要筛选的ID低位 
	CAN_FilterTypeDef.FilterMaskIdHigh= 0;		//第二个ID的高位
	CAN_FilterTypeDef.FilterMaskIdLow= 0;			//第二个ID的低位
	CAN_FilterTypeDef.FilterFIFOAssignment=CAN_FILTER_FIFO0 ;	//筛选器被关联到FIFO0
	CAN_FilterTypeDef.FilterActivation=ENABLE;			//使能筛选器
	HAL_CAN_ConfigFilter(&hcan1,&CAN_FilterTypeDef);
}

发送或者接收数据都有对应的结构体对帧的ID、帧类别、数据长度等进行了描述(CAN_RxHeaderTypeDef、CAN_TxHeaderTypeDef),发送时需要根据数据特性配置发送结构体,接收则是把接收数据的特性存储到指定的接收结构体。
消息发送函数
在这里插入图片描述
获取接收消息函数
在这里插入图片描述

因为只是简单的测试,我就在初始化阶段把他们设置好。
用一个CAN_Config把CAN的功能完整。

__IO uint32_t CAN_RxFlag = 0;		 //用于标志是否接收到数据,在中断函数中赋值

CAN_TxHeaderTypeDef TxMes;		//发送结构体
CAN_RxHeaderTypeDef RxMes;		//接收结构体

uint8_t CAN_TxDate[9]="CAN LOOP";								//发送缓冲区
uint8_t CAN_RxDate[9];								//接收缓冲区

uint32_t TxMailbox;			//用于指示CAN消息发送函数HAL_CAN_AddTxMessage使用了哪个邮箱来发送数据


/**
  * @brief  初始化 Rx Message数据结构体
  * @param  RxMessage: 指向要初始化的数据结构体
  * @retval None
  */
void CAN_RxMesInit(CAN_RxHeaderTypeDef* RxMessage)
{
  
	/*把接收结构体清零*/
  (*RxMessage).StdId = 0x00;
  (*RxMessage).ExtId = 0x00;
  (*RxMessage).IDE = CAN_ID_STD;
  (*RxMessage).DLC = 0;
  (*RxMessage).RTR = 0;
  (*RxMessage).FilterMatchIndex = 0;
	(*RxMessage).Timestamp = 0;
}

/*
 * 函数名:CAN_TxMsgInit
  * @brief  初始化TxMessage数据结构体
  * @param  TxMessage: 指向要初始化的数据结构体
  * @retval None
 */	 
void CAN_TxMsgInit(CAN_TxHeaderTypeDef* TxMessage)
{	  
  (*TxMessage).StdId=0x00;						 
  (*TxMessage).ExtId=0x2233;					 //使用的扩展ID
  (*TxMessage).IDE=CAN_ID_EXT;				  //扩展模式
  (*TxMessage).RTR=CAN_RTR_DATA;				 //发送的是数据
  (*TxMessage).DLC=8;							 //数据长度为8字节	
}

/*
 * 函数名:CAN_Config
 * 描述  :完整配置CAN的功能
 * 输入  :无
 * 输出  : 无
 * 调用  :MX的CAN初始化调用
 */
void CAN_Config(void)
{
  CAN_Filter_Config();
  CAN_TxMsgInit(&TxMes); 
  CAN_RxMesInit(&RxMes);
	HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);				//使能FIFO0接收到数据中断
	HAL_CAN_Start(&hcan1);		//开启CAN1
}
			

在CubeMX生成的CAN1初始化函数中调用自己写的CAN_Config(),完成CAN的设置并开启它。
在这里插入图片描述
FIFO0接收到数据的中断回调函数与CAN错误回调。

/**
  * @brief  CAN接收完成中断(非阻塞) 
  * @param  hcan: CAN句柄指针
  * @retval 无
  */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan)
{
	/* 比较ID是否为0x2233 */
	HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&RxMes,CAN_RxDate);
	if((RxMes.ExtId==0x2233) && (RxMes.IDE==CAN_ID_EXT) && (RxMes.DLC==8) )
	{
		CAN_RxFlag = 1; //接收成功  
	}
	else
	{
		CAN_RxFlag = 0; //接收失败
	}
}
/**
  * @brief  CAN错误回调函数
  * @param  hcan: CAN句柄指针
  * @retval 无
  */
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
	printf("\r\nCAN出错\r\n");
}

main函数。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern __IO uint32_t CAN_RxFlag;		 //用于标志是否接收到数据,在中断函数中赋值

extern uint8_t CAN_TxDate[8];								//发送缓冲区
extern uint8_t CAN_RxDate[8];								//接收缓冲区

extern CAN_TxHeaderTypeDef TxMes;		//发送结构体
extern CAN_RxHeaderTypeDef RxMes;		//接收结构体
extern uint32_t TxMailbox;					//用于指示CAN消息发送函数HAL_CAN_AddTxMessage使用了哪个邮箱来发送数据
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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_USART1_UART_Init();
  MX_CAN1_Init();

  /* Initialize interrupts */
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */
	printf("CAN LOOP TEST\r\n");
	HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox);			//发送数据
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		if(CAN_RxFlag)
		{
			CAN_RxFlag = 0;
			printf("Transmit message:%s\r\n",CAN_TxDate);
			printf("Receive message:%s\r\n",CAN_RxDate);
		}
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

效果
在这里插入图片描述

其他

最初,我把发送数组和接收数组的长度设为8

uint8_t CAN_TxDate[8]="CAN LOOP";								//发送缓冲区
uint8_t CAN_RxDate[8];								//接收缓冲区

结果是这样:
在这里插入图片描述
不出意外的话这两个数组在内存中是这样的:
在这里插入图片描述

如果只是用来存储CAN数据这倒是没什么问题,但是用printf把它们作为字符数组输出的话就可能会像图中一样越界了。
所以我把这两个数组长度定义为9,0~7字节是数据,第8字节一直是0(编译器给它初始化为0),这样就可以防止printf输出越界。

后续

接下来,在原来的基础上增加一个按键,把按键按下次数转换为字符串存储到CAN_TxDate数组,并在按键按下的中断里通过CAN发送,其他基本与上文无异,我的实现步骤如下:
首先打开原来的CubeMX工程文件,找到按键的引脚,把它配置为外部中断
在这里插入图片描述
使能中断
在这里插入图片描述
然后就可以生成代码了,别忘了勾选保留用户代码
在这里插入图片描述
写在用户区域的代码会得以保留,写的时候注意一下位置,我前面while循环里的代码写错位置了,重新生成文件时就被删了。
正确的位置

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(CAN_RxFlag)
		{
			CAN_RxFlag = 0;
			printf("Transmit message:%s\r\n",CAN_TxDate);
			printf("Receive message:%s\r\n",CAN_RxDate);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

错误的位置

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		if(CAN_RxFlag)
		{
			CAN_RxFlag = 0;
			printf("Transmit message:%s\r\n",CAN_TxDate);
			printf("Receive message:%s\r\n",CAN_RxDate);
		}
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

其实看注释名字就知道了。
在main.c里增加了按键中断的回调函数。

//按键中断
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	static uint16_t KeyTime = 0;
	if(GPIO_Pin == KEY1_Pin)
	{
		KeyTime++;
		sprintf((char*)CAN_TxDate,"%d",KeyTime);
		HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox);			//发送数据
	}
}	

删除了main函数里的CAN数据发送HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox);
这样,数据只在按键按下时发送,其他不变。
结果
在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:51:02  更:2021-08-23 16:52:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 22:53:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码