1.地址映射
Linux驱动开发可以操作寄存器,但不能直接对寄存器物理地址进行读写(裸机可以),因为linux会使能MMU。
Linux下操作的是虚拟地址,需要先把物理地址转为虚拟地址,使用方法为:
ioremap:从物理地址转为虚拟地址 虚拟地址=ioremap(物理地址,大小)
iounmap:iounmap(虚拟地址)
2.led驱动框架搭建
包括注册函数,注销函数,file_operations结构体(owner,open,release,write)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define LED_MAJOR 200
#define LED_NAME "led"
static int led_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int led_release(struct inode *inode,struct file *filp)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{
return 0;
}
static const struct file_operations led_fops={
.owner=THIS_MODULE,
.write=led_write,
.open=led_open,
.release=led_release,
};
//注册
static int __init led_init(void)
{
int ret=0;
/*1.注册字符设备*/
ret=register_chrdev(LED_MAJOR,LED_NAME,&led_fops);
if(ret<0)
{
printk("register fail\r\n");
return -EIO;
}
printk("led_init\r\n");
return 0;
}
//卸载
static void __exit led_exit(void)
{
/*注销字符设备*/
unregister_chrdev(LED_MAJOR,LED_NAME);
printk("led_exit\r\n");
}
/*注册驱动和卸载驱动*/
module_init(led_init);
module_exit(led_exit);
//license
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liujiasen");
3.驱动程序编写
1.初始化led的IO(使能时钟,IO复用,电气属性,输入输出设置)
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define LED_MAJOR 200
#define LED_NAME "led"
//寄存器物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
//虚拟地址
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
#define LEDOFF 0
#define LEDON 1
//led 打开和关闭
void led_switch(u8 sta)
{
u32 val=0;
if(sta==LEDON)
{
val=readl(GPIO1_DR);//读取寄存器的值
val &=~(1<<3);//把bit3值为0 打开led
writel(val,GPIO1_DR);
}
else
{
//close led
val=readl(GPIO1_DR);//读取寄存器的值
val |=(1<<3);//把bit3值为0 close led
writel(val,GPIO1_DR);
}
}
static int led_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int led_release(struct inode *inode,struct file *filp)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{
int retvalue;
unsigned char databuf[1];
retvalue=copy_from_user(databuf,buf,count);
if(retvalue<0)
{
printk("kernel write fail\r\n");
return -EFAULT;
}
//判断开灯还是关灯
led_switch(databuf[0]);
return 0;
}
static const struct file_operations led_fops={
.owner=THIS_MODULE,
.write=led_write,
.open=led_open,
.release=led_release,
};
//注册
static int __init led_init(void)
{
int ret=0;
unsigned int val=0;
/*1.初始化LED灯 地址映射*/
IMX6U_CCM_CCGR1=ioremap(CCM_CCGR1_BASE,4);
SW_MUX_GPIO1_IO03=ioremap(SW_MUX_GPIO1_IO03_BASE,4);
SW_PAD_GPIO1_IO03=ioremap(SW_PAD_GPIO1_IO03_BASE,4);
GPIO1_DR=ioremap(GPIO1_DR_BASE,4);
GPIO1_GDIR=ioremap(GPIO1_GDIR_BASE,4);
/*2.初始化LED灯 时钟*/
val=readl(IMX6U_CCM_CCGR1);//读取寄存器的值
val &=~(3<<26);//把26,27值为0
val |=3<<26;//把26,27值为1
writel(val,IMX6U_CCM_CCGR1);//把值写入寄存器
/*复用*/
writel(5, SW_MUX_GPIO1_IO03);
/*电器属性*/
writel(0x10B0, SW_PAD_GPIO1_IO03);
/*GPIO输入输出*/
val = readl(GPIO1_GDIR);
val &= ~(1 << 3); /* 清除以前的设置 */
val |= (1 << 3); /* 设置为输出 */
writel(val, GPIO1_GDIR);
/*莫认为高点平输出点亮led*/
val=readl(GPIO1_DR);//读取寄存器的值
val &=~(1<<3);//把bit3值为0 打开led
writel(val,GPIO1_DR);
/*1.注册字符设备*/
ret=register_chrdev(LED_MAJOR,LED_NAME,&led_fops);
if(ret<0)
{
printk("register fail\r\n");
return -EIO;
}
printk("led_init\r\n");
return 0;
}
//卸载
static void __exit led_exit(void)
{
unsigned int val=0;
//close led
val=readl(GPIO1_DR);//读取寄存器的值
val |=(1<<3);//把bit3值为0 close led
writel(val,GPIO1_DR);
/*取消地址映射*/
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
/*注销字符设备*/
unregister_chrdev(LED_MAJOR,LED_NAME);
printk("led_exit\r\n");
}
/*注册驱动和卸载驱动*/
module_init(led_init);
module_exit(led_exit);
//license
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liujiasen");
2.进行测试
通过注册和注销点亮和关闭led
4.应用程序编写
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/*
ledAPP <filename> <0:1> 1:open 2close
*/
//应用程序参数个数,传入的具体参数内容,格式为字符串
int main(int argc,char *argv[])
{
int ret=0;
int fd=0;
unsigned char databuf[1];
char *filename;
char readbuf[100],writebuf[100];
static char usrdata[]={"usr data!"};
if(argc!=3)
{
printf("Eroor usage\r\n");
return -1;
}
filename=argv[1];
//RDWR具有读写
fd=open(filename,O_RDWR);
if(fd<0){
printf("can't open file %s\r\n",filename);
return -1;
}
databuf[0]=atoi(argv[2]);
ret=write(fd,databuf,sizeof(databuf));
if(ret<0)
{
printf("write fail\r\n");
close(fd);
return -1;
}
close(fd);
return 0;
}