接觸單片機或者其他嵌入式芯片的編程中,最簡單的就是GPIO的High/Low控制。
比如學校裏面最基本的單片機實驗:led控制,流水燈,數碼管。其實都是最基本的GPIO操作。
那麼同樣,對應到linux嵌入式開發,GPIO的High/Low控制也是最簡單的操作。算的上是c語言的 hello world 例程一樣。
linux的設備有分幾個類別,這裏led一般認作是字符形設備。在linux中訪問設備就是訪問設備文件,所以linux也作基於文件的操作系統
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#define DEVICE_NAME "leds"
static int led_gpios[] = {
S5PV210_GPJ2(0),
S5PV210_GPJ2(1),
S5PV210_GPJ2(2),
S5PV210_GPJ2(3),
};
#define LED_NUM ARRAY_SIZE(led_gpios)
static long smart210_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > LED_NUM) {
return -EINVAL;
}
gpio_set_value(led_gpios[arg], !cmd);
printk(DEVICE_NAME": %ld %d\n", arg, cmd);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations smart210_led_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = smart210_leds_ioctl,
};
static struct miscdevice smart210_led_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &smart210_led_dev_fops,
};
static int __init smart210_led_dev_init(void) {
int ret;
int i;
for (i = 0; i < LED_NUM; i++) {
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
led_gpios[i], ret);
return ret;
}
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 1);
}
ret = misc_register(&smart210_led_dev);
printk(DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit smart210_led_dev_exit(void) {
int i;
for (i = 0; i < LED_NUM; i++) {
gpio_free(led_gpios[i]);
}
misc_deregister(&smart210_led_dev);
}
module_init(smart210_led_dev_init);
module_exit(smart210_led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
將此編譯進內核。
2.編寫led測試程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.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);
}
fd = open("/dev/leds0", 0);
if (fd < 0) {
fd = open("/dev/leds", 0);
}
if (fd < 0) {
perror("open device leds");
exit(1);
}
ioctl(fd, on, led_no);
close(fd);
return 0;
}
如此,將led驅動直接編譯進內核後,然後在busybox構建的文件系統中,調用led測試程序。結果發現可以按照預先的想法順利控制led的亮暗。
int gpio_request(unsigned gpio, const char *label) ,成功返回0,否則負數
gpio則爲你要申請的哪一個管腳,label則是爲其取一個名字
自動創建節點的字符雜項設備misc_register
註冊
int misc_register(struct miscdevice * misc);
釋放
int misc_deregister(struct miscdevice *misc);
雜項設備的
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
網上關於雜項字符設備與普通字符設備的區別描述如下:
雜項字符設備和一般字符設備的區別:
1.一般字符設備首先申請設備號。 但是雜項字符設備的主設備號爲10次設備號通過結構體struct miscdevice中的minor來設置。
2.一般字符設備要創建設備文件。 但是雜項字符設備在註冊時會自動創建。
3.一般字符設備要分配一個cdev(字符設備)。 但是雜項字符設備只要創建struct miscdevice結構即可。
4.一般字符設備需要初始化cdev(即給字符設備設置對應的操作函數集struct file_operation). 但是雜項字符設備在結構體truct miscdevice中定義。
5.一般字符設備使用註冊函數 int cdev_add struct (cdev *p,devt_t dev, unsigned)(第一個參數爲之前初始化的字符設備,第二個參數爲設備號,第三個參數爲要添加設備的個數) 而雜項字符設備使用int misc_register(struct miscdevice *misc)來註冊
驅動調用的實質:
就是通過 設備文件找到與之對應設備號的設備,再通過設備初始化時綁定的操作函數對硬件進行控制的
上面例子中是將led看作一個雜項設備來實現的