PICAXE BASIC嵌入式入门:从零搭建LED闪烁与交通灯项目

PICAXE BASIC嵌入式入门:从零搭建LED闪烁与交通灯项目

1. 为什么今天还有人用PICAXE BASIC?

如果你在2024年还在搜索“PICAXE BASIC”,可能会觉得有点穿越。毕竟,现在Arduino、树莓派Pico、MicroPython满天飞,一个基于BASIC语言的8位单片机平台,听起来像是上个世纪的古董。但恰恰是这种“过时”的特性,让PICAXE在特定的教育、入门和快速原型领域,依然有着不可替代的价值。我最早接触PICAXE是在十多年前,当时用它带学生做机器人项目,最大的感受就是:这东西真简单,简单到连小学生都能在半小时内让一个LED闪烁起来。

PICAXE的核心,是把一个预编程了BASIC解释器的微控制器(通常是Microchip PIC系列芯片)封装成一个易于使用的系统。你不需要购买昂贵的编程器,不需要理解复杂的寄存器配置,甚至不需要安装庞大的IDE。一根串口线(或USB转串口线)、一个免费的编辑器,就能开始你的嵌入式之旅。它的BASIC语言经过高度简化,去除了传统BASIC中繁琐的语法,保留了最核心的控制流、I/O操作和简单的数据处理命令。对于完全零基础的初学者来说,这种从“物理世界”(连接LED、传感器)到“逻辑世界”(写几行代码)的路径被极大地缩短了,挫败感也降到了最低。

当然,它的局限性也很明显:性能弱、内存小、缺乏现代的开发工具链和丰富的社区库。但对于“入门”和“教学”这两个核心场景,这些缺点反而成了优点。你不会被复杂的配置、库冲突、编译错误吓跑,可以专注于理解“输入-处理-输出”这一嵌入式系统最根本的逻辑。当你用PICAXE理解了什么是引脚、什么是循环、什么是条件判断之后,再转向Arduino或MicroPython,你会发现自己已经有了一个非常扎实的认知地基。所以,这篇教程不是教你用最先进的技术,而是带你掌握一种最有效的“入门思维工具”。我们从一个最简单的项目开始:让一个LED按照你的意愿闪烁。

2. 搭建你的第一个PICAXE工作环境

在写第一行代码之前,我们需要把硬件和软件环境准备好。这个过程本身,就是理解PICAXE哲学的第一步。

2.1 硬件选择与连接:从08M2开始

PICAXE有多个型号,如08M2、14M2、20M2等,数字代表引脚数量。对于初学者,我强烈推荐从PICAXE-08M2开始。它只有8个引脚,体积小巧,价格低廉,但功能齐全,足以完成绝大多数入门实验。

你需要准备以下硬件:

  1. 一片PICAXE-08M2芯片。这是核心。
  2. 一个实验板(面包板)和若干跳线。用于免焊接搭建电路。
  3. 一个USB转串口编程线。这是关键。PICAXE通过串口协议进行编程,你需要一根支持3.3V逻辑电平的USB转串口线(如基于FTDI或CP2102芯片的)。线上通常有黑(GND)、绿(RX)、白(TX)等颜色的线。
  4. 两个4.7kΩ的电阻和一个10kΩ的电阻。用于构建简单的串口下载电路。
  5. 一个LED和一个220Ω的限流电阻。用于我们的第一个实验。
  6. 一个3节AA电池盒(约4.5V)或一个3.3V-5V的直流稳压电源。PICAXE的工作电压是3.3V或5V。

