一.写在前面:

1.首先是第一次发布博客,内容写的不好,不喜勿喷;

2.我的代码含注释可直接用!!!

3.共享才能促进技术进步:因为工作需要国产MCU(HC32F460)驱动TI的音频编解码器TLV320AIC3254产生波形输出,同时也需要采集波形输入。在网上找了好久资料都没有相应的驱动例程,TI官网上有例程还都是Linux下的。更要命的是TI官网给的例程有部分寄存器配置和手册里面还不一样,真是无语死。

没办法,只能自己慢慢琢磨TLV320AIC3254的寄存器(没有上百也有大几十了),一点点调试,最终调试成功了,现把驱动共享给大家,共同学习进步。(HC32F460使用I2C的方式配置TLV320AIC3254,并通过I2S发送数据和读取数据),还可以用SPI配置TLV320AIC3254,但是我没有需要所以没用,但是应该跟I2C配置没啥大的区别。

首先HC32F460真的很难用好吗?I2C写地址非要左移一位用8地址(真的很不习惯,I2C大家都是用7为地址),还有ACK应答信号那里也是坑,正常人都会以为SET是ACK信号,它搞了个RESET是ACK信号

HC32F460的I2C代码

部分HC32F460的I2C配置代码如下:

*I2C写数据*/
int32_t writeRegister(uint8_t addr, uint8_t value)//写寄存器
{
	int32_t Ret = 0;
	uint8_t addrres=0;
	uint8_t data[1] = {0};
	addrres = addr;
	data[0] = value;
	I2C_Cmd(CM_I2C1,ENABLE);//使能打开
	I2C_SWResetCmd(CM_I2C1,ENABLE);
	DDL_DelayUS(10);
	I2C_SWResetCmd(CM_I2C1,DISABLE);
	DDL_DelayUS(10);
	 if(LL_OK!=I2C_Start(CM_I2C1,10))//开始
		   Ret |= 1;
		if(LL_OK!=I2C_TransAddr(CM_I2C1,TLV320_REG_WRITE,0,10000))//写设备地址
	     Ret |= 2;
		if(LL_OK!=I2C_WaitStatus(CM_I2C1,I2C_FLAG_ACKR,RESET,100))//获取应答信号
			 Ret |= 4;
	  if(LL_OK!=I2C_TransAddr(CM_I2C1,addrres,0,10000))//写寄存器地址
		   Ret |= 8;
		if(LL_OK!=I2C_WaitStatus(CM_I2C1,I2C_FLAG_ACKR,RESET,100))//获取应答信号
			 Ret |= 16;
	  if(LL_OK!=I2C_TransData(CM_I2C1,data,1,10000))//写寄存器数据
	 	  	Ret |= 32;
	  if(LL_OK!=I2C_WaitStatus(CM_I2C1,I2C_FLAG_ACKR,RESET,100))//获取应答信号
        Ret |= 64;
	  if(LL_OK!=I2C_Stop(CM_I2C1,1000))//停止
		    Ret |= 128;
	 I2C_Cmd(CM_I2C1,DISABLE);
		DDL_DelayUS(100);
		return Ret;
}
/*I2C读数据*/
static uint8_t readRegister(uint8_t addr)
{
	int32_t Ret = 0;
	uint8_t addrres=0;
	uint8_t data[1] = {0};
	addrres = addr;
	I2C_Cmd(CM_I2C1,ENABLE);//使能打开
	I2C_SWResetCmd(CM_I2C1,ENABLE);
	DDL_DelayUS(10);
	I2C_SWResetCmd(CM_I2C1,DISABLE);
	DDL_DelayUS(10);
	if(LL_OK!=I2C_Start(CM_I2C1,10))//开始
		   Ret |= 1;
  if(LL_OK!=I2C_TransAddr(CM_I2C1,TLV320_REG_WRITE,0,10000))//写设备地址
	     Ret |= 2;
  if(LL_OK!=I2C_WaitStatus(CM_I2C1,I2C_FLAG_ACKR,RESET,100))//获取应答信号
			 Ret |= 4;
  if(LL_OK!=I2C_TransAddr(CM_I2C1,addrres,0,10000))//写寄存器地址
		   Ret |= 8;
	if(LL_OK!=I2C_WaitStatus(CM_I2C1,I2C_FLAG_ACKR,RESET,100))//获取应答信号
			 Ret |= 16;
	if(LL_OK!=I2C_Restart(CM_I2C1,1000))//重新开始
       Ret |= 32;
  if(LL_OK!=I2C_TransAddr(CM_I2C1,TLV320_REG_WRITE,1,10000))//读设备地址
	     Ret |= 64;
  if(LL_OK!=I2C_WaitStatus(CM_I2C1,I2C_FLAG_ACKR,RESET,100))//获取应答信号
			 Ret |= 128;
	if(LL_OK!=I2C_ReceiveData(CM_I2C1,data,1,10000))//读寄存器数据
       Ret |= 256;
	//I2C_AckConfig(CM_I2C1, I2C_NACK);
	I2C_GenerateNACK(CM_I2C1);
	if(LL_OK!=I2C_Stop(CM_I2C1,1000))//停止
		   Ret |= 512;
	I2C_Cmd(CM_I2C1,DISABLE);
	printf("I2C READ ERROR=%d\r\n",Ret);
	return data[0];
}

