Доступ к большим массивам данных в программной памяти
- Войдите на сайт для отправки комментариев
Приветствую всех! Прошу помочь разобраться с проблемой считывания массива данных (размер более 64К) из PROGMEM. Пытаюсь сделать электронную ударную установку (барабаны) на Ардуино Mega 2560 для маленького внука (ну, и самому молодость вспомнить :)). Изначально проект делал для 8-битного звука (а еще раньше для MIDI, но малышу сложно подключать), объем сэмплов не превышал 64 кбайта и все работало замечательно c функцией pgm_read_byte, но качество звука не устроило. Как вариант, решил увеличить разрядность звука, и теперь в программной памяти располагается 8 звуковых сэмплов 16 бит 22 кHz (8 массивов данных int около 80 кбайт). Функция pgm_read_word_far упорно не хочет считывать нужные данные, вместо сэмплов считывает куски кода, соответственно, вместо звука треск и шум. Если использовать просто pgm_read_word, то все работает корректно пока не возникнет необходимости "дальний" сэмпл считать - вместо нужного звука считывается кусочек "ближнего", что , в принципе, и правильно. Перепробовал все сочетания присвоения указателей для pgm_read_word_far, но так ничего и не получилось. Пожалуста, помогите решить проблему. На кусочки от миди не обращайте внимание,по итогу будет поддерживаться и миди и wavetable
#include <Timer1.h>
#include <MIDI.h>
#include <avr/pgmspace.h>
// размещаем сэмплы в программной памяти
const char bassdrum[ /*36-6970 */ ] PROGMEM = {
0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF,
//....... и т.д.
};
const char pedalhat [ /* 44-4264 */ ] PROGMEM = {
0xC2, 0x01, 0xDB, 0x01, 0x61, 0x01, 0x83, 0x00, 0xE3, 0x00, 0x8C, 0x00, 0x87, 0x01, 0x08, 0x01, 0x42, 0x00, 0x94, 0x00, 0xEC, 0x01, 0x3F, 0x01,
//....... и т.д.
};
const char closehat [ /* 42-5386 */ ] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x42, 0xFF, 0xD3, 0xFE, 0xB9, 0xFF, 0x75, 0xFF, 0xC5, 0xFF
//....... и т.д.
};
const char tomlow [ /* 43-18250 */ ] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC8, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xBC, 0xFF
//....... и т.д.
};
const char tommid[ /* 47-12934 */ ] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFE, 0xFF, 0x02, 0x00, 0xFD, 0xFF, 0x04, 0x00,
0x80, 0x01, 0x57, 0x01, 0x5B, 0x01, 0x2E, 0x01, 0x0D, 0x01, 0x00, 0x01, 0xD6, 0x00, 0xCF, 0x00, 0x9E, 0x00, 0x9C, 0x00, 0x8F, 0x00
//....... и т.д.
};
const char tomhi[ /* 50_13274 */ ] PROGMEM = {
0xFD, 0xFF, 0x04, 0x00, 0xFA, 0xFF, 0x08, 0x00, 0xF6, 0xFF, 0x0B, 0x00, 0xF4, 0xFF, 0x0C, 0x00, 0xF6, 0xFF, 0x08, 0x00, 0xFB, 0xFF, 0x02, 0x00,
//....... и т.д.
};
const unsigned char snare [/* 38-16346 */] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0xFF, 0xFF, 0x02, 0x00, 0xFE, 0xFF, 0x03, 0x00, 0xFC, 0xFF, 0x04, 0x00,
//....... и т.д.
};
const char crash [/* 49-60380 */] PROGMEM = {
0x03, 0x00, 0xFC, 0xFF, 0x04, 0x00, 0xFB, 0xFF, 0x05, 0x00, 0xFA, 0xFF, 0x06, 0x00, 0xF9, 0xFF, 0x07, 0x00, 0xF9, 0xFF, 0x06, 0x00, 0xFA, 0xFF,
//....... и т.д.
};
int porog = 200;//порог для датчиков, ниже не учитываем
boolean beat[] = {0,0,0,0,0,0,0,0}; // удар или нет
int vol[] = {0,0,0,0,0,0,0,0}; //(МИДИ)громкость полученная с датчиков
int volset[] = {600,720,600,1000,1000,1000,520,600};//(МИДИ)минимальный уровень громкости, если на датчиках меньше
int anPin[] = {A0,A1,A2,A3,A4,A5,A6,A7};// входы датчиков
//const int dcnt = 8; //количество барабанов
int drums[]={47,43,50,44,36,42,38,57};//(МИДИ)номер ноты барабана или тарелки
//36 bass 3485 байт в сэмпле
//38 snare 8173
//42 close hat 2693
//43 low tom 9125
//44 pedal hat 2132
//47 mid tom 6467
//49 crash 30190
//50 hi tom 6637
unsigned long *ptr [8];// массив указателей на сэмплы
volatile uint16_t ln [] = {6465, 9123, 6635, 2130, 3483, 2691, 8171, 15000}; //длина каждого сэмпла в word (2байта)
//текущее положение в сэмпле. Изначально указаны, чтобы при первом включении не воспроизводились
volatile uint16_t cn [] = {6465, 9123, 6635, 2130, 3483, 2691, 8171, 15000};
unsigned int sd[] = {0,0,0,0,0,0,0,0}; //текущее считанное значение word из сэмпла
volatile uint16_t snd; //сумма данных из 8 сэмплов для вывода в порт
MIDI_CREATE_DEFAULT_INSTANCE();
void setup()
{
// MIDI.begin();
disableMillis();// на всякий случай, для увеличения быстродействия отключаем (библиотека Timer1);
//настройка портов и установка звукового 0(0x8000) для 16 разрядного ЦАП (R2R)
DDRC = B11111111;
PORTC = B00000000; //младший
DDRA = B11111111;
PORTA = B10000000;//старший
//заполняем массив указателей:
ptr[0]=(unsigned long*)&tommid[0];
ptr[1]=(unsigned long*)&tomlow[0];
ptr[2]=(unsigned long*)&tomhi[0];
ptr[3]=(unsigned long*)&pedalhat[0];
ptr[4]=(unsigned long*)&bassdrum[0];
ptr[5]=(unsigned long*)&closehat[0];
ptr[6]=(unsigned long*)&snare[0];
ptr[7]=(unsigned long*)&crash[0];
// Prepare Timer1 to send notifications every 1000000us (1s)
// On 16 MHz Arduino boards, this function has a resolution of 4us for intervals <= 260000,
// and a resolution of 16us for other intervals
// On 8 MHz Arduino boards, this function has a resolution of 8us for intervals <= 520000,
// and a resolution of 32us for other intervals
// cn = 0;
startTimer1(45L);// включаем таймер (22 кГц)
}
void loop()
{
for (int i=0;i < 8; i++) {
int sensorValue = analogRead(anPin[i]); //читаем информацию с датчиков
if (sensorValue > porog) {
if (!beat[i]){ // новый удар или хвост от датчика?
if (cn[i] > 1000){ // если время прошло, то не хвост, а новый удар
beat[i] = true;
// vol[i] = sensorValue; для МИДИ
}
//vol = max(vol,sensorValue); для МИДИ
}
// vol[i] = max(vol[i],sensorValue);для МИДИ
}
else
{if ((beat[i])&& (sensorValue == 0) ) // был удар и первая волна закончилась
{
//if (volset[i]>vol[i]){vol[i]=volset[i];}
// Для миди режима
// MIDI.sendNoteOn(drums[i],vol[i]/8,10);
//для wav режима
cn[i] = 0; //начинаем воспроизводить сэмпл сначала
beat[i] = false; //готовимся к следующему удару
}
}
}
}
// обработчик прерывания
ISR(timer1Event)
{
resetTimer1(); сбрасываем таймер
snd =0x8000; // звуковой 0
// пробегаемся по всем сэмплам
// если текущее положение в сэмпле меньшн его длины, то воспроизводим, иначе нет
for (int i=0; i<8; i++){
if (cn[i]<ln[i]) {
//Здесь начинаются проблемы если сэмплы превышают 64к "окно"
// - конец одного сэмпла может переплестись с началом другого.
// Использование указателей на long и макроса pgm_read_word_far не помогло, так как не знаю,
// как получить "длинный" адрес на сэмпл :(((
sd[i]=pgm_read_word_far((unsigned long*)ptr[i]+cn[i]);//считываем данные сэмпла из программной памяти
sd[i]=0x8000 - sd[i]; //переводим считанный данные из сэмпла знаковые в беззнаковые (0..FFFF)
sd[i]= sd[i] >> 2; // чтобы не превысить 16 разрядов в ЦАП,делим на четыре из расчета,
// что одновременно максимум 4 сэмпла будет звучать
snd=snd+sd[i]-0x2000; //суммируем и корректируем звуковой 0, на 1/4 часть, чтобы не было щелчков и помех
++cn[i];
}
}
//выводим в ЦАП
PORTC = snd;
PORTA = snd>>8;
}
1. вы это читали наверное http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
2. раз не даёте полный текст вашей программы, подготовьте и дайте короткие примеры, которые хорошо иллюстиируют рабочий вариант, который вас уже не устраивает, и целевой вариант, который пока не работает как надо
офтоп
3. что за цап у вас?
4. обработчик прерывания, по-возможности, должен быть коротким и быстрым
Спасибо за отклик!
1.Да, это читал и много другого. Попадалось дополнение к pgmspace, в котором подправлена работа с 32-битными адресами.Сразу страничку не сохранил и теперь не могу найти :((.
2.Я приложил полный работоспособный листинг программы, только сократил массивы сэмплов.
Вот из 8-битного рабочего варианта (в обработчике прерывания):
for (int i=0; i<8; i++){ if (cn[i]<ln[i]) { sd[i]=pgm_read_byte(ptr[i]+cn[i]); snd=snd+sd[i]; ++cn[i]; } else {snd = snd+128;} } PORTC = snd; PORTA = snd>>8;И указатели другого типа: ptr[0]=(char*)&crash[0];ptr[1]=(char*)&tomlow[0];и т.д.
3. ЦАП в комментариях к листингу есть - это линейка из резисторов R2R
4. Обработчик настолько короткий и быстрый, что можно и 44кГц 16 бит сразу 3 сэмпла одновременно воспроизводить и 8 ударов по барабанам обрабатывать(проверено с сэмплами по 20 кбайт).Возможно, и 8 можно, но только в памяти не помещаются.
Уважаемый toc!
Большое Вам спасибо! Благодаря Вам более внимательно рассмотрел morepgmspace.h, и это оказалось то что нужно.
В код добавил и поменял:
#include "morepgmspace.h"
unsigned long ptr [8];
ptr[0]=GET_FAR_ADDRESS(tommid[0]); ptr[1]=GET_FAR_ADDRESS(tomlow[0]); ptr[2]=GET_FAR_ADDRESS(tomhi[0]); ptr[3]=GET_FAR_ADDRESS(pedalhat[0]); ptr[4]=GET_FAR_ADDRESS(bassdrum[0]); ptr[5]=GET_FAR_ADDRESS(closehat[0]); ptr[6]=GET_FAR_ADDRESS(snare[0]); ptr[7]=GET_FAR_ADDRESS(crash[0]);
sd[i]=pgm_read_word_far(ptr[i]+cn[i]*2);
И все заработало!
Еще раз спасибо от меня и внука!