打印
[嵌入式linux]

开始学linux驱动,三个月熟悉,立帖为证!每日汇报进展

[复制链接]
楼主: tiger84
手机看帖
扫描二维码
随时随地手机跟帖
21
牛牛特工| | 2009-12-21 22:01 | 只看该作者 回帖奖励 |倒序浏览
先搞ubuntu交叉编译环境了 哎  
之前用的win下面的 缺好多工具 无法编译uboot 郁闷的

使用特权

评论回复
22
牛牛特工| | 2009-12-22 00:00 | 只看该作者
俄 我现在暂时就先用Ubuntu8.04了 省的还要下光盘~~
工具链arm-linux-gcc-3.4.5-glibc-2.3.6
按照这个网页建立了一个目标板定义
http://blog.chinaunix.net/u/26710/showart_402695.html
不过make之后出现点问题
make[1]: *** No rule to make target `/home/laoniu/uboot/u-boot-1.3.0-rc2/build/board/ourspxa270d/.depend', needed by `/home/laoniu/uboot/u-boot-1.3.0-rc2/build/board/ourspxa270d/libourspxa270d.a'.  Stop.
得研究下到底啥原因~~

使用特权

评论回复
23
tiger84|  楼主 | 2009-12-22 18:04 | 只看该作者
今天折腾了大半天,才把按键驱动折腾出来。
主要出在2个地方:
(1)request_irq,总是申请不到,后来问了技术支持,才知道at91sam9260的中断号是:
        pa + 0x20
        pb + 0x40
        pc + 0x60
       在这上面浪费了不少时间;
(2)定时器还没有初始化就启动了,导致加载模块时出现无效指针。
     参考别人的例子,可能别人的例子中在其他地方就已经初始化了吧。

肚子疼了好几天了,晚上去点滴。

使用特权

评论回复
24
牛牛特工| | 2009-12-22 21:06 | 只看该作者
本帖最后由 牛牛特工 于 2009-12-22 22:29 编辑

继续爬行~~
貌似连linux的基本命令都没掌握~~
在使用中慢慢熟悉把 哈哈
今天没看出啥头绪 不过可以编译通过了
昨天改makefile时,错了一个字母

使用特权

评论回复
25
tiger84|  楼主 | 2009-12-22 21:51 | 只看该作者
看病回来了,呵呵。
身体是革命的本钱。生了下病,花钱不说,还要浪费很多时间和精力。
所以,病好之后,**锻炼身体。

使用特权

评论回复
26
tiger84|  楼主 | 2009-12-22 21:54 | 只看该作者
icecut兄弟提醒的很对。我是应该把这些都记录的详细一点。
先把led驱动发下吧。基本上都是照葫芦画瓢弄成的,各位别拍砖,仅给用at91sam9260的兄弟参考下。

/*******************************************/
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/atmel_pdc.h>
#include <asm/io.h>
#include <linux/semaphore.h>
#include <linux/kernel.h>
#include <linux/cdev.h>

#include <linux/types.h>
#include <linux/fs.h>


#include <mach/board.h>
#include <mach/gpio.h>


/*******************************************/
void led_on(unsigned int led_num)
{   
     at91_set_gpio_output(AT91_PIN_PA5 + led_num,0);
}

void led_off(unsigned int led_num)
{   
     at91_set_gpio_output(AT91_PIN_PA5 + led_num,1);
}

void led_init(void)
{
    at91_set_GPIO_periph(AT91_PIN_PA5,0);
    at91_set_GPIO_periph(AT91_PIN_PA6,0);
    at91_set_GPIO_periph(AT91_PIN_PA7,0);
    at91_set_GPIO_periph(AT91_PIN_PA8,0);
   
    at91_set_A_periph(AT91_PIN_PA5,0);
    at91_set_A_periph(AT91_PIN_PA6,0);
    at91_set_A_periph(AT91_PIN_PA7,0);
    at91_set_A_periph(AT91_PIN_PA8,0);
   
}

struct light_dev
{
    struct cdev cdev;
    unsigned char value;
};

struct light_dev *light_devp;
int light_major = 252;

MODULE_AUTHOR("launch");
MODULE_LICENSE("Dual BSD/GPL");

// 打开和关闭函数
int light_open(struct inode *inode,struct file *filp)
{
    struct light_dev *dev;

    // 获得设备结构体指针
    dev = container_of(inode->i_cdev,struct light_dev,cdev);
    // 让设备结构体作为设备的私有信息
    filp->private_data = dev;

    return 0;
}

