C would be faster development time (than assembly) of course, but laughably slow performance.
propgcc contains full_duplex_serial_ht, in the demos directory. That is a full duplex serial driver entirely written in C. It works up to 115200 baud.
It was not meant for production use, I don't think the edge timings at those higher speeds are very good. Although I have never really checked. Rather it's an example of how to make threads in C running in a cog.
because 10 was too short causing waitcnt to stall for a "full wrap" or whatever it is that happens when you give waitcnt a time that is too short.
Is there any particular reason why others were able to use 10 and I can only use 15?
Also, it didn't seem entirely clear to me from these posts, but you connect your "dinout" pin on the Propeller to the dIN pin on the MCP3201, then you connect the dOUT pin on the MCP3208 to the dIN pin with a 3.3K Ohm resistor.
The point is to protect the prop from high output voltages from the MCP3208's dOUT pin. The CLK, DIN and CS pins can be connected directly to the prop with no resistor even if you are feeding the MCP3208 with 5V on VDD & VREF.
/*
* mcp3208.c
*
* Created on: Aug 18, 2012
* Author: AmosSam
* Version: 0.1
*/
#include <propeller.h>
/**
* @brief sets pin state, int pin, to high
*/
void pinHigh(int pin)
{
OUTA |= (1 << pin);
DIRA |= (1 << pin);
}
/**
* @brief sets pin state, int pin, to low
*/
void pinLow(int pin)
{
OUTA &= ~(1 << pin);
DIRA |= (1 << pin);
}
/**
* @brief sets pin direction, int pin, to input
*/
void pinInput(int pin)
{
DIRA &= ~(1 << pin);
}
/**
* @brief sets pin direction, int pin, to output
*/
void pinOutput(int pin)
{
DIRA |= (1 << pin);
}
/**
* @brief reads pin state, int pin
*/
int pinRead(int pin)
{
DIRA &= ~(1 << pin);
return (INA & (1 << pin)) ? 1 : 0;
}
/**
* @brief sets pin state, int pin, to int state
*/
int pinWrite(int pin, int state)
{
if (state)
OUTA |= (1 << pin);
else
OUTA &= ~(1 << pin);
DIRA |= (1 << pin);
return (OUTA & (1 << pin)) != 0;
}
/**
* @brief delay function, more precise on Propeller (?)
*/
void delay(int us)
{
waitcnt(us*(CLKFREQ/1000000)+CNT);
}
/**
* @brief sets pin, int pin, state to high, delays specified time, int d,
* and then puts pin low, and delays specified time, int d1
*/
void pinPulseHL(int pin, int d, int d1)
{
pinHigh(pin);
if (d > 10)
delay(d);
pinLow(pin);
if (d1 > 10)
delay(d1);
}
/**
* @brief sets pin, int pin, state to low, delays specified time, int d,
* and then puts pin high, and delays specified time, int d1
*/
void pinPulseLH(int pin, int d, int d1)
{
pinHigh(pin);
if (d > 10)
delay(d);
pinLow(pin);
if (d1 > 10)
delay(d1);
}
/**
* @brief Reads value from mcp3208/mcp3204 ADC
* @details This function reads data from mcp3208/mcp3204 ADC
* over SPI, with DIN and DOUT pins tied together to single
* pin on Propeller with 3.3K resistor in between pins on ADC
*
* @param channel What channel to read? 0 - 7
* @param dinout Pin number on which DIN and DOUT are connected
* @param clk Pin number on which CLK is connected
* @param cs Pin number on which CS is connected
* @returns Value read from mcp3208/mcp3204 ADC
*/
int readADC(int channel, int dinout, int clk, int cs)
{
int i;
int AdcResult;
int setup;
//Setting up pins
// In case pin was already been low, we put it high
// so we can initiate communication after setting up pins
pinOutput(cs);
pinHigh(cs);
pinOutput(dinout);
pinLow(dinout);
pinOutput(clk);
pinLow(clk);
pinLow(cs); // Active chip select by setting pin low
delay(10);
// Sending configuration to device
setup = channel | 0b11000;
for(i=0; i < 5;i++) {
pinPulseHL(clk, 10, 0);
if ((setup & 0b10000) == 0b10000)
pinHigh(dinout);
else
pinLow(dinout); // is MSB != 0
setup <<= 1; // shift left
delay(10);
}
pinPulseHL(clk, 10, 10); //Empty clock, for sampling
pinPulseHL(clk, 10, 10); //Device returns low, NULL bit, we ignore it...
pinInput(dinout);
// read ADC result 12 bit
AdcResult=0;
for(i=0;i<12;i++) {
// We are sending pulse, clock signal, to ADC, because on falling edge it will return data...
pinPulseHL(clk, 10, 0);
// Shifting bit to left, to make room for current one...
AdcResult<<=1;
AdcResult=AdcResult | (pinRead(dinout) & 0x01);
delay(10);
}
pinHigh(cs);
return(AdcResult);
}
/**
* @brief Reads specified number of values from mcp3208/mcp3204 ADC
* @details This function reads specified number of times
* data from mcp3208/mcp3204 ADC over SPI,
* with DIN and DOUT pins tied together to single
* pin on Propeller with 3.3K resistor in between pins on ADC
*
* @param channel What channel to read? 0 - 7
* @param dinout Pin number on which DIN and DOUT are connected
* @param clk Pin number on which CLK is connected
* @param cs Pin number on which CS is connected
* @param samples Number of samples that will be gathered, and then taken average before it's returned
* @returns Value read from mcp3208/mcp3204 ADC
*/
int readADCAverage(int channel, int dinout, int clk, int cs, int samples)
{
int i, k = 0;
for (i=0;i<samples;i++)
k = k + readADC(channel, dinout, clk, cs);
return k/samples;
}
Here is just the "delay" function:
/**
* @brief delay function, more precise on Propeller (?)
*/
void delay(int us)
{
waitcnt(us*(CLKFREQ/1000000)+CNT);
}
I had to change all instances of "delay(10)" to "delay(15)" and then it worked fine. Actually, "delay(11)" worked, but I was afraid it might be unstable?
I have just been writing in C with the default settings in SimpleIDE, so I am using whatever the default memory model is I guess. I don't understand what the benefits/drawbacks are of the various memory models are yet, but if you have some guidance on choosing a memory model I'd be happy to read it.
It's possible the compiler is cutting 1000000 short and adding the 'L' will fix that.
As for the memory models, LOTS of good info here. In short:
COG = Entire program fits in a single cog and runs in native assembly
Execution speed is fantastic. A surprising amount of code can fit in this memory model due to the lack of any overhead
CMM = compressed memory model
Execution speed ~ 40% slower than LMM
Code size ~ 2kB + LMM/2
LMM = long memory model
Execution speed is close to COG with minimal overhead (negligible?)
Code size is for a given program is similar to COG but with some overhead. Difference is, you get all 32kB of shared RAM instead of just a single COG's 2kB.
XMM* = extended memory model
Execution speed takes a huge hit
Code size is no longer a concern because you now have access to a large EEPROM or flash memory (like an SD card)
I had to change all instances of "delay(10)" to "delay(15)" and then it worked fine. Actually, "delay(11)" worked, but I was afraid it might be unstable?
Whether or not the fpucog.c was being used may play a part in that because fpucog.c speeds up multiplication and division, but has been found to be unreliable in some cases with release_1_0 propeller-gcc.
An experiment would be to move CNT. I'm not really sure when CNT is fetched in this case. You can find out for sure by looking at the ASM output for the file by opening the Project Manager panel, doing right-click on the file, and Show Assembly.
SwimDude thanks for posting the memory model info.
I am using CMM, and I think that is probably the issue.
I went ahead and used the code with "delay(15)" and, based on my testing, it doesn't not appear that increasing the time has any affect on the data retrieved from the MCP3208.
I'm guessing the only "down side" is a reduction in the speed at which readings can be retrieved?
Since I am only using this to get data from IR distance sensors, the speed is plenty fast enough with "delay(15)".
Thanks for all of the input and help. I like understanding why something is happening, which I think you have explained.
I'm guessing the only "down side" is a reduction in the speed at which readings can be retrieved?
That's exactly it. SPI is serial comms - you get one bit of data at a time. You're simply increasing the amount of time it takes to get each bit of data (decreasing clock frequency for the communication protocol). I skimmed the code as you posted it and i would bet you could run the whole program without any delays at all, but don't quote me on that. SPI devices are generally very fast - much faster than the Propeller.
The code you're using looks like it will work beautifully and is definitely easy to use. If, however, you ever get to the point where you need more SPI devices or higher performance, let me know. I'm always looking for more people to test PropWare's peripherals.
with special attention to attempting to read the value "immediately" after pulling clock low (I understand that the last parameter of your pinPulseHL stands for zero delay after pulling clk low)
I could enhance it, I think.
I would rather think to first do the "delay 10" and only then attempt to read. Perhaps simply putting the delay into the "pinPulseHL" routine.
I propose like this:
This way you increase certainty of output becoming the new value you intend to read. Then, you may proceed to the next CLK HI without further delay.
Basically, I'm referring to the "t DO" (="Clock Fall to Output Data Valid") per MCP's specification, Figure 1-1. I think you need to wait at least 200ns (nanosecs) which is specified as the max time needed for MCP to react to Clk low and effectively change its output. And then, cycle can be finished (of course, the Min Clk Lo time as the known 250 nanosec will apply).
In other words, "...data is output on the falling edge oth the clock..." per specs - which I believe is intended for reading near the rising edge of the clock.
What do you think?
No doubt, the "AdcResult<<=1;" operation (including other potential processor instructions till actual reading) takes some time, probably more than 200nanos, this way the original code works on some MCU.
fyi - I managed to achieve +/-0.3 (less than 1) digit accuracy with mach.lang code on an old SC-62015 proc, using delays ~half of the 10microsecs here. I had other problem though, that's why I'm reading here.
Comments
It was not meant for production use, I don't think the edge timings at those higher speeds are very good. Although I have never really checked. Rather it's an example of how to make threads in C running in a cog.
I may find myself using that module as inspiration for my own buffered version of the UART class in PropWare. I hadn't seen that before today.
Last I checked Heater's full_duplex_serial_ht was broken. It really did work a long time ago though.
I mentioned it several years ago.
Should have nagged I guess, but I don't have an apron, hair curlers, a broom, or a dough roller. LOL
full_duplex_serial_ht does compile here still. I don't have any Propellers around me at the moment so I can't test it.
"Dough roller", must be American for "rolling pin". You might do better with a cattle prod on this one:)
however, I had to change every instance of:
in the code to:
because 10 was too short causing waitcnt to stall for a "full wrap" or whatever it is that happens when you give waitcnt a time that is too short.
Is there any particular reason why others were able to use 10 and I can only use 15?
Also, it didn't seem entirely clear to me from these posts, but you connect your "dinout" pin on the Propeller to the dIN pin on the MCP3201, then you connect the dOUT pin on the MCP3208 to the dIN pin with a 3.3K Ohm resistor.
The point is to protect the prop from high output voltages from the MCP3208's dOUT pin. The CLK, DIN and CS pins can be connected directly to the prop with no resistor even if you are feeding the MCP3208 with 5V on VDD & VREF.
This post has a good diagram of how to connect the MCP3208 to the Propeller in the most simple way possible:
http://forums.parallax.com/showthread.php/138090-What-am-I-doing-wrong-(mcp3208)?p=1076335&viewfull=1#post1076335
Although I did not use a capacitor in the power feed. Maybe I should? My readings seem stable enough.
Also, while I'm adding information, here is a link to the data sheet for the MCP3208:
http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf
Also LMM or COGC code is faster than CMM code, so that could be a factor.
The "delay" funciton is from the mcp3208.h file from Post #18: http://forums.parallax.com/showthread.php/141893-SPI-amp-MCP3208-in-C?p=1119736&viewfull=1#post1119736
Here is the full code:
Here is just the "delay" function:
I had to change all instances of "delay(10)" to "delay(15)" and then it worked fine. Actually, "delay(11)" worked, but I was afraid it might be unstable?
I have just been writing in C with the default settings in SimpleIDE, so I am using whatever the default memory model is I guess. I don't understand what the benefits/drawbacks are of the various memory models are yet, but if you have some guidance on choosing a memory model I'd be happy to read it.
Thank you!
As for the memory models, LOTS of good info here. In short:
COG = Entire program fits in a single cog and runs in native assembly
Execution speed is fantastic. A surprising amount of code can fit in this memory model due to the lack of any overhead
CMM = compressed memory model
Execution speed ~ 40% slower than LMM
Code size ~ 2kB + LMM/2
LMM = long memory model
Execution speed is close to COG with minimal overhead (negligible?)
Code size is for a given program is similar to COG but with some overhead. Difference is, you get all 32kB of shared RAM instead of just a single COG's 2kB.
XMM* = extended memory model
Execution speed takes a huge hit
Code size is no longer a concern because you now have access to a large EEPROM or flash memory (like an SD card)
Whether or not the fpucog.c was being used may play a part in that because fpucog.c speeds up multiplication and division, but has been found to be unreliable in some cases with release_1_0 propeller-gcc.
An experiment would be to move CNT. I'm not really sure when CNT is fetched in this case. You can find out for sure by looking at the ASM output for the file by opening the Project Manager panel, doing right-click on the file, and Show Assembly.
SwimDude thanks for posting the memory model info.
I went ahead and used the code with "delay(15)" and, based on my testing, it doesn't not appear that increasing the time has any affect on the data retrieved from the MCP3208.
I'm guessing the only "down side" is a reduction in the speed at which readings can be retrieved?
Since I am only using this to get data from IR distance sensors, the speed is plenty fast enough with "delay(15)".
Thanks for all of the input and help. I like understanding why something is happening, which I think you have explained.
That's exactly it. SPI is serial comms - you get one bit of data at a time. You're simply increasing the amount of time it takes to get each bit of data (decreasing clock frequency for the communication protocol). I skimmed the code as you posted it and i would bet you could run the whole program without any delays at all, but don't quote me on that. SPI devices are generally very fast - much faster than the Propeller.
The code you're using looks like it will work beautifully and is definitely easy to use. If, however, you ever get to the point where you need more SPI devices or higher performance, let me know. I'm always looking for more people to test PropWare's peripherals.
Hello, Are you sure of this part of your driver? with special attention to attempting to read the value "immediately" after pulling clock low (I understand that the last parameter of your pinPulseHL stands for zero delay after pulling clk low)
I could enhance it, I think.
I would rather think to first do the "delay 10" and only then attempt to read. Perhaps simply putting the delay into the "pinPulseHL" routine.
I propose like this:
This way you increase certainty of output becoming the new value you intend to read. Then, you may proceed to the next CLK HI without further delay.
Basically, I'm referring to the "t DO" (="Clock Fall to Output Data Valid") per MCP's specification, Figure 1-1. I think you need to wait at least 200ns (nanosecs) which is specified as the max time needed for MCP to react to Clk low and effectively change its output. And then, cycle can be finished (of course, the Min Clk Lo time as the known 250 nanosec will apply).
In other words, "...data is output on the falling edge oth the clock..." per specs - which I believe is intended for reading near the rising edge of the clock.
What do you think?
No doubt, the "AdcResult<<=1;" operation (including other potential processor instructions till actual reading) takes some time, probably more than 200nanos, this way the original code works on some MCU.
fyi - I managed to achieve +/-0.3 (less than 1) digit accuracy with mach.lang code on an old SC-62015 proc, using delays ~half of the 10microsecs here. I had other problem though, that's why I'm reading here.