简介:这份资源面向使用STM32F103驱动TSL1401线性CCD模块的嵌入式开发者与智能车竞赛选手,提供从底层驱动到上位机调试的完整方案。包内包含STM32F103驱动TSL1401的工程代码、拉普兰德线性CCD调试上位机、指导手册,以及拉普兰德与逐飞红孩儿CCD的参考资料,示例工程基于正点原子MINI板与逐飞红孩儿CCD搭建,任意STM32F103单片机均可移植使用。资源共437个文件,以h头文件与c源文件为主体,配合cout、pbi、icf、cmd等编译与工程配置文件,另有pdf手册、txt说明、bat脚本及exe上位机程序,压缩包约12.56MB。已有2084人学习下载,读者可据此快速完成CCD采集、二值化与边线提取调试,并借助上位机观察波形、定位阈值问题;针对红孩儿模块停产的情况,资料也提示可替换为龙邱科技或平衡小车之家的同类TSL1401模块,便于持续复现与二次开发。1. 从一块线性 CCD 说起:为什么 STM32F103 驱动 TSL1401 值得单独拆一遍如果你手头有一块 TSL1401 线性 CCD 模块,想用 STM32F103 把它驱动起来,大概率会经历这么几个阶段:先找数据手册,再对着时序图发呆,然后发现网上能跑的完整工程要么缺文件、要么引脚对不上、要么上位机收不到数据。这个资源包解决的正是这条链路——从 STM32F103 的 GPIO 和 ADC 配置,到 TSL1401 的 SI、CLK 时序控制,再到串口把 128 个像素点丢给上位机做波形显示。它适合正在做循迹小车、简易线阵扫描、光电检测类项目的开发者,也适合想拿一个真实外设练手 STM32 底层时序的人。下面我按“能干什么 → 怎么跑 → 怎么改”的顺序,把这份工程拆开讲。2. 项目定位与技术选型:为什么是 F103 + TSL1401 这套组合2.1 TSL1401 到底是什么,能输出什么TSL1401 是一颗 128 像素的线性 CCD 传感器,内部集成了像素阵列、电荷转移和输出放大。它对外只暴露几个关键引脚:SI(串行输入,用来标记曝光开始)、CLK(时钟,驱动像素逐个输出)、AO(模拟输出,把每个像素的电压依次吐出来)。你给它一组干净的时钟,它就在 AO 上按顺序输出 128 个模拟电压,每个电压对应一个像素感受到的光强。这意味着两件事:第一,它输出的是模拟量,必须接 ADC 才能变成数字;第二,它的时序要求比较死,SI 和 CLK 的配合错了,输出就是乱的。很多新手翻车不是翻在代码逻辑,而是翻在时序没对齐。2.2 为什么选 STM32F103 而不是别的 MCUSTM32F103 在这个场景里有几个现实优势。它的 GPIO 翻转速度足够快,能产生 TSL1401 需要的 CLK 频率;它自带 12 位 ADC,采样速率和精度对 128 像素的线阵扫描够用;它的 USART 外设成熟,把数据打包发给上位机很顺手。更关键的是,F103 的资料和例程基数大,你遇到问题更容易找到参考。常见做法是:用两个普通 GPIO 分别控制 SI 和 CLK,用一个 ADC 通道接 AO,再用 USART1 做数据上传。这个资源包基本就是按这个思路组织的。2.3 上位机在链路里的角色光有 MCU 采到数据还不够,你得看见波形。资源里提到的上位机,作用就是通过串口接收 STM32 发来的像素数据,然后画成一条曲线。对调试来说,这比盯着串口助手里的一堆数字直观得多。你能立刻看出曝光时间够不够、有没有像素饱和、时序有没有错位。提示:上位机不是必须的,但强烈建议先用它把波形调正常,再去做后续的阈值判断或循迹逻辑。3. 工程结构与核心模块拆解:从 GPIO 到 ADC 再到串口3.1 目录结构先看清楚拿到一个 STM32 工程,我一般先看目录,判断它是标准库还是 HAL 库,外设初始化放在哪,主循环在哪。这个资源包的结构大致是:Project/ ├── CMSIS/ # 内核相关文件 ├── STM32F10x_StdPeriph_Driver/ # 标准外设库 ├── User/ │ ├── main.c # 主循环与采集逻辑 │ ├── tsl1401.c # CCD 驱动时序 │ ├── tsl1401.h │ ├── usart.c # 串口发送 │ └── delay.c # 延时 └── Project.uvprojx # Keil 工程文件如果你用的是 HAL 库,目录会不一样,但核心逻辑不变:时序驱动、ADC 采集、串口上传三块。3.2 TSL1401 的时序驱动代码这是整个项目最核心的部分。TSL1401 的一个采集周期大致是:拉高 SI 一段时间,然后在 CLK 的驱动下输出 128 个像素,最后再多给几个时钟把电荷清掉。// tsl1401.c #include "tsl1401.h" #include "delay.h" // 引脚定义,按实际接线修改 #define SI_H GPIO_SetBits(GPIOA, GPIO_Pin_0) #define SI_L GPIO_ResetBits(GPIOA, GPIO_Pin_0) #define CLK_H GPIO_SetBits(GPIOA, GPIO_Pin_1) #define CLK_L GPIO_ResetBits(GPIOA, GPIO_Pin_1) void TSL1401_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); SI_L; CLK_L; } // 采集一次,返回 128 个像素值 void TSL1401_Read(u16 *buffer) { u8 i; // 1. SI 拉高,标记曝光开始 SI_H; CLK_L; delay_us(1); SI_L; // 2. 第一个时钟,准备输出第一个像素 CLK_H; delay_us(1); CLK_L; delay_us(1); // 3. 依次读取 128 个像素 for (i = 0; i < 128; i++) { CLK_H; delay_us(1); buffer[i] = Get_ADC_Value(); // 读取 AO 上的模拟量 CLK_L; delay_us(1); } // 4. 额外时钟,清空电荷 for (i = 0; i < 2; i++) { CLK_H; delay_us(1); CLK_L; delay_us(1); } }逻辑说明:SI 的拉高拉低决定了曝光窗口,CLK 的每个上升沿推动一个像素输出到 AO。delay_us(1)是保守值,实际可以根据 TSL1401 的时钟频率上限适当缩短。Get_ADC_Value()是你自己的 ADC 读取函数,需要保证在 CLK 高电平期间完成采样。参数说明:buffer是长度为 128 的 u16 数组,用来存放每个像素的 ADC 值。如果你用的是 12 位 ADC,值域是 0 到 4095。3.3 ADC 采集与串口上传ADC 部分我一般用规则通道单次转换,每次在 CLK 高电平期间触发一次。串口部分则把 128 个值按固定格式发出去,方便上位机解析。// main.c 片段 u16 ccd_data[128]; int main(void) { USART1_Init(115200); TSL1401_Init(); ADC1_Init(); while (1) { TSL1401_Read(ccd_data); USART_Send_CCD_Data(ccd_data, 128); delay_ms(10); // 控制刷新率 } }逻辑说明:主循环里先采集一轮,再通过串口发送,然后延时控制刷新节奏。USART_Send_CCD_Data通常会把数据加上帧头帧尾,比如0xAA 0xBB开头,方便上位机做同步。参数说明:波特率 115200 是常见选择,128 个 12 位数据加上帧头,一帧大约 300 字节,115200 下传输时间约 26ms,所以delay_ms(10)加上传输时间,刷新率大概在 20 到 30 帧每秒。4. 编译、烧录与上位机联调:把波形跑出来的完整步骤4.1 环境准备与工程打开你需要 Keil MDK,版本不用太新,能打开标准库工程就行。打开Project.uvprojx后,先检查两件事:一是芯片型号是否选对(STM32F103C8 或你实际用的型号),二是下载器配置是否正确(ST-Link 或 J-Link)。如果编译报错找不到头文件,检查Options for Target里的 Include Paths 是否包含了User和STM32F10x_StdPeriph_Driver/inc。4.2 接线与上电检查接线按代码里的定义来:PA0 接 SI,PA1 接 CLK,AO 接你 ADC 初始化里配置的那个通道引脚,串口 TX 接 USB 转串口模块的 RX。上电后先用万用表量一下 SI 和 CLK,正常应该有电平变化。如果一直是高或一直是低,先查 GPIO 初始化和时钟使能。4.3 上位机配置与数据观察打开上位机,选对串口号和波特率,应该能看到一条 128 点的曲线。如果曲线是一条直线,可能是 AO 没接对或者 ADC 通道配错;如果曲线乱跳,先检查 SI 和 CLK 的延时是否太短。我一般会先把delay_us(1)改成delay_us(5),确认时序稳定后再往回调。注意:不同批次的 TSL1401 模块,AO 的驱动能力略有差异,如果 ADC 读数一直偏低,可以在 AO 和地之间并一个合适的电阻,或者降低 CLK 频率。5. 避坑与常见问题:时序、ADC 和串口这三处最容易翻车5.1 波形全黑或全白现象:上位机曲线要么贴底要么贴顶,没有中间变化。原因通常是曝光时间不对。SI 拉高到第一个 CLK 之间的时间决定了曝光窗口,太短则全黑,太长则全白。解决:调整SI_H之后的delay_us,或者改变主循环的刷新间隔。5.2 像素错位、曲线有重复现象:曲线看起来像被平移过,或者某几个点重复出现。原因多半是 CLK 和 ADC 采样的配合有问题。ADC 还没转换完,CLK 就翻了,导致采到的是上一个像素的值。解决:确保Get_ADC_Value()是阻塞式等待转换完成,或者在 CLK 高电平期间多等几个微秒。5.3 串口收到乱码现象:上位机解析不出数据,或者帧头对不上。原因一般是波特率不匹配,或者发送函数里用了printf但没重定向。解决:检查 USART 初始化的波特率和上位机是否一致;如果用了printf,确认fputc重定向到了 USART1。5.4 编译通过但下载后没反应现象:程序下载成功,但串口没数据,波形也不动。原因可能是启动文件选错,或者中断向量表偏移不对。解决:确认Options for Target里的 Device 和 Startup 文件匹配;如果用了 bootloader,检查SCB->VTOR的设置。5.5 ADC 读数跳动大现象:同一个像素,两次采集的值差很多。原因是 ADC 参考电压不稳或者模拟地有干扰。解决:在 AO 和地之间加一个小电容滤波;确保 ADC 的参考电压引脚有合适的去耦电容。6. 二次开发与改造思路:从能跑到好用6.1 把采集封装成定时器触发现在的主循环是软件延时控制刷新率,精度一般。更稳的做法是用 TIM 定时触发 ADC,ADC 转换完成后在中断里推动 CLK 状态机。这样采集节奏由硬件保证,CPU 占用也低。改造时注意中断优先级,ADC 中断不要被串口发送打断太久。6.2 加入二值化与阈值自适应如果你做循迹,128 个原始值直接用会比较吃力。常见做法是每帧算一次最大值和最小值,取中间值作为阈值,把像素分成黑白两段,再算黑线的中心位置。这个逻辑可以放在 MCU 里,也可以放在上位机里。放在 MCU 里响应更快,放在上位机里调试更方便。6.3 用 DMA 搬 ADC 数据当 CLK 频率提上去之后,CPU 在中断里逐个读 ADC 会来不及。这时候可以用 ADC 的 DMA 模式,让每次转换结果自动搬到数组里,CPU 只负责产生 CLK 和 SI。改造的关键是 DMA 的触发源要选对,缓冲区长度要覆盖 128 个点。6.4 上位机协议改成二进制帧现在的串口发送如果是 ASCII 文本,一帧数据量会比较大。改成二进制帧,比如0xAA 0xBB加 128 个 u16 小端数据加校验和,传输效率更高,也更适合高刷新率场景。上位机解析时注意字节序和帧同步。6.5 一个我踩过的坑最早调这个模块时,我以为 CLK 越快越好,直接把延时去掉,结果波形完全乱掉。后来用示波器看,发现 CLK 高电平时间太短,ADC 根本来不及采样。从那以后我每次改时序参数,都强制先用示波器看一遍 SI、CLK 和 AO 的对应关系,确认没有竞争再往下走。希望这个习惯能帮到你。本文还有配套的精品资源,点击获取
简介:这份资源面向使用STM32F103驱动TSL1401线性CCD模块的嵌入式开发者与智能车竞赛选手,提供从底层驱动到上位机调试的完整方案。包内包含STM32F103驱动TSL1401的工程代码、拉普兰德线性CCD调试上位机、指导手册,以及拉普兰德与逐飞红孩儿CCD的参考资料,示例工程基于正点原子MINI板与逐飞红孩儿CCD搭建,任意STM32F103单片机均可移植使用。资源共437个文件,以h头文件与c源文件为主体,配合cout、pbi、icf、cmd等编译与工程配置文件,另有pdf手册、txt说明、bat脚本及exe上位机程序,压缩包约12.56MB。已有2084人学习下载,读者可据此快速完成CCD采集、二值化与边线提取调试,并借助上位机观察波形、定位阈值问题;针对红孩儿模块停产的情况,资料也提示可替换为龙邱科技或平衡小车之家的同类TSL1401模块,便于持续复现与二次开发。1. 从一块线性 CCD 说起:为什么 STM32F103 驱动 TSL1401 值得单独拆一遍如果你手头有一块 TSL1401 线性 CCD 模块,想用 STM32F103 把它驱动起来,大概率会经历这么几个阶段:先找数据手册,再对着时序图发呆,然后发现网上能跑的完整工程要么缺文件、要么引脚对不上、要么上位机收不到数据。这个资源包解决的正是这条链路——从 STM32F103 的 GPIO 和 ADC 配置,到 TSL1401 的 SI、CLK 时序控制,再到串口把 128 个像素点丢给上位机做波形显示。它适合正在做循迹小车、简易线阵扫描、光电检测类项目的开发者,也适合想拿一个真实外设练手 STM32 底层时序的人。下面我按“能干什么 → 怎么跑 → 怎么改”的顺序,把这份工程拆开讲。2. 项目定位与技术选型:为什么是 F103 + TSL1401 这套组合2.1 TSL1401 到底是什么,能输出什么TSL1401 是一颗 128 像素的线性 CCD 传感器,内部集成了像素阵列、电荷转移和输出放大。它对外只暴露几个关键引脚:SI(串行输入,用来标记曝光开始)、CLK(时钟,驱动像素逐个输出)、AO(模拟输出,把每个像素的电压依次吐出来)。你给它一组干净的时钟,它就在 AO 上按顺序输出 128 个模拟电压,每个电压对应一个像素感受到的光强。这意味着两件事:第一,它输出的是模拟量,必须接 ADC 才能变成数字;第二,它的时序要求比较死,SI 和 CLK 的配合错了,输出就是乱的。很多新手翻车不是翻在代码逻辑,而是翻在时序没对齐。2.2 为什么选 STM32F103 而不是别的 MCUSTM32F103 在这个场景里有几个现实优势。它的 GPIO 翻转速度足够快,能产生 TSL1401 需要的 CLK 频率;它自带 12 位 ADC,采样速率和精度对 128 像素的线阵扫描够用;它的 USART 外设成熟,把数据打包发给上位机很顺手。更关键的是,F103 的资料和例程基数大,你遇到问题更容易找到参考。常见做法是:用两个普通 GPIO 分别控制 SI 和 CLK,用一个 ADC 通道接 AO,再用 USART1 做数据上传。这个资源包基本就是按这个思路组织的。2.3 上位机在链路里的角色光有 MCU 采到数据还不够,你得看见波形。资源里提到的上位机,作用就是通过串口接收 STM32 发来的像素数据,然后画成一条曲线。对调试来说,这比盯着串口助手里的一堆数字直观得多。你能立刻看出曝光时间够不够、有没有像素饱和、时序有没有错位。提示:上位机不是必须的,但强烈建议先用它把波形调正常,再去做后续的阈值判断或循迹逻辑。3. 工程结构与核心模块拆解:从 GPIO 到 ADC 再到串口3.1 目录结构先看清楚拿到一个 STM32 工程,我一般先看目录,判断它是标准库还是 HAL 库,外设初始化放在哪,主循环在哪。这个资源包的结构大致是:Project/ ├── CMSIS/ # 内核相关文件 ├── STM32F10x_StdPeriph_Driver/ # 标准外设库 ├── User/ │ ├── main.c # 主循环与采集逻辑 │ ├── tsl1401.c # CCD 驱动时序 │ ├── tsl1401.h │ ├── usart.c # 串口发送 │ └── delay.c # 延时 └── Project.uvprojx # Keil 工程文件如果你用的是 HAL 库,目录会不一样,但核心逻辑不变:时序驱动、ADC 采集、串口上传三块。3.2 TSL1401 的时序驱动代码这是整个项目最核心的部分。TSL1401 的一个采集周期大致是:拉高 SI 一段时间,然后在 CLK 的驱动下输出 128 个像素,最后再多给几个时钟把电荷清掉。// tsl1401.c #include "tsl1401.h" #include "delay.h" // 引脚定义,按实际接线修改 #define SI_H GPIO_SetBits(GPIOA, GPIO_Pin_0) #define SI_L GPIO_ResetBits(GPIOA, GPIO_Pin_0) #define CLK_H GPIO_SetBits(GPIOA, GPIO_Pin_1) #define CLK_L GPIO_ResetBits(GPIOA, GPIO_Pin_1) void TSL1401_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); SI_L; CLK_L; } // 采集一次,返回 128 个像素值 void TSL1401_Read(u16 *buffer) { u8 i; // 1. SI 拉高,标记曝光开始 SI_H; CLK_L; delay_us(1); SI_L; // 2. 第一个时钟,准备输出第一个像素 CLK_H; delay_us(1); CLK_L; delay_us(1); // 3. 依次读取 128 个像素 for (i = 0; i < 128; i++) { CLK_H; delay_us(1); buffer[i] = Get_ADC_Value(); // 读取 AO 上的模拟量 CLK_L; delay_us(1); } // 4. 额外时钟,清空电荷 for (i = 0; i < 2; i++) { CLK_H; delay_us(1); CLK_L; delay_us(1); } }逻辑说明:SI 的拉高拉低决定了曝光窗口,CLK 的每个上升沿推动一个像素输出到 AO。delay_us(1)是保守值,实际可以根据 TSL1401 的时钟频率上限适当缩短。Get_ADC_Value()是你自己的 ADC 读取函数,需要保证在 CLK 高电平期间完成采样。参数说明:buffer是长度为 128 的 u16 数组,用来存放每个像素的 ADC 值。如果你用的是 12 位 ADC,值域是 0 到 4095。3.3 ADC 采集与串口上传ADC 部分我一般用规则通道单次转换,每次在 CLK 高电平期间触发一次。串口部分则把 128 个值按固定格式发出去,方便上位机解析。// main.c 片段 u16 ccd_data[128]; int main(void) { USART1_Init(115200); TSL1401_Init(); ADC1_Init(); while (1) { TSL1401_Read(ccd_data); USART_Send_CCD_Data(ccd_data, 128); delay_ms(10); // 控制刷新率 } }逻辑说明:主循环里先采集一轮,再通过串口发送,然后延时控制刷新节奏。USART_Send_CCD_Data通常会把数据加上帧头帧尾,比如0xAA 0xBB开头,方便上位机做同步。参数说明:波特率 115200 是常见选择,128 个 12 位数据加上帧头,一帧大约 300 字节,115200 下传输时间约 26ms,所以delay_ms(10)加上传输时间,刷新率大概在 20 到 30 帧每秒。4. 编译、烧录与上位机联调:把波形跑出来的完整步骤4.1 环境准备与工程打开你需要 Keil MDK,版本不用太新,能打开标准库工程就行。打开Project.uvprojx后,先检查两件事:一是芯片型号是否选对(STM32F103C8 或你实际用的型号),二是下载器配置是否正确(ST-Link 或 J-Link)。如果编译报错找不到头文件,检查Options for Target里的 Include Paths 是否包含了User和STM32F10x_StdPeriph_Driver/inc。4.2 接线与上电检查接线按代码里的定义来:PA0 接 SI,PA1 接 CLK,AO 接你 ADC 初始化里配置的那个通道引脚,串口 TX 接 USB 转串口模块的 RX。上电后先用万用表量一下 SI 和 CLK,正常应该有电平变化。如果一直是高或一直是低,先查 GPIO 初始化和时钟使能。4.3 上位机配置与数据观察打开上位机,选对串口号和波特率,应该能看到一条 128 点的曲线。如果曲线是一条直线,可能是 AO 没接对或者 ADC 通道配错;如果曲线乱跳,先检查 SI 和 CLK 的延时是否太短。我一般会先把delay_us(1)改成delay_us(5),确认时序稳定后再往回调。注意:不同批次的 TSL1401 模块,AO 的驱动能力略有差异,如果 ADC 读数一直偏低,可以在 AO 和地之间并一个合适的电阻,或者降低 CLK 频率。5. 避坑与常见问题:时序、ADC 和串口这三处最容易翻车5.1 波形全黑或全白现象:上位机曲线要么贴底要么贴顶,没有中间变化。原因通常是曝光时间不对。SI 拉高到第一个 CLK 之间的时间决定了曝光窗口,太短则全黑,太长则全白。解决:调整SI_H之后的delay_us,或者改变主循环的刷新间隔。5.2 像素错位、曲线有重复现象:曲线看起来像被平移过,或者某几个点重复出现。原因多半是 CLK 和 ADC 采样的配合有问题。ADC 还没转换完,CLK 就翻了,导致采到的是上一个像素的值。解决:确保Get_ADC_Value()是阻塞式等待转换完成,或者在 CLK 高电平期间多等几个微秒。5.3 串口收到乱码现象:上位机解析不出数据,或者帧头对不上。原因一般是波特率不匹配,或者发送函数里用了printf但没重定向。解决:检查 USART 初始化的波特率和上位机是否一致;如果用了printf,确认fputc重定向到了 USART1。5.4 编译通过但下载后没反应现象:程序下载成功,但串口没数据,波形也不动。原因可能是启动文件选错,或者中断向量表偏移不对。解决:确认Options for Target里的 Device 和 Startup 文件匹配;如果用了 bootloader,检查SCB->VTOR的设置。5.5 ADC 读数跳动大现象:同一个像素,两次采集的值差很多。原因是 ADC 参考电压不稳或者模拟地有干扰。解决:在 AO 和地之间加一个小电容滤波;确保 ADC 的参考电压引脚有合适的去耦电容。6. 二次开发与改造思路:从能跑到好用6.1 把采集封装成定时器触发现在的主循环是软件延时控制刷新率,精度一般。更稳的做法是用 TIM 定时触发 ADC,ADC 转换完成后在中断里推动 CLK 状态机。这样采集节奏由硬件保证,CPU 占用也低。改造时注意中断优先级,ADC 中断不要被串口发送打断太久。6.2 加入二值化与阈值自适应如果你做循迹,128 个原始值直接用会比较吃力。常见做法是每帧算一次最大值和最小值,取中间值作为阈值,把像素分成黑白两段,再算黑线的中心位置。这个逻辑可以放在 MCU 里,也可以放在上位机里。放在 MCU 里响应更快,放在上位机里调试更方便。6.3 用 DMA 搬 ADC 数据当 CLK 频率提上去之后,CPU 在中断里逐个读 ADC 会来不及。这时候可以用 ADC 的 DMA 模式,让每次转换结果自动搬到数组里,CPU 只负责产生 CLK 和 SI。改造的关键是 DMA 的触发源要选对,缓冲区长度要覆盖 128 个点。6.4 上位机协议改成二进制帧现在的串口发送如果是 ASCII 文本,一帧数据量会比较大。改成二进制帧,比如0xAA 0xBB加 128 个 u16 小端数据加校验和,传输效率更高,也更适合高刷新率场景。上位机解析时注意字节序和帧同步。6.5 一个我踩过的坑最早调这个模块时,我以为 CLK 越快越好,直接把延时去掉,结果波形完全乱掉。后来用示波器看,发现 CLK 高电平时间太短,ADC 根本来不及采样。从那以后我每次改时序参数,都强制先用示波器看一遍 SI、CLK 和 AO 的对应关系,确认没有竞争再往下走。希望这个习惯能帮到你。本文还有配套的精品资源,点击获取