连接电路是第一个实操环节,原理图很重要,但我会用文字描述让你在面包板上“搭”出来:

  • 电源:将电池盒的正极(+)连接到面包板的电源正极排孔(通常标红),负极(-)连接到电源负极排孔(通常标蓝)。
  • 芯片供电:PICAXE-08M2的引脚1(VDD)接电源正极,引脚8(VSS)接电源负极。务必注意,PICAXE芯片是静电敏感器件,在拿取和插到面包板上前,最好触摸一下接地的金属物体释放静电。
  • 串口下载电路:这是PICAXE编程的“钥匙孔”。你需要构建一个简单的分压电路,将电脑串口发出的信号(通常是5V或3.3V)安全地传递给PICAXE。
    • 将USB转串口线的TX(发送)线通过一个1kΩ或4.7kΩ的电阻连接到PICAXE的Serial In 引脚(对于08M2,这是引脚2,标记为SISerial In)。
    • 将PICAXE的Serial Out 引脚(对于08M2,这是引脚5,标记为SOSerial Out)直接连接到USB转串口线的RX(接收)线
    • 在PICAXE的Serial In引脚(引脚2)和地(GND)之间,连接一个10kΩ的下拉电阻。这个电阻确保在无信号时,该引脚处于确定的低电平状态,防止误触发。
    • 最后,将USB转串口线的GND(地)线与面包板上的电源地连接起来,确保共地。

注意:很多新手在这里出错。请仔细核对:电脑的数据从TX发出,经过限流电阻进入PICAXE的SI;PICAXE的数据从SO发出,直接进入电脑的RX。地线必须连通。这个电路是编程通信的物理基础,搭错了代码就下载不进去。

  • 输出电路(LED):我们将控制PICAXE的引脚4(标记为C.1Leg 4)。将引脚4通过一个220Ω的电阻连接到LED的正极(长脚),LED的负极(短脚)连接到地(GND)。这个电阻必不可少,用于限制流过LED的电流,防止烧毁LED或损坏PICAXE的引脚。

2.2 软件安装与配置:PICAXE Editor 6

硬件连接好后,我们需要在电脑上安装编程软件。前往PICAXE官网,下载PICAXE Editor 6。这是一个非常轻量级的集成开发环境,只有几十MB,安装过程简单。

安装完成后,打开PICAXE Editor 6,需要进行关键配置:

  1. 选择芯片型号:在软件界面顶部,找到“Chip”下拉菜单,选择“PICAXE-08M2”。这一步至关重要,它决定了编译器使用的指令集和内存映射。
  2. 配置串口:将你的USB转串口线插入电脑。在软件顶部找到“Options” -> “Serial Port”,选择你的串口线对应的COM端口(在Windows设备管理器的“端口”中可查看,如COM3、COM4)。波特率通常保持默认的“Auto”(自动)即可,软件会自动与芯片协商。
  3. 语法高亮与自动完成:在“View”菜单中,确保“Syntax Highlighting”和“Auto Complete”是开启状态。这能极大提升编码体验,减少拼写错误。

现在,点击工具栏上的“Program”(编程)按钮(或按F5),如果硬件连接和软件配置都正确,你应该会在软件底部的输出窗口看到类似“Hardware connected on COM4 at 4800 baud”和“Downloading...”的信息,最后显示“Download Successful”。如果出现“No hardware found”错误,请返回检查串口连接电路和COM端口选择。

3. PICAXE BASIC语法核心:像说话一样控制硬件

PICAXE BASIC的语法极其简单,我们通过第一个LED闪烁程序来逐行解析。在PICAXE Editor 6中新建一个文件,输入以下代码:

main: high C.1 pause 1000 low C.1 pause 1000 goto main

这6行代码就是一个完整的、循环闪烁LED的程序。我们来拆解每一部分的含义和背后的逻辑。

3.1 程序结构:没有main函数的main循环

PICAXE BASIC程序从第一行开始顺序执行。它没有像C语言那样的main()函数入口概念。main:这一行是一个标签。标签是一个标记程序位置的名字,后面跟着一个冒号。它本身不执行任何操作,只是为goto语句提供一个跳转目标。

程序的执行流是:

  1. high C.1开始执行。
  2. 执行完low C.1和第二个pause 1000后,遇到goto main
  3. goto命令使程序跳回到main:标签处,从high C.1开始再次执行,从而形成一个无限循环。

这种“标签+goto”的结构是早期BASIC的典型特征。对于简单的控制程序,它非常直观易懂。当然,你也可以使用do...loop循环结构,但在这个入门例子中,goto更能清晰地展示程序流的跳转。

3.2 引脚控制命令:high,low,input,output

