Skip to main content

Coding Tricks Summary

This page summarizes best practices and advanced techniques for programming on this system. Core idea: the main program handles scheduling, subroutines handle execution.


âš™ī¸ System Features Review​

Key Instructions​

InstructionMnemonicFunction
01JTDigital Output: write value to GPIO pins
09DZConditional jump: if register value > threshold, jump
0EZYUnconditional jump
0CDYCall subroutine (records return address, up to 16 levels deep)
0DFHReturn from subroutine
0FJSHalt / stop
10FZAssign: write to register
11JFZConditional jump: if register value ≠ threshold, jump
15(rotate)Bit operation / rotate instruction
00(NOP)Empty lines default to 00 0000 0000, does nothing

Instruction Format​

Each line format: line_number opcode P-0 P-1

  • P-0: First 4-digit HEX number
  • P-1: Second 4-digit HEX number

Target address positions for jump/call instructions:

InstructionTarget PositionNotes
DYP-0Call subroutine, target address in first 4-digit HEX
ZYP-1Unconditional jump, target address in second 4-digit HEX
FHNot neededSystem automatically records return address
JTNot neededConditional jump, no target line address needed

Correct: DY 0060 0000 (call subroutine 0060, target in P-0)

Correct: ZY 0000 0005 (jump to line 005, target in P-1)

Input Signal Summary​

Available input signals and their corresponding registers/instructions:

Signal TypeRegister / InstructionPurposeValue Range
ButtonR3EButton status register0000 = not pressed, 0001 = pressed
TemperatureR2ETemperature sensor reading0 ~ 65535 (depends on actual sensor)
Color SensorR31Red component value0 ~ 255
R32Green component value0 ~ 255
R33Blue component value0 ~ 255
Analog Input (ADC)AD instructionResistive sensors (light, sound, etc.)AD 0000 XXXX reads channel 0 to register
e.g., sound sensor, photo resistor, potentiometer0 ~ max (hardware dependent)
Digital Input (GPIO)14 instructionDigital pin level reading0000 = low, 0001 = high

Output Signal / Module Summary​

Available outputs and their corresponding registers/instructions. Below is a detailed breakdown organized by hardware type:

Output TypeInstruction / RegisterPurposeExample
Digital Output (GPIO)JT (01)Control LEDs, buzzer, relays, simple peripheralsJT 00FF 00FF set all pins high
PWM ControlR2A, R2B, R2C, R2DPWM duty cycle registers (0~03E8 = 0%~100%), control LED brightness, motor speedWrite 0000 ~ 03E8 to adjust (max 03E8 = 100%)
WS2812B LEDCD (E4)Set full-color LED color, format CD 00RR GGBBCD 00FF 0000 red full brightness
Delay ControlYS (02)Program delay/waitYS 03E8 0000 delay 1 second
Register to OutputSS (13)Output register value directly to digital pinsFor complex pattern batch output

Hardware Module Details​

🔊 Buzzer (IO Control, No Tone)​

Use a GPIO pin connected to a buzzer, toggle high/low to make sound:

Pin StateEffectRegister Value
Constant highSilent0001
Constant lowSilent0000
Fast high/low toggleMakes pulsing soundToggle in program
; Short "beep" from buzzer (connected to GPIO bit 2)
000 JT 00FF 0004 ; Buzzer low = sound (assuming low-level triggered)
001 YS 0064 0000 ; 100ms delay
002 JT 00FF 0001 ; Buzzer off
003 FH 0000 0000 ; Return

đŸŒŦī¸ Small Fan (L9110H Dual H-Bridge Driver)​

The L9110H requires 2 pins to control one fan (A-IA controls speed/direction, A-IB controls direction):

A-IAA-IBMotor StateDescription
PWM highLowForward (speed adjustable)Write speed value to A-IA PWM register
LowPWM highReverse (speed adjustable)Write speed value to A-IB PWM register
LowLowCoast stopMotor slows naturally
HighHighBrake stopFast deceleration
; Fan forward, speed = 50% (assuming R2A=A-IA, R2B=A-IB)
000 FZ 002A 01F4 ; R2A = 01F4 (50%) → A-IA outputs PWM
001 FZ 002B 0000 ; R2B = 0 → A-IB stays low
002 FH 0000 0000 ; Result: fan forward, ~half speed

