细说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键,可以按下多次:

相关推荐
无情大菜刀44 分钟前
EPS32基础篇开发
单片机·嵌入式硬件
亿道电子Emdoor7 小时前
【ARM】MDK-快捷键添加及修改
arm开发·stm32·单片机
heater40410 小时前
【STM32】stm32启动流程
stm32·单片机·嵌入式硬件
honey ball12 小时前
滤波器的主要参数
人工智能·单片机·嵌入式硬件·学习
浅陌pa12 小时前
RTC:实时时钟
c语言·stm32·单片机·嵌入式硬件
湘の子13 小时前
汇编语言与接口技术--跑马灯
汇编·stm32·单片机·嵌入式硬件·51单片机
佳心饼干-13 小时前
单片机-蜂鸣器实验
单片机·嵌入式硬件
honey ball15 小时前
大功率PCB设计
单片机·嵌入式硬件
黑果果的思考15 小时前
LED背光驱动芯片RT9293应用电路
嵌入式硬件
逝灮16 小时前
【蓝桥杯——物联网设计与开发】Part2:OLED
驱动开发·stm32·单片机·嵌入式硬件·物联网·蓝桥杯·oled