I'm trying to analyse mic (A0) input, calculate certain spectral lines with Goertzel algorithm to detect a number encoded by DTFM and display it with 8x8 matrix display (max7219 driver).

The code works perfectly fine without a display sending the result to serial, however when I added a display, strange things started to happen.

  • if goertzel(..) is called before displayDigit the display doesn't show anything

  • if displayDigit is called first the output of goertzel(..) is a complete mess

  • if I comment out 1 line in goertzel(..) which is amp = sqrt(re * re + im * im); // [1] the display works (but it doesn't make sense)

I'm using Arduino UNO, ADC in free-run mode.

The code below:

/**
 * 8x8 LED matrix display with MAX7219 driver
 * +5V, GND, CLK (8 pin), CS (9 pin), DIN (10 pin)
 */
#include <binary.h> 
#include <LedControl.h>

#include "font8x8.h"

#define N           256
#define IX_LEN      8
#define THRESHOLD   20

/* Display PINs */
#define CLK     13//8
#define CS      12 //9
#define DIN     8 //10

LedControl matrix = LedControl(DIN, CLK, CS, 1);
int8_t lastDetectedDigit = -1;
int8_t currentDigit = -1;
uint32_t lastDetectedMillis = 0;

const int adc_channel = 0;

volatile uint16_t samples[N];
volatile uint16_t samplePos = 0;

float spectrum[IX_LEN] = {0,0,0,0,0,0,0,0};

// Frequences [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]
// Corresponding spectrum indexes [18, 20, 22, 25, 32, 35, 39, 43]

// Calculated for 9615Hz 256 samples  
const float cos_t[IX_LEN] = {
  0.9039892931234433, 0.881921264348355, 0.8577286100002721, 0.8175848131515837, 
  0.7071067811865476, 0.6531728429537769, 0.5758081914178454, 0.49289819222978415
  };

const float sin_t[IX_LEN] = {
  0.4275550934302821, 0.47139673682599764, 0.5141027441932217, 0.5758081914178453, 
  0.7071067811865475, 0.7572088465064845, 0.8175848131515837, 0.8700869911087113  
  };

const char table[4][4] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

const uint8_t char_indexes[4][4] = {
  {0, 1, 2, 12},
  {3, 4, 5, 13},
  {6, 7, 8, 14},
  {10, 9, 11, 15}
};


void initADC() {
  // Init ADC; f = ( 16MHz/prescaler ) / 13 cycles/conversion 
  ADMUX  = adc_channel; // Channel sel, right-adj, use AREF pin
  ADCSRA = _BV(ADEN)  | // ADC enable
           _BV(ADSC)  | // ADC start
           _BV(ADATE) | // Auto trigger
           _BV(ADIE)  | // Interrupt enable
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
//  ADCSRB = _BV(ADTS2) | _BV(ADTS0);              // Timer/Counter1 Compare Match B
  ADCSRB = 0; // Free-run mode
  DIDR0  = _BV(adc_channel); // Turn off digital input for ADC pin      
}

void goertzel(volatile uint16_t *samples, float *spectrum) {
  float v[N + 2];
  float re, im, amp;

  for (uint8_t k = 0; k < IX_LEN; k++) {
    float a = 2. * cos_t[k];
    v[0] = v[1] = .0;    
    for (uint16_t i = 2; i < N + 2; i++) {
      v[i] = float(samples[i - 2]) + a * v[i - 1] - v[i - 2];      
    }
    re = cos_t[k] * v[N + 1] - v[N];
    im = sin_t[k] * v[N + 1];
    amp = sqrt(re * re + im * im); // [1]
    spectrum[k] = amp;        
  } 
}

float avg(float *a, uint16_t len) {
  float result = .0;
  for (uint16_t i = 0; i < len; i++) {
    result += a[i];
  }
  return result / len;
}

int8_t get_single_index_above_threshold(float *a, uint16_t len, float threshold) {

  if (threshold < THRESHOLD) {
    return -1;
  }

  int8_t ix = -1;
  for (uint16_t i = 0; i < len; i++) {
    if (a[i] > threshold) {
      if (ix == -1) {
        ix = i;
      } else {
        return -1;
      }
    } 
  }
  return ix;  
}

char detect_digit(float *spectrum) {
  float avg_row = avg(spectrum, 4);
  float avg_col = avg(&spectrum[4], 4);
  int8_t row = get_single_index_above_threshold(spectrum, 4, avg_row);
  int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col);

  if (row != -1 && col != -1) {
    return table[row][col];
  } else {
    return 0;
  }
}

void displayDigit(uint8_t c) {
  if (currentDigit == c) {
    return;
  }

  for (uint8_t i = 0; i < 8; i++) {
    matrix.setColumn(0, 7 - i, IMAGES[c][i]); 
  }
  currentDigit = c;
}

