zoukankan      html  css  js  c++  java
  • 基于iTop4412的FM收音机系统设计(一)

    说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计

       现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:APP+Frameworks+JNI+HAL+Driver

    整个系统设计,大致分为三篇文章介绍完毕,包括:

    一、驱动设计篇

    二、系统API接口篇

    三、APP功能实现篇

    ---------------------------------------------------(一)驱动设计篇-----------------------------------------------------------------

    在前面介绍过iTop4412的字符驱动的编写方法,所以这里不详细介绍驱动的编写流程了,附上传送门:http://www.cnblogs.com/pngcui/p/4766504.html

    FM调频收音机芯片这里使用的是TEA5767HN模块,使用I2C进行通信,所以我们第一步肯定就是找到芯片的datasheet,找到芯片的设备地址,即0xC0

    同时需要在datasheet中找到I2C的通信协议

    这里需要注意的是,标准的I2C协议并没有这么简洁,不过该datasheet中这么写了,那么我们就根据他来写代码吧

    附上标准的I2C通信协议:

      写操作:START+器件地址+ACK+写寄存器地址+ACK+写数据+ACK+STOP

      读操作:START+(器件地址+写标志位)+ACK+写寄存器地址+ACK+(器件地址+读标志位)+ACK+读数据+STOP

      START信号:当SCL为高期间,SDA由高到低的跳变

      STOP信号: 当SCL为高期间,SDA由低到高的跳变

    有了I2C的通信协议,我们接下来就需要选取开发板的pin脚,去连接TEA5767HN芯片了。

    由于板子上camera接口没有被占用,所以我们在原理图上找到camrea接口,即j27

    我这里选取了CAM_HREF脚与CAM_PCLK脚分别作为I2C的SDA与SCL线,接下来我们需要到kernel的gpio的配置文件中找到这两个引脚的定义

        { EXYNOS4212_GPJ0(0),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_D6 by pngcui
        { EXYNOS4212_GPJ0(1),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_D7 by pngcui
        { EXYNOS4212_GPJ0(2),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_PCLK by pngcui
        { EXYNOS4212_GPJ0(3),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_D1 by pngcui
        { EXYNOS4212_GPJ0(4),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_D2 by pngcui
        { EXYNOS4212_GPJ0(5),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_D4 by pngcui
        { EXYNOS4212_GPJ0(6),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_D3 by pngcui
        { EXYNOS4212_GPJ0(7),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_VSYNC by pngcui
    
        { EXYNOS4212_GPJ1(0),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //I2C_SCL7 by pngcui
        { EXYNOS4212_GPJ1(1),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM2M_RST by pngcui
        { EXYNOS4212_GPJ1(2),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //CAM_HREF by pngcui
        { EXYNOS4212_GPJ1(3),  S3C_GPIO_SLP_OUT0,    S3C_GPIO_PULL_NONE},    //I2C_SDA7 by pngcui
        { EXYNOS4212_GPJ1(4),  S3C_GPIO_SLP_INPUT,    S3C_GPIO_PULL_DOWN},    //NC-CAM2M_PWDN low=0v high=0.3v bu pngcui

       这里需要注意的是,里面的注释与板子上的引脚是对应不起来的,所以最好还是事先写个测试程序,再使用万用表测试一下,引脚是否对应上了。

    到这里,我们就可以使用gpio_set_value函数去控制I2C的两根线了,接下来,就是正式的驱动程序的编写了!!!

    1.在平台文件中配置设备信息

      参考:http://www.cnblogs.com/pngcui/p/4766504.html

    2.注册驱动信息

     1 struct platform_driver tea5767_driver = {
     2     .probe = tea5767_probe,
     3     .remove = tea5767_remove,
     4     .shutdown = tea5767_shutdown,
     5     .suspend  = tea5767_suspend,
     6     .resume = tea5767_resume,
     7     .driver = {
     8         .name = DRIVER_NAME,
     9         .owner = THIS_MODULE,
    10     }
    11 };
    12 
    13 /* init the driver */
    14 static int __init tea5767_init(){
    15     
    16     int err;
    17     
    18     printk("tea5767   init   start...
    ");
    19     
    20     err = platform_driver_register(&tea5767_driver);
    21     
    22     printk("state is %d
    
    ",err);
    23     
    24     return 0;
    25 }
    26 
    27 /* cleanup the driver */
    28 static void __exit tea5767_exit(){
    29     
    30     int i;
    31     
    32     printk("tea5767  exit ..and free gpio
    ");
    33     gpio_free(SCL);
    34     gpio_free(SDA);
    35     
    36     platform_driver_unregister(&tea5767_driver);
    37 }
    38 
    39 MODULE_LICENSE("Dual BSD/GPL");
    40 MODULE_AUTHOR("PNGCUI");
    41 
    42 module_init(tea5767_init);
    43 module_exit(tea5767_exit);

      注:include/linux/init.h中说明了驱动函数的加载顺序,附详细说明链接:http://blog.csdn.net/maopig/article/details/7375933

    3.当平台文件中的配置的name与驱动中的name匹配成功之后,会自动进入驱动程序的probe函数中

     1 static int tea5767_probe(struct platform_device *pdv){
     2     int ret,i;
     3     printk("tea5767_probe start..
    ");
     4     
     5     pll = default_pll;
     6     
     7     
     8     for(i=0; i<GPIO_NUM; i++){
     9         
    10         ret = gpio_request(tea5767_gpios[i], "tea5767");
    11         if (ret < 0) {
    12             printk("%s: request GPIO %d for tea5767 failed, ret = %d
    ", DEVICE_NAME,i, ret);
    13             goto exit;
    14         }
    15         else{
    16             printk("%s: request GPIO %d for tea5767 success, ret = %d
    ", DEVICE_NAME,i, ret);
    17             s3c_gpio_cfgpin(tea5767_gpios[i], S3C_GPIO_OUTPUT);
    18             gpio_set_value(tea5767_gpios[i], 0);
    19             //gpio_free(tea5767_gpios[i]);
    20         }
    21         
    22     }
    23     
    24     //使能芯片
    25     //mute=1&stby=1
    26     write_data[0]=0xac;
    27     write_data[1]=0x7a;
    28     write_data[2]=0xd0;
    29     write_data[3]=0x57;
    30     write_data[4]=0x00;
    31     
    32     tea5767_write();
    33     tea5767_read();
    34     
    35     //初始化芯片
    36     //设置为mute=1&freq=87400&stby=0
    37     write_data[0]=0xa9;
    38     write_data[1]=0x9d;
    39     write_data[2]=0xa0;
    40     write_data[3]=0x17;
    41     write_data[4]=0x00;
    42     tea5767_write();
    43     tea5767_read();
    44     
    45     ret = misc_register(&tea5767_dev);
    46     if(ret<0)
    47     {
    48         printk("tea5767:register device failed!
    ");
    49         goto exit;
    50     }
    51 
    52     return 0;
    53 
    54 exit:
    55     misc_deregister(&tea5767_dev);
    56     return ret;
    57 }

    在这个函数中,可以进行一些初始化芯片的操作,使能芯片等,这里又需要查看芯片的datasheet中具体寄存器代表的含义了

    写模式:

    读模式:

    这里我总结在这里:

    /*
    *读入数据5byte
    -----------8---------------4------------------2-----------------1------------|----8----------------4---------------2--------------------1----
    *1st byte:MUTE(静音=1)    SM(自动搜索=1)     PLL13             PLL12         -|-   PLL11            PLL10            PLL9               PLL8 
    *2nd byte:PLL7           PLL6               PLL5              PLL4        -|—   PLL3             PLL2             PLL1                PLL0
    *3rd byte:SUD(SearchUp=1) SSL1(Search stop) SSL0(Search stop) HLSI(LO)    -|-   MS(单声道=1)     MR(右声道静音=1) ML(左声道静音=1)      SWP1(端口1为高=1)
    *4th byte:SWP2(端口2为高=1) STBY(待机=1)    BL(US/Europe=0)   XTAL(时钟频率) -|- SMUTE(soft mute) HCC(High Cut) SNC(立体音噪音消除1) SI(SWPORT1做ready flag 为1)
    *5th byte:PLLREF(时钟频率)  DTC(de-emphasis time) -              -         -|-   -                -                -                -
    */
    
    /*
    *读出数据5byte
    -----------8-----------------4------------------2-----------------1----------|----8----------------4---------------2-----------------1------
    *1st byte:RF(发现电台=1)    BLF(搜索到头=1)    PLL13             PLL12       -|-   PLL11            PLL10            PLL9             PLL8 
    *2nd byte:PLL7              PLL6             PLL5              PLL4        -|—   PLL3             PLL2             PLL1             PLL0
    *3rd byte:STEREO(立体声=1)  IF6(中频计数结果)   IF5(同前)         IF4(同前)    -|-   IF3(同前)        IF2(同前)        IF1(同前)        IF0(同前)
    *4th byte:LEV3(信号ADC)     LEV2(同前)         LEV1(同前)        LEV0(同前)  -|-   CI3(芯片标记)     CI2(同前)        CI1(同前)        0
    *5th byte:0                   0                0               0           -|-   0                0                0               0
    */

       更具体的说明可以查找芯片的datasheet。

    4.最后就可以使用ioctl接口进行具体功能的实现了

     1 static int tea5767_ioctl( struct file *files, int cmd, int arg){
     2     int ret;
     3     printk("Hello tea5767   and  cmd is %d,arg is %d
    ",cmd,arg);
     4     
     5     switch(cmd){
     6         
     7         //打开静音
     8         case OPENMUTE:
     9             openMute();
    10             break;
    11         
    12         //关闭静音
    13         case CLOSEMUTE:
    14             closeMute();
    15             break;
    16             
    17         //手动搜索
    18         case Search:
    19             search(arg);
    20             break;
    21             
    22         //自动搜索
    23         case AutoSearch:
    24             return auto_search(arg,1);
    25             //break;
    26         //获取信号强度
    27         case ADC:
    28             return getADC();
    29         
    30         //获取当前频道
    31         case FREQ:
    32             get_frequency();
    33             return Frequency_Read;
    34             
    35         //设置频道
    36         case SETFREQ:
    37             return setFreq(arg);
    38             //break;
    39             
    40         //待机,静音模式
    41         case SHUTDOWN:
    42             setShutDown();
    43             break;
    44 
    45         default:
    46             printk("default--cmd=%d,arg=%d
    ",cmd,arg);
    47             //test(cmd,arg);
    48             //break;
    49             return -1;
    50     }
    51     
    52     return 0;
    53 }

     5.附上完整的驱动测试程序

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 
     5 #include <sys/types.h>
     6 #include <sys/stat.h>
     7 #include <fcntl.h>
     8 
     9 
    10 #define MAXC 10
    11 
    12 main()
    13 {
    14     int fd;
    15     char state[MAXC],cmd[MAXC];
    16     char drv[] = "/dev/tea5767";
    17 
    18     if((fd = open(drv, O_RDWR|O_NOCTTY|O_NDELAY))<0)
    19         printf("open %s failed
    ",drv);   
    20     else{
    21         while(1){
    22             
    23             printf("Input cmd state: ");
    24             scanf("%s %s",cmd,state);
    25             printf("Your Input :cmd = %d,state=%d
    ",atoi(cmd),atoi(state));
    26             if(atoi(state) < 0 || atoi(cmd) < 0)
    27                 break;
    28             ioctl(fd,atoi(cmd),atoi(state));            
    29             
    30         }
    31     }
    32     return 0;
    33 }

    驱动篇大致介绍到这,后续会进行优化,欢迎大家指出错误与不足指出,非常感谢~~

    完整工程代码下载:

     https://github.com/pngcui/FM-radio  

  • 相关阅读:
    JS防抖和节流
    移动端屏幕适配
    vue、react对比
    HTTP缓存
    程序员必备技术网站
    W3C标准、表现与数据分离、web语义化
    VUE的响应式原理
    react更新渲染及渲染原理
    ubuntu下mysql的环境搭建及使用
    apktool反编译工具
  • 原文地址:https://www.cnblogs.com/pngcui/p/7153450.html
Copyright © 2011-2022 走看看