当前位置:网站首页>【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短接


边栏推荐
- 网络安全web渗透技术
- CC2530 common registers for ADC single channel conversion
- 【Try to Hack】主动侦查隐藏技术
- 网络硬盘NFS的安装与配置
- Redis: operation commands for list type data
- SVN完全备份svnadmin hotcopy
- C语言字符串练习
- CC2530 common registers for port interrupts
- An example of HP array card troubleshooting
- Prepare for the golden three silver four, 100+ software test interview questions (function / interface / Automation) interview questions. win victory the moment one raises one 's standard
猜你喜欢

大变局!全国房价,跌破万元大关

Why is WPA3 security of enterprise business so important?
![29: Chapter 3: develop Passport Service: 12: develop [obtain user account information, interface]; (use VO class to package the found data to meet the requirements of the interface for the returned da](/img/1c/c655c8232de1c56203873dcf171f45.png)
29: Chapter 3: develop Passport Service: 12: develop [obtain user account information, interface]; (use VO class to package the found data to meet the requirements of the interface for the returned da
![[try to hack] active detection and concealment technology](/img/43/d48f851268fec566ce0cc83bd9557e.png)
[try to hack] active detection and concealment technology

Mysql database -dql

Talk about several methods of interface optimization

What is the material of 13mnnimor? 13mnnimor steel plate for medium and low temperature pressure vessels

Meituan side: why does thread crash not cause JVM crash

CC2530 common registers for ADC single channel conversion

CC2530 common registers for watchdog
随机推荐
What material is 12cr1movr? Chemical property analysis of pressure vessel steel plate 12cr1movr
[combinatorics] recursive equation (example of solving recursive equation without multiple roots | complete process of solving recursive equation without multiple roots)
Simple use of unity pen XR grab
Daily code 300 lines learning notes day 10
Deep understanding of grouping sets statements in SQL
PHP online confusion encryption tutorial sharing + basically no solution
Mysql database -dql
Cross border e-commerce: advantages of foreign trade enterprises in overseas social media marketing
How do large consumer enterprises make digital transformation?
线程池:业务代码最常用也最容易犯错的组件
New library online | cnopendata complete data of Chinese insurance institution outlets
CC2530 common registers
29: Chapter 3: develop Passport Service: 12: develop [obtain user account information, interface]; (use VO class to package the found data to meet the requirements of the interface for the returned da
Bcvp developer community 2022 exclusive peripheral first bullet
Svn full backup svnadmin hotcopy
The way of wisdom (unity of knowledge and action)
CC2530 common registers for watchdog
MySQL user management
What material is sa537cl2 equivalent to in China? Sa537cl2 corresponding material
设计电商秒杀