Go Down

Topic: Algorithm for generating complicated pattens of pulses on several channels (Read 336 times) previous topic - next topic

leo_pribram

Hello,

My task is using millis()/micros() to generate on trigger consequences of pulses of predefined length (us) and period (ms), that are organized in the trains lasting predefined time (ms).

Trigger initiates first pulse in the train as soon as possible.

The trains are repeated at some intervals (ms) as long as the trigger is ON.

TWO (or more) channels are work independently. Tens of microseconds precision for pulses and tens of milliseconds for trains are acceptable.

Using this forum explanations (mainly Nick Gammon posts) I manage to generate regular pulses (without use of delay()), however how to organize nested cycles (I suppose that is what I need) for the trains of pulses and their intervals I do not know.

Would someone experienced be so kind as to provide me with an algorithm for this task.

Thanks in advance,
Leo

PS below is an example of the trains:

|||||           |||||          |||||         |||||

gcjr

it's not clear if the pulses appear at fixed intervals or are asynchronous

an asynchronous approach might determined when the output needs to change state assuming pulse widths vary.     triggering a sequence may be more about determining a relative set of times (usec)


a synchronous approach would potentially modify the output at every possibly pulse period regardless if triggered.  outputting a steady zero is the equivalent of no pulses.   when triggered, a sequence is generated and that sequence used to set/clear the output every pulse interval regardless of whether the output changes state

the asynchronous approach probably uses fewer MIPS and has more flexibility
greg - somerset, nj

aarg

Also please specify min, typ and max intervals for the pulses and the spaces between. "us" and "ms" are entirely too vague.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

leo_pribram

Pulses appear at fix intervals. Pulses are from 0.1 to 20 ms, period is 2 -1000 ms, train is 0.1 - 2 s, interval between trains is 0.1 - 10 s.
Thanks,
Leo

6v6gt

I've done something similar for storing and retrieving pulses for an infrared protocol generator.
There, the timings were in an array something like:

Code: [Select]

0: 9000
1: 4500
2:  562
3:  562
4:  562
5: 1688


where the timings were in microseconds and even indexes 0,2,4 etc. were pulse on periods and odd indexes 1,3,5 etc., were pulse off periods. Actually, these switched a carrier signal on and off. When an interval expired, the carrier was switched on or off as required and the next interval was read from the array.

Can you represent the timing sequences you have been given in such a way ?
Is the interval between repeating groups always the same ?


leo_pribram

As far as I understood approach hidden in "gcjr" question on synchronous pulsing and "6v6gt" suggestion, my attempt to use micros() for building a sequences of pulses was a bad idea.

Should I do the following:
1. Form carrier pulses (let say 1 us) using timer
2. Redirect pulses in two channels if I need two of them, or use two timers
3. Make a switch on each channel (how?)
4. Somehow (how?) tell the switch that the conditions (trigger ON/OFF, pulse ON/OFF time, burst ON/OFF time, train ON/OFF time) are met, and let the switch to zeroing pulses for given period.

Another way to control the switches is calculating number of carrier pulses with counters.

Is this correct approach? Will it be more precise than one using micros()? if so, how the core of the program should look like?

Leo


6v6gt

You must have clearer requirements than you are stating.

A 1uS carrier is 1MHz. That is very high considering the clock of the Arduino runs at 16MHz. The micros() function has a resolution of 4uS because it is driven by a 250kHz timer.
Are you using the same carrier frequency for the X channels you are hoping to drive ?
Are the channels to operate simultaneously ?
You can switch timer outputs on and off by multiple methods, e.g. setting the chosen output pin to input with pinMode() would work.
Counting the number of carrier pulses is good for the bursts if you drive a timer at the carrier frequency.
In the timer ISR, you can switch the carrier output on/off at the appropriate number of pulses. This is best done if your array, which stores the sequence of on/off intervals, contains the number of carrier pulses instead of a time in microseconds.

leo_pribram

I suppose that carrier frequency for all channels should be the same, is it?

