Проигрыватель WAV с использованием PIC16F877A и SD-карты

В этом посте показано, как я сделал простой проигрыватель аудиофайлов с помощью микроконтроллера 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

Добавить комментарий