当前位置: 首页 > news >正文

【蓝桥杯单片机】第二讲 过渡模拟二与LED模块

一、过渡模拟二

1 小数点输入

case11://小数点输入if(Seg_Disp_Mode==0&&Point_Flag==0&&Seg_Input[0]!=11)//处于温度采集界面、标志位为0、输入数组第一位有数据{Seg_Point[2+Seg_Input_Index]=1;Point_Wela=Seg_Input_Index;//记录此时指针状态 便于后期数据处理Point_Flag=1;//拉高标志位 保证一次输入周期小数点只能使能一次}break;
Temperature=Seg_Input[0]*100+Seg_Input[1]*10+Seg_Input[2]+5;while(3-Point_Wela){Temperature/=10.0;Point_Wela++;}

2 长按短按不同功能

数码管的减速时间需要改成10ms(与按键一致),否则会出现间隔加不连贯的情况(按键在加,但数码管没刷新)

3 亮度等级

核心原理:欺骗眼睛的“视觉暂留”

  • 如果不控制:LED 只有“全亮”和“全灭”两种状态。
  • PWM大法:让 LED 快速地一会儿开、一会儿关。
    • 周期长(慢):人眼能看清,就是闪烁
    • 周期短(快):人眼看不清开关过程,觉得它一直在亮,只是亮度变暗了

关键术语:

  • 周期 (Period):比如把时间切成一小块一小块,每一块总长度是固定的(代码里是 10)。
  • 占空比 (Duty Cycle):在这一小块时间里,灯“亮”了多久。
    • 亮的时间越长 = 占空比越高 = 灯越
    • 亮的时间越短 = 占空比越低 = 灯越

把代码想象成一个切蛋糕的过程。我们要把一块蛋糕切成10 份(这就是周期)。

// 设定:我想让灯有 30% 的亮度 (Led_Pwm = 3)// 设定:蛋糕总共切 10 份 (上限是 10)unsignedcharLed_Num;// 现在的刀切到第几份了 (0~9)unsignedcharLed_Pwm;// 决定前几份蛋糕给灯吃 (亮度值)// 每执行一次,切下一份if(++Led_Num==10)Led_Num=0;// 切完10份,这就叫一个“周期”结束,重头开始// 核心判断:只要还没切到第 Led_Pwm 份,灯就亮着if(Led_Num<Led_Pwm)Led_Disp(Led_Pos,ucLed[Led_Pos]);// 【开灯】前 3 份时间亮elseLed_Disp(Led_Pos,0);// 【关灯】后 7 份时间灭

附:写程序过程中遇到的问题
  1. 数码管没有闪烁,只是先亮,然后一直灭

    错误代码及原因:

    if(Seg_Input[2]==11)Seg_Input[Seg_Input_Index]=Seg_Flag?Seg_Input[Seg_Input_Index]:10;

    这里不是对Seg_Input操作,是对Seg_Buf操作


代码实现

略有点小瑕疵,比如亮度等级(由于8ms才扫描一个灯,导致PWM不太对),但总体已实现功能

