1、STM32Cube MX中CAN的设置
- 时钟的设置
我所使用的芯片是STM32F103R系列的,CAN通讯采用的时钟是PCLK1,APB1 peripheral clocks的时钟。 - CAN的设置
我们在很多资料中显示,CAN通讯的波特率是(SS+PTS+PBS1+PBS2+SJW)个Tq,在STM32Cube MX中我们可以通过预分频系数,PBS2段长度和PBS2段长度,这几个参数来设定波特率,并且根据设置会自动计算出所设定的波特率,对新手很友好。 STM32F103这款芯片CAN模式只能设置为Master Mode。 - CAN配置设置
等STM32Cube MX设置完成后产生代码。我们还需要进行CAN通讯的配置部分。这一部分主要是为了设置滤波器参数,也就是报文过滤配置。
#define CAN_BASE_ID 0
#define CAN_FILTER_MODE_MASK_ENABLE 1
#define CAN_ID_TYPE_STD_ENABLE 1
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
CAN_FilterRegTypeDef IDH = {0};
CAN_FilterRegTypeDef IDL = {0};
#if CAN_ID_TYPE_STD_ENABLE
IDH.Sub.STID = CAN_BASE_ID & 0x7fff;
IDL.Sub.STID = CAN_BASE_ID & 0x7fff;
#else
IDH.Sub.EXID = CAN_BASE_ID 0x3FFFF;
IDL.Sub.EXID = CAN_BASE_ID & 0x3FFFF;
IDL.Sub.IDE = 1;
#endif
sFilterConfig.FilterBank = 0;
#if CAN_FILTER_MODE_MASK_ENABLE
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
#else
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;
#endif
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = (IDH.value & 0xFFFF0000) >> 16;
sFilterConfig.FilterIdLow = IDH.value & 0x0000FFFF;
sFilterConfig.FilterMaskIdHigh = (IDL.value & 0xFFFF0000) >> 16;
sFilterConfig.FilterMaskIdLow = IDL.value & 0x0000FFFF;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_CAN_Start(&hcan) != HAL_OK)
{
Error_Handler();
}
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
}
上面的这部分代码适用于接收所有标准ID的报文信息,由于我们CAN_BASE_ID设置为0,其他的不同设置都可以进行修改,只要注意我上一篇讲的过滤器部分的设置就行,有些ID写入时需要进行移位。
CAN_TxPacketTypeDef g_CanTxPacket;
void CAN_SetTxPacket(uint16_t u16stdId, uint32_t u32CANRTR, uint8_t len)
{
g_CanTxPacket.hdr.StdId = u16stdId;
g_CanTxPacket.hdr.IDE = CAN_ID_STD;
g_CanTxPacket.hdr.DLC = len;
g_CanTxPacket.hdr.RTR = u32CANRTR;
g_CanTxPacket.hdr.TransmitGlobalTime = DISABLE;
}
发送前我们需要设置我们本身的ID,字长和发送类型。等设置好后,我们就可以进行发送操作了。
uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet)
{
uint16_t k = 5000;
while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan)==0)
{
}
if(HAL_CAN_AddTxMessage(&hcan, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)
{
while((hcan.Instance->TSR & CAN_TSR_TME0) == 0U)
{
delay_us(10);
k--;
if(k == 0) return 0;
}
return 1;
}
else
{
Error_Handler();
}
}
这里需要进行说明的点有
- 关于远程帧,我们在定义CAN_TxPacketTypeDef g_CanTxPacket时,里面有一项就是关于发送数据内容,对于数据帧和远程帧,我们所用的函数都是一样的,除了在设置CAN_SetTxPacket这个函数时注明下一步我们要发的是远程帧还是数据帧外没有区别,只是远程帧发送时不会发送数据段,而数据帧发送时会把CAN_TxPacketTypeDef里的payload部分进行发送。
- 关于数据段部分,每一次只能最多发送8个字节,当我们要发送多于8个字节的数据内容时我们就需要进行多次发送,少于8个字节就按实际字节进行填写即可。
- 当我们在连续发送多余3帧的数据帧时,我们必须加上
while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan)==0) { } 否则会出现数据丢失的现象。
- CAN的接收
由于我们设的是所有ID报文信息都接收,因此对于具体实际应用过程中我们可以再进行多一次的筛选,当然这需要我们在自己定义协议过程中进行设置。 CAN通讯的接收函数是
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *CanHandle)
{
uint16_t j;
header = NULL;
if (HAL_CAN_GetRxMessage(CanHandle, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
Error_Handler();
}
if ((RxHeader.StdId == BOX_ID) && (RxHeader.IDE == CAN_ID_STD))
{
}
}
注:对于HAL_CAN_RxFifo0MsgPendingCallback这个接收函数是由于我们在配置CAN过滤器的时候我们以下的配置:
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
简单的一个CAN发送和接收就完成了!
|