Datasheet and register question


Closed Thread
Results 1 to 22 of 22
  1. #1
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425

    Default Datasheet and register question

    This is probably a basic question for most of you but I can't quite figure it out. I have a register that needs to be set, lets say INEEDSLEEP. The addresses are 80h, 90h, 100h. My questions are:

    1. If the data sheet says it's bits <3:0>, why are there only three addresses when it covers four bits?

    2. Do you put the exact same values into all of the addresses?

    When I've used more basic registers, I just use something like INEEDSLEEP = %00010100. Maybe I just never noticed it but I don't remember ever having to put values into three different addresses for one value.

    I'll start with this and see if someone can get it through my thick skull.

  2. #2
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi Christopher,
    It would be nice if you linked to the datasheet you're looking at but due to your other currently 'active' threads I suspect it's the MCP2515 we're talking about? If not then this answer may not apply.

    1) There are three INEEDSLEEP registers, one at adress $80, one at $90 and one at $100. Judging by the other register names in the MCP2515 the description of the register in the datasheet would likely be something like INEEDSLEEPn where n is the number of the register. 0,1,2 or perhaps 1,2,3. There are 8bits en each register but you're interested in the lower 4bits <3:0> of each register.

    2) It depends on what INEEDSLEEP does and what YOU want to do really. Since I don't know CAN or the MCP2515 (if that's what we're really talking about here) I'm going to try a stupid example. If the chip wasn't a CAN driver but a say PWM chip with three output channels. Then you'd have three dutycycle registers in the chip. The functionality and operation of the registers would be exactly equal so instead of describing it three times in the datasheet the might show it DUTYn (at adresses $10, $20, $30 or whatever) where n is either 0,1 or 2 for the three different channels.

    Now, if you wanted to set the dutycycle of all three channels to 0 would you write 0 to all three registers? YES of course. If you then wanted to set the dutycycle of channel 1 to 50 would you write 50 to all three registers? NO of course not, you'd write 50 to DUTY1, which is at address $20 in the external chip. If it was in the PIC then the actual address would be of less interest because the compiler would include a file (think of it like sort of a phonebook) with register names and their addresses, so you could access it directly by its name (DUTY1).

    As I said, a stupid example but it's the best I can do without reference to the actual part and actual register. I hope it makes some sense though.

    /Henrik.

  3. #3
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Min forre flickvan (mycket vakra) var Svensk och jag prata lite Svenska! It's been years but I still remember a little, although there may be spelling mistakes

    Anyhow, I started this thread for the datasheet question because it was more of a generic question but yes, I am asking about the MCP2515. Here are the two things I can't understand:

    1. Page 21. For TXBnDm it gives a number of address values and they are looking for eight different numbers. I've found that the .asm files and the C files provided help out a little bit but do I need to put each of the TXBnDm numbers (0-7) in a different address or do I just assign a value to them in the code like TXB1Dm = $FF or is it written TXBnDm7 = $FF?

    2. I'm learning about the bit modify instruction but it isn't completely clear. I don't understand how to code the higher and lower order address bits. As an example, on page 61 I am having trouble understanding how to program TXBnSIDL and TXBnSIDH.

    Thanks,

    Chris

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hej Christopher,
    Din svenska är helt OK!

    1) The MCP2515 has three transmit buffers (ie it's to these buffers you write the data to be sent out on the CAN bus). Each of these transmit buffers consists of 8 bytes. The first transmit buffer starts at $36 and ends at $3D. So the first byte of the first buffer is at address $36 and would be called TXB0D0 (Transmit Buffer 0 DataByte 0). The first byte of the second buffer would be at address $46 and would be called TXB1D0 (Transmit Buffer 1 DataByte 0) and so on.

    You can't simply do TXB0D0 = $FF because, once again, the register is not in the PIC, you'll have to write to that register using either SHIFTOUT or the MSSP module like we discussed in one of the other threads. Ie, to load TXB0D0 with $FF you'd do
    Code:
    SHIFTOUT DataPin, ClkPin, MSBFirst, [2, $36, $FF]   ' 2=Write Instruction, $36=adress of TXB0D0 regsiter, $FF=Data to be written to that register
    Now, you can obviously create CONstants to the various registers and commands, like
    Code:
    WriteCmd CON 2
    TXB0D0 CON $36
    SHIFTOUT DataPin, ClkPin, MSBFirst, [WriteCmd, TXB0D0, $FF]

    2) The register map on page 61 shows the address of all the registers in the device. TXBnSIDL and TXBnSIDH are Transmit Buffer Standard Identifier (whatever that is) register and since there are three Transmit buffers there are three TXNnSIDL and TXBnSIDH registers (again, n would be 0,1 or 2 for the three individual registers "coupled" to each transmit buffer.

    So, looking at the table on page 61, you can see that the four high order bits of the address are in the columns and the four low order bits are in the rows. For TXB0SIDL you'll get 0011 for the high order bits and 0010 for the low order bits. Combing these give you %00110010 which is the same as $32 - which, if you look at the description for the TXBnSIDL register (page 20) matches what they say IS the address of TXB0SIDL.

    So, to write the value $12 to TXB0SIDL you'd do
    Code:
    SHIFTOUT DataPin, ClkPin, MSBFirst, [2, $32, $12]   '2=WriteInstruction, $32= Adress of register, $12=Data to write.
    In the register map all register which are allowed to be manipulated by the BitModify instruction are shaded. The TXBnSIDL registers are not shaded and can not be modified using the BitModify instruction.

    /Henrik.

  5. #5
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Thanks for the help Henrik. I still don't understand it. I've been trying for the past four days and I can't get my variables set up correctly. I've been looking at code on here and other places on the internet and I'm more confused than I was before - most of them are in C. The datasheet seems really confusing to me. I think I've wasted more than 100 hours so far.

  6. #6
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    I have a hammer in one hand and the 18F4550, along with the MCP2515 are on my table. It would really provide a lot of stress relief.

    I'm going to scrap this and just go with an 18F with CAN built in.

  7. #7


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    The 4550 is quite commonly supported... not only that but the datasheets seem (to me) to be written properly...

    I've not played with CAN, however i (along with a lot of other people on here) know the 4550 very well...

  8. #8
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi Christopher,
    This may be moot if you've already decided to switch devices but it still seems to me like if you're missing one crucial point. You said that you can't get your variable set up correctly which variables are you talking about here and how do you set them? I don't mean to which values you set them but how do you actually assign your values - whatever value it might be - TO the variable?

    The reason I ask is because I still think you may be missing the point that in order for the MCP2515 to work you need to set IT up - ie assign values to the regsiters that are IN the MCP2515. The MCP2515 is an intelligent device, it's not like a MAX232 which has nothing to set up. When you say variables it sounds as if you're still trying to assign values to variables that you create in the PIC and that won't work.

    Are you using SHIFTOUT to write the values or do you use the MSSP module?

    If it was me I start by making a routine which write values to the MCP2515. Then I'd make a routine which reads values from the MCP2515 to verify that the write routine is working properly.

    Can you post some code of your current attempts?

    /Henrik.

  9. #9
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Quote Originally Posted by comwarrior
    The 4550 is quite commonly supported... not only that but the datasheets seem (to me) to be written properly...

    I've not played with CAN, however i (along with a lot of other people on here) know the 4550 very well...
    Thank you for your substantive post. It has helped me greatly.

    Are you using SHIFTOUT to write the values or do you use the MSSP module?

    If it was me I start by making a routine which write values to the MCP2515. Then I'd make a routine which reads values from the MCP2515 to verify that the write routine is working properly.

    Can you post some code of your current attempts?
    I'm using the MSSP. I think my commands are getting to the MCP but I'm guessing my problem lies in one of these places:

    CNF1, CNF2, CNF3, SSPCON, SSPSTAT OR BAUDCON. It could be anywhere else too. Basically, the entire program is suspect. Thanks for the help Henrik. Maybe you can spot some issues with what I wrote:

    Code:
    INCLUDE "modedefs.bas"
    '          
    DEFINE OSC 20 
    
    
    '==============================================================================
    '                 SETTINGS
    ' ==============================================================================
               '76543210          '76543210
     TRISA   = %11111111: PORTA = %00000000 
     TRISB   = %00010001: PORTB = %00010000   
     TRISC   = %00000000: PORTC = %00000001
     TRISD   = %11111111: PORTD = %00000000
     TRISE   = %00010000: PORTE = %00010111
     ADCON0  = %00000000
     ADCON1  = %00000000
     ADRESH  = %00000000 
     ADRESL  = %00000000
     CMCON   = %00000000
     SSPSTAT = %11000100      
     SSPCON1 = %10110001 
     BAUDCON = %00001111   
        
    
    '===============================================================================
    '                   Register Mapping                          
    '================================================================================
    
     CANSTAT         CON    $0E
     CANCTRL         CON    $0F
     BFPCTRL         CON    $0C
     TEC             CON    $1C
     REC             CON    $1D
     CNF3            CON    $28
     CNF2            CON    $29
     CNF1            CON    $2A
     CANINTE         CON    $2B
     CANINTF         CON    $2C
     EFLG            CON    $2D
     TXRTSCTRL       CON    $0D
    
    ''  Recieve Filters ''
     RXF0SIDH        CON    $00
     RXF0SIDL        CON    $01
     RXF0EID8        CON    $02
     RXF0EID0        CON    $03
     RXF1SIDH        CON    $04
     RXF1SIDL        CON    $05
     RXF1EID8        CON    $06
     RXF1EID0        CON    $07
     RXF2SIDH        CON    $08
     RXF2SIDL        CON    $09
     RXF2EID8        CON    $0A
     RXF2EID0        CON    $0B
     RXF3SIDH        CON    $10
     RXF3SIDL        CON    $11
     RXF3EID8        CON    $12
     RXF3EID0        CON    $13
     RXF4SIDH        CON    $14
     RXF4SIDL        CON    $15
     RXF4EID8        CON    $16
     RXF4EID0        CON    $17
     RXF5SIDH        CON    $18
     RXF5SIDL        CON    $19
     RXF5EID8        CON    $1A
     RXF5EID0        CON    $1B
    
    '' Receive Masks ''
     RXM0SIDH        CON    $20
     RXM0SIDL        CON    $21
     RXM0EID8        CON    $22
     RXM0EID0        CON    $23
     RXM1SIDH        CON    $24
     RXM1SIDL        CON    $25
     RXM1EID8        CON    $26
     RXM1EID0        CON    $27
    
    '' Tx Buffer 0 ''
     TXB0CTRL        CON    $30
     TXB0SIDH        CON    $31
     TXB0SIDL        CON    $32
     TXB0EID8        CON    $33
     TXB0EID0        CON    $34
     TXB0DLC         CON    $35
     TXB0D0          CON    $36
     TXB0D1          CON    $37
     TXB0D2          CON    $38
     TXB0D3          CON    $39
     TXB0D4          CON    $3A
     TXB0D5          CON    $3B
     TXB0D6          CON    $3C
     TXB0D7          CON    $3D
                             
    '' Tx Buffer 1 ''
     TXB1CTRL        CON    $40
     TXB1SIDH        CON    $41
     TXB1SIDL        CON    $42
     TXB1EID8        CON    $43
     TXB1EID0        CON    $44
     TXB1DLC         CON    $45
     TXB1D0          CON    $46
     TXB1D1          CON    $47
     TXB1D2          CON    $48
     TXB1D3          CON    $49
     TXB1D4          CON    $4A
     TXB1D5          CON    $4B
     TXB1D6          CON    $4C
     TXB1D7          CON    $4D
    
    '' Tx Buffer 2 ''
     TXB2CTRL        CON    $50
     TXB2SIDH        CON    $51
     TXB2SIDL        CON    $52
     TXB2EID8        CON    $53
     TXB2EID0        CON    $54
     TXB2DLC         CON    $55
     TXB2D0          CON    $56
     TXB2D1          CON    $57
     TXB2D2          CON    $58
     TXB2D3          CON    $59
     TXB2D4          CON    $5A
     TXB2D5          CON    $5B
     TXB2D6          CON    $5C
     TXB2D7          CON    $5D
                             
    '' Rx Buffer 0 ''
     RXB0CTRL        CON    $60
     RXB0SIDH        CON    $61
     RXB0SIDL        CON    $62
     RXB0EID8        CON    $63
     RXB0EID0        CON    $64
     RXB0DLC         CON    $65
     RXB0D0          CON    $66
     RXB0D1          CON    $67
     RXB0D2          CON    $68
     RXB0D3          CON    $69
     RXB0D4          CON    $6A
     RXB0D5          CON    $6B
     RXB0D6          CON    $6C
     RXB0D7          CON    $6D
                             
    '' Rx Buffer 1 ''
     RXB1CTRL        CON    $70
     RXB1SIDH        CON    $71
     RXB1SIDL        CON    $72
     RXB1EID8        CON    $73
     RXB1EID0        CON    $74
     RXB1DLC         CON    $75
     RXB1D0          CON    $76
     RXB1D1          CON    $77
     RXB1D2          CON    $78
     RXB1D3          CON    $79
     RXB1D4          CON    $7A
     RXB1D5          CON    $7B
     RXB1D6          CON    $7C
     RXB1D7          CON    $7D
    
    
    '===============================================================================
    '                  Register Bit Masks                             
    '===============================================================================
    
     DLC_0          CON   $00
     DLC_1          CON   $01
     DLC_2          CON   $02
     DLC_3          CON   $03
     DLC_4          CON   $04
     DLC_5          CON   $05
     DLC_6          CON   $06
     DLC_7          CON   $07    
     DLC_8          CON   $08
    
    
    
    '===============================================================================
    ' '                  CAN SPI commands                               
    '===============================================================================
    '
    ' CAN_RESET         $C0
    ' CAN_READ          $03
    MCPWRT              CON       $02
    ' CAN_RTS           $80
    ' CAN_RTS_TXB0      $81
    ' CAN_RTS_TXB1      $82
    ' CAN_RTS_TXB2      $84
    ' CAN_RD_STATUS     $A0
    ' CAN_BIT_MODIFY    $05  
    ' CAN_RX_STATUS     $B0
    ' CAN_RD_RX_BUFF    $90
    ' CAN_LOAD_TX       $40  
    
    '===============================================================================
    '                         ALIAS & MODIFIERS
    '==============================================================================
    CS          VAR PORTB.4             ' CS LINE
    SCLK        VAR PORTB.1             ' CLOCK LINE
    SDI         VAR PORTB.0             ' DATA INPUT LINE
    SDO         VAR PORTC.7             ' DATA OUTPUT LINE
    SENDLED     VAR PORTA.5             ' LED TO INDICATE THE PROGRAM IS IN THE SENDING ROUTINE
    MAINLED     VAR PORTE.2             ' LED TO INDICATE THE PROGRAM IS IN THE MAIN ROUTINE
    INT         VAR PORTB.5             ' INTERRUPT OUTPUT FROM 18F4550
    MCPRST      VAR PORTC.0             ' RESET LINE FOR MCP2515
    SW_LOAD     VAR PORTA.4             ' BUTTON CONNECTED TO 18F4550 TO START SENDING DATA (WHEN IMPLEMENTED)
    
    '===============================================================================
    '                       SOFTWARE VARIABLES
    '===============================================================================
    
    DATAOUT VAR BYTE           'FIRST BYTE OF DATA TO BE SENT
    MCPREG VAR BYTE            'ADDRESS OF FIRST BYTE OF DATA TO BE SENT
    
    '==============================================================================
    '                   RESET MCP2515
    '==============================================================================
    
    LOW MCPRST                 ' COMMAND TO RESET THE MCP2515 (SOFTWARE)
    PAUSE 1
    HIGH MCPRST
    PAUSE 20
    
    '==============================================================================
    '                   INITIALIZE MCP2515
    '==============================================================================
    
    
    LOW CS
    SSPBUF = MCPWRT                 ' COMMAND TO WRITE TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND 
    
    MCPREG = CANCTRL                ' PUT THE MCP2515 INTO CONFIGURATION MODE 
    DATAOUT =  %10001010
    GOSUB SENDCANDATA  
    
    MCPREG =   CNF3                 'CNF3 - CONFIGURATION REGISTER 3 
    DATAOUT =  $07
    GOSUB SENDCANDATA
    
    MCPREG =   CNF2                 'CNF2 - CONFIGURATION REGISTER 2 
    DATAOUT =  $BA
    GOSUB SENDCANDATA
    
    MCPREG =   CNF1                 'CNF1 - CONFIGURATION REGISTER 1 
    DATAOUT =  $03
    GOSUB SENDCANDATA
        
    MCPREG =   TXB0SIDL             'TRANSMIT BUFFER 0 FOR SIDL
    DATAOUT =  %10000000    
    GOSUB SENDCANDATA 
    
    MCPREG =   TXB0SIDH             'TRANSMIT BUFFER 0 FOR SIDH
    DATAOUT =  %00000001
    GOSUB SENDCANDATA
        
    MCPREG =   CANCTRL              'PUT THE MCP2515 BACK INTO NORMAL MODE
    DATAOUT =  %00001010
    GOSUB SENDCANDATA
    HIGH CS
    
    
    
    '==============================================================================
    '                   MAIN PROGRAM FOR MCP2515
    '==============================================================================
    
    PROGRAMSTART:
    HIGH SENDLED 
    PAUSE 50
    
     LOW CS
    
    SSPBUF = $02                ' COMMAND TO WRITE TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    MCPREG =   CANINTE         ' DISABLE ALL INTERRUPTS
    DATAOUT =  %00000000
    GOSUB SENDCANDATA
                                
    MCPREG =   TXB0CTRL         ' FLAG THE TRANSMIT BUFFER - - 11=HIGH, 10=MEDIUM, 01=LOW, 00=LOWEST 
    DATAOUT =  %00001011
    GOSUB SENDCANDATA
    
    MCPREG =   TXB0SIDL         ' TRANSMIT BUFFER 0 FOR SIDH 
    DATAOUT =  %00110010    
    GOSUB SENDCANDATA 
    
    MCPREG =   TXB0SIDH        ' TRANSMIT BUFFER 0 FOR SIDH  
    DATAOUT =  %00110001
    GOSUB SENDCANDATA
     
    MCPREG =   TXB0DLC         ' NUMBER OF BYTES TO BE TRANSMITTED IN CAN FRAME  
    DATAOUT =  %00000010
    GOSUB SENDCANDATA
    
    MCPREG =   TXB0D0          ' DATA PACKET FOR BYTE 1 
    DATAOUT =  %00000001
    GOSUB SENDCANDATA
        
    MCPREG =   TXB0D1          'DATA PACKET FOR BYTE 2
    DATAOUT =  %00000100
    GOSUB SENDCANDATA
    
    
    HIGH CS
    
    LOW sendled
    pause 50
    
    
    
    goto PROGRAMSTART
    '===============================================================================
    '                           SEND CAN DATA SECTION
    '===============================================================================
    SENDCANDATA:
    
    SSPBUF = MCPREG          ' ADDRESS TO LOAD THE DATA TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 : WEND 
    SSPBUF = DATAOUT          'DATA BEING LOADED TO THE MCP2515 ON MCPREG
    PIR1.3 = 0              
    WHILE PIR1.3=0 : WEND
        
     RETURN

  10. #10
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi Christopher,
    OK, you're making progress, don't give up!

    You have ADCON1 = 0 which will have all 13 analog pins set as analog, you probably want to turn the analog stuff OFF by setting ADCON1 = 15. For example RB0 and RB1 are AN12 and AN10 respectively but they are also the SDI and SCK pins for the MSSP module - that may or may not be part of the problem.

    You have CMOCON = 0 which will leave both analog comparators ON, to turn them OFF on the 4550 you need to do CMCON = 7 - may or may not be part of the problem depending on exactly which pins they are on and what you're using, or trying to use, them for - I haven't looked into it.

    The MCP2515 SPI timing diagrams show the SPI clock idling LOW. You have SSPCON1.4 set to 1 which will make the clock idle HIGH, ie wrong polarity.

    Well, that's three things I see that may have something to do with it. Try adressing those, then try capturing the signals with your scope and see if they look anything like the timing diagrams in the MCP2515 datasheet.

    /Henrik.

  11. #11
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Jag är trött.....snälla hjälp mig!

    It's 1am here so hopefully this post is clear enough and someone can help me! I've been adjusting settings and trying new things and it still doesn't work. Here is what it boils down to. This is a picture of the working code from Microchip (if I knew C I'd be done already).

    Channel 1 (Yellow) SDO from 18F4550
    Channel 2 (Blue) CS from 18F4550
    Channel 3 (Purple) TXCAN from MCP2515
    Channel 4 (Green) CLK from 18F4550?

    Name:  Microchip code.jpg
