rt-thread audio框架移植stm32 adc+dac,用wavplayer录音和播放

发布于:2025-08-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

D1 参考

rt-thread官方sdk中,正点原子stm32f429-atk-appollo的board中有audio文件夹,包括了mic/play的程序,wm8978的库文件
在这里插入图片描述

因为我们基于stm32h750内置adc+dac设计,所以不需要wm8978.c/h。只需要移植drv_sound.c和drv_mic.c

D2 工程名字和路径

在这里插入图片描述

D3 drv_sound.c所作修改简单说明

  • 定义了一个正弦波用于测试,注意范围是0-4095
  • 定义了一个Nocache buffer for DMA,这个非常重要。我在这里最初没有使用Nocache buffer,折腾了很久,声音异常。
  • 这些参数的定义,来自CubeMX裸机例程。因此先把CubMX裸机例程调通,非常重要。
/****************************************************************************
* @brief 与cubeMX硬件初始化相关的变量定义
****************************************************************************/
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))

DAC_HandleTypeDef hdac1;
DMA_HandleTypeDef hdma_dac1_ch2;

TIM_HandleTypeDef htim2;

#define TONE_FREQ (440)
#define SIN_TABLE_SIZE (16000 / TONE_FREQ)  /*16kHz/440Hz=36 pts*/
#define M_PI 3.14159265358979323846
uint16_t sin_table[SIN_TABLE_SIZE];

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t dac_buffer[TX_DMA_FIFO_SIZE]; // Nocache buffer for DMA, important!

// static void MX_ADC1_Init(void);
static void MX_DAC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);

static void generate_sin_table(void)
{
    for (int i = 0; i < SIN_TABLE_SIZE; i++)
    {
        float angle = 2 * M_PI * i / SIN_TABLE_SIZE;
        sin_table[i] = (uint16_t)((sin(angle) + 1.0f) * 2047.5f); // 12-bit DAC, 0-4095 range
    }
}
  • 播放要实现的几个dev_audio设备驱动框架的几个函数包括:stm32_player_getcaps,stm32_player_configure, stm32_player_init, stm32_player_start, stm32_player_stop, stm32_player_buffer_info
  • 只有stm32_player_init, stm32_player_start是必须需要用户要实现的。 stm32_player_buffer_info也是要实现的,不过很简单,基本不用改。
  • 如果配置是CubeMX产生的,而且是固定的,那么stm32_player_getcaps,stm32_player_configure可以不用实现,直接返回RET_OK即可。
  • 对于本文,因为我们默认配置rt-thread的软件库wavplayer使用,wavplayer定义了AUDIO_TYPE_OUTPUT–>AUDIO_DSP_PARAM参数配置,因此我们在stm32_player_configure中实现了这个操作方法。主要是用来配置采样率。因为不同的音频,采样率不同。
  • 另外,因为音频驱动框架默认使用DMA模式,因此需要在传输完成和半传输完成中调用rt_audio_tx_complete,用于申请切换缓冲区
/****************************************************************************
* @brief 要实现的几个dev_audio接口函数
****************************************************************************/
void HAL_DACEx_ConvHalfCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{
    if (hdac->Instance == DAC1)
    {
        rt_audio_tx_complete(&_stm32_audio_play.audio);
    }
}


void HAL_DACEx_ConvCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{
    if (hdac->Instance == DAC1)
    {
        rt_audio_tx_complete(&_stm32_audio_play.audio);
    }
}


static rt_err_t stm32_player_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    return result;
}

static rt_err_t  stm32_player_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    switch (caps->main_type)
    {
    case AUDIO_TYPE_MIXER:
    {
        switch (caps->sub_type)
        {
        case AUDIO_MIXER_VOLUME:
        {
            int volume = caps->udata.value;
            st_audio->replay_volume = volume;

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }

        break;
    }
  
    case AUDIO_TYPE_OUTPUT:
    {
        switch (caps->sub_type)
        {
        case AUDIO_DSP_PARAM:
        {
            struct rt_audio_configure config = caps->udata.config;

            st_audio->replay_config.samplerate = config.samplerate;
            st_audio->replay_config.samplebits = config.samplebits;
            st_audio->replay_config.channels = config.channels;
            /*只添加调整采样率的功能*/
            __HAL_TIM_SET_AUTORELOAD(&htim2, 240000000 / st_audio->replay_config.samplerate - 1);
            rt_kprintf("samplerate: %d\n", st_audio->replay_config.samplerate);
            rt_kprintf("samplebits: %d\n", st_audio->replay_config.samplebits);
            rt_kprintf("channels: %d\n", st_audio->replay_config.channels);

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }
        break;
    }

    default:
        break;
    }

    return result;
}

static rt_err_t stm32_player_init(struct rt_audio_device *audio)
{
    MX_DMA_Init();
    MX_DAC1_Init();
    MX_TIM2_Init();

    return RT_EOK;
}

static rt_err_t stm32_player_start(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_TIM_Base_Start(&htim2);
        HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_2, (uint32_t *) _stm32_audio_play.tx_fifo, TX_DMA_FIFO_SIZE / 2, DAC_ALIGN_12B_R);
    }

    return RT_EOK;
}

static rt_err_t stm32_player_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_2);
        HAL_TIM_Base_Stop(&htim2);
    }

    return RT_EOK;
}

static void stm32_player_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
    /**
     *               TX_FIFO
     * +----------------+----------------+
     * |     block1     |     block2     |
     * +----------------+----------------+
     *  \  block_size  /
     */
    info->buffer = _stm32_audio_play.tx_fifo;
    info->total_size = TX_DMA_FIFO_SIZE;
    info->block_size = TX_DMA_FIFO_SIZE / 2;
    info->block_count = 2;
}
static struct rt_audio_ops _p_audio_ops =
{
    .getcaps     = stm32_player_getcaps,
    .configure   = stm32_player_configure,
    .init        = stm32_player_init,
    .start       = stm32_player_start,
    .stop        = stm32_player_stop,
    .transmit    = RT_NULL,
    .buffer_info = stm32_player_buffer_info,
};

int rt_hw_sound_init(void)
{
    rt_uint8_t *tx_fifo;

    /* player */
    tx_fifo = /*rt_malloc(TX_DMA_FIFO_SIZE)*/ dac_buffer; /*替换为nocache的dac_buffer, 非常重要! 折腾了几个小时!*/
    if (tx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }
    rt_memset(tx_fifo, 0, TX_DMA_FIFO_SIZE);
    _stm32_audio_play.tx_fifo = tx_fifo;

    /* register sound device */
    _stm32_audio_play.audio.ops = &_p_audio_ops;
    rt_audio_register(&_stm32_audio_play.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &_stm32_audio_play);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_sound_init);
  • 接下来定义了一个播放线程,用于播放前面定义的sine信号,输出440Hz钢琴标准音。
  • 在线程函数play_thread_entry循环体中,只需要调用rt_device_write不停的write即可。这个函数自带阻塞性质,因此不需要额外添加rt_thread_mdelay(也必须不能添加,否则有声音断断续续)
  • 通过MSH_CMD_EXPORT导出到msh中执行,方便测试
/****************************************************************************
* @brief 测试播放sin信号线程, 导入到msh
****************************************************************************/
void play_thread_entry(void *parameter)
{
    rt_device_t sound_dev;
    rt_size_t written;
    
    generate_sin_table();
    sound_dev = rt_device_find("sound0");
    rt_device_open(sound_dev, RT_DEVICE_OFLAG_WRONLY);

    while(1){
        written = rt_device_write(sound_dev, 0, sin_table, sizeof(sin_table));
        // rt_kprintf("Wrote %d bytes to sound device\n", written);
        // rt_thread_mdelay(1);
    }
}

