PCM1770测试程序

 

 

 

 

#include "PCM1770.h"
#include "PCM1770_WaveData.h"

uint16_t i2s_audio_freq    = I2S_AUDIOSAMPLE_8K;//I2S音频采样率
uint8_t audio_header_index = 0;
uint8_t *audio_file_header = 0;

/* audio codec variables */
wave_file_struct  wave_format;
__IO uint32_t audio_file_address;//播放文件首地址 
uint32_t audio_data_length;      //音频数据长度
uint32_t data_start_addr;        //音频数据起始地址
__IO uint32_t audio_data_index;  //音频数据索引

uint8_t audio_play_status;                 //音频播放状态
static uint32_t audio_replay_remain_count; //音频重播保持计数器
static __IO uint32_t audio_replay_count;   //音频重播计数器
static __IO uint32_t monovar;              //0表示左通道工作,非0表示右通道工作

//void My_I2S1_Init(uint16_t standard, uint16_t mclk_output, uint16_t audio_freq);
//void My_SPI0_Init(void);
void pcm1770_send_ctrl_data(uint8_t reg, uint8_t data);
uint8_t pcm1770_init(uint32_t address);

/*
BCK:I2S_CK是串行时钟信号;
DATA:I2S_SD是串行数据信号;
LRCK:I2S_WS是数据帧控制信号
SCKI:系统时钟,I2S_MCK是主时钟信号,它最大可提供一个256倍于Fs的时钟频率,其中Fs是音频采样率;

MC:SPI0_SCK时钟
MD:SPI0_MOSI数据
MS:SPI选择引脚

PD:复位引脚
AIN:单声模拟信号混频器输入;
HOUTL:左通道耳机放大器模拟信号输出;
HOUTR:右通道耳机放大器模拟信号输出;
*/
/*
I2S接口有4个引脚,分别是I2S_CK,I2S_WS,I2S_SD和I2S_MCK;
I2S_MCK是主时钟信号,它最大可提供一个256倍于Fs的时钟频率,其中Fs是音频采样率;
I2S_CK是串行时钟信号,与SPI_SCK共享引脚;
I2S_WS是数据帧控制信号,与SPI_NSS共享引脚;
I2S_SD是串行数据信号,与SPI_MOSI共享引脚;

I2S工作在主机模式下:
I2S_SD是串行数据信号,发送时变为输出,接收时变为输入;
I2S_CK是串行移位时钟信号,用作输出;
I2S_WS是选择左右通道信号,用作输出;
I2S_MCK是主时钟信号,只能输出;

I2S工作在从机模式下:
I2S_SD是串行数据信号,发送时变为输出,接收时变为输入;
I2S_CK是串行移位时钟信号,用作输入;
I2S_WS是选择左右通道信号,用作输入;
I2S_MCK是主时钟信号,只能输入;

对于所有标准和数据包类型来说,数据的最高有效位总是最先被发送的;
对于所有基于两通道分时复用的标准来说,总是先发送左通道,然后是右通道;

由于发送和接收的数据缓冲区都是16位宽度,且"通道长度必须大于或等于数据长度",所以有四种数据包类型:
1,16位数据打包成16位数据帧格式,访问1次SPI_DATA寄存器,只发bit15~bit0;
2,16位数据打包成32位数据帧格式,访问1次SPI_DATA寄存器,先发bit15~bit0,剩下的16位被硬件填充为16个0;
3,24位数据打包成32位数据帧格式,访问2次SPI_DATA寄存器,先发bit24~bit8,再发bit7~biT0,然后插入8个0;
4,32位数据打包成32位数据帧格式,访问2次SPI_DATA寄存器,先发bit31~bit16,再发bit15~biT0;
*/

