news 2026/10/14 17:15:30

RK3288W UBoot阶段双GPIO上拉配置指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RK3288W UBoot阶段双GPIO上拉配置指南

最近调试一块RK3288W的方案板,遇到一个很典型的状况:代码编译通过、UBoot也能正常跑起来,但用万用表量两个需要内部上拉的GPIO引脚,电压始终是0V。查来查去,问题出在上拉寄存器根本没配对。RK3288W这颗SoC上跑Android 9.0,UBoot阶段的GPIO上拉配置和内核里完全是两套逻辑,很多刚转BSP的兄弟容易在这里卡住。这篇文章把RK3288W在Android 9.0的UBoot阶段配置双GPIO上拉的完整思路、可复制的代码、还有我实测中踩过的坑都整理出来。内容适合做方案定制、驱动移植、硬件调试的工程师参考,也适合刚入门BSP想搞清楚GPIO上拉原理的同学。

1. 想让上拉真正生效,先搞清RK3288W的GPIO寄存器结构

1.1 GPIO模块的基础组成

RK3288W的GPIO控制器分成多个bank,分别是GPIO0到GPIO8,每个bank下有32个引脚,按A/B/C/D四个小组划分,每组8个引脚。所以一个引脚完整的叫法是"GPIO0_A2""GPIO3_B5"这样的形式。第一次看这部分代码的人,很容易把"GPIO3_B5"里的数字3理解成某个组号,其实它表示的是第3个bank,B表示组,5是该组第6个引脚。

每个bank有一组独立的寄存器,用来做数据读写、方向控制、上下拉配置、中断管理等操作。在UBoot阶段,我们最常接触的是这几类:数据寄存器DR、方向寄存器DDR、外部端口寄存器EXT_PORT,以及上下拉相关的PULL寄存器。DR负责写电平,DDR决定引脚是输入还是输出,EXT_PORT用于读取引脚实际电平,PULL寄存器则控制内部上下拉电阻是否接入、以及接入的是上拉还是下拉。

把这块的地址布局先说出来,后续写代码时直接对照。RK3288W的GPIO0基地址是0xFF750000,GPIO1到GPIO8依次是0xFF780000、0xFF790000、0xFF7A0000、0xFF7B0000、0xFF7C0000、0xFF7D0000、0xFF7E0000、0xFF7F0000。虽然RK3288W在封装上和RK3288有一些细节差异,但GPIO控制器部分基本通用,内核和UBoot的驱动也共用一套逻辑。这个地址信息可以在对应TRM或者SDK头文件里核对一遍,不同分支的基地址宏定义位置略有差别,但数值基本一致。

1.2 上拉上下拉寄存器到底怎么控制引脚

RK3288W的每个GPIO bank里都有两组与上下拉直接相关的寄存器,一组叫PULL_EN(上拉使能),一组叫PULL_SEL(上下拉方向选择),它们在bank内的偏移一般是0x140和0x144,具体以TRM为准。PULL_EN的每个bit对应bank里的一个引脚,置1表示这个引脚启用内部上下拉电阻,置0表示禁用。PULL_SEL同样是每个bit对应一个引脚,置1表示接入的是上拉电阻,置0表示接入的是下拉电阻。

所以"配一个引脚为内部上拉"这个动作,拆到寄存器层面就是两步:先把PULL_EN对应的bit写成1,再把PULL_SEL对应的bit写成1。如果只需要下拉,就把SEL对应的bit写成0。如果想让引脚保持高阻浮动,把EN写成0就行。很多人在这一步踩坑是因为只写了PULL_SEL没写PULL_EN,或者反过来,结果量下来引脚既不是稳定的高电平也不是稳定的低电平,而是随外部电路飘。

这里的电源域概念也要提一句。GPIO内部上拉电阻实际是接到该GPIO所在电源域的,RK3288W的引脚分布在不同的电源域上,比如VCCIO3、VCCIO1等。上拉后的电压等于对应电源域的电压,不是固定的3.3V。如果某个引脚所在电源域在UBoot阶段还没供电,上拉配置写进去也只是寄存器层面生效,引脚上量不到预期电平。排查上拉无效的时候,先把供电顺序捋一遍,往往能直接找到答案。

1.3 为什么必须在UBoot阶段处理双GPIO上拉

