Various stuff happens in the SPI module that indicate errors. Here is a short description of what is going on.
Double buffering
SPI on the PIC16F uses a double buffer consisting of SSPxSR and SSPxBUF.
SSPxSR is the shift register directly connected to the input, it receives bits when the clock runs. Once 8 bytes have been received, it transfers its content to SSPxBUF, sets the SSPxSTAT.BF (buffer full) bit and raises the SSPxIF interrupt. The BF bit is cleared automatically when reading SSPxBUF.
Data to be transferred is written to SSPxBUF and shifted out either immediately (for master) or the next time the other device runs the clock (for slave).
The following two bits indicate errors in transmission:
SSPxCON1.WCOL
Write Collision. Is set if the user tries to write a new byte to SSPxBUF before the current one has been shifted out/transferred. The data written to SSPxBUF is ignored, and all future writes are also ignored until the WCOL is cleared by the user.
SSPxCON1.SSPOV
Receive overflow. Is set if a new byte is received before the old one is read. Only used when device is an SPI slave. Slave must read SPPxBUF even before transfers to prevent the setting of this bit. On overflow, the current byte in SSPxSR is lost.
In my DCO I check this when SS is driven high, before copying received data to where the user program expects to find it.
SSPxCON3.BOEN
Buffer overwrite enable. If set to high, data is written to the SSPxBUF even if the current byte has not been read. The datasheet does not say explicitly that it does or does not set the SSPOV bit, but it can safely be ignored. BOEN is used if we want to chain SPI slaves as one long shift register - data written to the slave will automatically be transferred to the SSPxBUF and shifted out once the next byte is received, without being touched by the user code. This way, one can keep the slave select pin low, transfer any number of bytes corresponding to the number of slaves connected, and then raise the slave select pin once all data has been transferred.
This is NOT used for my DCO as it will receive three bytes for each slave before the SS is driven high and thus has its own logic for shifting data internally.
SS pin
The slave select pin synchronises reception. When in use it will be held high until the master is ready to communicate. The master will lower the pin, then transfer data and finally rise it. Using the SS pin makes sure data does not become out of sync and allows multiple slaves on the same SPI bus.
When the SS pin is driven high, the SPI bit counter is reset to 0, even if less than 8 bits have been received, meaning we will never get out of sync as long as SS is used.
SPI Initialisation
I can't find it right now, but I read that SPI must be initialised only when the clock is in the idle state. It may be that this is just because if not we risk getting incomplete/corrupt data, but it still seems like a good practice.
The way I've achieved this in the DCO is to initialise the SPI when the SS pin goes high the first time. That means that the master can either drive the SS pin low and high again, or it will happen automatically if data is transferred (but the data transferred will be lost) if the slave has restarted for some reason, or starts after the master has sent its first byte. This may not be the solution for everyone but it is sufficient for me. A different option would be to use some other scheme where an additional pin on the slave signals its status as "ready to receive" or similar.
To be able to do this, I had to both set use of SS to true and map a pin as SS, and add an edge trigger interrupt to the same pin. The edge bit should be set to 1 (rising edge).
Another PS for the use of SPI: The TRIS bits for the pins in use must be set manually upon initialisation;
SDI (TRIS set = input)
SDO (TRIS cleared = output)
SS (TRIS set = input)
SCK (TRIS cleared = output for master and TRIS set = input for slave)
Any function not desired may be disabled by setting the opposite data direction/TRIS value.
No comments:
Post a Comment