当前位置:首页>Linux>用 Linux 驱动点亮一颗 LED —— ioremap、RK3568 寄存器与真实硬件

用 Linux 驱动点亮一颗 LED —— ioremap、RK3568 寄存器与真实硬件

  • 2026-06-30 17:40:45
用 Linux 驱动点亮一颗 LED —— ioremap、RK3568 寄存器与真实硬件

上一篇《从零写一个能用的字符设备驱动》我们写了一个能读能写的 myfifo 字符设备驱动。但它只是个"纯内存设备"——数据在内核缓冲区里兜一圈,跟硬件没半毛钱关系。今天,我们把 ioremap 加上,操作 RK3568 的真实 GPIO 寄存器,用驱动点亮板子上的一颗 LED。从虚拟设备迈向物理世界。


unsetunset前置知识(一):MMU 是什么?为什么驱动开发绕不开它?unsetunset

在上一篇 myfifo 驱动里,我们没碰任何硬件寄存器,所以 MMU 对你来说是透明的。但一旦你要操作 GPIO 寄存器,MMU 就成了第一道门槛。

一个直觉类比:图书馆的书号 vs 书架位置

想象你是图书馆管理员。每本书都有一个 ISBN 号(比如 978-7-121-xxxxx-x),但你在书架上找书时用的是书架位置(比如"3 楼 A 区 第 5 排 第 2 格")。ISBN 号和书架位置之间需要一个映射表来对应。

MMU(Memory Management Unit,内存管理单元)干的就是这件事:CPU 核发出的地址是"虚拟地址"(VA),MMU 查页表把它翻译成"物理地址"(PA),然后才能真正访问到内存或外设寄存器。

为什么 CPU 不直接访问物理地址?

三个核心原因:

1. 进程隔离(安全)。如果每个用户程序都能直接看到物理地址,那程序 A 就可以偷看程序 B 的内存,甚至篡改内核的数据。虚拟地址让每个进程都有自己的"地址空间",彼此隔离。

2. 内存碎片化。物理内存可能被各种外设的固定地址割裂得七零八落,但 MMU 可以把零散的物理页映射成连续的虚拟地址空间,让每个进程都以为自己独占了一大块连续内存。

3. 外设寄存器访问控制。外设寄存器(比如 GPIO 控制器)的物理地址是固定的(RK3568 上 GPIO0 就是 0xFDD60000)。内核不能直接拿这个物理地址去解引用,必须通过 MMU 建立一个"Device 类型"的映射,以保证:

  • 访问不能重排(你写的"先关中断再清标志位"不能被 CPU 反过来执行)
  • 访问不能合并(两次 writel 不能优化成一次)
  • 不对齐访问要报错(寄存器通常是 4 字节对齐的)

MMU 和 ioremap 的关系

ioremap 做的事情,本质上就是在 MMU 的页表里新建一条映射:把外设寄存器的物理地址,映射到一个内核虚拟地址。映射完成后,你用 readl/writel 访问这个虚拟地址,MMU 自动把它翻译到对应的物理寄存器。整个过程对开发者是透明的——你不需要手动写页表,但需要理解这套机制的存在。


unsetunset前置知识(二):ioremap —— 给物理寄存器"办一张虚拟地址身份证"unsetunset

裸机 vs Linux:两句代码背后的天壤之别

如果你写过 STM32 裸机程序,你对这种写法一定不陌生:

// STM32 裸机 — 直接解引用物理地址#define GPIOA_ODR  (*(volatile uint32_t *)0x40020014)GPIOA_ODR |= (1 << 5);   // PA5 输出高电平,一句搞定

裸机上没有 MMU(或者说 MMU 没开),CPU 发出的地址就是物理地址,直接 * 解引用没问题。

但在 Linux 内核里,这样写会直接翻车。 内核开启了 MMU,0xFDD60000 这个值在当前的页表里压根没有映射。硬去 *(volatile int *)0xFDD60000 解引用,CPU 会触发 data abort——内核 oops,驱动挂掉。