void play_sine()
{
    rt_thread_t thread;
    thread = rt_thread_create("play_thread", play_thread_entry, RT_NULL, 2048, 2, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
}
MSH_CMD_EXPORT(play_sine, play_sine_test);
  • 接下来的部分是硬件初始化函数部分。主要包括 CubeMX底层硬件初始化, 包括xx_MSPInit(), xx_Init(), xx_IRQHandler()。
  • 直接从CubeMX生成工程拷贝过来的,不再赘述了。这部分的内容,之前已经在rt-thread中经过测试的,是可以正常工作的。
  • 注意我们TIM2配置的period频率是16kHz,这是默认值。可以通过stm32_player_configure修改。

D4 drv_mic.c所作修改简单说明

  • 定义了一个Nocache buffer for DMA,这个非常重要。我在这里最初没有使用Nocache buffer,折腾了很久,录音声音异常。
  • 这些参数的定义,来自CubeMX裸机例程。因此先把CubMX裸机例程调通,非常重要。
/****************************************************************************
* @brief 与cubeMX硬件初始化相关的变量定义
****************************************************************************/
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

extern TIM_HandleTypeDef htim2;

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t adc_buffer[RX_DMA_FIFO_SIZE]; // Nocache buffer for DMA, important!

static void MX_ADC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);
  • 录音要实现的几个dev_audio设备驱动框架的几个函数包括:stm32_mic_getcaps,stm32_mic_configure, stm32_mic_init, stm32_mic_start, stm32_mic_stop, stm32_mic_buffer_info
  • 只有stm32_mic_init, stm32_mic_start是必须需要用户要实现的。
  • 如果配置是CubeMX产生的,而且是固定的,那么stm32_mic_getcaps,stm32_mic_configure可以不用实现,直接返回RET_OK即可。
  • 对于本文,因为我们默认配置rt-thread的软件库wavplayer使用,wavplayer定义了AUDIO_TYPE_INPUT–>AUDIO_DSP_PARAM参数配置,因此我们在stm32_mic_configure中实现了这个操作方法。主要是用来配置采样率。
  • 另外,因为音频驱动框架默认使用DMA模式,因此需要在传输完成和半传输完成中调用rt_audio_rx_done,用于申请切换缓冲区
/****************************************************************************
* @brief 要实现的几个dev_audio接口函数
****************************************************************************/
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[RX_DMA_FIFO_SIZE / 2], RX_DMA_FIFO_SIZE / 2);
    }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[0], RX_DMA_FIFO_SIZE / 2);
    }
}

static rt_err_t stm32_mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    return result;
}

static rt_err_t  stm32_mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    switch (caps->main_type)
    {
    case AUDIO_TYPE_INPUT:
    {
        switch (caps->sub_type)
        {

        case AUDIO_DSP_PARAM:
        {
            _stm32_audio_record.config.samplerate = caps->udata.config.samplerate;
            _stm32_audio_record.config.channels   = caps->udata.config.channels;
            _stm32_audio_record.config.samplebits = caps->udata.config.samplebits;
            /*只添加调整采样率的功能*/
            __HAL_TIM_SET_AUTORELOAD(&htim2, 240000000 / _stm32_audio_record.config.samplerate - 1);
            rt_kprintf("samplerate: %d\n", _stm32_audio_record.config.samplerate);
            rt_kprintf("samplebits: %d\n", _stm32_audio_record.config.samplebits);
            rt_kprintf("channels: %d\n", _stm32_audio_record.config.channels);

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }
        /* After set config, MCLK will stop */
        break;
    }

    default:
        break;
    }

    return result;
}

static rt_err_t stm32_mic_init(struct rt_audio_device *audio)
{
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_TIM2_Init();

    return RT_EOK;
}

static rt_err_t stm32_mic_start(struct rt_audio_device *audio, int stream)
{
    rt_err_t result = RT_EOK;

    if (stream == AUDIO_STREAM_RECORD)
    {
        HAL_TIM_Base_Start(&htim2);
        HAL_ADC_Start_DMA(&hadc1, (uint32_t *) _stm32_audio_record.rx_fifo, RX_DMA_FIFO_SIZE/2);
    }

    return result;
}

static rt_err_t stm32_mic_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_RECORD)
    {
        HAL_ADC_Stop_DMA(&hadc1);
        HAL_TIM_Base_Stop(&htim2);
    }

    return RT_EOK;
}

static struct rt_audio_ops _mic_audio_ops =
{
    .getcaps     = stm32_mic_getcaps,
    .configure   = stm32_mic_configure,
    .init        = stm32_mic_init,
    .start       = stm32_mic_start,
    .stop        = stm32_mic_stop,
    .transmit    = RT_NULL,
    .buffer_info = RT_NULL,
};

int rt_hw_mic_init(void)
{
    struct rt_audio_device *audio = &_stm32_audio_record.audio;
    /* mic default */
    _stm32_audio_record.rx_fifo = /*rt_calloc(1, RX_DMA_FIFO_SIZE)*/ adc_buffer; /*替换为nocache的adc_buffer, 非常重要! 折腾了几个小时!*/
    if (_stm32_audio_record.rx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }

    _stm32_audio_record.config.channels = 1;
    _stm32_audio_record.config.samplerate = 16000;
    _stm32_audio_record.config.samplebits = 16;

    /* register mic device */
    audio->ops = &_mic_audio_ops;
    rt_audio_register(audio, "mic0", RT_DEVICE_FLAG_RDONLY, &_stm32_audio_record);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_mic_init);
  • 我们没有定义录音线程。我们直接使用wavplayer进行录音测试。
  • 接下来的部分是硬件初始化函数部分。主要包括 CubeMX底层硬件初始化, 包括xx_MSPInit(), xx_Init(), xx_IRQHandler()。
  • 直接从CubeMX生成工程拷贝过来的,不再赘述了。这部分的内容,之前已经在rt-thread中经过测试的,是可以正常工作的。
  • 注意我们TIM2配置的period频率是16kHz,这是默认值。可以通过stm32_mic_configure修改。
  • 注意TIM2的MSPInit()函数,定义在drv_sound.c中,因为2个音频设备都使用了TIM2, 这个函数是全局的,只能定义在一个地方。

D5 Menuconfig配置

  • 需要开启rt-thread的audio设备
    在这里插入图片描述

  • 需要开启rt-thread的online多媒体插件wavplayer
    在这里插入图片描述

  • wavplayer默认开启了播放。录音需要开启, 并设置设备名字为"mic0"
    在这里插入图片描述

  • 还需要勾选cherryusb设备msc class, 因为我们要用ramdisk作为msc设备。便于从pc上拷贝wav文件到ramdisk。

D6 程序在工程中的位置

在这里插入图片描述

D7 cherryusb_msc开启

E:\Share\rt-thread\bsp\stm32\my_boards\my_stm32_boards\h750_artpi\board\port2\msc_blkdev_template.c

// #define BLOCK_DEVICE_NAME "W25Q64S"
#define BLOCK_DEVICE_NAME "ramdisk0"
// #define BLOCK_DEVICE_NAME "sd0"

