总线会扫了。今天接第一颗练习芯片——环境光。遮一下、照一下读数必须跟着走。第 33 章把总线打通了。这章只盯一颗芯片I2C 地址0x10RGBW 环境光挂在i2c1。我见过的翻车几乎都不是「不会读寄存器」而是这几件扫错总线、IIO 设备号每次开机变、HAP 读 sysfs 权限 0444、用手掌盖住数值却不动——其实读的是另一颗 iio:device 的通道。数值会随遮光/照光变这一条是验收不是装饰。两条路都给你内核 IIO和用户态I2C_RDWR。IIO 通了用 IIO驱动没编进来用第 33 章那份三件套自己读。1. 这颗芯片在总线上干什么VEML6040 是 RGBW 四通道环境光。I2C 7 位地址0x10写死改不了。同一条 i2c1 上还有 0x40 / 0x57 / 0x5a / 0x73不会撞车。内部就几个 16 位寄存器低字节先寄存器地址作用CONF0x00积分时间、触发、关断R0x08红G0x09绿B0x0A蓝W0x0B白 / clear对可见光更宽CONF 低字节按数据手册bit0SD1 关断0 工作。驱动 probe 时写成 0。bit1AF0 自动1 强制触发。bit2TRIG强制模式写 1 启动一次。bit6:4IT积分时间。000 40 ms越长越灵敏、越慢。高字节保留写 0。板级驱动默认CONF 0x000040 ms、自动、开机。光进窗口四个通道的 raw 往上爬。你用手盖住clear 必须掉。掉不了你读错节点了或者芯片没在采。通信路径手指遮光 / 台灯 → 窗口光电 → 芯片 0x10 → i2c1 100 kHz → 内核 veml6040.ko编进 Image → /sys/bus/iio/devices/iio:deviceN/in_intensity_*_raw → 用户态 read() 或 ArkTS readText()用户态旁路跳过 IIOopen(/dev/i2c-1)i2cRdReg读 0x080x0B。两条路读到的是同一组寄存器。2. 设备树和内核开关板级 dts产品名rk3568_evbi2c1 { status okay; clock-frequency 100000; veml6040: veml604010 { compatible vishay,veml6040; reg 0x10; status okay; }; };compatible必须和驱动里of_device_id一字不差。驱动不在 mainline 默认配置里是板级丢进内核树的device/board/rk/rk3568_evb/kernel/veml6040.cbuild_kernel.sh在合并 defconfig 时写入CONFIG_VEML6040y并往drivers/iio/light/Makefile加一行obj-$(CONFIG_VEML6040) veml6040.o源文件拷进drivers/iio/light/。三处 CONFIG 同步源码 defconfig、out 里的、脚本合并段。只在 menuconfig 里打开下次全量重建会丢。驱动 probe 做两件事写 CONF 开机注册四个 IIO_INTENSITY 通道红、绿、蓝、clear。read_raw内部是i2c_smbus_read_word_data就是组合读 16 位。改 dts 或驱动之后删内核 checkpoint重打boot_linuxresource 分区的 dtb 一并刷。只刷 Image、设备树还是旧的0x10 节点都不会出现。3. 上板先确认这颗活着插上传感器模块i2c1 那排针3.3 V 有电。hdchdc shell ls -l /dev/i2c-1 i2cdetect -y -r 1 # 没有 i2cdetect 就 # /data/local/tmp/i2c_tool scan 10x10 那一格应该是10或UU。--的话停下来回到第 33 章供电、100 kHz、CAN 共脚、扫错总线。ls -l /sys/bus/i2c/devices/1-0010/driver # 期望指向 .../drivers/veml6040 cat /sys/bus/i2c/devices/1-0010/name # veml6040有目录、没 driverprobe 失败。dmesg | grep -i veml常见是写 CONF 被 NACK——还是总线问题不是 IIO 问题。IIO 侧for d in /sys/bus/iio/devices/iio:device*; do echo $d $(cat $d/name 2/dev/null) done你会看到一串名字saradc、mlx90614、veml6040、可能还有1-0040这种用 i2c 地址当 name 的。不要写死iio:device5。开机枚举顺序随 probe 变化今天是 5明天刷完内核变成 3。按名字找。权限。hdc 是 root应用不是ls -l /sys/bus/iio/devices/iio:device*/in_intensity_*_raw chmod 0666 /sys/bus/iio/devices/iio:device*/in_intensity_*_raw chmod 0666 /sys/bus/iio/devices/iio:device*/name长期放进board-perm.sh进 vendor。第 33 章写过脚本骨架。4. 用手验收不要用想象找到 veml6040 那颗 IIOBASE for d in /sys/bus/iio/devices/iio:device*; do n$(cat $d/name) [ $n veml6040 ] BASE$d done echo BASE$BASE cat $BASE/in_intensity_clear_raw cat $BASE/in_intensity_red_raw cat $BASE/in_intensity_green_raw cat $BASE/in_intensity_blue_raw记下四个数。然后手掌盖住传感器窗口再 cat 一次。clear 必须明显下降。拿手机手电筒贴窗口clear 必须明显上升。红光手电则 red 相对更高这是定性不是实验室。我经历过一次「数值恒定 65535」。那是读到了别的通道的满量程或者芯片关断位没清、总线全 1。全 1 也出现在没 ACK、读缓冲没刷新的情况。对照 scan0x10 必须在。绿通道粗略照度lux ≈ green_raw * 0.25168这是积分 40 ms、典型窗口下的经验系数不是校准证书。室内白天常见几百到两千手盖住掉到几十或更低。报「lux」的时候写清楚「约」。5. 完整 C优先 IIO没有再走 i2c-dev保存为veml6040_read.c。先按名字搜 IIO搜不到再打开/dev/i2c-1组合读 0x080x0B。两种路径都要会IIO 没编进来时还能自己读。/* veml6040_read.c — 环境光IIO 优先失败则 i2c-dev */ #include stdio.h #include stdlib.h #include stdint.h #include string.h #include dirent.h #include fcntl.h #include unistd.h #include errno.h #include sys/ioctl.h #ifndef I2C_RDWR #define I2C_RDWR 0x0707 #endif #ifndef I2C_M_RD #define I2C_M_RD 0x0001 #endif struct i2c_msg { uint16_t addr; uint16_t flags; uint16_t len; uint8_t *buf; }; struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; uint32_t nmsgs; }; static int i2cWr(int fd, uint8_t addr, uint8_t reg, const uint8_t *data, int len) { uint8_t b[32]; b[0] reg; if (data len 0) memcpy(b 1, data, (size_t)len); struct i2c_msg m { addr, 0, (uint16_t)(len 1), b }; struct i2c_rdwr_ioctl_data d { m, 1 }; return ioctl(fd, I2C_RDWR, d); } static int i2cRdReg(int fd, uint8_t addr, uint8_t reg, uint8_t *data, int len) { struct i2c_msg m[2] { { addr, 0, 1, reg }, { addr, I2C_M_RD, (uint16_t)len, data }, }; struct i2c_rdwr_ioctl_data d { m, 2 }; return ioctl(fd, I2C_RDWR, d); } static int read_file_int(const char *path, int *out) { char buf[64]; int fd open(path, O_RDONLY); if (fd 0) return -1; ssize_t n read(fd, buf, sizeof(buf) - 1); close(fd); if (n 0) return -1; buf[n] 0; *out atoi(buf); return 0; } static int find_iio(const char *want, char *out, size_t outlen) { DIR *d opendir(/sys/bus/iio/devices); if (!d) return -1; struct dirent *e; int found 0; while ((e readdir(d)) ! NULL) { if (strncmp(e-d_name, iio:device, 10) ! 0) continue; char namep[128], name[64]; snprintf(namep, sizeof(namep), /sys/bus/iio/devices/%s/name, e-d_name); int fd open(namep, O_RDONLY); if (fd 0) continue; ssize_t n read(fd, name, sizeof(name) - 1); close(fd); if (n 0) continue; name[n] 0; char *nl strchr(name, \n); if (nl) *nl 0; if (strstr(name, want)) { snprintf(out, outlen, /sys/bus/iio/devices/%s, e-d_name); found 1; break; } } closedir(d); return found ? 0 : -1; } static int read_iio(void) { char base[128]; if (find_iio(veml6040, base, sizeof(base)) 0) { fprintf(stderr, IIO 里没有 veml6040驱动没 bind 或没编进来\n); return -1; } int r, g, b, c; char p[160]; snprintf(p, sizeof(p), %s/in_intensity_red_raw, base); if (read_file_int(p, r) 0) { fprintf(stderr, 读 red 失败: %s %s\n, p, strerror(errno)); return -1; } snprintf(p, sizeof(p), %s/in_intensity_green_raw, base); read_file_int(p, g); snprintf(p, sizeof(p), %s/in_intensity_blue_raw, base); read_file_int(p, b); snprintf(p, sizeof(p), %s/in_intensity_clear_raw, base); read_file_int(p, c); double lux g * 0.25168; printf(IIO %s\n, base); printf(R%d G%d B%d clear%d 约 %.0f lux绿通道粗算\n, r, g, b, c, lux); printf(用手掌盖住再跑一次clear 必须下降。\n); return 0; } static int read_i2c(void) { int fd open(/dev/i2c-1, O_RDWR); if (fd 0) { fprintf(stderr, open /dev/i2c-1: %s\n, strerror(errno)); return -1; } /* 开机CONF0x0000SD0 */ uint8_t conf[2] { 0x00, 0x00 }; if (i2cWr(fd, 0x10, 0x00, conf, 2) 0) { fprintf(stderr, 写 CONF NACK0x10 不在\n); close(fd); return -1; } usleep(50000); /* 40 ms 积分 余量 */ uint8_t rb[2]; unsigned ch[4]; const uint8_t regs[4] { 0x08, 0x09, 0x0A, 0x0B }; const char *nm[4] { R, G, B, W }; for (int i 0; i 4; i) { if (i2cRdReg(fd, 0x10, regs[i], rb, 2) 0) { fprintf(stderr, 读 0x%02x NACK\n, regs[i]); close(fd); return -1; } ch[i] rb[0] | (rb[1] 8); /* 低字节先 */ } close(fd); printf(i2c-dev 0x10\n); for (int i 0; i 4; i) printf(%s%u%s, nm[i], ch[i], i 3 ? \n : ); printf(约 %.0f lux\n, ch[1] * 0.25168); return 0; } int main(int argc, char **argv) { int force_i2c (argc 1 strcmp(argv[1], i2c) 0); if (!force_i2c read_iio() 0) return 0; return read_i2c() 0 ? 0 : 1; }编译和上传和第 33 章同一套工具链OHOS源码根 CLANG$OHOS/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang SYSROOT$OHOS/prebuilts/ohos-sdk/linux/11/native/sysroot $CLANG --targetarm-linux-ohos --sysroot$SYSROOT -O2 -Wall \ -Wl,--dynamic-linker/system/lib/ld-musl-arm.so.1 \ -o veml6040_read veml6040_read.c hdc file send veml6040_read /data/local/tmp/veml6040_read hdc shell chmod 0755 /data/local/tmp/veml6040_read hdc shell /data/local/tmp/veml6040_read hdc shell /data/local/tmp/veml6040_read i2c第一次跑 IIO第二次强制 i2c-dev。两串数字应在同一量级。差出一个数量级检查有没有重复起始、有没有把高低字节倒了。这颗是小端 16 位rb[0] | (rb[1]8)。6. 现场数值不动学生说「我盖住了还是 1247」。我让他连续 catwhile true; do date cat $BASE/in_intensity_clear_raw sleep 1 done盖上、拿开、拿手电。数字跳芯片是活的应用读错路径。数字死往下查。读错 iio 设备。iio:device0经常是 saradc。saradc 的in_voltage0_raw是按键通道跟光无关你怎么盖窗口它都不动。findIio(veml6040)就是为这件事存在的。权限。应用readText返回空或Permission denied。hdc 下 cat 正常。chmod 0666写进 board-perm。驱动没 bind你还在读一个过期路径。重编内核之后 device 序号变了页面里写死的/sys/bus/iio/devices/iio:device5/in_intensity_clear_raw变成了别的芯片或者路径 404。每次按名字找。CONF.SD1。有人用户态写过关断又去读 IIOIIO 那条路径以为自己管着 CONF。别混用。选一条路。盖错地方。模块上有两颗窗口时环境光是那颗扁的小芯片不是红外测温的透镜。盖错颗clear 当然不动。对着丝印或用手电筒贴每一颗看谁跳。7. 现场IIO 没有 veml6040grep VEML6040 /proc/config.gz # 若没有 config.gz zcat /proc/config.gz 2/dev/null | grep VEML ls /sys/bus/i2c/drivers/veml6040 dmesg | grep -i vemlCONFIG_VEML6040没进去脚本合并段没写或重建时被 olddefconfig 吃掉。打开重编内核。驱动在client 不 bindcompatible 写错、status disabled、i2c1 没起来。反编译生效 dtb# 从板上把 dtb 抽出来看节点或直接 ls /proc/device-tree/i2c*/veml604010 cat /proc/device-tree/i2c*/veml604010/statusdisabled就是 dts 没 okay。okay仍不 bind看 dmesg 写 CONF 那一行。没有内核驱动也不丢人。veml6040_read i2c照样验收遮光。IIO 是方便 sysfs 的路不是物理定律。8. ArkTS 页面native 导出findIio、readText。页面不要写死 device 号。import board from libboard.so; Entry Component struct LightPage { State line: string 点读取。盖住窗口再点一次clear 必须变小。; private num(path: string): number { let s: string board.readText(path).trim(); return parseFloat(s); } readLight() { let base: string board.findIio(veml6040); if (base.length 0) { this.line 未找到 veml6040。查 i2c1 0x10 / CONFIG_VEML6040 / 权限; return; } let c: number this.num(base /in_intensity_clear_raw); if (Number.isNaN(c)) { this.line 读 clear 失败chmod 0666 in_intensity_*_raw; return; } let g: number this.num(base /in_intensity_green_raw); let r: number this.num(base /in_intensity_red_raw); let b: number this.num(base /in_intensity_blue_raw); let lux: number Number.isNaN(g) ? 0 : g * 0.25168; this.line clear c.toFixed(0) R r.toFixed(0) G g.toFixed(0) B b.toFixed(0) 约 lux.toFixed(0) lux\n base; } build() { Column() { Text(环境光 VEML6040 0x10).fontSize(18).margin({ bottom: 8 }) Button(读一次).onClick(() { this.readLight(); }) Text(this.line).margin({ top: 12 }).fontSize(16) }.padding(16) } }findIio的 native 实现和第 33 章、上面 C 的find_iio一样扫/sys/bus/iio/devicesname子串匹配。匹配veml6040不要匹配0040——那是另一颗。轮询的话 200500 ms 一次够了积分时间 40 ms更快没有新数据。不要在 UI 线程里setInterval同时打四次同步读还弹动画卡了会以为芯片死了。9. 和同总线其它芯片的关系i2c1 100 kHz 是全家的约定。你为了「环境光快点」把总线改回 400 kHz环境光可能仍 ACK红外测温会掉线。别改。内核占用 0x10 时 i2cdetect 是UU。用户态再读写 0x10 可能 EBUSY。页面走 IIO 就不要再走 i2cReadReg(1, 0x10, ...)。调试才用veml6040_read i2c。CAN0 和 i2c1 共脚开 CAN 的镜像上这章整章失效。不是芯片坏了。10. 验收i2cdetect -y -r 1或i2c_tool scan 1见到 0x10。/sys/bus/i2c/devices/1-0010/driver指向 veml6040走 IIO 时。按名字找到 IIO读到四个 raw。手掌盖住clear 下降手电筒clear 上升。两次读取间隔至少 100 ms。HAP 里同样能读0666 已放权且遮光仍变。你没有在代码里写死iio:device5。第 4 条不过不要标这颗完成。恒定值对应用来说比没节点更危险——它看起来「通了」。一次遮光实验的终端记录下面是板上真实会看到的形状。数字随环境变趋势必须对。# cat /sys/bus/iio/devices/iio:device3/name veml6040 # cat /sys/bus/iio/devices/iio:device3/in_intensity_clear_raw 1842 # 手掌盖住窗口等 200 ms # cat /sys/bus/iio/devices/iio:device3/in_intensity_clear_raw 117 # 拿开手电筒斜打 # cat /sys/bus/iio/devices/iio:device3/in_intensity_clear_raw 6120R/G/B 会一起动但比例不同。白炽灯偏红通道阴天窗口偏蓝。不要用「某一个通道没变」判坏——看 clear再看绿。同一段用用户态旁路# /data/local/tmp/veml6040_read i2c CONF0x0000 R210 G1880 B940 W1842 ~lux473IIO 和 i2c-dev 对同一颗芯片clear 应对得上差几个 count 是积分时刻不同差一个数量级才是读错寄存器或读了邻机。HAP 读到恒定值、hdc cat 会变应用打开的不是你以为的那份 sysfs。findIio(veml6040)每次测量都跑不要在aboutToAppear里缓存iio:device3用一整天——插上 USB 网卡、加载别的 IIO 设备序号会挤。CONF 写 0x0001SD1 关断后再读四个通道接近 0。这是确认「我读写的是 0x10」的最快办法。测完写回 0x0000否则 IIO 下一次 raw 也是 0两人会互相指责对方把芯片弄休眠了。积分时间 IT 拉长数值变大、更新变慢。默认 40 ms 够遮光实验。别在演示现场改 IT除非你把等待也改了。路径清单device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts i2c1 veml604010 compatible vishay,veml6040 device/board/rk/rk3568_evb/kernel/veml6040.c 板级 IIO 驱动CONF0x0000四通道 RAW device/board/rk/rk3568_evb/kernel/build_kernel.sh CONFIG_VEML6040y 拷贝 .c 进 drivers/iio/light/ kernel/linux/linux-5.10/drivers/iio/light/veml6040.c 编译后的落点由脚本放入 /sys/bus/iio/devices/iio:deviceN/ name veml6040 in_intensity_red_raw in_intensity_green_raw in_intensity_blue_raw in_intensity_clear_raw /dev/i2c-1 用户态旁路地址 0x10寄存器 0x00 / 0x08 / 0x09 / 0x0A / 0x0B device/board/rk/rk3568_evb/board_perm/board-perm.sh chmod 0666 iio in_* 与 /dev/i2c-1数量级免得把邻机读数当成环境光白天靠窗clear 常见四位数。手掌盖严掉到两位数或三位数低端。手电筒贴窗口可以冲到五千以上。夜间只开一盏台灯几百。这些不是指标是「我读的是这颗芯片」的气味。若 clear 恒定 0CONF 的 SD 位可能被写成 1或者你读的 IIO 根本不是 veml6040。回去cat name。若恒定 65535总线读出来全 1常见于没 ACK 仍把缓冲当数据、或读到了别的 16 位满量程通道。i2cdetect上 0x10 必须在。用户态读 0x08 若也是ff ff先量 3.3 V 和上拉。SARADC 那章的毫伏和这颗无关。有人把in_voltage2_raw的变化当成环境光——那是底板电位器或外部 ADC 通道遮窗口它不动。名字里没有 intensity 的不要拿来交差。绿通道乘 0.25168 得到的「约 lux」只在 IT40 ms、窗口没贴膜时有点边。IT 改成 80 ms、160 ms同一个房间数字会翻倍lux 公式不能再用。演示现场保持默认 CONF0x0000。应用层缓存iio:deviceN是下一颗坑。USB 网卡、SARADC、红外测温都可能先 probe序号被挤。每次读取都findIio(veml6040)多一次 open少一晚上假数据。编译用户态工具仍是 32 位 musl和第 33 章同一套 clang。编出来file必须看到ARM、interpreter /system/lib/ld-musl-arm.so.1。aarch64 静态编上去exec 直接失败现象像「工具没送上去」。hdc file send veml6040_read /data/local/tmp/veml6040_read hdc shell chmod 0755 /data/local/tmp/veml6040_read hdc shell /data/local/tmp/veml6040_read hdc shell /data/local/tmp/veml6040_read i2c两次输出的 clear 对得上这一章才能收工。对不上先看是不是内核占用 0x10 导致用户态 EBUSY——那就只信 IIO把 i2c 旁路留给驱动没编进来的镜像。ArkTS 侧不要自己拼/sys/bus/iio/devices/iio:device5/in_intensity_clear_raw。NAPI 提供findIio(veml6040)和readText。页面上两个按钮就够读一次、连续读。连续读间隔 200 ms 以上积分时间才 40 ms读太快数字看起来「不变」那是你采样比芯片还快。遮光实验必须在应用里再做一遍。hdc cat 会变、HAP 恒定十有八九是权限 0444 或读错了 device 序号。board-perm.sh把in_*chmod 0666进 vendor不要每次开机手敲。hdc shell cat /proc/partitions hdc shell ls -l /dev/i2c-1 /dev/ttyS0 hdc shell dmesg | grep -E i2c|mlx|hdc|uart0|vcc-camera | tail -30上面三条在换模块、改 dts、刷完分区之后各跑一次比在应用里加 log 便宜。输出贴进记录下一轮对照。i2c1 被 CAN 镜像关掉时这颗环境光会一起消失。不是 VEML6040 坏了是整条总线让路给了 CAN0 的 GPIO0_B3/B4。换回默认镜像i2c1 okay、can0 disabled再测。触摸在 i2c2不受影响。CAN 镜像把 i2c1 关掉时0x10 会从 i2cdetect 上消失。换回 i2c1 okay 的镜像再测不要先换环境光模块。遮光必须肉眼看见数字跳。跳了这章结束。。手盖住clear 必须下降。系列第 34 篇 · 芯片瑞芯微 RK3568 · OpenHarmony 4.1API 11 · Linux 5.10
企业数字化 ERP 产品动态
相关推荐
用Canvas手写烟花模拟器:粒子系统与性能优化实战 一说到过年,很多人第一反应就是找个现成的 GIF 烟花图、视频素材塞到页面上,但那种东西动起来是扁平的、卡顿的、也不够“活”。我今年要分享的是自己手写的一个 烟花模拟器 ,用 Canvas 从零实现满屏流光炸裂的效果,支持自动巡航… · 2026/9/26 8:25:42
cua刷屏揭秘:从拟声词到全网干饭暗号的传播逻辑 最近刷短视频的时候,你一定见过满屏飘“cua”的场面。评论区清一色的“cua”,视频标题也写“今天也要cua”,甚至聊天框里朋友没有任何铺垫直接丢来一个“cua”。这个全网刷屏的网络热词,表面上像某种神秘接头暗号,但如… · 2026/9/26 8:25:36
docling实战:从PDF到Markdown的文档解析与RAG应用指南 做RAG或者数据处理这块儿,文档解析永远是绕不开的坎。PDF转文本,听着简单,真上手才发现是一个无底洞:文本层和图片混排,表格解析完像一团乱麻,双栏论文读成一条直线,扫描件更是直接劝退。我一开… · 2026/9/26 8:25:36
AI大模型开发全栈实战:用TaoToken统一Key打通本地部署到Agent工具链 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 13:45:04
超声波风速仪原理、选型与安装维护全解析 1. 从机械风杯到声波时差:为什么我开始关注超声波风速仪三年前我在一个高海拔气象站做设备维护,冬天爬风塔换风杯轴承的滋味实在不好受。传统机械式风速仪有转动部件,轴承磨损、沙尘卡滞、结冰抱死这些问题几乎每个月都要处理一次,… · 2026/9/26 13:44:58
Cursor自动添加Co-authored-by署名的原理与关闭方案 1. 这不是Git的问题,是Cursor悄悄给你加的“合作者署名”最近好几位朋友在团队协作群里发截图:“哎?我刚提交的commit里怎么多了个co-author:cursor?我根本没写啊!”——这问题一出现,第一反应往… · 2026/9/26 13:44:32
X86工控一体机为什么稳?工业自动化选型与维护实战解析 去年在一家汽车零部件厂做产线数字化改造,电柜里那台用了快十年的X86工控一体机一直很稳。老师傅说除了换过硬盘,没出过大毛病。我特意看了一眼配置,Intel低压处理器,X86架构。当时我就有个很深的感触:工业自动化这个圈… · 2026/9/26 13:44:32
Claude Code 常用教程:用 TaoToken 统一 Key 打通 settings.json 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 13:44:25
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46