⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcan_sja1000.c

📁 CAN 驱动编程
💻 C
📖 第 1 页 / 共 2 页
字号:
//****************************************************************************
// Copyright (C) 2001,2002,2003  PEAK System-Technik GmbH
//                          part of this code is from Arnaud Westenberg 
//                          email:arnaud@wanadoo.nl
//
// linux@peak-system.com
// www.peak-system.com 
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
//****************************************************************************

//****************************************************************************
//
// pcan_sja1000.c - all about sja1000 init and data handling
//
// $Log: pcan_sja1000.c,v $// Revision 1.26  2003/03/02 10:58:07  klaus// merged USB thread into main path//// Revision 1.25  2003/03/02 10:58:07  klaus// merged USB thread into main path//// Revision 1.24.2.5  2003/01/29 20:34:20  klaus// release_20030129_a and release_20030129_u released//// Revision 1.24.2.4  2003/01/29 20:34:20  klaus// release_20030129_a and release_20030129_u released//// Revision 1.24.2.3  2003/01/28 23:28:26  klaus// reorderd pcan_usb.c and pcan_usb_kernel.c, tidied up//// Revision 1.24.2.2  2003/01/20 21:39:26  klaus// timestamp problems solved, remove crashed solved, 1st usable release////****************************************************************************

//****************************************************************************
// INCLUDES
#include <src/pcan_common.h>
#include <linux/sched.h>
#include <asm/errno.h>
#include <asm/byteorder.h>  // because of little / big endian
#include <linux/delay.h>
#include <src/pcan_main.h>
#include <pcan.h>
#include <src/pcan_fifo.h>

//****************************************************************************
// DEFINES

// sja1000 registers, only PELICAN mode - TUX like it
#define MODE                   0      // mode register
#define COMMAND                1
#define CHIPSTATUS             2
#define INTERRUPT_STATUS       3
#define INTERRUPT_ENABLE       4      // acceptance code
#define TIMING0                6      // bus timing 0
#define TIMING1                7      // bus timing 1
#define OUTPUT_CONTROL         8      // output control
#define TESTREG                9

#define ARBIT_LOST_CAPTURE    11      // transmit buffer: Identifier
#define ERROR_CODE_CAPTURE    12      // RTR bit und data length code
#define ERROR_WARNING_LIMIT   13      // start byte of data field
#define RX_ERROR_COUNTER      14
#define TX_ERROR_COUNTER      15

#define ACCEPTANCE_CODE_BASE  16
#define RECEIVE_FRAME_BASE    16
#define TRANSMIT_FRAME_BASE   16

#define ACCEPTANCE_MASK_BASE  20

#define RECEIVE_MSG_COUNTER   29
#define RECEIVE_START_ADDRESS 30

#define CLKDIVIDER            31      // set bit rate and pelican mode

// important sja1000 register contents, MODE register
#define SLEEP_MODE             0x10
#define ACCEPT_FILTER_MODE     0x08
#define SELF_TEST_MODE         0x04
#define LISTEN_ONLY_MODE       0x02
#define RESET_MODE             0x01
#define NORMAL_MODE            0x00

// COMMAND register
#define CLEAR_DATA_OVERRUN     0x08
#define RELEASE_RECEIVE_BUFFER 0x04
#define ABORT_TRANSMISSION     0x02
#define TRANSMISSION_REQUEST   0x01

// CHIPSTATUS register
#define BUS_STATUS             0x80
#define ERROR_STATUS           0x40
#define TRANSMIT_STATUS        0x20
#define RECEIVE_STATUS         0x10
#define TRANS_COMPLETE_STATUS  0x08
#define TRANS_BUFFER_STATUS    0x04
#define DATA_OVERRUN_STATUS    0x02
#define RECEIVE_BUFFER_STATUS  0x01

// INTERRUPT STATUS register
#define BUS_ERROR_INTERRUPT    0x80
#define ARBIT_LOST_INTERRUPT   0x40
#define ERROR_PASSIV_INTERRUPT 0x20
#define WAKE_UP_INTERRUPT      0x10
#define DATA_OVERRUN_INTERRUPT 0x08
#define ERROR_WARN_INTERRUPT   0x04
#define TRANSMIT_INTERRUPT     0x02
#define RECEIVE_INTERRUPT      0x01

// INTERRUPT ENABLE register
#define BUS_ERROR_INTERRUPT_ENABLE    0x80
#define ARBIT_LOST_INTERRUPT_ENABLE   0x40
#define ERROR_PASSIV_INTERRUPT_ENABLE 0x20
#define WAKE_UP_INTERRUPT_ENABLE      0x10
#define DATA_OVERRUN_INTERRUPT_ENABLE 0x08
#define ERROR_WARN_INTERRUPT_ENABLE   0x04
#define TRANSMIT_INTERRUPT_ENABLE     0x02
#define RECEIVE_INTERRUPT_ENABLE      0x01

