Steven Sarns
Published © CC BY

YAAAP (Yet Another Arduino Art Project)

Wife bought a garden art thing. Waited. Nothing happened. Added a few magnets, an Arduino and viola!

IntermediateFull instructions provided8 hours218
YAAAP (Yet Another Arduino Art Project)

Things used in this project

Hardware components

"Blue Pill" STM32F103T6 development board
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Hammer to hit yourself on the head when you make stupid mistake

Story

Read more

Schematics

Schematic

Created with DipTrace

Code

Pendulum_2022_0844

Arduino
See source for comments
// Triple Pendulum controller, STM32F103C6T8 "BluePill"
// IDE Tools setup: "Generic STM32F103C series", upload method=serial, optimise=fastest (uses 43% flash)
// Additional Boards Managers URLs: http://dan.drown.org/stm32duino/package_STM32duino_index.json

// 0819 change state names
// 0820 led, restructure main loop
// 0821 big test pwb, change pin numbers
// 0822 working, but analog signals messed up - feedthrough
// 0823 add aux blank
// 0824 revert - timer1 ch2 either kills print or does not get evoked
// 0825 abandon timer1 ch2, use timer4 instead
// 0828 all working
// 0830 P3 startup
// 0831 rethink boost calculation = constant in STABLE state
// 0832 STABLE state: min constrain = sustain value
// 0833 P1 startup, two cases; captured by magnet or not, difficult if not, eval STARTUP exit criteria
// 0834 work on dead state
// 0840 new state names, new dead state algorithm
// 0842 working
// 0843 reset boost counter BOOST->STABLE
// 0844 P1 start conditions, PxperiodCmd is being reset to zero

//                      +-----------+
// scope        T1BKIN  |PB12   GND |
//              T1C1N   |PB13   GND |
//              T1C2N   |PB14   V3V |
//              T1C3N   |PB15   RST |                   
//              T1C1    |PA8    PB11| RX3   SDA2        
// IDE      TX  T1C2    |PA9    PB10| TX3   SCL2         
// IDE      RX  T1C3    |PA10   PB1 | ADC9        T3C4  
//         USB- T1C4    |PA11   PB0 | ADC8        T3C3  scope
//         USB+ T1ETR   |PA12   PA7 | ADC7  MOSI1 T3C2  led
//              T2C1E   |PA15   PA6 | ADC6  MISO1 T3C1    
//              T2C2    |PB3    PA5 | ADC5  SCK1         
// P1 blank     T3C1    |PB4    PA4 | ADC4  NSS1        P3 signal
// P2 blank     T2C2    |PB5    PA3 | ADC3  RX2   T2C4  
// P3 blank     T4C1    |PB6    PA2 | ADC2  TX2   T2C3  
// P3 gate      T4C2    |PB7    PA1 | ADC1        T2C2  P2 signal 
// P2 gate SCL1 T4C3    |PB8    PA0 | ADC0  WKUP  T2C1E P1 signal
// P1 gate SDA1 T4C4    |PB9   PC15 | OSC32             
//              +5      |5V    PC14 | OSC32             
//              GND     |GND   PC13 | LED                
//              3V3     |3V3   VBAT |
//                      +-----------+

// ------------------------- STM32F103C6 ----------------------
// ints are 4 bytes, +/-2,147,483,648
// STM32F pullup/down R ~40k Ohms
// Fault Tolerant; PA8-15, PB2-4, 6-15, PC6-9, 10-12
// Not Fault Tolerant; PA0-7, PB0-1, 5, PC0-5, 13-15

// ------------------------ Coil data --------------------------
// coil is ~20 mH, saturates in 7 mS @ boost level=15
// ~12 Ohms (32ga wire), 6 Ohms (28ga wire)
// .5" soft iron core, 3" long, ~600 turns