Channels are operating asynchronously, each one react on its own trigger, however +/-10us in start of each channel does not matter for this type of experiments.

Most probably the approach that requires carrier frequency (including array with sequence of pulses, calculating pulses etc) will exceed my current experience (and mental ability :).

Below is what I managed to generate so far using millis(). However I still do not understand where to introduce code that will determine bursts of pulses (redLEDburst in ms). This code reproduce only one pulse at redLED interval:

 
Code: [Select]

      if (digitalRead (redTrigger) == HIGH) {
         
         if ( millis () - redLEDforinterval >= redLEDinterval)    {
              redLEDforinterval = millis ();
         
              if ( micros () - redLEDforperiod >= redLEDperiod)   
                redLEDforperiod = micros();
                redLEDforpulseWidth = micros();
                }
               
                if (micros() - redLEDforpulseWidth <= redLEDpulseWidth) {   
                digitalWrite (redLED, HIGH);
                }  else digitalWrite(redLED, LOW);
               
                }  else digitalWrite(redLED, LOW);

);

6v6gt

from post #3:

>> Pulses appear at fix intervals. Pulses are from 0.1 to 20 ms, period is 2 -1000 ms, train is 0.1 - 2 s, interval between trains is 0.1 - 10 s.


In any given train of pulses, is the pulse length, wave period (pulse length + following space) , length of pulse train, and interval between pulse trains all constant ?

You have mentioned multiple channels. Is the output of each channel identical except for the phase shift caused by the different start times ?








gcjr

i think "clocK" is more appropriate than "carrier".

don't understand the idea of a "switch" for multiple channels.  i assume each "channel" is a separate output pin and the data for each channel is independent of the other.

Pulses appear at fix intervals. Pulses are from 0.1 to 20 ms, period is 2 -1000 ms, train is 0.1 - 2 s, interval between trains is 0.1 - 10 s.
suggests the clock is every 100 msec usec, there could be 200 consecutive pulses that are "1" (20 ms) and up to 20,000 pulse (2s) per train.
greg - somerset, nj

gcjr

consider

Code: [Select]
#define Chan0 10
#define Chan1 11

// -----------------------------------------------------------------------------
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register

int lfsr1(void)
{
    static uint16_t start_state = 0xACE1u; /* Any nonzero will work. */
    static uint16_t lfsr = start_state;
    uint16_t bit;       /* Must be 16-bit to allow bit<<15 later in the code */

    /* taps: 16 14 13 11; feedback polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
    bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) /* & 1u */;
    lfsr = (lfsr >> 1) | (bit << 15);

    return lfsr & 1;
}

// -----------------------------------------------------------------------------
void setup() {
    Serial.begin(115200);
    Serial.print ("setup: ");

    pinMode (Chan0, OUTPUT);
    pinMode (Chan1, OUTPUT);
}

// -----------------------------------------------------------------------------
void loop() {
    static unsigned long msecLst = 0;
           unsigned long msec    = millis();

#define Period 100
    if (msec - msecLst >= Period)  {
        msecLst = msec;

        digitalWrite (Chan0, lfsr1());
        digitalWrite (Chan1, lfsr1());
    }
}
greg - somerset, nj

leo_pribram

"In any given train of pulses, is the pulse length, wave period (pulse length + following space) , length of pulse train, and interval between pulse trains all constant ?"

- Yes

"You have mentioned multiple channels. Is the output of each channel identical except for the phase shift caused by the different start times ?"

- No, timing is (or can be) different.

To gcjr: Thanks for the example. Each channel is indeed a separate output.
"Linear-feedback_shift_register" is a new concept for me, I'll try to understand it and its application for the task.

Leo

aarg

Linear-feedback_shift_register" is a new concept for me, I'll try to understand it and its application for the task.
It has no application for your task. It only generates a test stream to support the example.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

jimLee

Going over all this, it looks to me like you have a high frequency being toggled on and off by a low frequency. Am I right on this?

-jim lee
PNW Ardiuno & Maker club
1012 9Th Street, Anacortes, WA 98221 (Around the back of building)
GITHUB -> https://github.com/leftCoast