既然内核里也能配pinctrl和gpio,为什么很多方案还把上拉动作放在UBoot?最直接的原因是时序。Android 9.0系统启动到内核完全起来,中间有一个不算短的空窗期,这期间如果有屏、触摸、按键、充电检测这类外设需要依赖特定电平工作,UBoot不配好就会出现识别不到、误触发、甚至启动流程卡死的情况。

另一个原因是UBoot阶段就要做硬件检测。比如HDMI的HPD脚,热插拔信号在UBoot里必须能被识别,否则没有external display引导的选项;再比如USB OTG的ID脚、耳机检测的ACC_DET脚,都是靠上拉/下拉来确定状态的。这类引脚往往成对出现,一个负责中断检测,一个负责复位或使能,所以"双GPIO上拉"这个需求在方案定制里非常常见,本质是在系统最早期把两个引脚的默认电平确定下来。

还有个容易被忽略的点是,UBoot里配好的状态到内核阶段不一定会被保留,这件事后面第五节详细说。这里只需要记住一个原则:UBoot的GPIO配置是给bootloader阶段用的,不是给内核用的,搞清楚这一点才不会在定位问题时分不清是谁把电平改了。

2. 在UBoot源码里找到正确的注入点

2.1 Android 9.0对应SDK的UBoot代码结构

以Android 9.0配套的UBoot为例,代码一般基于2014.10或2017.09分支改造。整体目录结构里,板级相关代码通常在board/目录下,RK平台公共代码在arch/arm/mach-rockchip/目录下,GPIO驱动在drivers/gpio/目录下。你进去之后先搜一下board_late_init这个函数,它是板级初始化的常用挂载点。

启动流程中,board_init负责早期硬件初始化,board_late_init在UBoot引导流程较晚阶段执行,此时大部分外设驱动已经就绪,适合做一些GPIO检测、开机按键判断、LCD背光点亮这类动作。很多方案把自定义初始化都放在这里,包括开机音量键检测、充电模式判定、屏幕ID识别等。推荐的做法是新建一个自己的板级函数,比如board_gpio_pull_init,然后在board_late_init里调用它,不要直接改大段的公共初始化代码,方便后续维护和对比版本差异。

如果你拿到的SDK里找不到标准的名字,也可以搜索rk_board_late_init,部分版本会用这个名字。总之思路一样,找到板级初始化的"晚阶段入口",把我们待会要写的双GPIO配置函数挂进去。挂在太早的位置,gpio驱动和时钟可能没初始化完成;挂太晚,UBoot里前置的检测流程已经跑完,配置意义就打了折扣。

2.2 GPIO编号换算公式与常见笔误

在UBoot代码里,GPIO编号不是直接写"GPIO0_A2"这个字符串,而是要换算成整数。RK平台的通用换算是:bank编号乘以32,加上所在组的索引(A=0、B=1、C=2、D=3)乘以8,再加上组内引脚序号。以GPIO3_B5为例,bank是3,B组索引是1,组内序号是5,得到的整数是332 + 18 + 5 = 109。

这个换算搞错的人非常多,常见的笔误是把B当成2、把序号从1开始数。GPIO3_B5如果按错误算法算成332+28+1=113,那就配到了GPIO3_C1上去了。写代码前先把要用的几个脚全部按公式列出来,打印一次,比在代码里猜要靠谱得多。RK平台UBoot和内核的GPIO编号算法是一致的,你在内核dts里看到gpio3_b5这样的名字,也能用同一套公式转换,方便两边对照。

用一个常用场景示范:GPIO0_A2是编号032+08+2=2,GPIO3_B5是编号109,两个脚一个在bank0一个在bank3。如果两个脚恰好都在同一个bank,比如GPIO1_A3和GPIO1_C7,那换算后编号分别是35和55,寄存器基地址完全一样,只是操作不同的bit。这个差异在写通用函数时就要考虑到,同一个bank直接复用基地址,跨bank要切换基地址。

2.3 常用GPIO API盘点

UBoot里操作GPIO有一套通用接口,包括gpio_request、gpio_direction_input、gpio_direction_output、gpio_set_value,这些函数在include/asm-generic/gpio.h这类公共头文件有声明,具体实现由各家SoC的驱动提供。RK平台的GPIO驱动文件在drivers/gpio/下面,通常是rk_gpio.c之类的名字,里面能看到底层寄存器操作方法。