/* 头文件声明区 */#include<REGX52.H>//单片机寄存器专用头文件#include<Key.h>//按键底层驱动专用头文件#include<Seg.h>//数码管底层驱动专用头文件#include<Led.h>//Led底层驱动专用头文件/* 变量声明区 */unsignedcharKey_Val,Key_Down,Key_Old,Key_Up;//按键专用变量unsignedcharKey_Slow_Down;//按键减速专用变量unsignedcharSeg_Buf[6]={10,10,10,10,10,10};//数码管显示数据存放数组unsignedcharSeg_Pos;//数码管扫描专用变量unsignedcharSeg_Point[6]={0,0,0,0,0,0};//数码管小数点存放数组unsignedintSeg_Slow_Down;//数码管减速专用变量unsignedcharLed_Pos;//Led扫描专用变量unsignedcharLed[8]={0,0,0,0,0,0,0,0};//Led显示数据存放数组unsignedcharSeg_Disp_Mode;//0-温度采集界面 1-数据显示界面 2-参数设置界面unsignedcharSeg_Input[3]={11,11,11};unsignedcharSeg_Input_Index;unsignedintTimer_500ms;unsignedcharPoint_Wela;unsignedcharParameter[2]={30,20};unsignedcharParameter_Ctrol[2]={30,20};unsignedintTimer_Count;unsignedcharLed_Num;unsignedcharLed_PWM;floatTemperature;bit Seg_Flag;bit Point_Flag;bit Parameter_Index;bit Time_Flag;bit Error_Flag;/* 键盘处理函数 */voidKey_Proc(){unsignedchari;if(Key_Slow_Down)return;Key_Slow_Down=1;//键盘减速程序Key_Val=Key_Read();//实时读取键码值Key_Down=Key_Val&(Key_Old^Key_Val);//捕捉按键下降沿Key_Up=~Key_Val&(Key_Old^Key_Val);//捕捉按键上升沿Key_Old=Key_Val;//辅助扫描变量if(Key_Down>=1&&Key_Down<=10){if(Seg_Disp_Mode==0){Seg_Input[Seg_Input_Index]=Key_Down-1;Seg_Input_Index++;}}if(Seg_Disp_Mode==2){if(Key_Down==14)Time_Flag=1;}if(Timer_Count<500){if(Key_Up==14){Time_Flag=0;Timer_Count=0;if(++Parameter_Ctrol[Parameter_Index]>70)Parameter_Ctrol[Parameter_Index]=10;}}else{if(Key_Old==14){if(++Parameter_Ctrol[Parameter_Index]>70)Parameter_Ctrol[Parameter_Index]=10;if(Key_Up==14){Time_Flag=0;Timer_Count=0;}}}if(Seg_Disp_Mode==2){if(Key_Down==15)Time_Flag=1;}if(Timer_Count<500){if(Key_Up==15){Time_Flag=0;Timer_Count=0;if(--Parameter_Ctrol[Parameter_Index]<10)Parameter_Ctrol[Parameter_Index]=70;}}else{if(Key_Old==15){if(--Parameter_Ctrol[Parameter_Index]<10)Parameter_Ctrol[Parameter_Index]=70;if(Key_Up==15){Time_Flag=0;Timer_Count=0;}}}switch(Key_Down){case11:if(Seg_Disp_Mode==0&&Point_Flag==0&&Seg_Input[0]!=11){Seg_Point[2+Seg_Input_Index]=1;Point_Wela=Seg_Input_Index;Point_Flag=1;}break;case12:if(Seg_Disp_Mode!=0){Seg_Disp_Mode++;if(Seg_Disp_Mode>2)Seg_Disp_Mode=1;if(Seg_Disp_Mode==2){Parameter_Index=0;Parameter_Ctrol[0]=Parameter[0];Parameter_Ctrol[1]=Parameter[1];}else{if(Parameter_Ctrol[0]>=Parameter_Ctrol[1]){Error_Flag=1;Parameter[0]=Parameter_Ctrol[0];Parameter[1]=Parameter_Ctrol[1];}elseError_Flag=0;}}break;case13:if(Seg_Disp_Mode==2){Parameter_Index^=1;}break;case16:if(Seg_Disp_Mode==0){if(Point_Flag==0||Seg_Input_Index<3||Seg_Input[0]==0){Seg_Input_Index=0;for(i=0;i<3;i++)Seg_Input[i]=11;Point_Flag=0;Seg_Point[3]=Seg_Point[4]=0;}else{Temperature=Seg_Input[0]*100+Seg_Input[1]*10+Seg_Input[2]+5;if(Point_Wela==1)Temperature/=100.0;elseTemperature/=10.0;if((unsignedchar)Temperature<=85){Seg_Input_Index=0;Point_Flag=0;Seg_Point[3]=Seg_Point[4]=0;Seg_Disp_Mode=1;}else{Seg_Input_Index=0;for(i=0;i<3;i++)Seg_Input[i]=11;Point_Flag=0;Seg_Point[3]=Seg_Point[4]=0;}}}else{Seg_Input_Index=0;for(i=0;i<3;i++)Seg_Input[i]=11;Seg_Point[3]=Seg_Point[4]=0;Point_Flag=0;Seg_Disp_Mode=0;}}}/* 信息处理函数 */voidSeg_Proc(){unsignedchari;if(Seg_Slow_Down)return;Seg_Slow_Down=1;//数码管减速程序switch(Seg_Disp_Mode){case0:Seg_Buf[0]=12;Seg_Buf[1]=Seg_Buf[2]=10;for(i=0;i<3;i++)Seg_Buf[3+i]=Seg_Input[i];if(Seg_Input[2]==11)Seg_Buf[3+Seg_Input_Index]=Seg_Flag?Seg_Input[Seg_Input_Index]:10;break;case1:Seg_Buf[0]=13;Seg_Buf[1]=Seg_Buf[2]=Seg_Buf[3]=10;Seg_Buf[4]=(unsignedchar)Temperature/10%10;Seg_Buf[5]=(unsignedchar)Temperature%10;break;case2:Seg_Buf[0]=14;Seg_Buf[1]=10;Seg_Buf[2]=Parameter_Ctrol[0]/10;Seg_Buf[3]=Parameter_Ctrol[0]%10;Seg_Buf[4]=Parameter_Ctrol[1]/10;Seg_Buf[5]=Parameter_Ctrol[1]%10;if(!Parameter_Index){Seg_Buf[2]=Seg_Flag?Parameter_Ctrol[0]/10:10;Seg_Buf[3]=Seg_Flag?Parameter_Ctrol[0]%10:10;}else{Seg_Buf[4]=Seg_Flag?Parameter_Ctrol[1]/10:10;Seg_Buf[5]=Seg_Flag?Parameter_Ctrol[1]%10:10;}break;}}/* 其他显示函数 */voidLed_Proc(){if(Temperature>Parameter[0])Led_PWM=3;elseif(Temperature<Parameter[0]&&Temperature>Parameter[1])Led_PWM=6;elseLed_PWM=9;Led[0]=(int)Temperature/Parameter[0];Led[1]=(!((int)Temperature/Parameter[0]))&((int)Temperature/Parameter[1]);Led[2]=!((int)Temperature/Parameter[1]);Led[3]=Error_Flag;}/* 定时器0中断初始化函数 */voidTimer0Init(void)//1毫秒@12.000MHz{TMOD&=0xF0;//设置定时器模式TMOD|=0x01;//设置定时器模式TL0=0x18;//设置定时初始值TH0=0xFC;//设置定时初始值TF0=0;//清除TF0标志TR0=1;//定时器0开始计时ET0=1;//定时器0中断打开EA=1;//总中断打开}/* 定时器0中断服务函数 */voidTimer0Server()interrupt1{TL0=0x18;//设置定时初始值TH0=0xFC;//设置定时初始值if(++Key_Slow_Down==10)Key_Slow_Down=0;//键盘减速专用if(++Seg_Slow_Down==10)Seg_Slow_Down=0;//数码管减速专用if(++Seg_Pos==6)Seg_Pos=0;//数码管显示专用if(++Led_Pos==8)Led_Pos=0;//Led显示专用Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);if(++Timer_500ms>=500){Timer_500ms=0;Seg_Flag^=1;}if(Time_Flag){if(++Timer_Count==600)Timer_Count=600;}if(++Led_Num==10)Led_Num=0;if(Led_Num<Led_PWM)Led_Disp(Led_Pos,Led[Led_Pos]);elseLed_Disp(Led_Pos,0);}/* Main */voidmain(){Timer0Init();while(1){Key_Proc();Seg_Proc();Led_Proc();}}

