Skip to content

Commit b01a78a

Browse files
committed
Drive the nose too
1 parent a563d96 commit b01a78a

File tree

3 files changed

+135
-31
lines changed

3 files changed

+135
-31
lines changed

deploy/src/main.jl

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ include(joinpath(@__DIR__, "mpu6000.jl"))
66
include(joinpath(@__DIR__, "encoder.jl"))
77
include(joinpath(@__DIR__, "shift_driver.jl"))
88
include(joinpath(@__DIR__, "seven_seg.jl"))
9+
include(joinpath(@__DIR__, "ws2812_driver.jl"))
910

1011
using .GPIO
1112
using .PWM
@@ -29,7 +30,9 @@ const PWM_MAX_VALUE = 1024 # PWM duty cycle range (0-1024)
2930
# Global handles for GPIO pins and PWM channels
3031
mutable struct HardwareContext
3132
gpio::GPIO.GPIOController
33+
pio::PIOBlock
3234
chain::ShiftRegisterChain
35+
nose::WS2812
3336
pins::Vector{GPIO.GPIOPin}
3437
end
3538

@@ -42,7 +45,10 @@ Called on Ctrl-C or program exit.
4245
function shutdown!(hw::HardwareContext)
4346
println(Core.stdout, "Shutting down hardware...")
4447
hw.chain[0:23] = false
48+
set_color!(hw.nose, 0, 0, 0)
4549
close(hw.chain)
50+
close(hw.nose)
51+
close(hw.pio)
4652
for pin in hw.pins
4753
close(pin)
4854
end
@@ -61,6 +67,7 @@ end
6167
const CM_PRESENT=23
6268
const D1=5
6369
const D2=24
70+
const NOSE_RGB=18
6471

6572
function (@main)(args)::Cint
6673
println(Core.stdout, "Balance car starting...")
@@ -74,12 +81,20 @@ function (@main)(args)::Cint
7481
d2 = GPIO.request_output(gpio, D2, "d2", 0)
7582
println(Core.stdout, "GPIO configured")
7683

84+
# Open shared PIO block
85+
pio = open_pio(0)
86+
7787
# Initialize 3 chained shift registers via PIO
78-
chain = open_shift_registers()
88+
chain = open_shift_registers(pio)
7989
chain[0:23] = false
8090
println(Core.stdout, "Shift registers initialized ($NUM_REGISTERS x 8-bit, $NBITS outputs)")
8191

82-
hw = HardwareContext(gpio, chain, [cm_present, d1, d2])
92+
# Initialize nose RGB LED (WS2812) on same PIO block
93+
nose = open_ws2812(pio, NOSE_RGB)
94+
set_color!(nose, 0, 0, 0)
95+
println(Core.stdout, "Nose RGB LED initialized on pin $NOSE_RGB")
96+
97+
hw = HardwareContext(gpio, pio, chain, nose, [cm_present, d1, d2])
8398

8499
# Enable hardware
85100
GPIO.set_value(cm_present, 1)
@@ -93,6 +108,7 @@ function (@main)(args)::Cint
93108
disp2 = SevenSeg(chain, 16) # 3rd register: bits 16–23
94109

95110
digit = 0
111+
hue = 0
96112

97113
# Control loop timing (500ms = 2Hz)
98114
loop_period_ns = 500_000_000
@@ -119,6 +135,26 @@ function (@main)(args)::Cint
119135
show_digit!(disp1, (digit + 1) % 10)
120136
end
121137
digit = (digit + 1) % 10
138+
139+
# Cycle nose LED through hue wheel
140+
# Simple HSV→RGB with S=V=255, varying H
141+
h = hue ÷ 43
142+
f = (hue - h * 43) * 6
143+
q = 255 - f
144+
if h == 0
145+
set_color!(nose, 255, f, 0)
146+
elseif h == 1
147+
set_color!(nose, q, 255, 0)
148+
elseif h == 2
149+
set_color!(nose, 0, 255, f)
150+
elseif h == 3
151+
set_color!(nose, 0, q, 255)
152+
elseif h == 4
153+
set_color!(nose, f, 0, 255)
154+
else
155+
set_color!(nose, 255, 0, q)
156+
end
157+
hue = (hue + 25) % 256
122158
end
123159
catch e
124160
if e isa InterruptException

deploy/src/shift_driver.jl

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -84,42 +84,36 @@ mutable struct ShiftRegisterChain
8484
end
8585

8686
"""
87-
open_shift_registers(pio_idx=0) -> ShiftRegisterChain
87+
open_shift_registers(pio::PIOBlock) -> ShiftRegisterChain
8888
89-
Initialize the PIO and state machine for driving the shift register chain.
90-
Caller is responsible for calling `close` when done.
89+
Initialize a state machine on `pio` for driving the shift register chain.
90+
The PIO block must already be open (caller manages its lifetime).
9191
"""
92-
function open_shift_registers(pio_idx::Integer=0)
93-
pio = open_pio(pio_idx)
94-
try
95-
prog, config = shift_register_program(pio;
96-
ser_pin=SER_PIN, clk_pin=CLK_PIN, rclk_pin=RCLK_PIN,
97-
nbits=NBITS, clkdiv=CLKDIV,
98-
)
92+
function open_shift_registers(pio::PIOBlock)
93+
prog, config = shift_register_program(pio;
94+
ser_pin=SER_PIN, clk_pin=CLK_PIN, rclk_pin=RCLK_PIN,
95+
nbits=NBITS, clkdiv=CLKDIV,
96+
)
9997

