Mmc/sd wav стерео плеер на atmega32 с пду

MMC/SD WAV стерео плеер на ATmega32 с ПДУ

mmc/sd wav стерео плеер на atmega32 с ПДУ

Это мой первый и самый успешный проект на микроконтроллерах AVR. Несколько месяцев назад я попытался сделать WAV плеер на PIC16F877A. Он работал, но качество звука было не очень хорошим, потому что у него не было достаточного объема оперативной памяти, и я не смог сделать достаточный буфер данных.

Когда я купил микроконтроллер ATmega32, первое, что пришло мне в голову – сделать хороший WAV плеер. Я закончил свою работу и качество звука действительно хорошее. Я могу сказать, что мой WAV плеер способен воспроизводить 8-битное моно /стерео с максимальным битрейтом 1300kbps для моно и 1600kbps для стерео. Т.е.

можно играть 8-битное WAV моно с частотой дискретизации до 160 кГц и стерео до 96 кГц без шума!

Примечание: Полный спектр человеческого слуха составляет от 20 Гц до 20 кГц. Минимально достаточная частота дискретизации 40 кГц. Именно для этого частота дискретизации 44,1 кГц была выбрана для компакт-диска.

Я считаю, что это идеальный 8-битный WAV- стерео плеер. Я не нашел отличий между качеством звука с компьютера и этим плеером. Видео снималось мобильным телефоном, поэтому возможны потери качества звука. Поскольку ШИМ имеет некоторые ограничения, я пытался использовать R-2R ЦАП, но не заметил существенных улучшений.

Еще одной интересной особенностью плеера является то, что он может управляться с пульта от ТВ Philips (протокол RC5). Пультом можно переключатся между следующей и предыдущей песней, включать воспроизведение, ставить на паузу, включать некоторые забавные эффекты, такие как увеличение или уменьшение скорости воспроизведения.

Программа для работы с MMC картой памяти не писалась с нуля, а была взята готовая.

Возможности:

  • Дистанционное управление
  • Высокое качество на выходе
  • Максимальная скорость передачи данных – 144 Кб / сек.
  • Поддержка стерео
  • Автоматический возврат в начало после окончания всех песен
  • Дополнительная регулировка битрейта с пульта
  • Установка задержки перед воспроизведением

Краткое описание работы:

Инициализация MMC карты и работа с ней была описана ранее тут.

Я использую дисплей 16×2 для отображения многих вещей. Сначала он при необходимости отображает сообщение об ошибке при попытке инициализации MMC. Когда инициализация проходит успешно, он показывает сообщение об этом.

Потом проверяется загрузочный сектор карты MMC (сектор 0), чтобы проверить файловую систему карты. Для этого нам нужно прочитать сектор 0 карты MMC и копировать его в буфер (для  этого задействовано 512б буфера).

Мой код предназначен только для файловой системы FAT16, если на карте другая файловая система, он выдает сообщение об ошибке на дисплей. Если всё в порядке, то он читает данных из буфера и вычисляет номер сектора с началом данных, начало FAT и начало корневого каталога. Кроме того, он обнаруживает секторы кластеров.

Каждый сектор это 512 байт. Эти данные необходимы для дальнейшей работы MMC / SD карт, хранятся как глобальные переменные.

Теперь необходимо попасть в корневой каталог. Нам нужно загрузить первый сектор корневого каталога в 512 байт буфера. Каждая запись содержит подробную информацию о файле или папке в корневом каталоге. Оттуда мы можем прочитать имя файла, атрибуты файла, адрес самого файл и многое другое. Нас интересует расширение файлов (WAV), атрибутах файла, адрес первого кластера куда записан файл.

Теперь мы можем прочитать первый кластер (группу секторов, размер зависит от размера памяти MMC / SD). Теперь, после чтения и воспроизведения всех секторов, в первой группе (проигрывание будет описано далее), то мы должны найти следующий номер кластера того же файла.

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

Таким образом, мы не можем сказать, что следующий кластер файла будет ближайшим. Но для чтения каждого файла есть список кластеров FAT (File Allocation Table), в котором каждый номер кластера имеет уникальную позицию. Мы уже вычислили начальный адрес FAT.

С FAT мы можем получить следующий номер кластера в файле.

У нас есть номер первого кластера, и мы рассчитали начальный адрес сектора с определенным числом кластеров. Теперь мы читаем данные из первого сектора файла и оттуда мы получаем битрейт, частоту дискретизации, количество каналов и многое другое.

Возьмем битрейт, и использовать его, чтобы установить частоту прерывания таймера. Теперь таймер генерирует прерывания в зависимости от битрейта и количества канала. Теперь на каждом прерывании таймера, 8 бит данных вводится в регистр OCR модуль таймера ШИМ.

Соответственно, генерация ШИМ сигнала идет в фоновом режиме без затрат каких-либо ресурсов процессора. Этот сигнал ШИМ может быть легко демодулирован при помощи RC фильтра. Если значение конденсатора или резистора увеличивается или уменьшается , это повлияет на качество звука, т.е.

он может не отфильтровать некоторые высокие частоты и можно услышать сигнал AM радио.

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

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

При включении питания происходит задержка 400мс перед началом считывания сигнала с пульта. Возможно, плеер будет реагировать на сигнал с пульта со второго раза.

Чтобы этого надо декодировать RC5 вне atmega32 и отправить значения через последовательный интерфейс для лучшей производительности.

Кварц я использовал на 16.450 МГц, т.к. под рукой не оказалось 16 МГц. Это никак не повлияло на работу микроконтроллера.

Фото работы:

Fuse: LFUSE = 0b11101111

Скачать ПО можно здесь

Скачать список элементов (PDF)

Оригинал статьи

Прикрепленные файлы:

  • wav_player_atmega32.rar (19 Кб)

Источник: http://cxem.net/mc/mc103.php

