Please help me understand this code

I've successfully coded in the past, but not with timers. This one has me stumped.

I need to count pulses coming in the 1-2 mHz range. I found the following code that looks custom made for my application, accepting pulses as fast as 250 nS.

Fastest way to count pulses - #5 by tim7, scroll all the way down for the latest comments regarding it.

I uploaded the code to my UNO with the library streaming.h. It runs in the serial monitor but it's unstable. I'm wondering if there's something wrong with the code or my hardware.

Instead of using a pulse generator, I'm manually creating pulses using jumpers from Pin 2 and Pin 5 to GND and +5V. Doing so will start the counter, but it will not count pulses created by touching the pulse jumper on 5 to either +5V or GND.

When I power it up serial monitor displays:
19 pulses
Counting:

Then when I connect pin 2 to GND, the serial monitor displays:
Ready ... 0 pulses

The best I understand it, the serial monitor should first display "Counting: ," then when the counter is enabled by a rising pulse on 2, it should display "Ready..." Then when counter is disabled by a negative going pulse on 2, it should display "pulses."

I'd appreciate someone looking at the code and explaining how it works. I really want to understand why my creating pulses on pin 5 does not produce a count. Here's the code:

/*
  *Count pulses on pin 5 via Timer-1*
*  Start with rising edge on pin 2*
*  Stop with falling edge on pin 2*
*  ToDo:*
*   - deal with overflows*
*/

#include <Streaming.h>

void setup() {
  Serial.begin(19200);

  pinMode(2, INPUT); digitalWrite(2, HIGH); // trigger on pin-2 = PD2 = interrupt-0
  pinMode(5, INPUT); digitalWrite(5, HIGH); // pulses on pin-5 = PD5 = T1

  TCCR0A=0; // setup timer-0
  TCCR0B=0;
  TIMSK0=0;
  
  TCCR2A=0; // setup timer-2
  TCCR2B=0;
  TIMSK2=0;
  
  TCCR1A=0; // setup timer-1
  TCCR1C=0;
  TIMSK1=0;
  GTCCR=0;
  counting_stop();
}

void loop() {
  Serial << "Counting:" << endl;
  while (1) {}
}

void counting_start() {
  Serial << "Ready ... ";
  TCCR1B=7; // start
  attachInterrupt(0, counting_stop, FALLING); // ready to stop  
}

void counting_stop() {
  unsigned long counts;

  TCCR1B=0; // stop
  counts = TCNT1;
  TCNT1=0;
  
  attachInterrupt(0, counting_start, RISING); // ready to start
  Serial << counts << " pulses" << endl;
}

If you are doing that then you will have all sorts of random and unpredictable input pulses from bouncing and possibly floating inputs. I don't think there's any point in looking further until you are working with clean inputs with known pulse durations.

1-2 mHz range.

I suspect you mean 1-2MHz range, please clarify.

Agree, it's the same doubt I have. Especially because he wrote:

accepting pulses as fast as 250 nS.

where it leads to a 4 MHz pulse. I don't know if Arduino can handle (i.e. detect and do "something") with that kind of signal speed, together with Serial output sent from an ISR...

Hi, @Tom_Austin

Can you please tell us what your project is about?
Where will the pulses you wish to detect come from?
1MHz to 2MHz is quite a range of frequencies.

Tom.... :smiley: :+1: :coffee: :australia:

I've done some tests to see how the code from post #1 performs.
I used a Function Generator to provide pulses to pins 2 and 5 of an Arduino Uno R3.

Here is a 100us pulse, once per second on pin 2 (yellow trace), and a 100kHz signal going to pin5 (blue trace).
The code wrongly counts 3 pulses, instead of 10.
This error is due to the printing of "Ready ... " in the ISR.

Remove that printing of "Ready ... ", and the code will count correctly up to at least 200kHz:

But at frequencies above approximately 300kHz, we get extra pulses being counted.
I believe that this is due to the time taken for the ISR to stop the counting when the signal on pin 2 goes low.

At a frequency of 2MHz we count between 22 and 24 pulses instead of 20 in a time of 10µs:

At 2 MHz, we still count an extra 2 - 4 pulses if the sampling time is increased to 100µs:

The counter will in fact work with frequencies up to 8MHz:

1 Like

So this is your goal?

