Обновить

Диктофон на микроконтроллере (WM8731 + I2S + STM32 + SDIO + FAT16)

Уровень сложностиПростой
Время на прочтение14 мин
Охват и читатели6.2K
Всего голосов 13: ↑13 и ↓0+16
Комментарии22

Комментарии 22

Очень основательный подход! Следующим этапом было бы: своя печатная плата с расширением памяти и другими фишками под задачи(аппаратное отключение -например...)

Следующим этапом было бы: своя печатная плата с расширением памяти и другими фишками под задачи(аппаратное отключение -например...)

Проше взять готовую отладочную плату STM32F746G-DISCO . Где всё это добро уже есть

Для этого есть утилита H2testw.exe.

это вы под Windows на десктопе проверяете -

что карта в самом деле поддерживает нужную битовую скорость на запись

интересно? Если да, то как вы проверяете скорость записи на СТМ-ке, мне кажется у вас не очень понятно написано, а было бы интересно понять.

Не знаю это только я не понял есть ли у вас проверка при переключении буферов, что очередной буфер для записи свободен, то есть что данные из него полностью ушли на карту?

то как вы проверяете скорость записи на СТМ-ке, мне кажется у вас не очень понятно написано, а было бы интересно понять.

Вот такой модульный тест я написал для проверки скорости записи

Код измерения скорости записи в FAT-FS

/*
  tr fat_fs_write
*/
static uint8_t wrData[WRITE_PART_SIZE+1]={0};
bool test_fat_fs_write(void) {
    LOG_INFO(TEST, "%s():", __FUNCTION__);
    bool res = true;
    static uint32_t sn=0;
    FRESULT ret;
    sn++;

    memset(wrData, 0xAA, sizeof(wrData));
    FIL File = {0};
    uint32_t i = 0;
    uint32_t total_size = 0;
    uint32_t start_ms = time_get_ms32();
    char file_name[80]="";
    snprintf(file_name,sizeof(file_name),"TWP%u.bin", sn);
    LOG_INFO(TEST,"sizeof:%s ",file_name );
    BYTE mode = FA_WRITE | FA_CREATE_NEW |FA_OPEN_ALWAYS;
    FRESULT reto = f_open((FIL*)&File, (const TCHAR*) file_name, mode);
    LOG_INFO(TEST,"file_name:[%s],ret:%s ",file_name , FatFsResToStr(reto ));
    if(FR_EXIST==reto ) {
        ret = f_unlink(file_name);
        LOG_INFO(TEST,"DeleteFile:[%s],ret:%s ",file_name , FatFsResToStr(ret ));
        reto = f_open((FIL*)&File, (const TCHAR*) file_name, mode);
    }

    while(total_size < WAV_WRITE_SIZE) {
        UINT written = 0;
        ret = f_write(&File, wrData, WRITE_PART_SIZE, &written);
        EXPECT_EQ(FR_OK, ret);
        EXPECT_EQ(WRITE_PART_SIZE, written);
        if( FR_OK != ret ) {
            LOG_ERROR(TEST,"%u,WrError:%s",i, FatFsResToStr(ret ));
            break;
        }
        if(WRITE_PART_SIZE!=written) {
            break;
        }
        total_size += written;
        i++;
    }

    ret = f_close(&File);
    LOG_INFO(TEST,"CloseCode:%s " , FatFsResToStr(ret ));
    ASSERT_EQ(FR_OK, ret);
    uint32_t stop_ms = time_get_ms32();
    uint32_t diff_ms = stop_ms - start_ms;
    uint32_t data_rate= (1000*total_size) / (diff_ms);
    LOG_INFO(TEST,"totalSize:%u Byte,Duration:%u ms,WriteSpeed:%u Byte/s=%f kByte/s",
             total_size,
             diff_ms,
             data_rate,
             BYTES_2_KBYTES(data_rate));

    float ratio =((float) data_rate / (float) WAV_WRITE_SPEED);
    LOG_INFO(TEST,"ratio:%f ",ratio );
    ASSERT_GR(WAV_WRITE_SPEED, data_rate);

    EXPECT_EQ(WAV_WRITE_SIZE, total_size);
    return res;
}

так это не тест должен быть, это должна быть встроенная диагностика при переключении буферов в рантайме! Надо обязательно проверять что буфер свободен при переключении, иначе вы и будете ловить артефакты от наложений и не поймете откуда они у вас берутся.

Кстати в этом коде очень странно что вы время на открытие и закрытие файла и всякую другую дребедень тоже охватили:

    uint32_t start_ms = time_get_ms32();
    char file_name[80]="";
    snprintf(file_name,sizeof(file_name),"TWP%u.bin", sn);
    LOG_INFO(TEST,"sizeof:%s ",file_name );
    BYTE mode = FA_WRITE | FA_CREATE_NEW |FA_OPEN_ALWAYS;
    FRESULT reto = f_open((FIL*)&File, (const TCHAR*) file_name, mode);
    LOG_INFO(TEST,"file_name:[%s],ret:%s ",file_name , FatFsResToStr(reto ));
