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