最近要做一款汽车零部件Window Lifter driver的测试控制器,用到LIN通信,在此只讨论STM32做为LIN主机节点的情况。
一些基本常识:
◆LIN由于采用单线媒质传输,最大的传输波特率被限定在20kbit/s以内。该值为从满足信号同步而不产生冲突的最高值,到为满足电磁兼容性要求而要达到的传输最低值之间的实验中间值。最小的传输波特率为1kbit/s--这有助于避免在实际中产生超时冲突。
◆如果LIN总线处于未激活状态已经超过4秒了,从机节点也会自动进入休眠模式。
◆LIN2.0中文版本:https://pan.baidu.com/share/link?shareid=2071584885&uk=1645989455&errno=0&errmsg=Auth Login Sucess&&bduss=&ssnerror=0
1.首先需要了解一些基本知识:
<1>主/从机节点
LIN 的拓扑结构为单线总线,应用了单一主机多从机的概念。总线电平为 12V,传输位速率(Bitrate)最高为20kbps。由于物理层限制,一个LIN网络最多可以连接16个节点,典型应用一般都在12个节点以下,主机节点有且只有一个,从机节点有1到15个。主机节点(Master Node)包含主机任务(Master Task)和从机任务(Slave Task),从机节点(Slave Node)只包含从机任务,如下图所示:
主机任务负责:
(1) 调度总线上帧的传输次序(读数据、写命令);
(2) 监测数据,处理错误;
(3) 作为标准时钟参考;
(4) 接收从机节点发出的总线唤醒命令。
从机任务不能够主动发送数据,需要接收主机发送的帧头,根据帧头所包含的信息(这里指帧ID)判断:
(1) 发送应答(帧中除帧头外剩下的部分,参照3.1节的图3.1);
(2) 接收应答;
(3) 既不接收也不发送应答。
<2>帧的结构
帧(Frame)包含帧头(Header)和应答(Response)两部分。如下图所示:
<3>主机节点发送一帧在总线上的传输(写从节点)
通过LIN总线传输的实体为帧。一个报文帧由帧头以及回应(数据)部分组成。在一个激活的LIN 网络中,通讯通常由主节点启动,主节点任务发送包含有同步间隙的报文头,同步字节以及报文标志符(ID)。一个从节点的任务通过接收并过滤标志符被激活,并启动回应报文的传送。回应中包含了1到8个字节的数据以及一个字节的校验码。
传输一帧所花费的总的时间是发送每个字节所用的时间,加上从节点的回应间隙,再加上传输每个字节的间隙时间(inter-byte space)。字节间隙是指发送完前一个字节的停止位后到发送下一个字节的启动位之间的时间。帧在总线上的传输如下图所示:
<4>主机节点接收一帧在总线上的传输(读从节点)
主机任务负责发送帧头;从机任务接收帧头并对帧头
所包含信息进行解析,然后决定是发送应答,还是接收应答,还是不作任何反应。帧在总线上的传输如下图所示:
了解了这些之后,就可以开始编写主机节点的单片机程序了,在此采用STM32F407VGT6,LIN收发器采用ATMEL的ATA6625,原理图如下:(做为LIN主节点,应该对LINBUS加上拉电阻到VBAT)
采用STM32F4的USART6,LIN电路设计为全隔离模式。
USART6初始化程序如下:
void LIN_Configuration(unsigned long LIN_baudrate)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);//使能GPIOC时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOD时钟
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);
//LIN收发器ATA6625的引脚LIN_EN LON_RESET
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_SetBits(GPIOD, GPIO_Pin_8 |GPIO_Pin_9);
//初始化UART6
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
USART_InitStructure.USART_BaudRate = LIN_baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART6, &USART_InitStructure);
//配置LIN断开符检测长度 LBDL 11位
USART_LINBreakDetectLengthConfig(USART6,USART_LINBreakDetectLength_11b);
//打开LIN break detect 中断使能位
USART_ITConfig(USART6,USART_IT_LBD,ENABLE);
//置位LINEN位,打开LIN模式
USART_LINCmd(USART6,ENABLE);
USART_Cmd(USART6, ENABLE);
USART_ITConfig(USART6, USART_IT_RXNE,ENABLE);
USART_ITConfig(USART6, USART_IT_TXE, DISABLE);
USART_ITConfig(USART6, USART_IT_LBD, ENABLE);
}
linWriteMessage程序如下:
linStatus linWriteMessage(uint8_t id, uint8_t *msg, uint8_t dlc)
{
uint8_t i;
uint16_t csum=0;
id &= 0x3f;
if(id!=0x3c)
csum = lin_check_number(id);//LIN2.0
// csum=0;//LIN2.1
for (i = 0; i < dlc; i++)
{
linWriteChar(msg[i]);
//delay_n(100);
csum += msg[i]; //校验和
if (csum >= 256)
csum = (csum+1)&0xff;
}
linWriteChar(~(uint8_t)csum);
USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);
return linOK;
}
linReadMessage程序如下,并通过printf在串口1中打印接收到的数据:
linStatus linReadMessage(uint8_t id, uint8_t *msg)
{
int8_t i=0;
uint16_t csum;
id &= 0x3f;
memset(LinBuff,0,LIN_BUFF_SIZE); //填充LinBuff为0
ucpLinBuff=0;
delay_n(10);//10ms
if(LinBuff[1]==0x55)//
{
for (i = 0; i < ucpLinBuff; i++)
{
msg[i] = LinBuff[i];
printf("linReadMessage: %d \r\n",msg[i]);
}
csum =0;
for (i = 2; i < ucpLinBuff-1; i++)
{
csum += msg[i];
if (csum >= 256)
csum = csum&0x00ff + 1;
}
if ((LinBuff[ucpLinBuff-1]) != (uint8_t)(~(unsigned char)csum))
return linERR_CSUM;
}
else return linERR_HARDWARE;
return linOK;
}
按照Window Lifter driver Specification,
通过LIN发送ID为0x21,数据为0x20 0xFF,则背光点亮,效果如下图(上图未点亮,下图点亮):
读取LIN数据,返回数据,串口打印如下,和Window Lifter driver Specification一致:
对应示波器波形如下:
上一篇:STM32F4的外部SRAM_原理部分
下一篇:STM32中USART接收中断问题使单片机死机
推荐阅读最新更新时间:2024-03-16 16:16