未验证 提交 fcfa986b 编写于 作者: B Bernard Xiong 提交者: GitHub

Merge pull request #2928 from Zero-Free/audio_developer

添加qemu和stm32l475-atk-pandora对音频设备驱动框架的支持
/*
* File : audio_device.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2017, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-05-26 RT-Thread the first version
*/
#include <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#include <string.h>
#include "drv_pl041.h"
#include "drv_ac97.h"
#include "audio_device.h"
struct audio_device
{
struct rt_device *snd;
struct rt_mempool mp;
int state;
void (*evt_handler)(void *parameter, int state);
void *parameter;
};
static struct audio_device *_audio_device = NULL;
void *audio_device_get_buffer(int *bufsz)
{
if (bufsz)
{
*bufsz = AUDIO_DEVICE_DECODE_MP_BLOCK_SZ * 2;
}
return rt_mp_alloc(&(_audio_device->mp), RT_WAITING_FOREVER);
}
void audio_device_put_buffer(void *ptr)
{
if (ptr) rt_mp_free(ptr);
return ;
}
static rt_err_t audio_device_write_done(struct rt_device *device, void *ptr)
{
if (!ptr)
{
rt_kprintf("device buf_release NULL\n");
return -RT_ERROR;
}
rt_mp_free(ptr);
return RT_EOK;
}
void audio_device_write(void *buffer, int size)
{
if (_audio_device->snd && size != 0)
{
if (_audio_device->state == AUDIO_DEVICE_IDLE)
{
if (_audio_device->evt_handler)
_audio_device->evt_handler(_audio_device->parameter, AUDIO_DEVICE_PLAYBACK);
/* change audio device state */
_audio_device->state = AUDIO_DEVICE_PLAYBACK;
}
rt_device_write(_audio_device->snd, 0, buffer, size);
}
else
{
/* release buffer directly */
rt_mp_free(buffer);
}
return ;
}
int audio_device_init(void)
{
uint8_t *mempool_ptr;
if (!_audio_device)
{
_audio_device = (struct audio_device *) rt_malloc(sizeof(struct audio_device) + AUDIO_DEVICE_DECODE_MP_SZ);
if (_audio_device == NULL)
{
rt_kprintf("malloc memeory for _audio_device failed! \n");
return -RT_ERROR;
}
_audio_device->evt_handler = NULL;
_audio_device->parameter = NULL;
mempool_ptr = (uint8_t *)(_audio_device + 1);
rt_mp_init(&(_audio_device->mp), "adbuf", mempool_ptr, AUDIO_DEVICE_DECODE_MP_SZ, AUDIO_DEVICE_DECODE_MP_BLOCK_SZ * 2);
/* find snd device */
_audio_device->snd = rt_device_find("sound");
if (_audio_device->snd == NULL)
{
rt_kprintf("sound device not found \n");
return -1;
}
/* set tx complete call back function */
rt_device_set_tx_complete(_audio_device->snd, audio_device_write_done);
}
return RT_EOK;
}
int audio_device_set_evt_handler(void (*handler)(void *parameter, int state), void *parameter)
{
if (_audio_device)
{
_audio_device->evt_handler = handler;
_audio_device->parameter = parameter;
}
return 0;
}
void audio_device_set_rate(int sample_rate)
{
if (_audio_device->snd)
{
int rate = sample_rate;
rt_device_control(_audio_device->snd, CODEC_CMD_SAMPLERATE, &rate);
}
}
void audio_device_set_volume(int value)
{
if (_audio_device->snd)
{
rt_device_control(_audio_device->snd, CODEC_CMD_SET_VOLUME, &value);
}
}
int audio_device_get_volume(void)
{
int value = 0;
if (_audio_device->snd)
{
rt_device_control(_audio_device->snd, CODEC_CMD_GET_VOLUME, &value);
}
return value;
}
void audio_device_open(void)
{
_audio_device->state = AUDIO_DEVICE_IDLE;
rt_device_open(_audio_device->snd, RT_DEVICE_OFLAG_WRONLY);
}
void audio_device_close(void)
{
rt_device_close(_audio_device->snd);
if (_audio_device->state == AUDIO_DEVICE_PLAYBACK)
{
if (_audio_device->evt_handler)
_audio_device->evt_handler(_audio_device->parameter, AUDIO_DEVICE_CLOSE);
}
/* set to idle */
_audio_device->state = AUDIO_DEVICE_CLOSE;
}
/*
* File : audio_device.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2017, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-05-26 RT-Thread the first version
*/
#ifndef AUDIO_DEVICE_H__
#define AUDIO_DEVICE_H__
enum AUDIO_DEVICE_STATE
{
AUDIO_DEVICE_IDLE,
AUDIO_DEVICE_PLAYBACK,
AUDIO_DEVICE_CLOSE,
};
void *audio_device_get_buffer(int *bufsz);
void audio_device_put_buffer(void *ptr);
void audio_device_write(void *buffer, int size);
int audio_device_init(void);
void audio_device_close(void);
void audio_device_open(void);
int audio_device_set_evt_handler(void (*handler)(void *parameter, int state), void *parameter);
void audio_device_set_rate(int sample_rate);
void audio_device_set_volume(int volume);
void audio_device_wait_free(void);
#endif
\ No newline at end of file
/*
* File : drv_audio.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2017, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-05-26 RT-Thread the first version
*/
#include <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#include <string.h>
#include "drv_pl041.h"
#include "drv_ac97.h"
#include "drv_audio.h"
#define DATA_NODE_MAX (10)
#define CODEC_TX_FIFO_SIZE (256)
#define AUDIO_DEVICE_DECODE_MP_SIZE (4096)
#define AUDIO_DEVICE_DECODE_MP_CONUT (4)
struct codec_data_node
{
char *data_ptr;
rt_size_t data_size;
};
struct audio_buff_des
{
struct codec_data_node *data_list;
void (*free_fun)(void *);
rt_uint32_t read_offset;
rt_uint16_t node_num;
rt_uint16_t read_index, put_index;
};
struct audio_device
{
/* inherit from rt_device */
struct rt_device parent;
};
static struct audio_device audio_device_drive;
static struct audio_buff_des *audio_buff;
static int irq_flag = 0;
static void _audio_buff_cb(void *buff)
{
if (audio_device_drive.parent.tx_complete != RT_NULL)
{
audio_device_drive.parent.tx_complete(&audio_device_drive.parent, buff);
}
}
static rt_size_t _audio_buff_push(struct audio_buff_des *hdle, void *buff, int size)
{
struct codec_data_node *node;
rt_uint16_t next_index;
rt_uint32_t level;
if ((buff == RT_NULL) || (size == 0))
{
return 0;
}
next_index = hdle->put_index + 1;
if (next_index >= hdle->node_num)
next_index = 0;
/* check data_list full */
if (next_index == hdle->read_index)
{
rt_kprintf("data_list full\n");
rt_set_errno(-RT_EFULL);
return 0;
}
level = rt_hw_interrupt_disable();
node = &hdle->data_list[hdle->put_index];
hdle->put_index = next_index;
/* set node attribute */
node->data_ptr = (char *) buff;
node->data_size = size;
rt_hw_interrupt_enable(level);
return size;
}
static rt_size_t _audio_buff_pop(struct audio_buff_des *hdle, void *buff, int size)
{
struct codec_data_node *node;
rt_uint32_t next_index, count = 0, cp_size = 0, offset = 0;
node = &hdle->data_list[hdle->read_index];
if ((hdle->read_index == hdle->put_index) && (node->data_ptr == RT_NULL))
{
memset(buff, 0xff, size);
return 0;
}
while (count < size)
{
node = &hdle->data_list[hdle->read_index];
offset = hdle->read_offset;
cp_size = (node->data_size - offset) > (size - count) ? (size - count) : (node->data_size - offset);
if (node->data_ptr == RT_NULL)
{
memset(buff, 0, size - count);
return count;
}
memcpy((rt_uint8_t *)buff + count, (rt_uint8_t *)(node->data_ptr) + offset, cp_size);
hdle->read_offset += cp_size;
count += cp_size;
if (hdle->read_offset >= node->data_size)
{
/* notify transmitted complete. */
if (hdle->free_fun != RT_NULL)
{
hdle->free_fun(node->data_ptr);
}
/* clear current node */
memset(node, 0, sizeof(struct codec_data_node));
next_index = hdle->read_index + 1;
if (next_index >= hdle->node_num)
{
next_index = 0;
}
hdle->read_offset = 0;
hdle->read_index = next_index;
}
}
return count;
}
static void transit_wav_data(rt_uint32_t status)
{
rt_uint16_t sample[CODEC_TX_FIFO_SIZE];
int i = 0, size;
size = _audio_buff_pop(audio_buff, sample, CODEC_TX_FIFO_SIZE * sizeof(rt_uint16_t));
if ((size == 0) && (irq_flag == 1))
{
aaci_pl041_irq_disable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
irq_flag = 0;
}
for (i = 0; i < (size >> 1); i++)
{
aaci_pl041_channle_write(0, &sample[i], 1);
}
}
static void rt_hw_aaci_isr(rt_uint32_t status, void *user_data)
{
if (status & AACI_SR_TXHE)
{
transit_wav_data(status);
}
}
static rt_err_t codec_init(rt_device_t dev)
{
struct pl041_cfg _cfg;
_cfg.itype = PL041_CHANNLE_LEFT_ADC | PL041_CHANNLE_RIGHT_ADC;
_cfg.otype = PL041_CHANNLE_LEFT_DAC | PL041_CHANNLE_RIGHT_DAC;
_cfg.vol = 50;
_cfg.rate = 8000;
ac97_reset();
aaci_pl041_channle_cfg(0, &_cfg);
aaci_pl041_irq_register(0, rt_hw_aaci_isr, RT_NULL);
return RT_EOK;
}
static rt_err_t codec_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t codec_close(rt_device_t dev)
{
rt_uint16_t temp = 0, i = 1024 * 10;
while (PL041->sr1 & AACI_SR_TXB);
while (i)
{
if (aaci_pl041_channle_write(0, &temp, 1) != 0)
{
i--;
}
}
return RT_EOK;
}
static rt_size_t codec_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
return 0;
}
static rt_size_t codec_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
_audio_buff_push(audio_buff, (void *)buffer, size);
if (irq_flag == 0)
{
//open irq
irq_flag = 1;
aaci_pl041_channle_enable(0);
aaci_pl041_irq_enable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
}
return 0;
}
static rt_err_t codec_control(rt_device_t dev, int cmd, void *args)
{
rt_err_t result = RT_EOK;
switch (cmd)
{
case CODEC_CMD_RESET:
{
break;
}
case CODEC_CMD_SET_VOLUME:
{
uint32_t v;
v = *(rt_uint32_t *)args;
result = ac97_set_vol(v);
break;
}
case CODEC_CMD_GET_VOLUME:
{
int *v = args;
*v = ac97_get_vol();
break;
}
case CODEC_CMD_SAMPLERATE:
{
int v;
v = *(rt_uint32_t *)args;
ac97_set_rate(v);
break;
}
default:
result = RT_ERROR;
}
return result;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops codec_ops =
{
codec_init,
codec_open,
codec_close,
codec_read,
codec_write,
codec_control
};
#endif
int audio_hw_init(void)
{
struct audio_device *codec = &audio_device_drive;
codec->parent.type = RT_Device_Class_Sound;
codec->parent.rx_indicate = RT_NULL;
codec->parent.tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
codec->parent.ops = &codec_ops;
#else
codec->parent.init = codec_init;
codec->parent.open = codec_open;
codec->parent.close = codec_close;
codec->parent.read = codec_read;
codec->parent.write = codec_write;
codec->parent.control = codec_control;
#endif
codec->parent.user_data = RT_NULL;
audio_buff = rt_malloc(sizeof(struct audio_buff_des) + sizeof(struct codec_data_node) * DATA_NODE_MAX);
if (audio_buff == RT_NULL)
{
rt_kprintf("audio buff malloc fail\n");
return -1;
}
rt_memset(audio_buff, 0, sizeof(struct audio_buff_des) + sizeof(struct codec_data_node) * DATA_NODE_MAX);
audio_buff->data_list = (struct codec_data_node *)((rt_uint8_t *)audio_buff + sizeof(struct audio_buff_des));
audio_buff->free_fun = _audio_buff_cb;
audio_buff->node_num = DATA_NODE_MAX;
/* register the device */
rt_device_register(&codec->parent, "sound", RT_DEVICE_FLAG_WRONLY | RT_DEVICE_FLAG_DMA_TX);
aaci_pl041_init();
rt_device_init(&codec->parent);
return 0;
}
INIT_DEVICE_EXPORT(audio_hw_init);
/*
* File : drv_audio.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2017, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2018-05-26 RT-Thread the first version
*/
#ifndef __DRV_AUDIO_H__
#define __DRV_AUDIO_H__
#endif
......@@ -302,7 +302,7 @@ static void aaci_pl041_irq_handle(int irqno, void *param)
void *p_status;
mask = PL041_READ(&PL041->allints);
PL041_WRITE(PL041->intclr, mask);
PL041_WRITE(&PL041->intclr, mask);
for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
{
......
......@@ -225,7 +225,7 @@ typedef void (*pl041_irq_fun_t)(rt_uint32_t status, void * user_data);
rt_err_t aaci_pl041_init(void);
void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val);
rt_uint16_t aaci_ac97_read(rt_uint16_t reg);
int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cgf);
int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cfg);
int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count);
int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count);
int aaci_pl041_channle_enable(int channle);
......
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-23 Zero-Free first implementation
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_sound.h>
#include <drv_pl041.h>
#include <drv_ac97.h>
#define DBG_TAG "drv.sound"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define TX_FIFO_SIZE (3840)
struct sound_device
{
struct rt_audio_device audio;
struct rt_audio_configure replay_config;
rt_uint8_t *tx_fifo;
rt_uint8_t volume;
};
static struct sound_device snd_dev = {0};
static void rt_hw_aaci_isr(rt_uint32_t status, void *user_data)
{
if (status & AACI_SR_TXHE)
{
rt_audio_tx_complete(&snd_dev.audio);
}
}
static rt_err_t sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
caps->udata.config.channels = snd_dev->replay_config.channels;
caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = snd_dev->replay_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_MIXER: /* report the Mixer Units */
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = snd_dev->volume;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
default:
result = -RT_ERROR;
break;
}
return result;
}
static rt_err_t sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev;
struct rt_audio_replay *replay;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_VOLUME:
{
rt_uint8_t volume = caps->udata.value;
snd_dev->volume = volume;
ac97_set_vol(volume);
LOG_I("set volume %d", volume);
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
/* set samplerate */
ac97_set_rate(caps->udata.config.samplerate);
/* update buffer fifo informaition according samplerate */
replay = snd_dev->audio.replay;
replay->buf_info.total_size = caps->udata.config.samplerate / 50 * 4;
replay->buf_info.block_size = replay->buf_info.total_size / 2;
/* save configs */
snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
snd_dev->replay_config.channels = caps->udata.config.channels;
snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
ac97_set_rate(caps->udata.config.samplerate);
snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
break;
}
case AUDIO_DSP_CHANNELS:
{
/* not support */
snd_dev->replay_config.channels = caps->udata.config.channels;
LOG_D("set channels %d", snd_dev->replay_config.channels);
break;
}
case AUDIO_DSP_SAMPLEBITS:
{
/* not support */
snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
default:
break;
}
return result;
}
static rt_err_t sound_init(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev;
struct pl041_cfg _cfg;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
aaci_pl041_init();
_cfg.itype = PL041_CHANNLE_LEFT_ADC | PL041_CHANNLE_RIGHT_ADC;
_cfg.otype = PL041_CHANNLE_LEFT_DAC | PL041_CHANNLE_RIGHT_DAC;
_cfg.vol = snd_dev->volume;
_cfg.rate = snd_dev->replay_config.samplerate;
ac97_reset();
aaci_pl041_channle_cfg(0, &_cfg);
aaci_pl041_irq_register(0, rt_hw_aaci_isr, RT_NULL);
return result;
}
static rt_err_t sound_start(struct rt_audio_device *audio, int stream)
{
RT_ASSERT(audio != RT_NULL);
if (stream == AUDIO_STREAM_REPLAY)
{
LOG_D("open sound device");
aaci_pl041_channle_enable(0);
aaci_pl041_irq_enable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
}
return RT_EOK;
}
static rt_err_t sound_stop(struct rt_audio_device *audio, int stream)
{
RT_ASSERT(audio != RT_NULL);
if (stream == AUDIO_STREAM_REPLAY)
{
/* wait codec free */
rt_thread_mdelay(100);
/* disable irq and channels 0 */
aaci_pl041_irq_disable(0, AACI_IE_UR | AACI_IE_TX | AACI_IE_TXC);
aaci_pl041_channle_disable(0);
LOG_D("close sound device");
}
return RT_EOK;
}
static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
/**
* TX_FIFO
* +----------------+----------------+
* | block1 | block2 |
* +----------------+----------------+
* \ block_size /
*/
info->buffer = snd_dev->tx_fifo;
info->total_size = TX_FIFO_SIZE;
info->block_size = TX_FIFO_SIZE/2;
info->block_count = 2;
}
static rt_size_t sound_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
{
RT_ASSERT(audio != RT_NULL);
/* write data to channel_0 fifo */
aaci_pl041_channle_write(0, (rt_uint16_t *)writeBuf, size >> 1);
return size;
}
static struct rt_audio_ops snd_ops =
{
.getcaps = sound_getcaps,
.configure = sound_configure,
.init = sound_init,
.start = sound_start,
.stop = sound_stop,
.transmit = sound_transmit,
.buffer_info = sound_buffer_info,
};
int rt_hw_audio_init(void)
{
rt_uint8_t *tx_fifo;
if (snd_dev.tx_fifo)
return RT_EOK;
tx_fifo = rt_malloc(TX_FIFO_SIZE);
if (tx_fifo == RT_NULL)
return -RT_ENOMEM;
rt_memset(tx_fifo, 0, TX_FIFO_SIZE);
snd_dev.tx_fifo = tx_fifo;
/* init default configuration */
{
snd_dev.replay_config.samplerate = 44100;
snd_dev.replay_config.channels = 2;
snd_dev.replay_config.samplebits = 16;
snd_dev.volume = 55;
}
/* register sound device */
snd_dev.audio.ops = &snd_ops;
rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_audio_init);
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-23 Zero-Free first implementation
*/
#ifndef __DRV_SOUND_H__
#define __DRV_SOUND_H__
int rt_hw_audio_init(void);
#endif
#include <rtthread.h>
#include <rtdevice.h>
#include <finsh.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "audio_device.h"
#define BUFSZ 2048
struct RIFF_HEADER_DEF
{
char riff_id[4]; // 'R','I','F','F'
uint32_t riff_size;
char riff_format[4]; // 'W','A','V','E'
};
struct WAVE_FORMAT_DEF
{
uint16_t FormatTag;
uint16_t Channels;
uint32_t SamplesPerSec;
uint32_t AvgBytesPerSec;
uint16_t BlockAlign;
uint16_t BitsPerSample;
};
struct FMT_BLOCK_DEF
{
char fmt_id[4]; // 'f','m','t',' '
uint32_t fmt_size;
struct WAVE_FORMAT_DEF wav_format;
};
struct DATA_BLOCK_DEF
{
char data_id[4]; // 'R','I','F','F'
uint32_t data_size;
};
struct wav_info
{
struct RIFF_HEADER_DEF header;
struct FMT_BLOCK_DEF fmt_block;
struct DATA_BLOCK_DEF data_block;
};
static char file_name[32];
void wavplay_thread_entry(void *parameter)
{
FILE *fp = NULL;
uint16_t *buffer = NULL;
struct wav_info *info = NULL;
fp = fopen(file_name, "rb");
if (!fp)
{
printf("open file failed!\n");
goto __exit;
}
info = (struct wav_info *) malloc(sizeof(*info));
if (!info) goto __exit;
if (fread(&(info->header), sizeof(struct RIFF_HEADER_DEF), 1, fp) != 1) goto __exit;
if (fread(&(info->fmt_block), sizeof(struct FMT_BLOCK_DEF), 1, fp) != 1) goto __exit;
if (fread(&(info->data_block), sizeof(struct DATA_BLOCK_DEF), 1, fp) != 1) goto __exit;
printf("wav information:\n");
printf("samplerate %u\n", info->fmt_block.wav_format.SamplesPerSec);
printf("channel %u\n", info->fmt_block.wav_format.Channels);
audio_device_init();
audio_device_open();
audio_device_set_rate(info->fmt_block.wav_format.SamplesPerSec);
while (!feof(fp))
{
int length;
buffer = (uint16_t *)audio_device_get_buffer(RT_NULL);
length = fread(buffer, 1, BUFSZ, fp);
if (length)
{
if (info->fmt_block.wav_format.Channels == 1)
{
/* extend to stereo channels */
int index;
uint16_t *ptr;
ptr = (uint16_t *)((uint8_t *)buffer + BUFSZ * 2);
for (index = 1; index < BUFSZ / 2; index ++)
{
*ptr = *(ptr - 1) = buffer[BUFSZ / 2 - index];
ptr -= 2;
}
length = length * 2;
}
audio_device_write((uint8_t *)buffer, length);
}
else
{
audio_device_put_buffer((uint8_t *)buffer);
break;
}
}
audio_device_close();
__exit:
if (fp) fclose(fp);
if (info) free(info);
}
int wavplay(int argc, char **argv)
{
rt_thread_t tid = RT_NULL;
if (argc != 2)
{
printf("Usage:\n");
printf("wavplay song.wav\n");
return 0;
}
memset(file_name, 0, sizeof(file_name));
memcpy(file_name, argv[1], strlen(argv[1]));
tid = rt_thread_create("wayplay",
wavplay_thread_entry,
RT_NULL,
1024 * 8,
22,
10);
if (tid != RT_NULL)
rt_thread_startup(tid);
}
MSH_CMD_EXPORT(wavplay, wavplay song.wav);
......@@ -81,7 +81,7 @@
#define HAL_QSPI_MODULE_ENABLED
/*#define HAL_RNG_MODULE_ENABLED */
#define HAL_RTC_MODULE_ENABLED
/*#define HAL_SAI_MODULE_ENABLED */
#define HAL_SAI_MODULE_ENABLED
/*#define HAL_SD_MODULE_ENABLED */
/*#define HAL_SMBUS_MODULE_ENABLED */
/*#define HAL_SMARTCARD_MODULE_ENABLED */
......
......@@ -11,65 +11,73 @@ KeepUserPlacement=false
Mcu.Family=STM32L4
Mcu.IP0=ADC1
Mcu.IP1=IWDG
Mcu.IP10=TIM2
Mcu.IP11=TIM4
Mcu.IP12=TIM15
Mcu.IP13=TIM16
Mcu.IP14=TIM17
Mcu.IP15=USART1
Mcu.IP16=USART2
Mcu.IP17=USB_OTG_FS
Mcu.IP10=TIM1
Mcu.IP11=TIM2
Mcu.IP12=TIM4
Mcu.IP13=TIM15
Mcu.IP14=TIM16
Mcu.IP15=TIM17
Mcu.IP16=USART1
Mcu.IP17=USART2
Mcu.IP18=USB_OTG_FS
Mcu.IP2=NVIC
Mcu.IP3=QUADSPI
Mcu.IP4=RCC
Mcu.IP5=RTC
Mcu.IP6=SPI1
Mcu.IP7=SPI2
Mcu.IP8=SYS
Mcu.IP9=TIM1
Mcu.IPNb=18
Mcu.IP6=SAI1
Mcu.IP7=SPI1
Mcu.IP8=SPI2
Mcu.IP9=SYS
Mcu.IPNb=19
Mcu.Name=STM32L475V(C-E-G)Tx
Mcu.Package=LQFP100
Mcu.Pin0=PC14-OSC32_IN (PC14)
Mcu.Pin1=PC15-OSC32_OUT (PC15)
Mcu.Pin10=PE9
Mcu.Pin11=PE10
Mcu.Pin12=PE11
Mcu.Pin13=PE12
Mcu.Pin14=PE13
Mcu.Pin15=PE14
Mcu.Pin16=PE15
Mcu.Pin17=PB10
Mcu.Pin18=PB11
Mcu.Pin19=PB13
Mcu.Pin2=PH0-OSC_IN (PH0)
Mcu.Pin20=PB14
Mcu.Pin21=PB15
Mcu.Pin22=PA9
Mcu.Pin23=PA10
Mcu.Pin24=PA11
Mcu.Pin25=PA12
Mcu.Pin26=PA13 (JTMS-SWDIO)
Mcu.Pin27=PA14 (JTCK-SWCLK)
Mcu.Pin28=PB7
Mcu.Pin29=PB8
Mcu.Pin3=PH1-OSC_OUT (PH1)
Mcu.Pin30=VP_IWDG_VS_IWDG
Mcu.Pin31=VP_RTC_VS_RTC_Activate
Mcu.Pin32=VP_SYS_VS_Systick
Mcu.Pin33=VP_TIM1_VS_ClockSourceINT
Mcu.Pin34=VP_TIM2_VS_ClockSourceINT
Mcu.Pin35=VP_TIM4_VS_ClockSourceINT
Mcu.Pin36=VP_TIM15_VS_ClockSourceINT
Mcu.Pin37=VP_TIM16_VS_ClockSourceINT
Mcu.Pin38=VP_TIM17_VS_ClockSourceINT
Mcu.Pin4=PA2
Mcu.Pin5=PA3
Mcu.Pin6=PA5
Mcu.Pin7=PA6
Mcu.Pin8=PA7
Mcu.Pin9=PC5
Mcu.PinsNb=39
Mcu.Pin0=PE2
Mcu.Pin1=PE3
Mcu.Pin10=PA3
Mcu.Pin11=PA5
Mcu.Pin12=PA6
Mcu.Pin13=PA7
Mcu.Pin14=PC5
Mcu.Pin15=PE9
Mcu.Pin16=PE10
Mcu.Pin17=PE11
Mcu.Pin18=PE12
Mcu.Pin19=PE13
Mcu.Pin2=PE4
Mcu.Pin20=PE14
Mcu.Pin21=PE15
Mcu.Pin22=PB10
Mcu.Pin23=PB11
Mcu.Pin24=PB13
Mcu.Pin25=PB14
Mcu.Pin26=PB15
Mcu.Pin27=PA9
Mcu.Pin28=PA10
Mcu.Pin29=PA11
Mcu.Pin3=PE5
Mcu.Pin30=PA12
Mcu.Pin31=PA13 (JTMS-SWDIO)
Mcu.Pin32=PA14 (JTCK-SWCLK)
Mcu.Pin33=PB7
Mcu.Pin34=PB8
Mcu.Pin35=VP_IWDG_VS_IWDG
Mcu.Pin36=VP_RTC_VS_RTC_Activate
Mcu.Pin37=VP_SAI1_VP_$IpInstance_SAIA_SAI_BASIC
Mcu.Pin38=VP_SAI1_VP_$IpInstance_SAIB_SAI_BASIC
Mcu.Pin39=VP_SYS_VS_Systick
Mcu.Pin4=PE6
Mcu.Pin40=VP_TIM1_VS_ClockSourceINT
Mcu.Pin41=VP_TIM2_VS_ClockSourceINT
Mcu.Pin42=VP_TIM4_VS_ClockSourceINT
Mcu.Pin43=VP_TIM15_VS_ClockSourceINT
Mcu.Pin44=VP_TIM16_VS_ClockSourceINT
Mcu.Pin45=VP_TIM17_VS_ClockSourceINT
Mcu.Pin5=PC14-OSC32_IN (PC14)
Mcu.Pin6=PC15-OSC32_OUT (PC15)
Mcu.Pin7=PH0-OSC_IN (PH0)
Mcu.Pin8=PH1-OSC_OUT (PH1)
Mcu.Pin9=PA2
Mcu.PinsNb=46
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32L475VETx
......@@ -152,6 +160,16 @@ PE14.Signal=QUADSPI_BK1_IO2
PE15.Locked=true
PE15.Mode=Single Bank
PE15.Signal=QUADSPI_BK1_IO3
PE2.Mode=SAI_A_MasterWithClock
PE2.Signal=SAI1_MCLK_A
PE3.Mode=SAI_B_SyncSlave
PE3.Signal=SAI1_SD_B
PE4.Mode=SAI_A_MasterWithClock
PE4.Signal=SAI1_FS_A
PE5.Mode=SAI_A_MasterWithClock
PE5.Signal=SAI1_SCK_A
PE6.Mode=SAI_A_MasterWithClock
PE6.Signal=SAI1_SD_A
PE9.Signal=S_TIM1_CH1
PH0-OSC_IN\ (PH0).Mode=HSE-External-Oscillator
PH0-OSC_IN\ (PH0).Signal=RCC_OSC_IN
......@@ -240,6 +258,20 @@ RCC.VCOInputFreq_Value=8000000
RCC.VCOOutputFreq_Value=160000000
RCC.VCOSAI1OutputFreq_Value=96000000
RCC.VCOSAI2OutputFreq_Value=64000000
SAI1.AudioFrequency-SAI_A_MasterWithClock=SAI_AUDIO_FREQUENCY_44K
SAI1.ErrorAudioFreq-SAI_A_MasterWithClock=-72.09 %
SAI1.IPParameters=Instance-SAI_A_MasterWithClock,VirtualMode-SAI_A_MasterWithClock,MClockEnable-SAI_A_MasterWithClock,RealAudioFreq-SAI_A_MasterWithClock,ErrorAudioFreq-SAI_A_MasterWithClock,InitProtocol-SAI_A_MasterWithClock,VirtualProtocol-SAI_A_BASIC,AudioFrequency-SAI_A_MasterWithClock,OutputDrive-SAI_A_MasterWithClock,Instance-SAI_B_SyncSlave,VirtualMode-SAI_B_SyncSlave,InitProtocol-SAI_B_SyncSlave,VirtualProtocol-SAI_B_BASIC
SAI1.InitProtocol-SAI_A_MasterWithClock=Enable
SAI1.InitProtocol-SAI_B_SyncSlave=Enable
SAI1.Instance-SAI_A_MasterWithClock=SAI$Index_Block_A
SAI1.Instance-SAI_B_SyncSlave=SAI$Index_Block_B
SAI1.MClockEnable-SAI_A_MasterWithClock=SAI_MASTERCLOCK_ENABLE
SAI1.OutputDrive-SAI_A_MasterWithClock=SAI_OUTPUTDRIVE_ENABLE
SAI1.RealAudioFreq-SAI_A_MasterWithClock=53.571 KHz
SAI1.VirtualMode-SAI_A_MasterWithClock=VM_MASTER
SAI1.VirtualMode-SAI_B_SyncSlave=VM_SLAVE
SAI1.VirtualProtocol-SAI_A_BASIC=VM_BASIC_PROTOCOL
SAI1.VirtualProtocol-SAI_B_BASIC=VM_BASIC_PROTOCOL
SH.ADCx_IN14.0=ADC1_IN14,IN14-Single-Ended
SH.ADCx_IN14.ConfNb=1
SH.S_TIM1_CH1.0=TIM1_CH1,PWM Generation1 CH1
......@@ -280,6 +312,10 @@ VP_IWDG_VS_IWDG.Mode=IWDG_Activate
VP_IWDG_VS_IWDG.Signal=IWDG_VS_IWDG
VP_RTC_VS_RTC_Activate.Mode=RTC_Enabled
VP_RTC_VS_RTC_Activate.Signal=RTC_VS_RTC_Activate
VP_SAI1_VP_$IpInstance_SAIA_SAI_BASIC.Mode=SAI_A_BASIC
VP_SAI1_VP_$IpInstance_SAIA_SAI_BASIC.Signal=SAI1_VP_$IpInstance_SAIA_SAI_BASIC
VP_SAI1_VP_$IpInstance_SAIB_SAI_BASIC.Mode=SAI_B_BASIC
VP_SAI1_VP_$IpInstance_SAIB_SAI_BASIC.Signal=SAI1_VP_$IpInstance_SAIB_SAI_BASIC
VP_SYS_VS_Systick.Mode=SysTick
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
VP_TIM15_VS_ClockSourceINT.Mode=Internal
......
......@@ -70,10 +70,14 @@ QSPI_HandleTypeDef hqspi;
RTC_HandleTypeDef hrtc;
SAI_HandleTypeDef hsai_BlockA1;
SAI_HandleTypeDef hsai_BlockB1;
SPI_HandleTypeDef hspi1;
SPI_HandleTypeDef hspi2;
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim4;
TIM_HandleTypeDef htim15;
TIM_HandleTypeDef htim16;
......@@ -105,6 +109,8 @@ static void MX_TIM16_Init(void);
static void MX_TIM15_Init(void);
static void MX_TIM4_Init(void);
static void MX_TIM1_Init(void);
static void MX_SAI1_Init(void);
static void MX_TIM2_Init(void);
static void MX_USB_OTG_FS_PCD_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
......@@ -158,6 +164,8 @@ int main(void)
MX_TIM15_Init();
MX_TIM4_Init();
MX_TIM1_Init();
MX_SAI1_Init();
MX_TIM2_Init();
MX_USB_OTG_FS_PCD_Init();
/* USER CODE BEGIN 2 */
......@@ -222,10 +230,11 @@ void SystemClock_Config(void)
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
|RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_USB
|RCC_PERIPHCLK_ADC;
|RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_SAI1
|RCC_PERIPHCLK_USB|RCC_PERIPHCLK_ADC;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1;
PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
......@@ -235,7 +244,8 @@ void SystemClock_Config(void)
PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK|RCC_PLLSAI1_ADC1CLK;
PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK|RCC_PLLSAI1_48M2CLK
|RCC_PLLSAI1_ADC1CLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
......@@ -409,6 +419,55 @@ static void MX_RTC_Init(void)
}
/**
* @brief SAI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SAI1_Init(void)
{
/* USER CODE BEGIN SAI1_Init 0 */
/* USER CODE END SAI1_Init 0 */
/* USER CODE BEGIN SAI1_Init 1 */
/* USER CODE END SAI1_Init 1 */
hsai_BlockA1.Instance = SAI1_Block_A;
hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
{
Error_Handler();
}
hsai_BlockB1.Instance = SAI1_Block_B;
hsai_BlockB1.Init.AudioMode = SAI_MODESLAVE_RX;
hsai_BlockB1.Init.Synchro = SAI_SYNCHRONOUS;
hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SAI1_Init 2 */
/* USER CODE END SAI1_Init 2 */
}
/**
* @brief SPI1 Initialization Function
* @param None
......@@ -569,6 +628,69 @@ static void MX_TIM1_Init(void)
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
/**
* @brief TIM4 Initialization Function
* @param None
......@@ -856,10 +978,10 @@ static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
}
......
......@@ -814,6 +814,97 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef* hpcd)
}
static uint32_t SAI1_client =0;
void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* SAI1 */
if(hsai->Instance==SAI1_Block_A)
{
/* Peripheral clock enable */
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
}
SAI1_client ++;
/**SAI1_A_Block_A GPIO Configuration
PE2 ------> SAI1_MCLK_A
PE4 ------> SAI1_FS_A
PE5 ------> SAI1_SCK_A
PE6 ------> SAI1_SD_A
*/
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
if(hsai->Instance==SAI1_Block_B)
{
/* Peripheral clock enable */
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
}
SAI1_client ++;
/**SAI1_B_Block_B GPIO Configuration
PE3 ------> SAI1_SD_B
*/
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
}
void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
{
/* SAI1 */
if(hsai->Instance==SAI1_Block_A)
{
SAI1_client --;
if (SAI1_client == 0)
{
/* Peripheral clock disable */
__HAL_RCC_SAI1_CLK_DISABLE();
}
/**SAI1_A_Block_A GPIO Configuration
PE2 ------> SAI1_MCLK_A
PE4 ------> SAI1_FS_A
PE5 ------> SAI1_SCK_A
PE6 ------> SAI1_SD_A
*/
HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
}
if(hsai->Instance==SAI1_Block_B)
{
SAI1_client --;
if (SAI1_client == 0)
{
/* Peripheral clock disable */
__HAL_RCC_SAI1_CLK_DISABLE();
}
/**SAI1_B_Block_B GPIO Configuration
PE3 ------> SAI1_SD_B
*/
HAL_GPIO_DeInit(GPIOE, GPIO_PIN_3);
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
......
......@@ -47,6 +47,23 @@ menu "Onboard Peripheral Drivers"
select PKG_USING_AHT10_LATEST_VERSION
default n
menuconfig BSP_USING_AUDIO
bool "Enable Audio Device"
select RT_USING_AUDIO
select BSP_USING_I2C
select BSP_USING_I2C3
default n
if BSP_USING_AUDIO
config BSP_USING_AUDIO_PLAY
bool "Enable Audio Play"
default y
config BSP_USING_AUDIO_RECORD
bool "Enable Audio Record"
default n
endif
endmenu
menu "On-chip Peripheral Drivers"
......
......@@ -21,10 +21,20 @@ if GetDepend(['BSP_USING_SDCARD']):
if GetDepend(['BSP_USING_ICM20608']) or GetDepend(['BSP_USING_AHT10']):
src += Glob('ports/sensor_port.c')
if GetDepend(['BSP_USING_AUDIO']):
src += Glob('ports/audio/drv_es8388.c')
src += Glob('ports/audio/drv_sound.c')
if GetDepend(['BSP_USING_AUDIO_RECORD']):
src += Glob('ports/audio/drv_mic.c')
path = [cwd]
path += [cwd + '/CubeMX_Config/Inc']
path += [cwd + '/ports']
if GetDepend(['BSP_USING_AUDIO']):
path += [cwd + '/ports/audio']
startup_path_prefix = SDK_LIB
if rtconfig.CROSS_TOOL == 'gcc':
......
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-31 Zero-Free first implementation
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "drv_es8388.h"
/* ES8388 address */
#define ES8388_ADDR 0x10 /*0x11:CE=1;0x10:CE=0*/
struct es8388_device
{
struct rt_i2c_bus_device *i2c;
rt_uint16_t pin;
};
static struct es8388_device es_dev = {0};
static rt_uint16_t reg_read(rt_uint8_t addr)
{
struct rt_i2c_msg msg[2] = {0};
uint8_t val = 0xff;
RT_ASSERT(es_dev.i2c != RT_NULL);
msg[0].addr = ES8388_ADDR;
msg[0].flags = RT_I2C_WR;
msg[0].len = 1;
msg[0].buf = &addr;
msg[1].addr = ES8388_ADDR;
msg[1].flags = RT_I2C_RD;
msg[1].len = 1;
msg[1].buf = &val;
if (rt_i2c_transfer(es_dev.i2c, msg, 2) != 2)
{
rt_kprintf("I2C read data failed, reg = 0x%02x. \n", addr);
return 0xff;
}
return val;
}
static void reg_write(rt_uint8_t addr, rt_uint8_t val)
{
struct rt_i2c_msg msgs[1] = {0};
rt_uint8_t buff[2] = {0};
RT_ASSERT(es_dev.i2c != RT_NULL);
buff[0] = addr;
buff[1] = val;
msgs[0].addr = ES8388_ADDR;
msgs[0].flags = RT_I2C_WR;
msgs[0].buf = buff;
msgs[0].len = 2;
if (rt_i2c_transfer(es_dev.i2c, msgs, 1) != 1)
{
rt_kprintf("I2C write data failed, reg = 0x%2x. \n", addr);
return;
}
}
static int es8388_set_adc_dac_volume(int mode, int volume, int dot)
{
int res = 0;
if (volume < -96 || volume > 0)
{
if (volume < -96)
volume = -96;
else
volume = 0;
}
dot = (dot >= 5 ? 1 : 0);
volume = (-volume << 1) + dot;
if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC)
{
reg_write(ES8388_ADCCONTROL8, volume);
reg_write(ES8388_ADCCONTROL9, volume); //ADC Right Volume=0db
}
if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC)
{
reg_write(ES8388_DACCONTROL5, volume);
reg_write(ES8388_DACCONTROL4, volume);
}
return res;
}
void es8388_set_voice_mute(rt_bool_t enable)
{
uint8_t reg = 0;
reg = reg_read(ES8388_DACCONTROL3);
reg = reg & 0xFB;
reg_write(ES8388_DACCONTROL3, reg | (((int)enable) << 2));
}
rt_err_t es8388_init(const char *i2c_name, rt_uint16_t pin)
{
es_dev.i2c = rt_i2c_bus_device_find(i2c_name);
if (es_dev.i2c == RT_NULL)
{
rt_kprintf("%s bus not found\n", i2c_name);
return -RT_ERROR;
}
es_dev.pin = pin;
reg_write(ES8388_DACCONTROL3, 0x04); // 0x04 mute/0x00 unmute&ramp;DAC unmute and disabled digital volume control soft ramp
/* Chip Control and Power Management */
reg_write(ES8388_CONTROL2, 0x50);
reg_write(ES8388_CHIPPOWER, 0x00); //normal all and power up all
reg_write(ES8388_MASTERMODE, 0x00); //TODO:CODEC IN I2S SLAVE MODE
/* dac */
reg_write(ES8388_DACPOWER, 0xC0); //disable DAC and disable Lout/Rout/1/2
reg_write(ES8388_CONTROL1, 0x12); //Enfr=0,Play&Record Mode,(0x17-both of mic&paly)
// reg_write(ES8388_CONTROL2, 0); //LPVrefBuf=0,Pdn_ana=0
reg_write(ES8388_DACCONTROL1, 0x18);//1a 0x18:16bit iis , 0x00:24
reg_write(ES8388_DACCONTROL2, 0x02); //DACFsMode,SINGLE SPEED; DACFsRatio,256
reg_write(ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
reg_write(ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
reg_write(ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
reg_write(ES8388_DACCONTROL21, 0x80); //set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
reg_write(ES8388_DACCONTROL23, 0x00); //vroi=0
es8388_set_adc_dac_volume(ES_MODE_DAC, 0, 0); // 0db
reg_write(ES8388_DACPOWER, 0x3c); //0x3c Enable DAC and Enable Lout/Rout/1/2
/* adc */
reg_write(ES8388_ADCPOWER, 0xFF);
reg_write(ES8388_ADCCONTROL1, 0xbb); // MIC Left and Right channel PGA gain
reg_write(ES8388_ADCCONTROL2, 0x00); //0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1
reg_write(ES8388_ADCCONTROL3, 0x02);
reg_write(ES8388_ADCCONTROL4, 0x0d); // Left/Right data, Left/Right justified mode, Bits length, I2S format
reg_write(ES8388_ADCCONTROL5, 0x02); //ADCFsMode,singel SPEED,RATIO=256
//ALC for Microphone
es8388_set_adc_dac_volume(ES_MODE_ADC, 0, 0); // 0db
reg_write(ES8388_ADCPOWER, 0x09); //Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
/* enable es8388 PA */
es8388_pa_power(RT_TRUE);
return RT_EOK;
}
rt_err_t es8388_start(enum es8388_mode mode)
{
int res = 0;
uint8_t prev_data = 0, data = 0;
prev_data = reg_read(ES8388_DACCONTROL21);
if (mode == ES_MODE_LINE)
{
reg_write(ES8388_DACCONTROL16, 0x09); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 by pass enable
reg_write(ES8388_DACCONTROL17, 0x50); // left DAC to left mixer enable and LIN signal to left mixer enable 0db : bupass enable
reg_write(ES8388_DACCONTROL20, 0x50); // right DAC to right mixer enable and LIN signal to right mixer enable 0db : bupass enable
reg_write(ES8388_DACCONTROL21, 0xC0); //enable adc
}
else
{
reg_write(ES8388_DACCONTROL21, 0x80); //enable dac
}
data = reg_read(ES8388_DACCONTROL21);
if (prev_data != data)
{
reg_write(ES8388_CHIPPOWER, 0xF0); //start state machine
// reg_write(ES8388_ADDR, ES8388_CONTROL1, 0x16);
// reg_write(ES8388_ADDR, ES8388_CONTROL2, 0x50);
reg_write(ES8388_CHIPPOWER, 0x00); //start state machine
}
if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC || mode == ES_MODE_LINE)
{
reg_write(ES8388_ADCPOWER, 0x00); //power up adc and line in
}
if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC || mode == ES_MODE_LINE)
{
reg_write(ES8388_DACPOWER, 0x3c); //power up dac and line out
es8388_set_voice_mute(RT_FALSE);
}
return res;
}
rt_err_t es8388_stop(enum es8388_mode mode)
{
int res = 0;
if (mode == ES_MODE_LINE)
{
reg_write(ES8388_DACCONTROL21, 0x80); //enable dac
reg_write(ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
reg_write(ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
reg_write(ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
return res;
}
if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC)
{
reg_write(ES8388_DACPOWER, 0x00);
es8388_set_voice_mute(RT_TRUE); //res |= Es8388SetAdcDacVolume(ES_MODULE_DAC, -96, 5); // 0db
// reg_write(ES8388_ADDR, ES8388_DACPOWER, 0xC0); //power down dac and line out
}
if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC)
{
// Es8388SetAdcDacVolume(ES_MODULE_ADC, -96, 5); // 0db
reg_write(ES8388_ADCPOWER, 0xFF); //power down adc and line in
}
if (mode == ES_MODE_DAC_ADC)
{
reg_write(ES8388_DACCONTROL21, 0x9C); //disable mclk
// reg_write(ES8388_CONTROL1, 0x00);
// reg_write(ES8388_CONTROL2, 0x58);
// reg_write(ES8388_CHIPPOWER, 0xF3); //stop state machine
}
return RT_EOK;
}
rt_err_t es8388_fmt_set(enum es8388_mode mode, enum es8388_format fmt)
{
uint8_t reg = 0;
if (mode == ES_MODE_ADC || mode == ES_MODE_DAC_ADC)
{
reg = reg_read(ES8388_ADCCONTROL4);
reg = reg & 0xfc;
reg_write(ES8388_ADCCONTROL4, reg | fmt);
}
if (mode == ES_MODE_DAC || mode == ES_MODE_DAC_ADC)
{
reg = reg_read(ES8388_DACCONTROL1);
reg = reg & 0xf9;
reg_write(ES8388_DACCONTROL1, reg | (fmt << 1));
}
return RT_EOK;
}
void es8388_volume_set(rt_uint8_t volume)
{
if (volume > 100)
volume = 100;
volume /= 3;
reg_write(ES8388_DACCONTROL24, volume);
reg_write(ES8388_DACCONTROL25, volume);
}
rt_uint8_t es8388_volume_get(void)
{
rt_uint8_t volume;
volume = reg_read(ES8388_DACCONTROL24);
if (volume == 0xff)
{
volume = 0;
}
else
{
volume *= 3;
if (volume == 99)
volume = 100;
}
return volume;
}
void es8388_pa_power(rt_bool_t enable)
{
rt_pin_mode(es_dev.pin, PIN_MODE_OUTPUT);
if (enable)
{
rt_pin_write(es_dev.pin, PIN_HIGH);
}
else
{
rt_pin_write(es_dev.pin, PIN_LOW);
}
}
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-31 Zero-Free first implementation
*/
#ifndef __DRV_ES8388_H__
#define __DRV_ES8388_H__
/* ES8388 register space */
#define ES8388_CONTROL1 0x00
#define ES8388_CONTROL2 0x01
#define ES8388_CHIPPOWER 0x02
#define ES8388_ADCPOWER 0x03
#define ES8388_DACPOWER 0x04
#define ES8388_CHIPLOPOW1 0x05
#define ES8388_CHIPLOPOW2 0x06
#define ES8388_ANAVOLMANAG 0x07
#define ES8388_MASTERMODE 0x08
#define ES8388_ADCCONTROL1 0x09
#define ES8388_ADCCONTROL2 0x0a
#define ES8388_ADCCONTROL3 0x0b
#define ES8388_ADCCONTROL4 0x0c
#define ES8388_ADCCONTROL5 0x0d
#define ES8388_ADCCONTROL6 0x0e
#define ES8388_ADCCONTROL7 0x0f
#define ES8388_ADCCONTROL8 0x10
#define ES8388_ADCCONTROL9 0x11
#define ES8388_ADCCONTROL10 0x12
#define ES8388_ADCCONTROL11 0x13
#define ES8388_ADCCONTROL12 0x14
#define ES8388_ADCCONTROL13 0x15
#define ES8388_ADCCONTROL14 0x16
#define ES8388_DACCONTROL1 0x17
#define ES8388_DACCONTROL2 0x18
#define ES8388_DACCONTROL3 0x19
#define ES8388_DACCONTROL4 0x1a
#define ES8388_DACCONTROL5 0x1b
#define ES8388_DACCONTROL6 0x1c
#define ES8388_DACCONTROL7 0x1d
#define ES8388_DACCONTROL8 0x1e
#define ES8388_DACCONTROL9 0x1f
#define ES8388_DACCONTROL10 0x20
#define ES8388_DACCONTROL11 0x21
#define ES8388_DACCONTROL12 0x22
#define ES8388_DACCONTROL13 0x23
#define ES8388_DACCONTROL14 0x24
#define ES8388_DACCONTROL15 0x25
#define ES8388_DACCONTROL16 0x26
#define ES8388_DACCONTROL17 0x27
#define ES8388_DACCONTROL18 0x28
#define ES8388_DACCONTROL19 0x29
#define ES8388_DACCONTROL20 0x2a
#define ES8388_DACCONTROL21 0x2b
#define ES8388_DACCONTROL22 0x2c
#define ES8388_DACCONTROL23 0x2d
#define ES8388_DACCONTROL24 0x2e
#define ES8388_DACCONTROL25 0x2f
#define ES8388_DACCONTROL26 0x30
#define ES8388_DACCONTROL27 0x31
#define ES8388_DACCONTROL28 0x32
#define ES8388_DACCONTROL29 0x33
#define ES8388_DACCONTROL30 0x34
enum es8388_mode
{
ES_MODE_NONE = 0x00,
ES_MODE_DAC = 0x01,
ES_MODE_ADC = 0x02,
ES_MODE_DAC_ADC = 0x03,
ES_MODE_LINE = 0x04,
ES_MODE_MAX = 0x06,
};
enum es8388_format
{
ES_FMT_NORMAL = 0,
ES_FMT_LEFT = 1,
ES_FMT_RIGHT = 2,
ES_FMT_DSP = 3,
};
rt_err_t es8388_init(const char *i2c_name, rt_uint16_t pin);
rt_err_t es8388_start(enum es8388_mode mode);
rt_err_t es8388_stop(enum es8388_mode mode);
rt_err_t es8388_fmt_set(enum es8388_mode mode, enum es8388_format fmt);
void es8388_volume_set(rt_uint8_t volume);
rt_uint8_t es8388_volume_get(void);
void es8388_pa_power(rt_bool_t enable);
#endif
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-31 Zero-Free first implementation
*/
#include <board.h>
#include "drv_es8388.h"
#define DBG_TAG "drv.mic"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define RX_FIFO_SIZE (1024)
struct mic_device
{
struct rt_audio_device audio;
struct rt_audio_configure record_config;
rt_uint8_t *rx_fifo;
rt_uint8_t volume;
};
static struct mic_device mic_dev = {0};
static rt_uint16_t zero_frame[2] = {0};
static SAI_HandleTypeDef SAI1B_Handler = {0};
static DMA_HandleTypeDef SAI1_RXDMA_Handler = {0};
extern SAI_HandleTypeDef SAI1A_Handler;
extern DMA_HandleTypeDef SAI1_RXDMA_Handler;
extern void SAIA_Frequency_Set(uint32_t frequency);
void SAIB_Init(void)
{
HAL_SAI_DeInit(&SAI1B_Handler);
SAI1B_Handler.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
SAI1B_Handler.Instance = SAI1_Block_B;
SAI1B_Handler.Init.AudioMode = SAI_MODESLAVE_RX;
SAI1B_Handler.Init.Synchro = SAI_SYNCHRONOUS;
SAI1B_Handler.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
SAI1B_Handler.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
SAI1B_Handler.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
SAI1B_Handler.Init.MonoStereoMode = SAI_MONOMODE;
SAI1B_Handler.Init.Protocol = SAI_FREE_PROTOCOL;
SAI1B_Handler.Init.DataSize = SAI_DATASIZE_16;
SAI1B_Handler.Init.FirstBit = SAI_FIRSTBIT_MSB;
SAI1B_Handler.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;
SAI1B_Handler.FrameInit.FrameLength = 64;
SAI1B_Handler.FrameInit.ActiveFrameLength = 32;
SAI1B_Handler.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
SAI1B_Handler.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
SAI1B_Handler.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;
SAI1B_Handler.SlotInit.FirstBitOffset = 0;
SAI1B_Handler.SlotInit.SlotSize = SAI_SLOTSIZE_32B;
SAI1B_Handler.SlotInit.SlotNumber = 2;
SAI1B_Handler.SlotInit.SlotActive = SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1;
HAL_SAI_Init(&SAI1B_Handler);
__HAL_SAI_ENABLE(&SAI1B_Handler);
/* Configure DMA used for SAI1 */
__HAL_RCC_DMA2_CLK_ENABLE();
SAI1_RXDMA_Handler.Init.Request = DMA_REQUEST_1;
SAI1_RXDMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
SAI1_RXDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;
SAI1_RXDMA_Handler.Init.MemInc = DMA_MINC_ENABLE;
SAI1_RXDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
SAI1_RXDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
SAI1_RXDMA_Handler.Init.Mode = DMA_CIRCULAR;
SAI1_RXDMA_Handler.Init.Priority = DMA_PRIORITY_HIGH;
SAI1_RXDMA_Handler.Instance = DMA2_Channel2;
__HAL_LINKDMA(&SAI1B_Handler, hdmarx, SAI1_RXDMA_Handler);
HAL_DMA_DeInit(&SAI1_RXDMA_Handler);
HAL_DMA_Init(&SAI1_RXDMA_Handler);
__HAL_DMA_ENABLE(&SAI1_RXDMA_Handler);
HAL_NVIC_SetPriority(DMA2_Channel2_IRQn, 0x01, 0);
HAL_NVIC_EnableIRQ(DMA2_Channel2_IRQn);
}
void SAIB_Channels_Set(uint8_t channels)
{
if (channels == 1)
{
SAI1B_Handler.Init.MonoStereoMode = SAI_MONOMODE;
}
else
{
SAI1B_Handler.Init.MonoStereoMode = SAI_STEREOMODE;
}
__HAL_SAI_DISABLE(&SAI1B_Handler);
HAL_SAI_Init(&SAI1B_Handler);
__HAL_SAI_ENABLE(&SAI1B_Handler);
}
void DMA2_Channel2_IRQHandler(void)
{
HAL_DMA_IRQHandler(&SAI1_RXDMA_Handler);
}
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[0], RX_FIFO_SIZE / 2);
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[RX_FIFO_SIZE / 2], RX_FIFO_SIZE / 2);
}
static rt_err_t mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct mic_device *mic_dev;
RT_ASSERT(audio != RT_NULL);
mic_dev = (struct mic_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_INPUT | AUDIO_TYPE_MIXER;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_INPUT: /* Provide capabilities of INPUT unit */
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = mic_dev->record_config.samplerate;
caps->udata.config.channels = mic_dev->record_config.channels;
caps->udata.config.samplebits = mic_dev->record_config.samplebits;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = mic_dev->record_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = mic_dev->record_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = mic_dev->record_config.samplebits;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_MIXER: /* report the Mixer Units */
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_LINE;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = mic_dev->volume;
break;
case AUDIO_MIXER_LINE:
break;
default:
result = -RT_ERROR;
break;
}
break;
}
default:
result = -RT_ERROR;
break;
}
return result;
}
static rt_err_t mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct mic_device *mic_dev;
RT_ASSERT(audio != RT_NULL);
mic_dev = (struct mic_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_VOLUME:
{
rt_uint32_t volume = caps->udata.value;
mic_dev->volume = volume;
LOG_D("set volume %d", volume);
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_INPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
SAIA_Frequency_Set(caps->udata.config.samplerate);
HAL_SAI_DMAStop(&SAI1B_Handler);
SAIB_Channels_Set(caps->udata.config.channels);
HAL_SAI_Transmit(&SAI1A_Handler, (uint8_t *)&zero_frame[0], 2, 0);
HAL_SAI_Receive_DMA(&SAI1B_Handler, mic_dev->rx_fifo, RX_FIFO_SIZE / 2);
/* save configs */
mic_dev->record_config.samplerate = caps->udata.config.samplerate;
mic_dev->record_config.channels = caps->udata.config.channels;
mic_dev->record_config.samplebits = caps->udata.config.samplebits;
LOG_D("set samplerate %d", mic_dev->record_config.samplerate);
LOG_D("set channels %d", mic_dev->record_config.channels);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
mic_dev->record_config.samplerate = caps->udata.config.samplerate;
LOG_D("set channels %d", mic_dev->record_config.channels);
break;
}
case AUDIO_DSP_CHANNELS:
{
mic_dev->record_config.channels = caps->udata.config.channels;
LOG_D("set channels %d", mic_dev->record_config.channels);
break;
}
default:
break;
}
break;
}
default:
break;
}
return result;
}
static rt_err_t mic_init(struct rt_audio_device *audio)
{
struct mic_device *mic_dev;
RT_ASSERT(audio != RT_NULL);
mic_dev = (struct mic_device *)audio->parent.user_data;
SAIB_Init();
/* set default params */
SAIB_Channels_Set(mic_dev->record_config.channels);
return RT_EOK;
}
static rt_err_t mic_start(struct rt_audio_device *audio, int stream)
{
struct mic_device *mic_dev;
RT_ASSERT(audio != RT_NULL);
mic_dev = (struct mic_device *)audio->parent.user_data;
if (stream == AUDIO_STREAM_RECORD)
{
es8388_start(ES_MODE_ADC);
HAL_SAI_Transmit(&SAI1A_Handler, (uint8_t *)&zero_frame[0], 2, 0);
HAL_SAI_Receive_DMA(&SAI1B_Handler, mic_dev->rx_fifo, RX_FIFO_SIZE / 2);
}
return RT_EOK;
}
static rt_err_t mic_stop(struct rt_audio_device *audio, int stream)
{
if (stream == AUDIO_STREAM_RECORD)
{
HAL_SAI_DMAStop(&SAI1B_Handler);
HAL_SAI_Abort(&SAI1A_Handler);
es8388_stop(ES_MODE_ADC);
}
return RT_EOK;
}
static struct rt_audio_ops mic_ops =
{
.getcaps = mic_getcaps,
.configure = mic_configure,
.init = mic_init,
.start = mic_start,
.stop = mic_stop,
.transmit = RT_NULL,
.buffer_info = RT_NULL,
};
int rt_hw_mic_init(void)
{
rt_uint8_t *rx_fifo;
if (mic_dev.rx_fifo)
return RT_EOK;
rx_fifo = rt_malloc(RX_FIFO_SIZE);
if (rx_fifo == RT_NULL)
return -RT_ENOMEM;
rt_memset(rx_fifo, 0, RX_FIFO_SIZE);
mic_dev.rx_fifo = rx_fifo;
/* init default configuration */
{
mic_dev.record_config.samplerate = 44100;
mic_dev.record_config.channels = 2;
mic_dev.record_config.samplebits = 16;
mic_dev.volume = 55;
}
/* register sound device */
mic_dev.audio.ops = &mic_ops;
rt_audio_register(&mic_dev.audio, "mic0", RT_DEVICE_FLAG_RDONLY, &mic_dev);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_mic_init);
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-31 Zero-Free first implementation
*/
#include <board.h>
#include "drv_sound.h"
#include "drv_es8388.h"
#define DBG_TAG "drv.sound"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define TX_FIFO_SIZE (2048)
struct sound_device
{
struct rt_audio_device audio;
struct rt_audio_configure replay_config;
rt_uint8_t *tx_fifo;
rt_uint8_t volume;
};
static struct sound_device snd_dev = {0};
SAI_HandleTypeDef SAI1A_Handler = {0};
DMA_HandleTypeDef SAI1_TXDMA_Handler = {0};
static void SAIA_Init(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/* Configure and enable PLLSAI1 clock to generate 45.714286MHz */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI2;
PeriphClkInit.PLLSAI2.PLLSAI2Source = RCC_PLLSOURCE_HSE;
PeriphClkInit.PLLSAI2.PLLSAI2M = 1;
PeriphClkInit.PLLSAI2.PLLSAI2N = 40;
PeriphClkInit.PLLSAI2.PLLSAI2P = RCC_PLLP_DIV7;
PeriphClkInit.PLLSAI2.PLLSAI2R = RCC_PLLR_DIV2;
PeriphClkInit.PLLSAI2.PLLSAI2ClockOut = RCC_PLLSAI2_SAI2CLK;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
HAL_SAI_DeInit(&SAI1A_Handler);
SAI1A_Handler.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
SAI1A_Handler.Instance = SAI1_Block_A;
SAI1A_Handler.Init.AudioMode = SAI_MODEMASTER_TX;
SAI1A_Handler.Init.Synchro = SAI_ASYNCHRONOUS;
SAI1A_Handler.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
SAI1A_Handler.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
SAI1A_Handler.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
SAI1A_Handler.Init.MonoStereoMode = SAI_STEREOMODE;
SAI1A_Handler.Init.Protocol = SAI_FREE_PROTOCOL;
SAI1A_Handler.Init.DataSize = SAI_DATASIZE_16;
SAI1A_Handler.Init.FirstBit = SAI_FIRSTBIT_MSB;
SAI1A_Handler.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE;
SAI1A_Handler.FrameInit.FrameLength = 64;
SAI1A_Handler.FrameInit.ActiveFrameLength = 32;
SAI1A_Handler.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION;
SAI1A_Handler.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
SAI1A_Handler.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT;
SAI1A_Handler.SlotInit.FirstBitOffset = 0;
SAI1A_Handler.SlotInit.SlotSize = SAI_SLOTSIZE_32B;
SAI1A_Handler.SlotInit.SlotNumber = 2;
SAI1A_Handler.SlotInit.SlotActive = SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1;
HAL_SAI_Init(&SAI1A_Handler);
__HAL_SAI_ENABLE(&SAI1A_Handler);
/* Configure DMA used for SAI1 */
__HAL_RCC_DMA2_CLK_ENABLE();
SAI1_TXDMA_Handler.Init.Request = DMA_REQUEST_1;
SAI1_TXDMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH;
SAI1_TXDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE;
SAI1_TXDMA_Handler.Init.MemInc = DMA_MINC_ENABLE;
SAI1_TXDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
SAI1_TXDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
SAI1_TXDMA_Handler.Init.Mode = DMA_CIRCULAR;
SAI1_TXDMA_Handler.Init.Priority = DMA_PRIORITY_HIGH;
SAI1_TXDMA_Handler.Instance = DMA2_Channel1;
__HAL_LINKDMA(&SAI1A_Handler, hdmatx, SAI1_TXDMA_Handler);
HAL_DMA_DeInit(&SAI1_TXDMA_Handler);
HAL_DMA_Init(&SAI1_TXDMA_Handler);
__HAL_DMA_ENABLE(&SAI1_TXDMA_Handler);
HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 0x01, 0);
HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);
}
void DMA2_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(&SAI1_TXDMA_Handler);
}
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
if (hsai == &SAI1A_Handler)
{
rt_audio_tx_complete(&snd_dev.audio);
}
}
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
if (hsai == &SAI1A_Handler)
{
rt_audio_tx_complete(&snd_dev.audio);
}
}
void SAIA_Frequency_Set(uint32_t frequency)
{
RCC_PeriphCLKInitTypeDef PeriphClkInit;
HAL_RCCEx_GetPeriphCLKConfig(&PeriphClkInit);
if ((frequency == SAI_AUDIO_FREQUENCY_11K) || (frequency == SAI_AUDIO_FREQUENCY_22K) || (frequency == SAI_AUDIO_FREQUENCY_44K))
{
/* Configure and enable PLLSAI1 clock to generate 45.714286MHz */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI2;
PeriphClkInit.PLLSAI2.PLLSAI2Source = RCC_PLLSOURCE_HSE;
PeriphClkInit.PLLSAI2.PLLSAI2M = 1;
PeriphClkInit.PLLSAI2.PLLSAI2N = 40;
PeriphClkInit.PLLSAI2.PLLSAI2ClockOut = RCC_PLLSAI2_SAI2CLK;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
}
else
{
/* Configure and enable PLLSAI1 clock to generate 49.142857MHz */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI2;
PeriphClkInit.PLLSAI2.PLLSAI2Source = RCC_PLLSOURCE_HSE;
PeriphClkInit.PLLSAI2.PLLSAI2M = 1;
PeriphClkInit.PLLSAI2.PLLSAI2N = 43;
PeriphClkInit.PLLSAI2.PLLSAI2P = RCC_PLLP_DIV7;
PeriphClkInit.PLLSAI2.PLLSAI2ClockOut = RCC_PLLSAI2_SAI2CLK;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
}
/* Disable SAI peripheral to allow access to SAI internal registers */
__HAL_SAI_DISABLE(&SAI1A_Handler);
/* Update the SAI audio frequency configuration */
SAI1A_Handler.Init.AudioFrequency = frequency;
HAL_SAI_Init(&SAI1A_Handler);
/* Enable SAI peripheral to generate MCLK */
__HAL_SAI_ENABLE(&SAI1A_Handler);
}
void SAIA_Channels_Set(uint8_t channels)
{
if (channels == 1)
{
SAI1A_Handler.Init.MonoStereoMode = SAI_MONOMODE;
}
else
{
SAI1A_Handler.Init.MonoStereoMode = SAI_STEREOMODE;
}
__HAL_SAI_DISABLE(&SAI1A_Handler);
HAL_SAI_Init(&SAI1A_Handler);
__HAL_SAI_ENABLE(&SAI1A_Handler);
}
/**
* RT-Thread Audio Device Driver Interface
*/
static rt_err_t sound_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
caps->udata.config.channels = snd_dev->replay_config.channels;
caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = snd_dev->replay_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_MIXER: /* report the Mixer Units */
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = es8388_volume_get();
break;
default:
result = -RT_ERROR;
break;
}
break;
}
default:
result = -RT_ERROR;
break;
}
return result;
}
static rt_err_t sound_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_VOLUME:
{
rt_uint8_t volume = caps->udata.value;
es8388_volume_set(volume);
snd_dev->volume = volume;
LOG_D("set volume %d", volume);
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
/* set samplerate */
SAIA_Frequency_Set(caps->udata.config.samplerate);
/* set channels */
SAIA_Channels_Set(caps->udata.config.channels);
/* save configs */
snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
snd_dev->replay_config.channels = caps->udata.config.channels;
snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
SAIA_Frequency_Set(caps->udata.config.samplerate);
snd_dev->replay_config.samplerate = caps->udata.config.samplerate;
LOG_D("set samplerate %d", snd_dev->replay_config.samplerate);
break;
}
case AUDIO_DSP_CHANNELS:
{
SAIA_Channels_Set(caps->udata.config.channels);
snd_dev->replay_config.channels = caps->udata.config.channels;
LOG_D("set channels %d", snd_dev->replay_config.channels);
break;
}
case AUDIO_DSP_SAMPLEBITS:
{
/* not support */
snd_dev->replay_config.samplebits = caps->udata.config.samplebits;
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
default:
break;
}
return result;
}
static rt_err_t sound_init(struct rt_audio_device *audio)
{
rt_err_t result = RT_EOK;
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
es8388_init("i2c3", GET_PIN(A, 5));
SAIA_Init();
/* set default params */
SAIA_Frequency_Set(snd_dev->replay_config.samplerate);
SAIA_Channels_Set(snd_dev->replay_config.channels);
return result;
}
static rt_err_t sound_start(struct rt_audio_device *audio, int stream)
{
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
if (stream == AUDIO_STREAM_REPLAY)
{
LOG_D("open sound device");
es8388_start(ES_MODE_DAC);
HAL_SAI_Transmit_DMA(&SAI1A_Handler, snd_dev->tx_fifo, TX_FIFO_SIZE / 2);
}
return RT_EOK;
}
static rt_err_t sound_stop(struct rt_audio_device *audio, int stream)
{
RT_ASSERT(audio != RT_NULL);
if (stream == AUDIO_STREAM_REPLAY)
{
HAL_SAI_DMAStop(&SAI1A_Handler);
es8388_stop(ES_MODE_DAC);
LOG_D("close sound device");
}
return RT_EOK;
}
static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
struct sound_device *snd_dev;
RT_ASSERT(audio != RT_NULL);
snd_dev = (struct sound_device *)audio->parent.user_data;
/**
* TX_FIFO
* +----------------+----------------+
* | block1 | block2 |
* +----------------+----------------+
* \ block_size /
*/
info->buffer = snd_dev->tx_fifo;
info->total_size = TX_FIFO_SIZE;
info->block_size = TX_FIFO_SIZE / 2;
info->block_count = 2;
}
static struct rt_audio_ops snd_ops =
{
.getcaps = sound_getcaps,
.configure = sound_configure,
.init = sound_init,
.start = sound_start,
.stop = sound_stop,
.transmit = RT_NULL,
.buffer_info = sound_buffer_info,
};
int rt_hw_sound_init(void)
{
rt_uint8_t *tx_fifo;
if (snd_dev.tx_fifo)
return RT_EOK;
tx_fifo = rt_malloc(TX_FIFO_SIZE);
if (tx_fifo == RT_NULL)
return -RT_ENOMEM;
rt_memset(tx_fifo, 0, TX_FIFO_SIZE);
snd_dev.tx_fifo = tx_fifo;
/* init default configuration */
{
snd_dev.replay_config.samplerate = 44100;
snd_dev.replay_config.channels = 2;
snd_dev.replay_config.samplebits = 16;
snd_dev.volume = 55;
}
/* register sound device */
snd_dev.audio.ops = &snd_ops;
rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Date Author Notes
* 2019-07-31 Zero-Free first implementation
*/
#ifndef __DRV_SOUND_H__
#define __DRV_SOUND_H__
int rt_hw_sound_init(void);
int rt_hw_mic_init(void);
#endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册