high C.1low C.1是控制数字输出的核心命令。

  • high:将指定引脚设置为逻辑高电平(对于工作在5V的PICAXE,就是输出约5V电压)。
  • low:将指定引脚设置为逻辑低电平(输出0V电压)。
  • C.1是引脚的名称。在PICAXE-08M2上,物理引脚4被命名为C.1。这种命名方式源于芯片内部端口的分组(Port C)。你需要查阅具体芯片的数据手册来知道每个物理引脚对应的逻辑名称。对于08M2,常用的还有C.2(引脚5)、C.4(引脚3)等。

除了输出,引脚还可以作为输入。相关命令是:

  • input pin:将指定引脚设置为输入模式,用于读取外部信号(如按钮、传感器)。
  • output pin:将指定引脚设置为输出模式(这是默认模式,执行highlow时会自动切换为此模式)。

一个常见的误区是认为引脚模式是固定的。实际上,你可以在程序中动态切换同一个引脚的模式。例如,可以先output C.1驱动一个LED,然后input C.1来读取连接在该引脚上的一个开关状态。但要注意,切换模式后,之前输出的电平状态会丢失。

3.3 时间控制命令:pause与内部时钟

pause 1000命令让程序暂停执行。参数1000的单位是毫秒(千分之一秒)。所以pause 1000就是暂停1秒。

这里涉及一个关键概念:时间精度。PICAXE的pause命令依赖于其内部振荡器产生的时钟。这个时钟的精度并不高,受温度和电压影响会有微小漂移。对于要求精确计时的应用(如产生特定频率的PWM信号),这不是最佳选择。但对于LED闪烁、简单的延时等待,它完全足够。pause的参数范围是 0 到 65535,即最长约65.5秒。如果需要更长的延时,可以用循环嵌套pause

3.4 程序流控制:goto,if...then, 以及初识do...loop

我们已经见到了goto,它是一种无条件跳转。虽然在现代编程中过度使用goto被认为会导致“面条代码”,但在这种简单的状态机或顶层循环中,它清晰直接。

更常用的是条件分支命令if...then。它的基本结构是:

if pinC.1 = 1 then high C.2 endif

这行代码的意思是:如果引脚C.1(设置为输入模式)读取到的电平为高(1),那么就执行then后面的语句(直到遇到endif),将C.2引脚输出高电平。if...then可以嵌套,也可以使用else来处理条件不成立的情况。

另一种强大的结构是循环,除了用goto模拟,更规范的是使用do...loop

do high C.1 pause 500 low C.1 pause 500 loop

这段代码会无限循环执行花括号内的所有语句。你还可以使用do while...loopdo until...loop来创建条件循环。对于初学者,我建议先从do...loopif...then开始,它们能帮你构建更结构化的程序思维。

4. 从下载到调试:完成你的第一次“烧录”

代码写好了,电路连好了,现在到了最激动人心的环节:把程序“灌入”芯片,并观察结果。

4.1 下载流程与常见错误排查

在PICAXE Editor 6中,确保代码编辑区是当前活动窗口,然后点击工具栏的“Program”按钮(或按F5)。软件会依次执行以下步骤:

  1. 语法检查:编译器会先检查你的BASIC代码是否有语法错误,如拼写错误的命令、未定义的标签等。任何错误都会在底部“Syntax”窗口显示具体行号和错误信息。
  2. 编译:将人类可读的BASIC代码翻译成PICAXE芯片能执行的机器码。这个过程很快,如果语法正确,“Syntax”窗口会显示“Compilation successful”以及程序占用了多少字节的内存(对于08M2,通常有256字节的程序空间)。
  3. 连接硬件:软件通过你选择的COM端口,发送一个特定的握手信号到PICAXE芯片。如果电路连接正确且芯片完好,芯片会回应这个信号。
  4. 擦除与编程:软件先擦除芯片内旧程序,然后将新编译好的机器码通过串口线传输进去。
  5. 校验与运行:传输完成后,软件会读取芯片中的内容进行校验,确保下载无误。校验成功后,芯片会自动复位并开始运行新程序。

