设备配置笔记01

具体配置详情在上个笔记,此为上次笔记的代码化
(还有一部分未完成)

//wm8978.c
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "sys.h"
#include "i2c.h"
#include "wm8978.h"

//WM8978寄存器值缓冲区,共58个寄存器,写WM8978的相应寄存器时同步保存到本地,又因为WM8978寄存器值是9位的因此用u16保存
static u16 WM8978_REGVAL_TBL[58]=
{
	0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000,
	0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF,
	0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000,
	0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
	0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9,
	0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100,
	0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039,
	0X0001,0X0001
};

u8 WM8978_RegConfig(u8 regadd,u16 val){

	IIC_Start();//启用IIC准备发送WM8978地址
	IIC_Send_Byte(WM8978_ADDR);
	if(IIC_Wait_Ack()){
		printf("WM8978_RegConfig err :\n");
		return 1;
	}
	IIC_Send_Byte((regadd<<1)|((val>>8)&0x01));//发送寄存器地址(高七位)以及配置数据的第一位
	if(IIC_Wait_Ack()){
		printf("WM8978_RegConfig err :\n");
		return 2;
	}
	IIC_Send_Byte(val);//发送数据,此处与源程序不同,源程序为IIC_Send_Byte(val&0xFF)
	if(IIC_Wait_Ack()){
		printf("WM8978_RegConfig err :\n");
		return 3;
	}
	delay_ms(5);
	IIC_Stop();
	WM8978_REGVAL_TBL[regadd]=val;
return 0;
}


u8 WM8978_Init(){

	u8 res;
	GPIO_InitTypeDef  GPIO_InitStruct;
	//因为I2S的线连接情况,需要使能相应的时钟和配置相应引脚
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC, ENABLE);			//使能外设GPIOB,GPIOC时钟
     
	//PB12/13 复用功能输出,对应I2S的LRCK和SCLK
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽 
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStruct);//初始化
	
	//PC2/PC3/PC6复用功能输出,对应SDOUT、SDIN和MCLK
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3|GPIO_Pin_6;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽 
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOC, &GPIO_InitStruct);//初始化
	
	//因为I2S2和SPI2是共用一个复用功能的,因此配置相应GPIO的相应引脚如下:
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_SPI2); //PB12,AF5  I2S_LRCK
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2);	//PB13,AF5  I2S_SCLK 
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource3,GPIO_AF_SPI2);	//PC3 ,AF5  I2S_DACDATA 
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_SPI2);	//PC6 ,AF5  I2S_MCK
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource2,GPIO_AF6_SPI2);	//PC2 ,AF6  I2S_ADCDATA  I2S2ext_SD是AF6,影响录音机
	

	
	IIC_Init();//初始化IIC接口
	WM8978_RegConfig(0,0);//写任意值到R0对WM8978进行软复位
	WM8978_RegConfig(1,0x1B);//00011011 MICEN设1使能,BIASEN设1开启模拟,VMIDSEL设置为11——5k欧姆
	//WM8978的每一个模拟输出都可以单独的使能或者不使能,联合到模拟混合器的每一个输出可以单独的使能,
	//所有输出都是默认不使能。为了节省电能,WM8978不用的部分应该保留不使能。输出可以在任何时间被使能,
	//但当配置为推动模式时如果BUFIO被禁用或者BUFDCOP被停用不推荐这样做,因为这可能会导致弹出式噪音。
	WM8978_RegConfig(2,0x1B0);//0001 1011 0000 不先使能adc
	WM8978_RegConfig(3,0x6C);//设置LOUT2EN(bit6),ROUT2EN(bit5),RMIXER(bit3),LMIXER(bit2),DACENR(bit1)和DACENL(bit0)等6个位。
	//LOUT2EN和ROUT2EN,设置为1,使能喇叭输出:LMIXER和RMIXER设置为1,使能左右声道混合器: DACENL和DACENR是使能左右声道的DAC,必须设置为1。
	//此处不先设置为1的原因是,因为均设置为1使能了dac,而只有在输入的时候使能dac,可以在后面需要的时候再配置
	WM8978_RegConfig(4,0x06);
	WM8978_RegConfig(6,0x00);
	WM8978_RegConfig(10,0x008);
	WM8978_RegConfig(49,0x60);
	
return 0;
}

u16 WM8978_ReadReg(u8 regadd){

return WM8978_REGVAL_TBL[regadd];
}