int light_release(struct inode *inode,struct file *filp)
{
    return 0;   
}


// ioctl
int light_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,
unsigned long arg)
{
    struct light_dev *dev = filp->private_data;

    switch(cmd)
    {
        case 0:
            dev->value = 0;
            led_off(arg);         
        break;

        case 1:
            dev->value = 1;
            led_on(arg);
        break;

        default:

            return -ENOTTY;
        // break;
    }

    return 0;
}     

struct file_operations light_fops =
{
    .owner = THIS_MODULE,
    .ioctl = light_ioctl,
    .open  = light_open,
    .release = light_release,
};

// 设置字符设备cdev结构体
static void light_setup_cdev(struct light_dev *dev,int index)
{
    int err,devno = MKDEV(light_major,index);

    cdev_init(&dev->cdev,&light_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &light_fops;

    err = cdev_add(&dev->cdev,devno,1);

    if(err)
    {
        printk(KERN_NOTICE "Error %d adding LED%d",err,index);
    }
}

// 模块加载函数
int light_init(void)
{
    int result;

    dev_t dev = MKDEV(light_major,0);

    // 申请字符设备号
    if(light_major)
    {
        
        result = register_chrdev_region(dev,1,"leds");
    }

    if(result < 0)
    {
        return result;
    }

    // 分配设备结构体的内存
    light_devp = kmalloc(sizeof(struct light_dev),GFP_KERNEL);
    if(!light_devp)
    {
        result = - ENOMEM;
        goto fail_malloc;
    }

    memset(light_devp,0,sizeof(struct light_dev));
    light_setup_cdev(light_devp,0);
    led_init();

    return 0;

    fail_malloc:unregister_chrdev_region(dev,light_devp);
    return result;
   
}

// 模块卸载函数
void light_cleanup(void)
{
    cdev_del(&light_devp->cdev);   // 删除字符设备结构体
    kfree(light_devp);
    unregister_chrdev_region(MKDEV(light_major,0),1);  // 删除字符设备
}

module_init(light_init);
module_exit(light_cleanup);

使用特权

评论回复
27
tiger84|  楼主 | 2009-12-22 21:55 | 只看该作者
本帖最后由 tiger84 于 2009-12-22 21:56 编辑

写LED驱动时,主要就是把物理地址和虚拟地址的关系没有对应好,把源代码看了下后,清晰了一些。
以下是Makefile文件

ARCH=arm
CROSS_COMPILE=arm-none-linux-gnueabi-
obj-m := leds.o
KDIR := /home/zhh/linux-2.6.27/
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

使用特权

评论回复
28
tiger84|  楼主 | 2009-12-22 21:59 | 只看该作者
下面是按键的驱动
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/atmel_pdc.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/input.h>
#include <linux/errno.h>
#include <linux/irq.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/list.h>


#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
#include <mach/at91_pio.h>
#define DEVICE_NAME "button"

#define KEY_TIMER_DELAY1    (HZ/50)             //按键按下去抖延时20毫秒        
#define KEY_TIMER_DELAY2    (HZ/10)             //按键抬起去抖延时100毫秒

#define KEYSTATUS_DOWN              0           //按键按下                    
#define KEYSTATUS_UP                1           //按键抬起               
#define KEYSTATUS_DOWNX             2           //按键不确定  
#define KEY_NUM                     4           // 4个按键

#define DP_MAJOR 0//主设备号
#define DP_MINOR 0  //次设备号
unsigned int button_major = DP_MAJOR;

// 按键驱动的设备结构体、定时器
#define MAX_KEY_BUF     16   // 按键缓存区大小
typedef unsigned char KEY_RET;
// 设备结构体
typedef struct
{
    unsigned int keyStatus[KEY_NUM];    // 4个按键的按键状态
    KEY_RET buf[MAX_KEY_BUF];
    unsigned int head,tail;             // 按键缓存区头和尾
    wait_queue_head_t wq;               // 等待队列
    struct cdev cdev;                   // cdev 结构体
}KEY_DEV;
KEY_DEV key_dev,*key_devp;


#define BUF_HEAD (key_dev.buf[key_dev.head]) //缓冲头
#define BUF_TAIL (key_dev.buf[key_dev.tail]) //缓冲尾
#define INCBUF(x,mod) ((++(x))&((mod)-1))


static struct timer_list key_timer[KEY_NUM]; // 4个按键去抖定时器

// 按键硬件资源、键值信息结构体
static struct key_info
{
    int irq_no;                         // 中断号
    int irq_type;                       // 中断类型
    unsigned int gpio_port;             // GPIO端口
    int key_no;                         // 键值
   
   
}key_info_tab[4]=
// 按键所使用的CPU资源
{
   
    {
        AT91_PIN_PB0,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB0,1
    },
    {
        AT91_PIN_PB1,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB1,2
    },
    {
        AT91_PIN_PB2,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB2,3
    },
    {
        AT91_PIN_PB3,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB3,4
    },
};

// 初始化
static void at91sam9260_key_io_init(void)
{
    at91_set_gpio_input(AT91_PIN_PB0, 1);       
        at91_set_deglitch(AT91_PIN_PB0, 1);
       
        at91_set_gpio_input(AT91_PIN_PB1, 1);       
        at91_set_deglitch(AT91_PIN_PB1, 1);

        at91_set_gpio_input(AT91_PIN_PB2, 1);       
        at91_set_deglitch(AT91_PIN_PB2, 1);
       
        at91_set_gpio_input(AT91_PIN_PB3, 1);       
        at91_set_deglitch(AT91_PIN_PB3, 1);
}

/* 记录键值并唤醒等待队列 */
static void keyEvent(unsigned int key)
{
    BUF_HEAD = key_info_tab[key].key_no;                  // 记录键值
    key_dev.head = INCBUF(key_dev.head, MAX_KEY_BUF); // 调整缓冲区头指针
    wake_up_interruptible(&(key_dev.wq));                // 唤醒等待队列
}


// 按键设备的中断处理
// 键被按下后,将发生中断,在中断处理程序中,应该关闭中断进入查询模式,延迟20ms以实现去抖动
// 这个中断处理过程只包含顶半部,无底半部
static irqreturn_t at91sam9260_enit_key(int irq,void *dev_id,struct pt_regs *reg)
{
    int key = (int)dev_id;
    int found = 0;
    int i;
  
    for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)   // 查找产生中断的按键
    {
        if (key_info_tab[i].irq_no == irq)
        {
            found = 1;
            break;
        }
    }
    if (!found)                                     // 没找到
    {
        printk(KERN_NOTICE"bad irq %d in button\n", irq);
        return IRQ_NONE;   //错误的中断
    }
   
    disable_irq(key_info_tab[key].irq_no);          // 找到,关闭对应中断   

    key_dev.keyStatus[key] = KEYSTATUS_DOWNX;  // 不确定是否为按下
    key_timer[key].expires = jiffies + KEY_TIMER_DELAY1;// 延迟
    add_timer(&key_timer[key]);  // 启动定时器  

    return IRQ_HANDLED;   //正确的中断
}
// 定时器处理函数
static void key_timer_handler(unsigned long data)
{
    int key = data;
    //获取当前按键引脚上的电平值来判断按键是按下还是抬起
    int up = at91_get_gpio_value(key_info_tab[key].gpio_port);
    if (!up)
    {
        if (key_dev.keyStatus[key] == KEYSTATUS_DOWNX)
        //从中断进入
        {
            key_dev.keyStatus[key] = KEYSTATUS_DOWN;
            key_timer[key].expires = jiffies + KEY_TIMER_DELAY2; //延迟           
            keyEvent(key);  //记录键值,唤醒等待队列
            add_timer(&key_timer[key]);
        }
        else
        {
            key_timer[key].expires = jiffies + KEY_TIMER_DELAY2; //延迟
            add_timer(&key_timer[key]);
        }
    }
    else       //键已抬起
    {
        key_dev.keyStatus[key] = KEYSTATUS_UP;
        enable_irq(key_info_tab[key].irq_no);
    }
}