TLV320AIC3254的配置代码:

static void TLV320AIC3254_CodecInit(void)
{
	int32_t Ret = 0;
/* codec DAC init*/
	//select Page 0
	if(LL_OK!=writeRegister(PAGE_SELECT_REGISTER, 0))
		printf("ERROR=1\r\n");	 
	// 自动清除复位
	if(LL_OK!=writeRegister(0x01, 0x01))
		printf("ERROR=2\r\n");
	//MCLK IS CODEC_CLKIN=12
	if(LL_OK!=writeRegister(0x04, 0x00))
		printf("ERROR=18\r\n");	
	// Power up the NDAC divider with value 1
	if(LL_OK!=writeRegister(0x0B, 0x81))
		printf("ERROR=3\r\n");	
	// Power up the MDAC divider with value 2
	if(LL_OK!=writeRegister(0x0C, 0x82))
		printf("ERROR=4\r\n");
	// Program the OSR of DAC to 128/LSB Value
	if(LL_OK!=writeRegister(0x0D, 0x00))
		printf("ERROR=5\r\n");	
	if(LL_OK!=writeRegister(0x0E, 0x80))
		printf("ERROR=6\r\n");	
	// Set the  Audio Interface mode to I2S,word length=32,BCLK/WCLK input
	if(LL_OK!=writeRegister(0x1B, 0x30))
		printf("ERROR=7\r\n");	
	// Set the DAC Mode to Processing Block PRB_P1
	if(LL_OK!=writeRegister(0x3C, 0x01))
		printf("ERROR=8\r\n");	
	// Select Page 1
	if(LL_OK!=writeRegister(PAGE_SELECT_REGISTER, 0x01))
		printf("ERROR=9\r\n");
	// Disable weak connection of AVDD with DVDD
	if(LL_OK!=writeRegister(0x01, 0x08))
		printf("ERROR=10\r\n");	
	// Enable Master Analog Power Control
	//内部AVDD输出使能,1.72V
	if(LL_OK!=writeRegister(0x02, 0x01))
		printf("ERROR=11\r\n");	
	// Set the REF charging time to 40ms
	if(LL_OK!=writeRegister(0x7B, 0x01))
		printf("ERROR=12\r\n");	
	// HP soft stepping settings for optimal pop performance at power up
	// Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
	// capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
//	if(LL_OK!=writeRegister(0x14, 0x25))//耳机输出,可不需要
//		printf("ERROR=13\r\n");	
	// Set the Input Common Mode to 0.9V and Output Common Mode for Headphone to Input Common Mode
	//耳机和扬声器输出都是FULL-CHIP COMMON MODE
	//扬声器输出电压为LDOIN提供,范围1.8-3.6V
	//LOR和LOL输出模式可更改,以及供电也可以更改
	if(LL_OK!=writeRegister(0x0A, 0x03))
		printf("ERROR=14\r\n");
	// Route Left DAC to LOL
	if(LL_OK!=writeRegister(0x0E, 0x08))
		printf("ERROR=15\r\n");	
	// Route Right DAC to LOR
	if(LL_OK!=writeRegister(0x0F, 0x08))
		printf("ERROR=16\r\n");	
	// Set the DAC PTM mode to PTM_P3/4
	if(LL_OK!=writeRegister(0x03, 0x00))
		printf("ERROR=17\r\n");	
	if(LL_OK!=writeRegister(0x04, 0x00))
		printf("ERROR=18\r\n");	
	// Set the HPL gain to 0dB
	if(LL_OK!=writeRegister(0x10, 0x00))
		printf("ERROR=19\r\n");
	// Set the HPR gain to 0dB
	if(LL_OK!=writeRegister(0x11, 0x00))
		printf("ERROR=20\r\n");
	// Set the LOL gain to 0dB
	if(LL_OK!=writeRegister(0x12, 0x00))
		printf("ERROR=21\r\n");
	// Set the LOR gain to 0dB
	if(LL_OK!=writeRegister(0x13, 0x00))
		printf("ERROR=22\r\n");
	// Power up LOL and LOR drivers
	if(LL_OK!=writeRegister(0x09, 0x0c))//0X0C
		printf("ERROR=23\r\n");
	// Wait for 2.5 sec for soft stepping to take effect
	// Else read Page 1, Register 63d, D(7:6). When = “11” soft-stepping is complete
	DDL_DelayMS(2500);
	// Select Page 0
	if(LL_OK!=writeRegister(PAGE_SELECT_REGISTER, 0))
		printf("ERROR=24\r\n");
	// Power up the Left and Right DAC Channels with route the Left Audio digital data to
	// Left Channel DAC and Right Audio digital data to Right Channel DAC, soft-step volume change enable
	if(LL_OK!=writeRegister(0x3F, 0xD6))//0XD5
		printf("ERROR=25\r\n");
	// Unmute the DAC digital volume control
	if(LL_OK!=writeRegister(0x40, 0x00))
		printf("ERROR=26\r\n");

//codec ADC init
	// Initialize to Page 0
	if(LL_OK!=writeRegister(PAGE_SELECT_REGISTER, 0))
		printf("ERROR=27\r\n");
	// Power up NADC divider with value 1
	if(LL_OK!=writeRegister(0x12, 0x81))
		printf("ERROR=28\r\n");
	// Power up MADC divider with value 2
	if(LL_OK!=writeRegister(0x13, 0x82))
		printf("ERROR=29\r\n");
	// Program OSR for ADC to 128
	if(LL_OK!=writeRegister(0x14, 0x80))
		printf("ERROR=30\r\n");
	// Select ADC PRB_R1
	if(LL_OK!=writeRegister(0x3D, 0x01))//OX3D
		printf("ERROR=31\r\n");
	// Select Page 1
	if(LL_OK!=writeRegister(PAGE_SELECT_REGISTER, 1))
		printf("ERROR=32\r\n");
	// Select ADC PTM_R4
	if(LL_OK!=writeRegister(0x3D, 0x00))
		printf("ERROR=33\r\n");
	// Set MicPGA startup delay to 3.1ms
	if(LL_OK!=writeRegister(0x47, 0x32))
		printf("ERROR=34\r\n");
	//通道1在RADC差分采集,通道2在LADC差分采集
	// Route IN1R to RIGHT_P with 20K input impedance
	if(LL_OK!=writeRegister(0x37, 0x80))
		printf("ERROR=35\r\n");
	// Route IN1L to RIGHT_N with 20K input impedance
	if(LL_OK!=writeRegister(0x39, 0x20))
		printf("ERROR=36\r\n");
	// Route IN2L to LEFT_P with 20K input impedance
	if(LL_OK!=writeRegister(0x34, 0x20))
		printf("ERROR=37\r\n");
	// Route IN2R to LEFT_N with 20K input impedance
	if(LL_OK!=writeRegister(0x36, 0x20))
    printf("ERROR=38\r\n");		
	// Unmute Left MICPGA, Gain selection of 6dB to make channel gain 0dB
	// Register of 6dB with input impedance of 20K => Channel Gain of 0dB
	if(LL_OK!=writeRegister(0x3B, 0x0C))
		printf("ERROR=39\r\n");
	// Unmute Right MICPGA, Gain selection of 6dB to make channel gain 0dB
	// Register of 6dB with input impedance of 20K => Channel Gain of 0dB
	if(LL_OK!=writeRegister(0x3C, 0x0C))
		printf("ERROR=40\r\n");
	// Set MICBIAS voltage 1.25 V 差分信号已经有外部电压偏置
	if(LL_OK!=writeRegister(0x33, 0x40))
		printf("ERROR=41\r\n");
	// Select Page 0
	if(LL_OK!=writeRegister(PAGE_SELECT_REGISTER, 0))
		printf("ERROR=42\r\n");
	// Power up Left and Right ADC Channels
	if(LL_OK!=writeRegister(0x51, 0xC0))
		printf("ERROR=43\r\n");
	//	// DOUT为I2S数据输出
	if(LL_OK!=writeRegister(0x35, 0x02))
		printf("ERROR=7.2\r\n");
	// Unmute Left and Right ADC Digital Volume Control.
	if(LL_OK!=writeRegister(0x52, 0x00))
		printf("ERROR=44\r\n");
	
	//// DIN为主数据输入
//	if(LL_OK!=writeRegister(0x36, 0x02))
//		printf("ERROR=7.1\r\n");
//	// DOUT为I2S数据输出
//	if(LL_OK!=writeRegister(0x35, 0x02))
//		printf("ERROR=7.2\r\n");
}

