当前位置:网站首页>【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短接
边栏推荐
- How SVN views modified file records
- 深入理解 SQL 中的 Grouping Sets 语句
- How to delete a specific line from a text file using the SED command?
- 关于学习Qt编程的好书精品推荐
- Kotlin学习快速入门(7)——扩展的妙用
- 数据分析必备的能力
- Installation and configuration of network hard disk NFS
- 人生还在迷茫?也许这些订阅号里有你需要的答案!
- One brush 145 force deduction hot question-2 sum of two numbers (m)
- [mathematical logic] equivalent calculus and reasoning calculus of propositional logic (propositional logic | equivalent calculus | principal conjunctive (disjunctive) paradigm | reasoning calculus)**
猜你喜欢
Depth first search of graph
What material is 12cr1movr? Chemical property analysis of pressure vessel steel plate 12cr1movr
Mysql database DDL and DML
What material is 13crmo4-5 equivalent to in China? 13crmo4-5 chemical composition 13crmo4-5 mechanical properties
Talk about several methods of interface optimization
美团一面:为什么线程崩溃崩溃不会导致 JVM 崩溃
Web crawler knowledge day03
【Try to Hack】主动侦查隐藏技术
Leetcode: lucky number in matrix
智慧之道(知行合一)
随机推荐
C language modifies files by line
function overloading
[JDBC] API parsing
Unity notes unityxr simple to use
[combinatorics] polynomial theorem (polynomial coefficients | full arrangement of multiple sets | number of schemes corresponding to the ball sub model | polynomial coefficient correlation identity)
Capacités nécessaires à l'analyse des données
How to promote cross department project collaboration | community essay solicitation
Overview of satellite navigation system
CC2530 common registers for watchdog
RF analyze demo build step by step
LeetCode 1656. Design ordered flow
Kotlin学习快速入门(7)——扩展的妙用
One brush 149 force deduction hot question-10 regular expression matching (H)
网络安全web渗透技术
How do large consumer enterprises make digital transformation?
汇编实例解析--实模式下屏幕显示
深入理解 SQL 中的 Grouping Sets 语句
MySQL user management
[combinatorics] recursive equation (the relationship theorem between the solution of the recursive equation and the characteristic root | the linear property theorem of the solution of the recursive e
CC2530 common registers for port interrupts