# SOUNDEC SDK软件模块说明

# 概述

《SOUNDEC SDK软件模块说明》的目标读者群体是使用SNC8x系列专业音频处理器的开发工程师,本说明以SNC8600为例,描述了SOUNDEC SDK各软件模块的功能,接口及使用等。本说明可配合《SOUNDEC SDK软件架构说明》一起使用,以加深对九音科技软件平台的理解。关于SNC8x系列处理器的对比,请参考产品对比表 (opens new window)

SNC8600集成了Cadence最高可达200Mhz的高性能HiFi3 DSP内核,24bit/192Ksps高规格立体声输入输出Codec, 可外接2路模拟麦以及最多10个数字麦,提供512KB RAM以及可配置的Flash闪存(缺省为1MB)。其他外围电路还包括支持USB UAC2.0控制器,3路I2S, 2路I2C, 一路UART以及一组GPIO,同时还有一路辅助ADC用于按键检测及模拟传感器监控。

SOUNDEC SDK软件包提供了完善的设备驱动,音频框架,样例程序以及定制模板文件,本说明集中描述SNC8600在音频应用中可能使用到的各软件模块、接口、功能及使用等,以帮助用户加深对SOUNDEC SDK的了解。

# GPIO说明

# 功能综述

SNC8600的通用IO的管脚位置和名称如下表所示

SNC8600共有17组GPIO ,除GPIOAO固定作为系统唤醒的输入使用,其余16组都是可复用管脚,可通过配置适当的寄存器给它们分配不同的I/O功能。 可复用GPIO可以配置为内部上拉/下拉。当被配置为输⼊时,可通过读取寄存器获取输⼊值,输⼊可以被设置为边沿触发或电平触发来产⽣CPU中断。

# GPIO使用

GPIO 管脚

GPIO的使用主要是配置管脚复用以及中断处理,涉及到的API包括:

  • hal_gpio_set_pinmux_function //设置管脚复用

  • hal_gpio_set_direction //设置输入输出

  • gpio_cfg_intr //配置中断

  • gpio_enable_intr //使能中断

# 示例代码

GPIO的使用示例代码,请参见app_gpio_test.c

# I2S接口说明

# 功能综述

SNC8600使用I2S接口与其他外部数字音频设备相连接,比如驱动功放,与蓝牙芯片以及上位主控之间的音频数据传输:

  • 支持飞利浦I2S协议,支持MSB、LSB对齐格式

  • 支持4路I2S

  • 支持Master模式和Slave模式

  • 支持12、16、20、24、32位的音频数据

  • sclk的门控信号可由外部控制

  • FIFO的深度为16,触发级可配置

  • 内置可编程的DMA寄存器

  • 支持TDM(Slave)

  • 支持8K到192K的采样频率

注:

32帧长(Wordsize)下支持的采样率:8K-96K

24帧长(Wordsize)下支持的采样率:8K-192K(这边的192K实际采样率是193.5K)

16帧长(Wordsize)下支持的采样率:8K-192K(这边的192K实际采样率是193.5K)

# 信号时序图

# 编程接口

8600有3路I2S, 可以工作在master或slave模式下, 以I2S1为例

# 初始化 I2S_init()

hal_sysctrl_set_clock_gate(hal_sysctrl_get_clock_gate() | (1 << HAL_SYSCTRL_CLKGT_I2S1)); //设置时钟源
hal_gpio_set_pinmux_function(GPIO_0, 2);  //设置I2S的GPIO复用引脚
......
i2s_cfg.mode 	 	 = I2S_MODE_MASTER;  //I2S的参数设置
i2s_cfg.standard	 = I2S_STANDARD_PHILIPS;
i2s_cfg.frameLength  = I2S_FRAMELENGTH_32B;
....
hal_i2s_init(I2S_INDEX_1, &i2s_cfg);  //初始化I2S1
i2s_it_start(I2S_INDEX_1, I2S1_IRQn, rxCB, txCB, i2s1_isr_handler); //使能I2S1通道
1
2
3
4
5
6
7
8
9

# 中断处理程序

I2S的数据发送和接收实际上是在I2S的中断处理中进行的,当I2S的发送FIFO为空,或者接收FIFO已满的情况下,会触发I2S的中断,进入相应的中断处理程序,调用者可以在中断处理中以回调形式发送数据,或者处理接收到的数据.

void i2s_isr_status_handler(uint8_t i2sx)
{
......
	int32_t rxData[I2S_RX_FIFO_LEVEL*2] = {0};
	int32_t txData[I2S_TX_FIFO_LEVEL*2] = {0}; 
	if(hal_i2s_tx_ready(i2sx)) //如果是EMPTY中断
	{	
		if (i2sDevInfos[i2sx].isrCb[0])
			i2sDevInfos[i2sx].isrCb[0](i2sDevInfos[i2sx].txCbArg, &txData[0], &len,BIT_SLOT_32);//通过回调获取要发送的数据
		hal_i2s_tx_data(i2sx, &txData[txCount]); //发送数据
	}
	if(hal_i2s_rx_ready(i2sx)) //如果是FULL中断
	{
		if (i2sDevInfos[i2sx].isrCb[1])
			i2sDevInfos[i2sx].isrCb[1](i2sDevInfos[i2sx].rxCbArg, &rxData[0], &len,BIT_SLOT_32);//通过回调传回接收到的数据
	}
	//其他传输状态的处理
....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# SET接口和GET接口

drv_status_t i2s_set_audio_freq(uint8_t i2sx, uint32_t audioFreq);
void i2s1_set_audio_freq(uint32_t audioFreq);
void i2s2_set_audio_freq(uint32_t audioFreq);
void i2s3_set_audio_freq(uint32_t audioFreq);
uint32_t i2s1_get_audio_freq(void);
uint32_t i2s2_get_audio_freq(void);
uint32_t i2s3_get_audio_freq(void);
drv_status_t i2s_tx_enable(uint8_t i2sx, uint8_t enable);
drv_status_t i2s_rx_enable(uint8_t i2sx, uint8_t enable);
drv_status_t i2s_set_mode(uint8_t i2sx, uint8_t mode);
drv_status_t i2s_set_format(uint8_t i2sx, uint8_t format);
drv_status_t i2s_set_sample_rate(uint8_t i2sx, uint32_t sampleRate);
drv_status_t i2s_set_word_length(uint8_t i2sx, uint8_t wordLength);
drv_status_t i2s_set_frame_length(uint8_t i2sx, uint8_t frameLength);
drv_status_t i2s_set_mute(uint8_t i2sx, uint8_t mute);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# I2C接口说明

# 功能综述

SNC8600提供的I2C接口,既可以作为主设备(Master),对其他I2C从设备(例如大多数数字传感器)进行控制与读写, 也可以作为从设备(Master),接收其他I2C主设备(比如其他上位机)的数据。

# 信号时序图

# 接口使用

以Master接口为例(Slave模式类似),

# 初始化 i2c_init()

void i2c_init(void)  
{
    //配置I2C的时钟信号
	uint32_t clkGate = hal_sysctrl_get_clock_gate();
	SETBIT(clkGate, HAL_SYSCTRL_CLKGT_I2C2);
	hal_sysctrl_set_clock_gate(clkGate);
	//配置I2C接口参数
	hal_i2c_disable(I2C_MASTER_SEL);
	hal_i2c_set_mode(I2C_MASTER_SEL, &i2cMasterCfg);
	hal_i2c_set_speed(I2C_MASTER_SEL, &i2cMasterCfg);
	hal_i2c_set_tx_fifo_level(I2C_MASTER_SEL, i2cMasterCfg.txFifo);
	hal_i2c_set_rx_fifo_level(I2C_MASTER_SEL, i2cMasterCfg.rxFifo);
    //配置并使能I2C中断
	hal_i2c_set_intr_mask(I2C_MASTER_SEL, &i2cMasterCfg);
	hal_interrupt_register_isr_handler(I2C_MASTER_SEL_IRQ, i2c_master_isr);
	hal_interrupt_enable_irq(I2C_MASTER_SEL_IRQ);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 数据传输

I2C的数据传输有两种方式,一种是在中断处理过程中根据中断状态进行数据收发,另一种就是直接调用发送和接收接口函数。

1)中断处理函数 i2c_Master_isr()

static void i2c_master_isr(void* arg)
{
    uint16_t stat = hal_i2c_get_intr_stat(I2C_MASTER_SEL); //获取中断状态
	if (stat & I2C_INTR_STAT_RX_UNDER) //接收fifo空
	{
	    //调用I2C Read接口读取数据
	}
	if(stat & I2C_INTR_STAT_STOP_DET) //I2C一次读写结束
	{
	    //如果有数据发送
		调用I2C Write接口继续发送数据
		//如果有数据接收
		调用I2C Read接口读取数据
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

2)发送数据接口i2c_Master_write_bulk()

//传输I2C从设备地址
hal_i2c_update_tar_addr(i2cx, devAddr);
hal_i2c_write_data(i2cx, regAddr);
//传输数据
while(len)
{
	if((hal_i2c_get_status(i2cx)&I2C_STAT_TFNF))
	{
		hal_i2c_write_data(i2cx, buf[i]);
		i++;
		len--;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

3)接收数据接口i2c_Master_read_bulk()

while(len)
{
	uint8_t rx = hal_i2c_get_rx_fifo_len(i2cx);

	if(rx < 7 && cnt++ >= 100)
	{
		cnt = 0;
		//发送从设备地址准备读取数据
		hal_i2c_master_read_command(i2cx);
	}
	if(rx)
	{   //读取数据
		buf[i++] = hal_i2c_read_data(i2cx);
		len--;
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# UART接口

# 功能综述

SNC8600提供一个UART接口,可用来做串口调试,或者是和其他外部IC的串口通信,其基本功能:

  • 全双工操作

  • 5-8位字符操作

  • 可配置的奇偶校验(偶校验,奇校验,不用奇偶校验,或固定校验位)

  • 可配置的停止位(1,1.5,2位)

  • 中止(Break)产生和探测功能

  • 16级深度(字节宽度)的接收FIFO,可配置触发级和超时中断

  • 16级深度(字节宽度)的发送FIFO,可配置触发级中断

  • 4位可屏蔽中断源,中断优先级处理

  • 灵活的波特率配置

# 编程API

# 初始化配置

uart_init

//配置时钟,波特率,数据格式,FIFO深度等

void uart_init(void)
{

	uint32_t clkGate = hal_sysctrl_get_clock_gate();
	SETBIT(clkGate, HAL_SYSCTRL_CLKGT_UART);
	hal_sysctrl_set_clock_gate(clkGate);
	uart_cfg_t config;
	config.baudRate 	= UART_BUARD_RATE;
	config.dataLength   = UART_DATALENGRTH_8BIT;
	config.stopBits 	= UART_STOPBITS_1BIT;
	config.parityEnable = UART_PARITY_DISABLE;
	config.paritySelect = UART_PARITY_NO;			//UART_Parity_Even;
	config.breakControl = UART_BREAK_NORMAL;
	config.rxFifoLevel  = UART_R_FIFO_LEVEL_8B;	//UART_R_FIFO_Level_1B;
	config.txFifoLevel  = UART_T_FIFO_LEVEL_8B;	//UART_T_FIFO_Level_0B;
	config.inteConfig   = UART_ISR_ALL_DISABLE;
	hal_uart_init(&config);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 数据传输

数据的发送和接收可以采用两种方式,一种是中断传输,就是在代表可以发送的硬件中断发生后通过回调来发送或接收数据,另一种是普通传输,通过查询硬件状态位,等待硬件ready之后再发送和接收数据。

1)中断传输

中断传输需要首先初始化中断,注册中断处理程序及对应中断状态对应的回调函数

void uart_interrupt_init(void)
{
    hal_interrupt_register_isr_handler(UART_IRQn, uart_isr_handler);
    hal_interrupt_enable_irq(UART_IRQn);
    uart_register_callback(UART_TIMEOUT_CALLBACK_ID, timeout_callback);
}
1
2
3
4
5
6

中断发送

注册发送回调函数并使能发送中断

void uart_transmit_interrupt_config(uint8_t *txBuffer, uint16_t num)
{
	uart_queue_t *queue = uqueue;

	queue->head = queue->tail =  0;
	if(num > queue->size)
		num = queue->size;

	while(num)
	{
		queue->pdata[queue->tail] = *txBuffer;
		txBuffer++;
		queue->tail++;
		num--;
	}
	uart_register_callback(UART_TX_CALLBACK_ID, tx_callback);
	hal_uart_int_config(HAL_UART_IER_THREIENABLE_MASK,TURN_ON);//enable uart tx interrupt
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

数据发送在回调函数中完成

void uart_transmit_interrupt(void)
{
	uart_queue_t *queue = uqueue;

	while(queue->head < queue->size)
	{
		if(hal_uart_get_status(UART_FLAG_TXE) == HAL_STATUS_OK)
		{
		    hal_uart_tx_data(queue->pdata[queue->head]);
		   	queue->head++;
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

中断接收

注册接收回调函数并使能接收中断

void uart_receive_interrupt_config(uint16_t num)
{
	uart_queue_t *queue = uqueue;

	queue->head = queue->tail =  0;
	queue->size = num;

	uart_register_callback(UART_RX_CALLBACK_ID, rx_callback);
	hal_uart_int_config(HAL_UART_IER_RDLIENABLE_MASK,TURN_ON);
}
1
2
3
4
5
6
7
8
9
10

数据接收在回调函数中完成

void uart_receive_interrupt(void)
{
	uart_queue_t *queue = uqueue;

	while(hal_uart_get_status(UART_FLAG_RXNE) == HAL_STATUS_OK && queue->tail < queue->size)
	{
		queue->pdata[queue->tail] = hal_uart_rx_data();
		queue->tail++;
	}
}

1
2
3
4
5
6
7
8
9
10
11

2)普通发送和接收

uart_tx_byte 和 uart_tx_array(多字节发送)

uart_rx_byte 和 uart_rx_array (多字节接收)

# 中断处理程序

根据不同的标志位调用不同的回调处理函数

static void uart_isr_handler(void *arg)
{
	SaveVectors(UART_IRQn);
	uint8_t stat = hal_uart_get_int_status();
	switch(stat)
	{
		case UART_LINE_STATUS:
			if(uart_callback[UART_RX_LINE_STATUS_CALLBACK_ID])
				uart_callback[UART_RX_LINE_STATUS_CALLBACK_ID]();
			hal_uart_int_config(HAL_UART_IER_RLSIENABLE_MASK,TURN_OFF);//disable receiver data status interrupt
			break;
		case UART_RX_FIFO_ISR:
			if(uart_callback[UART_RX_CALLBACK_ID])
				uart_callback[UART_RX_CALLBACK_ID]();
			break;
		case UART_TX_FIFO_ISR:
			if(uart_callback[UART_TX_CALLBACK_ID])
				uart_callback[UART_TX_CALLBACK_ID]();
			hal_uart_int_config(HAL_UART_IER_THREIENABLE_MASK,TURN_OFF);//diable tx fifo interrupt
			break;
		case UART_RX_TIMEOUT:
			if(uart_callback[UART_TIMEOUT_CALLBACK_ID])
				uart_callback[UART_TIMEOUT_CALLBACK_ID]();
			 break;
		case UART_MODEM_STATUS:
			if(uart_callback[UART_MODEM_CALLBACK_ID])
				uart_callback[UART_MODEM_CALLBACK_ID]();
			hal_uart_int_config(HAL_UART_IER_MSTENABLE_MASK,TURN_OFF);//disable modem status change interrupt
			 break;
		default:
			break;
	}
	RestoreVectors(UART_IRQn);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 使用示例

UART的使用请见app_uart_test.c

# CODEC(ADC/DAC)

# 功能综述

SNC8600的CODEC支持:

  • 双路立体声24-bit ADC, 动态范围高达106dB

  • 双路立体声24-bit DAC, 动态范围高达110dB

  • ADC/DAC支持的采样率: 8k, 16k, 32k, 44.1k, 48k, 88.2k, 96k, 176.4k, 192k

  • 10路数字麦克风

  • ADC支持Fast &slow模式 , 后者可以保留更高的精度

  • ADC/DAC支持Master或Slave模式

  • Low power voice detection

  • 支持风噪检测,AGC, DRC, 混音等处理

# ADC接口

# 参数配置

1) 配置参数数据结构

typedef struct {
	hal_codec_ai_mode_t master_slave;			/* Master or slave mode */
	uint32_t sample_rate;						/* ADC12 sample rate */
	
	hal_codec_channel_sel_t aiadc1_sel;			/* Mixer channel 1 output selection on ADC path, 0:ADC1, 1:ADC2, 2:(ADC1+ADC2)/2, 3:None */
	hal_codec_channel_sel_t aiadc2_sel;			/* Mixer channel 2 output selection on ADC path, 0:ADC2, 1:ADC1, 2:(ADC1+ADC2)/2, 3:None */
	hal_codec_adc_mixer_mode_t mix_rec;			/* ADC12 mixed with DAC or not */	
	hal_codec_adc12_power_mode_t adc12_mode;	/* ADC1 and ADC2 power mode, 0: Normal mode, 1: Low power mode */
	hal_codec_wnf_mode_t wnf;					/* ADC12 wind noise filter */
	
	bool	agc_enable;							/* ADC12 AGC Enable or not */
	bool	hpf_enable;							/* ADC12 HPF Enable or not */
	bool 	is_amic;							/* Indicates whether it's analog microphone or not; true:analog microphone; false:digital microphone. */

	/* ADC12 can be as either analog microphone or digital microphone */
	codec_amic12_config_t amic;					/* Analog microphone specified config */
	codec_dmic12_config_t dmic;					/* Digital microphone specified config */

	hal_codec_adc_agc_config_t *agc_config;		/* ADC12 AGC configuration information */
} codec_adc12_config_t;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

2) 配置接口codec_config_adc12(codec_adc12_config_t *config)()

//如果是adc配置为DMIC, 需配置复用PIN脚做时钟及数据输入

if (!config->is_amic)
{
	hal_gpio_set_pinmux_function(GPIO_8, GPIO_8_FUNC_DMIC_CLK11);
	hal_gpio_set_pinmux_function(GPIO_9, GPIO_9_FUNC_DMIC_IN1);
}
1
2
3
4
5

//配置parallel数据读入,采样率及Master/Slave

hal_codec_set_adc12_audioif_parallel();
hal_codec_set_adc12_master_slave_mode(config->master_slave);
hal_codec_set_adc12_sample_rate(codec_samplerate_transform(config->sample_rate));
1
2
3

//配置ADC两路数字输出通道选择以及是否与DAC混音

hal_codec_select_adc1_output(config->aiadc1_sel);
hal_codec_select_adc2_output(config->aiadc2_sel);
hal_codec_select_adc12_mixer_mode(config->mix_rec);
hal_codec_set_adc12_power_mode(config->adc12_mode);
1
2
3
4

//根据配置为AMIC或者DMIC再进行进一步的配置

if (config->is_amic) //Analog Microphone
{
	hal_codec_disable_pga_lf_drift();
	hal_codec_set_amic1_capcouple(config->amic.capcouple1);
	hal_codec_set_amic2_capcouple(config->amic.capcouple2);
	
	hal_codec_select_adc1_data_provided(HAL_CODEC_ADC_DATA_PROVIEDE_ANALOG_ADC);
	hal_codec_select_adc2_data_provided(HAL_CODEC_ADC_DATA_PROVIEDE_ANALOG_ADC);
	if (config->adc12_mode == HAL_CODEC_ADC12_NORMAL_MODE) {
		hal_codec_set_dmic12_clock(HAL_CODEC_DMIC_RATIO_12);
	} else {
		hal_codec_set_dmic12_clock(HAL_CODEC_DMIC_RATIO_4);
	}
	
	hal_codec_set_amic1_input_mode(config->amic.micdiff1);
	hal_codec_set_amic1_analog_gain(config->amic.gim1);
	hal_codec_set_adc1_digital_gain(config->amic.gid1);

	hal_codec_set_amic2_input_mode(config->amic.micdiff2);
	hal_codec_set_amic2_analog_gain(config->amic.gim2);
	hal_codec_set_adc2_digital_gain(config->amic.gid2);
	
	hal_codec_set_amic1_bias_state_in_power_down(config->amic.micbias1_v);
	hal_codec_set_amic2_bias_state_in_power_down(config->amic.micbias2_v);
} 
else //Digital Microphone
{
	hal_codec_set_dmic12_clock(config->dmic.clock);	
	hal_codec_set_adc1_digital_gain(config->dmic.gid1);
	hal_codec_set_adc2_digital_gain(config->dmic.gid2);
	hal_codec_select_adc1_data_provided(config->dmic.adc_dmic_sel1);
	hal_codec_select_adc2_data_provided(config->dmic.adc_dmic_sel2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 初始化

codec_adc12_init(void)

//配置中断,FIFO等,并使能ADC
codec_adc12_config_t *config = &g_adc12_config;

codec_register_cb(CODEC_CALLBACK_ADC, app_codec_adc_callback);
codec_config_adc12(config);
hal_codec_fifo_init(HAL_CODEC_FIFO_ADC12, CODEC_FIFO_AF_LEVEL);
#if !CODEC_USE_ONE_IRQ
hal_codec_unmask_interrupt(CODEC_ADC_INT_CHANNEL, CODEC_ADC_INT_SRC);	//Unmask a FIFO interrupt
#endif
codec_adc12_start();
1
2
3
4
5
6
7
8
9
10

# 中断处理程序

codec_adc_isr_handler(读取转换后的数据)

//如果ADC已完成一次转换,按照设定的位宽读取转换后的数值
if (hal_codec_get_interrupt_status(CODEC_ADC_INT_CHANNEL) & (1 << CODEC_ADC_INT_SRC))
{
    if(hal_codec_get_adc_data_mode() == CODEC_ADC_DATA_MODE_16BIT)
	{
#if CODEC_ADC12_ENABLE
		hal_codec_read_adc12_data_16bit(&adcData[0]);
#endif
    }else
	{
#if CODEC_ADC12_ENABLE
		hal_codec_read_adc12_data_24bit(&adcData[0], &adcData[1]);
#endif
    }
}
//读到的数据交由上层处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# ADC其他接口(SET/GET)

drv_status_t codec_enable_adc12_hpf(uint8_t enable);
drv_status_t codec_select_adc12_mixer_mode(uint8_t enable);
drv_status_t codec_set_adc1_aiadc(uint8_t sel);
drv_status_t codec_set_adc2_aiadc(uint8_t sel);
drv_status_t codec_set_adc12_wnf_mode(uint8_t mode);
drv_status_t codec_adc12_agc_enable(uint8_t enable);
drv_status_t codec_set_adc12_agc_stereo(uint8_t enable);
drv_status_t codec_set_adc12_agc_target(uint8_t target);
drv_status_t codec_adc12_agc_noise_gate_enable(uint8_t enable);
drv_status_t codec_set_adc12_agc_noise_gate_thres(uint8_t threshold);
drv_status_t codec_adc12_agc_snr_optimzation(uint8_t enable);
drv_status_t codec_set_adc12_agc_hold_time(uint8_t holdTime);
drv_status_t codec_set_adc12_agc_attack_time(uint8_t attackTime);
drv_status_t codec_set_adc12_agc_decay_time(uint8_t decayTime);
drv_status_t codec_set_adc12_agc_min_gain(uint8_t gain);
drv_status_t codec_set_adc12_agc_max_gain(uint8_t gain);
drv_status_t codec_adc12_enable(uint8_t enable, auChannel_t lr);
drv_status_t codec_set_adc12_gain(int8_t gain, uint8_t isAgain, auChannel_t lr);
void codec_adc12_aias(float s, int32_t dir);
void codec_tune_adc_sample_rate(uint32_t dataLen, uint32_t fifoSize);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# DAC接口

# 参数配置

接口为codec_config_dac(),配置参数数据结构如下

codec_dac_config_t g_dac_config = {
	.master_slave 			= CODEC_DEFAULT_MODE,
	.sample_rate 			= CONFIG_CODEC_FREQUENCY,
	.is_hp					= true,
	.drc_enable				= false,
	.left_only 				= false,

	.aidacl_sel 			= HAL_CODEC_CHANNEL_SEL_NORMAL_INPUTS,
	.aidacr_sel				= HAL_CODEC_CHANNEL_SEL_NORMAL_INPUTS,
	.dac_mix				= HAL_CODEC_PLAYBACK_DAC_ONLY,
	.mixdacl_sel			= HAL_CODEC_CHANNEL_SEL_NORMAL_INPUTS,
	.mixdacr_sel			= HAL_CODEC_CHANNEL_SEL_NORMAL_INPUTS,
	.mixadc1_sel			= HAL_CODEC_CHANNEL_SEL_NORMAL_INPUTS,
	.mixadc2_sel			= HAL_CODEC_CHANNEL_SEL_NORMAL_INPUTS,

	.godl					= 0,
	.godr					= 0,
	.gomixl					= 0,
	.gomixr					= 0,
	.gimixl					= 0,
	.gimixr					= 0,

	.lthres					= 0,
	.rthres					= 0,
	.lcomp					= HAL_CODEC_DRC_RATE_1,
	.rcomp					= HAL_CODEC_DRC_RATE_1,

	.hp_sel					= 0,
#ifdef SUPPORT_SPEAKER_EQ
	.gol					= 5,
	.gor					= 5,
#else
	.gol					= 0,
	.gor					= 0,
#endif
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 初始化 codec_dac_init()

//使能中断,FIFO等,并启动DAC
codec_set_operation_mode(CODEC_OP_MODE_PLAYBACK_OPEN);
codec_config_dac(&g_dac_config);
hal_codec_fifo_init(HAL_CODEC_FIFO_DAC, CODEC_FIFO_AE_LEVEL);
#if CODEC_USE_ONE_IRQ
hal_codec_unmask_interrupt(CODEC_INT_CHANNEL, CODEC_INT_SRC);	//Unmask a FIFO interrupt
#else
hal_codec_unmask_interrupt(CODEC_DAC_INT_CHANNEL, CODEC_DAC_INT_SRC);	//Unmask a FIFO interrupt
#endif
codec_dac_start();
codec_register_cb(CODEC_CALLBACK_DAC, app_codec_dac_callback);
1
2
3
4
5
6
7
8
9
10
11

# 中断处理

codec_dac_isr_handler (中断处理过程中发送需要做转换的数据)

SaveVectors(CODEC_DAC_IRQ);
hal_interrupt_clear_irq(CODEC_DAC_IRQ);
//如果中断状态指示可以开始数据转换,则发送需要转换的数据
if (hal_codec_get_interrupt_status(CODEC_DAC_INT_CHANNEL) & (1 << CODEC_DAC_INT_SRC))
{
	if (g_codec_cb_table[CODEC_CALLBACK_DAC])
		g_codec_cb_table[CODEC_CALLBACK_DAC](&dil, &dir);
    //要转换的数据存放于dil和dir中(左右声道)
	hal_codec_write_dac_data(dil, dir);

	hal_codec_clear_interrupt(CODEC_DAC_INT_CHANNEL, CODEC_DAC_INT_SRC);
}
RestoreVectors(CODEC_DAC_IRQ);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 其他DAC接口

drv_status_t codec_dac_drc_enbale(uint8_t enable);
drv_status_t codec_set_dac_drc_lthres(uint8_t threshold);
drv_status_t codec_set_dac_drc_rthres(uint8_t threshold);
drv_status_t codec_set_dac_drc_lcomprate(uint8_t comprate);
drv_status_t codec_set_dac_drc_rcomprate(uint8_t comprate);
drv_status_t codec_dac_enable(uint8_t enable);
drv_status_t codec_dac_mix_enable(uint8_t enable);
drv_status_t codec_set_dac_golmix_gain(uint8_t gain);
drv_status_t codec_set_dac_gormix_gain(uint8_t gain);
drv_status_t codec_set_dac_gilmix_gain(uint8_t gain);
drv_status_t codec_set_dac_girmix_gain(uint8_t gain);
drv_status_t codec_set_dac_mixdacl(uint8_t sel);
drv_status_t codec_set_dac_mixdacr(uint8_t sel);
drv_status_t codec_set_dac_aidacl(uint8_t sel);
drv_status_t codec_set_dac_aidacr(uint8_t sel);
drv_status_t codec_select_dac_mixadc1_input(uint8_t sel);
drv_status_t codec_select_dac_mixadc2_input(uint8_t sel);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# USB UAC

# 概述

SNC8600 USB driver 实现了UAC2.0及UAC1.0的以下特性:

  • USB device descriptor管理

  • Configuration device descriptor管理

  • Audio Control request : 支持SET_CUR 和GET_CUR

  • Audio feature unit

  • Audio 同步类型: Asynchronous and feedback

  • Audio Class-Specific AC Interfaces

  • Audio Class-Specific AS Interfaces

  • 自适应UAC2.0&UAC1.0

  • 支持从8k到192k的采样率

Audio的数据传输通过Isochronous Endpoint来进行, Audio控制请求都是通过Control Endpoint (Endpoint 0)来管理的。音频质量取决于数据传输的同步机制。 SNC8600 USB Driver的同步有两种方式, 通过SOF Packet同步,以及UAC1.0的Feedback机制。为了防止数据接收Overwrite, SNC8600使用了DMA传输以及多buffer存储。USB Driver也支持基本的音频控制请求.

对于UAC1.0,SNC8600支持除SET_MEM和GET_MEM外其他音频控制请求,UAC2.0则只支持REQ_CUR和REQ_RANGE。

# UAC的核心数据结构

# UAC的配置描述符

uac_CfgDesc结构 (参见usb_descriptor.h)

# UAC class 接口

该接口管理audio数据的传输和控制请求, 在初始化usb协议栈时,通过usb_dev_register_class()加载。

usb_dev_class_cb_t usbDevComposite[USB_FUN_MAX_NUM_INTERFACES] =
{
    {
        uac_init,
        uac_de_init,
        uac_setup,
        uac_ep0_tx_ready,
        uac_ep0_rx_ready,
        NULL,
        NULL,
        uac_sof,
        NULL,
        NULL,
        uac_get_cfg_desc,
        uac_get_cfg_desc,
        uac_get_cfg_desc,
        uac_get_device_qualifier_desc,
    },

    #if SUPPORT_USB_SPK
    {
        uac_init,
        uac_de_init,
        NULL,
        NULL,
        NULL,
        uac_feedback,
        uac_data_out,
        NULL,
        uac_iso_in_incomplete,
        uac_iso_out_incomplete,
        NULL,
        NULL,
        NULL,
        NULL,
    },
    #endif

    #if SUPPORT_USB_MIC
    {
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        uac_mic_in,
        NULL, /*EP0_TxSent*/
        NULL, /*SOF */
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
    },

    #endif
    #if SUPPORT_USB_HID
    {
        usb_dev_hid_init,
        usb_dev_hid_de_init,
        NULL,
        NULL, /*EP0_TxSent*/
        usb_dev_hid_ep0_rx_ready,
        usb_dev_hid_data_in,
        usb_dev_hid_data_out,
        NULL, /*SOF */
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
    }

    #endif
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# Audio device interface接口

usb_audio_interface_t uac_fops =
{
    Audio_Init,
    Audio_DeInit,
    Audio_PlaybackCmd,
    Audio_VolumeCtl,
    Audio_MuteCtl,
    Audio_PeriodicTC,
    Audio_GetState,
};
1
2
3
4
5
6
7
8
9
10

该接口控制底层audio device, 提供了基本的audio控制,包括mute,音量控制等。这个接口在USB初始化时通过uac_register_interface加载。

# 如何使用UAC

1)定义设备配置描述符, 参见usb_decriptor.h

2)在usb_init中,调用

/* Add Supported Class */
usb_dev_register_class(&gUsbDevice, usbDevComposite);
/* Add Interface callbacks for AUDIO Class */
uac_register_interface(&gUsbDevice, &uac_fops);
/* Start Device Process */
usb_dev_start(&gUsbDevice);
hal_interrupt_enable_irq(USB_IRQn);
1
2
3
4
5
6
7

3)音频数据的传输 (都在下面的函数中处理)

uac_data_out -- 从USB接收音频数据并存储到接收FIFO

uac_mic_in -- 待发送的音频数据从发送FIFO通过USB发送

4)音频的控制请求

通过USBD_AUDIO_Setup() and USBD_AUDIO_EP0_RxReady()来处理,这些函数最终会调用底层的uac_fops来实际完成控制。

# USB HID

# 功能概述

SNC8600支持的HID 遵循Class v1.11规范,支持双向的HID通信,即可在USB音频应用中用来实现音量控制及播放暂停,恢复等,也可用于定制HID消息的传送,所有通信都是以中断传输方式进行传输,发送的HID数据包最大为64字节,超过此大小的数据包需应用做分包组包处理。

# 核心数据结构

# HID描述符

uac_CfgDesc配置描述符里的hid部分配置了两个输入输出端点,HID最核心的配置是在report描述符里边,定义了HID要支持哪些功能,发送的数据如何解析等。

#if SUPPORT_USB_HID
	DESC_SIZE_INTF,                             /* Size of this descriptor in bytes */
	DESC_TYPE_INTF,	                             /* INTERFACE Descriptor Type */
	INTF_HID,                            /* Number of this interface. */
	0x00U,							                             /* Value used to select this alternate setting for the interface identified in the prior field */
	USB_HID_KEYBOARD_ENDPOINT_COUNT,                             /* Number of endpoints used by this interface (excluding endpoint zero). */
	HID_CLASS, 		                             /* Class code (assigned by the USB-IF). */
	0x00U,		                             					 /* Subclass code (assigned by the USB-IF). */
	0X00U,		                             					 /* Protocol code (assigned by the USB). */
	0x00U,							                             /* Index of string descriptor describing this interface */

	DESC_SIZE_HID,		                             /* Numeric expression that is the total size of the HID descriptor. */
	DESC_TYPE_HID,		                             /* Constant name specifying type of HID descriptor. */
	0x11U, 0x01U,					                             /* Numeric expression identifying the HID Class Specification release. */
	0x00U,							                             /* Numeric expression identifying country code of the localized hardware */
	0x01U,							                             /* Numeric expression specifying the number of class descriptors(at least one report descriptor) */
	DESC_TYPE_HID_REPORT,                              /* Constant name identifying type of class descriptor. */
	USB_SHORT_GET_LOW(DESC_SIZE_HID_KEYBOARD_REPORT),/* Numeric expression that is the total size of the Report descriptor. */
	USB_SHORT_GET_HIGH(DESC_SIZE_HID_KEYBOARD_REPORT),

	/*OUT*/
	0x07, 							 		 					  /* Size of this descriptor in bytes */
	DESC_TYPE_EP,								  /* endpoint Descriptor Type */
	AUDIO_HID_OUT_EP,					                          /* The address of the endpoint on the USB device described by this descriptor. */
	EP_TYPE_INTR, 									  /* This field describes the endpoint's attributes */
	0x40,														  /* Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. */
	0x00,
	0x20,												           /* Interval for polling endpoint for data transfers. */
	/*HID KEY IN*/
	DESC_SIZE_EP,                               /* Size of this descriptor in bytes */
	DESC_TYPE_EP,	                              /* endpoint Descriptor Type */
	AUDIO_HID_IN_EP,
	/* The address of the endpoint on the USB device described by this descriptor. */
	EP_TYPE_INTR,                                       /* This field describes the endpoint's attributes */
	0x40,                                                         /* Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. */
	0x00,
	0x04,                                                        /* Interval for polling endpoint for data transfers. */
#endif
uint8_t g_UsbDeviceHidKeyboardReportDescriptor[HID_KEYBOARD_DESC_SIZE] =
{
	0x05, 0x0C, // Usage Page (Consumer)
	0x09, 0x01, // Usage(Consumer Control)
	0xA1, 0x01, // Collection(Application )
	/***************************************************/
	0x85, 0x01,
	0x09, 0x04,
	0x15, 0x00,
	0x26, 0xFF, 0x00,
	0x75, 0x08,
	0x95, 0x3F,
	0x91, 0x82,
	/***************************************************/
	0x85, 0x01,
	0x15, 0x00, // Logical Minimum(0x0 )
	0x26, 0xFF, 0x00,
	//0x25, 0x01, // Logical Maximum(0x1 )
	0x75, 0x01, // Report Size(0x1 )
	0x95, 0x01, // Report Count(0x5 )

	0x09, 0xE9, // Usage(Volume Increment)
	0x81, 0x02,

	0x09, 0xEA, // Usage(Volume Decrement)
	0x81, 0x02, // Input(Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bit Field)

	0x09, 0xCD, // Usage(Play/Pause)
	0x81, 0x02,

	0x09, 0xB5, // Usage(Scan Next Track)
	0x81, 0x02,

	0x09, 0xB6, // Usage(Scan Previous Track)
	0x81, 0x02,

	0x09, 0xB7, // Usage(Stop)
	0x81, 0x02,

	0x09, 0xB3, // Usage(Fast Forward)
	0x81, 0x02,

	0x09, 0xB4, // Usage(Rewind)
	0x81, 0x02, // Input(Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bit Field)

	0x05, 0x0B,
	0x09, 0x24,
	0x81, 0x02,

	0x09, 0x20,
	0x81, 0x02, // Input(Data, Variable, Absolute, No Wrap, Linear, Preferred State, No Null Position, Bit Field)

	0x09, 0x2F,
	0x81, 0x06,
	0x95, 0x05, // Report Count(0x5 )
	0x81, 0x01,

	0x85, 0x02,
	0x09, 0x03,
	0x15, 0x00,
	0x26, 0xFF, 0x00,
	0x75, 0x08,
	0x95, 0x3F,
	0x81, 0x02,

	0xC0
};	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

# HID class接口

该接口包含HID class的初始化,以及消息的传输接口,在初始化usb协议栈时,和UAC class一起通过usb_dev_register_class()加载。

{
	usb_dev_hid_init,
	usb_dev_hid_de_init,
	NULL,
	NULL, /*EP0_TxSent*/
	usb_dev_hid_ep0_rx_ready,
	usb_dev_hid_data_in,
	usb_dev_hid_data_out,
	NULL, /*SOF */
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

HID数据的接收和发送在usb_dev_hid_data_out和usb_dev_hid_data_in中进行

# 如何使用HID

和UAC类似,请参阅USB UAC