蓝桥杯单片机组学习笔记(一)

蓝桥杯单片机入门培训笔记 (一)

第一章:工程搭建 - 给程序一个“家”

核心思想:写程序就像盖房子,需要先规划好地方、准备好图纸和工具。

1. 建立项目文件夹

  • 比喻:在电脑上新建一个文件夹,就像为你的“代码房子”买了一块地皮。以后这个项目的所有文件(图纸、材料)都放在这里,方便管理,不会乱。

  • 操作:在合适的位置(如桌面或D盘),新建一个文件夹,命名为 LED_Test

2. 使用Keil创建工程

  • 比喻:Keil是我们的“建筑软件”,用来编写和编译代码。创建工程就是在这个软件里为新房子立项。

  • 步骤

    1. 打开Keil uVision。

    2. Project → New μVision Project…

    3. 导航到你刚建立的 LED_Test 文件夹。

    4. 输入工程名(建议和文件夹同名,如 LED_Test),点击保存。

    5. 在弹出的芯片选择窗口中,找到并选择 AT89C52,点击OK。

    6. 关于“是否添加启动文件”的弹窗,选择“否”即可(对于基础学习够用了)。

3. 工程设置与文件创建

  • 步骤

    1. 点击工具栏的 “魔法棒” (Options for Target) 按钮。

    2. 切换到 “Output” 选项卡。

    3. 勾选 “Create HEX File”(关键!) 这个 HEX 文件就是能下载到单片机里运行的“可执行文件”。

    4. 点击 “品字形” (Manage Project Items) 按钮。

    5. Project Targets 下将 Target 1 改名为你的工程名(如 LED_Test)。

    6. Groups 下,将原有的 Source Group 1 重命名为 User(寓意用户编写的代码组)。点击OK。

    7. 在左侧 Project 窗口,右键点击 User 组,选择 “Add New Item to Group ‘User’…”

    8. 选择 C File (.c),命名为 main.c,点击 Add

4. 编写初始代码框架

  • 习惯:良好的代码结构是优秀程序员的标志。我们采用分区编写法。

  • 操作:在 main.c 文件中输入以下内容。/* ... */// 是注释,用于解释代码,不会被编译。

 /*=============================================
    * 头文件声明区域 (工具箱引入区)
    * 说明:这里用来包含程序需要的“工具箱”,比如单片机寄存器的定义。
 =============================================*/
 #include <REGX52.H> // 引入了AT89C52单片机的“操作说明书”
 ​
 /*=============================================
    * 主函数区域 (程序心脏区)
    * 说明:单片机通电后,就从这里开始执行代码。
 =============================================*/
 void main()  // 主函数,程序入口
 {
     while(1) // 一个无限循环,让单片机永不停止地工作
     {
         // 你的功能代码将写在这里
     }
 }

第二章:点亮LED - 与硬件第一次对话

核心原理:单片机通过控制其引脚(Pin)输出高电平(1,通常是5V)或低电平(0,通常是0V)来控制外设。根据开发板原理图,LED通常一端接电源(VCC),另一端通过电阻连接到单片机引脚。当引脚输出低电平(0)时,形成电压差,LED点亮;输出高电平(1)时,没有电压差,LED熄灭。

  • 比喻:单片机的引脚就像一排开关。输出“0”是按下开关(灯亮),输出“1”是抬起开关(灯灭)。

方法一:整体赋值法(操控整个端口)

  • 思路:P1端口有8个引脚(P1.0 ~ P1.7),对应8个LED(通常是D1 ~ D8)。我们可以一次性给整个P1端口赋值一个8位二进制数来控制所有LED。

  • 例如:想让 D2D6 亮,其余灭。

    • 从右向左看(P1.0 → P1.7):P1.7 P1.6 P1.5 P1.4 P1.3 P1.2 P1.1 P1.0

    • 灯亮为0,灯灭为1:1 0 1 1 1 0 1 1

    • 得到二进制:1011 1011

    • 转换为十六进制:0xBB (在计算器程序员模式下转换)

  • 代码实现:将以下代码放入 while(1) 循环中。

 /*
    * 实验:点亮指定LED (整体赋值法)
    * 效果:D2和D6亮,其余灭
 */
 P1 = 0xBB; // 一次性给P1口所有引脚赋值

方法二:位赋值法(操控单个引脚)

  • 思路:直接操作某个具体的引脚,语法为 P1_引脚号。更加直观灵活。

  • 代码实现:将以下代码放入 while(1) 循环中。

 /*
    * 实验:点亮指定LED (位赋值法)
    * 效果:第一个灯(D1,对应P1.0)和最后一个灯(D8,对应P1.7)亮
 */
 P1_0 = 0; // 点亮连接到P1.0引脚的LED (D1)
 P1_7 = 0; // 点亮连接到P1.7引脚的LED (D8)
 // 注意:其它引脚状态未知,可能为亮也可能为灭。通常先全部关闭再控制特定灯是更好的习惯。

第三章:LED闪烁 - 让程序学会“等待”

原理:让灯亮一会,然后灭一会,循环往复,就形成了闪烁。

关键问题:单片机执行一条指令的速度极快(微秒甚至纳秒级别),如果直接 亮 -> 灭 -> 亮 -> 灭,人眼根本无法分辨,看到的将是灯持续亮着但稍微变暗。所以我们需要一个延时函数,让程序在执行 之间“休息”一会儿。

  • 比喻:延时函数就像一个“定时器”或“让CPU打盹的指令”,告诉单片机:“现在什么也别做,数够一定的时间再继续”。