正确的做法分两步

// 第 1 步:ioremap 建立映射("办身份证")void __iomem *DR;DR = ioremap(0xFDD600044);  // 把物理地址 0xFDD60004 开始 4 字节映射进来// 第 2 步:用 readl/writel 访问("刷卡")u32 val = readl(DR);          // 读寄存器writel(val | (1 << 0), DR);   // 写寄存器// 用完记得释放iounmap(DR);

用生活化的比喻来理解:

概念
比喻
寄存器物理地址 0xFDD60000
你家在房管局登记的坐标(经纬度)
ioremap
去派出所登记户口,拿到一个社会认可的住址
映射后的内核虚拟地址
你拿到的身份证上的地址("XX市XX路XX号")
readl/writel
快递员按身份证地址送货
iounmap注销户口,地址回收

没人能直接按经纬度收快递,同样,内核不能直接拿物理地址去解引用。

为什么还需要 readl/writel,不能直接用 *

哪怕 ioremap 返回的地址"看起来"是一个普通指针:

void __iomem *DR = ioremap(0xFDD600044);u32 val = *(u32 *)DR;    // 不要这样做!

ARM64 上 MMIO 区域的映射属性是 Device-nGnRnE(不缓存、不合并、不重排、不对齐报错、不做推测)。普通的 C 语言 * 解引用可能被编译器优化成 ldr 指令(不带屏障),但 MMIO 访问需要 str/ldr 配合 dsb 之类内存屏障指令。readl/writel 内部会保证正确的屏障语义——ARM64 上 readl 展开后大致是 ldr + 一个 __iormb() 屏障,保证后续对这段内存的读不会被重排到 readl 之前。

记住一句话:ioremap 拿到的地址,永远用 readl/writel 访问,别手痒用 *


unsetunset先读懂硬件:RK3568 怎么控制一个 GPIO?unsetunset

写驱动之前,必须对着芯片手册查清三件事:LED 接在哪个引脚 → 引脚怎么复用 → GPIO 寄存器地址和位域是什么。

以正点原子 ATK-DLRK3568 开发板为例,LED 接在 GPIO0_C0(GPIO0 控制器的 C 口第 0 脚)。我们分两步来配置它。

第一步:告诉芯片"这个引脚当 GPIO 用"(IOMUX 配置)

RK3568 的大部分引脚都是多功能复用的——同一个物理引脚,既可以当普通 GPIO,也可以当 UART 的 TX、SPI 的 CLK 等。需要通过 IOMUX(输入输出复用)寄存器 来选择功能。

IOMUX 寄存器位于 PMU GRF(电源管理单元 - 通用寄存器文件) 中:

PMU_GRF 基地址:  0xFDC20000  ├── GPIO0C_IOMUX_L  (offset 0x0010)  → 0xFDC20010   引脚复用选择  └── GPIO0C_DS_0     (offset 0x0090)  → 0xFDC20090   驱动能力选择

GPIO0C_IOMUX_L 这个寄存器中,bit[2:0] 控制 GPIO0_C0 的功能:

  • 000 = 普通 GPIO(我们想要的)
  • 001 ~ 111 = 各种外设功能(UARTSPII2C 等)

把 bit[2:0] 写成 000,就选了 GPIO 模式。 这就像告诉芯片:"这根脚你别当 UART 用了,给我当普通 IO 口。"

一个容易踩的坑:GRF 寄存器的"高 16 位写使能"

RK3568 的 GRF 寄存器有一个特殊机制:**高 16 位是"写使能掩码"**。什么意思?

当你写一个 GRF 寄存器时,高 16 位的某一位为 1,低 16 位的对应位才能真正被写入;如果高 16 位的某一位是 0,低 16 位的对应位不会被改写,保持原值。

打个比方:你要修改一扇门上某个格子的锁,但门上有 32 把锁。你必须先把对应位置的那把小钥匙(高 16 位)插进去,才能拧动对应的锁芯(低 16 位)。

具体代码这样写:

val = readl(IMUX);                    // 先读出当前值val &= ~(0x7 << 0);                   // bit[2:0] 清零(准备写 0 → 选 GPIO)val |= ((0x7 << 16) | (0x0 << 0));    // bit[18:16]=1 开写使能, bit[2:0]=0 选 GPIOwritel(val, IMUX);                    // 写回寄存器

(0x7 << 16) = bit[18:16] 全部置 1,意思是"bit[2:0] 这三位的值允许被写入"。如果只写低 16 位而高 16 位全是 0,bit[2:0] 的值根本写不进去——这是初学者最容易犯的错误。

这个设计的用意是什么?

在 SMP(多核)系统上,两个 CPU 核可能同时写同一个 GPIO 寄存器的不同 bit。如果按传统的 read-modify-write 方式(读 → 改 → 写),第二个 CPU 的写操作可能覆盖掉第一个 CPU 刚改的 bit。

高 16 位写使能机制优雅地解决了这个问题:每个核只把自己要改的那些 bit 对应的高 16 位置 1,其他 bit 的高 16 位写 0。这样一来,两个核同时写同一个寄存器也不会互相踩。

第二步:配置 GPIO 方向和数据

引脚设为 GPIO 模式之后,还需要配置方向寄存器(DDR)和数据寄存器(DR)。

RK3568 的 GPIO0 控制器寄存器:

GPIO0 基地址:  0xFDD60000  ├── SWPORT_DR_L   (offset 0x0000)  → 0xFDD60000   数据寄存器(A/B口,低半)  ├── SWPORT_DR_H   (offset 0x0004)  → 0xFDD60004   数据寄存器(C/D口,高半)  ├── SWPORT_DDR_L  (offset 0x0008)  → 0xFDD60008   方向寄存器(低半)  └── SWPORT_DDR_H  (offset 0x000C)  → 0xFDD6000C   方向寄存器(高半)

GPIO0 有 4 个口(A/B/C/D),每个口 8 个 pin,共 32 个引脚。"L"(低半)这组寄存器管 A/B 口(bit 0~15),"H"(高半)这组管 C/D 口(bit 16~31)。**GPIO0_C0 是 C 口第 0 脚,对应 H 组的 bit0:**

  • **DDR_H bit[0]**:控制方向。1 = 输出(CPU 控制引脚电平),0 = 输入(读取外部电平)
  • **DR_H bit[0]**:控制电平。1 = 高电平,0 = 低电平

GPIO 控制器的写使能机制和 GRF 一样:高 16 位是写使能掩码。

硬件操作流程总结

 配 IOMUX:   GPIO0_C0 → GPIO 功能     (写 GRF 寄存器)   "这根脚当 IO 口" 配驱动能力:  设为 level 5            (写 GRF 寄存器)   "要有足够的驱动电流" 设方向:      输出                    (写 GPIO DDR)    "我要往外输出电压" 设电平:      低电平 → LED 灭         (写 GPIO DR)     "默认先关灯"⑤ 应用写1 →  DR 置1 → LED 亮           (通过 write 系统调用触发)

unsetunset完整驱动代码unsetunset

下面这个 led.c 完整展示了"字符设备驱动 + ioremap + 真实 GPIO 寄存器操作"的全流程。为了把注意力集中在硬件操作上,字符设备注册这里用了最简洁的 register_chrdev 方式——一个函数调用就把设备号申请和 cdev 注册全部搞定。后面我们会升级到更规范的分步注册方式。