//函数功能:初始化I2S1
void My_I2S1_Init(uint16_t standard, uint16_t mclk_output, uint16_t audio_freq)
{
/I2S1配置开始//
	rcu_periph_clock_enable(RCU_AF);   //使能RCU_AF外设时钟
	rcu_periph_clock_enable(RCU_SPI1); //使能RCU_SPI1外设时钟
	rcu_periph_clock_enable(RCU_GPIOB);//使能RCU_GPIOB外设时钟
	rcu_periph_clock_enable(RCU_GPIOC);//使能RCU_GPIOC外设时钟

	gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
	//I2S1引脚I2S_WS:配置GPIOB12的工作模式为复用功能IO推挽输出,输出速度最大为50MHz
	gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
	//I2S1引脚I2S_CK:配置GPIOB13的工作模式为复用功能IO推挽输出,输出速度最大为50MHz
  gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
	//I2S1引脚I2S_SD:配置GPIOB15的工作模式为复用功能IO推挽输出,输出速度最大为50MHz
	gpio_init(GPIOC, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
	//I2S1引脚I2S_MCK:配置GPIOC6的工作模式为复用功能IO推挽输出,输出速度最大为50MHz

	spi_i2s_deinit(SPI1);//复位SPI1外设;
	i2s_init(SPI1, I2S_MODE_MASTERTX, standard, I2S_CKPL_HIGH);//初始化I2S1
	//I2S_MODE_MASTERTX:I2S主机发送模式
	//standard=I2S_STD_PHILLIPS:I2S标准选择"飞利浦标准":16位数据,左对齐,空余16位为0
	//I2S_CKPL_HIGH:I2S空闲时,时钟极性位为高电平
	i2s_psc_config(SPI1, audio_freq, I2S_FRAMEFORMAT_DT16B_CH16B, mclk_output);
	//设置"I2S音频采样率",帧格式,I2S时钟输出
	spi_i2s_interrupt_disable(SPI1, SPI_I2S_INT_TBE);//不使能"SPI发送缓冲区空"产生中断
	i2s_enable(SPI1);//I2S1使能
/I2S1配置结束//

  nvic_irq_enable(SPI1_IRQn, 0, 1);
	//设置SPI1_IRQn的中断优先级,抢占优先级为0,子优先级为1
}

//函数功能:初始化SPI0
void My_SPI0_Init(void)
{
	spi_parameter_struct spi_init_struct;

/SPI0配置开始//
	rcu_periph_clock_enable(RCU_AF);    //使能RCU_AF外设时钟
	rcu_periph_clock_enable(RCU_SPI0);  //使能RCU_SPI0外设时钟
	rcu_periph_clock_enable(RCU_GPIOA); //使能RCU_GPIOA外设时钟

	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
	//SPI0_SCK:配置GPIOA5的工作模式为复用功能IO推挽输出,输出速度最大为50MHz
	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
	//SPI0_MOSI:配置GPIOA7的工作模式为复用功能IO推挽输出,输出速度最大为50MHz

	gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
	//配置GPIOA4的工作模式为通用型IO推挽输出和输出速度最大为50MHz
	PCM1770_CS_HIGH(); //不选中PCM1770

	spi_i2s_deinit(SPI0);//复位SPI0外设
	spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX; //SPI在全双工通讯中接收/发送数据
	spi_init_struct.device_mode          = SPI_MASTER;               //SPI为主机模式且SWNSS=1
	spi_init_struct.nss                  = SPI_NSS_SOFT;             //使用NSS软件模式:NSS电平取决于SWNSS位;
	spi_init_struct.frame_size           = SPI_FRAMESIZE_16BIT;      //SPI使用16位数据帧格式
	spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
	//在SPI为空闲状态时,CLK引脚拉高,且"在第2个时钟跳变沿时开始采集第1位数据"
	spi_init_struct.prescale             = SPI_PSC_32;                //SPI时钟预分频器值为32
	spi_init_struct.endian               = SPI_ENDIAN_MSB;            //先发送最高位
	spi_init(SPI0, &spi_init_struct);//使用spi_init_struct结构参数初始化SPI0

	spi_enable(SPI0);//使能SPI0
/SPI0配置结束//
}

//函数功能:将data写入"SPI0数据寄存器"
void SPI0_send_data(uint16_t data)
{
	while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE))
	{//等待建立"SPI发送缓冲区空"标志
	}
	spi_i2s_data_transmit(SPI0, data);//将data写入"SPI0数据寄存器"
}

//函数功能:将data的值写入"PCM1770的寄存器",寄存器地址为reg
//reg为PCM1770寄存器地址,data为待写入的控制数据
void pcm1770_send_ctrl_data(uint8_t reg, uint8_t data)
{
	uint16_t reg_data, i;

	reg_data = (0x00FF & reg);
	reg_data = reg_data << 8;
	reg_data = reg_data | data;

	PCM1770_CS_LOW();
	SPI0_send_data(reg_data);//将reg_data写入"SPI0数据寄存器"
	for(i=0; i<0xFFU; i++)
	{//MLH >= 20ns
	}
	PCM1770_CS_HIGH();
}

