/*
  Blink with Scope https://github.com/Dlloydev/Wokwi-Chip-Scope
          and https://github.com/Dlloydev/Wokwi-Chip-PWM

  Wokwi Uno https://wokwi.com/projects/390819301187622913
  Wokwi Mega: https://wokwi.com/projects/390819455604080641


*/
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;   // onboard-LED uno, mega


constexpr float PWM_bitIsOne  = 8620.69;//17241.379; // 15e3;  // 15 kHz
constexpr float PWM_bitIsZero = 5000.00; // 15e3;  // 15 kHz

constexpr uint16_t PERIOD_bitIsOne  = round(F_CPU / PWM_bitIsOne);
constexpr uint16_t PERIOD_bitIsZero = round(F_CPU / PWM_bitIsZero);

constexpr uint16_t PERIOD_Diff = PERIOD_bitIsZero - PERIOD_bitIsOne;

volatile boolean bitIs1 = true;
volatile byte ISRCnt = 0;

void setupTimer1() {
  // Configure Timer 1 for X kHz PWM on pin 9 = PB1 = OC1A.
  DDRB  |= _BV(PB5);    // set pin as output
  //pinMode(11,OUTPUT);
  TCCR1B = 0;           // stop timer
  TCCR1A = _BV(COM1A1)  // non-inverting PWN on OC1A
           | _BV(WGM11);  // mode 14: fast PWM, TOP = ICR1
  TCCR1B = _BV(WGM12)     // ditto
           | _BV(WGM13)   // ditto
           | _BV(CS10) ;
 //          | 0b100<<CS10;   // clock @ F_CPU
  ICR1   = PERIOD_bitIsOne - 1;  // period
  OCR1A  = PERIOD_bitIsOne / 2;  // duty cycle 1/2 = 50%

  TIMSK1 |= (1 << TOIE1) | (1<<OCIE1A); // enable timer compare interrupt

  dbg("TS", PERIOD_bitIsOne);
  dbg("TS", PERIOD_bitIsZero);
}

void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();

  setupTimer1();
}

// enable with TIMSK1 |= (1<<OCIE1A);
ISR(TIMER1_COMPA_vect){
  // set the on-time cor the next bit
   OCR1A =  bitIs1 ? PERIOD_bitIsZero / 2 : PERIOD_bitIsOne / 2; 
}

ISR(TIMER1_OVF_vect) {

  if (bitIs1) {
    bitIs1 = false;
    ICR1   = PERIOD_bitIsZero - 1;  // period
    OCR1A  = PERIOD_bitIsZero / 2;  // duty cycle 1/2 = 50%
  }
  else {
    bitIs1 = true;
    ICR1   = PERIOD_bitIsOne - 1;  // period
    //OCR1A  = PERIOD_bitIsOne / 2;  // duty cycle 1/2 = 50%
  }
}



void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

}




// helper-functions
void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
// explanation see here
// https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}



void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}
Loading chip...chip-scope
Loading chip...chip-pwm
Wokwi MegaScope https://wokwi.com/projects/390819455604080641