网站ftp上传工具哪个好用,五块钱,站长统计app下载免费,网页设计实训总结意义报告目录
一、工程配置
1、时钟、DEBUG、USART6、GPIO、CodeGenerator
2、CAN1
3、NVIC
二、软件设计
1、KEYLED
2、can.h
3、can.c
#xff08;1#xff09;CAN1中断初始化
#xff08;2#xff09;RNG初始化和随机数产生
#xff08;3#xff09; 筛选器组设置…目录
一、工程配置
1、时钟、DEBUG、USART6、GPIO、CodeGenerator
2、CAN1
3、NVIC
二、软件设计
1、KEYLED
2、can.h
3、can.c
1CAN1中断初始化
2RNG初始化和随机数产生
3 筛选器组设置
4发送消息
5中断方式接收消息
4、main.c
三、下载与运行 在实际的CAN通信中使用轮询方式发送消息使用中断方式接收消息更加实用和普遍。本实例设计一个CAN通信使用中断方式接收消息并且测试在两个FIFO上使用不同的筛选器。 本实例仍然使用使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。需要参考本文作者的其他文章 参考文章1 细说STM32F407单片机轮询方式CAN通信_stm32f407can波特率配置表-CSDN博客 https://wenchm.blog.csdn.net/article/details/144852504 参考文章2细说STM32F407单片机CAN基础知识及其HAL驱动程序-CSDN博客 https://wenchm.blog.csdn.net/article/details/144769950 示例仍然引用KEYLED文件夹中的文件。 示例的功能和使用流程如下 示例工程中使用开发板上的S2键按下S2键后发送一个随机数的数据帧。并用LED1显示工作状态输出到串口助手上。按下S6键开发板复位。示例的功能还包括
使用CAN1的回环模式自发自收。开启FIFO0的接收中断开启FIFO1的接收中断。为FIFO0设置筛选器只接收标识符ID为奇数的消息为FIFO1设置筛选器接收所有消息。使用随机数生成器(Random Number Generator,RNG)在发送消息时用随机数作为帧的数据。
[S2]KeyUp Send a Data Frame LED1 ON
一、工程配置 CAN接口原理图同参考文件1。
1、时钟、DEBUG、USART6、GPIO、CodeGenerator 与参考 文件1的设置相同或近似。 这里设置HCLK168MHzPCLK142MHzPCLK284MHz。
2、CAN1 CAN1模块的参数设置与参考文件1相似。 3、NVIC 使用CAN1模块的接收中断打开CAN1 RX0中断和CAN1 RX1中断两个中断的抢占优先级都设置为1一个CAN模块有4个中断RX0中断是FIFO0的中断RX1中断是FIFO1的中断每个中断有几个中断事件和对应的回调函数详见参考文章2。 二、软件设计
1、KEYLED 本例的项目中要使用KEYLED其中的keyled.c和keyled.h的使用方法与参考文章1相同。
2、can.h
/* USER CODE BEGIN Prototypes */
HAL_StatusTypeDef CAN_SetFilters();
void CAN_TestPoll(uint8_t msgID,uint8_t frameType);
/* USER CODE END Prototypes */
3、can.c
/* USER CODE BEGIN 0 */
#include rng.h
#include stdio.h
/* USER CODE END 0 */
/* USER CODE BEGIN 1 */
//设置筛选器需要在完成CAN初始化之后调用此函数
HAL_StatusTypeDef CAN_SetFilters()
{CAN_FilterTypeDef canFilter;//1. 设置FIFO0的筛选器canFilter.FilterBank 0; //筛选器组编号canFilter.FilterMode CAN_FILTERMODE_IDMASK; //ID掩码模式canFilter.FilterScale CAN_FILTERSCALE_32BIT; //32位长度//只接收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; //应用于FIFO0canFilter.FilterActivation ENABLE; //使用筛选器canFilter.SlaveStartFilterBank 14; //从CAN控制器筛选器起始的BankHAL_StatusTypeDef resultHAL_CAN_ConfigFilter(hcan1, canFilter);//2. 设置FIFO1的筛选器canFilter.FilterBank 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位所有位任意canFilter.FilterFIFOAssignment CAN_RX_FIFO1; //应用于FIFO 1resultHAL_CAN_ConfigFilter(hcan1, canFilter);return result;
}void CAN_SendMsg(uint8_t msgID,uint8_t frameType)
{CAN_TxHeaderTypeDef TxHeader;TxHeader.StdId msgID; //stdIDTxHeader.RTR frameType; //数据帧,CAN_RTR_DATATxHeader.IDE CAN_ID_STD; //标准格式TxHeader.DLC 4; //数据长度TxHeader.TransmitGlobalTime DISABLE;uint32_t rand;HAL_RNG_GenerateRandomNumber(hrng, rand); //产生32位随机数uint8_t TxData[8]; //最多8个字节TxData[3] rand 0x000000FF;TxData[2] (rand 0x0000FF00)8;TxData[1] (rand 0x00FF0000)16;TxData[0] (rand 0xFF000000)24;while(HAL_CAN_GetTxMailboxesFreeLevel(hcan1) 1) {} //等待有可用的发送邮箱printf(Send MsgID %X\r\n,msgID);uint32_t TxMailbox; //临时变量,用于返回使用的邮箱编号/* 发送到邮箱由CAN模块负责发送到CAN总线 */if(HAL_CAN_AddTxMessage(hcan1,TxHeader,TxData,TxMailbox) ! HAL_OK)printf(Send to mailbox error.\r\n);
}//读取和显示FIFO0或FIFO1的消息
//参数FIFO_num是FIFO编号CAN_RX_FIFO0或CAN_RX_FIFO1
void CAN_ReadMsg(uint32_t FIFO_num)
{CAN_RxHeaderTypeDef RxHeader;uint8_t RxData[8]; //接收数据缓存区8个字节if(FIFO_numCAN_RX_FIFO0){printf(Msg received by FIFO0.\r\n);if(HAL_CAN_GetRxMessage(hcan1,CAN_RX_FIFO0,RxHeader,RxData) ! HAL_OK){printf(Read FIFO0 error.\r\n);return;}}else{printf(Msg received by FIFO1.\r\n);if(HAL_CAN_GetRxMessage(hcan1, CAN_RX_FIFO1, RxHeader, RxData) ! HAL_OK){printf(Read FIFO1 error.\r\n);return;}}//显示接收到的消息printf(StdID %lX\r\n,RxHeader.StdId);printf(RTR(0Data,2Remote) %lX\r\n,RxHeader.RTR);printf(IDE(0Std,4Ext) %lX\r\n,RxHeader.IDE);printf(FilterMatchIndex %lX\r\n,RxHeader.FilterMatchIndex);printf(DLC(Data length) %lX\r\n,RxHeader.DLC);for (uint8_t i0; i4; i){printf(Data[0] %X\r\n,RxData[0]);printf(Data[1] %X\r\n,RxData[1]);printf(Data[2] %X\r\n,RxData[2]);printf(Data[3] %X\r\n,RxData[3]);}printf(** Reselect menu or reset **\r\n);
}
//FIFO0接收到新消息事件中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO0);
}//FIFO1接收到新消息事件中断回调函数
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO1);
}
/* USER CODE END 1 */
1CAN1中断初始化 函数MX_CAN1_Init()的代码与参考文章1完全相同函数HAL_CAN_MspInit()中与参考文章1比较增加了两个中断的初始化设置开启了CAN1_RX0和CAN1_RX1中断 /* CAN1 interrupt Init */HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 0);HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 0);HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
2RNG初始化和随机数产生 RNG是MCU的一个内部单元其初始化很简单就是定义了RNG模块的外设对象变量开启其时钟。相关代码如下
#include rng.h
RNG_HandleTypeDef hrng;void MX_RNG_Init(void)
{hrng.Instance RNG;if (HAL_RNG_Init(hrng) ! HAL_OK){Error_Handler();}
}void HAL_RNG_MspInit(RNG_HandleTypeDef* rngHandle)
{if(rngHandle-InstanceRNG){/* RNG clock enable */__HAL_RCC_RNG_CLK_ENABLE();}
}void HAL_RNG_MspDeInit(RNG_HandleTypeDef* rngHandle)
{if(rngHandle-InstanceRNG){/* Peripheral clock disable */__HAL_RCC_RNG_CLK_DISABLE();}
} 可以使用轮询方式或中断方式产生32位的随机数分别对应两个函数。
HAL_RNG_GenerateRandomNumber() //轮询方式产生随机数。
HAL_RNG_GetRandomNumber_IT() //中断方式产生随机数。 HAL_RNG_GenerateRandomNumber()的函数原型如下
HAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng,uint32_t*random32bit) RNG由时钟树中的时钟信号由PCLK1 42MHz驱动典型值是42MHz这个时钟频率不能太低在本例中这个时钟频率是42MHz。产生两个连续随机数的最小间隔是42个PLL42CLK周期。
3 筛选器组设置 在文件can.h中自定义的函数CAN_SetFilters()用于对FIFO0和FIFO1进行筛选器设置。详细代码在can.c中实现。 为FIFO0设置的筛选器是只接收标识符ID为奇数的消息为FIFO1设置的筛选器是可以接收任何消息。注意可以为一个FIFO设置多个筛选器但是一个筛选器只能用于一个FIFO所以这两个筛选器的FilterBank必须不同。结构体CAN_FilterTypeDef各成员变量的意义以及筛选器的设置原理详见参考文章1。
4发送消息 在main.c中调用函数CAN_SendMsg()发送数据帧这个自定义函数的实现代码与参考文章1不同并在can.c中实现这个函数的代码。 在其实现的程序中还调用函数HAL_RNG_GenerateRandomNumber()产生一个32位随机数然后分解为4字节存入发送数据缓冲区。程序仍然是用函数HAL_CAN_AddTxMessage()将需要发送的消息写入发送邮箱由CAN模块自动将消息发送到CAN总线上。函数CAN_SendMsg()只管发送消息接收消息由中断去处理。
5中断方式接收消息 由于开启了CAN1的RX0中断和RX1中断在文件stm32f4xx_it.c中自动生成了这两个中断的ISR框架。 CAN1_RX0是FIFO0接收消息、满或上溢时产生的中断接收消息中断事件对应的回调函数是HAL_CAN_RxFifo0MsgPendingCallback()。同样的FIFO1接收消息中断事件对应的回调函数是HAL_CAN_RxFifo1MsgPendingCallback()。CAN1_RX0和CAN1_RX1中断事件与回调函数的对应关系详见参考文章1。所以要使用中断方式处理FIFO0和FIFO1接收的消息只需重新实现这两个回调函数即可。在文件can.c中重新实现这两个回调函数。
//FIFO0接收到新消息事件中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO0);
}//FIFO1接收到新消息事件中断回调函数
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO1);
} 两个回调函数都调用了同一个函数CAN_ReadMsg()只是传递了相应的FIFO编号。函数CAN_ReadMsg()负责读取FIFO0或FIFO1的消息并显示。读取FIFO里面收到的消息仍然使用函数HAL_CAN_GetRxMessage()消息头结构体CAN_RxHeaderTypeDef的意义见参考文章1。这里显示了一个成员变量FilterMatchIndex的值这是接收消息的FIFO内接收了消息的筛选器的序号是在一个FIFO内的筛选器的序号而不是筛选器的FilterBank属性值。
4、main.c
/* USER CODE BEGIN Includes */
#include keyled.h
#include stdio.h
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */printf(Demo18_2_CAN:CAN Interrupt.\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);//必须开启FIFO接收到消息中断事件否则不会响应中断事件__HAL_CAN_ENABLE_IT(hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);__HAL_CAN_ENABLE_IT(hcan1, CAN_IT_RX_FIFO1_MSG_PENDING);//HAL_CAN_ActivateNotification(hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);//HAL_CAN_ActivateNotification(hcan1, CAN_IT_RX_FIFO1_MSG_PENDING);// MCU output low level LED light is onLED1_OFF();/* USER CODE END 2 */ 在外设初始化部分函数MX_RNG_Init()用于RNG的初始化函数MX_CAN1_Init()用于CAN1模块的初始化。 函数CAN_SetFilters()用于设置FIFO0和FIFO1的筛选器组与参考文章1的同名函数代码不同。这里要使用中断方式进行消息接收还需要开启FIFO0和FIFO1的接收新消息的中断事件即
__HAL_CAN_ENABLE_IT(hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
__HAL_CAN_ENABLE_IT(hcan1,CAN_IT_RX_FIFO1_MSG_PENDING); 其中的两个宏分别是FIFO0和FIFO1接收新消息的中断事件使能控制位的宏定义也作为中断事件类型宏定义详见参考文章2。 主程序的while()循环中调用自定义函数CAN_SendMsg()以轮询方式发送一个数据帧接收数据帧在中断里处理。 /* USER CODE BEGIN WHILE */uint8_t msgID1;while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */KEYS curKey ScanPressedKey(KEY_WAIT_ALWAYS);if (curKeyKEY_UP){CAN_SendMsg(msgID, CAN_RTR_DATA);LED1_ON();}elseLED1_OFF();HAL_Delay(500); //延时消除按键抖动}/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
//串口打印
int __io_putchar(int ch)
{HAL_UART_Transmit(huart6,(uint8_t*)ch,1,0xFFFF);return ch;
}
/* USER CODE END 4 */
三、下载与运行 下载后在串口助手上先显示菜单或者按下S6复位键也显示菜单。 每次按下KeyUp键可以发送一个标准格式数据帧msgID加1msgID作为数据帧的标识符ID。 运行时会发现msgID为奇数时是由FIFO0接收消息msgID为偶数时是由FIFO1接收消息。因为在设置筛选器组时设置FIFO0只能接收标识符ID为奇数的消息FIFO1可以接收任意标识符ID的消息。当标识符ID为偶数时只能由FIFO1接收当标识符ID为奇数时两个FIFO都可以接收但是由FIFO0优先接收。 不管是哪个FIFO接收的消息显示的FilterMatchIndex的值都是0因为它们都只有一个筛选器。