Linux字符设备驱动——ZYNQ PS_LED(MIO9)驱动开发

原理图

PS_LED原理图
PS_LED连接在MIO9上,ZYNQ的GPIO中,BANK0和BANK1通过MIO连接在PS端,BANK2和BANK3通过EMIO连接在PL端。
GPIO块图

GPIO寄存器

GPIO基地址
由上图可知,ZYNQ GPIO寄存器的基地址是 0xE000A000。

在这里插入图片描述
GPIO相关寄存器
上图中为GPIO的相关寄存器,为了控制PS_LED,只需关注上图中红色框里的几个特定寄存器即可。

DATA寄存器

DATA_0寄存器
DATA寄存器就是控制管脚输出高低电平的,所以在输出模式下,就可以通过对寄存器的相应 bit位写 0或 1来控制某个 GPIO输出电平为高还是低。

DIRM寄存器

在这里插入图片描述DIRM_0寄存器
方向控制寄存器控制GPIO的输入和输出。

OUTEN寄存器

在这里插入图片描述
OEN_0寄存器

GPIO时钟

时钟控制寄存器
APER_CLK_CTRL寄存器属于系统级别的控制寄存器。
在这里插入图片描述
GPIO时钟寄存器细节
该寄存器用于控制ZYNQ AMBA外设时钟,从图中可以知道该寄存器的 bit22位 GPIO时钟控制位,向该位写入 0禁止 GPIO时钟,写入 1则使能 GPIO时钟。

驱动编写

/** ===================================================== **
 *Author : ALINX Electronic Technology (Shanghai) Co., Ltd.
 *Website: http://www.alinx.com
 *Address: Room 202, building 18, 
           No.518 xinbrick Road, 
           Songjiang District, Shanghai
 *Created: 2020-3-2 
 *Version: 1.0
 ** ===================================================== **/

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/ide.h>  
#include <linux/types.h>  
  
/* 驱动名称 */  
#define DEVICE_NAME       "gpio_leds"  
/* 驱动主设备号 */  
#define GPIO_LED_MAJOR    200  
  
/* gpio寄存器虚拟地址 */  
static unsigned int gpio_add_minor;  
/* gpio寄存器物理基地址 */  
#define GPIO_BASE         0xE000A000  
/* gpio寄存器所占空间大小 */  
#define GPIO_SIZE         0x1000  
/* gpio方向寄存器 */  
#define GPIO_DIRM_0       (unsigned int *)(0xE000A204 - GPIO_BASE + gpio_add_minor)  
/* gpio使能寄存器 */   
#define GPIO_OEN_0        (unsigned int *)(0xE000A208 - GPIO_BASE + gpio_add_minor)  
/* gpio控制寄存器 */  
#define GPIO_DATA_0       (unsigned int *)(0xE000A040 - GPIO_BASE + gpio_add_minor)  
  
/* 时钟使能寄存器虚拟地址 */  
static unsigned int clk_add_minor;  
/* 时钟使能寄存器物理基地址 */  
#define CLK_BASE          0xF8000000  
/* 时钟使能寄存器所占空间大小 */  
#define CLK_SIZE          0x1000  
/* AMBA外设时钟使能寄存器 */  
#define APER_CLK_CTRL     (unsigned int *)(0xF800012C - CLK_BASE + clk_add_minor)        
  
/* open函数实现, 对应到Linux系统调用函数的open函数 */  
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)  
{  
	/* 把需要修改的物理地址映射到虚拟地址 */
	gpio_add_minor = (unsigned int)ioremap(GPIO_BASE, GPIO_SIZE);  
	clk_add_minor = (unsigned int)ioremap(CLK_BASE, CLK_SIZE); 

    /* MIO_0时钟使能 */  
   // *APER_CLK_CTRL |= 0x00400000; 
*APER_CLK_CTRL |= (0x1U << 22); 

    /* MIO_0设置成输出 */  
  //  *GPIO_DIRM_0 |= 0x00000001;  
*GPIO_DIRM_0 |= (0x1U << 9); 
    /* MIO_0使能 */  
   // *GPIO_OEN_0 |= 0x00000001;  
*GPIO_OEN_0 |= (0x1U << 9); 
      
    printk("gpio_test module open\n");  
      
    return 0;  
}  
  
  
/* write函数实现, 对应到Linux系统调用函数的write函数 */  
static ssize_t gpio_leds_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)  
{  
    int rst;  
    char writeBuf[5] = {0};  
      
    printk("gpio_test module write\n");  
  
    rst = copy_from_user(writeBuf, buf, len);  
    if(0 != rst)  
    {  
        return -1;    
    }  
      
    if(1 != len)  
    {  
        printk("gpio_test len err\n");  
        return -2;  
    }  
    if(1 == writeBuf[0])  
    {  
      //  *GPIO_DATA_0 &= 0xFFFFFFFE; 
       *GPIO_DATA_0 &= (0xFFFFFFFEU << 9 ); 
        printk("gpio_test ON\n");  
    }  
    else if(0 == writeBuf[0])  
    {  
     //   *GPIO_DATA_0 |= 0x00000001; 
*GPIO_DATA_0 |= (0x1U << 9);
        printk("gpio_test OFF\n");  
    }  
    else  
    {  
        printk("gpio_test para err\n");  
        return -3;  
    }  
      
    return 0;  
}  
  
/* release函数实现, 对应到Linux系统调用函数的close函数 */  
static int gpio_leds_release(struct inode *inode_p, struct file *file_p)  
{     	
    printk("gpio_test module release\n");  
    return 0;  
}  
	  
/* file_operations结构体声明, 是上面open、write实现函数与系统调用函数对应的关键 */  
static struct file_operations gpio_leds_fops = {  
    .owner   = THIS_MODULE,  
    .open    = gpio_leds_open,  
    .write   = gpio_leds_write,     
    .release = gpio_leds_release,   
};  
  
/* 模块加载时会调用的函数 */  
static int __init gpio_led_init(void)  
{  
    int ret;  
      
    /* 通过模块主设备号、名称、模块带有的功能函数(及file_operations结构体)来注册模块 */  
    ret = register_chrdev(GPIO_LED_MAJOR, DEVICE_NAME, &gpio_leds_fops);  
    if (ret < 0)   
    {  
        printk("gpio_led_dev_init_ng\n");  
        return ret;  
    }  
    else  
    {  
        /* 注册成功 */ 
        printk("gpio_led_dev_init_ok\n");  
    }  
    return 0;  
}  
  
/* 卸载模块 */  
static void __exit gpio_led_exit(void)  
{  
    /* 释放对虚拟地址的占用 */  
    iounmap((unsigned int *)gpio_add_minor);  
    iounmap((unsigned int *)clk_add_minor); 
    /* 注销模块, 释放模块对这个设备号和名称的占用 */  
    unregister_chrdev(GPIO_LED_MAJOR, DEVICE_NAME);
	
    printk("gpio_led_dev_exit_ok\n");  
}  
  
/* 标记加载、卸载函数 */  
module_init(gpio_led_init);  
module_exit(gpio_led_exit);  
  
/* 驱动描述信息 */  
MODULE_AUTHOR("Alinx");  
MODULE_ALIAS("gpio_led");  
MODULE_DESCRIPTION("GPIO LED driver");  
MODULE_VERSION("v1.0");  
MODULE_LICENSE("GPL");  


测试应用程序编写

/** ===================================================== **
 *Author : ALINX Electronic Technology (Shanghai) Co., Ltd.
 *Website: http://www.alinx.com
 *Address: Room 202, building 18, 
           No.518 xinbrick Road, 
           Songjiang District, Shanghai
 *Created: 2020-3-2 
 *Version: 1.0
 ** ===================================================== **/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd;
    char buf;

    /* 验证输入参数个数 */
    if(3 != argc)
    {
        printf("none para\n");
        return -1;
    }

    /* 打开输入的设备文件, 获取文件句柄 */
    fd = open(argv[1], O_RDWR);
    if(fd < 0)
    {
        /* 打开文件失败 */
        printf("Can't open file %s\r\n", argv[1]);
        return -1;
    }

    /* 判断输入参数, on就点亮led, off则熄灭led */
    if(!strcmp("on",argv[2]))
    {
        printf("ps_led1 on\n");
        buf = 1;
        write(fd, &buf, 1);
    }
    else if(!strcmp("off",argv[2]))
    {
        printf("ps_led1 off\n");
        buf = 0;
        write(fd, &buf, 1);
    }
    else
    {
        /* 输入参数错误 */
        printf("wrong para\n");
        return -2;
    }

    /* 操作结束后关闭文件 */
    close(fd);
    return 0;
}

驱动程序编译与应用程序编译

通过ptealinux编译驱动程序,使用交叉编译工具编译应用程序,将驱动和应用程序转移到ZYNQ开发板。
应用程序和驱动

******驱动加载*********
insmod ax-led-drv.ko       		加载驱动模块
cat /proc/devices          		查看已经被使用的设备号
mknod /dev/alinx-led c 200 0	创建设备文件
ls /dev							查找设备文件
./a.out /dev/alinx-led on		执行程序,开灯
./a.out /dev/alinx-led off		执行程序,关灯
rmmod ax_led_drv				卸载驱动模块
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值