查找硬件
在板子上看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,有两个
- PMU_GRF_GPIO0B_IOMUX_H:
复用引脚,就是它,当14-12位为0x000时,引脚作用为GPIO0_B7.
- 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
查找端口数据寄存器
有两个
- GPIO_SWPORT_DR_L:
控制或读取 GPIO(通用输入输出)端口的低 16 位(bit0 到 bit15)的状态。
- 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");