//使能adc为输出、使能dac为输入
void WM8978_ADDR_CFG(u8 adcen,u8 dacen){
	u16 regval=0;
	//设置与dac相关的R3
	regval=WM8978_REGVAL_TBL[3];
	if(dacen==1)regval=regval|3;
	else regval=regval & ~(3);
	WM8978_RegConfig(3,regval);
	//设置与adc相关的R2
	regval=WM8978_REGVAL_TBL[2];
	if(adcen==1)regval=regval|3;
	else regval=regval & ~(3);
	WM8978_RegConfig(2,regval);
}
void WM8978_OUTCfg(u8 dacen,u8 byp){

	u16 regval=0;
	regval=WM8978_REGVAL_TBL[50];
	if(dacen==1)regval=regval|1;//将左右声道的dac输出接入左右声道混合器里
	if(byp==1){
	regval=regval|2;//使能byp
	regval=regval|20;//设置byp的增益为0db
	}
	//R50控制左声道,R51控制右声道,寄存器相同,配置相同
	WM8978_RegConfig(50,regval);
	WM8978_RegConfig(51,regval);
}

//fmt=1,MSB;fmt=0,LSB;fmt=2,I2S飞利浦标准;fmt=3,DSP/PCM
//wl=00,16bits;wl=01,20bits;wl=10,24bits;wl=11,32bits;
void WM8978_I2SCfg(u8 fmt,u8 wl){
	if(wl>3)return ;
	if(fmt>3)return ;
	fmt=fmt<<3;//配置01到bits3、bits4
	wl=wl<<5;//配置10到bits5,bits6
	WM8978_RegConfig(4,fmt|wl);//进行寄存器更新配置
	
}

//可添加耳机函数,此处用不到不添加

void WM8978_TrumpetVol(u8 val){
	if(val==0)val=val|1<<6;
	WM8978_RegConfig(54,val);
	WM8978_RegConfig(55,val|(1<<8));//bits8配置为1,进行配置更新
	return ;
}
//还可添加EQ的配置,暂时还不确定需不需要添加


//由于需要通过IIC来配置寄存器,因此需要配置IIC
//I2C.c
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "sys.h"
#include "i2c.h"

void IIC_Init(void){

	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8 | GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;//普通输出模式
	GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//推挽输出
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_100MHz;
	GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;//高电平有效,上拉
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	IIC_SCL=1;
	IIC_SDA=1;

}
void IIC_Start(void){
	SDA_OUT();
	IIC_SDA=1;
	IIC_SCL=1;
	delay_ms(5);//延时10ms
	IIC_SDA=0;
	delay_ms(5);
	IIC_SCL=0;
return ;
}
void IIC_Stop(void){
	SDA_OUT();
	IIC_SCL=0;
	IIC_SDA=0;
	delay_ms(5);
	IIC_SCL=1;
	delay_ms(2);
	IIC_SDA=1;
	delay_ms(5);
}
void IIC_Send_Byte(u8 sendb){
	u8 wt;//等待次数,8位
	SDA_OUT();
	IIC_SCL=0;
	delay_ms(5);
	IIC_SDA=1;//注意此处与源程序不一样
	for(wt=0;wt<8;wt++){
	IIC_SDA=sendb>>7;
	sendb<<=1;
		delay_ms(2);
		IIC_SCL=1;
		delay_ms(2);
		IIC_SDA=0;
	}
	
return ;
}
u8 IIC_Read_Byte(unsigned char ack){
	unsigned char re=0;
	int i;
	SDA_IN();
	for(i=0;i<8;i++){
	
	IIC_SCL=0;
	delay_ms(2);
	IIC_SCL=1;
	re<<=1;
		re=re|SDA_TS;//if(SDA_TS)re++;注意此处与源程序不同
	}
if(!ack)IIC_NAck();
	else IIC_Ack();
return re;
}
u8 IIC_Wait_Ack(void){
	u16 waittime=0;
	SDA_IN();//应答信号是主机等待从机应答,所以应该设置为输入
	IIC_SDA=1;
	delay_ms(2);
	IIC_SCL=1;
	delay_ms(2);
	while(SDA_TS){
	waittime++;
		if(waittime>200){
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//返回默认低电平状态
	return 0;
}
void IIC_Ack(void){

	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;


	return ;
}
void IIC_NAck(void){

IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;


	return ;
}

//I2S.c部分配置,大量参考正点的课程
//I2S初始化
//参数I2S_Standard:  @ref SPI_I2S_Standard  I2S标准,
//I2S_Standard_Phillips,飞利浦标准;
//I2S_Standard_MSB,MSB对齐标准(右对齐);
//I2S_Standard_LSB,LSB对齐标准(左对齐);
//I2S_Standard_PCMShort,I2S_Standard_PCMLong:PCM标准
//参数I2S_Mode:  @ref SPI_I2S_Mode  I2S_Mode_SlaveTx:从机发送;I2S_Mode_SlaveRx:从机接收;I2S_Mode_MasterTx:主机发送;I2S_Mode_MasterRx:主机接收;
//参数I2S_Clock_Polarity   @ref SPI_I2S_Clock_Polarity:  I2S_CPOL_Low,时钟低电平有效;I2S_CPOL_High,时钟高电平有效
//参数I2S_DataFormat: @ref SPI_I2S_Data_Format :数据长度,I2S_DataFormat_16b,16位标准;I2S_DataFormat_16bextended,
//16位扩展(frame=32bit);I2S_DataFormat_24b,24位;I2S_DataFormat_32b,32位.
void I2S2_Init(u16 I2S_Std,u16 I2S_Mode,u16 I2S_Clock_Polarity,u16 I2S_DataFormat){
	
	 I2S_InitTypeDef I2S_InitStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//使能SPI2时钟
	
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,ENABLE); //复位SPI2
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI2,DISABLE);//结束复位
  
	I2S_InitStruct.I2S_Mode=I2S_Mode;//IIS模式
	I2S_InitStruct.I2S_Standard=I2S_Std;//IIS标准
	I2S_InitStruct.I2S_DataFormat=I2S_DataFormat;//IIS数据长度
	I2S_InitStruct.I2S_MCLKOutput=I2S_MCLKOutput_Disable;//主时钟输出禁止(先禁止再通过后续进行配置)
	I2S_InitStruct.I2S_AudioFreq=I2S_AudioFreq_Default;//IIS频率设置
	I2S_InitStruct.I2S_CPOL=I2S_Clock_Polarity;//空闲状态时钟电平
	I2S_Init(SPI2,&I2S_InitStruct);//初始化IIS,实际对I2SPR和I2SCFGR两个寄存器配置

 
	SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);//操作SPI_CR2寄存器,进行SPI2 TX DMA请求使能.
  I2S_Cmd(SPI2,ENABLE);//SPI2 I2S EN使能.	用的是I2S2,因此使能I2S2
	
}