; Fan reverse, speed = 80%
010 FZ 002A 0000 ; R2A = 0 → A-IA low
011 FZ 002B 0320 ; R2B = 80% (0320) duty → A-IB outputs PWM
012 FH 0000 0000 ; Result: fan reverse, ~80% speed

; Fan stop (coast)
020 FZ 002A 0000 ; Both to 0
021 FZ 002B 0000
022 FH 0000 0000

Note: One L9110H chip can drive 2 independent motors (channel A and B), so controlling 2 fans requires 4 PWM pins.

🌈 External RGB LED (3 pins, IO or PWM Control)​

Control red/green/blue with 3 independent pins:

Control MethodWiringEffect
Pure IO controlR/G/B to one GPIO eachOnly 8 colors possible (on/off)
PWM controlR/G/B to one PWM pin eachCan mix ~16 million colors
; External RGB shows red (R pin → R2A, G → R2B, B → R2C)
000 FZ 002A 03E8 ; Red = max brightness
001 FZ 002B 0000 ; Green = off
002 FZ 002C 0000 ; Blue = off
003 FH 0000 0000 ; Result: pure red

; Show cyan (green + blue mixed)
010 FZ 002A 0000 ; Red = off
011 FZ 002B 03E8 ; Green = max
012 FZ 002C 03E8 ; Blue = max
013 FH 0000 0000 ; Result: cyan

; Breathing light gradient effect (red from dim to bright and back)
020 FZ 0025 0000 ; R25 = brightness counter, start at 0
021 FZ 002A 0000 ; First set red to 0
022 XDZ 0025 0032 ; Brightness + 50 (fine adjustment each step)
023 FZ 002A 0025 ; Write current brightness to R2A (red PWM)
024 YS 0019 0000 ; 25ms delay
025 DYZ 0025 03E8 ; Not at max brightness (03E8=100%) yet?
026 ZY 0000 022 ; Yes → keep increasing
027 ZY 0000 020 ; No → start over

đŸĻž Servo Motor (PCA9685 Module Driver)​

The PCA9685 is an I²C-based 16-channel PWM expander chip — it uses only 2 wires (SDA data / SCL clock) to control 16 servos.

FeatureDescription
CommunicationI²C (2 wires)
Drivable servos16 per module, chainable up to 62 modules → 992 total
PWM resolution12-bit (4096 steps)
Typical servo frequency50Hz (20ms period)
Servo pulse width1ms = 0°, 1.5ms = 90° (center), 2ms = 180°
; Using PCA9685 to control servo angle
; Assuming system has I²C driver, configure PCA9685 registers to set servo angle
; Servo angle → PWM duty cycle mapping:
; 0° → pulse 1.0ms → ~5% duty at 50Hz
; 90° → pulse 1.5ms → ~7.5% duty at 50Hz
; 180° → pulse 2.0ms → ~10% duty at 50Hz

; Rotate servo to 90° (center position)
; Subroutine 0E0: Set channel 0 servo angle = parameter R20
; Here showing logic flow: send config data via I²C to PCA9685
0E0 FZ 0026 0040 ; PCA9685 I²C address = 0x40 (default)
0E1 FZ 0027 0006 ; Channel 0 register start = 0x06
0E2 FZ 0028 0000 ; PWM ON time = 0 (pull high from cycle start)
0E3 FZ 0029 0180 ; PWM OFF time = value for 1.5ms pulse (90°)
0E4 FH 0000 0000 ; Done via I²C register write

Why use PCA9685? Regular MCUs have limited PWM channels (typically 4-8), but PCA9685 expands to 16 PWM outputs with just 2 wires — perfect for multi-servo robotic projects. Servos themselves only need a PWM signal as position command, no continuous CPU intervention required.

Pin Resource Usage Overview​