1. 获取延时函数代码

我们使用STC官方工具 STC-ISP 来生成精准的延时代码,这是一个非常实用的“外挂”。

  1. 打开 STC-ISP 软件。

  2. 找到 “软件延时计算器” 选项卡。

  3. 系统频率 设置为 12MHz (蓝桥杯板载晶振通常是这个频率)。

  4. 定时长度 设置为 100 毫秒。

  5. 选择STC-Y1 型号。

  6. 点击 “生成C代码”

  7. 复制生成的代码。

2. 将延时函数加入工程

将复制的代码粘贴到你的 main.c 文件中,放在头文件声明之后,主函数之前。

 /*=============================================
    * 头文件声明区域
 =============================================*/
 #include <REGX52.H>
 ​
 /*=============================================
    * 延时函数区域 (系统休眠区)
 =============================================*/
 void Delay100ms(void)   //@12.000MHz
 {
     unsigned char data i, j; // 声明两个变量用于循环计数
     i = 195;
     j = 138;
     do
     {
         while (--j); // 内层循环,消耗时间
     } while (--i);   // 外层循环
 }
 ​
 /*=============================================
    * 主函数区域
 =============================================*/
 void main()
 {
     while(1)
     {
         P1_0 = 0;          // 步骤1:点亮D1 (给低电平)
         Delay100ms();      // 步骤2:等待100毫秒 (注意!调用时不需要写 `void`)
         P1_0 = 1;          // 步骤3:熄灭D1 (给高电平)
         Delay100ms();      // 步骤4:再等待100毫秒
         // 然后循环回到步骤1,实现闪烁
     }
 }

3. 进阶:可调参数的延时函数

每次都生成不同时长的延时函数很麻烦。我们可以改造它,让它通过参数来控制延时长短。

方法:先利用 STC-ISP 生成一个 1毫秒 的基础延时函数,然后通过循环调用它 xms 次来实现任意毫秒的延时。

 /*=============================================
  * 可调参数延时函数
  * 函数名:Delay1ms
  * 参数:xms - 希望延时的毫秒数
  * 示例:Delay1ms(500); // 延时500毫秒
 =============================================*/
 void Delay1ms(unsigned int xms)     //@12.000MHz
 {
     unsigned char i, j;
     while(xms--) // 循环xms次,每次循环大约耗时1毫秒
     {
         // 以下是STC-ISP生成的1ms延时核心代码
         i = 12;
         j = 169;
         do
         {
             while (--j);
         } while (--i);
     }
 }

使用示例:实现一个频率更快的闪烁。

 void main()
 {
     while(1)
     {
         P1_0 = 0;
         Delay1ms(50); // 只延时50ms
         P1_0 = 1;
         Delay1ms(50); // 只延时50ms
     }
 }

温馨提示:如果生成的代码中有 _nop_(); 语句,而你的代码没有包含 #include <INTRINS.H>,编译器会报错。简单处理方法是直接删除这行 _nop_(); 语句,通常不影响基础延时功能。


第四章:流水灯 - 让灯光“流动”起来

思路:让亮灯的状态像水流一样在8个LED之间依次移动。例如:1000 00000100 00000010 0000 → …

实现妙招:使用C51标准库里的循环移位函数,它能让我们的代码极其简洁。

1. 引入“工具库”

循环移位函数 _crol_ (循环左移) 和 _cror_ (循环右移) 定义在 intrins.h 头文件中,所以需要先引入它。

2. 代码实现(循环左移流水灯)

 /*=============================================
    * 头文件声明区域
 =============================================*/
 #include <REGX52.H>
 #include <INTRINS.H> // 引入循环移位函数库
 ​
 /*=============================================
    * 延时函数区域 (使用可调参数版)
 =============================================*/
 void Delay1ms(unsigned int xms)     //@12.000MHz
 {
     unsigned char i, j;
     while(xms--)
     {
         i = 12;
         j = 169;
         do
         {
             while (--j);
         } while (--i);
     }
 }
 ​
 /*=============================================
    * 变量声明区域
 =============================================*/
 unsigned char ucLed = 0xFE; // 初始值 1111 1110, 表示只有D1(P1.0)亮
 ​
 /*=============================================
    * 主函数区域
 =============================================*/
 void main()
 {
     while(1)
     {
         P1 = ucLed;               // 将当前灯的状态输出到P1口
         Delay1ms(200);            // 保持当前状态200ms,让人眼能看到
         ucLed = _crol_(ucLed, 1); // 关键!将ucLed的8位二进制数循环左移1位
         // 例如:1111 1110 -> 1111 1101 (D1灭,D2亮)
     }
 }

效果:你会看到亮灯从D1流向D2,再流向D3……到D8后,又从D1开始,循环往复。

尝试修改:将 _crol_ 改为 _cror_,观察灯光流动方向的变化。


恭喜你! 至此,你已经掌握了单片机最基础的输入输出控制、函数使用和库函数调用,成功迈入了单片机编程的大门。接下来的学习就是对更多硬件模块(如数码管、键盘、蜂鸣器、定时器)的探索,但核心思想都是相通的:查看原理图 → 控制对应引脚 → 考虑时序延时

1 个赞