HardwareTimer pirTimer(1);  // timer1, name=pirTimer, use channel 1
HardwareTimer P2timer(2);   // timer2, name=P2timer, using channels 1,2,3
HardwareTimer P3timer(3);   // timer3, name=P3timer, using channels 1,2,3
HardwareTimer P4timer(4);   // timer4, name=P4timer, using channels 1,2,3

// ------------------------- Pins ------------------------------------
#define P1signalPin PA0     // P1 signal, analog
#define P2signalPin PA1     // P2 signal, analog
#define P3signalPin PA4     // P3 signal, analog
#define P1blankPin  PB4     // P1 MOSFET blanking clamp
#define P2blankPin  PB5     // P2 MOSFET blanking clamp
#define P3blankPin  PB6     // P3 MOSFET blanking clamp
#define P1gatePin   PB9     // P1 MOSFET coil power
#define P2gatePin   PB8     // P2 MOSFET coil power
#define P3gatePin   PB7     // P3 MOSFET coil power
#define ledPin      PA7     // led, active high
#define scopePin    PB0     // debug

// ------------------------------- Variables ---------------------------------------------
byte        report;         // which P to report on
byte        lineCounter;    // renew heading title line counter
uint32_t    loops;          // show main loops/second execution rate
uint16_t    seconds;        // seconds counter, very long time
char const  TAB     = 9;    // tab
byte const  STARTUP = 0;    // all pendulums start in STARTUP state
byte const  BOOST   = 1;    // BOOSTing, either being kicked by STARTUP isr or BOOST case
byte const  STABLE  = 2;    // STABLE, kicked by STABLE case
byte const  timex   = 50;   // supplimental blank transcient suppression, 1mS ticks

byte        P1boostCount;   // count after successfully exiting STARTUP state
byte        P1state;        // state of P1
byte        P1startCount;   // not used
uint16_t    P1periodCmd;    // desired period
uint16_t    P1periodAct;    // actual period
uint16_t    P1signal;       // signal from magnet approaching coil
uint16_t    P1sigMax;       // used to detect approaching magnet
uint16_t    P1countA;       // half period
uint16_t    P1countB;       // other half period
uint16_t    P1boost;        // amount of energization to deliver to coil
uint16_t    P1blank;        // time to clamp P1 signal to ground, greater than boost
int16_t     P1periodErr;    // signed, command - actual
boolean     P1flip;         // flipflop for period measurement
boolean     P1blankFinished;// signal clamp released
boolean     P1blankx;       // brief blank active when another channel releases
boolean     P1osc;          // flag set by isr channel 3 to excite P1 at fundamental freq
float       P1Kp=1;         // proportional term of PID control loop

byte        P2boostCount;   // count after successfully exiting STARTUP state
byte        P2state;        // state of P2
byte        P2startCount;   // not used
uint16_t    P2periodCmd;    // desired period
uint16_t    P2periodAct;    // actual period
uint16_t    P2signal;       // signal from magnet approaching coil
uint16_t    P2sigMax;       // used to detect approaching magnet
uint16_t    P2countA;       // half period
uint16_t    P2countB;       // other half period
uint16_t    P2boost;        // amount of mag energization to deliver to coil
uint16_t    P2blank;        // time to clamp P1 signal to ground, greater than boost
int16_t     P2periodErr;    // signed, command - actual
uint16_t    P2sigAveSave;   // save for report
boolean     P2flip;         // flipflop for period measurement
boolean     P2magDetect;    // mag signal detected flag
boolean     P2blankFinished;// signal clamp released
boolean     P2blankx;       // brief blank active when another channel releases
boolean     P2osc;          //
float       P2Kp=.5;        // proportional term of PID control loop

