Proton BASIC Compiler - Software SPI

  • PicŪ Basic

  • Software SPI

    The Serial Peripheral Interface (SPI) is a synchronous serial communication interface and was developed by Motorola to allow its chips to communicate with each other at very high speeds. SPI has some advantages, in particular support for higher data transfer rate. SPI also has a duplex capability which makes it particularly suitable for those applications that require simultaneous two-way communication, using a master-slave architecture with a single master.

    Bit banging is a technique for serial communications using software instead of dedicated hardware. Software directly sets and samples the state of pins on the microcontroller, and is responsible for all parameters of the signal: timing, levels, synchronization, etc. Bit-banging carries a software overhead consuming CPU cycles.
    The bit banging method has another consequence for SPI, the clock may be completely asymmetrical. No reason for alarm unless the peripheral is too slow to accept this clock as fast.

    However in each command, a number of Nops have been added to symmetrize the clock. The direct consequence is that the clock is slowed. This function can be enable/disable: $Define SCKsym. I do not know if this function is useful, however can be disabled. The PDS DelayCS command has not been used because the compiler introduces too much (clrwdt) lines of code when the "Declare Watchdog = On" is enabled, especially in the BB2 version. A single line (clrwdt) is added to the beginning of each subroutine when this function is activated.
    Similarly the code takes into account the use of interrupts by disabling the GIE bit. Anyway, the code is so basic that an interruption, if short, will have little impact on the transfert of data (I hope), anyway I introduced the automatic disable/enable GIE bit code inside the subroutines.

    All timing measurements refer to the use of PIC18xxKxx series with clock running at 64 Mhz (16 Mips).
    In this file, the method described uses a loop to simplify the code. It is based on an article written by Les and Tim and published in the forum. This method is very efficient and using small number of lines of code. It is easy to use and can write/read Byte, Word and Dword variables. But within these constraints may make some modifications such as write/read 13 bits, for example by modifying the 16-bit version (Word), modifying only 2 parameters (Choose the correct variable and change the number of loops).
    In this procedure the instruction code is written bit by bit as simple as possible. It is the hard way to write code but can increase up to twice the speed. Another disadvantage is the number of lines of code that is very high. But it is easy to use and can write/read Byte, Word, 3 Bytes and Dword variables, without constraint about the number of bits.
    For example using an existing code, it is possible to modify the number of bits copying and pasting the bit code.
    Some peripherical, like an absolute encoders, could needs n + 1 clock bits (n = number of bits data), changing the format of the SPI standard protocol, that could be resolved with further handling of the bits. This is the code that could be chosen for maximum bus speed SPI by software. It will depend on the clock used.

    Write 8 bits with symetrical clock.

    Write 8 bits with not symetrical clock.

    Include the .inc file at the top of your basic program, after the variables declaration. Before the file, enable the different mode of SPI protocol available in the library.

    Declaration of the SPI pins:
    This library allows to use any PIC pins. The user will care the analog interfaces not interfere to the SPI bus.
    The SPI pins must be defined by the preprocessor before the .inc file.
    You also need to define one or more Chip Select pin for peripherals. Five Chips Select are provided in the library to be configured correctly.
    Example how to define a Chip Select pin: $define SPI_CS0_Pin LATC.0

    Initializing the PIC, corresponding pins are initialized as inputs/outputs and high/low state as defined.
    The user does not have to worry about the SPI bus, the library makes all is running. However, the SPI bus pins cannot be manipulated individually after the .inc file, which could affect the operation. Always use the commands of the library. The code has been written for PIC18 only.

    Configuration of the SPI bus pins.
        ' Configure the pins for SDO, SDI and SCK
        $define SPI_SDI_Pin PORTC.4
        $define SPI_SDO_Pin LATC.5
        $define SPI_SCK_Pin LATC.3    
        ' Configure the pins for the Chips Select.
        $define SPI_CS0_Pin LATC.0
        $define SPI_CS1_Pin LATC.1
        $define SPI_CS2_Pin LATC.2
        ' ANSEL Pins: Write to the registers to set every pin Digital.
        $define SDI_Ansel_Pin ANSELC.4
        $define SDO_Ansel_Pin ANSELC.5
        $define SCK_Ansel_Pin ANSELC.3
        $define CS0_Ansel_Pin ANSELC.0
        $define CS1_Ansel_Pin ANSELC.1
        $define CS2_Ansel_Pin ANSELC.2  
        $define SCKsym
        $define SPI_Mode0
    '    $define SPI_Mode1    
    '    $define SPI_Mode2
    '    $define SPI_Mode3 
    Include ""
    SPI initialization:
    The configuration of all pins are made when the PIC is initialized. You can see that 5 Chip Selects were prepared.
    ' Configure the SPI lines to digital,if needed.
        $ifdef SDO_Ansel_Pin
        SDO_Ansel_Pin = 0
        $ifdef SDI_Ansel_Pin    
        SDI_Ansel_Pin = 0
        $ifdef SCK_Ansel_Pin    
        SCK_Ansel_Pin = 0
        $ifdef CS0_Ansel_Pin    
        CS0_Ansel_Pin = 0
        $ifdef CS1_Ansel_Pin    
        CS1_Ansel_Pin = 0
        $ifdef CS2_Ansel_Pin    
        CS2_Ansel_Pin = 0
        $ifdef CS3_Ansel_Pin    
        CS3_Ansel_Pin = 0
        $ifdef CS4_Ansel_Pin    
        CS4_Ansel_Pin = 0
    ' TRIS the SPI lines.
        Output SPI_SDO_Pin
        Output SPI_SCK_Pin
        Input SPI_SDI_Pin
    ' Configures the IDLE STATE of SCK.
    $if _defined(SPI_Mode0) Or _defined(SPI_Mode1)
        Clear SPI_SCK_Pin   
    $if _defined(SPI_Mode2) Or _defined(SPI_Mode3)
        Set SPI_SCK_Pin   
    ' Configures the IDLE STATE of the Chip Select pins.
    $ifdef SPI_CS0_Pin
        High SPI_CS0_Pin
    $ifdef SPI_CS1_Pin
        High SPI_CS1_Pin
    $ifdef SPI_CS2_Pin
        High SPI_CS2_Pin
    $ifdef SPI_CS3_Pin
        High SPI_CS3_Pin
    $ifdef SPI_CS4_Pin
        High SPI_CS4_Pin

    Commands available:

    Put the Chip Select pin number 0 to a low level to select a peripheral.

    Put the Chip Select pin number 0 to a high level to disable a peripheral.

    Send a byte (ByteOut) to the SPI bus.

    ByteIn = SPI_WriteRead(ByteOut)
    Send a byte (ByteOut) to the SPI bus and get a byte (ByteIn) from the SPI bus in the same time.

    WordIn = SPI_WriteRead16(WordOut)
    Send 16 bits (WordOut) to the SPI bus and get 16 bits (WordIn) from the SPI bus in the same time.

    ByteIn = SPI_Read()
    Receive a byte (ByteIn) from the SPI bus.

    WordIn = SPI_Read16()
    Receive 16 bits (WordIn) from the SPI bus.

    WordIn = SPI_Read13()
    Receive 13 bits (WordIn) from the SPI bus. It is an example how the user can make modifications to read 9 to 16 bits from the SPI bus. The bits of WordIn are right justified.

    Send a byte (ByteOut) to the SPI peripheral 0.

    Write a command byte (8 bits) to the SPI peripheral 0 and then receive 16 bits data.
    WordIn = SPI_Read16()

    You could modify and write new as many commands as you need in your project.


    The proposed commands are basic. The code is simple to understand, it would be easy to adapt any routine to meet your needs. I inserted an ISIS schematic to simulate the Software SPI bit banging code.
    If you want to post a comment, please better do it in the WIKI Discussion section.
    Good luck.