概要
CH32マイコンを用いてFRISKのケースに収まる大きさのUSB DAC開発ボードを作成しました。32bit/384kHzに(のみ)対応しています。
はじめに
CH32V305/307にはUSB-HS PHYが搭載されており、これを用いるとUSB周りの外付け部品なしでUAC2.0によりハイレゾ音源を再生できるようになります。今回は秋月電子で取り扱いが開始されたCH32V305FBP6を用いたシンプルなUSB DAC開発ボードを作成しました。
USB DAC開発ボードの作成
今回はプログラムの動作確認を行うために必要最小限の機能を備えることを目的とし、CH32V305FBP6にPCM5102Aを直接接続しただけのシンプルな構成を採用しました。 あまりにもシンプルな回路なので、持ち運びしやすいように仕上げることとしました。具体的には、FRISKのケースに収まるように基板を設計しました。
プログラムの準備
公式githubにUSB Audioのサンプルは執筆時点で公開されていませんが、解説記事とともにサンプルプログラムを公開されている方がいるため、こちらを参考にプログラムを改変していきます。
今回作成した回路のクロックは16MHzであるため、まずはUSB周りのクロックの設定を変更します。こちらは先に示した記事にある通りにch32v30x_usbhs_device.c
内にあるUSBHS_RCC_Init()
関数の内容を次のように分周比が2となるように変更します。
RCC_USBHSPLLCLKConfig( RCC_HSBHSPLLCLKSource_HSE ); RCC_USBHSConfig( RCC_USBPLL_Div2 ); RCC_USBHSPLLCKREFCLKConfig( RCC_USBHSPLLCKREFCLK_8M ); RCC_USBCLK48MConfig( RCC_USBCLK48MCLKSource_USBPHY ); RCC_USBHSPHYPLLALIVEcmd( ENABLE ); RCC_AHBPeriphClockCmd( RCC_AHBPeriph_USBHS, ENABLE );
次にI2S周りのクロックの設定を変更します。system_ch32v30x.c
内にあるmyI2S_SetSysClock()
関数の内容を次のように予め用意されてある16MHz向けのものに変更します。
#if 1 //384K 32bit MCK DISABLE HSE 16M u32 temp=0; //HSE temp=RCC->CTLR; temp|=RCC_HSEON; RCC->CTLR = temp; while(RCC->CTLR & RCC_HSERDY==0); temp=RCC->CFGR0; temp&=(u32)~0x3FF0;//AHB APB2 APB1 temp|=(u32)0x3D0002;//PLL-16 HSESource PLLSource RCC->CFGR0=temp; RCC->CFGR2=0x10000|0x40|0x0A00|0x04;//DIV2-5 PLL2-12 DIV1-5 temp=RCC->CTLR; temp|=(1<<26); RCC->CTLR=temp; while( (RCC->CTLR & (1<<27)) == 0 );//PLL2READY temp=RCC->CTLR; temp |= (1 << 24); RCC->CTLR=temp; while( (RCC->CTLR & (1<<25)) == 0 );//PLLREADY while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08); #endif #if 0 //384K 32bit MCK DISABLE HSE 24M u32 temp=0; //HSE temp=RCC->CTLR; temp|=RCC_HSEON; RCC->CTLR = temp; while(RCC->CTLR & RCC_HSERDY==0); temp=RCC->CFGR0; temp&=(u32)~0x3FF0;//AHB APB2 APB1 temp|=(u32)0x3D0002;//PLL-16 HSESource PLLSource RCC->CFGR0=temp; RCC->CFGR2=0x10000|0x40|0x0600|0x04;//DIV2-5 PLL2-8 DIV1-5 temp=RCC->CTLR; temp|=(1<<26); RCC->CTLR=temp; while( (RCC->CTLR & (1<<27)) == 0 );//PLL2READY temp=RCC->CTLR; temp |= (1 << 24); RCC->CTLR=temp; while( (RCC->CTLR & (1<<25)) == 0 );//PLLREADY while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08); #endif
次にmain.c
内にGPIOの設定を行う関数MyGPIO_Init()
を作成します。
void MyGPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure={0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_WriteBit(GPIOC, GPIO_Pin_6, Bit_RESET); GPIO_WriteBit(GPIOC, GPIO_Pin_7, Bit_RESET); GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); GPIO_WriteBit(GPIOC, GPIO_Pin_9, Bit_RESET); }
このとき、同じくmain.c
内にあるI2S_DMA_Init()
関数にてI2S周りの設定が記述されていますが、今回はMCLKを出力しないため、該当する部分をコメントアウトします。
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO_Init(GPIOC, &GPIO_InitStructure);
最後にmain.c
内にあるmain
関数内の内容を次のように書き換えてプログラムの準備は終了です。ここでは不意にプログラムがコケたときのために、無限ループ内に1秒周期でLEDが光るようにしています。
int main(void) { NVIC_InitTypeDef NVIC_InitStructure = {0}; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Delay_Init(); MyGPIO_Init(); I2S_DMA_Init(); USBHS_RCC_Init( ); USBHS_Device_Init( ENABLE ); NVIC_InitStructure.NVIC_IRQChannel = USBHS_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); SetVTFIRQ((u32)USBHS_IRQHandler,USBHS_IRQn,1,ENABLE); while(1) { Delay_Ms(500); GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); Delay_Ms(500); GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); } }
感想
基板を起こしたことを除くとサンプルプログラムをほぼそのまま動かしたような形になりましたが、あっさりとハイレゾ対応のUSB DACが作れてしまいました。主要部品が秋月電子で揃えられるという手軽さのインパクトを強烈に感じますがいかがでしょうか。
サンプルプログラムでは32bit/384kHzにのみ対応しており、また不意に不調となる場合があるため、USBの勉強をしながら適切な割り込み処理や様々なレートに対応できるようにしたいです。
補足
44.1kHz系のレートに対応できるか検討したところ逓倍率の上手い設定が無いらしく厳しいみたいです。USB-HS使用時には外部からそこそこ精度の良いクロックを供給しなければならず、これと両立させようとすると本稿で示した48kHz系のみとなってしまうようです。I2S専用のクロック入力を受け付けるようなモデルの登場を期待したいです。