在许多嵌入式项目中都会涉及板卡、设备之间的通信,其大致可以分为两类:
一类是一主多从形式,一台机器位主机,其余都是从机;其特点大致为:各个从机不会主动跟主机通信,由主机向各个从机发起通信;常见于RS485通信;
另一类是无所谓主机从机形式,典型案例如CAN总线通信;但更多场景可能是一个主控板通过网口与后台服务器通信、通过N个串口与N个控制板通信、通过一个CAN口与多个采样板通信……其特点包括:
通信介质多样化;
通信介质固定:一旦需求确定一般不会再增加或删除某种通信介质;
本文要描述的通信场景如下图所示:

图中主控板需要通过CAN总线与显示板、控制板、采样板等板卡通信,同时还需要通过串口1与扩展板通信、通过串口2与电源板通信。本文通过实现主控板与其它板卡通信搭建一种可移植性较高的通信框架。
一、通信架构
CAN的接收需要配置筛选器,用于筛选出本板卡感兴趣的数据帧;这里筛选方式有两种:
筛选ID设置为本机板卡ID,那么其它机器要
二、驱动层
COMDriver.h
/*******************************************************************************
* COMDriver.h
******************************************************************************/
#ifndef COM_DRIVER_H_
#define COM_DRIVER_H_
#ifdef __cplusplus
extern "C" {
#endif
/*******************************************************************************
* include
******************************************************************************/
#include "MC_TypeDef.h"
/*******************************************************************************
* global type define
******************************************************************************/
typedef enum
{
BOARD_MAIN = 1,
BOARD_UI,
BOARD_CONTROL,
BOARD_SAMPLE,
BOARD_POWER,
BOARD_EXTEND,
BOARD_NUM
}BOARD_ID; // 需要为每一个板卡分配一个唯一的ID
#define LOCAL_BOARD_ID BOARD_MAIN
/*******************************************************************************
* global variable declaration
******************************************************************************/
/*******************************************************************************
* global function declaration
******************************************************************************/
#ifdef __cplusplus
}
#endif
#endif /* COM_DRIVER_H_ */
CAN驱动,can_ex.c:
/*******************************************************************************
* can_ex.c
******************************************************************************/
#include "can.h"
#include "can_ex.h"
/*******************************************************************************
* Private define
******************************************************************************/
#define ID_MASK 0x00FF
#define H_CAN (&hcan1)
/*******************************************************************************
* Private typedef
******************************************************************************/
typedef void (* Callback)(uint8_t board_id, uint8_t *p_data, uint8_t size);
/*******************************************************************************
* Private function declaration
******************************************************************************/
static CAN_HandleTypeDef* getCAN(uint8_t dst_id);
static uint8_t waitTxFree(CAN_HandleTypeDef *hcan, uint8_t timeout);
static void configFilter(CAN_HandleTypeDef* hcan, uint8_t local_id);
/*******************************************************************************
* Private variable define
******************************************************************************/
static Callback pFunRxCallback = NULL;
/*******************************************************************************
* global function define
******************************************************************************/
void CAN_Init(void *param)
{
pFunRxCallback = (Callback)param;
configFilter(H_CAN, LOCAL_BOARD_ID);
if (HAL_OK == HAL_CAN_Start(H_CAN))
{
HAL_CAN_ActivateNotification(H_CAN, CAN_IT_RX_FIFO0_MSG_PENDING); // 打开接收中断
}
}
uint16_t CAN_Send(uint8_t dst_id, const uint8_t *p_data, uint16_t size)
{
uint32_t txMailbox = 0;
int16_t remain = size;
CAN_TxHeaderTypeDef header;
CAN_HandleTypeDef* hcan = getCAN(dst_id);
if(NULL == hcan)
{
return 0;
}
header.ExtId = (LOCAL_BOARD_ID << 4)|dst_id;
header.IDE = CAN_ID_EXT;
header.RTR = CAN_RTR_DATA;
header.TransmitGlobalTime = ENABLE;
if(FALSE == waitTxFree(hcan, 100))
{
return 0;
}
while (remain > 0)
{
if(remain > 8)
{
header.DLC = 8;
}
else
{
header.DLC = remain;
}
remain -= header.DLC;
if (HAL_OK != HAL_CAN_AddTxMessage(hcan, &header, (UINT8 *)p_data, &txMailbox))
{
return 0;
}
if(FALSE == waitTxFree(hcan, 100))
{
return 0;
}
}
return size;
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef header = {0};
uint8_t data[8] = {0};
if(HAL_OK == HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &header, data))
{
if(CAN_ID_EXT == header.IDE)
{
if(NULL != pFunRxCallback)
{
uint8_t board = (header.ExtId >> 4)&ID_MASK;
pFunRxCallback(board, data, header.DLC); // 根据扩展ID将接收到的数据插入对应的接收缓冲区中
}
}
}
}
/*******************************************************************************
* Private function define
******************************************************************************/
// 扩展ID的低4位用于表示数据的目的地(即数据是发给谁的)
// 扩展ID的其余位用于表示数据的来源地(即数据是谁发的)
// 筛选扩展帧、筛选器尺度为32位、掩码模式、筛选器分配到FIFO0上
static void configFilter(CAN_HandleTypeDef* hcan