Работа с SD картой. Воспроизведение wav файла. Ч3

Низкоуровневые функции для работы с SD картой

Чтобы использовать библиотеку Petit FatFs с SD картой, нужно реализовать три низкоуровневые функции для работы с ней – это функция инициализации, чтения и записи.

Если вы читали предыдущий материал, в котором была описана библиотека Petit FatFs, то должны помнить, что “пустышки” этих функций находятся в файле diskio.

cНа сайте Elm Chan`a есть примеры использования библиотеки с SD картами, поэтому можно взять уже готовые функции из этих проектов, что я и сделал. Я позаимствовал из одного примера файл mmc.c и заменил им файл diskio.c , однако файл mmc.c тоже потребовал небольшого “допиливания”.

В начале файла определены макросы, реализующие работу с SPI интерфейсом. Можно определить эти макросы для работы с аппаратным SPI модулем, а можно реализовать программный SPI. Это уже вопрос ресурсов и желания пользователя.Макросы такие:

#define SELECT() – формирует низкий уровень на CS выводе карты

#define DESELECT() – формирует высокий уровень на СS выводе карты
#define MMC_SEL – возвращает единицу, если на выводе CS низкий уровень
#define init_spi() – инициализирует SPI модуль
#define dly_100us() – формирует задержку на 100 микросекунд
#define xmit_spi(d) – передает байт данных по SPI
#define rcv_spi() – принимает байт данных по SPI
#define FORWARD(d) – перенаправляет поток данных, этот макрос можно оставить пустым

Все эти макросы легко реализовать, если прочитать материал про SPI модуль AVR микроконтроллера. Я как раз взял оттуда spi драйвер и “прицепил” его к файлу mmc.c.

Короче, получается такая последовательность. Мы берем библиотеку Petit FatFs добавляем к ней файл mmc.c из примеров Elm Chan`a, описываем макросы реализующие spi и после этого можем работать с SD картой.

Немного заморочено, но если один раз разобрался, то все становится понятно.

Итак, я все это проделал и теперь могу использовать SD карту.

И чтобы показать, что это действительно работает, я написал проект, в котором микроконтроллер читает с SD карты wav файл и воспроизводит.

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

Поскольку проект исключительно демонстрационный, я не гнался за качеством звука, а постарался сделать код как можно более простым. В качестве файла для воспроизведения я взял произвольный mp3 трек (мне попалась песня группы Prodigy) и перекодировал его в wav файл с такими параметрами: 8 бит, 22 кГц, моно.

Для конвертирования файла я использовал видео редактор Sony Vegas, но можно найти программу и попроще. Например, такая функция есть во многих аудио редакторах вроде Sound Forge, WaveLab, Cool Edit и т.д. “8 бит” – это разрядность одной выборки аналогового сигнала.

Звук хорошего качества обычно имеет разрядность 16 (CD качество) или 24 бита (студийная запись), но для микроконтроллерной “говорилки” 8-и разрядов хватит за глаза. “22 кГц” – это частота дискретизации. То есть частота, с которой из аналогового сигнала “брались” выборки.

С этой же частотой мы должны преобразовывать цифровые выборки сигнала в аналоговые напряжения. Цифровой звук хорошего качества обычно имеет частоту дискретизации 44.1 кГц (CD качество), 96 кГц ( студийная запись) и т.д. “моно” – означает одну звуковую дорожку, которая будет воспроизводиться как в правом, так и в левом аудио каналах.

Итак, для воспроизведения wav файла с параметрами 8 бит, 22 кГц, моно, нам понадобится одноканальный 8-и разрядный ЦАП, способный формировать на выходе аналоговые напряжения с частотой 22 кГц.

Поскольку у большинства AVR`ок нет встроенного цифро-аналогового преобразователя, мы можем использовать следующие варианты:- аппаратный ШИМ,- программный ШИМ, – внешний интегральный ЦАП,- внешняя схема ЦАП`a .Реализация программного ШИМ`a требует от микроконтроллера большого быстродействия, поэтому я не захотел с ним связываться.

Внешний ЦАП обычно использует SPI, который нужен для SD карты. Внешняя схема ЦАП`а, например схема R-2R, неплохой вариант, но под нее нужно отдать целый порт микроконтроллера. Исходя из этого, я остановил свой выбор на аппаратном 8-и разрядном ШИМ`е. Во первых, эта функция есть во всех микроконтроллерах AVR, а во-вторых, для реализации ЦАП`а требуется всего один вывод микроконтроллера.

В одном из старых материалов я уже описывал принцип формирования аналогового сигнала с помощью ШИМ, однако в случае wav файла здесь не все так просто.

С какой частотой можно формировать аналоговые напряжения с помощью ШИМ? Это зависит от трех параметров: тактовой частоты микроконтроллера, делителя таймера и его разрядности. Например, для 8-и разрядного ШИМ сигнала при тактовой частоте микроконтроллера 16 МГц можно получить следующие частоты.

Читайте также:  Индикатор неисправности стоп-сигнала

Тактовая частота микроконтроллера Fcpu = 16000000 ГцТактовая частота таймера Т0 Ftim = Fcpu/k , где k – 1, 8, 64, 256, 1024.

Частота ШИМ сигнала Fpwm = (Fcpu/k)/2^8 = Fcpu/(k*256) при k = 1 Fpwm = 62500при k = 8 Fpwm = 7812при k = 64 Fpwm = 976при k = 256 Fpwmn = 244при k = 1024 Fpwm = 61Ближайшая частота к требуемым 22 килогерцам – это 7812, но такая частота не подойдет. Файл, воспроизводимый с такой частотой, будет уж слишком замедленным.

Надо подобрать такую тактовую частоту микроконтроллера, при которой можно получить требуемую частоту формирования аналоговых напряжений. Неплохой результат получается при 6 МГц и k = 1Fcpu = 6000000 ГцFan = 6000000/(2^8 * 1) = 23437 Гц Звуковой файл будет немного ускоренно воспроизводиться, но на слух это почти незаметно.

