STM32驱动TEA5767收音机模块
Tea5767是飞利浦公司出的一款集成化的收音机芯片,大四的时候机缘巧合遇到了这个芯片,用了一下,写点资料
主要特性
TEA5767HN是一款低功耗立体声收音IC,广泛应用于手机MP3 、MP 4 播放器等便携系统。接收频率 76 MHz ~108MHz ( 日本/美国/欧洲频段选择) ,中频频率 225kHz采用锁相环调谐系统,带有AG C电路,并可以使用软件进行静音和消除噪音。主要电性能指标工作电压:2.5 V~5.0 V,工作电流10 m A,灵敏度1 5 V,立体声分离度30dB,信噪比 6 0 d B,输出信号电平7 5mV 。总线通信界面i 2 C和3 线总线可选,具有 R F信号强度ADC输出,软件静音。TEA5767HN采用HVQFN40封装
模块额示意图如下
该芯片主要依靠IIC通讯,不支持SUB_CLASS模式,也就是说,这个芯片没有内部寄存器的概念,一共有五个命令字节,每次读取直接发送芯片地址然后就可以读取或者写入,如下
IICstartàsend device addràwait ackàsend commandàwait ackàrepeat 5àsend stop
可见,没有写入寄存器
那么这五个命令字节分别有什么含义呢?见下表
另外,读和写并不是相同的定义,见下表
另外,对TEA5764的控制我们推荐每次写入读出都按照五个字节来做,因为这样,在写入完后,才能完全是我们设置的内容,但是,也可以发四个字节后直接停止,不影响,没发的那个字节的数据在芯片内部保持不变(但是如果要修改第五个字节,就必须要发送第五位了.为了简单起见,就发送五位吧)
另外,我们在芯片内部存放的是当前频道的PLL值,频率值需要根据PLL计算出来,计算公式如下
射频信号经由天线输入并经过LNA放大后,经过混频后产生固定的中频225KHZ
采用高边带接收时换算公式( HISI = 1 ) :
.采用低边带接收时换算公式(HISI = 0)
要接收 98 MHz频率,采用高边带接收,晶体振荡器为32.768kHz,则对应PLL值为(十进制):
(4×98000000 + 225000))/32768 =11990
换算为十六进制制为2ED6H。
而如果采用低边带接收,对应PLL值为(十进制) :
(4×98000000—225000))/32768=11935
换算为十六进制为 2E9F
搜台的基本流程如下
设置搜索方向,当前频率,通过SM启动搜索,读取数据(50MS一次),当RF==1的时候停止了,此时BLF==0,说明不是因为到极限停止的,这就是一个台,查看IF6看是否正确调谐,是的话,就说明这是个真台
具体代码如下
#ifndef __TEA5767_H #define __TEA5767_H #include "uart.h" #include "delay.h" #include "ioremap.h" //不完善,但是可通信了 #define MAX_FREQ 108000 #define MIN_FREQ 87500 #define MAX_PLL 0x339b //108MHz时的pll, #define MIN_PLL 0x299d //87.5MHz时的pll #define F_IF 225000 //225KHZ中频 #define F_RES 32768 //基准频率 #define TEA_ACK_WAIT_TIME 200 //IIC ack等待时间 200us #define TEA5767ADDR_W 0xc0 #define TEA5767ADDR_R TEA5767ADDR_W+0X01 #define TEA_DEBUG 1 //初始化命令序列 #define TEA_INIT_CIMMAND1 0x2E #define TEA_INIT_CIMMAND2 0xD6 #define TEA_INIT_CIMMAND3 0x01 #define TEA_INIT_CIMMAND4 0x07 #define TEA_INIT_CIMMAND5 0x00 extern u8 TeaSendBuffer[5]; //写入命令序列 extern u8 TeaReadBuffer[5]; //读出命令序列 //初始化,初始化参数在初始化宏中定义 u8 TeaInit(void); //读取一次 u8 TeaReadStatus(void); //获取下一个电台的频率 u32 TeaGetNextCh(u8 dir); u32 TeaGetCurrentFreq(void); #endif
#include "tea5767.h" u8 TeaSendBuffer[5] = {0}; //写入命令序列 u8 TeaReadBuffer[5] = {0}; //读出命令序列 //IO方向设置 #define TEA_SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;} #define TEA_SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;} //IO操作函数 #define TEA_SCL PCout(10) //TEA SCL #define TEA_SDA PCout(11) //TEA SDA #define TEA_READ_SDA PCin(11) //输入SDA static void TEAIoInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;//PC10 PC11 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); TEA_SCL = 1;//初始化均为浮空状态 TEA_SDA = 1; TeaSendBuffer[0] = TEA_INIT_CIMMAND1; TeaSendBuffer[1] = TEA_INIT_CIMMAND2; TeaSendBuffer[2] = TEA_INIT_CIMMAND3; TeaSendBuffer[3] = TEA_INIT_CIMMAND4; TeaSendBuffer[4] = TEA_INIT_CIMMAND5; } //发送IIC起始信号 static void ComStart(void) { TEA_SDA_OUT(); //sda线输出 TEA_SDA=1; TEA_SCL=1; DelayUs(5); TEA_SDA=0;//START:when CLK is high,DATA change form high to low DelayUs(5); TEA_SCL=0;//钳住I2C总线,准备发送或接收数据 } //发送IIC停止信号 static void ComStop(void) { TEA_SDA_OUT();//sda线输出 TEA_SDA=0;//STOP:when CLK is high DATA change form low to high TEA_SCL=1; DelayUs(5); TEA_SDA=1;//发送I2C总线结束信号 DelayUs(5); } //等待ACK,为1代表无ACK 为0代表等到了ACK static u8 ComWaitAck(void) { u8 waitTime = 0; TEA_SDA_OUT();//sda线输出 TEA_SDA = 1; DelayUs(5); TEA_SDA_IN(); //SDA设置为输入 TEA_SCL=1; DelayUs(5); while(TEA_READ_SDA) { waitTime++; DelayUs(1); if(waitTime > TEA_ACK_WAIT_TIME) { ComStop(); return 1; } } TEA_SCL = 0; return 0; } static void ComSendAck(void) { TEA_SCL = 0; TEA_SDA_OUT(); TEA_SDA = 0; DelayUs(2); TEA_SCL = 1; DelayUs(5); TEA_SCL = 0; DelayUs(5); } static void ComSendNoAck(void) { TEA_SCL = 0; TEA_SDA_OUT(); TEA_SDA = 1; DelayUs(2); TEA_SCL = 1; DelayUs(5); TEA_SCL = 0; DelayUs(5); } //返回0 写入收到ACK 返回1写入未收到ACK static u8 ComSendByte(u8 byte) { u8 t; TEA_SDA_OUT(); for(t=0;t<8;t++) { TEA_SDA=(byte&0x80)>>7; byte<<=1; TEA_SCL=1; DelayUs(5); TEA_SCL=0; DelayUs(5); } return ComWaitAck(); } static void ComReadByte(u8* byte) { u8 i,receive=0; TEA_SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { receive <<= 1; TEA_SCL=1; DelayUs(5); if(TEA_READ_SDA)receive++; TEA_SCL=0; DelayUs(5); } *byte = receive; } /********************************************************************************************************/ u8 TeaWriteCommand(void) { u8 res = 0; u8 i = 0; ComStart(); res = ComSendByte(TEA5767ADDR_W);//发送地址 if(res) { #ifdef TEA_DEBUG printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__); #endif return res; } for(i = 0; i < 5; i++) { res = ComSendByte(TeaSendBuffer[i]);//发送命令数据 if(res) { #ifdef TEA_DEBUG printf("file=%s ,func=%s ,line=%d \r\n",__FILE__,__FUNCTION__,__LINE__); #endif return res; } } ComStop(); return 0; } //返回0成功 返回1失败 u8 TeaReadStatus(void) { u8 res = 0; u8 i = 0; ComStart(); res = ComSendByte(TEA5767ADDR_R);//读取地址 if(res) { #ifdef TEA_DEBUG printf("file=%s,func=%s,line=%d\r\n",__FILE__,__FUNCTION__,__LINE__); #endif return res; } for(i = 0; i < 5; i++) { ComReadByte(TeaReadBuffer+i);//发送命令数据 if(i == 4) ComSendNoAck(); else ComSendAck(); } ComStop(); return 0; } //返回0成功 返回1失败 u8 TeaInit(void) { TEAIoInit(); return TeaWriteCommand(); } u32 TeaGetCurrentFreq(void) { u8 hlsi; u32 pll = 0; u32 freq = 0; if(TeaReadStatus())return 0; hlsi = TeaSendBuffer[2]&0x10; //HLSI位 pll = TeaReadBuffer[0]&0x3f; pll<<=8; pll += TeaReadBuffer[1]; if(hlsi) { freq = (pll*(F_RES/4)) - F_IF; } else { freq = (pll*(F_RES/4)) + F_IF; } return freq; } void TeaSetFreq(u32 freq) { u8 hlsi; u32 pll = 0; u8 pllH,pllL; hlsi = TeaSendBuffer[2]&0x10; //HLSI位 if (hlsi) pll=(unsigned int)((float)((freq+F_IF)*4)/(float)F_RES); //频率单位:HZ else pll=(unsigned int)((float)((freq-F_IF)*4)/(float)F_RES); //频率单位:HZ pll &= 0x00003fff; pllL = (u8)pll; pllH = (u8)(pll>>8);//分别PLL TeaSendBuffer[0] &= 0xc0; TeaSendBuffer[0] |= pllH; TeaSendBuffer[1] = pllL;//插入发送序列 TeaWriteCommand();//写入芯片 } //1向上搜索 0向下搜索 u32 TeaGetNextCh(u8 dir) { if(dir)TeaSendBuffer[2] |= 0x80; else TeaSendBuffer[2] &= ~0x80;//确定方向 TeaSendBuffer[0] |= 0x40;//启动搜索 DelayMs(50); do { TeaReadStatus(); }while((TeaReadBuffer[0]&0x80) == 0); //已经结束 if((TeaReadBuffer[0]&0x40) == 0) { //找到一个台 return TeaGetCurrentFreq(); } else { return 0; } }