D8 wavplayer的修改

  • wavplayer_cmd.c的bug修改, wavplay_args_prase函数中调用optparse_init()函数少了一个参数argc
    /* Parse cmd */
    optparse_init(&options, argc, argv);
  • wavrecorder_cmd.c的bug修改, wavrecord_args_prase函数中调用optparse_init()函数少了一个参数argc
    /* Parse cmd */
    optparse_init(&options, argc, argv);
  • wavplayer.c中wavplayer_entry函数修改, 因为要适配DAC需要使用12bits unsigned int,wav原始数据是16bits signed,需要转换。
  case PLAYER_EVENT_NONE:
  {
      /* read raw data from file stream */
      size = fread(player.buffer, WP_BUFFER_SIZE, 1, player.fp);
      if (size != 1)
      {
          /* FILE END*/
          player.state = PLAYER_STATE_STOPED;
      }
      else
      {
          int16_t *p = (int16_t *)player.buffer;
          for(int i=0; i < WP_BUFFER_SIZE/2; i++){ /*Add by xujc, for 12bits DAC play*/
              p[i] += 32768;
              p[i] >>= 4;
          }

          /*witte data to sound device*/
          rt_device_write(player.device, 0, player.buffer, WP_BUFFER_SIZE);
      }
      break;
  }
  • wavrecorder.c中wavrecord_entry函数修改, 因为要适配ADC需要使用16bits unsigned,wav原始数据是16bits signed,需要转换。
    /* read raw data from sound device */
    size =  rt_device_read(record.device, 0, record.buffer, WR_BUFFER_SIZE);
    if (size)
    {
        int16_t *buffer = (int16_t *)record.buffer;
        for(int i = 0; i < size/2; i++) {
            buffer[i] = (int16_t)(((uint16_t *)record.buffer)[i] - 32768);
        }

        fwrite(record.buffer, size, 1, record.fp);
        total_length += size;
    }

D9 测试

  • play_sine测试ok

msh />help
RT-Thread shell commands:
play_sine        - play_sine_test
sdram_test       - sdram test
reboot           - Reboot System
ramdisk          - create ramdisk device.
wavplay          - play wav music
wavrecord        - record wav music
usbh_init        - init usb host
usbh_deinit      - deinit usb host
lsusb            - ls usb devices
fal              - FAL (Flash Abstraction Layer) operate.
list_blk         - dump all of blks information
adc              - adc [option]
dac              - dac function
pin              - pin [option]
sf               - SPI Flash operate.
clear            - clear the terminal screen
version          - show RT-Thread version information
list             - list objects
help             - RT-Thread shell help
ps               - List threads in the system
free             - Show the memory usage in the system
ls               - List information about the FILEs.
cp               - Copy SOURCE to DEST.
mv               - Rename SOURCE to DEST.
cat              - Concatenate FILE(s)
rm               - Remove(unlink) the FILE(s).
cd               - Change the shell working directory.
pwd              - Print the name of the current working directory.
mkdir            - Create the DIRECTORY.
mkfs             - format disk with file system
mount            - mount <device> <mountpoint> <fstype>
umount           - Unmount the mountpoint
df               - disk free
echo             - echo string to file
tail             - print the last N - lines data of the given file
backtrace        - print backtrace of a thread

msh />play_sine
  • 播放wavplayer自带的3个example wav文件(这个非常不错,自带wav,省得去找)
    在这里插入图片描述

  • 将3个wav文件拷贝到ramdisk msc u盘中
    在这里插入图片描述

  • 用wavplay命令来播放wav文件,效果非常不错。

msh />ls ramdisk
Directory ramdisk:
System Volume Inform<DIR>
sample3.wav         1411248
sample1.wav         497904
sample2.wav         705644
msh />wavplay -h
usage: wavplay [option] [target] ...

usage options:
  -h,     --help                     Print defined help message.
  -s URI, --start=URI                Play wav music with URI(local files).
  -t,     --stop                     Stop playing music.
  -p,     --pause                    Pause the music.
  -r,     --resume                   Resume the music.
  -v lvl, --volume=lvl               Change the volume(0~99).
  -d,     --dump                     Dump play relevant information.
msh />wavplay -s ramdisk/sample1.wav
samplerate: 44100
samplebits: 16
channels: 2
[I/WAV_PLAYER] play start, uri=ramdisk/sample1.wav
msh />[I/WAV_PLAYER] play end

msh />wavplay -s ramdisk/sample2.wav
samplerate: 44100
samplebits: 16
channels: 2
[I/WAV_PLAYER] play start, uri=ramdisk/sample2.wav
msh />[I/WAV_PLAYER] play end

msh />wavplay -s ramdisk/sample3.wav
samplerate: 44100
samplebits: 16
channels: 2
[I/WAV_PLAYER] play start, uri=ramdisk/sample3.wav
msh />[I/WAV_PLAYER] play end

  • 录音和播放测试
  • 我说一段话,然后用wavrecord录音,以16kHz录音,录音文件保存在ramdisk中
  • 用wavplay命令来播放录音文件,效果非常不错。

msh />wavrecord
usage: wavrecord [option] [target] ...

usage options:
  -h,     --help                        Print defined help message.
  -s file --start=file  <samplerate> <channels> <samplebits>
                                        record wav music to filesystem.
  -t,     --stop                        Stop record.
msh />wavrecord -s ramdisk/my.wav 16000 1 16
Information:
samplerate 16000
channels 1
samplerate: 16000
samplebits: 16
channels: 1
msh />wavrecord -t
msh />ls ramdisk
Directory ramdisk:
System Volume Inform<DIR>
sample3.wav         1411248
sample1.wav         497904
sample2.wav         705644
my.wav              311340
msh />wavplay -s ramdisk/my.wav
samplerate: 16000
samplebits: 16
channels: 1
[I/WAV_PLAYER] play start, uri=ramdisk/my.wav
msh />[I/WAV_PLAYER] play end

  • 电脑打开抖音,播放一段仙剑奇侠传主题曲《此生不换》
  • 开启wavrecord录音, 这次以44100采样率录制,音质较高, 录音时间较长,文件较大
  • 用wavplay命令来播放录音文件,效果非常不错。

msh />wavrecord -s ramdisk/my2.wav 44100 1 16
Information:
samplerate 44100
channels 1
samplerate: 44100
samplebits: 16
channels: 1
msh />wavrecord -t
msh />ls ramdisk
Directory ramdisk:
System Volume Inform<DIR>
sample3.wav         1411248
sample1.wav         497904
sample2.wav         705644
my.wav              311340
my2.wav             5419008
msh />wavplay -s ramdisk/my2.wav
samplerate: 44100
samplebits: 16
channels: 1
[I/WAV_PLAYER] play start, uri=ramdisk/my2.wav

D10 附录1:drv_sound.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author         Notes
 * 2019-07-28     Ernest         the first version
 */

#include "board.h"
#include "drv_sound.h"

#define DBG_TAG              "drv.sound"
#define DBG_LVL              DBG_INFO
#include <rtdbg.h>

#define TX_DMA_FIFO_SIZE (2048)

struct stm32_audio
{
    struct rt_audio_device audio;
    struct rt_audio_configure replay_config;
    int replay_volume;
    rt_uint8_t *tx_fifo;
    rt_bool_t startup;
};
struct stm32_audio _stm32_audio_play = {0};





/****************************************************************************
* @brief 与cubeMX硬件初始化相关的变量定义
****************************************************************************/
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))

DAC_HandleTypeDef hdac1;
DMA_HandleTypeDef hdma_dac1_ch2;

TIM_HandleTypeDef htim2;

#define TONE_FREQ (440)
#define SIN_TABLE_SIZE (16000 / TONE_FREQ)  /*16kHz/440Hz=36 pts*/
#define M_PI 3.14159265358979323846
uint16_t sin_table[SIN_TABLE_SIZE];

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t dac_buffer[TX_DMA_FIFO_SIZE]; // Nocache buffer for DMA, important!

// static void MX_ADC1_Init(void);
static void MX_DAC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);

