15.增改源代码,通过CAN通讯实现自定义协议的控制
- 已经实现的LED的闪烁来表达系统的运行状态。如果电机运行故障,则会相应的闪烁LED来指示故障信息。
- 对于一个控制系统,应该有检测反馈,控制决策,动力执行。电机驱动是动力执行单元,受控于上层控制器。在常规的电机驱动控制中,有模拟量控制、232/485类的串口控制、CAN通讯控制、网络控制。现在采用can通讯的接口方式来控制。
- CAN通讯,在调试的时候,有几点需要经常注意。CAN网络上面必须有接收节点,CAN消息才能被准确发送;CAN收发控制器,不同的芯片等,要注意波特率的配置确确实实是一致的;CAN的终端电阻也非常关键,应该让总线的两根线间电阻保持在120欧姆,这样才能让电压波形在总线上快速准确的建立。
配置CAN通讯,还得遵照HAL库的方式来进行,虽然是手写代码来实现。新建了一个can.c.h文件块。
做完这几个函数的配置后,再收发数据控制电机就非常容易了,就是基本的一些通讯概念。
1.使能CAN通讯
void MX_CAN_Init(u32 tsjw,u32 tbs1,u32 tbs2,u16 brp,u32 mode)
{
CAN_FilterTypeDef sFilterConfig;
/*##-1- Configure the CAN peripheral #######################################*/
CanHandle.Instance = CANx;
CanHandle.Init.TimeTriggeredMode = DISABLE;
CanHandle.Init.AutoBusOff = DISABLE;
CanHandle.Init.AutoWakeUp = DISABLE;
CanHandle.Init.AutoRetransmission = ENABLE;
CanHandle.Init.ReceiveFifoLocked = DISABLE;
CanHandle.Init.TransmitFifoPriority = DISABLE;
CanHandle.Init.Mode =mode;// CAN_MODE_NORMAL; //模式设置
CanHandle.Init.SyncJumpWidth =tsjw;// CAN_SJW_1TQ; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ
CanHandle.Init.TimeSeg1 = tbs1;//CAN_BS1_9TQ; //tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ
CanHandle.Init.TimeSeg2 = tbs2;//CAN_BS2_8TQ; //tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ
CanHandle.Init.Prescaler = brp;
if (HAL_CAN_Init(&CanHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/*##-2- Configure the CAN Filter ###########################################*/
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000; //32位ID
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; //32位MASK
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //过滤器0关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; //激活滤波器0
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&CanHandle, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
Error_Handler();
}
/*##-3- Start the CAN peripheral ###########################################*/
if (HAL_CAN_Start(&CanHandle) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/*##-4- Activate CAN RX notification #######################################*/
if (HAL_CAN_ActivateNotification(&CanHandle, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
/* Notification Error */
Error_Handler();
}
/*##-5- Configure Transmission process #####################################*/
TxHeader.StdId = 0x321;
TxHeader.ExtId = 0x00;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.IDE = CAN_ID_STD;
TxHeader.DLC = 8;
TxHeader.TransmitGlobalTime = DISABLE;
}
在这个初始化函数里,也将需要设置的波特率和工作模式通过参数传入进来。将CAN配置来工作在CAN 2.0A标准帧的工作方式,将标准帧ID先赋值一个数据。
配置波特率的参数的时候,尤其要注意,can的时钟频率是多少。STM32 CAN 是与fclk1接在一起的,我的工程中,外接晶振是8M,系统主频是72M,fclk1的频率是36M的。
2.配置CAN通讯用到的引脚
使能can通讯后,在HAL库的模式下,还需要用一个回调函数的方式,将引脚进行配置使能。
/**
* @brief CAN MSP Initialization
* This function configures the hardware resources used in this example
* @param hcan: CAN handle pointer
* @retval None
*/
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hcan->Instance==CAN)
{
/* USER CODE BEGIN CAN_MspInit 0 */
/* USER CODE END CAN_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**CAN GPIO Configuration
PA11 ------> CAN_RX
PA12 ------> CAN_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_CAN;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN CAN_MspInit 1 */
/* USER CODE END CAN_MspInit 1 */
}
}
3.发送数据函数
编写can通讯发送消息的函数,传入要发送的数据消息的指针,要发送的数据长度,ID号。
在实际传送消息的函数调用前,检查一下是否有空消息邮箱。如果没有的话,那消息邮箱表示已经满了,消息是不能被发送的。消息邮箱被占用完的很大原因,就是CAN总线不对,消息没有准备的被任一节点收到。
TxMailbox可以自定义一个U16数就行,它是储存一下,现在用的是哪个消息邮箱进行的数据发送。
u8 CAN1_Send_Msg(u8* msg,u8 len,u16 StdId)
{
u16 i=0;
TxHeader.StdId = StdId;
// TxHeader.ExtId = ExtId;
// TxHeader.RTR = CAN_RTR_DATA;
// TxHeader.IDE = CAN_ID_STD;
TxHeader.DLC = len;
for(i=0;i<len;i++)
TxData[i]=msg[i];
while( HAL_CAN_GetTxMailboxesFreeLevel( &CanHandle ) == 0 );
/* Start the Transmission process */
if (HAL_CAN_AddTxMessage(&CanHandle, &TxHeader, TxData, &TxMailbox) != HAL_OK)
{
/* Transmission request Error */
Error_Handler();
return 1; //发送
}
return 0;
}
4.消息接收
没有使用中断的方式来接收can消息,而是轮询的方式。轮询的时候,检查FIFO中是否有消息在,有的话就取出来放到RxHeader里面去。
u8 CanReceiveMsg(void)
{
/* Get RX message */
if (HAL_CAN_GetRxMessage(&CanHandle, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
/* Reception Error */
Error_Handler();
return 0;
}
return 1;
}