// 按键设备驱动的中断申请函数
// 申请系统中断,中断方式为下降沿触发
static int request_irqs(void)
{
    struct key_info *k;
    int i;
    int ret;
    for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++)
  //  for(i = 0; i < 1; i++)
    {
        k = key_info_tab + i;
        //设置4个IO口为中断下降沿触发方式
         set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type);      // 设置中断类型
            
        //申请中断(类型为快速中断,中断服务时屏蔽所有外部中断?)将按键序列号作为参数传入中断服务程序        
      //  if(request_irq(k->irq_no,at91sam9260_enit_key,0,DEVICE_NAME,(void *)i))
        ret = request_irq(k->irq_no,at91sam9260_enit_key,0,DEVICE_NAME,(void *)i);        
        if(ret)
        {
            printk(KERN_NOTICE "buttons:ret is %d\r\n",ret);
            return -1;
        }
    }

    return 0;
   
}
// 释放中断
static void free_irqs(void)
{
    struct key_info *k;
    int i;

    for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++)
    {
        k = key_info_tab + i;
        free_irq(k->irq_no,(void *)i);
    }
   
}




// 按键设备驱动的打开、释放函数
static int at91sam9260_key_open(struct inode *inode, struct file *filp)
{
    key_dev.head = key_dev.tail = 0; //清空按键动作缓冲区
  //  keyEvent = keyEvent_raw; //函数指针指向按键处理函数keyEvent_raw
   
    return 0;
}