> 常见错误与排查:

  • “No hardware found”:这是最典型的错误。
    • 检查1:COM端口。确认在软件中选择的COM号与设备管理器里显示的一致。拔插USB线后,COM号可能会变。
    • 检查2:串口电路。务必确认TX->电阻->SI, SO->RX, GND共地,以及SI引脚的下拉电阻(10kΩ到地)是否接好。可以用万用表测量SI引脚在空闲时是否为低电平(接近0V)。
    • 检查3:电源。确保芯片供电正常(VDD和VSS之间有3-5V电压)。编程时,USB转串口线通常不提供电源,必须由外部电源(如电池盒)给PICAXE供电。
    • 检查4:芯片型号。确认软件中选择的芯片型号与实际使用的完全一致。
  • “Syntax Error”:根据错误信息修改代码。常见的如标签名后漏了冒号,命令拼写错误等。
  • 程序下载成功但LED不亮
    • 检查LED极性:长脚(正极)接电阻和PICAXE引脚,短脚(负极)接地。接反了LED不会亮。
    • 检查限流电阻:确认220Ω电阻已串联在电路中。没有电阻直接连接可能会损坏LED或芯片引脚。
    • 检查代码控制引脚:确认代码中high/low控制的引脚号(如C.1)与实际连接LED的物理引脚(08M2的引脚4)对应。

4.2 使用debug命令进行串口调试

让LED闪烁只是第一步。我们如何知道程序内部变量的值?如何确认某个条件判断是否被执行?PICAXE提供了一个极其有用的调试命令:debug

debug命令可以将芯片内部变量(字节型变量b0,b1, ...b27)的值,通过串口发送回电脑,在PICAXE Editor 6的“Debug”窗口中显示。这相当于给你的程序开了个“后门”,让你能实时观察运行状态。

让我们修改一下闪烁程序,加入一个计数器,并通过debug观察:

symbol counter = b0 ‘ 给变量b0起个别名叫counter,提高可读性 main: for counter = 1 to 5 debug counter ‘ 在调试窗口显示counter的当前值 high C.1 pause 200 low C.1 pause 200 next counter pause 2000 ‘ 等待2秒,方便观察 goto main

下载这个程序后,你需要点击PICAXE Editor 6工具栏上的“Debug”按钮(或按F8),打开调试窗口并启动调试模式。然后复位你的PICAXE电路(可以短暂断开电源再接通)。你会在“Debug”窗口中看到数字1到5依次输出,同时LED快速闪烁5次,然后停顿2秒,再重复。

使用debug的注意事项:

  1. debug语句会显著减慢程序执行速度,因为每次执行都要进行串口通信。所以不要在高速循环中频繁使用。
  2. 调试完成后,务必记得将debug语句删除或注释掉(在行首加英文单引号),否则会影响最终程序的运行效率。
  3. debug只能发送变量值,不能发送文本字符串。它的输出是纯数字。

通过debug,你可以验证循环次数、检查传感器读数、判断程序是否进入某个分支,是排查逻辑错误的神器。

5. 变量、数据类型与简单运算

虽然第一个程序没用到变量,但任何有用的程序都离不开数据。PICAXE BASIC中的变量处理非常简洁,但也有限制。

5.1 字节变量与位变量

PICAXE-08M2有28个字节变量,命名为b0,b1,b2, ...b27。每个变量可以存储一个0到255之间的无符号整数。这是最常用的变量类型。

此外,还有位变量(bit0bit31),它们实际上是字节变量中特定位的别名。例如,bit0bit7对应b0这个字节的8个位。操作位变量可以直接进行位逻辑运算,非常高效。

为了提高代码可读性,强烈建议使用symbol关键字为变量起别名:

symbol led_pin = C.1 symbol delay_time = b0 symbol button_state = bit0

这样,后面就可以使用led_pin,delay_time这样的名字,而不是冰冷的C.1,b0

5.2 赋值与算术运算

赋值使用等号=

b0 = 10 ‘ 将10存入变量b0 b1 = b0 + 5 ‘ 将b0的值加5后存入b1,此时b1=15 b2 = b1 * 2 ‘ 乘法,b2=30 b3 = b1 / 3 ‘ 除法,注意这是整数除法!b3=5(15除以3等于5,余数被丢弃) b4 = b1 % 3 ‘ 取模(求余数)运算,b4=0(15除以3余0)

