最近在调试LIN总线通讯,使用的是STM32F072。使用F0与F1区别还是有一点,不过大致都可以共用,这里总结一下遇到的一些问题和解决方法。
首先看到上图,一帧的结构数据其实简单理解就是发送同步间隔段之后+串口数据。
发送同步间隔段F0遇到的问题就是没有发送断行字符的功能,需要用IO模拟同步间隔段,而F1有,可以直接调用库函数,或者直接操作寄存器。
F1 :CR1寄存器
所以F0需要模拟发送同步间隔段
/**
* @brief 发送IO切换io模式
* @param 无
* @retval 无
*/
void GPIO_Tx_Normal_Config(uint8_t Set)
{
GPIO_InitTypeDef GPIO_InitStructure;
//设置发送的引脚为普通IO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
if(0 == Set)
{
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //普通IO
}
else
{
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用IO
}
GPIO_Init(GPIOB, &GPIO_InitStructure); //引脚初始化
}
/**
* @brief LIN同步间隔段发送
* @param 无
* @retval 无
*/
void LIN_SendBreak(void) //仿造Break时序写的
{
/*F1 可以直接调用库函数
USART_SendBreak(USART1);
*/
GPIO_Tx_Normal_Config(0);
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
delay_us(700);
GPIO_SetBits(GPIOB,GPIO_Pin_6);
GPIO_Tx_Normal_Config(1);
delay_us(55);
}
解决这个问题后,发送基本就是很简单,加上0x55同步段,加上PID,加上要发的数据,最后发送校验和。
用以下部分代码举例主机发送完整的一帧数据过程。
//主机写:发送帧头 + 响应
u8 senddata[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
u8 Linbuffer[11];
//同步段+PID+数据段(1~8)+校验和=(最大11个字节)
PID = LIN_GetPID(FrameID); //获取PID
Linbuffer[0] = 0x55;
Linbuffer[1] = PID;
for ( i = 0; i < 8; i++)
{
Linbuffer[i + 2] = *(senddata + i);
}
Checksum = LIN_GetChecksum(FrameID,Linbuffer,DataLen); //获取校验和
Linbuffer[8 + 2] = Checksum;
LIN_SendBreak(USARTx);
LIN_SendBytes(USARTx, Linbuffer, 11);
下面是一些主从都用到的代码
/**
* @brief LIN_PID校验函数
* @param ID(FrameID):帧ID(0 ~ 63)
* P0(bit6) = ID0 ^ ID1 ^ ID2 ^ ID4 <==> (偶校验:操作数中1的个数为偶数,校验位为0,1的个数为奇数校验位为1)
* P1(bit7) = ~(ID1 ^ ID3 ^ ID4 ^ ID5) <==> (奇校验:操作数中1的个数为奇数,校验位为0,1的个数为偶数校验位为1)
* @retval 返回PID
*/
uint8_t LIN_GetPID(uint8_t ID)
{
uint8_t PID = 0,P0 = 0,P1 = 0;
P0 = (((ID>>0)^(ID>>1)^(ID>>2)^(ID>>4))&0x01)<<6; //偶校验位
P1 = ((~((ID>>1)^(ID>>3)^(ID>>4)^(ID>>5)))&0x01)<<7; //奇校验位
PID = (ID|P0|P1);
return PID;
}
/**
* @brief LIN协议规定校验和长度为1个字节
* @param ID:校验ID,pData:数据指针,DataLen:数据长度
* @retval 累加校验和
*/
uint8_t LIN_GetChecksum(uint8_t ID, uint8_t* pData,uint8_t DataLen)
{
uint8_t i = 0;
uint16_t CheckSum = 0;
if(ID!=0x3C)
{ //诊断帧只能使用标准校验和,标准校验和不包含PID
CheckSum = LIN_GetPID(ID); //获取PID
}
for ( i = 2; i < DataLen; i++)
{
CheckSum += pData[i];
if (CheckSum > 0xFF){
CheckSum -= 0xFF;
}
}
return (~CheckSum) & 0xFF;
}
总的来说简单的发送过程就是这样,当然为了严谨性使用的时候主从都需要加入接收缓存区减少丢包,还有加入错误判断和断开检测等。
有不懂的朋友可以在评论区留言交流,大家一下学习。