我这边用I2S收发输出,让TLV320AIC3254输出正弦波,同时音频采集为也采集正弦波输出到串口屏上显示。

I2S收发数据代码:

void SINE_I2S(uint32_t VPPL,uint32_t VPPR)
{
	  uint32_t Vfz1=VPPL>>4;//左通道峰值
	  uint32_t Vfz2=VPPR>>4;//右通道峰值
	  uint8_t i=0,j=0,point=128;
	  uint8_t oled2=0;
	  uint32_t temp=0;
	  uint32_t t0=0;
	  uint32_t sindataL[128]={0};
		uint32_t sindataR[128]={0};
	  uint32_t RXDATAL[128]={0};
		uint32_t RXDATAR[128]={0};
		uint32_t TEMPRXDATAR[128]={0};
	  float  hd=0.0;//弧度
	  float  fz1=0.0;//左通道幅值
		float  fz2=0.0;//右通道幅值
		float  f=50.0;//频率50Hz
		float  t1=0.0;
		float  oled1=0.0; 
	/*频率固定50Hz,一个周期内128个点*/
	  t1=((1/f)*1000000-6000)/point;//频率需要实测调整
		t0=(int)t1/2;
//		printf("t=%d\r\n",t0);	
	for(i=0;i<point;i++)
	{
		hd=2.0f*PI/((float)point);
		fz1=Vfz1*sin(hd*i)+Vfz1;//向上平移VPP/2
		sindataL[i]=round(fz1);
		fz2=Vfz2*sin(hd*i)+Vfz2;//向上平移VPP/2
		sindataR[i]=round(fz2);
	I2S_WriteData(CM_I2S1,(uint32_t) sindataL[i]);//左声道电压U
		RXDATAL[i]=I2S_ReadData(CM_I2S1);
	I2S_WriteData(CM_I2S1, (uint32_t)sindataR[i]);//右声道电流I
		RXDATAR[i]=I2S_ReadData(CM_I2S1);
		oled1=(((float)RXDATAR[i])/((float)0X781a9f77))*0.835;//1.67Vpp
		//printf("RXDATA=%x\r\n",RXDATAR[i]);
		oled2=(uint8_t)((oled1*64)/0.835);
		//printf("oled1=%.2f\r\n",oled1);
		//oled2=30*sin(hd*i)+32;
		OLED_DrawPoint(i,oled2,1);
		//DDL_DelayMS(500);
		DDL_DelayUS(t0);
	}
	
//	printf("sin sceuss\r\n");
}

写在最后

第一次写博客,给出的代码可以直接copy使用的,希望大家看完三连一下,谢谢!!!

(更详细的知识解答请私聊)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