ModulePins RequiredPin Type
Single buzzer1GPIO
1 fan (L9110H)2PWM × 2
2 fans (L9110H)4PWM × 4
1 external RGB LED3GPIO or PWM × 3
1 servo (via PCA9685)0 (uses 2 I²C bus lines)I²C shared
16 servos (via 1 PCA9685)0 (still just 2 I²C bus lines)I²C shared

Core Capabilities​

  • 16-level nesting depth: subroutines can call other subroutines, max 16 levels
  • Recursion supported: a subroutine can call itself
  • Empty lines default to 00: unfilled lines are not "blank" — they are 00 0000 0000 and the program will execute and pass through them in sequence

📐 Core Principles: Three Iron Rules​

RuleDescription
① Each subroutine does one thingEach subroutine starts with its logic at line 1. Never let a "return point" fall through into another subroutine
② Every logic path ends with FH/ZY/JSNo matter which branch your subroutine takes internally, it must eventually hit FH, ZY, or JS
â‘ĸ Don't place subroutines contiguouslyKeep buffer space (default 00 lines) between subroutines, making future expansion easier

đŸ—ī¸ Architecture 1: Layered Design​

Divide the line-number address space into three layers, organized like a "library."

Suggested Layout​

000 ~ 01F  →  Main program layer   (control flow, decisions, call subroutines)
020 ~ 07F → Atomic ops layer (frequently-called short subroutines, each does one thing)
080 ~ 0FF → Feature module layer (complete feature modules, can call atomic layer)

Why Layer?​

BenefitDescription
Main program is instantly readable000-03F contains only "who to call, where to jump" — logic is obvious
Fix once, benefit everywhereImprove the "delay subroutine" and everything using it automatically improves
Replaceable when space is tightIf a feature isn't needed, overwrite it without touching other modules
Room for expansionLeave blank lines between layers — adding new features in 3 months won't require shifting existing code

📚 Architecture 2: Atomic Operation Library (020-07F)​

Write your most-used operations as tiny 1-3 line subroutines. These are your "standard library functions."

Example: LED Control Subroutines​

040  CD  0000  0000   ; Sub 040: Turn all LEDs off
041 FH 0000 0000 ; Return

048 CD 00FF FFFF ; Sub 048: Turn all LEDs on
049 FH 0000 0000

050 FZ 002A 0080 ; Sub 050: Set PWM defaults
051 FZ 002B 0080
052 FZ 002C 0080
053 FH 0000 0000

Example: Delay Subroutines​

060  YS  03E8  0000   ; Sub 060: Long delay (~1 sec)
061 FH 0000 0000

068 YS 000A 0000 ; Sub 068: Short delay (~40 ms)
069 FH 0000 0000

With the atomic library, your main program becomes minimal​

005  DY  0048  0000   ; LEDs on (target in P-0)
006 DY 0060 0000 ; Wait 1 sec
007 DY 0040 0000 ; LEDs off
008 DY 0060 0000 ; Wait 1 sec
009 ZY 0000 0005 ; Loop (ZY target in P-1)

📝 Trick 3: Parameter Register Convention​

This is what makes subroutines truly powerful. Agree that certain registers are reserved for passing parameters — write them before calling, and the subroutine reads them.

Suggested Convention (customize as needed)​

**R20** = Delay parameter    Write before calling; subroutine uses it for timing
**R21** = Display mode 0=normal, 1=alarm, 2=blink
**R22** = Target register Which register the subroutine operates on
**R23** = Target value Value to write/compare

Example: Generic Delay Subroutine​

; Main program
005 FZ 0020 01F4 ; Write param first: R20 = 500ms
006 DY 0060 0000 ; Call delay subroutine

010 FZ 0020 07D0 ; Different param: R20 = 2 seconds
011 DY 0060 0000 ; Same subroutine! Different effect

Key insight: The subroutine doesn't need to know "why" we're delaying. It just does "whatever the agreed-upon location says." You change parameters, not subroutine code.


🔄 Trick 4: Recursive Programming (16 Levels Deep)​

Since recursion is supported (a subroutine can call itself) with 16 levels of nesting, you can implement real recursive algorithms.

