Jump to content
abecedarian

How fast does this execute?

Recommended Posts

@@abecedarian

 

Ultimately you will want to go direct 'c' (ccs / gcc) to get accurate timing. You cannot rely on your loop w/ Energia. It has this built-in timer WDT interrupt that would throw your own counting off.

 

The current code is not working as (1) you need to also multiply 100 to your t2 value (orginal 440), but then in the loop it will never reach 44000 as your angle value / degree is incrementing by 1125. I changed it to the closest value of 43875.

 

I make use of the Energia function "micros()" to see how long it takes for a 720 degree loop. The code changes are as follows;

 

I had changed some of the led pins as I run it on a bare LP, but timing should not be affected.

const uint16_t t1 = 0;  //  right cylinder baseline spark
//const uint16_t t2 = 440; // left cylinder baseline spark relative right cylinder
//const uint16_t t2 = 44000; // also no good, not divisible by 1125
const uint16_t t2 = 43875; // this is the closest 
const uint8_t cam = 0; // camshaft trigger relative right cylinder TDC


const uint8_t AMBER_LED = 5;  // LP pin indicating camshaft trigger signal occurrence
// I am using existing RED_LED, GREEN_LED for right and left cylinder TDC indicators




uint32_t degree = 0; // counter for crankshaft rotation in degrees


void setup()
{
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED,OUTPUT);
  pinMode(AMBER_LED, OUTPUT);
}


void loop()
{
    digitalWrite(GREEN_LED, 0);
  uint32_t elapsed = micros();
  while (1) {
  for (degree = 0; degree < 72000; degree+= 1125)  {
// 11.25 = spacing between tooth centers on a 32 tooth wheel
// changed to 1125 to get rid of decimals and work without floats


// Camshaft:
    digitalWrite(AMBER_LED,degree == cam);


// Right cylinder:
    digitalWrite(RED_LED, degree == t1);


// Left cylinder:
    digitalWrite(AMBER_LED, degree == t2);


  }
  //if ((micros() - elapsed) < 1120) {
  // my test show that the "loop" spend 1120 micro sec.. i.e. max is 892Hz.
  // for elapsed between the cylinders, it would be about 440/720*1120 or about 1.46khz
  if ((micros() - elapsed) < 200) {
      // elapsed time less then 0.002 sec, i.e faster than 5khz for a 720 degree complete loop, which did not happen
    digitalWrite(GREEN_LED, 1);
  }//if
  }//for
}

The finding is that for a full loop (? your engine cycle?) it takes 1120 uSecs to run. That would make it 892Hz.

 

I think a lot of cycles are wasted by setting the various LEDs off when it is not necessary to do so. We could just reset them in a outer loop and turn them on only when triggered (although you may need to a some delays for your pulse width requirement, etc).

 

void loop()
{
    digitalWrite(GREEN_LED, 0);
    digitalWrite(AMBER_LED, 0);  // set all leds off
    digitalWrite(RED_LED, 0);
  uint32_t elapsed = micros();
  while (1) {
  for (degree = 0; degree < 72000; degree+= 1125)  {
// 11.25 = spacing between tooth centers on a 32 tooth wheel
// changed to 1125 to get rid of decimals and work without floats


// Camshaft:
if (degree == cam) {
    digitalWrite(AMBER_LED,1); // pulse when value equals, do nothing otherwise
    digitalWrite(AMBER_LED,0); // pulse when value equals, do nothing otherwise
}
// Right cylinder:
if (degree == t1) {
    digitalWrite(RED_LED, 1);
    digitalWrite(RED_LED, 0);
}
// Left cylinder:
if (degree == t2) {
    digitalWrite(AMBER_LED, 1);
    digitalWrite(AMBER_LED, 0);
}
  }


  //if ((micros() - elapsed) < 200) {
      // elapsed time less then 0.002 sec, i.e faster than 5khz for a 720 degree complete loop, which did not happen
  if ((micros() - elapsed) < 225) {  // max = 4.444khz
    digitalWrite(GREEN_LED, 1);
  }//if
  }//for
}
So this ends up just shy of 5Khz. If 5Khz is all you need, you can try change the logic into a sequence machine so that you do need to test all three time values all the time.
 
I.e. (not tested)
 
int elapsed = micros();
while (1) {
  righ_spark_pulse();
  while ((micros()-elapsed) <= 440*FACTOR) __asm("nop");
  left_spark_pulse();
  while ((micros()-elapsed) <= 720*FACTOR) __asm("nop");
  // done
}//while 1 engine cycle
 
 

 

 

 

 

 

Share this post


Link to post
Share on other sites

As a note, I've found that generally "if" is an expensive thing to say if (heh) you're looking at time consumption.

Same for "for". Haven't checked "while", but it probably is too.