void setup() {  
  cli();
  initADC();
  sei();

  matrix.shutdown(0, false);
  matrix.setIntensity(0, 2);

  Serial.begin(115200);

}

unsigned long z = 0;

void loop() {
  while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish

  goertzel(samples, spectrum);   
  samplePos = 0;

//  if (z % 25 == 0) {
    displayDigit(2); 

    for (int i = 0; i < IX_LEN; i++) {
      Serial.print(spectrum[i]);
      Serial.print("\t");
    }
    Serial.println();
    char digit = detect_digit(spectrum);
    Serial.println(digit);
//  }
  z++;



  ADCSRA |= _BV(ADIE);       // Resume sampling interrupt
}

ISR(ADC_vect) { 
  uint16_t sample = ADC;

  samples[samplePos++] = sample;

  if(samplePos >= N) {
    ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off
  }
}

font8x8.h:

const byte IMAGES[16][8] = {
{
  B00001000,
  B00011000,
  B00001000,
  B00001000,
  B00001000,
  B00001000,
  B00001000,
  B00011100
},{
  B00111100,
  B01000010,
  B01000010,
  B00000100,
  B00011000,
  B00100000,
  B01000000,
  B01111110
},{
  B00111100,
  B01000010,
  B00000010,
  B00000100,
  B00000100,
  B01000010,
  B01000010,
  B00111100
},{
  B00010100,
  B00100100,
  B00100100,
  B01000100,
  B01111110,
  B00000100,
  B00000100,
  B00000100
},{
  B00111110,
  B01000000,
  B01000000,
  B01111100,
  B00000010,
  B00000010,
  B01000010,
  B00111100
},{
  B00111100,
  B01000010,
  B01000000,
  B01111100,
  B01000010,
  B01000010,
  B01000010,
  B00111100
},{
  B00111110,
  B01000010,
  B00000100,
  B00000100,
  B00001000,
  B00001000,
  B00010000,
  B00010000
},{
  B00111100,
  B01000010,
  B01000010,
  B00111100,
  B01000010,
  B01000010,
  B01000010,
  B00111100
},{
  B00111100,
  B01000010,
  B01000010,
  B01000010,
  B00111110,
  B00000010,
  B01000010,
  B00111100
},{
  B00111100,
  B01000110,
  B01001010,
  B01010010,
  B01010010,
  B01010010,
  B01100010,
  B00111100
},{
  B00000000,
  B01010100,
  B00111000,
  B01111100,
  B00111000,
  B01010100,
  B00000000,
  B00000000
},{
  B00101000,
  B00101000,
  B11111110,
  B00101000,
  B11111110,
  B00101000,
  B00101000,
  B00000000
},{
  B00011100,
  B00100010,
  B00100010,
  B01000010,
  B01111110,
  B01000010,
  B01000010,
  B01000010
},{
  B01111100,
  B01000010,
  B01000010,
  B01111100,
  B01000010,
  B01000010,
  B01000010,
  B01111100
},{
  B00111100,
  B01000010,
  B01000000,
  B01000000,
  B01000000,
  B01000000,
  B01000010,
  B00111100
},{
  B01111000,
  B01000100,
  B01000110,
  B01000010,
  B01000010,
  B01000010,
  B01000010,
  B01111100
}};

I use LedControl.h library but inside is uses only digitalWrite, no timing, interrupts.

Can somebody explain what is the problem?

UPD This is what IDE tells me about the sizes:

Sketch uses 5602 bytes (17%) of program storage space. Maximum is 32256 bytes.
Global variables use 1048 bytes (51%) of dynamic memory, leaving 1000 bytes for local variables. Maximum is 2048 bytes.

This is LedControl library I'm using

  • 1
    Please update your question with more information. Can you give a link to the LedControl library and tell us what the compiler says about flash and ram usage. Start looking for an other arduino board, for example an arduino mkr or a arduino compatible board with a esp8266 or esp32. – Jot Nov 28 at 17:13
  • 1
    How much static RAM does your program use, according to the compiler? Seems to me that should be close to 1 K, leaving little more than 1 K for the stack. Your goertzel() function alone uses more than 1 K of stack, so you could well be smashing the stack against the BSS. – Edgar Bonet Nov 28 at 20:18
  • Thx, @EdgarBonet I haven't even though about these constraints. I'll try to move constants to flash memory and replace floats to fixed point. – zjor Nov 29 at 9:15
up vote 3 down vote accepted

From your recent edit:

Global variables use 1048 bytes (51%) of dynamic memory, leaving 1000 bytes for local variables. Maximum is 2048 bytes.

Your goertzel() function defines this

float v[N + 2];

where N is 256. This alone takes 258×4 = 1032 bytes of stack and you only have 1000 bytes available. Then, as I suspected, you are smashing the stack against the .bss section, effectively overwriting some of your global variables with the local ones.

It is hard to predict what can happen in such circumstances. The program is corrupting its memory: you can only expect it to act erratically.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.