; Main program
000 FZ 0020 0005 ; R20 = blink 5 times (recursion counter)
001 DY 0010 0000 ; Call "blink" subroutine
002 JS 0000 0000

; Sub 010: Blink recursively (R20 = remaining count)
010 DYZ 0020 0000 ; If R20 > 0, continue
011 ZY 0000 0020 ; Else jump to 020 (return without doing anything)

012 CD 00FF FFFF ; LEDs ON
013 YS 01F4 0000 ; Delay
014 CD 0000 0000 ; LEDs OFF
015 YS 01F4 0000 ; Delay
016 XDZ 0020 0001 ; R20 = R20 - 1 (decrement counter)
017 DY 0010 0000 ; ← Call itself! Recursion!
018 FH 0000 0000

020 FH 0000 0000 ; Recursion terminator: return

Execution Flow (with R20=5)​

Call stack depth progression:

Level 0: Main → DY 010
Level 1: 010 → check 5>0 → blink once → R20=4 → DY 010
Level 2: 010 → check 4>0 → blink once → R20=3 → DY 010
Level 3: 010 → check 3>0 → blink once → R20=2 → DY 010
Level 4: 010 → check 2>0 → blink once → R20=1 → DY 010
Level 5: 010 → check 1>0 → blink once → R20=0 → DY 010
Level 6: 010 → check 0=0 → jump 020 → FH return
← Unwind: level 5 FH → 4 FH → ... → level 1 FH → main

As long as your counter doesn't exceed 15, you won't overflow the nesting depth.


đŸ›Ąī¸ Trick 5: Fall-Through Prevention for Subroutines​

The most common bug: after returning from a subroutine, the program "falls through" in sequence into the next block of code, causing unintended execution.

Anti-Pattern (buggy)​

010  DY  0015  0000   ; Sub 010: calls 015 (target in P-0)
; ← NO FH! Returns to 011, then falls through!
011 00 0000 0000 ; Default empty line (00)
...
015 15 1101 0001 ; ← Gets executed TWICE!
016 FH 0000 0000

Good Pattern (correct)​

010  DY  0015  0000   ; Sub 010: calls 015
011 FH 0000 0000 ; ← Must end with FH/ZY/JS!
; Lines 012+ won't be reached via normal flow

015 15 1101 0001 ; Sub 015
016 FH 0000 0000 ; ← Must end with FH/ZY/JS!

Bumper Guard Pattern (safer)​

010  DY  0015  0000
011 FH 0000 0000 ; Normal return
012 JS 0000 0000 ; ← Bumper guard: stop if we somehow get here
013 JS 0000 0000
014 JS 0000 0000

015 15 1101 0001
016 FH 0000 0000
017 JS 0000 0000 ; ← Bumper guard

📊 Complete Example: Full Architecture​

Combining all the techniques above into one complete program.

Main Program Layer (000-01F)​

000  FZ  0020  0000   ; Initialize status register
001 FZ 0021 0001 ; Direction register: 1=forward

010 DY 0080 0000 ; Call "read sensors"
011 DY 00A0 0000 ; Call "display status"
012 DY 00C0 0000 ; Call "check alarm"
013 DY 0060 0000 ; Call "delay"
014 ZY 0000 0010 ; Loop

01F JS 0000 0000 ; Redundant halt (never reached, but reassuring)

Atomic Operations Library (040-07F)​

040  CD  0000  0000   ; Sub 040: Clear LEDs
041 FH 0000 0000

048 CD 00FF FFFF ; Sub 048: Light LEDs (white full)
049 FH 0000 0000

050 CD 00FF 0000 ; Sub 050: Alarm LED (flash red)
051 YS 00FF 0000
052 CD 0000 0000
053 YS 00FF 0000
054 FH 0000 0000

060 FZ 0020 03E8 ; Sub 060: Delay (R20 controls duration)
061 YS (R20) 0000
062 FH 0000 0000

Feature Module Layer (080-0FF)​

080  AD  0000  0010   ; Sub 080: Read sensors, store in R10
081 FH 0000 0000

0A0 AD 0000 0010 ; Sub 0A0: Display based on R10
0A1 JT 00FF (R10)
0A2 FH 0000 0000

