Trung tâm đào tạo thiết kế vi mạch Semicon


  • ĐĂNG KÝ TÀI KHOẢN ĐỂ TRUY CẬP NHIỀU TÀI LIỆU HƠN!
  • Create an account
    *
    *
    *
    *
    *
    Fields marked with an asterisk (*) are required.
wafer.jpg

Viết gpio driver đơn giản điều khiển leds, buttons

E-mail Print PDF

1. Giới thiệu

Bài viết này hướng dẫn viết một gpio driver đơn giản trên linux (trình điều khiển thiết bị giao tiếp vào ra cơ bản).  Minh họa driver cho KIT FriendlyArm 2440 điều khiển led, button trên một board mở rộng được ghép nối thông qua cổng GPF0-6

2. Viết led driver

Driver này sẽ bật/tắt các led đơn ghép nối trên các chân GPF0-7

Bước 1. Viết mã nguồn của led driver, file mini2440_gpfxleds.c:

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/mm.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <linux/delay.h>

#include <linux/moduleparam.h>

#include <linux/slab.h>

#include <linux/errno.h>

#include <linux/ioctl.h>

#include <linux/cdev.h>

#include <linux/string.h>

#include <linux/list.h>

#include <linux/pci.h>

#include <linux/gpio.h>

#include <asm/uaccess.h>

#include <asm/atomic.h>

#include <asm/unistd.h>

#include <linux/sched.h>

#define DEVICE_NAME "GPFxLeds"

#define MAX            7

static unsigned long led_table [] = {

            S3C2410_GPF(0),   

            S3C2410_GPF(1),

            S3C2410_GPF(2),

            S3C2410_GPF(3),   

            S3C2410_GPF(4),

            S3C2410_GPF(5),

            S3C2410_GPF(6)    

};

 static unsigned int led_cfg_table [] = {

            S3C2410_GPIO_OUTPUT,

            S3C2410_GPIO_OUTPUT,

            S3C2410_GPIO_OUTPUT,

            S3C2410_GPIO_OUTPUT,

            S3C2410_GPIO_OUTPUT,

            S3C2410_GPIO_OUTPUT,

            S3C2410_GPIO_OUTPUT

};

 // Implementation of the ioctl

static int sbc2440_gpf0_ioctl(struct inode *inode, struct file *file,

                                                 unsigned int cmd, unsigned long arg)

{

            switch(cmd) //Switch the value of cmd.

            // If Cmd = 0 we put the pin to 0. If Cmd = 1,we put the pin 1.

            {

                        case 0:

                        case 1:

                                    if (arg > MAX - 1 )

                                    {

                                                return -EINVAL;

                                    }

                                    s3c2410_gpio_setpin(led_table[arg], !cmd); // Change the state of the pin

                                    return 0;

                        default:

                                    return -EINVAL;

            }

}

 // The structure that contains pointers to

// functions defined in the module that perform read-write operations ...

static struct file_operations dev_fops = {

            .owner = THIS_MODULE,

            .ioctl = sbc2440_gpf0_ioctl

};

 static struct miscdevice misc = {

.minor = MISC_DYNAMIC_MINOR,

.name = DEVICE_NAME,

.fops = &dev_fops

};

 static int __init dev_init(void)

{

            int ret;

            // Function init

            int i;

          

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

                        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

                        s3c2410_gpio_setpin(led_table[i], 0);

            }

      

            ret = misc_register(&misc);

             printk (DEVICE_NAME"\tInitialized\n");

             return ret;

}

static void __exit dev_exit(void)

{

            // Function exit

            misc_deregister(&misc);

}

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("TH");

Trong đó mảng leds_table liệt kê các chân driver sẽ giao tiếp của cổng GPF gồm 7 chân từ GPF0-7.

Hàm sbc2440_gpf0_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) là hàm điều khiển ra (on/off) của từng chân vi xử lý. 

Bước 2. Viết Makefile để biên dịch mã nguồn trên, được file mini2440_gpfxleds.ko

