基于S3C6410的KT0801A的驱动

来源:互联网 发布:淘宝i7主机那么便宜 编辑:程序博客网 时间:2024/06/13 02:58

很多车载GPS,车载MP3等上都装有一个FM发射装备。目前比较流行的FM发射芯片有昆天科的QN8006,昆腾微的KT0801A等。本文介绍采用KT0801AWINCE上的驱动的移植。

KT0801A的使用很简单,在正常上电复位后,只用设置发射频率,增益,以及PA即可。不过设备的前提就是I2C必须通信上。

三星的S3C6410WINCE平台上做好了I2C底层通信驱动,音频接口就是调用的该驱动实现的I2C通信。不过读设备不正常,而且只支持标准I2C设备。下面以两种I2C的通信方法介绍针对KT0801A的使用。

方法一:调用I2C驱动接口

首先需要打开I2C驱动。代码如下:

DWORD FMR_I2C_Init()

{

     RETAILMSG(ZONE_DEBUG,(TEXT("FMR_I2C_Init-------------------/r/n")));

     DWORD dwErr = ERROR_SUCCESS, bytes;

     BOOL bRet;

     UINT32 uiIICDelay;

     // 打开I2C驱动

     hIIC = CreateFile(L"IIC0:",

                       GENERIC_READ|GENERIC_WRITE,

                       FILE_SHARE_READ|FILE_SHARE_WRITE,

                       NULL, OPEN_EXISTING, 0, 0);

     if ( hIIC == INVALID_HANDLE_VALUE )

     {

         dwErr = GetLastError();

         RETAILMSG(ZONE_ERROR, (TEXT("[FMR] Error %d opening device '%s' /r/n"), dwErr, L"IIC0:" ));

     }

 

     // 设置I2C延时

     uiIICDelay = Clk_0;

     bRet = DeviceIoControl(hIIC,

                            IOCTL_IIC_SET_DELAY,

                            &uiIICDelay, sizeof(UINT32),

                            NULL, 0,

                            &bytes, NULL);

     if (bRet == FALSE)

     {

         RETAILMSG(ZONE_ERROR, (TEXT("[FMR] I2C0: Device Set Delay Failed./n")));

         dwErr = GetLastError();

     }

     return dwErr;

}

写寄存器函数如下:

DWORD FMR_WriteRegisters(PUCHAR pBuff,       // 寄存器值

                                     UCHAR StartReg,     // 寄存器起始地址

                                     DWORD nRegs         // 寄存器数目

                                     )

{

     DWORD dwErr=0,bytes;

     UCHAR buff[2];

     IIC_IO_DESC IIC_Data;

 

     buff[0] = StartReg;

     buff[1] = pBuff[0];

 

     IIC_Data.SlaveAddress = KT0801A_WRITE;

     IIC_Data.Data = buff;

     IIC_Data.Count = 2;

 

     RETAILMSG(1,(_T("[FMR] WriteRegisters address:0x%x  data:0x%x/n"),buff[0],buff[1]));

 

     // use iocontrol to write

     if ( !DeviceIoControl(hIIC,

                         IOCTL_IIC_WRITE,

                         &IIC_Data, sizeof(IIC_IO_DESC),

                         NULL, 0,

                         &bytes, NULL) )

     {

         dwErr = GetLastError();

     }

 

     if ( dwErr )

     {

         RETAILMSG(1,(_T("[FMR] I2CWrite ERROR: %u /r/n"), dwErr));

     }

 

     return dwErr;

}

读寄存器函数如下:

DWORD FMR_ReadRegisters(PUCHAR pBuff,       // 寄存器值

                                     UCHAR StartReg,     // 寄存器起始地址

                                     DWORD nRegs         // 寄存器数目

                                     )

{

     DWORD dwErr=0;

     DWORD bytes;

     IIC_IO_DESC IIC_AddressData, IIC_Data;

//   static UCHAR  pBuff[1];// added by lqm.

 

     IIC_AddressData.SlaveAddress = KT0801A_READ;

     IIC_AddressData.Data = &StartReg;

     IIC_AddressData.Count = 1;

 

     IIC_Data.SlaveAddress = KT0801A_READ;

     IIC_Data.Data = pBuff;

     IIC_Data.Count = 1;

 

     RETAILMSG(1,(_T("[FMR] HW_ReadRegisters()/r/n")));

 

     // use iocontrol to read

     if ( !DeviceIoControl(hIIC,

                              IOCTL_IIC_READ,

                              &IIC_AddressData, sizeof(IIC_IO_DESC),

                              &IIC_Data, sizeof(IIC_IO_DESC),

                              &bytes, NULL) )

     {

         dwErr = GetLastError();

     }

     if ( dwErr )

     {

         RETAILMSG(1,(_T("[FMR] I2CRead ERROR: %u /r/n"), dwErr));

     }

 

     return dwErr;

}

至此,就可以通过上面的读写寄存器函数操作从设备的寄存器了。但是通过上面的方法不能正常读。为此,这里采用第二种方法。

方法二:模拟I2C接口

由于和默认I2C接口共用一组IO口,如果直接改为模拟IO口,那么其他使用该接口的驱动就无法正常使用了。所以在使用之前需备份它之前的属性。这里给出其读写函数。

读函数如下:

int S3C6410_GPIORdI2C(unsigned    char RegAddr, unsigned    char  *lpData)

{

 

      EnterCriticalSection(&gpio_Lock);

      unsigned long      savereg[3];

      if(GPIO_I2C_Init())

      {

               unsigned    char READ_DATA;

               //保存初始状态

               savereg[0]  = s6410IOP->GPBCON &((0xf<<20)|(0xf<<24));

               savereg[2]  = s6410IOP->GPBPUD  &((3<<10)|(3<<12));

              

               CFG_WRITE(SIO_C);

               CFG_WRITE(SIO_D);

               mdelay(10);

               READ_DATA = GPIO_I2C_receivebyte(RegAddr );

               *lpData=READ_DATA;

                RETAILMSG(0,(TEXT("[IIC_GPIO] read RegAddr[0x%x]= 0x%x/r/n"),RegAddr,*lpData));

                //还原原来的GPIO配置

               s6410IOP->GPBCON=(s6410IOP->GPBCON &~((0xf<<20)|(0xf<<24)));

               s6410IOP->GPBCON|=savereg[0];

               s6410IOP->GPBPUD =(s6410IOP->GPBPUD &~((3<<10)|(3<<12))) ;

               s6410IOP->GPBPUD|=savereg[2] ;

 

               LeaveCriticalSection(&gpio_Lock);

               return TRUE;  

      }

      LeaveCriticalSection(&gpio_Lock);        

      return FALSE;

}

写函数如下:

int S3C6410_GIPOWrI2C(unsigned    char RegAddr, unsigned    char Data)

{

      EnterCriticalSection(&gpio_Lock);

      unsigned long      savereg[3];

      if(GPIO_I2C_Init())

      {

               //保存初始状态

               savereg[0]  = s6410IOP->GPBCON &((0xf<<20)|(0xf<<24));

               savereg[2]  = s6410IOP->GPBPUD  &((3<<10)|(3<<12));

               CFG_WRITE(SIO_C);

               CFG_WRITE(SIO_D);

                                                           

               GPIO_I2C_sendbyte(RegAddr , Data );      

               RETAILMSG(0,(TEXT("write addr  RegAddr[0x%x]= *lpData[0x%x]/r/n"),RegAddr,Data));

                //还原原来的GPIO配置

               s6410IOP->GPBCON=(s6410IOP->GPBCON &~((0xf<<20)|(0xf<<24)));

               s6410IOP->GPBCON|=savereg[0];

               s6410IOP->GPBPUD =(s6410IOP->GPBPUD &~((3<<10)|(3<<12))) ;

               s6410IOP->GPBPUD|=savereg[2] ;

              

               LeaveCriticalSection(&gpio_Lock);        

     

             return TRUE;

      }

      LeaveCriticalSection(&gpio_Lock);                 

      return FALSE;

}

从上面的程序可以看出,在更改寄存器之前,都是先保存其寄存器的属性,使用完马上还原。这样不会影响到像音频驱动这些使用该组I2C总线的器件。而且针对不同协议的I2C接口,可以灵活的变动。

KT0801A的上电函数如下:

BOOL  FMR_KT0801_Power_Set(BOOL Poweron)

{   

RETAILMSG(ZONE_DEBUG,(TEXT("<FMR> FMR_KT0801_Power_Set--%d!!/r/n"),Poweron));

 

      //FMEN----GPK14    FMRST----GPK15   

      S3C6410IOP->GPKCON1 =(S3C6410IOP->GPKCON1 & ~(0xff<<24)) | (0x11<<24);       

      S3C6410IOP->GPKPUD = S3C6410IOP->GPKPUD & ~(0xf<<28) ;       

 

      if(Poweron)

      {      

               S3C6410IOP->GPKDAT |= (1<<14);  //上电

               S3C6410IOP->GPKDAT &= ~(1<<15);  //复位

               Sleep(200);                  

               S3C6410IOP->GPKDAT |= (1<<15);  //复位清除

               Sleep(150);                           

      }

      else

      {

               S3C6410IOP->GPKDAT &= ~(1<<14);  //power down

      }

      return TRUE;

}

该函数比较重要的就是复位的时间了。上面的两个Sleep()相当重要,处理不当,芯片将无法复位,那么以后再正确的操作也不会使它正常工作起来。

频率设置函数如下:

BOOL FMR_KT0801_Frequent_Set(PBYTE pBufOut)

{

      FMR_Freq_Buffer *pArg = (FMR_Freq_Buffer *)pBufOut;

 

      if((pArg->dwFreqBuffer>=OMITFREQUENT_MIN) & (pArg->dwFreqBuffer<=OMITFREQUENT_MAX))

      {

               RETAILMSG(1,  (TEXT("<FMR> Set KT0801_Frequent:%d!/r/n"),pArg->dwFreqBuffer));

               S3C6410_GIPOWrI2C(0x00,(unsigned char)(pArg->dwFreqBuffer & 0xff));

               S3C6410_GIPOWrI2C(0x01,(unsigned char)((pArg->dwFreqBuffer>>8) & 0x7 | 0xC0));// PGA Gain:0dB

               S3C6410_GIPOWrI2C(0x02,0x40);//清掉CHSEL[0],使用step=100K.RFGAIN:112.5dBuV

               S3C6410_GIPOWrI2C(0x13,0x84);

               S3C6410_GIPOWrI2C(0x0B,0x00);//打开PA

               // 通知音频驱动,FM已经打开

               SetEventData(g_hEventFMOpen,1);

               SetEvent(g_hEventFMOpen);

      }

      else

      {

               RETAILMSG(1,  (TEXT("<FMR> Not support Frequent:%d!/r/n"),pArg->dwFreqBuffer));

               S3C6410_GIPOWrI2C(0x0B,0x20);//关闭PA

               FMR_KT0801_Power_Set(FALSE);

               // 通知音频驱动,FM已经关闭

               SetEventData(g_hEventFMOpen,0);

               SetEvent(g_hEventFMOpen);

               FMR_KT0801_Deinit();

               return TRUE;

      }

      return TRUE;

}

上面的函数用于KT0801A的寄存器操作,设置发射频率,PA等。在后面通过SetEventData函数通知音频驱动,现在正在使用FM发射,音频驱动将音频通道切换至FM通道。所有这些函数都通过IOCTL控制,实现频率的设置,模块的睡眠模式,工作模式,打开,关闭,PA设置等。