zoukankan      html  css  js  c++  java
  • 驱动实现led,pwm和中断基础知识

    2015.4.8
    星期三 晴天

    今天老师讲的内容是内核编写led和pwm驱动,实现花样灯和放歌的功能。理解应用和驱动的对接,最后自己实现了在放歌的时候根据
    歌曲的节奏亮灭一个小灯,应为两个独立的驱动都已经写好,想要组合其实很简单,只要在主调函数里面打开两个驱动的设备节点,
    分别进行操作并有机的组合在一起就行了。最后老师复习了中断的一些基础知识,总结一下:

    异常处理:
    当异常发送时:
    nand flash 拷贝到sdram中运行,这是和nor flash 的区别之一

    1.拷贝cpsr到spsr
    2.设置适当的cpsr位
    2.1 改变处理器状态进入arm态
    2.2 改变处理器模式进入相应的异常模式
    2.3 设置中断禁止位禁止相应中断
    3.保存返回地址pc+4到lr
    4.设置pc相应的异常向量

    返回时,异常处理需要:
    1.从SPSR恢复到CPSR
    2.从LR恢复到PC
    3.Noto: 这些操作只能在ARM态执行

    中断的控制步骤:
    1.要想使用中断:首先需要打开总中断
    2.设置中断控制器使能(vic0intenable)
    3.设置外设屏蔽寄存器:mask
    4.相应管脚设成可以触发中断
    5.设置中断激发方式:上升沿,下降沿......
    6.中断处理完成后需要清pending, pending 标识这个外设曾经发生过中断,中断触发后自动置1

    笼统的说就是:
    总中断
    mask
    enable
    清pending 不然会一直中断

    下面是pwm控制的内核驱动程序,可参考学习:
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/string.h>
    #include <asm/uaccess.h> /*copy_to_user copy_from_user*/
    #include <asm/io.h> /*ioremap*/

    #define NUM_MINORS 1
    #define MINOR_NR 0
    #define DEV_NAME "s5pc100_pwm_char"
    #define KBUF_SIZE 50

    #define CMD_ON _IO('k', 0)
    #define CMD_OFF _IO('k', 1)

    #define GPDCON 0xe0300080
    #define TIMER_BASE 0xea000000 这是偏移量的基址,下面的寄存器地址加上这个基址构成真正的地址
    #define TCFG0 0x0
    #define TCFG1 0x4
    #define TCON 0x8
    #define TCNTB1 0x18
    #define TCMPB1 0x1c

    #define magic_number 'k'
    #define BEEP_ON _IO(magic_number,0)
    #define BEEP_OFF _IO(magic_number,1)
    #define SET_FRE _IO(magic_number,2)

    static void *vgpdcon;
    static void *vtimer_base;

    static struct cdev s5pc100_pwm_cdev;
    static int s5pc100_pwm_major = 250; /*Documentation/devices.txt*/
    static dev_t s5pc100_pwm_devno;

    MODULE_LICENSE("GPL");


    void pwm_init(void)
    {
    /*init pin func as pwm tout1*/
    writel((readl(vgpdcon) & ~(0xf << 4)) | (0x2 << 4), vgpdcon); 先读后写,避免改变寄存器的其他位,值得学习
    /*init first prescaler: fout = pclk/(value + 1) */
    writel(65, vtimer_base + TCFG0);
    /*init mux*/
    writel(readl(vtimer_base + TCFG1) & ~(0xf << 4), vtimer_base + TCFG1);
    /*init TCMPB1 TCNTB1*/
    writel(1000, vtimer_base + TCNTB1);
    writel(500, vtimer_base + TCMPB1);
    /*manual update*/
    writel(readl(vtimer_base + TCON) & ~(0xf << 8) | (1 << 9),
    vtimer_base + TCON);
    }

    void timer_start(void)
    {
    /*start timer and enable auto reload*/
    writel(readl(vtimer_base + TCON) & ~(0xf << 8) | (1 << 11) | (1 << 8),
    vtimer_base + TCON);
    }

    void timer_stop(void)
    {
    writel(readl(vtimer_base + TCON) & ~(1 << 8),
    vtimer_base + TCON);
    }

    void set_tcntb1(unsigned int value)
    {
    writel(value, vtimer_base + TCNTB1);
    }

    void set_tcmpb1(unsigned int value)
    {
    writel(value, vtimer_base + TCMPB1);
    }

    static int s5pc100_pwm_open(struct inode *nodp, struct file *filp)
    {
    pwm_init(); 在打开设备的时候初始化定时器的设置,方便应用的操作
    printk(KERN_INFO "s5pc100_pwm open! ");
    return 0;
    }

    static int s5pc100_pwm_release(struct inode *nodp, struct file *filp)
    {
    timer_stop(); 在退出程序的时候,停止定时器
    printk(KERN_INFO "s5pc100_pwm release! ");
    return 0;
    }


    static long
    s5pc100_pwm_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
    switch (cmd) {
    case BEEP_ON:
    printk(KERN_INFO "cmd = %d arg = %lu ", cmd, arg);
    timer_start();
    break;
    case BEEP_OFF:
    printk(KERN_INFO "cmd = %d arg = %lu ", cmd, arg);
    timer_stop();
    break;
    case SET_FRE:
    printk(KERN_INFO "cmd = %d arg = %lu ", cmd, arg);
    set_tcntb1(1000000/arg);
    set_tcmpb1(1000000/(arg + arg));
    default:
    printk(KERN_INFO "no such command! ");
    break;
    }
    return 0;
    }

    struct file_operations s5pc100_pwm_ops = {
    .open = s5pc100_pwm_open,
    .release = s5pc100_pwm_release,
    .unlocked_ioctl = s5pc100_pwm_unlocked_ioctl,
    };

    static int __init s5pc100_pwm_init(void)
    {
    int ret;

    /*construct cdev number*/
    s5pc100_pwm_devno = MKDEV(s5pc100_pwm_major, MINOR_NR); //s5pc100_pwm_major << 20 | s5pc100_pwm_minor;
    ret = register_chrdev_region(s5pc100_pwm_devno, NUM_MINORS, DEV_NAME);

    /*register char dev number*/
    if (ret) {
    printk(KERN_WARNING "register_chrdev_region failed! ");
    ret = alloc_chrdev_region(&s5pc100_pwm_devno, MINOR_NR, NUM_MINORS, DEV_NAME);
    if (ret) {
    printk(KERN_WARNING "alloc chrdev_region failed! ");
    goto err_0;
    }
    }

    /*register char device*/
    /*cdev initialize*/
    cdev_init(&s5pc100_pwm_cdev, &s5pc100_pwm_ops);

    /*add to kernel*/
    ret = cdev_add(&s5pc100_pwm_cdev, s5pc100_pwm_devno, NUM_MINORS);
    if (ret) {
    printk(KERN_WARNING "cdev add failed! ");
    goto err_1;
    }

    /*physical address ---> virtual address*/
    vgpdcon = ioremap(GPDCON, 4); 将物理地址转化成虚拟地址,每个地址转换为四字节
    vtimer_base = ioremap(TIMER_BASE, 0x20); ***这里需要注意一下,因为这里不是针对一个地址转化,这里表示一组地址,所以转换地址需要够大才行***

    printk(KERN_INFO "s5pc100_pwm_init ! ");
    return 0;

    /*error management*/
    err_1:
    unregister_chrdev_region(s5pc100_pwm_devno, NUM_MINORS);
    err_0:
    return ret;
    }


    static void __exit s5pc100_pwm_exit(void)
    {
    iounmap(vgpdcon);
    iounmap(vtimer_base);
    cdev_del(&s5pc100_pwm_cdev);
    unregister_chrdev_region(s5pc100_pwm_devno, NUM_MINORS);
    printk("s5pc100_pwm_exit ! ");
    }

    module_init(s5pc100_pwm_init);
    module_exit(s5pc100_pwm_exit);

    MODULE_AUTHOR("minj@farsight.com.cn");
    MODULE_DESCRIPTION("just for test!");

    led小灯的驱动实现程序,很简单,但是很有参考价值,这些都是基础:

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/string.h>
    #include <asm/uaccess.h> /*copy_to_user copy_from_user*/
    #include <asm/io.h> /*ioremap*/

    #define NUM_MINORS 1
    #define MINOR_NR 0
    #define DEV_NAME "s5pc100_led_char"
    #define KBUF_SIZE 50

    #define CMD_ON _IO('k', 0)
    #define CMD_OFF _IO('k', 1)

    #define GPG3CON 0xe03001c0
    #define GPG3DAT 0xe03001c4
    static int *vgpg3con;
    static int *vgpg3dat;

    static struct cdev s5pc100_led_cdev;
    static int s5pc100_led_major = 250; /*Documentation/devices.txt*/
    static dev_t s5pc100_led_devno;
    static char kbuf[KBUF_SIZE];

    MODULE_LICENSE("GPL");

    /**
    * led_init: init led pins
    */
    void led_init(void)
    {
    writel((readl(vgpg3con) & ~(0xffff)) | 0x1111, vgpg3con);
    }

    void led_exit(void)
    {
    writel(readl(vgpg3dat) & ~(0xf), vgpg3con);
    }

    static int s5pc100_led_open(struct inode *nodp, struct file *filp)
    {
    led_init();
    printk(KERN_INFO "s5pc100_led open! ");
    return 0;
    }
    static int s5pc100_led_release(struct inode *nodp, struct file *filp)
    {
    led_exit();
    printk(KERN_INFO "s5pc100_led release! ");
    return 0;
    }


    static long
    s5pc100_led_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
    switch (cmd) {
    case CMD_ON: 这个参数相当于应用的命令
    printk(KERN_INFO "cmd = %d arg = %lu ", cmd, arg);
    writel(readl(vgpg3dat) | (1 << arg), vgpg3dat); 注意参数的作用和传递方式
    break;
    case CMD_OFF:
    printk(KERN_INFO "cmd = %d arg = %lu ", cmd, arg);
    writel(readl(vgpg3dat) & ~(1 << arg), vgpg3dat);
    break;
    default:
    printk(KERN_INFO "no such command! ");
    break;
    }
    return 0;
    }

    struct file_operations s5pc100_led_ops = {
    .open = s5pc100_led_open,
    .release = s5pc100_led_release,
    .unlocked_ioctl = s5pc100_led_unlocked_ioctl,
    };

    static int __init s5pc100_led_init(void)
    {
    int ret;

    /*construct cdev number*/
    s5pc100_led_devno = MKDEV(s5pc100_led_major, MINOR_NR); //s5pc100_led_major << 20 | s5pc100_led_minor;
    ret = register_chrdev_region(s5pc100_led_devno, NUM_MINORS, DEV_NAME);

    /*register char dev number*/
    if (ret) {
    printk(KERN_WARNING "register_chrdev_region failed! ");
    ret = alloc_chrdev_region(&s5pc100_led_devno, MINOR_NR, NUM_MINORS, DEV_NAME);
    if (ret) {
    printk(KERN_WARNING "alloc chrdev_region failed! ");
    goto err_0;
    }
    }

    /*register char device*/
    /*cdev initialize*/
    cdev_init(&s5pc100_led_cdev, &s5pc100_led_ops);

    /*add to kernel*/
    ret = cdev_add(&s5pc100_led_cdev, s5pc100_led_devno, NUM_MINORS);
    if (ret) {
    printk(KERN_WARNING "cdev add failed! ");
    goto err_1;
    }

    /*physical address ---> virtual address*/
    vgpg3con = ioremap(GPG3CON, 4);
    vgpg3dat = ioremap(GPG3DAT, 4);

    printk(KERN_INFO "s5pc100_led_init ! ");
    return 0;

    /*error management*/
    err_1:
    unregister_chrdev_region(s5pc100_led_devno, NUM_MINORS);
    err_0:
    return ret;
    }


    static void __exit s5pc100_led_exit(void)
    {
    iounmap(vgpg3con);
    iounmap(vgpg3dat);
    cdev_del(&s5pc100_led_cdev);
    unregister_chrdev_region(s5pc100_led_devno, NUM_MINORS);
    printk("s5pc100_led_exit ! ");
    }

    module_init(s5pc100_led_init);
    module_exit(s5pc100_led_exit);

    MODULE_AUTHOR("minj@farsight.com.cn");
    MODULE_DESCRIPTION("just for test!");

  • 相关阅读:
    poj2392 Space Elevator(多重背包问题)
    poj1703 Find them, Catch them(并查集的应用)
    HDU 1867 A + B for you again(KMP算法的应用)
    HDU 1358 Period(kmp简单解决)
    nyoj 460 项链 (区间dp)
    Python内置函数(9)——callable--转载
    Python的hasattr() getattr() setattr() 函数使用方法详解--转载
    python assert 断言详细用法格式
    sam文件格式
    Linux中重定向--转载
  • 原文地址:https://www.cnblogs.com/cnlg/p/4403959.html
Copyright © 2011-2022 走看看