obj-m += mini2440_gpfxleds.o

all:

            make -C /lib/modules/2.6.32.2-FriendlyARM/build M=$(PWD) modules

clean:

            make -C /lib/modules/2.6.32.2-FriendlyARM/build M=$(PWD) clean

Chú ý: Makefile phụ thuộc vào linux kernel được sử dụng. Minh họa này sử dụng phiên bản linux 2.6.32.2 cho KIT FriendlyARM micro2440

Bước 3. Copy và cài đặt driver lên KIT:

Lệnh: insmod mini2440_gpfxleds.ko

Bước 4. Viết chương trình sử dụng driver vừa tạo

Chương trình C đơn giản

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/select.h>

#include <sys/time.h>

#include <string.h>

int main(int argc, char **argv)

{

            int on;

            int led_no;

            int fd;

            if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||

                on < 0 || on > 1 || led_no < 0 || led_no > 3) {

                        fprintf(stderr, "Usage: leds led_no 0|1\n");

                        exit(1);

            }

            //Open leds device file

            fd = open("/dev/GPFxLeds", 0);

           

            if (fd < 0) {

                        perror("error open device leds\n");

                        exit(1);

            }

            ioctl(fd, on, led_no);

            close(fd);

            return 0;

}

3. Viết buttons driver

Driver này sẽ đọc trạng thái các nút bấm ghép nối trên các chân GPF0-7 (sử dụng phương pháp thăm dò polling)

Bước 1: Viết mã nguồn (File mini2440_gpfxbuttons.c)

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <linux/poll.h>

#include <linux/irq.h>

#include <asm/irq.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <linux/platform_device.h>

#include <linux/cdev.h>

#include <linux/miscdevice.h>

#include <linux/sched.h>

#include <linux/gpio.h>

 

#define DEVICE_NAME     "GPFxButtons"

struct button_irq_desc {

    int irq;

    int pin;

    int pin_setting;

    int number;

    char *name;          

};

static struct button_irq_desc button_irqs [] = {

    {IRQ_EINT0, S3C2410_GPF(0) ,  S3C2410_GPF0_EINT0  , 0, "KEY0"},

    {IRQ_EINT1, S3C2410_GPF(1) ,  S3C2410_GPF1_EINT1 , 1, "KEY1"},

    {IRQ_EINT2, S3C2410_GPF(2) ,  S3C2410_GPF2_EINT2 , 2, "KEY2"},

    {IRQ_EINT3, S3C2410_GPF(3) ,  S3C2410_GPF3_EINT3 , 3, "KEY3"},

    {IRQ_EINT4, S3C2410_GPF(4) ,  S3C2410_GPF4_EINT4 , 4, "KEY4"},

    {IRQ_EINT5, S3C2410_GPF(5) ,  S3C2410_GPF5_EINT5 , 5, "KEY5"},

    {IRQ_EINT6, S3C2410_GPF(6) ,  S3C2410_GPF6_EINT6 , 6, "KEY6"},

};

static volatile char key_values [] = {'0', '0', '0', '0', '0', '0', '0'};

static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 

static volatile int ev_press = 0;

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

    int down;

    // udelay(0);

    down = !s3c2410_gpio_getpin(button_irqs->pin);

    if (down != (key_values[button_irqs->number] & 1)) { // Changed

            key_values[button_irqs->number] = '0' + down;      

        ev_press = 1;

        wake_up_interruptible(&button_waitq);

    }

    return IRQ_RETVAL(IRQ_HANDLED);

}

static int s3c24xx_buttons_open(struct inode *inode, struct file *file)

{

    int i;

    int err = 0;

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

            if (button_irqs[i].irq < 0) {

                        continue;

            }

        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,

                          button_irqs[i].name, (void *)&button_irqs[i]);

        if (err)

            break;

    }

    if (err) {

        i--;

        for (; i >= 0; i--) {

                if (button_irqs[i].irq < 0) {

                        continue;

                }

                disable_irq(button_irqs[i].irq);

            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

        }

        return -EBUSY;

    }

    ev_press = 1; 

    return 0;

}

