Saturday, January 7, 2017

VU3XVR Software Defined Radio SDR Using Si5351 DDS VFO and ATMega8 Microcontroller

This is a demonstration on Software Defined Radio Using Si5351 DDS VFO. Original designer is Burkard Kainka, Author of Elektor  SDR-2007. I Just modified VFO by using my own Si5351 based VFO, For more details please chek my blog vu3xvr dot blogspot dot in

Specification :

Frequency Range : 3 to 30 MHz
Mode  : AM - SSB - CW - DRM
Hardware Used :  https://www.elektormagazine.de/magazine/elektor-201607/29066
Software Used : SDRadio by I2PHD available from this website http://digilander.libero.it/i2phd/sdradio/








Thursday, January 5, 2017

VU3XVR Si5351 DDS VFO to Use BITX40

Click here for the video demo of sending CW using BITX (real carrier !!! not modified tone)




Tuesday, August 2, 2016

A Simple ATTiny13 Keyer / Paddle Keyer / ElectronicKeyer

;************************************************************************************

;            A Simple ATTiny13 Keyer / Electronic Keyer                                *

; Program by : Ram Sankar.V | callsign : VU3XVR | ramvsankar @ g m a i l . c o m    *
; Version 1.0, | Date : 02-Aug-2016                                                    *
; www.vu3xvr.blogspot.com                                                            *
; Compiled using AVR Studio 4.19                                                    *
; Microcontroller : ATMEL ATTiny13, clock 9.6MHZ , internally divide by 8            *      


;                                                                                    *

; Features:                                                                            *

;    Selectable IAMBIC Mode A. B or Snglehandle key                                    *
;    Adjustable WPM using potentiometer (fixed to 5,10,15,20,25,30,35,40 wpm)        *
;    Twin-T Sine wave tone oscillator, a very pleasant tone for your ear                *
;    Very small code size, 216 bytes                                                    *
;    Easy to customize or automate for beacons                                        *  
;    On board LED indicatior of dit & dah                                            *
;                                                                                    *

; Operation:                                                                        *

;    By default its selected with IAMBIC B mode                                        *
;    To enable IAMBIC A mode, keep press DAH key while power ON                         *
;    To enable single handle key mode, Connect the key to DIT pin                    *
;        and keep press DIT key while power ON                                         *
;    To change WPM, adjust the VR1, and restart to changes take effect.                *
;    To connect it to rig plug in streo pin into headphone jack (written "To Rig")    *
;        Now the on board sine wave tone oscillator is disabled.                        *
;                                                                                    *
;************************************************************************************

;                       Iambic mode B     Paddle Keyer                                *

; while we're generating a dit or dah, we have to watch for the                     *
; opposite paddle to get closed and then remember it so we can immediately send its    *
; element after the current one finishes. Ideally, we would be checking the other    *
; paddle contact after every delaymillis subroutine.                                 *
; Credits : Rich Helneck, AC7MA, author of "A Simple PICAXE Keyer"                    *
;************************************************************************************                                                        *
; This program is free software. you can redistribute it and/or                        *
; modify it under the terms of the GNU General Public License.                        *
; This program is distributed in the hope that it will be useful,                    *
; but WITHOUT ANY WARRANTY                                                            *

Schematics :  

https://drive.google.com/open?id=0B-XdS52xCN8wNVc5SE54dWZITDg

Complete source code, schematics can be download from here

;************************************************************************************
;                 ____________                                                        *
;            1 /            |8                                                        *
;   RESET o--|            |--o VCC                                                    *
;                  2|     AT      |7                                                        *
;    -PB3   o--|             |--o PB2 --->> ADC1 / WPM potentiometer                    *
;                  3|    tiny     |6                                                        *
; OUT PB4 o--|             |--o PB1 --->> DAH                                        *
;                  4|     13      |5                                                        *
;     GND o--|             |--o PB0 --->> DIT                                        *
;                    |_____________|                                                        *
;                                                                                    *
;************************************************************************************
.nolist
.include "tn13def.inc"     ;(ATTiny13 definitions)
.list

.def    A             = R16 ; General purpose accumulator
.def    counter         = R17    ; Used for delayMillis counter
.def    wpm            = R18    ; Words per minute value read from ADC
.def    flagRegister    = R19    ; register reserved for latchflag compare

.equ    ditPin        = PB0
.equ    dahPin        = PB1
.equ    outPin        = PB4  
.equ    ledPin        = PB3

.equ    ditLatch        = 0    ; BIT 0 of register flagRegister used for dit key
.equ    dahLatch        = 1    ; BIT 1 of register flagRegister used for dah key

.equ    preloadTimer    = 4 ; prescale 4, @1.2MHz, = 1.05 milli Secs

.ORG 0000
    RJMP ON_RESET ;Reset vector
.ORG 0006
    RJMP TIM0_COMPA ; Timer compare match interrupt handler

;********************************************************************
;             TIMER OVER-FLOW INTERUPT ROUTINE                        *
;********************************************************************
; Clock Speed = 9.6MHz ; internaly divide by 8 ; Prescaler = 256    *
; 9.6MHz /8/ 256 = 4687.5Hz = 0.00021 Secs pulse                    *
; Preloader = 4 ; counting from 0-4 = 5 counts                        *
; 0.00021 * 5 = 0.00105secs  which is approx equal to 1 milli Sec    *
;********************************************************************
TIM0_COMPA:
    INC    counter    ; this counter is used in delaymillis
RETI

;************************************************************
;        Microcontroller Initiation Procedure                *
;************************************************************
ON_RESET:
    LDI    A, (1<<outPin) | (1<<ledPin)
    OUT    DDRB, A    ; set PB3, PB4 as output
    LDI    A, (1<<ditPin) | (1<<dahPin)
    OUT    PORTB, A    ; set internal pullup
    LDI    A, (1<<COM0A0) | (1<<WGM01)    ; set CTC Mode
    OUT    TCCR0A, A
    LDI    A, (1<<CS02)
    OUT    TCCR0B,A     ; Set prescaler to 1/256
    LDI    A, (1<<OCIE0A)
    OUT    TIMSK0, A    ; Enable timer compare match interrupt
    LDI    A,preloadTimer
    OUT    OCR0A, A    ; load the compare register = 4
    SEI     ; Enable interrupts globally
    LDI    A,(1<<ADEN) | (1<<ADPS1) | (1<<ADPS0)
    OUT    ADCSRA, A    ; ADC enable, ADC prescaler div factor 8 for better stability
    LDI    A, (0<<REFS0) | (1<<ADLAR) | (1<<MUX0)
    OUT    ADMUX, A     ; AVcc, left adjust( we need just ADCH only), ADC1
    SBI    ADCSRA, ADSC ; start ADC conversion
keep_polling:
    SBIS    ADCSRA, ADIF ; look for ADC conversion finish flag ADIF
    RJMP    keep_polling  
    RCALL map        ; wpm to millis conversion loop. This determines the speed

    SBR    flagRegister, (1<<ditLatch) | (1<<dahLatch)
    RCALL delayMillis

    SBIS    PINB, dahPin    ; key not pressed
    RCALL iambicModeA
    SBIS    PINB, ditPin    ; key not pressed
    RCALL singleHandleMode
    RCALL    iambicModeB

;************************************************************
;*        Main loop starts here                                *
;************************************************************
iambicModeB:
    SBIS    PINB, ditPin    ; check if dit key is pressed
    RCALL dit        ; call dit routine
    SBRS    flagRegister, dahLatch    ; check if dit  key is pressed during dah key
    RJMP    dahLatchLoop

    SBIS    PINB, dahPin    ; check if dah key is pressed
    RCALL dah        ; call dah routine
    SBRS    flagRegister, ditLatch    ; check if dah key pressed during dit key
    RJMP    ditLatchLoop
    RJMP iambicModeB

ditLatchLoop:
    SBR    flagRegister, (0<<ditLatch)
    RCALL dit
    RJMP iambicModeB

dahLatchLoop:
    SBR    flagRegister, (0<<dahLatch)
    RCALL dah
    RJMP iambicModeB

iambicModeA:
    SBIS    PINB, ditPin    ; check if dit key is pressed
    RCALL dit        ; call dit routine

    SBIS    PINB, dahPin    ; check if dah key is pressed
    RCALL dah        ; call dah routine

    RJMP iambicModeA

singleHandleMode:
    SBIS    PINB, ditPin    ; check if dit key is pressed
    RJMP    outputON
    SBIC    PINB, ditPin    ; check if dit key is pressed
    RJMP    outputOFF
    RJMP singleHandleMode

outputON:
    SBI    PORTB, outPin         ;
    SBI    PORTB, ledPin
    RJMP singleHandleMode

outputOFF:  
    CBI    PORTB, outPin
    CBI    PORTB, ledPin
    RJMP singleHandleMode


;************************ Main Loop ENDS HERE **************

;************************************************************
;*        DIT Subroutine                                        *
;************************************************************
dit:
    SBI    PORTB, outPin
    SBI    PORTB, ledPin
    RCALL delayMillis
    SBIS    PINB, dahPin ; skip next If dah key is NOT pressed
    SBR    flagRegister, (1<<dahLatch) ; read this if dah key pressed
    CBI     PORTB, outPin
    CBI    PORTB, ledPin
    RCALL delayMillis
    RET

;************************************************************
;*        DIT Subroutine                                        *
;************************************************************
dah:
    SBI    PORTB, outPin
    SBI    PORTB, ledPin
    RCALL delayMillis
    SBIS    PINB, ditPin     ; skip next If dit key is NOT pressed
    SBR    flagRegister, (1<<ditLatch) ; read this if dit key pressed
    RCALL delayMillis
    SBIS    PINB, ditPin
    SBR    flagRegister, (1<<ditLatch)
    RCALL delayMillis
    SBIS    PINB, ditPin
    SBR    flagRegister, (1<<ditLatch)
    CBI     PORTB, outPin
    CBI    PORTB, ledPin
    RCALL delayMillis
    RET

;************************************************************
;*        Delay Millis Subroutine                                *
;************************************************************
delayMillis:
    LDI    counter, 0
millisStart:
    CP    counter, wpm    ; check counter value with wpm value
    BRNE    millisStart        ; do until counter = wpm
    RET

;************************************************************
;*    Subroutine for MAP value of ADC into WPM                  *
;************************************************************
map:
    IN    A, ADCH    ; load ADC value into temp register A

    LDI    wpm, 30    ; re-assign wpm 30, 1200ms/40wpm = 30ms
    CPI    A, 10     ; check with ADC value
    BRLO    mapEnd

    LDI    wpm, 34    ; delaymillis = 1200ms/35wpm = 34ms
    CPI    A, 35
    BRLO    mapEnd

    LDI    wpm, 40    ; delaymillis = 1200ms/30wpm = 40ms
    CPI    A, 75
    BRLO    mapEnd

    LDI    wpm, 48    ; delaymillis = 1200ms/25wpm = 48ms
    CPI    A, 105
    BRLO    mapEnd

    LDI    wpm, 60    ; delaymillis = 1200ms/20wpm = 60ms
    CPI    A, 145
    BRLO    mapEnd

    LDI    wpm, 80    ; delaymillis = 1200ms/15wpm = 80ms
    CPI    A, 175
    BRLO     mapEnd

    LDI    wpm, 120    ; delaymillis = 1200ms/10wpm = 120ms
    CPI    A, 185
    BRLO    mapEnd

    LDI    wpm, 240     ; delaymillis = 1200ms/5wpm = 240ms
mapEnd:  
    RET

Friday, March 11, 2016

Washing Machine Controller Using AVR ATMEL ATmega8 Microcontroller Using Assembly Language (NOT Arduino)

Washing Machine Controller Using  AVR ATMEL ATmega8 Microcontroller Using Assembly Language (NOT Arduino)

A brief history about why I decided to make my own controller :-

I have a washing machine, with control board problems. I did changed the control board from OEM 2 times, each time it cost Rs.3900 + 300 for service charges. So, I spent Rs.8200/- since 2008. Actual machine cost is Rs.12,000/- Now in year 2016 the same control board SMPS failure . The washing machine model is Whirlpool Splash 55. Seems the designer of the board is a beer addict during design ! Very sad that not only for me for those having this model is having the same issue!!!. But this time I'm decided to design my own control board with my experiences. My control board costs less than Rs.500 and all of the components are from my junk box. not spent a paisa but time.

 

 Basic operation sequence of a washing machine :-

There is just 4 relays to be controlled in a regular sequence, not a rocket science either. The basic operation sequence is like this
  1. Water filling (at actual time)
  2. Agitronic soak (15mins)
  3. First wash    (15mins)
  4. First drain (4mins)
  5. Second wash or call it as first rinse (10mins)
  6. Second drain (4mins)
  7. Spin (5mins)
  8. Buzzer chim (15sec)
There are lot of  information available about the time taken for each operation from several washing machine manufacturer. But i referred Whirlpools manual and it recommends as below
Program Time / Cycle time for each mode
                     All values in minutes
                 Agitronic    Normal    Speedy    R+S    S only
 Soak            20            -                  -         -            -
 Wash           20          20                 9          -            -  
 Rinse           16          16               16         8             -
 Spin              8           8                  6         6             6
And there is a very nice recommendation from Bureau Of Energy Efficiency
Energy Labeling Scheme For Washing Machines by Bureau Of Energy Efficiency
https://www.beestarlabel.com/Content/Files/Schedule12-WM.pdf

From the above given recommendation i choose  following timings
   soakTime     = 15 mins
   washTime     = 15 mins (For one wash cycle)
   rinseTime     =  8 mins
   spinTime     =  5 mins
At this point of time, there is no selector / program selector available on the controller. But it's on my wish list.

Why decided to use AVR Assembly language and not to use Arduino C

For this washing machine controller i decided to use assembly language, and it'll be my first AVR assembly language project. I'm using AVR Studio 4.19 and ProgISP to compile and upload.
I personally do not recommend higher version of AVR Studio. V 4.19 is lite and there is not much improvement on ASM compiler between V4.19 and V6.2. (Both IDE's assembler version is 2.1.xx)
Since 2006 i'm using Arduino, now i'm feeling limited with arduino resources when there is really a lot of things can be done with AVRs other line of products like ATtiny13, 2313, etc....Arduino will not help to develop codes for this. The size of HEX code for LedBlink in arduino is 890 bytes, the same LedBlink in assembly will be just 34 bytes !!!! Wow ahh.. Excess  856 bytes of code dumped inside my ATmega8 for nothing. When comes to industrial application the unwanted junk of code will generate lot of issues. That's why the Arduino's liquidCrystal library is not recommended by most of developers as it collide with interrupt cycle or millis() library.
Arduino is for kids and new comers. For real time application Arduino is not the right tool to write my code. Best to go for ASM Assembly language for AVR.

Hardware setup

I had my own ATmega8 break out board with 6 inputs and 4 outputs. I'm using this pcbs for most of my project. Here is the schematic diagram of controller.

Inputs

Water level pressure switch
Loading door open / close switch. (in my project, i'm not using this sensor)

Outputs

Buzzer
Motor forward
Motor reverse
Water inlet solenoid
Water drain traction motor
           ( In my model Whirlpool Splash 55, Its single motor for slow spin and fast spin, it's the drain traction motor change the gear wheel between slow and fast spin )
Wish list : heartbeat LED, sequence indication LEDs, start / pause button, EEPROM storage of electricity failure recovery, etc...

Here is my complete source code in ASM language  for ATMEL ATmega8 controlled Washing Machine
 AVR-ATMEL-ATmega8-Microcontroller-controllerd-washing-machine- Using-Assembly-Language.asm

Complete project file in zip file (asm, HEX, etc..)
AVR-ATMEL-ATmega8-Microcontroller-controllerd-washing-machine- Using-Assembly-Language

/************************************************************************************
* This program is to control WhirlPool Washing machine with 6 Kg capacity        *
* Using ATmega8 and 1MHz internal oscillator & 1/1024 timer0 prescaler            *
* Devloped by Ram Sankar Pillai    email: ramvsankar AT gmail dot com            *   
* Date started 10-Feb-2016                                        *
* Washine machine Model : Whirlpool, Splash 6Kg (Indian version)                *   
* timer over flow interrupt method                                    *
* trial and error method                                        *
* Definitely there will be a better way to do program                        *
* Any suggestions welcome                                        *
* delay in seconds and minutes should not exceed 60                        *
*    soakTime     = 15 mins                                        *
*    washTime     = 15 mins (For one wash cycle)                         *
*    rinseTime     =  8 mins                                        *
*    spinTime     =  5 mins                                        *            
*************************************************************************************
*                                                        *
* This program is free software. you can redistribute it and/or                *
* modify it under the terms of the GNU General Public License.                *
* This program is distributed in the hope that it will be useful,                *
* but WITHOUT ANY WARRANTY                                        *
*************************************************************************************
*/
.nolist
.include "m8def.inc" ;(include ATmega8 definitions)
.list

.def     A         =     R16    ;General purpose accumulators
.def    B        =    R17
.def    C        =    R18    ; to hold preloader value
.def    counter    =    R19
.def    seconds    =    R20
.def    minutes    =    R21
.def    oneTenthSec    =    R22
.def    targetSec    =    R23
.def    targetMin    =    R24
.def    targetOneTenthSec    =    R25


.equ    preloadTimer    =    165 ; Change this value according to oscillator error
.equ    pressureSensorPin    =    PC1   
.equ    inletValvePin    =    PB4
.equ    drainValvePin    =    PB3
.equ    drumFwdPin        =    PB2
.equ    drumRevPin        =    PB1
.equ    buzzerPin        =    PB0

.ORG 0x000    RJMP onReset ;RESET VECTOR
.ORG 0x009    RJMP timerOverFlow ; Timer0 Overflow Handler

;***********************************
; TIMER OVER-FLOW INTERUPT ROUTINE *
;***********************************
timerOverFlow:
    inc    counter
    LDI     C, preloadTimer     ;PRELOAD THE TIMER
    OUT     TCNT0, C
    RETI
;************************************************
;     Macro for ONE TENTH of A SECOND delay    *
;************************************************
.macro delayOneTenthSec
    ldi    targetOneTenthSec, @0
    ldi    counter, 0
oneTenthSecStart:
    ; @ 165 in TCNT0 and 1024 prescale and 1MHz osc
    cpi     counter, 1    ; 1 represents 1/10 second
    brlo    oneTenthSecStart
    ldi    counter, 0
    inc     oneTenthSec
    cp    oneTenthSec, targetOneTenthSec
    brne    oneTenthSecStart
    ldi     oneTenthSec, 0
.endmacro
;************************************
;     Macro for ONE SECOND delay    *
;************************************
.macro delaySecs
    ldi    targetSec, @0
    ldi    counter, 0
secStart:
    ; @ 165 in TCNT0 and 1024 prescale and 1MHz osc
    cpi     counter, 10    ; 10 represents 1 second
    brlo    secStart
    ldi    counter, 0
    inc     seconds
    cp    seconds, targetSec    ; check for 1 minute
    brne    secStart
    ldi     seconds, 0
.endmacro
;************************************
;     Macro for ONE MINUTE delay    *
;************************************
.macro delayMins
    ldi    targetMin, @0
    ldi    minutes, 0
minStart:
    delaySecs(60);    set 60 for one minute
    inc     minutes
    cp    minutes, targetMin    ; check for 1 minute
    brne    minStart
    ldi     minutes, 0
.endmacro

;************************************************************
;        Microcontroller Initiation Procedure        *
;************************************************************
onReset:
    ldi    A, HIGH(RAMEND)
    out    SPH, A
    ldi    A, LOW(RAMEND)
    out    SPL, A
    ldi     A,    0b00000101 ;SET PRESCALER TO /1024
    out     TCCR0, A         ;TIMER/COUNTER CONTROL REGISTER
    ldi     A,    0b0000_0001 ;ENABLE TIMER-OVERFLOW INTERUPT
    out     TIMSK, A
    ldi     C,preloadTimer     ;PRELOAD THE TIMER
    out     TCNT0, C
    ldi    seconds, 0
    ldi    counter, 0
    ldi    oneTenthSec, 0
    sei                 ;ENABLE INTERUPTS GLOBALLY

    cbi    DDRC, pressureSensorPin
    sbi    PORTC, pressureSensorPin
    ldi    A, 0b0011_1111    ; PB1,2,3,4,5 as OUTPUTs
    out    DDRB,    A
    ldi    A, 0x00        ; All outputs LOW
    out    PORTB, A
   
;************************************************************
;        Main Program STARTS Here                *
;************************************************************
   
mainLoop:
    sbi    PORTB, buzzerPin    ; Power ON indication
    delayOneTenthSec(7);
    cbi    PORTB, buzzerPin
    rcall waterFilling    ; Water filling subroutine
     rcall agitronicSoak    ; Soak subroutine
      rcall firstWashing    ; 1st wash subroutine, with 3 mins delay
      rcall draining        ; 1st drain water subroutine
      rcall waterFilling    ; Water filling subroutine
    rcall secondWashing    ; 2nd wash subroutine, with 1 min delay
      rcall draining        ; 2nd drain water subroutine
      rcall spinning        ; spin subroutine   
    rcall    chiming        ; Chiming subroutine   
endlessLoop:
    RJMP endlessLoop
   
;************************************************************
;            Main Program ENDS Here                *
;************************************************************

;***************** Water Filling sequence *******************
waterFilling:
    cbi    PORTB, drainValvePin
    cbi    PORTB, drumFwdPin
    cbi    PORTB, drumRevPin
    sbi    PORTB, inletValvePin
waterCheck:
    sbic    PINC,  pressureSensorPin
    rjmp waterCheck
    cbi    PORTB, inletValvePin
    RET

;************ Agitronic Soak sequence starts here ***************
agitronicSoak:
    cbi    PORTB, drainValvePin
    cbi    PORTB, drumFwdPin
    cbi    PORTB, drumRevPin
    cbi    PORTB, inletValvePin
    ldi    A, 0
soakAgain:
    cpi    A, 5    ; change this to increase or decrease no cycles
    breq    agitronicEnd
    rcall pulsatingSpin
    delayMins(3) ; changed here to reduce cycle time
    inc A
    rjmp soakAgain
agitronicEnd:
    RET

;*********** delay sequence during agitronic soak ***************
pulsatingSpin:
    sbi    PORTB, drumFwdPin    ; drum rotates FWD for 2 secs
    delaySecs(2)       
    cbi    PORTB, drumFwdPin
    delaySecs(1)        ; Dwell for 1 sec
    sbi    PORTB, drumRevPin    ; and rotate REV for 2 secs
    delaySecs(2)
    cbi    PORTB, drumRevPin
    delaySecs(1)        ; Dwell for 1 sec
    RET

;*********** Washing sequence starts here ***************
firstWashing:
    cbi    PORTB, drainValvePin
    cbi    PORTB, drumFwdPin
    cbi    PORTB, drumRevPin
    cbi    PORTB, inletValvePin
    ldi    A, 0
washAgain:
    cpi    A, 5         ;change this to increase or decrease no cycles
    breq    washingEnd
    rcall    pulsatingWashSlow
    rcall    pulsatingWashFast
    delayMins(3)    ; changed here to reduce cycle time
    inc A   
    rjmp    washAgain
washingEnd:           
    RET
;*********** Washing sequence starts here ***************
secondWashing:
    cbi    PORTB, drainValvePin
    cbi    PORTB, drumFwdPin
    cbi    PORTB, drumRevPin
    cbi    PORTB, inletValvePin
    ldi    A, 0
secondWashAgain:
    cpi    A, 5         ;change this to increase or decrease no cycles
    breq    secondWashingEnd
    rcall    pulsatingWashSlow
    rcall    pulsatingWashFast
    delayMins(1)    ; changed here to reduce cycle time
    inc A   
    rjmp    secondWashAgain
secondWashingEnd:           
    RET
;*********** Pulsating wash SLOW subroutine ***************
pulsatingWashSlow:    ; do this for 10 times at an interval of 3 mins
    ldi    B, 0
slowAgain:
    cpi    B, 12    ;  change this to increase or decrease no cycles
    breq    pulsatingWashSlowEnd
    sbi    PORTB, drumFwdPin
    delaySecs(2)
    cbi    PORTB, drumFwdPin
    delayOneTenthSec(7)
    sbi    PORTB, drumRevPin
    delaySecs(2)
    cbi    PORTB, drumRevPin
    delayOneTenthSec(7)
    inc     B
    rjmp    slowAgain
pulsatingWashSlowEnd:   
    RET

;*********** Pulsating wash FAST subroutine ***************
pulsatingWashFast:    ; do this for 12 times at an interval of 3 mins
    ldi    B, 0
fastAgain:
    cpi    B, 12        ; change this to increase or decrease no cycles
    breq    pulsatingWashFastEnd
    sbi    PORTB, drumFwdPin
    delaySecs(1)
    cbi    PORTB, drumFwdPin
    delayOneTenthSec(3)
    sbi    PORTB, drumRevPin
    delaySecs(1)
    cbi    PORTB, drumRevPin
    delayOneTenthSec(3)
    inc     B
    rjmp    fastAgain
pulsatingWashFastEnd:   
    RET

;*********** Drain sequence subroutine ***************
draining:        ; 1st drain water
    cbi    PORTB, drumFwdPin
    cbi    PORTB, drumRevPin
    cbi    PORTB, inletValvePin
    sbi    PORTB, drainValvePin ; drain valve ON
    delaySecs(10)    ; delay for tracton motor to pull drain valve completely and gear engagement
drainCheck:
    sbic    PINC,  pressureSensorPin ; Check for water level if low then delay another 4mins
    rjmp drainCheck   
    delayMins(4)    ; 4 mins delay time to drain all water
    RET

;*********** Spin sequence subroutine ***************
spinning:        ; spin
    cbi    PORTB, drumFwdPin
    cbi    PORTB, drumRevPin
    cbi    PORTB, inletValvePin
    sbi    PORTB, drainValvePin ; drain valve ON
    delaySecs(10)    ; delay for tracton motor to pull drain valve completely
    sbi    PORTB, drumFwdPin ; small interrupt rotation for drum weight balancing
    delaySecs(8)
    cbi    PORTB, drumFwdPin
    delaySecs(4)        ; small interrupt rotation for drum weight balancing
    sbi    PORTB, drumFwdPin
    delaySecs(8)
    cbi    PORTB, drumFwdPin
    delaySecs(4)
    sbi    PORTB, drumFwdPin
    delayMins(5)    ; change here to reduce cycle time
    cbi    PORTB, drumFwdPin
    delaySecs(10)
    cbi    PORTB, drainValvePin
    RET
;*********** Chiming sequence subroutine ***************
chiming:    ; 20 times beep sound indication of cycle end
    ldi    A, 0
chimAgain:
    cpi    A, 20
    breq    chimingEnd
    sbi    PORTB, buzzerPin
    delayOneTenthSec(3)
    cbi    PORTB, buzzerPin
    delayOneTenthSec(1)
    inc     A
    rjmp chimAgain
chimingEnd:
    RET