SPI & MCP3208 in C
amossam
Posts: 35
Me again... and in advance, my apologies if I'm asking stupid question!
i have MCP3208, and following GadgetGangster tutorial, i'm able to read values using SPIN and MCP3208 object.
Since I don't know SPIN, or PASM, I'm trying to do SPI communication in C/C++.
With _no_ success! :blank:
How I understand MCP3208 specs pdf, this is how communication should work:
CS OUT and HIGH
DIN_DOUT out and HIGH
CLK OUT and HIGH
and now setup (send 1 0 0 0 - single ended CH0):
DIN_DOUT high AND CLK low
pause for 50 micro seconds (with usleep(50))
DIN_DOUT low AND CLK high
pause for 50 micro seconds
DIN_DOUT low AND CLK low
pause for 50 micro seconds
DIN_DOUT low AND CLK high
pause for 50 micro seconds
this should be end of setup, and after CLK low - pause - CLK high, now goes reading:
repeat 12 times (for 12 bits result)
CLK not CLK
read from DIN_DOUT (i'm reading pin state with "(INA & mask) ? 1 : 0" and tried "(INA & mask) != 0" it that correct?? )
pause for 50 micro seconds
But, all I'm getting from DIN_DOUT is 0!
What am I doing wrong?
Code example for this would be awesome! :cool: becasue I can find any code regarding SPI communication in plain C/C++...
thx for any assistance!
i have MCP3208, and following GadgetGangster tutorial, i'm able to read values using SPIN and MCP3208 object.
Since I don't know SPIN, or PASM, I'm trying to do SPI communication in C/C++.
With _no_ success! :blank:
How I understand MCP3208 specs pdf, this is how communication should work:
CS OUT and HIGH
DIN_DOUT out and HIGH
CLK OUT and HIGH
and now setup (send 1 0 0 0 - single ended CH0):
DIN_DOUT high AND CLK low
pause for 50 micro seconds (with usleep(50))
DIN_DOUT low AND CLK high
pause for 50 micro seconds
DIN_DOUT low AND CLK low
pause for 50 micro seconds
DIN_DOUT low AND CLK high
pause for 50 micro seconds
this should be end of setup, and after CLK low - pause - CLK high, now goes reading:
repeat 12 times (for 12 bits result)
CLK not CLK
read from DIN_DOUT (i'm reading pin state with "(INA & mask) ? 1 : 0" and tried "(INA & mask) != 0" it that correct?? )
pause for 50 micro seconds
But, all I'm getting from DIN_DOUT is 0!
What am I doing wrong?
Code example for this would be awesome! :cool: becasue I can find any code regarding SPI communication in plain C/C++...
thx for any assistance!
Comments
I'm pretty sure you need to set SPI_CS low for the device through most of your sequence. The times it should go high is at the beginning before you send the start, single/diff mode, and D2,1,0 bits. SPI_CS should be low before the start bit as well as at the end of the transaction. Page 20 of the .pdf specification has a great picture showing the signals.
Show your code if you can. That's been the best way to get a little help around here for years. Use the code markup if possible to make a box.
Hope this helps.
--Steve
hey!
you are correct about SPI_CS. i put it HIGH and then LOW and then start sending data.
I didn't posted any code, because it was too crude, i tried to use (translate) arduino code, but no help...
Here is current version, that still doesn't work! :blank:
helper functions
main code:
thx
Also don't you need 6 clocks before data starts coming? I see only 3. (JL below represents clock going high then low).
CSHI, CSLO, START JL, SINGLE JL, D2 JL, D1 JL, D0 JL, EXTRA JL, RXNULL JL, RXBIT11 JL, RXBIT10 JL, ... RXBIT0 JL, CSHI
Folks around here don't mind crude code.
Ok, will try when I get home, currently on work, and will report back...
Goot to know that! :cool:
P.S. it there, somewhere, minimal propeller usb stick? i mean, just FTDI and Propeller chip, maybe 2-3 LEDs, USB connector and that's it! No pins, not anything! So, something small size, that can be plugged into USB like small data stick and stay there? So that when you are not at home, and have time, continue working on propeller, and it doesn't take too much space!
I know I saw someone that made such a thing or darn close to it. Closest I have used to that is a Quickstart. It's nice because it has touch buttons and 8 LEDs for easy feedback. Not sure if you already have that. Very easy to play with to as it powers from USB, but it sure is not a 'stick'.
Yea, I have QuickStart, and it is small, but it's not easy portable! :-D
Maybe create some casing from plexy, but it's still too bulky to carry around! :cool:
Well, I'll probably have to just wait for winter, and little more free time, and see what can be done about that....
edit: Something like PropStick, but dualsided (so it could be shorter), without pin headers and with USB connector for direct plugging to pc!
But, values i got back are almost the same like in tutorial mentioned before, but my results oscillates much more!! i got around 2030 +- ~10, and SPIN code get 2080 +- ~2, with exactly same hardware setup!
any hints?
thx
@Dave Hein: isn't spinsim for SPIN and PASM simulator?
Good job!
I was going to mention this before, but wanted to see what happened with your new code ....
It is possible that usleep() is causing some issues in your oscillations. While usleep() is necessary for pthread code (typically code that starts more than one thread per COG), it actually adds uncertainty. When not using pthread code, one can use waitcnt(). The waitcnt() function timing is deterministic where usleep() is not. So what to do? Replace usleep(microseconds) with delay(microseconds) as defined below.
Dave's spinsim supports SPIN and PASM.
There have been a few USB-stick designs floating around. I have PCBs for one, but it is very difficult to assemble. @peward has a USB-stick called PropKey that looks good. Not sure what the status of it is though. http://forums.parallax.com/showthread.php?139043-PropKey-USB-keychain-Prop-board-(was-...toy...)
Minimal footprint, but no LED's:
http://www.parallax.com/Store/Microcontrollers/PropellerChips/tabid/142/ProductID/411/List/0/Default.aspx?SortField=ProductName,ProductName
or 16 pin I/O:
http://www.parallax.com/StoreSearchResults/tabid/768/List/0/SortField/4/ProductID/448/Default.aspx?txtSearch=Spin+Stamp
// macro version of delay(us) function that works to about 6us
#define delay(us) waitcnt((us)*(CLKFREQ/1000000)+CNT)
that's exactly what I had in mind! :-D have to check status of that sticks! :cool:
well, 10us is OK, since I have 16(?) delays in function, that sums up to 160 us or 0.16ms and that is below 1.2ms frame that ADC gives for reading data...
and off to check the code, something is not ok in there...
but, something I'm doing wrong in setup part....
when I set 0b1000 for setup, that should be chanel0 single-ended.. and if I set setup to 0b1001 that should be chanel1...
but for some reason, 0b1000 select correct channel, but 0b1001 selects chanel4, and CH4 is 0b1100 by the specs sheet!? :blank:
any help?
thx
i finally got it working, on all 8 channels!!!!!!
i'll clean up code, and upload it here in case someone needs it or finds it usefull!
Great. Congrats.
It took some time, nerves, and loot of googling and poking around the net...
thx goes to jazzed, who helped a lot!
So, it's all in a .h file, ready to be included and used.
There are two main functions:
First one will read single value from ADC and return it,
while second one will read specified number of values and return average...
Any feedback is welcome, especially for optimizing code!
P.S. @jazzed: What another method of delay had you had in mind? With current code, it can take only(?) around 1750 samples per second, with delay of 10 usec, and I saw code for this ADC that used delays down to 2 us!
The waitcnt2(count,wait) macro can allow very fine delay control.
thx jazzed for suggestion, but I wasn't been able to change code to work with waitcnt2....
but, I have read al little about counters, and tried to use them...
I succeeded, and now I can read few (4-5) times more samples (guessing) per second then with original code using waitcnt!
(If I understand specs correctly, this chip can do a 50 ksps ("50 ksps max.sampling rate atVDD=2.7V") and that is 50 000 samples per second?
and I guestimate that I'm reading at about 5000-6000 times per second)
But, if I increase frequency of pulses generated by counter, I'm unable to read data, in fact I get garbage data.
In what way code can be optimized more?
thx
P.S. this is more an exercise then something I need (at least for now)! so, if someone sees that I'm doing wrong something or there is better way, feel free to drop a comment!
thx again
Here is code, it's using functions from mcp3208.h
P.S.
I was unable to figure out what freq is this!?!?
when running on a Prop ASC+ . Schematic for this board is here: http://mghdesigns.com/wiki/_media/asc:pasc-f.pdf
with one of these stacked on top:
http://www.hobbyking.com/hobbyking/store/__25087__Arduino_LCD_Keypad_Shield.html?gclid=CI3ciKXY_rcCFU_ZQgodGwwA8Q
Am I misinterpreting something here?
Please observe the 3.3k resistor described in the .h file (between Dout and Din pins).
Gotta love that extra pin freed up in this function!
I downloaded the mcp3208.h file on this thread (POST #18) and tucked it in ~ c:/program files/simple IDE/ prop gcc/ prop elf / include
I ran this sample code fine in Simple IDE with Prop Activity Board in LMM model:
#include "simpletools.h"
#include "mcp3208.h" // COMPILE IN LMM MODE !!!
serial *text;
int value;
int adc;
int main()
{
while(1)
{
text = serial_open(31,30,1,115200);
putStr("Enter a decimal:"); //terminal com input verify
dscan(text, "%d", &value);
pause(100);
dprint(text,"You entered %d\n",value); //terminal com output verify
pause(500);
adc=readADCAverage(7,2,1,0,3); // read from Channel 7, data pin 2, clock pin 1, chip select pin 0, average 3 samples
dprint(text,"ADC reads: %d out of 4096 bits\n",adc); // display a proportion of bits to the ref voltage you are running
pause(1000);
}
}
Can I make a suggestion?
Instead of using serial *text and all that entails, just use the default debug statements.
I.E. instead of this:
dprint(text,"You entered %d\n",value); //terminal com output verify
Do this:
print("You entered %d\n",value);
Same goes for any of the functions that use the text pointer. You can also get rid of text = serial_open(...).
There is a Simple Library update coming that has a smaller version of print also. The iprint function is the same as print but with no floating point support. Additionally there are "digits" versions of putDec, putBin, putHex, etc... aptly named putDecDigits, putBinDigits, putHexDigits.
Cheers.
--Steve
FYI, You are awesome.
At this point,the attached code example is adapted from "my universal" starting point for general Propeller C I/O.
It is quick and easy to copy and paste LCD and other devices in from this little file.
I am new to the Propeller (and C) and I certainly struggle a bit. Thanks for the tips!
Anywho, my next task is to interface the TI "TLC5628C" 8ch, 8 bit, DAC. I think this source code will help me a lot in that project.
Don't forget the 7 bit RS232 ! -E-
Have you seen PropWare or libpropeller? I skimmed your thread and didn't see any warning flags that would keep you from using one of them. The one requirement is that both libpropeller's MCP3208 and PropWare's MCP3000 classes require an independent cog for SPI communication. If you do not have a cog to spare, they won't help you. The good news is, PropWare's SPI cog can be used for other SPI peripherals as well (libpropeller's is locked to the MCP3208).
Here is the doc page for PropWare::MCP3000 (ignore the class doc at the top that says only 3004/3008. I forgot to update that after adding support for the 320x and 330x).
Doc page for libpropeller::MCP3208
(Do note: libpropeller is not my product. It was written by forum member SRLM and he was gracious enough to allow me to package it along with PropWare. Original source is here)
If you download a binary distribution of PropWare v2.0-beta1, it comes bundled with libpropeller and Simple so you can continue using your Simple functions and choose whichever API (PropWare or libpropeller) suits your comfort zone better. If you want to build from source, just run the "propwareImporter.py" script in the util directory and then "make" in the PropWare root directory.
http://david.zemon.name/downloads/PropWare_Binaries/PropWare_v2.0-beta1.zip
If you have questions, please let me know. PropWare is in active development and I am always looking for feedback.
I have a TLC series DAC sitting around. I think 12-bit? Not sure. If you'd like, I can add support for that as well. I haven't looked at the TLC5628C, but I'll bet the protocols are similar enough that I can add support for both in a single class, just like the MCP3000 series.
I am most interested in interfacing older RS232 equipment with the propeller, namely 7 bits is my main problem. Jazzed said he will look at the serial libs, but I know he is very busy and I could be about the only one that is interested in 7E2 RS232 com.
Any thoughts on that using Propware? At this point, I have cogs galore, so that is a non-issue. Thanks!
I don't think C performance should be bad -- the propgcc SimpleSerial routines are written in C, and I've looked at the assembly output and it's fairly reasonable. They are only half duplex, of course, but if I remember correctly Heater did a version of FullDuplexSerial in C.
Eric
-EP
You're absolutely right. Performance turned out to be a non-issue. Mine is only somewhat full-duplex though. I've currently implemented a solution that uses dedicated I/O lines but only a single cog - so that means you can only send or receive - not both simultaneously. And there is no buffer, so the send and receive calls are infinitely blocking. I've started jotting down some notes during speed tests and putting my findings here:
http://david.zemon.name/professional/PropWare/docs/html/classPropWare_1_1UART.html#details
--edit--
I shouldn't say "you can only send or receive". There's no reason you couldn't start two HalfDuplex cogs and use one for sends and the other for receives. Perhaps this is even a better way to do it?