蚂蚁S9矿板PS led驱动的三个实验-第1课 字符设备驱动

参考

蚂蚁S9矿板引脚定义.csdn

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
相关推荐
江南十四行1 小时前
Maven 学习框架:依赖管理 + 仓库配置 + IDEA 集成
学习·maven·intellij-idea
其实防守也摸鱼2 小时前
镜像校验完成iso完整操作流程(Windows 环境,适配 Ubuntu 22.04 / 24.04)
linux·服务器·windows·学习·ubuntu·教程
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:基本查询(下)
android·数据库·学习·mysql·面试·八股文
晓梦林2 小时前
[极客大挑战 2019]Secret File学习笔记
笔记·学习
STLearner3 小时前
ICML 2026 | LLM×Graph论文总结[2]【Graph4LLM,Graph4Agent,智能体记忆(Memory)
大数据·人工智能·python·深度学习·学习·机器学习·数据挖掘
其实防守也摸鱼4 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·运维·网络·人工智能·学习·安全·macos
YM52e9 小时前
鸿蒙Flutter Padding内边距:EdgeInsets详解
android·学习·flutter·华为·harmonyos·鸿蒙
西安老张(AIGC&ComfyUI)11 小时前
第034章:ComfyUI&AIGC一阶段学习总结及下阶段学习安排
学习·aigc
正经人_x14 小时前
学习日记43:FINO
学习