EDIT: sorry, you aren't the OP so it isn't "your" goal, but you just tested the quality. Thanks.

Yep! Gernerally any Serial in the ISR should be avoided, anyway why just 19200? I don't know if increasing that serial speed up to 921600 could mitigate the count errors.

Excellent, JohnLincoln. And thank you all for the replies.

My project is a coil Q measurement device using the ring down method. It uses hardware to measure and create an interval pulse (rising, high, falling) during which a number of coil oscillations get counted. The circuit has two outputs that become inputs into the Arduino. One is the interval pulse on pin 2, the other is the train of pulses to be counted. With high Q coils, the maximum number of pulses counted would be 250-300. Low Q coils might have a count of 50.

Yes, the measuring frequency is 1 MHz. Actually there are three coil/capacitor combinations to be measured at three resonant frequencies, 500 kHz, 1 MHz, and 1.5 MHz, hence my stated range of 1-2 MHz. The code developer gave the working pulse reading as 250 nS, which is 4 MHz, so I figured this would work.

I don't have a function generator, but I know how to use a 555 timer to make a pulse debouncer and will do that for my next testing.

And thank you, johnLincoln, for testing the code and demonstrating its limits. If I don't like how it performs in the MHz ranges, I can add a hardware frequency divider and compensate for it in the Arduino. But having to do that kind of defeats the purpose of directly using the Arduino's internal timers to speed things up, doesn't it.

To be clear, did you simply remove the line " Serial << "Ready ... "; "

1 Like

Yes, I actually just "commented it out", which is effectively the same as removing it.

if you still have problems consider using the ESP32 PCNT (Pulse Counter)
each pulse counter has two inputs pulse count and level enable (which enables counting during either high or low levels)

code (EDIT: display frequency of test signal)

// ESP32 - 
// Arduio forum https://forum.arduino.cc/t/please-help-me-understand-this-code/1381333/9

// 1. measure pulse width and rising edge-to-edge time using GPIO 16 and 17
// 2. using PCNT count pulses on GPIO 4 while signal on GPIO 16 is high

// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/pcnt.html

#include "driver/pulse_cnt.h"
#include "driver/gpio.h"
#include "esp_err.h"

// define input GPIO pins
#define PULSE_COUNT_PIN 4     // measure pulse count
#define PULSE_LEVEL_PIN_1 16  // measure pulse width and rising edge-to-edge time
#define PULSE_LEVEL_PIN_2 17

// PCNT pulse counter definitions
#define EXAMPLE_PCNT_HIGH_LIMIT 20000
#define EXAMPLE_PCNT_LOW_LIMIT -100

pcnt_unit_config_t unit_config = {
  .low_limit = EXAMPLE_PCNT_LOW_LIMIT,
  .high_limit = EXAMPLE_PCNT_HIGH_LIMIT,
  .flags = {
    .accum_count = 1,
  }
};
pcnt_unit_handle_t pcnt_unit = NULL;

pcnt_chan_config_t chan_config = {
  .edge_gpio_num = PULSE_COUNT_PIN,
  .level_gpio_num = PULSE_LEVEL_PIN_1,
};
pcnt_channel_handle_t pcnt_chan = NULL;

// timer to measure pulse width and rising edge-to-edge time
hw_timer_t *Timer1_Cfg = NULL;  // timer object

// pulse timing data
volatile uint64_t riseTime, period, width;
// rising edge interrupt calculate period uSec
void IRAM_ATTR rising() {
  //pcnt_unit_clear_count(pcnt_unit); // clear PCNT count
  uint64_t rise = timerReadMicros(Timer1_Cfg);
  period = rise - riseTime;
  riseTime = rise;
}

// falling edge interrupt calculate pulse width uSec
int pulse = 0;
void IRAM_ATTR falling() {
  width = timerReadMicros(Timer1_Cfg) - riseTime;
  pulse = 1;
}

void setup(void) {
  Serial.begin(115200);
  delay(2000);
  Serial.println("\n\nESP32 pulse counter using PCNT unit");

  Serial.println("setup timer and GPIO interrupts");
  if ((Timer1_Cfg = timerBegin(1000000)) == NULL)  // API 3.0 setup timer for 1uSec
    Serial.println("timerBegin Failed!!");
  Serial.print("timerBegin() OK frequenmcy ");
  Serial.println(timerGetFrequency(Timer1_Cfg));
  //  setup interrput routines - connect signal to pins 16 and 17
  attachInterrupt(digitalPinToGPIONumber(PULSE_LEVEL_PIN_1), rising, RISING);    // detect rising edge on pin 16
  attachInterrupt(digitalPinToGPIONumber(PULSE_LEVEL_PIN_2), falling, FALLING);  // detect falling edge on pin 17

  Serial.println("Setup PCNT (pulse Counter) ");
  Serial.println("install pcnt unit");
  ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
  Serial.println("install pcnt channels");
  ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
  Serial.println("set edge and level actions for pcnt channels");
  ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_HOLD, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
  ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD));
  Serial.println("add watch point");
  ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, EXAMPLE_PCNT_HIGH_LIMIT));

  Serial.println("enable pcnt unit");
  ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
  Serial.println("clear pcnt unit");
  ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
  Serial.println("start pcnt unit");
  ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
}

// display pulse count on falling edge of GPIO 16
void loop() {
  if (pulse == 0) return;  // on pulse falling edge display results

  static int pulse_count_old = 0, pulse_count = 0;
  pcnt_unit_get_count(pcnt_unit, &pulse_count);  // read PCNT pulse count
  Serial.printf("test Pulse count: %d frequency %dHz\n", pulse_count - pulse_count_old
                       , ( pulse_count - pulse_count_old) * 10000);
  pulse_count_old = pulse_count;
  Serial.printf("period %llduSec width = %llduSec  frequency %.2fHz\n",
                period, width, 1000000.0 / period);
  pulse = 0;
}

signals (testing 100KHz pulse input)

  1. pulse frequency 1Hz duty cycle 0.01 (100uSec pulse) on GPIO 16 and 17 - yellow trace
  2. square wave for counting on GPIO 4 - tested with frequencies 100KHz to 20MHz - blue trace

the oscilloscope measures the pulse on GPIO 16 an 17 as width 99uSec which is reflected in results below

test results

GPIO 16/17 1Hz duty cycle 0.01 (100uSec) 

GPIO 4 100KHz test signal

test Pulse count: 10 frequency 100000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz

----------------------------------------------------------
GPIO 4 1MHz test signal

test Pulse count: 99 frequency 990000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 99 frequency 990000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 99 frequency 990000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 99 frequency 990000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 99 frequency 990000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz

-------------------------------------------------------------------
GPIO 4 2MHz test signal

test Pulse count: 198 frequency 1980000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 199 frequency 1990000Hz
period 999993uSec width = 97uSec  frequency 1.00Hz
test Pulse count: 198 frequency 1980000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 199 frequency 1990000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 198 frequency 1980000Hz

-------------------------------------------------------
GPIO 4 4MHz test signal

test Pulse count: 396 frequency 3960000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 396 frequency 3960000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 397 frequency 3970000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 397 frequency 3970000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 397 frequency 3970000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 396 frequency 3960000Hz

--------------------------------------------------------------------------
GPIO 4 10MHz test signal

test Pulse count: 992 frequency 9920000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 992 frequency 9920000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 992 frequency 9920000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 992 frequency 9920000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 992 frequency 9920000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 992 frequency 9920000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 992 frequency 9920000Hz

-----------------------------------------------------------------
GPIO 4 20MHz test signal

test Pulse count: 1984 frequency 19840000Hz
period 999992uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 1984 frequency 19840000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 1984 frequency 19840000Hz
period 999993uSec width = 97uSec  frequency 1.00Hz
test Pulse count: 1984 frequency 19840000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 1984 frequency 19840000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 1983 frequency 19830000Hz

-----------------------------------------------------------------
GPIO 4 30MHz test signal

test Pulse count: 2976 frequency 29760000Hz
period 999992uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 2975 frequency 29750000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 2975 frequency 29750000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 2975 frequency 29750000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 2975 frequency 29750000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 2975 frequency 29750000Hz
period 999993uSec width = 98uSec  frequency 1.00Hz

replaced signal generator with ESP32S3 using the RMT to generate the test signals

// ESP32S3 RMT two signals synchroinized
//  1. square wave 100KHz, 1MHz, etc on GPIO 15
//  2. 100uSec pulse every second on GPIO 14

// https://docs.espressif.com/projects/esp-idf/en/v5.3.1/esp32/api-reference/peripherals/rmt.html