static int at91sam9260_key_release(struct inode *inode, struct file *filp)
{
  //  keyEvent = keyEvent_dummy; //函数指针指向空函数
   
    return 0;
}


// 按键设备驱动读函数
static ssize_t at91sam9260_key_read(struct file *filp, char __user *buf, ssize_t count,
    loff_t*ppos)
{
    unsigned int key_ret;
    unsigned long flags;
retry:
    if (key_dev.head != key_dev.tail)                         // 缓冲区有数据?
    {
        local_irq_save(flags);                                      // 进入临界区 ,关中断      
        key_ret = BUF_TAIL;                                         // 读出键值
        key_dev.tail = INCBUF(key_dev.tail, MAX_KEY_BUF);     // 调整缓冲区尾指针
        local_irq_restore(flags);                                   // 退出临界区,开中断        
        if(copy_to_user(buf, (void *)&key_ret, sizeof(unsigned int)))       // 拷贝到用户空间   
        {
            return -EFAULT;
        }
        else
        {
            return sizeof(unsigned int);
        }
        
    }
    else   // 缓冲区没数据
    {
        if (filp->f_flags & O_NONBLOCK)                             // 若采用非阻塞方式读取则返回错误
        {
            return -EAGAIN;
        }
        interruptible_sleep_on(&(key_dev.wq));                   // 使进程睡眠
        if (signal_pending(current))                               //在这里等中断
        {   // 如果是信号中断
            return -ERESTARTSYS;
        }
        goto retry;
    }
   
    return sizeof(unsigned int);
}

使用特权

评论回复
29
tiger84|  楼主 | 2009-12-22 21:59 | 只看该作者
// 接着上面的
// 按键设备驱动文件操作结构体,主要实现打开、释放和读函数
static struct file_operations at91sam9260_key_fops =
{
    .owner = THIS_MODULE,
    .open = at91sam9260_key_open,            // 启动设备
    .release = at91sam9260_key_release,      // 关闭设备
    .read = at91sam9260_key_read,            // 读取按键的键值
};


// 模块加载函数包括:设备号申请、cdev的添加,另:申请中断、初始化定时器和队列;
// 模块卸载函数恰好相反
static int __init at91sam9260_key_init(void)
{
    int i;
    int result;  
   
    // 申请设备号      
    dev_t dev = MKDEV(button_major, DP_MINOR);  

     /* 申请设备号 */
    if (button_major)
        result = register_chrdev_region(dev, 1, DEVICE_NAME);
    else    /* 动态申请设备号 */
    {
        result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
        button_major = MAJOR(dev);
        printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, button_major);
    }
  

    // 分配设备结构体的内存
    key_devp = kmalloc(sizeof(key_dev),GFP_KERNEL);
    if(!key_devp)
    {
        result = - ENOMEM;
        goto fail_malloc;
    }

    // 添加cdev
    memset(key_devp,0,sizeof(key_dev));

    cdev_init(&key_dev.cdev, &at91sam9260_key_fops);
    if(cdev_add(&key_dev.cdev, dev, 1)){
        printk(KERN_ALERT"Add char dev error!\n");
    }
  

    key_dev.head = key_dev.tail = 0;             // 初始化结构体
    for(i = 0; i < KEY_NUM; i++)
    {
        key_dev.keyStatus[i] = KEYSTATUS_UP;
    }
    init_waitqueue_head(&(key_dev.wq));  // 等待队列

  
    // 初始化定时器,实现软件的去抖动

     /* 初始化定时器 */
    for(i = 0; i < KEY_NUM; i++)
    {        
        key_timer[i].function = key_timer_handler;
        key_timer[i].data = i;      
        init_timer(&key_timer[i]);
    }
    at91sam9260_key_io_init();   // 初始化   
     // 注册中断函数
    if(-1 == request_irqs())
    {
        printk(KERN_NOTICE "request_irqs failed!\r\n");
    }
    else
    {
        printk(KERN_NOTICE "request_irqs success!\r\n");
    }
    /*
    for(i = 0; i < KEY_NUM; i++)
    {
        setup_timer(&key_timer[i],key_timer_handler,i);
    }
    */
    fail_malloc:unregister_chrdev_region(dev,1);

    return result;
   
}

