# 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通道
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);//通过回调传回接收到的数据
}
//其他传输状态的处理
....
}
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);
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);
}
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接口读取数据
}
}
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--;
}
}
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--;
}
}
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);
}
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);
}
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
}
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++;
}
}
}
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);
}
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++;
}
}
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);
}
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;
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);
}
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));
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);
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);
}
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();
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
}
}
//读到的数据交由上层处理
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);
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
};
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);
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);
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);
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
};
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,
};
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);
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
};
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,
}
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