LIN帧头发送

**在MC9S12XS128上实现LIN通讯**


为何树可以长那么高,为什么狗可以单身这么久

之前一直接触的是CAN通讯、串口之类的,突然有一个LIN通讯的CASE让我有点措手不及,在习惯性的在CSDN上寻找答案时发现这一块好像并不是很多,所以在结束了这个CASE之后想着写点关于做LIN通讯时的所见所感。

1.关于LIN总线

LIN的英文全称是Local Interconnect Network,它是基于UART/SCI的低成本串行通讯协议,主要用与车上传感器和执行器之间,例如车窗、后视镜、大灯、门锁等,我的那个CASE是用在换挡器手球和手柄之间的LIN通讯。
LIN的传输不同于CAN,用的是单线传输,单主机多从机(最多16个从机),总线电平12V,最大速率20Kbps。
在这里插入图片描述
总线任务:调度线上帧传输次序;监测数据处理错误;接收唤醒总线命令(这个我没研究)。
从机任务:发送应答;接收应答;既不接受也不应答(注:从机不能直接向总线发送数据,在收到主节点的帧头后通过ID判断具体操作)。
LIN的特点:最大的特点应该就是比CAN便宜吧

2.方向

这次案例是换挡器手柄的LIN通讯检测,我用的是MC9S12XS128,接下来写的主要是思路和代码,硬件部分暂不叙述。
由于我要检测的是一个LIN从机部分的应答,所以我需要做一个LIN的主机,这个主机的功能就是让我要检测的从机发送相应的应答。
从机发送应答的前提是主机提供了帧头以及帧头带有相应的ID,那么接下来我的目标就明确了:做一个循环发送从机对应的帧头就OK了。理论上这样从机就可以发送出我需要检测的应答部分,当然这边还需要一个接收应答的功能,综上可以分为两步:
1.循环发送帧头
2.接收从机的应答

3.具现

LIN的报文分为两部分:帧头和应答。帧头部分由同步间隔段、同步段、ID组成;应答部分由数据段、校验和组成。
在这里插入图片描述
我需要做的主节点发送帧头那发送的内容如下图
在这里插入图片描述
我一开始不理解后面的那段平的怎么发或者说怎么用代码表达,其实不用表达这部分,就只发前面的帧头部分就可以了。
所有的代码如下

#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */
#define LEDCPU PORTK_PK4
#define LEDCPU_dir DDRK_DDRK4
#define BUS_CLOCK		   32000000	   //总线频率
#define OSC_CLOCK		   16000000	   //晶振频率
#define BAUD 19200                   //串口波特率
#define BIT(A,B)      ((A>>B)&0x01)   // A 为变量 
#define     ID         0x20           // ID场值为0x30
#define     EN         PTM_PTM6     //LIN使能
#define     EN_dir     DDRM_DDRM6
//unsigned int x=0;
Bool a;                                      
struct message 
{
  unsigned char identifier;
  unsigned char data[8];
};

struct message msg_send;
struct message msg_get;

// 定义LIN状态
enum lin_state { IDLE, _BREAK, SYNCH, PROTECTED_IDENTIFIER, DATA0, DATA1, 
                 DATA2, DATA3, DATA4, DATA5, DATA6, DATA7, CHECKSUM };

// 定义帧结构体
struct frame {
  unsigned char protected_id;
  unsigned char data[8];
  unsigned char check;
  enum lin_state state;
  unsigned char error;
};

struct frame frame_receive;

/*************************************************************/
/*                      初始化锁相环  ,frame_receive                       */
/*************************************************************/
void INIT_PLL(void) 
{
    CLKSEL &= 0x7f;       //set OSCCLK as sysclk
    PLLCTL &= 0x8F;       //Disable PLL circuit
    CRGINT &= 0xDF;
    
    #if(BUS_CLOCK == 40000000) 
      SYNR = 0x44;
    #elif(BUS_CLOCK == 32000000)
      SYNR = 0x43;     
    #elif(BUS_CLOCK == 24000000)
      SYNR = 0x42;
    #endif 

    REFDV = 0x81;         //PLLCLK=2×OSCCLK×(SYNDIV+1)/(REFDIV+1)=64MHz ,fbus=32M
    PLLCTL =PLLCTL|0x70;  //Enable PLL circuit
    asm NOP;
    asm NOP;
    while(!(CRGFLG&0x08)); //PLLCLK is Locked already
    CLKSEL |= 0x80;        //set PLLCLK as sysclk
}

