OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4140|回复: 12

LIN总线驱动

[复制链接]

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
399
金钱
399
注册时间
2018-5-1
在线时间
145 小时
发表于 2018-10-8 13:08:13 | 显示全部楼层 |阅读模式
之前编写lin总线的程序,因为是汽车上用的,刚接触,在学习过程中发现lin的资料论坛中挺少的,所以特意分享下,自己在网上找到的资料,希望大家可以多多交流
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
399
金钱
399
注册时间
2018-5-1
在线时间
145 小时
 楼主| 发表于 2018-10-8 13:13:29 | 显示全部楼层
#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */

#define BUZZ PORTB_PB5
#define BUZZ_dir DDRB_DDRB5

#define LEDCPU PORTD_PD3
#define LEDCPU_dir DDRD_DDRD3

#define  BUS_CLOCK                   16000000           //总线频率
#define  BAUD 9600                   //串口波特率

#define BIT(A,B)      ((A>>B)&0x01)   // A 为变量
                                      // B 表示A中的第几位
#define     ID         0x30           // ID场值为0x30
#define     EN         PORTD_PD5     //LIN使能
#define     EN_dir     DDRD_DDRD5

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_send,frame_receive;

/*************************************************************/
/*                      初始化锁相环                         */
/*                  使用外部晶振:16MHz                      */
/*                  设置总线频率:16MHz                      */
/*************************************************************/
void INIT_PLL(void)
{
    CPMUPROT=0x26;   //解除时钟配置保护     
   
    CPMUOSC_OSCE=1;    //使能外部晶振
    while(CPMUOSC_OSCPINS_EN==0);    //等待外部晶振使能
   
    CPMUCLKS &=(~0x40);
    CPMUCLKS |= 0x80;        //设置PLLCLK为系统时钟

    //PLLCLK=2×OSCCLK×(SYNDIV+1)/(REFDIV+1), fbus=PLLCLK/2
    CPMUSYNR=0x01;       //SYNDIV的值为1,
    CPMUREFDIV = 0x81;   //REFDIV的值为1
    CPMUPOSTDIV=0x00;
    CPMUPLL=0x10;

    while(CPMUFLG_LOCK==0); //等待PLLCLK锁定
   
    CPMUPROT=0x00;         //使能时钟配置保护
}

/************************************************************/
/*                    初始化TIM模块                         */
/************************************************************/
void initialize_TIM(void){
  TSCR1_TFFCA = 1;  // 定时器标志位快速清除
  TSCR1_TEN = 1;    // 定时器使能位. 1=允许定时器正常工作; 0=使主定时器不起作用(包括计数器)
  TIOS  = 0xff;      //指定所有通道为输出比较方式
  TCTL1 = 0x00;            // 后四个通道设置为定时器与输出引脚断开
  TCTL2 = 0x00;     // 前四个通道设置为定时器与输出引脚断开
  TIE   = 0x00;     // 禁止所有通道定时中断
  TSCR2 = 0x06;            // 预分频系数pr2-pr0:110,时钟周期为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_send.protected_id=0;
  frame_send.state=IDLE;
  frame_send.error=0;
  frame_send.check=0;
  for (i=0;i<8;i++)
    frame_send.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_send.state < status)
     if(frame_send.error)
     return(FALSE);
   
  switch(status)
  {
    case _BREAK:
    case SYNCH:
      break;
      
    case PROTECTED_IDENTIFIER:
      if(frame_send.protected_id != val)
        return(FALSE);
      break;
        
    case DATA0:
    case DATA1:
    case DATA2:
    case DATA3:
    case DATA4:
    case DATA5:
    case DATA6:
    case DATA7:
      if(frame_send.data[status-DATA0] != val)
        return(FALSE);
      break;
        
    case CHECKSUM:
      if(frame_send.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);
}

