Two problems with delayMicroseconds on Tiva (and I believe on CC3200)
1) The delay interval is erratic, appearently because delayMicroseconds does not handle rollovers of the systick counter correctly.
2) delayMicroseconds can not work for dealys longer that 10,000 microseconds. (Even though the documentation says that it works up to 16383, and even includes an example.)
Details:
Issue 1)
Code to demonstrate problem:
#define DELAY_FOR 5000
void setup()
{
Serial.begin(9600);
Serial.print("Delay");
Serial.println(DELAY_FOR);
Serial.print("elapsed tics should be: "); Serial.println(F_CPU/1000000);
}
void loop()
{
unsigned long i = micros();
unsigned long systicstart = HWREG(NVIC_ST_CURRENT);
delayMicroseconds(DELAY_FOR);
unsigned long systicstop = HWREG(NVIC_ST_CURRENT);
unsigned long p = micros();
unsigned long o = p-i;
Serial.print("Before: "); Serial.print(i); Serial.print(" Systic: "); Serial.println(systicstart, HEX);
Serial.print("After: "); Serial.print(p); Serial.print(" Systic: "); Serial.println(systicstop, HEX);
Serial.print("After-Before: "); Serial.print(o); Serial.print(" Systic: "); Serial.println(systicstart - systicstop, HEX);
Serial.print("Error: "); Serial.print(((long int)DELAY_FOR) - ((long int)o)); Serial.print("\t Systic: "); Serial.println((long int)((systicstart - systicstop) & 0xFFFFFF)-((long int)(F_CPU/1000000)));
Serial.println();
}
Just loops forever doing a delay, then printing how long the delay took (prints both in micros(), and ticks of the systick counter. (Note that micros() does not use the systick timer.)
Example output:
Before: 8470600 Systic: B5751 After: 8475601 Systic: 53C94 After-Before: 5001 Systic: 61ABD Error: -1 Systic: 399981 Before: 8599763 Systic: 2735 After: 8599893 Systic: C33E4 After-Before: 130 Systic: FFF3F351 Error: 4870 Systic: 15987457 Before: 8734135 Systic: 7055D After: 8739137 Systic: EAA0 After-Before: 5002 Systic: 61ABD Error: -2 Systic: 399981
While usually the delay is about 5000 microseconds, often it isn't (in the middle reading above the delay is much too short).
The code from wiring.c (this is for Tiva, the CC3200 code is somewhat similar).
void delayMicroseconds(unsigned int us)
{
volatile unsigned long elapsedTime;
unsigned long startTime = HWREG(NVIC_ST_CURRENT);
do{
elapsedTime = startTime-(HWREG(NVIC_ST_CURRENT) & 0x00FFFFFF);
}
while(elapsedTime <= us * (F_CPU/1000000));
}
There are a few suspect things about this code:
Since it is subtracting unsigned values, it seems to be the intention to rely on modular arithmatic to get the comparisons right. (As in discussed in this page http://www.thetaeng.com/TimerWrap.htm ).
However as I understand it, for that to work the timer needs to overflow at the full width of the values being stored. In this case the timer is at most a 24 bit timer, but the values are 32 bits.
If the full timer was being used, it might be possible to fix that by masking off the top 8 bits.
elapsedTime = (startTime-HWREG(NVIC_ST_CURRENT)) & 0x00FFFFFF;
However in this case the full width of the timer is not being used, it wraps to F_CPU/SYSTICKHZ after reaching 0 (it is a count down timer).
So it looks like every time the systick timer rolls over, any delayMicroseconds which is in progress will have an unexpected result. (In the above output, where the delay was short, not the starting the starting value of NVIC_ST_CURRENT is small, and the ending value is close to C3500, which is F_CPU/SYSTICKHZ on a Tiva launchpad, i.e. the counter rolled over).
Small side issue: The code also seems to assume that the upper 8 bits of NVIC_ST_CURRENT will always be 0 (the manual warns against making assumptions about such bits).
A fix for that would be to add & 0x00FFFFFF to where startTime is set.
I do not have a particular suggestion of how to fix the code.
I think this issue also applies to the CC3200 (just from looking at the source code, but I have not checked that out in detail, and I do not have one to test.)
--
2) Since the systick counter rolls over every 10000 microseconds, delayMicroseconds can not delay for longer than that.
.../energia-0101E0013/reference/DelayMicroseconds.html
The documentation says "Currently, the largest value that will produce an accurate delay is 16383."
(There is also an example which uses delays of this length.)
The documentation may be a holdover from Arduino, which says the same thing, even though it does not appear to apply to the Arduino Due, for instance. (I do not know if this was true of MSP430.)
A possible fix for the delay duration issue would be to add something like the following to the beginning of delayMicroseconds
if (us > 9000){
delay( us / 1000);
us = us % 1000;
};
(the 9000 was somewhat arbitrary, rather than a constant it should be calculated based on SYSTICKHZ ).
Might also be well to clarify the documentation about which platform the 16383 relates to.
This is all on Energia 13, but I believe was also present in Energia 12.