// ...
// ...
// ...


ret = f_close(&File);
// ...
// ...
// ...

    uint32_t stop_ms = time_get_ms32();

Зачем вы все это учитываете как время записи?

Кстати в этом коде очень странно что вы время на открытие и закрытие файла и всякую другую дребедень тоже охватили:

Потому, что FATFS фактическую запись как раз и делает внутри f_close.
Внутри f_close вызывается f_sync для фактической загрузки в накопитель.

Не знаю это только я не понял есть ли у вас проверка при переключении буферов, что очередной буфер для записи свободен, то есть что данные из него полностью ушли на карту?

У меня есть проверка на срыв потока.
Если длительность записи фрагмента превысила таймаут REC_PART_TIMEOUT_US , то происходит аварийная остановка записи.

static inline bool sound_rec_save_part_offset(SoundRecorderHandle_t* Node, 
                                              uint32_t offset, 
                                              uint32_t need_size) {
    bool res = false;
    if(Node->cur_save_size < Node->total_file_size) {
        UINT written = 0;
        uint32_t start_save_us = time_get_us32();
        void* buff = (void* )    ( (   (uint32_t ) Node->RxSampleArray) +  offset     );
        FRESULT ret = f_write(&Node->FileToRec, buff, need_size, &written);
        uint32_t end_save_us = time_get_us32();
        if(FR_OK == ret) {
            if(need_size == written) {
                Node->cur_save_size += written;
                Node->wr_ok_cnt++;
                res = true;
            } else {
                Node->error_size_cnt++;
                //LOG_ERROR(SOUND_RECORDER,"WriteSmall:%u Byte", written);
                res = sound_recorder_stop_ll(Node);
            }
            Node->action = SOUND_RECORDER_ACTION_NOPE;
        } else {
            Node->error_cnt++;
            Node->action = SOUND_RECORDER_ACTION_NOPE;
            res = sound_recorder_stop_ll(Node);
        }
        uint32_t diff_us = end_save_us-start_save_us;
        if (REC_PART_TIMEOUT_US < diff_us) {
            Node->long_write_error_cnt++;
            res = sound_recorder_stop_ll(Node);
        }
    } else {
        res = sound_recorder_stop_ll(Node);
    }
    return res;
}

ну вот здесь у вас один f_write

        uint32_t start_save_us = time_get_us32();
        void* buff = (void* )    ( (   (uint32_t ) Node->RxSampleArray) +  offset     );
        FRESULT ret = f_write(&Node->FileToRec, buff, need_size, &written);
        uint32_t end_save_us = time_get_us32();

измеряется, и получается он измеряет непонятно что если

FATFS фактическую запись как раз и делает внутри f_close

Но с точки зрения проверки что буфер свободен это корректно, тем не менее.

Но получается вы используете двойную буферизацию - одна своя, вторая от файловой системы. Это как минимум не очень эффективно, а как максимум будет иметь всякие странные эффекты, которые и приводят к артефактам в записанных данных и которые вы не отладите пока в конце концов не влезете с ногами в код реализации этой файловой системы и ее буферизации. Чужая реализация файловой системы в эмбеддед это почти всегда беда, такая черная дыра в которой размножаются баги безконтрольно. В эмбеддед Операционная Система (ОС) практически не бывает полноценная, там куча ограничений которые еще фиг все найдешь-отловишь. Фактически вы вместо того чтобы заниматься отладкой своего кода вынуждены заниматься исследованием кода этой ОС большую часть времени.

Не, ну на кодеке любой сделает. А вот ежели через ADC и DAC микроконтроллера...

У аудиокодека шире динамический диапазон: 24 бит вместо 12 на MCU. Плюс есть аналоговые предусилитель для микрофона, цифровой фильтр нижних частот.

Звук получается чище и разборчивее.

Все нормально там со звуком у STM32. Используте i2s microphone и sigma delta.
https://habr.com/ru/articles/662141/
Здесь сигма дельта на тaймерах stm32f401

https://www.youtube.com/watch?v=8jB26_xrSuw

Зачем диктофону частота дискретизации 96 кГц? И тем более 384 кбайт/с, то есть стерео? Ему и 44,1 кГц - много. 32 кГц моно - вполне достаточно. А вот FAT16 - это анахронизм, особенно для диктофона, особенно без сжатия данных. А кодек WM8731 числится устаревшим - его уже не продают на Маузере и Дижикее, по крайней мере поштучно.

Зачем диктофону частота дискретизации 96 кГц? Ему и 44,1 кГц - много. 32 кГц - вполне достаточно.


Я хочу сделать автономный сонар из диктофона.
https://habr.com/ru/articles/868672/

Если повысить частоту дискретизации до 96kHz, то это улучшит разрешение измерения расстояния.

На частоте дискретизации 44100 Hz за один семпл (22.6 us) звук пролетает 331*(1/44100)=7.5mm. Свертка ЛЧМ дает максимум в одном семпле. Получается погрешность измерения расстояния должна быть -/+ 7.5 mm.