//函数功能:从audio_file_header[]中连续读取bytes_num个字节数据,按照指定的大小端方式保存到temp中
//读取的字节数量,
//小端模式是指将数据的低位放在内存的低地址上,而数据的高位放在内存的高地址上;
//大端模式是指将数据的低位放在内存的高地址上,而数据的高位放在内存的低地址上;
static uint32_t audio_read_unit(uint8_t bytes_num, endianness_enum bytes_endian)
{
	uint32_t i;
	uint32_t temp;
	uint32_t k;
	uint32_t t;

	i=0;
	temp=0;
	if(LITTLE_ENDIAN == bytes_endian)
	{//小端模式是指将数据的低位放在内存的低地址上,而数据的高位放在内存的高地址上;
		for(i = 0; i < bytes_num; i++)
		{
			t=audio_file_header[audio_header_index];
			t=(uint32_t)(t&0x000000FF);//保证最低8位有效
			k=i;k=k*8;t=t<<k;
			temp=(uint32_t)(temp|t);
			//i=0时,参与"相或"的是t的bit0~bit7,即为audio_file_header[0]
			//i=1时,参与"相或"的是t的bit8~bit15,即为audio_file_header[1]
			//i=2时,参与"相或"的是t的bit16~bit23,即为audio_file_header[2]
			//i=3时,参与"相或"的是t的bit24~bit31,即为audio_file_header[3]
			audio_header_index++;//修改audio_file_header[]下标值
		}
	}
	else
	{//大端模式是指将数据的低位放在内存的高地址上,而数据的高位放在内存的低地址上;
		for(i = bytes_num; i != 0; i--)
		{
			t=audio_file_header[audio_header_index];
			t=(uint32_t)(t&0x000000FF);//保证最低8位有效
			k=i-1;k=k*8;t=t<<k;
			temp=(uint32_t)(temp|t);
			//i=4时,参与"相或"的是t的bit24~bit31,即为audio_file_header[0]
			//i=3时,参与"相或"的是t的bit16~bit23,即为audio_file_header[1]
			//i=1时,参与"相或"的是t的bit8~bit15,即为audio_file_header[2]
			//i=0时,参与"相或"的是t的bit0~bit7,即为audio_file_header[3]
			audio_header_index++;//修改audio_file_header[]下标值
		}
	}

	return temp;
}

//audio_file_header_address为播放文件指针
errorcode_enum audio_format_parsing(uint8_t* audio_file_header_address)
{
	uint32_t ret_Data;
	uint32_t extra_format_bytes = 0;

	audio_file_header = audio_file_header_address;//设置"音频文件头指针"
	audio_header_index = 0;//设置音频头索引为0,the audio_header_index

///使用大端方式读取"文件头数据"开始
	ret_Data=audio_read_unit(4, BIG_ENDIAN);
	//从audio_file_header[]中连续读取4个字节数据,按照指定的大端方式保存到ret_Data中
	if(ret_Data!=AUDIO_CHUNKID)
	{//使用0x52494646表示字符串"RIFF"
		return(UNVALID_RIFF_ID);
	}
///使用大端方式读取"文件头数据"结束

	ret_Data=audio_read_unit(4, LITTLE_ENDIAN);
	//audio_header_index初始值为4
	//从audio_file_header[]中连续读取4个字节数据,按照指定的小端方式保存到ret_Data中
  //read the audio file length
	wave_format.riff_chunk_size = ret_Data;//保存音频文件长度

	ret_Data=audio_read_unit(4, BIG_ENDIAN);
	//audio_header_index初始值为8
	//从audio_file_header[]中连续读取4个字节数据,按照指定的大端方式保存到ret_Data中
  //read the audio file format, and it must be 'WAVE'
	if( ret_Data != AUDIO_FILEFORMAT )
	{//使用0x57415645表示字符串'WAVE'
		return(UNVALID_WAVE_FORMAT);
	}

	ret_Data=audio_read_unit(4, BIG_ENDIAN);
	//audio_header_index初始值为12
	//从audio_file_header[]中连续读取4个字节数据,按照指定的大端方式保存到ret_Data中
  //read the format chunk, and it must be 'fmt'
	if( ret_Data != AUDIO_FORMATID )
	{//使用0x666D7420表示字符串"fmt "
		return(UNVALID_FORMATCHUNK_ID);
	}

	ret_Data=audio_read_unit(4, LITTLE_ENDIAN);
	//audio_header_index初始值为16
	//从audio_file_header[]中连续读取4个字节数据,按照指定的小端方式保存到ret_Data中 
  //读取FMT数据长度为0x10	
  //read the length of the 'fmt' data, and it must be 0x00000010
	if( ret_Data != AUDIO_FORMATLEN )
	{//使用0x00000010表示"fmt数据的长度"
		extra_format_bytes = 1;
	}

  ret_Data=audio_read_unit(2, LITTLE_ENDIAN);
	//audio_header_index初始值为20
	//从audio_file_header[]中连续读取2个字节数据,按照指定的小端方式保存到ret_Data中 	
  //read the audio format, and it must be 0x01 (PCM)
	wave_format.format_tag = ret_Data;
	if( ret_Data != AUDIO_WAVE_FORMAT_PCM)
	{//PCM音频数据格式为0x0001
		return(UNSUPPORETD_FORMATTAG);
	}

	ret_Data=audio_read_unit(2, LITTLE_ENDIAN);
	//audio_header_index初始值为22
	//从audio_file_header[]中连续读取2个字节数据,按照指定的小端方式保存到ret_Data中 	
  //read the number of channels: 0x02->Stereo(立体声) and 0x01->Mono(单声道)
	wave_format.num_channels = ret_Data;//立体声为0x0002
	//保存通道数量
	//wave_format.num_channels=0x02立体声
	//wave_format.num_channels=0x01表示单声道

	ret_Data=audio_read_unit(4, LITTLE_ENDIAN);
	//audio_header_index初始值为24
	//从audio_file_header[]中连续读取4个字节数据,按照指定的小端方式保存到ret_Data中
  //read the sample rate
	wave_format.sample_rate = ret_Data;//音频采样速率:0x001F40=8000

	ret_Data=audio_read_unit(4, LITTLE_ENDIAN);
	//audio_header_index初始值为28
	//从audio_file_header[]中连续读取4个字节数据,按照指定的小端方式保存到ret_Data中
  //read the byte rate
	wave_format.byte_rate = ret_Data;//字节的速度:0x00007D00

	ret_Data=audio_read_unit(2, LITTLE_ENDIAN);
	//audio_header_index初始值为32
	//从audio_file_header[]中连续读取2个字节数据,按照指定的小端方式保存到ret_Data中
  //read the block alignment
	wave_format.block_align = ret_Data;//块对齐:0x0004,(是不是16位,右对齐数据)

  ret_Data=audio_read_unit(2, LITTLE_ENDIAN);
	//audio_header_index初始值为34
	//从audio_file_header[]中连续读取2个字节数据,按照指定的小端方式保存到ret_Data中
  //read the number of bits per sample
	wave_format.bits_per_sample = ret_Data;
	if( ret_Data !=AUDIO_BIT_PER_SAMPLE_16 )
	{//如果ret_Data不等于0x0010,这里正确
		return(UNSUPPORETD_BITS_PER_SAMPLE);
	}

//if there are extra format bytes, these bytes will be defined in "Fact Chunk"
  if(1 == extra_format_bytes)//由于extra_format_bytes=0,所以该判断语句不执行
	{
    ret_Data=audio_read_unit(2, LITTLE_ENDIAN);
	  //audio_header_index初始值为36
	  //从audio_file_header[]中连续读取2个字节数据,按照指定的小端方式保存到ret_Data中
    //read the extra format bytes, and it must be 0x00
		if( ret_Data != 0x0000 )
		{
			return(UNSUPPORETD_EXTRAFORMATBYTES);
		}

    ret_Data=audio_read_unit(4, BIG_ENDIAN);
	  //audio_header_index初始值为38
	  //从audio_file_header[]中连续读取4个字节数据,按照指定的大端方式保存到ret_Data中
    //read the fact chunk, and it must be 'fact'
		if( ret_Data!= AUDIO_FACTID )
		{//使用0x66616374表示字符串"fact"
			return(UNVALID_FACTCHUNK_ID);
		}

    ret_Data=audio_read_unit(4, LITTLE_ENDIAN);
	  //audio_header_index初始值为42
	  //从audio_file_header[]中连续读取4个字节数据,按照指定的小端方式保存到ret_Data中
    //read fact chunk data size and set the index to start reading after the header end
		audio_header_index += ret_Data;
	}

  ret_Data=audio_read_unit(4, BIG_ENDIAN);
	//audio_header_index初始值为36
	//从audio_file_header[]中连续读取4个字节数据,按照指定的大端方式保存到ret_Data中	
  //read the data chunk, and it must be 'data'
	if( ret_Data != AUDIO_DATAID )//ret_Data=0x01000200
	{//使用0x64617461表示字符串'data'
		return(UNVALID_DATACHUNK_ID);
	}

	ret_Data=audio_read_unit(4, LITTLE_ENDIAN);
	//audio_header_index初始值为40
	//从audio_file_header[]中连续读取4个字节数据,按照指定的小端方式保存到ret_Data中
  //read the size of sample data
	wave_format.data_size = ret_Data;//音频数据长度为0x0000DA40=55872

  //set first address of the effective audio data
	//audio_header_index初始值为44
	data_start_addr += audio_header_index;//记录"音频数据起始地址"

	return(VALID_WAVE_FILE);
}

//函数功能:音频文件初始化,并设置音频文件长度和音频采样率
//audio file init and set the audio file length and sample_rate
uint8_t audio_init(void)
{
	errorcode_enum ret_Data;

	ret_Data=audio_format_parsing( (uint8_t*)(audio_file_address) );
	if( ret_Data==VALID_WAVE_FILE )
	{
		i2s_audio_freq = (uint16_t)wave_format.sample_rate;
		//采样频率:0x001F40=8000
		//I2S_AUDIOSAMPLE_8K=8000
		return AUDIO_OK;
	}
	return AUDIO_FAIL; 
}

//函数功能:设置"音频重播计数器"和"音频重播保持计数器"
void pcm1770_replay_config(uint32_t replay_count)
{
	audio_replay_count = replay_count;        //设置"音频重播计数器"
	audio_replay_remain_count = replay_count; //设置"音频重播保持计数器"
}
//函数功能:PCM1770初始化
uint8_t pcm1770_init(uint32_t address)
{
	uint8_t ret_Data;

	audio_play_status = STOPPED;     //音频播放状态为"停止"
	audio_replay_count=0;            //音频重播计数器
	audio_replay_remain_count=0xFFFF;//音频重播保持计数器

	audio_file_address=address;//添加播放文件首地址,如AUDIO_FILE_ADDRESS
	audio_data_length = countof(PCM1770_WaveData);//音频数据长度
	data_start_addr=0; //音频数据起始地址为0
	audio_data_index=0;//音频数据索引为0
	monovar=0;//0表示左通道工作,非0表示右通道工作

	ret_Data=audio_init();
	//音频文件初始化,并设置音频文件长度和音频采样率(I2S_AUDIOSAMPLE_8K=8000)
	if(ret_Data==AUDIO_OK)
	{
		My_SPI0_Init();//初始化SPI0
		My_I2S1_Init(I2S_STANDARD, I2S_MCLKOUTPUT, i2s_audio_freq);
		//初始化I2S1
		//I2S标准选择"MSB对齐标准"
		//I2S_MCK时钟输出使能

		pcm1770_send_ctrl_data(1,0x3F);//左耳机输出没有衰减
		pcm1770_send_ctrl_data(2,0x3F);//右耳机输出没有衰减
		pcm1770_send_ctrl_data(3,0x01);
		//右耳机同相输出,OVER=0 128fS 过取样;16~24位,I2S格式
		//pcm1770_send_ctrl_data(0x03, 0x84);//16位,右对齐数据,OVER=1 192fS,256fS,384fS 过取样
		pcm1770_send_ctrl_data(4,0x00);
		//ZCAT=0正常衰减;PWRD=0,正常运行;

		pcm1770_replay_config(1); //设置播放一次
		audio_play_status=PLAYING;//设置为"音频播放中"
		spi_i2s_interrupt_enable(SPI1, SPI_I2S_INT_TBE);
		//使能"I2S1发送缓冲区空"中断"
	}
	return ret_Data;
}


//函数功能:设置audio_play_status,记录音频播放状态
//play_status=PLAYING,播放中
//play_status=STOPPED,停止
//play_status=PAUSED,暂停
//play_status=PLAYING_MUTE,使用弱音器播放
//play_status=STOPPED_MUTE,停止弱音器播放
//play_status=PAUSED_MUTE,暂停弱音器
uint8_t audio_set_play_status(uint8_t play_status)
{ 
    audio_play_status = play_status;
   
    return audio_play_status;
}

//函数功能:读取audio_play_status,返回"音频播放状态"
//play_status=PLAYING,播放中
//play_status=STOPPED,停止
//play_status=PAUSED,暂停
//play_status=PLAYING_MUTE,使用弱音器播放
//play_status=STOPPED_MUTE,停止弱音器播放
//play_status=PAUSED_MUTE,暂停弱音器
uint8_t audio_get_play_status(void)
{    
    return audio_play_status;
}

//函数功能:暂停音频播放
uint32_t pcm1770_audio_file_paused()
{   
  //set playing status as PAUSED or PAUSED_MUTE
	if(1 == (audio_play_status >> 2))
	{
	  //audio_play_status=PLAYING_MUTE,使用弱音器播放
    //audio_play_status=STOPPED_MUTE,停止弱音器播放
    //audio_play_status=PAUSED_MUTE,暂停弱音器
		audio_play_status=PAUSED_MUTE;//暂停弱音器
	}
	else
	{
		audio_play_status=PAUSED;//暂停
	}

	spi_i2s_interrupt_disable(SPI1, SPI_I2S_INT_TBE);

	return audio_data_index;
}


/I2S1中断服务程序及其子程序//
//函数功能:修改"音频数据索引"
void audio_data_index_inc(uint32_t inc_num)
{
	audio_data_index += (uint32_t)inc_num;
	//audio_data_index为音频数据索引
	//立体声:audio_data_index=audio_data_index+2
	//单声道:audio_data_index=audio_data_index+1

	if(audio_data_index >= audio_data_length)
	{//播放完毕
	  if(audio_replay_count != 0)
	  {
		  audio_replay_remain_count--;
		  if(audio_replay_remain_count==0)
		  {//播放任务结束
			  if(1 == (audio_play_status >> 2))
			  {//audio_play_status=PLAYING_MUTE,使用弱音器播放
         //audio_play_status=STOPPED_MUTE,停止弱音器播放
         //audio_play_status=PAUSED_MUTE,暂停弱音器
				  audio_play_status=STOPPED_MUTE;//停止弱音器播放
			  }
			  else
			  {
				  audio_play_status=STOPPED;//停止
			  }

			  spi_i2s_interrupt_disable(SPI1, SPI_I2S_INT_TBE);
			  //不使能"I2S1发送缓冲区空"中断
		  }
	  }
	}
}

//函数功能:从音频文件中读取半字 
uint16_t audio_read_half_word(uint32_t address_offset)
{
	uint32_t value = 0U;

	if((audio_file_address + address_offset ) >= AUDIOFILEADDRESS_END)
	{//"播放文件首地址" + "偏移地址" >= "音频文件结束地址"
		audio_data_index = 0;
	}

    //the left channel is to be sent
	if(0U == monovar )//左通道工作
	{
		value =  (*(__IO uint16_t *) (audio_file_address + address_offset));
		//从音频文件中读取数据  
		if(AUDIO_CHANNEL_MONO == wave_format.num_channels)//立体声
		{
			monovar++;
		}
		return value;
	}
	else//右通道工作
	{//right channel to be sent in mono format
	 //reset the monovar variable
		monovar = 0U;
		//return the previous read data in mono format
		return value;
	}
}

//函数功能:发送音频数据,send audio data
void I2S1_Send_Data_To_PCM1770(void)
{
	uint16_t tmpData;

	tmpData=audio_read_half_word(audio_data_index + data_start_addr);
	//data_start_addr为音频数据起始地址
	//audio_data_index为音频数据索引
	//从音频文件中读取半字
	spi_i2s_data_transmit(SPI1, tmpData);
	//将tmpData通过I2S1发送PCM1770

	audio_data_index_inc(wave_format.num_channels);
	//修改"音频数据索引"
	//wave_format.num_channels=0x02立体声
	//wave_format.num_channels=0x01表示单声道
}

//函数功能:I2S1中断服务程序
void SPI1_IRQHandler(void)
{
	if(SET == spi_i2s_interrupt_flag_get(SPI1, SPI_I2S_INT_TBE))
	{//读取"I2S1发送缓冲区空"中断标志"
		I2S1_Send_Data_To_PCM1770();//发送音频数据,send audio data
	}
}

/*
地址 B15 B14  B13  B12  B11  B10  B9   B8   B7   B6   B5   B4   B3   B2   B1   B0
 1   0   IDX6 IDX5 IDX4 IDX3 IDX2 IDX1 IDX0 MUTR MUTL ATL5 ATL4 ATL3 ATL2 ATL1 ATL0
寄存器地址保存在IDX[6:0]中,为0x01
MUTR=0(默认),右衰减器立即增加到先前设定的衰减;MUTR=1,使能右耳机声音减弱,减弱值为ATR[5:0];
MUTL=0(默认),左衰减器立即增加到先前设定的衰减;MUTL=1,使能左耳机声音减弱,减弱值为ATL[5:0];
ATL[5:0]   左耳机输出的数字衰减水平设置
11 1111b   0 dB, 左耳机输出没有衰减(默认)
11 1110b   –1 dB
11 1101b   –2 dB
: :
00 0010b   –61 dB
00 0001b   –62 dB
00 0000b   静音

地址 B15 B14  B13  B12  B11  B10  B9   B8   B7   B6   B5   B4   B3   B2   B1   B0
2    0   IDX6 IDX5 IDX4 IDX3 IDX2 IDX1 IDX0 RSV  RSV  ATR5 ATR4 ATR3 ATR2 ATR1 ATR0
寄存器地址保存在IDX[6:0]中,为0x02
RSV:保留
ATR[5:0]   右耳机输出的数字衰减水平设置
11 1111b   0 dB, 右耳机输出没有衰减(默认)
11 1110b   –1 dB
11 1101b   –2 dB
: :
00 0010b   –61 dB
00 0001b   –62 dB
00 0000b   静音

地址 B15 B14  B13  B12  B11  B10  B9   B8   B7   B6   B5   B4   B3   B2   B1   B0
3    0   IDX6 IDX5 IDX4 IDX3 IDX2 IDX1 IDX0 OVER RSV  RINV AMIX DEM  FMT2 FMT1 FMT0
寄存器地址保存在IDX[6:0]中,为0x03
OVER=0 128fS 过取样;OVER=1 192fS,256fS,384fS 过取样
RINV=0,右耳机同相输出;RINV=1,右耳机反相输出;
AMIX=0,不允许"AIN引脚输入的信号"混合到DAC输出;AMIX=1,允许"AIN引脚输入的信号"混合到DAC输出;
DEM=0,不使能44.1khz滤波器;DEM=1,使能44.1khz滤波器;
FMT[2:0] 音频数据格式选择
000      16~24位,左对齐格式(默认),低电平收到的是左通道数据,高电平选择右通道
001      16~24位,I2S格式,也是左对齐,高电平收到的是左通道数据,低电平选择右通道
010      24位,右对齐数据
011      20位,右对齐数据
100      16位,右对齐数据
101      16~24位,左对齐格式,主模式
110      保留
111      保留

地址 B15 B14  B13  B12  B11  B10  B9   B8   B7   B6   B5   B4   B3   B2   B1   B0
4    0   IDX6 IDX5 IDX4 IDX3 IDX2 IDX1 IDX0 RSV  RSV  RSV  ZCAT RSV  RSV  RSV  PWR
寄存器地址保存在IDX[6:0]中,为0x04
ZCAT=0(默认),正常衰减;ZCAT=1,零交叉衰减;
PWRD=0,正常运行;PWRD=1,省电状态;
*/

 

 

#ifndef __PCM1770_H
#define __PCM1770_H

#include "sys.h"
//#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool

#define PCM1770_CS_LOW()  gpio_bit_reset(GPIOA, GPIO_PIN_4) //选中PCM1770
#define PCM1770_CS_HIGH() gpio_bit_set(GPIOA, GPIO_PIN_4)   //不选中PCM1770

extern const char PCM1770_WaveData[];
#define AUDIOFILEADDRESS      (uint32_t)PCM1770_WaveData
#define AUDIOFILEADDRESS_END  (uint32_t)(PCM1770_WaveData + (countof( PCM1770_WaveData)))
#define countof(a)  (sizeof(a) / sizeof(a[0])) //计算数组a[]的内成员个数

/* correspond to the letters 'RIFF' */
#define  AUDIO_CHUNKID                      0x52494646 //使用0x52494646表示字符串"RIFF" 
/* correspond to the letters 'WAVE' */
#define  AUDIO_FILEFORMAT                   0x57415645 //使用0x57415645表示字符串"WAVE"  
/* correspond to the letters 'fmt' */
#define  AUDIO_FORMATID                     0x666D7420 //使用0x666D7420表示字符串"fmt " 
/* correspond to the letters 'data' */
#define  AUDIO_DATAID                       0x64617461 //使用0x64617461表示字符串'data'
/* correspond to the letters 'fact' */
#define  AUDIO_FACTID                       0x66616374 //使用0x66616374表示字符串"fact" 
/* correspond to the letters 'length of the fmt data' */
#define  AUDIO_FORMATLEN                    0x10  //使用0x10表示"fmt数据的长度" 

#define  AUDIO_WAVE_FORMAT_PCM              0x01  //PCM音频数据格式为0x0001
#define  AUDIO_FORMATCHUNKSIZE              0x10
#define  AUDIO_CHANNEL_MONO                 0x01 //单声道
#define  AUDIO_CHANNEL_STEREO               0x02 //立体声

/* play staus structure */
#define PLAYING       0x00  //播放中
#define STOPPED       0x01  //停止
#define PAUSED        0x02  //暂停
#define PLAYING_MUTE  0x04  //使用弱音器播放
#define STOPPED_MUTE  0x05  //停止弱音器播放
#define PAUSED_MUTE   0x06  //暂停弱音器

/* bits per sample */
#define  AUDIO_BIT_PER_SAMPLE_8    8
#define  AUDIO_BIT_PER_SAMPLE_16   16  //音频为16位

#define  AUDIO_OK                  1
#define  AUDIO_FAIL                0



//音量控制常数,volume control constants
#define MAX_VOL      0x3F
#define DEFAULT_VOL  MAX_VOL

//弱音器命令,MUTE commands 
#define MUTE_ON    0xFF
#define MUTE_OFF   0x3F

/* I2S configuration parameters */
#define I2S_STANDARD    I2S_STD_MSB       //I2S标准选择"MSB对齐标准"
#define I2S_MCLKOUTPUT  I2S_MCKOUT_ENABLE //I2S_MCK时钟输出使能 

typedef enum
{
	VALID_WAVE_FILE = 0,                    /* valid wave file */
	UNVALID_RIFF_ID,                        /* unvalid riff id */
	UNVALID_WAVE_FORMAT,                    /* unvalid wave format */
	UNVALID_FORMATCHUNK_ID,                 /* unvalid format chunk id */
	UNSUPPORETD_FORMATTAG,                  /* unsupporetd format tag */
	UNSUPPORETD_NUMBER_OF_CHANNEL,          /* unsupporetd number of channel */
	UNSUPPORETD_SAMPLE_RATE,                /* unsupporetd sample rate */
	UNSUPPORETD_BITS_PER_SAMPLE,            /* unsupporetd bits per sample */
	UNVALID_DATACHUNK_ID,                   /* unvalid data chunk id */
	UNSUPPORETD_EXTRAFORMATBYTES,           /* unsupporetd extra format bytes */
	UNVALID_FACTCHUNK_ID                    /* unvalid fact chunk id */
}errorcode_enum;

/* endianness enum */
//小端模式是指将数据的低位放在内存的低地址上,而数据的高位放在内存的高地址上;
//大端模式是指将数据的低位放在内存的高地址上,而数据的高位放在内存的低地址上;
typedef enum
{
	LITTLE_ENDIAN,
	BIG_ENDIAN
}endianness_enum;

typedef struct
{
	uint32_t  riff_chunk_size;              /* riff chunk size */
	uint16_t  format_tag;                   /* format tag */
	uint16_t  num_channels;                 /* number of channel */
	uint32_t  sample_rate;                  /* audio sample rate */
	uint32_t  byte_rate;                    /* byte rate */
	uint16_t  block_align;                  /* block align */
	uint16_t  bits_per_sample;              /* bits per sample */
	uint32_t  data_size;                    //音频数据长度audio data size
}wave_file_struct;

extern uint8_t pcm1770_init(uint32_t address);
#endif
#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool
#include "delay.h"
//#include "IWDG.h"
//#include "WWDGT.h"
#include "stdio.h"  //使能printf(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()

#include "LED.h"
#include "PCM1770.h"

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";

int main(void)
{

	//NVIC_PRIGROUP_PRE4_SUB0:抢占优先级为4bit(取值为0~15),子优先级为0bit(没有响应优先级)
	//NVIC_PRIGROUP_PRE3_SUB1:抢占优先级为3bit(取值为0~7),子优先级为1bit(取值为0~1)
	//NVIC_PRIGROUP_PRE2_SUB2:抢占优先级为2bit(取值为0~3),子优先级为2bit(取值为0~3)
	//NVIC_PRIGROUP_PRE1_SUB3:抢占优先级为1bit(取值为0~1),子优先级为3bit(取值为0~7)
	//NVIC_PRIGROUP_PRE0_SUB4:抢占优先级为0bit(没有抢占优先级),子优先级为3bit(取值为0~15)
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);//设置系统中断优先级"抢占优先级为4bit,子优先级为0bit"
  INTX_ENABLE();//开启所有中断

	LED2_Init();//初始化LED2端口
	LED3_Init();//初始化LED3端口
	LED4_Init();//初始化LED4端口
	LED5_Init();//初始化LED5端口

	delay_init();
	pcm1770_init(AUDIOFILEADDRESS);
	while(1)
	{
	}
}

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的MATLAB程序,实现了PCM编解码的基本功能。您可以根据需要进行修改和完善。 1. PCM编码 ```matlab function [code] = pcm_encode(signal, bits) % PCM编码 % signal: 输入信号 % bits: 编码位数 % code: 编码后的数字信号 L = 2^bits; % 量化电平数 delta = max(signal) / L; % 量化间隔 levels = (0:L-1) * delta; % 量化电平值 code = zeros(size(signal)); % 初始化编码结果 for i = 1:length(signal) [~, index] = min(abs(signal(i) - levels)); % 找到最近的量化电平 code(i) = index - 1; % 保存编码值(从0开始) end end ``` 2. PCM解码 ```matlab function [signal] = pcm_decode(code, bits) % PCM解码 % code: 输入数字信号 % bits: 编码位数 % signal: 解码后的模拟信号 L = 2^bits; % 量化电平数 delta = max(signal) / L; % 量化间隔 levels = (0:L-1) * delta; % 量化电平值 signal = levels(code + 1); % 解码并重建信号 end ``` 3. PCM编解码仿真 ```matlab % 生成测试信号 fs = 8000; % 采样频率 t = 0:1/fs:1; % 时间向量 f1 = 500; % 信号频率 f2 = 2000; signal = 0.5 * sin(2*pi*f1*t) + 0.5 * sin(2*pi*f2*t); % PCM编码 bits = 8; % 编码位数 code = pcm_encode(signal, bits); % 添加噪声 noise = randn(size(code)); % 高斯白噪声 snr = 20; % 信噪比 noise = noise / norm(noise) * norm(code) / 10^(snr/20); % 根据信噪比计算噪声功率 noisy_code = code + noise; % PCM解码 recovered_signal = pcm_decode(noisy_code, bits); % 显示结果 subplot(3, 1, 1) plot(t, signal) title('原始信号') subplot(3, 1, 2) plot(code) title('编码后的信号') subplot(3, 1, 3) plot(t, recovered_signal) title('解码后的信号') ``` 在以上代码中,我们首先生成一个测试信号,然后对其进行PCM编码,并添加了高斯白噪声。最后对带噪声的编码信号进行解码,得到还原后的模拟信号,并将结果显示在图形界面中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值