byte        P3boostCount;   // count after successfully exiting STARTUP state
byte        P3state;        // state of P3
byte        P3startCount;   //
uint16_t    P3periodCmd;    // desired period
uint16_t    P3periodAct;    // actual period
uint16_t    P3signal;       // signal from magnet approaching coil
uint16_t    P3sigMax;       //
uint16_t    P3countA;       // half period
uint16_t    P3countB;       // other half period
uint16_t    P3boost;        // amount of mag energization to deliver to coil
uint16_t    P3blank;        // time to clamp P1 signal to ground, greater than boost
int16_t     P3periodErr;    // signed, command - actual
uint16_t    P3sigAveSave;   // save for report
boolean     P3flip;         // flipflop for period measurement
boolean     P3magDetect;    // mag signal detected flag
boolean     P3blankFinished;// signal clamp released
boolean     P3blankx;       // brief blank active when another channel releases
boolean     P3osc;
float       P3Kp=1;         // proportional term of PID control loop

uint16_t volatile P1timex;  // 3 timers control blanking of signal artifact generated
uint16_t volatile P2timex;  // when a coil is switched off.  Timers generate blanking on
uint16_t volatile P3timex;  // other coil signals for brief period (~50ms).
// -------------------------- Timer1 ISR --------------------------------
void pirIsr(void) {     // interrupt is called 1000x/sec
  if(P1timex) {         // decrement timer until=0
    P1timex--;          // if signal normal blanking is not active release clamp
    if(!P1timex && P1blankFinished) digitalWrite(P1blankPin, LOW);// release signal clamp
  }
  if(P2timex) {
    P2timex--;
    if(!P2timex && P2blankFinished) digitalWrite(P2blankPin, LOW);
  }
  if(P3timex) {
    P3timex--;
    if(!P3timex && P3blankFinished) digitalWrite(P3blankPin, LOW);
  }
}

// ------------------------- Timer4 ISRs --------------------------------
// Timer 1 channel 2 interacts with print, so must use timer 4
// Timer 4 is associated with Pendulum 1, Timer2->P2, Timer3->P3
// Channel 1 turns off coil MOSFET gate
// Channel 2 turns off signal blanking clamp MOSFET
// Channel 3 detects no activity -> issues kick to start moving
void P4chan1isr() {                 // coil off->induces spike on other analog channels
  digitalWrite(P1gatePin,LOW);      // turn off coil
  digitalWrite(ledPin,  LOW);       // turn off led
  digitalWrite(P2blankPin, HIGH);   // inactive channels clamped
  digitalWrite(P3blankPin, HIGH);   // inactive channels clamped
  P1timex = 0;                      // not this channel
  P2timex = timex;                  // start blanking, short time
  P3timex = timex;                  // start blanking
}

void P4chan2isr() {                 // channel 2 turns off signal blanking clamp P1
  digitalWrite(P1blankPin,LOW);     // turn off blanking clamp
  P1blankFinished = true;           // set flag -> blank finished
}

void P4chan3isr() {                 // channel 3 generates startup waveform then detects dead P1  
  P1startCount = 0;                 // not used
  P1state = STARTUP;                // P1 must be dead if this isr executes
  report = 1;
  lineCounter = 0;
  P1osc = true;
}


// ----------------------- Timer2 ISRs -----------------------------------
void P2chan1isr() {
  digitalWrite(P2gatePin,LOW);      // kick off
  digitalWrite(ledPin,  LOW);       // turn off led
  digitalWrite(P1blankPin, HIGH);
  digitalWrite(P3blankPin, HIGH);
  P2timex = 0;
  P1timex = timex;
  P3timex = timex;
}

void P2chan2isr() {
  digitalWrite(P2blankPin,LOW);     // blank off
  P2blankFinished = true;
}

void P2chan3isr() {                 // 
  P2startCount = 0;
  P2state = STARTUP;                // must clamp for 1.5sec after pulse finish
  report = 2;
  lineCounter = 0;
  P2osc = true;
}


// -------------------------- Timer3 ISRs --------------------------------
void P3chan1isr() {
  digitalWrite(P3gatePin,LOW);  // kick off
  digitalWrite(ledPin,  LOW);  // turn off led
  digitalWrite(P2blankPin, HIGH);
  digitalWrite(P1blankPin, HIGH);
  P3timex = 0;
  P2timex = timex;
  P1timex = timex;
}