// OUTPUT CONTROL register
#define OUTPUT_CONTROL_TRANSISTOR_P1  0x80
#define OUTPUT_CONTROL_TRANSISTOR_N1  0x40
#define OUTPUT_CONTROL_POLARITY_1     0x20
#define OUTPUT_CONTROL_TRANSISTOR_P0  0x10
#define OUTPUT_CONTROL_TRANSISTOR_N0  0x08
#define OUTPUT_CONTROL_POLARITY_0     0x04
#define OUTPUT_CONTROL_MODE_1         0x02
#define OUTPUT_CONTROL_MODE_0         0x01

// TRANSMIT or RECEIVE BUFFER
#define BUFFER_EFF                    0x80 // set for 29 bit identifier
#define BUFFER_RTR                    0x40 // set for RTR request
#define BUFFER_DLC_MASK               0x0f

// CLKDIVIDER register
#define CAN_MODE                      0x80
#define CAN_BYPASS                    0x40
#define RXINT_OUTPUT_ENABLE           0x20
#define CLOCK_OFF                     0x08
#define CLOCK_DIVIDER_MASK            0x07

// additional informations
#define CLOCK_HZ                  16000000 // crystal frequency

// time for mode register to change mode
#define MODE_REGISTER_SWITCH_TIME 100 // msec 

// some CLKDIVIDER register contents, hardware architecture dependend 
#define PELICAN_SINGLE  (CAN_MODE | CAN_BYPASS | 0x07 | CLOCK_OFF)
#define PELICAN_MASTER  (CAN_MODE | CAN_BYPASS | 0x07            )
#define PELICAN_DEFAULT (CAN_MODE                                )
#define CHIP_RESET      PELICAN_SINGLE 

// hardware depended setup for OUTPUT_CONTROL register
#define OUTPUT_CONTROL_SETUP (OUTPUT_CONTROL_TRANSISTOR_P0 | OUTPUT_CONTROL_TRANSISTOR_N0 | OUTPUT_CONTROL_MODE_1)

// the interrupt enables
#define INTERRUPT_ENABLE_SETUP (RECEIVE_INTERRUPT_ENABLE | TRANSMIT_INTERRUPT_ENABLE | DATA_OVERRUN_INTERRUPT_ENABLE | BUS_ERROR_INTERRUPT_ENABLE | ERROR_PASSIV_INTERRUPT_ENABLE)

// the maximum number of handled messages in one interrupt 
#define MAX_MESSAGES_PER_INTERRUPT 8

// the maximum number of handled sja1000 interrupts in 1 handler entry
#define MAX_INTERRUPTS_PER_ENTRY   4

// constants from Arnaud Westenberg email:arnaud@wanadoo.nl
#define MAX_TSEG1  15
#define MAX_TSEG2  7
#define BTR1_SAM   (1<<1)

// a helper for fast conversion between SJA1000 data ordering and host data order
typedef union
{
  u8  ucID[4];
  u32 dwID;
} ID;

//****************************************************************************
// GLOBALS

//****************************************************************************
// LOCALS

//****************************************************************************
// CODE  

//----------------------------------------------------------------------------
// switches the chip into reset mode
static int set_reset_mode(struct pcandev *dev)
{
  u32 dwStart = get_mtime();
  u8  tmp;
  
  tmp = dev->readreg(dev, MODE);
  while (!(tmp & RESET_MODE) && ((get_mtime() - dwStart) < MODE_REGISTER_SWITCH_TIME)) 
  {
    dev->writereg(dev, MODE, RESET_MODE); // force into reset mode
    wmb();
    schedule();
    tmp = dev->readreg(dev, MODE);
  }   
  
  if (!(tmp & RESET_MODE))
    return -EIO;  
  else
    return 0;  
} 

//----------------------------------------------------------------------------
// switches the chip back from reset mode
static int set_normal_mode(struct pcandev *dev, u8 ucModifier)
{
  u32 dwStart = get_mtime();
  u8  tmp;
  
  tmp = dev->readreg(dev, MODE);
  while ((tmp != ucModifier) && ((get_mtime() - dwStart) < MODE_REGISTER_SWITCH_TIME))
  {
    dev->writereg(dev, MODE, ucModifier); // force into normal mode
    wmb();
    schedule();
    tmp = dev->readreg(dev, MODE);
  }   
  
  if (tmp != ucModifier)
    return -EIO;  
  else
    return 0;  
} 

