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 beforedisplayDigit
the display doesn't show anythingif
displayDigit
is called first the output ofgoertzel(..)
is a complete messif I comment out 1 line in
goertzel(..)
which isamp = 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.
1 Answer 1
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 ×ばつ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.
goertzel()
function alone uses more than 1 K of stack, so you could well be smashing the stack against the BSS.