/*************************************************************/
/*                     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);
}

/*************************************************************/
/*                     LIN接收字节函数                       */
/*************************************************************/
Bool LINGetChar(void)
{
  unsigned volatile char ch;
  
  // LIN接收通道状态
  switch(frame_send.state)
  {
    case IDLE:
      if(!(SCI0SR1&0x22))
        return(FALSE);  
      if(SCI0DRL)
        return(FALSE);
      break;
    case _BREAK:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      if(SCI0DRL != 0x55)
        return(FALSE);   
      break;  
    case SYNCH:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      ch = SCI0DRL;
      frame_send.protected_id = ch;
      break;   
    case PROTECTED_IDENTIFIER:
    case DATA0:
    case DATA1:
    case DATA2:
    case DATA3:
    case DATA4:
    case DATA5:
    case DATA6:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      ch = SCI0DRL;
      frame_send.data[frame_send.state-PROTECTED_IDENTIFIER] = ch;
      break;
    case DATA7:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      ch = SCI0DRL;
      frame_send.check = ch;
      break;
    case CHECKSUM:
      return(FALSE);  
  }
  frame_send.state+=1;
  return(TRUE);
}

/*************************************************************/
/*                         延时函数                          */
/*************************************************************/
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())
  {
    BUZZ=1;
    frame_send.error = 1;
    frame_send.state = IDLE;
  }
}
#pragma CODE_SEG DEFAULT


/*************************************************************/
/*                           主函数                          */
/*************************************************************/
void main(void) {
  DisableInterrupts;
  INIT_PLL();
  initialize_TIM();
  INIT_LIN();
  LEDCPU_dir=1;
  LEDCPU=0;
  BUZZ_dir=1;
  BUZZ=0;
        EnableInterrupts;

  msg_send.identifier = ID;    // 标识符
  msg_send.data[0] = 'F';      // 数据  
  msg_send.data[1] = 'R';
  msg_send.data[2] = 'E';
  msg_send.data[3] = 'E';
  msg_send.data[4] = 'F';
  msg_send.data[5] = 'L';
  msg_send.data[6] = 'Y';
  msg_send.data[7] = '!';
  frame_send.state = IDLE;

  for(;;)
  {
      delay1ms(500);
      a = LINSendMsg();      //LIN发送数据
      if(a)
         LEDCPU=~LEDCPU;
      else
         BUZZ=1;
  }
}
回复 支持 1 反对 0

使用道具 举报

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
399
金钱
399
注册时间
2018-5-1
在线时间
145 小时
 楼主| 发表于 2018-10-8 13:10:56 | 显示全部楼层
回复 支持 反对

使用道具 举报

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
399
金钱
399
注册时间
2018-5-1
在线时间
145 小时
 楼主| 发表于 2018-10-8 13:11:32 | 显示全部楼层
谁知道如何添加附件
回复 支持 反对

使用道具 举报

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
399
金钱
399
注册时间
2018-5-1
在线时间
145 小时
 楼主| 发表于 2018-10-8 13:14:06 | 显示全部楼层
#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */
#include "LCD.h"      

#define BUZZ PORTB_PB5
#define BUZZ_dir DDRB_DDRB5

#define LEDCPU PORTD_PD3
#define LEDCPU_dir DDRD_DDRD3

#define  BUS_CLOCK                   16000000           //总线频率
#define BAUD 9600                    //串口波特率

#define BIT(A,B)      ((A>>B)&0x01)   // A 为变量
                                      // B 表示A中的第几位
#define     ID         0x30           // ID场值为0x30
#define     EN         PORTD_PD5     //LIN使能
#define     EN_dir     DDRD_DDRD5
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_send,frame_receive;

char *xianshi[3] = {
"飞翔科技开发板",
"接收到的数据为:",
};

/*************************************************************/
/*                      初始化锁相环                         */
/*                  使用外部晶振:16MHz                      */
/*                  设置总线频率:16MHz                      */
/*************************************************************/
void INIT_PLL(void)
{
    CPMUPROT=0x26;   //解除时钟配置保护     
   
    CPMUOSC_OSCE=1;    //使能外部晶振
    while(CPMUOSC_OSCPINS_EN==0);    //等待外部晶振使能
   
    CPMUCLKS &=(~0x40);
    CPMUCLKS |= 0x80;        //设置PLLCLK为系统时钟

    //PLLCLK=2×OSCCLK×(SYNDIV+1)/(REFDIV+1), fbus=PLLCLK/2
    CPMUSYNR=0x01;       //SYNDIV的值为1,
    CPMUREFDIV = 0x81;   //REFDIV的值为1
    CPMUPOSTDIV=0x00;
    CPMUPLL=0x10;

    while(CPMUFLG_LOCK==0); //等待PLLCLK锁定
   
    CPMUPROT=0x00;         //使能时钟配置保护
}

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


/*************************************************************/
/*                      LIN接收字节函数                      */
/*************************************************************/
Bool LINGetChar(void)
{
  unsigned volatile char ch;
  
  // LIN接收通道状态
  switch(frame_receive.state)
  {
    case IDLE:
      if(!(SCI0SR1&0x22))
        return(FALSE);  
      if(SCI0DRL)
        return(FALSE);
      break;
    case _BREAK:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      if(SCI0DRL != 0x55)
        return(FALSE);   
      break;  
    case SYNCH:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      ch = SCI0DRL;
      frame_receive.protected_id = ch;
      break;   
    case PROTECTED_IDENTIFIER:
    case DATA0:
    case DATA1:
    case DATA2:
    case DATA3:
    case DATA4:
    case DATA5:
    case DATA6:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      ch = SCI0DRL;
      frame_receive.data[frame_receive.state-PROTECTED_IDENTIFIER] = ch;
      break;
    case DATA7:
      if(!(SCI0SR1&0x20))
        return(FALSE);
      ch = SCI0DRL;
      frame_receive.check = ch;
      break;
    case CHECKSUM:
      return(FALSE);  
  }
  frame_receive.state+=1;
  return(TRUE);
}

/************************************************************/
/*                在液晶上显示接收到的数据                  */
/************************************************************/
void play_data(void)
{
    unsigned char l;
    write_command(0x8A);
    for(l=0;l<8;l++)
       write_Data(frame_receive.data[l]);
}

/************************************************************/
/*                         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();
  INIT_PORT();
  LEDCPU_dir=1;
  LEDCPU=0;
  BUZZ_dir=1;
  BUZZ=0;
        EnableInterrupts;


  for(;;)
  {
       if(frame_receive.state == CHECKSUM)
       {
             //判断数据接收是否正确
             if((frame_receive.protected_id == LINCalcParity(ID))&&
                (frame_receive.check == LINCalcChecksum(frame_receive.data)))
                {
                  LEDCPU=~LEDCPU;
                  lcd_clear();
                  lcd_string(0,0,xianshi[0]);
                  lcd_string(1,0,xianshi[1]);
                  play_data(); //显示接收到的数据
                }
        frame_receive.state = IDLE;
            
       }
   
  }
}
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
6
金钱
6
注册时间
2018-11-24
在线时间
1 小时
发表于 2018-11-26 09:44:06 | 显示全部楼层
谢谢楼主分享
回复 支持 反对

使用道具 举报

2

主题

4

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2015-11-19
在线时间
16 小时
发表于 2019-1-22 22:09:24 | 显示全部楼层
感谢楼主
回复 支持 反对

使用道具 举报

2

主题

57

帖子

0

精华

高级会员

Rank: 4

积分
590
金钱
590
注册时间
2014-3-3
在线时间
56 小时
发表于 2019-3-8 22:10:51 | 显示全部楼层
感谢楼主无私奉献,谢谢
回复 支持 反对

使用道具 举报

50

主题

385

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1126
金钱
1126
注册时间
2014-8-24
在线时间
146 小时
发表于 2019-9-3 16:01:37 | 显示全部楼层
谢谢楼主,目前刚接触lin
找一份喜欢的工作,这样每天工作的8个小时是快乐的。 找一个喜欢的人,这样每天工作之外的16个小时也是快乐的。
回复 支持 反对

使用道具 举报

10

主题

293

帖子

0

精华

高级会员

Rank: 4

积分
623
金钱
623
注册时间
2019-6-3
在线时间
107 小时
发表于 2019-9-4 15:57:56 | 显示全部楼层
点击高级这里有个附件添加
{E1DB84A8-AE8B-48FB-9A15-59E28AB848FD}.png
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2013-8-24
在线时间
6 小时
发表于 2019-12-15 20:47:05 | 显示全部楼层
感谢楼主分享  只是不知道是哪个MCU的
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
95
金钱
95
注册时间
2019-7-18
在线时间
38 小时
发表于 2019-12-23 21:01:16 | 显示全部楼层
感谢感谢,真好最近在调lin总线
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2019-5-20
在线时间
10 小时
发表于 2020-5-5 11:31:29 | 显示全部楼层
感谢分享,正准备学习
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-29 05:44

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表