> 重要提示:整数除法。PICAXE BASIC的除法/整数除法,结果会舍弃小数部分。例如b0 = 7 / 2,结果b0是3,不是3.5。如果需要更精确的计算,需要自己通过缩放因子来实现(例如,全部乘以10进行计算,最后再除以10)。

运算顺序遵循基本的数学规则,可以用括号()来改变优先级。

5.3 变量的作用域与生存期

对于初学者来说,一个好消息是:PICAXE BASIC中没有变量作用域的概念。所有变量(b0-b27,bit0-bit31)都是全局变量,在任何地方(主程序、子程序)都可以直接访问和修改。

这意味着你需要非常小心地管理这些变量。如果一个变量在多个地方被使用和修改,很容易产生意想不到的冲突。一个好的编程习惯是:

  1. 规划变量用途:在程序开头用symbol定义好所有变量的别名和用途注释。
  2. 避免滥用:尽量使用局部性强的变量。例如,一个只在某个循环中使用的临时计数器,就专门指定一个变量(如b25)给它,并注释清楚,避免在其他地方误用。
  3. 子程序参数传递:当通过gosub调用子程序时,如果需要传递参数,通常约定使用某几个特定变量(如b0,b1作为输入,b2作为输出),并在子程序开头用注释明确说明。

6. 深入if...then判断与逻辑运算

让程序做出决策是编程的核心乐趣。if...then判断结构配合比较和逻辑运算,能让你的PICAXE项目变得“智能”起来。

6.1 比较运算符

PICAXE BASIC支持标准的比较运算符,用于比较两个数值或变量:

  • =:等于
  • <>:不等于
  • <:小于
  • >:大于
  • <=:小于或等于
  • >=:大于或等于

这些比较的结果是一个“真”或“假”的逻辑值。在if语句中,如果比较结果为真,则执行then后的代码块。

示例:模拟一个简单的温控器

symbol temperature = b1 symbol heater_pin = C.2 main: ‘ 假设b0中存储了从温度传感器读取的值(已做缩放处理) temperature = b0 if temperature > 25 then high heater_pin ‘ 温度高于25度,关闭加热器(假设高电平关闭) else low heater_pin ‘ 温度低于等于25度,开启加热器 endif pause 10000 ‘ 每10秒检查一次 goto main

6.2 逻辑运算符:AND,OR,NOT

当判断条件更复杂时,需要逻辑运算符来组合多个比较。

  • AND:逻辑与。两边条件都为真,结果才为真。
  • OR:逻辑或。两边条件任意一个为真,结果就为真。
  • NOT:逻辑非。将真变假,假变真。

示例:一个安全门控系统

symbol door_sensor = pinC.3 ‘ 门磁传感器,关门时为1(高电平) symbol key_switch = pinC.4 ‘ 钥匙开关,插入钥匙为1 symbol alarm_pin = C.5 main: ‘ 如果门被打开(door_sensor为0)并且没有插入合法钥匙(key_switch为0),则触发报警 if door_sensor = 0 AND key_switch = 0 then high alarm_pin pause 5000 ‘ 报警响5秒 low alarm_pin endif pause 100 ‘ 短暂延迟,避免循环过快 goto main

在这个例子中,pinC.3pinC.4的用法是读取作为输入模式的引脚电平。door_sensor = 0表示门磁传感器断开(门被打开)。

6.3 嵌套判断与程序结构优化

if语句可以嵌套,但嵌套过深会降低代码可读性。对于多分支选择,PICAXE BASIC提供了select...case语句,它更清晰:

symbol mode = b2 select case mode case 0 ‘ 执行模式0的任务 high C.1 case 1 ‘ 执行模式1的任务 low C.1 high C.2 case 2 ‘ 执行模式2的任务 ‘ ... else ‘ 如果mode不是0,1,2中的任何一个,执行这里 low C.1 low C.2 endselect

select...case会根据变量mode的值,跳转到对应的case分支执行,然后直接跳到endselect之后继续执行。这比一连串的if...elseif...endif要简洁明了得多。

7. 构建可复用的代码块:子程序gosub

