基于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...\n");
19
20     err = platform_driver_register(&tea5767_driver);
21
22     printk("state is %d\n\n",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\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
 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\n", DEVICE_NAME,i, ret);
13             goto exit;
14         }
15         else{
16             printk("%s: request GPIO %d for tea5767 success, ret = %d\n", 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!\n");
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\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,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\n",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\n",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\n",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

时间: 2024-11-07 17:47:58

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

FM收音机 RDS的强大功能

FM收音机 RDS的强大功能 分类: MTK2011-04-26 16:06 14889人阅读 评论(6) 收藏 举报 交通公告体育音乐娱乐教育 前言 随着发展,会有越来越多的电台具有RDS广播功能,RDS可以显示电台名称.节目类型.节目内容.日期时间及其它信息,而它具有的AF和TA功能,则更强大. 通常一个广播电台想扩大它的覆盖率,可能会进行多个频率点的发射,比如某个电台有87.8和95.8MHZ两个频点,当你开着车听着87.8,去到某个地方时,信号变差了, 这时候,开了AF功能,AF功能会自

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

  --物联网系统架构设计   1,订阅发布(subscriber-publisher) 订阅发布模式最典型的应用场景就是消息系统的设计.在消息系统的架构中,消息的发送者称作(publisher),消息的接收者称作(subscriber),参见wikipedia: Publish–subscribe pattern.整个消息系统的架构可以用如下图1来描述: 图1 由图1可知消息系统主要包括3个组件: 发布者,订阅者和消息代理(Broker),而整个消息系统的核心即是Broker,而目前就业务能力

基于C/S自动更新系统设计(一)

要求:用C语言写一个基于C/S架构的自动更新系统. 确定更新的内容 版本号码 数字签名 (每个文件的 SHA1是不是一样的) 下载和更新系统 这里现在只能画出部分的流程图 系统模块流程图 socket通信的流程图 文件读写的操作流程图

学习手记-基于iTOP4412开发板NFS服务器搭建及测试

NFS特点: 1)基于UDP/IP 2)功能和网盘基本上差不多,但性能没那么强. NFS服务器搭建步骤: 在ubantu上安装nfs软件:nfs-kernel-server 配置文件 1)打开配置文件:/etc/exports 2)增加配置:/home/topeet/minLinux/ *(rw,sync,no_root_squash) 含义如下: /home/topeet/minLinux/:服务器上的共享目录 *:表示接受任何网段的访问 rw:可读可写 sync:同步 no_root_squ

基于J2EE的毕业论文管理系统设计与实现,免费分享

今天将为大家分析一个毕业论文管理系统(为加强本科生毕业论文管理与监控,提高毕业论文管理信息化程度,基于J2EE开发了毕业论文管理系统.根据实际需求,设计了毕业论文的选题.任务签订.开题.中期检查.论文提交.指导.评阅和论文答辩等相关环节,构建了系统的E-R图与逻辑结构,最后编程实现.),该项目使用框架为SSM(MYECLIPSE),选用开发工具为MYECLIPSE.毕业论文管理系统为一个 后台项目. 为了完成该系统,我们首先需要对该系统进行需求分析.一个毕业论文管理系统应包含用户角色有管理员.教

基于51单片机简易操作系统设计

前言 看了很久的操作系统原理,ucos源码也看了大半,但是感觉总是懵懵懂懂,用句流行的网络用语就是始终上不了车,后来在网上被人推荐了一篇文章<建立一个属于自己的操作系统>,这篇文章真的非常好,也附有源码,但不知道是不是我找的文章有差错还是啥,我根据文章提供的源码贴代码,根本无法编译,然后开始读代码修改代码最后成功编译但是在硬件平台运行根本不行.后来又断断续续看ucos源码,反正各种什么数据结构啊的通信什么的让人头痛,后来大学的单片机原理完课,学校安排课设,我选了时钟定时器(有点像闹钟),这种开

学习手记-基于iTOP4412开发板Linux最小系统烧录

最小系统只能通过超级终端进行操作,没有画面显示. 使用工具:超级终端.fastboot 通信:串口RS232 操作环境:开机后完成bootloader,但还未挂载文件系统之前.(开机后通过"超级终端"中止文件系统挂载) 操作权限:root 操作步骤: 1.格式化(按顺序来) fdisk -c 0     [分区命令 分区序号:0 (该命令将会清空emmc)] fatformat mmc 0:1    [按"FAT"格式进行格式化,对象:mmc,分区序号0第一分区]

基于 spring cloud 的广告系统设计与实现(二)

1. 概念 2. 层级 3. 表设计 原文地址:https://www.cnblogs.com/rabbitli/p/11143004.html

基于JAVA WEB技术旅游服务网站系统设计与实现网上程序代写

基于JAVA WEB技术旅游服务网站系统设计与实现网上程序代写 专业程序代写服务(QQ:928900200) 随着社会的进步.服务行业的服务水平不断发展与提高,宾馆.酒店.旅游等服务行业的信息量和工作量日益变大,而传统的人工管理方式已经远远不能满足现在旅游的服务方式.传统的旅游方式经分析其有诸多的缺陷,存在数据维护效率低下,不易保管,容易丢失和出错.同时查询也不方便,劳动力成本过高导致的旅游资源信息不方便,也在一定程度上导致了对各种信息反应缓慢,容易丧失商机.为了弥补上述缺陷,便于开展旅游预订工