目录
一、CAN模块配置流程
1.1 图形化界面设置can的波特率和相关工作模式
1.2 代码设置can的滤波器和中断
二、代码详解
2.1 CAN信息发送函数
2.2 CAN信息接收函数
2.3 main()函数
2.4 CAN接收回调函数
三、 总结
硬件条件:带有CAN总线接口的STM32开发板/工控板。
实现功能:
本文默认读者已经有了如下的基本知识和技能:
下面直接开始工程(工程中只讲述与can有关的地方)。
STM32中CAN的配置分为两大部分:图形化界面设置can的波特率、相关工作模式和中断优先级;代码设置can的滤波器和使能FIFO中断。现具体讲述着两大部分。
5. 进入"NVIC Settings"中勾选RX0的中断使能,如图1-2;并在软件左侧NVIC栏里设置它的中断优先级,如图1-3。
6. 到这里,图形化页面里能配置的都配置完了。当然,can滤波器的配置还没有进行,这是在下一个部分里。
在软件自动生成的代码里找到can.c文件,这里面是工程对can配置的部分信息,与刚才图形化界面里我们的配置信息保持一致。
图个方便,我们直接在void MX_CAN_Init(void)函数里添加上滤波器的配置并使能对应的接收FIFO中断,如图1-3。这里别忘了在该函数外面定义需要的结构体变量!!!
到此,can的配置全部OK。
现在只需要在main()里调用函数HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan),就可以开启can使它工作了。
要想can发送信息,需要用到函数:HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox),我在这里把它进行再封装一下,变得更好用:
CAN_TxHeaderTypeDef Tx_pHeader;
/*
* @brief: CAN Send Message.
* @param: "TxData[]" stored the message of ready to send, which length must between 0 and 8.
* @param: "length" stored the number of the data (one data is 8 bit) of ready to send.
* @retval: Tx_Error: send error; other: the mailbox which has been used, this parameter can be a CAN_TX_MAILBOX0,
* CAN_TX_MAILBOX1,
* CAN_TX_MAILBOX2.
*/
uint32_t CAN_TX_Message(uint8_t TxData[], uint8_t length)
{
uint32_t TxMailboxNumber = 0x00000000U; // 存储本次发送所使用邮箱的邮箱号
Tx_pHeader.StdId = 0x000; // 以此ID发送
Tx_pHeader.ExtId = 0x0000; // 扩展ID(此处无用)
Tx_pHeader.IDE = CAN_ID_STD; // 标准帧
Tx_pHeader.RTR = CAN_RTR_DATA; // 数据帧
Tx_pHeader.DLC = length; // 发送数据的长度
Tx_pHeader.TransmitGlobalTime = DISABLE;
if(HAL_CAN_AddTxMessage(&hcan, &Tx_pHeader, TxData, &TxMailboxNumber) != HAL_OK)
{
return Tx_Error;
}
return TxMailboxNumber;
}
同样,接受can上的信息,需要用到函数:HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]),我也对其进行再封装:
CAN_RxHeaderTypeDef Rx_pHeader;
/*
* @brief: CAN Receive Message.
* @param: "RxData[]" will store the message which has been received, which length must between 0 and 8.
* @retval: receive status.
*/
uint32_t CAN_RX_Message(uint8_t RxData[])
{
uint8_t aData[8]; // 缓存接收到的信息
Rx_pHeader.StdId = 0x000; // 接收ID(此处无用,can接收所有的ID号)
Rx_pHeader.ExtId = 0x0000;
Rx_pHeader.IDE = CAN_ID_STD; // 接收标准帧
Rx_pHeader.DLC = 8; // 接收8个8bit数据
Rx_pHeader.RTR = CAN_RTR_DATA; // 接收数据帧
Rx_pHeader.FilterMatchIndex = 0; // 使用0号过滤器
Rx_pHeader.Timestamp = 0;
if(HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &Rx_pHeader, aData) != HAL_OK)
{
return Rx_Error;
}
else
{
// 取出接收到的信息
for(uint8_t i = 0; i
/* USER CODE BEGIN PV */
uint8_t TxData[8] = {0}; // 缓存待发送的信息
uint8_t length = 0x00; // 待发送信息的长度
/* USER CODE END PV */
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_CAN_Init();
/* USER CODE BEGIN 2 */
TxData[7] = 0x1a; // 给定待发送的信息
TxData[6] = 0x1b;
TxData[5] = 0x1c;
TxData[4] = 0x1d;
TxData[3] = 0x1e;
TxData[2] = 0x1f;
TxData[1] = 0x10;
TxData[0] = 0x11;
length = 0x08; // 更新待发送信息的数据长度
if (HAL_CAN_Start(&hcan) != HAL_OK) // 开启CAN
{
Error_Handler();
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if (CAN_TX_Message(TxData, length) == Tx_Error) // 轮询方式发送CAN信息
{
Error_Handler();
}
HAL_Delay(500); // 延时0.5s
}
/* USER CODE END 3 */
}
回调函数是中断后执行的用户函数,can的回调函数有多个。想知道can每一个回调函数的信息,请到最后一章中找答案。
因为之前在滤波器中配置使用FIFO0进行接收信息,所以这里一定要用FIFO0对应的回调函数:
/* USER CODE BEGIN PV */
uint8_t RxData[8] = {0}; // 缓存接收到的信息
/* USER CODE END PV */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
// 判断是哪一路的CAN发生了中断
if(hcan->Instance == CAN1)
{
if(CAN_RX_Message(RxData) != Rx_OK)
{
// 接收信息失败
printf("MCU Received CAN Data ERROR!!!");
printf("\n\r");
printf("\n\r");
}
else
{
// 接受信息成功,处理数据
printf("MCU Received CAN Data: ");
for(uint8_t i = 0; i<8; i++)
{
printf("%d ", RxData[i]);
}
printf("\n\r");
printf("\n\r");
}
}
}
stm32的can配置和使用流程在stm32cubeide软件中是这样的:
我遇到的错误点总结:
所有与can相关的函数,它们的功能,传递参数的含义、类型、取值,返回值等;所有与can相关的结构体,它们的定义,成员变量的含义、类型、取值等,或是其它更多的信息。这些请自行在工程里的这两个文件中进行查阅: