Skidlz

Arduino R4 Input Capture (Guitar Tuner)

Mar 1st, 2026 (edited)
201
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 3.74 KB | Source Code | 0 0
  1. //Arduino Nano R4 Input Capture example - Zack Nelson 2026
  2. //Input on D7, OLED on A4/5
  3. #include "Arduino.h"
  4. #include <math.h>
  5. #include <U8g2lib.h>
  6. uint32_t lastTS, currentTS, delta = 0; //input capture readings
  7. #define readingsMax 16
  8. uint32_t readingsBuffer[readingsMax] = {}; //store multiple input capture readings
  9. uint8_t readingsIndex = 0;
  10.  
  11. const char* const noteNames[12] = {"A", "A#/Bb", "B", "C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#/Ab"};
  12. char buffer[20]; //text buffer
  13.  
  14. //Asynchronous General Purpose Timer interrupt (from vector_data.h)
  15. static const IRQn_Type IRQn_CCMPA = AGT0_INT_IRQn;
  16.  
  17. //128x32 I2C OLED on pins A4 (data) & A5 (clock)
  18. U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
  19.  
  20. void captureISR() {
  21.   lastTS = currentTS;
  22.   currentTS = R_GPT0->GTCCR[0]; //read from input capture register
  23.   delta = currentTS - lastTS;
  24.  
  25.   readingsBuffer[readingsIndex++] = delta;
  26.   readingsIndex %= readingsMax; //wrap around at max
  27.  
  28.   //clear interrupt flag for capture compare match A
  29.   R_ICU->IELSR_b[IRQn_CCMPA].IR = 0;
  30. }
  31.  
  32. void setup() {
  33.   u8g2.begin(); //start OLED
  34.  
  35.   //Timer Setup------------------------------------------------------------------------------------
  36.   R_MSTP->MSTPCRD_b.MSTPD5 = 0; //enable GPT0 module clock (General PWM Timer 321 to 320 Module Stop)
  37.   delayMicroseconds(10);
  38.  
  39.   R_GPT0->GTCR_b.CST = 0; //stop timer
  40.   R_GPT0->GTCR_b.MD = 0b000; //saw-wave PWM mode (000b)
  41.  
  42.   R_GPT0->GTUDDTYC = 0b11; //set 11b first (per datasheet Figure 22.17)
  43.   R_GPT0->GTUDDTYC = 0b01; //01b for up-counting
  44.  
  45.   //R_GPT0->GTCR_b.TPCS = 0b011; //PCLKD/64 prescale
  46.   R_GPT0->GTPR = 0xFFFFFFFF; //max cycle
  47.   R_GPT0->GTCNT = 0; //0 initial count
  48.  
  49.   //GTCCRA input capture enabled on the...
  50.   R_GPT0->GTICASR_b.ASCARBL = 1; //rising edge of GTIOCA input when GTIOCB input is 0
  51.   R_GPT0->GTICASR_b.ASCARBH = 1; //rising edge of GTIOCA input when GTIOCB input is 1
  52.  
  53.   R_GPT0->GTCR_b.CST = 1; //start count operation
  54.  
  55.   //GPIO Pin D7 (P107) Setup-----------------------------------------------------------------------
  56.   R_PFS->PORT[1].PIN[7].PmnPFS_b.PMR = 1; //Used as an I/O port for peripheral functions
  57.   R_PFS->PORT[1].PIN[7].PmnPFS_b.PSEL = 0b11; //GTIOC0A (GPT peripheral function)
  58.  
  59.   //Interrupt Setup--------------------------------------------------------------------------------
  60.   //assign GPT0 Capture to IRQn_CCMPA (AGT0_INT_IRQn #17)
  61.   R_ICU->IELSR_b[IRQn_CCMPA].IELS = ELC_EVENT_GPT0_CAPTURE_COMPARE_A;
  62.   NVIC_SetVector(IRQn_CCMPA, (uint32_t)captureISR); //point to the ISR function
  63.   NVIC_SetPriority(IRQn_CCMPA, 12);
  64.   NVIC_EnableIRQ(IRQn_CCMPA);
  65. }
  66.  
  67. void loop() {
  68.   uint64_t averageReading = 0; //get average of all readings
  69.   for (uint8_t i = 0; i < readingsMax; i++) averageReading += readingsBuffer[i];
  70.   averageReading /= readingsMax;
  71.  
  72.   float freq = 48000000.0/averageReading; //convert from timer count difference to frequency in Hz
  73.   float note = 12 * log2(freq/440); //convert from Hz to note #
  74.   while (note < 0) note += 12; //fix negative modulo weirdness
  75.   int8_t noteRounded = (int)round(note) % 12;
  76.   int8_t deviation = (note - round(note)) * 100; //calculate % out of tune
  77.  
  78.   //update OLED screen
  79.   u8g2.clearBuffer();
  80.  
  81.   u8g2.setFont(u8g2_font_6x13_tr);
  82.   sprintf(buffer, "Freq: %.1f", freq);
  83.   u8g2.drawStr(0, 10, buffer);
  84.  
  85.   sprintf(buffer, "Deviation: %d%%\n", deviation);
  86.   u8g2.drawStr(0, 24, buffer);
  87.  
  88.   u8g2.setFont(u8g2_font_8x13_tr); //larger font for the note name
  89.   u8g2.drawStr(86, 10, noteNames[noteRounded]);
  90.  
  91.   u8g2.drawLine(64, 30, 64 + deviation, 30); //Draw thick line to show tuning deviation
  92.   u8g2.drawLine(64, 31, 64 + deviation, 31);
  93.   u8g2.drawLine(64, 32, 64 + deviation, 32);
  94.  
  95.   u8g2.sendBuffer();
  96.  
  97.   delay(1000/5); //5Hz refresh
  98. }
Advertisement
Add Comment
Please, Sign In to add comment