目的:学习使用STM32CubeIDE进行CAN通讯编程学习;
准备条件:STM32F103ZET6开发板,PA11 PA12用于CAN + TJA1040,USB-CAN适配器1个;
操作流程:
第一步:使用cubeIDE自动生成代码;
第二步: 编辑代码,loopback条件下工作,中断接收CAN消息;
第三步:连接USB-CAN适配器,进行通讯测试。
详细操作步骤如下:
Step1:cubeIDE 生成project;File\New\STM32 Project;
Step2: Part Number中输入 STM32F103ZE, 选择LQFP144 package;
Step3:配置晶振和调试方法(PinOUT&Configuration)
Step4:配置时钟Clock Configration,确认APB1 peripheral clocks(MHz) = 16
bxCAN 对应的时钟为:APB1外设时钟; 见《STM32F10xx中文参考手册》“2.3 存储器映像” page28/547;
Step5: 配置bxCAN的波特率为100K
Step6:配置bxCAN的接收中断
Step7:配置读取中断的优先级别为15; USB low priority or CAN RX0 interrupts, preemption Priority =15;
Step8:生成代码后,在main.c中添加bxCAN初始化代码:
static?void?MX_CAN_Init(void)
{
??/* USER CODE BEGIN CAN_Init 0 */
??/* USER CODE END CAN_Init 0 */
??/* USER CODE BEGIN CAN_Init 1 */
??/* USER CODE END CAN_Init 1 */
??hcan.Instance?= CAN1;
??hcan.Init.Prescaler?= 16;
??hcan.Init.Mode?= CAN_MODE_NORMAL;
??hcan.Init.SyncJumpWidth?= CAN_SJW_1TQ;
??hcan.Init.TimeSeg1?= CAN_BS1_6TQ;
??hcan.Init.TimeSeg2?= CAN_BS2_3TQ;
??hcan.Init.TimeTriggeredMode?= DISABLE;
??hcan.Init.AutoBusOff?= DISABLE;
??hcan.Init.AutoWakeUp?= DISABLE;
??hcan.Init.AutoRetransmission?= DISABLE;
??hcan.Init.ReceiveFifoLocked?= DISABLE;
??hcan.Init.TransmitFifoPriority?= DISABLE;
??if?(HAL_CAN_Init(&hcan) != HAL_OK)
??{
????Error_Handler();
??}
??/* USER CODE BEGIN CAN_Init 2 */
???CAN_FilterTypeDef?_CAN_FilterTypeDef;
_CAN_FilterTypeDef.FilterBank?= 0; // filter 0
_CAN_FilterTypeDef.FilterMode?= CAN_FILTERMODE_IDMASK; // mask mode
_CAN_FilterTypeDef.FilterScale?= CAN_FILTERSCALE_32BIT;
_CAN_FilterTypeDef.FilterIdHigh?= 0;
_CAN_FilterTypeDef.FilterIdLow?= 0;
_CAN_FilterTypeDef.FilterMaskIdHigh?= 0;
_CAN_FilterTypeDef.FilterMaskIdLow?= 0; // set mask 0 to receive all ??can id
_CAN_FilterTypeDef.FilterFIFOAssignment?= CAN_RX_FIFO0; // assign to fifo0
_CAN_FilterTypeDef.FilterActivation?= CAN_FILTER_ENABLE;// enable can filter
_CAN_FilterTypeDef.SlaveStartFilterBank?= 27;
if(HAL_CAN_ConfigFilter(&hcan,&_CAN_FilterTypeDef) != HAL_OK)
Error_Handler();
if(HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
Error_Handler();
if(HAL_CAN_Start(&hcan) != HAL_OK)
Error_Handler();
??/* USER CODE END CAN_Init 2 */
}
Step9:生成代码后,在main.c中添加bxCAN读、写、中断接收代码:
/* USER CODE BEGIN 0 */
#define Tx_Error 3
#define Rx_Error 2
#define Rx_OK 0
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 = 0x030; // 以此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_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; // 接收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<Rx_pHeader.DLC; i++)
{
RxData[i] = aData[i];
}
return Rx_OK;
}
}
uint8_t RxData[8] = {0}; // 缓存接收到的信息
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");
}
}
}
uint8_t TxData[8] = {0}; // 缓存待发送的信息
uint8_t length = 0x00; // 待发送信息的长度
/* USER CODE END 0 */
Step10:生成代码后,在main.c中添加main函数中添加主代码:
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; // 更新待发送信息的数据长度
/* 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.5
}
/* USER CODE END 3 */
}
结果:调试成功后,可以在usb can收到消息
使用注意事项:
stm32的bxCAN配置和使用流程在cubeide软件中如下:
Step1: ioc文件里图形化使能can并配置工作时钟、波特率、工作模式、中断优先级;
Step2:自动生成代码后,在can.c文件里添加滤波器的配置并使能FIFO中断;
Step3:在MX_CAN_Init初始化完毕后启动can,才能发送信息;
Step4:在main.c文件下的main()外用户自定义中断回调函数。
常见错误点总结:
- 没有启动can,can是无法发送信息、接收不到信息的。对应库函数HAL_CAN_Start。
- 没有使能FIFO中断,can是不会中断的。
- 回调函数的函数名使用错误(因为有多个用途不同的函数名),can中断后不调用该函数。对应函数名:HAL_CAN_RxFifo0MsgPendingCallback;
- CAN调试的时候,可以先使用loopback模式,进行发送接收测试,没问题后,再设置为normal模块,连接USB-CAN适配器,进行实际测试;
- 波特率设置中的 Time Quanta Bit Segmenet1 和Time Quanta Bit Segmenet 2,尽量大于2,否则会影响发送和接收的稳定性。
- 希望自己能记住,同时看到的人也有帮助。
-
本文的源代码下载链接:https://download.csdn.net/download/qq_23313467/86730136
|