TINY4412调试LED
LED一般由CPU GPIO口控制,将GPIO口设置为输出口,再控制其输出高低电平即可控制灯亮、灯灭。
一、看原理图确定LED接入GPIO口
![](/attachment/201703/29/20698826_1490755884GEEd.png)
![](/attachment/201703/29/20698826_14907553094Bf7.png)
![](/attachment/201703/29/20698826_1490755324Xx7W.png)
![](/attachment/201703/29/20698826_1490755336406b.png)
原理图分析结果:
通过分析原理LED最终接入了CPU GPX3_2脚,GPX3_2输出低电平是LED灯灭,输出高电平时LED灯亮。
所有我们需要将GPX3_2先设置为输出引脚,在控制其输出高低电平,就可以控制灯灭、灯亮了。
二、看CPU芯片手册找GPIO寄存器信息
在4412 CPU datasheet中查找GPX3,获得GPX3CON和GPX3DAT寄存器的信息如下:
1、GPX3CON寄存器:
![](/attachment/201703/29/20698826_1490755459cBdn.png)
![](/attachment/201703/29/20698826_1490755538DMVF.png)
从CPU datasheet可以得到,GPX3CON的地址为0x11000C60, GPX3CON_2在GPX3CON地址的11-8位,将GPX3CON的11-8位设置为0x01即为输出功能。
代码如下:
/* 从gpx3con的物理地址得到gpx3con, gpx3dat的虚拟地址 */
gpx3con = (volatile unsigned long *)ioremap(0x11000C60, 16);
gpx3dat = gpx3con + 1;
/* 将GPX3CON[11:8]设置为输出引脚0x01,11,10,9位设置为低,8设置为高 ====> [11:10:9:8] ===> 0001 */
*gpx3con &= ~(0x0F << (4 * 2)); /* 将第11,10,9,8位全部置为0 */
*gpx3con |= (0x01 << (4 * 2)); /* 将第8位置为1 */
2、GPX3DAT寄存器:
![](/attachment/201703/29/20698826_1490755747GAyb.png)
GPX3DAT的地址为0x11000C64,8位大小,将GPX3CON指定的pin脚设置为0,1即可指定输出低,高电平。
比如:GPX3CON的11-8个bit对应的是GPX3CON[2],那么GPX3DAT的第2个bit对应的 0,1就是控制GPX3CON[2]输出低、高电平了。
代码如下:
/* 将GPX3DAT的第2位设置为输出低电平0灭灯 */
*gpx3dat &= ~(0x01 << 2);
/* 将GPX3DAT的第2位设置为输出高电平1亮灯 */
*gpx3dat |= (0x01 << 2);
三、写LED驱动
1、字符串设备驱动file_operations结构体
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_drv_open,
.write = led_drv_write,
};
.open对应文件系统open系统调用接口;
.wirte对应文件系统write系统调用接口;
2、驱动入口函数
驱动入口函数根据字符设备驱动框架申请设备号,创建/dev/led0,添加字符设备,寄存器物理地址映射工作,内容如下:
-
/* 驱动入口函数 */
-
static int led_drv_init(void)
-
{
-
int retval;
-
dev_t devid;
-
-
/* 分配设备编号(devid) */
-
if (major)
-
{
-
devid = MKDEV(major, 0);
-
retval = register_chrdev_region(devid, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
-
}
-
else
-
{
-
/* 动态分配主设备号 */
-
retval = alloc_chrdev_region(&devid, 0, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
-
major = MAJOR(devid);
-
}
-
-
if (retval)
-
{
-
printk(KERN_ERR "Unable to register minors for led\n");
-
return retval;
-
}
-
-
/* 由内核自动创建/dev/led0设备 */
-
leddrv_class = class_create(THIS_MODULE, "led");
-
device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led0"); /* /dev/led0 */
-
-
cdev_init(&led_cdev, &led_fops);
-
cdev_add(&led_cdev, devid, LED_DEV_MAX);
-
-
-
/* 用来将I/O内存资源的物理地址映射到内核虚地址空间(3GB-4GB)中 */
-
/* 0x11000C60: 由芯片手册获得,GPX3CON物理地址 */
-
gpx3con = (volatile unsigned long *)ioremap(0x11000C60, 16);
-
gpx3dat = gpx3con + 1;
-
-
printk("LED driver create device ------> /dev/led0 suucess!\n");
-
-
return 0;
-
}
3、驱动open函数
-
/* 驱动open函数 */
-
static int led_drv_open(struct inode *inode, struct file *file)
-
{
-
//printk("led_drv_open\n");
-
-
//1. 设置GPX3CON为输出引脚,输出低电平是灯灭,输出高电平是灯亮
-
//将GPX3CON[11:8]设置为输出引脚0x01,11,10,9位设置为低,8设置为高 ====> [11:10:9:8] ===> 0001
-
*gpx3con &= ~(0x0F << (4 * 2)); /* 将第11,10,9,8位全部置为0 */
-
*gpx3con |= (0x01 << (4 * 2)); /* 将第8位置为1 */
-
-
return 0;
-
}
4、启动write函数
-
/* 驱动write函数 */
-
static int led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-
{
-
int val = 0;
-
-
//printk("led_drv_write\n");
-
-
/* 从应用层拷贝数据 */
-
if (copy_from_user(&val, buf, count) != 0)
-
{
-
return -EFAULT;
-
}
-
-
/* 灭灯 */
-
if (val == 0)
-
{
-
/* 将GPX3DAT的第2位设置为输出低电平0灭灯 */
-
*gpx3dat &= ~(0x01 << 2);
-
}
-
/* 亮灯 */
-
else
-
{
-
/* 将GPX3DAT的第2位设置为输出高电平1亮灯 */
-
*gpx3dat |= (0x01 << 2);
-
}
-
-
return 0;
-
}
5、驱动模块出口函数
-
static void led_drv_exit(void)
-
{
-
dev_t devid = MKDEV(major, 0);
-
-
device_destroy(leddrv_class, devid);
-
class_destroy(leddrv_class);
-
-
cdev_del(&led_cdev);
-
-
unregister_chrdev_region(devid, LED_DEV_MAX);
-
-
iounmap(gpx3con);
-
-
printk("LED driver destory device ------> /dev/led0 suucess!\n");
-
}
完整驱动代码如下:
-
#include <linux/kernel.h>
-
#include <linux/module.h>
-
#include <linux/interrupt.h>
-
#include <linux/slab.h>
-
#include <linux/errno.h>
-
#include <linux/miscdevice.h>
-
#include <linux/pci.h>
-
#include <linux/wait.h>
-
#include <linux/init.h>
-
#include <linux/fs.h>
-
#include <linux/cdev.h>
-
-
#include <asm/io.h>
-
#include <asm/uaccess.h>
-
-
/* 设备数量1个 */
-
#define LED_DEV_MAX 1
-
-
/* 主设备号 */
-
static int major;
-
-
/* 内核字符设备结构体 */
-
static struct cdev led_cdev;
-
-
static struct class *leddrv_class;
-
-
volatile unsigned long *gpx3con = NULL;
-
volatile unsigned long *gpx3dat = NULL;
-
-
/* 驱动open函数 */
-
static int led_drv_open(struct inode *inode, struct file *file)
-
{
-
//printk("led_drv_open\n");
-
-
//1. 设置GPX3CON为输出引脚,输出低电平是灯灭,输出高电平是灯亮
-
//将GPX3CON[11:8]设置为输出引脚0x01,11,10,9位设置为低,8设置为高 ====> [11:10:9:8] ===> 0001
-
*gpx3con &= ~(0x0F << (4 * 2)); /* 将第11,10,9,8位全部置为0 */
-
*gpx3con |= (0x01 << (4 * 2)); /* 将第8位置为1 */
-
-
return 0;
-
}
-
-
/* 驱动write函数 */
-
static int led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-
{
-
int val = 0;
-
-
//printk("led_drv_write\n");
-
-
/* 从应用层拷贝数据 */
-
if (copy_from_user(&val, buf, count) != 0)
-
{
-
return -EFAULT;
-
}
-
-
/* 灭灯 */
-
if (val == 0)
-
{
-
/* 将GPX3DAT的第2位设置为输出低电平0灭灯 */
-
*gpx3dat &= ~(0x01 << 2);
-
}
-
/* 亮灯 */
-
else
-
{
-
/* 将GPX3DAT的第2位设置为输出高电平1亮灯 */
-
*gpx3dat |= (0x01 << 2);
-
}
-
-
return 0;
-
}
-
-
static struct file_operations led_fops = {
-
.owner = THIS_MODULE,
-
.open = led_drv_open,
-
.write = led_drv_write,
-
};
-
-
/* 驱动入口函数 */
-
static int led_drv_init(void)
-
{
-
int retval;
-
dev_t devid;
-
-
/* 分配设备编号(devid) */
-
if (major) {
-
devid = MKDEV(major, 0);
-
retval = register_chrdev_region(devid, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
-
} else {
-
/* 动态分配主设备号 */
-
retval = alloc_chrdev_region(&devid, 0, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
-
major = MAJOR(devid);
-
}
-
-
if (retval) {
-
printk(KERN_ERR "Unable to register minors for led\n");
-
return retval;
-
}
-
-
/* 由内核自动创建/dev/led0设备 */
-
leddrv_class = class_create(THIS_MODULE, "led");
-
device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led0"); /* /dev/led0 */
-
-
cdev_init(&led_cdev, &led_fops);
-
cdev_add(&led_cdev, devid, LED_DEV_MAX);
-
-
/* 用来将I/O内存资源的物理地址映射到内核虚地址空间(3GB-4GB)中 */
-
/* 0x11000C60: 由芯片手册获得,GPX3CON物理地址 */
-
gpx3con = (volatile unsigned long *)ioremap(0x11000C60, 16);
-
gpx3dat = gpx3con + 1;
-
-
printk("LED driver create device ------> /dev/led0 suucess!\n");
-
-
return 0;
-
}
-
-
static void led_drv_exit(void)
-
{
-
dev_t devid = MKDEV(major, 0);
-
-
device_destroy(leddrv_class, devid);
-
class_destroy(leddrv_class);
-
-
cdev_del(&led_cdev);
-
-
unregister_chrdev_region(devid, LED_DEV_MAX);
-
-
iounmap(gpx3con);
-
-
printk("LED driver destory device ------> /dev/led0 suucess!\n");
-
}
-
-
module_init(led_drv_init);
-
module_exit(led_drv_exit);
-
-
MODULE_LICENSE("GPL");
7、Makefile:
-
KERN_DIR = /code/tiny4412/linux-3.5
-
-
all:
-
make -C $(KERN_DIR) M=`pwd` modules
-
-
clean:
-
make -C $(KERN_DIR) M=`pwd` modules clean
-
rm -rf modules.order
-
-
obj-m += led.o
8、编译模块
在驱动代码目录执行make命令,编译生成led.ko
# make
四、写LED驱动测试程序
-
#include <stdio.h>
-
#include <string.h>
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
-
int main(int argc, char **argv)
-
{
-
if (argc != 2)
-
{
-
printf("usage: led_test <0(off)|1(on)> \n");
-
return -1;
-
}
-
-
int fd = open("/dev/led0", O_RDWR);
-
if (fd < 0)
-
{
-
printf("open /dev/led0 fail. \r\n");
-
return -1;
-
}
-
-
int val = 0;
-
-
if (strncmp(argv[1], "0", 1) == 0)
-
{
-
val = 0;
-
}
-
else
-
{
-
val = 1;
-
}
-
-
write(fd, &val, sizeof(val));
-
-
close(fd);
-
-
return 0;
-
}
编译驱动测试程序:
arm-linux-gcc led_test.c -o led_test
五、测试
1、将led.ko、led_test文件tftp到设备,安装led.ko
# insmod led.ko
2、查看/dev目录下是否存在led0设备文件
# ls -l /dev/led0
3、运行led_test
# ./led_test 0 //熄灭led
# ./led_test 1 //点亮led
阅读(1686) | 评论(0) | 转发(0) |