Introduction.
Hey there,
In this blog-post, I will be sharing the source code of the needle sweep patch made for cluster VMMJ08MH 9.00, including a brief explanation on how this works.
This is useful for new developers or if you want to learn how to make a patch code for these clusters – once you manage to control it, you can create infinite amounts of codes and/or fixes for the cluster.
I won’t go into full detail here, these clusters have a W65C816 MCU that controls the entire cluster, and you write code in W65C816 assembler opcodes to give instructions on what to do – this is basically programming the MCU.
The MCU model that this cluster uses is a Micronas CDVV 1304.
The code.
DISCLAIMER: this code is licensed under the GNU LGPL v3.0 or later, see the LICENSE tab for more details.
; Needle and led sweep for cluster VMMJ08MH 9.00
; Made by Feche
; https://feche.ar/
.org $0E8F
MaxSweepProgress = $55
MaxDelayProgress = $60
sub_5587:
; Check if cluster in on
JSR $6027
BCC ResetSweep
; Check if needles are homed
JSR IsHomed
BCS ResetSweep
; If sweep is done, branch
JSR Progress
BCS Continue
JSR NeedleSweep
JSR LightSweep
Continue:
JSR CheckLeds
LDA $81
JMP $558A
ResetSweep:
STZ DelayProgress
STZ SweepProgress
STZ EnableLeds
BCC Continue
; Disable delay if not homed
LDA #MaxDelayProgress
STA DelayProgress
BRA Continue
; NeedleSweep ------------------------------------------------------------------
NeedleSweep:
LDA SweepProgress
CMP #(MaxSweepProgress/2)
BCS ZeroOutNeedles
; Do needle sweep!
LDA #$5
STA $26D
STA $273
LDA #$80
STA $272
STA $26C
LDA #$0F
STA $26F
STA $271
LDA #$B0
STA $270
STA $26E
RTS
ZeroOutNeedles:
; Set needles to zero
LDX #$0
Loop:
STZ $26C, X
INX
CPX #8
BNE Loop
RTS
; LightSweep ------------------------------------------------------------------
LightSweep:
PHX
PHY
STZ $ACD
STZ $ACE
; Load speedometer
LDX #$E7
; Get needle position
LDY 7, X
LDA LedSweepData, Y
LDY SweepProgress
CPY #(MaxSweepProgress/2)
BCS NeedleDown
TSB LedData
BRA LightContinue
NeedleDown:
TRB LedData
LightContinue:
LDA LedData
STA $ACE
LDA #$CD
STA 0
LDA #$A
STA 1
; this function turns on/off leds
JSR $65D9
PLY
PLX
RTS
; Progress ---------------------------------------------------------------------
Progress:
; Check if we need to apply delay
LDA DelayProgress
CMP #MaxDelayProgress
; Delay done
BEQ DelayDone
; Increment delay counter
SEC
INC DelayProgress
JSR ZeroOutNeedles
RTS
DelayDone:
; Increment progress counter
SEC
LDA SweepProgress
CMP #MaxSweepProgress
; Progress done!
BEQ ProgressDone
CLC
INC SweepProgress
RTS
ProgressDone:
; Enable leds after we finish the sweep
LDA #1
STA EnableLeds
;STZ SweepProgress ; Loop!
RTS
; CheckLeds --------------------------------------------------------------------
CheckLeds:
LDA EnableLeds
CMP #0
BEQ DisableLed
RTS
DisableLed:
LDA #1
TSB $A56
RTS
; IsHomed ----------------------------------------------------------------------
IsHomed:
LDA $B41
CMP #$1E
BEQ HomedTrue
CMP #$10
BEQ HomedTrue
HomedFalse:
SEC
RTS
HomedTrue:
CLC
RTS
; 0 1 2 3 4 5 6 7 8 9
LedSweepData: .byte $00, $80, $00, $01, $00, $40, $00, $04, $00, $02
.byte $00, $00, $20, $00, $08, $00, $00, $00, $00, $00
.byte $00, $00, $00, $00, $00, $00, $00
DelayProgress: .byte $00
SweepProgress: .byte $00
EnableLeds: .byte $00
LedData: .byte $00
Explanation.
I’ve included comments in the code to understand it better, but in a quick glance:
- We call routine $6027 to check if cluster is on/off. (ignition on/off)
- We call routine IsHomed() to check if the needles are in the homed position. (needles ready)
- If one of the both are false, the needle sweep resets back to zero and this process repeats until both checks are true.
- Now that we are ready, we call Progress() routine to increment the progress variable used as reference for start – end – stop cycle of the sweep.
- Inside Progress() routine, we check for delay – a delay is needed when the needle is already homed, caused by a rapid on/off of the ignition switch. If the needle is not homed, the cluster constantly checks the needles until it is ready – this process produces a delay, and this is the delay we simulate to keep both sweeps (homed/not homed) having the same duration.
- Once the delay is done, the DelayProgress variable starts to increment until MaxSweepProgress is reached, once reached the sweep is done.
- If the sweep is not done yet, we move the needles/leds upwards until MaxSweepProgress/2, beyond that the needles need to go down, the ‘zero-out’ position.
The code can then be compiled in any W65C816 compiler, there is an online compiler version above in ‘useful links’.
This is programming in low-level language, and in order to fully understand it you will need previous knowledge on bitwise operations, memory addressing, stack pointer, etc.
Useful links.
- W65C816 opcode list
- MCU datasheet
- Online W65C816 compiler
- https://github.com/gmenounos/vwcluster/tree/main
Happy coding 🙂
UPDATES:
09/12/24
– Fixed an issue with the temperature needle not being zeroed correctly.
– Reduced initial sweep delay.