蓝桥杯单片机四合一实战:DS18B20、超声波、光敏、红外多传感器融合

发布时间:2026/10/13 14:32:08
蓝桥杯单片机四合一实战:DS18B20、超声波、光敏、红外多传感器融合 简介这是一份面向蓝桥杯单片机竞赛选手的综合实训资源聚焦温度显示、超声波测距、光敏D/A显示与红外接收数据显示四大模块并融入矩阵键盘按键切换功能适合正在备赛或希望提升传感器与显示接口开发能力的学习者。资源为zip压缩包共39个文件包含C语言源码、头文件、编译生成的lst与obj文件、hex烧录文件及Keil工程配置uvproj/uvopt可完整查看代码逻辑、编译过程与烧录结果便于对照学习与二次修改。压缩包整体约90KB结构紧凑按模块拆分DS18B20、IIC、infrared、Keyboard、SMG、Dis等文件方便逐项理解温度采集、I2C通信、红外解码、数码管显示等实现细节。已有1640人学习下载适合边练边查、快速上手蓝桥杯单片机常见外设编程。通过本资料读者可获得一套可直接运行的工程示例结合内容预览中的main.c与各外设驱动文件快速梳理完整程序框架掌握多模块协同设计的基本思路。1. 蓝桥杯单片机四合一实战资源为什么这套组合题值得拆开练蓝桥杯单片机组省赛和国赛里温度、超声波、光敏、红外这四个模块的单体赛题已经是最常见的老朋友但真正让人头疼的是把它们塞进同一块板子、同一个定时器中断和同一套显示系统里。很多同学拿到这套资源的第一反应是“四个模块都会合起来就乱”而这恰恰是这套四合一资源的价值所在它不是四份独立代码的简单打包而是把DS18B20温度读取、HC-SR04超声波测距、光敏电阻配合D/A输出、红外遥控解码四路数据统一到一套按键切换和数码管/液晶显示框架里让你直接看到多传感器系统在蓝桥杯板子上的真实组织方式。适合备赛省赛国赛的考生也适合准备课程设计和毕业设计里“多参数采集系统”这类题的人。我拆完这套资源后的判断是代码可以直接烧但如果你只烧不读下一道变形题照样卡住所以这篇笔记把原理、接线、参数和坑都摊开讲。2. 从数据手册到寄存器四路采集的硬件通路与代码骨架2.1 DS18B20温度采集单总线时序与12位分辨率转换温度显示模块用的是一线总线的数字温度传感器单根数据线既要发命令又要读数据所以代码的核心是时序控制。这套资源里没有用现成的延时函数库而是基于STC单片机的机器周期手动写了微秒级延时因为DS18B20对时序窗口要求很苛刻复位脉冲低电平要保持480us以上存在脉冲窗口在60-240us之间读时隙必须在15us内完成采样。// DS18B20 复位与存在检测 bit DS18B20_Reset(void) { bit presence; DQ 0; // 拉低总线 delay_us(500); // 主机发出复位脉冲480us DQ 1; // 释放总线 delay_us(60); // 等待器件应答窗口 presence DQ; // 采样总线器件拉低则存在 delay_us(240); // 读完整时隙 return presence; }这段代码里最关键的是delay_us(500)后面的采样点。我调试时曾经把60us等待写成30us结果存在检测偶尔成功偶尔失败高温环境下尤其明显。原因是一线总线上DS18B20的上拉电阻和寄生供电状态下释放总线后的恢复时间变长采样点需要落在应答窗口的后半段才稳定。读温度数据的过程就是先跳过ROM匹配0xCC再发温度转换命令0x44等待750ms转换完成最后发读取暂存器命令0xBE连续读两个字节拼成16位温度值。注意这750ms是12位分辨率下的典型转换时间如果用户代码里改成9位分辨率转换时间降到93.75ms但精度降为0.5摄氏度。蓝桥杯赛题通常要求显示到0.1摄氏度所以资源里保持12位分辨率低4位是小数部分需要右移4位后乘0.0625。2.2 HC-SR04超声测距10us触发脉冲与回波计时窗口超声波模块的测距原理不复杂给Trig脚一个10us以上的高电平触发模块内部发8个40kHz方波然后Echo脚输出一个与距离成正比的高电平脉冲。测距的核心变成“测量Echo高电平持续的时间”再乘声速344米每秒除以2。// 超声测距T0计时方式读取回波宽度 void Ultrasonic_Measure(void) { uint16_t time_cnt 0; Trig 1; delay_us(15); // 触发脉冲必须 10us Trig 0; while (!Echo); // 等待回波高电平到来 TR0 1; // 启动定时器0 while (Echo); // 等待回波结束 TR0 0; // 停止定时器 time_cnt TH0 8 | TL0; distance_mm time_cnt * 0.017; // 每1us对应0.17mm }这段代码有一个很隐蔽的隐患如果超声波模块前方没有障碍物Echo脚会一直保持低电平while (!Echo)就变成死等。我一般在真实项目里给回波等待加一个超时判断比如超过50ms就认为测量无效返回值直接置为999走异常显示分支。蓝桥杯的板载超声波模块在无遮挡时回波通常会在20ms内返回但赛场环境未必理想资源里没有做超时保护这是我唯一不满意的地方后面避坑章会细说。3. 四合一资源怎么落地硬件接线、工程结构与逐模块复现步骤3.1 工程目录与代码组织方式这套资源解压后是一个完整的蓝桥杯竞赛工程目录不是散点代码。主文件里包含了时钟初始化、定时器配置、按键扫描、数码管段码表和各模块驱动头文件。推荐的阅读顺序是先看main.c里的主循环和状态切换逻辑再看timer.c里定时器中断的分配最后逐个打开外设驱动文件。// 主循环中的模块切换逻辑 void main(void) { Sys_Init(); while (1) { Key_Scan(); // 按键扫描切换显示模式 switch (disp_mode) { case 0: Temp_Display(); break; // 温度显示 case 1: Ultra_Display(); break; // 超声波测距显示 case 2: Light_Display(); break; // 光敏D/A显示 case 3: IR_Display(); break; // 红外接收显示 } } }主循环的思路是按键改变disp_mode每个case里只做对应模块的采集与刷新。这种模式的好处是模块间互不干扰缺点是四个case都在主循环里轮询如果某个模块的采集函数里有长延时就会拖慢其他模块的响应。实际比赛中更稳的做法是把采集放到定时器中断的时隙里主循环只做显示刷新但资源的代码架构相对保守适合先跑通再优化。3.2 光敏D/A显示模块从光强到电压再到数码管的整条链路光敏电阻配合D/A输出是蓝桥杯里比较绕的一道题很多同学只关心怎么把光敏电阻的阻值变化读回来忽略了输出端要用D/A芯片产生电压信号。资源的实现方式是光敏电阻和固定电阻分压分压点接入ADC输入通道单片机采集到数字量后通过D/A芯片输出对应的模拟电压数码管同时显示ADC值和D/A输出值。// 光敏采集与 D/A 输出联动 void Light_DA_Process(void) { uint16_t adc_val ADC_Read(LIGHT_CH); // 读取光敏分压值 uint16_t dac_val (uint16_t)(adc_val * 5.0f / 255.0f * 51.0f); DAC_Write(0, (uint8_t)dac_val); // 通道0输出对应电压 Display_Number(adc_val, 2); // 数码管显示2位ADC值 }这里adc_val * 5.0f / 255.0f是把8位ADC采集值换算成实际电压再乘51是因为蓝桥杯板载D/A芯片输出范围是0-5V对应的数字量0-255乘一个系数让数值和数码管显示位数匹配。实际上D/A芯片的输出电压和数字量是线性关系理想情况下数字量255对应5V不需要乘51这个系数。这位作者加这个系数我猜测是为了让数码管显示“ADC值”和“D/A输出值”看起来有区分度实际赛题里如果要求显示电压值这个系数反而会造成误差。建议拿到资源后先核对D/A芯片型号再决定要不要保留这个系数。3.3 红外接收显示NEC协议解码与键值映射表红外模块用的是NEC协议载波频率38kHz数据帧由9ms引导码、4.5ms结果码、8位地址码、8位地址反码、8位命令码和8位命令反码组成。资源里用外部中断0配合定时器1来完成解码下降沿触发中断后在中断服务函数里读取定时器的计数值来判断当前是引导码还是数据位。// 红外解码定时器计数值区分码位 void Exti0_ISR(void) interrupt 0 { uint16_t timer_cnt Get_Timer1(); if (timer_cnt 12000) // 引导码约9ms4.5ms { ir_state 1; // 收到引导码开始接收数据 bit_cnt 0; } else if (timer_cnt 1000 timer_cnt 1400) { ir_data | (1 bit_cnt); // 1.125ms 宽度为逻辑1 bit_cnt; } else if (timer_cnt 500 timer_cnt 900) { ir_data ~(1 bit_cnt); // 0.5625ms 宽度为逻辑0 bit_cnt; } }这段代码的核心逻辑是用时间宽度区分引导码、逻辑1和逻辑0。NEC协议里逻辑1的总时间是1.125ms逻辑0是0.5625ms所以阈值在800us附近分界即可。2100us的定时器计数值换算关系取决于定时器时钟分频蓝桥杯板载晶振通常是11.0592MHz定时器1的计数频率经过12分频后约920kHz所以逻辑0大约对应520个计数逻辑1对应1040个计数上面的阈值判断就是基于这个换算。解码完成后查表映射0x45对应按键10x46对应按键2等等。4. 四合一实战避坑数据冲突、长延时与显示毛刺的排查记录4.1 超声波测距卡死Echo等待无超时保护现象没有障碍物时按下超声波测距按键程序卡在测量函数里数码管不刷新按键无响应整块开发板像死机一样。原因HC-SR04在无回波时Echo脚保持低电平while (!Echo)循环永远等不到高电平。蓝桥杯板载超声波模块虽然没有外置独立模块那么极端但在测量距离超过量程上限时同样会出现回波丢失。解决给回波等待加超时限制最省事的办法是在等待循环里查询一个软件标志位这个标志位由定时器中断周期性置位。uint8_t timeout_flag 0; // 定时器0中断里每1ms置位一次 void Timer0_ISR(void) interrupt 1 { timeout_flag 1; } // 回波等待带超时 while (!Echo) { if (timeout_flag) { timeout_flag 0; time_ms; if (time_ms 50) { distance_mm 999; // 超时置无效值 TR0 0; return; } } }从那以后我每次写超声波测距都强制走一遍超时保护的逻辑排队等回波的死循环必须有一个跳出条件这个习惯在后续做双超声避障小车时救了我两次。4.2 温度读取偶尔跳变复位时序的延时边界现象温度显示在正常读数和小数部分乱跳之间切换有时候跳变值固定在-0.5度有时候完全是随机数。原因DS18B20的读时隙对时序非常敏感读操作必须在拉低总线后的15us内完成采样如果读时隙的延时过长读到的是下一个时隙的数据或者总线释放后的高电平产生随机值。资源代码里的延时函数是基于空循环计算的编译器优化等级不同实际延时时间会偏差10%-20%。解决把延时代码里的空循环改成_nop_()配合变量递减计算时按最坏情况留足余量。DS18B20的读时隙时间窗口是从拉低总线开始算15us后器件释放总线主机必须在1us到15us之间完成采样所以采样点放在拉低后的第8us左右最安全。4.3 光敏D/A显示数值不稳定ADC采样抖动现象环境光线不变光敏通道的ADC值在上下几十个数之间跳动D/A输出电压也跟着波动数码管的末位不停变化。原因光敏电阻的阻值本身会有微小漂移加上ADC转换的参考电压如果来自VCC而数码管扫描恰好在这一瞬间造成电源跌落采集值就会被污染。解决做多次采样取平均是常规做法但注意不要在中断里做连续采样因为每次ADC启动到转换完成需要十几个机器周期连续读取会占用过长的中断时间。我一般是在主循环里连采8次去掉最大最小再平均效果比单纯平均好因为这能滤掉单次偶发干扰。4.4 数码管显示毛刺段选和位选切换过近现象四路数据显示时数码管偶发出现短暂的重影和亮度不均特别是温湿度同时刷新时最明显。原因数码管动态扫描的原理是人眼余晖效应每一位点亮时间通常在1ms到2ms之间。如果段选切换后没有留出足够的IO稳定时间就切换位选前一位的残影会漏到后一位上造成重影。解决位选切换前先关闭所有位选再设置段码最后重新打开目标位选。这套资源的扫描代码里没有做消隐需要自己加一个P0 0x00的关位选动作。这个坑在蓝桥杯板子上几乎必现只是严重程度不同赛场上如果数码管亮度不均会被扣印象分。4.5 红外按键重复触发按一次跳两个数字现象按下红外遥控器按键数码管显示的通道号或者数值有时候跳两次看起来像是按键连击。原因NEC协议里短按键和一帧数据对应但长按会每110ms重发一次命令帧。如果接收端没有等待整帧结束就处理数据重复帧会被当成新按键。解决在红外解码完成标志置位后增加一个延时等待下一帧引导码在这段时间内忽略所有输入。具体做法是收到完整32位数据帧后设置一个软件标志禁止解码500ms用定时器软件计时到时间后重新开放接收。这个思路和按键消抖一样只是时间尺度从毫秒变成几百毫秒。5. 进阶实战把四路数据整合进同一定时器中断与状态机调度5.1 改造主循环扫描为中断时隙调度资源的原始代码是主循环轮询架构四个模块的采集函数串行执行某个模块阻塞时其他模块实时性变差。进阶改造的第一步是把采集动作拆成两个阶段非阻塞的“启动采集”和“查询结果”。以温度为例主循环里发温度转换命令后立刻返回定时器中断里每500ms查询一次转换完成标志再把结果搬到显示缓冲区。// 定时器中断里的调度时隙 void Timer0_ISR(void) interrupt 1 { static uint8_t slot 0; slot; if (slot 4) slot 0; switch (slot) { case 0: Temp_Poll(); break; // 查询温度转换结果 case 1: Ultra_Start(); break; // 发起一次超声测量 case 2: Light_ADC_Sample(); break; // 触发一次光敏采样 case 3: IR_Check(); break; // 检查红外解码缓冲区 } Display_Refresh(); // 每时隙刷新一次显示 }这样改造后每个时隙只执行少量代码单次中断时间可以控制在50us以内即使四个模块全部开启主循环仍然可以流畅响应按键。代价是代码结构从顺序执行变成状态轮询每个模块都要增加一个“忙标志”来避免重复触发测量。5.2 显示缓冲区的统一管理四路数据最终都要在数码管或液晶上显示如果每个模块直接写显示口后期加切换功能就很痛苦。推荐的做法是设置一个结构体作为显示缓冲区各模块只更新缓冲区内容显示任务统一从缓冲区取数。typedef struct { uint8_t mode; // 当前显示模式 int16_t temp_x10; // 温度*10保留一位小数 uint16_t dist_mm; // 距离单位mm uint8_t adc_light; // 光敏ADC值 uint8_t ir_code; // 红外键值 } Disp_Info_t; volatile Disp_Info_t disp_info;所有采到的数据先写入这个结构体显示任务读取时统一处理符号位和小数点位置。这样还有个额外好处如果之后要加串口打印调试只需要把结构体里的数据格式化输出不用再翻各个模块的代码。5.3 按键状态机替代轮询按键扫描蓝桥杯最常见的按键写法是死循环扫描加延时消抖这种写法在单模块程序里够用但四模块整合后容易引发显示闪烁。进阶做法是用定时器中断维护一个20ms周期的按键状态机主循环只检测按键事件标志。// 按键状态机仅在定时器中断中调用 void Key_Scan_SM(void) { uint8_t curr P3 0x0F; static uint8_t last 0xFF; if (curr ! last) { if ((curr 0x01) 0) key_event | KEY1_PRESS; if ((curr 0x02) 0) key_event | KEY2_PRESS; last curr; } }这个写法省掉了显式的delay消抖因为状态机的采样周期天然起到了消抖作用。实测在20ms采样周期下抖动毛刺一般不会超过10ms所以状态切换一次后下一次采样已经是稳定值。关于按键状态机的采样周期我习惯是15-20ms太短消抖不彻底太长按键响应肉眼看有延迟。最后一个实用的技巧是“数据有效性标记”。和直接显示数值不同我会给每个传感器增加一个“数据新鲜度”计数器。比如超声波测量失败时标记为无效主循环只在数据有效时刷新显示无效时显示“---”或保留上次值。这个小习惯让整套系统的鲁棒性提升了一个档次在模拟的环境准备环节多次派上用场。做法很简单每个模块更新数据时顺手重置计数器主循环里定时把这个计数器加一超过阈值就认为对应的传感器掉线。这套四合一资源如果只看代码结构其实和真实的工业数据采集终端已经非常接近把它吃透后再应对“多路采集显示切换”这个蓝桥杯高频题型的变形基本不用再慌乱去找资料。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询