与上下拉直接相关的是gpio_set_pull这个接口,或者类似名字的封装。它对上层隐藏了PULL_EN和PULL_SEL两组寄存器的细节,只要传入GPIO编号和上拉/下拉/悬空枚举就行。不过这个接口是否可用,取决于驱动是否在UBoot早期完成注册。不少人早期直接在board_init里调它,结果返回错误或者没反应,就是因为驱动初始化还没完成。通用接口的好处是语义清晰,坏处是依赖系统的gpio框架,排查问题时要在框架和寄存器之间来回跳。

除了gpio框架接口,RK平台的代码里还经常看到rk_mux、rk_iomux这类复用配置接口,它们负责把引脚从GPIO功能切换成UART、I2C、PWM等专用功能,或者相反。配置上拉之前,如果这个脚默认被复用成其他功能,光写PULL寄存器是不够的,还要先把iomux切到GPIO模式。这个非常关键,第五节第一个坑就是它。

3. 双GPIO上拉完整写法与可复制代码

3.1 方式A:直接寄存器读写(推荐在UBoot里用)

直接操作寄存器的方式在UBoot阶段通常更可靠,因为不依赖gpio框架的初始化时序。核心思路就是前文说的,把目标bank的PULL_EN和PULL_SEL对应bit写1。下面这段代码基于RK3288W的GPIO组织方式,直接可参考。

#include <common.h> #include <asm/io.h> #define RK3288_GPIO0_BASE 0xFF750000 #define RK3288_GPIO1_BASE 0xFF780000 #define RK3288_GPIO2_BASE 0xFF790000 #define RK3288_GPIO3_BASE 0xFF7A0000 #define RK3288_GPIO4_BASE 0xFF7B0000 #define RK3288_GPIO5_BASE 0xFF7C0000 #define RK3288_GPIO6_BASE 0xFF7D0000 #define RK3288_GPIO7_BASE 0xFF7E0000 #define RK3288_GPIO8_BASE 0xFF7F0000 #define RK3288_GPIO_PULL_EN 0x140 #define RK3288_GPIO_PULL_SEL 0x144 static unsigned long rk3288_gpio_base(int bank) { static const unsigned long base[] = { RK3288_GPIO0_BASE, RK3288_GPIO1_BASE, RK3288_GPIO2_BASE, RK3288_GPIO3_BASE, RK3288_GPIO4_BASE, RK3288_GPIO5_BASE, RK3288_GPIO6_BASE, RK3288_GPIO7_BASE, RK3288_GPIO8_BASE, }; return base[bank]; } static void rk3288_gpio_pull_up(int gpio) { unsigned int bank = gpio / 32; unsigned int bit = gpio % 32; unsigned long base = rk3288_gpio_base(bank); unsigned int val; /* 使能内部上拉电阻 */ val = readl(base + RK3288_GPIO_PULL_EN); val |= (1 << bit); writel(val, base + RK3288_GPIO_PULL_EN); /* 选择上拉方向 */ val = readl(base + RK3288_GPIO_PULL_SEL); val |= (1 << bit); writel(val, base + RK3288_GPIO_PULL_SEL); }

这段代码的关键在于每个操作都先readl再writel,也就是读改写。原因很简单:一个bank的PULL_EN寄存器是32位的,里面每1位控制一个引脚,如果直接对整个寄存器赋值,会把同bank其他引脚的配置全部冲掉。我刚接触RK平台时图省事直接writel(1<<bit),结果同bank另一个脚的外设配置被清掉,排查了大半天。所以这个"先读后改再写"的习惯一定要养成。

3.2 方式B:SDK封装API调用

如果你的UBoot版本里gpio框架初始化完成得比较早,或者你的代码本来就挂在board_late_init这种靠后的位置,直接用SDK封装API会更省事。大致的写法是:

#include <asm/gpio.h> static void board_gpio_pull_init(void) { gpio_request(2, "gpio0_a2"); gpio_request(109, "gpio3_b5"); gpio_direction_input(2); gpio_direction_input(109); gpio_set_pull(2, GPIO_PULL_UP); gpio_set_pull(109, GPIO_PULL_UP); }

gpio_request用来申请引脚,让系统知道这个脚被谁占用了;gpio_direction_input把方向设为输入。这里要特别提醒,方向设置和上拉设置是两码事,不能因为配了上拉就默认方向是输入。引脚可以配置成输出带上拉,也可以配置成输入带上拉,组合由硬件需求决定,不是自动关联的。如果SDK里gpio_set_pull的枚举名不同,比如GPIO_PULL_NONE/GPIO_PULL_DOWN/GPIO_PULL_UP,就去头文件里搜一下,枚举本质都是对PULL_EN和PULL_SEL的寄存器操作。