//----------------------------------------------------------------------------
// init CAN-chip
int sja1000_open(struct pcandev *dev, u16 btr0btr1, u8 bExtended, u8 bListenOnly)
{
  int result     = 0;
  u8  clkdivider = PELICAN_DEFAULT;
  u8  ucModifier = (bListenOnly) ? LISTEN_ONLY_MODE : NORMAL_MODE;
  
  DPRINTK(KERN_DEBUG "%s: sja1000_open()\n", DEVICE_NAME);
  
  // switch to reset 
  result = set_reset_mode(dev);
  if (result)
    goto fail;    
    
  // take a fresh status
  dev->wCANStatus = 0;
  
  // store extended mode (standard still accepted)
  dev->bExtended = bExtended;
    
  // configure clock divider register, switch into pelican mode, depended of of type
  if (dev->wType == HW_PCI)
    clkdivider = (dev->port.pci.ucMasterDevice == CHANNEL_MASTER) ? PELICAN_MASTER : PELICAN_SINGLE;
  
  dev->writereg(dev, CLKDIVIDER, clkdivider);
  
  // configure acceptance code registers
  dev->writereg(dev, ACCEPTANCE_CODE_BASE,     0);
  dev->writereg(dev, ACCEPTANCE_CODE_BASE + 1, 0);
  dev->writereg(dev, ACCEPTANCE_CODE_BASE + 2, 0);
  dev->writereg(dev, ACCEPTANCE_CODE_BASE + 3, 0);
  
  // configure all acceptance mask registers to don't care 
  dev->writereg(dev, ACCEPTANCE_MASK_BASE,     0xff);
  dev->writereg(dev, ACCEPTANCE_MASK_BASE + 1, 0xff);
  dev->writereg(dev, ACCEPTANCE_MASK_BASE + 2, 0xff);
  dev->writereg(dev, ACCEPTANCE_MASK_BASE + 3, 0xff);
  
  // configure bus timing registers
  dev->writereg(dev, TIMING0, (u8)((btr0btr1 >> 8) & 0xff));
  dev->writereg(dev, TIMING1, (u8)((btr0btr1     ) & 0xff));  
  
  // configure output control registers
  dev->writereg(dev, OUTPUT_CONTROL, OUTPUT_CONTROL_SETUP);
  
  // clear any pending interrupt
  dev->readreg(dev, INTERRUPT_STATUS);
  
  // enter normal operating mode
  result = set_normal_mode(dev, ucModifier);
  if (result)
    goto fail;    
  
  // enable CAN interrupts
  dev->writereg(dev, INTERRUPT_ENABLE, INTERRUPT_ENABLE_SETUP);
  
  fail:
  return result;
}

//----------------------------------------------------------------------------
// release CAN-chip
void sja1000_release(struct pcandev *dev) 
{
  DPRINTK(KERN_DEBUG "%s: sja1000_release()\n", DEVICE_NAME);
  
  // abort pending transmissions
  dev->writereg(dev, COMMAND, ABORT_TRANSMISSION);
    
  // disable CAN interrupts and set chip in reset mode
  dev->writereg(dev, INTERRUPT_ENABLE, 0);
  set_reset_mode(dev);
}

//----------------------------------------------------------------------------
// read CAN-data from chip, supposed a message is available
static int sja1000_read(struct pcandev *dev)
{
  int i = MAX_MESSAGES_PER_INTERRUPT;
  int	result   = 0;
  TPCANRdMsg   *m;
  u8           rxfi;  // receive frame information byte
  u8           ucLen;
  ID           local;
  int          j;

  // DPRINTK(KERN_DEBUG "%s: sja1000_read() %d\n", DEVICE_NAME, dev->readFifo.nStored);
  
  do
  {
    // aquire a fifo element
    result = pcan_fifo_claim_for_put(&dev->readFifo, (void *)&m);
    if (result)
      goto fail;
        
    // fix time of reading   
    m->dwTime  = get_mtime() - *dev->pdwInitTime;
    
    rxfi       = dev->readreg(dev, RECEIVE_FRAME_BASE);
    
    // filter out extended messages in non extended mode
    if (dev->bExtended || !(rxfi & BUFFER_EFF))
    { 
      m->Msg.LEN = ucLen = rxfi & BUFFER_DLC_MASK;    
      if (ucLen > 8)
        ucLen = 8;
  
      j = 0;
      if (rxfi & BUFFER_EFF)
      {
        m->Msg.MSGTYPE = MSGTYPE_EXTENDED;
  
        #ifdef __LITTLE_ENDIAN
        local.ucID[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
        local.ucID[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
        local.ucID[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 3);
        local.ucID[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 4);
        #else
        local.ucID[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
        local.ucID[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
        local.ucID[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 3);
        local.ucID[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 4);
        #endif
  
        local.dwID   >>= 3;

        while (ucLen--)
        {
          m->Msg.DATA[j] = dev->readreg(dev, RECEIVE_FRAME_BASE + 5 + j);
	        j++;
	      }
      }
      else
      {
        m->Msg.MSGTYPE = MSGTYPE_STANDARD;

        local.dwID    = 0;
  
        #ifdef __LITTLE_ENDIAN
        local.ucID[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
        local.ucID[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
        #else
        local.ucID[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
        local.ucID[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
        #endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -