1、实验程序
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#define LEDBASE_MAJOR 200 //主设备号
#define LEDBASE_NAME "LEDBASE" //主设备号
#define LEDOFF 0
#define LEDON 1
/*寄存器物理地址*/
#define CCM_CCGR1 (0X020C406C)
#define MUX_GPIO1_IO03 (0X020E0068)
#define PAD_GPIO1_IO03 (0X020E02F4)
#define GPIO1_DR (0X0209C000)
#define GPIO1_GDIR (0X0209C004)
static void __iomem *IMX_CCM_CCGR1;
static void __iomem *IMX_MUX_GPIO1_IO03;
static void __iomem *IMX_PAD_GPIO1_IO03;
static void __iomem *IMX_GPIO1_DR;
static void __iomem *IMX_GPIO1_GDIR;
void led_switch(int sta)
{
int val=0;
val=readl(IMX_GPIO1_DR);
val&=~(1<<3);//将val变为0
if(sta ==0)
{
val&=~(1<<3);
writel(val,IMX_GPIO1_DR);
}
else
{
val|=(1<<3);
writel(val,IMX_GPIO1_DR);
}
}
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int ledbase_open(struct inode *inode, struct file *filp)
{
printk("chrdevbase open!\r\n");
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t ledbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == 0) {
led_switch(0); /* 打开 LED 灯 */
} else if(ledstat == 1) {
led_switch(1); /* 关闭 LED 灯 */
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int ledbase_release(struct inode *inode, struct file *filp)
{
//printk("chrdevbase release!\r\n");
return 0;
}
/*字符设备操作集合*/
static struct file_operations ledbase_ops={
.owner=THIS_MODULE,
.open=ledbase_open,
.release=ledbase_release,
.write=ledbase_write,
};
/*驱动入口函数*/
static int __init ledbase_init(void)
{
/*将物理地址转化为虚拟地址*/
int val=0;
IMX_CCM_CCGR1=ioremap(CCM_CCGR1,4);
IMX_MUX_GPIO1_IO03=ioremap(MUX_GPIO1_IO03,4);
IMX_PAD_GPIO1_IO03=ioremap(PAD_GPIO1_IO03,4);
IMX_GPIO1_DR=ioremap(GPIO1_DR,4);
IMX_GPIO1_GDIR=ioremap(GPIO1_GDIR,4);
/*开启时钟*/
val=readl(IMX_CCM_CCGR1);
val&=~(3<<26);
val|=3<<26;
writel(val,IMX_CCM_CCGR1);
/*配置为GPIO1_IO03*/
writel(5,IMX_MUX_GPIO1_IO03);
/*配置电气属性*/
writel(0x10b0,IMX_PAD_GPIO1_IO03);
/*将GPIO1_IO03设置为输出*/
val=readl(IMX_GPIO1_GDIR);
val&=~(1<<3);
val|=(1<<3);
writel(val,IMX_GPIO1_GDIR);
/*设置默认输出低电平*/
val=readl(IMX_GPIO1_DR);
val&=~(1<<3);
writel(val,IMX_GPIO1_DR);
int ret=0;
printk("init\n");
/*字符设备注册函数*/
ret=register_chrdev(LEDBASE_MAJOR,LEDBASE_NAME,&ledbase_ops);
if(ret<0){
printk("register_chrdev failed\r\n");
}
else{
printk("register_chrdev success\r\n");
}
return 0;
}
/*驱动出口函数*/
static void __exit ledbase_exit(void)
{
int val=0;
val=readl(IMX_GPIO1_DR);
val|=(1<<3);
writel(val,IMX_GPIO1_DR);
/*释放虚拟地址*/
iounmap(IMX_CCM_CCGR1);
iounmap(IMX_MUX_GPIO1_IO03);
iounmap(IMX_PAD_GPIO1_IO03);
iounmap(IMX_GPIO1_DR);
iounmap(IMX_GPIO1_GDIR);
/*注销字符设备*/
unregister_chrdev(LEDBASE_MAJOR,LEDBASE_NAME);
printk("exit\n");
}
/*模块的加载与卸载*/
module_init(ledbase_init);
module_exit(ledbase_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchuanqiang");
2、程序说明:
- 字符设备驱动中,不可对物理地址进行操作,需要将物理地址映射成虚拟地址
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
static void __iomem* SW_MUX_GPIO1_IO03;
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);//4表示一个寄存器是32位的,需要4个节。
此时可以使用SW_MUX_GPIO1_IO03进行读写操作。
2.如何关闭打开LED操作与裸机的操作寄存器相同:
(1)打开GPIO对应的时钟;
(2)将引脚复用为GPIO;
(3)配置GPIO的电气属性;
(4)设置GPIO的输入输出;
(5)设置GPIO的默认输出电平。
3.在驱动出口函数中需要使用iounmap(IMX_CCM_CCGR1);函数进行释放虚拟地址。
4、readb、readw、readl;writeb、writew、wrirel是对映射后的内存进行读写操作。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/*
argc:应用程序参数个数
argv[]:具体的参数内容,字符串形式,这个函数中的第一个参数内容./chrdevbaseAPP;
第二个参数内容filename;第三个参数是写入的1
执行文件格式:
./ledAPP /dev/led 1
*/
int main(int argc,char *argv[])
{
int ret=0;
int fd=0;
char *filename;
int writebuf;//
int writevalue=atoi(argv[2]);
filename=argv[1];//一个字符串的声明
if(argc!=3)
{
printf("error\n");
return -1;
}
//打开文件
fd=open(filename, O_RDWR);
//判断有没有打开成功
if(fd<0)
{
printf("open failed\r\n");
return -1;
}
//向文件写入内容
ret= write( fd, &writevalue, 1);
if(ret<0)
{
printf("writeuser failed\r\n");
close(fd);
}
//关闭文件
ret=close(fd);
if(ret<0)
{
printf("close failed\r\n");
return -1;
}
return 0;
}
程序说明:
- int writevalue=atoi(argv[2]);进行数据转换,将字符型数据转化为整型数据。
2.在驱动模块加载完成以后,使用mknod /dev/led c 200 0创建设备节点,使用./ledAPP /dev/led 1来执行应用程序。
字符设备驱动框架搭建,存在需要手动创建设备节点的缺点。