void P3chan2isr() {
  digitalWrite(P3blankPin,LOW); // blank off
  P3blankFinished = true;
}

void P3chan3isr() {
  P3startCount = 0;
  P3state = STARTUP;
  report = 3;
  lineCounter = 0;
  P3osc = true;
}




//------------------------------- Setup ---------------------------------
void setup() {
  disableDebugPorts();              // required to access PB3, PB4(!), PA13, PA14 & PA15
  pinMode(P1signalPin,INPUT_ANALOG);// signal detection circuit 
  pinMode(P2signalPin,INPUT_ANALOG);// 
  pinMode(P3signalPin,INPUT_ANALOG);// 
  pinMode(P1gatePin,  OUTPUT);      // coil MOSFET gate pin
  pinMode(P2gatePin,  OUTPUT);
  pinMode(P3gatePin,  OUTPUT);
  pinMode(P1blankPin, OUTPUT);      // signal blanking MOSFET gate pin
  pinMode(P2blankPin, OUTPUT);
  pinMode(P3blankPin, OUTPUT);
  pinMode(ledPin,     OUTPUT);
  pinMode(scopePin,   OUTPUT);      // debug
  digitalWrite(ledPin,   HIGH);     // led on
  digitalWrite(P1gatePin, LOW);     // turn off P1 coil
  digitalWrite(P2gatePin, LOW);
  digitalWrite(P3gatePin, LOW);
  digitalWrite(P1blankPin,LOW);     // P1 blanking off, allow P1 signal
  digitalWrite(P2blankPin,LOW);
  digitalWrite(P3blankPin,LOW);
  Serial.begin(9600);   // 9600 Baud to Arduino IDE monitor

  // -------------------- Timer1, 1 mS timebase -----------------------
  pirTimer.pause();                           // pause timer4
  pirTimer.setPrescaleFactor(72);             // set timer 4 prescaler
  pirTimer.setOverflow(1000);                 // period
  pirTimer.setMode(1,TIMER_OUTPUT_COMPARE);   // channel 1, generate interrupt on compare
  pirTimer.setCompare(1,1);                   // interrupt at start
  pirTimer.attachInterrupt(1,pirIsr);         // attach interrupt
  pirTimer.refresh();                         // scope confirm 1mS timebase
  pirTimer.resume();                          // continue
  
// -------------------- Timer4, P1 timer -----------------------
  P4timer.pause();                            // pause timer4
  P4timer.setPrescaleFactor(36000);           // 72MHz/36000=2kHz, .5mS/tick
  P4timer.setOverflow(65535);                 // period, 32.768 sec rollover
  P4timer.refresh();                          // refresh
  P4timer.resume();                           // continue

// -------------------- timer2, P2 timer -----------------------
  P2timer.pause();                            // pause timer2
  P2timer.setPrescaleFactor(36000);           // set timer 2 prescaler, .5mS/tick
  P2timer.setOverflow(65535);                  // period, 32 sec
  P2timer.refresh();                          // refresh
  P2timer.resume();                           // continue
  
// -------------------- Timer3, P3 timer -------------------------
  P3timer.pause();                            // pause timer3
  P3timer.setPrescaleFactor(36000);           // set timer 3 prescaler
  P3timer.setOverflow(65535);                  // period, 32 sec
  P3timer.refresh();                          // refresh
  P3timer.resume();                           // continue
  
  P1periodCmd = 8100;                         // nominal periods for pendulums
  P2periodCmd = 12500;
  P3periodCmd = 6600;

  P1blankFinished = true;
  P2blankFinished = true;
  P3blankFinished = true;
  digitalWrite(P1blankPin, LOW);
  digitalWrite(P2blankPin, LOW);
  digitalWrite(P3blankPin, LOW);

  P1osc = true;
  P2osc = true;
  P3osc = true;
  
  report = 2;
}

