Netduino home hardware projects downloads community

Jump to content


Photo

SPI Configuration questions


  • Please log in to reply
6 replies to this topic

#1 curado

curado

    New Member

  • Members
  • Pip
  • 8 posts

Posted 24 May 2012 - 04:15 AM

Hi. I've been enjoying working with the Netduino Plus for a car computer integration. Looking to communicate with a total of 4 SPI slave devices. 2 input devices / 2 output devices. Is it okay to initialize 4 SPI.Configuration objects and keep them in memory? Each needs its own chip select. Is there a practical limit to how many devices can be on the bus before communication becomes unreliable? My bigger issue at the moment is the clock speed. It's looking like 380 khz is the minimum speed when set to a value of 1 for the 7th parameter. This is a little too fast for the PIC chips I'm trying to clock the data into. Any way to slow it down? Bit banging slows it down way too much. Orders of microseconds vs milliseconds. Need a happy medium! Thanks for any input. Or output was the case may be :)

#2 curado

curado

    New Member

  • Members
  • Pip
  • 8 posts

Posted 25 May 2012 - 04:43 AM

Looking at the bus with a logic analyzer... at the minimum clock speed, seeing clock width of 1.333 microseconds from the Netduino. Running the PIC16F648A with a 16 mhz crystal, the instruction execution time is measured to be 0.25 microseconds. Does not look sufficient to be able to reliably frame the SPI output from the netduino in assembly at this clock speed. No way to slow the Netduino output down to say maybe 50 khz?

#3 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1676 posts
  • LocationVenezia, Italia

Posted 25 May 2012 - 05:15 AM

Why not using the UART instead of SPI?
Biggest fault of Netduino? It runs by electricity.

#4 nakchak

nakchak

    Advanced Member

  • Members
  • PipPipPip
  • 404 posts
  • LocationBristol, UK

Posted 25 May 2012 - 07:35 AM

As Mario has suggested why not use a UART to do the comms? Although the ND only has 2 serial ports you could still use a SPI UART like the MAX3100 so you can still address the 4 slaves...

#5 curado

curado

    New Member

  • Members
  • Pip
  • 8 posts

Posted 25 May 2012 - 02:51 PM

OBD-II is occupying one of the USARTS. Thanks for the suggestion on the MAX3100. I was wondering what I was going to do about that if I want to add a GPS. That will make at least 5 SPI devices on the bus then. The PIC16F648A's can be configured in SPI Slave mode. It looks complicated to get going. And I have been using socket programming, not ICSP/ICD to do this. Uggggg. I'm wondering how many slaves would fit on the bus before a multiplexer is required?

#6 curado

curado

    New Member

  • Members
  • Pip
  • 8 posts

Posted 28 May 2012 - 01:58 AM

Strike the above. The PIC16F648A has no built in provisions for SPI. Only a USART. It would appear that I am limited to bit banging SPI, or using a MAX3100 as suggested.

For note:
// N
// 2 = 2 us
// 3 = 1.33 us
// 4 = 2.33 us
// 5  = 1.333 us
// 6 = 0.667 us
// 7 = 2.083 us
// 8 =  1.167 us
// 9  = 2.25 us
// 10 = 2 us
// 11 = 0.125 us
// 12 = 1.667 us
// 18 = 1.125 us
// 20 = 1 us
// 50 = 2 us
// 51 = 1.79 us
// 100 = 2.33 us
// 10000 = 42 ns

SPI.Configuration spi_config = new SPI.Configuration( Pins.GPIO_PIN_D10 ,  false, 500, 1, true, false,  [b]N[/b], SPI.SPI_module.SPI1);

Various values are given for N and their corresponding bit widths, 2.33 microseconds being the largest observed, so this means a period of 4.66 microseconds between bits. I am assuming 0.25 microseconds per cycle of the PIC16F648A at 16 mhz. So this means with the quickest possible loop, up to 0.75 microseconds could pass after the falling edge of the clock.

	// PORT B 16F648A
	// BIT PIN  I/O     FUNC    DESCRIPTION
	// RB0 6	INPUT	CS		SPI SLAVE CPU2 INTERRUPT
	// RB1 7	INPUT
	// RB2 8	INPUT	SCK		SPI SLAVE CLOCK
	// RB3 9	INPUT	SI		SPI SLAVE DATA
	// RB4 10	INPUT
	// RB5 11	OUTPUT	FREQ	MPH PULSE OUT
	// RB6 12	INPUT	ENB		CPU1 ENABLE SIGNAL
	// RB7 13	INPUT

	// interrupt vector
	// at this point, clock is assumed high and chip select is low
	// global interrupt handling is disabled
	// master is about to clock in data on the SPI bus which will last about 75 microseconds total (0.075 ms)
	// bit widths of 2.33 microseconds / clock period of 4.66 microseconds
	// cycle width measured to be 0.25 microseconds at 16 mhz

_asm
			banksel _PORTB		; working with port B registers (call once?)

