嵌入式 Linux LED 驱动开发实验

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 





#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");

程序说明:

  1. 字符设备驱动中,不可对物理地址进行操作,需要将物理地址映射成虚拟地址
#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);函数进行释放虚拟地址。

#include 

#include 

#include 

#include 

#include 

#include 

#include 

/*

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;

}

程序说明:

  1. int writevalue=atoi(argv[2]);进行数据转换,将字符型数据转化为整型数据。

     2.在驱动模块加载完成以后,使用mknod /dev/led c 200 0创建设备节点,使用./ledAPP /dev/led 1来执行应用程序。

你可能感兴趣的:(Linux驱动开发,字符驱动框架,驱动开发,linux,运维)