/************************************************************/
/*                    初始化TIM模块                         */
/************************************************************/
void initialize_TIM(void)
{
  TSCR1_TFFCA = 1;  // 定时器标志位快速清除
  TSCR1_TEN = 1;    // 定时器使能位. 1=允许定时器正常工作; 0=使主定时器不起作用(包括计数器)
  TIOS  = 0xff;      //指定所有通道为输出比较方式
  TCTL1 = 0x00;	    // 后四个通道设置为定时器与输出引脚断开
  TCTL2 = 0x00;     // 前四个通道设置为定时器与输出引脚断开
  TIE   = 0x00;     // 禁止所有通道定时中断
  TSCR2 = 0x07;	    // 预分频系数pr2-pr0:111,,时钟周期为4us,
  TFLG1 = 0xff;	    // 清除各IC/OC中断标志位
  TFLG2 = 0xff;     // 清除自由定时器中断标志位
}

/*************************************************************/
/*                        初始化LIN                          */
/*************************************************************/
void INIT_LIN(void) 
{
  unsigned char i;
  SCI0BD = BUS_CLOCK/16/BAUD;   //设置SCI0波特率为9600  
  SCI0CR1 = 0x00;        //设置SCI0为正常模式,八位数据位,无奇偶校验
  SCI0CR2 = 0x2c;        //允许接收和发送数据,允许接收中断功能 
  SCI0SR2_BRK13 = 1;  
  frame_receive.protected_id=0;
  frame_receive.state=IDLE;
  frame_receive.error=0;
  frame_receive.check=0;
  for (i=0;i<8;i++)
    frame_receive.data[i]=0;
  EN_dir=1;
  EN=1;
}



/*************************************************************/
/*                     计算奇偶校验位                        */
/*************************************************************/
unsigned char LINCalcParity(unsigned char id)
{
  unsigned char parity, p0,p1;
  parity=id; 
  p0=(BIT(parity,0)^BIT(parity,1)^BIT(parity,2)^BIT(parity,4))<<6;     //偶校验位
  p1=(!(BIT(parity,1)^BIT(parity,3)^BIT(parity,4)^BIT(parity,5)))<<7;  //奇校验位
  parity|=(p0|p1);
  return parity;
}

/*************************************************************/
/*                       计算和校验位                        */
/*************************************************************/
unsigned char LINCalcChecksum(unsigned char *data)         
{
  unsigned int sum = 0;
  unsigned char i;

  for(i = 0; i < 8; i++)
  {
    sum += data[i];
    if(sum&0xFF00)
      sum = (sum&0x00FF) + 1;
  }
  sum ^= 0x00FF;         
  return (unsigned char)sum;
}

/*************************************************************/
/*                       发送检测函数                        */
/*************************************************************/
Bool LINCheckSend(enum lin_state status, unsigned char val)
{
  //等待串口数据接收到为止  
  while(frame_receive.state < status)
     if(frame_receive.error)
     return(FALSE);    
  switch(status) 
  {
    case _BREAK:
    case SYNCH:
      break;      
    case PROTECTED_IDENTIFIER:
      if(frame_receive.protected_id != val)
        return(FALSE);
      break;        
    case DATA0:
    case DATA1:
    case DATA2:
    case DATA3:
    case DATA4:
    case DATA5:
    case DATA6:
    case DATA7:
      if(frame_receive.data[status-DATA0] != val)
        return(FALSE);
      break;
        
    case CHECKSUM:
      if(frame_receive.check != val)
        return(FALSE);
      break;
  }
  return(TRUE);
}

/*************************************************************/
/*                     LIN发送字节函数                       */
/*************************************************************/
Bool LINSendChar(unsigned char ch)
{
  while(!SCI0SR1_TDRE);         //等待发送数据寄存器(缓冲器)为空
  SCI0DRL = ch;
  return(TRUE);
}

/*************************************************************/
/*                      LIN发送间隔场                        */
/*************************************************************/
Bool LINSendbreak(void)
{
  while(!SCI0SR1_TDRE);         //等待发送数据寄存器(缓冲器)为空
  SCI0CR2_SBK = 1;           //队列待发送的中止字符
  SCI0CR2_SBK = 0;           //返回正常发送操作
  return(TRUE);
}


