mini2440上LED对应的IO:
LED1 |
GPB5 |
LED2 |
GPB6 |
LED3 |
GPB7 |
LED4 |
GPB8 |
低电平有效(点亮)
寄存器:
GPxCON |
设置端口功能(00表示输入,01表示输出,10表示特殊功能,11保留不用) |
GPxDAT |
用于读写数据 |
GPxUP |
用于是否使用内部上拉电阻(0表示无上拉,1表示上拉) |
混杂设备驱动
在Linux系统中,存在一类字符设备,它们共享一个主设备号(一定要是10),但次设备号不同,我们称这类设备为混杂设备。所有的混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的混杂设备。
Linux内核使用struct miscdevice来描述一个混杂设备
struct miscdevice
{
int minor;//次设备号
const char *name;//设备名
const struct file_operation *fops;//文件操作
struct list_head list;
struct device *parent;
struct device *this_device;
}
Linux内核使用struct miscdevice 函数来注册一个混杂设备驱动
int misc_register(struct miscdevice *misc)
注销一个混杂设备驱动
misc_deregister(&misc);
混杂设备LED驱动程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "led"
static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
static int mini2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4)
{
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
/*文件操作结构体*/
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = mini2440_leds_ioctl,
/*混杂类型虽然没有open、release这两个设备方法,但内核自动帮你现实*/
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,//动态的混杂次设备号,系统自己帮你选
.name = DEVICE_NAME,
.fops = &dev_fops,//关联文件操作
};
/*初始化设备驱动*/
static int __init dev_init(void)
{
int ret;
int i;
for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);//设置输出
s3c2410_gpio_setpin(led_table[i], 0);//数据输出0
}
/*注册混杂型字符设备驱动*/
ret = misc_register(&misc);//返回0,成功,负数,不成功
printk (DEVICE_NAME"tinitializedn");
return ret;
}
static void __exit dev_exit(void)
{
/*注销混杂型字符设备驱动*/
misc_deregister(&misc);//返回0,成功,负数,不成功
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bai");
测试程序:
#include
#include
#include
#include
int main(int argc, char **argv)
{
int on;
int fd;
if (argc != 2 || sscanf(argv[1], "%d", &on) != 1 || on < 0 || on > 1 )
{
fprintf(stderr, "Usage: led 0|1n");
exit(1);
}
fd = open("/dev/led", 0);
if (fd < 0) {
fd = open("/dev/led", 0);
}
if (fd < 0) {
perror("open device led");
exit(1);
}
if(on==1)
{
ioctl(fd, on);
printf("all leds on!");
}
if(on==0)
{
ioctl(fd, on);
printf("all leds off!");
}
close(fd);
return 0;
}
这个驱动程序关键是
s3c2410_gpio_setpin(led_table[arg], !cmd);
内核定义好的GPIO接口(S3C2410_GPB5和S3C2410_GPB5_OUTP)和GPIO操作函数(s3c2410_gpio_setpin和s3c2410_gpio_cfgpin)。可移植性好,也是正确的做法。内核的GPIO操作函数也是通过一些的运算将GPIO接口换算成虚拟内存地址然后进行访问的。
提示:
sscanf
语法:
#include int sscanf( const char *buffer, const char *format, ... ); |
函数sscanf()和scanf()类似, 只是输入从buffer(缓冲区)中读取.
sscanf(argv[1],"%d", &on)
表示从字符串argv[1]转化成整形,再赋值给on
使用:
1)用Makefile编译成ko文件放到开发板上
2)arm-linux-gcc led_test.c -o led_test 编译后放到开发板上
3)insmod mini2440_leds.ko加载模块
4)./led_test 0 再./led_test 1测试
5)rmmod mini2440_leds卸载