// -------------------------------- Main ---------------------------------
// Each pendulum has 3 conditions; STARTUP, BOOSTing and STABLE
// Pendulum starts STARTUP. Channel 3 kicks after timeout.  Random kicks will get it moving.
// Dead kick recurring timeout should be greater than natural period.
// After STARTUP kick, pendulm enters BOOST state where signal determines kick timing.
// After several swings->STABLE state.  Amount of kick in STABLE state is closed loop control.
//  P1    P2    P3
//  --    --    --
//  240   420   200 millisec  period during startup (magnetic capture) phase
//  7800  11800 6200 timer ticks period during stable running state
//  8300  12500 6750 max period of pendulums
//  7650  11500 6100 min period

//  digitalWrite(scopePin, !digitalRead(scopePin));
//  digitalWrite(ledPin, !digitalRead(ledPin));

void loop() {
  loops++;                        // execution rate 66k (P3) to 120k (P2)loops/kick
  if(P2state==STABLE)  P1();      // start P2 after P1 is STABLE
  P2();                           // P2 is hardest to start, goes longest, start first
  if(P1state==STABLE) P3();       // P3 is easiest to start, sustains least, start last

//  P1();
//  P2();
//  P3();
  if(Serial.available()) opCom(); // get operator command
}

// --------------------------- P1 state machine --------------------------------
void P1() {
  P1signal = analogRead(P1signalPin);         // read signal for this pendulum
  P1sigMax = max(P1signal,P1sigMax);          // looking for max signal level
  switch(P1state) {                           // STABLE, BOOST, STARTUP
    case STABLE:                              // steady state - works perfectly
      if((P1sigMax>3000)&&(P1signal<10)&&P1blankFinished) {
        P1periodErr = P1periodCmd - P1periodAct;// error
        P1boost = 10 + (P1Kp*P1periodErr);    // calc P term
        P1boost = constrain(P1boost,9,80);    // "9" is sustain
        P1blank = 2600;                       // period is ~3900, blank 2600 good
        P1kick(P1boost, P1blank,10000);       // blank for 1 second, STARTUP after 30 sec
      }    
      break;
    
    case BOOST:                               // tricky->low signal level, ADC noise
      if((P1sigMax>2000)&&(P1signal<10)&&P1blankFinished) {
        P1boostCount++;                       // count successful kicks
        if(P1boostCount>10) {
          P1boostCount = 0;
          P1state = STABLE;                   // change state
        }                                                                                                                                
        P1boost = 100;                        // must synch with rapid freq period
        P1blank = 300;                        // during BOOST phase (captive of magnet), else 2700
        P1kick(P1boost,P1blank,5000);         // 
      }
      break;
      
    case STARTUP:                             // signal goes to zero only when
      if(P1sigMax>3500) P1state = BOOST;      // magnet swings past coil
      if(P1osc) {                             // BOOST state does not trigger
        P1osc = false;                        // if magnet recaptures pendulum
        P1kick(200, 250, random(1050,1150));   // after exiting STARTUP state
      }                                       // 1100 works
      break;
  }
}

// ----------------------------- P2 state machine -----------------------------------
void P2() {  
  P2signal = analogRead(P2signalPin);         // read signal for this pendulum
  P2sigMax = max(P2signal,P2sigMax);          // recored max sig for this cycle
  switch(P2state) {                           // STARTUP or STABLE
    case STABLE:                              // steady state
      if((P2sigMax>3000)&&(P2signal<10)&&P2blankFinished) {
        P2periodErr = P2periodCmd - P2periodAct;// error
        P2boost = 14 + (P2Kp*P2periodErr);    // calc P term
        P2boost = constrain(P2boost,9,80);    // "9" is sustain
        P2blank = 5000;                       // period ~5900, 5000 good for period>10800
        P2kick(P2boost,P2blank,20000);        // blank for 1 second, STARTUP after 5 sec
      }    
      break;
    
    case BOOST:                               // tricky->low signal level, ADC noise
      if((P2sigMax>2000)&&(P2signal<10) && P2blankFinished) {
        P2boostCount++;                       // count successsful kicks
        if(P2boostCount>10) {
          P2boostCount = 0;
          P2state = STABLE;                   // change state
        }
        P2boost = 100;
        P2blank = 1000;                       // high freq, captive of magnet
        P2kick(P2boost,P2blank,20000);        // big kick, blank 1.5s, STARTUP after 10s
      }
      break;
      
    case STARTUP:                             // 
      if(P2sigMax>3500) P2state = BOOST;      // magnet swings past coil
      if(P2osc) {                             // BOOST state does not trigger
        P2osc = false;                        // if magnet recaptures pendulum
        P2kick(400, 450, random(2700,2800));  // after exiting STARTUP state
      }                                       // 2750 works
      break;
    }
  }


