细说STM32F407单片机轮询方式CAN通信

目录

一、项目介绍

二、项目配置

1、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator

2、CAN1

[(1)Bit Timings Parameters组,位时序参数](#(1)Bit Timings Parameters组,位时序参数)

[(2)Basic Parameters组,基本参数](#(2)Basic Parameters组,基本参数)

[(3)Advanced Parameters组,高级参数](#(3)Advanced Parameters组,高级参数)

三、软件设计

1、KEYLED

2、can.h

3、can.c

4、main.c

四、下载与调试


本文旨在以案例说明STM32F407单片机轮询方式CAN通信的方法。本文仍然使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。需要参考本文作者的其他文章:

参考文章1:细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV_stm32f407 spiflash驱动程序-CSDN博客 https://wenchm.blog.csdn.net/article/details/144587209

参考文章2:细说STM32F407单片机CAN基础知识及其HAL驱动程序-CSDN博客 https://wenchm.blog.csdn.net/article/details/144769950

一、项目介绍

本文实例的动作说明:使用开发板上的按键S2、S3,按下S2后,发送一个数据帧,按下S3后,发送一个遥控帧。开发板上的LED1、LED2,依次响应按键的动作。按下S6后,开发板复位。

cpp 复制代码
[S2]KeyUp  = Send a Data Frame.   LED1 ON
[S3]KeyDown= Send a Remote Frame. LED2 ON

示例还有以下功能:

  • 使用CAN测试模式中的回环模式,进行自发自收的测试。
  • 设置筛选器组,只接收消息ID为奇数的消息。
  • 使用轮询方式接收数据。

开发板上CAN收发器型号是VD230。相关跳线的操作在这里省略10000字。有关CAN的原理图如下:

二、项目配置

1、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator

与参考文章1相同或近似。其中的时钟在本例中,HCLK=100MHz,PCLK1=25MHz,PCLK2=50MHz。

2、CAN1

CAN1的参数设置分为3个部分:

(1)Bit Timings Parameters组,位时序参数

位时序和波特率的原理详见参考文章2,这里设置的参数如下:

  • Prescaler,预分频系数,这里设置为5,可设置范围是1~1024。CAN1的时钟频率由PCLK1经过分频后得到,本例中PCLK1=25MHz,经过5分频后,=5MHz。
  • Time Quantum,时间片。在设置预分频系数后,时间片会被自动计算:tq=1/(5×1000000)=200ns。
  • Time Quantam in Bit Segment 1,位段1的时间片个数为m,范围为1~16,本例设置为4。
  • Time Quantam in Bit Segment 2,位段2的时间片个数为n,范围为1~8,本例设置为3。
  • ReSynchronization Jump Width(SJW),再同步跳转宽度,设置范围为1~4,本例设置为1。CAN通信的波特率由同步段、BS1、BS2的时间片个数决定,波特率计算公式如下:Baudrate=1/(1+m+n)=1s/(8×200ns)=625kbit/s。

注意,STM32F407的CAN控制器在闭环CAN网络中波特率范围是125kbit/s~1Mbit/s,如果计算的实际波特率不在这个范围内,则需要调整分频系数或各位段的时间片个数。

(2)Basic Parameters组,基本参数

基本参数与CAN主控制寄存器CAN_MCR中的一些位对应,对CAN模块的一些特性进行设置。

  • Time Triggered Communication Mode(TTCM位),时间触发通信模式。设置为Disable表示禁止时间触发通信模式,若启用TTCM,则在发送或接收消息时,会加上一个内部计数器的计数值。
  • Automatic Bus-Off Management(ABOM位),自动的总线关闭管理。设置为Disable表示不使用自动的总线关闭。
  • Automatic Wake-Up Mode(AWUM位),自动唤醒模式。这个参数用于控制CAN模块在睡眠模式下接收消息时的行为,如果设置为Enable,则表示只要接收消息,就通过硬件自动退出睡眠模式。
  • Automatic Retransmission(NART位),自动重发。若设置为Enable,CAN模块将自动重发消息,直到发送成功为止。若设置为Disable,则无论发送结果如何,消息只发送一次。这个设定值实际是对NART位值取反,因为NART表示禁止自动重发。
  • Receive Fifo Locked Mode(RFLM位),接收FIFO锁定模式。若设置为Disable,表示FIFO上溢不锁定,下一条新消息覆盖前一条消息。若设置为Enable,则表示上溢后锁定,丢弃下一条新消息。
  • Transmit Fifo Priority(TXFP位),发送FIFO优先级。若设置为Disable,表示消息优先级由标识符决定;若设置为Enable,表示优先级由请求顺序决定。

(3)Advanced Parameters组,高级参数

  • Operating Mode,用于设置CAN模块的工作模式,有4种工作模式可选,即正常(Normal)、静默(Silent)、回环(Loopback)、回环静默(Loopback combined with Silent)。其中,后3种是CAN模块的测试模式。本例设置为Loopback,使用其自发自收功能进行CAN收发功能的测试。

本例使用轮询方式测试CAN模块的数据发送和接收功能,所以不开启CAN1的任何中断。

三、软件设计

1、KEYLED

本例的项目中要使用KEYLED,其中的keyled.c和keyled.h的使用方法与参考文章1相同。

2、can.h

cpp 复制代码
/* USER CODE BEGIN Prototypes */
HAL_StatusTypeDef CAN_SetFilters();
void CAN_TestPoll(uint8_t msgID,uint8_t frameType);
/* USER CODE END Prototypes */

3、can.c

cpp 复制代码
/* USER CODE BEGIN 0 */
#include <stdio.h>
/* USER CODE END 0 */
cpp 复制代码
/* USER CODE BEGIN 1 */
//设置筛选器,要在完成CAN初始化之后调用此函数
HAL_StatusTypeDef CAN_SetFilters()
{
	CAN_FilterTypeDef canFilter;//筛选器结构体变量

 // Configure the CAN Filter
	canFilter.FilterBank = 0;	//筛选器组编号
	canFilter.FilterMode = CAN_FILTERMODE_IDMASK;	//ID掩码模式
	canFilter.FilterScale = CAN_FILTERSCALE_32BIT;	//32位长度

//设置1:接收所有帧
//	canFilter.FilterIdHigh = 0x0000;		//CAN_FxR1 的高16位
//	canFilter.FilterIdLow = 0x0000;			//CAN_FxR1 的低16位
//	canFilter.FilterMaskIdHigh = 0x0000;	//CAN_FxR2 的高16位,所有位任意
//	canFilter.FilterMaskIdLow = 0x0000;		//CAN_FxR2 的低16位,所有位任意

//设置2:只接收stdID为奇数的帧
	canFilter.FilterIdHigh = 0x0020;		//CAN_FxR1 的高16位
	canFilter.FilterIdLow = 0x0000;			//CAN_FxR1 的低16位
	canFilter.FilterMaskIdHigh = 0x0020;	//CAN_FxR2 的高16位
	canFilter.FilterMaskIdLow = 0x0000;		//CAN_FxR2 的低16位

	canFilter.FilterFIFOAssignment = CAN_RX_FIFO0;		//应用于FIFO0
	canFilter.FilterActivation = ENABLE;	//使用筛选器
	canFilter.SlaveStartFilterBank = 14;	//从CAN控制器筛选器起始的Bank

	HAL_StatusTypeDef result=HAL_CAN_ConfigFilter(&hcan1,&canFilter);
	return result;
}

void CAN_TestPoll(uint8_t msgID, uint8_t frameType)
{
//1. 要发送的数据
	uint8_t TxData[8];	//发送数据缓存区,最多8字节
	TxData[0]=msgID;
	TxData[1]=msgID+11;

	CAN_TxHeaderTypeDef TxHeader;	//发送消息的结构体变量
	TxHeader.StdId = msgID;			//stdID
	TxHeader.RTR = frameType;		//数据帧或遥控帧,CAN_RTR_DATA或CAN_RTR_REMOTE
	TxHeader.IDE = CAN_ID_STD;		//标准格式
	TxHeader.DLC = 2;   			//数据字节数
	TxHeader.TransmitGlobalTime = DISABLE;//禁用时间戳

	while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) < 1) {
	} //等待有可用的发送邮箱

	uint32_t TxMailbox;				//临时变量,用于返回使用的邮箱编号
	/* 将消息发送到邮箱 */
	if(HAL_CAN_AddTxMessage(&hcan1,&TxHeader,TxData,&TxMailbox) != HAL_OK)
	{
		printf("Transmit error.\r\n");
		return;
	}
	printf("Send MsgID= %X\r\n",msgID);

//2. 轮询方式接收消息
	/* 等待邮箱发送完毕,也就是等待空闲邮箱个数恢复为3 */
	while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3) {
	}

	CAN_RxHeaderTypeDef RxHeader;	//接收消息的结构体变量
	uint8_t             RxData[8]; 	//接收数据缓存区

	HAL_Delay(1);
	if(HAL_CAN_GetRxFifoFillLevel(&hcan1,CAN_RX_FIFO0) != 1)
	{
		printf("Message is not received.\r\n");
		return;
	}
	printf("Message is received.\r\n");
	if(HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&RxHeader,RxData) == HAL_OK)
	{
		printf("StdID= %lX\r\n",RxHeader.StdId);
		printf("RTR(0=Data,2=Remote)= %lX\r\n",RxHeader.RTR);
		printf("IDE(0=Std,4=Ext)= %lX\r\n",RxHeader.IDE);
		printf("DLC(Data length)= %lX\r\n",RxHeader.DLC);
		//数据帧,显示数据内容,遥控帧没有数据
		if (TxHeader.RTR == CAN_RTR_DATA)
		{
			printf("Data[0]= %X\r\n",RxData[0]);
			printf("Data[1]= %X\r\n",RxData[1]);
		}
	}
}
/* USER CODE END 1 */