0C0 DYZ 0010 0032 ; Sub 0C0: If R10 > 50, alarm
0C1 DY 0050 0000 ; YES → call alarm subroutine
0C2 FH 0000 0000
0C3 DY 0048 0000 ; NO → normal display
0C4 FH 0000 0000

💡 Advanced: Condition-First vs Condition-Last​

Two approaches to combining condition checks with subroutines — each has advantages.

Approach A: Condition-First (Traditional)​

; Check in the main program, then call
010 DYZ 0010 0032 ; Main program checks: R10>50?
011 DY 0050 0000 ; YES → call alarm
012 ZY 0000 0014 ; Skip normal branch
013 DY 0048 0000 ; NO → call normal display
014 ...

Feature: The main program worries about "decision logic" — code is scattered.

; Main program just calls — decision lives inside the subroutine
010 DY 00C0 0000 ; Call "smart display" subroutine
011 ...

; Sub 0C0: Decides internally what to display
0C0 DYZ 0010 0032 ; Check condition inside subroutine
0C1 ZY 0000 00C4 ; >50 → jump to alarm branch
0C2 DY 0048 0000 ; Else normal display
0C3 FH 0000 0000 ; ← Return
0C4 DY 0050 0000 ; Alarm branch
0C5 FH 0000 0000

Feature: The main program stays clean forever. Decision logic is hidden in the subroutine. Changing thresholds doesn't touch the main program. This is the recommended pattern for large programs.


📋 Quick Checklist​

After writing code, verify against these items:

CheckDescriptionOK?
Does every subroutine end with FH/ZY/JS?Prevents program "fall-through"☐
Is your main program just DY/ZY/JS calls?Main program should be a "calling list"☐
Recursion counter ≤ 15?Max 16 nesting levels — keep 1 for main☐
Register usage follows your convention?Don't let two subroutines share a register for different purposes☐
Are there buffer lines between subroutines?Default 00 lines = your buffer☐
Are DY targets in P-0 and ZY targets in P-1?DY=P-0, ZY=P-1, FH/JT don't need target☐

đŸŽ¯ The Goal​

When your code looks like the example below, you've fully mastered this architecture:

; Main program (you can understand the entire program at a glance)
000 DY 0080 0000 ; Initialize (DY target in P-0)
001 DY 0090 0000 ; Read input
002 DY 00A0 0000 ; Make decision
003 DY 00B0 0000 ; Output result
004 DY 00C0 0000 ; Delay
005 ZY 0000 0001 ; Loop (ZY target in P-1)

; ↓ HOW things are done lives entirely in subroutines

The main program becomes a "configuration table". You're not writing code — you're writing a directory of "which features to call." Three months from now, you'll glance at it and instantly know the whole program's intent.


🌟 Complete Example: Smart Environment Monitoring System​

Below is a comprehensive example combining multiple inputs (temperature, sound, button) and multiple outputs (external RGB LED, L9110H fan, IO buzzer, PCA9685 servo), demonstrating how to implement a "smart room environment monitoring system" using the layered architecture.

Feature Description​

FeatureTrigger ConditionAction
Temperature MonitoringR2E > threshold 1 (e.g., 30°C)Start fan (L9110H) + RGB LED shows red
Temperature AlarmR2E > threshold 2 (e.g., 40°C)Fan full speed + IO buzzer "beep beep beep" alarm
Ambient BrightnessAnalog input (ADC channel 0) < thresholdExternal RGB LED shows warm white (R2A/R2B/R2C three-channel PWM)
Button ControlButton pressed (R3E = 0001)Toggle manual/auto mode + servo rotate 90° to open curtain
Noise AlertAnalog input (ADC channel 1) > thresholdRGB LED quick blue flash

Hardware Wiring Convention​

