; *************************************************************************** ; 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 .ifdef MULTIFAN_SETUPIRQ_FN bigcall MULTIFAN_SETUPIRQ_FN .endif sec ret ; @end ; --------------------------------------------------------------------------- ; @routine MultiFan_OnEverySecond @global ; ; @clobbers r16, r17, r18, r19, r24, Y MultiFan_OnEverySecond: 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 adiw yh:yl, MULTIFAN_FAN_SIZE dec r24 brne MultiFan_OnEverySecond_loop pop r15 ret ; @end ; --------------------------------------------------------------------------- ; @routine MultiFan_CheckPort @global ; ; @param r16 new port value (e.g. PINB) ; @param r18 port change value (0 in a bit pos means: bit flipped to 0) ; @clobbers r24, r25, Y MultiFan_CheckPort: 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 ; (r24, r25) .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, r25 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< 1 outr TC1H, r17 outr OCR1B, r16 .endif .if MULTIFAN_FANS > 2 outr TC1H, r17 outr OCR1D, r16 .endif ret ; @end ; --------------------------------------------------------------------------- ; @routine multiFanPinChangeIsr @global @isr ; multiFanPinChangeIsr: push r15 in r15, SREG push r16 in r16, MULTIFAN_TACHO_INPUT push r18 lds r18, multiFanPrevPortVal sts multiFanPrevPortVal, r16 eor r18, r16 ; r18: changes in bits=1 com r18 ; r18: changes in bits=0 or r18, r16 ; r18: 0=bit flipped to 0 push r24 push r25 push yl push yh rcall MultiFan_CheckPort ; (r24, r25, Y) pop yh pop yl pop r25 pop r24 pop r18 pop r16 out SREG, r15 pop r15 reti ; @end #endif