static void generate_sin_table(void)
{
    for (int i = 0; i < SIN_TABLE_SIZE; i++)
    {
        float angle = 2 * M_PI * i / SIN_TABLE_SIZE;
        sin_table[i] = (uint16_t)((sin(angle) + 1.0f) * 2047.5f); // 12-bit DAC, 0-4095 range
    }
}







/****************************************************************************
* @brief 要实现的几个dev_audio接口函数
****************************************************************************/
void HAL_DACEx_ConvHalfCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{
    if (hdac->Instance == DAC1)
    {
        rt_audio_tx_complete(&_stm32_audio_play.audio);
    }
}


void HAL_DACEx_ConvCpltCallbackCh2(DAC_HandleTypeDef* hdac)
{
    if (hdac->Instance == DAC1)
    {
        rt_audio_tx_complete(&_stm32_audio_play.audio);
    }
}


static rt_err_t stm32_player_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    return result;
}

static rt_err_t  stm32_player_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;
    struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    switch (caps->main_type)
    {
    case AUDIO_TYPE_MIXER:
    {
        switch (caps->sub_type)
        {
        case AUDIO_MIXER_VOLUME:
        {
            int volume = caps->udata.value;
            st_audio->replay_volume = volume;

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }

        break;
    }
  
    case AUDIO_TYPE_OUTPUT:
    {
        switch (caps->sub_type)
        {
        case AUDIO_DSP_PARAM:
        {
            struct rt_audio_configure config = caps->udata.config;

            st_audio->replay_config.samplerate = config.samplerate;
            st_audio->replay_config.samplebits = config.samplebits;
            st_audio->replay_config.channels = config.channels;
            /*只添加调整采样率的功能*/
            __HAL_TIM_SET_AUTORELOAD(&htim2, 240000000 / st_audio->replay_config.samplerate - 1);
            rt_kprintf("samplerate: %d\n", st_audio->replay_config.samplerate);
            rt_kprintf("samplebits: %d\n", st_audio->replay_config.samplebits);
            rt_kprintf("channels: %d\n", st_audio->replay_config.channels);

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }
        break;
    }

    default:
        break;
    }

    return result;
}

static rt_err_t stm32_player_init(struct rt_audio_device *audio)
{
    MX_DMA_Init();
    MX_DAC1_Init();
    MX_TIM2_Init();

    return RT_EOK;
}

static rt_err_t stm32_player_start(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_TIM_Base_Start(&htim2);
        HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_2, (uint32_t *) _stm32_audio_play.tx_fifo, TX_DMA_FIFO_SIZE / 2, DAC_ALIGN_12B_R);
    }

    return RT_EOK;
}

static rt_err_t stm32_player_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_REPLAY)
    {
        HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_2);
        HAL_TIM_Base_Stop(&htim2);
    }

    return RT_EOK;
}

static void stm32_player_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
    /**
     *               TX_FIFO
     * +----------------+----------------+
     * |     block1     |     block2     |
     * +----------------+----------------+
     *  \  block_size  /
     */
    info->buffer = _stm32_audio_play.tx_fifo;
    info->total_size = TX_DMA_FIFO_SIZE;
    info->block_size = TX_DMA_FIFO_SIZE / 2;
    info->block_count = 2;
}
static struct rt_audio_ops _p_audio_ops =
{
    .getcaps     = stm32_player_getcaps,
    .configure   = stm32_player_configure,
    .init        = stm32_player_init,
    .start       = stm32_player_start,
    .stop        = stm32_player_stop,
    .transmit    = RT_NULL,
    .buffer_info = stm32_player_buffer_info,
};

int rt_hw_sound_init(void)
{
    rt_uint8_t *tx_fifo;

    /* player */
    tx_fifo = /*rt_malloc(TX_DMA_FIFO_SIZE)*/ dac_buffer; /*替换为nocache的dac_buffer, 非常重要! 折腾了几个小时!*/
    if (tx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }
    rt_memset(tx_fifo, 0, TX_DMA_FIFO_SIZE);
    _stm32_audio_play.tx_fifo = tx_fifo;

    /* register sound device */
    _stm32_audio_play.audio.ops = &_p_audio_ops;
    rt_audio_register(&_stm32_audio_play.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &_stm32_audio_play);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_sound_init);





/****************************************************************************
* @brief 测试播放sin信号线程, 导入到msh
****************************************************************************/
void play_thread_entry(void *parameter)
{
    rt_device_t sound_dev;
    rt_size_t written;
    
    generate_sin_table();
    sound_dev = rt_device_find("sound0");
    rt_device_open(sound_dev, RT_DEVICE_OFLAG_WRONLY);

    while(1){
        written = rt_device_write(sound_dev, 0, sin_table, sizeof(sin_table));
        // rt_kprintf("Wrote %d bytes to sound device\n", written);
        // rt_thread_mdelay(1);
    }
}

void play_sine()
{
    rt_thread_t thread;
    thread = rt_thread_create("play_thread", play_thread_entry, RT_NULL, 2048, 2, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
}
MSH_CMD_EXPORT(play_sine, play_sine_test);









 /****************************************************************************
* @brief CubeMX底层硬件初始化, 包括xx_MSPInit(), xx_Init(), xx_IRQHandler()
****************************************************************************/
 /**
 * @brief DAC MSP Initialization
 * This function configures the hardware resources used in this example
 * @param hdac: DAC handle pointer
 * @retval None
 */
void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hdac->Instance==DAC1)
  {
    /* USER CODE BEGIN DAC1_MspInit 0 */

    /* USER CODE END DAC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_DAC12_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**DAC1 GPIO Configuration
   PA5     ------> DAC1_OUT2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* DAC1 DMA Init */
    /* DAC1_CH2 Init */
    hdma_dac1_ch2.Instance = DMA1_Stream1;
    hdma_dac1_ch2.Init.Request = DMA_REQUEST_DAC2;
    hdma_dac1_ch2.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_dac1_ch2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_dac1_ch2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_dac1_ch2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_dac1_ch2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_dac1_ch2.Init.Mode = DMA_CIRCULAR;
    hdma_dac1_ch2.Init.Priority = DMA_PRIORITY_LOW;
    hdma_dac1_ch2.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_dac1_ch2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    hdma_dac1_ch2.Init.MemBurst = DMA_MBURST_INC4;
    hdma_dac1_ch2.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_dac1_ch2) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hdac,DMA_Handle2,hdma_dac1_ch2);

    /* USER CODE BEGIN DAC1_MspInit 1 */

    /* USER CODE END DAC1_MspInit 1 */

  }

}
 
 /**
 * @brief DAC MSP De-Initialization
 * This function freeze the hardware resources used in this example
 * @param hdac: DAC handle pointer
 * @retval None
 */
void HAL_DAC_MspDeInit(DAC_HandleTypeDef* hdac)
{
  if(hdac->Instance==DAC1)
  {
    /* USER CODE BEGIN DAC1_MspDeInit 0 */

    /* USER CODE END DAC1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_DAC12_CLK_DISABLE();

    /**DAC1 GPIO Configuration
   PA5     ------> DAC1_OUT2
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5);

    /* DAC1 DMA DeInit */
    HAL_DMA_DeInit(hdac->DMA_Handle2);
    /* USER CODE BEGIN DAC1_MspDeInit 1 */

    /* USER CODE END DAC1_MspDeInit 1 */
  }

}

 
/**
  * @brief TIM_Base MSP Initialization
  * This function configures the hardware resources used in this example
  * @param htim_base: TIM_Base handle pointer
  * @retval None
  */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
    /* USER CODE BEGIN TIM2_MspInit 0 */

    /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
    /* USER CODE BEGIN TIM2_MspInit 1 */

    /* USER CODE END TIM2_MspInit 1 */

  }

}