// on a ESP32 displays error
//  rmt_new_sync_manager(345): sync manager not supported

#include "Arduino.h"
#include "driver/rmt_tx.h"

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("ESP32S3 RMT two signals synchroinized");
  Serial.println(" 1. square wave 100KHz, 1MHz, etc on GPIO 15");
  Serial.println(" 2. 100uSec pulse every second on GPIO 14");

  delay(2000);
  // setup two Tx channels
  rmt_channel_handle_t tx_channels[2] = { NULL };
  gpio_num_t tx_gpio_number[2] = { GPIO_NUM_15, GPIO_NUM_14 };  // pin numbers
  rmt_tx_channel_config_t tx_chan_config_1 = {
    .gpio_num = tx_gpio_number[0],
    .clk_src = RMT_CLK_SRC_DEFAULT,
    .resolution_hz = 20 * 1000 * 1000,  // 20MHz clock
    .mem_block_symbols = 64,
    .trans_queue_depth = 1,
  };
  ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config_1, &tx_channels[0]));
  rmt_tx_channel_config_t tx_chan_config_2 = {
    .gpio_num = tx_gpio_number[1],
    .clk_src = RMT_CLK_SRC_DEFAULT,
    .resolution_hz = 1000000,  // 1MHz clock
    .mem_block_symbols = 64,
    .trans_queue_depth = 1,
  };
  ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config_2, &tx_channels[1]));
  rmt_transmit_config_t transmitConfig = {
    .loop_count = -1
  };
  rmt_encoder_handle_t copyEncoder{ NULL };
  rmt_copy_encoder_config_t copyEncoderConfig = {};
  assert(rmt_new_copy_encoder(&copyEncoderConfig, &copyEncoder) == ESP_OK && "Failed to Create Copy Encoder");
  ESP_ERROR_CHECK(rmt_enable(tx_channels[1]));
  ESP_ERROR_CHECK(rmt_enable(tx_channels[0]));

  // setup sync manage for the two pulses
  rmt_sync_manager_handle_t synchro = NULL;
  rmt_sync_manager_config_t synchro_config = {
    .tx_channel_array = tx_channels,
    .array_size = sizeof(tx_channels) / sizeof(tx_channels[0]),
  };
  ESP_ERROR_CHECK(rmt_new_sync_manager(&synchro_config, &synchro));

  Serial.println("Starting Transmitters");
  // 100KHz square wave - clock is 20MHz
  const rmt_symbol_word_t pulsePattern1[] = {
    { .duration0 = 100, .level0 = 1, .duration1 = 100, .level1 = 0 },  };
  // 100uSec pulse every second - clock is 1mHz
  const rmt_symbol_word_t pulsePattern2[] = {
  // 100uSec pluse
  {50,1,50,1,},
  // total 1 second period
  {24950,0,24950,0,}, // note using 15bit counters - see Layout of RMT Symbols
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
  {25000,0,25000,0,},
    // Looping mode needs a Zero ending data to mark the EOF
  { 0, 0, 0, 0 }
};
  assert(rmt_transmit(tx_channels[0], copyEncoder, &pulsePattern1, sizeof(pulsePattern1), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  assert(rmt_transmit(tx_channels[1], copyEncoder, &pulsePattern2, sizeof(pulsePattern2), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  Serial.println("Transmitters Running");
}
void loop() {
}

e.g. 100KHz signal

serial output of test runs counting pulses up to 4MHz

GPIO 4 100KHz

test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999990uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 10 frequency 100000Hz

----------------------------------------------------
GPIO 4 1MHz signal

test Pulse count: 100 frequency 1000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999992uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999992uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999992uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 100 frequency 1000000Hz

----------------------------------------------------------------

2MHz

period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 200 frequency 2000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz

------------------------------------------------------------

4MHz

test Pulse count: 4 frequency 40000Hz
period 534287uSec width = 8090521uSec  frequency 1.87Hz
test Pulse count: 1 frequency 10000Hz
period 534287uSec width = 10990883uSec  frequency 1.87Hz
test Pulse count: 400 frequency 4000000Hz
period 10991020uSec width = 99uSec  frequency 0.09Hz
test Pulse count: 400 frequency 4000000Hz
period 999992uSec width = 98uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999990uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz
test Pulse count: 400 frequency 4000000Hz
period 999991uSec width = 99uSec  frequency 1.00Hz

UPDATE: I made a hardware square wave generator arbitrarily outputting 26 pulses visible on my scope during the interval pulse. Using JohnLIncoln's suggestion to comment out the offending line, it does work, somewhat. It counts 26 pulses, sometimes 25 and occasionally 24. That may be due to glitches in the hardware, as it's not the same as a commercial signal generator.

What I don't like is that it outputs the number of pulses as they are counted. I need only a single total of all the pulses within the interval. I've forgotten so much about coding that I'm at a standstill. As others have mentioned, I may be expecting too much from Arduino and may be better off using an up-counter IC and using the Arduino to display its output.

Horace, your ESP code comes closer to what I'm looking for, showing the total number of pulses within the interval. I've never heard of ESP32 and have spent some time looking it up. Can you use the Arduino IDE to program it? If so, how does it show up in the devices?

I think that the miscounting is due to the time that it takes for the interrupt to begin counting pulses.

To demonstrate this, I modified your code to turn pin 8 high at the beginning of the 'counting_start' ISR and then low again at the beginning of the 'counting_stop' ISR.

I used direct port manipulation to make the pin change state as quickly as possible (in 1 clock cycle : 62.5ns).

/*
   Count pulses on pin 5 via Timer-1
   Start with rising edge on pin 2
   Stop with falling edge on pin 2
   ToDo:
    - deal with overflows
*/

#include <Streaming.h>

void setup() {
  Serial.begin(19200);

  pinMode(2, INPUT); digitalWrite(2, HIGH); // trigger on pin-2 = PD2 = interrupt-0
  pinMode(5, INPUT); digitalWrite(5, HIGH); // pulses on pin-5 = PD5 = T1
  pinMode(8, OUTPUT);

  TCCR0A = 0; // setup timer-0
  TCCR0B = 0;
  TIMSK0 = 0;

  TCCR2A = 0; // setup timer-2
  TCCR2B = 0;
  TIMSK2 = 0;

  TCCR1A = 0; // setup timer-1
  TCCR1C = 0;
  TIMSK1 = 0;
  GTCCR = 0;
  counting_stop();
}

void loop() {
  Serial << "Counting:" << endl;
  while (1) {}
}

void counting_start() {
  // Serial << "Ready ... ";
  PORTB = 0B00000001;
  TCCR1B = 7; // start
  attachInterrupt(0, counting_stop, FALLING); // ready to stop
}

void counting_stop() {
  PORTB = 0B00000000;
  unsigned long counts;

  TCCR1B = 0; // stop
  counts = TCNT1;
  TCNT1 = 0;

  attachInterrupt(0, counting_start, RISING); // ready to start
  Serial << counts << " pulses" << endl;
}

You can see from the following oscilloscope trace that it takes just over 3µs for the ISR to start working. This means that at a frequency of 1MHz the first 3 pulses don't get counted.

I sent 26 pulses to pin 5, but only 23 are counted.

  • Channel 1 - yellow trace - signal to pin 2.
  • Channel 2 - red trace - pin 8, counting occurs when high.
  • Channel 3 - blue trace - 26 pulses at 1MHz to pin 5.

The trace above also shows why in my earlier tests when I had a continuous stream of pulses that I counted more pulses than expected.

It takes 3µs for pin 8 to go high after pin 2 goes high, but 5µs to go low after pin 2 goes low. Thus I was counting for an extra 2µs.

yes - try a web search for ESP32 Arduino, e.g. ESP32 getting_started and installing-the-esp32-board-in-arduino-ide-windows-instructions

after installing the ESP32 click Tools>Board select ESP32 then the specific board, e.g.

Thanks for the ESP info. I may end up using that.

In the meantime, I cobbled up a hardware up counter using MC14520. As I suspected the hardware glitch was due to using two independent clocks that were not synchronised. Because the interval could catch a clock pulse on the rising or falling edge, or anywhere in between, my hardware counter with an interval of seven pulses would count seven pulses three times successively, then eight pulses three times successively, then back to seven pulses. Good enough for now, I know what to look for later. I'm more inclined to implement as much in hardware as I can and not worry about interrupts and delays. THANKS for all the help.

I would probably use an FPGA

Following up, I think I have an acceptable solution. The bulk of the instrument is hardware based. The Arduino is used only to receive the binary count, perform the necessary math, and then display the output on an LCD. This way the count is limited only by the speed of the CMOS ICs, no need to deal with interrupts, etc.

The MC14520 8-bit counter is connected to pins 2-9. All eight pins 2-9 will be needed. The raw count should not exceed 255, as that would be an exceedingly improbable Q. Output will be displayed on 16x2 LCD, which I still have to buy, so for now the output is to the serial console. Code is simplified using only four bits and will be modified to 8 bits. I see no need for floating point math, as the count is integers.

int totalcount = 0;
int bcdValue = 0;
int multipliedcount = 0;
const int pin1 = 2;
const int pin2 = 3;
const int pin4 = 4;
const int pin8 = 5;

void setup() {

  Serial.begin(115200);
  
  pinMode(pin1, INPUT);
  pinMode(pin2, INPUT);
  pinMode(pin4, INPUT);
  pinMode(pin8, INPUT);
}

void loop() {
  
  totalcount=bcdValue;
  // Read the state of each BCD switch pin
  int value1 = digitalRead(pin1);
  int value2 = digitalRead(pin2);
  int value4 = digitalRead(pin4);
  int value8 = digitalRead(pin8);
  
  // Calculate the BCD value
  bcdValue = (value8 * 8) + (value4 * 4) + (value2 * 2) + value1;
  delay(100);

 if (bcdValue < totalcount) {(multipliedcount = totalcount * 453/100);
 Serial.println(multipliedcount);}

don't remember seeing mention of display in previous posts

this is using a CYD to display pulse count etc (input 1MHz)

main.ino

// ESP32 - count pulses and display on CYD TFT

// Arduino forum https://forum.arduino.cc/t/please-help-me-understand-this-code/1381333/9

#include <TFT_eSPI.h>
#include <TFT_eSPI_Scroll.h>
#include <4bit.h>

TFT_eSPI tft;
TFT_eSPI_Scroll scroll;

// 1. measure pulse width and rising edge-to-edge time using GPIO 16 and 17
// 2. using PCNT count pulses on GPIO 4 while signal on GPIO 16 is high

// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/pcnt.html

#include "driver/pulse_cnt.h"
#include "driver/gpio.h"
#include "esp_err.h"

// define input GPIO pins
#define PULSE_COUNT_PIN 35    // measure pulse count
#define PULSE_LEVEL_PIN_1 22  // measure pulse width and rising edge-to-edge time
#define PULSE_LEVEL_PIN_2 27

// PCNT pulse counter definitions
#define EXAMPLE_PCNT_HIGH_LIMIT 20000
#define EXAMPLE_PCNT_LOW_LIMIT -100

pcnt_unit_config_t unit_config = {
  .low_limit = EXAMPLE_PCNT_LOW_LIMIT,
  .high_limit = EXAMPLE_PCNT_HIGH_LIMIT,
  .flags = {
    .accum_count = 1,
  }
};
pcnt_unit_handle_t pcnt_unit = NULL;

pcnt_chan_config_t chan_config = {
  .edge_gpio_num = PULSE_COUNT_PIN,
  .level_gpio_num = PULSE_LEVEL_PIN_1,
};
pcnt_channel_handle_t pcnt_chan = NULL;

// timer to measure pulse width and rising edge-to-edge time
hw_timer_t *Timer1_Cfg = NULL;  // timer object

// pulse timing data
volatile uint64_t riseTime, period, width;
// rising edge interrupt calculate period uSec
void IRAM_ATTR rising() {
  //pcnt_unit_clear_count(pcnt_unit); // clear PCNT count
  uint64_t rise = timerReadMicros(Timer1_Cfg);
  period = rise - riseTime;  // calculate period
  riseTime = rise;
}

// falling edge interrupt calculate pulse width uSec
volatile bool newPulse = false;
void IRAM_ATTR falling() {
  uint64_t fall = timerReadMicros(Timer1_Cfg);
  width = fall - riseTime;  // calculate pulse width
  newPulse = true;          // indicate new data
}

void setup(void) {
  Serial.begin(115200);
  delay(2000);
  Serial.println("\n\nESP32 pulse counter using PCNT unit");

  Serial.println("setup timer and GPIO interrupts");
  if ((Timer1_Cfg = timerBegin(1000000)) == NULL)  // API 3.0 setup timer for 1uSec
    Serial.println("timerBegin Failed!!");
  Serial.print("timerBegin() OK frequenmcy ");
  Serial.println(timerGetFrequency(Timer1_Cfg));
  //  setup interrput routines - connect signal to pins 16 and 17
  attachInterrupt(digitalPinToGPIONumber(PULSE_LEVEL_PIN_1), rising, RISING);    // detect rising edge on pin 16
  attachInterrupt(digitalPinToGPIONumber(PULSE_LEVEL_PIN_2), falling, FALLING);  // detect falling edge on pin 17

  Serial.println("Setup PCNT (pulse Counter) ");
  Serial.println("install pcnt unit");
  ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
  Serial.println("install pcnt channels");
  ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
  Serial.println("set edge and level actions for pcnt channels");
  ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_HOLD, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
  ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_HOLD));
  Serial.println("add watch point");
  ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, EXAMPLE_PCNT_HIGH_LIMIT));

  Serial.println("enable pcnt unit");
  ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
  Serial.println("clear pcnt unit");
  ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
  Serial.println("start pcnt unit");
  ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));

  // setup TFT
  tft.init();
  // Initializing the tft_espi_scroll int 1bit B/W
  if (scroll.init(&tft, 4) != NO_ERROR) {
    Serial.println("Failed... Reseting...");
    return;
  }
  String data = String("TFT setup");
  scroll.write(data);
}