二、蓝桥杯LED模块

1 添加芯片头文件

  • 打开烧录工具STC-ISP

  • 进入"Keil仿真设置"

  • 选择单片机型号:IAP15F2K61S2

  • 点击添加型号和头文件到Keil中

  • 选择Keil的安装路径

2 锁存器

锁存器的作用是保存数据,防止数据在总线上变化时影响输出。

2.1 RS锁存器

Reset(复位) 和 Set(置位)。

  • R=1(使能), S=0 -> Q=0(复位)
  • R=0, S=1(使能) -> Q=1(置位)
  • R=0, S=0 -> 保持不变

真值表

RSQ
00Q
011
100
11X
2.2 D锁存器

EN=0时,输出状态保持不变

EN=1时,输出随输入状态而改变(当D=0,Q=0;当D=1,Q=1)

蓝桥杯板载芯片74HC573

八个锁存器都是D型锁存器

LE (Latch Enable):使能端,高电平有效。

LE = 1 (高电平):Q 跟随 D 变化 (直通模式)。

LE = 0 (低电平):Q 锁存住之前的数据,D 再变化 Q 也不变 (锁存模式)。

3 译码器

将 3 个输入引脚 (A, B, C) 转换成 8 个输出引脚 (Y0-Y7) 的低电平信号,用于选中不同的锁存器(片选)。

  • 连接方式:P25 -> A, P26 -> B, P27 -> C (高位对应高位)。
  • 输出特性:低电平有效。被选中的通道输出 0,其余输出 1。
