当前位置:网站首页>【RT-Thread】nxp rt10xx 设备驱动框架之--Audio搭建和使用
【RT-Thread】nxp rt10xx 设备驱动框架之--Audio搭建和使用
2022-07-03 17:07:00 【L_17】
开发前准备
- 硬件平台:nxp rt10xx单片机
- IDE: Keil
1.Kconfig 修改和menuconfig配置
在menuconfig中的Hardware Drivers Config中不一定有SAI的选项,需要用户修改Kconfig完善
menuconfig BSP_USING_AUDIO
bool "Enable AUDIO"
select RT_USING_AUDIO
default n
if BSP_USING_AUDIO
config BSP_USING_AUDIO_PLAY
bool "Enable Audio Play"
default y
config BSP_USING_AUDIO_RECORD
bool "Enable AUDIO RECORD"
default n
config BSP_AUDIO_USING_DMA
bool "Enable AUDIO DMA"
default n
endif

在RT-Thread Components组件中选择Using Audio device drivers,这样才能使用audio框架

2.添加audio驱动框架和BSP驱动接口
驱动框架:audio.c audio_pipe.c BSP接口:drv_sai.c fsl_edma.c fsl_sai.c fsl_sai_edma.c

3.添加或修改drv_sai.c
官方bsp不一定完全支持,我们需要修改bsp,实现ops相关函数
audio.h头文件中修改
struct rt_audio_ops
{
rt_err_t (*getcaps)(struct rt_audio_device *audio, struct rt_audio_caps *caps);
rt_err_t (*configure)(struct rt_audio_device *audio, struct rt_audio_caps *caps);
rt_err_t (*init)(struct rt_audio_device *audio);
rt_err_t (*start)(struct rt_audio_device *audio, int stream);
rt_err_t (*stop)(struct rt_audio_device *audio, int stream);
rt_err_t (*control)(struct rt_audio_device *audio, int cmd, void *arg);
rt_size_t (*transmit)(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size);
/* get page size of codec or private buffer's info */
void (*buffer_info)(struct rt_audio_device *audio, struct rt_audio_buf_info *info);
};
从git下载下来的rt系列bsp支持包有很多问题,有些英文命名有很多错误地方笔者顺便也修改了
static struct rt_audio_ops imxrt_payer_ops =
{
.getcaps = imxrt_audio_getcaps,
.configure = imxrt_audio_configure,
.init = imxrt_audio_init,
.start = imxrt_audio_start,
.stop = imxrt_audio_stop,
.transmit = imxrt_audio_transmit,
.buffer_info = imxrt_audio_buffer_info,
.control = imxrt_audio_control,
};
int rt_hw_sound_init(void)
{
rt_uint8_t* tx_fifo = RT_NULL;
rt_uint8_t* rx_fifo = RT_NULL;
sai_tx.base = SAI1;
sai_rx.base = SAI1;
sai_tx.irqn = SAI1_IRQn;
#ifdef BSP_AUDIO_USING_DMA
static struct saidma_tx_config sai_txdma = {
.channel = C052_TX_CHANNEL, .request = kDmaRequestMuxSai1Tx };
sai_tx.dma_tx = &sai_txdma;
sai_tx.dma_flag |= RT_DEVICE_FLAG_DMA_TX;
#if defined (BSP_USING_AUDIO_RECORD)
static struct saidma_rx_config sai_rxdma = {
.channel = C052_RX_CHANNEL, .request = kDmaRequestMuxSai1Rx };
sai_rx.dma_rx = &sai_rxdma;
#endif
#endif
/* 分配 DMA 搬运 buffer */
tx_fifo = rt_calloc(1, AUD_FIFO_SIZE);
rx_fifo = rt_calloc(1, AUD_FIFO_SIZE);
if(tx_fifo == RT_NULL || rx_fifo == RT_NULL)
{
return -RT_ENOMEM;
}
//缓存初始化
rt_memset(tx_fifo, 0, AUD_FIFO_SIZE);
imxrt_payer_dev.tx_fifo = tx_fifo;
rt_memset(rx_fifo, 0, AUD_FIFO_SIZE);
imxrt_payer_dev.rx_fifo = rx_fifo;
imxrt_payer_dev.audio.ops = &imxrt_payer_ops;
//audio 注册会调用 init 所以前面不用init
rt_audio_register(&imxrt_payer_dev.audio, "sound0", RT_DEVICE_FLAG_RDWR, &imxrt_payer_dev);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);
audio.c 框架中,注册实现如下内容
rt_err_t rt_audio_register(struct rt_audio_device *audio, const char *name, rt_uint32_t flag, void *data)
{
rt_err_t result = RT_EOK;
struct rt_device *device;
RT_ASSERT(audio != RT_NULL);
device = &(audio->parent);
device->type = RT_Device_Class_Sound;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
#ifdef RT_USING_DEVICE_OPS
device->ops = &audio_ops;
#else
device->init = _audio_dev_init;
device->open = _audio_dev_open;
device->close = _audio_dev_close;
device->read = _audio_dev_read;
device->write = _audio_dev_write;
device->control = _audio_dev_control;
#endif
device->user_data = data;
/* register a character device */
result = rt_device_register(device, name, flag | RT_DEVICE_FLAG_REMOVABLE);
/* initialize audio device */
if (result == RT_EOK)
result = rt_device_init(device);
return result;
}
注册流程,主要实现设备链表注册和audio外设初始化
1.int rt_hw_sound_init(void)
2.rt_err_t rt_audio_register(struct rt_audio_device *audio, const char *name, rt_uint32_t flag, void *data)
函数内实现 rt_device_register 然后执行 rt_device_init即:ops结构体中注册的init
3.rt_err_t rt_device_register(rt_device_t dev,const char *name,rt_uint16_t flags)
4.void rt_object_init(struct rt_object *object,enum rt_object_class_type type,const char *name)
将设备对象信息写入链表
补充一点,笔者发现官方audio框架很多地方英文名称有误,比如:将audio写成aduio,希望官方组件注意完善

