Аномальное поведение операций АЦП, цифровой записи и операций с плавающей запятой.
Я пытаюсь проанализировать входной сигнал микрофона (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
@zjor, 👍1
Обсуждение1 ответ
Лучший ответ:
Из вашего недавнего редактирования:
Глобальные переменные используют 1048 байт (51%) динамической памяти, оставляя 1000 байт. байты для локальных переменных. Максимум — 2048 байт.
Ваша функция goertzel()
определяет это
float v[N + 2];
где N
равно 256. Одно это занимает 258×4 = 1032 байт
стек, и у вас есть только 1000 байт. Затем, как я и подозревал,
вы фактически разбиваете стек по разделу .bss
перезаписать некоторые глобальные переменные локальными.
Трудно предсказать, что может произойти в таких обстоятельствах. Программа портит его память: можно только ожидать, что он будет действовать беспорядочно.
- Float печатается только 2 десятичных знака после запятой
- Отправка и получение различных типов данных через I2C в Arduino
- Избегайте математических вычислений с плавающей запятой, чтобы ускорить Arduino
- Взаимодействие ADS8319 с Arduino UNO
- Ускорение двигателя постоянного тока с помощью ШИМ
- Как найти пиковое значение аналогового сигнала?
- Как преобразовать эту программу в сборку из c++
- Хранение значений широты и долготы в виде символов с заданной точностью
Пожалуйста, обновите свой вопрос, добавив дополнительную информацию. Можете ли вы дать ссылку на библиотеку LedControl и рассказать, что говорит компилятор об использовании флэш-памяти и оперативной памяти. Начните искать другую плату Arduino, например, Arduino MKR или плату, совместимую с Arduino, с esp8266 или esp32., @Jot
Сколько статической оперативной памяти использует ваша программа по данным компилятора? Мне кажется, это должно быть близко к 1 КБ, оставляя в стеке чуть больше 1 КБ. Одна только ваша функция goertzel() использует более 1 КБ стека, так что вы вполне можете разбить стек на BSS., @Edgar Bonet
Спасибо, @EdgarBonet, я даже не думал об этих ограничениях. Я попробую переместить константы во флэш-память и заменить числа с плавающей запятой на фиксированную точку., @zjor