#include<linux/types.h>#include<linux/kernel.h>#include<linux/delay.h>#include<linux/ide.h>#include<linux/init.h>#include<linux/module.h>#include<linux/errno.h>#include<linux/gpio.h>#include<asm/uaccess.h>#include<asm/io.h>#define LED_MAJOR       200         /* 主设备号 */#define LED_NAME        "led"/* 设备名字 */#define LEDOFF          0           /* 关灯 */#define LEDON           1           /* 开灯 *//* ============ RK3568 寄存器物理地址(来自芯片手册) ============ */#define PMU_GRF_BASE              (0xFDC20000)#define PMU_GRF_GPIO0C_IOMUX_L    (PMU_GRF_BASE + 0x0010)#define PMU_GRF_GPIO0C_DS_0       (PMU_GRF_BASE + 0x0090)#define GPIO0_BASE                (0xFDD60000)#define GPIO0_SWPORT_DR_H         (GPIO0_BASE + 0x0004)#define GPIO0_SWPORT_DDR_H        (GPIO0_BASE + 0x000C)/* 映射后的内核虚拟地址(__iomem 提醒你:这是 MMIO 指针,别用 * 解引用) */static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;static void __iomem *GPIO0_SWPORT_DR_H_PI;static void __iomem *GPIO0_SWPORT_DDR_H_PI;/* ============ LED 开关函数 ============ */voidled_switch(u8 sta){    u32 val = 0;if (sta == LEDON) {        val = readl(GPIO0_SWPORT_DR_H_PI);        val &= ~(0x1 << 0);                      /* bit0 清零 */        val |= ((0x1 << 16) | (0x1 << 0));       /* bit16=1 写使能, bit0=1 高电平亮 */        writel(val, GPIO0_SWPORT_DR_H_PI);    } elseif (sta == LEDOFF) {        val = readl(GPIO0_SWPORT_DR_H_PI);        val &= ~(0x1 << 0);                      /* bit0 清零 */        val |= ((0x1 << 16) | (0x0 << 0));       /* bit16=1 写使能, bit0=0 低电平灭 */        writel(val, GPIO0_SWPORT_DR_H_PI);    }}/* ============ ioremap 和 iounmap 封装 ============ */voidled_remap(void){    PMU_GRF_GPIO0C_IOMUX_L_PI = ioremap(PMU_GRF_GPIO0C_IOMUX_L, 4);    PMU_GRF_GPIO0C_DS_0_PI      = ioremap(PMU_GRF_GPIO0C_DS_0, 4);    GPIO0_SWPORT_DR_H_PI         = ioremap(GPIO0_SWPORT_DR_H, 4);    GPIO0_SWPORT_DDR_H_PI        = ioremap(GPIO0_SWPORT_DDR_H, 4);}voidled_unmap(void){    iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);    iounmap(PMU_GRF_GPIO0C_DS_0_PI);    iounmap(GPIO0_SWPORT_DR_H_PI);    iounmap(GPIO0_SWPORT_DDR_H_PI);}/* ============ file_operations ============ */staticintled_open(struct inode *inode, struct file *filp){return 0;}staticssize_tled_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){return 0;   /* LED 驱动不需要读 */}staticssize_tled_write(struct file *filp, constchar __user *buf,size_t cnt, loff_t *offt){int retvalue;unsignedchar databuf[1];unsignedchar ledstat;    retvalue = copy_from_user(databuf, buf, cnt);if (retvalue < 0)return -EFAULT;    ledstat = databuf[0];           /* 用户传来的 0 或 1 */if (ledstat == LEDON)        led_switch(LEDON);          /* 写 1 → 亮灯 */elseif (ledstat == LEDOFF)        led_switch(LEDOFF);         /* 写 0 → 灭灯 */return 0;}staticintled_release(struct inode *inode, struct file *filp){return 0;}static structfile_operationsled_fops = {    .owner   = THIS_MODULE,    .open    = led_open,    .read    = led_read,    .write   = led_write,    .release = led_release,};/* ============ 驱动入口:本文的核心——硬件初始化 ============ */staticint __init led_init(void){    u32 val = 0;int retvalue;/* ---- 1. ioremap:给 4 个寄存器"办身份证" ---- */    led_remap();/* ---- 2. IOMUX:告诉芯片"GPIO0_C0 当普通 GPIO 用" ---- */    val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);        /* 读出当前配置 */    val &= ~(0x7 << 0);                             /* bit[2:0] 清零 */    val |= ((0x7 << 16) | (0x0 << 0));              /* bit[18:16] 写使能, bit[2:0]=000 选 GPIO */    writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);/* ---- 3. 驱动能力:设为 level 5(保证能驱动 LED) ---- */    val = readl(PMU_GRF_GPIO0C_DS_0_PI);    val &= ~(0x3F << 0);    val |= ((0x3F << 16) | (0x3F << 0));    writel(val, PMU_GRF_GPIO0C_DS_0_PI);/* ---- 4. 方向:设为输出 ---- */    val = readl(GPIO0_SWPORT_DDR_H_PI);    val &= ~(0x1 << 0);    val |= ((0x1 << 16) | (0x1 << 0));              /* bit16=1 写使能, bit0=1 输出 */    writel(val, GPIO0_SWPORT_DDR_H_PI);/* ---- 5. 默认输出低电平,LED 灭 ---- */    val = readl(GPIO0_SWPORT_DR_H_PI);    val &= ~(0x1 << 0);    val |= ((0x1 << 16) | (0x0 << 0));              /* bit16=1 写使能, bit0=0 低电平 */    writel(val, GPIO0_SWPORT_DR_H_PI);/* ---- 6. 注册字符设备 ---- */    retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);if (retvalue < 0) {        printk("register chrdev failed!\r\n");goto fail_map;    }    printk("led: loaded, major=%d, /dev/led ready (需手动 mknod)\n", LED_MAJOR);return 0;fail_map:    led_unmap();return -EIO;}/* ============ 驱动出口:注册的反向操作 ============ */staticvoid __exit led_exit(void){    led_unmap();                                 /* 释放 IO 映射 */    unregister_chrdev(LED_MAJOR, LED_NAME);      /* 注销字符设备 */    printk("led: unloaded\n");}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("qian");MODULE_DESCRIPTION("RK3568 LED driver via ioremap");

