라즈베리파이 피코와 오디오 모듈을 조합해서 wav 파일을 재생하는 기능을 구현하는게 가능합니다.
우선 하드웨어 설비는요.
라즈베리파이 피코1 1대
오디오 모듈 1대
모듈을 간편하게 다는 확장 보드 1대 (보조전원 있는 것으로)
스피커 한쌍
UART TO USB 1대
USB 메모리 1개
USB micro male, USB-A female 케이블 1개
USB-A male, USB micro male 케이블 1개
중요한 작동은 (1) 외부 저장장치에서 wav 파일을 읽어오는 작동 (2) wav 파일을 링버퍼에 두고 offset 만큼 갱신하면서 읽어오는 작동 (3) I2S 초기화 (4) 읽어온 링버퍼를 I2S에 쓰기 동작입니다.
대충 설명해봅니다.
(1) 보조기억장치에서 wav 파일을 읽어오는 작동은 우선 여러 보조기억장치가 가능하지만, USB 메모리를 써서 USB MSC를 구현하여 host로 작동하게 하면 편리합니다. 라즈베리파이 피코를 대용량 USB 장치를 제어하는 일종의 허브로 만들어 USB 메모리를 탈착할때마다 인식해서 wav 파일에 액세스를 하게 합니다. 이는 TinyUSB를 쓰면 간단히 구현할 수 있구요. TinyUSB로 구현하면, 마운트, 마운트 해제, 작업이 끝나고나서 실행하는 콜백 정도만 코딩해주면 USB 메모리를 읽어오는 작업이 쉽게 가능해져서 좋습니다.
참고로 내장 플래시를 쓸수도 있는데 이는 추천을 안합니다. wav 파일이 3분 가량의 노래라면 거의 30MB인데, 라즈베리파이 피코 내장 플래시는 2MB밖에 안되고 BOOTSEL 작동시 내용물이 휘발되므로 USB 메모리를 씁니다. 라즈베리파이 피코에 내장된 USB 포트에 장착해야 해서, 모듈을 간편하게 다는 확장 보드 1대를 준비해두면 좋구요. 확장 보드에 보조전원 연결이 가능한게 좋습니다.
USB 메모리 말고, microSD 카드도 대안인데요. 이를 위해서는 카드마다 특성을 탈 수 있어서 전 USB 메모리로 대용했습니다. FatFS 설정을 잘 봐야 하고 모듈도 고장이 안난 제품이어야 하고 핀설정도 챙겨야 하는데 이게 잘 되어 있어도 어떤 이유에서인지 액세스가 불가능하네요. FAT32나 exFAT으로 포맷해도 안되던데 어느 프로젝트 저자는 microSD 카드의 브랜드 탓이라고도 하던데 반드시 microSD여야 한다면 깃헙에서 검색해보시고 기존의 프로젝트에서 작동성공으로 보고한 브랜드로 쓰시면 될 것 같습니다.
그다음으로 (2) wav 파일을 링버퍼에 두고 offset만큼 읽어오는 작동은 FatFS과 연동해서 구현하면 편합니다. FatFS는 마이크로콘트롤러 업계에서 표준처럼 된 파일시스템 관련 라이브러리로 설정 매크로를 지정해주고나서 보조기억장치에 파일을 읽고 쓰고 디렉토리를 만드는 등의 기능을 쉽게 구현하게 해주는 고마운 라이브러리입니다. 우선 RIFF WAVE 형식으로 된 16비트, signed, 44.1kHz의 wav 파일을 준비하고 USB 메모리에 저장해서 (1)이 구현된 전제 하에 라즈베리파이 피코에 장착해보세요. 잘 구현된 조건이면 USB 메모리가 인식이 될 것이구요. 제일 중요한 링버퍼 구현은 루프를 돌려서 USB 메모리의 wav파일을 f_read()함수로 읽어들이되, eof에 도달할때까지 offset을 일정하게 늘려가면서 반복해서 f_read()로 읽어옵니다. 이 읽어오는 작동과 (4) 읽어온 링버퍼를 I2S에 쓰기동작을 반복하면 재생이 될 것입니다.
링버퍼는 큐로 보통 구현하구요. left값을 갱신하면서 pop과 push를 해가면서 I2S에 쓰기동작하면서 조금씩 소비하고 조금씩 읽어들이면 됩니다.
링버퍼가 효율적으로 작동한다면, 오디오 모듈에 읽어들인 파일 스트림을 보내서 재생을 하게 해야 합니다. 오디오 하드웨어 제어에서 제일 중요한 (3) I2S 초기화는 PIO를 통해서 합니다. 보통 라즈베리파이 재단에서 제공하는 pico-extras에 포함된 audio_i2s 를 그대로 가져와서 쓰거나 개량해서 합니다. 라즈베리파이 피코 표준에서 제시하는 pioasm 문법을 써서 일종의 어셈블리 코드처럼 명령을 기재하고 C SDK에 바인딩한 초기화 함수를 정의하는 과정입니다. audio_i2s의 종류가 여럿인데, 라즈베리파이 피코 재단에서 만든 원본이 있고, 깃헙 등지에서 찾아보면 재생 사양을 더 높힌 수정본이 있습니다. 원본은 16비트 signed, 44.1kHz 재생이 무리가 없다고 알려져 있네요. 원본이든 수정본이든 하나를 택해 (3) I2S 초기화를 해두고 (2) 링버퍼가 작동 될때 (4) I2S 쓰기 동작을 루프돌려가며 반복 실행합니다.
(2)를 할때의 선택사항은 RIFF WAVE 파일의 앞부분에 있는 헤더를 읽어와서 정보를 처리할지 아니면 헤더를 과감히 스킵만 할 것인지의 여부인데요. 이미 파일 정보를 알고 있는 wav 파일 하나만 재생하는 경우 헤더를 안읽고 이미 아는 정보를 소스코드에 수동으로 기재하면 코드가 간단해집니다. 헤더에 기재된 샘플링 레이트와 같은 사양을 이미 알고 있다면 그냥 헤더 구간의 시작부터 끝만큼 스킵해서 오디오 데이터를 즉시 접근하면 코드가 간단해질 수 있습니다. 그러나 랜덤으로 저장한 여러파일을 골라서 재생한다면 헤더를 읽어서 처리하는 코드도 만드는게 필요하네요.
잘 이해되게 기술한지는 모르지만 아래와 같은 순서로 프로그램 작성이 되면 됩니다.
(1) USB MSC를 host로 작동하게 돌림 (TinyUSB로)
(2) I2S를 초기화 함 (PIO 이용)
(3) 링버퍼에 wav 파일을 읽어들임 (FatFS 이용)
(4) I2S에 쓰기 작동 (PIO 이용)
이 순서네요.
여기에
(5) LCD 모듈에 메뉴를 표시하고 버튼을 눌러 선택한 음악파일만 재생
이 기능도 도전과제입니다.