蓝桥杯板载芯片74HC138

连接到P2口,P2高位在前(前三位:27、26、25)

真值表(基于8421BCD码)

输入 C (P27)输入 B (P26)输入 A (P25)选通输出 (低电平有效)功能映射 (蓝桥杯板)
000Y0未使用
001Y1未使用
010Y2未使用
011Y3未使用
100Y4LED控制
101Y5蜂鸣器/继电器
110Y6数码管位选
111Y7数码管段选

4 原理

先通过 P2 口的高 3 位控制译码器,选中对应的锁存器(LE置1),然后通过 P0 口写入数据,最后关闭锁存器(LE置0)。

5 编写程序

5.1 新建工程

选择芯片时需要先选择STC MCU Database

再选择15F2K60S2 Series

其余步骤与之前相同

5.2 关闭外设

原因:上电时 P0 口状态不确定,蜂鸣器可能会叫,继电器可能会吸合,LED可能会乱亮。初始化时需统一关闭。

init.c
#include<init.h>voidSystem_Init(){P0=0xff;P2=P2&0x1f|0x80;//先屏蔽低五位,再改变高三位P2&=0x1f;P0=0x00;P2=P2&0x1f|0xa0;P2&=0x1f;}
注释版init.c
#include<init.h>voidSystem_Init(){// 关闭 LED (Y4)P0=0xFF;// LED是低电平点亮,写入FF为全灭P2=(P2&0x1F)|0x80;// 保留P2低5位,将高3位设为 100 (Y4选中)P2&=0x1F;// 将高3位清零,关闭锁存器(LE=0)// 关闭蜂鸣器和继电器 (Y5)P0=0x00;// 蜂鸣器和继电器是高电平触发,写入00为全关P2=(P2&0x1F)|0xA0;// 保留P2低5位,将高3位设为 101 (Y5选中)P2&=0x1F;// 关闭锁存器}
init.h
#include<STC15F2K60S2.H>voidSystem_Init();
5.3 Led底层

为了避免在操作 LED 时影响其他 P2 口的设备(虽然在这个板子上基本只用高3位,但养成好习惯很重要),或者避免反复开关锁存器造成干扰。