/***********发送帧头 ****************/
Bool LINZhuSendMsg(void)
{
  unsigned char  parity_id;
  
  frame_receive.error = 0;
  
    
    if(!LINSendbreak())// 发送间隔场
      return(FALSE);
    
    if(!LINCheckSend(_BREAK,0x00))// 检查间隔场发送
      return(FALSE);       
    
    if(!LINSendChar(0x55))// 发送同步场
      return(FALSE);
    
    if(!LINCheckSend(SYNCH,0x55))// 检查同步场发送
      return(FALSE); 
   
    parity_id=LINCalcParity(msg_send.identifier); // 计算奇偶校验
   
    if(!LINSendChar(parity_id)) // 发送标识符场
      return(FALSE);
    
    if(!LINCheckSend(PROTECTED_IDENTIFIER, parity_id))// 检查标识符场发送
      return(FALSE);
    return(TRUE);
}

/*************************************************************/
/*                     LIN接收字节函数                       */
/*************************************************************/
Bool LINGetChar(void)
{
  unsigned volatile char ch;
  // LIN接收通道状态
  switch(frame_receive.state)
  {
    case IDLE:               /*1.总线空闲时,检测间隔场*/  
      if(!(SCI0SR1&0x22))
        return(FALSE);  
      if(SCI0DRL)
        return(FALSE);
      break;
    case _BREAK:            /*2.检测到间隔场,检测同步场*/ 
      if(!(SCI0SR1&0x20))
        return(FALSE); 
      if(SCI0DRL != 0x55)
        return(FALSE);   
      break;  
    case SYNCH:             /*3.检测到同步场,接收PID*/ 
      if(!(SCI0SR1&0x20))
        return(FALSE); 
      ch = SCI0DRL;
      frame_receive.protected_id = ch;
      break;   
    case PROTECTED_IDENTIFIER:/*4.接收到PID,接收数据0*/ 
    case DATA0:               /*5.接收数据1-7*/
    case DATA1:                       //5
    case DATA2:                       //6
    case DATA3:                       //7
    case DATA4:                       //8
    case DATA5:                       //9
    case DATA6:                       //10
      if(!(SCI0SR1&0x20))
        return(FALSE); 
      ch = SCI0DRL;
      frame_receive.data[frame_receive.state-PROTECTED_IDENTIFIER] = ch;
      break;
    case DATA7:               /*6.接收到数据7,接收CheckSum*/  
      if(!(SCI0SR1&0x20))
        return(FALSE); 
      ch = SCI0DRL;
      frame_receive.check = ch;
      break;
    case CHECKSUM:            /*7.CheckSum接收完成*/     
      return(FALSE);  
  }
  frame_receive.state+=1;
  return(TRUE);
}
/***************************************************
函数名称:UC LinSlave_Msg_Handle(void)
函数功能: 从机接收数据后处理
形式参数:
返回值:  TRUE  --  成功
          FALSE --  失败
*****************************************************/
Bool LINSlave_Msg_Handle(void)
{
 if(frame_receive.state == CHECKSUM) 
 {
        //判断数据接收是否正确
  if((frame_receive.protected_id == LINCalcParity(ID))&&(frame_receive.check == LINCalcChecksum(frame_receive.data))) 
   {
                  //LEDCPU=~LEDCPU;
    return TRUE;
   }
   frame_receive.state = IDLE;
   return TRUE;
  }
  return FALSE;
}

/*************************************************************/
/*                         延时函数                          */
/*************************************************************/
void delay1ms(unsigned int n) 
{
    unsigned int i;
    for(i=0;i<n;i++) 
    {
        TFLG1_C0F = 1;              //清除标志位
        TC0 = TCNT + 250;             //设置输出比较时间为1ms
        while(TFLG1_C0F == 0);      //等待,直到发生输出比较事件
    }
}
/*************************************************************/
/*                    LIN检查发送的数据                      */
/*************************************************************/
#pragma CODE_SEG __NEAR_SEG NON_BANKED
interrupt void LINreceive(void)
{ 
  if(!LINGetChar()) 
  {
    frame_receive.error = 1;
    frame_receive.state = IDLE;
  } 
}
#pragma CODE_SEG DEFAULT
/*************************************************************/
/*                           主函数                          */
/*************************************************************/
void main(void) 
{
  DisableInterrupts;
  INIT_PLL();
  initialize_TIM();
  INIT_LIN();
  LEDCPU_dir=1;
  LEDCPU=0;
	EnableInterrupts;
  msg_send.identifier = ID;    // 标识符 
  for(;;) 
  {
   a = LINZhuSendMsg();
   delay1ms(10);
   if(a) 
   {
    delay1ms(10);
    if(frame_receive.state == CHECKSUM) 
    {
        //判断数据接收是否正确
     if((frame_receive.protected_id == LINCalcParity(ID))&&(frame_receive.check == LINCalcChecksum(frame_receive.data))) 
     {
      
      LEDCPU=~LEDCPU;
     }
     frame_receive.state = IDLE;
    }
   }
   delay1ms(500);
  }
}

