通信基础知识
- 分层思想:不同层分工不同,抽象层级不同
- CAN通信(Controller Area Network)
- 1.物理信号层:规定了硬件电路的设计规则、用户使用的接线方式、线路上传输信号与电平格式的对应关系、波特率比特率、数据同步异步传输、收发模式等内容
- 2.链路传输层:规定了地址(CAN ID)、冲突检测与避免(仲裁)、误码校验、滤波器与掩码、该层的帧格式
- 3.应用数据层:传输的数据主体;链路层把应用层的数据拿来,加上相关的地址、校验等信息封装;物理层把链路层的数据拿来,编码成物理信号传输出去。
物理信号
接线方式:总线接法
每个设备引出两根线,一根是CAN_H,一根是CAN_L(也有单线CAN,我们不考虑使用);不同设备的CAN_H接到一起,CAN_L接到一起
CAN_H与CAN_L通过120Ω电阻连接在一起,防止总线上传输的高频信号存在反射振铃效应导致信号不稳定、寄生电容导致信号响应速度变慢等情况。
线路上传输信号的电平格式
差分信号传输:在长距离传输时可保证信号质量(用CAN_H和CAN_L的相对信号来代替绝对信号)
- 码元1被编码成CAN_H与CAN_L电平相同,称为隐形电平,一般两根线电平均为2.5V
- 码元0被编码成CAN_H与CAN_L电平不同,称为显性电平,一般CAN_H为3.5V,CAN_L为1.5V
比特率和波特率
- 比特率(bit/s,bps):每秒传输的二进制码元的数量
- 波特率(symbol/s):每秒钟传输的有效信息码元的数量
- 举例
- CAN通信中,比特率等于波特率(UART也是),有效信息码元直接对应二进制码元
- 十六进制字符编码中,如果一秒传输1000个字符,每个字符对应一种唯一的电平信号,那么波特率就是1000symbol/s;由于一个十六进制字符要用四位二进制编码表示,那么他的波特率就是4000bit/s
异步通信
异步通信通过把每个比特(码元)细分时间片,不同时间片做不同的事情
1.同步段对整条总线各个设备进行同步
2.传播段是时间占用片,因为有些CAN总线可能比较长(可能十几米),要考虑物理信号在总线的传播时间以及CAN驱动器的处理时间。传播速度一般是光速的2/3,也就是2*10^8m/s(CAN通信速率也很高,RM中大概是1Mbps);传播段时间一般是信号在总线上传播时间的两倍。
其他通信协议例如UART也有类似操作
其他通信协议也有同步通信方式,比如IIC(IIC需统一的时钟信号给各设备用于同步)
收发模式
- 常规模式
- 向总线发送
- 从总线接收
- 回环模式
- 向总线和本机发送
- 不从总线接收,仅从本机接收
- 静默模式
- 不向总线发送,仅向本机发送
- 从总线和本机接收
- 回环静默模式
- 不向总线发送
- 不向总线接收
- 自发自收
链路传输
地址(CAN ID)
CAN ID是广播性质的总线协议(用地址表示其实并不准确),CAN ID承担了类似于地址的功能,是一帧的标识符,一般指明是“谁发的”
- 在计算机网络中的大多数协议里,地址与一个设备或一个终端完全绑定且一定范畴内一一对应;但CAN设备不存在一个固定的地址,而是以ID加以区分不同的帧的来源或目标
- 例如,IP地址可以在一个网络内唯一指定一个网络设备(不考虑伪造等非法的网络攻击或IP冲突等错误情况);但CAN ID不可以,它只对它发送的报文负责,我的设备可以发ID为0x201的报文,也可以发ID为0x202的报文,每个报文也可以指定不同的信息
冲突检测与避免(仲裁)
一根物理意义上的导线不可能同时是高电平和低电平,一条传统通信方式的链路上也不可能同时传输0和1,因此需要一定的冲突处理机制
冲突的处理机制主要有两种,一种是冲突检测,一种是冲突避免,CAN采用的是冲突检测
- 冲突检测
- 出现可能的冲突,然后解决冲突
- 当显性和隐性电平都存在时,一般表现为显性
- 当链路上存在设备想发送0的同时又有设备发送1的时候,链路中表现为0,因为0是显性电平
- 冲突避免
- 避免可能出现的冲突
误码校验
误码校验是除差分信号传输外,防止信号出错对系统产生负面作用的另一大利器
差分信号在物理层一定程度上解决了接口间信道上传输过程中环境噪声问题,而误码校验在链路层上减少了硬件偶然错误的信息编码被解析利用,以及仍可能存在的强干扰环境噪声导致信道电压波动很大改变电平的风险
- 采用的是CRC(Cyclic Redundancy Check)校验算法
滤波器与掩码
滤波器用于配置接收设备,注意是滤波通过,而非过滤排除机制,即符合条件的报文会接收
-
滤波器用于过滤出想要接收的信息,掩码Mask用于匹配想要接收的信息ID
-
CAN ID匹配规则。标准格式中的CAN ID长度为11b,也就是CAN ID最大为0x7ff
- 一个CAN ID为0x114,如果我要配置一个滤波器只接收这个CAN ID的帧的话,就需要配置滤波器过滤目标为0x114,掩码为0x7ff。流程就是把掩码变成二进制01串,为1的位要与过滤目标一致
- 一段CAN ID为0x114~0x117,配置一个滤波器只接受这一段CAN ID的帧的话,就要配置过滤目标为0x114,掩码为0x7fc。也就是掩码变成二进制01串,为1的位要与过滤目标一致
CAN通信帧
在计算机网络上一般而言物理层叫信号,链路层叫帧,网络层叫报文,传输层叫流,再往上叫数据,当然也可以统一都叫数据
帧分类(5种):1.数据帧、2.遥控帧、3.错误帧、4.过载帧、5.帧间隔。后三者是硬件设备自行发送的,不用管
- 数据帧:发送的数据。一种是标准数据帧,一种是扩展数据帧,区别在于他们的CAN ID长度不同
- 遥控帧:该帧的ID表明请求该ID对应设备的数据,也有标准遥控帧和扩展遥控帧两种,区别在于他们的CAN ID长度不同
- 错误帧:发送硬件错误回送数据,硬件设备自行生成与接受,一般无需主动发送
- 过载帧如果某接收设备忙,无法处理较多的数据,会自动发送该帧来提醒其他设备减缓数据发送速率
- 间隔帧:分割主动发送帧之间的间隔,此时间内主动发送帧不可被发送,但自动发送帧可以发送,以便插入错误帧和过载帧
标准数据帧格式
扩展数据帧格式
遥控帧格式,相比数据帧,没有数据段,仅在RTR段电平不同
CAN通信的硬件实现
STM32自带CAN控制器,箭头表示收发关系,直线表示我们自行接到外部的总线
STM32一般搭配的收发器型号为SN65HVD230
- 发送方式:把发送的数据加到CAN收发器中,由它自主控制发送(放进去就不用管了)
- 接收方式:从接收队列(FIFO队列)选择接收
示例
元件与功能描述
- RM A板
- 主控单元为STM32F427IIH6
- 12MHz晶振,可配置最高主频180MHz
- 功能描述
- CAN1总线自发自收
- 发送方发送一字节数据指明灯的序号
- 接收方接受后直接在中断函数中处理信息,熄灭对应的灯,直到下次指令到来重新点灯
工程配置
-
选择MCU
- 配置RCC的HSE为外部晶振
- 配置时钟树HSE输出主频
- Debug选项选择serial wire
- 工程管理中修改工程与路径,注意不要有中文等字符
- 代码生成到单独头文件
-
元件与引脚对应关系
- LED1-8:PG1-PG8,电路原理图中低电平点亮
- CAN1:PD0、PD1,在APB1总线上
CAN参数
-
Prescaler是9,从APB1的45MHz分频出来
-
时间元1和2分别为2,采样在两者间
-
采用回环模式
-
剩下的默认
-
使能中断
-
代码编写
- 初始化配置
- 发送报文
- 接收报文
//滤波器编号
#define CAN_FILTER(x) ((x) << 3)
//接收队列
#define CAN_FIFO_0 (0 << 2)
#define CAN_FIFO_1 (1 << 2)
//标准帧或扩展帧
#define CAN_STDID (0 << 1)
#define CAN_EXTID (1 << 1)
//数据帧或遥控帧
#define CAN_DATA_TYPE (0 << 0)
#define CAN_REMOTE_TYPE (1 << 0)
/**
* @brief 初始化CAN总线
*
*@param hcan CAN编号
*@param Callback_Function 处理回调函数
*/
void CAN_Init(CAN_HandleTypeDef *hcan)
{
HAL_CAN_Start(hcan);
__HAL_CAN_ENABLE_IT(hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
__HAL_CAN_ENABLE_IT(hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
}
/**
* @brief 配置CAN的滤波器
*
*@param hcan CAN编号
*@param Object_Para 编号 | FIFOx | ID类型 | 帧类型
*@param ID ID
*@param Mask_ID 屏蔽位(0x3ff,0x1fffffff)
*/
void CAN_Filter_Mask_Config(CAN_HandleTypeDef *hcan,uint8_t Object_Para,uint32_t ID,uint32_t Mask_ID)
{
CAN_FilterTypeDef can_filter_init_structure;
//检测关键传参
assert_param(hcan != NULL);
if((Object_Para & 0x02))
{
//数据帧
//掩码后ID的高16bit
can_filter_init_structure.FilterIdHigh = ID << 3 <<16;
//掩码后ID的低16bit
can_filter_init_structure.FilterIdLow = ID << 3 | ((Object_Para & 0x03) << 1);
//ID 掩码值高16bit
can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 3 << 16;
can_filter_init_structure.FilterMaskIdLow = Mask_ID << 3 | ((Object_Para & 0x03) << 1);
}
else
{
//遥控帧
//掩码后ID的高16
can_filter_init_structure.FilterIdHigh = ID << 5;
//掩码后ID的低16bit
can_filter_init_structure.FilterIdLow = ((Object_Para & 0x03) << 1);
//ID掩码值高16bit
can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 5;
//ID掩码值低16bit
can_filter_init_structure.FilterMaskIdHigh = ((Object_Para & 0x03) << 1);
}
//滤波器序号,0-27,共28个滤波器
can_filter_init_structure.FilterBank = Object_Para >> 3; //CAN1是0~13,CAN2是14~28
//滤波器绑定FIFOx,只能绑定一个
can_filter_init_structure.FilterFIFOAssignment = (Object_Para >> 2) & 0x01;
//使能滤波器
can_filter_init_structure.FilterMode = CAN_FILTERSCALE_32BIT;
//从机模式选择开始单元
can_filter_init_structure.SlaveStartFilterBank = 14;
HAL_CAN_ConfigFilter(hcan ,&can_filter_init_structure);
}
/**
* @brief 发送数据帧
*
* @param hcan CAN编号
* @param ID ID
* @param Data 被发送的数据指针
* @param Length 长度
* @return uint8_t 执行状态
*/
uint8_t CAN_Send_Data(CAN_HandleTypeDef *hcan,uint16_t ID,uint8_t *Data,uint16_t Length)
{
CAN_TxHeaderTypeDef tx_header;
uint32_t used_mailbox;
//检测关键传参
assert_param(hcan != NULL);
tx_header.StdId = ID;
tx_header.ExtId = 0;
tx_header.IDE = 0;
tx_header.RTR = 0;
tx_header.DLC = Length;
return (HAL_CAN_AddTxMessage(hcan, &tx_header, Data, &used_mailbox));
}
/**
* @brief 点灯
*
* @param data 收到的数据
*/
void LED_Control(uint8_t data)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_1,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_2,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_3,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_4,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_5,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_6,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_7,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_8,((data & 1)==0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/**
* @brief HAL库CAN接受FIFO中断
*
* @param hcan CAN编号
*/
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef header;
uint8_t data;
HAL_CAN_GetRxMessage(hcan,CAN_FILTER_FIFO1,&header,&data);
LED_Contol(data);
}
uint8_t Send_Data=0;
CAN_Init(& hcan1);
CAN_Filter_Mask_Config(&hcan1,CAN_FILTER(13) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE,0x114,0x7ff);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
Send_Data++;
CAN_Send_Data(&hcan1,0x114,&Send_Data,1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(250);
}
/* USER CODE END 3 */
}
当你用灵魂来做事时,你会感觉到生命泉水的流动。 —鲁米