// ----------------------------- P3 state machine -----------------------------------
void P3() {
  P3signal = analogRead(P3signalPin);         // read signal for this pendulum
  P3sigMax = max(P3signal,P3sigMax);          // record max signal
  switch(P3state) {                           // STARTUP, BOOST, STABLE
    case STABLE:                              // steady state - works perfectly
      if((P3sigMax>3000)&&(P3signal<10)&&P3blankFinished) {
        P3periodErr = P3periodCmd - P3periodAct;// error
        P3boost = 10 + (P3Kp*P3periodErr);    // calc P term
        P3boost = constrain(P3boost,7,80);    // "7" is sustain
        P3blank = 2400;                       // period ~3100 (2400 good)
        P3kick(P3boost,P3blank,10000);        // blank for 1 second, STARTUP after 10 sec
      }    
      break;
    
    case BOOST:                               // getting started, constant kick
      if((P3sigMax>2000)&&(P3signal<10)&&P3blankFinished) {
        P3boostCount++;     // count successful kicks
        if(P3boostCount>10) {
          P3boostCount = 0;
          P3state = STABLE; // change state
        }
        P3boost = 100;                        // constant, large kick in boost phase
        P3blank = 500;                        // blank should be brief in case wrong trigger
        P3kick(P3boost,P3blank,4000);         // big kick
      }
      break;
      
    case STARTUP:                             // tricky->low signal level, ADC noise
      if(P3sigMax>3500) P3state = BOOST;      // magnet swings past coil
      if(P3osc) {                             // BOOST state does not trigger
        P3osc = false;                        // if magnet recaptures pendulum
        P3kick(400, 600, random(1500,2600));  // after exiting STARTUP state
      }                                       // random(1500,2600) works
      break;
  }
}

// -------------------------- P1 coil control ----------------------------
void P1kick(uint16_t bst, uint16_t blnk, uint16_t ded) {
  if(report==1) digitalWrite(ledPin,HIGH);// turn on led if report to this P is active
  P1flip = !P1flip;                       // flip flop
  P4timer.pause();                        // stop timer
  if(P1flip) P1countA = P4timer.getCount();// calc period of P4
  else       P1countB = P4timer.getCount();// get half count
  P1periodAct = P1countA + P1countB;      // add two halves
  P4timer.setCount(0);                    // restart timer at zero count
  P4timer.attachInterrupt(1,P4chan1isr);  // isr will turn off boost
  P4timer.attachInterrupt(2,P4chan2isr);  // isr will turn off blanking
  P4timer.attachInterrupt(3,P4chan3isr);  // isr will detect STARTUP condition
  P4timer.setMode(1,TIMER_OUTPUT_COMPARE);// channel 1, generate interrupt on compare
  P4timer.setMode(2,TIMER_OUTPUT_COMPARE);// channel 4, generate interrupt on compare
  P4timer.setMode(3,TIMER_OUTPUT_COMPARE);// channel 3, generate interrupt on compare
  P4timer.setCompare(TIMER_CH1,bst);      // boost
  P4timer.setCompare(TIMER_CH2,blnk);     // blank
  P4timer.setCompare(TIMER_CH3,ded);      // STARTUP
  P4timer.refresh();                      // update
  P4timer.resume();                       // start timer
  digitalWrite(P1gatePin, HIGH);          // turn on magnet
  digitalWrite(P1blankPin,HIGH);          // turn on signal blanking  
  P1blankFinished = false;                // reset blank finished flag
  if(report==1) report1();                // report to IDE monitor
  P1sigMax = 0;
}