BIT1_LOOP

			BTFSC _PORTB, 2		; skip next line if RB2 clock is 0
								; cycles: 2

			GOTO BIT1_LOOP		; clock is still high, remain in loop
							    ; cycles: 1

							    ; total time: up to 0.75 us after falling edge of clock

			MOVF _PORTB, 0		; w = _PORTB
								; cycles: 1

			BTFSC _PORTB, 3		; skip next line if RB3 is clear
								; cycles: 2

			BSF data, 0			; set bit 0 of data high
								; cycles: 1

								; total time: up to 1.75 us after falling edge of clock

BIT2_LOOP

; .......


_endasm

Unless I've completely missed something, I think the above would do it? I wrote assembly for one semester in college and have never used the PIC opcodes ... uncharted waters for me.

edit:

Looking at the PIC16F648A data sheet, interrupt latency is the same regardless whether processing a 1 cycle or 2 cycle instruction. Interrupt should trigger within 2 cycles of chip select assertion (worst case). So assuming cycle time of 0.250 microseconds and using a frequency that yields bit width of 1.000 microseconds - is the SPI clock pin even required? Only talking 16 bits total, and using a crystal as the clock source of the PIC.

#7 curado

curado

    New Member

  • Members
  • Pip
  • 8 posts

Posted 29 May 2012 - 05:29 AM

Here's the test code so far, with the expectation of receiving 2 bytes into data[0] and data[1]:

void main()
{
    TRISA = 0b11111101;
    TRISB = 0b00001100; //output = 0

        //int bits = 0;
        unsigned char data[2] = {0, 0};
        unsigned char bitc = 8; // bits remaining counter
        unsigned char bfsr = 0;
        unsigned char bytec = 2; // bytes remaining counter

        RA1 = 1; // turn on led

    // PORT B 16F648A
    // BIT PIN  I/O     FUNC    DESCRIPTION
    // RB0 6    INPUT    CS        SPI SLAVE CPU2 INTERRUPT
    // RB1 7    INPUT
    // RB2 8    INPUT    SCK        SPI SLAVE CLOCK
    // RB3 9    INPUT    SI        SPI SLAVE DATA
    // RB4 10    INPUT
    // RB5 11    OUTPUT    FREQ    MPH PULSE OUT
    // RB6 12    INPUT    ENB        CPU1 ENABLE SIGNAL
    // RB7 13    INPUT

    // interrupt vector
    // at this point, clock is assumed high and chip select is low
    // global interrupt handling is disabled
    // master is about to clock in data on the SPI bus which will last about 75 microseconds total (0.075 ms)
    // bit widths of 2.33 microseconds / clock period of 4.66 microseconds
    // cycle width measured to be 0.25 microseconds at 16 mhz
   
    _asm

                banksel _PORTB        ; working with port B register file (call once?)
   
                                    ; backup the FSR register content:
                MOVF fsr, w            ; [w] = [FSR register]
                MOVWF main@bfsr, f    ; [main@bfsr] = [w]

                MOVLW main@data        ; move address k of [main@data] into w
                MOVWF fsr, w        ; [FSR register] = [w]


    INPUT_LOOP:
                BTFSC _PORTB, 2        ; skip next line if RB2/clock goes low
                GOTO INPUT_LOOP        ; clock is still high, remain in loop

    STORE_BIT:
                                    ; sample the input on PORTB
                MOVF _PORTB, w        ; [w] = [_PORTB]

                   
                BTFSC _PORTB, 3        ; skip next line if RB3/data is 0

                RLF indf, f            ; indirectly rotate [main@data] to the left

                BSF indf, 0            ; indirectly set bit 0 of [main@data] to 1

                                    ; stop watch at this point from INPUT_LOOP:
                                    ; best case total time since falling edge 1.50 usec
                                    ; worst case total time since falling edge 2.25 usec
   
    DEC_BITC:

                DECFSZ main@bitc    ; decrement remaining bits. skip next statement if zero.
                GOTO BRIEF_DELAY    ; ensures that clock has gone high before returning to input_loop.

    ADVANCE_BYTE:                    ; 0 bits remaining of this byte

                                    ; reset bit counter to 8
                MOVLW 8                ; [w] = 8
                MOVWF main@bitc        ; [main@bitc] = [w]

                INCF fsr, f            ; advance FSR register one position (currently pointing at [main@data array])
               
                DECFSZ main@bytec    ; decrement [main@bytec] bytes remaining counter
                GOTO INPUT_LOOP        ; bytes remaining > 0. return to loop start
                GOTO EXIT_READ        ; bytes remaining = 0. Exit

    BRIEF_DELAY:
                NOP
                GOTO INPUT_LOOP



EXIT_READ:

                                    ; restore original content of FSR register
                MOVF main@bfsr, w    ; [w] = [main@bfsr]
                MOVF fsr, f            ; [FSR] = [w]

; wait until clock goes high before enabling interrupts again

Something is going wrong with the statement:

MOVLW main@data        ; move address k of [main@data] into w

W is not updated, but the next instruction executes. I am trying to load the address of main@data[0]. Not working for me!

Also I am getting some other weird behavior going on with the program counter. It's not starting out at main() like it normally would. Execution starts in the middle of my code at some unexpected offset. I am unsure how to correct for this.

Probably the wrong forum for this being that it's going in depth into PIC, but people here seem knowledgeable!




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2010-2014 Secret Labs LLC  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.