这个方式的优点是可读性好,代码量少,前提是你确认这个入口点gpio框架已经就绪。判断方法很简单,如果调用后返回值一直是负数,或者引脚状态没变化,就要怀疑是不是框架没初始化,这时切回方式A直读寄存器验证是最快的排查手段。

3.3 双GPIO组合配置实例(TP检测脚和I2C场景)

实际项目中,两个GPIO往往不是"各自独立上拉"这么简单。以经典的触摸屏INT和RST组合为例,INT脚是开漏输出,必须外部或内部上拉,RST脚在系统启动阶段要保持复位状态,需要下拉。在UBoot里要同时把INT做成上拉、RST做成下拉。用上面的rk3288_gpio_pull_up函数改一下pull方向,写一个支持上拉和下拉的通用版本:

static void rk3288_gpio_set_pull(int gpio, int pull_up) { unsigned int bank = gpio / 32; unsigned int bit = gpio % 32; unsigned long base = rk3288_gpio_base(bank); unsigned int val; val = readl(base + RK3288_GPIO_PULL_EN); if (pull_up) { val |= (1 << bit); } else { val &= ~(1 << bit); } writel(val, base + RK3288_GPIO_PULL_EN); val = readl(base + RK3288_GPIO_PULL_SEL); if (pull_up) { val |= (1 << bit); } else { val &= ~(1 << bit); } writel(val, base + RK3288_GPIO_PULL_SEL); } static void board_gpio_pull_init(void) { /* TP_INT 上拉,TP_RST 下拉 */ rk3288_gpio_set_pull(2, 1); rk3288_gpio_set_pull(35, 0); }

这里用了两个不同GPIO编号,一个上拉一个下拉,演示了双GPIO的最常见组合。另一个典型场景是I2C总线的SCL和SDA两个引脚都配上拉,这时你只需要把上面代码里的两个GPIO都传pull_up=1。不过要说明白,RK3288W内部上拉的阻值一般在几十千欧量级,用于检测电平这类场景足够,但I2C在高速模式下往往需要2.2k到4.7k欧的外部上拉才能保证沿质量。UBoot阶段用内部上拉只是让总线空闲电平不悬浮,量产硬件设计还是建议按I2C规范预留外部上拉电阻位置。

在board_late_init里的挂载代码大概是:

int board_late_init(void) { board_gpio_pull_init(); /* 其他板级初始化 */ return 0; }

如果板级文件里默认有别的逻辑,注意不要直接覆盖原函数内容,而是尽量在原有调试代码后面追加一段对board_gpio_pull_init的调用,防止破坏现有启动流程。

3.4 两种方式怎么选

直接寄存器方式和SDK API方式的取舍,本质是在依赖完整框架和减少依赖之间取平衡。GPIO框架在UBoot里往往只支持基础的gpio_request/gpio_direction_*,gpio_set_pull在某些老版本里甚至没有实现,或者实现里有bug。如果你在SDK里搜gpio_set_pull搜不到稳定实现,直接寄存器方式就是唯一可靠的选择。

我自己在多个平台上验证下来的建议是:板级自定义初始化函数里优先用直接寄存器方式,因为它稳定、可控、不随框架改动而失效;但如果项目代码规范要求统一走gpio框架,那就用SDK API,并做好返回值判断。两种方式的底层寄存器最终操作是一致的,差异只是封装程度。用表格把对比列出来:

对比项直接寄存器方式SDK API方式
依赖条件只需GPIO时钟已使能依赖gpio框架初始化完成
可读性偏低,要看寄存器手册偏高,函数名即语义
可控性高,bit级操作中,受驱动实现限制
移植成本低,改个基地址就行中,受SDK版本影响
适用阶段早期初始化、框架未ready框架ready后的中后期

4. 编译自检与硬件测量技巧

4.1 编译后如何确认配置已经生效

代码写完先别急着烧机,先在UBoot里加一行调试打印,把要配置的GPIO编号和寄存器写入值打印出来。比如在rk3288_gpio_set_pull里加printf,把bank、bit、base、写入值都打出来,然后编译烧录。串口日志里如果能看到正确的编号和寄存器值,说明代码路径执行到了,剩下就是硬件层面的验证。