Views: 1941
Size:  92.8 KB
    This is what I'm looking to achieve.

    This is what my code looks like:
    Name:  My code.jpg
Views: 1355
Size:  71.4 KB
    At least it's not that much different and I'm in the ballpark. What I can see is the signals are similar but way more compressed. It seems like a timing issue but I really don't know.

    I adjusted the oscilloscope to the same seconds/division as the Microchip example and it's very compressed. It looks like this:Name:  My code with the same division as Microchip.jpg
Views: 1219
Size:  105.1 KB

    I tried adjusting the HSPLL to HS and that seemed to make things a little better but didn't do much as far as a working program. The clock still looks strange when I compare it with the Microchip code.
    Name:  My code with the same division as Microchip but with HS instead of HSPLL.jpg
Views: 1203
Size:  93.4 KB

    The other things I see wrong:

    1. The code I'm using is sending one data packet, along with SIDH and SIDL. I setup Microchip's program to do the same thing. I can't figure out why but my code is sending a lot more than a few bytes of data. In fact, it overloads the demo program that Microchip supplies and shuts it down.

    2. Microchip's code sends the CAN data after the CS line goes high. For some reason, my program sends it halfway through the data transfer and my SDO line and the CAN output line are sending data at the same time. I did it once (send data after the CS line went high) but I can't remember what I did.

    3. I'm confused with the CKE/CKP bits and not sure what I need to set them as. I see this "mode 0,0" advertised in the MCP2515 datasheet but it literally means nothing to me and I'm sure it's pretty important.

    4. Did I properly set my BAUDCON?

  12. #12
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi Christopher,
    Being tired and trying to solve problems usually don't go hand in hand....

    1) I don't know why your code is sending more data than the Microchip example because A) I don't know which Microchip example your talking about and B) I don't see your code so it's impossible to say. Besides I'm not very good at reading C either.

    2) Your program probably transfers the CAN data to the MCP2515. It then starts to send it down the CAN-bus WHILE your program continues to transfer some other data to the MCP2515. The MCP example, since it apparently transfers LESS data the MCP2515 than your code, de-asserts the CS line right after transfering the actual data to be sent. (All guesses of course.)

    3) CKP is Clock Polarity. 0 means clock idles low, 1 means clock idles high. CKE is the "phase" of the data being output by the PIC on the SDO-pin. Here's a quote form the MCP2515 datasheet
    Commands and data are sent to the device via the SI pin, with data being clocked in on the rising edge of SCK. Data is driven out by the MCP2515 (on the SO line) on the falling edge of SCK.
    This means that the PIC must have the state of SDO pin valid by the time the rising edge of the clock occurs. The datasheet for the 18F4550 explains: When CKE bit is set, the SDO data is valid before there is a clock edge on SCK. So, my vote is for CKP=0, CKE=1 which should have the data output covered.

    The SMP bit in the PIC controls when the SDI-input is sampled relative to the clock. Since the MCP2515 outputs data on the falling edge of CLK-input we need to sample at, or after, that event. Looking at figure 19-3 and following the CKP0=0,CKE=0 trace down in the diagram we can see that in order to sample at the falling edge of the clock we need to have SMP=1.

    It is a bit confusing and I can honestly say that I'm not 100% confident I've got this right but that's my interpretation of the two the datasheets and it is what I'd try as starting point.

    4) BAUDCON in the 18F4550 is used to control the baudrate of the EUSART and has nothing to do with the MSSP module. The SPI clock rate of the MSSP module is a function of the device clock (Fosc) and the divide ratio set by the lower two bits in the SSPCON1 register.

    /Henrik.

  13. #13
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    I made my last post at 1:23am, it's now 5:11am and I've been up for 20 minutes getting ready for work. My math tells me that's about 3.5 hours or so.....

    Yes, I see I forgot to attach my code. I knew I forgot something! I also attached the Microchip C code as well.

    Code:
     
    
    ==============================================================================
    '                       DEFINES & INCLUDES
    '===============================================================================
    INCLUDE "modedefs.bas"
    '          
    DEFINE OSC 20 
    
    ' ==============================================================================
    '                        CONFIGS
    '===============================================================================
    
    @ __CONFIG _CONFIG1L, _PLLDIV_5_1L & _CPUDIV_OSC1_PLL2_1L & _USBDIV_2_1L
    @ __CONFIG _CONFIG1H, _FOSC_HSPLL_HS_1H & _FCMEN_OFF_1H & _IESO_OFF_1H
    @ __CONFIG _CONFIG2L, _PWRT_OFF_2L & _BOR_OFF_2L & _BORV_3_2L & _VREGEN_ON_2L
    @ __CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_32768_2H 
    @ __CONFIG _CONFIG3H, _CCP2MX_ON_3H & _PBADEN_OFF_3H & _LPT1OSC_OFF_3H & _MCLRE_ON_3H
    @ __CONFIG _CONFIG4L, _STVREN_ON_4L & _LVP_OFF_4L & _ICPRT_ON_4L & _XINST_OFF_4L
    
    '==============================================================================
    '                 SETTINGS
    ' ==============================================================================
               '76543210          '76543210
     TRISA   = %00010110: PORTA = %00000000 
     TRISB   = %00010001: PORTB = %00010000   
     TRISC   = %00000000: PORTC = %00000001
     TRISD   = %00011100: PORTD = %00000000
     TRISE   = %00010000: PORTE = %00010111
     ADCON0  = %00111100
     ADCON1  = 15
     ADRESH  = %00000000 
     ADRESL  = %00000000
     CMCON   = 7
     SSPSTAT = %01100100      
     SSPCON1 = %00100001 
     BAUDCON = %00001111     
        
    
    '===============================================================================
    '                   Register Mapping                          
    '================================================================================
    
     CANSTAT         CON    $0E
     CANCTRL         CON    $0F
     BFPCTRL         CON    $0C
     TEC             CON    $1C
     REC             CON    $1D
     CNF3            CON    $28
     CNF2            CON    $29
     CNF1            CON    $2A
     CANINTE         CON    $2B
     CANINTF         CON    $2C
     EFLG            CON    $2D
     TXRTSCTRL       CON    $0D
    
    ''  Recieve Filters ''
     RXF0SIDH        CON    $00
     RXF0SIDL        CON    $01
     RXF0EID8        CON    $02
     RXF0EID0        CON    $03
     RXF1SIDH        CON    $04
     RXF1SIDL        CON    $05
     RXF1EID8        CON    $06
     RXF1EID0        CON    $07
     RXF2SIDH        CON    $08
     RXF2SIDL        CON    $09
     RXF2EID8        CON    $0A
     RXF2EID0        CON    $0B
     RXF3SIDH        CON    $10
     RXF3SIDL        CON    $11
     RXF3EID8        CON    $12
     RXF3EID0        CON    $13
     RXF4SIDH        CON    $14
     RXF4SIDL        CON    $15
     RXF4EID8        CON    $16
     RXF4EID0        CON    $17
     RXF5SIDH        CON    $18
     RXF5SIDL        CON    $19
     RXF5EID8        CON    $1A
     RXF5EID0        CON    $1B
    
    '' Receive Masks ''
     RXM0SIDH        CON    $20
     RXM0SIDL        CON    $21
     RXM0EID8        CON    $22
     RXM0EID0        CON    $23
     RXM1SIDH        CON    $24
     RXM1SIDL        CON    $25
     RXM1EID8        CON    $26
     RXM1EID0        CON    $27
    
    '' Tx Buffer 0 ''
     TXB0CTRL        CON    $30
     TXB0SIDH        CON    $31
     TXB0SIDL        CON    $32
     TXB0EID8        CON    $33
     TXB0EID0        CON    $34
     TXB0DLC         CON    $35
     TXB0D0          CON    $36
     TXB0D1          CON    $37
     TXB0D2          CON    $38
     TXB0D3          CON    $39
     TXB0D4          CON    $3A
     TXB0D5          CON    $3B
     TXB0D6          CON    $3C
     TXB0D7          CON    $3D
                             
    '' Tx Buffer 1 ''
     TXB1CTRL        CON    $40
     TXB1SIDH        CON    $41
     TXB1SIDL        CON    $42
     TXB1EID8        CON    $43
     TXB1EID0        CON    $44
     TXB1DLC         CON    $45
     TXB1D0          CON    $46
     TXB1D1          CON    $47
     TXB1D2          CON    $48
     TXB1D3          CON    $49
     TXB1D4          CON    $4A
     TXB1D5          CON    $4B
     TXB1D6          CON    $4C
     TXB1D7          CON    $4D
    
    '' Tx Buffer 2 ''
     TXB2CTRL        CON    $50
     TXB2SIDH        CON    $51
     TXB2SIDL        CON    $52
     TXB2EID8        CON    $53
     TXB2EID0        CON    $54
     TXB2DLC         CON    $55
     TXB2D0          CON    $56
     TXB2D1          CON    $57
     TXB2D2          CON    $58
     TXB2D3          CON    $59
     TXB2D4          CON    $5A
     TXB2D5          CON    $5B
     TXB2D6          CON    $5C
     TXB2D7          CON    $5D
                             
    '' Rx Buffer 0 ''
     RXB0CTRL        CON    $60
     RXB0SIDH        CON    $61
     RXB0SIDL        CON    $62
     RXB0EID8        CON    $63
     RXB0EID0        CON    $64
     RXB0DLC         CON    $65
     RXB0D0          CON    $66
     RXB0D1          CON    $67
     RXB0D2          CON    $68
     RXB0D3          CON    $69
     RXB0D4          CON    $6A
     RXB0D5          CON    $6B
     RXB0D6          CON    $6C
     RXB0D7          CON    $6D
                             
    '' Rx Buffer 1 ''
     RXB1CTRL        CON    $70
     RXB1SIDH        CON    $71
     RXB1SIDL        CON    $72
     RXB1EID8        CON    $73
     RXB1EID0        CON    $74
     RXB1DLC         CON    $75
     RXB1D0          CON    $76
     RXB1D1          CON    $77
     RXB1D2          CON    $78
     RXB1D3          CON    $79
     RXB1D4          CON    $7A
     RXB1D5          CON    $7B
     RXB1D6          CON    $7C
     RXB1D7          CON    $7D
    
    
    '===============================================================================
    '                  Register Bit Masks                             
    '===============================================================================
    
     DLC_0          CON   $00
     DLC_1          CON   $01
     DLC_2          CON   $02
     DLC_3          CON   $03
     DLC_4          CON   $04
     DLC_5          CON   $05
     DLC_6          CON   $06
     DLC_7          CON   $07    
     DLC_8          CON   $08
    
    
    
    '===============================================================================
    ' '                  CAN SPI commands                               
    '===============================================================================
    '
    CAN_RST             CON       $C0
    ' CAN_READ          $03
    MCPWRT              CON       $02
    ' CAN_RTS           $80
    ' CAN_RTS_TXB0      $81
    ' CAN_RTS_TXB1      $82
    ' CAN_RTS_TXB2      $84
    ' CAN_RD_STATUS     $A0
    ' CAN_BIT_MODIFY    $05  
    ' CAN_RX_STATUS     $B0
    ' CAN_RD_RX_BUFF    $90
    ' CAN_LOAD_TX       $40  
    
    '===============================================================================
    '                         ALIAS & MODIFIERS
    '==============================================================================
    CS          VAR PORTB.4             ' CS LINE
    SCLK        VAR PORTB.1             ' CLOCK LINE
    SDI         VAR PORTB.0             ' DATA INPUT LINE
    SDO         VAR PORTC.7             ' DATA OUTPUT LINE
    SENDLED     VAR PORTA.5             ' LED TO INDICATE THE PROGRAM IS IN THE SENDING ROUTINE
    MAINLED     VAR PORTE.2             ' LED TO INDICATE THE PROGRAM IS IN THE MAIN ROUTINE
    INT         VAR PORTB.5             ' INTERRUPT OUTPUT FROM 18F4550
    MCPRST      VAR PORTC.0             ' RESET LINE FOR MCP2515
    SW_LOAD     VAR PORTA.4             ' BUTTON CONNECTED TO 18F4550 TO START SENDING DATA (WHEN IMPLEMENTED)
    RX_LED      VAR PORTD.1             ' USB RECEIVE LED
    TX_LED      VAR PORTD.0             ' USB TRANSMIT LED
    
    '===============================================================================
    '                       SOFTWARE VARIABLES
    '===============================================================================
    
    DATAOUT VAR BYTE           'FIRST BYTE OF DATA TO BE SENT
    MCPREG VAR BYTE            'ADDRESS OF FIRST BYTE OF DATA TO BE SENT
    
    
    TX_LED=1
    RX_LED=1
    '==============================================================================
    '                   RESET CAN
    '==============================================================================
    
    
    
    
    '==============================================================================
    '                   RESET CAN
    '==============================================================================
    
    '==============================================================================
    '                   INITIALIZE MCP2515
    '==============================================================================
    
    
    LOW CS
    SSPBUF = MCPRST               ' COMMAND TO RESET THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    HIGH CS
                     
    PAUSE 1000
    LOW CS                         
    
    SSPBUF = mcpwrt                 ' COMMAND TO WRITE TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    MCPREG =   CANCTRL              'PUT THE MCP2515 INTO CONFIGURATON MODE
    DATAOUT =  $08
    GOSUB SEND_CAN_DATA                    
    
    SSPBUF = cnf3                   ' CNF3 REGISTER
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    SSPBUF = $07                    ' PART OF BAUD RATE
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    SSPBUF = $BA                    ' PART OF BAUD RATE
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    SSPBUF = $03                    ' PART OF BAUDRATE
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    MCPREG =   CANCTRL              'PUT THE MCP2515 BACK INTO NORMAL MODE
    DATAOUT =  $0
    GOSUB SEND_CAN_DATA
    
    high cs
    
    
                                                                                                                  
    
    
    
    
    '==============================================================================
    '                   MAIN PROGRAM FOR MCP2515
    '==============================================================================
    
    START:
    HIGH SENDLED 
    PAUSE 4000
    
    LOW CS
       
    SSPBUF = mcpwrt                 ' COMMAND TO WRITE TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND              
     
    MCPREG =   CANINTE              ' DISABLE ALL INTERRUPTS
    DATAOUT =  %00000000
    GOSUB SEND_CAN_DATA            
    
    high cs                         ' ON THE MICROCHIP EXAMPLE, THEY PULLED THE CS LINE LOW, SENT
                                    ' SOME DATA, THEN HIGH AND LOW AGAIN TO RESEND MORE DATA. I 
                                    ' I STILL CAN'T FIGURE OUT WHY. IT'S PROBABLY SOMETHING 
                                    ' SIMPLE I AM MISSING BUT I TRIED TO DUPLICATE IT.
    LOW CS
    
    SSPBUF = mcpwrt                ' COMMAND TO WRITE TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    
    MCPREG =   TXB0CTRL         ' FLAG THE TRANSMIT BUFFER - - 11=HIGH, 10=MEDIUM, 01=LOW, 00=LOWEST 
    DATAOUT =  %00001011
    GOSUB SEND_CAN_DATA
    
    MCPREG =   TXB0SIDL         ' TRANSMIT BUFFER 0 FOR SIDH 
    DATAOUT =  %0000000    
    GOSUB SEND_CAN_DATA 
    
    MCPREG =   TXB0SIDH        ' TRANSMIT BUFFER 0 FOR SIDH  
    DATAOUT =  %00001000
    GOSUB SEND_CAN_DATA
     
    MCPREG =   TXB0DLC         ' NUMBER OF BYTES TO BE TRANSMITTED IN CAN FRAME  
    DATAOUT =  %00000001
    GOSUB SEND_CAN_DATA
    
    MCPREG =   TXB0D0          ' DATA PACKET FOR BYTE 1 
    DATAOUT =  %00000111
    GOSUB SEND_CAN_DATA
        
    'MCPREG =   TXB0D1          'DATA PACKET FOR BYTE 2
    'DATAOUT =  %00001111
    'GOSUB SEND_CAN_DATA 
    
    HIGH CS
    
    
    LOW sendled
    pause 4000 
    
    goto START
    
    
    '===============================================================================
    '                           SEND CAN DATA SECTION
    '===============================================================================
    SEND_CAN_DATA:
    
    
     
    
    SSPBUF = MCPREG          ' ADDRESS TO LOAD THE DATA TO THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 : WEND 
    
    SSPBUF = DATAOUT          'DATA BEING LOADED TO THE MCP2515 ON MCPREG
    PIR1.3 = 0              
    WHILE PIR1.3=0 : WEND
    
        
    RETURN
    Code:
    /*********************************************************************
     *
     *                Microchip USB C18 Firmware Version 1.0
     *
     *********************************************************************
     * FileName:        main.c
     * Dependencies:    See INCLUDES section below
     * Processor:       PIC18
     * Compiler:        C18 2.30.01+
     * Company:         Microchip Technology, Inc.
     *
     * Software License Agreement
     *
     * The software supplied herewith by Microchip Technology Incorporated
     * (the “Company”) for its PICmicro® Microcontroller is intended and
     * supplied to you, the Company’s customer, for use solely and
     * exclusively on Microchip PICmicro Microcontroller products. The
     * software is owned by the Company and/or its supplier, and is
     * protected under applicable copyright laws. All rights are reserved.
     * Any use in violation of the foregoing restrictions may subject the
     * user to criminal sanctions under applicable laws, as well as to
     * civil liability for the breach of the terms and conditions of this
     * license.
     *
     * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
     * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
     * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
     * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
     * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
     *
     * Author               Date        Comment
     *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * Rawin Rojvanit       11/19/04    Original.
     ********************************************************************/
    	
    		#pragma config PLLDIV   = 5         // (20 MHz crystal on PICDEM FS USB board)
            #pragma config CPUDIV   = OSC1_PLL2   
            #pragma config USBDIV   = 2         // Clock source from 96MHz PLL/2
            #pragma config FOSC     = HSPLL_HS
            #pragma config FCMEN    = OFF
            #pragma config IESO     = OFF 
            #pragma config PWRT     = OFF 
            #pragma config BOR      = OFF
            #pragma config BORV     = 3
            #pragma config VREGEN   = ON      //USB Voltage Regulator
            #pragma config WDT      = OFF
            #pragma config WDTPS    = 32768
            #pragma config MCLRE    = ON
            #pragma config LPT1OSC  = OFF
            #pragma config PBADEN   = OFF
          #pragma config CCP2MX   = ON
            #pragma config STVREN   = ON
            #pragma config LVP      = OFF
            #pragma config ICPRT    = ON       // Dedicated In-Circuit Debug/Programming
            #pragma config XINST    = OFF       // Extended Instruction Set
            #pragma config CP0      = OFF
            #pragma config CP1      = OFF
          #pragma config CP2      = OFF
          #pragma config CP3      = OFF
            #pragma config CPB      = OFF
          #pragma config CPD      = OFF
            #pragma config WRT0     = OFF
            #pragma config WRT1     = OFF
          #pragma config WRT2     = OFF
          #pragma config WRT3     = OFF
            #pragma config WRTB     = OFF       // Boot Block Write Protection
            #pragma config WRTC     = OFF
          #pragma config WRTD     = OFF
            #pragma config EBTR0    = OFF
            #pragma config EBTR1    = OFF
          #pragma config EBTR2    = OFF
          #pragma config EBTR3    = OFF
            #pragma config EBTRB    = OFF
    	
    
    
    /** I N C L U D E S **********************************************************/
    #include <p18cxxx.h>
    #include "system\typedefs.h"                        // Required
    #include "system\usb\usb.h"                         // Required
    #include "io_cfg.h"                                 // Required
    
    #include "system\usb\usb_compile_time_validation.h" // Optional
    #include "user\BusMon.h"                        // Modifiable
    
    
    /** V A R I A B L E S ********************************************************/
    #pragma udata
    
    /** P R I V A T E  P R O T O T Y P E S ***************************************/
    static void InitializeSystem(void);
    void USBTasks(void);
    
    /** V E C T O R  R E M A P P I N G *******************************************/
    
    /*
    extern void _startup (void);        // See c018i.c in your C18 compiler dir
    #pragma code _RESET_INTERRUPT_VECTOR = 0x002000
    void _reset (void)
    {
        _asm goto _startup _endasm
    }
    */
    
    //#pragma code _HIGH_INTERRUPT_VECTOR = 0x002008
    //void _high_ISR (void)
    //{
    //    ;
    //}
    //
    //#pragma code LOW_INTERRUPT_VECTOR = 0x002018
    //void _low_ISR (void)
    //{
    //  _asm
    //    goto InterruptHandler
    //  _endasm
    //}
    
    #pragma code                                                                                                                
    
    /******************************************************************************
     * Function:        void main(void)
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        Main program entry point.
     *
     * Note:            None
     *****************************************************************************/
    void main(void)
    {
      InitializeSystem();
      UserInit();         // See BusMon.c & .h
      while(1)
      {    
       if(usb_power_check)  //Only do USB USB if not powered by CAN connector
          USBTasks();         // USB Tasks
    //  INTCONbits.GIE = 1;
        ProcessIO();        // See user\BusMon.c & .h
      }
    }//end main
    
    /******************************************************************************
     * Function:        static void InitializeSystem(void)
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        InitializeSystem is a centralize initialization routine.
     *                  All required USB initialization routines are called from
     *                  here.
     *
     *                  User application initialization routine should also be
     *                  called from here.                  
     *
     * Note:            None
     *****************************************************************************/
    static void InitializeSystem(void)
    {
        ADCON1 |= 0x0F;                 // Default all pins to digital
        
        #if defined(USE_USB_BUS_SENSE_IO)
        tris_usb_bus_sense = INPUT_PIN; // See io_cfg.h
        #endif
        
        #if defined(USE_SELF_POWER_SENSE_IO)
        tris_self_power = INPUT_PIN;
        #endif
        
        if(usb_power_check)  //Only initialize USB if not powered by CAN connector
          mInitializeUSBDriver();         // See usbdrv.h
        
    }//end InitializeSystem
    
    /******************************************************************************
     * Function:        void USBTasks(void)
     *
     * PreCondition:    InitializeSystem has been called.
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        Service loop for USB tasks.
     *
     * Note:            None
     *****************************************************************************/
    void USBTasks(void)
    {
        /*
         * Servicing Hardware
         */
        USBCheckBusStatus();                    // Must use polling method
        if(UCFGbits.UTEYE!=1)
            USBDriverService();                 // Interrupt or polling method
    
    }// end USBTasks
    
    
    ////*****************************************************************************
    //// High priority interrupt vector
    //#pragma code InterruptVectorHigh = 0x08
    //void InterruptVectorHigh(void)
    //{
    //_asm
    //bra InterruptHandler // jump to interrupt routine
    //_endasm
    //}
    //#pragma code InterruptVectorHigh = 0x08
    //void InterruptVectorLow(void)
    //{
    //_asm
    //bra InterruptHandler // jump to interrupt routine
    //_endasm
    //}
    //
    //
    //#pragma code
    //#pragma interrupt InterruptHandler
    //void InterruptHandler(void)
    //{
    //}
    
    /** EOF main.c ***************************************************************/

  14. #14
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi Christopher,
    That Microchip code doesn't make any sense at all in this context. I see a lot of USB references but nothing about the MSSP module or the MCP2515.

    But I took another look at your code.... At the very beginning, in the INITIALIZE MCP2515 section you have this piece of code:
    Code:
    LOW CS
    SSPBUF = MCPRST               ' COMMAND TO RESET THE MCP2515
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    HIGH CS
    I then went to see exactly what MCPRST was and the only thing I found in your code was an alias to PortC.0 so what's happening in that piece of code is that the PIC reads PortC.0 and then the result of that read operation (1 or 0) to SSPBUF. To reset the device you either pull the hardware reset line LOW or send $C0 over SPI.

    Properly resetting the device automatically puts in the configuration mode so therotically you shouldn't need to put it in that mode directly after a (proper) reset. Then your comments says that you're writing to "part of baudrate" or something but $07 is the EXTENDED IDENTIFIER LOW register receiver 1, $03 is EXTENDED IDENTIFIER LOW register for receiver 0 and I can't even find what $BA is.

    OK, moving to MAIN then....
    You send the command to write then you send the value 0 to CANINTE at adress $02, that all seems OK to me if disabling interrupts is what you want to do.

    Next you set the TXB0CTRL register for a high priority message and request it to send. Then you write the TRANSMIT BUFFER STANDARD IDENTIFIER register. I have no idea what these does so I'm going to asume it's correct. Then, you tell it you want to send a single byte by writing the value 1 to TXB0DLC and then you write the actual data.

    Personally I would've thought you should first load the data to be sent THEN tell the device to send it. Ie write TXB0CTRL after you've loaded the data but the way you have it might be correct.

    OK, that's it for now. It feels like your pretty close.

    /Henrik.

  15. #15
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Yeah, I can see where my post was confusing. Let me clarify a few things.

    That Microchip code doesn't make any sense at all in this context. I see a lot of USB references but nothing about the MSSP module or the MCP2515.
    I attached the main code but I meant to attach another sheet.
    Code:
    /** D E C L A R A T I O N S **************************************************/
    
    #define Mode00  0
    #define Mode11  1
    
    #define	ON	1
    #define	OFF	0
    
    #define HIGH  1
    #define LOW   0
    
    
    //#pragma code
    //-----------------------------------------------------------------------------
    //  SPIReset()
    //-----------------------------------------------------------------------------
    void SPIReset(void)
    {
      CS_2515_LOW();
    	WriteSPI(CAN_RESET);
      CS_2515_HIGH();
      for(dummy=0; dummy<255; dummy++);
    
    
    }
    
    //-------------------------------------------------------------------------
    //  SPIByteWrite()
    //-------------------------------------------------------------------------
    void SPIByteWrite(unsigned char addr, unsigned char value )
    {
      CS_2515_LOW();
      while(WriteSPI(CAN_WRITE));
      while(WriteSPI(addr));
      while(WriteSPI(value));
      CS_2515_HIGH();
    }
    
    //-------------------------------------------------------------------------
    //     Function Name:    SPISegWrite                                
    //     Return Value:     None                                        
    //     Parameters:       Starting address, numbytes, and pointer
    //                       to an array.               
    //     Description:      This routine performs a sequential write.         
    //-------------------------------------------------------------------------
    void SPISeqWrite(unsigned char startaddr, unsigned char numbytes, char *data)
    {
      unsigned char n;
    
      CS_2515_LOW();
      while( WriteSPI(CAN_WRITE) );
      while( WriteSPI(startaddr) );
      for(n=0; n<numbytes; n++)
        while( WriteSPI(data[n]) );
      CS_2515_HIGH();
    }
    
    
    //-----------------------------------------------------------------------------
    //  SPIByteRead()
    //-----------------------------------------------------------------------------
    unsigned char SPIByteRead(unsigned char addr)
    {
      unsigned char tempdata;
      CS_2515_LOW();
      WriteSPI(CAN_READ);
      WriteSPI(addr);
      tempdata = ReadSPI();
      CS_2515_HIGH();
      return tempdata;
    }
    
    //-----------------------------------------------------------------------------
    //	SPISeqRead(unsigned char startaddr, unsigned char numbytes, *data)
    //     Return Value:     None (puts read data in string)                                        
    //     Parameters:       Starting address, numbytes, and pointer
    //                       to an array.               
    //     Description:      Sequential read from the MCP2515         
    //-----------------------------------------------------------------------------
    void SPISeqRead(unsigned char startaddr, unsigned char numbytes, char *data)
    {
      unsigned char n;
      CS_2515_LOW();
      while( WriteSPI(CAN_READ) );
      while( WriteSPI(startaddr) );
      for(n=0; n<numbytes; n++)
      {
        data[n] = ReadSPI();
    	}
      data[n] = ReadSPI();  //Last byte
      CS_2515_HIGH();
    }
    
    //-----------------------------------------------------------------------------
    //  SPIReadRX(unsigned char opcode, unsigned char numbytes, unsigned char *data)
    //	opcode = SPI opcode and pointer to RXBn register location// (see datasheet)
    //  numbytes = number of bytes to read
    //  *data = pointer to a string of data
    //	
    //-----------------------------------------------------------------------------
    void SPIReadRX(unsigned char opcode, unsigned char numbytes, char *data)
    {
      unsigned char n;
      CS_2515_LOW();
      while( WriteSPI(opcode));
      for(n=0; n<numbytes; n++)
      {
        data[n] = ReadSPI();
    	}
      data[n] = ReadSPI();  //Last byte
      CS_2515_HIGH();
    }
    
    //-----------------------------------------------------------------------------
    //  SPILoadTX(unsigned char opcode, unsigned char numbytes, unsigned char *data)
    //	opcode = SPI opcode and pointer to TXBn register location// (see datasheet)
    //  numbytes = number of bytes to write
    //  *data = pointer to a string of data
    //	
    //-----------------------------------------------------------------------------
    char SPILoadTX(unsigned char opcode, unsigned char numbytes, char *data)
    {
      unsigned char n, buff;
      
      //Check if this function is suppossed to choose the TX buffer to load
      if(opcode == CAN_LOAD_TX)//This function chooses the buffer
      {
        n = SPIReadStatus();
        //Check for free buffer
        if(!(n & 0x04))             //TXB0 is available
          opcode = CAN_LD_TXB0_ID;  //Point to TXB0SIDH
        else if(!(n & 0x10))        //TXB1 is available
          opcode = CAN_LD_TXB1_ID;  //Point to TXB1SIDH
        else if(!(n & 0x40))        //TXB2 is available
          opcode = CAN_LD_TXB2_ID;  //Point to TXB2SIDH
        else if(n & 0x54)           //No buffers available
        {
          SPIByteWrite(TXB0CTRL, 0x00); //Clear TXREQ (and whole register)
          for(dummy=0; dummy<255; dummy++);
          opcode = CAN_LD_TXB0_ID;  //Point to TXB0SIDH
        }
      }
    
      CS_2515_LOW();
      while( WriteSPI(opcode) );
      for(n=0; n<numbytes; n++)
       while( WriteSPI(data[n]) );
      CS_2515_HIGH();
      
      return(opcode); //So calling function knows which buffer was loaded
    }
    
    //-----------------------------------------------------------------------------
    //  RTS(buffer)
    //	buffer = CAN_RTS_TXBn; where 'n' = 0, 1, 2
    //  OR
    //	buffer = CAN_RTS; followed by | 0 - 7 (e.g., "CAN_RTS | 7" sends TX0 and TX1)
    //  OR
    //	buffer = CAN_RTS_TXBn | CAN_RTS_TXBn | CAN_RTS_TXBn; where 'n' = 0, 1, 2
    //-----------------------------------------------------------------------------
    void SPI_RTS(unsigned char buffer)
    {
      unsigned char tempdata;
    	tempdata = SPIReadStatus();
    	
    	if(buffer & 0x01)	//Buffer 0	
    		if(tempdata & 0x04)	//Check TXREQ first
    		{
    			Delay_ms(1);
    			SPIByteWrite(TXB0CTRL, 0);			//Clear TXREQ (and everything else... not clean)
    			while(SPIReadStatus() & 0x04); //Wait for TXREQ to clear
    	  }
    	if(buffer & 0x02)	//Buffer 1
    		if(tempdata & 0x10)	//Check TXREQ first
    		{
      		Delay_ms(1);
      		SPIByteWrite(TXB1CTRL, 0);			//Clear TXREQ (and everything else... not clean)
    			while(SPIReadStatus() & 0x10); //Wait for TXREQ to clear
    		}
    	if(buffer & 0x04)	//Buffer 2
    		if(tempdata & 0x40)	//Check TXREQ first
    		{
      		Delay_ms(1);
      		SPIByteWrite(TXB2CTRL, 0);			//Clear TXREQ (and everything else... not clean)
    			while(SPIReadStatus() & 0x40); //Wait for TXREQ to clear
    		}
    	
      CS_2515_LOW();
    	WriteSPI(buffer);
      CS_2515_HIGH();
    }
    
    //-----------------------------------------------------------------------------
    //  char SPIReadStatus//(void)
    //    Performs Read Status command and returns result
    //-----------------------------------------------------------------------------
    char SPIReadStatus(void)
    {
      unsigned char tempdata;
      CS_2515_LOW();
      WriteSPI(CAN_RD_STATUS);
      tempdata = ReadSPI();
      CS_2515_HIGH();
      return tempdata;
    }
    
    //-----------------------------------------------------------------------------
    //  Delay_ms()
    //  Tcy = 1 us
    //  Tcy X 1000 = 1 ms
    //  FFFFh - 3E8h (1000d) = FC17h
    //  For(){} loop is in 1 ms increments
    //-----------------------------------------------------------------------------
    void Delay_ms(unsigned char num_ms)
    {
      unsigned char n;
      
      TMR1H = 0xFC;
      TMR1L = 0x17;
      
      T1CONbits.TMR1ON = 1; //Start timer
      
      for(n = 0; n < num_ms; n++)
      {
        while(!PIR1bits.TMR1IF);  //Wait for timer flag
        PIR1bits.TMR1IF = 0;      //Clear flag
        TMR1H = 0xFC;
        TMR1L = 0x17;
      }
    
      T1CONbits.TMR1ON = 0; //Stop timer
    }
    Code:
    /** P R I V A T E  P R O T O T Y P E S ***************************************/
    void CheckLoadButton(void);
    unsigned char ReadSPI( void );
    void SetBitTiming(char data_rate);
    void CANLoadTX(void);
    
    /** D E C L A R A T I O N S **************************************************/
    #define CAN_1000kbps  0       //BRP setting in CNF1
    #define CAN_500kbps   1       //
    #define CAN_250kbps   3       //
    #define CAN_125kbps   7       //
    
    /** S T R U C T S ********************************************************/
    struct {
      unsigned char   INTF:1;   //bit 0 
    	unsigned char   MCP_RXBn:2; 
      unsigned char   SOF:1;
      unsigned char   USBsend:1;
      unsigned char   USBQueue:1;
      unsigned char   CANLoading:1;
      unsigned char   Flag8:1;
    } UserFlag;
    
    
    struct {
      unsigned char   BusLoad:2;    //bit 0 
    	unsigned char   TX_buff:2;    //OUT message to instruct TX buff to send
      unsigned char   CAN_USB_PTR:2;
      unsigned char   nu2:1;
      unsigned char   nu3:1;
    } usbcan_STATUS;
    
    
    /** V A R I A B L E S ********************************************************/
    #pragma udata
    //byte old_sw2,old_sw3;
    
    BOOL emulate_mode;
    rom signed char dir_table[]={-4,-4,-4, 0, 4, 4, 4, 0};
    byte movement_length;
    byte vector = 0;
    
    byte AquireData = 1;  //0 = STOP; 1 = Aquire (Not Used)
    char buffer[3];
    char inbuffer[BUF_SIZE];            // 64 byte input to USB device buffer
    char outbuffer[BUF_SIZE];           // 64 byte output to USB device buffer
    
    byte TimerCounter = 0xF0;
    static unsigned char gTimeout = 0;
    
    unsigned int CANLoadTimer = 0x00;
    unsigned int LoadPrescaler = 0x8000;
    
    static byte LOAD_FLAG = PERCENT_0;
    
    static char USB_ptr = 0xFF; //Points to the USB location of the start of the
                                  //four possible CAN messages      
    static char old_CANCTRL;    //Used to track CANCTRL changes from host                              
    
                                  
    char DataArray[32];
    char ReadArray[32];
    
    #pragma code
    
    /******************************************************************************
     * Function:        void ProcessIO
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function is a place holder for other user routines.
     *                  It is a mixture of both USB and non-USB tasks.
     *
     * Note:            None
     *****************************************************************************/
    void ProcessIO(void)
    {   
      unsigned char n, b, c, y;
      int a;
      // User Application USB tasks
    
      if(!(usb_power_check))  //Only generate traffic if NOT connected to USB
      {
        CheckLoadButton();
        CANLoadTX();
      }
    
      if((usb_device_state < CONFIGURED_STATE)||(UCONbits.SUSPND==1)) return;
    
    //----- Read USB buffer if it has data from the host -----
      if (HIDRxReport(inbuffer, 64))   // USB receive buffer has data
      {
        LED_RX_ON();                //Turn LED on
        T0CONbits.TMR0ON = 1;       //Start timer for TX LED on time
        gTimeout = 0;               //Reset timout
    
    //---- CANmsgs: Check if host has requested CAN messages to be transmited
        switch(inbuffer[u_CANmsgs])           // interpret command
        {
          case 0x00:     // No messages are available
          break;
    
          case 0x01:      // Message 1 is available
            GetUSBData(m1_SIDH, 13, DataArray);
            n = SPILoadTX(CAN_LOAD_TX, 13, DataArray);
    
            if(n == CAN_LD_TXB0_ID) //if TX0 is loaded
            {
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
            }
            else if(n == CAN_LD_TXB1_ID) //if TX1 is loaded
            {
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
            }
            else if(n == CAN_LD_TXB2_ID) //if TX2 is loaded
            {
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
            }
          break;
    
          case 0x02:      // Message 1 and 2 are available
            //Message 1
            GetUSBData(m1_SIDH, m1_DLC + 5, DataArray);
            n = SPILoadTX(CAN_LOAD_TX, m1_DLC + 5, DataArray);
    
            if(n == CAN_LD_TXB0_ID) //if TX0 is loaded
            {
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
            }
            else if(n == CAN_LD_TXB1_ID) //if TX1 is loaded
            {
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
            }
            else if(n == CAN_LD_TXB2_ID) //if TX2 is loaded
            {
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
            }
    
            //Message 2
            GetUSBData(m2_SIDH, m2_DLC + 5, DataArray);
            n = SPILoadTX(CAN_LOAD_TX, m2_DLC + 5, DataArray);
    
            if(n == CAN_LD_TXB0_ID) //if TX0 is loaded
            {
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
            }
            else if(n == CAN_LD_TXB1_ID) //if TX1 is loaded
            {
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
            }
            else if(n == CAN_LD_TXB2_ID) //if TX2 is loaded
            {
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
            }
          break;
    
          case 0x03:      // Message 1, 2, and 3 are available
            //Message 1
            GetUSBData(m1_SIDH, m1_DLC + 5, DataArray);
            n = SPILoadTX(CAN_LOAD_TX, m1_DLC + 5, DataArray);
    
            if(n == CAN_LD_TXB0_ID) //if TX0 is loaded
            {
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
            }
            else if(n == CAN_LD_TXB1_ID) //if TX1 is loaded
            {
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
            }
            else if(n == CAN_LD_TXB2_ID) //if TX2 is loaded
            {
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
            }
    
            //Message 2
            GetUSBData(m2_SIDH, m2_DLC + 5, DataArray);
            n = SPILoadTX(CAN_LOAD_TX, m2_DLC + 5, DataArray);
    
            if(n == CAN_LD_TXB0_ID) //if TX0 is loaded
            {
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
            }
            else if(n == CAN_LD_TXB1_ID) //if TX1 is loaded
            {
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
            }
            else if(n == CAN_LD_TXB2_ID) //if TX2 is loaded
            {
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
            }
    
            //Message 3
            GetUSBData(m3_SIDH, m3_DLC + 5, DataArray);
            n = SPILoadTX(CAN_LOAD_TX, m3_DLC + 5, DataArray);
    
            if(n == CAN_LD_TXB0_ID) //if TX0 is loaded
            {
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
            }
            else if(n == CAN_LD_TXB1_ID) //if TX1 is loaded
            {
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
            }
            else if(n == CAN_LD_TXB2_ID) //if TX2 is loaded
            {
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
            }
          break;
    
          case 0x04:      //--FUTURE-- Message 1, 2, 3, and 4 are available
          break;
    
          default:                    // unrecognized or null command
            ;
        }// END switch: inbuffer[u_CANmsgs]
    
    //---- CANCTRL: Write to the CANCTRL register if changed
        if(inbuffer[u_CANCTRL] != old_CANCTRL) //If host sent new CANCTRL value
        {
          SPIByteWrite(CANCTRL,inbuffer[u_CANCTRL]);  //Write to CANCTRL
    			EEPROMBYTEWrite(CANCTRL,inbuffer[u_CANCTRL]);
    			EEPROMCRCWrite(0,128);
          old_CANCTRL = inbuffer[u_CANCTRL];          //
          outbuffer[u_CANSTAT] = SPIByteRead(CANSTAT);
          while((outbuffer[u_CANSTAT] & 0xE0) != (inbuffer[u_CANCTRL] & 0xE0))//if didn't change modes yet
          {
            outbuffer[u_CANSTAT] = SPIByteRead(CANSTAT);
          }
          UserFlag.USBsend = 1;               //Set flag so will send USB message
        }
    
        //---- SPI: SPI command from host
        if(inbuffer[u_SPI]) //If host sent SPI command (non-zero)
        {
          switch(inbuffer[u_SPI])           
          {
            case CAN_RESET:     // 
              SPIReset();
            break;
      
            case CAN_READ:      // 
              if(!UserFlag.USBQueue)  // If previous message is queued
              {
                outbuffer[u_SPI] = CAN_READ;        //Send back to host
                outbuffer[u_REG] = inbuffer[u_REG]; //Send back to host
                outbuffer[u_DATA] = SPIByteRead(inbuffer[u_REG]); //Send back to host
              }
              UserFlag.USBsend = 1;               //Set flag so will send USB message
              UserFlag.USBQueue = 1;  //Indicates msg is queued, but not sent
            break;
      
            case CAN_WRITE:      // 
              //outbuffer[u_SPI] = 0;        //Send back to host //JM
              SPIByteWrite(inbuffer[u_REG],inbuffer[u_DATA]);
    		  EEPROMBYTEWrite(inbuffer[u_REG],inbuffer[u_DATA]);	
    		  EEPROMCRCWrite(0,128);	
            break;
      
            case CAN_RTS:      // 
              SPI_RTS(inbuffer[u_DATA]);
            break;
      
            case CAN_RD_STATUS:      //  
              outbuffer[u_DATA] = SPIReadStatus();
              UserFlag.USBsend = 1;               //Set flag so will send USB message
            break;
      
            default:                    // unrecognized or null command
              ;
          }// END switch: inbuffer[u_SPI]
        }
      }//END if (HIDRxReport(inbuffer, 1)
    
    //---- Check RXnBF pins and service messages as needed ---
      switch(CheckCANRX())           // Check if CAN message received
      {
        case 0x01:      // Message in RXB0 (Msgs in this buffer are Standard)
          SPIReadRX(CAN_RD_START_RXB0SIDH, 13, ReadArray);
          LoadUSBString(ReadArray);
        break;
      
        case 0x02:      // Message in RXB1 (Msgs in this buffer are Extended)
          SPIReadRX(CAN_RD_START_RXB1SIDH, 13, ReadArray);
          LoadUSBString(ReadArray);
        break;
      
        case 0x03:      // Message in RXB0 and RXB1
          SPIReadRX(CAN_RD_START_RXB0SIDH, 13, ReadArray);
          LoadUSBString(ReadArray);
          SPIReadRX(CAN_RD_START_RXB1SIDH, 13, ReadArray);
          LoadUSBString(ReadArray);
        break;
      
        default:                    // unrecognized or null command
          ;
      }// END switch: CheckCANRX()
    
    //---- The following turns off the TX and RX USB indicator LEDs after some time
      //Inst. cycle = 200 ns; TMR0IF sets every 51 us
      if(INTCONbits.TMR0IF)	
      {
      	TimerCounter++;
      	if (!TimerCounter)      //if rolled over, set flag. User code will handle the rest.
      	{
          LED_TX_OFF();                //Turn LED off
          LED_RX_OFF();                //Turn LED off
      	  T0CONbits.TMR0ON = 0;       //Start timer for TX LED on time
      	  TimerCounter = 0xFE;
      	  gTimeout = 1;               //Reset timout
      	}
      	INTCONbits.TMR0IF = 0;
      }
      
    //------ Load USB Data to be transmitted to the host --------
      if(UserFlag.MCP_RXBn | UserFlag.USBsend)
      {
        if(!mHIDTxIsBusy())
        {
          HIDTxReport(outbuffer, 64);
          
          outbuffer[0] = 0x00;  //PKR$$$ Need this??
    
          UserFlag.MCP_RXBn = 0;      //clear flag
          UserFlag.USBsend = 0;       //clear flag
          UserFlag.USBQueue = 0;      //Clear message queue
    
          LED_TX_ON();                //Turn LED on
      	  T0CONbits.TMR0ON = 1;       //Start timer for TX LED on time
      	  gTimeout = 0;               //Reset timout
    
          outbuffer[u_SPI] = 0x00;     //clear back to 00h so host doesn't detect "SPI response"
          USB_ptr = 0xFF;    //Point to location 0xFF
          outbuffer[u_CANmsgs] = 0x00;      //Clear message status
        }
      }
    }//end ProcessIO
    
    
    /******************************************************************************
     * Function:        char CheckCANRX(void)
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          Which RX buffer have message
     *                  0 = None; 1 = RXB0; 2 = RXB1; 3 = Both buffers
     *
     * Side Effects:    None
     *
     * Overview:        
     *
     * Note:            
     *****************************************************************************/
    char CheckCANRX(void)
    {
      //First, set the correct flag. ORs with flag because this might not be the
      //1st time through
      UserFlag.MCP_RXBn = 0;
      
      if(!RXB0_2515) UserFlag.MCP_RXBn = UserFlag.MCP_RXBn | 0x01; //RXB0 is active low
      if(!RXB1_2515) UserFlag.MCP_RXBn = UserFlag.MCP_RXBn | 0x02; //RXB1 is active low
        
      return UserFlag.MCP_RXBn;
    }
    
    /******************************************************************************
     * Function:        void LoadUSBString//(char *data)
     *
     * PreCondition:    Set the pointer to the USB array "USB_ptr"
     *
     * Input:           Pointer to array that contains the data to write to USB
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        Loads data read from the MCP2515 RX buffer. Note, the
     *                  function already knows the *data is 13 bytes
     *
     * Note:            
     *****************************************************************************/
    void LoadUSBString(char *data)
    {
      unsigned char n, rtr, ide, dlc, num_bytes, CAN_ptr;
      
      CAN_ptr = 0;
      
      if(USB_ptr == 0xFF) //If 0xFF, then this is the 1st time through
        USB_ptr = 0;
        
      //-----Calculate how big the CAN msg is... how many USB bytes to use---
      if(data[mx_SIDL] & 0x08){     //If extended ID
        ide = 0x20;                 //Maps to 'INFO' byte in USB
        num_bytes = 0x04;   `       //All four ID registers (extended ID)
      }
      else{
        ide = 0x00;                 //Maps to 'INFO' byte in USB
        num_bytes = 0x02;           //Just the standard ID registers
      }
        
      if(ide)     //If extended ID, then RTR is in DLC; else in SIDL
      {
        if(data[mx_DLC] & 0x40){      //RTR bit in DLC
          rtr = 0x10;                 //Maps to 'INFO' byte in USB
        }
        else{
          rtr = 0x00;                 //Maps to 'INFO' byte in USB
        }
      }
      else
      {
        if(data[mx_SIDL] & 0x10){     //RTR bit in SIDL
          rtr = 0x10;                 //Maps to 'INFO' byte in USB
        }
        else{
          rtr = 0x00;                 //Maps to 'INFO' byte in USB
        }
      }
      dlc = data[mx_DLC] & 0x0F;  //Determine the data length code
      //END--Calculate how big the CAN msg is... how many USB bytes to use---END
      
      //Calculate if there is enough room to add the CAN message to USB:
      //52 is the location of the 1st control message. Subract the pointer location
      //If this is larger than the current CAN message (includes the "INFO" byte)
      //then can load 'outbuffer[]'. Otherwise, will need to switch interfaces
      //(assuming currently on the 1st interface).
      
      //If enough room, add the bytes
      if(52 - USB_ptr > num_bytes)
      {
        //Now load the 'INFO' byte
        outbuffer[USB_ptr] = 0x80 + ide + rtr + dlc;
        USB_ptr++;
    
        //Load the identifier registers
        for(n=0; n<num_bytes; n++)
        {
          outbuffer[USB_ptr] = data[CAN_ptr];
          USB_ptr++;
          CAN_ptr++;
        }
        
        if(!ide) CAN_ptr = CAN_ptr + 2; //skip over EXIDE regs
        CAN_ptr++;  //skip over the DLC register
    
        if(!rtr)  //if a data frame
        {   
          //Load the data registers
          for(n=0; n<dlc; n++) //
          {
            outbuffer[USB_ptr] = data[CAN_ptr];
            USB_ptr++;
            CAN_ptr++;
          }
        }
        
        //Now clear next byte (STATUS). Cleared to 0 in case this is the last time through.
        //PC looks at MSb and, if clear, no message follows
        outbuffer[USB_ptr] = 0x00;
        
        //Next, need to load the other status bytes from byte 53 - 59
        //[CANINTF, EFLG, TEC, REC, CANSTAT, CANCTRL]
    
        //Need to read TEC, REC, and CANSTAT to report to host
        SPISeqRead(TEC, 3, ReadArray);
        outbuffer[u_TEC] = ReadArray[0];
        outbuffer[u_REC] = ReadArray[1];
        outbuffer[u_CANSTAT] = ReadArray[2];
      }
    }
    
    /******************************************************************************
     * Function:        void GetUSBData//(char start_ptr, char num_bytes, char *data)
     *
     * PreCondition:    None
     *
     * Input:           Pointer to location in USB string
     *                  Number of bytes to get
     *                  Pointer to array that contains the data to write to USB
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        Gets data from the USB string.
     *
     * Note:            
     *****************************************************************************/
    void GetUSBData(char start_ptr, char num_bytes, char *data)
    {
      unsigned char n;
    
      for(n=start_ptr; n<start_ptr+num_bytes; n++)
        data[n - start_ptr] = inbuffer[n];
    }
    
    /******************************************************************************
     * Function:        void UserInit
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function initializes the PIC.
     *                  
     *
     * Note:            None
     *****************************************************************************/
    
    void UserInit(void)
    {
      byte i;
      
      CS_2515_HIGH();  //Drive high
      tris_CS = 0; //Output
    	OpenSPI(SPI_FOSC_16, MODE_00, SMPMID);
      
      TRISBbits.TRISB0 = 1;   //SDI
      TRISBbits.TRISB1 = 0;   //SCK
    
      //-------------------------
      // initialize variables
      //-------------------------
      for (i=0; i<BUF_SIZE; i++)     // initialize input and output buffer to 0
      {
        inbuffer[i]=0;
        outbuffer[i]=0;
      }
        
    	//Timer 0
      TMR0H = 0;                    //clear timer
      TMR0L = 0;                    //clear timer
    	T0CONbits.PSA = 0;	          //Assign prescaler to Timer 0
    	T0CONbits.T0PS2 = 1;	        //Setup prescaler
    	T0CONbits.T0PS1 = 1;	        //Will time out every 51 us based on
    	T0CONbits.T0PS0 = 1;	        //20 MHz Fosc
    	T0CONbits.T0CS = 0;	          //Increment on instuction cycle
    	
    	INTCON2 = 0xFF;           //INT2 on rising edge
    	INTCON3bits.INT2IP = 0;   //Low priority
    	INTCON3bits.INT2IF = 0;   //Clear flag
    	INTCON3bits.INT2IE = 1;   //Enable INT2 interrupt
    
      //Outputs for the LEDs
      ADCON1 = 0x0F;      //Digital pins
      CMCON = 0x07;       //Digital pins
    
      LED_25PCT_OFF();
      LED_50PCT_OFF();
      LED_75PCT_OFF();
      LED_100PCT_OFF();
      
      tris_LED_25PCT =  OUTPUT_PIN;
      tris_LED_50PCT =  OUTPUT_PIN;
      tris_LED_75PCT =  OUTPUT_PIN;
      tris_LED_100PCT = OUTPUT_PIN;
      
      UserFlag.CANLoading = OFF;
      
      LED_RX_OFF();
      LED_TX_OFF();
    
      tris_LED_TX = OUTPUT_PIN;
      tris_LED_RX = OUTPUT_PIN;
      
      tris_SW_LOAD = INPUT_PIN;
    
      //RTS Pin Outputs
      RTS0_2515_HIGH();
      tris_RTS0_pin = OUTPUT_PIN;
    
      RTS1_2515_HIGH();
      tris_RTS1_pin = OUTPUT_PIN;
    
      RTS2_2515_HIGH();
      tris_RTS2_pin = OUTPUT_PIN;
    
      tris_CAN_RES = OUTPUT_PIN;
      CAN_RES_ON(); //JM
    //  CAN_RES_OFF();  //Disconnect the termination resistor by default
    
      UserFlag.MCP_RXBn = 0;      //clear flag
      UserFlag.USBsend = 0;       //clear flag
      UserFlag.USBQueue = 0;      //Clear message queue
    
      //Need to set up MCP2515 before enabling interrupts
      CANInit();          // See BusMon.c & .h
    
      RCONbits.IPEN = 1;
      INTCONbits.PEIE = 1;
      INTCONbits.GIE = 1;
    }//end UserInit
    
    /******************************************************************************
     * Function:        void CANInit//(void)
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function initializes the MCP2515.
     *                  
     *
     * Note:            None
     *****************************************************************************/
    void CANInit()
    {
      unsigned char n,EEPROM_ver;
      SPIReset();           //Wait state is in SPIReset() function
    	EEPROM_ver=EEPROMCRCCheck(0,128);
    	EEPROM_ver=0;
      for(n=0; n<250; n++); //Wait anyway
    
      //Set Config Mode and verify
      //CANCTRL Register
    	/*
    	if(EEPROM_ver)
    	{
    		CANCTRL_byte=EEPROMBYTERead(CANCTRL);
    	}
    	else
    	{
    			EEPROMBYTEWrite(CANCTRL,CANCTRL_byte);
    	}
    	*/
      SET_CONFIG;         //Set Configuration mode
      CLEAR_OSM;          //Not One-Shot-Mode
      CLEAR_ABORT;        //Clear ABAT bit
      CLKOUT_ENABLED;     //Enabled CLKOUT pin (will configure as SOF pin in CNF3)
    	//EEPROMBYTEWrite(CANCTRL,CANCTRL_byte);
    
      SPIByteWrite(CANCTRL,CANCTRL_byte); //Write to CANCTRL
      //Verify Mode change
      if ((SPIByteRead(CANSTAT)& 0xE0) != OPMODE_CONFIG)
        SPIByteWrite(CANCTRL,CANCTRL_byte); //Try again
    
      if(usb_power_check)  //Only if connected to USB. Traffic gen board doesn't process RX msgs
      {
        //Configure INTERRUPTS
    		/*
    		if(EEPROM_ver)
    		{
    			CANINTE_byte=EEPROMBYTERead(CANINTE);
    		}
    		else
    		{
    			EEPROMBYTEWrite(CANINTE,CANINTE_byte);
    		}
    		*/
        G_RXB_INT_ENABLED;
     	  G_TXB_INT_ENABLED;
    		//EEPROMBYTEWrite(CANINTE,CANINTE_byte);
    	
        SPIByteWrite(CANINTE, CANINTE_byte);
      }
        
      //Use RXnBF pins as interrupts on RX //(BFPCTRL register)
      RXB0BF_PIN_INT;
      RXB1BF_PIN_INT;
      SPIByteWrite(BFPCTRL, BFPCTRL_byte);
    
      //Configure TXnRTS pins as RTS pins
      TXRTSCTRL_byte = 0x07;
      SPIByteWrite(TXRTSCTRL, TXRTSCTRL_byte);
    
      //Configure MASKS
      DataArray[0] = 0x00;  
      DataArray[1] = 0x00;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXM0SIDH);
    		DataArray[1]=EEPROMBYTERead(RXM0SIDL);
    		DataArray[2]=EEPROMBYTERead(RXM0EID8);
    		DataArray[3]=EEPROMBYTERead(RXM0EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXM0SIDH,&DataArray[0],4);
    	}
    */
      SPISeqWrite(RXM0SIDH, 4, DataArray);  //RX Mask 0
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x00;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXM1SIDH);
    		DataArray[1]=EEPROMBYTERead(RXM1SIDL);
    		DataArray[2]=EEPROMBYTERead(RXM1EID8);
    		DataArray[3]=EEPROMBYTERead(RXM1EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXM1SIDH,&DataArray[0],4);
    	}
    */
      SPISeqWrite(RXM1SIDH, 4, DataArray);  //RX Mask 1
      //SPISeqWrite(RXB0CTRL, 1, 0);  //RXB0 Control JM
      //SPISeqWrite(RXB1CTRL, 1, 0);  //RXB1 Control JM
      
      //Configure FILTERS
      //Will RX standard messages into RXB0 and extended into RXB1
    
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x00;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXF0SIDH);
    		DataArray[1]=EEPROMBYTERead(RXF0SIDL);
    		DataArray[2]=EEPROMBYTERead(RXF0EID8);
    		DataArray[3]=EEPROMBYTERead(RXF0EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXF0SIDH,&DataArray[0],4);
    	}
    */
    	SPISeqWrite(RXF0SIDH, 4, DataArray);
      //SPIByteWrite(RXF0SIDL, 0x00); //Clearing EXIDE... the rest is "don't care"
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x00;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXF1SIDH);
    		DataArray[1]=EEPROMBYTERead(RXF1SIDL);
    		DataArray[2]=EEPROMBYTERead(RXF1EID8);
    		DataArray[3]=EEPROMBYTERead(RXF1EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXF1SIDH,&DataArray[0],4);
    	}
    */
    	SPISeqWrite(RXF1SIDH, 4, DataArray);
    
      //SPIByteWrite(RXF1SIDL, 0x00); //Clearing EXIDE... the rest is "don't care" //JM
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x08;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXF2SIDH);
    		DataArray[1]=EEPROMBYTERead(RXF2SIDL);
    		DataArray[2]=EEPROMBYTERead(RXF2EID8);
    		DataArray[3]=EEPROMBYTERead(RXF2EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXF2SIDH,&DataArray[0],4);
    	}
    */
    	SPISeqWrite(RXF2SIDH, 4, DataArray);
    
      //SPIByteWrite(RXF2SIDL, 0x08); //Setting EXIDE... the rest is "don't care"
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x08;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*	
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXF3SIDH);
    		DataArray[1]=EEPROMBYTERead(RXF3SIDL);
    		DataArray[2]=EEPROMBYTERead(RXF3EID8);
    		DataArray[3]=EEPROMBYTERead(RXF3EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXF3SIDH,&DataArray[0],4);
    	}
    */	
    	SPISeqWrite(RXF3SIDH, 4, DataArray);
    
    
      //SPIByteWrite(RXF3SIDL, 0x08); //Setting EXIDE... the rest is "don't care" //JM
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x08;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*	
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXF4SIDH);
    		DataArray[1]=EEPROMBYTERead(RXF4SIDL);
    		DataArray[2]=EEPROMBYTERead(RXF4EID8);
    		DataArray[3]=EEPROMBYTERead(RXF4EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXF4SIDH,&DataArray[0],4);
    	}
    */	
    	SPISeqWrite(RXF4SIDH, 4, DataArray);
    
      //SPIByteWrite(RXF4SIDL, 0x08); //Setting EXIDE... the rest is "don't care" //JM
    
      DataArray[0] = 0x00;  
      DataArray[1] = 0x08;
      DataArray[2] = 0x00;
      DataArray[3] = 0x00;
    /*	
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(RXF5SIDH);
    		DataArray[1]=EEPROMBYTERead(RXF5SIDL);
    		DataArray[2]=EEPROMBYTERead(RXF5EID8);
    		DataArray[3]=EEPROMBYTERead(RXF5EID0);
    	}
    	else
    	{
    		EEPROMDataWrite(RXF5SIDH,&DataArray[0],4);
    	}
    */
    	SPISeqWrite(RXF5SIDH, 4, DataArray);
    
      //SPIByteWrite(RXF5SIDL, 0x08); //Setting EXIDE... the rest is "don't care" //JM
    
      //Configure TX BUFFER 0
      DataArray[0] =  0xA3;
      DataArray[1] =  0x09;
      DataArray[2] =  0x12;
      DataArray[3] =  0x34;
      DataArray[4] =  0x04;
      DataArray[5] =  0x00;
      DataArray[6] =  0x01;
      DataArray[7] =  0x02;
      DataArray[8] =  0x03;
      DataArray[9] =  0x04;
      DataArray[10] = 0x05;
      DataArray[11] = 0x06;
      DataArray[12] = 0x07;
    
    /*	
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(TXB0SIDH);
    		DataArray[1]=EEPROMBYTERead(TXB0SIDL);
    		DataArray[2]=EEPROMBYTERead(TXB0EID8);
    		DataArray[3]=EEPROMBYTERead(TXB0EID0);
    		DataArray[4]=EEPROMBYTERead(TXB0DLC);
    		DataArray[5]=EEPROMBYTERead(TXB0D0);
    		DataArray[6]=EEPROMBYTERead(TXB0D1);
    		DataArray[7]=EEPROMBYTERead(TXB0D2);
    		DataArray[8]=EEPROMBYTERead(TXB0D3);
    		DataArray[9]=EEPROMBYTERead(TXB0D4);
    		DataArray[10]=EEPROMBYTERead(TXB0D5);
    		DataArray[11]=EEPROMBYTERead(TXB0D6);
    		DataArray[12]=EEPROMBYTERead(TXB0D7);
    
    	}
    	else
    	{
    		EEPROMDataWrite(TXB0SIDH,&DataArray,13);
    	}
    */	
      SPILoadTX(CAN_LD_TXB0_ID, 13, DataArray); 
      
      //Configure TX BUFFER 1
      DataArray[0] =  0x22;
      DataArray[1] =  0x00;
      DataArray[2] =  0x64;
      DataArray[3] =  0x68;
      DataArray[4] =  0x02;
      DataArray[5] =  0x00;
      DataArray[6] =  0x11;
      DataArray[7] =  0x22;
      DataArray[8] =  0x33;
      DataArray[9] =  0x44;
      DataArray[10] = 0x55;
      DataArray[11] = 0x66;
      DataArray[12] = 0x77;
    /*	
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(TXB1SIDH);
    		DataArray[1]=EEPROMBYTERead(TXB1SIDL);
    		DataArray[2]=EEPROMBYTERead(TXB1EID8);
    		DataArray[3]=EEPROMBYTERead(TXB1EID0);
    		DataArray[4]=EEPROMBYTERead(TXB1DLC);
    		DataArray[5]=EEPROMBYTERead(TXB1D0);
    		DataArray[6]=EEPROMBYTERead(TXB1D1);
    		DataArray[7]=EEPROMBYTERead(TXB1D2);
    		DataArray[8]=EEPROMBYTERead(TXB1D3);
    		DataArray[9]=EEPROMBYTERead(TXB1D4);
    		DataArray[10]=EEPROMBYTERead(TXB1D5);
    		DataArray[11]=EEPROMBYTERead(TXB1D6);
    		DataArray[12]=EEPROMBYTERead(TXB1D7);
    
    	}
    	else
    	{
    		EEPROMDataWrite(TXB1SIDH,&DataArray,13);
    	}
    */	
      SPILoadTX(CAN_LD_TXB1_ID, 13, DataArray); 
    
      //Configure TX BUFFER 2
      DataArray[0] =  0xAA;
      DataArray[1] =  0x03;
      DataArray[2] =  0x33;
      DataArray[3] =  0x33;
      DataArray[4] =  0x08;
      DataArray[5] =  0xAA;
      DataArray[6] =  0xBB;
      DataArray[7] =  0xCC;
      DataArray[8] =  0xDD;
      DataArray[9] =  0xEE;
      DataArray[10] = 0xFF;
      DataArray[11] = 0x0A;
      DataArray[12] = 0x0B;
      SPILoadTX(CAN_LD_TXB2_ID, 13, DataArray);
    /*	
    	if(EEPROM_ver)
    	{
    		DataArray[0]=EEPROMBYTERead(TXB2SIDH);
    		DataArray[1]=EEPROMBYTERead(TXB2SIDL);
    		DataArray[2]=EEPROMBYTERead(TXB2EID8);
    		DataArray[3]=EEPROMBYTERead(TXB2EID0);
    		DataArray[4]=EEPROMBYTERead(TXB2DLC);
    		DataArray[5]=EEPROMBYTERead(TXB2D0);
    		DataArray[6]=EEPROMBYTERead(TXB2D1);
    		DataArray[7]=EEPROMBYTERead(TXB2D2);
    		DataArray[8]=EEPROMBYTERead(TXB2D3);
    		DataArray[9]=EEPROMBYTERead(TXB2D4);
    		DataArray[10]=EEPROMBYTERead(TXB2D5);
    		DataArray[11]=EEPROMBYTERead(TXB2D6);
    		DataArray[12]=EEPROMBYTERead(TXB2D7);
    
    	}
    	else
    	{
    		EEPROMDataWrite(TXB2SIDH,&DataArray,13);
    	}
    */	
      
      SPISeqRead(TXB1SIDH, 13, ReadArray);
        
      //Configure BIT TIMING
      SOF_ENABLED; //Start-of-Frame signal enabled. Will set when "SetBitTiming()" is called
    /*	
    	if(EEPROM_ver)
    	{
    	  DataArray[0] = EEPROMBYTERead(CNF3); //Load array
    	  DataArray[1] = EEPROMBYTERead(CNF2); //
    	  DataArray[2] = EEPROMBYTERead(CNF1); //
    	  SPISeqWrite(CNF3, 3, DataArray);  //Write registers
      
    	}
    	else
    */	
    	{
      	SetBitTiming(CAN_125kbps); //Note, this function puts device in Normal Mode
    		
    	}
      
      //Set Normal Mode and verify
      SET_NORMAL;
      SPIByteWrite(CANCTRL,CANCTRL_byte); //Write to CANCTRL
      old_CANCTRL = 0x00;
    	//EEPROMCRCWrite(0,128);
    
      //Verify Mode change
      if ((SPIByteRead(CANSTAT)& 0xE0) != OPMODE_NORMAL)
        SPIByteWrite(CANCTRL,CANCTRL_byte); //Try again
    }  
    
    /******************************************************************************
     * Function:        void SetBitTiming//(char data_rate)
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function sets the bit timing.
     *                  
     *
     * Note:            None
     *****************************************************************************/
    void SetBitTiming(char data_rate)
    {
      unsigned char a,b,c,dummy;
      //Set Config Mode and verify
      SET_CONFIG;         //Set Configuration mode
      SPIByteWrite(CANCTRL,CANCTRL_byte); //Write to CANCTRL
      //Verify Mode change
      if ((SPIByteRead(CANSTAT)& 0xE0) != OPMODE_CONFIG)
        SPIByteWrite(CANCTRL,CANCTRL_byte); //Try again
    
      //Need 10TQ based on 20 MHz osc
      //Will only need to modify BRP to acheive the 4 bit rates
      //First clear CNFn registers
      CNF1_byte = 0;
      CNF2_byte = 0;
      CNF3_byte = 0;
      //Now set individual bits
      SJW_1TQ;        //Sync Jump Width
      PRSEG_3TQ;      //Prop Segment
      PHSEG1_3TQ;     //Phase Seg 1
      PHSEG2_3TQ;     //Phase Seg 2
      BTLMODE_CNF3;   //Use CNF3 to set Phase Seg 2
    
      SOF_ENABLED; //Start-of-Frame signal enabled. Will set when "SetBitTiming()" is called
    
      //Configure Baud Rate Prescaler
      switch(data_rate)     // interpret command
      {
        case CAN_125kbps:   // Set to 125 kbps
          BRP7;             //
        break;
    
        case CAN_250kbps:   // Set to 250 kbps
          BRP3;             //
        break;
    
        case CAN_500kbps:   // Set to 500 kbps
          BRP1;             //
        break;
    
        case CAN_1000kbps:  // Set to 1000 kbps
          BRP0;             //
        break;
    
        default:                    // unrecognized or null command
          ;
      }// END switch: data_rate
    
      //Now write the CNFn registers
      DataArray[0] = CNF3_byte; //Load array
      DataArray[1] = CNF2_byte; //
      DataArray[2] = CNF1_byte; //
      SPISeqWrite(CNF3, 3, DataArray);  //Write registers
      
      //Set Normal Mode and verify
      SET_NORMAL;
      SPIByteWrite(CANCTRL,CANCTRL_byte); //Write to CANCTRL
      //Verify Mode change
      if ((SPIByteRead(CANSTAT)& 0xE0) != OPMODE_CONFIG)
        SPIByteWrite(CANCTRL,CANCTRL_byte); //Try again
    	EEPROMDataWrite(CNF3,&DataArray,3);
    }
    
    
    /******************************************************************************
     * Function:        void CANLoadTX(void)
     *
     * PreCondition:    None
     *
     * Input:           None
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function transmits messages on the CAN bus depending
     *                  on the Load Button setting
     *
     * Note:            
     *****************************************************************************/
    void CANLoadTX(void)
    {
      static char buffer_q;
    
      if(UserFlag.CANLoading) //if bus loading is turned on
      {
      
        CANLoadTimer++; //Increment the counter
        if (!CANLoadTimer)      //if rolled over, set flag. User code will handle the rest.
        {
          CANLoadTimer = LoadPrescaler;
          switch(buffer_q)           // interpret command
          {
            case 0:          // Send buffer 0
              RTS0_2515_LOW();
              RTS0_2515_HIGH();
              buffer_q = 1;   //For next time around
            break;
            case 1:          // Send buffer 1
              RTS1_2515_LOW();
              RTS1_2515_HIGH();
              buffer_q = 2;   //For next time around
            break;
            case 2:          // Send buffer 2
              RTS2_2515_LOW();
              RTS2_2515_HIGH();
              buffer_q = 0;   //For next time around
            break;
            default:                  // unrecognized or null command
              buffer_q = 0;
          }// END switch: 
        }//END: if (!CANLoadTimer)
      }//END: if(UserFlag.CANLoading)
    }
    
    /******************************************************************************
     * Function:        void EEPROMBYTEWrite(char addr,char data)
     *
     * PreCondition:    None
     *
     * Input:           data and address
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function writes a byte to EEPROM in addr
     *
     * Note:            
     *****************************************************************************/
    	void EEPROMBYTEWrite(char addr,char data)
    {
    	char buf_con=INTCON;
    	return;
    	EEADR=addr;
    	EEDATA=data;
    	EECON1bits.EEPGD=0;
    	EECON1bits.CFGS=0;
    	EECON1bits.WREN=1;
    	INTCONbits.GIE=0;
    	EECON2=0x55;
    	EECON2=0xaa;
    	EECON1bits.WR=1;
    	while(EECON1bits.WR==1);
    	EECON1bits.WREN=0;
    	//INTCONbits.GIE=1;
    	INTCON=buf_con;
    
    	
    }
    /******************************************************************************
     * Function:        void EEPROMDataWrite(char addr,char* data,char len)
     *
     * PreCondition:    void EEPROMBYTEWrite(char addr,char data)
     *
     * Input:           begin address,data address,data length
     *
     * Output:          None
     *
     * Side Effects:    None
     *
     * Overview:        This function writes a byte to EEPROM in addr
     *
     * Note:            
     *****************************************************************************/
    	void EEPROMDataWrite(char addr,char* data,char len)
    {
    	unsigned char i;
    	for(i=0;i<len;i++)
    	{
    		EEPROMBYTEWrite(addr+i,data[i]);
    	}
    }
    
    /******************************************************************************
     * Function:        void EEPROMBYTERead(char addr,char data)
     *
     * PreCondition:    None
     *
     * Input:           address
     *
     * Output:          data
     *
     * Side Effects:    None
     *
     * Overview:        This function writes a byte to EEPROM in addr
     *
     * Note:            
     *****************************************************************************/
    
    	char EEPROMBYTERead(char addr)
    {
    	EEADR=addr;
    	EECON1bits.EEPGD=0;
    	EECON1bits.CFGS=0;
    	EECON1bits.RD=1;
    	return EEDATA;
    	
    }
    
    /******************************************************************************
     * Function:        void EEPROMDataRead(char addr,char data)
     *
     * PreCondition:    None
     *
     * Input:           address in EEPROM,buffer address,data length
     *
     * Output:          data
     *
     * Side Effects:    None
     *
     * Overview:        This function writes a byte to EEPROM in addr
     *
     * Note:            
     *****************************************************************************/
    	void EEPROMDataRead(char addr,char* data,char len)
    {
    	unsigned char i;
    	for(i=0;i<len;i++)
    	{
    		data[i]=EEPROMBYTERead(addr+i);
    	}
    }
    
    
    /******************************************************************************
     * Function:        void EEPROMCRCWrite(char addr,char len)
     *
     * PreCondition:    None
     *
     * Input:           address in EEPROM,data length
     *
     * Output:          data
     *
     * Side Effects:    None
     *
     * Overview:        This function writes a byte to EEPROM in addr
     *
     * Note:            
     *****************************************************************************/
    
    	void EEPROMCRCWrite(char addr,char len)
    {
    	unsigned char buf,i;
    	buf=0;
    	for(i=0;i<len;i++)
    	{
    		buf+=EEPROMBYTERead(i+addr);
    	}
    	EEPROMBYTEWrite(128,buf);
    	
    }
    /******************************************************************************
     * Function:        bool EEPROMCRCCheck(char addr,char len)
     *
     * PreCondition:    None
     *
     * Input:           address in EEPROM,data length
     *
     * Output:          true for checksum equal
     *
     * Side Effects:    None
     *
     * Overview:        This function writes a byte to EEPROM in addr
     *
     * Note:            
     *****************************************************************************/
    
    	BOOL EEPROMCRCCheck(char addr,char len)
    {
    	unsigned char buf,i;
    	buf=0;
    	for(i=0;i<len;i++)
    	{
    		buf+=EEPROMBYTERead(i+addr);
    	}
    
    	if(buf==EEPROMBYTERead(128))
    	{
    		return TRUE;
    	}
    	else
    	{
    		return FALSE;
    	}
    	
    }
    
    /** EOF BusMon.c********************************************************/
    But I took another look at your code.... At the very beginning, in the INITIALIZE MCP2515 section you have this piece of code:
    Code:
    LOW CS
    SSPBUF = MCPRST ' COMMAND TO RESET THE MCP2515
    PIR1.3 = 0
    WHILE PIR1.3=0 :WEND
    HIGH CS
    I then went to see exactly what MCPRST was and the only thing I found in your code was an alias to PortC.0 so what's happening in that piece of code is that the PIC reads PortC.0 and then the result of that read operation (1 or 0) to SSPBUF. To reset the device you either pull the hardware reset line LOW or send $C0 over SPI.
    I tried $C0 and pulling the reset line low, both of which didn't seem to work. The problem is, I was trying multiple things at the same time and I ended up just putting the MCPRST command thinking it was $C0 when I meant to use CAN_RST, which is already in my code! Good catch! Maybe I should not work on this until 1am tonight!

    Properly resetting the device automatically puts in the configuration mode so therotically you shouldn't need to put it in that mode directly after a (proper) reset. Then your comments says that you're writing to "part of baudrate" or something but $07 is the EXTENDED IDENTIFIER LOW register receiver 1, $03 is EXTENDED IDENTIFIER LOW register for receiver 0 and I can't even find what $BA is.
    I was confused about this too so I searched the internet for someone who has written this code properly AND it worked. I found this:

    Code:
    Public Sub mcp2515_CAN_Freq()
        mcp_assert_cs()                         ' start comms to MCP2515
       spi_transfer(SPI_WRITE)                 ' send write command
       spi_transfer(CNF3)                      ' address
       spi_transfer(config3)                   ' CNF3       
       spi_transfer(config2)                   ' CNF2     
       spi_transfer(config1)                   ' CNF1
        mcp_deassert_cs()                       ' stop comms to MCP2515
    End Sub
    This portion of C code seems self explanatory but configuring CNF1, CNF2 AND CNF3 was confusing. They talk about propagation segment, bit time length, Fosc, BRP, etc. It's all new to me and confusing so I went with values I found on the internet, which were $07, $BA and $03. It has to do with timing the MCP frequency with the oscillator frequency and I *think* those numbers are correct.

    OK, moving to MAIN then....
    You send the command to write then you send the value 0 to CANINTE at adress $02, that all seems OK to me if disabling interrupts is what you want to do.
    For now I just want to send something so I'm disabling the interrupts.

    Next you set the TXB0CTRL register for a high priority message and request it to send. Then you write the TRANSMIT BUFFER STANDARD IDENTIFIER register. I have no idea what these does so I'm going to asume it's correct. Then, you tell it you want to send a single byte by writing the value 1 to TXB0DLC and then you write the actual data.
    The SID, if I understand correctly, is the ID of the message. I've circled it in red and I think this is the SID:Name:  ID for MCP2515.JPG
Views: 1173
Size:  75.3 KB

    Personally I would've thought you should first load the data to be sent THEN tell the device to send it. Ie write TXB0CTRL after you've loaded the data but the way you have it might be correct.
    I'll play around with it and see what works better.

    I don't understand what Fosc command to use. I have 20 MhZ oscillator. Do I use:

    SSPM3:SSPM0: Master Synchronous Serial Port Mode Select bits
    0101 = SPI Slave mode, clock = SCK pin, SS pin control disabled, SS can be used as I/O pin(3)
    0100 = SPI Slave mode, clock = SCK pin, SS pin control enabled(3)
    0011 = SPI Master mode, clock = TMR2 output/2(3)
    0010 = SPI Master mode, clock = FOSC/64(3)
    0001 = SPI Master mode, clock = FOSC/16(3)
    0000 = SPI Master mode, clock = FOSC/4(3)
    Thanks for all of your help Henrik, I wouldn't have gotten this far without it.

  16. #16
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi Christopher,

    OK, hang on a minute.... I may have spotted something here.
    Lets take a look at the SPI section of the MCP2515 datasheet again, section 12.5
    The Write instruction is started by lowering the CS pin. The Write instruction is then sent to the MCP2515 followed by the adress and then at least one byte of data.
    Now let's take a look at your configuration section again, with my comments in it.
    Code:
    LOW CS			' The Write instruction is started by lowering the CS pin                         
    SSPBUF = mcpwrt		' The Write instruction then sent to the MCP2515
    
    PIR1.3 = 0		' Wait for it to tranfer              
    WHILE PIR1.3=0 :WEND
    
    MCPREG =   CANCTRL	' followed by the adress
    DATAOUT =  $08		' and at least one byte of data.			
    
    GOSUB SEND_CAN_DATA
    So far so good, as far as I can see. Now lets continue where we left off in the datasheet:
    It is possible to write sequential registers by continuing to clock in data bytes, as long as CS is held low.
    Keyword here: sequential. Now back to your code again:
    Code:
    SSPBUF = cnf3                   ' CNF3 REGISTER
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    SSPBUF = $07                    ' PART OF BAUD RATE
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    SSPBUF = $BA                    ' PART OF BAUD RATE
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    SSPBUF = $03                    ' PART OF BAUDRATE
    PIR1.3 = 0              
    WHILE PIR1.3=0 :WEND
    
    MCPREG =   CANCTRL              'PUT THE MCP2515 BACK INTO NORMAL MODE
    DATAOUT =  $0
    GOSUB SEND_CAN_DATA
    
    high cs
    If I understand this correctly the intention of your code is to write the value $07 to register CNF3 (at adress $28) but since you haven't deasserted the CS line the next byte transfered to the MCP2515 will automatically get written to next adress (right after CANCTRL in this case). So what actually happens is that the value $28 (which is the adress of CNF3) gets written to the register after CANCTRL (CANCTRL is at $0F so $28 will end up at adress $10) and then value $07 gets written to the register after that ($11).

    If you intend to write to registers which aren't sequential in the MCP2515 I think you need must deassert the CS line and start all over again: CS low, write instruction, adress, data.


    I'd try with the slowest SPI clock possible to begin with. Ie SSPM3:SSPM0 = 0010

    /Henrik.

  17. #17
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Success! .........sort of

    I've been over the sequential write portion of code more times than I can count and it may have something to do with the problem. I was just now able to succeed sending data over the CAN! While the information isn't correct, some of the numbers I'm putting in are coming up on the computer so that's a good thing.

    I think the sequential write/bit modify stuff has something to do with it. Where I'm expecting data (i.e. - the data for byte one (D0) in the data field), I'm seeing the address. Perhaps it is expecting just to run through the data and not have to address each variable when I load the buffer. That may be why I was overloading the number of bytes and crashed the Microchip interface. Instead of seeing 8 bytes of data, it was seeing 16.

    First I'm going to SAVE this code! Then I'll start to modify the code a little bit and work my way up to two-way communication with all of the little additions needed to have an error free CAN node. If it took me this long to get something going, I can't imagine how long it will take to write the full code

    But, at least I'm past step one, which hopefully was the hardest. Again, thank you so much for your help. I'll be back sooner rather than later to ask a million more questions.

    Chris

  18. #18
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Success! .........sort of

    Microchip does it like this:
    Code:
    SPILoadTX(CAN_LD_TXB0_ID, 13, DataArray); 
      
      //Configure TX BUFFER 1
      DataArray[0] =  0x22;
      DataArray[1] =  0x00;
      DataArray[2] =  0x64;
      DataArray[3] =  0x68;
      DataArray[4] =  0x02;
      DataArray[5] =  0x00;
      DataArray[6] =  0x11;
      DataArray[7] =  0x22;
      DataArray[8] =  0x33;
      DataArray[9] =  0x44;
      DataArray[10] = 0x55;
      DataArray[11] = 0x66;
      DataArray[12] = 0x77;

  19. #19
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Hi,
    For transfering the actual data to be sent and setting up the packet lenght etc I'd probably try something like this:
    Code:
    CAN_DATA VAR BYTE[8]
    CAN_LENGTH VAR BYTE
    CAN_ID_H VAR BYTE
    CAN_ID_L VAR BYTE
    i VAR BYTE
    
    ' Prepare a message
    ArrayWrite CAN_DATA[$11, $22, $33, $44, $55, $66, $77, $88]  ' Actual data, 8 bytes
    CAN_LENGTH = 8   ' 8 because that's how many bytes we want to transfer
    CAN_ID_H = 0
    CAN_ID_L = 0
    
    GOSUB Transfer_CAN_Data
    
    END
    
    '----Subrotuine to set the packet length, Standard identifiers, load the data to be sent from array and tell MCP2515 to send it when it can (CAN).
    Tranfer_CAN_Data:
    
    	LOW CS					' Select device
    
    	SSPBUF = MCPWRT : PAUSE 1		' Send write command
    	SSPBUF = TXB0SIDL : PAUSE 1		' Select TXB0SIDL register.
    	SSPBUF = CAN_ID_L : PAUSE 1		' Send low byte of standard identifier	
    
    	' Next register is te TXB0SIDH so we can continue spitting out bytes.
    	SSPBUF = CAN_ID_H : PAUSE 1		' Send high byte of standard identifier
    
    	HIGH CS					' Deselect device to end write operation
    	PAUSE 10
    	LOW CS					' Select device
    
    	' Now set the Transmit bufferdata length register
    	SSPBUF = MCPWRT : PAUSE 1		' Send write command
    	SSPBUF = TXB0DLC : PAUSE 1		' Point at register
    	SSPBUF = CAN_LENGTH : PAUSE 1		' Send length of packet
    
    
    
    	' The first databyte register in the TX buffer is located right after the message
    	' length register so we can use the sequential write feature and just keep going
    	' with the actual data.	
    
    	FOR i = 0 to CAN_LENGHT - 1		' Iterate thru the array and transfer
    		SSPBUF = CAN_LENGTH[i]		' one byte at the time
    		PAUSE 1				' Wait for it
    	NEXT
    
    	HIGH CS					' Deselect device to end write operation
    	PAUSE 10
    	LOW CS					' Select device
    
    	' Now the the message lenth is set and the data to be sent is transfered.
    
    	SSPBUF = MCPWRT : PAUSE 1		' Send write command
    	SSPBUF = TXB0CTRL : PAUSE 1		' Transmitt buffer control register
    	SSPBUF = %00001011 : PAUSE 1		' Request message to be sent with highest priority
    
    	' According to the datasheet this does NOT inititare the actual transfer. It politely
    	' tells the MCP2515 that the databuffer is ready and can be sent whent the bus is available.
    
    RETURN
    Now, I don't know about the Standard Identifier, if they need to be set on a message per message basis or if they are like a node adress or something. In the code above they gets set each time which might be unneccessary. I'm also using PAUSE to wait for the MSSP module, looking at the interrupt flag or buffer free flag as you've been doing is obviously better but it cluttered up the overall structure so I used PAUSE for clarity.

    Not saying it'll work but that's what I'd try.

    /Henrik.

  20. #20
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    I still have version 2.5. I think you need 2.6 for arraywrite, correct? I have a program that will only work with 2.5 so I haven't moved past that point yet but it looks like I may need to. Is there a similar command that will work?

  21. #21
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,557


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Just load the array manually
    Code:
    CAN_DATA[0] = $00
    CAN_DATA[1] = $11
    CAN_DATA[2] = $22
    ' and so on

  22. #22
    Join Date
    Oct 2005
    Location
    New Jersey
    Posts
    425


    Did you find this post helpful? Yes | No

    Default Re: Datasheet and register question

    Something changed and I can't figure out what I did. My waveforms appear to match the Microchip program, except for one. The SDO from the 18F4550 is at 5V instead of 0V. I can't find any reference to change that. The only thing I've found is commands to change the clock signal but nothing for the SDO. Any ideas?

    I'm very close now.

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts