本篇详细的记录了如何使用STM32CubeMX配置 STM32F103C8T6 的硬件CAN接口与另一个开发板之间通信。
我这里使用的TJA1050T作为CAN通信收发器,连接f103的PA11,PA12两个管脚,分别作为接收和发送管脚,CAN收发器的H和L之间需要加120欧姆电阻用来做阻抗匹配。
先选择芯片,我这里用的是stm32f103c8t6
我这里用的是J-Link,所以要选择Serial Wire,使用DIO和CLK两个管脚下载程序
接着在RCC中设置时序,我使用的是外部时钟,
CAN收发使用的是PCLK1,这里使用外部时钟倍频后,PCLK1的时钟频率为36M
接着配置CAN外设
先设置CAN通信波特率,我们的目标通信波特率是500KHz,分频系数选择6,那么设一个未知数X,X=6M/500K,那么X=12,因此,BS1+BS2+SJW=12,最终我们会得到BS1,BS2,以及SJW的值
开启CAN接收中断CAN RX0
接着设置串口一用于单片机在接收到数据后将数据发送到上位机,默认波特率是115200就可以
最后设置工程
3.编写程序
总体程序的流程图如下
我们这里对这些设备不做任何过滤,先对过滤器进行配置,放在CAN.C文件中
void CANFilter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0; //CAN过滤器编号,范围0-27
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //CAN过滤器模式,掩码模式或列表模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //CAN过滤器尺度,16位或32位
sFilterConfig.FilterIdHigh = 0x000 << 5; //32位下,存储要过滤ID的高16位
sFilterConfig.FilterIdLow = 0x0000; //32位下,存储要过滤ID的低16位
sFilterConfig.FilterMaskIdHigh = 0x0000; //掩码模式下,存储的是掩码
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = 0; //报文通过过滤器的匹配后,存储到哪个FIFO
sFilterConfig.FilterActivation = ENABLE; //激活过滤器
sFilterConfig.SlaveStartFilterBank = 0;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
然后在CAN.C文件中定义接收和发送消息变量:
static CAN_TxHeaderTypeDef TxMessage; //CAN发送的消息的消息头
static CAN_RxHeaderTypeDef RxMessage; //CAN接收的消息的消息头
在CAN.C最后编写CAN接收中断函数:
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
uint8_t data[8];
HAL_StatusTypeDef status;
if (hcan == hcan) {
status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
if (HAL_OK == status){
printf("--->Data Receieve!\r\n");
printf("RxMessage.StdId is %#x\r\n", RxMessage.StdId);
printf("data[0] is 0x%02x\r\n", data[0]);
printf("data[1] is 0x%02x\r\n", data[1]);
printf("data[2] is 0x%02x\r\n", data[2]);
printf("data[3] is 0x%02x\r\n", data[3]);
printf("<---\r\n");
}
}
}
编写CAN发送测试数据函数
void CAN1_Send_Test()
{
uint32_t TxMailbox;
uint8_t data[4] = {0x01, 0x02, 0x03, 0x04};
TxMessage.IDE = CAN_ID_STD; //设置ID类型
TxMessage.StdId = 0x111; //设置ID号
TxMessage.RTR = CAN_RTR_DATA; //设置传送数据帧
TxMessage.DLC = 4; //设置数据长度
if (HAL_CAN_AddTxMessage(&hcan, &TxMessage, data, &TxMailbox) != HAL_OK)
{
Error_Handler();
}
}
编写开启CAN函数
void CAN_Start_Init()
{
if (HAL_CAN_Start(&hcan) != HAL_OK)
{
Error_Handler();
}
/* 3. Enable CAN RX Interrupt */
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
}
重置printf函数
在main.c中添加#include"stdio.h"
//使用printf
int fputc(int ch,FILE *f)
{
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
}
接着在main函数中将函数开启,
MX_CAN_Init();//can初始化
MX_USART1_UART_Init();
CANFilter_Config();
CAN_Start_Init();
CAN1_Send_Test();
给第二块F103C8T6下载程序时,把Send发送函数中,将ID号进行修改,
这个工程中容易出现错误的地方就在发送函数中,HAL_CAN_AddTxMessage(&hcan, &TxMessage, data, &TxMailbox),第四个参数需要自己定义一个32位数,不然的话程序会陷入死循环,我们可以在这个函数的定义中看到关于这个属性的介绍,这个参数是CAN在发送完一帧数据后返回的值,因此需要自己定义一个变量来存放。
程序参考了Mculover666这位博主的文章STM32CubeMX | 36 - 使用CAN总线进行双板通信(TJA1050)并进行了修改,最终实现了双板通信以及三板和四板通信