代码导读:重点看 led_init 里的 6 个步骤

这 6 步中,前 5 步是本文与上一篇《从零写一个能用的字符设备驱动》本质的区别——从"纯软件"迈向了"真实硬件":

步骤
做什么
通俗理解
ioremap
给 4 个寄存器建立虚拟地址映射
办身份证
 写 IOMUX
GPIO0_C0
 选 GPIO 功能
这根脚当 IO 口
 写 DS
驱动能力设 level 5
给足电流推动 LED
 写 DDR
方向设为输出
我要往外输出电平
 写 DR
默认低电平 = LED 灭
先关着灯
⑥ register_chrdev
注册字符设备,应用层可见
告诉内核:/dev/led 归我管

第 6 步 register_chrdev 是最简写法——一个函数同时完成设备号申请和 cdev 注册。缺点是加载后 /dev/led 不会自动出现,需要手动 mknod /dev/led c 200 0。下一篇我们会升级到更规范的方式,让设备节点自动生成。


unsetunsetMakefileunsetunset

# PC 测试用(x86 走不通——x86 上没有 RK3568 的寄存器)# KERNEL_DIR := /lib/modules/$(shell uname -r)/build# ARM 交叉编译(目标平台:RK3568)KERNEL_DIR := /home/alientek/rk3568_linux_sdk/kernelARCH := arm64CROSS_COMPILE := aarch64-none-linux-gnu-obj-m := led.oall:$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) clean

这个驱动只能在 RK3568 开发板上跑。在 x86 PC 上编译能通过,但 insmod 时会失败——因为 PC 上没有 0xFDD60000 这个物理地址对应的 GPIO 外设。


unsetunset测试程序unsetunset

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#define LEDOFF  0#define LEDON   1intmain(int argc, char *argv[]){int fd;unsignedchar databuf[1];if (argc != 3) {printf("Usage: %s /dev/led 0|1\n", argv[0]);printf("       0 = off, 1 = on\n");return -1;    }    fd = open(argv[1], O_RDWR);if (fd < 0) {printf("open %s failed!\n", argv[1]);return -1;    }    databuf[0] = atoi(argv[2]);       /* 命令行参数 0 或 1 */    write(fd, databuf, sizeof(databuf));  /* 写进驱动 → 控制 LED */    close(fd);return 0;}