static int s3c24xx_buttons_close(struct inode *inode, struct file *file)

{

    int i; 

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

            if (button_irqs[i].irq < 0) {

                continue;

            }

            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

    }

    return 0;

}

static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

    unsigned long err; 

    if (!ev_press) {

            if (filp->f_flags & O_NONBLOCK)

                return -EAGAIN;

            else

                wait_event_interruptible(button_waitq, ev_press);

    } 

    ev_press = 0;

    err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));

    return err ? -EFAULT : min(sizeof(key_values), count);

} 

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)

{

    unsigned int mask = 0;

    poll_wait(file, &button_waitq, wait);

    if (ev_press)

        mask |= POLLIN | POLLRDNORM;

    return mask;

}

static struct file_operations dev_fops = {

    .owner   =   THIS_MODULE,

    .open    =   s3c24xx_buttons_open,

    .release =   s3c24xx_buttons_close,

    .read    =   s3c24xx_buttons_read,

    .poll    =   s3c24xx_buttons_poll,

};

static struct miscdevice misc = {

            .minor = MISC_DYNAMIC_MINOR,

            .name = DEVICE_NAME,

            .fops = &dev_fops,

};

static int __init dev_init(void)

{

            int ret;

            ret = misc_register(&misc);

            printk (DEVICE_NAME"\tinitialized\n");

            return ret;

}

static void __exit dev_exit(void)

{

            misc_deregister(&misc);

}

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("TH");

Bước 2. Viết Makefile để biên dịch mã nguồn trên, được file mini2440_gpfxbuttons.ko

obj-m += mini2440_gpfxbuttons.o

all:

            make -C /lib/modules/2.6.32.2-FriendlyARM/build M=$(PWD) modules 

clean:

            make -C /lib/modules/2.6.32.2-FriendlyARM/build M=$(PWD) clean

Chú ý:

- Makefile trên biên dịch cho linux kernel 2.6.32.2 cho FriendlyARM micro2440, để chạy trên các phiên bản nhân khác cần sửa đường dẫn tương ứng này.

- Biên dịch nhân để tạo lib/modules, di chuyển vào trong thư mục mã nguồn của linux kernel rồi dùng lệnh: make modules sau đó make modules_install

Bước 3. Copy và cài đặt driver lên KIT

Lệnh: insmod mini2440_gpfxbuttons.ko

      Bước 4. Viết chương trình giao tiếp driver trên     

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/select.h>

#include <sys/time.h>

#include <string.h>

int main(int argc, char **argv)

{

            int buttons_fd;

            char buttons[7] = {'0', '0', '0', '0', '0', '0', '0'};

 

            buttons_fd = open("/dev/GPFxButtons", 0);

            if (buttons_fd < 0) {

                        perror("open device buttons");

                        exit(1);

            }

            for (;;) {

                        char current_buttons[7];

                        int count_of_changed_key;

                        int i;

                        if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {

                                    perror("read buttons:");

                                    exit(1);

                        }

                        for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {

                                    if (buttons[i] != current_buttons[i]) {

                                                buttons[i] = current_buttons[i];

                                                printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");

                                                count_of_changed_key++;

                                    }

                        }

                        if (count_of_changed_key) {

                                    printf("\n");

                        }             

            }

            close(buttons_fd);

            return 0;

}

 4. Chương trình giao diện đồ họa Qt giao tiếp led, button drivers

NGUỒN https://sites.google.com

Bạn có đam mê ngành thiết kế vi mạch và bạn muốn có mức lương 1000 usd cùng lúc bạn

đang muốn tìm một Trung tâm để học vậy hãy đến với ngành vi mạch tại SEMICON

  HotLine: 0972 800 931 Ms Duyên

 

 

Related Articles

Chat Zalo