Led.c
#include<Led.h>voidLed_Disp(unsignedcharaddr,unsignedcharenable){staticunsignedchartemp=0x00;staticunsignedchartemp_old=0xff;if(enable)temp|=0x01<<addr;elsetemp&=~(0x01<<addr);if(temp!=temp_old){P0=~temp;P2=P2&0x1f|0x80;P2&=0x1f;temp_old=temp;}}

注意static局部变量只在第一次初始化,后续调用函数时,它会记住上一次的值,非常适合保存外设状态。

注释版Led.c
#include<Led.h>/* @brief 控制指定位置的LED亮灭 @param addr LED位置 (0-7) @param enable 1-亮, 0-灭 */voidLed_Disp(unsignedcharaddr,unsignedcharenable){staticunsignedchartemp=0x00;// 记录当前的LED状态 (1表示亮)staticunsignedchartemp_old=0xff;// 记录上一次写入P0的值// 更新状态变量if(enable)temp|=(0x01<<addr);// 对应位置1elsetemp&=~(0x01<<addr);// 对应位置0// 只有当状态发生改变时,才操作硬件if(temp!=temp_old){P0=~temp;// 取反,因为LED是低电平点亮 (temp中1表示想让它亮,P0需输出0)// 打开Y4锁存器P2=(P2&0x1F)|0x80;// 关闭Y4锁存器P2&=0x1F;temp_old=temp;// 更新旧值}}
Led.h
#include<STC15F2K60S2.H>voidLed_Disp(unsignedcharaddr,unsignedcharenable);
5.4 主程序
//头文件声明区#include<Init.h>#include<Led.h>//Mainvoidmain(){System_Init();while(1){Led_Disp(0,1);}}
http://www.cnnetsun.cn/news/24933.html

相关文章:

  • 【压力】矩阵-断裂-瓦格压力瞬态曲线模型和类型曲线【含Matlab源码 14685期】
  • Swagger UI高效调试实战:从入门到精通的全链路解决方案
  • 数字员工是什么?熊猫智汇在提升AI销售工具效率上的优势是什么?
  • 文献查询:高效获取与管理学术资源的实用指南
  • VLC播放器UOS ARM版离线部署指南
  • 税局正在调研“赛维模式”?广东多地卖家收到通知
  • OpenPose Editor完整教程:3步实现精准AI姿势控制
  • 学生成绩查询管理系统,AI智能评语与数据分析工具
  • WebAssembly反编译实战:从二进制迷雾到清晰代码的蜕变之旅
  • RankMixer:工业级推荐系统中排序模型的规模化扩展
  • 【SSM网上跳蚤市场】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
  • Qwen3-4B-FP8模型:5分钟轻松上手的AI开发新选择
  • Version-Fox终极插件管理指南:从零开始掌握多版本控制
  • Cloudpods终极指南:简单快速实现多云管理自动化
  • 极速AI助手快速接入腾讯混元大模型教程
  • 淘宝直播数据抓取终极指南:快速掌握实时监控技巧
  • Tomcat跨域配置终极指南:5分钟解决CORS问题
  • JAVA中如何操作文件
  • Tauri WebAssembly实战指南:5个技巧让应用性能飙升300%
  • Obsidian日历插件终极指南:5分钟打造高效笔记管理系统
  • 英伟达奖学金半数以上都是华人?
  • ManiSkill机器人仿真环境完全使用指南
  • GLM语言模型深度解析:从技术原理到实战应用的进阶指南
  • RookieAI_yolov8:2025年AI游戏辅助的完整解决方案与性能优化指南
  • 海量数据中找出最大的前100个(find_topk)最小堆算法
  • Claude Code Router多模型集成实战:打造智能开发工作流
  • 水稻病害检测(YOLO数据集,多分类,稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫)
  • ABB机器人省气装置在薄板焊接中的实际效果
  • 京东Java面试被问:ZGC的染色指针如何实现?内存屏障如何处理?
  • 硬件 - 高速协议设计整合