; PWM pin allocation (R2A-R2D connected to different peripherals):
; R2A → External RGB LED R channel (PWM dimming)
; R2B → External RGB LED G channel (PWM dimming)
; R2C → External RGB LED B channel (PWM dimming)
; R2D → Spare (can connect other PWM devices)
;
; L9110H fan driver (2 PWM pins):
; GPIO bit 0 (A-IA) → Fan forward speed (controlled via PWM duty cycle)
; GPIO bit 1 (A-IB) → Fan reverse direction (this example uses only forward, so fixed at 0)
;
; Buzzer:
; GPIO bit 2 → IO-controlled buzzer (high/low toggle makes sound)
;
; PCA9685 servo (via I²C, no extra GPIO pins used):
; Channel 0 → Curtain servo, 0°=closed, 90°=open

Parameter Register Convention​

R20 = low temperature threshold (e.g., 30°C value)
R21 = high temperature threshold (e.g., 40°C value)
R22 = low brightness threshold (ADC channel 0)
R23 = noise threshold (ADC channel 1)
R24 = operating mode (0=auto, 1=manual)
R25 = temporary storage / status flag

Complete Program (Mnemonic Form)​

Main Program Layer (000-01F): Scheduling Only​

000  DY  0080  0000   ; Initialize: set default parameters
001 DY 0085 0000 ; Read all sensors
002 DY 0090 0000 ; Make decisions based on sensor values
003 DY 00A0 0000 ; Execute output actions
004 DY 00C0 0000 ; Delay/wait
005 ZY 0000 0001 ; Back to 001, loop monitoring

Initialization Subroutine (080-084)​

080  FZ  0020  1E00   ; Temperature low threshold = 0x1E00 (~30°C)
081 FZ 0021 2800 ; Temperature high threshold = 0x2800 (~40°C)
082 FZ 0022 0500 ; Brightness low threshold = 0x0500
083 FZ 0023 0A00 ; Noise threshold = 0x0A00
084 FH 0000 0000 ; Return

Sensor Reading Subroutine (085-08F)​

; Read analog input channel 0 (brightness/light), result to R2A
085 AD 0000 002A ; ADC channel 0 → R2A

; Read analog input channel 1 (sound), result to R2B
086 AD 0001 002B ; ADC channel 1 → R2B

; Copy temperature value (R2E) to R2C for easier comparison
087 JFZ 002C 002E ; R2C = R2E (temperature)

; Copy button status (R3E) to R25
088 JFZ 0025 003E ; R25 = R3E (button)

089 FH 0000 0000 ; Return

Decision Logic Subroutine (090-09F): Condition-First Pattern​

; Check if button pressed → toggle mode
090 DYZ 0025 0001 ; R25(button) > 0 ?
091 ZY 0000 093 ; Not pressed → skip toggle
092 XDZ 0024 0001 ; Pressed → R24 = 1 - R24 (toggle)
093 XDZ 0025 0001 ; Clear button flag, avoid repeated triggering

; Check temperature → determine fan state
094 DYZ 002C 0020 ; Temperature > low threshold ?
095 DY 00B0 0000 ; Yes → fan-on processing
096 DY 00B5 0000 ; No → fan-off processing

; Check brightness → determine lighting
097 DYZ 0022 002A ; Threshold > brightness reading ? (i.e., too dark)
098 DY 00BA 0000 ; Yes → turn lights on
099 DY 00BF 0000 ; No → turn lights off

; Check noise → flash alert
09A DYZ 002B 0023 ; Sound > noise threshold ?
09B DY 00C5 0000 ; Yes → flash alert
09C FH 0000 0000 ; No → return

Output Action Subroutine (0A0-0DF)​

; ===== Temperature-related (0B0-0B9)=====
; Note: Fan driven by L9110H, A-IA=GPIO bit 0 (PWM speed control), A-IB=GPIO bit 1 (direction)
; This example uses only forward, so A-IB is always 0

0B0 DYZ 002C 0021 ; Temperature > high threshold ? (serious overheat)
0B1 ZY 0000 0B4 ; Yes → jump to serious alarm
0B2 FZ 002A 01F4 ; No → fan half speed (PWM=01F4 (50%)), adjust duty at R2A output bit 0
0B3 FH 0000 0000 ; Return
0B4 FZ 002A 03E8 ; Serious: fan full speed
0B5 DY 00D0 0000 ; Call buzzer alarm subroutine
0B6 FH 0000 0000 ; Return