How expensive it is depends on the chip and the variables you're comparing of course. If you're comparing bytes on a 16bit chip that ought to be faster than comparing longs on said 16bit chip, but may not be on a 32bit chip.

 

That said, writing code that doesn't say "if" is a good trick.

 

 

Oh and I tested the code (modified to work on a launchpad without extra stuff attached) on a scope, and got ~900ms/loop also.

Share this post


Link to post
Share on other sites

900ms sounds a bit shy of the target I'm hoping for.

 

Re-reading things here, allow me to apologize for the confusion, and maybe rephrase / restate the intentions. I got a little ahead of myself.

 

Goal(s)- simulate various sensors as would be installed on the engine including manifold pressure and temperature, coolant temperature, throttle position and the (a) crankshaft and ( B) camshaft trigger signals. Analog sensors like temp and pressure can probably be done with linear pots; I'm fine with that. The crankshaft signals will come at a rate of 32 pulses per crankshaft revolution, with approximately 50% duty, as far as I can tell. Crank trigger setup-

post-26656-0-33932300-1407815999_thumb.jpg

*note I am only going to use one of the two VR sensors- this is a modified stock CX/GL pickup setup, but I'm trying to avoid modifying anything mechanical on this or similar bikes if possible.

 

The camshaft is similar, but with only a single tooth and two pickups, again of which I'm only using one.

 

The 11.25 number came about because 360 degrees divided across 32 teeth is 11.25 degrees from tooth to tooth. That was my mistake for including that since the ECU will be responsible for decoding and calculating when things should fire. I'll delve into what I think should happen there, if anyone is interested.

 

So back on topic: it seems that to simulate 200 RPM, I'd need to generate (200/60*32) pulses per second, or ~107 Hz, for the crank, and ~3 Hz for the cam; sounds more realistic. At 11000 RPM, that's ~5867 & 184 PPS for crank and cam, respectively. Using PWM for the signal seems logical in retrospect since they are continuous, repetitive signals, but isn't around 250Hz the lower end of what's possible? Actually, 200 RPM is probably low even, and I could probably get away with 500- but I'm trying to account for worse-case possible engine start speeds... and 200 RPM is probably a dead battery anyways so.... ;)

 

Crankshaft trigger duty cycle will not change and it's likely 50% will work fine- ~1mm wide tooth face & ~1mm wide valley between teeth. I'll have to figure out what the cam should be since it has a similar tooth profile to the crank sensor, but has only one tooth and rotates 1/2 crank speed... maybe turn on with one crank tooth then turn off with the next. I also still have to determine the cam sensor's absolute placement / relation with regards to the teeth on the crank sensor, as in does it exactly coincide with a tip or valley on the crank sensor, or somewhere in between (mostly relevant to the ECU's operation, not this).

 

To add, I've no issue dedicating a Stellaris or Tiva Launchpad for this if either is more appropriate.

 

I'm babbling, yet again. :(;)

Share this post


Link to post
Share on other sites