编译的时候要注意,UBoot的编译命令在不同SDK里有细微差别,Android 9.0配套的SDK一般通过打包脚本直接编UBoot,也可以进入u-boot目录单独编。我常用的方式是make distclean后重新配置板级config,再执行make。如果你对当前SDK的UBoot编译流程不熟,先看顶层脚本或者编译文档,别盲目改环境变量。编译报错大多是头文件路径不对或者函数未声明,对症处理即可。

日志打印代码如下参考:

printf("gpio%d bit%d pull_en=0x%x pull_sel=0x%x\n", bank, bit, readl(base + RK3288_GPIO_PULL_EN), readl(base + RK3288_GPIO_PULL_SEL));

看到日志里PULL_EN和PULL_SEL的对应位置确实是1,寄存器配置这一层就算过了。

4.2 万用表与示波器测量方法

寄存器配好之后,硬件验证是另一道关卡。用万用表量引脚对地电压是最直接的方式。把万用表拨到直流电压档,黑表笔接GND,红表笔接被测GPIO。配置为内部上拉的引脚,在外部没有其他拉低源的情况下,电压应该等于所在电源域的电压。比如VCCIO3是3.3V,量出来的值应该在3.3V附近。如果量到0V或者电压很低,先看外部电路有没有把它拉低,比如连到了某个IC的开漏输出。

这里有个容易误判的点:有些引脚在UBoot阶段虽然配了上拉,但同时被复用成其他功能,等量的时候引脚可能已经不在GPIO模式下。所以测量前最好先确认这个时间点UBoot跑到了哪个阶段,以及这个引脚当前处于什么复用状态。用示波器看波形的话,重点看电平是否稳定,有没有明显毛刺。内部上拉的高电平一般比较平直,如果波形上出现周期性下拉,多半是后级IC在翻转输出,而不是上拉没配好。

电阻档直接量引脚对地在上电状态下是危险的,容易把IC内部结构判断错,不建议这么做。我早期就吃过亏,用电阻档量GPIO,量出来一个奇怪阻值,既不像上拉也不像下拉,实际上测量方式本身就不对。正确姿势是断电后用二极管档或电阻档,而且最好在原理图确认没有其他并联通路后再判断。

5. 高频踩坑场景与排查速查表

5.1 现象一:代码执行了,引脚还是低电平

这是双GPIO上拉配置里最常见的坑。代码跑到了,寄存器也打印对了,但引脚量下来仍然不是高电平。第一怀疑对象就是引脚复用。RK3288W的引脚在复位后不一定是GPIO功能,很多引脚默认被复用成其他功能。你在GPIO控制器里写PULL寄存器,但引脚实际工作在UART或者PWM功能上,上拉配置自然就表现不出来。

排查方法是把复用的寄存器也拉出来看。RK平台配置引脚功能的地方在GRF(通用寄存器文件)里,不同引脚对应不同offset,SDK里搜iomux或者rk_mux就能找到相关配置函数。确认当前引脚已经被配成GPIO功能之后,再回头看PULL寄存器。还有可能是引脚所接外部电路有强下拉,比如接了一根对地低阻的走线,这种情况下内部上拉几十千欧根本拉不动。判断办法是先断开外部负载再量,如果断开后电平正常,问题在外围电路。

5.2 现象二:同bank相邻引脚被误伤

在UBoot里操作GPIO寄存器,最容易写的低级错误就是把writel(1<<bit)当成"只改这一个bit",结果整个bank的所有引脚配置全部被覆盖。症状是原本正常的某个外设突然异常,而且往往不是自己刚配的那个脚,而是同bank里看起来无关的另一个脚。

这个问题的根因就是没有做read-modify-write。正确写法永远是readl -> 修改bit -> writel。如果你在代码里用了val |= (1 << bit)这样的写法,基本就不会出这类问题。另外,当一个bank里有多个引脚需要配置时,尽量把操作合并到同一个val变量里,减少readl和writel次数,既降低出错概率,也减少对总线的影响。

5.3 现象三:内核启动后配置被重置

UBoot阶段千辛万苦配好的上拉,等Android 9.0系统起来后发现又变了。这不是UBoot代码的问题,是内核的pinctrl机制在启动时会重新加载设备树里的pin controller配置,把引脚的状态重新刷一遍。内核dts里如果定义了这个引脚是某个功能,或者没有配置上拉,内核启动后就会覆盖UBoot的设置。

如果你确实需要内核阶段也保持上拉,正确做法是在设备树的pinctrl节点里同时配置,而不是只改UBoot。反过来,如果只想在UBoot阶段有效,内核阶段由驱动自己去管理,那UBoot里配的临时状态被覆盖是正常现象。理解了这个机制,就不会在做方案时把两个阶段的配置混在一起排查。

