尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32F103与AT24C02的I2C读写实战:时序、硬件与调试避坑指南
我不止一次在论坛上看到有人拿着STM32F103和AT24C02点灯顺利却卡在I2C读写上要么读出来全是0xFF要么写完数据再上电就丢要么干脆卡死在某个等待标志位。这芯片组合明明是最经典的I2C入门搭配却也是翻车率最高的组合之一。AT24C02这颗2Kbit的EEPROM容量不大但胜在用法简单、接口标准非常适合用来吃透I2C总线那几个关键点起始信号、终止信号、应答位、重复起始条件。这篇文章就把我从裸机底层寄存器到实际读写整个流程的思考梳理一遍包括硬件上容易被忽略的细节、初始化代码怎么配、单字节读写和页面写完整的时序拆解再把调试过程中遇到的几个典型问题串起来讲希望能让正在折腾这两颗芯片的人少走点弯路。1. 为什么必须理解时序再谈代码1.1 I2C总线本质两根线怎么传数据I2C总线只有两根信号线一根SCL时钟线一根SDA数据线。很多刚开始写驱动的人会陷入一个误区以为像SPI那样有独立的片选线或者像串口那样有固定的波特率总线上直接发数据就行。I2C完全不是这个思路它靠的是SCL高电平时SDA必须保持稳定、SCL低电平时SDA允许变化这个约定来传输每一位。空闲时两根线都是高电平从机靠检测特定跳变来识别起始和停止。起始条件定义是SCL保持高电平期间SDA产生一个下降沿。停止条件则是SCL高电平期间SDA产生一个上升沿。也就是说SDA电平的跳变必须在SCL高电平期间完成而且这个跳变只有在高电平区间的跳变才有特殊含义数据位的跳变只允许发生在SCL低电平区间。这个区别是几乎所有时序问题的根源逻辑分析仪上只要看到SDA跳变不在SCL低电平中间基本就是时序错乱。数据字节传输出是高位先行MSB first每个字节后跟一个应答位应答位同样是受这个规则约束。主机在第9个时钟脉冲前释放SDA从机如果收到数据正确且地址匹配就会在第9个时钟的低到高期间把SDA拉低表示ACK。如果从机不响应SDA保持高表示NACK。主机自己读数据时每收到一个字节后需要通过输出ACK来告诉从机继续发下一字节在最后一个字节必须反馈NACK以通知从机停止发送。1.2 AT24C02的256字节寻址逻辑AT24C02容量是2Kbit也就是256字节内部被组织成32页每页8字节。它的从机地址固定是1010加上A2、A1、A0三个外部引脚的电平组合再拼上读写位。比如三个地址引脚都接地写字节的完整地址就是0xA010100000读字节地址就是0xA110100001。注意这个地址是8位且自带读写位很多初学者会以为地址是7位的发完地址再发读写位在具体代码里其实已经封装好了。内部字地址Word Address是8位从0x00到0xFF正好覆盖整个存储空间。每次读或写之前主机都要把这个字地址通过I2C发送到芯片里芯片内部用地址锁存器记住当前操作的位置。这也是读操作比写操作复杂的原因之一你要先做一次“伪写”把字地址写进去才能开始真正读数据。理解了这层逻辑后面随机读和顺序读流程就不容易绕晕。1.3 时钟速率的选择100kHz与400kHz的取舍STM32F103的硬件I2C外设支持标准模式100kHz和快速模式400kHz。原则上AT24C02最低支持到20kHz快速模式最高400kHz似乎怎么选都行但实际工程中建议先老老实实跑100kHz。原因很简单I2C总线上除了EEPROM往往还挂着传感器、实时时钟等别的从机大家都有最小总线空闲时间要求而且线缆和上拉电阻带来的寄生电容会影响沿的陡峭程度。100kHz模式下时序裕量充足即使PCB布线不太干净也能跑。如果非要用400kHz就得严格计算上拉电阻。快速模式要求上升时间不超过300ns这决定了上拉电阻上限同时还要保证灌入引脚的电流不超过规格这决定了电阻下限。我自己的经验是3.3V供电、总线等效电容约100pF时选择2.2k欧姆到4.7k欧姆的上拉电阻都比较安全。400kHz的配置里节省的时间一般很小对EEPROM这种慢存储来说真正卡住吞吐的是那5ms的写周期而不是总线速率所以为了可靠起见600kHz以下随便用。2. 硬件连接上拉电阻、地址线与去耦2.1 上拉电阻为什么必须接阻值怎么算这是提到最多但也最容易出错的地方。I2C的SDA和SCL都是开漏结构芯片内部不会主动输出高电平只能把线路拉低高电平完全靠外部的上拉电阻提供。忘接上拉电阻后总线会一直处在0V附近主设备发起始条件时根本拉不出高电平逻辑分析仪上只能看到一根扁平的线几乎所有数据都是0。上拉电阻阻值的选择要同时满足上升时间和低电平低输入电压高阈值两个约束。简单估算Rpu(min) (Vcc - 0.4V) / 3mA假设灌电流能力3mARpu(max) 上升时间所需最长时间。对于100kHz模式最大可容忍的上升时间是1000ns如果总线电容按100pF算那Rpu(max) ≈ 1000ns / (2.2*100pF) ≈ 4.5k欧姆。所以100kHz和3.3V供电的情况下2.2k到4.7k都能用。板子如果走线较长、挂载设备多适当调低一点比如2.2k。2.2 A0/A1/A2地址引脚挂多片的关键AT24C02允许最多8片挂在同一条I2C总线上靠的就是A2、A1、A0三个引脚对从机地址进行000到111的编码。这些引脚内部没有上拉也没有下拉必须外部硬接悬空会导致电平不确定从机地址可能漂移造成总线上有多于一个设备响应同一个地址数据全乱。所以哪怕只焊一片芯片三个地址脚也必须明确接GND或VCC不要图省事让它悬空。另外很多开发板会把A0引脚引出来作为手动拨码开关这在批量产品里没问题但在原型调试时如果拨码开关状态不确定主机映地址会不一致。我习惯把三个地址引脚全部接地固定成0xA0写地址这样在脑内推演时序时不会额外增加变量。2.3 硬件翻车现场忘接WP与供电电压AT24C02的WP引脚是写保护输入。WP接高电平时芯片会禁止写操作——发送的写命令包括页写和字节写都会被忽略并且不产生额外状态反馈。你从程序上很难区分到底是因为上拉太弱导致地址没被正确识别还是因为WP拉高了写保护。更隐蔽的是供电电压。AT24C02的标称工作范围通常是1.8V到5.5V但它的写周期功耗比读周期略高如果供电纹波太大在写周期内电压跌到低于某个下限芯片内部的写操作就会失败。我在一块实验板上用过一个DCDC输出3.3V空载时纹波只有20mV接上负载后由于输出电容太小写数据时能看到明显的电压凹陷导致偶尔写入失败。解决方法是先看示波器确认波形然后给VCC和GND之间加上100nF陶瓷电容如果板子空间允许再并一个10uF钽电容问题立刻消失。3. 初始化配置寄存器级别的关键宏定义3.1 开启GPIO与I2C时钟的顺序STM32F103的I2C1位于APB1总线上GPIOB在AHB总线上确切的说是APB2。初始化时很多人直接把GPIOB和I2C1的时钟一起开启然后一股脑配置寄存器结果I2C外设初始化失败。正确的顺序是先打开GPIO时钟再打开I2C时钟并且让GPIO端口的模式配置先于I2C的使能。用标准外设库写老项目常用GPIO和I2C初始化代码大致如下void I2C_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOB和I2C1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // I2C1SCL-PB6, SDA-PB7复用开漏输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_OD; // 复用开漏 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); }如果用的是HAL库逻辑一样配置成AF_OD即可。注意这里不能配置成GPIO_Mode_Out_OD普通开漏输出因为那样就丢了复用功能I2C外设的信号根本不会接到引脚上。这也是一个极其常见的坑芯片手册里不会特别强调但一旦配错引脚的输出不受I2C外设控制。3.2 I2C时序寄存器计算200kHz为例I2C外设需要配置时钟控制寄存器I2C_CR2里的FREQ字段和时序寄存器I2C_CCR。FREQ是输入I2C外设的时钟频率MHz必须正确设置否则所有时序脉冲都是错的。官方库函数用I2C_InitStructure.I2C_ClockSpeed来设置总线频率内部会计算CCR值。CCR的计算公式和标准模式、快速模式不同。标准模式下CCR PCLK1 / (2 * 目标频率)。比如APB1主频是36MHz目标总线速率100kHzCCR 36e6 / (2 * 100e3) 180。如果换成了72MHz的APB1CCR 72e6 / (2 * 100e3) 360。快速模式下占空比一般为169CCR PCLK1 / (3 * 目标频率)。400kHz快速模式且PCLK136MHz时CCR 36e6 / (3 * 400e3) 30。这个计算看起来很直接但要注意STM32F103的最大I2C速率和PCLK1的分频关系。有些人在Fast Mode设置里把I2C_DutyCycle配错导致实际输出的SCL频偏过大。我的建议是不要盲目追求极限值。设置I2C_ClockSpeed为100000让库去算就行如果自定义寄存器就固定用标准模式公式。3.3 手写EV5/EV6事件等待的细节标准外设库为了减少门槛提供了I2C_CheckEvent函数来等待事件。但很多人会发现读数据的时候频繁卡在I2C_EVENT_MASTER_BYTE_RECEIVED或者发数据时卡在I2C_EVENT_MASTER_BYTE_TRANSMITTING。这背后其实是硬件状态机在每次完成一个步骤后需要软件响应比如读数据时如果不先读出DR数据状态机就不会继续导致事件永远不到来。一个经常被忽略的坑是I2C_CheckEvent检查的EV5起始条件已发送EV6从机地址已发送。但发出地址后为了确定从机是否应答ACK通常要检查SR1的ADDR位以及AF位。如果从机地址错误或从机不存在AF标志应答失败会置1此时ADDR永远不会置1。很多初学者把代码逻辑写成“while(!I2C_CheckEvent(...EV6))”如果从机没接就永远卡死在循环里。因此更稳妥的写法是先检测AF错误再等待EV6并设置一个超时计数uint32_t timeout 0xFFFF; while (timeout--) { if (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)) break; if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) { // 处理从机无应答 return 1; } }硬件I2C的坑大多是这一类事件标志和中断顺序造成的尤其在修改别人代码、混入中断和主循环同时操作时更容易踩雷。4. 读写流程实战拆解4.1 单字节写入Start、SLAW、WordAddr、Data、StopAT24C02的单字节写时序如下主机发送起始条件发送从机写地址0xA0等待ACK发送内部字地址0x00到0xFF等待ACK发送一个数据字节等待ACK发送停止条件进入写周期tWR约5ms这里面的关键点是“停止条件后芯片才真正写EEPROM”。如果你在发送完数据后没有发送停止条件或停止条件波形不完整芯片会认为数据还没发完要么不写入要么写入的是错误内容。我在调试时曾经为了节省代码少写了一行I2C_GenerateSTOP结果读出来的数据全是初始状态0xFF还以为是写保护的问题。标准外设库的写法有几种比较简洁的是用I2C_SendData依次发送。下面给个可直接运行的示例uint8_t AT24C02_WriteByte(uint8_t addr, uint8_t data) { // 1. 发送起始条件 I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 2. 发送写地址 0xA0 I2C_Send7bitAddress(I2C1, 0xA0, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) return 1; // 3. 发送内部字地址 I2C_SendData(I2C1, addr); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) return 1; // 4. 发送数据字节 I2C_SendData(I2C1, data); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)); if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) return 1; // 5. 停止条件 I2C_GenerateSTOP(I2C1, ENABLE); // 6. 等待内部写周期完成 delay_ms(5); return 0; }每一步都对应着文章开头提到的时序约定。如果中间某个ACK失败一般是从机地址错误、WP拉高、总线没有上拉或者供电异常逐项排查即可。4.2 读取流程当前地址读与随机读的差异读取是大多数人容易懵的地方。AT24C02有当前地址读Current Address Read和随机读Random Read。当前地址读利用芯片内部地址指针上一次操作后的值直接发读地址0xA1然后开始读数据。由于不需要指定字地址它少了一步伪写时序上是从START到SLAR然后直接收数据。随机读则是先按写地址发起始条件发送从机写地址0xA0再发送要读取的内部字地址然后不发送停止条件而是直接再发一个“重复起始条件”Repeated START接着换成读地址0xA1最后才读数据。这个重复起始条件和普通的起始条件的区别在于它产生之前总线没有被释放总线仍然被主机占用目的是在不丢失总线控制权的情况下切换读写方向。很多人问随机读为什么要先写一遍字地址因为AT24C02内部只有8位的地址锁存器它不知道你想读哪个地址必须有人先把地址告诉它。当前地址读之所以不需要是因为地址锁存器里已经保存了上一次操作后的位置。上电后未进行任何操作时地址锁存器内部值为0xFF所以上电后直接当前地址读一般会读到0xFF地址的数据这往往不是你要的东西。4.3 页写8字节边界到底怎么算页写就是把多个字节一次写入前提是这些字节必须在同一个页面内。AT24C02的页面大小是8字节。很多手册上画了一个表格上面写着“1 Page 8 Bytes”但是遇到跨页问题的人依然非常多。假设你要写数据到地址0x05连续写4个字节那么地址是0x05-0x06-0x07-0x08。这个操作看起来只写了4个字节但因为地址0x08已经是下一页的起始地址AT24C02在页写模式下地址指针会回绕到当前页的起始地址0x00而不是进位到0x08。结果就是0x05、0x06、0x07写入了预期的数据而原本想写入0x08的数据被写到了0x00覆盖了0x00原来的值。所以正确的页写代码应该在写入前检查要写入的字节数是否会导致地址跨页。公式是(addr 0x07) len 8 则跨页。跨页时应该拆分写或者干脆逐字节写。以下是一个安全的页写封装uint8_t AT24C02_WritePage(uint8_t addr, uint8_t *buf, uint8_t len) { uint8_t i; // 如果跨页先写当前页到页尾的部分 uint8_t firstPageLen 8 - (addr 0x07); if (len firstPageLen) { // 全部在页内 ... } else { // 先写第一部分 // 再写剩余部分到新页 } }页写虽然减少了起始和停止条件的开销但对写周期没有任何帮助芯片依然是在收到停止条件后一次性烧写且总写入时间仍然要等5ms左右。如果写入12K数据单字节写和8字节页写的总时间差距非常大从70ms减到30ms左右所以少量参数逐字节写没问题批量下载参数表时用页写会明显更快。4.4 连续读如何正确生成NACK并停止连续读Sequential Read允许从任意地址开始连续读取多个字节。内部地址会自动递增但注意页写时跨页会回绕而连续读跨页时地址指针则会继续递增直到0xFF后停在0xFF不会回绕。读操作不受写周期限制但有使用上的要求除了最后一个字节前面每个字节接收方主机都要发送ACK给从机读取最后一个字节时主机必须发NACK然后发送停止条件。如果你不对最后一个字节发NACK而是仍发ACK从机会继续把下一个地址的数据放到SDA上主机读到的数据就会多出莫名其妙的一帧。所以连续读的核心代码框架应该是for (i 0; i len - 1; i) { buf[i] I2C_ReceiveData(I2C1); I2C_AcknowledgeConfig(I2C1, ENABLE); // ACK } buf[len - 1] I2C_ReceiveData(I2C1); I2C_AcknowledgeConfig(I2C1, DISABLE); // NACK I2C_GenerateSTOP(I2C1, ENABLE);硬件上I2C_ReceiveData并不是直接读取SDA上的电平而是读取数据寄存器DR。DR是双缓冲的接收到的数据先进入移位寄存器再转入DR。所以在读最后一个字节之前就提前关闭ACK而数据在DR里等待读取时序上需要合理安排。我经常看到有人在循环里先读数据后配置ACK导致ACK状态位总是晚一个节拍最后读出的数据错位。5. 调试时最常踩的五个坑5.1 写周期内的等待漏了AT24C02数据手册明确给出写周期典型时间2ms最大5ms。很多人在连续写操作时不等待5ms直接发下一轮起始条件芯片因为还在内部烧写根本不会响应新命令总线上表现为ACK异常或根本没有ACK。有一种特殊情况如果写入数据完全相同芯片可能比标称时间更快完成内部操作但这种特性不能依赖。必须在每次停止条件之后等待至少5ms最好是等待6ms保险起见甚至可以读一下写周期结束标志有些系列没有这个查询位。我在批量初始化参数时踩过这个坑当时用的是for循环连续写多个字节每字节后直接跟上写地址的起始条件结果第一个字节写进去了后面几个就随机丢了。后来在每次写命令末尾加上了delay_ms(6)成绩立刻稳定。5.2 跨页写入数据丢失上面已经解释了跨页回绕机制。很多人看手册时看到“如果跨越页边界数据会回绕到当前页开头”但没有实际遇到就不会重视。实际表现很迷惑你往地址0x00开始连续写10个字节结果读回来发现前8个字节对了但地址0x08的数据出现在地址0x00而地址0x08实际是0xFF。这是因为第九个字节被回绕到0x00把原来写好的第一个字节覆盖了。排查时如果不清楚页写页面大小会以为写入的缓冲区长度设置错了。定期记住AT24C02页大小是8字节不是16也不是32。用连续写功能时超过8字节一定要拆页。5.3 应答位检测缺失导致死循环标准外设库的I2C_CheckEvent会返回事件标志但如果从机没有回应ACK很多事件永远不会置位。特别是初始化时I2C外设已经使能了但SDA、SCL的上拉电阻焊错或焊盘脱落从机地址的ACK根本不会出现而你的while循环还在傻等。应对方法很简单每个等待循环都加上超时变量或者周期性检查AF错误标志。我在写所有底层I2C驱动时都会封装一个带超时的等待宏比如下面这种#define I2C_WAIT_FLAG(flag, timeout) \ do { \ uint32_t _t timeout; \ while (!(flag)) { if (--_t 0) return TIMEOUT_ERR; } \ } while(0)很多人担心超时会不会导致操作失败其实不会因为I2C本来就是慢速总线正常通信耗时差别不大。加了超时后至少能从“死机”变成“返回错误码”方便定位是哪个环节出了问题。5.4 总线死锁与复位I2C总线死锁是一个现象SDA一直为低电平怎么发起始条件都没反应。原因通常是某个从机在半途接收了数据但主机异常终止从机还在等待更多的时钟SDA保持低。一个常见的恢复手段是主机手动翻转SCL 9个时钟周期让从机同步状态机然后发送一个停止条件。在程序里可以这样实现void I2C_Bus_Reset(void) { GPIO_InitTypeDef GPIO_InitStructure; uint8_t i; // 将SCL和SDA都配置为推挽输出模式 GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; ... // 先让SDA释放SCL保持低 // 然后手动产生至少9个时钟脉冲 for (i 0; i 9; i) { GPIO_SetBits(GPIOB, GPIO_Pin_6); // SCL高 delay_us(2); GPIO_ResetBits(GPIOB, GPIO_Pin_6); // SCL低 delay_us(2); } // 最后发送停止条件 GPIO_SetBits(GPIOB, GPIO_Pin_7); // SDA高 delay_us(2); GPIO_SetBits(GPIOB, GPIO_Pin_6); // SCL高 delay_us(2); GPIO_ResetBits(GPIOB, GPIO_Pin_7); // SDA低 delay_us(2); GPIO_SetBits(GPIOB, GPIO_Pin_6); // SCL高 delay_us(2); GPIO_SetBits(GPIOB, GPIO_Pin_7); // SDA高 delay_us(2); // 恢复为复用开漏模式 }这个手动翻转时序在大多数情况下都能解决总线锁死同时可以配合在I2C初始化前调用一次保证上电后总线状态干净。5.5 硬件I2C与外设库的冲突关于STM32F103的硬件I2C流传很广的一个说法是“I2C外设不好用有bug容易卡死”。从我实际使用的情况看大多数卡死都出现在事件处理不完整或中断优先级配置错误的情况下。标准外设库的事件标志位有时间窗口如果软件响应不及时状态机无法继续但这不是芯片永久损坏只要正确复位即可。如果你在项目里同时使用了硬件I2C和软件模拟I2C要注意两者不要在总线上互相干扰。我见过一个项目板上同一组SDA/SCL引脚被两个驱动同时占用一个在中断里用软件模拟操作另一个在主循环里用硬件外设操作结果波形紊乱。复盘时发现是GPIO复用配置被两个驱动重复操作互相覆盖了模式。解决办法是统一管理I2C外设的初始化同一时刻只让一个驱动占有总线。写在最后的实操体会讲了这么多真正上手做的时候我建议你先把逻辑分析仪挂上去抓一次完整的单字节写时序。不需要一次性实现全部功能先从最简单的一行代码开始启动I2C外设发送一个起始条件发送0xA0然后看逻辑分析仪上有没有对应的波形和ACK位。如果这一段跑通了后面的读写流程就是状态机的机械化推进。我这个项目从零开始写驱动光是排查ACK失败问题就用掉大半天最后发现只是WP引脚没有处理接了上拉到VCC导致芯片一直处于写保护状态。所以硬件上的每个引脚电平都值得花时间确认一遍尤其是WP这种不起眼但能一票否决写功能的引脚。希望你也能在调试中静下心来多看波形少猜代码。
RELATED

