zoukankan      html  css  js  c++  java
  • TEA5676 + AT24C08 FM收音机 搜台 存台 mmap 实现读写


    硬件说明
    TEA5767 + AT24c08
    要使用耳机收听,不加功放芯片,声音非常小。

    这2个芯片都支持 3.3 或 5.0 电源支持
    连线比较简单,sda scl 接到 2440 对应的 排针上,找出一路 3.3v GND ,这里我是从 com 口上接出。
    如下接线图,tea5767 at24c08 来自于,之前用 51 单片机做的 FM 收音机,因为电平不同,就把 51 单片机取下来了。
    1602 因为 2440 开发板,没有这么多可用 GPIO 这次就不接了。

    中间的转换板是我自己焊的,是一个 5V 转 3.3V 和 LED 台灯 和 排针连接工具。 用的是 asm1117 3.3 。
    制作中出现过一个严重的问题,led 灯全烧了,之前用 笔记本上面的 usb 供电,led 灯虽然说是亮些,但是没有烧过。
    这次新买了一个 台达5v 5a 电源,接上线后全烧了。加了一个100欧,限流电阻后正常了。

    编译环境
    ubunto 12
    arm-linux-gcc 4.3.2

    使用说明
    本项目基于 linux4.3.36 (2.6 内核肯定是不能用,3.1 之后的也许可以,如果不能用请自行修改)
    首先要配置内核启用 i2c 的支持
    make menuconfig
    -> Device Drivers
       -> I2C support
          -> I2C support (I2C [=y])
             -> I2C Hardware Bus support    

    -> Device Drivers
        -> I2C support
           -> I2C support (I2C [=y])


    基本上有难度的是 at24c08 的 mmap 实现。 不是普通的读写。 这样做,程序写起来比较简单。

    打开 fm 应用程序后,打开 tea5767 和 at24c08 对 at24c08 进行 mmap 映射为 unsigned long 来表现,电台的 频率, 单位是 Khz (因为小数不容易存)。
    搜到台以后, 把台的频率(单位是Khz)保存到 mmap 中, 退出的时候,在写回 at24c08 。简化了程序。

    内核驱动中需要注意的是, mmap 的 内存要求是物理地址连续,所以使用 dma_alloc_writecombine 分配。

    请把 项目中的所有文件,下载到本地,修改 Makefile 中的 内核路径,除非你的和我的一样,这样就不用修改了。
    执行
    在项目目录中 执行 make
    生成 4个 ko 文件 和 fm 应用程序。 把这5个文件复制到,开发板上,或 nfs 中。

    启动开发板,请一定要注意,内核有 i2c 硬件支援,然后加载4个ko 文件。
    insmod tea5767/tea5767.ko
    insmod tea5767/tea5767_dev.ko
    insmod tea5767/at24cxx.ko
    insmod tea5767/at24cxx_dev.ko

    执行测试 fm 程序
    tea5767/fm

    输入 s 按回车 自动搜台
    搜完毕后,自动打开第1个台
    可以按 n 切换下一台
    可以按 p 切换上一台
    可以按 q 退出
    可以按 h 查看帮助信息
    可以按 t 进行测试模式 这里收听一个 北京台 99.6Mhz

    主要发下 at24c08 的 mmap 实现

    #include <linux/i2c.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/of.h>
    #include <linux/slab.h>
    #include <linux/spinlock.h>
    #include <linux/sysfs.h>
    #include <asm/uaccess.h>
    #include <linux/fs.h>
    #include <linux/mm.h>
    #include <linux/mman.h>
    #include <linux/mutex.h>
    #include <linux/slab.h>
    #include <linux/dma-mapping.h>
    #include <linux/delay.h>
    
    //主设备号
    static int major;
    //类描述为创建设备使用
    static struct class *cls;
    //i2c client 发送接收数据使用
    static struct i2c_client *at24cxx_client;
    
    //互斥锁
    static struct mutex at24cxx_mutex;
    
    //i2c table 和 board 中进行比较 相同调用 probe
    static struct i2c_device_id at24cxx_id_table[] = {
        {"at24c08", 0},
        {}
    };
    
    //mmap 使用内存
    static unsigned char *at24cxx_buf;
    //物理地址 使用和 lcd fb 一样的分配内存的方法 dma_alloc_writecombine
    static dma_addr_t at24cxx_phys;
    
    #define AT24cxx_BUF_SIZE 64
    
    static int at24cxx_mmap(struct file *file, struct vm_area_struct *vma)
    {
        u64 len = vma->vm_end - vma->vm_start;
        len = min(len, AT24cxx_BUF_SIZE);
        //no cache
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
        return vm_iomap_memory(vma, at24cxx_phys, len);
    }
    
    static int at24cxx_open(struct inode *note, struct file *f)
    {
        //打开时 填充 mmap 的buf
        int i;
        memset(at24cxx_buf, 0, AT24cxx_BUF_SIZE);
        for(i=0; i<AT24cxx_BUF_SIZE; i++)
        {
            at24cxx_buf[i] = i2c_smbus_read_byte_data(at24cxx_client, i);
        }
        return 0;
    }
    
    static int at24cxx_release(struct inode *note, struct file *f)
    {
        //写 mmap 中的数据回 at24c08
        int i;
        for(i=0; i<AT24cxx_BUF_SIZE; i++)
        {
            i2c_smbus_write_byte_data(at24cxx_client, i, at24cxx_buf[i]);
            mdelay(100);
        }
        return 0;
    }
    
    static struct file_operations at24cxx_ops = {
        .owner   = THIS_MODULE,
        .open    = at24cxx_open,
        .release = at24cxx_release,
        .mmap    = at24cxx_mmap,
    };
    
    //当idtab中比较相同以后调用这个
    static int at24cxx_probe(struct i2c_client *client,const struct i2c_device_id *dev)
    {
        //初始化锁
        mutex_init(&at24cxx_mutex);
        //设给全局变量
        at24cxx_client = client;
        //创建字库设备
        cls = class_create(THIS_MODULE, "at24cxx");
        major = register_chrdev(0, "at24cxx", &at24cxx_ops);
        device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx");
    
        //vmalloc 分配的物理地址连续
        at24cxx_buf = dma_alloc_writecombine(NULL, AT24cxx_BUF_SIZE, &at24cxx_phys, GFP_KERNEL);
        return 0;
    }
    
    static int at24cxx_remove(struct i2c_client *client)
    {
        device_destroy(cls, MKDEV(major, 0));
        class_destroy(cls);
        unregister_chrdev(major, "at24cxx");
        dma_free_writecombine(NULL, AT24cxx_BUF_SIZE, at24cxx_buf, at24cxx_phys);
        return 0;
    }
    
    static struct i2c_driver at24cxx_drv = {
        .driver = {
            .name  = "at24cxx",
            .owner = THIS_MODULE,
        },
        .probe  = at24cxx_probe,
        .remove = at24cxx_remove,
        .id_table = at24cxx_id_table,
    };
    
    static int at24cxx_init(void)
    {
        i2c_add_driver(&at24cxx_drv);
        return 0;
    }
    
    static void at24cxx_exit(void)
    {
        i2c_del_driver(&at24cxx_drv);
    }
    
    module_init(at24cxx_init);
    module_exit(at24cxx_exit);
    MODULE_LICENSE("GPL");

     
     GIT 项目地址 https://github.com/nejidev/tea5767_at24c08_fm_radio_linux

  • 相关阅读:
    c#之字符串,列表,接口,队列,栈,多态
    c#之函数
    KMP算法
    字符串Hash
    洛谷P1807 最长路_NOI导刊2010提高(07)
    洛谷P2863 [USACO06JAN]牛的舞会The Cow Prom
    洛谷P2071 座位安排
    二分图最大匹配,匈牙利算法
    差分约束系统
    搜索
  • 原文地址:https://www.cnblogs.com/ningci/p/6561892.html
Copyright © 2011-2022 走看看