简单讲解一下代码部分:
主函数里面的逻辑是发送帧头,检测是否发送成功,发送成功后接收报文。接收部分用的是中断接收。
在这里插入图片描述

enum lin_state { IDLE, _BREAK, SYNCH, PROTECTED_IDENTIFIER, DATA0, DATA1, 
                 DATA2, DATA3, DATA4, DATA5, DATA6, DATA7, CHECKSUM };

这里面定义的就是上面图中描述的LIN完整报文的组成部分。
完整的代码里主节点发送的只是帧头;下面是主节点发送完整的报文的代码,相比之下多了数据和校验部分。

/*************************************************************/
/*                     LIN发送信息函数                       */
/*************************************************************/
Bool LINSendMsg(void)
{
  unsigned char check_sum, parity_id, i;
  
  frame_send.error = 0;
  
    // 发送间隔场
    if(!LINSendbreak())
      return(FALSE);
    // 检查间隔场发送
    if(!LINCheckSend(_BREAK,0x00))
      return(FALSE);       
    // 发送同步场
    if(!LINSendChar(0x55))
      return(FALSE);
    // 检查同步场发送
    if(!LINCheckSend(SYNCH,0x55))
      return(FALSE); 
    // 计算奇偶校验
    parity_id=LINCalcParity(msg_send.identifier);
    // 发送标识符场
    if(!LINSendChar(parity_id))
      return(FALSE);
    // 检查标识符场发送
    if(!LINCheckSend(PROTECTED_IDENTIFIER, parity_id))
      return(FALSE);  
    
    for(i=0; i < 8; i++) 
    {
      // 发送数据场
      if(!LINSendChar(msg_send.data[i]))
        return(FALSE);
      // 检查数据场发送
      if(!LINCheckSend(DATA0+i, msg_send.data[i]))
        return(FALSE); 
    }
    check_sum = LINCalcChecksum(msg_send.data);
    // 发送校验和场
    if(!LINSendChar(check_sum))
      return(FALSE);
    // 检查校验和场发送
    if(!LINCheckSend(CHECKSUM, check_sum))
     return(FALSE);  
    frame_send.state = IDLE;
  return(TRUE);
}

最后在废话一句:我用的是SCI0不是SCI1。

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
MC9S12X512 LIN是一种基于S12X系列微控制器的车载局域网(Local Interconnect Network)通信协议的实现方案。它通过LIN总线连接车辆内的各种电子控制单元,实现它们之间的通信和数据交换。 首先需要了解S12X系列微控制器的特点。MC9S12X512是一款高性能、低功耗的32位微控制器,具有多种接口和功能模块,适用于车载电子系统的开。它集成了LIN硬件接口和支持LIN协议的软件库,可以方便地实现LIN通信。 在使用MC9S12X512 LIN实现LIN通信时,需要先配置和初始化LIN硬件接口,并设置相关参数,比如波特率、格式等。然后,可以通过编程方式和接收消息,实现各个控制单元之间的数据传输。 MC9S12X512 LIN实现的主要步骤包括:初始化LIN硬件接口、配置波特率、设置数据格式、设置从节点ID和响应处理、消息、接收消息等。具体操作可以使用MC9S12X512开板配合相关开工具进行。 MC9S12X512 LIN的实现可以带来以下优势:首先,使用LIN协议可以降低成本,缩短开周期;其次,LIN总线可以连接多个控制单元,实现复杂系统的数据交换;此外,LIN协议还支持错误检测和诊断功能,提高了系统的可靠性和稳定性。 总而言之,MC9S12X512 LIN实现是一种基于S12X系列微控制器的车载局域网通信方案,通过LIN总线连接车辆内的控制单元,实现它们之间的通信和数据交换,具有成本低、开周期短、可靠性高等优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值