相关推荐

YOLOv6工业视觉检测优化与部署实战:从能检出到敢上线

YOLOv6工业视觉检测优化与部署实战:从能检出到敢上线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 3:56:38
Seedance 2.5 视频生成提示词完全指南:从底层逻辑到实操避坑

Seedance 2.5 视频生成提示词完全指南:从底层逻辑到实操避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 3:56:38
STM32寄存器白话手册:从地址映射到位操作的系统级理解

STM32寄存器白话手册:从地址映射到位操作的系统级理解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/23 3:56:38
MORE NEWS

更多资讯

📰

REA reverse-engineer-anything 技能完整安装指南:让 AI 编码助手在本地完成逆向取证

REA reverse-engineer-anything 技能完整安装指南:让 AI 编码助手在本地完成逆向取证 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea R…

📰

为小说生成封面与插画:Denova 图像创作完全指南

【免费下载链接】denova An AI creative platform for novel writing and AI generated RPG, with built-in support for AI agents, Skills, subagent workflows, automations, image generation, and version control. 项目地址: https://gitcode.com/gh_mirrors/…

📰

esformatter编辑器深度集成:VS Code、Vim保存即格式化与CI风格检查配置指南

开发工具 【免费下载链接】esformatter ECMAScript code beautifier/formatter 项目地址: https://gitcode.com/gh_mirrors/es/esformatter 点击查看 免费下载 esformatter 是一款开源的 ECMAScript(JavaScript)代码格式化工具,它…

📰

基于微信小程序的“畅游广昌”智慧旅游服务平台设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

基于微信小程序的宠物社区系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

FairScan Scan-to-PDF Intent开发指南:让你的应用调用隐私优先的扫描器

【免费下载链接】FairScan An Android app to scan your documents — simple and respectful 项目地址: https://gitcode.com/gh_mirrors/fa/FairScan 点击查看 免费下载 FairScan 是一个开源的 Android 文档扫描应用,主打「简单、尊重用户」&#xff…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