Аномальное поведение операций АЦП, цифровой записи и операций с плавающей запятой.

Я пытаюсь проанализировать входной сигнал микрофона (A0), вычислить определенные спектральные линии с помощью алгоритма Герцеля, чтобы обнаружить число, закодированное DTFM, и отобразить его на матричном дисплее 8x8 (драйвер max7219).

Код прекрасно работает и без отображения результата на последовательный порт, однако когда я добавил дисплей, начали происходить странные вещи.

  • если goertzel(..) вызывается до displayDigit, на дисплее ничего не отображается

  • если displayDigit вызывается первым, вывод goertzel(..) представляет собой полный беспорядок

  • если я закомментирую 1 строку в goertzel(..), то есть amp = sqrt(re * re + im * im); // [1] отображение работает (но смысла в этом нет)

Я использую Arduino UNO, АЦП в автономном режиме.

Код ниже:

/**
* Светодиодный матричный дисплей 8x8 с драйвером MAX7219.
* +5 В, GND, CLK (8-контактный), CS (9-контактный), DIN (10-контактный)
*/
#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};

// Частоты [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]
// Соответствующие индексы спектра [18, 20, 22, 25, 32, 35, 39, 43]

// Рассчитано для 256 выборок 9615 Гц
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() {
  // Инициализируем АЦП; f = (16 МГц/прескалер)/13 циклов/преобразование
  ADMUX  = adc_channel; // Выбор канала, регулировка вправо, использование контакта AREF
  ADCSRA = _BV(ADEN)  | // включение АЦП
           _BV(ADSC)  | // запуск АЦП
           _BV(ADATE) | // Автоматический триггер
           _BV(ADIE)  | // Разрешение прерывания
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1/13 = 9615 Гц
// ADCSRB = _BV(ADTS2) | _BV(ADTS0); // Таймер/Счетчик1 сравниваем совпадение B
  ADCSRB = 0; // Режим свободного запуска
  DIDR0  = _BV(adc_channel); // Отключаем цифровой вход для вывода АЦП
}

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)); // Дождитесь завершения выборки звука

  goertzel(samples, spectrum);   
  samplePos = 0;

// если (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);       // Возобновляем прерывание выборки
}

ISR(ADC_vect) { 
  uint16_t sample = ADC;

  samples[samplePos++] = sample;

  if(samplePos >= N) {
    ADCSRA &= ~_BV(ADIE); // Буфер полон, прерывание выключено
  }
}

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
}};

Я использую библиотеку LedControl.h, но внутри она использует только digitalWrite, без синхронизации и прерываний.

Может кто-нибудь объяснить, в чем проблема?

UPD Вот что IDE сообщает мне о размерах:

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.

Я использую библиотеку LedControl

, 👍1

Обсуждение

Пожалуйста, обновите свой вопрос, добавив дополнительную информацию. Можете ли вы дать ссылку на библиотеку LedControl и рассказать, что говорит компилятор об использовании флэш-памяти и оперативной памяти. Начните искать другую плату Arduino, например, Arduino MKR или плату, совместимую с Arduino, с esp8266 или esp32., @Jot

Сколько статической оперативной памяти использует ваша программа по данным компилятора? Мне кажется, это должно быть близко к 1 КБ, оставляя в стеке чуть больше 1 КБ. Одна только ваша функция goertzel() использует более 1 КБ стека, так что вы вполне можете разбить стек на BSS., @Edgar Bonet

Спасибо, @EdgarBonet, я даже не думал об этих ограничениях. Я попробую переместить константы во флэш-память и заменить числа с плавающей запятой на фиксированную точку., @zjor


1 ответ


Лучший ответ:

2

Из вашего недавнего редактирования:

Глобальные переменные используют 1048 байт (51%) динамической памяти, оставляя 1000 байт. байты для локальных переменных. Максимум — 2048 байт.

Ваша функция goertzel() определяет это

float v[N + 2];

где N равно 256. Одно это занимает 258×4 = 1032 байт стек, и у вас есть только 1000 байт. Затем, как я и подозревал, вы фактически разбиваете стек по разделу .bss перезаписать некоторые глобальные переменные локальными.

Трудно предсказать, что может произойти в таких обстоятельствах. Программа портит его память: можно только ожидать, что он будет действовать беспорядочно.

,