unsetunset编译、加载、点灯unsetunset

# 1. 交叉编译驱动(在 PC 上执行,需要安装 SDK)make# 2. 把 .ko 和测试程序推送到开发板adb push led.ko /root/adb push ledApp /root/# 3. 在板子上加载驱动insmod led.ko# 4. 手动创建设备节点(register_chrdev 不会自动创建)mknod /dev/led c 200 0# 5. 点亮 LED./ledApp /dev/led 1# 6. 熄灭 LED./ledApp /dev/led 0# 7. 卸载驱动rmmod led

加载后通过 dmesg 查看内核日志:

led: loaded, major=200, /dev/led ready (需手动 mknod)

加载驱动后需要手动执行 mknod /dev/led c 200 0 来创建设备节点,这是 register_chrdev 这种简写方式的一个不便之处。


unsetunset上一篇 vs 本文:差别到底在哪unsetunset

上一篇虚拟 FIFO
本文 LED 驱动
数据流向
内核缓冲区 ↔ 用户空间
用户空间 → 物理GPIO 寄存器
read实现copy_to_user发数据给应用
空函数(LED 不需要读)
write实现copy_from_user存到缓冲区
写入 0/1 → led_switch() → GPIO 电平翻转
关键新 API
无(纯软件)
ioremap, iounmapreadlwritel
硬件依赖
无(PC 上就能跑)
强依赖 RK3568
设备注册
alloc_chrdev_region
 + cdev_add(分步注册)
register_chrdev
(老式简写)
设备节点
class_create
 + device_create 自动生成
手动 mknod

本文特意用了更简洁的 register_chrdev,目的是让硬件操作的部分更突出——不被设备号分配、类创建这些"软件脚手架"分散注意力。至于分步注册 + 自动生成设备节点的升级版,下一篇就会讲到。


unsetunset三个容易踩的坑unsetunset

1. ioremap 拿到的地址不能直接用 * 解引用

staticvoid __iomem *DR;// u32 val = *(u32 *)DR;          // ← 错误!可能 oopsu32 val = readl(DR);               // ← 正确

哪怕 ioremap 返回的地址"看起来"是一个普通指针,也绝对不能用 * 去解引用。原因前面说了——ARM64 上 MMIO 区域的映射属性是 Device-nGnRnEreadl/writel 内部带了内存屏障,普通的 ldr/str 不满足这些约束。

2. 忘记 iounmap

ioremap 会消耗内核的 vmalloc 虚拟地址空间。只 ioremap 不 iounmap,反复加载/卸载模块会慢慢泄漏虚拟地址空间。就像一个旅馆只有 100 间房,客人只入住不退房,不出几轮就满了。

3. 高 16 位写使能忘记置 1

这是 RK3568/RK3588 芯片手册里最容易忽略的细节。对比一下:

// 错误写法 —— 你以为写了,其实没写进去:val = readl(DR);val |= (0x1 << 0);                // bit16 还是 0!写使能没开!writel(val, DR);// 结果:bit0 没被改写,LED 不亮。你对着代码查了半天,百思不得其解。// 正确写法:val = readl(DR);val |= ((0x1 << 16) | (0x1 << 0)); // bit16=1 开写使能,bit0=1 才真正生效writel(val, DR);

每次都记得:低 16 位的哪一位要改,高 16 位的对应位就要写 1。 这是 Rockchip 平台的"仪式感"——忘了就白写。