// -------------------------- P2 coil control ----------------------------
void P2kick(uint16_t bst, uint16_t blnk, uint16_t ded) {
  if(report==2) digitalWrite(ledPin,HIGH); // turn on led if report to this P is active
  P2flip = !P2flip;                       // calculate period of P2
  P2timer.pause();                        // stop timer
  if(P2flip) P2countA = P2timer.getCount();
  else       P2countB = P2timer.getCount();// get half count
  P2periodAct = P2countA + P2countB;      // add two halves
  P2timer.setCount(0);                    // reset
  P2timer.attachInterrupt(1,P2chan1isr);// isr will turn off boost
  P2timer.attachInterrupt(2,P2chan2isr);// isr will turn off blanking
  P2timer.attachInterrupt(3,P2chan3isr);// isr will detect STARTUP condition
  P2timer.setMode(1,TIMER_OUTPUT_COMPARE);   // channel 1, generate interrupt on compare
  P2timer.setMode(2,TIMER_OUTPUT_COMPARE);   // channel 2, generate interrupt on compare
  P2timer.setMode(3,TIMER_OUTPUT_COMPARE);   // channel 3, generate interrupt on compare
  P2timer.setCompare(TIMER_CH1,bst);      // boost
  P2timer.setCompare(TIMER_CH2,blnk);     // blank
  P2timer.setCompare(TIMER_CH3,ded);      // STARTUP
  P2timer.refresh();                      // update
  P2timer.resume();                       // start timer
  digitalWrite(P2gatePin, HIGH);          // turn on magnet
  digitalWrite(P2blankPin,HIGH);          // turn on signal blanking  
  P2blankFinished = false;                // reset blank finished flag
  if(report==2) report2();                // report to IDE monitor
  P2sigMax = 0;
}

// -------------------------- P3 coil control ----------------------------
void P3kick(uint16_t bst, uint16_t blnk, uint16_t ded) {
  if(report==3) digitalWrite(ledPin,HIGH); // turn on led if report to this P is active
  P3flip = !P3flip;                       // calculate period of P3
  P3timer.pause();                        // stop timer
  if(P3flip) P3countA = P3timer.getCount();
  else       P3countB = P3timer.getCount();// get half count
  P3periodAct = P3countA + P3countB;      // add two halves
  P3timer.setCount(0);                    // reset
  P3timer.attachInterrupt(1,P3chan1isr);  // isr will turn off boost
  P3timer.attachInterrupt(2,P3chan2isr);  // isr will turn off blanking
  P3timer.attachInterrupt(3,P3chan3isr);  // isr will detect STARTUP condition
  P3timer.setMode(1,TIMER_OUTPUT_COMPARE);// channel 1, generate interrupt on compare
  P3timer.setMode(2,TIMER_OUTPUT_COMPARE);// channel 2, generate interrupt on compare
  P3timer.setMode(3,TIMER_OUTPUT_COMPARE);// channel 3, generate interrupt on compare
  P3timer.setCompare(TIMER_CH1,bst);      // boost
  P3timer.setCompare(TIMER_CH2,blnk);     // blank
  P3timer.setCompare(TIMER_CH3,ded);      // STARTUP
  P3timer.refresh();                      // update
  P3timer.resume();                       // start timer
  digitalWrite(P3gatePin, HIGH);          // turn on magnet
  digitalWrite(P3blankPin,HIGH);          // turn on signal blanking  
  P3blankFinished = false;                // reset blank finished flag
  if(report==3) report3();                // report to IDE monitor
  P3sigMax = 0;
}  

