2014年4月1日 星期二

Control two MCP2515 chips

I modified the source code of MCP2515.ccp
(http://code.google.com/p/netduino-mcp2515/downloads/list)
to meet my project's requirement.
 It support two CAN bus protocol chips and control by one SPI interface.

//-------------------------------------------------------------------------------------------
 using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace System
{
    /// <summary>
    /// Represents a class that handles the CAN transceiver MCP2515.
    /// </summary>
    public class MCP2515
    {
        //-------------------------------------
        #region REGISTERS
        //-------------------------------------
        private const byte RXF0SIDH = 0x00;
        private const byte RXF0SIDL = 0x01;
        private const byte RXF0EID8 = 0x02;
        private const byte RXF0EID0 = 0x03;
        private const byte RXF1SIDH = 0x04;
        private const byte RXF1SIDL = 0x05;
        private const byte RXF1EID8 = 0x06;
        private const byte RXF1EID0 = 0x07;
        private const byte RXF2SIDH = 0x08;
        private const byte RXF2SIDL = 0x09;
        private const byte RXF2EID8 = 0x0A;
        private const byte RXF2EID0 = 0x0B;
        private const byte BFPCTRL = 0x0C;
        private const byte TXRTSCTRL = 0x0D;
        private const byte CANSTAT = 0x0E;
        private const byte CANCTRL = 0x0F;
        private const byte RXF3SIDH = 0x10;
        private const byte RXF3SIDL = 0x11;
        private const byte RXF3EID8 = 0x12;
        private const byte RXF3EID0 = 0x13;
        private const byte RXF4SIDH = 0x14;
        private const byte RXF4SIDL = 0x15;
        private const byte RXF4EID8 = 0x16;
        private const byte RXF4EID0 = 0x17;
        private const byte RXF5SIDH = 0x18;
        private const byte RXF5SIDL = 0x19;
        private const byte RXF5EID8 = 0x1A;
        private const byte RXF5EID0 = 0x1B;
        private const byte TEC = 0x1C;
        private const byte REC = 0x1D;
        private const byte RXM0SIDH = 0x20;
        private const byte RXM0SIDL = 0x21;
        private const byte RXM0EID8 = 0x22;
        private const byte RXM0EID0 = 0x23;
        private const byte RXM1SIDH = 0x24;
        private const byte RXM1SIDL = 0x25;
        private const byte RXM1EID8 = 0x26;
        private const byte RXM1EID0 = 0x27;
        private const byte CNF3 = 0x28;
        private const byte CNF2 = 0x29;
        private const byte CNF1 = 0x2A;
        private const byte CANINTE = 0x2B;
        private const byte MERRE = 7;
        private const byte WAKIE = 6;
        private const byte ERRIE = 5;
        private const byte TX2IE = 4;
        private const byte TX1IE = 3;
        private const byte TX0IE = 2;
        private const byte RX1IE = 1;
        private const byte RX0IE = 0;
        private const byte CANINTF = 0x2C;
        private const byte MERRF = 7;
        private const byte WAKIF = 6;
        private const byte ERRIF = 5;
        private const byte TX2IF = 4;
        private const byte TX1IF = 3;
        private const byte TX0IF = 2;
        private const byte TX0IF_MASK = 0x04;
        private const byte RX1IF = 1;
        private const byte RX0IF = 0;
        private const byte RX1IF_MASK = 0x02;
        private const byte RX0IF_MASK = 0x01;
        private const byte EFLG = 0x2D;
        private const byte TXB0CTRL = 0x30;
        private const byte TXREQ = 3;
        private const byte TXB0SIDH = 0x31;
        private const byte TXB0SIDL = 0x32;
        private const byte EXIDE = 3;
        private const byte EXIDE_MASK = 0x08;
        private const byte TXB0EID8 = 0x33;
        private const byte TXB0EID0 = 0x34;
        private const byte TXB0DLC = 0x35;
        private const byte TXRTR = 7;
        private const byte TXB0D0 = 0x36;
        private const byte RXB0CTRL = 0x60;
        private const byte RXM1 = 6;
        private const byte RXM0 = 5;
        private const byte RXRTR = 3;
        // Bits 2:0 FILHIT2:0
        private const byte RXB0SIDH = 0x61;
        private const byte RXB0SIDL = 0x62;
        private const byte RXB0EID8 = 0x63;
        private const byte RXB0EID0 = 0x64;
        private const byte RXB0DLC = 0x65;
        private const byte RXB0D0 = 0x66;
        //-------------------------------------
        #endregion REGISTERS
        //-------------------------------------
       
        //MCP2515 Command Bytes
        private readonly byte RESET = 0xC0;
        private readonly byte READ = 0x03;
       // private readonly byte READ_RX_BUFFER = 0x90;
        private readonly byte WRITE = 0x02;
       // private readonly byte LOAD_TX_BUFFER = 0x40;
       // private readonly byte RTS = 0x80;
       // private readonly byte READ_STATUS = 0xA0;
       // private readonly byte RX_STATUS = 0xB0;
        private readonly byte BIT_MODIFY = 0x05;
        /// <summary>Represent a LOW  (false) state.</summary>
        private const bool LOW = false;
        /// <summary>Represent a HIGH (true) state.</summary>
        private const bool HIGH = true;
        /// <summary></summary>
        private const byte DataIndexOffset = 2;
        /// <summary>The max CAN ID that can be represented on an 11 bit ID.</summary>
        private const int CANID_11BITS = 0x7FF;
        //-------------------------------------------------------------
        //// This must be modified for your CAN ID
        private const uint VMS_CAN0_ID = 0x7E8, VMS_CAN1_ID = 0x329;
        //-------------------------------------------------------------
        // This must be modified depend on hardware
        /// <summary>SPI PIN for VMS control.</summary>
        public const  Cpu.Pin SPI_CAN1_SEL = (Cpu.Pin)45;    // PC13
        public const  Cpu.Pin SPI_CAN0_SEL = (Cpu.Pin)1;     // PA1
        /// <summary>the SPI object that communicate with the transceiver.</summary>
        private SPI spi;
         // Configure SPI  CAN channel0 and channel1           
        private  SPI.Configuration [] configSPI = new SPI.Configuration[2];
       
    
        /// <summary>
        /// Represents the available baud rates.
        /// </summary>
        public enum enBaudRate
        {
            CAN_BAUD_10K = 1,  CAN_BAUD_50K = 2, CAN_BAUD_100K = 3,
            CAN_BAUD_125K = 4, CAN_BAUD_250K = 5, CAN_BAUD_500K = 6
        }
        /// <summary>Represents a CAN message.</summary>
        public class CANMSG
        {
            public uint CANID { get; set; }
            public bool IsExtended { get { return CANID > CANID_11BITS; } }
            public bool IsRemote { get; set; }
            public int DataLength { get { return data.Length; } }
            public byte[] data = new byte[8];
        } 
        /// <summary>Initialize the CAN transceiver.</summary>
        /// <param name="baudrate">The selected baud rate.</param>
        /// <returns>True if configuration was successful.</returns>
        /// <remarks>Transceiver needs to be set to normal mode before starting TX/RX operations.</remarks>
        public bool InitCAN(enBaudRate baudrate)
        {
           configSPI[0] = new SPI.Configuration(SPI_CAN0_SEL, LOW, 0, 0, HIGH, HIGH, 10000, SPI.SPI_module.SPI1);
           configSPI[1] = new SPI.Configuration(SPI_CAN1_SEL, LOW, 0, 0, HIGH, HIGH, 10000, SPI.SPI_module.SPI1); 
       
            spi = new SPI(configSPI[0]);
            // Write reset to the CAN0/CAN1 transceiver.
            spi.Write(new byte[] {RESET});
            spi.Config = configSPI[1];
            spi.Write(new byte[] {RESET});
            //Read mode and make sure it is config
            Thread.Sleep(100);
          
            byte mode0 = (byte)(ReadRegister(0,CANSTAT) >> 5);
            byte mode1 = (byte)(ReadRegister(1,CANSTAT) >> 5);
            if ((mode0 != 0x04) || (mode1 != 0x04))
            {
                return false;
            }
            else // ------ Config mode -------------------
            {
                VMS_FilterInit();
                SetCANBaud(baudrate); // will set to normal
                SetCANNormalMode();
                VMS_CAN0IdFilter();
                //--------------- normal mode -----------------
               
                return true;
            }
        }
     
        /// <summary>Set the CAN baud rate.</summary>
        /// <param name="baudrate">The configured baud rate.</param>
        /// <returns>True if configured.</returns>
        private bool SetCANBaud(enBaudRate baudrate)
        {
            byte brp;
 
            //BRP<5:0> = 00h, so divisor (0+1)*2 for 125ns per quantum at 16MHz for 500K  
            //SJW<1:0> = 00h, Sync jump width = 1
            switch (baudrate)
            {
                case enBaudRate.CAN_BAUD_10K:
                    brp = 5;
                    break;
                case enBaudRate.CAN_BAUD_50K:
                    brp = 4;
                    break;
                case enBaudRate.CAN_BAUD_100K:
                    brp = 3;
                    break;
                case enBaudRate.CAN_BAUD_125K:
                    brp = 2;
                    break;
                case enBaudRate.CAN_BAUD_250K:
                    brp = 1;
                    break;
                case enBaudRate.CAN_BAUD_500K:
                    brp = 0;
                    break;
                default:
                    return false;
                  
            }
          
            byte[] cmdBuffer = new byte[] { WRITE, CNF1, (byte)(brp & 0x3F) };
            spi.Config = configSPI[0];
            spi.Write(cmdBuffer);
            //-------------------------------------------------
            //PRSEG<2:0> = 0x01, 2 time quantum for prop
            //PHSEG<2:0> = 0x06, 7 time constants to PS1 sample
            //SAM = 0, just 1 sampling
            //BTLMODE = 1, PS2 determined by CNF3
          
            spi.Write(new byte[] { WRITE, CNF2, 0xB1 });
            //-----------------------------------------------
            //PHSEG2<2:0> = 5 for 6 time constants after sample
            //SOF enable <bit7>
            spi.Write(new byte[] { WRITE, CNF3, 0x85 });
            spi.Config = configSPI[1];
            spi.Write(cmdBuffer);
            spi.Write(new byte[] { WRITE, CNF2, 0xB1 });
            spi.Write(new byte[] { WRITE, CNF3, 0x85 });
            //SyncSeg + PropSeg + PS1 + PS2 = 1 + 2 + 7 + 6 = 16
            return true;
        }
        /// <summary>Writes a value to the selected register.</summary>
        /// <param name="registerAddress">The address of the register.</param>
        /// <param name="value">The value to be write to the register.</param>
        private void WriteRegister(uint channel ,byte registerAddress, byte value)
        {
            spi.Config = configSPI[channel];
            spi.Write(new byte[] { WRITE, registerAddress, value});
          
          
        }
        /// <summary>Reads the value of the selected register.</summary>
        /// <param name="registerAddress">The address of the register.</param>
        /// <returns>A byte with the value read from the register.</returns>
        private byte ReadRegister(uint channel,byte registerAddress)
        {
            byte[] CmdBuffer = new byte[] { READ, registerAddress };
            byte[] outData = new byte[1];
            spi.Config = configSPI[channel];
            spi.WriteRead(CmdBuffer, outData, 2);
         
            return outData[0];
        }
        /// <summary>Writes a bit to a register.</summary>
        /// <param name="registerAddress">The address of the register that supports bit writing.</param>
        /// <param name="bitNumber">The zero index based of the bit to write.</param>
        /// <param name="value">the value of the bit to write.</param>
        private void WriteRegisterBit(uint channel,byte registerAddress, byte bitNumber, byte value)
        {
            spi.Config = configSPI[channel];
            //spi.Write(new byte[] { BIT_MODIFY, regno, (byte)(1 << bitno) });
            if (value != 0)
            {
            
                spi.Write(new byte[] { BIT_MODIFY, registerAddress, (byte)(1 << bitNumber), 0xFF });
             
            }
            else
            {
              
                    spi.Write(new byte[] { BIT_MODIFY, registerAddress, (byte)(1 << bitNumber), 0x00 });
            }
        }
        /// <summary>Transmit a CAN message to the bus.</summary>
        /// <param name="message">The CAN Message to be transmitted.</param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public bool Transmit(uint channel ,CANMSG message, int timeout)
        {
            // Holds if message was sent or not.
            bool sentMessage = false;
            // Calculate the end time based on current device time.
           TimeSpan startTime = Utility.GetMachineTime();
           TimeSpan endTime = startTime.Add(new TimeSpan(0,0,0,0,timeout));
            //--------------------------------------
            // Set the CAN ID.
            byte val = 0x00;
            uint bit11Address = 0x00;
            uint bit18Address = 0x00;
           
            // Build Extended CAN ID
            if (message.IsExtended)
            {
                // Split the 11 bit and 18 bit sections of the ID.
                bit11Address = (uint)((message.CANID >> 16) & 0xFFFF);
                bit18Address = (uint)(message.CANID & 0xFFFFF);
                // Set the first part of the ID
                val = (byte)(bit11Address >> 5);
                WriteRegister(channel,TXB0SIDH, val);
                // Set the second part of the ID.
                val = (byte)(bit11Address << 3);
                val |= (byte)(bit11Address & 0x07);
                // Mark the message as extended.
                val |= 1 << EXIDE;
                WriteRegister(channel, TXB0SIDL, val);
                // Write the 18 bit part of the ID
                val = (byte)(bit18Address >> 8);
                WriteRegister(channel, TXB0EID8, val);
                val = (byte)(bit18Address);
                WriteRegister(channel, TXB0EID0, val);
            }
            else
            {
                // Transmit a 11 bit ID.
                bit11Address = (uint)(message.CANID);
                val = (byte)(bit11Address >> 3);
                WriteRegister(channel, TXB0SIDH, val);
                val = (byte)(bit11Address << 5);
                WriteRegister(channel, TXB0SIDL, val);
            }

            //--------------------------------------
            val = (byte)(message.DataLength & 0x0f);
            // Check if is a remote frame
            if (message.IsRemote)
            {
                // Mark the frame as remote
                val |= (byte)(1UL << (TXRTR));
                WriteRegisterBit(channel, val, TXRTR, 1);
            }
            WriteRegister(channel, TXB0DLC, val);
            //--------------------------------------
            //Write Message Data
            byte[] txDATA = new byte[10];
            txDATA[0] = WRITE;
            txDATA[1] = TXB0D0;
            for (int i = DataIndexOffset; i < message.DataLength + DataIndexOffset; i++)
            {
                txDATA[i] = message.data[i - DataIndexOffset];
            }
            spi.Config = configSPI[channel];
            spi.Write(txDATA);
            //----------------------
            // Command to transmit the CAN message
            WriteRegisterBit(channel,TXB0CTRL, TXREQ, 1);
           
            //----------------------
            // Wait until time out to get confirmation of message was sent.
           while (Utility.GetMachineTime() < endTime)
           {
                val = ReadRegister(channel, CANINTF);           
                if((val & TX0IF_MASK ) == TX0IF_MASK )
                {
                    sentMessage = true;
                 break;
                }
            }
            ////Abort the send if failed
            WriteRegisterBit(channel, TXB0CTRL, TXREQ, 0);
 
            ////And clear write interrupt
            WriteRegisterBit(channel, CANINTF, TX0IF, 0);
            return sentMessage;
        }
        /// <summary>
        /// Check if a new message was received by the transceiver.
        /// </summary>
        /// <param name="msg">The CAN message that will contain the retrived message.</param>
        /// <param name="timeout">The time to wait if a message become available.</param>
        /// <returns>True if a message was received.</returns>
        public bool Receive(uint channel,out CANMSG msg, int timeout)
        {
            bool gotMessage = false;
            uint val;
            msg = new CANMSG();
           TimeSpan startTime = Utility.GetMachineTime();
           TimeSpan endTime = startTime.Add(new TimeSpan(0, 0, 0, 0, timeout));
            gotMessage = false;
            while (Utility.GetMachineTime() < endTime)
            {
                val = ReadRegister(channel, CANINTF);
                //If we have a message available, read it
                if ((val & RX0IF_MASK) == RX0IF_MASK)
                {
                    gotMessage = true;
                   break;
                }
            }
            if (gotMessage)
            {
                val = ReadRegister(channel, RXB0CTRL);
                msg.IsRemote = ((val & 0x04) == 0x04) ? true : false;
                //Address received from
                uint adddresVal = 0;
                val = ReadRegister(channel, RXB0SIDH);
                adddresVal |= (val << 3);
                val = ReadRegister(channel, RXB0SIDL);
                adddresVal |= (val >> 5);
                bool isExtended = ((val & EXIDE_MASK) == EXIDE_MASK) ? true : false;
                uint adddresExtVal = 0;
                if (isExtended)
                {
                    adddresExtVal = (uint)((val & 0x03) << 16);
                    val = ReadRegister(channel, RXB0EID8);
                    adddresExtVal |= (uint)(val << 8);
                    val = ReadRegister(channel, RXB0EID0);
                    adddresExtVal |= val;
                    adddresVal = ((adddresVal << 18) | adddresExtVal);
                }
                msg.CANID = (uint)adddresVal;
                //Read data bytes
                val = ReadRegister(channel,RXB0DLC);
                uint dataLen = (val & 0xf);
                byte[] CmdBuffer = new byte[] { READ, RXB0D0 };
                msg.data = new byte[dataLen];
                spi.Config = configSPI[channel];
                spi.WriteRead(CmdBuffer, msg.data, 2);
             
                //And clear read interrupt
                WriteRegisterBit(channel,CANINTF, RX0IF, 0);
            }
            else
            {
            }
            return gotMessage;
        }

        /// <summary>Set transceiver to normal state.</summary>
        /// <remarks>This state needs to be selected before starting TX/RX.</remarks>
        public void SetCANNormalMode()
        {
            //REQOP2<2:0> = 000 for normal mode
            //ABAT = 0, do not abort pending transmission
            //OSM = 0, not one shot
            //CLKEN = 1, disable output clock
            //CLKPRE = 0b11, clk/8
             WriteRegister(0,CANCTRL,0x07);
             WriteRegister(1,CANCTRL,0x07);
           
            //Read mode and make sure it is normal
            byte mode = ReadRegister(0,CANSTAT);
            ReadRegister(1, CANSTAT);
            mode = (byte)(mode >> 5);
            if (mode != 0)
            { }
            // Set RX buffer control to turn filters OFF and receive any message.      
            WriteRegister(0,RXB0CTRL, 0x60);
            WriteRegister(1,RXB0CTRL, 0x60);
        }
        //--------------------------------------------------------------
        //                      VMS_FilterInit()
        //purpose : write the filter to CAN0'Receiver buf0 and buf1
        //          to receive the VMS_CAN0_ID and VMS_CAN1_ID
        //-------------------------------------------------------------
        private void VMS_FilterInit()
        {
            WriteRegister(0, RXF0SIDH, (byte)(VMS_CAN0_ID >> 3));
            WriteRegister(0, RXF0SIDL , (byte)((VMS_CAN0_ID << 5)&0xe0));
            WriteRegister(0, RXF1SIDH, (byte)(VMS_CAN1_ID >> 3));
            WriteRegister(0, RXF1SIDL, (byte)((VMS_CAN1_ID << 5) & 0xe0));
            WriteRegister(0, RXM0SIDH, 0xff); // config the MASK
            WriteRegister(0, RXM0SIDL, 0xff);
        }
      
        //---------------------------------------------
        public void VMS_CAN0IdFilter()
        {
            WriteRegister(0, RXB0CTRL, 0x0); // receive the standard/ext ID with RXF0 filter 
        }
        //---------------------------------------------
        public void VMS_CAN1IdFilter()
        {
            WriteRegister(0, RXB0CTRL, 0x1); // receive the standard/ext ID with RXF1 filter 
        }
        //---------------------------------------------
        public void SetConfigMode(uint channel)
        {
            WriteRegister(channel, CANCTRL, 0x87);
        }

    }
}

沒有留言:

張貼留言