在文件can.h中有两个自定义函数,其中函数CAN_SetFilters()用于筛选器设置。

4、main.c

cpp 复制代码
/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
cpp 复制代码
/* USER CODE BEGIN 2 */
  printf("Demo18_1_CAN:CAN Polling.\r\n");
  printf("Test mode:Loopback.\r\n");
  if (CAN_SetFilters() == HAL_OK)   //设置筛选器
  	printf("ID Filter: Only Odd IDs.\r\n");
  if (HAL_CAN_Start(&hcan1) == HAL_OK)  //启动CAN1模块
	printf("CAN is started.\r\n");

  printf("[S2]KeyUp  = Send a Data Frame.\r\n");
  printf("[S3]KeyDown= Send a Remote Frame.\r\n");

  // MCU output low level LED light is on
  LED1_OFF();
  LED2_OFF();
  /* USER CODE END 2 */
cpp 复制代码
 /* USER CODE BEGIN WHILE */
  uint8_t msgID=1;
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	KEYS curKey=ScanPressedKey(KEY_WAIT_ALWAYS);

	if (curKey==KEY_UP)
	{
		CAN_TestPoll(msgID++,CAN_RTR_DATA);	  //发送数据帧
		LED1_ON();
		LED2_OFF();
	}
	else if (curKey==KEY_DOWN)
	{
		CAN_TestPoll(msgID++,CAN_RTR_REMOTE); //发送遥控帧
		LED1_OFF();
		LED2_ON();
	}
	printf("** Reselect menu or reset **\r\n");
	HAL_Delay(500);
  }
  /* USER CODE END 3 */

在进入while循环之前,显示了菜单提示信息。

在while()循环中,检测按键输入,当KeyUp键按下时,调用函数CAN_TestPoll()测试发送数据帧,当KeyDown键按下时,调用函数CAN_TestPoll()测试发送遥控帧。函数CAN_TestPoll()是在文件can.c中实现的自定义函数。 函数CAN_TestPoll()用于测试CAN1模块在轮询方式下的数据发送和接收。

cpp 复制代码
/* USER CODE BEGIN 4 */

//串口打印
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
	return ch;
}
/* USER CODE END 4 */

四、下载与调试

下载后,自动显示:

按下S2键,可以按下多次:

按下S3键,可以按下多次:

相关推荐
llilian_1613 小时前
总线授时卡 CPCI总线授时卡的工作原理及应用场景介绍 CPCI总线校时卡
运维·单片机·其他·自动化
禾仔仔14 小时前
USB MSC从理论到实践(模拟U盘为例)——从零开始学习USB2.0协议(六)
嵌入式硬件·mcu·计算机外设
The Electronic Cat15 小时前
树莓派使用串口启动死机
单片机·嵌入式硬件·树莓派
先知后行。18 小时前
常见元器件
单片机·嵌入式硬件
恒锐丰小吕18 小时前
屹晶微 EG2302 600V耐压、低压启动、带SD关断功能的高性价比半桥栅极驱动器技术解析
嵌入式硬件·硬件工程
Dillon Dong19 小时前
按位或(|=)的核心魔力:用宏定义优雅管理嵌入式故障字
c语言·stm32
Free丶Chan20 小时前
dsPIC系列-1:dsPIC33点灯 [I/O、RCC、定时器]
单片机·嵌入式硬件
v先v关v住v获v取21 小时前
塔式立体车库5张cad+设计说明书+三维图
科技·单片机·51单片机
恒锐丰小吕21 小时前
屹晶微 EG2106D 600V耐压、半桥MOS/IGBT驱动芯片技术解析
嵌入式硬件·硬件工程