/**
  * @brief TIM_Base MSP De-Initialization
  * This function freeze the hardware resources used in this example
  * @param htim_base: TIM_Base handle pointer
  * @retval None
  */
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM2)
  {
    /* USER CODE BEGIN TIM2_MspDeInit 0 */

    /* USER CODE END TIM2_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM2_CLK_DISABLE();
    /* USER CODE BEGIN TIM2_MspDeInit 1 */

    /* USER CODE END TIM2_MspDeInit 1 */
  }

}
 

/**
  * @brief DAC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_DAC1_Init(void)
{

  /* USER CODE BEGIN DAC1_Init 0 */

  /* USER CODE END DAC1_Init 0 */

  DAC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN DAC1_Init 1 */

  /* USER CODE END DAC1_Init 1 */

  /** DAC Initialization
  */
  hdac1.Instance = DAC1;
  if (HAL_DAC_Init(&hdac1) != HAL_OK)
  {
    Error_Handler();
  }

  /** DAC channel OUT2 config
  */
  sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
  sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
  sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
  if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DAC1_Init 2 */

  /* USER CODE END DAC1_Init 2 */

}


/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
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};

  /* 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 = 240e6/16e3;
  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();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * @brief This function handles DMA1 stream1 global interrupt.
  */
void DMA1_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */

  /* USER CODE END DMA1_Stream1_IRQn 0 */
  rt_base_t level = rt_hw_interrupt_disable();
  HAL_DMA_IRQHandler(&hdma_dac1_ch2);
  rt_hw_interrupt_enable(level);
  /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */

  /* USER CODE END DMA1_Stream1_IRQn 1 */
}

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}

D11 附录1:drv_mic.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author         Notes
 * 2019-07-28     Ernest         the first version
 */

#include "board.h"
#include "drv_mic.h"

#define DBG_TAG              "drv.mic"
#define DBG_LVL              DBG_INFO
#include <rtdbg.h>

#define RX_DMA_FIFO_SIZE (2048)

struct stm32_mic
{
    struct rt_audio_device audio;
    struct rt_audio_configure config;
    rt_uint8_t *rx_fifo;
    rt_bool_t startup;
};
static struct stm32_mic _stm32_audio_record = {0};




/****************************************************************************
* @brief 与cubeMX硬件初始化相关的变量定义
****************************************************************************/
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

extern TIM_HandleTypeDef htim2;

USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t adc_buffer[RX_DMA_FIFO_SIZE]; // Nocache buffer for DMA, important!

static void MX_ADC1_Init(void);
static void MX_TIM2_Init(void);
static void MX_DMA_Init(void);




/****************************************************************************
* @brief 要实现的几个dev_audio接口函数
****************************************************************************/
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[RX_DMA_FIFO_SIZE / 2], RX_DMA_FIFO_SIZE / 2);
    }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        rt_audio_rx_done(&(_stm32_audio_record.audio), &_stm32_audio_record.rx_fifo[0], RX_DMA_FIFO_SIZE / 2);
    }
}

static rt_err_t stm32_mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    return result;
}

static rt_err_t  stm32_mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
    rt_err_t result = RT_EOK;

    LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);

    switch (caps->main_type)
    {
    case AUDIO_TYPE_INPUT:
    {
        switch (caps->sub_type)
        {

        case AUDIO_DSP_PARAM:
        {
            _stm32_audio_record.config.samplerate = caps->udata.config.samplerate;
            _stm32_audio_record.config.channels   = caps->udata.config.channels;
            _stm32_audio_record.config.samplebits = caps->udata.config.samplebits;
            /*只添加调整采样率的功能*/
            __HAL_TIM_SET_AUTORELOAD(&htim2, 240000000 / _stm32_audio_record.config.samplerate - 1);
            rt_kprintf("samplerate: %d\n", _stm32_audio_record.config.samplerate);
            rt_kprintf("samplebits: %d\n", _stm32_audio_record.config.samplebits);
            rt_kprintf("channels: %d\n", _stm32_audio_record.config.channels);

            break;
        }

        default:
            result = -RT_ERROR;
            break;
        }
        /* After set config, MCLK will stop */
        break;
    }

    default:
        break;
    }

    return result;
}

static rt_err_t stm32_mic_init(struct rt_audio_device *audio)
{
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_TIM2_Init();

    return RT_EOK;
}

static rt_err_t stm32_mic_start(struct rt_audio_device *audio, int stream)
{
    rt_err_t result = RT_EOK;

    if (stream == AUDIO_STREAM_RECORD)
    {
        HAL_TIM_Base_Start(&htim2);
        HAL_ADC_Start_DMA(&hadc1, (uint32_t *) _stm32_audio_record.rx_fifo, RX_DMA_FIFO_SIZE/2);
    }

    return result;
}

static rt_err_t stm32_mic_stop(struct rt_audio_device *audio, int stream)
{
    if (stream == AUDIO_STREAM_RECORD)
    {
        HAL_ADC_Stop_DMA(&hadc1);
        HAL_TIM_Base_Stop(&htim2);
    }

    return RT_EOK;
}

static struct rt_audio_ops _mic_audio_ops =
{
    .getcaps     = stm32_mic_getcaps,
    .configure   = stm32_mic_configure,
    .init        = stm32_mic_init,
    .start       = stm32_mic_start,
    .stop        = stm32_mic_stop,
    .transmit    = RT_NULL,
    .buffer_info = RT_NULL,
};

int rt_hw_mic_init(void)
{
    struct rt_audio_device *audio = &_stm32_audio_record.audio;
    /* mic default */
    _stm32_audio_record.rx_fifo = /*rt_calloc(1, RX_DMA_FIFO_SIZE)*/ adc_buffer; /*替换为nocache的adc_buffer, 非常重要! 折腾了几个小时!*/
    if (_stm32_audio_record.rx_fifo == RT_NULL)
    {
        return -RT_ENOMEM;
    }

    _stm32_audio_record.config.channels = 1;
    _stm32_audio_record.config.samplerate = 16000;
    _stm32_audio_record.config.samplebits = 16;

    /* register mic device */
    audio->ops = &_mic_audio_ops;
    rt_audio_register(audio, "mic0", RT_DEVICE_FLAG_RDONLY, &_stm32_audio_record);

    return RT_EOK;
}

INIT_DEVICE_EXPORT(rt_hw_mic_init);





/****************************************************************************
* @brief 录音测试线程, 这里没有实现, 直接用wavplayer库进行测试
****************************************************************************/





