SPI library or driver?
Heya!
Is there any SPI library, function or driver, but written in C?
I have written a small function that "should" be generic, but I'm not totally satisfied with it...
Here is a code
If I understand SPI protocol correctly, data is sent on rising edge and read on falling edge, and with following call:
And I need simple SPI read/write functions, because I ordered few wireless board on 433MHz based on CC1101 chip which I plan to use with propeller as master node and few attiny's as clients...
thx!
P.S. Would i gain anything if I put writeReadSPI into .cogc file and use mailbox for communication with SPI devices? (and I'm not sure yet how to do that, but if needed I'll figure it out!
)
Is there any SPI library, function or driver, but written in C?
I have written a small function that "should" be generic, but I'm not totally satisfied with it...
Here is a code
int writeReadSPI(int SPI_cs, int SPI_clk, int SPI_do,int SPI_di, int data, int size)
{
pinLow(SPI_clk);
pinHigh(SPI_cs);
pinOutput(SPI_do);
pinLow(SPI_do);
pinInput(SPI_di);
int mask = 0b1 << 30;
int reg = (0b0100 << 26) + SPI_clk;
int result = 0;
CTRA = reg;
FRQA = 4194304; //2^22
waitpne((1 << SPI_clk), (1 << SPI_clk)); //low
waitpeq((1 << SPI_clk), (1 << SPI_clk)); //high
pinLow(SPI_cs);
int i;
for (i = 0; i < 32; i++)
{
waitpne((1 << SPI_clk), (1 << SPI_clk)); //low
pinWrite(SPI_do, ((data & mask) == mask));
waitpeq((1 << SPI_clk), (1 << SPI_clk)); //high
result = (result << 1) | pinRead(SPI_di);
data <<= 1;
}
pinHigh(SPI_cs);
CTRA = 0; //disable counter
result >>= size;
return result;
}
If I understand SPI protocol correctly, data is sent on rising edge and read on falling edge, and with following call:
writeReadSPI(CS, CLK, DO, DI, (0b011000 << 26), 13);I get data from MCP3208 with this function, but I can't test it anything else currently...
And I need simple SPI read/write functions, because I ordered few wireless board on 433MHz based on CC1101 chip which I plan to use with propeller as master node and few attiny's as clients...
thx!
P.S. Would i gain anything if I put writeReadSPI into .cogc file and use mailbox for communication with SPI devices? (and I'm not sure yet how to do that, but if needed I'll figure it out!

Comments
Some library functions are a work in progress. We have a list, but it is slow to be provided.
This is a good question. With propeller-gcc supporting CMM, a cogstart thread driver might be smaller and just as effective (though slower) than a driver written for COGC.
Here is a simple example of how to use a cogstart thread. https://sites.google.com/site/propellergcc/documentation/libraries/propeller-h-library/#TOC-cogstart
That I think is wrong.
I wrote something together quickly:
#define SPI_CLK 0 #define SPI_DI 1 #define SPI_DO 2 #define SPI_CS 3 #define SPI_CLOCKMASK (1 << SPI_CLK) #define SPI_DIMASK (1 << SPI_DI) #define SPI_DOMASK (1 << SPI_DO) #define SPI_CSMASK (1 << SPI_CS) #define SPI_OUTMASK (SPI_CLOCKMASK | SPI_DOMASK | SPI_CSMASK) #define SPI_INIT() (DIRA |= SPI_OUTMASK) #define GET_SPI_IN() ((INA & SPI_DIMASK) >> SPI_DI) #define SPI_ENABLE() OUTA &= ~(SPI_CSMASK) #define SPI_DISABLE() OUTA |= SPI_CSMASK unsigned char ubPutByte(unsigned char b) { unsigned char in = 0,i; for(i = 0; i < 8; i++) { if(b & 0x80) // MSB High { OUTA |= SPI_DOMASK; // Data High } else { OUTA &= ~(SPI_DOMASK); // Data Low } OUTA |= SPI_CLOCKMASK; // Clock High in <<= 1; in |= GET_SPI_IN(); OUTA &= ~(SPI_CLOCKMASK); // Clock Low b <<= 1; // next Bit } return(in); } void main (void) { unsigned char byte,cmd; SPI_INIT(); SPI_ENABLE(); byte = ubPutByte(cmd); //do something byte = ubPutByte(cmd); //..... SPI_DISABLE(); }I would try it first without the timer. This device seems to be quite complicated.
well, with your suggestion i got data back (after few small adaptations), but as with my function from first post I would occasionally got stray data every 5000-6000 samples. for ex. result sould be 4086, and I would get 4465 back! and for using your function in other situations I would need to change #define's....
But your code gave me few ideas, and now I can get around 10k samples per second!
Also, it seems (at least when this adc is asked) that data _is_ sent to adc on rising edge of clk and read from adc on falling edge...
here is current version of function, which now doesn't use any other functions, it's completely self-contained!
int writeReadSPI(int SPI_cs, int SPI_clk, int SPI_do,int SPI_di, int data, int size) { DIRA |= (1 << SPI_clk); //set output DIRA |= (1 << SPI_cs); //set output OUTA &= ~(1 << SPI_clk); //set low OUTA |= (1 << SPI_cs); //set high DIRA |= (1 << SPI_do); //set output OUTA &= ~(1 << SPI_do); //set low DIRA &= ~(1 << SPI_di); //set input int mask = 1 << 31; //mask for comparing bits int reg = (0b0100 << 26) + SPI_clk;//set counter mode int result = 0; CTRA = reg; FRQA = 33554432; //2^25 waitpeq((1 << SPI_clk), (1 << SPI_clk)); //high waitpne((1 << SPI_clk), (1 << SPI_clk)); //low OUTA &= ~(1 << SPI_cs); int i; for (i = 0; i < 32; i++) { if ((data & mask) == mask) OUTA |= (1 << SPI_do); //set high else OUTA &= ~(1 << SPI_do); //set low data <<= 1; //shift data left for next bit waitpeq((1 << SPI_clk), (1 << SPI_clk)); //high waitpne((1 << SPI_clk), (1 << SPI_clk)); //low result = (result << 1) | ((INA & (1 << SPI_di)) ? 1 : 0); } OUTA |= (1 << SPI_cs); // cs set to high CTRA = 0; //disable counter result >>= (size + 1); return result; } #define CS 2 #define DI 3 #define DO 1 #define CLK 0 int main (int argc, char* argv[]) { while(1) { int i, k = 0; // average of 5000 samples (overkill, but ftdi drivers on osx obviously have buffering issues, // because if i'm getting data back too fast, complete USB subsystem on osx goes down! for (i = 0; i < 5000; i++) k += writeReadSPI(CS, CLK, DO, DI, (0b011000 << 26), 12); k /= 5000; printf("Res = %i\n", k); //sleep(3); } return 0; }currently, function is defined to use 32 bit packet, but that can be easily changed (that's next TODO!)...what I would like is to someone tries this function on something other than MCP320x, and reports how it's working...
thx!
But is it right to clock 32 bits at once?
Maybe I do not understand your code completly.
Well, since "data" parameter of function is int, I automatically put in loop 32, but now that you mentioned it, I see that i'm loosing 15 (or 14?) cycles for nothing! (5 for setup, 12 for reading data in this example!) hmm, will try later to use "size" parameter for packet (send + receive data) size, not for result size! so, you could pass (byte << 24) (shifting left because it's sending MSB first) as "data" and size of 8, and i would sent and receive packet of size byte (8 bits)!
thx!
edit: said and done! when I set function (code below) for 18 bit packet (this size is explained in code), I can read 10k samples in 0.488364 sec!! (if used function for timing is correct! I took it from post improving CMM performance )
here is complete code, with timing:
#include <propeller.h> #include <stdio.h> #include <time.h> //used only for timing #include <sys/time.h> //used only for timing int writeReadSPI(int SPI_cs, int SPI_clk, int SPI_do,int SPI_di, int data, int size) { DIRA |= (1 << SPI_clk); //set output DIRA |= (1 << SPI_cs); //set output OUTA &= ~(1 << SPI_clk); //set low OUTA |= (1 << SPI_cs); //set high DIRA |= (1 << SPI_do); //set output OUTA &= ~(1 << SPI_do); //set low DIRA &= ~(1 << SPI_di); //set input int mask = 1 << 31; //mask for comparing bits int reg = (0b0100 << 26) + SPI_clk;//set counter mode int result = 0; CTRA = reg; FRQA = 33554432; //2^25 //FRQA = 4194304; // 2^22 waitpeq((1 << SPI_clk), (1 << SPI_clk)); //high waitpne((1 << SPI_clk), (1 << SPI_clk)); //low OUTA &= ~(1 << SPI_cs); int i; for (i = 0; i < size; i++) { if ((data & mask) == mask) OUTA |= (1 << SPI_do); //set high else OUTA &= ~(1 << SPI_do); //set low data <<= 1; //shift data left for next bit waitpeq((1 << SPI_clk), (1 << SPI_clk)); //high waitpne((1 << SPI_clk), (1 << SPI_clk)); //low result = (result << 1) | ((INA & (1 << SPI_di)) ? 1 : 0); //read value } OUTA |= (1 << SPI_cs); // cs set to high CTRA = 0; //disable counter return result; } double dtime() { struct timeval tv; gettimeofday(&tv, NULL); return (double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0); } #define CS 2 #define DI 3 #define DO 1 #define CLK 0 int main (int argc, char* argv[]) { int i, k = 0; double elapsed; while(1) { k = 0; elapsed = dtime(); for (i = 0; i < 10000; i++) k += ((writeReadSPI(CS, CLK, DO, DI, (0b11000 << 27), 18)) & 0xFFF); // 18 for size = 5 for setup + 1 for sample period + 12 for result... // 0xFFF - we are interested only in last 12 bits... k /= 10000; elapsed = dtime() - elapsed; printf("Res = %i, in %f sec\n", k, elapsed); } return 0; }results in: