编写rk3568驱动点亮led灯(从原理图到代码)

查找硬件

在板子上看led模块,发现有两个led灯: LED1 LED9,但是不知道哪个能用,需要看原理图.

查看原理图

因为LED灯在底板上(下面的是底板,上面的是核心版)

在资料中的topeet_rk3568_main_v1_7.pdf中底板原理图上查找LED

LED1:

直接是串联在电路中,明显是电源灯.

LED9:

可以看出,是由GPIO0_B7控制,并且是H

控制底板的GPIO0_B7引脚是在核心板上的,所以接下来去技术参考手册中找GPIO0_B7

  • 技术参考手册 (TRM):提供详细的寄存器描述、功能模块说明及引脚复用信息。
  • 数据表 (Datasheet):包含引脚定义、电气特性等基本信息。

查找数据手册

在资料中的Rockchip RK3568 TRM Part1 V1.1-20210301.pdf中查找GPIO0_B7,有两个

  1. PMU_GRF_GPIO0B_IOMUX_H:

复用引脚,就是它,当14-12位为0x000时,引脚作用为GPIO0_B7.

  1. PWM:

明显不是.

接着查找GPIO0_B7引脚对应GPIO寄存器的哪一位.

查找GPIO0B_IOMUX_H

发现B7对应gpio0的第15位,意味着操作该位为0/1就能控制灯的亮灭

接着查找PMU_GRF寄存器的地址和偏移量,去设置[14-12]为GPIO0_B7功能

查找PMU_GRF寄存器

目的:设置引脚复用功能为GPIO

地址:

偏移量:

在原理图中看到是H,所以选择H

那么得到

PMU_GRF_GPIO0B_IOMUX_H寄存器地址 = 0xFDC20000 + 0x000C = 0xFDC2000C

接下来查看该地址信息:

io -r -4 0xFDC2000C

从物理地址 0xFDC2000C 读取 4 字节数据。

io:
这是命令的名称,表示这是一个与硬件 IO(输入/输出)相关的工具。
在嵌入式系统中,io 工具通常用于读取或写入硬件寄存器。
-r:
表示 "read"(读取),意味着这个命令是用来从指定地址读取数据的。
如果是写操作,可能会有类似的选项如 -w(write)。
-4:
表示读取的数据宽度为 4 字节(32 位)。
类似的选项可能包括 -1(1 字节)、-2(2 字节)、-8(8 字节),具体取决于工具支持的宽度。
0xFDC2000C:
这是一个十六进制地址,表示要读取的目标寄存器的物理地址。
在嵌入式系统中,每个硬件模块(如 GPIO、UART、I2C 等)都有对应的寄存器映射到内存地址空间中。

发现改地址的值为

[14-12]位为 000,所以默认设置的为GPIO功能.

查找GPIO0寄存器

GPIO0的基地址 = 0xFDD60000

查找端口数据寄存器

有两个

  1. GPIO_SWPORT_DR_L:

控制或读取 GPIO(通用输入输出)端口的低 16 位(bit0 到 bit15)的状态。

  1. GPIO_SWPORT_DR_H:

控制或读取 GPIO(通用输入输出)端口的高 16 位(bit0 到 bit15)的状态。

根据上文找到的B7对应于第15位,是低位,选择 GPIO_SWPORT_DR_L

查找方向控制寄存器

有两个

详细说明:

同端口数据寄存器,选 GPIO_SWPORT_DDR_L

方向选择

查看数据控制描述

翻译:

在软件控制下,信号的数据和方向控制来自
端口数据寄存器 (GPIO_SWPORT_DR_L/GPIO_SWPORT_DR_H) 
和方向控制寄存器 (GPIO_SWPORT_DDR_L/GPIO_SWPORT_DRR_H)。
外部 I/O 引脚的方向由端口数据方向寄存器的值控制。
写入这些内存映射寄存器的数据会被映射到 GPIO 外设的输出信号 (gpio_port_ddr)。
该输出信号控制外部 I/O 引脚的方向。默认数据方向为输入。
写入端口数据寄存器的数据驱动 I/O 引脚的输出缓冲区 (gpio_port_dr)。
外部数据通过外部数据信号 (gpio_ext_port) 输入。
读取外部信号寄存器 (GPIO_EXT_PORT) 即可显示该信号的值,无论方向如何。
该寄存器是只读的,这意味着它不能从 APB 软件接口写入。

想通过GPIO控制LED灯需要设置GPIO为输出模式.

那么: 方向寄存器的地址=基地址+偏移地=0xFDD60000+0x0008=0xFDD60008
然后使用 IO 命令查看该寄存器的值

发现第15位默认为1,设置为输出.

设置数据寄存器

数据寄存器的地址为基地址+偏移地址=0xFDD60000
使用 IO 命令查看地址的值

根据上文,第15位设置的是开关,想要改变第15位值,那么需要将对应的31位使能位也设置为1

计算

要操作的地址:

基地址+数据寄存器偏移地址 = 0xFDD60000

写入数据:

1000 0000 0000 0000 1100 0000 0100 0000= 0x8000c040 亮灯

1000 0000 0000 0000 0100 0000 0100 0000= 0x80004040 灭灯

写入的数据

写对应代码修改目标地址的值.

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

int main(int argc, char *argv[]) {
    char buffRead[32];
    char buffWrite[32] = {0};
    if (argc != 3) {
        printf("params != 3... \n");
        return 0;
    }

    int fd = open(argv[1], O_RDWR, 0666);

    if (fd < 0) {
        perror("open failed... \n");
        return -1;
    }
    printf("file open success... \n");

    buffWrite[0] = atoi(argv[2]);

    if (!strcmp(argv[2], "read")) {
        if (read(fd, buffRead, sizeof(buffRead)) < 0) {
            printf("read filed... \n");
            return -1;
        }
    }

    if(buffWrite[0] == 2) {
        while(1) {
            write(fd, "1", 1);
            sleep(1);
            write(fd, "0", 1);
            sleep(1);
        }
    } else {
        if (write(fd, buffWrite, sizeof(buffWrite)) < 0) {
            printf("write filed... \n");
            return -1;
        }
    }
    printf("led write ok... \n");
    close(fd);
    return 0;
}
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/kthread.h>
#include <linux/delay.h>

#define DEVICE_NAME "createDeviceNode"
#define CLASS_NAME "cdev_class"
#define DEVICENODE_NAME "cdev_node"

#define GPIO_DR_BASE 0xFDD60000

static char kbuf[10] = {0};

struct device_test {
    dev_t dev_num;
    unsigned int major;
    unsigned int minor;
    struct cdev cdev_test;
    struct class* led_class;
    char databuff[32];
    unsigned int* virtual_gpio_dr;
};

struct device_test dev1;

ssize_t ledRead(struct file *, char __user *, size_t, loff_t *);
ssize_t ledWrite(struct file *, const char __user *, size_t, loff_t *);
int ledOpen(struct inode *, struct file *);
int ledRelease(struct inode *, struct file *);

struct file_operations cdev_fileops = {
    .owner = THIS_MODULE,
    .open = ledOpen,
    .read = ledRead,
    .write = ledWrite,
    .release = ledRelease,
};

ssize_t ledRead(struct file *f, char __user *user, size_t size, loff_t *loff)
{
    struct device_test* test = (struct device_test *) f->private_data;
    if(copy_to_user(user, test->databuff, size)) {
        printk("copy_to_user error... \n");
        return -1;
    }
    printk("ledRead... \n");
    return 0;
}

ssize_t ledWrite(struct file *f, const char __user *user, size_t size, loff_t *loff)
{
    struct device_test* test = (struct device_test *) f->private_data;
    if(copy_from_user(test->databuff, user, size)) {
        printk("copy_from_user error... \n");
        return -1;
    }
    printk("ledWrite... \n");
    if(test->databuff[0] == '0') {
        *(test->virtual_gpio_dr) = 0x80004040;
    } else if(test->databuff[0] == '1') {
        *(test->virtual_gpio_dr) = 0x8000c040;
    }
    printk("test->databuff[0] = %d \n", test->databuff[0]);
    
    return 0;
}

int ledOpen(struct inode *node, struct file *f)
{
    f->private_data = &dev1;
    printk("ledOpen... \n");
    return 0;
}

int ledRelease(struct inode *node, struct file *f)
{
    printk("ledRelease... \n");
    return 0;
}

static int __init led_init(void) 
{
    int ret;
    if (dev1.major)
    {
        dev1.dev_num = MKDEV(dev1.major, dev1.minor);
        printk("major = %d \n", dev1.major);
        printk("minor = %d \n", dev1.minor);
        ret = register_chrdev_region(dev1.dev_num, 1, DEVICE_NAME);
        if (ret < 0) {
            printk("register_chrdev_region failed!\n");
        }
        printk("register_chrdev_region successful!\n");
    }
    else{
        // dynamic
        ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, DEVICE_NAME);
        if (ret < 0) {
            printk("alloc_chrdev_region failed!\n");
        }
        printk("alloc_chrdev_region successful!\n");
        dev1.major = MAJOR(dev1.dev_num);
        dev1.minor = MINOR(dev1.dev_num);
        printk("major = %d\n", dev1.major);
        printk("minor = %d\n", dev1.minor);
    }
    printk("led_init\n");
    cdev_init(&dev1.cdev_test, &cdev_fileops);

    ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
    if (ret < 0) {
        printk("cdev_add failed!\n");
    }
    printk("cdev_add successful!\n");

    // 创建class
    dev1.led_class = class_create(THIS_MODULE, CLASS_NAME);
    // 创建device
    device_create(dev1.led_class, NULL, dev1.dev_num, NULL, DEVICENODE_NAME);

    dev1.virtual_gpio_dr = ioremap(GPIO_DR_BASE, 4);

    return 0;
}

static void __exit led_exit(void) {
    // 销毁设备节点
    if (dev1.led_class) {
        device_destroy(dev1.led_class, dev1.dev_num);
        pr_info("device_destroy successful!\n");
    }

    // 销毁设备类
    if (dev1.led_class) {
        class_destroy(dev1.led_class);
        pr_info("class_destroy successful!\n");
    }

    // 删除字符设备
    cdev_del(&dev1.cdev_test);
    pr_info("cdev_del successful!\n");

    // 释放设备号
    unregister_chrdev_region(dev1.dev_num, 1);
    pr_info("unregister_chrdev_region successful!\n");

    // 释放虚拟地址
    iounmap(dev1.virtual_gpio_dr);
    pr_info("iounmap ok! \n");

    pr_info("Module unloaded successfully.\n");
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HUXIAO");
MODULE_DESCRIPTION("XXX machine main controLler board IO test driver");
MODULE_VERSION("1.0.0");


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值