Linux驱动编程【直接操作寄存器控制LED】

一、内存映射

        1、裸机LED灯实验就是操作6ULL的寄存器

        2、Linux驱动开发也可以操作寄存器。Linux下不能直接对寄存器的物理地址进行读写操作,比如寄存器A物理地址为0x01010101。裸机可以直接对0x01010101的物理地址进行操作,但是在Linux下不行。因为linux会使能MMU。

        在Linux里面操作的都是虚拟地址,所以需要先得到0x01010101这个物理地址所对应的虚拟地址

        MMU:       

         ①、完成虚拟内存到物理内存的映射

         ②、内存保护,设置存储器的访问权限

Linux驱动编程【直接操作寄存器控制LED】_第1张图片

Ⅰ、ioremap函数

        用于获取指定物理地址空间对应的虚拟地址

        #define ioremap (cookie,size)

                cookie:物理地址的起始地址

                size :要转换的字节数量

        例如:0x01010101开始10个地址进行转换

                va = ioremap(0x01010101, 10)

Ⅱ、iounmap函数

        卸载驱动时,获取的虚拟地址进行释放

        #define iounmap (va)

二、LED灯字符设备驱动框架搭建

#include 
#include 
#include 
#include 
#include 
#include 

#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_GDIR_BASE         (0X0209C004)
#define GPIO1_DR_BASE           (0X0209C000)

/* 虚拟地址 */
static void __iomem *IMX6ULL_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_GDIR;
static void __iomem *GPIO1_DR;

#define LEDOFF  0
#define LEDON   1
/*LED 亮/灭*/
void led_switch(u8 sta)
{
    u32 val = 0;
    if(sta == LEDOFF) {
        val = readl(GPIO1_DR);
        val |= (1 << 3); 
        writel(val, GPIO1_DR);
    } else if(sta == LEDON) {
        val = readl(GPIO1_DR);
        val &= ~(1 << 3); 
        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 ret;
    unsigned char databuf[1];

    ret = copy_from_user(databuf, buf, count);
    if(ret < 0) {
        printk("kernel wirte failed");
        return -EFAULT;
    }

    /* open or off*/
    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;
    int val = 0;
    /*1. 初始化虚拟地址*/
    IMX6ULL_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_GDIR        = ioremap(GPIO1_GDIR_BASE, 4);
    GPIO1_DR          = ioremap(GPIO1_DR_BASE, 4);
    /*2. 使能时钟*/
    val = readl(IMX6ULL_CCM_CCGR1);
    val &= ~(3 << 26);
    val |= 3 << 26; 
    writel(val, IMX6ULL_CCM_CCGR1);

    writel(0x5, SW_MUX_GPIO1_IO03);/*端口复用*/
    writel(0x10B0, SW_PAD_GPIO1_IO03);/*电器属性*/
    
    val = readl(GPIO1_GDIR);
    val |= 1 << 3; 
    writel(val, GPIO1_GDIR);
    /*点亮LED*/

    val = readl(GPIO1_DR);
    val &= ~(1 << 3); 
    writel(val, GPIO1_DR);

    /*2. 注册字符驱动 */
    ret = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
    if(ret < 0) {
        printk("register chrdev failed\r\n");
        return -EIO;
    }

    printk("len_init \r\n");
    return 0;
}
/* 卸载驱动 */
static void __exit led_exit(void)
{   
    /*关闭LED*/
    int val = 0;
    val = readl(GPIO1_DR);
    val |= (1 << 3); 
    writel(val, GPIO1_DR);
    /*注销虚拟内存*/

    iounmap(IMX6ULL_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_GDIR);
    iounmap(GPIO1_DR);

    unregister_chrdev(LED_MAJOR, LED_NAME);
    printk("led_exit \r\n");
}
/*驱动模块加载*/
module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhang");



三、应用APP搭建

#include 
#include 
#include 
#include 
#include 
#include 
/*
*argc:命令行参数个数
*argv: 0/1
*函数功能:添加LED驱动 按0 -> LEDON 1-> LEDOFF
*/

#define LEDOFF 0
#define LEDON  1
int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    filename = argv[1];
    unsigned char databuf[1];
    if(argc != 3) {
        printf("Error Usage!\r\n");
        return -1;
    }
    
    fd = open(filename, O_RDWR);
    if(fd < 0) {
        printf("open failed\r\n");
        return -1;
    }
    databuf[0] = atoi(argv[2]);
    ret = write(fd, databuf, sizeof(databuf));
    
    close(fd);
    return 0;
}

你可能感兴趣的:(linux)