//采样率计算公式:Fs=I2SxCLK/[256*(2*I2SDIV+ODD)]
//I2SxCLK=(HSE/pllm)*PLLI2SN/PLLI2SR
//一般HSE=8Mhz 
//pllm:在Sys_Clock_Set设置的时候确定,一般是8
//PLLI2SN:一般是192~432 
//PLLI2SR:2~7
//I2SDIV:2~255
//ODD:0/1
//I2S分频系数表@pllm=8,HSE=8Mhz,即vco输入频率为1Mhz
//表格式:采样率/10,PLLI2SN,PLLI2SR,I2SDIV,ODD
const u16 I2S_PSC_TBL[][5]=
{
	{800 ,256,5,12,1},		//8Khz采样率
	{1102,429,4,19,0},		//11.025Khz采样率 
	{1600,213,2,13,0},		//16Khz采样率
	{2205,429,4, 9,1},		//22.05Khz采样率
	{3200,213,2, 6,1},		//32Khz采样率
	{4410,271,2, 6,0},		//44.1Khz采样率
	{4800,258,3, 3,1},		//48Khz采样率
	{8820,316,2, 3,1},		//88.2Khz采样率
	{9600,344,2, 3,1},  	//96Khz采样率
	{17640,361,2,2,0},  	//176.4Khz采样率 
	{19200,393,2,2,0},  	//192Khz采样率
};  

//根据公式计算出对应的采样率放入一个数组中,通过查表法直接查询,免去计算
u8 I2S_SampleRateSet(u32 samplerate){
	u8 i=0;
	u32 temp=0;	
	samplerate=samplerate/10;
		for(i=0;i<(sizeof(I2S_PSC_TBL)/10);i++)
		//看看采样率是否存在表中
	{
		if(samplerate==I2S_PSC_TBL[i][0])break;
	}
 
	RCC_PLLI2SCmd(DISABLE);//先关闭PLLI2S
	if(i==(sizeof(I2S_PSC_TBL)/10))return 1;//不存在的采样率
	RCC_PLLI2SConfig((u32)I2S_PSC_TBL[i][1],(u32)I2S_PSC_TBL[i][2]);//设置I2SxCLK的频率(x=2)  设置PLLI2SN PLLI2SR
 
	RCC->CR|=1<<26;					//开启I2S时钟
	while((RCC->CR&1<<27)==0);		//等待I2S时钟开启成功. 
	temp=I2S_PSC_TBL[i][3]<<0;	//设置I2SDIV
	temp|=I2S_PSC_TBL[i][4]<<8;	//设置ODD位
	temp|=1<<9;					//使能MCKOE位,输出MCK
	SPI2->I2SPR=temp;			//设置I2SPR寄存器 
	return 0;
	

}

上一篇:Verilog 代码编写 IIC通信-主到从向芯片写入数据


下一篇:Proteus IIC协议(PCF8574芯片) 仿真 LCD1602 C51 代码