// display pulse count on falling edge of GPIO 16
void loop() {
  if (!newPulse) return;  // on pulse falling edge display results

  newPulse = false;
  static int pulse_count_old = 0, pulse_count = 0;
  pcnt_unit_get_count(pcnt_unit, &pulse_count);  // read PCNT pulse count
  Serial.printf("test Pulse count: %d frequency %dHz\n", pulse_count - pulse_count_old, (pulse_count - pulse_count_old) * 10000);
  //pulse_count_old = pulse_count;
  Serial.printf("period %llduSec width = %llduSec  frequency %.2fHz\n",
                period, width, 1000000.0 / period);

                // display on TFT
                  scroll.reset();
  if (scroll.init(&tft, 4) != NO_ERROR) {
    Serial.println("Failed... Reseting...");
    return;
  }
  char text[500] = { 0 };
  sprintf(text, "Pulse count: %d\n   freq %dHz\n\n", pulse_count - pulse_count_old, (pulse_count - pulse_count_old) * 10000);
  scroll.write(text);
  text[0] = 0;
  pulse_count_old = pulse_count;
  sprintf(text, "\nperiod %llduSec\n    width = %llduSec\n    freq %.2fHz\n\n",
          period, width, 1000000.0 / period);
  scroll.write(text);
}

User_Setup.h (in samr directory as about main.ino file)

