• linux驱动学习(2)beep驱动


    今天拿beep程序练手,主要学习linux driver的开发流程和内核编程相关的重要接口。

    弄完之后就要写uart驱动了。

    参考书籍:《华清远见 linux驱动开发详解》

    开发板:tq2440

    下面是tq2440的蜂鸣器电路的电路图:

    核心板:

    可知,通过对TOUT0/GPB0进行编程可控制蜂鸣器

    而对于s3c2440gpio来说,只需要设置configdata寄存器即可。

    Gpb0设置为输出,gpbcon寄存器[10]设置为01;

    之后就可以用gpbdat来设置其值了。

    下面通过s3c2440 pwm来控制bepp的鸣叫。

    pwm设置占空比

    pwmTIMER0模块硬件结构图

    使用Timer0

    1、选择Timer0预置溢出数值,设置为15表示计数满PCLK/16/devided value

    Diviedr 值由TCFG0/1设置

    计数缓冲寄存器值和匹配缓冲寄存器来共同决定占空比大小。

    TCNTBn用来设置down_counter(减法计数器)计数大小,从而决定一个周期的大小。

    TCNTB..

    小结一下:

    当定时器计数递减到0,一个定时器的自动加载操作将发生。因此,TCNTn一开始的值已经被用户预先定义。在这种情况下,设置新的开始值需要通过人工加载位来被加载。下面部分描述如何开始一个定时器:

    1)初始化TCNTBnTCMPn

    2)设置相应定时器的人工加载位。不管是否使用极性转换功能,推荐都配置下极性转换功能位。

    3)设置相应定时器的启动位来启动定时器,同时清除人工加载位。

    如果定时器在计数过程中被强行停止,则TCNTn保持计数值,如需设置新的数值,需要人工加载。

    最后设置TCON使能即可。

    设置bit[4:0]11101

    B = 01011

    ~2 = 01101

    linux驱动代码如下:

    //-------------------------------------------------------------

    //        NAME:xiaoyang_beep.c

    //        By Xiaoyang Yi@2011.4.20

    //-------------------------------------------------------------

    #include <linux/module.h>

    #include <linux/kernel.h>

    #include <linux/fs.h>

    #include <linux/init.h>

    #include <linux/device.h>

    #include <linux/miscdevice.h>

    #include <linux/delay.h>

    #include <linux/mm.h>

    #include <asm/irq.h>

    #include <plat/regs-timer.h>

    #include <mach/regs-gpio.h>

    #include <mach/map.h>

    #include <mach/regs-irq.h>

    #include <asm/io.h>

    #include <mach/hardware.h>

    #include <asm/uaccess.h>

    #include <asm/system.h>

    #define DEVICE_NAME                "xiaoyang-beep"                /* 加载模式后,执行"cat /proc/devices"命令看到的设备名称 */

    #define BEEP_MAJOR                233                                                /* 主设备号 */

    //-------------------------------------------------------------

    // 应用程序对设备文件/dev/xiaoyang-Beep执行ioclt(...)时,

    // 就会调用xiaoyang_beep_ioctl函数

    // cmd:<0不使用pwm

    //         >0设置down_counter中的数值(鸣叫频率)

    //-------------------------------------------------------------

    static int xiaoyang_beep_ioctl(struct inode *inode,        struct file *file,        unsigned int cmd,unsigned long arg)

    {

    unsigned long temp;

    if(cmd <= 0)

    {

    //set as gpb0,output

    temp = __raw_readl(S3C2410_GPBCON);        //GPBCON IO Control

    temp &= ~3;        //just select the GPBCON[1:0]bits for gpb0

    temp |= 1;        //set gpb[0] as output

    __raw_writel(temp, S3C2410_GPBCON);

    //set gpbdata[0](beep value) as 0

    temp = __raw_readl(S3C2410_GPBDAT);        //GPBDAT

    temp &= ~1;

    __raw_writel(temp, S3C2410_GPBDAT);

    }

    else

    {

    //set as TOUT0

    temp = __raw_readl(S3C2410_GPBCON);        //GPBCON

    temp &= ~3;

    temp |= 2;                //using TOUT0(pwm timer)

    __raw_writel(temp, S3C2410_GPBCON);

    //using pwm timer TOUT0

    temp = __raw_readl(S3C2410_TCFG0);        //TCFG0

    temp &= ~0xff;        //select Timer0

    temp |= 15;                //FCLK/(15+1)/(devided value),prescaler value!

    __raw_writel(temp, S3C2410_TCFG0);

    temp = __raw_readl(S3C2410_TCFG1);        //TCFG1

    temp &= ~0xf;        //select MUX0

    temp |= 1;                //1/4 diveder

    __raw_writel(temp, S3C2410_TCFG1);

    //--------------------------------------------------------------

    //        now set TCNTB and TCMPB,generate 1:1 diveded timer

    //         TCNTB寄存器设置装载到递减计数器中的初始值,并依次递减

    //         TCMPB寄存器则用于装载同递减计数器进行比较的值

    //        所以当设置TCMPB TCNTB/2即表示进行1:1分时

    //--------------------------------------------------------------

    temp = (50000000/64)/cmd;//set timer count buffer rgister ,50MHZ/(16*4*cmd)

    __raw_writel(temp, S3C2410_TCNTB(0));

    temp >>= 1;

    __raw_writel(temp, S3C2410_TCMPB(0));

    //setup timer0

    temp = __raw_readl(S3C2410_TCON);        //TCON

    temp &= ~0x1f;

    temp |= 0xb;//disable deadzone,auto-reload,interval-off,update TCNTB and TCMPB,start timer0

    __raw_writel(temp, S3C2410_TCON);

    temp &= ~2;//set bit[0] as 0,clear manual update-setting bit

    __raw_writel(temp, S3C2410_TCON);        

    }

    return 0;

    }

    //--------------------------------------------------------------

    //        这个结构是字符设备驱动程序的核心

    //        当应用程序操作设备文件时所调用的openreadwriteioctl等函数,

    //        最终会调用这个结构中指定的对应函数

    //--------------------------------------------------------------

    static struct file_operations xiaoyang_beep_fops = {

    .owner        =        THIS_MODULE,

    .ioctl        =        xiaoyang_beep_ioctl,

    };

    static char __initdata banner[] = "TQ2440 Beep, 2010-xiaoyang yi\n";

    static struct class *beep_class;

    //--------------------------------------------------------------

    //        执行"insmod xiaoyang_beep.ko"命令时就会调用这个函数

    //--------------------------------------------------------------

    static int __init xiaoyang_beep_init(void)

    {

    int ret;

    printk(banner);

     

    //--------------------------------------------------------------

    // 注册字符设备驱动程序

    // 参数为主设备号、设备名字、file_operations结构;

    // 这样,主设备号就和具体的file_operations结构联系起来了,

    // 操作主设备为BEEP_MAJOR的设备文件时,就会调用xiaoyang_beep_fops中的相关成员函数

    // BEEP_MAJOR可以设为0,表示由内核自动分配主设备号

    //--------------------------------------------------------------

    ret = register_chrdev(BEEP_MAJOR, DEVICE_NAME, &xiaoyang_beep_fops);//分配设备号

    if (ret < 0) {

    printk(DEVICE_NAME " can't register major number\n");

    return ret;

    }

    //注册一个类,使mdev可以在"/dev/"目录下面建立设备节点

    beep_class = class_create(THIS_MODULE, DEVICE_NAME);

    if(IS_ERR(beep_class))

    {

    printk("Err: failed in xiaoyang-Beep class. \n");

    return -1;

    }

    //创建一个设备节点,节点名为DEVICE_NAME

    device_create(beep_class, NULL, MKDEV(BEEP_MAJOR, 0), NULL, DEVICE_NAME);

    printk(DEVICE_NAME " initialized\n");

    return 0;

    }

    //--------------------------------------------------------------

    //        执行"rmmod xiaoyang_beep.ko"命令时就会调用这个函数

    //--------------------------------------------------------------

    static void __exit xiaoyang_beep_exit(void)

    {

    /* 卸载驱动程序 */

    unregister_chrdev(BEEP_MAJOR, DEVICE_NAME);

    device_destroy(beep_class, MKDEV(BEEP_MAJOR, 0));                //删掉设备节点

    class_destroy(beep_class);                                                                        //注销类

    }

    //指定驱动程序的初始化函数和卸载函数

    module_init(xiaoyang_beep_init);

    module_exit(xiaoyang_beep_exit);

    /* 描述驱动程序的一些信息,不是必须的 */

    MODULE_AUTHOR("software-hit");                                //驱动作者

    MODULE_DESCRIPTION("TQ2440 Beep Driver");        //描述信息

    MODULE_LICENSE("GPL");                                                //遵循的协议

    将其加入内核,配置drivers/char/KconfigMakefile将根目录下的config_EmbededSky_W35拷贝到.config然后运行:Make menuconfig进行配置。

    可将beep作为模块或者必选(作为内核的一部分)加入内核。则运行

    Make oldconfig

    Make

    生成zImage烧写到开发板后就有/dev/beep设备。

    若作为模块作编的话仅仅运行Make SUBDIR=drivers/char/ modules即可编译出EmbededSky_Beep.ko,将其拷贝到开发板insmod加载。

    其中在编译过程出现了以下问题:

    drivers/char/EmbedSky_beep.c: In function 'xiaoyang_beep_init':

    drivers/char/EmbedSky_beep.c:134: error: implicit declaration of function 'class_device_create'

    drivers/char/EmbedSky_beep.c: In function 'xiaoyang_beep_exit':

    drivers/char/EmbedSky_beep.c:148: error: implicit declaration of function 'class_device_destroy'

    make[2]: *** [drivers/char/EmbedSky_beep.o] 错误 1

    make[1]: *** [drivers/char] 错误 2

    make: *** [drivers] 错误 2

    [xiaoyang@localhost linux-2.6.3

    解决办法:

    这主要是版本间的接口变化引起的。

    Linux2.6中,针对上面的这个问题不同的版本有些修改,使用前要先查看下/.../include/linux/device.h里的函数声明,如我用的是Linux2.6.30,里面就没有class_device_create函数,而直接使用device_create就可以了,而在之前的版本如Linux2.6.15,里面就要用class_device_create函数

    所以不要使用class_device_create 而改用device_create即可,另外一个接口也是。

    编写应用程序:

    //----------------------------------------------------------

    // xiaoyang@2011.4.21

    // beep driver/module test

    //----------------------------------------------------------

    #include <stdio.h>

    #include <stdlib.h>

    #include <unistd.h>

    #include <fcntl.h>

    #include <sys/ioctl.h>

    #include <sys/stat.h>

    int main()

    {

    int fd;

    int i = 0;

    int m_pwm_val;

     

    fd = open("/dev/xiaoyang-beep",O_RDWR);

    if(fd < 0){

    perror("open device xiaoyang-beep error!");

    exit(1);

    }

     

    for(i = 0; i < 1000; i++){

    scanf("%d",&m_pwm_val);

    printf("your pwm_val is %d\n",m_pwm_val);

    ioctl(fd,m_pwm_val,4);//最后这个参数没有什么用,ioctllinux接口采用统一接口进行设备的操作

    if(m_pwm_val == 0){

    break;

    }

    }

     

    close(fd);

    return 0;        

    }

    makefie如下:

    CROSS_COMPILE=arm-linux-

    CC=$(CROSS_COMPILE)gcc

    obj-m := hello.o

    all:beep

    beep:beep.c

    $(CC) -o beep beep.c

    $(CROSS_COMPILE)strip beep

    clean:

    rm -rf *.o beep

    实验结果截图:

    输入数字后beep按照不同的频率发声,太刺耳了。

  • 相关阅读:
    福建90后大学生陈贺的果园梦
    95后女孩穿旗袍走红,老手艺在淘宝迎来不一样的改变
    动态创建完整功能的图片元素
    jquery 插入dom
    它会选择相对于span的所有div父元素
    表单过滤器之checkbox
    匹配所有的最后一个子元素
    docker 常用指令
    splice操作
    当《连线》说“第一台值得买的智能电视终于出现”时,你信不信?
  • 原文地址:https://www.cnblogs.com/yixiaoyang/p/2024222.html
Copyright © 2020-2023  润新知