5.4 排查速查表

症状可能原因解决方向
寄存器配置正确但引脚低电平引脚被复用成其他功能检查iomux/GRF配置,切回GPIO
寄存器配置正确但引脚电压不足外部负载下拉过强断开外部负载验证
同bank其他引脚异常写寄存器时覆盖整bank改为read-modify-write
gpio_set_pull返回值错误gpio框架未ready用直接寄存器方式
上拉后波形有毛刺内部上拉阻值偏大增加外部上拉电阻
内核启动后状态变了内核pinctrl覆盖同步修改dts配置
引脚电压等于0.8V附近电源域未完全上电检查供电时序
复位瞬间有异常电平UBoot配置时机太晚提前到更早的init入口

调试RK3288W这类RK平台的UBoot,我的感受是遇到GPIO问题先别急着怀疑代码,按"寄存器-复用-外部电路"三层顺序排查,效率会高很多。双GPIO上拉这种需求本身不复杂,但它藏在启动早期、又容易和内核pinctrl纠缠,所以显得棘手。最后分享一个习惯:我习惯在板级文件里把需要初始化的GPIO统一放到一个表格里,用循环执行,后续换脚、增脚只改数据不改逻辑,维护起来省心不少。希望这篇对正在调RK3288W上拉的朋友有参考价值。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 17:15:30

Vue.js入门:从响应式原理到工程化项目实战

1. 为什么我建议把Vue.js作为前端入门的第一框架去年有位刚转行的朋友问我&#xff0c;零基础学前端&#xff0c;第一套框架到底选什么。我当时的回答很简单&#xff1a;如果你没有特殊偏好&#xff0c;就先学Vue.js。这不是因为我只用Vue&#xff0c;而是因为我在带过几轮新人…

作者头像 李华
网站建设 2026/10/14 17:14:24

死锁排查与预防实战:从线程快照到锁顺序优化

前言死锁这玩意儿&#xff0c;像程序世界里的灵魂绑架。线程们互相攥着对方想要的锁&#xff0c;谁也不撒手&#xff0c;然后一起卡死在那里&#xff0c;既不报错也不退出&#xff0c;CPU 占用看着还挺正常甚至接近 100%&#xff0c;可业务就是一动不动。我见过不少项目&#x…

作者头像 李华
网站建设 2026/10/14 17:14:01

GitHub日榜趋势监测系统:从原始数据到技术信号提取

1. 这不是“榜单截图”&#xff0c;而是一套可复用的 GitHub 日榜趋势观测系统你有没有试过打开 GitHub Trending 页面&#xff0c;刷出一堆陌生项目&#xff0c;点开 README 又看不懂技术栈&#xff1f;或者想跟踪某个新兴框架的爆发节奏&#xff0c;却只能靠手动刷新、截图、…

作者头像 李华
网站建设 2026/10/14 17:13:02

Python爬虫实战:B站动漫数据抓取与可视化分析全流程

简介&#xff1a;这是一份面向Python爬虫初学者与数据分析爱好者的实战项目资源&#xff0c;以B站番剧为切入点&#xff0c;串联爬虫抓取、数据清洗、统计分析与可视化全流程。项目先爬取总榜获取动漫粗略信息与直达链接&#xff0c;再逐条访问详情页补充数据&#xff0c;最终结…

作者头像 李华
网站建设 2026/10/14 17:12:14

信息学竞赛复赛试题解析:从线性枚举到前缀和与约瑟夫环

简介&#xff1a;第18届绍兴市少儿信息学竞赛复赛试题已整理为一份Word文档&#xff0c;面向小学阶段的信息学爱好者与编程初学者&#xff0c;既可用于备赛练习&#xff0c;也可作为课堂或社团的测评资料。卷面包含“好朋友”“统计人口”“卫星”“粉刷匠”四道经典题目&#…

作者头像 李华
网站建设 2026/10/14 17:10:15

OpenCode Go额度又提升了

10 月 12 日 17:14&#xff0c;OpenCode 官方账号在 X 上发了条帖子。Operation Cheepseek 第三阶段&#xff0c;$10的GO套餐&#xff0c;DeepSeek V4.1 Flash 使用额度提升到 $80。 X 上 OpenCode 官方账号的公告帖&#xff0c;两行字&#xff0c;603K 次查看&#xff0c;6.9K…

作者头像 李华