初始化函数,就按照裸机思维方式写就行
static rt_err_t imxrt_audio_init(struct rt_audio_device* audio)
{
RT_ASSERT(audio != RT_NULL);
CLOCK_EnableClock(kCLOCK_Iomuxc);
CLOCK_EnableClock(kCLOCK_Sai1);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_09_SAI1_MCLK, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC, 1U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_09_SAI1_MCLK,0x10B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00,0x10B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00,0x10B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK,0x10B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC,0x10B0u);
CLOCK_InitAudioPll(&audioPllConfig);
CLOCK_SetMux(kCLOCK_Sai1Mux, DEMO_SAI1_CLOCK_SOURCE_SELECT);
CLOCK_SetDiv(kCLOCK_Sai1PreDiv, DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER);
CLOCK_SetDiv(kCLOCK_Sai1Div, DEMO_SAI1_CLOCK_SOURCE_DIVIDER);
BOARD_EnableSaiMclkOutput(RT_TRUE);
EDMA_CreateHandle(&sai_tx.dma_tx->edma, DMA0, sai_tx.dma_tx->channel);
DMAMUX_SetSource(DMAMUX, sai_tx.dma_tx->channel, (rt_uint8_t)sai_tx.dma_tx->request);
DMAMUX_EnableChannel(DMAMUX, sai_tx.dma_tx->channel);
SAI_TxGetDefaultConfig(&config);
config.masterSlave = kSAI_Master;
config.protocol = kSAI_BusI2S;
SAI_TxInit(sai_tx.base, &config);
#if defined (BSP_USING_AUDIO_RECORD)
EDMA_CreateHandle(&sai_rx.dma_rx->edma, DMA0, sai_rx.dma_rx->channel);
DMAMUX_SetSource(DMAMUX, sai_rx.dma_rx->channel, (rt_uint8_t)sai_rx.dma_rx->request);
DMAMUX_EnableChannel(DMAMUX, sai_rx.dma_rx->channel);
SAI_RxGetDefaultConfig(&config);
config.masterSlave = kSAI_Slave;
config.protocol = kSAI_BusI2S;
SAI_RxInit(sai_rx.base, &config);
#endif
//dma 中断优先级设置
NVIC_SetPriority(C052_SAIDMA_IRQ,SAI_ISR_PRE);
format.bitWidth = kSAI_WordWidth32bits;
format.channel = 0U;
format.sampleRate_Hz = kSAI_SampleRate44100Hz;
format.protocol = config.protocol;
format.stereo = kSAI_Stereo;
format.isFrameSyncCompact = false;
format.watermark = FSL_FEATURE_SAI_FIFO_COUNT / 2U;
SAI_TransferTxCreateHandleEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, sai_TxDmaCallback, NULL, &sai_tx.dma_tx->edma);
SAI_TransferTxSetFormatEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
#if defined (BSP_USING_AUDIO_RECORD)
SAI_TransferRxCreateHandleEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, sai_RxDmaCallback, NULL, &sai_rx.dma_rx->edma);
SAI_TransferRxSetFormatEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
#endif
return RT_EOK;
}
#define AUD_BLOCK_CNT 2
#define AUD_BLOCK_SIZE 512
#define AUD_FIFO_SIZE (AUD_BLOCK_SIZE * AUD_BLOCK_CNT)
static void sai_TxDmaCallback(I2S_Type* base, sai_edma_handle_t* handle, rt_int32_t status, void* userData)
{
rt_audio_tx_complete(&imxrt_payer_dev.audio);
}
#if defined (BSP_USING_AUDIO_RECORD)
static void sai_RxDmaCallback(I2S_Type* base, sai_edma_handle_t* handle, rt_int32_t status, void* userData)
{
rt_audio_rx_done(&imxrt_payer_dev.audio, &imxrt_payer_dev.rx_fifo[0], AUD_BLOCK_SIZE);
}
#endif
//开始dma传输
static rt_err_t imxrt_audio_start(struct rt_audio_device* audio, int stream)
{
RT_ASSERT(audio != RT_NULL);
xfer.data = imxrt_payer_dev.rx_fifo;
xfer.dataSize = AUD_BLOCK_SIZE;
#if defined (BSP_USING_AUDIO_RECORD)
SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
#endif
xfer.data = imxrt_payer_dev.tx_fifo;
xfer.dataSize = AUD_BLOCK_SIZE;
SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
return RT_EOK;
}
static rt_err_t imxrt_audio_control (struct rt_audio_device *audio, int cmd, void *args)
{
rt_err_t result = RT_EOK;
switch (cmd)
{
case AUDIO_CTL_START:
imxrt_audio_start(audio,AUDIO_STREAM_REPLAY);
break;
case AUDIO_CTL_STOP:
imxrt_audio_stop(audio,AUDIO_STREAM_REPLAY);
break;
default:
result = -RT_ERROR;
break;
}
return result;
}
4.搭建应用层demo
应用层使用可参考官方链接,功能不正常就需要看bsp层是不是没搭建好:AUDIO设备 (rt-thread.org)
/**************************************************START OF FILE*****************************************************/
/*------------------------------------------------------------------------------------------------------------------ Includes */
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
/*------------------------------------------------------------------------------------------------------------------ Macros */
#define SOUND_DEVICE_NAME "sound0"
#define MIC_DEVICE_NAME "record"
#define THREAD_PRIORITY 12
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 2
#define FIFO_SIZE 512
/*------------------------------------------------------------------------------------------------------------------ Variables */
static rt_device_t h_dev_speaker;
static rt_device_t h_dev_mic;
static rt_thread_t h_thread_audio = RT_NULL;
int32_t micBuf[FIFO_SIZE/4],speBuf[FIFO_SIZE/4];
/*------------------------------------------------------------------------------------------------------------------ Functions */
static void thread_audio(void *parameter)
{
int i;
int length;
rt_kprintf("thread audio start\n");
#if 0 //bypass
while(1)
{
length = rt_device_read(h_dev_mic, 0, micBuf, FIFO_SIZE);
if(length)
{
memcpy(speBuf,micBuf,length);
rt_device_write(h_dev_speaker, 0, speBuf, length);
}
}
#else //短接输出到输入
for(i=0;i<FIFO_SIZE/4;i++)
{
speBuf[i] = i*0x100;
}
rt_device_write(h_dev_speaker, 0, speBuf, FIFO_SIZE);
while(1)
{
length = rt_device_read(h_dev_mic, 0, micBuf, FIFO_SIZE);
if(length)
{
rt_device_write(h_dev_speaker, 0, speBuf, length);
}
}
#endif
}
int xAPP_AudioInit(void)
{
rt_err_t ret = RT_EOK;
/* 根据设备名称查找 Audio 设备,获取设备句柄 */
h_dev_speaker = rt_device_find(SOUND_DEVICE_NAME);
if (!h_dev_speaker)
{
rt_kprintf("find %s failed!\n", SOUND_DEVICE_NAME);
return RT_ERROR;
}
ret = rt_device_open(h_dev_speaker, RT_DEVICE_OFLAG_WRONLY);
if (ret != RT_EOK)
{
rt_kprintf("open %s failed!\n", SOUND_DEVICE_NAME);
return RT_ERROR;
}
h_dev_mic = rt_device_find(MIC_DEVICE_NAME);
if (!h_dev_mic)
{
rt_kprintf("find %s failed!\n", MIC_DEVICE_NAME);
return RT_ERROR;
}
ret = rt_device_open(h_dev_mic, RT_DEVICE_OFLAG_WRONLY);
if (ret != RT_EOK)
{
rt_kprintf("open %s failed!\n", MIC_DEVICE_NAME);
return RT_ERROR;
}
ret = rt_device_control(h_dev_speaker, AUDIO_CTL_START, RT_NULL);
if (ret != RT_EOK)
{
rt_kprintf("start %s failed!\n", SOUND_DEVICE_NAME);
return RT_ERROR;
}
//创建Audio线程
h_thread_audio = rt_thread_create("t_audio",thread_audio,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY,THREAD_TIMESLICE);
if (h_thread_audio != RT_NULL)
{
rt_thread_startup(h_thread_audio);
}
return 0;
}
/* 导出到 msh 命令列表中 */
//MSH_CMD_EXPORT(audioInit, audio init);
/****************************************************END OF FILE*****************************************************/
测试输入输出,SAI RT和TX短接