当一段代码(比如控制一个特定图案的LED闪烁序列)需要在程序的不同地方多次使用时,复制粘贴不是好办法。这会让代码冗长,且一旦需要修改,就得在所有粘贴的地方修改,极易出错。这时就该使用子程序

7.1 使用gosub调用与return返回

PICAXE BASIC使用gosub(Go to Subroutine)命令来调用子程序,使用return命令从子程序返回。

‘ 主程序 main: high C.1 gosub blink_led ‘ 调用名为blink_led的子程序 low C.1 gosub blink_led ‘ 再次调用同一个子程序 pause 2000 goto main ‘ 子程序定义 blink_led: high C.2 pause 250 low C.2 pause 250 high C.2 pause 250 low C.2 pause 250 return ‘ 子程序结束,返回到gosub的下一条语句

执行流程:当主程序执行到gosub blink_led时,它会跳转到标签blink_led:处,开始执行子程序里的代码。执行到return时,程序会跳回刚才gosub语句的下一条语句(第一次是low C.1,第二次是pause 2000)继续执行。

7.2 子程序中的变量管理与参数传递

如前所述,所有变量都是全局的。在子程序中修改变量,会直接影响主程序。因此,子程序与主程序之间可以通过这些全局变量来传递参数和结果。

一种常见的约定是:

  • 输入参数:使用b0,b1,b2等作为子程序的输入。
  • 输出结果:使用b10,b11等作为子程序的输出。
  • 临时变量:子程序内部使用的变量,尽量使用靠后的变量(如b20-b27),并在子程序开头用注释说明。

示例:一个计算平方的子程序

‘ 主程序 symbol input_num = b0 symbol result_square = b10 main: input_num = 12 gosub calculate_square ‘ 此时,result_square中应该存储了144 debug result_square ‘ 可以输出验证 pause 10000 goto main ‘ 子程序:计算平方 ‘ 输入:b0 - 要计算平方的数 ‘ 输出:b10 - 计算结果 ‘ 使用:b1作为临时变量 calculate_square: result_square = input_num * input_num return

虽然这个例子很简单,但它展示了模式。对于更复杂的子程序,清晰的注释至关重要。

7.3 避免子程序陷阱:递归与栈溢出

PICAXE的调用栈深度非常有限(通常只有几层)。这意味着你不能在子程序中调用自身(递归),也不能进行过深的嵌套调用(如A调用B,B又调用C,C又调用D...)。否则会导致栈溢出,程序行为不可预测甚至崩溃。

一个良好的实践是保持子程序调用层次扁平化。如果逻辑复杂,考虑使用状态机(select case基于一个状态变量)来替代深层次的子程序调用。子程序应该用于封装明确的、可复用的功能片段,而不是用来构建复杂的程序流程。

8. 实战:制作一个可交互的交通灯模型

现在,让我们综合运用以上所有知识,完成一个稍微复杂点的项目:一个简单的行人请求式交通灯模型。这个项目会用到LED、按钮、延时、状态判断和子程序。

8.1 项目需求与电路设计

需求

  1. 正常情况下,主干道绿灯亮,人行道红灯亮。
  2. 当行人按下请求按钮时,主干道绿灯开始闪烁(提示即将变灯),然后变黄灯,最后变红灯。
  3. 主干道红灯亮起后,人行道绿灯亮起,允许行人通过,并持续一段时间。
  4. 人行道绿灯结束前闪烁,然后变回红灯。
  5. 人行道红灯亮起后,主干道黄灯短暂亮起,然后恢复绿灯,回到状态1。

电路设计: 我们需要6个LED和1个按钮。

  • 主干道:使用3个LED(红、黄、绿)分别连接PICAXE的C.1(绿),C.2(黄),C.3(红),每个LED串联220Ω电阻到地。
  • 人行道:使用2个LED(红、绿)分别连接C.4(红),C.5(绿)。
  • 行人按钮:一个常开按钮,一端接电源正极(通过一个上拉电阻,如10kΩ),另一端接PICAXE的输入引脚C.0(配置为输入,并启用内部上拉电阻)。当按钮按下时,C.0读到低电平(0);未按下时,读到高电平(1)。这种“按下为低”的连接方式可以节省一个外部下拉电阻,并利用芯片内部上拉。

8.2 代码实现与状态机思维