unsetunset本文你学到了什么unsetunset

  1. MMU 的作用 —— 把 CPU 核的虚拟地址翻译成物理地址,实现进程隔离、内存碎片整理、外设访问控制
  2. ioremap 的本质 —— 在 MMU 页表里新建一条映射,给物理寄存器"办一张虚拟地址的身份证"
  3. readl / writel —— MMIO 访问的标准接口,自带内存屏障,保证访问顺序和原子性
  4. RK3568 GPIO 操作流程 —— 两步走:先配 IOMUX(选 GPIO 功能),再配 GPIO 控制器(方向 + 数据)
  5. 高 16 位写使能机制 —— Rockchip GRF/GPIO 寄存器的写保护设计,每次写入都要置对应的高 16 位 bit
  6. __iomem 类型 —— 编译器帮你标记"这是 MMIO 指针",提示用 readl/writel 而非 * 解引用
  7. 字符驱动框架复用 —— 设备注册、file_operations、goto 错误回滚,和上一篇完全一样,一次学会,所有驱动通用

unsetunset本文的局限性,以及接下来学什么unsetunset

本文的 LED 驱动虽然能亮能灭,但它有两个工程上的短板:

短板一:设备注册方式可以更规范。 本文使用的是 register_chrdev 一把梭的老式注册方式——一个函数调用就完成了设备号的申请和 cdev 的注册。这种方式简单粗暴,但会占用主设备号下的全部次设备号,而且不支持在一个驱动里管理多个设备实例。Linux 内核从 2.6 版本开始提供了更精细的注册方式:alloc_chrdev_region + cdev_init + cdev_add 分步走,需要几个次设备号就申请几个,而且可以配合 class_create + device_create 自动生成 /dev 下的设备节点,不用手动 mknod

短板二:寄存器地址硬编码。#define PMU_GRF_BASE (0xFDC20000) 把硬件地址写死在了 C 代码里。LED 换到 GPIO3_D4 → 改代码;换一块 RK3588 板子、GPIO 基地址变了 → 改代码;客户的产品用了不同的引脚分配 → 又得改代码。驱动和硬件焊死了。

工程级的做法分两步走:

第一步——用设备树把硬件描述从代码中剥离:把"GPIO0_C0 的寄存器地址是多少、引脚怎么复用"这些硬件信息从 C 代码里搬到一个独立的描述文件(设备树 .dts)中。驱动启动时从设备树读取地址,而不是从 #define 读取。同一份驱动源码,换板子只改 .dts 文件,不改 C 代码。

第二步——用 GPIO 子系统替代手写寄存器:设备树解决了"地址从哪来"的问题,但驱动里还是要手写 readl/writel、手动处理高 16 位写使能这些芯片细节。内核的 GPIO 子系统把这些底层操作用 gpio_request()gpio_direction_output()gpio_set_value() 等接口封装好了——你只需要告诉它"用哪个 GPIO、输出高还是低",连 ioremap 都不用写了。

下一篇,我们就从这三个方面把驱动升级一遍:更规范的新字符设备注册方式 → 设备树 → GPIO 子系统,让驱动真正达到工程级水准。


关注「钱途无量嵌入式」,专注 Linux 驱动与 BSP 开发,每周硬核输出。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 10:59:34 HTTP/2.0 GET : https://f.mffb.com.cn/a/496661.html
  2. 运行时间 : 0.262805s [ 吞吐率:3.81req/s ] 内存消耗:4,731.91kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=fe783a75701cef3e12e67d914e26fc88
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000393s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000573s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000260s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000276s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000576s ]
  6. SELECT * FROM `set` [ RunTime:0.000282s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000653s ]
  8. SELECT * FROM `article` WHERE `id` = 496661 LIMIT 1 [ RunTime:0.000413s ]
  9. UPDATE `article` SET `lasttime` = 1783047574 WHERE `id` = 496661 [ RunTime:0.000504s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000220s ]
  11. SELECT * FROM `article` WHERE `id` < 496661 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.008572s ]
  12. SELECT * FROM `article` WHERE `id` > 496661 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.006364s ]
  13. SELECT * FROM `article` WHERE `id` < 496661 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.072553s ]
  14. SELECT * FROM `article` WHERE `id` < 496661 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.067592s ]
  15. SELECT * FROM `article` WHERE `id` < 496661 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.035361s ]
0.264364s