Files
aqhomecontrol/avr/modules/multifan/main.asm
2026-06-29 22:15:15 +02:00

350 lines
8.3 KiB
NASM

; ***************************************************************************
; copyright : (C) 2026 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#ifndef AQH_AVR_MODULES_MULTIFAN_MAIN_ASM
#define AQH_AVR_MODULES_MULTIFAN_MAIN_ASM
; ***************************************************************************
; defs
.equ MULTIFAN_FAN_OFFS_COUNTER_LO = 0
.equ MULTIFAN_FAN_OFFS_COUNTER_HI = 1
.equ MULTIFAN_FAN_OFFS_CURR_FREQUENCY_LO = 2
.equ MULTIFAN_FAN_OFFS_CURR_FREQUENCY_HI = 3
.equ MULTIFAN_FAN_OFFS_DUTYCYCLE = 4 ; 0-99 (*40/100=PWM value)
.equ MULTIFAN_FAN_SIZE = 5
; ***************************************************************************
; data
.dseg
multiFanDataBegin:
multiFanPrevPortVal: .byte 1
multiFanObjects: .byte (MULTIFAN_FANS*MULTIFAN_FAN_SIZE)
multiFanDataEnd:
; ***************************************************************************
; code
.cseg
MultiFan_Init:
ldi xl, LOW(multiFanDataBegin)
ldi xh, HIGH(multiFanDataBegin)
ldi r17, (multiFanDataEnd-multiFanDataBegin)
clr r16
bigcall Utils_FillSram
ldi r16, 0xff
sts multiFanPrevPortVal, r16
rcall multiFanSetupTimer
rcall multiFanSetupPins
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine MultiFan_OnEverySecond @global
;
; @clobbers r16, r17, r18, r19, r24, Y
MultiFan_OnEverySecond:
ret ; DEBUG
ldi yl, LOW(multiFanObjects)
ldi yh, HIGH(multiFanObjects)
ldi r24, MULTIFAN_FANS
push r15
inr r15, SREG
MultiFan_OnEverySecond_loop:
cli
ldd r16, Y+MULTIFAN_FAN_OFFS_COUNTER_LO
ldd r17, Y+MULTIFAN_FAN_OFFS_COUNTER_HI
; clear counter for next round
clr r18
std Y+MULTIFAN_FAN_OFFS_COUNTER_LO, r18
std Y+MULTIFAN_FAN_OFFS_COUNTER_HI, r18
outr SREG, r15 ; restore IRQ flag
; counter*30=RPM
mov r18, r16
mov r19, r17
lsl r16 ; x2
rol r17
add r16, r18 ; x3
adc r17, r19
lsl r16 ; x6
rol r17
add r16, r18 ; x7
adc r17, r19
lsl r16 ; x14
rol r17
add r16, r18 ; x15
adc r17, r19
lsl r16 ; x30
rol r17
std Y+MULTIFAN_FAN_OFFS_CURR_FREQUENCY_LO, r16
std Y+MULTIFAN_FAN_OFFS_CURR_FREQUENCY_HI, r17
dec r24
brne MultiFan_OnEverySecond_loop
pop r15
ret
; @end
; ---------------------------------------------------------------------------
; @routine MultiFan_CheckPort @global
;
; @param r16 new port value (e.g. PINB)
; @clobbers r17, r18, r24, r25, Y
MultiFan_CheckPort:
lds r17, multiFanPrevPortVal
sts multiFanPrevPortVal, r16
mov r18, r17
eor r18, r16 ; changed bits=1
com r18 ; complement: changed bits=0
or r18, r16 ; only bits are 0 which have just changed to 0
ldi yl, LOW(multiFanObjects)
ldi yh, HIGH(multiFanObjects)
sbrs r18, MULTIFAN_TACHO1_PIN
rcall multiFanIncCounter ; (r24, r25)
.if MULTIFAN_FANS >1
adiw yh:yl, MULTIFAN_FAN_SIZE
sbrs r18, MULTIFAN_TACHO2_PIN
rcall multiFanIncCounter
.endif
.if MULTIFAN_FANS >2
adiw yh:yl, MULTIFAN_FAN_SIZE
sbrs r18, MULTIFAN_TACHO3_PIN
rcall multiFanIncCounter
.endif
ret
; @end
MultiFan_SetFan1Duty:
ldi yl, LOW(multiFanObjects)
ldi yh, HIGH(multiFanObjects)
std Y+MULTIFAN_FAN_OFFS_DUTYCYCLE, r16
rcall multiFanCalcTimerValue ; r17:r16=timer value
; set OC register
outr TC1H, r17
outr OCR1A, r16
ret
; @end
.if MULTIFAN_FANS > 1
MultiFan_SetFan2Duty:
ldi yl, LOW(multiFanObjects+MULTIFAN_FAN_SIZE)
ldi yh, HIGH(multiFanObjects+MULTIFAN_FAN_SIZE)
std Y+MULTIFAN_FAN_OFFS_DUTYCYCLE, r16
rcall multiFanCalcTimerValue ; r17:r16=timer value
; set OC register
outr TC1H, r17
outr OCR1B, r16
ret
; @end
.endif
.if MULTIFAN_FANS > 2
MultiFan_SetFan3Duty:
ldi yl, LOW(multiFanObjects+(MULTIFAN_FAN_SIZE*2))
ldi yh, HIGH(multiFanObjects+(MULTIFAN_FAN_SIZE*2))
std Y+MULTIFAN_FAN_OFFS_DUTYCYCLE, r16
rcall multiFanCalcTimerValue ; r17:r16=timer value
; set OC register
outr TC1H, r17
outr OCR1D, r16
ret
; @end
.endif
; ---------------------------------------------------------------------------
; @routine multiFanCalcTimerValue
;
; @param r16 duty value (0-99)
; @return r17:r16 timer value
; @clobbers r16, r17, r18, r19, r20, r21, r25
multiFanCalcTimerValue:
; x40
clr r17
mov r18, r16
mov r19, r17
lsl r16 ; x2
rol r17
lsl r16 ; x4
rol r17
add r16, r18 ; x5
adc r17, r19
lsl r16 ; x10
rol r17
lsl r16 ; x20
rol r17
lsl r16 ; x40
rol r17
; /100
mov r20, r16
mov r21, r17
ldi r22, 100
clr r23
bigjmp Utils_Divu16_16_16 ; r17:r16=result
; @end
; ---------------------------------------------------------------------------
; @routine multiFanIncCounter
;
; @param Y pointer to FAN object
; @clobbers r24, r25
multiFanIncCounter:
ldd r24, Y+MULTIFAN_FAN_OFFS_COUNTER_LO
ldd r25, Y+MULTIFAN_FAN_OFFS_COUNTER_HI
adiw r25:r24, 1
std Y+MULTIFAN_FAN_OFFS_COUNTER_LO, r24
std Y+MULTIFAN_FAN_OFFS_COUNTER_HI, r24
ret
; @end
; ---------------------------------------------------------------------------
; @routine multiFanSetupPins
;
; @clobbers r16
multiFanSetupPins:
; set pins for PWM to output
inr r16, MULTIFAN_PWM_DDR
.if MULTIFAN_FANS == 1
ori r16, (1<<MULTIFAN_PWM1_PIN) | (0<<MULTIFAN_PWM2_PIN) | (0<<MULTIFAN_PWM3_PIN)
.elif MULTIFAN_FANS == 2
ori r16, (1<<MULTIFAN_PWM1_PIN) | (1<<MULTIFAN_PWM2_PIN) | (0<<MULTIFAN_PWM3_PIN)
.elif MULTIFAN_FANS == 3
ori r16, (1<<MULTIFAN_PWM1_PIN) | (1<<MULTIFAN_PWM2_PIN) | (1<<MULTIFAN_PWM3_PIN)
.else
.error "Invalid number of fans (only 1-3 allowed)
.endif
outr MULTIFAN_PWM_DDR, r16
; set pins for Tacho to input
inr r16, MULTIFAN_TACHO_DDR
.if MULTIFAN_FANS == 1
cbr r16, (1<<MULTIFAN_TACHO1_PIN) | (0<<MULTIFAN_TACHO2_PIN) | (0<<MULTIFAN_TACHO3_PIN)
.elif MULTIFAN_FANS == 2
cbr r16, (1<<MULTIFAN_TACHO1_PIN) | (1<<MULTIFAN_TACHO2_PIN) | (0<<MULTIFAN_TACHO3_PIN)
.elif MULTIFAN_FANS == 3
cbr r16, (1<<MULTIFAN_TACHO1_PIN) | (1<<MULTIFAN_TACHO2_PIN) | (1<<MULTIFAN_TACHO3_PIN)
.else
.error "Invalid number of fans (only 1-3 allowed)
.endif
outr MULTIFAN_PWM_DDR, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine multiFanSetupTimer
;
; @clobbers r16, r17
multiFanSetupTimer:
; prescaler 1
ldi r16, (0<<PSR1)|(0<<DTPS11)|(0<<DTPS10) | (0<<CS13)|(0<<CS12)|(0<<CS11)|(1<<CS10)
outr TCCR1B, r16
; select fast PWM, enable PIN output (don't activate negated pin outputs)
.if MULTIFAN_FANS == 1
ldi r16, (1<<COM1A1)|(0<<COM1A0) | (0<<COM1B1)|(0<<COM1B0) | (1<<PWM1A) | (0<<PWM1B)
outr TCCR1A, r16
ldi r16, (1<<COM1A1S)|(0<<COM1A0S) | (1<<COM1B1S)|(0<<COM1B0S) | (0<<PWM1D)
outr TCCR1C, r16
ldi r16, (0<<FPIE1)|(0<<FPEN1)|(0<<FPNC1)|(0<<FPES1)(0<<FPAC1)|(0<<FPF1) | (0<<WGM11)|(0<<WGM10)
outr TCCR1D, r16
.elif MULTIFAN_FANS == 2
ldi r16, (1<<COM1A1)|(0<<COM1A0) | (1<<COM1B1)|(0<<COM1B0) | (1<<PWM1A) | (1<<PWM1B)
outr TCCR1A, r16
ldi r16, (1<<COM1A1S)|(0<<COM1A0S) | (1<<COM1B1S)|(0<<COM1B0S) | (0<<COM1D1)|(0<<COM1D0) | (0<<PWM1D)
outr TCCR1C, r16
ldi r16, (0<<FPIE1)|(0<<FPEN1)|(0<<FPNC1)|(0<<FPES1)(0<<FPAC1)|(0<<FPF1) | (0<<WGM11)|(0<<WGM10)
outr TCCR1D, r16
.elif MULTIFAN_FANS == 3
ldi r16, (1<<COM1A1) |(0<<COM1A0) | (1<<COM1B1) |(0<<COM1B0) | (1<<PWM1A) | (1<<PWM1B)
outr TCCR1A, r16
ldi r16, (1<<COM1A1S)|(0<<COM1A0S) | (1<<COM1B1S)|(0<<COM1B0S) | (1<<COM1D1)|(0<<COM1D0) | (1<<PWM1D)
outr TCCR1C, r16
ldi r16, (0<<FPIE1)|(0<<FPEN1)|(0<<FPNC1)|(0<<FPES1)|(0<<FPAC1)|(0<<FPF1) | (0<<WGM11)|(0<<WGM10)
outr TCCR1D, r16
.else
.error "Invalid number of fans (only 1-3 allowed)
.endif
; 1MHz/40: 25kHz
clr r17
ldi r16, 40-1
outr TC1H, r17
outr OCR1C, r16
; preset fans to 50 %
ldi r16, 20
outr TC1H, r17
outr OCR1A, r16
.if MULTIFAN_FANS > 1
outr TC1H, r17
outr OCR1B, r16
.endif
.if MULTIFAN_FANS > 2
outr TC1H, r17
outr OCR1D, r16
.endif
ret
; @end
#endif