В этом посте показано, как я сделал простой проигрыватель аудиофайлов с помощью микроконтроллера PIC16F877A, где аудиофайл хранится на 8-гигабайтной карте micro SD.
Я использовал аудиофайл с частотой 8000 Гц, 8-битный стереофонический (2 канала), который я преобразовал из формата MP3 в формат WAV с помощью бесплатной программы с открытым исходным кодом под названием Audacity (сайт: http://www.audacityteam.org/).
Мы будем использовать компилятор CCS C (CCS PIC-C Compiler), а не поставляемый Microchip компилятор XC8, и полагаться на библиотеки, предоставляемые CCS.
Поэтому чтобы скомпилировать код С, указанный ниже, нужно установить CCS PIC-C Compiler версии 5.051.
Скачать CCS PIC-C Compiler версии 5.051 (устанавливал на Windows XP):
– в rar архиве.
– в tar.gz архиве.
Скачать готовый HEX файл, С файл и библиотеку:
– в zip архиве.
– в tar.gz архиве.
Требуемое оборудование:
Микроконтроллер PIC16F877A
SD-карта (отформатирована в формате FAT16 или FAT32)
Регулятор напряжения ASM1117 3.3
Усилитель звука (например, динамик для ПК, LM386 ….)
Динамик
Кварцевый генератор частотой 20 МГц
2 керамических конденсатора по 22 пф
резистор 3 x 3,3 К Ом
резистор 3 x 2,2Ком
резистор 2 x 10 Ком
резистор 2 x 1 Ком
поляризованный конденсатор 3 x 10 мкФ
Керамический конденсатор емкостью 100 нф
Источник питания 5 В
Макетная доска
Соединительные провода
Схема:
Микроконтроллер генерирует звук с использованием технологии PWM, если аудиофайл wave является монофоническим (1 канал), микроконтроллер будет генерировать только 1 ШИМ-сигнал (PWM1), и, следовательно, мы будем слышать звук только из 1 динамика. Если аудиофайл wave является стереофоническим, звук будут воспроизводить оба динамика.
Код на языке Си:
Приведённый ниже код на C был протестирован с помощью компилятора CCS версии 5.051.
В этом проекте я использовал библиотеку FAT (FAT16 и FAT32), исходный код которой можно найти в следующей теме:
Библиотека FAT SD-карты для компилятора CCS C:
– cкачать библиотеку FAT SD-карты в формате rar
– cкачать библиотеку FAT SD-карты в формате tar.gz
Я протестировал этот проект с картами micro-SD FAT32 8 ГБ и FAT16 2 ГБ.
Имя аудиофайла, который я использовал, было mywav (mywav.wav с расширением), частота дискретизации — 8000 Гц, 2 канала (стерео).
Сначала я инициализировал SD-карту с помощью функции sdcard_init(); эта функция возвращает 0, если инициализация прошла успешно, и ненулевое значение, если произошла ошибка. После инициализации SD-карты я инициализировал файловую систему FAT с помощью функции fat_init();, а затем открыл аудиофайл с заранее выбранным именем mywav.wav. Все три предыдущие функции возвращают 0 в случае успешного выполнения и ненулевое значение в случае ошибки.
Если инициализация SD-карты, системы FAT и открытие файла прошли успешно, это означает, что переменная ok = 0 и воспроизведение аудиофайла начинается с помощью функции play();.
Чтобы определить, является ли wav-файл монофоническим (1 канал) или стереофоническим (2 канала), микроконтроллер считывает байт 22 wav-файла с помощью функции :
sdcard_read_byte(адрес_указателя + 22, &количество_каналов);
где переменная address_pointer относится к библиотеке FAT, эта переменная позволяет мне узнать начальный адрес аудиофайла в формате wave.
Если аудиофайл в формате wave монофонический, то channel_count = 1, а если стереофонический, то channel_count = 2.
Я установил буфер данных на 16, чтобы каждый раз микроконтроллер считывал 32 байта с SD-карты. Буфер данных может быть меньше или больше 16.
Функция fat_read_data(16, data) продолжает считывать данные с SD-карты до тех пор, пока не вернёт 1, что означает, что достигнут конец файла.
Файл с волновой функцией должен быть 8-битным, и для этого я настроил выходы ШИМ на максимальную частоту с 8-битным разрешением. Для этого я настроил модуль Timer2, как показано ниже:
setup_timer_2(T2_DIV_BY_1, 63, 1);
Разрешение ШИМ-сигнала можно рассчитать с помощью функции:
Разрешение ШИМ = Log[(PR2 + 1)*4]/Log(2) = Log[(63 + 1)*4]/Log(2) = 8
Частота ШИМ должна быть как можно выше, и при предыдущей конфигурации я получил частоту ШИМ 78,125 кГц. Это можно рассчитать с помощью приведенной ниже функции:
PWM_Frequency = Fosc/[(PR2 + 1)*4*TMR2_Prescaler] = 20*10^6/[(63 + 1)*4*1] = 78,125 кГц.
Если количество_каналов = 2второй рабочий цикл ШИМ также будет обновлен, и звук будет генерироваться на выходах ШИМ1 (RC2) и ШИМ2 (RC1) (левый и правый).
Теперь о том, как я использовал модуль Timer1 и частоту дискретизации волнового файла (8000 Гц):
циклы ШИМ должны обновляться каждые 125 мкс ( = 1/8000 Гц), для этого я использовал Timer1, чтобы микроконтроллер ждал 125 мкс. В этом примере я не использовал прерывание Timer1.
Я настроил модуль Timer1 на увеличение на каждый цикл микроконтроллера (0,2 мкс) и вычисление значения Timer1 (значения между двумя обновлениями) Я использовал функцию:
Fosc/[частота дискретизации * 4) = 20 * 10^6/(8000 * 4) = 625
где частота дискретизации = 18000, а Fosc = 20 * 10^6.
В этом примере я использовал значение 500 вместо 625, потому что у меня была медленная потоковая передача звука (то есть некоторые инструкции тратились на циклы).
Полный код на C приведён ниже.
————————————–
При компиляции нижеприведённого кода выдавало ошибку – решил следующим образом:
const int8 *wav = «mywav.wav»;
Измените эту строку на следующую:
int8 wav[] = "mywav.wav";
Затем он скомпилировался без ошибок или предупреждений.
————————————–
// WAV Player using PIC16F877A microcontroller and SD card CCS C code. // SD Card module connections #define SDCARD_SPI_HW #define SDCARD_PIN_SELECT PIN_D3 // End SD card module connections #include <16F877A.h> #fuses HS,NOWDT,NOPROTECT,NOLVP #use delay(clock = 20MHz) #use fast_io(D) #include <FAT_Lib.c> const int8 *wav = "mywav.wav"; int1 ok = 0; int8 i, j, data[16], channel_count; void play(){ sdcard_read_byte(address_pointer + 22, &channel_count); // Read number of channels while(fat_read_data(16, data) == 0){ for(i = 0; i < 16; i++){ set_timer1(0); j = data[i]; set_pwm1_duty((int16)j); // Update PWM1 duty cycle if(channel_count == 2){ // If 2-channel wave file (stereo) i++; // increment i j = data[i]; set_pwm2_duty((int16)j); // Update PWM2 duty cycle } while(get_timer1() < 500); // Wait some time (about 125us) to update the duty cycles } } } void main(){ delay_ms(2000); setup_ccp1(CCP_PWM); // Configure CCP1 as a PWM setup_ccp2(CCP_PWM); // Configure CCP2 as a PWM set_pwm1_duty(0); // set PWM1 duty cycle to 0 set_pwm2_duty(0); // set PWM2 duty cycle to 0 setup_timer_2(T2_DIV_BY_1, 63, 1); // Set PWM frequency to maximum with 8-bit resolution setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); ok |= sdcard_init(); // Initialize the SD card module ok |= fat_init(); // Initialize FAT library ok |= fat_open_file(wav); // Open the wave file if(ok == 0){ play(); } set_pwm1_duty(0); // set PWM1 duty cycle to 0 set_pwm2_duty(0); // set PWM2 duty cycle to 0 } // End