基于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(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D6 by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D7 by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_PCLK by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D1 by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D2 by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D4 by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_D3 by pngcui
{ EXYNOS4212_GPJ0(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_VSYNC by pngcui
{ EXYNOS4212_GPJ1(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //I2C_SCL7 by pngcui
{ EXYNOS4212_GPJ1(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM2M_RST by pngcui
{ EXYNOS4212_GPJ1(), S3C_GPIO_SLP_INPUT, S3C_GPIO_PULL_DOWN}, //CAM_HREF by pngcui
{ EXYNOS4212_GPJ1(), S3C_GPIO_SLP_OUT0, S3C_GPIO_PULL_NONE}, //I2C_SDA7 by pngcui
{ 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.注册驱动信息
struct platform_driver tea5767_driver = {
.probe = tea5767_probe,
.remove = tea5767_remove,
.shutdown = tea5767_shutdown,
.suspend = tea5767_suspend,
.resume = tea5767_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
/* init the driver */
static int __init tea5767_init(){
int err;
printk("tea5767 init start...\n");
err = platform_driver_register(&tea5767_driver);
printk("state is %d\n\n",err);
return ;
}
/* cleanup the driver */
static void __exit tea5767_exit(){
int i;
printk("tea5767 exit ..and free gpio\n");
gpio_free(SCL);
gpio_free(SDA);
platform_driver_unregister(&tea5767_driver);
}
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("PNGCUI");
module_init(tea5767_init);
module_exit(tea5767_exit);
注:include/linux/init.h中说明了驱动函数的加载顺序,附详细说明链接:http://blog.csdn.net/maopig/article/details/7375933
3.当平台文件中的配置的name与驱动中的name匹配成功之后,会自动进入驱动程序的probe函数中
static int tea5767_probe(struct platform_device *pdv){
int ret,i;
printk("tea5767_probe start..\n");
pll = default_pll;
for(i=; i<GPIO_NUM; i++){
ret = gpio_request(tea5767_gpios[i], "tea5767");
if (ret < ) {
printk("%s: request GPIO %d for tea5767 failed, ret = %d\n", DEVICE_NAME,i, ret);
goto exit;
}
else{
printk("%s: request GPIO %d for tea5767 success, ret = %d\n", DEVICE_NAME,i, ret);
s3c_gpio_cfgpin(tea5767_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(tea5767_gpios[i], );
//gpio_free(tea5767_gpios[i]);
}
}
//使能芯片
//mute=1&stby=1
write_data[]=0xac;
write_data[]=0x7a;
write_data[]=0xd0;
write_data[]=0x57;
write_data[]=0x00;
tea5767_write();
tea5767_read();
//初始化芯片
//设置为mute=1&freq=87400&stby=0
write_data[]=0xa9;
write_data[]=0x9d;
write_data[]=0xa0;
write_data[]=0x17;
write_data[]=0x00;
tea5767_write();
tea5767_read();
ret = misc_register(&tea5767_dev);
if(ret<)
{
printk("tea5767:register device failed!\n");
goto exit;
}
return ;
exit:
misc_deregister(&tea5767_dev);
return ret;
}
在这个函数中,可以进行一些初始化芯片的操作,使能芯片等,这里又需要查看芯片的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接口进行具体功能的实现了
static int tea5767_ioctl( struct file *files, int cmd, int arg){
int ret;
printk("Hello tea5767 and cmd is %d,arg is %d\n",cmd,arg);
switch(cmd){
//打开静音
case OPENMUTE:
openMute();
break;
//关闭静音
case CLOSEMUTE:
closeMute();
break;
//手动搜索
case Search:
search(arg);
break;
//自动搜索
case AutoSearch:
return auto_search(arg,);
//break;
//获取信号强度
case ADC:
return getADC();
//获取当前频道
case FREQ:
get_frequency();
return Frequency_Read;
//设置频道
case SETFREQ:
return setFreq(arg);
//break;
//待机,静音模式
case SHUTDOWN:
setShutDown();
break;
default:
printk("default--cmd=%d,arg=%d\n",cmd,arg);
//test(cmd,arg);
//break;
return -;
}
return ;
}
5.附上完整的驱动测试程序
#include <stdio.h>
#include <stdlib.h> #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #define MAXC 10 main()
{
int fd;
char state[MAXC],cmd[MAXC];
char drv[] = "/dev/tea5767"; if((fd = open(drv, O_RDWR|O_NOCTTY|O_NDELAY))<)
printf("open %s failed\n",drv);
else{
while(){ printf("Input cmd state: ");
scanf("%s %s",cmd,state);
printf("Your Input :cmd = %d,state=%d\n",atoi(cmd),atoi(state));
if(atoi(state) < || atoi(cmd) < )
break;
ioctl(fd,atoi(cmd),atoi(state)); }
}
return ;
}
驱动篇大致介绍到这,后续会进行优化,欢迎大家指出错误与不足指出,非常感谢~~
完整工程代码下载:
https://github.com/pngcui/FM-radio
基于iTop4412的FM收音机系统设计(一)的更多相关文章
- 基于iTop4412的FM收音机系统设计(二)
说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...
- 基于iTop4412的FM收音机系统设计(三)
说明:第一版架构为:APP+JNI(NDK)+Driver(linux),优点是开发简单,周期短,也作为自己的毕业设计 现在更新第二版,FM服务完全植入Android系统中,成为系统服务,架构为:AP ...
- TEA5676 + AT24C08 FM收音机 搜台 存台 mmap 实现读写
硬件说明TEA5767 + AT24c08 要使用耳机收听,不加功放芯片,声音非常小. 这2个芯片都支持 3.3 或 5.0 电源支持连线比较简单,sda scl 接到 2440 对应的 排针上,找出 ...
- 与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器
原文:与众不同 windows phone (20) - Device(设备)之位置服务(GPS 定位), FM 收音机, 麦克风, 震动器 [索引页][源码下载] 与众不同 windows phon ...
- FM收音机 RDS的强大功能
FM收音机 RDS的强大功能 分类: MTK2011-04-26 16:06 14889人阅读 评论(6) 收藏 举报 交通公告体育音乐娱乐教育 前言 随着发展,会有越来越多的电台具有RDS广播功能, ...
- 基于Http协议订阅发布系统设计
基于Http协议订阅发布系统设计 --物联网系统架构设计 1,订阅发布(subscriber-publisher) 订阅发布模式最典型的应用场景就是消息系统的设计.在消息系统的架构中 ...
- 文献综述三:基于JSP的商品信息管理系统设计与开发
一.基本信息 标题:基于JSP的商品信息管理系统设计与开发 时间:2015 出版源:Computer Knowledge and Technology 文件分类:jsp技术的系统开发 二.研究背景 通 ...
- 基于web的图书管理系统设计与实现
原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述 图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...
- 基于web的图书管理系统设计与实现(附演示地址)
欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...
随机推荐
- python3--json反序列化
# Auther: Aaron Fan # 加载文件中的数据 import json with open('test.txt','r',encoding='utf-8') as f: info = j ...
- jdk、jre、jvm的区别联系
jdk包括以下三个东西: D:\Program Files\jdk1.7.0_21\bin\javac.exe和java.exe D:\Program Files\jdk1.7.0_21\jre D: ...
- MVC各个层的作用
(1)控制器的作用是调用模型,并调用视图,将模型产生的数据传递给视图.并让相关视图去显示.(2)模型的作用是获取数据并处理数据.(3)视图的作用是将取得的数据进行组织.美化等,并最终向用户终端输出.
- kalilinux系统设置
echo LANG="zh_CN.UTF-8" > /etc/default/locale
- .NET基础 (06)面向对象的实现
面向对象的实现1 C#中类可以有多个父类.可以实现多个接口吗2 简述C#中重写.重载和隐藏的概念3 为什么在构造方法中调用虚方法会导致问题4 在C#中如何声明一个类不能被继承 面向对象的实现 1 C# ...
- 自己(转)JAVA中toString方法的作用
JAVA中toString方法的作用 因为它是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”. 它通常只是为了方便输出,比如System.out.print ...
- 金牌选手zzy的卡常头文件
一定要粘上去啊,亲测快两倍 #pragma GCC diagnostic error "-std=c++11" #pragma GCC optimize("-fdelet ...
- Mysql简介与编译安装
==========MYSQL工作原理图: 1>数据库简介:简单的说数据库(database)就是一个存储数据的仓库,它将数据按照特定的规律存储到磁盘上,通过数据库管理系统,能够有效的管理存储在 ...
- mvc权限验证--AuthorizeAttribute
在做后台管理时用户登录后就需要验证哪些权限了,没有登录的就直接退出到登录页面. 系统有自带的权限[Authorize],可用于几个地方: 1.将属性[Authorize]置于相关的action上方,验 ...
- APIO2012 派遣dispatching | 左偏树
题目链接:戳我 就是尽可能地选取排名小的,加起来就可以了.然后我们考虑利用一个大根堆,一个一个合并,如果超过派遣的钱,我们就把费用最大的那个忍者丢出队列. 左偏树,作为一个十分优秀的可并堆,我们这道题 ...