/****************************************************************************
* @brief CubeMX底层硬件初始化, 包括xx_MSPInit(), xx_Init(), xx_IRQHandler()
****************************************************************************/
/**
  * @brief ADC MSP Initialization
  * This function configures the hardware resources used in this example
  * @param hadc: ADC handle pointer
  * @retval None
  */
 void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
 {
   if(hadc->Instance==ADC1)
   {
     /* USER CODE BEGIN ADC1_MspInit 0 */
 
     /* USER CODE END ADC1_MspInit 0 */
     /* Peripheral clock enable */
     __HAL_RCC_ADC12_CLK_ENABLE();
 
     __HAL_RCC_GPIOA_CLK_ENABLE();
     /**ADC1 GPIO Configuration
     PA1_C     ------> ADC1_INP1
     */
     HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PA1, SYSCFG_SWITCH_PA1_OPEN);
 
     /* ADC1 DMA Init */
     /* ADC1 Init */
     hdma_adc1.Instance = DMA1_Stream0;
     hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
     hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
     hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
     hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
     hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
     hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
     hdma_adc1.Init.Mode = DMA_CIRCULAR;
     hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
     hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
     hdma_adc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
     hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;
     hdma_adc1.Init.PeriphBurst = DMA_PBURST_INC4;
     if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
     {
       Error_Handler();
     }
 
     __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
 
     /* ADC1 interrupt Init */
     HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
     HAL_NVIC_EnableIRQ(ADC_IRQn);
     /* USER CODE BEGIN ADC1_MspInit 1 */
 
     /* USER CODE END ADC1_MspInit 1 */
 
   }
 
 }
 
 /**
   * @brief ADC MSP De-Initialization
   * This function freeze the hardware resources used in this example
   * @param hadc: ADC handle pointer
   * @retval None
   */
 void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)
 {
   if(hadc->Instance==ADC1)
   {
     /* USER CODE BEGIN ADC1_MspDeInit 0 */
 
     /* USER CODE END ADC1_MspDeInit 0 */
     /* Peripheral clock disable */
     __HAL_RCC_ADC12_CLK_DISABLE();
 
     /* ADC1 DMA DeInit */
     HAL_DMA_DeInit(hadc->DMA_Handle);
 
     /* ADC1 interrupt DeInit */
     HAL_NVIC_DisableIRQ(ADC_IRQn);
     /* USER CODE BEGIN ADC1_MspDeInit 1 */
 
     /* USER CODE END ADC1_MspDeInit 1 */
   }
 
 }

// /**
//   * @brief TIM_Base MSP Initialization
//   * This function configures the hardware resources used in this example
//   * @param htim_base: TIM_Base handle pointer
//   * @retval None
//   */
// void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
// {
//   if(htim_base->Instance==TIM2)
//   {
//     /* USER CODE BEGIN TIM2_MspInit 0 */

//     /* USER CODE END TIM2_MspInit 0 */
//     /* Peripheral clock enable */
//     __HAL_RCC_TIM2_CLK_ENABLE();
//     /* USER CODE BEGIN TIM2_MspInit 1 */

//     /* USER CODE END TIM2_MspInit 1 */

//   }

// }

// /**
//   * @brief TIM_Base MSP De-Initialization
//   * This function freeze the hardware resources used in this example
//   * @param htim_base: TIM_Base handle pointer
//   * @retval None
//   */
// void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
// {
//   if(htim_base->Instance==TIM2)
//   {
//     /* USER CODE BEGIN TIM2_MspDeInit 0 */

//     /* USER CODE END TIM2_MspDeInit 0 */
//     /* Peripheral clock disable */
//     __HAL_RCC_TIM2_CLK_DISABLE();
//     /* USER CODE BEGIN TIM2_MspDeInit 1 */

//     /* USER CODE END TIM2_MspDeInit 1 */
//   }

// }
 

/**
 * @brief ADC1 Initialization Function
 * @param None
 * @retval None
 */
static void MX_ADC1_Init(void)
{

/* USER CODE BEGIN ADC1_Init 0 */

/* USER CODE END ADC1_Init 0 */

ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};

/* USER CODE BEGIN ADC1_Init 1 */

/* USER CODE END ADC1_Init 1 */

/** Common config
 */
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_16B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.Oversampling.Ratio = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
    Error_Handler();
}

/** Configure the ADC multi-mode
 */
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
    Error_Handler();
}

/** Configure Regular Channel
 */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_8CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
sConfig.OffsetSignedSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */

/* USER CODE END ADC1_Init 2 */

}

/**
 * @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};

/* 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 = 240e6/16e3;
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();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
    Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */

/* USER CODE END TIM2_Init 2 */

}
 
/**
  * @brief This function handles DMA1 stream0 global interrupt.
  */
void DMA1_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */

  /* USER CODE END DMA1_Stream0_IRQn 0 */
  rt_base_t level = rt_hw_interrupt_disable();
  HAL_DMA_IRQHandler(&hdma_adc1);
  rt_hw_interrupt_enable(level);
  /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */

  /* USER CODE END DMA1_Stream0_IRQn 1 */
}

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);

}

D12 附录3:wavplayer.c(我们做了一点修改适配)

/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Date           Author       Notes
 * 2019-07-15     Zero-Free    first implementation
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <wavhdr.h>
#include <wavplayer.h>

#define DBG_TAG              "WAV_PLAYER"
#define DBG_LVL              DBG_INFO
#include <rtdbg.h>

#define VOLUME_MIN (0)
#define VOLUME_MAX (99)

#define WP_BUFFER_SIZE (2048)
#define WP_VOLUME_DEFAULT (55)
#define WP_MSG_SIZE (10)
#define WP_THREAD_STATCK_SIZE (2048)
#define WP_THREAD_PRIORITY (15)

enum MSG_TYPE
{
    MSG_NONE   = 0,
    MSG_START  = 1,
    MSG_STOP   = 2,
    MSG_PAUSE  = 3,
    MSG_RESUME = 4,
};

enum PLAYER_EVENT
{
    PLAYER_EVENT_NONE   = 0,
    PLAYER_EVENT_PLAY   = 1,
    PLAYER_EVENT_STOP   = 2,
    PLAYER_EVENT_PAUSE  = 3,
    PLAYER_EVENT_RESUME = 4,
};

struct play_msg
{
    int type;
    void *data;
};

struct wavplayer
{
    int state;
    char *uri;
    char *buffer;
    rt_device_t device;
    rt_mq_t mq;
    rt_mutex_t lock;
    struct rt_completion ack;
    FILE *fp;
    int volume;
};

static struct wavplayer player;

#if (DBG_LEVEL >= DBG_LOG)

static const char *state_str[] =
{
    "STOPPED",
    "PLAYING",
    "PAUSED",
};

static const char *event_str[] =
{
    "NONE",
    "PLAY"
    "STOP"
    "PAUSE"
    "RESUME"
};

#endif

static void play_lock(void)
{
    rt_mutex_take(player.lock, RT_WAITING_FOREVER);
}

static void play_unlock(void)
{
    rt_mutex_release(player.lock);
}

static rt_err_t play_msg_send(struct wavplayer *player, int type, void *data)
{
    struct play_msg msg;

    msg.type = type;
    msg.data = data;

    return rt_mq_send(player->mq, &msg, sizeof(struct play_msg));
}

int wavplayer_play(char *uri)
{
    rt_err_t result = RT_EOK;

    rt_completion_init(&player.ack);

    play_lock();
    if (player.state != PLAYER_STATE_STOPED)
    {
        wavplayer_stop();
    }
    if (player.uri)
    {
        rt_free(player.uri);
    }
    player.uri = rt_strdup(uri);
    result = play_msg_send(&player, MSG_START, RT_NULL);
    rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
    play_unlock();

    return result;
}

int wavplayer_stop(void)
{
    rt_err_t result = RT_EOK;

    rt_completion_init(&player.ack);

    play_lock();
    if (player.state != PLAYER_STATE_STOPED)
    {
        result = play_msg_send(&player, MSG_STOP, RT_NULL);
        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
    }
    play_unlock();

    return result;
}

int wavplayer_pause(void)
{
    rt_err_t result = RT_EOK;

    rt_completion_init(&player.ack);

    play_lock();
    if (player.state == PLAYER_STATE_PLAYING)
    {
        result = play_msg_send(&player, MSG_PAUSE, RT_NULL);
        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
    }
    play_unlock();

    return result;
}