直接使用一堆pauseif会使得代码混乱,难以维护。更好的方法是使用状态机。我们将交通灯的工作流程划分为几个明确的状态。

‘ 定义引脚别名 symbol MAIN_GREEN = C.1 symbol MAIN_YELLOW = C.2 symbol MAIN_RED = C.3 symbol PED_RED = C.4 symbol PED_GREEN = C.5 symbol BUTTON = pinC.0 ‘ 按钮输入 ‘ 定义状态变量 symbol state = b10 ‘ 状态说明: ‘ 0: 正常状态,主干道绿,人行道红 ‘ 1: 响应请求,主干道绿闪 ‘ 2: 主干道黄 ‘ 3: 主干道红,人行道绿 ‘ 4: 人行道绿闪 ‘ 5: 主干道黄(恢复前),人行道红 ‘ 定义计时变量 symbol flash_count = b11 symbol delay_counter = b12 ‘ 初始化所有灯 high PED_RED ‘ 人行道红灯亮 low PED_GREEN low MAIN_YELLOW low MAIN_RED high MAIN_GREEN ‘ 主干道绿灯亮 main: select case state case 0 ‘ 状态0:正常,检测按钮 if BUTTON = 0 then ‘ 按钮被按下(低电平) state = 1 ‘ 切换到状态1 flash_count = 0 ‘ 初始化闪烁计数器 endif pause 100 ‘ 检测间隔 case 1 ‘ 状态1:主干道绿灯闪烁 toggle MAIN_GREEN ‘ toggle命令翻转引脚电平,实现闪烁 pause 500 ‘ 闪烁周期1秒(亮500ms,灭500ms) inc flash_count ‘ 闪烁次数加1 if flash_count >= 3 then ‘ 闪烁3次后 low MAIN_GREEN ‘ 确保绿灯灭 state = 2 ‘ 进入状态2 endif case 2 ‘ 状态2:主干道黄灯亮 high MAIN_YELLOW pause 2000 ‘ 黄灯亮2秒 low MAIN_YELLOW state = 3 case 3 ‘ 状态3:主干道红,人行道绿 high MAIN_RED low PED_RED high PED_GREEN pause 5000 ‘ 人行道绿灯时间5秒 state = 4 flash_count = 0 case 4 ‘ 状态4:人行道绿灯闪烁 toggle PED_GREEN pause 500 inc flash_count if flash_count >= 3 then low PED_GREEN high PED_RED state = 5 endif case 5 ‘ 状态5:主干道黄灯(恢复),人行道红 high MAIN_YELLOW pause 2000 low MAIN_YELLOW low MAIN_RED high MAIN_GREEN state = 0 ‘ 回归正常状态 endselect goto main

8.3 代码解析与优化点

  1. 状态机state变量是整个程序的核心。程序永远在main循环中,通过select case根据当前state的值执行相应的代码块。每个状态块执行自己的任务,并在任务完成后更新state到下一个值。这种结构逻辑清晰,易于扩展和调试。
  2. toggle命令:这是一个非常方便的命令,用于翻转(Toggle)一个输出引脚的电平。如果引脚当前是高,执行toggle后变低;如果是低,则变高。用它来实现闪烁比用high/low加变量判断要简洁。
  3. 按钮防抖:上面的代码中,在状态0检测按钮时没有做防抖处理。在实际应用中,机械按钮在按下和释放的瞬间会产生快速的电平抖动,可能导致程序误认为按下了多次。一个简单的软件防抖方法是:检测到低电平后,延时10-50毫秒再次检测,如果还是低电平,才确认是有效按下。
  4. 时间管理:所有延时都用了pause。在更复杂的系统中,如果需要同时处理多个定时任务,pause会阻塞整个程序。这时可以考虑使用timer寄存器或自己用循环和变量实现非阻塞的定时。但对于这个简单模型,pause足够用。

这个项目虽然简单,但它涵盖了输入、输出、状态判断、延时、子程序(状态块可以看作一种抽象)等核心概念。当你成功让这个交通灯模型按照预期运行时,你对PICAXE BASIC编程的理解就已经从“知道命令”进阶到“组织逻辑”的层面了。