100-
for pin in (SER_PIN, CLK_PIN, RCLK_PIN)
101-
pio_pin_init!(pio, pin)
102-
end
98+
for pin in (SER_PIN, CLK_PIN, RCLK_PIN)
99+
pio_pin_init!(pio, pin)
100+
end
103101

104-
offset = load_program!(pio, prog, config)
105-
106-
sm = claim_sm(pio)
107-
try
108-
pin_mask = UInt32(1) << SER_PIN | UInt32(1) << CLK_PIN | UInt32(1) << RCLK_PIN
109-
set_pindirs!(sm, pin_mask, pin_mask)
110-
PIOLib.init!(sm, offset, config)
111-
setup_shift_register!(sm, NBITS)
112-
enable!(sm)
113-
catch
114-
unclaim!(sm)
115-
rethrow()
116-
end
102+
offset = load_program!(pio, prog, config)
117103

118-
return ShiftRegisterChain(pio, sm, UInt32(0), false)
104+
sm = claim_sm(pio)
105+
try
106+
pin_mask = UInt32(1) << SER_PIN | UInt32(1) << CLK_PIN | UInt32(1) << RCLK_PIN
107+
set_pindirs!(sm, pin_mask, pin_mask)
108+
PIOLib.init!(sm, offset, config)
109+
setup_shift_register!(sm, NBITS)
110+
enable!(sm)
119111
catch
120-
close(pio)
112+
unclaim!(sm)
121113
rethrow()
122114
end
115+
116+
return ShiftRegisterChain(pio, sm, UInt32(0), false)
123117
end
124118

125119
# --- Flush ---
@@ -265,5 +259,4 @@ end
265259
function Base.close(chain::ShiftRegisterChain)
266260
disable!(chain.sm)
267261
unclaim!(chain.sm)
268-
close(chain.pio)
269262
end

deploy/src/ws2812_driver.jl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using PIOLib
2+
3+
function ws2812_program(pio::PIOBlock; pin::Integer, sys_clk::Real=200_000_000.0)
4+
T1 = 2
5+
T2 = 5
6+
T3 = 3
7+
cycles_per_bit = T1 + T2 + T3
8+
div = clkdiv(800_000.0 * cycles_per_bit; sys_clk)
9+
10+
prog = build_program([
11+
WrapTarget(),
12+
Label(:bitloop),
13+
Out(RegX(), 1; sideset=0, delay=T3-1),
14+
Jmp{:not_x}(:do_zero; sideset=1, delay=T1-1),
15+
Jmp{:always}(:bitloop; sideset=1, delay=T2-1),
16+
Label(:do_zero),
17+
Nop(; sideset=0, delay=T2-1),
18+
Wrap(),
19+
]; sideset_bits=1)
20+
21+
config = SMConfig(pio;
22+
sideset_pin_base=pin,
23+
sideset=(1, false, false),
24+
out_shift=(false, true, 24),
25+
clkdiv=div,
26+
wrap=(prog.wrap_target, prog.wrap),
27+
fifo_join=PIOLib.LibPIO.PIO_FIFO_JOIN_TX,
28+
)
29+
30+
prog, config
31+
end
32+
33+
ws2812_put!(sm::StateMachine, grb::UInt32) = put!(sm, grb << 8)
34+
35+
function ws2812_rgb!(sm::StateMachine, r::Integer, g::Integer, b::Integer)
36+
grb = (UInt32(g) << 16) | (UInt32(r) << 8) | UInt32(b)
37+
ws2812_put!(sm, grb)
38+
end
39+
40+
struct WS2812
41+
pio::PIOBlock
42+
sm::StateMachine
43+
end
44+
45+
"""
46+
open_ws2812(pio::PIOBlock, pin::Integer) -> WS2812
47+
48+
Initialize a WS2812 LED on `pin` using an SM from the given PIO block.
49+
The PIO block must already be open (shared with other drivers).
50+
"""
51+
function open_ws2812(pio::PIOBlock, pin::Integer)
52+
prog, config = ws2812_program(pio; pin=pin)
53+
54+
pio_pin_init!(pio, pin)
55+
offset = load_program!(pio, prog, config)
56+
57+
sm = claim_sm(pio)
58+
try
59+
set_consecutive_pindirs!(sm, pin, 1, true)
60+
PIOLib.init!(sm, offset, config)
61+
enable!(sm)
62+
catch
63+
unclaim!(sm)
64+
rethrow()
65+
end
66+
67+
WS2812(pio, sm)
68+
end
69+
70+
set_color!(led::WS2812, r::Integer, g::Integer, b::Integer) = ws2812_rgb!(led.sm, r, g, b)
71+
72+
function Base.close(led::WS2812)
73+
disable!(led.sm)
74+
unclaim!(led.sm)
75+
end

0 commit comments

Comments
 (0)