int wavplayer_resume(void)
{
    rt_err_t result = RT_EOK;

    rt_completion_init(&player.ack);

    play_lock();
    if (player.state == PLAYER_STATE_PAUSED)
    {
        result = play_msg_send(&player, MSG_RESUME, RT_NULL);
        rt_completion_wait(&player.ack, RT_WAITING_FOREVER);
    }
    play_unlock();

    return result;
}

int wavplayer_volume_set(int volume)
{
    struct rt_audio_caps caps;

    if (volume < VOLUME_MIN)
        volume = VOLUME_MIN;
    else if (volume > VOLUME_MAX)
        volume = VOLUME_MAX;

    player.device = rt_device_find(PKG_WP_PLAY_DEVICE);
    if (player.device == RT_NULL)
        return -RT_ERROR;

    player.volume = volume;
    caps.main_type = AUDIO_TYPE_MIXER;
    caps.sub_type  = AUDIO_MIXER_VOLUME;
    caps.udata.value = volume;

    LOG_D("set volume = %d", volume);
    return rt_device_control(player.device, AUDIO_CTL_CONFIGURE, &caps);
}

int wavplayer_volume_get(void)
{
    return player.volume;
}

int wavplayer_state_get(void)
{
    return player.state;
}

char *wavplayer_uri_get(void)
{
    return player.uri;
}

static rt_err_t wavplayer_open(struct wavplayer *player)
{
    rt_err_t result = RT_EOK;
    struct rt_audio_caps caps;
    struct wav_header wav;

    /* find device */
    player->device = rt_device_find(PKG_WP_PLAY_DEVICE);
    if (player->device == RT_NULL)
    {
        LOG_E("device %s not find", PKG_WP_PLAY_DEVICE);
        return - RT_ERROR;
    }

    /* open file */
    player->fp = fopen(player->uri, "rb");
    if (player->fp == RT_NULL)
    {
        LOG_E("open file %s failed", player->uri);
        result = -RT_ERROR;
        goto __exit;
    }

    /* open sound device */
    result = rt_device_open(player->device, RT_DEVICE_OFLAG_WRONLY);
    if (result != RT_EOK)
    {
        LOG_E("open %s device faield", PKG_WP_PLAY_DEVICE);
        goto __exit;
    }

    LOG_D("open wavplayer, device %s", PKG_WP_PLAY_DEVICE);
    /* read wavfile header information from file */
    wavheader_read(&wav, player->fp);

    LOG_D("Information:");
    LOG_D("samplerate %d", wav.fmt_sample_rate);
    LOG_D("channels %d", wav.fmt_channels);
    LOG_D("sample bits width %d", wav.fmt_bit_per_sample);

    /* set sampletate,channels, samplebits */
    caps.main_type = AUDIO_TYPE_OUTPUT;
    caps.sub_type  = AUDIO_DSP_PARAM;
    caps.udata.config.samplerate = wav.fmt_sample_rate;
    caps.udata.config.channels = wav.fmt_channels;
    caps.udata.config.samplebits = wav.fmt_bit_per_sample;
    rt_device_control(player->device, AUDIO_CTL_CONFIGURE, &caps);

    /* set volume according to configuration */
    caps.main_type = AUDIO_TYPE_MIXER;
    caps.sub_type  = AUDIO_MIXER_VOLUME;
    caps.udata.value = player->volume;
    rt_device_control(player->device, AUDIO_CTL_CONFIGURE, &caps);

    return RT_EOK;

__exit:
    if (player->fp)
    {
        fclose(player->fp);
        player->fp = RT_NULL;
    }

    if (player->device)
    {
        rt_device_close(player->device);
        player->device = RT_NULL;
    }

    return result;
}


static void wavplayer_close(struct wavplayer *player)
{
    if (player->fp)
    {
        fclose(player->fp);
        player->fp = RT_NULL;
    }

    if (player->device)
    {
        rt_device_close(player->device);
        player->device = RT_NULL;
    }

    LOG_D("close wavplayer");
}

static int wavplayer_event_handler(struct wavplayer *player, int timeout)
{
    int event;
    struct play_msg msg;
#if (DBG_LEVEL >= DBG_LOG)
    rt_uint8_t last_state;
#endif
    rt_ssize_t result = rt_mq_recv(player->mq, &msg, sizeof(struct play_msg), timeout);
#if defined(RT_VERSION_CHECK) && (RTTHREAD_VERSION >= RT_VERSION_CHECK(5, 0, 1))
    if (result <= 0)
#else
    if (RT_EOK != result)
#endif
    {
        event = PLAYER_EVENT_NONE;
        return event;
    }
#if (DBG_LEVEL >= DBG_LOG)
    last_state = player->state;
#endif

    switch (msg.type)
    {
    case MSG_START:
        event = PLAYER_EVENT_PLAY;
        player->state = PLAYER_STATE_PLAYING;
        break;

    case MSG_STOP:
        event = PLAYER_EVENT_STOP;
        player->state = PLAYER_STATE_STOPED;
        break;

    case MSG_PAUSE:
        event = PLAYER_EVENT_PAUSE;
        player->state = PLAYER_STATE_PAUSED;
        break;

    case MSG_RESUME:
        event = PLAYER_EVENT_RESUME;
        player->state = PLAYER_STATE_PLAYING;
        break;

    default:
        event = PLAYER_EVENT_NONE;
        break;
    }

    rt_completion_done(&player->ack);

#if (DBG_LEVEL >= DBG_LOG)
    LOG_D("EVENT:%s, STATE:%s -> %s", event_str[event], state_str[last_state], state_str[player->state]);
#endif

    return event;
}

static void wavplayer_entry(void *parameter)
{
    rt_err_t result = RT_EOK;
    rt_int32_t size;
    int event;

    player.buffer = rt_malloc(WP_BUFFER_SIZE);
    if (player.buffer == RT_NULL)
        return;
    rt_memset(player.buffer, 0, WP_BUFFER_SIZE);

    player.mq = rt_mq_create("wav_p", sizeof(struct play_msg), 10, RT_IPC_FLAG_FIFO);
    if (player.mq == RT_NULL)
        goto __exit;

    player.lock = rt_mutex_create("wav_p", RT_IPC_FLAG_FIFO);
    if (player.lock == RT_NULL)
        goto __exit;

    player.volume = WP_VOLUME_DEFAULT;

    while (1)
    {
        /* wait play event forever */
        event = wavplayer_event_handler(&player, RT_WAITING_FOREVER);
        if (event != PLAYER_EVENT_PLAY)
            continue;

        /* open wavplayer */
        result = wavplayer_open(&player);
        if (result != RT_EOK)
        {
            player.state = PLAYER_STATE_STOPED;
            LOG_I("open wav player failed");
            continue;
        }

        LOG_I("play start, uri=%s", player.uri);
        while (1)
        {
            event = wavplayer_event_handler(&player, RT_WAITING_NO);

            switch (event)
            {
            case PLAYER_EVENT_NONE:
            {
                /* read raw data from file stream */
                size = fread(player.buffer, WP_BUFFER_SIZE, 1, player.fp);
                if (size != 1)
                {
                    /* FILE END*/
                    player.state = PLAYER_STATE_STOPED;
                }
                else
                {
                    int16_t *p = (int16_t *)player.buffer;
                    for(int i=0; i < WP_BUFFER_SIZE/2; i++){ /*Add by xujc, for 12bits DAC play*/
                        p[i] += 32768;
                        p[i] >>= 4;
                    }

                    /*witte data to sound device*/
                    rt_device_write(player.device, 0, player.buffer, WP_BUFFER_SIZE);
                }
                break;
            }

            case PLAYER_EVENT_PAUSE:
            {
                /* wait resume or stop event forever */
                event = wavplayer_event_handler(&player, RT_WAITING_FOREVER);
            }

            default:
                break;
            }

            if (player.state == PLAYER_STATE_STOPED)
                break;
        }

        /* close wavplayer */
        wavplayer_close(&player);
        LOG_I("play end");
    }

__exit:
    if (player.buffer)
    {
        rt_free(player.buffer);
        player.buffer = RT_NULL;
    }

    if (player.mq)
    {
        rt_mq_delete(player.mq);
        player.mq = RT_NULL;
    }

    if (player.lock)
    {
        rt_mutex_delete(player.lock);
        player.lock = RT_NULL;
    }
}