Итак, аналоговые напряжения будут формироваться с помощью ШИМ функции аппаратного таймера Т0, но как разнести процесс чтения данных с процессом воспроизведения? Считывать с SD карты по одной выборке сигнала с частотой 22 кГц не получится, микроконтроллер не будет успевать это сделать.

Тут понадобится буфер, условно состоящий из двух одинаковых половинок. Пока одна часть буфера заполняется данными с SD карты, из другой части буфера данные передаются в ЦАП (в нашем случае таймеру). Нужно только выбрать такой размер буфера, при котором эти два процесса не будет “наезжать”друг на друга. Я подбирал размер буфера следующим образом.

Задал максимальную частоту SPI модуля микроконтроллера atmega16 и посмотрел сколько времени затрачивается на чтение данных с SD карты. То есть сколько времени выполняется функция pf_read(..). При тактовый частоте Fcpu = 6 МГц эта функция выполнялась ~2.5 мс, но иногда попадались циклы по 5 мс (наверное из-за чтения на границе секторов ..

напишите в комментариях, если знаете). Причем это время не зависело от количества данных – и 32, и 64, и 128 байт читались за одно и то же время. Затем я посчитал сколько данных будет передано в ЦАП за время 5 мс. Частота нашего псевдо ЦАП`a = 23437 Гц, соответственно период = 42.6 мкс. Делим 5 мс на 42.6 и получаем искомую цифру.n = 0.

005/(1/23437) = 117 То есть за 5 мс микроконтроллер выдаст 117 выборок сигнала, при этом за это же время успеет прочитать с карты 128 выборок. Получается, что если взять буфер размером 256 байт микроконтроллер будет успевать выполнять обе задачи и даже остается небольшой запас времени. Он, кстати, необходим, потому что в процесс чтения данных с SD карты, будут вклиниваться прерывания таймера Т0.

Так я и сделал. Выбрал размер буфера равным 256 байт.

Схема для проекта

Схема питается от двух стабилизаторов – 3.3 В и 5 В. Как вариант можно запитать все схему от 3-х вольтового источника (тогда даже не понадобятся преобразователи уровней) или понизить 5-и вольтовое напряжение с помощью трех последовательно включенных диодов и запитать таким образом SD карту.

Микроконтроллер тактируется от внешнего кварцевого резонатора с частотой 6 МГц. SD карта подключена к микроконтроллеру по одной из приведенных ранее схем.

Для преобразования ШИМ сигнала в постоянное напряжение используется низкочастотный RC фильтр из двух каскадов. Частота среза фильтра около 10 кГц, что соответствует полосе воспроизводимого цифрового сигнала.

Для индикации состояния устройства используется светодиод LED1. Если карта не считывается в момент включения устройства, светодиод начинает моргать.

Код проекта

Все основное действо заключено в файле main.c. При старте программы происходит инициализация переменных и настройка выводов – настраивается ШИМ выход и выход для светодиода. Затем монтируется SD карта, открывается файл под названием 1.wav и из него (в буфер) читаются 256 байт. Далее проверяется результат выполнения операций с картой.

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

В первом состоянии идет заполнение нижней половины буфера, во втором – верхней половины, а в третьем программа ничего не делает. В третье состояние автомат переходит, когда wav файл заканчивается. При этом гасится светодиод и выключается таймер Т0.Параллельно основному циклу программы выполняется прерывание таймера Т0.

Оно сделано предельно коротким. В микроконтроллер загружает в регистр сравнения OCR0 содержимое ячейки буфера, на которую указывает индексная переменная, а затем инкрементирует ее. Поскольку размер буфера равен 256 байт, индексную переменную не приходится проверять на граничное значение.

Автомат, заполняющий буфер данными, проверяет индексную переменную. И по ее значению “понимает” в какую часть буфера можно писать данные.

Неиспользуемые функции библиотеки Petit FatFs я отключил в файле pff.h.

Из недостатков проекта можно отметить два момента:- при воспроизведении не пропускается заголовок wav файла- когда wav файл заканчивается таймер выключается, не “доигрывая” оставшееся содержимое буфера.

//****************************************//// Author(s)…: Pashgan//// Target(s)…: Mega16//// Compiler….: GCC //// Description.: Воспроизведение wav файла с SD карты////****************************************#include “compilers_4.h”#include “diskio.h”#include “pff.h”/* выводы микроконтроллера */#define LED_PORT PORTD#define LED_DIR DDRD#define LED_PIN 4#define PWM_PORT PORTB#define PWM_DIR DDRB#define PWM_PIN 3/* буфер */#define BUF_SIZE 256UL#define HALF_BUF ((BUF_SIZE)/2)uint8_t buf[BUF_SIZE];/*переменные для доступа к SD*/FATFS fs;WORD s1;FRESULT res;/*остальные переменные*/typedef enum{ST_LOW_BUF, ST_HI_BUF, ST_STOP}state_t;static state_t st;static volatile uint8_t i;static char f[] = “1.wav”;int main( void ){ st = 0; i = 0; /*настройка шим выхода*/ PWM_DIR |= (1

Источник: http://chipenable.ru/index.php/programming-avr/item/212

Аудио проигрыватель на Atmega8 и WTV020

» Схемы » Аудио · Применение микроконтроллеров

05-11-2014

Захаров Денис, Украина

На просторах Интернета можно встретить немало проектов, связанных с воспроизведением звуковых файлов с помощью микроконтроллеров. В большинстве случаев аудиофайл хранится в карте памяти объемом до 2 Гб.

Однако реализовать полноценную систему FAT на 8-разрядных микроконтроллерах очень сложно, поэтому лучше использовать модуль, который сможет работать с картой памяти, не загружая ресурсы основного контроллера.

Самым миниатюрным и простым в обращении из таких модулей является WTV020 (Рисунок 1).

Рисунок 1.

Модуль работает с отформатированными в FAT16 SD-картами объемом не более 2 Гб и воспроизводит в любой последовательности звуковые фрагменты в форматах .ad4 или .wav. Предусмотрено питание внутренних цепей модуля от встроенного стабилизатора на 3.

3 В, что очень удобно, поскольку это позволяет на сам модуль подавать напряжение 5 В. (Для этого нужно замкнуть площадку «5V» со средней площадкой на плате модуля, предварительно, разомкнув перемычку из припоя с площадкой «3.

3V», как показано на Рисунке 2).

Рисунок 2.  

Управлять модулем можно как «вручную», так и с помощью микроконтроллера. В «ручном» режиме достаточно подключить к устройству кнопки, согласно схеме, представленной на Рисунке 3. В техническом описании модуля WTV020 можно найти другие варианты подключения, которые по функциональности мало чем отличаются от предложенной схемы.

Рисунок 3.   

Динамик подключается к выходам ШИМ или к встроенному 16-разрядному ЦАП. В последнем случае нужно подключать внешний ОУ и усилитель (Рисунок 4). При подключении к ШИМ каналу разрешается коммутировать динамики сопротивлением 8 Ом и мощностью до 0.5 Вт.

Рисунок 4.

Назначение выводов модуля WTV020 показано в Таблице 1. Каналы двухпроводного интерфейса связи могут использоваться как для подключения кнопок, так и для внешнего микроконтроллера.

Таблица1.
Номервывода Назначение
1 Сброс
2 Аудио выход с ЦАП
3 Не используется
4 ШИМ выход
5 ШИМ выход
6 Не используется
7 Громкость «+» / CLK
8 Общий
9 Воспроизведение – пауза
10 Громкость «-» / DI
11 Не используется
12 Повтор
13 Следующий файл
14 Не используется
15 BUSY
16 Питание

Для визуального контроля были использованы LCD дисплей 2×16 и согласующий контроллер ATmega8. Общая схема проигрывателя представлена на Рисунке 5. Микроконтроллер и модуль питаются напряжением 3.

3 В, дисплей – 4 В, поскольку для выбранного LCD напряжения 3.3 В было недостаточно. На прием данных от МК эта разница напряжений никак не влияет.

Внутренний стабилизатор модуля WTV020 автор решил не активировать.

Рисунок 5.

Для передачи данных в модуль WTV020 используются линии CLK и DI. Согласно техническому описанию (Рисунок 6а), 16 бит данных должны передаваться с периодичностью 200 мкс, однако на практике эти значение нужно увеличить до 2 мс (Рисунок 6б).

Рисунок 6а.
Рисунок 6б.

Исходя из документации, после подачи питания рекомендуется на выход «Reset» модуля подать отрицательный импульс длительностью 5 мс, и по истечении 300 мс отправлять команды. Но это явная ошибка, поскольку время инициализации модуля WTV020 составляет порядка 600 мс. Если подавать команды раньше, чем через 600 мс после сброса, модуль их просто не воспринимает.

Список основных команд, принимаемых модулем, представлен в Таблице 2. Из таблицы видно, что максимальное количество воспроизводимых аудио файлов составляет 512, однако автор ограничился тридцатью.

Громкость регулируется в 7 диапазонах. На практике с адреса FFF0 по FFF3 наблюдаются искажения звука, причем как с ШИМ-выхода, так и с ЦАП. Команды FFFE (Play/Pause) и FFFF (Stop/Play) – триггерные.

Таблица 2.
Команда Назначение
0000-0511 Выбор номера аудио записи (1-512)
FFF0-FFF7 Настройка громкости
FFFEH Воспроизведение – пауза
FFFFH Воспроизведение – стоп

Модуль WTV020 воспринимает аудиофайлы формата .wav или .ad4 (аналог mp3). Для конвертирования файлов из mp3 в ad4 нужно использовать программу USBRecording (Рисунок 7). Карту памяти форматировать в систему FAT. Аудиофайлы именовать только так: 0000.ad4, 0001.ad4. В противном случае модуль не сможет выбрать адрес файла для воспроизведения.

Рисунок 7.

На экран LCD выводятся номер воспроизводимого файла и громкость в виде шкалы из 7 закрашенных прямоугольников. Фото готового устройства показано на Рисунке 8.

Рисунок 8.

Демонстрационное видео:
 

Программное обеспечение МК, виртуальная модель Proteus и аудиофайл формата .ad4 – скачать

Программа для конвертирования аудио записи в формат .ad4 – скачать  

Источник: https://www.rlocman.ru/shem/schematics.html?di=152865

SD/MMC карта памяти и микроконтроллер AVR (часть 2) Система FAT, Petit FatFs

В этой заметке рассмотрим работу совместно с файловой системой FAT. Итак, мы имеем микроконтроллер Atmega32 и SD/MMC карту памяти, отформатированную в системе FAT. На карте памяти, в корне есть файлы read.txt и write.txt.

Из read.txt нужно прочитать содержимое и вывести на программу Terminal 1.9 по UART. А в файл write.txt требуется записать некоторые тестовые данные, предположим «write ok».
Про подключение и по блоковую работу карты памяти SD|MMC с микроконтроллером писалось в предидущей статье цикла.

Читайте также:  Компания toshiba выпустила новый, сдвоенный mosfet для заряда мобильных устройств

Теперь попробуем пописать/почитать карту памяти на уровне файлов. С этим нам поможет библиотека Petit FatFs от мистера ChaN (elm-chan.org). Petit FatFs умеет работать с FAT16, FAT32, и предоставляет высокоуровневые функции работы с файлами.

Эта библиотека представляет собой «драйвер» FAT, к которому можно подключить практически любой носитель с файловой системой FAT. Но для этого нужно написать 3 функции взаимодействия с носителем:
1) DSTATUS disk_initialize (void) Инициализирует носитель, в случае успешной инициализации возвращает 0.

В случае, если носитель не инициализировался, возвращает STA_NOINIT, а если носитель отсутствует – STA_NODISK.

  1. typedef BYTE DSTATUS; //Статус носителя

  2. #define STA_NOINIT 0x01 //Носитель не инициализирован

  3. #define STA_NODISK 0x02 //Носитель отсутствует

2) DRESULT disk_readp (BYTE* Buffer, DWORD SectorNumber, WORD Offset, WORD Count) Считывает часть сектора в область, куда указывает указатель Buffer. Считывает Count байт, начиная Offset-го байта сектора под номером SectorNumber (номер сектора в LBA адресации). Возвращает следующий результат:

  1. typedef enum { //Статус дисковой операции

  2. RES_OK = 0, //Операция успешно завершена

  3. RES_ERROR, //Ошибка носителя

  4. RES_NOTRDY, //Носитель не готов

  5. RES_PARERR //Ошибочный аргумент

  6. } DRESULT;

3) DRESULT disk_writep (BYTE* Buffer, DWORD SectorBytes) Пишет данные, на которые указывает указатель Buffer. Если Buffer=NULL, то это инициализирует/завершает операцию записи в сектор. SectorBytes выполняет двоякую роль, при инициализации указывают номер сектора, а при записи – количество байт, которые нужно записать. Пример работы с функцией disk_writep:

  1. disk_writep(0, sector_number); //Инициализирует операцию записи в сектор sector_number<\p>

  2. disk_writep(data, byte_to_write); //Пишем данные в сектор, до 512 байт<\p>

  3. disk_writep(data, byte_to_write);

  4. disk_writep(data, byte_to_write);

  5. disk_writep(0, 0); // Завершаем операцию записи сектора,<\p>

  6. // если сектор не был дописан до конца, оставшиеся байты забиваются нулями.

Результат возвращает тот же, что и disk_readp.

На сайте мистера ChaN был приведен пример использования библиотеки Petit FatFs совместно с SD|MMC картой. Этот пример я взял за базу, и немного переделав его, получил удобный инструмент работы с системой FAT.

Далее была написана программа-пример, которая демонстрирует чтение из файла read.txt и запись в файл write.txt которые расположены в корне SD|MMC карты памяти. Также было показано сканирования папки на предмет наличия в ней файлов и остальных папок.

Рассмотрим поподробнее этот пример.

Скачав исходный код, и открыв его в AVR Studio 4 можно увидеть следующее:

Сам драйвер Petit FatFs находится в файлах pff.c, pff.h. Как видно из кода выше, стандартные типы char, int, long int в Petit FatFs были переопределены.

Эти переопределения можно посмотреть в файле integer.h. В файле diskio.h находятся заголовки функций disk_initialize, disk_readp, disk_writep. В mmc.c – все для работы c SD|MMC картой, и сами функции disk_initialize, disk_readp, disk_writep. В оставшемся файле Petit_FAT_FS_sample.

c производиться собственно использование Petit FatFS для чтения/записи файлов на карте.
Для подключения карты памяти к микроконтроллеру используется так же макетка и схема, что и в статье «SD/MMC карта памяти и микроконтроллер AVR (часть 1) Базовые операции.

» Если же карта подключена к PortB по другому, то стоит подредактировать секцию в mmc.c:

  1. #define SD_DI 0

  2. #define SD_DO 1

  3. #define SD_CLK 2<\p>

  4. #define SD_CS 3

  5. #define SD_INS 4

  6. #define SD_WP 5

Теперь поподробнее рассмотрим инструментарий:

1) FRESULT pf_mount (FATFS* FileSystemObject) Монтирует файловую систему FAT, где указатель FileSystemObject указывает на объект файловой системы. Возвращает FRESULT:

  1. typedef enum {

  2. FR_OK = 0, //Все хорошо

  3. FR_DISK_ERR, //Ошибка диска

  4. FR_NOT_READY, //Диск не готов

  5. FR_NO_FILE, //Нет файла

  6. FR_NO_PATH, //Неправильный путь

  7. FR_NOT_OPENED, //Файл не открыт

  8. FR_NOT_ENABLED, //Раздел не смонтирован

  9. FR_NO_FILESYSTEM //На диске не имеется файловой системы FAT

  10. } FRESULT;

А точнее, возвращает только FR_OK, FR_NOT_READY, FR_DISK_ERR, FR_NO_FILESYSTEM

2) FRESULT pf_open (const char* FileName)
Открывает файл для подальших операций чтения/записи. Указатель FileName указывает на строку – путь к файлу. Возвращает FR_OK, FR_NO_FILE, FR_NO_PATH, FR_DISK_ERR, FR_NOT_ENABLED.

3) FRESULT pf_read (void* Buffer, WORD ByteToRead, WORD* BytesRead)
Считывает ByteToRead байт в область, на которую указывает Buffer из открытого файла. BytesRead – количество считанных байт, если BytesRead< ByteToRead, то был достигнут конец файла. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED, FR_NOT_ENABLED.

4) FRESULT pf_write (void* Buffer, WORD ByteToWrite, WORD* BytesWritten) Пишет ByteToWrite байтов из буфера, на который указывает Buffer в открытый файл. BytesWritten – указатель на число записанных байт. Записанные в файл данные следует финализровать. Вот так:

  1. pf_open (path); //Открываем файл

  2. pf_write(buff, btw, &bw); //Пишем

  3. pf_write(buff, btw, &bw); //Еще пишеи

  4. pf_write(0, 0, &bw); //Финализируем

Эта функция имеет существенные ограничения. Нельзя с ее помощью создать файл, нельзя увеличить размер файла (в секторах), может стартовать только с начала сектора, не учитывает файловый атрибует read-only. Если *BytesWritten < ByteToWrite, то это означает что был достигнут конец файла и дальше писать данные невозможно. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED, FR_NOT_ENABLED.

5) FRESULT pf_lseek (DWORD Offset)
Устанавливает курсор для операции чтения внутри открытого файла в позицию Offset. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED

6) FRESULT pf_opendir (DIR* DirObject, const char* DirName)
Открывает директорию c путем на который указывает DirName в объект, на который указывает DirObject. Структура DirObject может быть в любое время уничтожена. Возвращает FR_OK, FR_NO_PATH, FR_NOT_READY, FR_DISK_ERR, FR_NOT_ENABLED.

7) FRESULT pf_readdir (DIR* DirObject, FILINFO* FileInfo)
Считывает содержимое директории по указателю DirObject в структуру по указателю FileInfo. Возвращает FR_OK, FR_DISK_ERR, FR_NOT_OPENED.

Имея описанный выше инструментарий, мы можем почитать/пописать данные на карту SD|MMC на уровне файлов:

  1. #include<\p>

  2. #include<\p>

  3. #include<\p>

  4. #include “pff.h”

  5. #include “diskio.h”

  6. #include “integer.h”

  7. //——————————————————————

  8. char read_buf[128]={};

  9. char write_buf[128]={'w','r','i','t','e',' ','o','k','
    ','
    ',0x00};

  10. //——————————————————————

  11. void uart_init(void)

  12. {

  13. UBRRH = 0x00; //Установим скорость UART 256000 bit/sec

  14. UBRRL = 0x01;

  15. UCSRA = 0x00;

  16. UCSRB = (1

Источник: https://avrlab.com/node/167

Звонок и MMC/SD карта памяти

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

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

Самым простым решением для воспроизведения голоса и полифонии является проигрывание содержимого wav-файла через ЦАП или ШИМ-модулятор. Первый вариант проще в реализации (благо, выводов у меги8 для 8-битного ЦАПа хватает), и не требует последующей фильтрации сигнала.

Поскольку wav-файлы занимают много места, для их хранения нужен достаточно емкий носитель. Но с бурным развитием индустрии флеш-памяти и флеш карт этот вопрос решается сам собою. Для хранения мелодий удобно использовать карты памяти MMC и SD/SDHC, т.к.

эти карты поддерживают режим низкоскоростного доступа (для наших целей его будет более, чем достаточно) совместимый и шиной ISP, что сильно упрощает программную реализацию.

Еще один большой плюс использования карт памяти – их всегда можно перезаписать на компьютере.

Вообщем, вопрос остается только в представлении данных на карте: можно хранить мелодии в виде файлов стандартной файловой системы (например FAT16), а можно придумать свой двоичный формат.

В первом случае для записи мелодий будет достаточно только компьютера с картридером, но при этом сильно усложняется написание управляющей программы для звонка – библиотеки, реализующие работу с FAT16 существуют уже готовые, но они требуют ощутимо много памяти – потребуется МК с минимум 16КБ Flash-а и 1..2КБ RAM.

Во втором случае разработка звонка значительно упрощается, но для записи мелодий на компьютере пришлось бы разрабатывать специальный драйвер, для поддержания нестандартной “файловой системы”, иначе, операционная система просто не распознала бы карту (последнее относится только к Windows, в *nix-системах запись “сырых” даных на накопитель никаких проблем не вызывает).

Но существует еще третий, компромиссный вариант: на карте памяти создается один большой файл, в начале которого записывается некая сигнатура, после которой помещаются собственно “сырые” данные.

Управляющая программа просто находит эту сигнатуру, вместо того, чтобы анализировать FAT и собирать файл “по кусочкам”, и может считывать нужные данные просто пропустив блоки MBR и FAT.

Тогда для замены мелодий достаточно перезаписать один файл, созданный специальным компилятором. Этот вариант и был реализован.

Схема цифровой части получившегося звонка показана на рисунке.

Микроконтроллер ATMega8 включен по типовой схеме, к его порту D подключен ЦАП на резисторах R3 ..R10, R11 ..R17, с выхода которого (на схеме AUDIO OUT) сигнал поступает на усилитель.

Карта памяти вставляется в разъем X1 и питается через стабилизатор IC2. Вывод PC5 МК подключен к джамперу, который используется загрузчиком кода прошивки. Выводы PC0..

PC4 остались незадействованы, поэтому, подключил к ним светодиоды (анодами к МК и катодами на землю), мигающие в зависимости от проигрываемой ноты или амплитуды аудиофрагмента.

К выводу PB0 подключается кнопка “застопоривания” смены мелодий: если она не нажата, мелодии будут проигрываться одна за другой, по списку, если нажата – будет зациклена последняя проигрываемая мелодия. Вывод PB1 управляет реле, которая самоблокирует кнопку звонка.

В схеме использован кварц на 11059200Гц, в случае использования другого резонатора, надо изменить значение F_CPU в файле config.h.
Схема аналоговой части состоит из усилителя на основе TDA2003, включенной по типовой схеме и стабилизаторе 5В.

Транзистр T1 управляет реле, подключаемой к выводам R1.1, R1.2, вход X1 подключается к выходу PB1 МК. Вход INP подключается через переменный резистор (10 КОм) – регулятор громкости к выходу ЦАП (крайние выводы резистора идут на AUDIO OUT и землю, а вертушек – на INP).

Схемы и разводка печатных плат прилагаются (Eagle). Надо заметить, что на плате аналоговой части микросхемы устанавливаются на общий радиатор, который одновременно соединяет их земли. Это позволило развести одностороннюю плату без перемычек.

В качестве корпуса я использовал бокс для двух выключателей-автоматов.

Слот для MMC-карты был вырезан из разъема для картриджа от игровой приставки, Кнопку звонка я вывел на лицевую панель (она идет в параллель той кнопке, что находится в подъеде) для того, чтобы можно было выбирать мелодию циклическим переключением. Так же для удобства добавил кнопку, отключающую звук. Вместо 5 светодиодов, как нарисовано на схеме, sподключил только 4 – разных цветов.

Внешний вид того, что получилось.

Внутренности

В корпусе

Пару слов о резисторах R3 .. R10, R11 .. R17: их надо выбрать с минимальными отклонениями сопротивления и соотношением 2:1. Я отбирал их из 50шт каждого номинала так, чтобы отклонение сопротивления не превышало процента.

Для компиляции мелодий написана утилита MelodyFlasher. Перед первой записью надо иннициализировать карту памяти – отформатировать ее и создать файл данных, занимающий все доступное место. Это делается следующей командой:

mf i -d

Программа позволяет формировать мелодии из нотных фрагментов, записываемых в формате мелодий для телефонов Motorola T720. Для прослушивания и редактирования мелодий удобно использовать программу Melody Player for Motorola, в ней есть большая база разных мелодий.

Аудиофрагменты надо преобразовать в формат WAV, обязательно нежатый и монофонический. Гарантированно получить читаемый формат можно средствами “Звукозаписи” в Windows. После подготовки всех фрагментов создается файл списка (в прилагаемом примере – melodies.lst).

В каждой строке этого файла описывается одна мелодия, которая может состоять из любого числа .mel-фрагментов (нотная запись) и .wav-фрагментов, имена файлов этих фрагментов просто записываются в одну строку через запятую (см. прилагаемый пример).

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

Компиляции мелодий и их запись на карту производится следующей командой:

mf u -d -f

Прошивка и все исходники прилагаются. Для работы с MMC/SD использована библиотека от Roland Riegel, которая была несколько модифицирована.

Файлы:

Прошивка (кварц на 11.0592 МГц)

Прошивка (кварц на 12 МГц)

Исходники прошивки

Схема и плата для Eagle

MelodyFlasher

Исходники MelodyFlasher

Пример мелодий

Все вместе одним архивом

Еще один вариант печатной платы для SMD-компонент был прислан читателем. Плата выглядит так:

Плата для SMD (Sprint Layout)

Источник: http://trolsoft.ru/ru/sch/zvonokmmc

AVR: Воспроизведение PCM-аудио с SD-карты

Собственно, выкатываю обещанную ранее статью. Надо сказать, что этот проект оказался чрезвычайно востребован (сам удивляюсь) — разработанные модули пошли аж в два устройства, и вроде как случится третье. Так что не явить их в блоге было бы вдвойне неправильно. 

Для начала, наверное, стоит рассказать о том, как вообще выводить звук с микроконтроллера. Тема эта, в общем, рассмотрена достаточно и много где, но я все равно пробегусь по ней, чисто для порядка. А то статья окончательно выродится в справку по двум программным модулям.

Вывод звука — суть цифро-аналоговое преобразование. Осуществить оное можно разными способами —  поставить R-2R матрицу, взять готовую микросхему ЦАП, использовать ШИМ-вывод.

Нам интересен последний, ибо он позволяет обойтись наименьшим количеством внешних компонентов, а заодно и создает некоторые предпосылки для получения выского КПД усилителя ЗЧ (если цель — подключение динамика, а не вывод на линейный выход, например).

Про ШИМ (в частности, применительно к AVR) я уже писал. Вкратце, замечателен он тем, что среднеквадратичное значение такого сигнала пропорционально его коэффициенту заполнения.

Соответственно, если с частотой выборок интересующего оцифрованного сигнала устанавливать значения коэффициента заполнения, пропорциональные собственно значениям выборок, среднеквадратичное значение ШИМ-сигнала в зависимости от времени будет представлять собой воспроизводимый сигнал.

Останется только отфильтровать несущую частоту ШИМ (и заодно частоту дискретизации), и на выходе получим аналоговый сигнал из цифрового. В случае с динамиком можно вообще ничего не фильтровать — инерция механики сделает все сама. Тогда усилитель может работать в ключевом режиме и, соответственно, иметь высокий КПД.

Базовые соотношения:

  1. Теорема Котельникова-Найквиста: частота дискретизации должна быть как минимум в два раза выше максимальной частоты воспроизводимого сигнала.
  2. Эмпирическое соотношение: частота ШИМ-несущей должна быть как минимум в два раза выше частоты дискретизации.

Железо

Как уже сказано выше, прелесть выбранного способа в том, что из внешних компонентов требуется всего ничего — встроенный блок генерации ШИМ есть почти во всех контроллерах. Если хотим линейный выход, ставим RC-фильтр с частотой среза, определяемой теоремой Котельникова-Найквиста.

Лучше бы, конечно, поставить фильтр второго порядка, но в смысле прослушивания звука сойдет и простая RC-цепь. Если хотим мощный выход на динамик — ставим ключевой каскад.

Мне по ТЗ нужен был именно динамик, так что я поставил ключевой каскад (схема в лучшем качестве есть в файлах проекта):

Схема спроектирована с учетом перспектив сборки в разных конфигурациях. Без изменения печатной платы возможно изготовить следующие варианты:

1. Основную схему, которая ушла заказчику.

  • не установлены: C2, DA1, Q5, Q6;
  • установлена перемычка R2;
  • R5, R6, R7 заменены перемычками, сигнал снимается с выхода PWMA.

В такой конфигурации она питается от батареи напряжением 3В (требование SD-карты) и теоретически  способна выдавать в нагрузку мощность где-то до ватта. Реально же измеренный средний ток в рабочем режиме составляет 30 мА (нагрузка 16 Ом).

2. При желании, схему можно умощнить:

  • установить Q5 и Q6, R5/R6/R7 указанных номиналов, снимать сигнал с PWMB;
  • поднять напряжение питания, убрать перемычку R2 и установить стабилизатор и C2. В этом случае предельная мощность в динамике будет определяться исключительно нагрузочной способностью выходных транзисторов и может составлять десятки ватт. Возможно, потребуется подобрать R6 для обеспечения оптимального режима работы выходного каскада (этот резистор задает защитный интервал, без него каскад при серьезной нагрузке испустит дым).

Очень рекомендуется соединить верхний по схеме вывод R3 с землей через резистор сопротивлением где-то 10K. Это убережет Q2 от работы в режиме с оторванной базой в то время, пока контроллер еще не настроил ножку OC0B на выход. Виноват, изначально проглядел это, проект делался в спешке. В принципе, работать будет и так, но лучше подстраховаться.

Динамик подключается через конденсатор емкостью около 22 мкФ, монтируемый навесом. Этого вполне хватает.

К выводу RDSW оригинально подключался геркон. Когда контакты RDSW замкнуты, схема отключена.

Распиновка разъема SD-карты не приведена на схеме, так как определяется программно — в этом проекте я использовал свой драйвер для SD-карты в исходном виде, с программным SPI. Да, в ATmega48 есть аппаратный SPI, но программный удобнее и его скорости вполне хватает, так что я использовал его.

Плата оттрассировалась на одном дыхании. Уложился в одну сторону:

Софт

Итак, мы собираемся играть PCM-звук с SD-карты. Начнем с первой части, про звук.

Воспроизведение звука

Для воспроизведения звука был написан отдельный модуль (pcm_out.c + pcm_out.h), реализующий настройку двух таймеров (один для генерации ШИМ, другой для отсчета временных интервалов дискретизации), вывод восьмибитных выборок в прерывании, а также двойную буферизацию. Извне модуля доступны две функции и две переменные:

//Enables audio output #define PCM_ENABLED 0x01 //Gets set when entire buffer was played #define PCM_BUFFER_ENDED 0x02 //Istructs system to flip buffers when curent buffer ends #define PCM_ENABLE_AUTOFLIP 0x04 //Gets set when 3/4 of buffer was played #define PCM_BUFFER_ENDS 0x08 extern volatile uint8_t PCM_status; //Buffer to write PCM data to extern uint8_t volatile * volatile PCM_write_buffer; //Initialize PCM module void PCM_Init(void); //Flip internal buffer with the new one void PCM_FlipBuffers(void);

PCM_Init(), как нетрудно догадаться, инициализирует все, относящееся к выводу звука. Далее работа с модулем выглядит так:

  • чтобы запустить воспроизведение буфера, устанавливаем в PCM_status бит PCM_ENABLED;
  • пока воспроизводится теневой буфер, пишем новые данные в PCM_write_buffer; когда буфер кончится, в PCM_status будет установлен бит PCM_BUFFER_ENDED;
  • тут мы можем вызвать PCM_FlipBuffers(). Эта функция меняет буфера местами, и мы снова можем писать в PCM_write_buffer, уже новый, пока воспроизводится тот, в который мы писали в предыдущий раз;
  • tсли взвести PCM_ENABLE_AUTOFLIP, то после окончания теневого буфера он будет автоматически заменен на новый, но только один раз, после чего этот флаг будет сброшен.

Настройки — период частоты дисктетизации в тиках таймера и размер буфера в байтах. Буферов два, так что занимаемый объем памяти будет в два раза больше.

//16 KHz @ 8 MHz main clock #define PCM_SAMPLE_PERIOD 0x01F4 //8 msec of audio at 16 KHz sample rate #define PCM_BUFSIZE 128

На всякий случай, формат воспроизведения, разумеется, unsigned 8-bit PCM.

Со звуком все.

Доступ к SD-карточке

Вот с этого момента начинатся самое интересное. По крайней мере, для меня.

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

Эту проблему я решил следующим образом: на карточку кладется файл, по сути своей представляющий упрощенную файловую систему (я назвал ее minifs). В начале оного находится сигнатура, по которой контроллер может найти его при посекторном чтении карточки.

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

Очень удобно — находим начальное смещение по сигнатуре и читаем подряд, обходя FAT, но не конфликтуя с ним.

Таким образом, у нас имеется отдельная файловая система для МК, которая видится на компьютере как файл и, таким образом, легкодоступна для редактирования.

Сам файл minifs представляет собой склеенные блоки данных — «внутренние» файлы — и имеет формат, описанный ниже (все смещения, разумеется, выровнены по границе 512 байт, физическому размеру сектора карточки).

В самом начале файла (смещение 0х00000000) расположена сигнатура — строка «MINIFS v1.0 FILE STARTS HERE»:

0x4D 0x49 0x4E 0x49 0x46 0x53 0x20 0x76 0x31 0x2E 0x30 0x20 0x46 0x49 0x4C 0x45 0x20 0x53 0x54 0x41 0x52 0x54 0x53 0x20 0x48 0x45 0x52 0x45 0x00 0x00 0x00 0x00

Выше показана сигнатура в шестнадцатеричном виде с учетом выравнивания по границе 16 байт. Последующие 480 байт сектора (до границы следующего блока в 512 байт) зарезервированы.

Далее (со смещения 0х00000200) расположена таблица смещений файлов внутри minifs. Она состоит из четырехбайтных записей. Первая запись содержит количество файлов в minifs, последующие указывают на смещения концов файлов. Т.е., вторая запись указывает, где начинается второй файл / кончается первый, и т.п. Все смещения выровнены по границе 512 байт. Максимальный размер таблицы — 512 байт.

Дальше (со смещения 0х00000400) начинаются непосредственно данные — первый файл в minifs.

Кстати, формат minifs, как легко видеть, очень напоминает FAT. Только очень упрощенный.

Источник: https://embedderslife.wordpress.com/2013/12/27/wav-avr-sd/

Ссылка на основную публикацию
Adblock
detector