0B7 FZ 002A 0000 ; Temperature not high: turn off fan
0B8 FH 0000 0000 ; Return

; ===== Lighting-related (0BA-0C1)=====
; Note: External RGB LED uses R2A(R)/R2B(G)/R2C(B) three-channel PWM for color brightness control

0BA FZ 002A 03E8 ; R channel = max (warm white: red+green, blue slightly dimmer)
0BB FZ 002B 03E8 ; G channel = max
0BC FZ 002C 0320 ; B channel = ~80% (makes light warmer)
0BD FH 0000 0000 ; Return

0BE FZ 002A 0000 ; Turn off: all three PWM channels to zero
0BF FZ 002B 0000
0C0 FZ 002C 0000
0C1 FH 0000 0000 ; Return

; ===== Noise Alert (0C2-0C9)=====

0C2 FZ 002A 0000 ; R = off
0C3 FZ 002B 0000 ; G = off
0C4 FZ 002C 03E8 ; B = max → blue flash
0C5 YS 0100 0000 ; Delay ~250ms
0C6 FZ 002C 0000 ; Blue = off
0C7 YS 0100 0000 ; Wait a bit more
0C8 FH 0000 0000 ; Return

; ===== General Delay (0C9-0CD)=====

0C9 YS 01F4 0000 ; Main loop delay ~500ms
0CA FH 0000 0000 ; Return

; ===== Buzzer Alarm (0D0-0DB)=====
; Note: IO buzzer connected to GPIO bit 2, low-level triggered. Continuous "beep beep beep" three times

0D0 JT 00FF 0004 ; GPIO bit 2 = low → buzzer sound
0D1 YS 0032 0000 ; Sound 50ms
0D2 JT 00FF 0000 ; GPIO bit 2 = high → stop
0D3 YS 0064 0000 ; Interval 100ms
0D4 JT 00FF 0004 ; Second beep
0D5 YS 0032 0000
0D6 JT 00FF 0000
0D7 YS 0064 0000
0D8 JT 00FF 0004 ; Third beep
0D9 YS 0032 0000
0DA JT 00FF 0000
0DB FH 0000 0000 ; Alarm done, return

Architecture Explanation​

The code above demonstrates these key techniques:

  1. Minimal main program (000-005): only 6 lines — you can understand what the entire system does at a glance
  2. Condition-first: subroutines internally check whether they need to execute (e.g., 094-09C), the main program doesn't need to worry
  3. Modular layering: each function has an independent subroutine, modifying one doesn't affect others
  4. Parameter register convention: R20-R25 all used for parameter passing, subroutines don't need to "re-read"
  5. Clear line number partitioning: 080-084=init, 085-089=read sensors, 090+=decision output
  6. Flexible multi-hardware control: L9110H fan with PWM speed control, RGB LED with three-channel PWM color mixing, buzzer with IO toggle sound, servo with PCA9685 I²C expansion

Compact Version (Practical Running Code)​

If hardware resources are limited, you can write this more compact version (retaining core architecture):

; Main program
000 DY 0080 0000 ; Initialize
001 AD 0000 002A ; Read brightness → R2A
002 AD 0001 002B ; Read sound → R2B
003 JFZ 002C 002E ; Read temperature → R2C
004 DY 0090 0000 ; Decision & output
005 YS 03E8 0000 ; 1 second delay
006 ZY 0000 001 ; Loop

; Initialize
080 FZ 0020 1E00 ; Temperature threshold 1
081 FZ 0021 2800 ; Temperature threshold 2
082 FZ 0022 0500 ; Brightness threshold
083 FH 0000 0000 ; Return

; Decision & output (condition-first, all together)
090 DYZ 002C 0020 ; Temperature > threshold 1 ?
091 FZ 002A 01F4 ; Yes → fan half speed (PWM 50%)
092 DYZ 0022 002A ; Too dark ?
093 FZ 002B 03E8 ; Yes → green light on (ambient lighting)
094 FZ 002B 0000 ; No → green light off
095 FH 0000 0000 ; Return to main program