6v6gt

@gcjr has produced an impressively compact solution.

I've just put this one together, based on a finite state machine model.

Code: [Select]



class PulseTrain {

    enum state_t { WAIT, TRAIN_TRIGGERED, IN_CARRIER_MARK, IN_CARRIER_SPACE, IN_TRAIN_SPACE, AT_END } ;
    state_t state = WAIT ;

    // from constructor
    uint8_t _pulsePin ;
    uint32_t _pulseLengthUs ;
    uint32_t _pulseSpaceUs ;
    uint32_t _trainLengthMs ;
    uint32_t _trainSpaceMs ;

    uint32_t trainStartMs ;
    uint32_t trainSpaceStartMs ;
    uint32_t pulseStartUs ;
    uint32_t pulseSpaceStartUs ;



  public:

    PulseTrain( uint8_t pulsePin, uint32_t pulseLengthUs, uint32_t pulseSpaceUs, uint32_t trainLengthMs, uint32_t trainSpaceMs )
    {
      _pulsePin = pulsePin ;
      _pulseLengthUs = pulseLengthUs ;
      _pulseSpaceUs = pulseSpaceUs ;
      _trainLengthMs = trainLengthMs ;
      _trainSpaceMs = trainSpaceMs ;
    }

    void begin()
    {
      pinMode( _pulsePin, OUTPUT ) ;
    }

    void triggerOn()
    {
      state = TRAIN_TRIGGERED ;
    }

    void triggerOff()
    {
      state = AT_END ;
    }

    void loop() ;

} ;


void PulseTrain::loop()
{
  switch ( state ) {
    case TRAIN_TRIGGERED :
      trainStartMs = millis() ;
      pulseStartUs = micros() ;
      state = IN_CARRIER_MARK ;
      digitalWrite( _pulsePin, HIGH ) ;
      break ;

    case IN_CARRIER_MARK :
      if ( micros()  - pulseStartUs > _pulseLengthUs ) {
        // end of pulse
        pulseSpaceStartUs = micros() ;
        state = IN_CARRIER_SPACE ;
        digitalWrite( _pulsePin, LOW ) ;
      }
      else if ( millis() - trainStartMs > _trainLengthMs ) {
        // end of train
        trainSpaceStartMs = millis() ;
        state = IN_TRAIN_SPACE ;
        digitalWrite( _pulsePin, LOW ) ;
      }
      break ;

    case IN_CARRIER_SPACE :
      if ( micros()  - pulseSpaceStartUs > _pulseSpaceUs ) {
        // start of pulse
        pulseStartUs = micros() ;
        state = IN_CARRIER_MARK ;
        digitalWrite( _pulsePin, HIGH ) ;
      }
      else if ( millis() - trainStartMs > _trainLengthMs ) {
        // end of train
        trainSpaceStartMs = millis() ;
        state = IN_TRAIN_SPACE ;
        digitalWrite( _pulsePin, LOW ) ;
      }
      break ;

    case IN_TRAIN_SPACE :
      if ( millis() - trainSpaceStartMs > _trainSpaceMs ) {
        state = TRAIN_TRIGGERED ;
      }
      break ;

    case AT_END :
      digitalWrite( _pulsePin, LOW ) ;
      state = WAIT ;
      break ;

    case WAIT :
      // do nothing
      break ;
  }
}


PulseTrain testSequence1( 12, 10000UL , 20000UL , 1000 , 2000 ) ;  // define test sequence 1
PulseTrain testSequence2( 13, 20000UL , 50000UL , 1000 , 3000 ) ;  // define test sequence 2


void setup() {
  testSequence1.begin() ;
  testSequence1.triggerOn() ;   // trigger test sequence 1

  testSequence2.begin() ;
  testSequence2.triggerOn() ;   // trigger test sequence 2
}

void loop() {
  testSequence1.loop() ;
  testSequence2.loop() ;

}


and the output from channel testSequence2 (pin 13)



Go Up