边栏推荐
- CC2530 common registers for watchdog
- 手把手带你入门 API 开发
- Simple configuration of postfix server
- One brush 146 force buckle hot question-3 longest substring without repeated characters (m)
- 建立自己的网站(23)
- An example of HP array card troubleshooting
- 大消费企业怎样做数字化转型?
- LeetCode13.罗马数字转整数(三种解法)
- 静态程序分析(一)—— 大纲思维导图与内容介绍
- Thread pool: the most common and error prone component of business code
猜你喜欢

UCORE overview

Network security web penetration technology

Mysql database DDL and DML

C语言按行修改文件

What is the difference between 14Cr1MoR container plate and 14Cr1MoR (H)? Chemical composition and performance analysis of 14Cr1MoR

线程池:业务代码最常用也最容易犯错的组件

手把手带你入门 API 开发
![[try to hack] active detection and concealment technology](/img/43/d48f851268fec566ce0cc83bd9557e.png)
[try to hack] active detection and concealment technology
![[JDBC] API parsing](/img/75/0f69a4e246a571688355bb13e2cd73.jpg)
[JDBC] API parsing

CC2530 common registers for timer 1
随机推荐
Squid 服务启动脚本
Talk about several methods of interface optimization
在iptables防火墙下开启vsftpd的端口
Apache服务挂起Asynchronous AcceptEx failed.
网络安全web渗透技术
Mysql database DDL and DML
远程办公之如何推进跨部门项目协作 | 社区征文
Rsync远程同步
绝对定位时元素水平垂直居中
Life is still confused? Maybe these subscription numbers have the answers you need!
Thread pool: the most common and error prone component of business code
CC2530 common registers for watchdog
简单配置PostFix服务器
CC2530 common registers for crystal oscillator settings
13mnnimo5-4 German standard steel plate 13MnNiMo54 boiler steel 13MnNiMo54 chemical properties
HP 阵列卡排障一例
Host based intrusion system IDS
What material is sa537cl2 equivalent to in China? Sa537cl2 corresponding material
CC2530 common registers for ADC single channel conversion
IL Runtime