int wavplayer_init(void)
{
    rt_thread_t tid;

    tid = rt_thread_create("wav_p",
                           wavplayer_entry,
                           RT_NULL,
                           WP_THREAD_STATCK_SIZE,
                           WP_THREAD_PRIORITY, 10);
    if (tid)
        rt_thread_startup(tid);

    return RT_EOK;
}

INIT_APP_EXPORT(wavplayer_init);

D13 附录3:wavrecorder.c(我们做了一点修改适配)

/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Date           Author       Notes
 * 2019-07-16     Zero-Free    first implementation
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <wavhdr.h>
#include <wavrecorder.h>

#define DBG_TAG              "WAV_RECORDER"
#define DBG_LVL              DBG_INFO
#include <rtdbg.h>

struct recorder
{
    rt_device_t device;
    struct wavrecord_info info;
    struct rt_event *event;
    struct rt_completion ack;
    rt_uint8_t *buffer;
    FILE *fp;
    rt_bool_t activated;
};

enum RECORD_EVENT
{
    RECORD_EVENT_STOP  = 0x01,
    RECORD_EVENT_START = 0x02,
};

#define WR_BUFFER_SIZE (2048)

static struct recorder record;

static rt_err_t wavrecorder_open(struct recorder *record)
{
    rt_err_t result = RT_EOK;

    record->device = rt_device_find(PKG_WP_RECORD_DEVICE);
    if (record->device == RT_NULL)
    {
        LOG_E("device %s not find", PKG_WP_RECORD_DEVICE);
        return -RT_ERROR;
    }

    /* malloc internal buffer */
    record->buffer = rt_malloc(WR_BUFFER_SIZE);
    if (record->buffer == RT_NULL)
    {
        result = -RT_ENOMEM;
        LOG_E("malloc internal buffer for recorder failed");
        goto __exit;
    }
    rt_memset(record->buffer, 0, WR_BUFFER_SIZE);

    /* open file */
    record->fp = fopen(record->info.uri, "wb+");
    if (record->fp == RT_NULL)
    {
        result = -RT_ERROR;
        LOG_E("open file %s failed", record->info.uri);
        goto __exit;
    }

    /* open micphone device */
    result = rt_device_open(record->device, RT_DEVICE_OFLAG_RDONLY);
    if (result != RT_EOK)
    {
        result = -RT_ERROR;
        LOG_E("open %s device faield", PKG_WP_RECORD_DEVICE);
        goto __exit;
    }

    record->event = rt_event_create("wav_r", RT_IPC_FLAG_FIFO);
    if (record->event == RT_NULL)
    {
        result = -RT_ERROR;
        LOG_E("create event for wav recorder failed");
        goto __exit;
    }

    return RT_EOK;

__exit:
    if (record->buffer)
    {
        rt_free(record->buffer);
        record->buffer = RT_NULL;
    }

    if (record->fp)
    {
        fclose(record->fp);
        record->fp = RT_NULL;
    }

    if (record->device)
    {
        rt_device_close(record->device);
        record->device = RT_NULL;
    }

    if (record->event)
    {
        rt_event_delete(record->event);
        record->event = RT_NULL;
    }

    return result;
}

void wavrecorder_close(struct recorder *record)
{
    if (record->buffer)
    {
        rt_free(record->buffer);
        record->buffer = RT_NULL;
    }

    if (record->fp)
    {
        fclose(record->fp);
        record->fp = RT_NULL;
    }

    if (record->device)
    {
        rt_device_close(record->device);
        record->device = RT_NULL;
    }

    if (record->event)
    {
        rt_event_delete(record->event);
        record->event = RT_NULL;
    }
}

static void wavrecord_entry(void *parameter)
{
    rt_err_t result;
    rt_size_t size;
    struct wav_header wav = {0};
    struct rt_audio_caps caps;
    rt_uint32_t recv_evt, total_length = 0;

    result = wavrecorder_open(&record);
    if (result != RT_EOK)
    {
        LOG_E("open wav recorder failed");
        return;
    }

    record.activated = RT_TRUE;

    /* write 44 bytes wavheader */
    fwrite(&wav, 44, 1, record.fp);

    rt_kprintf("Information:\n");
    rt_kprintf("samplerate %d\n", record.info.samplerate);
    rt_kprintf("channels %d\n", record.info.channels);

    /* set sampletate,channels, samplebits */
    caps.main_type = AUDIO_TYPE_INPUT;
    caps.sub_type  = AUDIO_DSP_PARAM;
    caps.udata.config.samplerate = record.info.samplerate;
    caps.udata.config.channels = record.info.channels;
    caps.udata.config.samplebits = 16;
    rt_device_control(record.device, AUDIO_CTL_CONFIGURE, &caps);

    LOG_D("ready to record, device %s, uri %s", PKG_WP_PLAY_DEVICE, record.info.uri);

    while (1)
    {
        /* read raw data from sound device */
        size =  rt_device_read(record.device, 0, record.buffer, WR_BUFFER_SIZE);
        if (size)
        {
            int16_t *buffer = (int16_t *)record.buffer;
            for(int i = 0; i < size/2; i++) {
                buffer[i] = (int16_t)(((uint16_t *)record.buffer)[i] - 32768);
            }

            fwrite(record.buffer, size, 1, record.fp);
            total_length += size;
        }

        /* recive stop event */
        if (rt_event_recv(record.event, RECORD_EVENT_STOP,
                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                          RT_WAITING_NO, &recv_evt) == RT_EOK)
        {

            /* re-write wav header */
            wavheader_init(&wav, record.info.samplerate, record.info.channels, total_length);
            fseek(record.fp, 0, SEEK_SET);
            wavheader_write(&wav, record.fp);
            wavrecorder_close(&record);

            LOG_D("total_length = %d", total_length);

            /* ack event */
            rt_completion_done(&record.ack);
            record.activated = RT_FALSE;

            break;
        }
    }

    return;
}

rt_err_t wavrecorder_start(struct wavrecord_info *info)
{
    if (record.activated != RT_TRUE)
    {
        rt_thread_t tid;

        if (record.info.uri)
            rt_free(record.info.uri);
        record.info.uri = rt_strdup(info->uri);

        record.info.samplerate = info->samplerate;
        record.info.channels   = info->channels;
        record.info.samplebits = info->samplebits;

        tid = rt_thread_create("wav_r", wavrecord_entry, RT_NULL, 2048, 19, 20);
        if (tid)
            rt_thread_startup(tid);
    }

    return RT_EOK;
}

rt_err_t wavrecorder_stop(void)
{
    if (record.activated == RT_TRUE)
    {
        rt_completion_init(&record.ack);
        rt_event_send(record.event, RECORD_EVENT_STOP);
        rt_completion_wait(&record.ack, RT_WAITING_FOREVER);
    }

    return RT_EOK;
}

rt_bool_t wavrecorder_is_actived(void)
{
    return record.activated;
}


网站公告

今日签到

点亮在社区的每一天
去签到