说明:第一版架构为: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的配置文件中找到这两个引脚的定义

  1. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D6 by pngcui
  2. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D7 by pngcui
  3. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_PCLK by pngcui
  4. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D1 by pngcui
  5. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D2 by pngcui
  6. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D4 by pngcui
  7. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D3 by pngcui
  8. { EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_VSYNC by pngcui
  9.  
  10. { EXYNOS4212_GPJ1(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //I2C_SCL7 by pngcui
  11. { EXYNOS4212_GPJ1(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM2M_RST by pngcui
  12. { EXYNOS4212_GPJ1(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_HREF by pngcui
  13. { EXYNOS4212_GPJ1(), S3C_GPIO_SLP_OUT0, S3C_GPIO_PULL_NONE}, //I2C_SDA7 by pngcui
  14. { EXYNOS4212_GPJ1(), 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...\n");
  19.  
  20. err = platform_driver_register(&tea5767_driver);
  21.  
  22. printk("state is %d\n\n",err);
  23.  
  24. return ;
  25. }
  26.  
  27. /* cleanup the driver */
  28. static void __exit tea5767_exit(){
  29.  
  30. int i;
  31.  
  32. printk("tea5767 exit ..and free gpio\n");
  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..\n");
  4.  
  5. pll = default_pll;
  6.  
  7. for(i=; i<GPIO_NUM; i++){
  8.  
  9. ret = gpio_request(tea5767_gpios[i], "tea5767");
  10. if (ret < ) {
  11. printk("%s: request GPIO %d for tea5767 failed, ret = %d\n", DEVICE_NAME,i, ret);
  12. goto exit;
  13. }
  14. else{
  15. printk("%s: request GPIO %d for tea5767 success, ret = %d\n", DEVICE_NAME,i, ret);
  16. s3c_gpio_cfgpin(tea5767_gpios[i], S3C_GPIO_OUTPUT);
  17. gpio_set_value(tea5767_gpios[i], );
  18. //gpio_free(tea5767_gpios[i]);
  19. }
  20.  
  21. }
  22.  
  23. //使能芯片
  24. //mute=1&stby=1
  25. write_data[]=0xac;
  26. write_data[]=0x7a;
  27. write_data[]=0xd0;
  28. write_data[]=0x57;
  29. write_data[]=0x00;
  30.  
  31. tea5767_write();
  32. tea5767_read();
  33.  
  34. //初始化芯片
  35. //设置为mute=1&freq=87400&stby=0
  36. write_data[]=0xa9;
  37. write_data[]=0x9d;
  38. write_data[]=0xa0;
  39. write_data[]=0x17;
  40. write_data[]=0x00;
  41. tea5767_write();
  42. tea5767_read();
  43.  
  44. ret = misc_register(&tea5767_dev);
  45. if(ret<)
  46. {
  47. printk("tea5767:register device failed!\n");
  48. goto exit;
  49. }
  50.  
  51. return ;
  52.  
  53. exit:
  54. misc_deregister(&tea5767_dev);
  55. return ret;
  56. }

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

写模式:

读模式:

这里我总结在这里:

  1. /*
  2. *读入数据5byte
  3. -----------8---------------4------------------2-----------------1------------|----8----------------4---------------2--------------------1----
  4. *1st byte:MUTE(静音=1) SM(自动搜索=1) PLL13 PLL12 -|- PLL11 PLL10 PLL9 PLL8
  5. *2nd byte:PLL7 PLL6 PLL5 PLL4 -|— PLL3 PLL2 PLL1 PLL0
  6. *3rd byte:SUD(SearchUp=1) SSL1(Search stop) SSL0(Search stop) HLSI(LO) -|- MS(单声道=1) MR(右声道静音=1) ML(左声道静音=1) SWP1(端口1为高=1)
  7. *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)
  8. *5th byte:PLLREF(时钟频率) DTC(de-emphasis time) - - -|- - - - -
  9. */
  10.  
  11. /*
  12. *读出数据5byte
  13. -----------8-----------------4------------------2-----------------1----------|----8----------------4---------------2-----------------1------
  14. *1st byte:RF(发现电台=1) BLF(搜索到头=1) PLL13 PLL12 -|- PLL11 PLL10 PLL9 PLL8
  15. *2nd byte:PLL7 PLL6 PLL5 PLL4 -|— PLL3 PLL2 PLL1 PLL0
  16. *3rd byte:STEREO(立体声=1) IF6(中频计数结果) IF5(同前) IF4(同前) -|- IF3(同前) IF2(同前) IF1(同前) IF0(同前)
  17. *4th byte:LEV3(信号ADC) LEV2(同前) LEV1(同前) LEV0(同前) -|- CI3(芯片标记) CI2(同前) CI1(同前) 0
  18. *5th byte:0 0 0 0 -|- 0 0 0 0
  19. */

  更具体的说明可以查找芯片的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\n",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,);
  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\n",cmd,arg);
  47. //test(cmd,arg);
  48. //break;
  49. return -;
  50. }
  51.  
  52. return ;
  53. }

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

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

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

完整工程代码下载:

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

基于iTop4412的FM收音机系统设计(一)的更多相关文章

  1. 基于iTop4412的FM收音机系统设计(二)

    说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...

  2. 基于iTop4412的FM收音机系统设计(三)

    说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...

  3. TEA5676 + AT24C08 FM收音机 搜台 存台 mmap 实现读写

    硬件说明TEA5767 + AT24c08 要使用耳机收听,不加功放芯片,声音非常小. 这2个芯片都支持 3.3 或 5.0 电源支持连线比较简单,sda scl 接到 2440 对应的 排针上,找出 ...

  4. 与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器

    原文:与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器 [索引页][源码下载] 与众不同 windows phon ...

  5. FM收音机 RDS的强大功能

    FM收音机 RDS的强大功能 分类: MTK2011-04-26 16:06 14889人阅读 评论(6) 收藏 举报 交通公告体育音乐娱乐教育 前言 随着发展,会有越来越多的电台具有RDS广播功能, ...

  6. 基于Http协议订阅发布系统设计

      基于Http协议订阅发布系统设计 --物联网系统架构设计   1,订阅发布(subscriber-publisher)      订阅发布模式最典型的应用场景就是消息系统的设计.在消息系统的架构中 ...

  7. 文献综述三:基于JSP的商品信息管理系统设计与开发

    一.基本信息 标题:基于JSP的商品信息管理系统设计与开发 时间:2015 出版源:Computer Knowledge and Technology 文件分类:jsp技术的系统开发 二.研究背景 通 ...

  8. 基于web的图书管理系统设计与实现

    原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述     图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...

  9. 基于web的图书管理系统设计与实现(附演示地址)

    欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...

随机推荐

  1. Linux系统巡检常用命令-乾颐堂

    Linux系统需要定期巡检,以检查服务器软硬件使用情况,相当于对人的体检,确保可以及时发现问题.解决问题,降低损失,常用的巡检命令如下: # uname -a # 查看内核/操作系统/CPU信息 # ...

  2. 查看并解除Oracle锁

    当某个数据库用户在数据库中插入.更新.删除一个表的数据,或者增加一个表的主键时或者表的索引时, 常常会出现ora-00054:resource busy and acquire with nowait ...

  3. SQL序列键

    当需要更新表中的数据或像表中插入数据时,在很多情况下需要产生唯一的整数序列键 一:更新列的值为唯一值 原数据如下图: 可以定义一个CTE,返回orerid列的值以及row_number()的计算结果. ...

  4. libxml2

    http://www.xmlsoft.org/downloads.html   建议直接用openscenegraph下载的3rdparty:http://www.openscenegraph.org ...

  5. bootstrap-海棠

    12 缩略图和警告框 <p class='alert alert-info'>这个是警告组<button class='close' data-dismiss='alert'> ...

  6. JVM 方法调用之解析

    方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还没有涉及到方法内部的具体运行过程.在程序运行时,进行方法调用是最普遍最频繁的操作,但Class文件 ...

  7. 终端工具putty访问vmware centos系统

    当我们安装好后,可以通过shell来输入命令行来操作centos,当我们一般为了方便可以用终端进行远程连接虚拟机. 软件下载:http://www.chiark.greenend.org.uk/~sg ...

  8. PyCharm社区版+Django搭建web开发环境-2

    接上一篇:PyCharm社区版+Django搭建web开发环境-1 1. 创建好django项目并建立app应用:web 2. setting.py:配置app应用 INSTALLED_APPS = ...

  9. Android-AIDL调用Android操作系统Music的方式来播放音乐

    Android操作系统Music源码修改:把包名,各种命名都修改成自己的 修改Android操作系统Music源码,把后台播放核心服务对外暴漏: <!-- 核心服务 --> <!-- ...

  10. WebApi中跨域请求的解决方案和原理

    跨域请求仅发生在JavaScript发起Ajax请求时,浏览器对请求的限制,通常只允许访问同一个域中的资源,或者目标服务器明确的通知浏览器允许该域访问资源. 那么什么叫跨域的:主机地址或者ip地址或者 ...