外部DACとしてES9023を使用
![]() |
| Covia Zeal EdgeのI2S DAC化 |
ES9023 I2S関連ピン
I2Sピン設定
I2S2ピンの設定
各ブロックに設定する前にクロックをONにする設定およびPA15, PB3, PB5ピンをGPIOではなくAlternate Function(I2S2)として使用するための設定です。PAxxはGPIOAブロックのピン、PBxxはGPIOBブロックのピンでブロック毎に別に設定する必要があります。
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
gpio_pin_remap_config(GPIO_SWJ_DISABLE_REMAP, ENABLE);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5);
I2Sの初期化
I2Sのフォーマットとして、I2S_STD_PHILLIPSを指定します。I2S_CKの極性については、I2S_CKPL_LOWにしておくと送信開始前にI2S_SDに不要なエッジが出力されるようなので、I2S_CKPL_HIGHにしておきました。
I2S_FRAMEFORMAT_DT24B_CH32Bは、1chあたり32bitのデータからを実効下詰め24bitデータをMSBから出力する設定となります。I2S_AUDIOSAMPLE_44Kで、サンプリング周波数を指定します。
rcu_periph_clock_enable(RCU_SPI2);
i2s_init(SPI2, I2S_MODE_MASTERTX, I2S_STD_PHILLIPS, I2S_CKPL_HIGH);
i2s_psc_config(SPI2, I2S_AUDIOSAMPLE_44K, I2S_FRAMEFORMAT_DT24B_CH32B, I2S_MCKOUT_DISABLE);
i2s_enable(SPI2);![]() |
| I2S Clock Path(ドキュメントから抜粋) |
これらの設定により、I2S_CKの周波数も同時に決定されます。
- I2S_CK周波数 = 108 (MHz) / 38 = 2.842 (MHz)
- サンプリング周波数 = 108 (MHz) / (64 x 38) = 44.41 (KHz)
ただし、サンプリング周波数44.1KHzに対して44.41KHzでは誤差が大きすぎるのでこのままでは問題があります。
より正確なサンプリング周波数に対応するため、クロックの使用経路を以下のように見直します。I2Sブロックのクロックとして108MHzではなく、96MHzを供給するようにします。この場合I2Sブロック内で分周比が34に設定されれば
- I2S_CK周波数 = 96 (MHz) / 34 = 2.824 (MHz)
- サンプリング周波数 = 96 (MHz) / (64 x 34) = 44.118 (KHz)
となるはずです。
![]() |
| I2S Clock Paths その2 (ドキュメントから抜粋) |
そのための追加の設定は以下の通りです。
RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN);
rcu_predv1_config(RCU_PREDV1_DIV2);
rcu_pll2_config(RCU_PLL2_MUL12);
rcu_i2s2_clock_config(RCU_I2S2SRC_CKPLL2_MUL2);
RCU_CTL |= (RCU_CTL_PLL1EN | RCU_CTL_PLL2EN);DMAによるデータ転送
- メモリからペリフェラルの転送
- ペリフェラル側はアドレス固定: SPI_DATA(SPI2)
- メモリ側は16bit転送、ペリフェラル側は32bit転送
- DMA1のCH1を使用
void init_dma_i2s2(uint32_t memory_addr, uint32_t trans_number)
{
rcu_periph_clock_enable(RCU_DMA1);
dma_struct_para_init(&dma_param);
dma_param.periph_addr = &SPI_DATA(SPI2);
dma_param.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
dma_param.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_param.memory_addr = memory_addr;
dma_param.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_param.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_param.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_param.number = trans_number;
dma_param.priority = DMA_PRIORITY_HIGH;
dma_init(DMA1, DMA_CH1, &dma_param);
}
#define SIZE_OF_SAMPLES 512 // samples for 2ch
int32_t audio_buf0[SIZE_OF_SAMPLES];
int32_t audio_buf1[SIZE_OF_SAMPLES];
init_i2s2();
init_dma_i2s2(audio_buf0, SIZE_OF_SAMPLES*2);
spi_dma_enable(SPI2, SPI_DMA_TRANSMIT);
dma_channel_enable(DMA1, DMA_CH1);
int count = 0;
while (1) {
if (SET == dma_flag_get(DMA1, DMA_CH1, DMA_FLAG_FTF)) {
dma_flag_clear(DMA1, DMA_CH1, DMA_FLAG_FTF);
dma_channel_disable(DMA1, DMA_CH1);
if (count % 2 == 0) {
init_dma_i2s2(audio_buf1, SIZE_OF_SAMPLES*2);
} else {
init_dma_i2s2(audio_buf0, SIZE_OF_SAMPLES*2);
}
dma_channel_enable(DMA1, DMA_CH1);
// 次のaudio_bufの準備をここで行う
count++;
}
}コード例
#include "gd32vf103_rcu.h"
#include "gd32vf103_gpio.h"
#include "gd32vf103_spi.h"
#include "gd32vf103_dma.h"
#define SIZE_OF_SAMPLES 512 // samples for 2ch
#define SAMPLE_RATE (44100)
#define WAVE_FREQ_HZ (440)
#define PI (3.14159265)
#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)
#define DELTA 2.0*PI*WAVE_FREQ_HZ/SAMPLE_RATE
int32_t audio_buf0[SIZE_OF_SAMPLES];
int32_t audio_buf1[SIZE_OF_SAMPLES];
static double ang = 0;
static int count = 0;
static double triangle_float = 0.0;
union U {
uint32_t i;
uint16_t s[2];
} u;
// 16bit入れ替え
uint32_t swap16b(uint32_t in_val)
{
u.i = in_val;
return ((uint32_t) u.s[0] << 16) | ((uint32_t) u.s[1]);
}
double _square_wave(void)
{
double dval;
if (ang >= 2.0*PI) {
ang -= 2.0*PI;
triangle_float = -(double) pow(2, 22);
}
if (ang < PI) {
dval = 1.0;
} else {
dval = -1.0;
}
return dval;
}
// 三角波生成
void setup_triangle_sine_waves(int32_t *samples_data)
{
unsigned int i;
double square_float;
double triangle_step = (double) pow(2, 23) / SAMPLE_PER_CYCLE;
for(i = 0; i < SIZE_OF_SAMPLES/2; i++) {
square_float = _square_wave();
ang += DELTA;
if (square_float >= 0) {
triangle_float += triangle_step;
} else {
triangle_float -= triangle_step;
}
square_float *= (pow(2, 23) - 1);
samples_data[i*2+0] = swap16b((int) square_float * 256);
samples_data[i*2+1] = swap16b((int) triangle_float * 256);
}
}
void prepare_audio_buf(void)
{
setup_triangle_sine_waves(audio_buf0);
setup_triangle_sine_waves(audio_buf1);
init_i2s2();
init_dma_i2s2(audio_buf0, SIZE_OF_SAMPLES*2);
spi_dma_enable(SPI2, SPI_DMA_TRANSMIT);
dma_channel_enable(DMA1, DMA_CH1);
count = 0;
}
void run_audio_buf(void)
{
if (SET == dma_flag_get(DMA1, DMA_CH1, DMA_FLAG_FTF)) {
dma_flag_clear(DMA1, DMA_CH1, DMA_FLAG_FTF);
dma_channel_disable(DMA1, DMA_CH1);
if (count % 2 == 0) {
init_dma_i2s2(audio_buf1, SIZE_OF_SAMPLES*2);
} else {
init_dma_i2s2(audio_buf0, SIZE_OF_SAMPLES*2);
}
dma_channel_enable(DMA1, DMA_CH1);
if (count % 2 == 0) {
setup_triangle_sine_waves(audio_buf0);
} else {
setup_triangle_sine_waves(audio_buf1);
}
count++;
}
}
dma_parameter_struct dma_param;
void init_dma_i2s2(uint32_t memory_addr, uint32_t trans_number)
{
rcu_periph_clock_enable(RCU_DMA1);
dma_struct_para_init(&dma_param);
dma_param.periph_addr = &SPI_DATA(SPI2);
dma_param.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
dma_param.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_param.memory_addr = memory_addr;
dma_param.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_param.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_param.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_param.number = trans_number;
dma_param.priority = DMA_PRIORITY_HIGH;
dma_init(DMA1, DMA_CH1, &dma_param);
}
void init_i2s2(void)
{
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_SPI2);
RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN);
rcu_predv1_config(RCU_PREDV1_DIV2);
rcu_pll2_config(RCU_PLL2_MUL12);
rcu_i2s2_clock_config(RCU_I2S2SRC_CKPLL2_MUL2);
RCU_CTL |= (RCU_CTL_PLL1EN | RCU_CTL_PLL2EN);
gpio_pin_remap_config(GPIO_SWJ_DISABLE_REMAP, ENABLE);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5);
i2s_init(SPI2, I2S_MODE_MASTERTX, I2S_STD_PHILLIPS, I2S_CKPL_HIGH);
i2s_psc_config(SPI2, I2S_AUDIOSAMPLE_44K, I2S_FRAMEFORMAT_DT24B_CH32B, I2S_MCKOUT_DISABLE);
i2s_enable(SPI2);
}
int main(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1|GPIO_PIN_2);
prepare_audio_buf();
while (1) {
run_audio_buf();
}
return 0;
}





0 件のコメント:
コメントを投稿