// -------------------------------  operator interface -------------------------------------
void opCom() {
  int inNum;  
  char inChar;
  inChar = Serial.read();                       // get char from incoming serial stream
  if((inChar==13)||(inChar==10)) return;        // ignore trailing <CR> <LF> last command
  inChar = toupper(inChar);
  switch(inChar)    {
    case 'R':                     // (R)eport, select which pendulum
      report = Serial.parseInt(); // activate selected report
      lineCounter = 0;
      break;       
  
    case 'P':                     // Set period of selected pendulum
      inNum = Serial.parseInt();  // get number
      Serial.println();
      Serial.print("P");          // (P)eriod
      Serial.print(report);       // 
      Serial.print(" Period ");   // 
      Serial.println(inNum);      // <LF><CR>
      if(report==1) P1periodCmd = inNum;
      if(report==2) P2periodCmd = inNum;
      if(report==3) P3periodCmd = inNum;
      break;

     default:
       Serial.print(" ?");
       inNum = inChar;
       Serial.println(char(inNum));
       break;
  }
}

void plot() {
  Serial.println(P2sigAveSave);           // change baud to 115200
  delay(10);
}

// ---------------------------- reports ---------------------------------------------
void  report1() {
  if(!(lineCounter%20)){
    Serial.println();
    Serial.println("P1State Psigmax Pact    Pcmd    Err     Boost   Loops");
  }
  lineCounter++;
  if(P1state==STARTUP)  Serial.print("STARTUP");
  if(P1state==BOOST)    Serial.print("BOOST");
  if(P1state==STABLE)   Serial.print("STABLE");
  Serial.print(TAB);
  Serial.print(P1sigMax);
  Serial.print(TAB);
  Serial.print(P1periodAct);
  Serial.print(TAB);
  Serial.print(P1periodCmd);
  Serial.print(TAB);
  Serial.print(P1periodErr);
  Serial.print(TAB);
  Serial.print(P1boost);
  Serial.print(TAB);
  Serial.print(loops);
  Serial.println();
  loops = 0;
}

void  report2() {
  if(!(lineCounter%20)){
    Serial.println();
    Serial.println("P2State Psigmax Pact    Pcmd    Err     Boost   Loops");
  }
  lineCounter++;
  if(P2state==STARTUP)  Serial.print("STARTUP");
  if(P2state==BOOST)    Serial.print("BOOST");
  if(P2state==STABLE)   Serial.print("STABLE");
  Serial.print(TAB);
  Serial.print(P2sigMax);
  Serial.print(TAB);
  Serial.print(P2periodAct);
  Serial.print(TAB);
  Serial.print(P2periodCmd);
  Serial.print(TAB);
  Serial.print(P2periodErr);
  Serial.print(TAB);
  Serial.print(P2boost);
  Serial.print(TAB);
  Serial.print(loops);
  Serial.println();
  loops = 0;
}

void  report3() {
  if(!(lineCounter%20)){
    Serial.println();
    Serial.println("P3State Psigmax Pact    Pcmd    Err     Boost   Loops");
  }
  lineCounter++;
  if(P3state==STARTUP)  Serial.print("STARTUP");
  if(P3state==BOOST)    Serial.print("BOOST");
  if(P3state==STABLE)   Serial.print("STABLE");
  Serial.print(TAB);
  Serial.print(P3sigMax);
  Serial.print(TAB);
  Serial.print(P3periodAct);
  Serial.print(TAB);
  Serial.print(P3periodCmd);
  Serial.print(TAB);
  Serial.print(P3periodErr);
  Serial.print(TAB);
  Serial.print(P3boost);
  Serial.print(TAB);
  Serial.print(loops);
  Serial.println();
  loops = 0;
}

Untitled file

ABAP
No preview (download only).

Untitled file

ABAP
No preview (download only).

Untitled file

ABAP
No preview (download only).

Credits

Steven Sarns
2 projects • 0 followers
Retired EE. Hobbyist. Started programming microprocessors in 1981.
Contact

Comments

Please log in or sign up to comment.