OK so far so good. (The following may be obvious but I think I'm learning something here...)

As you keep specifying your simulation requirements in frequency, I am assuming you want a UNIFORM frequency resolution.

I may be wrong, but you in general cannot linearly specify frequency, only period (there is 1 exception)*.

Frequency changes asymptotically WRT to period, so you end up with a linear region only a fraction of your whole range.

Higher frequencies will have much less resolution than lower frequencies.

 

So for the crank, 107Hz is approximately 10000us and 6000Hz is approximately 166us.

A PWM peripheral running at 1 MHz would have a TACCR0 at 10000 at the slowest, and 166 at the fastest (36Hz worst res).

A PWM peripheral running at 4 MHz would have a TACCR0 at 40000 at the slowest, and 666 at the fastest (2Hz worst res).

(note these are off by 1, since TA0R starts at 0)

 

The exception is you can linearly control the DCO, which is basically a linear (5) bit mixer between two frequencies, giving you an extra 1/32 resolution.

VCC will also affect your effective frequency, so make sure it does not change.

 

If you use a synchronous protocol like SPI or I2C, you could turn one of your G2553s into a slave square wave generator with fairly precise control (UART would not work since it is clock dependent).

Share this post


Link to post
Share on other sites

If my reading of datasheets and energia device files is correct you can solder the optional 32.something kHz crystal onto the launchpad and use that as a clock source for one of the timers to use with PWM. At that point you could have anything down to a 0.5Hz PWM output, I think. Maybe even less if you feed it through a /8 divider first.

Low speed PWM shouldn't be an issue.

 

I'm not especially well versed in this business yet, so I may be way off base.

Share this post


Link to post
Share on other sites

 .... i'll throw my hat into the ring here ....

 
if the question still is     :    'how fast does this execute' 
 
     ------>  and additionally:
 
      - you'd like it to be easy to set up
      - you have an additional Launchpad and serial port (or spare LCD) available
      - you are satisfied with +- 10 / 20 uSec resolutions
      - you don't want / need a scope or logic analyzer
 

then, in the spirit of 'rapid prototyping', here's an idea.
please note I haven't done this, but i've used these energia routines myself.
I use Energia and a bunch of handy, jumperized, IO devices for quickie measurement projects.

 

DIAGRAM:
========
your existing LP#1 is your 'simulator', already described.     The LP#2 just measures and displays a pulse width.
 
LAUNCHPAD#1          -------> PULSE OUT PER LOOP  --->     LAUNCHPAD#2
TO BE TIMED                    (2 wires)                   MEASURING  ----> DISPLAY
(SW added to toggle a pin                                  THE PULSE        (SERIAL or LCD)
a pulse at top of loop)

 

 
OVERALL DESCRIPTION:
====================
- for the loop timing to be measured (ie: how fast does this execute)  LP#1 has a spare IO bit set for output which

toggles at the top of the loop (XOR). The resulting high pulse is wired over to LP#2 to be measured and displayed by a

quick-'n-dirty Energia script.
 
 
SOFTWARE
========

LP#1     - you know how to do this.    and you've wired 1 output bit + ground to LP#2 input bit of your choice.

LP#2     - i think you've worked with Energia. Key is the pulseIn() function.  So here's some example code:

int pin = 7;
unsigned long duration;

void setup()
{
  pinMode(pin, INPUT);
}

void loop()
{
  duration = pulseIn(pin, HIGH);
}

-------------------------------->    then display 'duration' to the device of your choice

 

 

Share this post


Link to post
Share on other sites

This sure is a rapid prototype, but the signal he's trying to measure goes down to about 200us in period.

So using pulseIn is going to have wildly inaccurate results at his top end.

Looking at the source code for pulseIn almost made me vomit.

Ignores this thing called TimerA, susceptible to interrupts, etc.

 

pulseIn may be OK for measuring human reaction time, but certainly nothing faster.

Share this post


Link to post
Share on other sites

good to know about pulseIn    !

 

i didn't use it at that narrow a pulse width (200 uS)

   glad to hear that you checked the source code as a bonus

 

 

---->>   as an alternative,  perhaps the Energia   'microcseconds' function   micros(),   with a call at the rising edge, a second call at the falling edge, then a subtraction.

 

 

cheers,

Share this post


Link to post
Share on other sites

Throwing in an idea, not sure whether it's practical, but here you go.

 

Build a jig that runs at the speed (rpm) you target (DC motor?). Use a sensor to detect when it completes one rotation (hall effect, light barrier?). Fix an LED to it and turn it on/off at the start/end of your loop, or when the MCU is supposed to do control something. Basically a persistence of vision setup.

 

You will be able to see how much of the time budget your code uses, jitter and other parameters by looking at the LED trace (how much of the circle is it on? does it stay put or wiggle around?). It will also help to simulate other aspects of your motor control, e.g. synchronization with rotation.

Share this post


Link to post
Share on other sites

If you are still interested in ideas about how to measure the speed - if you have access to a computer with a sound input, have you considered connecting the LP output to sound input and using the sound card as an oscilloscope/measurement device?

 

You can record and analyze waveforms with something like Audacity, or there are various programs which give an oscilloscope like interface for a sound card.  (Sorry, I haven't used any of them to recommend one in particular.)

Since your frequencies fall in audible range that seems like a simple way to do it.

 

Of course you have to investigate how accurate such devices are, but at least audiophiles care about things like jitter, and accurately reproducing frequencies, so should be able to find specs. to estimate how far off such measurements might be. 

 

For a rough measure, hook your launchpad to a speaker and use a synthesizer to play a 5KHz (or whatever tone) and listen for beat frequencies (interference when the two tones are not quite the same).  Like the way a musician tunes an instrument.

(May have to make the signal pulses wider for this?)

 

As far as pulsein - you might consider this library for the Stellaris/Tiva lp

http://forum.stellarisiti.com/topic/1923-energia-library-timer-based-non-blocking-pulsein-equivalent/

which uses the timer as a counter.  (I haven't used it yet, or tested the limits, but seems like it should be a lot more capable

than the built in library).

Share this post


Link to post
Share on other sites

@@igor - Hadn't thought about using the sound card on my PC. Good idea, actually. The signals are pretty much within human hearing range so shouldn't be mucked with much on the inputs.

Share this post


Link to post
Share on other sites

actually, the sound card scope is a great idea.

i should really use it more often for low frequency stuff.

here's the one i use, v1.4.1  (i have no affiliations, but take my hat off to herr zeitnitz):

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×