На частоте 96k Hz погрешность измерения расстояния должна быть -/+ 3.4 mm.

А кодек WM8731 числится устаревшим - его уже не продают на Маузере и Дижикее.

Зато он прост в программировании (11 регистров всего-лишь) и его можно купить на AliExpress прямо в составе готового модуля


https://aliexpress.ru/item/32857846271.html?shpMethod=CAINIAO\_STANDARD&sku\_id=65320258244&spm=a2g2w.productlist.search\_results.10.53306900uSNlJF

 А вот FAT16 - это анахронизм, особенно для диктофона.

Ну а что тут поделаешь, если модульные тесты проходят на FAT16 и проваливаются на FAT32?

А кодек WM8731 числится устаревшим - его уже не продают на Маузере и Дижикее, по крайней мере поштучно.

Какой тогда аудиокодек Вы предлагаете применить?

Не подскажу - задача специфическая.

Все таки стоит на нормальную SD карту делать запись и как было сказано ранее, сделать готовое устройство. Просто на этих макетках как то кустарно получается. А так, сделать печатную плату, красивый корпус, все укомпоновать, добавить кнопки быстрого доступа к командам, небольшой дисплей и будет изделие, а не полуфабрикат.

Автор, я говорил тебе уже, у тебя такой потенциал, а ты каждый раз переизобретаешь велосипед, самокат, колесо и кирпичи. И все бы ничего, дальше макетной платы ты не идёшь, почему?

Статей много, ты делишься информацией, очень круто, а что дальше? Если ты просто хочешь научить всех, показать, как можно делать и что для этого нужно, то хорошо, пусть будет так.

Это проект - интересный, но много НО! Контроллер на макетке, аудиоплата на макетке, какой микрофон - непонятно, как прослушать на диктофоне - непонятно. Управление - непонятно, индикации нет. Схемы устройства нет. Карта памяти только на FAT16 (объем я так понимаю был только такой в наличии) и FAT32 не работает.

Повторить такое устройство смогут единицы, если захотят (если найдут такие компоненты и будут согласны на программно-аппаратные ограничения).

В итоге, автор сделал классную работу, показал, выяснил, узнал, но для себя. Да я понимаю этот кайф, когда разобрался с какими-то деталями, мелочам, все заработало и на какие-то мелочи даже не обращаешь внимания, потому что сама идея была именно в этом! А про остальные важные моменты забываешь, упускаешь, предполага что это же всё просто, значит люди поймут и разберутся сами... Но это сделают единицы, поймут тоже единицы, а все кто захочет повторить - не смогут...

Это не в упрёк, это взгляд со стороны.

 А так, сделать печатную плату, красивый корпус, все укомпоновать, добавить кнопки быстрого доступа к командам, небольшой дисплей и будет изделие

Среди российских программистов-микроконтроллеров в организациях в принципе нет пром-дизайнеров как класса. Хотя факультеты "Промышленного Дизайн" сейчас есть почти в каждом ВУЗзе. Если у нас делают какой-то электронный прибор (пусть даже с хорошей прошивкой), то он похож на кирпич, гробик или шайбу. В качестве корпуса обычно бездумно покупают готовые прямоугольные серые корпуса в ЧипИдип-е. Разъём всегда ставят DB-9 (привет из 195х). Нет уплотнений. щели получаются такие, что между разъёмом и корпусом можно аж палец засунуть. Получившееся изделие даже в руках держать противно. Торчат куски платы об которые можно поцарапаться. Даже если электроника и прошивка вдруг и внезапно получились великолепно, то механическое исполнение продукта все перечеркивает и обнуляет результат на корню.

Автор, я говорил тебе уже, у тебя такой потенциал, а ты каждый раз переизобретаешь велосипед, самокат, колесо и кирпичи.

Спасибо. А что еще остается делать ?
В России не разрабатывают по-настоящему крутую электронную технику.
Негде приложить свои силы.
Не разрабатывают вертикально приземляющиеся ракеты (реактивная посадка), не делают смартфоны, не делают электромобили, не делают радары с синтезом апертуры (SAR локаторы), не делают ЯМРТ томографы. Уровень современной embedded разработки РФ - это, условно, сделать прошивку платы для открытия и закрытия трубы - это максимум. И то с натягом.

И все бы ничего, дальше макетной платы ты не идёшь, почему?

Я специалист по прошивкам. Чертить 3D корпуса не умею. Топологию PCB трассировать тоже. Для того чтобы спроектировать полноценный продукт надо минимум 3 инженера: электронщик, пром-дизайнер и программист.

Статей много, ты делишься информацией, очень круто, а что дальше? Если ты просто хочешь научить всех, показать, как можно делать и что для этого нужно, то хорошо, пусть будет так.

Логика проста.
Российские налогоплательщики финансировали мое бюджетное образование в казёном Политехе. Теперь надо, по возможности, возвращать результаты полученного технического образования обратно в народ.

Хороший труд. Спасибо.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации