// LogicAnalyzerDemonstration.ino
// 12 March 2022, Version 1, by Koepel, Public Domain
// 14 March 2022, Version 2, by Koepel, Public Domain
//    Added a tone() output and a serial toggle output.
//    Added "Advanced" to the Instructions for some in-depth analysis of the signals.
// 4 Nov 2022, Version 3, The VCD file has now channel names.
// The Guide how to use the Logic Analyzer in PulseView:
//   https://docs.wokwi.com/guides/logic-analyzer
// The documentation for the Logic Analyzer in the circuit:
//   https://docs.wokwi.com/parts/wokwi-logic-analyzer
// This sketch provides signals for the Logic Analyzer.
// Instructions:
// -------------
//   Let the sketch run for a few seconds.
//   Stop the sketch.
//   If you are asked to store the file, store it on your computer.
//   Open the *.vcd file in PulseView with "import Value Change Dump data".
//   Set the "Downsampling factor" to 10 (or 50 if you have a slow computer).
//   Add a decoder "UART", it will automatically select the "TX" channel.
//     Channel "TX" is D1 on the Logic Analyzer, it is the second channel.
//     Set it to 115200 Baud rate and "ascii" Data format.
//   Add a decoder "I²C", it will automatically select channels SCL and SDA.
//      Channel "SCL" is at D2 and "SDA" is at D3.
//   Advanced:
//     (1)
//     When the Serial.print("Hello World"); is used with a long text, 
//     it will be transmitted after the the other code has finished.
//     That is because the data is copied into a 64 byte buffer, 
//     and the bytes are transmitted on a interrupt basis. 
//     (2)
//     Add a decoder "UART" and set the TX to channel "toggleTX" at D5.
//     Select the "ascii" Data format and 9600 Baud rate.
//     Move the serialToggle("Wokwi"); to the middle of the sketch and see what happens.
//     Since the serialToggle() turns off the interrupts now and then, 
//     it influences all interrupt driven signals (Serial, I2C, tone).
//     (3)
//     Notice how the pulses for a single byte of the Serial and I2C bus
//     are not influenced by the other interrupts, but the tone() pulses are.
//     That is because the bytes for the Serial and I2C signals 
//     are generated by hardware and every tone() pulse is
//     generated in a software interrupt.

#include <Wire.h>

void setup() 
  pinMode( 13, OUTPUT);      // for a start pulse and stop pulse
  pinMode( 11, OUTPUT);      // for a pin toggle serial output
  digitalWrite( 11, HIGH);   // A serial signal is idle high
  Serial.begin( 115200);
  delay( 100);

  // A pulse to identify the start.
  // Arduino pin 13 is connected to D1 of the Logic Analyzer.
  digitalWrite( 13, HIGH);   
  digitalWrite( 13, LOW);

  // Set a tone output of 20kHz on pin 8.
  // Arduino pin 8 is connected to D4 of the Logic Analyzer.
  tone(8, 20000);

  // Serial output on TX.
  // Arduino pin 1 (TX) is connected to D1 of the Logic Analyzer.
  Serial.print( "Hello World");

  // A I2C write to an address that does not exist (no acknowledge).
  // The I2C bus is at Arduino pins A4 and A5.
  // Pin A4 is SDA and is connected to D3 of the Logic Analyzer.
  // Pin A5 is SCL and is connected to D2 of the Logic Analyzer.
  Wire.beginTransmission( 8);

  // A small gap between the I2C sessions to be able to distinguish them better.
  delayMicroseconds( 100);

  // A I2C write to a I2C address.
  Wire.beginTransmission( 0x68);
  Wire.write( 0);

  // a small gap between the I2C sessions.
  delayMicroseconds( 100);    
  // A I2C read from a I2C address.
  Wire.requestFrom( 0x68, 1);

  // Stop the tone output on pin 8
  noTone( 8);

  // Send a pin toggle to pin 11 that makes a Serial signal.
  // Arduino pin 11 is connected to D5 of the Logic Analyzer.
  serialToggle( "Wokwi");

  // A pulse to identify the end
  digitalWrite( 13, HIGH);   
  digitalWrite( 13, LOW);

void loop() {}

// Function: serialToggle()
// Using a digital pin to toggle a signal to produce serial output.
// This will only work on a Arduino Uno and only for a low baudrate and short texts.
// It is not accurate.
// The digital pin should already be set as output and high.
// Pin 11 is PB3 for a Arduino Uno
#define serialToggleHIGH   bitSet(PORTB,3)
#define serialToggleLOW    bitClear(PORTB,3)
#define serialToggleCYCLES 1665

void serialToggle( char *text)
  for( int i=0; i<strlen(text); i++)      // the characters of the text string
    noInterrupts();                       // disable interrupts to keep the delays accurate
    serialToggleLOW;                      // start pulse
    __builtin_avr_delay_cycles(serialToggleCYCLES);  // width of the start pulse

    for( int j=0; j<8; j++)     // the bits of a character, starting with lowest bit
      if( bitRead( text[i], j) == 0)
      __builtin_avr_delay_cycles(serialToggleCYCLES);  // width of a data pulse
    serialToggleHIGH;                     // stop bit
    // A stop-bit is the same as a "idle" and is a gap between the pulses so the
    // receiver can synchronize with the start-bit.
    // Since the baudrate of this function might be slower, two stopbits are used.
    __builtin_avr_delay_cycles( 2 * serialToggleCYCLES);