【硬件】
3568的LED9 :引脚 GPIO0 B7
【配置GPIO的复用】
找配置复用关系的寄存器基地址、偏移地址、对应配置的GPIO。
查找:io -r -4 0xfdc2000c 系统设置的默认值
结果为1,意思是只有bit 0是1,其他全都为0。所以系统默认就是配置GPIO功能,所以可以不用设置复用GPIO。
【配置GPIO的方向 Direction Register】
查找对应IO配置:io -r -4 0xfdd60008 的默认值(GPIO基地址为0xfdd60000,GPIOB地址偏移8位:0xfdd60008)
结果为:0000c064
二进制 : 1100 0000 0110 0010
bit 15为1 ,所以默认已经配置为输出了。
【配置GPIO数据寄存器】
查找对应IO配置:io -r -4 0xfdd60000 的默认值
结果为:8000c040
可知bit15 默认为1
PB7写1对应的数据寄存器地址: 8000c040
【问】B7的写使能位是哪一位?
bit31。
PB7写0对应的数据寄存器地址:80004040
PS : bit15设置为0(PB7写0),B7对应控制位bit31必须使能1(写使能)
【驱动】
#define GPIO_DR 0xFDD60000struct device_test{dev_t dev_num;static int major = 0;static int minor = 0;struct cdev cdev_test;struct class *class;struct device *device;char kbuf[32]; //模拟内核寄存器的数据/************************/unsigned int *vir_gpio_dr; //保存GPIO寄存器虚拟地址用的
};/* 然后定义结构体变量 */
struct device_test dev1;static int moduleparam_init()
{int ret;ret = alloc_chrdev_region(&dev1.dev_num,0,1,"HELLO"); if(ret<0){goto err_chrdev;}printk("alloc_chrdev_region ok\n"); dev1.major_num =MAJOR(dev1.dev_num); //将主设备号取出来dev1.minor_num = MINOR(dev1.dev_num);//将次设备号取出来printk("major_num = %d\n", dev1.major_num);//打印传入进来的主设备号printk("minor_num = %d\n", dev1.minor_num);//打印传入进来的次设备号dev1.cdev_test.owner = THIS_MODULE;cdev_init(dev1.cdev_test, &cdev_file_operations);ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);if(ret<0){goto err_chradd;}dev1.class = class_creat(THIS_MODULE, "test");/* 判断创建类有没有失败 */if(IS_ERR(dev1,device)){ret = PTR_ERR(dev1,device);goto err_class_creat; }dev1.device = device_creat( dev1.class,NULL, dev1.dev_num,,NULL,"test") /* 判断创建类有没有失败 */if(IS_ERR(dev1.device)){ret = PTR_ERR(dev1,device);goto err_device_creat; } return 0;dev1.vir_gpio_dr = ioremap(GPIO_DR, 4);if(IS_ERR(dev1.vir_gpio_dr)){ret = PTR_ERR(dev1.vir_gpio_dr);goto err_ioremap; } err_ioremap:iounmap(GPIO_DR); //要取消虚拟映射的地址err_device_creat: //设备添加失败,意味着设备类添加成功,就要把这个类删掉class_destroy( dev1.class);
err_class_creat: //类添加失败,意味着字符设备添加成功,就要把这个字符设备删掉cdev_del(& dev1.cdev_test); //销毁字符设备
err_chradd: //添加cdev失败,意味着添加设备号成功,就要释放这个设备号unregister_chrdev_region( dev1.dev_num,DEVICE_NUMBER);//注销设备号
err_chrdev:return ret;
}
关键:
static ssize_t cdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{struct device_test *test_dev = (struct device_test *)file->private_data;test_dev->kbuf = {0};if(copy_from_user( test_dev->kbuf, buf ,sizeof(buf))!= 0);{printk("copy_from_user error !\n");return -1;}if(test_dev -> kbuf[0] == 1){*(test_dev->vir_gpio_dr) = 0x8000c040;else if{*(test_dev->vir_gpio_dr) = 0x80004040; } }return 0;
}static void hello_exit(void)
{unregister_chrdev_region( dev1.dev_num,DEVICE_NUMBER);//注销设备号cdev_del(& dev1.cdev_test); //销毁字符设备device_destroy( dev1.class, dev1.dev_num);class_destroy( dev1.class);iounmap(GPIO_DR);printk("gooodbye! \n");
}
【APP】
int main(int argc, char *argv[])
{int fd;char buf[32] = {0};fd1 = open("/dev/test", HELLO); /* 打开设备节点1 */if(fd < 0){perror("open error \n");return fd; } buf[0] = atoi(argv[1]);write(fd, buf, sizeof(buf));close(fd); return 0;
}
【实验效果】
./a.out 1 可以实现点灯
./a.out 0 可以实现灭灯