参考
PS GPIO 测试
bash
# 内核配置
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_ZYNQ=y
gpio子系统测试
Zynq Linux GPIO 编号计算
bash
GPIO编号 = GPIO_BASE + MIO编号
当前: GPIO_BASE = 906
所以:
| LED | MIO | Linux GPIO |
|---|---|---|
| RED | MIO37 | 943 |
| GREEN | MIO38 | 944 |
bash
# 查看 GPIO 控制器
root@antbase:~# cat /sys/class/gpio/gpiochip906/label
zynq_gpio
# 导出红灯
root@antbase:~# echo 943 > /sys/class/gpio/export
root@antbase:~# ls /sys/class/gpio/
export gpio943 gpio953 gpio957 gpiochip906 unexport
root@antbase:~# echo out > /sys/class/gpio/gpio943/direction
# 红灯亮
root@antbase:~# echo 1 > /sys/class/gpio/gpio943/value
# 红灯灭
root@antbase:~# echo 0 > /sys/class/gpio/gpio943/value
# 导出绿灯
root@antbase:~# echo 944 > /sys/class/gpio/export
root@antbase:~# echo out > /sys/class/gpio/gpio944/direction
# 绿灯亮
root@antbase:~# echo 1 > /sys/class/gpio/gpio944/value
# 绿灯灭
root@antbase:~# echo 0 > /sys/class/gpio/gpio944/value
led.ko 驱动测试
led.c
c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
/*
* 字符设备
*/
#define LED_MAJOR 200
#define LED_NAME "led"
/*
* Zynq-7000 PS GPIO寄存器
*
* GPIO:
*
* Bank0:
* MIO0 ~ MIO31
*
* Bank1:
* MIO32 ~ MIO53
*
*/
#define ZYNQ_GPIO_REG_BASE 0xE000A000
/*
* Bank1 GPIO寄存器偏移
*
* DATA:
* Bank0 0x40
* Bank1 0x44
*
* DIRM:
* Bank0 0x204
* Bank1 0x244
*
* OUTEN:
* Bank0 0x208
* Bank1 0x248
*
* INTDIS:
* Bank0 0x214
* Bank1 0x254
*/
#define DATA_OFFSET 0x44 // GPIO 数据寄存器偏移地址
#define DIRM_OFFSET 0x244 // GPIO 方向控制寄存器偏移地址
#define OUTEN_OFFSET 0x248 // GPIO 输出使能寄存器偏移地址
#define INTDIS_OFFSET 0x254 // GPIO 中断禁止寄存器偏移地址
/*
* PS GPIO时钟
*
* APER_CLK_CTRL bit22
*/
#define APER_CLK_CTRL 0xF800012C
/*
* LED GPIO定义
*
* 注意:
*
* MIO编号 != GPIO寄存器bit
*
* Bank1:
*
* MIO32 -> bit0
* MIO33 -> bit1
* ...
*
* MIO37 -> bit5
* MIO38 -> bit6
*
*/
#define RED_PIN 5 // MIO37
#define GREEN_PIN 6 // MIO38
/*
* GPIO虚拟地址
*/
static void __iomem *data_addr;
static void __iomem *dirm_addr;
static void __iomem *outen_addr;
static void __iomem *intdis_addr;
static void __iomem *aper_clk_addr;
/*
* 打开设备
*/
static int led_open(struct inode *inode,
struct file *filp)
{
printk(KERN_INFO "led open\n");
return 0;
}
/*
* 读取
*
* 未实现
*/
static ssize_t led_read(struct file *filp,
char __user *buf,
size_t cnt,
loff_t *offt)
{
return 0;
}
/*
* 写LED控制
*
* 命令:
*
* 0x01 RED ON
* 0x02 RED OFF
*
* 0x03 GREEN ON
* 0x04 GREEN OFF
*
*/
static ssize_t led_write(struct file *filp,
const char __user *buf,
size_t cnt,
loff_t *offt)
{
char cmd;
u32 val;
printk(KERN_INFO
"led_write cnt=%ld\n",
cnt);
/*
* 只接受一个字节
*/
if(cnt != 1)
{
printk(KERN_ERR
"invalid count=%ld\n",
cnt);
return -EINVAL;
}
/*
* 用户空间复制到内核
*/
if(copy_from_user(&cmd,buf,1))
{
printk(KERN_ERR
"copy_from_user failed\n");
return -EFAULT;
}
printk(KERN_INFO
"LED cmd=0x%02x\n",
(unsigned char)cmd);
/*
* 读取当前GPIO状态
*/
val = readl(data_addr);
switch(cmd)
{
/*
* RED ON
*
* MIO37 = 1
*/
case 0x01:
val |= (1U << RED_PIN);
printk(KERN_INFO
"RED ON\n");
break;
/*
* RED OFF
*
* MIO37 = 0
*/
case 0x02:
val &= ~(1U << RED_PIN);
printk(KERN_INFO
"RED OFF\n");
break;
/*
* GREEN ON
*
* MIO38 = 1
*/
case 0x03:
val |= (1U << GREEN_PIN);
printk(KERN_INFO
"GREEN ON\n");
break;
/*
* GREEN OFF
*
* MIO38 = 0
*/
case 0x04:
val &= ~(1U << GREEN_PIN);
printk(KERN_INFO
"GREEN OFF\n");
break;
default:
printk(KERN_WARNING
"unknown cmd=0x%02x\n",
(unsigned char)cmd);
return -EINVAL;
}
/*
* 更新GPIO输出
*/
writel(val,data_addr);
/*
* 等待写入完成
*/
readl(data_addr);
return cnt;
}
/*
* 关闭设备
*/
static int led_release(struct inode *inode,
struct file *filp)
{
printk(KERN_INFO "led release\n");
return 0;
}
static struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static int __init led_init(void)
{
u32 val;
int ret;
/*
* 映射GPIO寄存器
*/
data_addr = ioremap(ZYNQ_GPIO_REG_BASE + DATA_OFFSET,4);
dirm_addr =ioremap(ZYNQ_GPIO_REG_BASE + DIRM_OFFSET,4);
outen_addr =ioremap(ZYNQ_GPIO_REG_BASE + OUTEN_OFFSET,4);
intdis_addr =ioremap(ZYNQ_GPIO_REG_BASE + INTDIS_OFFSET,4);
aper_clk_addr =ioremap(APER_CLK_CTRL,4);
if(!data_addr ||
!dirm_addr ||
!outen_addr ||
!intdis_addr ||
!aper_clk_addr)
{
printk(KERN_ERR
"ioremap failed\n");
return -ENOMEM;
}
/*
* 开启GPIO时钟
*/
val = readl(aper_clk_addr);
val |= (1 << 22);
writel(val,aper_clk_addr);
/*
* 禁止GPIO中断
*
* Bank1:
*
* MIO37 bit5
* MIO38 bit6
*/
val = readl(intdis_addr);
val |= (1 << RED_PIN) |
(1 << GREEN_PIN);
writel(val,intdis_addr);
/*
* 设置输出方向
*
* 1 输出
*/
val = readl(dirm_addr);
val |= (1 << RED_PIN) |
(1 << GREEN_PIN);
writel(val,dirm_addr);
/*
* 开启输出
*/
val = readl(outen_addr);
val |= (1 << RED_PIN) |
(1 << GREEN_PIN);
writel(val,outen_addr);
/*
* 默认关闭LED
*
* 当前假设:
*
* GPIO=0 灭
*
* 如果硬件低有效,
* 这里需要改成置1
*/
val = readl(data_addr);
val &= ~((1 << RED_PIN) |
(1 << GREEN_PIN));
writel(val,data_addr);
/*
* 注册字符设备
*/
ret = register_chrdev(
LED_MAJOR,
LED_NAME,
&led_fops);
if(ret < 0)
{
printk(KERN_ERR
"register_chrdev failed\n");
iounmap(data_addr);
iounmap(dirm_addr);
iounmap(outen_addr);
iounmap(intdis_addr);
iounmap(aper_clk_addr);
return ret;
}
printk(KERN_INFO
"Zynq PS GPIO LED driver loaded\n");
printk(KERN_INFO
"RED=MIO37(GPIO943) GREEN=MIO38(GPIO944)\n");
return 0;
}
static void __exit led_exit(void)
{
u32 val;
/*
* 关闭LED
*/
val = readl(data_addr);
val &= ~((1 << RED_PIN) |
(1 << GREEN_PIN));
writel(val,data_addr);
unregister_chrdev(
LED_MAJOR,
LED_NAME);
iounmap(data_addr);
iounmap(dirm_addr);
iounmap(outen_addr);
iounmap(intdis_addr);
iounmap(aper_clk_addr);
printk(KERN_INFO
"LED driver unloaded\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("ming");
MODULE_DESCRIPTION("Zynq7010 PS GPIO MIO37 MIO38 LED Driver");
MODULE_LICENSE("GPL");
test_led.py
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import os
DEV_PATH = "/dev/led"
def led_write(cmd):
try:
with open(DEV_PATH, "wb") as f:
f.write(chr(cmd))
except Exception as e:
print("LED write error:", e)
def red_on():
print("RED ON")
led_write(0x01)
def red_off():
print("RED OFF")
led_write(0x02)
def green_on():
print("GREEN ON")
led_write(0x03)
def green_off():
print("GREEN OFF")
led_write(0x04)
if __name__ == "__main__":
red_on()
time.sleep(2)
red_off()
green_on()
time.sleep(2)
green_off()
测试
bash
root@antbase:~# insmod led.ko
Zynq PS GPIO LED driver loaded
RED=MIO37 GREEN=MIO38
# 红灯亮
root@antbase:~# printf "\x01" > /dev/led
led open
led_write cnt=1
LED cmd=0x01
RED ON
led release
# 红灯灭
root@antbase:~# printf "\x02" > /dev/led
led open
led_write cnt=1
LED cmd=0x02
RED OFF
led release
# 绿灯亮
root@antbase:~# printf "\x03" > /dev/led
led open
led_write cnt=1
LED cmd=0x03
GREEN ON
led release
# 绿灯灭
root@antbase:~# printf "\x04" > /dev/led
led open
led_write cnt=1
LED cmd=0x04
GREEN OFF
led release
# python 测试
root@antbase:~# python test_led.py
RED ON
led open
led_write cnt=1
LED cmd=0x01
RED ON
led release
RED OFF
led open
led_write cnt=1
LED cmd=0x02
RED OFF
led release
GREEN ON
led open
led_write cnt=1
LED cmd=0x03
GREEN ON
led release
GREEN OFF
led open
led_write cnt=1
LED cmd=0x04
GREEN OFF
led release
newchrled.ko 驱动测试
自动建设备文件/dev/newchrled
bash
alloc_chrdev_region()
|
| 分配设备号
v
cdev_add()
|
| 建立设备号和file_operations关联
v
class_create()
|
| 创建设备类
v
device_create()
|
| 通知内核有新设备
v
udev/mdev
|
| 根据设备号自动创建设备文件
v
/dev/newchrled
# 监听 udev 设备事件
root@antbase:~# udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
newchrled exit
KERNEL[1385.454748] remove /devices/virtual/newchrled/newchrled (newchrled)
KERNEL[1385.454886] remove /newchrled (class)
KERNEL[1385.454961] remove /module/newchrled (module)
UDEV [1385.457424] remove /devices/virtual/newchrled/newchrled (newchrled)
UDEV [1385.458128] remove /newchrled (class)
UDEV [1385.460125] remove /module/newchrled (module)
KERNEL[1392.476993] add /module/newchrled (module)
KERNEL[1392.478146] add /class/newchrled (cnewchrled init ok
lass)
KERNEL[1392.479720] add /devices/virtual/newchrled/newchrled (newchrled)
UDEV [1392.480486] add /module/newchrled (module)
UDEV [1392.482634] add /class/newchrled (class)
UDEV [1392.491062] add /devices/virtual/newchrled/newchrled (newchrled)
root@antbase:~# ls /sys/class/newchrled/
newchrled
root@antbase:~# rmmod newchrled
newchrled exit
root@antbase:~# ls /sys/class/newchrled/
ls: /sys/class/newchrled/: No such file or directory
root@antbase:~# ls /dev/newchrled
ls: /dev/newchrled: No such file or directory
newchrled.c
c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1
#define NEWCHRLED_NAME "newchrled"
/*
* Zynq-7000 PS GPIO寄存器
*
* GPIO基地址:
* 0xE000A000
*
* MIO37:
* GPIO37
* Bank1 bit5
*
* MIO38:
* GPIO38
* Bank1 bit6
*
*
* Bank1寄存器:
*
* DATA1 0x44
* DIRM1 0x244
* OUTEN1 0x248
* INTDIS1 0x254
*
*/
#define ZYNQ_GPIO_REG_BASE 0xE000A000
#define DATA_OFFSET 0x44
#define DIRM_OFFSET 0x244
#define OUTEN_OFFSET 0x248
#define INTDIS_OFFSET 0x254
/*
* GPIO37 / GPIO38
*
* Bank1:
*
* GPIO32 -> bit0
* GPIO33 -> bit1
* ...
*
*/
#define MIO37_BIT 5
#define MIO38_BIT 6
/*
* GPIO时钟控制寄存器
*
* bit22:
* GPIO APB Clock Enable
*
*/
#define APER_CLK_CTRL 0xF800012C
/*
* 虚拟地址
*/
static void __iomem *data_addr;
static void __iomem *dirm_addr;
static void __iomem *outen_addr;
static void __iomem *intdis_addr;
static void __iomem *aper_clk_ctrl_addr;
/*
* 字符设备结构
*/
struct newchrled_dev
{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
};
static struct newchrled_dev newchrled;
/*
* 打开设备
*/
static int led_open(struct inode *inode,
struct file *filp)
{
filp->private_data=&newchrled;
return 0;
}
/*
* 读取
*/
static ssize_t led_read(struct file *filp,
char __user *buf,
size_t cnt,
loff_t *offt)
{
return 0;
}
/*
* 写LED
用户:
echo 1 > /dev/newchrled
MIO37=1
MIO38=1
echo 0 > /dev/newchrled
MIO37=0
MIO38=0
*/
static ssize_t led_write(struct file *filp,
const char __user *buf,
size_t cnt,
loff_t *offt)
{
char value;
u32 reg;
if(copy_from_user(&value,buf,1))
return -EFAULT;
/*
读取DATA1寄存器
*/
reg=readl(data_addr);
if(value==0)
{
/*
输出低
GPIO37 bit5
GPIO38 bit6
*/
reg &= ~(1<<MIO37_BIT);
reg &= ~(1<<MIO38_BIT);
}
else
{
/*
输出高
*/
reg |= (1<<MIO37_BIT);
reg |= (1<<MIO38_BIT);
}
writel(reg,data_addr);
return cnt;
}
/*
关闭设备
*/
static int led_release(struct inode *inode,
struct file *filp)
{
return 0;
}
/*
GPIO寄存器映射
*/
static void led_ioremap(void)
{
data_addr=ioremap(
ZYNQ_GPIO_REG_BASE+DATA_OFFSET,4);
dirm_addr=ioremap(
ZYNQ_GPIO_REG_BASE+DIRM_OFFSET,4);
outen_addr=ioremap(
ZYNQ_GPIO_REG_BASE+OUTEN_OFFSET,4);
intdis_addr=ioremap(
ZYNQ_GPIO_REG_BASE+INTDIS_OFFSET,4);
aper_clk_ctrl_addr=ioremap(
APER_CLK_CTRL,4);
}
static void led_iounmap(void)
{
iounmap(data_addr);
iounmap(dirm_addr);
iounmap(outen_addr);
iounmap(intdis_addr);
iounmap(aper_clk_ctrl_addr);
}
static struct file_operations newchrled_fops=
{
.owner=THIS_MODULE,
.open=led_open,
.read=led_read,
.write=led_write,
.release=led_release,
};
static int __init led_init(void)
{
int ret;
u32 reg;
/*
1. 映射寄存器
*/
led_ioremap();
/*
2. 开GPIO时钟
bit22=1
*/
reg=readl(aper_clk_ctrl_addr);
reg |= (1<<22);
writel(reg,aper_clk_ctrl_addr);
/*
3. 禁止GPIO中断
*/
reg=readl(intdis_addr);
reg |= (1<<MIO37_BIT);
reg |= (1<<MIO38_BIT);
writel(reg,intdis_addr);
/*
4. GPIO方向设置
DIRM:
0 输入
1 输出
*/
reg=readl(dirm_addr);
reg |= (1<<MIO37_BIT);
reg |= (1<<MIO38_BIT);
writel(reg,dirm_addr);
/*
5. 输出使能
*/
reg=readl(outen_addr);
reg |= (1<<MIO37_BIT);
reg |= (1<<MIO38_BIT);
writel(reg,outen_addr);
/*
6. 默认关闭
*/
reg=readl(data_addr);
reg &= ~(1<<MIO37_BIT);
reg &= ~(1<<MIO38_BIT);
writel(reg,data_addr);
/*
7. 注册字符设备
*/
ret=alloc_chrdev_region(
&newchrled.devid,
0,
NEWCHRLED_CNT,
NEWCHRLED_NAME);
if(ret)
return ret;
newchrled.major=MAJOR(newchrled.devid);
newchrled.minor=MINOR(newchrled.devid);
cdev_init(
&newchrled.cdev,
&newchrled_fops);
ret=cdev_add(
&newchrled.cdev,
newchrled.devid,
NEWCHRLED_CNT);
if(ret)
goto err1;
newchrled.class=
class_create(
THIS_MODULE,
NEWCHRLED_NAME);
newchrled.device=
device_create(
newchrled.class,
NULL,
newchrled.devid,
NULL,
NEWCHRLED_NAME);
printk("newchrled init ok\n");
return 0;
err1:
unregister_chrdev_region(
newchrled.devid,
NEWCHRLED_CNT);
led_iounmap();
return ret;
}
static void __exit led_exit(void)
{
device_destroy(
newchrled.class,
newchrled.devid);
class_destroy(newchrled.class);
cdev_del(&newchrled.cdev);
unregister_chrdev_region(
newchrled.devid,
NEWCHRLED_CNT);
led_iounmap();
printk("newchrled exit\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ming");
MODULE_DESCRIPTION("Zynq7010 MIO37 MIO38 GPIO Driver");
测试
bash
root@antbase:~# insmod newchrled.ko
newchrled init ok
# 红黄灭
root@antbase:~# printf "\x00" > /dev/newchrled
# 红黄亮
root@antbase:~# printf "\x01" > /dev/newchrled