OV7670无FIFO在STM32F1上的EXTI中断图像采集方案

发布时间:2026/11/5 5:48:45
OV7670无FIFO在STM32F1上的EXTI中断图像采集方案 简介面向STM32F1平台与OV7670无FIFO摄像头调试的学习者这份工程源码针对网上资料零散、缺少可直接运行参考的问题给出了一套自写实现在无FIFO条件下完成图像采集与显示并集成亮度、饱和度、对比度调节按下KEY1可切换二值化与灰度化同时预留颜色识别接口便于继续做视觉实验。压缩包共312个文件、约10.8MB以71个C源文件与63个头文件为核心辅以Keil工程配置、编译中间文件、PNG截图和说明文本覆盖摄像头、LCD显示、SD卡与FatFs文件系统等模块适合按工程结构梳理驱动关系。已有4561人学习下载可作为STM32图像采集入门、课程设计或毕设原型的参考底稿代码中的图像处理接口也便于继续扩展阈值调节、颜色识别与显示效果验证减少从零搭建底层驱动与调试时序的时间。1. 项目定位OV7670 无 FIFO 在 STM32F1 上到底卡在哪一块 OV7670 无 FIFO 模块二十块钱不到接上 STM32F1 就能出图——这话对但第一次接上的人多半看到的是满屏雪花。原因很实在无 FIFO 版本没有 AL422B 那颗缓存芯片做缓冲像素数据会跟着 PCLK 一路往外输出MCU 必须在每个时钟边沿把那 8 位数据及时接收过来晚一步就少一个字节整幅图立刻错位。这份资源包解决的就是这个时序问题用 STM32F1 的 EXTI 中断配合 GPIO 整字节读取把 OV7670 无 FIFO 的裸时序跑通做到低分辨率实时采集、显示、串口导出。适合手上只有 F103、预算有限又想试试图像采集的开发者也适合已经在用带 FIFO 版本、想省一颗芯片成本、看看能不能不牺牲效果的人。2. 硬件与工程结构引脚分配、时钟树和内存预算OV7670 无 FIFO 的接线方案不是随便凑出来的每一个引脚都得为“中断响应要快、数据要一次性读走”这个目标服务。把它当成一个“带并行总线的慢速传感器”来看待而不是当成 SPI 外设接线的取舍就清楚了。2.1 引脚连接把 8 位数据口排在同一个 IDR 字节里数据口我建议固定接 PB0PB7原因不是方便画板而是GPIOB-IDR的低 8 位刚好对应 PB0PB7一条GPIOB-IDR 0x00FF就能把 8 位像素值一次读走省掉八次位带操作。这个细节在 PCLK 中断处理中直接决定能不能跟上节奏。信号引脚说明D0D7PB0PB7数据总线必须同一端口连续 8 位PCLKPA0像素时钟EXTI0 下降沿触发HREFPA1行有效EXTI1 双沿触发VSYNCPA2帧同步EXTI2 下降沿触发SCLPB10SCCB 时钟软件模拟SDAPB11SCCB 数据开漏加 4.7k 上拉XCLKPA8MCO 输出 8MHz 给摄像头主时钟RESETPC13复位低有效PWDNPC14掉电控制低有效正常工作拉低XCLK 用 MCO 输出是最省事的方式。F1 的 MCO 只能选 HSE、SYSCLK/2、PLL/2 三档72MHz 系统时钟下 SYSCLK/2 是 36MHz超过 OV7670 允许的输入范围所以直接选 HSE 直出 8MHzRCC-CFGR里把 MCO 位设成0b110。这里有个反直觉的地方很多人以为主频越高越好其实 OV7670 的 PCLK 是从 XCLK 分频出来的XCLK 给得高PCLK 就跟着高中断反而更追不上。8MHz 是个甜点值。SCCB 虽然名字像 I2C但时序不完全兼容标准 I2C用硬件 I2C 外设经常会遇到 ACK 收不到的问题所以我一般用两根 GPIO 软件模拟频率压到 100kHz 以下写四五十个寄存器也就几毫秒。2.2 工程目录驱动、采集、渲染三层切开ov7670_nofifo/ ├── Core/ │ ├── main.c 主循环初始化 - 起采 - 出图 │ └── stm32f1xx_it.c 中断入口只做转发 ├── Drivers/ │ ├── ov7670/ │ │ ├── sccb.c/.h SCCB 时序读写寄存器 │ │ ├── ov7670_regs.c 寄存器配置表按格式分组 │ │ └── ov7670.c/.h 上电序列、格式切换 │ ├── capture/ │ │ ├── cam_capture.c/.h EXTI 中断、像素拼装、帧切换 │ │ └── cam_buffer.c/.h 双缓冲管理 │ └── bsp/ │ ├── delay.c us 级延时DWT 或 SysTick │ ├── uart.c 串口导出 │ └── tft_ili9341.c SPI 屏驱动可选 └── App/ └── app_main.c 采集 - 处理 - 显示的业务串分层的意义在于sccb.c只管时序ov7670_regs.c只管一堆寄存器数值cam_capture.c只管中断里的字节流三者可以单独替换。我见过把寄存器表和中断服务函数全塞进main.c的写法调分辨率的时候改一个数要翻三百行改完之后连自己都不知道动了什么。这个项目的拆分方式换分辨率只需要改cam_buffer.h里的宽高宏加ov7670_regs.c里的一行其余代码一行不用碰。cam_buffer.c单独抽出来是有讲究的。无 FIFO 方案里缓冲区是最容易被写坏的公共资源中断在往里写主循环在往外读一旦交叠画面就会撕裂或者整帧花掉。把缓冲的申请、切换、占用标志全收在一个文件里能保证这套规则不至于散落到五个地方各写一遍。2.3 内存与时间预算先把账算清楚再动手OV7670 无 FIFO 在 F1 上最大的限制不是 CPU 快慢而是 SRAM 容量和中断耗时。先算内存分辨率格式单帧字节数F103C8T620KBF103ZET664KB320×240RGB565153600放不下放不下160×120RGB56538400放不下可以剩 25KB160×120灰度取高字节19200勉强几乎无余量可以80×60RGB5659600可以双缓冲 19200 太紧可以这张表是选型的起点。C8T6 只有 20KB做双缓冲 RGB565 基本没戏实际落地要么降到灰度、要么砍到 80×60。ZET6 有 64KB160×120 RGB565 单缓冲能放下双缓冲 76.8KB 又超了所以常见做法是“单采集缓冲 单显示行缓冲”边采边刷屏。再算时间。EXTI 中断在 72MHz 下的进出开销压栈、跳转、清挂起、读 IDR、写内存、返回保守按 1.52μs 估。也就是说每秒最多能进大约 50 万次中断。QQVGA 160×120 的 RGB565 每帧需要 160×120×2 38400 个 PCLK按 500k 次/秒算理论天花板 13fps扣掉消隐和主循环占用实际能跑到 58fps 就算不错。所以 OV7670 的 PCLK 必须压到几百 kHz 量级靠寄存器 0x11CLKRC做内部分频把 XCLK 8MHz 分下来。具体分到多少别信计算拿示波器夹 PA0 看实际频率能配合中断耗时跑通就行。提示F1 上没有 DCMI 外设GPIO 也不是 DMA 请求源所以“PCLK 触发 DMA 直接搬 GPIO-IDR”这条路在 F103 上走不通。看到别人这么写的方案多半是 F4 或者外挂了 CPLD别照抄到 F1 上浪费时间。3. OV7670 寄存器配置SCCB 写入与输出格式取舍寄存器配置错了后面所有代码都是白调。这部分最容易出问题因为网上流传的配置表版本多、注释少有的人直接抄了一份 VGA 的表跑 QQVGA结果图像缩在一角。3.1 SCCB 三字节写时序OV7670 的写操作是固定三字节设备地址 0x42、寄存器地址、数据。读要分两阶段先写地址再读比标准 I2C 少一个重复起始位。/* 开漏输出外部 4.7k 上拉 */ #define SCL_H() (GPIOB-BSRR GPIO_BSRR_BS10) #define SCL_L() (GPIOB-BSRR GPIO_BSRR_BR10) #define SDA_H() (GPIOB-BSRR GPIO_BSRR_BS11) #define SDA_L() (GPIOB-BSRR GPIO_BSRR_BR11) #define SDA_IN() (GPIOB-IDR GPIO_IDR_IDR11) static void sccb_delay(void) { delay_us(5); } /* 约 100kHz */ static void sccb_start(void) { SDA_H(); SCL_H(); sccb_delay(); SDA_L(); sccb_delay(); /* SCL 高时 SDA 下降 起始 */ SCL_L(); sccb_delay(); } static void sccb_stop(void) { SDA_L(); SCL_H(); sccb_delay(); SDA_H(); sccb_delay(); /* SCL 高时 SDA 上升 停止 */ } /* 返回 0 表示收到 ACK非 0 表示从机没应答 */ static uint8_t sccb_write_byte(uint8_t dat) { for (uint8_t i 0; i 8; i) { if (dat 0x80) SDA_H(); else SDA_L(); dat 1; sccb_delay(); SCL_H(); sccb_delay(); SCL_L(); } SDA_H(); /* 释放 SDA 给从机拉低 */ sccb_delay(); SCL_H(); sccb_delay(); uint8_t ack SDA_IN(); /* 0 ACK */ SCL_L(); sccb_delay(); return ack; } uint8_t ov7670_write_reg(uint8_t reg, uint8_t val) { uint8_t err; sccb_start(); err sccb_write_byte(0x42); /* 写地址 */ err | sccb_write_byte(reg); err | sccb_write_byte(val); sccb_stop(); return err; }这里面err |的写法是有意为之三次字节传输只要有一次 NACK整体就判为失败调用方拿到非 0 直接打印寄存器号比在每一行手写判断省事得多。sccb_delay里的 5μs 对应约 100kHz 时钟OV7670 手册允许到 400kHz但面包板加杜邦线的场景下我一般压到 100kHz 以内出问题的概率明显低。SDA_H()之后一定要给从机拉低的机会所以在读 ACK 之前先释放总线并多等一个sccb_delay()。漏掉这一拍ACK 采样点会落在 SDA 还没稳定的时候表现出来就是初始化时零星几个寄存器写不进去重上电有时候又好了——典型的时序边缘问题。3.2 关键寄存器格式、分频、窗口下面是我在 160×120 RGB565 场景下用的一组核心值。表中只列真正影响结果的几项其余伽马、自动增益、白平衡用芯片默认值即可新手阶段不建议动。typedef struct { uint8_t reg, val; } reg_pair_t; const reg_pair_t ov7670_init_seq[] { /* 软复位后必须留 1ms 再写后续寄存器 */ {0x12, 0x80}, /* COM7复位 */ {0x11, 0x1F}, /* CLKRC内部分频PCLK 压到几百 kHz */ {0x12, 0x14}, /* COM7RGB 输出 QVGA 分辨率位 */ {0x8C, 0x00}, /* RGB444 关闭走 RGB565 */ {0x40, 0xD0}, /* COM15RGB565 输出数据范围 0~255 */ {0x14, 0x00}, /* COM10VSYNC 负极性、HREF 高有效 */ {0x17, 0x13}, /* HSTART */ {0x18, 0x01}, /* HSTOP */ {0x32, 0x80}, /* HREF水平有效宽度 */ {0x19, 0x02}, /* VSTART */ {0x1A, 0x7A}, /* VSTOP */ {0x03, 0x0A}, /* VREF垂直有效高度 */ {0x13, 0x00}, /* COM8先关自动白平衡/增益稳定后再开 */ };0x11这一行是整个项目里最关键的参数。值越大内部分频越狠PCLK 越低中断越轻松但帧率也越低。我一般在 0x0F 到 0x1F 之间试配合示波器看 PA0 的频率落到 300600kHz 这个区间基本能稳定跑。0x12 0x14的位含义是 bit[2] 置 1 选 RGB 输出、bit[6:4] 选分辨率档位。不同批次的模块对 QQVGA 这一档的编码不完全一致有人用 0x0C 出 QQVGA、有人用 0x1C这一点在手册里写得绕我是直接试出来的写完读回来用串口打印一行的字节数对得上就是对的。这也解释了为什么网上配置表看起来互相打架——不是谁抄错了是模块版本不同。0x40 0xD0配上0x8C 0x00是 RGB565 的组合如果0x8C误设成 0x01输出会掉到 RGB444一个像素还是两字节但只用了半个字节的信息画面会明显偏灰、细节全丢很容易被误判成“镜头脏了”。4. 采集核心PCLK 中断 IDR 整体读取的完整链路真正让无 FIFO 方案跑通的部分都在这里。中断服务函数里做的每个动作都会占用一个 PCLK 周期的时间所以代码要写得极简任何函数调用和浮点运算都得挪出去。4.1 EXTI0 中断一个像素两个字节怎么拼OV7670 在 RGB565 模式下一个像素输出两个字节先高字节后低字节中间夹着一个 PCLK。所以中断里要做的是两拍状态机而不是一次写完一个像素。#define OV_W 160 #define OV_H 120 #define OV_BYTES (OV_W * OV_H * 2) static uint8_t frame_buf[OV_BYTES]; static uint16_t byte_idx 0; /* 当前帧已写入的字节数 */ static uint8_t phase 0; /* 0等高字节 1等低字节 */ static uint8_t high_byte 0; static volatile uint8_t cap_en 0; /* HREF 高电平期间为 1 */ static volatile uint8_t fr_rdy 0; /* 一帧完整通知主循环 */ void EXTI0_IRQHandler(void) { EXTI-PR EXTI_PR_PR0; /* 写 1 清挂起别用读改写 */ if (!cap_en) return; /* HREF 无效时直接丢 */ uint8_t d (uint8_t)(GPIOB-IDR 0x00FF); /* PB0~PB7 一次读走 */ if (phase 0) { high_byte d; phase 1; } else { if (byte_idx OV_BYTES) { frame_buf[byte_idx] high_byte; /* 高字节在前 */ frame_buf[byte_idx] d; } phase 0; } }EXTI-PR必须用直接赋值而不是|因为 PR 是写 1 清除读回来全是 0 再写回去等于什么都没清中断会一直挂在里面反复进出。这个坑我在别的外设上踩过一次现象是程序卡死但看门狗不喂就复位。byte_idx OV_BYTES那个边界判断不能省。HREF 有效期间摄像头可能多吐出几个字节取决于窗口寄存器和 HREF 的实际宽度是否对齐没有这个判断就会踩穿数组跑到相邻变量上去表现出来就是“跑久了画面莫名其妙变成全黑”。4.2 HREF 与 VSYNC行同步和帧同步怎么挂PCLK 中断只管搬字节至于什么时候开始搬、搬多少算一行、什么时候算一帧结束全靠 HREF 和 VSYNC 两个中断来定。/* HREF上升沿开始一行下降沿结束一行 */ void EXTI1_IRQHandler(void) { EXTI-PR EXTI_PR_PR1; if (GPIOA-IDR GPIO_IDR_IDR1) { cap_en 1; phase 0; /* 每行开头对齐字节相位防止奇数丢失累积 */ } else { cap_en 0; } } /* VSYNC下降沿表示一帧结束切换缓冲 */ void EXTI2_IRQHandler(void) { EXTI-PR EXTI_PR_PR2; if (GPIOA-IDR GPIO_IDR_IDR2) return; /* 只处理下降沿 */ if (byte_idx OV_BYTES) { /* 收到完整一帧才算数 */ fr_rdy 1; byte_idx 0; /* 复位索引准备下一帧 */ } else { byte_idx 0; /* 不完整直接丢弃 */ } }phase 0这一行放在 HREF 上升沿里是为了处理一种边界情况如果某一行因为丢字节导致相位错位下一行开头强行归零就能把错误限制在当前行不会像多米诺骨牌一样把后面所有行全部带歪。第一次调试没加这行的时候画面表现是整体斜切从第 3 行开始一路歪到底。VSYNC 的极性由COM10寄存器决定。我在配置表里写的是0x14 0x00也就是 VSYNC 负极性下降沿代表帧开始/结束。如果极性反了fr_rdy永远不会被置位主循环一直等在那里看起来就像“程序跑飞了但其实没跑飞”。4.3 主循环出图与串口导出中断只负责填缓冲剩下的渲染、上传、处理全部放主循环两者通过fr_rdy这个标志握手。int main(void) { clock_init(); /* 72MHzMCO 输出 HSE 8MHz */ delay_init(); uart_init(115200); sccb_gpio_init(); data_gpio_init(); /* PB0~PB7 上拉输入 */ exti_init(); /* PA0/PA1/PA2 三路 EXTI */ tft_init(); if (ov7670_init() ! 0) { printf(ov7670 sccb fail\r\n); while (1); } ov7670_set_format(FMT_RGB565_QQVGA, 0x1F); /* 分辨率 分频 */ while (1) { if (fr_rdy) { fr_rdy 0; /* 屏显逐行推别整帧 memcpy 到显存F1 扛不住 */ tft_blit_rgb565(0, 0, OV_W, OV_H, frame_buf); /* 串口导出加两字节帧头方便上位机对齐 */ uart_send_frame(frame_buf, OV_BYTES); } } }tft_blit_rgb565里做的是“按行设置窗口 连续写”每行 320 字节一次 SPI 突发写完避免每写一个像素就拉一次 DC 和 CS。这里如果把整帧先复制到一个显存数组再刷F103 的 64KB RAM 直接爆掉所以必须边读边发。串口导出同理加0xAA 0x55帧头比一上来就灌数据有用得多。上位机靠帧头同步丢一个字节之后能自己找回来否则一旦错位后面整段数据全是垃圾你还得反复拔插重启。115200 波特率下传一帧 38400 字节大约 3.3 秒所以串口导出只适合抓单帧做验证实时预览还是得靠屏。5. 避坑排查雪花、斜切、偏色这五类问题无 FIFO 方案调不通的时候画面症状和原因通常是一一对应的把现象对上号能省掉大半天。下面五条都是我在这个组合上真实遇到过的。现象一整屏雪花偶尔闪过一条横带。原因基本是 PCLK 中断根本没进来或者进了但cap_en一直是 0。先查 EXTI 的触发边沿OV7670 的 PCLK 是在数据稳定后输出上升沿配置时选下降沿采样选反了读到的就是相邻像素的残值。再查EXTI-IMR有没有使能对应通道以及 NVIC 里的优先级分组别把三路中断排到同一个抢占级导致互相打断。 解决拿示波器同时看 PA0 和 PA1确认 HREF 为高的区间内确实有 PCLK 脉冲再看 CubeMX 生成的中断优先级把 PCLK 设成最高抢占级HREF/VSYNC 次之。现象二图像整体向右下方斜切越往下越歪。原因是行内字节数计算和实际 HREF 宽度对不上。160 宽 RGB565 应该是 320 字节一行如果OV_W写成 160 但缓冲按 160 字节一行算每行就会多写或漏写。另一种情况是phase在某行开头没归零导致后面的行全部差半个字节。 解决在 HREF 下降沿里把byte_idx % 320打印出来正常应该是 0不是 0 就说明这一行的字节数和预期不符先回去核对COM7选的分辨率档位和OV_W/OV_H宏是否对应。现象三画面只有上半部分有内容下半部分重复上一帧或者全黑。这是帧率超了缓冲承受能力。中断里写完一帧但主循环还没处理完下一帧直接把同一块内存覆盖掉读到的东西就是半新半旧的。 解决加双缓冲或者在fr_rdy为 1 期间在 HREF 上升沿里直接cap_en 0停止采集等主循环处理完再放行。代价是帧率掉一半但画面至少是对的。C8T6 这种 20KB 的芯片双缓冲放不下就只能走“采一帧、停一帧、刷一帧”的路子。现象四SCCB 写寄存器返回非 0摄像头初始化直接退出。三个常见原因SDA/SCL 没加上拉电阻很多模块板上没有必须外部补 4.7kXCLK 没给OV7670 内部逻辑不工作从机自然不应答PWDN 引脚悬空被拉高芯片处于掉电态。 解决先量 PA8 有没有 8MHz 方波再量 SDA 空闲时是不是被上拉到 3.3V最后确认 PWDN 被拉低、RESET 已经释放。这三步按顺序走比盲目改延时靠谱。现象五图像能出但颜色不对绿得像夜视仪或者整体偏紫红。偏绿通常是字节序反了RGB565 的高低字节在缓冲里位置颠倒G 分量被拼到了最高位偏紫红多半是格式选错实际输出的是 YUV422 却按 RGB565 解释。 解决先确认0x40 0xD0、0x8C 0x00这两项再在 EXTI0 里把high_byte和d的写入顺序对调试一次两种顺序各拍一帧对比颜色正常的那一组就是对的。这个排查过程不到两分钟比查手册快。6. 二次开发换分辨率、搬到 F4、加一层简单识别这套采集链路跑通之后改动的空间比想象中大但每一步都要重新算账不能只改宏定义。换分辨率是最常见的需求对照表如下目标分辨率格式单帧字节建议分频0x11备注80×60RGB56596000x0FC8T6 可双缓冲160×120灰度192000x13只取高字节省一半内存160×120RGB565384000x1FZET6 单缓冲帧率 58fps320×240灰度768000x3FZET6 极限帧率 12fps改的时候三处要同步OV_W/OV_H宏、ov7670_regs.c里COM7的分辨率位、窗口寄存器HSTART/HSTOP/VSTART/VSTOP。只改前两处不改窗口出来的图会是原分辨率裁出来的一块边缘还会带黑边。灰度模式不需要改摄像头配置在 EXTI0 里只存high_byte就行一个像素一个字节中断耗时直接砍半帧率能翻倍。搬到 F4 或者 F7 上整个采集层可以扔掉重写。这些片子有 DCMI 外设配 DMA 之后 CPU 完全不参与搬运OV7670 的 PCLK 接 DCMI_PIXCLK、8 位数据接 DCMI_D0D7、HREF 接 DCMI_HSYNC、VSYNC 接 DCMI_VSYNC一帧采完才进一次中断。我在 F407 上做过同样的摄像头320×240 RGB565 稳定 30fps和 F1 上的 5fps 完全不是一个量级。但 F1 这套代码的价值在于把时序搞明白了——知道 PCLK 是什么、HREF 什么时候有效、字节相位怎么对齐换到 DCMI 上配寄存器时心里有底不会照抄例程然后对着花屏干瞪眼。加简单识别也不难在主循环拿到frame_buf之后做就行不用碰中断/* 在 160x120 RGB565 里找红色色块返回中心坐标 */ int find_red_blob(const uint8_t *buf, int *cx, int *cy) { int sum_x 0, sum_y 0, cnt 0; for (int y 0; y OV_H; y 2) { /* 隔行扫省一半时间 */ for (int x 0; x OV_W; x 2) { int i (y * OV_W x) * 2; uint16_t px (buf[i] 8) | buf[i 1]; /* 高字节在前 */ int r (px 11) 0x1F; int g (px 5) 0x3F; int b px 0x1F; if (r 20 g 12 b 12) { /* 阈值按现场光调 */ sum_x x; sum_y y; cnt; } } } if (cnt 20) return -1; /* 太小当噪声丢掉 */ *cx sum_x / cnt; *cy sum_y / cnt; return 0; }阈值那几个数没有通用值白光灯和暖光灯下差得很远我一般是先把cnt和几个采样点的 RGB 打印出来看着实际分布定而不是凭感觉写 15、10、10。隔行隔列扫描是有意为之160×120 全扫 19200 个像素在 72MHz 上大概 8ms隔行隔列能压到 2ms 左右对质心计算的影响可以忽略。验证改造是否成功我有个固定套路先降到 80×60 灰度、PCLK 分频拉到最大确保能出稳定的一帧然后逐项放开——先提分辨率、再换 RGB565、最后提帧率。每放开一项就用串口抓一帧存成文件看不和屏幕较劲。这条路径慢但每次只改一个变量出问题时原因一定是刚改的那一项不用回头怀疑已经验证过的东西。第一次调这个方案时我图省事把分辨率、格式、分频三个参数一起改结果花屏花了两个晚上最后发现只是 CLKRC 设得太大导致 PCLK 慢到 HREF 提前拉低。从那以后每次动 OV7670 的寄存器我都强制自己一次只改一行改完必须用示波器确认 PCLK 和 HREF 的关系再往下走。这个习惯看着笨但确实省下了大量来回折腾的时间。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询