I started to like the range of PIC microcontrollers to further my hobby - mostly to make little widgets that could use the internal RC dedicating only a dollar or
An issue is I do not know how to use SPI unless I spend the time to hack together bitbanging functions which I suppose I can do to communicate, but are there any libraries available? I see references to spi.c/h that are often in HTC files however they are not found on my compiler.
It seems odd that I cannot find a simple .c and .h library that someone wrote to not have to worry about implementing the SPI communication as much and just do the protocol to write to the shift register. Maybe I am overthinking things and spi_init/read are simple functions and I should just write them.
Thoughts? Maybe the jump in to PIC is a bit more of an undertaking than I thought? I don't expect an Arduino experience but anything to make 'needless' steps faster would be very welcomed including how people do this for simple hobby projects.
If your PIC has hardware SPI, I suggest you use it. It's really not that difficult to get it going. You don't really need to use a premade library, using the hardware registers directly isn't that big of a deal. Microchip's documentation usually explains it well.
Let me show you some example code. I use MPLAB X and XC8. This is from a project using a PIC16F1824, which is a modern but low-end PIC. It'll work the same on other newer PICs, but maybe the registers are layed out a bit differently for older ones. For example, I was able to use the same code on an PIC18F46J50.
Btw, for all the register names to work, you'll need to include the MCU-specific header. In my case this is:
#include <pic16f1824.h>It might also prove useful to have a look at that file, to learn about naming conventions and the such. For example, there you'll see that to access an individual port bit, you can use
PORTAbits.RA1, or that plain
RA1 works too (but personally I prefer the longer variant, since it's more selfdocumenting.)
Anyway, here's how I setup the SPI controller (I'll skip the pin configuration) :
SSP1STAT = 0b01000000; // SMP = 0, CKE = 1
SSP1CON1 = 0b00100000; // WCOL = 0, SSPOV = 0, SSPEN = 1, CKP = 0 (idle low), SSPM = 0 (SPI Master, FOSC/4)oh, lots of cryptic names there. So lets refer to pages 292 and 293 of the
Datasheet, where these two registers are explained. SSP1STAT has just two bits that you can actually set (indicated by R/W-0/0), all the others are read-only (indicated by R-0/0) so they're not important right now. SMP specifies the sampling time, but the default works just fine (you might need to set it do 1 when running at high speeds.) CKE specifies which edge of the clock to use for changing the signal. Unfortunately, this varies between devices, you'll have to check the datasheet of whatever device you're communicating with. The wording here is a bit complex, speaking of active and idle states instead of high and low, but this is because you get to define whether active = high or low yourself. Again, because device manufacturers couldn't agree on that either... *sigh*.
Anyway, on to SSP1CON1 on the next page. There are a couple more status bits that you can ignore for the moment. The important bits are SSPEN, which actually turns the whole controller on, CKP which sets active to high or low as I just mentioned, and of course SSPM, through which you set the mode and clock rate.
And thats it. Two registers to set, and the SPI module is ready to use.
Now how do you actually use it? I use this method:
void spiTxRx(const uint8_t *txBuf, uint8_t *rxBuf, size_t len) {
PIN_CS = 0; // CS is active low
for (size_t i = 0; i < len; ++i) {
PIR1bits.SSP1IF = 0; // reset interrupt flag
SSP1BUF = txBuf[i]; // writing to SSP1BUF immediately starts the transmission
while (0 == PIR1bits.SSP1IF); // Wait until the interrupt is sent, which means the byte is transmitted.
// Even if interrupts aren't actually used. So you can just poll for it.
for (uint8_t j = 0; j < 16; ++j) ; // Delay a bit.
if (rxBuf) { rxBuf[i] = SSP1BUF; } // SPI is full duplex, so we received something at the same time. Maybe save it. Or just ignore it.
}
PIN_CS = 1;
}
And that's really all there is to it. The data to be sent is copied byte by byte into SSP1BUF and is immediately sent. At the same time, data is received is made available in the same SSP1BUF once the transmission has ended. If you only want to write, just ignore it. If you only want to receive, you'll need to copy a dummy byte into SSP1BUF to start the transfer, a 0 will do.
What about all the status bits? Well, I chose to ignore them here. If something goes wrong, it'll be the peripheral I'm talking to, and it'll be an issue I haven't found in testing. Instead of worrying too much about it, I'll just let the microcontroller spin in the while loop until the watchdog resets everything. For more complex boards with more than just two chips, some error handling might be advisable.
Anyway, I hope this helps.