当前位置:网站首页>[RT thread] NXP rt10xx device driver framework -- Audio construction and use
[RT thread] NXP rt10xx device driver framework -- Audio construction and use
2022-07-03 17:11:00 【L_ seventeen】
Preparation before development
- Hardware platform :nxp rt10xx Single chip microcomputer
- IDE: Keil
1.Kconfig Modifications and menuconfig To configure
stay menuconfig Medium Hardware Drivers Config There may not be SAI The option to , User modification required Kconfig perfect
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

stay RT-Thread Components Select from components Using Audio device drivers, So that we can use audio frame

2. add to audio Drive framework and BSP Driver interface
Drive frame :audio.c audio_pipe.c BSP Interface :drv_sai.c fsl_edma.c fsl_sai.c fsl_sai_edma.c

3. Add or modify drv_sai.c
official bsp May not fully support , We need to change bsp, Realization ops Correlation function
audio.h Modify in the header file
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);
};
from git downloaded rt series bsp There are many problems with the support package , There are many mistakes in some English names. The author also revised them by the way
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
/* Distribute DMA Carry 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;
}
// Cache initialization
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 Registration will call init So don't use the front 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 In the frame , Register to achieve the following
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;
}
Registration process , It mainly realizes the registration of equipment linked list and audio Peripheral initialization
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)
Implement... In function rt_device_register And then execute rt_device_init namely :ops Registered in the structure 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)
Write the device object information into the linked list
add , The author found that the official audio The English name of the framework is wrong in many places , such as : take audio It's written in aduio, I hope the official components can be improved

Initialization function , Just write according to the bare metal thinking mode
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 Interrupt priority setting
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
// Start dma transmission
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. Build application layer demo
For the use of the application layer, please refer to the official link , If the function is abnormal, you need to see bsp Is the layer not built well :AUDIO equipment (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 // Short the output to the input
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;
/* Find by device name Audio equipment , Get device handle */
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;
}
// establish Audio Threads
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;
}
/* Export to msh In the command list */
//MSH_CMD_EXPORT(audioInit, audio init);
/****************************************************END OF FILE*****************************************************/
Test input and output ,SAI RT and TX Short circuit


边栏推荐
- HP 阵列卡排障一例
- 美团一面:为什么线程崩溃崩溃不会导致 JVM 崩溃
- RF Analyze Demo搭建 Step by Step
- Squid 服务启动脚本
- Host based intrusion system IDS
- Informatics Olympiad all in one YBT 1175: divide by 13 | openjudge noi 1.13 27: divide by 13
- Talk about several methods of interface optimization
- Recommendation of good books on learning QT programming
- C language string inversion
- 人生还在迷茫?也许这些订阅号里有你需要的答案!
猜你喜欢

Great changes! National housing prices fell below the 10000 yuan mark

CC2530 common registers for serial communication

CC2530 common registers for ADC single channel conversion

What is your income level in the country?

Pools de Threads: les composants les plus courants et les plus sujets aux erreurs du Code d'affaires

The most complete postman interface test tutorial in the whole network, API interface test

kubernetes资源对象介绍及常用命令(三)

One brush 149 force deduction hot question-10 regular expression matching (H)

静态程序分析(一)—— 大纲思维导图与内容介绍

Build your own website (23)
随机推荐
29:第三章:开发通行证服务:12:开发【获得用户账户信息,接口】;(使用VO类包装查到的数据,以符合接口对返回数据的要求)(在多处都会用到的逻辑,在Controller中可以把其抽成一个共用方法)
[2. Basics of Delphi grammar] 2 Object Pascal data type
Depth first search of graph
静态程序分析(一)—— 大纲思维导图与内容介绍
PHP production website active push (website)
The most complete postman interface test tutorial in the whole network, API interface test
The largest matrix (H) in a brush 143 monotone stack 84 histogram
數據分析必備的能力
Leetcode13. Roman numeral to integer (three solutions)
SVN完全备份svnadmin hotcopy
CC2530 common registers for watchdog
Deep understanding of grouping sets statements in SQL
深入理解 SQL 中的 Grouping Sets 语句
网络硬盘NFS的安装与配置
Take you to API development by hand
[combinatorics] recursive equation (constant coefficient linear homogeneous recursive equation | constant coefficient, linear, homogeneous concept description | constant coefficient linear homogeneous
比亚迪、长城混动市场再“聚首”
SVN如何查看修改的文件记录
C语言按行修改文件
【RT-Thread】nxp rt10xx 设备驱动框架之--Audio搭建和使用