//                            USER DEFINED SETTINGS
//   Set driver type, fonts to be loaded, pins used and SPI control method etc
//
//   See the User_Setup_Select.h file if you wish to be able to define multiple
//   setups and then easily select which setup file is used by the compiler.
//
//   If this file is edited correctly then all the library example sketches should
//   run without the need to make any more changes for a particular hardware setup!
//   Note that some sketches are designed for a particular TFT pixel width/height


// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################

// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32

// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT

// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS

// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT

// Display type -  only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI

// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER       // Generic driver for common displays
#define ILI9341_2_DRIVER     // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER      // Define additional parameters below for this display
//#define ILI9163_DRIVER     // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//define ILI9488_DRIVER     // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER      // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER    // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER

// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:

// #define TFT_SDA_READ      // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only

// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display

//  #define TFT_RGB_ORDER TFT_RGB  // Colour order Red-Green-Blue
//  #define TFT_RGB_ORDER TFT_BGR  // Colour order Blue-Green-Red

// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below

// #define M5STACK

// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH  80
// #define TFT_WIDTH  128
// #define TFT_WIDTH  128 // ST7789 240 x 240 and 240 x 320
#define TFT_WIDTH  240
// #define TFT_WIDTH  320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
//#define TFT_HEIGHT 160 // ST7789 240 x 240
 #define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240 //#define TFT_HEIGHT 480

// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:

// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128    // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80   // For 160 x 80 display with 24 pixel offset

// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.

// #define TFT_INVERSION_ON
// #define TFT_INVERSION_OFF


// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################

// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);

 #define TFT_BL   21            // LED back-light control pin
 #define TFT_BACKLIGHT_ON HIGH  // Level to turn ON back-light (HIGH or LOW)



// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO  to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED       to NodeMCU pin VIN (or 5V, see below)
// Display SCK       to NodeMCU pin D5
// Display SDI/MOSI  to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET     to NodeMCU pin D4 (or RST, see below)
// Display CS        to NodeMCU pin D8 (or GND, see below)
// Display GND       to NodeMCU pin GND (0V)
// Display VCC       to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.


// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######

// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS   PIN_D8  // Chip select control pin D8
//#define TFT_DC   PIN_D3  // Data Command control pin
//#define TFT_RST  PIN_D4  // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST  -1    // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V

//#define TFT_BL PIN_D1  // LED back-light (only for ST7789 with backlight control pin)

//#define TOUCH_CS PIN_D2     // Chip select pin (T_CS) of touch screen

//#define TFT_WR PIN_D2       // Write strobe for modified Raspberry Pi TFT only


// ######  FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES  ######

// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3  S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined

//#define TFT_SPI_OVERLAP

// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS   PIN_D3
//#define TFT_DC   PIN_D5  // Data Command control pin
//#define TFT_RST  PIN_D4  // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST  -1  // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V


// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP   ######

// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins

// #define TFT_MISO -1 
// #define TFT_MOSI 14
// #define TFT_SCLK 33
// #define TFT_CS   -1  // Chip select control pin
// #define TFT_DC    13  // Data Command control pin
// #define TFT_RST   12  // Reset pin (could connect to RST pin)
//#define TFT_RST  -1  // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST

// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins

#define TFT_MOSI 13 // In some display driver board, it might be written as "SDA" and so on.
#define TFT_SCLK 14
#define TFT_CS   15  // Chip select control pin
#define TFT_DC   2  // Data Command control pin
#define TFT_RST  12  // Reset pin (could connect to Arduino RESET pin)
#define TFT_BL   21  // LED back-light

#define TOUCH_CS 33     // Chip select pin (T_CS) of touch screen

//#define TFT_WR 22    // Write strobe for modified Raspberry Pi TFT only

// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS   14  // Chip select control pin
//#define TFT_DC   27  // Data Command control pin
//#define TFT_RST  33  // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL   32  // LED back-light (required for M5Stack)

// ######       EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP        ######

// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!

// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays

// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT

// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS   33  // Chip select control pin (library pulls permanently low
//#define TFT_DC   15  // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST  32  // Reset pin, toggles on startup

//#define TFT_WR    4  // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD    2  // Read strobe control pin

//#define TFT_D0   12  // Must use pins in the range 0-31 for the data bus
//#define TFT_D1   13  // so a single register write sets/clears all bits.
//#define TFT_D2   26  // Pins can be randomly assigned, this does not affect
//#define TFT_D3   25  // TFT screen update performance.
//#define TFT_D4   17
//#define TFT_D5   16
//#define TFT_D6   27
//#define TFT_D7   14

// ######       EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP        ######

// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5

//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13

// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS   D5 // Chip select control pin to TFT CS
//#define TFT_DC   D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST  D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS   PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC   PE9  // Nucleo-F767ZI equivalent of D6
//#define TFT_RST  PF13 // Nucleo-F767ZI equivalent of D7

//#define TFT_RST  -1   // Set TFT_RST to -1 if the display RESET is connected to processor reset
                        // Use an Arduino pin for initial testing as connecting to processor reset
                        // may not work (pulse too short at power up?)

// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################

// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!

#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT


// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################

// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.

// #define SPI_FREQUENCY   1000000
// #define SPI_FREQUENCY   5000000
//#define SPI_FREQUENCY  10000000
//#define SPI_FREQUENCY  20000000
//#define SPI_FREQUENCY  27000000
//#define SPI_FREQUENCY  40000000
#define SPI_FREQUENCY  55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
//#define SPI_FREQUENCY  65000000
 //#define SPI_FREQUENCY  80000000

// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY  20000000

// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
 #define SPI_TOUCH_FREQUENCY  2500000

// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
//#define USE_HSPI_PORT

// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!

// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.

// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect

// #define SUPPORT_TRANSACTIONS

the CYD has an onboard ESP32 microcontroller so minimal changes to add TFT display output (plus GPIO input pins updated)