// 按键设备驱动的模块卸载函数
static void __exit at91sam9260_key_exit(void)
{
    free_irqs();    // 注销中断

    // 释放设备号,删除cdev
    cdev_del(&key_dev.cdev);   // 删除字符设备结构体
    kfree(key_devp);
    unregister_chrdev_region(MKDEV(button_major,DP_MINOR),1);  // 删除字符设备
}

MODULE_AUTHOR("launch");
MODULE_LICENSE("Dual BSD/GPL");

module_init(at91sam9260_key_init);
module_exit(at91sam9260_key_exit);

使用特权

评论回复
30
tiger84|  楼主 | 2009-12-22 22:02 | 只看该作者
打算明天把按键驱动利用poll试试。
同时也继续串口驱动

使用特权

评论回复
31
tiger84|  楼主 | 2009-12-22 22:11 | 只看该作者
本帖最后由 tiger84 于 2009-12-22 22:14 编辑

目前仍然有些混乱,手头上关于Linux驱动的书就是宋宝华的那本,linux基础的书就是unix环境高级编程,不过一直没来得及看,书还新的很。对于at91sam9260,也是用到哪,看到哪。
项目也比较紧,不可能有时间去慢慢消化这些知识。
我目前基本上是需要用到什么,再去看什么,所以不能很好的把握整个系统。虽然说这样有利于自身的学习,但主要还是要最快的产品化,且稳定。

所以想找个比较好的方法,各位兄弟,提提建议吧。

使用特权

评论回复
32
icecut| | 2009-12-23 13:45 | 只看该作者
恩慢慢来.不错.

等你熟悉了再回来搞键盘,搞成和pc这个键盘这样子,比如某个键是回车.一按系统就能收到回车消息.
这个现在不用搞,等你后续搞定的.

使用特权

评论回复
33
tiger84|  楼主 | 2009-12-23 21:24 | 只看该作者
今天上午依旧是开会讨论,基本上整个上午都浪费了。
下午把串口应用程序弄了下,基本可以用。原计划是打算写串口驱动的,不过有现成可以用的,就先不弄了,以后有空慢慢研究。
向公司申请了台破电脑,准备安装fedora 11。折腾了很久,都没安装好,估计是硬盘有问题,明天换个硬盘再试试。
明天计划:
(1)安装好Linux
(2)完成按键poll驱动及应用程序select用法;
(3)开始简单的网络应用编程

使用特权

评论回复
34
牛牛特工| | 2009-12-24 10:24 | 只看该作者
本帖最后由 牛牛特工 于 2009-12-24 10:25 编辑

纯linux用的不熟 虽有eclipse不过编辑代码还是有些不方便 在虚拟机和win之间切来切去又麻烦的很
所以暂时按照这种模式来做:
在windows下用sourceinsight编辑 通过svn与linux共享 在linux下只编译
貌似可以 现在有点奇怪的 某些文件 checkout出来后权限变化了
还得自己去改一下  还好只有几个而已

使用特权

评论回复
35
lelee007| | 2009-12-24 11:12 | 只看该作者
两台电脑 ,linux + win

linux上搞个FTP

使用特权

评论回复
36
牛牛特工| | 2009-12-24 13:10 | 只看该作者
嗯 ftp也弄了

使用特权

评论回复
37
一朝成名| | 2009-12-24 17:38 | 只看该作者
用ssh~搞个服务器~

使用特权

评论回复
38
andysun1986| | 2009-12-24 20:36 | 只看该作者
听楼主一说,我更有信心学习了啊,老师要求基于arm9+linux开发,现在只才看了zlg的easyarm2200,熟悉arm体系结构,不过操作系统只懂皮毛,只会简单的linux命令操作,还有很多要看啊
:'(

使用特权

评论回复
39
icecut| | 2009-12-24 23:33 | 只看该作者
scope好像是这个做源代码查找

eclipse 一般机器跑不动。有点累。其他的我也没有什么好办法。

使用特权

评论回复
40
tiger84|  楼主 | 2009-12-25 08:28 | 只看该作者
昨天晚上有事给耽误了汇报的时间。按照计划完成了该做的。
(1)安装好Linux
(2)完成按键poll驱动及应用程序select用法;
(3)开始简单的网络应用编程
今天计划:
在板子上跑起网络应用。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则