3IM6ULL LINUX驱动之LED字符驱动设备

223 阅读3分钟

一、Led点灯实验

我们肯定是需要往寄存器内些数据的,Linux下也可以操作寄存器,但是不能直接对物理地址进行直接读写操作。因为linux下会使能MMU,MMU是内存管理单元,在老Linux中要求处理器必须有MMU,它主要完成功能:

①、虚拟空间到物理空间的映射

②、内存保护、设置存储器的访问权限。设置虚拟存储空间的缓冲特性。

1.地址映射

操作4G的虚拟地址,对虚拟地址使用ioremap函数,写在驱动使用iounmap函数。函数作用:将物理地址传入以后指向虚拟地址。

phys_addr:物理地址的其实大小。

size:转化的字节数量。

void __iomem *ioremap (unsigned long phys_addr, unsigned long size);
void iounmap (volatile void __iomem *addr);

过程创建:

  1. 定义物理地址

  2. 定义映射后的虚拟指针,如下

    static void __iomem *IMX6U_CCM_CCGR1;
    
    3.  使用ioremap进行字节对齐,映射到虚拟地址上。
    ​
    4.  使用虚拟地址进行读写操作寄存器。
    ​
    5.  释放程序之前记得iounmap释放掉地址。
    

2.LED驱动

1.框架搭建

2.初始化LED -->

/*
    1.映射地址
    2.初始化时钟
    3.复用IO,电气特性配置
    4.DR寄存器写入
    
注:调试的时候,这个地方出了问题:
    具体原因是按照裸机编程的时候使用的是NXP官方提供的接口,所以只需要IO的MUX地址,从而忽略了手册当中使用的PAD地址。
*/

3.补充fops的函数指针。

根据Led应用程序,写出相应的驱动read , write接口函数。

3.led_driver.c程序

#include<linux/module.h>
#include<linux/init.h> 
#include<linux/fs.h>
#include <asm/uaccess.h>
#include<linux/io.h>
/*
    load module : mknod /dev/led c 200 0
*/
#define LED_MAJOR 200
#define LED_NAME  "led"

#define LED_OFF     0
#define LED_ON      1
#define LED_RED     0
#define LED_GREEN   1
#define LED_BLUE    2

#define CLEAR_TMP(x)    (x = 0)

/*寄存器物理地址*/
#define     CCM_CCGR1_BASE                          (0x020C406C)    //GPIO1的CCM1
#define     CCM_CCGR3_BASE                          (0x020C4074)    //GPIO4的CCM3

#define     IOMUXC_GPIO1_IO04_GPIO1_IO04_BASE       (0x020E006C)    //红灯的复用寄存器
#define     IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04_BASE   (0x020E02F8)    //红灯的电气特性寄存器

#define     IOMUXC_CSI_HSYNC_GPIO4_IO20_BASE        (0x020E01E0)
#define     IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC_BASE    (0X020E046C)

#define     IOMUXC_CSI_VSYNC_GPIO4_IO19_BASE        (0x020E01DC)
#define     IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE    (0X020E0468)

#define     GPIO1_DR_BASE                           (0x0209C000)    //GPIO1_DR
#define     GPIO1_GDIR_BASE                         (0x0209C004)    //GPIO1_GDIR
#define     GPIO4_DR_BASE                           (0x020A8000)    //GPIO4_DR
#define     GPIO4_GDIR_BASE                         (0x020A8004)    //GPIO4_GDIR

/*地址映射后的虚拟地址指针*/
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *IMX6U_CCM_CCGR3;
static void __iomem *IOMUXC_GPIO1_IO04_GPIO1_IO04;
static void __iomem *IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04;
static void __iomem *IOMUXC_CSI_HSYNC_GPIO4_IO20;
static void __iomem *IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC;
static void __iomem *IOMUXC_CSI_VSYNC_GPIO4_IO19;
static void __iomem *IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
static void __iomem *GPIO4_DR;
static void __iomem *GPIO4_GDIR;

unsigned char readbuff[10];
unsigned char writebuff[1];

/*
    LED模块化程序
*/
void led_on(unsigned char led_num){
	u32 tmp = 0;
    switch(led_num){
        case LED_RED:
            tmp = readl(GPIO1_DR);              /*DR寄存器 , 低电平打开LED*/
            tmp &= ~(1 << 4);
            writel(tmp , GPIO1_DR);
            break;
        case LED_GREEN:
            tmp = readl(GPIO4_DR);              /*DR寄存器 , 低电平打开LED*/
            tmp &= ~(1 << 20);
            writel(tmp , GPIO4_DR);
            break;
        case LED_BLUE:
            tmp = readl(GPIO4_DR);              /*DR寄存器 , 低电平打开LED*/
            tmp &= ~(1 << 19);
            writel(tmp , GPIO4_DR);
            break;
        default:
            printk(" invalid commond \r\n");
            break;
    }
}
void led_off(unsigned char led_num){
	u32 tmp = 0;
    switch(led_num){
        case LED_RED:
            tmp = readl(GPIO1_DR);              /*DR寄存器 , 高电平关闭LED*/
            tmp |= (1 << 4);
            writel(tmp , GPIO1_DR);
            break;
        case LED_GREEN:
            tmp = readl(GPIO4_DR);              /*DR寄存器 , 高电平关闭LED*/
            tmp |= (1 << 20);
            writel(tmp , GPIO4_DR);
            break;
        case LED_BLUE:
            tmp = readl(GPIO4_DR);              /*DR寄存器 , 高电平关闭LED*/
            tmp |= (1 << 19);
            writel(tmp , GPIO4_DR);
            break;
        default:
            printk(" invalid commond \r\n");
            break;
    }
}
void Led_Init(void){
    u32 tmp = 0;
/*  1.初始化LED地址映射 */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE , 4);
    IMX6U_CCM_CCGR3 = ioremap(CCM_CCGR3_BASE , 4);
    IOMUXC_GPIO1_IO04_GPIO1_IO04 = ioremap(IOMUXC_GPIO1_IO04_GPIO1_IO04_BASE , 4);
    IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04 = ioremap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04_BASE , 4);
    IOMUXC_CSI_HSYNC_GPIO4_IO20 = ioremap(IOMUXC_CSI_HSYNC_GPIO4_IO20_BASE , 4);
    IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC = ioremap(IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC_BASE , 4);
    IOMUXC_CSI_VSYNC_GPIO4_IO19 = ioremap(IOMUXC_CSI_VSYNC_GPIO4_IO19_BASE , 4);
    IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC = ioremap(IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE , 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE , 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE , 4);
    GPIO4_DR = ioremap(GPIO4_DR_BASE , 4);
    GPIO4_GDIR = ioremap(GPIO4_GDIR_BASE , 4);

    /*  2.初始化时钟    */
    tmp = readl(IMX6U_CCM_CCGR1);       /*[27,26]GPIO1的时钟*/
    tmp &= ~(3 << 26);
    tmp |= (3 << 26);
    writel(tmp , IMX6U_CCM_CCGR1);
    CLEAR_TMP(tmp);

    tmp = readl(IMX6U_CCM_CCGR3);       /*[13,12]GPIO4的时钟*/
    tmp &= ~(3 << 12);
    tmp |= (3 << 12);
    writel(tmp , IMX6U_CCM_CCGR3);
    CLEAR_TMP(tmp);

    /*  3.电气特性设置    */
    writel(0x5 , IOMUXC_GPIO1_IO04_GPIO1_IO04);     /*复用GPIO*/
    writel(0x10b0 , IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04);  /*电气特性设置*/

    writel(0x5 , IOMUXC_CSI_HSYNC_GPIO4_IO20);
    writel(0x10b0 , IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC);

    writel(0x5 , IOMUXC_CSI_VSYNC_GPIO4_IO19);
    writel(0x10b0 , IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC);

    /*  4.寄存器设置    */
    tmp = readl(GPIO1_GDIR);            /*GDIR寄存器为输出*/
    tmp |= (1 << 4);
    writel(tmp , GPIO1_GDIR);
    led_off((unsigned char)LED_RED);
    CLEAR_TMP(tmp);

    tmp = readl(GPIO4_GDIR);            /*GDIR寄存器为输出*/
    tmp |= (1 << 20);
    writel(tmp , GPIO4_GDIR);
    led_off((unsigned char)LED_GREEN);
    CLEAR_TMP(tmp);
    
    tmp = readl(GPIO4_GDIR);            /*GDIR寄存器为输出*/
    tmp |= (1 << 19);
    writel(tmp , GPIO4_GDIR);
    led_off((unsigned char)LED_BLUE);
    CLEAR_TMP(tmp);
}
void led_switch(u8 status , u8 led_num){
    if(status == LED_ON){
        switch(led_num){
            case LED_RED:
                led_on((unsigned int)LED_RED);
                break;
            case LED_GREEN:
                led_on((unsigned int)LED_GREEN);
                break;
            case LED_BLUE:
                led_on((unsigned int)LED_BLUE);
                break;
        }
    }else if(status == LED_OFF){
        switch(led_num){
            case LED_RED:
                led_off((unsigned int)LED_RED);
                break;
            case LED_GREEN:
                led_off((unsigned int)LED_GREEN);
                break;
            case LED_BLUE:
                led_off((unsigned int)LED_BLUE);
                break;
        }
    }
}
/*
    fileoperation的实现
*/
static int led_open(struct inode *inode, struct file *filp){
    printk("------led_open-------\r\n");
    led_on((unsigned int)LED_RED);
    led_on((unsigned int)LED_GREEN);
    led_on((unsigned int)LED_BLUE);
    return 0;
}
int led_release(struct inode *inode, struct file *filp){
    printk("------led_release--------\r\n");
    led_off((unsigned int)LED_RED);
    led_off((unsigned int)LED_GREEN);
    led_off((unsigned int)LED_BLUE);
    return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){
    unsigned int ret = 0;
	printk("---------led_read--------\r\n");
    ret = copy_to_user(buf, readbuff, count);
    if(ret == 0){
		printk("kernel sendata:%s \r\n" , readbuff);
    }else{
        
    }
    return 0;
}

ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){
    int ret = 0 ;
    unsigned char databuf[1];   //接受数据
	printk("---------led_write--------\r\n");
    ret = copy_from_user(databuf , buf , count);
	printk("databuf : %d\n" , databuf[0]);
    if(ret == 0){
		if(databuf[0] == LED_ON){
            led_on((unsigned int)LED_RED);
            led_on((unsigned int)LED_GREEN);
            led_on((unsigned int)LED_BLUE);
        }else if(databuf[0] == LED_OFF){
            led_off((unsigned int)LED_RED);
            led_off((unsigned int)LED_GREEN);
            led_off((unsigned int)LED_BLUE);
        }
    }else{
        printk("app write error\r\n");
        return -EFAULT;
    }
    return 0;
}
static const struct file_operations led_fops = {
    .owner      = THIS_MODULE,
    .open       = led_open,
    .release    = led_release,
    .read       = led_read,
    .write      = led_write,
};

/*
    注册与注销设备
*/
static int __init led_init(void){
    int major = 0;
    printk("-----led_init-----\n");
    Led_Init();                             //LED初始化

    // /*注册设备号*/
    if( (major = register_chrdev(LED_MAJOR, LED_NAME, &led_fops)) < 0){
        printk("LED设备号申请错误!\n");
        return -EIO;
    }

    return 0;
}
static void __exit led_exit(void){
    printk("-----Led_exit-----\n");

    /*取消LED地址映射*/
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(IMX6U_CCM_CCGR3);
    iounmap(IOMUXC_GPIO1_IO04_GPIO1_IO04);
    iounmap(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04);
    iounmap(IOMUXC_CSI_HSYNC_GPIO4_IO20);
    iounmap(IOMUXC_SW_PAD_CTL_PAD_CSI_HSYNC);
    iounmap(IOMUXC_CSI_VSYNC_GPIO4_IO19);
    iounmap(IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
    iounmap(GPIO4_DR);
    iounmap(GPIO4_GDIR);

    unregister_chrdev(LED_MAJOR, LED_NAME);

}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

4.led_app.c

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/*
    要求命令    ./led_app <dev_nod> <read/write>
*/
int main(int argc, char *argv[])
{
    int fd = 0 , ret = 0 ;
	int tmp = atoi(argv[2]);
    char read_buff[10] , write_buff[1];
    
	if(argc != 3){
        printf("Error  :  argc at list 3!\r\n");
        return 0;
    }
    fd = open("/dev/led" , O_RDWR);
    if(fd < 0){
        printf("打开LED设备结点失败\n");
        return -1;
    }
    while(1){
        printf("Please enter commond:\r\n");
        printf("LED open? close?\r\n");
        printf("Open :1 \nClose:0\n");
        scanf("%d" , write_buff);

        if(tmp == 0){
			printf("-------->read\n");
            ret = read(fd , read_buff  ,10);
            if(ret < 0){
                printf("read error!\n");
            }
            else{
            }
        }
        else if(tmp == 1){
			printf("-------->write\n");
            ret = write(fd , write_buff  ,sizeof(write_buff));
            if(ret < 0){
                printf("write error!\n");
            }
            else{
            }
        }
		printf("writebuff : %d\n\n\n" , write_buff);
    }
    close(fd);
    return 0;
}

5.测试程序

1、在控制台中创建设备结点

2、加载驱动

3、使用应用程序

mknod /dev/led c 200 0
cd mydrivers
insmod led_driver.c
./led_app /dev/led 1