内核版本:linux-3.5
平台:tiny4412
一、关于混杂设备
此版本内核led驱动使用的是混杂设备misc,具体misc.c的实现路径:linux-3.5/drivers/char/misc.c
这就很大程度简化了我们的驱动代码,没有发现ldd3中提到的各种字符设备注册函数,而是发现了一个misc_register函数(共用的注册函数),这说明led设备是作为杂项设备出现在内核中的,在内核中,misc杂项设备驱动接口是对一些字符设备的简单封装,他们共享一个主设备号,有不同的次设备号,共享一个open调用,其他的操作函数在打开后运用linux驱动程序的方法重载进行装载。
二、关于gpio:
查找tiny4412的datasheet以及pcb电路图。
1、查找I/O口对应引脚:
从图中我们可以看出,四个led灯分别对应四个i/o口1,2,3,4。
所以可以创建结构体来保存:
static int led_gpios[] = {
EXYNOS4X12_GPM4(0),
EXYNOS4X12_GPM4(1),
EXYNOS4X12_GPM4(2),
EXYNOS4X12_GPM4(3),
};
2、查看led电路图:
由电路图可知当led为低电平时,led灯被点亮。。。且为输出模式。
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT); //设置为输出模式
gpio_set_value(led_gpios[i], 1); //将引脚设置为高电平,默认为全灭
3、相关gpio函数:
1、int gpio_request(unsigned gpio, const char *label);
获得并占有 GPIO port 的使用权,由参数 gpio 指定具体 port,非空的lables指针有助于诊断。主要是告诉内核这地址被占用了。当其它地方调用同一地址的gpio_request就会报告错误,该地址已被申请。在/proc/mem应该会有地址占用表描述。
这种用法的保护作用前提是大家都遵守先申请再访问,有一个地方没遵守这个规则,这功能就失效了。好比进程互斥,必需大家在访问临界资源的时候都得先获取锁一样,其中一个没遵守约定,代码就废了。
2、void gpio_free(unsigned gpio);//释放 GPIO port 的使用权,由gpio 指定具体 port。
例2:gpio_free(RK29_PIN0_PA0);//释放GPIO0_A0
、
3、int gpio_direction_input(unsigned gpio);//设置输入模式
例3:gpio_direction_input (RK29_PIN0_PA0);//把GPIO0_A0设置为输入
4、int gpio_direction_output(unsigned gpio, int value);//设置输出模式
例4:gpio_direction_output(RK29_PIN0_PA0,GPIO_LOW);//把GPIO0_A0设置为输出口,且其电平拉低。
5、int gpio_get_value(unsigned gpio);//获取电平值
例5:ret = gpio_get_value (RK29_PIN0_PA0);// 读取GPIO0_A0的电平,并赋值给变量ret。
6、void gpio_set_value(unsigned gpio, int value);设置引脚电平值
gpio_set_value (RK29_PIN0_PA0, GPIO_HIGH);// 设置RK29_PIN0_PA0电平为高。
7、 int gpio_pull_updown(unsigned gpio,unsigned value);
value = 0, normal
value = 1, pull up
value = 2, pull down
例7、gpio_pull_updown(RK29_PIN0_PA0,1)上拉
int gpio_cansleep(unsigned gpio);
支持这种gpio的平台为了通过在这个函数中返回非零来区分其它类型的gpio(需要一个已经被 gpio_request申请的gpio号)为了访问这些端口,定义了另一组函数接口:
8、 int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);只能在允许睡眠的上下文中访问这些端口,比如线程化的中断中,
9、 static inline int gpio_is_valid(int number)//判断GPIO是否有效,有效返回0
10、 int gpio_export(unsigned gpio, bool direction_may_change);//返回0成功
void gpio_unexport(); //返回0成功
int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
//创建到导出GPIO的 sysfs link ,第一个参数是在哪个dev下创建,第二个是参数名字,第三个是gpio编号
具体可参考:
http://blog.csdn.net/andrinux/article/details/38725619
http://blog.sina.com.cn/s/blog_a6559d9201015vx9.html
三、驱动代码分析:
流程:初始化(注册misc设备)—>定义fops结构体与misc结构体—>实现fops中相关函数—>把fops绑定到misc结构体—>注销misc设备。
#include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "leds" //设备名 static int led_gpios[] = { EXYNOS4X12_GPM4(0), EXYNOS4X12_GPM4(1), EXYNOS4X12_GPM4(2), EXYNOS4X12_GPM4(3), }; #define LED_NUM ARRAY_SIZE(led_gpios) //因为是低电平点亮,但是这里我们cmd的值传入1点亮led灯,这是为了照顾惯性思维,当然,也可以让它传入0变亮。 static long tiny4412_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); //取反,所以传入1后点亮 //printk(DEVICE_NAME": %d %dn", arg, cmd); break; default: return -EINVAL; } return 0; } //fops的实现,无需加入open,使用misc.c中共用的open static struct file_operations tiny4412_led_dev_fops = { .owner = THIS_MODULE, .unlocked_ioctl = tiny4412_leds_ioctl, //led控制函数 }; //混杂设备结构体,将fops绑定到其中 static struct miscdevice tiny4412_led_dev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &tiny4412_led_dev_fops, }; //初始化:申请I/O口--->设置为输出模式--->默认设置全灭 static int __init tiny4412_led_dev_init(void) { int ret; int i; for (i = 0; i < LED_NUM; i++) { ret = gpio_request(led_gpios[i], "LED"); //获得并占有 GPIO port 的使用权 if (ret) { printk("%s: request GPIO %d for LED failed, ret = %dn", 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(&tiny4412_led_dev); //注册混杂设备 printk(DEVICE_NAME"tinitializedn"); return ret; } static void __exit tiny4412_led_dev_exit(void) { int i; for (i = 0; i < LED_NUM; i++) { gpio_free(led_gpios[i]); //释放 GPIO port 的使用权 } misc_deregister(&tiny4412_led_dev); //注销混杂设备 } module_init(tiny4412_led_dev_init); module_exit(tiny4412_led_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("FriendlyARM Inc."); 四、测试用例: #include #include #include #include int main(int argc, char **argv) { int on; int led_no; int fd; /* 检查 led 控制的两个参数,如果没有参数输入则退出。 */ 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|1n"); exit(1); } /*打开/dev/leds 设备文件*/ fd = open("/dev/leds0", 0); if (fd < 0) { fd = open("/dev/leds", 0); } if (fd < 0) { perror("open device leds"); exit(1); } /*通过系统调用 ioctl 和输入的参数控制 led*/ ioctl(fd, on, led_no); /*关闭设备句柄*/ close(fd); return 0; } 交叉编译后将a.out拷贝到开发板。 运行:(第一个参数表示第led 0/1/2/3,第二个参数1表示开启,0表示关闭) ./a.out 1 1 //开启第二盏灯 ./a.out 2 0 //关闭第三盏灯
上一篇:Tiny6410 简单的LED字符设备驱动
下一篇:Arm 2440——Nand flash启动模式详解(LED程序为例)
推荐阅读最新更新时间:2024-11-19 17:10
设计资源 培训 开发板 精华推荐
- 使用 Analog Devices 的 LTC1775CS 的参考设计
- C2912955_RGB颜色传感器IC验证板
- KIT33772CTPLEVB: MC33772C评估板,具有隔离的菊花链通信
- 基于STC3115的带有报警输出的电池监视方案,用于燃气表应用
- DER-1029 - 使用 InnoSwitch3-EP (750 V PowiGaN) 的太阳能赛车用 50 W DC-DC 转换器
- TWR-K70F120M-KIT、K70F120M 塔式套件、Kinetis MCU 模块是适用于 K61 和 K70 系列、32 位 ARM Cortex-M4 MCU 的开发板
- EVAL-AD7719-EB,使用 AD7719、24 位、双 Sigma Delta 模数转换器的评估板
- 单片机最小系统板-IAP15W4K58S4
- 小电视天气时钟
- LTC3203B 多输出 PD(设置为 3.3V)演示板