[bsp][qemu]添加Audio驱动
This commit is contained in:
parent
6a1e8d5626
commit
bc544bec47
|
@ -15,3 +15,8 @@ config BSP_DRV_EMAC
|
|||
bool "EMAC driver"
|
||||
depends on RT_USING_LWIP
|
||||
default y
|
||||
|
||||
config BSP_DRV_AUDIO
|
||||
bool "Audio driver"
|
||||
select RT_USING_AUDIO
|
||||
default n
|
||||
|
|
|
@ -2,7 +2,9 @@ from building import *
|
|||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
list = os.listdir(cwd)
|
||||
CPPPATH = [cwd]
|
||||
objs = []
|
||||
|
||||
if not GetDepend('BSP_DRV_EMAC'):
|
||||
SrcRemove(src, ['drv_smc911x.c'])
|
||||
|
@ -12,4 +14,10 @@ if not GetDepend('BSP_DRV_CLCD'):
|
|||
|
||||
group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
objs = objs + group
|
||||
|
||||
Return('objs')
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c') + Glob('*.S')
|
||||
CPPPATH = [cwd]
|
||||
|
||||
group = DefineGroup('drv_audio', src, depend = ['BSP_DRV_AUDIO'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* File : drv_ac97.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-25 RT-Thread the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rthw.h>
|
||||
#include "drv_pl041.h"
|
||||
#include "drv_ac97.h"
|
||||
|
||||
void ac97_reset(void)
|
||||
{
|
||||
aaci_ac97_write(AC97_RESET, 0xFFFF);
|
||||
}
|
||||
|
||||
rt_err_t ac97_set_vol(int vol)
|
||||
{
|
||||
rt_uint16_t tmp = vol | (vol << 8);
|
||||
|
||||
if (vol < 0 || vol > 0x3f)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
aaci_ac97_write(AC97_MASTER, tmp);
|
||||
aaci_ac97_write(AC97_HEADPHONE, tmp);
|
||||
aaci_ac97_write(AC97_MASTER_MONO, tmp);
|
||||
aaci_ac97_write(AC97_PCM, tmp);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
int ac97_get_vol(void)
|
||||
{
|
||||
rt_uint16_t v;
|
||||
|
||||
v = aaci_ac97_read(AC97_MASTER);
|
||||
if (v == (~0x0))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
return 0x3F & v;
|
||||
}
|
||||
|
||||
rt_err_t ac97_set_rate(int rate)
|
||||
{
|
||||
if (rate < 0)
|
||||
{
|
||||
return -RT_ERROR;
|
||||
}
|
||||
aaci_ac97_write(AC97_PCM_FRONT_DAC_RATE, rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ac97_get_rate(void)
|
||||
{
|
||||
rt_uint16_t v;
|
||||
|
||||
v = aaci_ac97_read(AC97_PCM_FRONT_DAC_RATE);
|
||||
if (v == (~0x0))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define AC97_DUMP(_v) rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, (_v), (aaci_ac97_read(_v)))
|
||||
int _ac97_reg_dump(int argc, char **argv)
|
||||
{
|
||||
AC97_DUMP(AC97_RESET);
|
||||
AC97_DUMP(AC97_MASTER);
|
||||
AC97_DUMP(AC97_HEADPHONE);
|
||||
AC97_DUMP(AC97_MASTER_MONO);
|
||||
AC97_DUMP(AC97_MASTER_TONE);
|
||||
AC97_DUMP(AC97_PC_BEEP);
|
||||
AC97_DUMP(AC97_PHONE);
|
||||
AC97_DUMP(AC97_MIC);
|
||||
AC97_DUMP(AC97_LINE);
|
||||
AC97_DUMP(AC97_CD);
|
||||
AC97_DUMP(AC97_VIDEO);
|
||||
AC97_DUMP(AC97_AUX);
|
||||
AC97_DUMP(AC97_PCM);
|
||||
AC97_DUMP(AC97_REC_SEL);
|
||||
AC97_DUMP(AC97_REC_GAIN);
|
||||
AC97_DUMP(AC97_REC_GAIN_MIC);
|
||||
AC97_DUMP(AC97_GENERAL_PURPOSE);
|
||||
AC97_DUMP(AC97_3D_CONTROL);
|
||||
AC97_DUMP(AC97_INT_PAGING);
|
||||
AC97_DUMP(AC97_POWERDOWN);
|
||||
AC97_DUMP(AC97_PCM_FRONT_DAC_RATE);
|
||||
AC97_DUMP(AC97_PCM_SURR_DAC_RATE);
|
||||
AC97_DUMP(AC97_PCM_LFE_DAC_RATE);
|
||||
AC97_DUMP(AC97_PCM_LR_ADC_RATE);
|
||||
AC97_DUMP(AC97_PCM_MIC_ADC_RATE);
|
||||
AC97_DUMP(AC97_DAC_SLOT_MAP);
|
||||
AC97_DUMP(AC97_ADC_SLOT_MAP);
|
||||
return 0;
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT_ALIAS(_ac97_reg_dump, __cmd_ac97_dump, ac97 dump reg.);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* File : drv_ac97.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-25 RT-Thread the first version
|
||||
*/
|
||||
|
||||
#ifndef __DRV_AC97_H__
|
||||
#define __DRV_AC97_H__
|
||||
|
||||
/* Register offsets */
|
||||
#define AC97_RESET 0x00
|
||||
#define AC97_MASTER 0x02
|
||||
#define AC97_HEADPHONE 0x04
|
||||
#define AC97_MASTER_MONO 0x06
|
||||
#define AC97_MASTER_TONE 0x08
|
||||
#define AC97_PC_BEEP 0x0A //mixer volume
|
||||
#define AC97_PHONE 0x0C
|
||||
#define AC97_MIC 0x0E //qwert db
|
||||
#define AC97_LINE 0x10
|
||||
#define AC97_CD 0x12
|
||||
#define AC97_VIDEO 0x14
|
||||
#define AC97_AUX 0x16
|
||||
#define AC97_PCM 0x18
|
||||
#define AC97_REC_SEL 0x1A //0 represent mic
|
||||
#define AC97_REC_GAIN 0x1C
|
||||
#define AC97_REC_GAIN_MIC 0x1E
|
||||
#define AC97_GENERAL_PURPOSE 0x20
|
||||
#define AC97_3D_CONTROL 0x22
|
||||
#define AC97_INT_PAGING 0x24 //qwert
|
||||
#define AC97_POWERDOWN 0x26
|
||||
#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */
|
||||
#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */
|
||||
#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */
|
||||
#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */
|
||||
#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */
|
||||
#define AC97_DAC_SLOT_MAP 0x6C
|
||||
#define AC97_ADC_SLOT_MAP 0x6E
|
||||
|
||||
void ac97_reset(void);
|
||||
rt_err_t ac97_set_vol(int vol);
|
||||
int ac97_get_vol(void);
|
||||
rt_err_t ac97_set_rate(int rate);
|
||||
int ac97_get_rate(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* File : drv_pl041.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-25 RT-Thread the first version
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rthw.h>
|
||||
#include "drv_pl041.h"
|
||||
#include "drv_ac97.h"
|
||||
#include "realview.h"
|
||||
|
||||
#define DBG_ENABLE
|
||||
#define DBG_SECTION_NAME "[PL041]"
|
||||
// #define DBG_LEVEL DBG_LOG
|
||||
// #define DBG_LEVEL DBG_INFO
|
||||
#define DBG_LEVEL DBG_WARNING
|
||||
// #define DBG_LEVEL DBG_ERROR
|
||||
#define DBG_COLOR
|
||||
#include <rtdbg.h>
|
||||
|
||||
#define FRAME_PERIOD_US (50)
|
||||
#define PL041_CHANNLE_NUM (4)
|
||||
|
||||
#define PL041_READ(_a) (*(volatile rt_uint32_t *)(_a))
|
||||
#define PL041_WRITE(_a, _v) (*(volatile rt_uint32_t *)(_a) = (_v))
|
||||
|
||||
struct pl041_irq_def
|
||||
{
|
||||
pl041_irq_fun_t fun;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static struct pl041_irq_def irq_tbl[PL041_CHANNLE_NUM];
|
||||
|
||||
static void aaci_pl041_delay(rt_uint32_t us)
|
||||
{
|
||||
volatile int i;
|
||||
|
||||
for (i = us * 10; i != 0; i--);
|
||||
}
|
||||
|
||||
static void aaci_ac97_select_codec(void)
|
||||
{
|
||||
rt_uint32_t v, maincr;
|
||||
|
||||
maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
|
||||
AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
|
||||
|
||||
v = PL041_READ(&PL041->slfr);
|
||||
if (v & AACI_SLFR_2RXV)
|
||||
{
|
||||
PL041_READ(&PL041->sl2rx);
|
||||
}
|
||||
if (v & AACI_SLFR_1RXV)
|
||||
{
|
||||
PL041_READ(&PL041->sl1rx);
|
||||
}
|
||||
|
||||
if (maincr != PL041_READ(&PL041->maincr))
|
||||
{
|
||||
PL041_WRITE(&PL041->maincr, maincr);
|
||||
aaci_pl041_delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val)
|
||||
{
|
||||
rt_uint32_t v, timeout;
|
||||
|
||||
aaci_ac97_select_codec();
|
||||
|
||||
PL041_WRITE(&PL041->sl2tx, val << 4);
|
||||
PL041_WRITE(&PL041->sl1tx, reg << 12);
|
||||
|
||||
aaci_pl041_delay(FRAME_PERIOD_US);
|
||||
|
||||
timeout = FRAME_PERIOD_US * 8;
|
||||
do
|
||||
{
|
||||
aaci_pl041_delay(1);
|
||||
v = PL041_READ(&PL041->slfr);
|
||||
}
|
||||
while ((v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB)) && --timeout);
|
||||
|
||||
if (v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB))
|
||||
{
|
||||
dbg_log(DBG_ERROR, "timeout waiting for write to complete\n");
|
||||
}
|
||||
}
|
||||
|
||||
rt_uint16_t aaci_ac97_read(rt_uint16_t reg)
|
||||
{
|
||||
rt_uint32_t v, timeout, retries = 10;
|
||||
|
||||
aaci_ac97_select_codec();
|
||||
|
||||
PL041_WRITE(&PL041->sl1tx, (reg << 12) | (1 << 19));
|
||||
aaci_pl041_delay(FRAME_PERIOD_US);
|
||||
|
||||
timeout = FRAME_PERIOD_US * 8;
|
||||
do
|
||||
{
|
||||
aaci_pl041_delay(1);
|
||||
v = PL041_READ(&PL041->slfr);
|
||||
}
|
||||
while ((v & AACI_SLFR_1TXB) && --timeout);
|
||||
|
||||
if (v & AACI_SLFR_1TXB)
|
||||
{
|
||||
dbg_log(DBG_ERROR, "timeout on slot 1 TX busy\n");
|
||||
v = ~0x0;
|
||||
return v;
|
||||
}
|
||||
|
||||
aaci_pl041_delay(FRAME_PERIOD_US);
|
||||
timeout = FRAME_PERIOD_US * 8;
|
||||
do
|
||||
{
|
||||
aaci_pl041_delay(1);
|
||||
v = PL041_READ(&PL041->slfr) & (AACI_SLFR_1RXV | AACI_SLFR_2RXV);
|
||||
}
|
||||
while ((v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV)) && --timeout);
|
||||
|
||||
if (v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV))
|
||||
{
|
||||
dbg_log(DBG_ERROR, "timeout on RX valid\n");
|
||||
v = ~0x0;
|
||||
return v;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
v = PL041_READ(&PL041->sl1rx) >> 12;
|
||||
if (v == reg)
|
||||
{
|
||||
v = PL041_READ(&PL041->sl2rx) >> 4;
|
||||
break;
|
||||
}
|
||||
else if (--retries)
|
||||
{
|
||||
dbg_log(DBG_ERROR, "ac97 read back fail. retry\n");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log(DBG_ERROR, "wrong ac97 register read back (%x != %x)\n", v, reg);
|
||||
v = ~0x0;
|
||||
}
|
||||
}
|
||||
while (retries);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int aaci_pl041_channle_disable(int channle)
|
||||
{
|
||||
rt_uint32_t v;
|
||||
void *p_rx, *p_tx;
|
||||
|
||||
p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
|
||||
p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
|
||||
v = PL041_READ(p_rx);
|
||||
v &= ~AACI_CR_EN;
|
||||
PL041_WRITE(p_rx, v);
|
||||
v = PL041_READ(p_tx);
|
||||
v &= ~AACI_CR_EN;
|
||||
PL041_WRITE(p_tx, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aaci_pl041_channle_enable(int channle)
|
||||
{
|
||||
rt_uint32_t v;
|
||||
void *p_rx, *p_tx;
|
||||
|
||||
p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
|
||||
p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
|
||||
v = PL041_READ(p_rx);
|
||||
v |= AACI_CR_EN;
|
||||
PL041_WRITE(p_rx, v);
|
||||
v = PL041_READ(p_tx);
|
||||
v |= AACI_CR_EN;
|
||||
PL041_WRITE(p_tx, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aaci_pl041_channle_read(int channle, rt_uint16_t *buff, int count)
|
||||
{
|
||||
void *p_data, *p_status;
|
||||
int i = 0;
|
||||
|
||||
p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
|
||||
p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
|
||||
for (i = 0; (!(PL041_READ(p_status) & AACI_SR_RXFE)) && (i < count); i++)
|
||||
{
|
||||
buff[i] = (rt_uint16_t)PL041_READ(p_data);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int aaci_pl041_channle_write(int channle, rt_uint16_t *buff, int count)
|
||||
{
|
||||
void *p_data, *p_status;
|
||||
int i = 0;
|
||||
|
||||
p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
|
||||
p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channle * 0x20);
|
||||
for (i = 0; (!(PL041_READ(p_status) & AACI_SR_TXFF)) && (i < count); i++)
|
||||
{
|
||||
PL041_WRITE(p_data, buff[i]);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int aaci_pl041_channle_cfg(int channle, pl041_cfg_t cgf)
|
||||
{
|
||||
rt_uint32_t v;
|
||||
void *p_rx, *p_tx;
|
||||
|
||||
p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channle * 0x14);
|
||||
p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channle * 0x14);
|
||||
v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->itype;
|
||||
PL041_WRITE(p_rx, v);
|
||||
v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->otype;
|
||||
PL041_WRITE(p_tx, v);
|
||||
|
||||
ac97_set_vol(cgf->vol);
|
||||
ac97_set_rate(cgf->rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aaci_pl041_irq_enable(int channle, rt_uint32_t vector)
|
||||
{
|
||||
rt_uint32_t v;
|
||||
void *p_irq;
|
||||
|
||||
vector &= vector & 0x7f;
|
||||
p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
|
||||
v = PL041_READ(p_irq);
|
||||
v |= vector;
|
||||
PL041_WRITE(p_irq, v);
|
||||
}
|
||||
|
||||
void aaci_pl041_irq_disable(int channle, rt_uint32_t vector)
|
||||
{
|
||||
rt_uint32_t v;
|
||||
void *p_irq;
|
||||
|
||||
vector &= vector & 0x7f;
|
||||
p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channle * 0x14);
|
||||
v = PL041_READ(p_irq);
|
||||
v &= ~vector;
|
||||
PL041_WRITE(p_irq, v);
|
||||
}
|
||||
|
||||
rt_err_t aaci_pl041_irq_register(int channle, pl041_irq_fun_t fun, void *user_data)
|
||||
{
|
||||
if (channle < 0 || channle >= PL041_CHANNLE_NUM)
|
||||
{
|
||||
dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
irq_tbl[channle].fun = fun;
|
||||
irq_tbl[channle].user_data = user_data;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
rt_err_t aaci_pl041_irq_unregister(int channle)
|
||||
{
|
||||
if (channle < 0 || channle >= PL041_CHANNLE_NUM)
|
||||
{
|
||||
dbg_log(DBG_ERROR, "%s channle:%d err.\n", __FUNCTION__, channle);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
irq_tbl[channle].fun = RT_NULL;
|
||||
irq_tbl[channle].user_data = RT_NULL;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static void aaci_pl041_irq_handle(int irqno, void *param)
|
||||
{
|
||||
rt_uint32_t mask, channle, m;
|
||||
struct pl041_irq_def *_irq = param;
|
||||
void *p_status;
|
||||
|
||||
mask = PL041_READ(&PL041->allints);
|
||||
PL041_WRITE(PL041->intclr, mask);
|
||||
|
||||
for (channle = 0; (channle < PL041_CHANNLE_NUM) && (mask); channle++)
|
||||
{
|
||||
mask = mask >> 7;
|
||||
m = mask & 0x7f;
|
||||
if (m & AACI_ISR_ORINTR)
|
||||
{
|
||||
dbg_log(DBG_WARNING, "RX overrun on chan %d\n", channle);
|
||||
}
|
||||
|
||||
if (m & AACI_ISR_RXTOINTR)
|
||||
{
|
||||
dbg_log(DBG_WARNING, "RX timeout on chan %d\n", channle);
|
||||
}
|
||||
|
||||
if (mask & AACI_ISR_URINTR)
|
||||
{
|
||||
dbg_log(DBG_WARNING, "TX underrun on chan %d\n", channle);
|
||||
}
|
||||
|
||||
p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channle * 0x14);
|
||||
if (_irq[channle].fun != RT_NULL)
|
||||
{
|
||||
_irq[channle].fun(PL041_READ(p_status), _irq[channle].user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rt_err_t aaci_pl041_init(void)
|
||||
{
|
||||
rt_uint32_t i, maincr;
|
||||
|
||||
maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
|
||||
AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
void *base = (void *)((rt_uint32_t)(&PL041->rxcr1) + i * 0x14);
|
||||
|
||||
PL041_WRITE(base + AACI_IE, 0);
|
||||
PL041_WRITE(base + AACI_TXCR, 0);
|
||||
PL041_WRITE(base + AACI_RXCR, 0);
|
||||
}
|
||||
|
||||
PL041_WRITE(&PL041->intclr, 0x1fff);
|
||||
PL041_WRITE(&PL041->maincr, maincr);
|
||||
|
||||
PL041_WRITE(&PL041->reset, 0);
|
||||
aaci_pl041_delay(2);
|
||||
PL041_WRITE(&PL041->reset, RESET_NRST);
|
||||
|
||||
rt_hw_interrupt_install(43, aaci_pl041_irq_handle, &irq_tbl, "aaci_pl041");
|
||||
rt_hw_interrupt_umask(43);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define PL041_DUMP(_v) rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, &(_v), (_v))
|
||||
int _aaci_pl041_reg_dump(int argc, char **argv)
|
||||
{
|
||||
PL041_DUMP(PL041->rxcr1);
|
||||
PL041_DUMP(PL041->txcr1);
|
||||
PL041_DUMP(PL041->sr1);
|
||||
PL041_DUMP(PL041->isr1);
|
||||
PL041_DUMP(PL041->iie1);
|
||||
PL041_DUMP(PL041->rxcr2);
|
||||
PL041_DUMP(PL041->txcr2);
|
||||
PL041_DUMP(PL041->sr2);
|
||||
PL041_DUMP(PL041->isr2);
|
||||
PL041_DUMP(PL041->iie2);
|
||||
PL041_DUMP(PL041->rxcr3);
|
||||
PL041_DUMP(PL041->txcr3);
|
||||
PL041_DUMP(PL041->sr3);
|
||||
PL041_DUMP(PL041->isr3);
|
||||
PL041_DUMP(PL041->iie3);
|
||||
PL041_DUMP(PL041->rxcr4);
|
||||
PL041_DUMP(PL041->txcr4);
|
||||
PL041_DUMP(PL041->sr4);
|
||||
PL041_DUMP(PL041->isr4);
|
||||
PL041_DUMP(PL041->iie4);
|
||||
PL041_DUMP(PL041->sl1rx);
|
||||
PL041_DUMP(PL041->sl1tx);
|
||||
PL041_DUMP(PL041->sl2rx);
|
||||
PL041_DUMP(PL041->sl2tx);
|
||||
PL041_DUMP(PL041->sl12rx);
|
||||
PL041_DUMP(PL041->sl12tx);
|
||||
PL041_DUMP(PL041->slfr);
|
||||
PL041_DUMP(PL041->slistat);
|
||||
PL041_DUMP(PL041->slien);
|
||||
PL041_DUMP(PL041->intclr);
|
||||
PL041_DUMP(PL041->maincr);
|
||||
PL041_DUMP(PL041->reset);
|
||||
PL041_DUMP(PL041->sync);
|
||||
PL041_DUMP(PL041->allints);
|
||||
PL041_DUMP(PL041->mainfr);
|
||||
PL041_DUMP(PL041->dr1[0]);
|
||||
PL041_DUMP(PL041->dr2[0]);
|
||||
PL041_DUMP(PL041->dr3[0]);
|
||||
PL041_DUMP(PL041->dr4[0]);
|
||||
return 0;
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT_ALIAS(_aaci_pl041_reg_dump, __cmd_pl041_dump, aaci pl041 dump reg.);
|
||||
#endif
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* File : drv_pl041.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-25 RT-Thread the first version
|
||||
*/
|
||||
|
||||
#ifndef __DRV_PL041_H__
|
||||
#define __DRV_PL041_H__
|
||||
|
||||
#define PL041_BASE_ADDR (0x10004000)
|
||||
|
||||
/* offsets in CTRL_CH */
|
||||
#define AACI_RXCR 0x00 /* 29 bits Control Rx FIFO */
|
||||
#define AACI_TXCR 0x04 /* 17 bits Control Tx FIFO */
|
||||
#define AACI_SR 0x08 /* 12 bits Status */
|
||||
#define AACI_ISR 0x0C /* 7 bits Int Status */
|
||||
#define AACI_IE 0x10 /* 7 bits Int Enable */
|
||||
|
||||
/* both for AACI_RXCR and AACI_TXCR */
|
||||
#define AACI_CR_FEN (1 << 16) /* fifo enable */
|
||||
#define AACI_CR_COMPACT (1 << 15) /* compact mode */
|
||||
#define AACI_CR_SZ16 (0 << 13) /* 16 bits */
|
||||
#define AACI_CR_SZ18 (1 << 13) /* 18 bits */
|
||||
#define AACI_CR_SZ20 (2 << 13) /* 20 bits */
|
||||
#define AACI_CR_SZ12 (3 << 13) /* 12 bits */
|
||||
#define AACI_CR_SL12 (1 << 12)
|
||||
#define AACI_CR_SL11 (1 << 11)
|
||||
#define AACI_CR_SL10 (1 << 10)
|
||||
#define AACI_CR_SL9 (1 << 9)
|
||||
#define AACI_CR_SL8 (1 << 8)
|
||||
#define AACI_CR_SL7 (1 << 7)
|
||||
#define AACI_CR_SL6 (1 << 6)
|
||||
#define AACI_CR_SL5 (1 << 5)
|
||||
#define AACI_CR_SL4 (1 << 4)
|
||||
#define AACI_CR_SL3 (1 << 3)
|
||||
#define AACI_CR_SL2 (1 << 2)
|
||||
#define AACI_CR_SL1 (1 << 1)
|
||||
#define AACI_CR_EN (1 << 0) /* receive enable */
|
||||
|
||||
/* status register bits */
|
||||
#define AACI_SR_RXTOFE (1 << 11) /* rx timeout fifo empty */
|
||||
#define AACI_SR_TXTO (1 << 10) /* rx timeout fifo nonempty */
|
||||
#define AACI_SR_TXU (1 << 9) /* tx underrun */
|
||||
#define AACI_SR_RXO (1 << 8) /* rx overrun */
|
||||
#define AACI_SR_TXB (1 << 7) /* tx busy */
|
||||
#define AACI_SR_RXB (1 << 6) /* rx busy */
|
||||
#define AACI_SR_TXFF (1 << 5) /* tx fifo full */
|
||||
#define AACI_SR_RXFF (1 << 4) /* rx fifo full */
|
||||
#define AACI_SR_TXHE (1 << 3) /* tx fifo half empty */
|
||||
#define AACI_SR_RXHF (1 << 2) /* rx fifo half full */
|
||||
#define AACI_SR_TXFE (1 << 1) /* tx fifo empty */
|
||||
#define AACI_SR_RXFE (1 << 0) /* rx fifo empty */
|
||||
|
||||
#define AACI_ISR_RXTOFEINTR (1 << 6) /* rx fifo empty */
|
||||
#define AACI_ISR_URINTR (1 << 5) /* tx underflow */
|
||||
#define AACI_ISR_ORINTR (1 << 4) /* rx overflow */
|
||||
#define AACI_ISR_RXINTR (1 << 3) /* rx fifo */
|
||||
#define AACI_ISR_TXINTR (1 << 2) /* tx fifo intr */
|
||||
#define AACI_ISR_RXTOINTR (1 << 1) /* rx timeout */
|
||||
#define AACI_ISR_TXCINTR (1 << 0) /* tx complete */
|
||||
|
||||
/* interrupt enable */
|
||||
#define AACI_IE_RXTOIE (1 << 6) /*rx timeout interrupt enable*/
|
||||
#define AACI_IE_URIE (1 << 5) /*Transmit underrun interrupt enable*/
|
||||
#define AACI_IE_ORIE (1 << 4) /*Overrun receive interrupt enable*/
|
||||
#define AACI_IE_RXIE (1 << 3) /*Receive interrupt enable*/
|
||||
#define AACI_IE_TXIE (1 << 2) /*Transmit interrupt enable*/
|
||||
#define AACI_IE_RXTIE (1 << 1) /*Receive timeout interrupt enable*/
|
||||
#define AACI_IE_TXCIE (1 << 0) /*Transmit complete interrupt enable*/
|
||||
|
||||
/* interrupt status */
|
||||
#define AACI_ISR_RXTOFE (1 << 6) /* rx timeout fifo empty */
|
||||
#define AACI_ISR_UR (1 << 5) /* tx fifo underrun */
|
||||
#define AACI_ISR_OR (1 << 4) /* rx fifo overrun */
|
||||
#define AACI_ISR_RX (1 << 3) /* rx interrupt status */
|
||||
#define AACI_ISR_TX (1 << 2) /* tx interrupt status */
|
||||
#define AACI_ISR_RXTO (1 << 1) /* rx timeout */
|
||||
#define AACI_ISR_TXC (1 << 0) /* tx complete */
|
||||
|
||||
/* interrupt enable */
|
||||
#define AACI_IE_RXTOFE (1 << 6) /* rx timeout fifo empty */
|
||||
#define AACI_IE_UR (1 << 5) /* tx fifo underrun */
|
||||
#define AACI_IE_OR (1 << 4) /* rx fifo overrun */
|
||||
#define AACI_IE_RX (1 << 3) /* rx interrupt status */
|
||||
#define AACI_IE_TX (1 << 2) /* tx interrupt status */
|
||||
#define AACI_IE_RXTO (1 << 1) /* rx timeout */
|
||||
#define AACI_IE_TXC (1 << 0) /* tx complete */
|
||||
|
||||
/* slot flag register bits */
|
||||
#define AACI_SLFR_RWIS (1 << 13) /* raw wake-up interrupt status */
|
||||
#define AACI_SLFR_RGPIOINTR (1 << 12) /* raw gpio interrupt */
|
||||
#define AACI_SLFR_12TXE (1 << 11) /* slot 12 tx empty */
|
||||
#define AACI_SLFR_12RXV (1 << 10) /* slot 12 rx valid */
|
||||
#define AACI_SLFR_2TXE (1 << 9) /* slot 2 tx empty */
|
||||
#define AACI_SLFR_2RXV (1 << 8) /* slot 2 rx valid */
|
||||
#define AACI_SLFR_1TXE (1 << 7) /* slot 1 tx empty */
|
||||
#define AACI_SLFR_1RXV (1 << 6) /* slot 1 rx valid */
|
||||
#define AACI_SLFR_12TXB (1 << 5) /* slot 12 tx busy */
|
||||
#define AACI_SLFR_12RXB (1 << 4) /* slot 12 rx busy */
|
||||
#define AACI_SLFR_2TXB (1 << 3) /* slot 2 tx busy */
|
||||
#define AACI_SLFR_2RXB (1 << 2) /* slot 2 rx busy */
|
||||
#define AACI_SLFR_1TXB (1 << 1) /* slot 1 tx busy */
|
||||
#define AACI_SLFR_1RXB (1 << 0) /* slot 1 rx busy */
|
||||
|
||||
/* Interrupt clear register */
|
||||
#define AACI_ICLR_RXTOFEC4 (1 << 12) /* Receive timeout FIFO empty clear */
|
||||
#define AACI_ICLR_RXTOFEC3 (1 << 11) /* Receive timeout FIFO empty clear */
|
||||
#define AACI_ICLR_RXTOFEC2 (1 << 10) /* Receive timeout FIFO empty clear */
|
||||
#define AACI_ICLR_RXTOFEC1 (1 << 9) /* Receive timeout FIFO empty clear */
|
||||
#define AACI_ICLR_TXUEC4 (1 << 8) /* Transmit underrun error clear */
|
||||
#define AACI_ICLR_TXUEC3 (1 << 7) /* Transmit underrun error clear */
|
||||
#define AACI_ICLR_TXUEC2 (1 << 6) /* Transmit underrun error clear*/
|
||||
#define AACI_ICLR_TXUEC1 (1 << 5) /* Transmit underrun error clear */
|
||||
#define AACI_ICLR_RXOEC4 (1 << 4) /* Receive overrun error clear */
|
||||
#define AACI_ICLR_RXOEC3 (1 << 3) /* Receive overrun error clear */
|
||||
#define AACI_ICLR_RXOEC2 (1 << 2) /* Receive overrun error clear */
|
||||
#define AACI_ICLR_RXOEC1 (1 << 1) /* Receive overrun error clear */
|
||||
#define AACI_ICLR_WISC (1 << 0) /* Wake-up interrupt status clear */
|
||||
|
||||
/* Main control register bits AACI_MAINCR */
|
||||
#define AACI_MAINCR_SCRA(x) ((x) << 10) /* secondary codec reg access */
|
||||
#define AACI_MAINCR_DMAEN (1 << 9) /* dma enable */
|
||||
#define AACI_MAINCR_SL12TXEN (1 << 8) /* slot 12 transmit enable */
|
||||
#define AACI_MAINCR_SL12RXEN (1 << 7) /* slot 12 receive enable */
|
||||
#define AACI_MAINCR_SL2TXEN (1 << 6) /* slot 2 transmit enable */
|
||||
#define AACI_MAINCR_SL2RXEN (1 << 5) /* slot 2 receive enable */
|
||||
#define AACI_MAINCR_SL1TXEN (1 << 4) /* slot 1 transmit enable */
|
||||
#define AACI_MAINCR_SL1RXEN (1 << 3) /* slot 1 receive enable */
|
||||
#define AACI_MAINCR_LPM (1 << 2) /* low power mode */
|
||||
#define AACI_MAINCR_LOOPBK (1 << 1) /* loopback */
|
||||
#define AACI_MAINCR_IE (1 << 0) /* aaci interface enable */
|
||||
|
||||
/* Reset register bits. P65 */
|
||||
#define RESET_NRST (1 << 0)
|
||||
|
||||
/* Sync register bits. P65 */
|
||||
#define SYNC_FORCE (1 << 0)
|
||||
|
||||
/* Main flag register bits. P66 */
|
||||
#define MAINFR_TXB (1 << 1) /* transmit busy */
|
||||
#define MAINFR_RXB (1 << 0) /* receive busy */
|
||||
|
||||
#define PL041_CHANNLE_LEFT_DAC (0x1 << 3)
|
||||
#define PL041_CHANNLE_RIGHT_DAC (0x1 << 3)
|
||||
#define PL041_CHANNLE_LEFT_ADC (0x1 << 3)
|
||||
#define PL041_CHANNLE_RIGHT_ADC (0x1 << 3)
|
||||
|
||||
struct reg_pl041
|
||||
{
|
||||
volatile rt_uint32_t rxcr1; /* 0x000 */
|
||||
volatile rt_uint32_t txcr1; /* 0x004 */
|
||||
volatile rt_uint32_t sr1; /* 0x008 */
|
||||
volatile rt_uint32_t isr1; /* 0x00c */
|
||||
volatile rt_uint32_t iie1; /* 0x010 */
|
||||
volatile rt_uint32_t rxcr2; /* 0x014 */
|
||||
volatile rt_uint32_t txcr2; /* 0x018 */
|
||||
volatile rt_uint32_t sr2; /* 0x01c */
|
||||
volatile rt_uint32_t isr2; /* 0x020 */
|
||||
volatile rt_uint32_t iie2; /* 0x024 */
|
||||
volatile rt_uint32_t rxcr3; /* 0x028 */
|
||||
volatile rt_uint32_t txcr3; /* 0x02c */
|
||||
volatile rt_uint32_t sr3; /* 0x030 */
|
||||
volatile rt_uint32_t isr3; /* 0x034 */
|
||||
volatile rt_uint32_t iie3; /* 0x038 */
|
||||
volatile rt_uint32_t rxcr4; /* 0x03c */
|
||||
volatile rt_uint32_t txcr4; /* 0x040 */
|
||||
volatile rt_uint32_t sr4; /* 0x044 */
|
||||
volatile rt_uint32_t isr4; /* 0x048 */
|
||||
volatile rt_uint32_t iie4; /* 0x04c */
|
||||
volatile rt_uint32_t sl1rx; /* 0x050 */
|
||||
volatile rt_uint32_t sl1tx; /* 0x054 */
|
||||
volatile rt_uint32_t sl2rx; /* 0x058 */
|
||||
volatile rt_uint32_t sl2tx; /* 0x05c */
|
||||
volatile rt_uint32_t sl12rx; /* 0x060 */
|
||||
volatile rt_uint32_t sl12tx; /* 0x064 */
|
||||
volatile rt_uint32_t slfr; /* 0x068 */
|
||||
volatile rt_uint32_t slistat; /* 0x06c */
|
||||
volatile rt_uint32_t slien; /* 0x070 */
|
||||
volatile rt_uint32_t intclr; /* 0x074 */
|
||||
volatile rt_uint32_t maincr; /* 0x078 */
|
||||
volatile rt_uint32_t reset; /* 0x07c */
|
||||
volatile rt_uint32_t sync; /* 0x080 */
|
||||
volatile rt_uint32_t allints; /* 0x084 */
|
||||
volatile rt_uint32_t mainfr; /* 0x088 */
|
||||
volatile rt_uint32_t res08c;
|
||||
volatile rt_uint32_t dr1[8]; /* 0x090 */
|
||||
volatile rt_uint32_t dr2[8]; /* 0x0b0 */
|
||||
volatile rt_uint32_t dr3[8]; /* 0x0d0 */
|
||||
volatile rt_uint32_t dr4[8]; /* 0x0f0 */
|
||||
};
|
||||
|
||||
typedef struct reg_pl041 *reg_pl041_t;
|
||||
|
||||
#define PL041 ((reg_pl041_t)PL041_BASE_ADDR)
|
||||
|
||||
struct pl041_cfg
|
||||
{
|
||||
rt_uint32_t itype;
|
||||
rt_uint32_t otype;
|
||||
int vol;
|
||||
int rate;
|
||||
};
|
||||
typedef struct pl041_cfg *pl041_cfg_t;
|
||||
|
||||
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_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);
|
||||
int aaci_pl041_channle_disable(int channle);
|
||||
rt_err_t aaci_pl041_irq_register(int channle, pl041_irq_fun_t fun, void *user_data);
|
||||
rt_err_t aaci_pl041_irq_unregister(int channle);
|
||||
void aaci_pl041_irq_disable(int channle, rt_uint32_t vector);
|
||||
void aaci_pl041_irq_enable(int channle, rt_uint32_t vector);
|
||||
#endif
|
|
@ -0,0 +1,144 @@
|
|||
#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);
|
Loading…
Reference in New Issue