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â
| Instruction | Mnemonic | Function |
|---|---|---|
01 | JT | Digital Output: write value to GPIO pins |
09 | DZ | Conditional jump: if register value > threshold, jump |
0E | ZY | Unconditional jump |
0C | DY | Call subroutine (records return address, up to 16 levels deep) |
0D | FH | Return from subroutine |
0F | JS | Halt / stop |
10 | FZ | Assign: write to register |
11 | JFZ | Conditional 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:
Instruction Target Position Notes DY P-0 Call subroutine, target address in first 4-digit HEX ZY P-1 Unconditional jump, target address in second 4-digit HEX FH Not needed System automatically records return address JT Not needed Conditional 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 Type | Register / Instruction | Purpose | Value Range |
|---|---|---|---|
| Button | R3E | Button status register | 0000 = not pressed, 0001 = pressed |
| Temperature | R2E | Temperature sensor reading | 0 ~ 65535 (depends on actual sensor) |
| Color Sensor | R31 | Red component value | 0 ~ 255 |
| R32 | Green component value | 0 ~ 255 | |
| R33 | Blue component value | 0 ~ 255 | |
| Analog Input (ADC) | AD instruction | Resistive sensors (light, sound, etc.) | AD 0000 XXXX reads channel 0 to register |
| e.g., sound sensor, photo resistor, potentiometer | 0 ~ max (hardware dependent) | ||
| Digital Input (GPIO) | 14 instruction | Digital pin level reading | 0000 = low, 0001 = high |
Output Signal / Module Summaryâ
Available outputs and their corresponding registers/instructions. Below is a detailed breakdown organized by hardware type:
| Output Type | Instruction / Register | Purpose | Example |
|---|---|---|---|
| Digital Output (GPIO) | JT (01) | Control LEDs, buzzer, relays, simple peripherals | JT 00FF 00FF set all pins high |
| PWM Control | R2A, R2B, R2C, R2D | PWM duty cycle registers (0~03E8 = 0%~100%), control LED brightness, motor speed | Write 0000 ~ 03E8 to adjust (max 03E8 = 100%) |
| WS2812B LED | CD (E4) | Set full-color LED color, format CD 00RR GGBB | CD 00FF 0000 red full brightness |
| Delay Control | YS (02) | Program delay/wait | YS 03E8 0000 delay 1 second |
| Register to Output | SS (13) | Output register value directly to digital pins | For 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 State | Effect | Register Value |
|---|---|---|
| Constant high | Silent | 0001 |
| Constant low | Silent | 0000 |
| Fast high/low toggle | Makes pulsing sound | Toggle 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-IA | A-IB | Motor State | Description |
|---|---|---|---|
| PWM high | Low | Forward (speed adjustable) | Write speed value to A-IA PWM register |
| Low | PWM high | Reverse (speed adjustable) | Write speed value to A-IB PWM register |
| Low | Low | Coast stop | Motor slows naturally |
| High | High | Brake stop | Fast 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 Method | Wiring | Effect |
|---|---|---|
| Pure IO control | R/G/B to one GPIO each | Only 8 colors possible (on/off) |
| PWM control | R/G/B to one PWM pin each | Can 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.
| Feature | Description |
|---|---|
| Communication | I²C (2 wires) |
| Drivable servos | 16 per module, chainable up to 62 modules â 992 total |
| PWM resolution | 12-bit (4096 steps) |
| Typical servo frequency | 50Hz (20ms period) |
| Servo pulse width | 1ms = 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â
| Module | Pins Required | Pin Type |
|---|---|---|
| Single buzzer | 1 | GPIO |
| 1 fan (L9110H) | 2 | PWM Ã 2 |
| 2 fans (L9110H) | 4 | PWM Ã 4 |
| 1 external RGB LED | 3 | GPIO 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 0000and the program will execute and pass through them in sequence
đ Core Principles: Three Iron Rulesâ
| Rule | Description |
|---|---|
| â Each subroutine does one thing | Each 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/JS | No matter which branch your subroutine takes internally, it must eventually hit FH, ZY, or JS |
| âĸ Don't place subroutines contiguously | Keep 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?â
| Benefit | Description |
|---|---|
| Main program is instantly readable | 000-03F contains only "who to call, where to jump" â logic is obvious |
| Fix once, benefit everywhere | Improve the "delay subroutine" and everything using it automatically improves |
| Replaceable when space is tight | If a feature isn't needed, overwrite it without touching other modules |
| Room for expansion | Leave 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â
- Mnemonic Form
- Instruction Form
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
040 E4 0000 0000
041 0D 0000 0000
048 E4 00FF FFFF
049 0D 0000 0000
050 10 002A 0080
051 10 002B 0080
052 10 002C 0080
053 0D 0000 0000
Example: Delay Subroutinesâ
- Mnemonic Form
- Instruction Form
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
060 02 03E8 0000
061 0D 0000 0000
068 02 000A 0000
069 0D 0000 0000
With the atomic library, your main program becomes minimalâ
- Mnemonic Form
- Instruction Form
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)
005 0C 0048 0000
006 0C 0060 0000
007 0C 0040 0000
008 0C 0060 0000
009 0E 0005 0000
đ 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â
- Mnemonic Form
- Instruction Form
; 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
005 10 0020 01F4
006 0C 0060 0000
010 10 0020 07D0
011 0C 0060 0000
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.
Example: Blink N Times Recursivelyâ
- Mnemonic Form
- Instruction Form
; 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
000 10 0020 0005
001 0C 0010 0000
002 0F 0000 0000
010 0A 0020 0000
011 0E 0020 0000
012 E4 00FF FFFF
013 02 01F4 0000
014 E4 0000 0000
015 02 01F4 0000
016 XDZ 0020 0001
017 0C 0010 0000
018 0D 0000 0000
020 0D 0000 0000
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)â
- Mnemonic Form
- Instruction Form
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
010 0C 0015 0000
011 00 0000 0000
...
015 15 1101 0001
016 0D 0000 0000
Good Pattern (correct)â
- Mnemonic Form
- Instruction Form
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!
010 0C 0015 0000
011 0D 0000 0000
015 15 1101 0001
016 0D 0000 0000
Bumper Guard Pattern (safer)â
- Mnemonic Form
- Instruction Form
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
010 0C 0015 0000
011 0D 0000 0000
012 0F 0000 0000
013 0F 0000 0000
014 0F 0000 0000
015 15 1101 0001
016 0D 0000 0000
017 0F 0000 0000
đ Complete Example: Full Architectureâ
Combining all the techniques above into one complete program.
Main Program Layer (000-01F)â
- Mnemonic Form
- Instruction Form
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)
000 10 0020 0000
001 10 0021 0001
010 0C 0080 0000
011 0C 00A0 0000
012 0C 00C0 0000
013 0C 0060 0000
014 0E 0010 0000
01F 0F 0000 0000
Atomic Operations Library (040-07F)â
- Mnemonic Form
- Instruction Form
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
040 E4 0000 0000
041 0D 0000 0000
048 E4 00FF FFFF
049 0D 0000 0000
050 E4 00FF 0000
051 02 00FF 0000
052 E4 0000 0000
053 02 00FF 0000
054 0D 0000 0000
060 10 0020 03E8
061 02 (R20) 0000
062 0D 0000 0000
Feature Module Layer (080-0FF)â
- Mnemonic Form
- Instruction Form
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
080 A0 0000 0010
081 0D 0000 0000
0A0 A0 0000 0010
0A1 01 00FF (R10)
0A2 0D 0000 0000
0C0 0A 0010 0032
0C1 0C 0050 0000
0C2 0D 0000 0000
0C3 0C 0048 0000
0C4 0D 0000 0000
đĄ Advanced: Condition-First vs Condition-Lastâ
Two approaches to combining condition checks with subroutines â each has advantages.
Approach A: Condition-First (Traditional)â
- Mnemonic Form
- Instruction Form
; 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 ...
010 0A 0010 0032
011 0C 0050 0000
012 0E 0014 0000
013 0C 0048 0000
014 ...
Feature: The main program worries about "decision logic" â code is scattered.
Approach B: Condition-Last (Encapsulated, Recommended)â
- Mnemonic Form
- Instruction Form
; 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
010 0C 00C0 0000
011 ...
0C0 0A 0010 0032
0C1 0E 00C4 0000
0C2 0C 0048 0000
0C3 0D 0000 0000
0C4 0C 0050 0000
0C5 0D 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:
| Check | Description | OK? |
|---|---|---|
| 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â
| Feature | Trigger Condition | Action |
|---|---|---|
| Temperature Monitoring | R2E > threshold 1 (e.g., 30°C) | Start fan (L9110H) + RGB LED shows red |
| Temperature Alarm | R2E > threshold 2 (e.g., 40°C) | Fan full speed + IO buzzer "beep beep beep" alarm |
| Ambient Brightness | Analog input (ADC channel 0) < threshold | External RGB LED shows warm white (R2A/R2B/R2C three-channel PWM) |
| Button Control | Button pressed (R3E = 0001) | Toggle manual/auto mode + servo rotate 90° to open curtain |
| Noise Alert | Analog input (ADC channel 1) > threshold | RGB 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:
- Minimal main program (000-005): only 6 lines â you can understand what the entire system does at a glance
- Condition-first: subroutines internally check whether they need to execute (e.g., 094-09C), the main program doesn't need to worry
- Modular layering: each function has an independent subroutine, modifying one doesn't affect others
- Parameter register convention: R20-R25 all used for parameter passing, subroutines don't need to "re-read"
- Clear line number partitioning: 080-084=init, 085-089=read sensors, 090+=decision output
- 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