; ATmega328.107.asm Jan 02, 2021
;basic source file to make an Si5351A chip "sing" and
; using 16 bits as a tune register with full LCD frequency display
; with optical encoder tested
; and with push putton tested - change tune rate
; and with 4x4 keypad

; cd ~/Desktop/freddieneil.com/kitsandparts.com/tutorials/ATmega328/107/

; avra ATmega328.107.asm

; avrdude -P usb -p m328p -c avrispv2 -U ATmega328.107.hex
; avrdude -P usb -p m328p -c avrispv2 -U flash:w:ATmega328.107.hex -U eeprom:w:ATmega328.107.eep.hex
; avrdude -P usb -p m328p -c avrispv2 -U lfuse:w:0x62:m  -U hfuse:w:0xD9:m  -U efuse:w:0xFF:m

; define ATmega version
.equ  ATmega = 1   ; Declares the symbol ATmega328
;.equ  ATmega = 2   ; Declares the symbol ATmega644

;.equ  encoder_type  = 0 ; Declare no encoder in use
;.equ  encoder_type  = 1 ; Declare encoder to mechanical type - uses EXT_INT1
.equ  encoder_type  = 2 ; Declare encoder to optical type - uses EXT_INT0 & EXT_INT1

.nolist

; conditional assembly: .define .undef .if .ifdef .ifndef .else .elif .endif .error .warning .message 
.if    ATmega  ==  1
  .include  "../../include/m328Pdef.inc"
  .message  "m328Pdef.inc selected"
.elif  ATmega  ==  2
  .include  "../../include/m644Adef.inc"
  .message  "m644Adef.inc selected"
.else
  .error  "include file not specified"
.endif

.list

.equ  lcd_drs   =	portc0    ; data line for 74HC164
.equ  lcd_e	    =	portc2    ; lcd Enable
.equ  lcd_cp	  =	portc1    ; clock line for 74HC164
.equ  tune_bit  = portc3
.equ  sda_bit   = portc4
.equ  scl_bit   = portc5
.equ  led_bit   = portd0
.equ  button    = portd1
.equ  output_a  = portd2
.equ  output_b  = portd3
.equ  si5351_i2cAddr  = 0xC0  ; si5351A address = 0x60 but needs to shift left 1 bit


; Define register names
; r0,r1 are used by Z-register and MUL

.def  temp0         = r0  ; used by MUL & SPM instructions and Z register pointer
.def  last_keypad   = r1  ; used by MUL & SPM instructions

.def  temp29  = r2  ; r29_VCOa P1[15:8]
.def  temp30  = r3  ; r30_VCOa P1[7:0]
.def  temp31  = r4  ; r31_VCOa P3[19:16]:P2[19:16]
.def  temp32  = r5  ; r32_VCOa P2[15:8]
.def  temp33  = r6  ; r33_VCOa P2[7:0]
.def  temp34  = r7  ; extra byte for finer inc/dec resolution

.def  step0   = r8  ; high byte add/sub for inc/dec
.def  step1   = r9  ; next byte add/sub for inc/dec
.def  step2   = r10 ; next byte add/sub for inc/dec
.def  step3   = r11 ; next byte add/sub for inc/dec

.def  BCDhi   = r12 ; store for 5351 VFO frequency
.def  BCDlo   = r13

.def  tune_rate   = r14
.def  last_tune_rate   = r15

.def  temp1             = r16
.def  temp2             = r17
.def  temp3             = r18
.def  temp4             = r19
.def  delay_1ms         = r20
.def  i2cData           = r21
.def	si5351_regNum     = r22
.def	si5351_regVal     = r23

.def  current_encoder_lo = r24
.def  current_encoder_hi = r25

.def  last_encoder_lo    = r26
.def  last_encoder_hi    = r27

; r24-r31 used as 16 bit register pointers Y ,Z
            
.dseg
.org SRAM_START
cursor_number_sram: .byte 1

.eseg
.org 0

.dseg
.org SRAM_START

.cseg
.org 0
  jmp RESET
  jmp EXT_INT0  ; External Interrupt Request 0
  jmp EXT_INT1  ; External Interrupt Request 1
  jmp NOT_USED	; Pin Change Interrupt Request 0
  jmp NOT_USED	; Pin Change Interrupt Request 1
  jmp PCI2	; Pin Change Interrupt Request 2
  jmp NOT_USED	; Watchdog Time-out Interrupt
  jmp NOT_USED	; Timer/Counter2 Compare Match A
  jmp NOT_USED	; Timer/Counter2 Compare Match A
  jmp NOT_USED	; Timer/Counter2 Overflow
  jmp NOT_USED	; Timer/Counter1 Capture Event
  jmp NOT_USED	; Timer/Counter1 Compare Match A
  jmp NOT_USED	; Timer/Counter1 Compare Match B
  jmp NOT_USED	; Timer/Counter1 Overflow
  jmp NOT_USED	; TimerCounter0 Compare Match A
  jmp NOT_USED	; TimerCounter0 Compare Match B
  jmp TIMER0_OVF  ; Timer/Counter0 Overflow
  jmp NOT_USED	; SPI Serial Transfer Complete
  jmp NOT_USED	; USART Rx Complete
  jmp NOT_USED	; USART, Data Register Empty
  jmp NOT_USED	; USART Tx Complete
  jmp NOT_USED	; ADC Conversion Complete
  jmp NOT_USED	; EEPROM Ready
  jmp NOT_USED	; ;Analog Comparator
  jmp NOT_USED	; Two-wire Serial Interface
  jmp NOT_USED	; Store Program Memory Read
NOT_USED: 
  reti

; 100 Hz sets at 7 MHz, A=35, D=100, XTAL=20 MHz
REGS2933_FL: .db $0F,$80,$F0,$00,$00,$00
CR4445FL: .db $00,$30
STEP_FL:  .db $01,$06,$24,$BC,$6A,0

; 100 Hz sets at 7 MHz, A=28, D=100, XTAL=25 MHz
;REGS2933_FL: .db $0C,$00,$F0,$00,$00,$00
;CR4445FL: .db $00,$30
;STEP_FL:  .db $00,$D1,$B6,$E9,$79,0

RESET:
	ldi		temp1,  low(RAMEND)
	out   SPL,    temp1			    ; Set stack pointer to last internal RAM location
	ldi		temp1,  high(RAMEND)
	out		SPH,    temp1

;init keypad
	ldi		temp1,$00
	out		DDRB,temp1		;make PA0-7 pins all inputs
	ldi		temp1,$FF			;set pullup resistors on PA0-7
	out		PORTB,temp1 	;
	
;led_init
  ldi   temp1, 0b00000001 ; all PORTD is INPUT except portd0 which is the led_bit
  out   DDRD, temp1       ; all of portd is input	except PD0

;Pin Change Interrupt Request 2 init
  ldi   temp1,  0b00000100  ;  all PORTD is INPUT except portd0 which is the led_bit
  sts   PCICR, temp1        ; Any change on any enabled PCINT[23:16] pin will cause an interrupt
  ldi   temp1,  0b00000010  ; PCINT17 - portd1 - enable bit
  sts   PCMSK2, temp1       ; Pin Change Mask Register 2
  ldi   temp1,  $31         ; initial tune_rate
  mov   tune_rate,  temp1
  dec   temp1
  mov   last_tune_rate,  temp1

i2c_init:
	cbi   DDRC,   sda_bit ; make sda_bit an input - release Si5351A sda_bit i2c sda line - float high via pull up resistor
	cbi   DDRC,   scl_bit ; make scl_bit an input - release Si5351A scl_bit i2c scl line - float high via pull up resistor
	cbi   PORTC,  sda_bit ; set sda_bit to logic "0" (when DDRC(sda_bit) changes to an output)
	cbi   PORTC,  scl_bit ; set scl_bit to logic "0" (when DDRC(scl_bit) changes to an output)

; set System Clock Prescaler - system default clock is 8.0 MHz - can be changed to 6.4 MHz by Fuse Low Byte
  ldi   temp1,  0b10000000  ; init for prescaler change
  sts   CLKPR,  temp1       ; else out instruction to CLKPR will not take effect
; ldi   temp1,  0b00000000  ; prescaler / 1 - ( 8.0 MHz / 1) = 8.000 MHz = 125 nanoseconds
; ldi   temp1,  0b00000001  ; prescaler / 2 - ( 8.0 MHz / 2) = 4.000 MHz = 250 nanoseconds
; ldi   temp1,  0b00000010  ; prescaler / 4 - ( 8.0 MHz / 4) = 2.000 MHz = 500 nanoseconds
  ldi   temp1,  0b00000011  ; prescaler / 8 - factory default; set by Fuse Low Byte ( 8.0 MHz / 8) = 1.000 MHz = 1.000 microsecond
; ldi   temp1,  0b00000100  ; prescaler / 16 - ( 8.0 MHz / 16) = 500 KHz = 2.000 microseconds
; the setting below disable reprogramming the uP - must use "-B 500" with AVRDUDE to fix
; ldi   temp1,  0b00000101  ; prescaler / 32 - ( 8.0 MHz / 32) = 250 KHz = 4.000 microseconds
; ldi   temp1,  0b00000110  ; prescaler / 64 - ( 8.0 MHz / 64) = 125 KHz = 8.000 microseconds
; ldi   temp1,  0b00000111  ; prescaler / 128 - ( 8.0 MHz / 128) = 62.5 KHz = 16.000 microseconds
; ldi   temp1,  0b00001000  ; prescaler / 256 - ( 8.0 MHz / 256) = 31250 Hz = 32.000 microseconds
  sts   CLKPR,  temp1

; with prescaler set to 8:
; ldi   temp1,  0b00000001  ; no prescaling
  ldi   temp1,  0b00000010  ; CLK 1/8 from prescaler (6.667 uS fo ATtiny13, 8.000 uS for ATtinyx5)
; ldi   temp1,  0b00000011  ; CLK 1/64
; ldi   temp1,  0b00000100  ; CLK 1/256
; ldi   temp1,  0b00000101  ; CLK 1/1024
  out   TCCR0B, temp1	      ; set timer prescaler
  
  ldi   temp1,  1           ; overflow interrupt enable bit
  sts   TIMSK0, temp1       ; enable TIMER0 overflow interrupts

	sbi		DDRC,lcd_e		; make lcd_e an output
	sbi		DDRC,lcd_drs  ; make lcd_drs an output
	sbi		DDRC,lcd_cp   ; make lcd_cp an output

	cbi		PORTC,lcd_e		;set lcd_e low
 	cbi		PORTC,lcd_cp	;set lcd_cp low
 	
  ldi ZH, high(2*REGS2933_FL)
  ldi ZL, low(2*REGS2933_FL)
  lpm		temp29,Z+
  lpm		temp30,Z+
  lpm		temp31,Z+
  lpm		temp32,Z+
  lpm		temp33,Z+
  lpm		temp34,Z+ ; temp34 is a dummy for better INC/DEC resolution

  ldi ZH, high(2*STEP_FL)
  ldi ZL, low(2*STEP_FL)
  lpm		step0,Z+
  lpm		step1,Z+
  lpm		step2,Z+
  lpm		step3,Z+


.if encoder_type  ==  1  
	ldi		temp1,0b00000100	; Any change of INT1 generate an interrupt request
	sts		EICRA,  temp1
	sbi		EIMSK,1               ;enable INT1 interrupts
.elif encoder_type  ==  2
	ldi		temp1,0b00000101	; Any change of INT0 & INT1 generate an interrupt request
	sts		EICRA,  temp1
	sbi		EIMSK,0 ;enable INT0 interrupts
  sbi		EIMSK,1 ;enable INT1 interrupts
.endif	

	ldi		current_encoder_hi, $00               ;set current_encoder_hi interrupt counts
	ldi		current_encoder_lo, $00               ;set current_encoder_lo interrupt counts
	mov		last_encoder_hi, current_encoder_hi   ;set last_encoder_hi interrupt counts
	mov		last_encoder_lo, current_encoder_lo   ;set last_encoder_lo interrupt counts

	clr   BCDhi
	clr   BCDlo

; next six lines are for testing only
	;ldi   temp1,$09
	;mov   BCDhi, temp1
	;ldi   temp1,$88
	;mov   BCDlo, temp1
	ldi   temp1,  'E'
	mov   last_keypad,  temp1
	
	sei   ;global all interrupt enable

  rcall Si5351  ; init Si5351 chip to base frequency

  rcall lcd_init
  
  rjmp  main

MSG_1:
;    1234567890123456789012345678901234567890
.db "328 ASM Tutorial",0,0

main:   ; start of main program

  ldi ZH,high(2*MSG_1)
  ldi ZL,low(2*MSG_1)
  ldi temp2,$80				;display on line 1
  rcall LCDCMD
  rcall DISPLAY_LINE

  rcall	SHOW_ENCODER
  rcall	SHOW_BCD

main10:
  cp  last_tune_rate, tune_rate
  breq    main15
  mov     last_tune_rate, tune_rate
  rcall	SHOW_RATE
main15:  
  rcall   get_keypad ; return character in temp1
  cp      last_keypad,  temp1
  brne    main20
  rcall	  SHOW_KEYPAD
  
main20:
  cp  current_encoder_lo,last_encoder_lo
	cpc current_encoder_hi,last_encoder_hi
  breq main10   ; test to see if encoder has not changed
  
main22:
  brlo  DN_STEP ; test hi limit - see if current encoder is lower than last
  ldi temp3,  $B8 ; test hi limit ($B8 - 1 is max)
  ldi temp4,  $0B ; test hi limit $0B
  cp  current_encoder_lo, temp3; test hi limit
	cpc current_encoder_hi, temp4; test hi limit
	brlo  main23  ; current encoder is less than the hi limit
  mov current_encoder_lo,last_encoder_lo  ; yes current encoder is higher or = than the hi limit so restore current
  mov current_encoder_hi,last_encoder_hi  ; yes current encoder is higher or = than the hi limit so restore current
  rjmp  main20  ; start over

main23: ; lower than the limit so step up freq
  rcall STEP_UP
  adiw  last_encoder_lo,  1
  rcall INC_BCD
  rcall	SHOW_BCD
  rjmp  main28
  
DN_STEP:
  ldi temp3,  $00 ; test lo limit
  ldi temp4,  $00 ; test lo limit
  cp  current_encoder_lo, temp3 ; test lo limit
	cpc current_encoder_hi, temp4 ; test lo limit
	brsh  main25
  mov current_encoder_lo,last_encoder_lo  ; yes current encoder is lower than the lo limit so restore current
  mov current_encoder_hi,last_encoder_hi  ; yes current encoder is lower than the lo limit so restore current
  rjmp  main20  ; start over

main25:	
  rcall STEP_DN
  sbiw  last_encoder_lo, 1
  rcall DEC_BCD
  rcall	SHOW_BCD
  rjmp  main28

main28:
  cp  current_encoder_lo,last_encoder_lo
	cpc current_encoder_hi,last_encoder_hi
  brne  main22
  rcall UPDATE_2930313233
main29:
  rcall	SHOW_ENCODER  ; display current_encoder_lo at beginning of line 2
  rjmp main10

INC_BCD:
  inc   BCDlo
  mov   temp1,  BCDlo
  push  temp1
  andi  temp1,  $0F
  cpi   temp1,  $0A
  pop   temp1
  brne  INC_BCD_exit
  subi  temp1, $FA
  mov   BCDlo,  temp1 
  cpi   temp1,  $A0
  brne  INC_BCD_exit
  clr   BCDlo
  
  inc   BCDhi
  mov   temp1,  BCDhi
  push  temp1
  andi  temp1,  $0F
  cpi   temp1,  $0A
  pop   temp1
  brne  INC_BCD_exit  
  subi  temp1, $FA
  mov   BCDhi,  temp1  
INC_BCD_exit:
  ret
  
DEC_BCD:
  dec   BCDlo
  mov   temp1,  BCDlo
  push  temp1
  andi  temp1,  $0F
  cpi   temp1,  $0F
  pop   temp1
  brne  DEC_BCD_exit
  subi  temp1, $06
  mov   BCDlo,  temp1 
  cpi   temp1,  $F9
  brne  INC_BCD_exit
  subi  temp1,  $60
  mov   BCDlo,  temp1
  
  dec   BCDhi
  mov   temp1,  BCDhi
  push  temp1
  andi  temp1,  $0F
  cpi   temp1,  $0F
  pop   temp1
  brne  INC_BCD_exit  
  subi  temp1, $06
  mov   BCDhi,  temp1  
DEC_BCD_exit:
  ret

SHOW_BCD:
  ldi temp2,  $C5
  rcall LCDCMD
  ldi   temp1,  '7'
  rcall LCDCHR
  ldi   temp1,  ','
  rcall LCDCHR
  ldi temp2,  $30
  mov   temp1,  BCDhi
  andi  temp1,  $F0
  swap  temp1
  add   temp1,  temp2
  rcall LCDCHR
  mov   temp1,  BCDhi
  andi  temp1,  $0F
  add   temp1,  temp2
  rcall LCDCHR
  mov   temp1,  BCDlo
  andi  temp1,  $F0
  swap  temp1
  add   temp1,  temp2
  rcall LCDCHR
  ldi   temp1,  '.'
  rcall LCDCHR
  mov   temp1,  BCDlo
  andi  temp1,  $0F
  add   temp1,  temp2
  rcall LCDCHR
  ret

SHOW_RATE:
  push  temp1
  push  temp2
  ldi temp2,  $CD
  rcall LCDCMD
  mov   temp1,  tune_rate
  rcall LCDCHR
  pop   temp2
  pop   temp1
  ret

SHOW_KEYPAD:
  push  temp1
  push  temp2
  ldi temp2,  $CF
  rcall LCDCMD
  mov   temp1,  last_keypad
  rcall LCDCHR
  pop   temp2
  pop   temp1
  ret
  
  mov   temp1,  current_encoder_hi
  ldi   temp2,  $C0
  rcall	SHOW_HEX_BYTE  ; display current_encoder_hi at beginning of line 2
  mov   temp1,  current_encoder_lo
  ldi   temp2,  $C2
  rcall	SHOW_HEX_BYTE  ; display current_encoder_lo at beginning of line 2
  ret
    
SHOW_ENCODER:
  mov   temp1,  current_encoder_hi
  ldi   temp2,  $C0
  rcall	SHOW_HEX_BYTE  ; display current_encoder_hi at beginning of line 2
  mov   temp1,  current_encoder_lo
  ldi   temp2,  $C2
  rcall	SHOW_HEX_BYTE  ; display current_encoder_lo at beginning of line 2
  ret

;************************************
;* LCDCMD - Send a command to the LCD
;* temp2 = cmd
;*****************************************
;* LCDCHR - Display a character on the LCD
;* temp1 = data
;************************************

STEP_UP:
  clr   temp1
  add   temp34, step3
  adc   temp33, step2
  adc   temp32, step1
  adc   temp31, step0
  adc   temp30, temp1
  adc   temp29, temp1
  ldi   temp1,  $F0
  or    temp31, temp1
  ret

STEP_DN:
  clr   temp1
  ldi   temp1,  $0F
  and   temp31, temp1
  clr   temp1
  sub   temp34, step3
  sbc   temp33, step2
  sbc   temp32, step1
  sbc   temp31, step0
  sbc   temp30, temp1
  sbc   temp29, temp1
  ldi   temp1,  $F0
  or    temp31, temp1
  ret

Si5351:  ; demo freq set for 7.000 MHz
  ldi si5351_regNum, 3
  ldi si5351_regVal, 0xFF
  rcall i2cSend   ; disable all outputs

  ldi   si5351_regNum, 16
  ldi   si5351_regVal, 0x80
  rcall i2cSend   ; power down clock0

  ldi   si5351_regNum, 17
  ldi   si5351_regVal, 0x80
  rcall i2cSend   ; power down clock1

  ldi   si5351_regNum, 18
  ldi   si5351_regVal, 0x80
  rcall i2cSend   ; power down clock2

  ;ldi   si5351_regNum, 15
  ;ldi   si5351_regVal, 0x00   ; default is 0x00  
  ;rcall i2cSend   ; select PLLa,b source and use xtal input

  ;ldi   si5351_regNum, 24
  ;ldi   si5351_regVal, 0x00   ; default is 0x00  
  ;rcall i2cSend   ; clock disable state

  ldi si5351_regNum, 149
  ldi si5351_regVal, 0    ; disable spread spectrum on PLLa
  rcall i2cSend   ; reset both PLLs
  
; Output Multisynth0, e=0, f=1, MS0_P2 and MS0_P3
  ldi   si5351_regNum, 42
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P3[15:8]

  ldi   si5351_regNum, 43
  ldi   si5351_regVal, 1
  rcall i2cSend   ; set MS0_P3[7:0]
  
  ldi   si5351_regNum, 47
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P3[19:16] | MS0_P2[19:16]

  ldi   si5351_regNum, 48
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P2[15:8]

  ldi   si5351_regNum, 49
  ldi   si5351_regVal, 0
  rcall i2cSend   ; set MS0_P2:[7:0]
  
  ldi   si5351_regNum, 16
  ;ldi   si5351_regVal, 0x4C ; 2 mA - 144 ohms output Z (default)
  ;ldi   si5351_regVal, 0x4D ; 4 mA - 61 ohms output Z
  ;ldi   si5351_regVal, 0x4E ; 6 mA - 56 ohms output Z
  ldi   si5351_regVal, 0x4F ; 8 mA - 44 ohms output Z
  rcall i2cSend   ; power up clock0, MS0 in integer mode,  PLLa at 8 mA

  ;ldi si5351_regNum, 183
  ;ldi si5351_regVal, 0x52 ;$D2=default 10pF, $92=8pF, $52=6pF
  ;rcall i2cSend   ; reset both PLLs

  ldi si5351_regNum, 177
  ldi si5351_regVal, 0xA0
  rcall i2cSend   ; reset both PLLs
  
  ldi si5351_regNum, 3
  ldi si5351_regVal, 0xFE
  rcall i2cSend   ; enable clock0 output only 
  
; Load PLLa Feedback Multisynth 26,27,28

  ldi   si5351_regNum,  26
  ldi   si5351_regVal,  0xff
  rcall i2cSend   ; set MSNA_P3[15:8]

  ldi   si5351_regNum,  27
  ldi   si5351_regVal,  0xff
  rcall i2cSend   ; set MSNA_P3[7:0]

  ldi   si5351_regNum,  28
  ldi   si5351_regVal,  0
  rcall i2cSend   ; set MSNA_P1[17:16]
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  rcall UPDATE_2930313233
   
; Load Output Multysynth with d (e and f already set during init and never changes) 44,45,46

  ldi ZH, high(2*CR4445FL)
  ldi ZL, low(2*CR4445FL)

  ldi   si5351_regNum,  44
  lpm		si5351_regVal,  Z+
  rcall i2cSend   ; set MS0_P1[17:16]

  ldi   si5351_regNum,  45
  lpm		si5351_regVal,  Z+
  rcall i2cSend   ; set MS0_P1[15:8]  
 

  ;ldi   si5351_regNum,  44
  ;ldi   si5351_regVal,  0
  ;rcall i2cSend   ; set MS0_P1[17:16]

  ;ldi   si5351_regNum,  45
  ;ldi   si5351_regVal,  0x30
  ;rcall i2cSend   ; set MS0_P1[15:8]

  ldi   si5351_regNum,  46
  ldi   si5351_regVal,  0
  rcall i2cSend   ; set MS0_P1[7:0]

  ldi si5351_regNum, 177
  ldi si5351_regVal, 0x20
  rcall i2cSend   ; reset PLLa only
  ret
  
UPDATE_2930313233: ; Load PLLa Feedback Multisynth 29,30,31,32,33
  ldi   si5351_regNum,  29
  mov   si5351_regVal,  temp29
  rcall i2cSend   ; set MSNA_P1[15:8]
  
  ldi   si5351_regNum,  30
  mov   si5351_regVal,  temp30
  rcall i2cSend   ; set MSNA_P1[7:0]
  
  ldi   si5351_regNum,  31
  mov   si5351_regVal,  temp31
  rcall i2cSend   ; set MSNA_P3[19:16] | MSNA_P2[19:16]
  
  ldi   si5351_regNum,  32
  mov   si5351_regVal,  temp32
  rcall i2cSend   ; set MSNA_P2[15:8]
  
  ldi   si5351_regNum,  33
  mov   si5351_regVal,  temp33
  rcall i2cSend   ; set MSNA_P2[7:0]
  ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

i2cSend:  ; (uses si5351_regNum, si5351_regVal)
	rcall	i2c_start				  ; Send start condition and address
	MOV		i2cdata,  si5351_regNum	; Write word address (0x00)
	rcall	i2c_write_8bits 	  ; Execute transfer
	MOV		i2cdata,  si5351_regVal	; Set write data to 01010101b
	rcall	i2c_write_8bits 	  ; Execute transfer
	rcall	i2c_stop				  ; Send stop condition
	ret

i2c_start:
  ldi   i2cdata,  si5351_i2cAddr     ; load i2c device address to transmit register
  sbi   DDRC,     sda_bit   ; force data line low
  rcall i2cDelay_6
  sbi   DDRC,     scl_bit   ; force clock line low
  rcall i2cDelay_6
i2c_write_8bits:
  sec                       ; set carry flag
  rol   i2cdata             ; shift in carry bit and out bit into carry bit
i2c_write_bit:
  breq  i2c_get_ack
  brcc  i2c_bit_write_low
clock_it_high:
  cbi   DDRC,   sda_bit     ; release sda_bit - float sda high
  rjmp  clock_it
i2c_bit_write_low:
  sbi   DDRC,   sda_bit     ; force sda line low
clock_it:
  rcall i2cDelay_6
  cbi   DDRC,   scl_bit     ; release scl_bit - float scl high
  rcall i2cDelay_6
  sbi   DDRC,   scl_bit     ; force scl_bit low
  rcall i2cDelay_6
next_i2c_byte:
  lsl i2cdata
  rjmp  i2c_write_bit
  
i2c_get_ack:
  cbi   DDRC,   sda_bit     ; release sda_bit - float sda high
  rcall i2cDelay_6
  cbi   DDRC,   scl_bit     ; release scl_bit - float scl high
  rcall i2cDelay_6
i2c_get_ack_wait:
  sbis  PINC,   scl_bit     ; wait sda_bit high
  rjmp  i2c_get_ack_wait
  sbi   DDRC,   sda_bit     ; force sda_bit low
  rcall i2cDelay_6
  sbi   DDRC,   scl_bit     ; force scl_bit low
  rcall i2cDelay_6
  clc                       ; clear carry flag
  sbic  PINC,   sda_bit     ; if sda_bit is high
  sec                       ; set carry flag
  rcall i2cDelay_6
  ret  

i2c_stop:
  sbi   DDRC,   scl_bit     ; force scl_bit low
  sbi   DDRC,   sda_bit     ; force sda_bit low
  rcall i2cDelay_6
  cbi   DDRC,   scl_bit     ; release scl_bit
  rcall i2cDelay_6
  cbi   DDRC,   sda_bit     ; release sda_bit
  rcall i2cDelay_6
  ret
  
i2cDelay_6: ; 6 clock cycles or 6 uSeconds
; rcall instruction takes 2 clock cycles
; ret instruction takes 4 clock cycles
  ret

; Data transfer is initiated with a start condition (S) signaled by sda_bit being pulled low while SCL stays high.

;************************************
lcd_init:
;* uses temp2 to send LCD Command
;************************************
	ldi		delay_1ms,20	;wait 20 milliseconds for LCD to stabilize
	rcall	WAIT

	ldi		temp2,$38			;set for 8 bits, 2 lines, default font
	rcall LCDCMD
	ldi		delay_1ms,2		;wait 2 millisecond
  rcall	WAIT

	ldi		temp2,$0E			;Display on, CURSOR on, blink off
	rcall LCDCMD
	ldi		delay_1ms,2		;wait 2 millisecond
  rcall	WAIT

	ldi		temp2,$01			;Clear screen, CURSOR home
	rcall LCDCMD
	ldi		delay_1ms,2		;wait 2 millisecond
  rcall	WAIT
  
	ret

;************************************
;* LCDCMD - Send a command to the LCD
;* temp2 = cmd
;*****************************************
;* LCDCHR - Display a character on the LCD
;* temp1 = data
;************************************
LCDCMD:
  push temp1
  mov temp1,temp2
	rcall	SENDBYTE
	cbi		PORTC,lcd_drs			;set RS low
	rjmp	LCDEXEC
LCDCHR:
  push temp1
	rcall	SENDBYTE
	sbi		PORTC,lcd_drs			;set RS High
LCDEXEC:
	sbi		PORTC,lcd_e				;E clocks on rising edge
	ldi		delay_1ms,2				;wait 2 milliseconds
	cbi		PORTC,lcd_e				;set E low
	rcall	WAIT
	pop temp1
	ret

;**********************************
;* SENDBYTE clock byte into 74hc164
;* temp1 = data
;* temp2 = bitcounter
;***********************************
SENDBYTE:
  push temp1
	push	temp2
	ldi		temp2,8					;bit counter
ShiftBits:
	cbi		PORTC,lcd_drs   ;set LCD Data bit to 0
	lsl		temp1						;shift bits in AReg into carry
	brcc	ClockBit				;if zero, clock it out
	sbi		PORTC,lcd_drs   ;if 1, set LCD Data bit to 1
ClockBit: 
	sbi		PORTC,lcd_cp		  ;bit clocks on rising edge
	cbi		PORTC,lcd_cp		  ;bring clock back to 0
	dec		temp2						;shift out 8 bits
	brne	ShiftBits
	pop		temp2
	pop temp1
	ret

DISPLAY_LINE:
	lpm		temp1,Z+
	tst		temp1
	breq	DISPLAY_LINE_FINIS
	rcall	LCDCHR
	rjmp	DISPLAY_LINE
DISPLAY_LINE_FINIS:

	;ldi   temp2,  $C8
	;mov temp1,last_encoder_hi
	;rcall	SHOW_HEX_BYTE
;loop1: rjmp loop1

 ;ldi XH,high(cursor_number_sram)
 ;ldi XL,low(cursor_number_sram)
 ;ld temp2,X
 ;cpi temp2,4
 ;brlo cursor9
 ;inc temp2
 ;cpi temp2,8
 ;brlo cursor9
 ;inc temp2
cursor9:
 ;ldi temp1,$8B
 ;sub temp1,temp2
 ;mov temp2,temp1
 ;rcall LCDCMD
 ret

SHOW_HEX_BYTE: ; display temp1 as two (2) hex chars
  push  temp1
  rcall LCDCMD
  pop temp1
	push	temp1
	swap	temp1
	rcall SHOW_NIBBLE ; process upper nibble
	pop		temp1
	rcall SHOW_NIBBLE ; process lower nibble
	ret
SHOW_NIBBLE: ; display lower 4 bits in temp1 as hex char
  push	temp2
	andi	temp1,  $0F
	ori		temp1,  $30
	cpi		temp1,  $3A
	brlo	SHOW_NIBBLE2
	ldi   temp2,  $07
	add 	temp1,  temp2
SHOW_NIBBLE2:
	rcall	LCDCHR  ; display temp1
	pop   temp2
	ret

SHOW_DEC_BYTES: ; display temp1 as three (3) decimal chars 000-255
  ldi   temp2,  $C6   ; set display to middle of line 2
  rcall LCDCMD


  ret
  
SHOW_DEC_BYTE: ; display temp1 as three (3) decimal chars 000-255
  ldi   temp2,  $C6   ; set display to middle of line 2
  rcall LCDCMD
  ldi   temp1,  '7'
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  '0'
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  $30
  mov   temp2,  current_encoder_lo
SHOW_DEC_BYTE2:
  inc   temp1
  subi  temp2,  $64
  brcc  SHOW_DEC_BYTE2
  subi  temp2,  $9C
  dec   temp1
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  $30
SHOW_DEC_BYTE3:
  inc   temp1
  subi  temp2,  $0A
  brcc  SHOW_DEC_BYTE3
  subi  temp2,  $F6
  dec   temp1
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  '.'
  rcall	LCDCHR  ; display temp1
  mov   temp1,  temp2
  subi  temp1,  $D0
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  ' '
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  'K'
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  'H'
  rcall	LCDCHR  ; display temp1
  ldi   temp1,  'z'
  rcall	LCDCHR  ; display temp1
  ret

  
;************************************
;* WAIT
;* temp1 = WAIT time in milliseconds
;************************************
WAIT:
	tst		delay_1ms
	brne	WAIT
	ret

.if encoder_type  ==  2

EXT_INT0: ; logic change on INT0 (PD2, output_a)
	push	temp1           ;save temp1 register
	in		temp1,SREG      ;save the status register
	push	temp1
  in    temp1,  PIND          ; get portd
  andi  temp1,  $0C           ; mask off portd2 & portd3
  breq  INT0_1                ; if both bits are clear decrement current_encoder_lo
  cpi   temp1,  $0C           ; are both bits set?
  breq  INT0_1                ; if both bits are set decrement current_encoder_lo
  adiw	current_encoder_lo, 1 ; else increment current_encoder_lo
  rjmp	INT0_EXIT
INT0_1:
  sbiw	current_encoder_lo, 1
INT0_EXIT:
	pop		temp1
	out		SREG,temp1      ;restore the status register
	pop		temp1           ;restore temp1 register
  reti
  
EXT_INT1: ; logic change on INT1 (PD3, output_b)
	push	temp1           ;save temp1 register
	in		temp1,SREG      ;save the status register
	push	temp1
  in    temp1,  PIND          ; get portd
  andi  temp1,  $0C           ; mask off portd2 & portd3
  breq  INT1_1                ; if both bits are clear increment current_encoder_lo
  cpi   temp1,  $0C           ; are both bits set?
  breq  INT1_1                ; if both bits are set increment current_encoder_lo
  sbiw	current_encoder_lo, 1 ; else decrement current_encoder_lo
  rjmp	INT1_EXIT
INT1_1:
  adiw	current_encoder_lo, 1
INT1_EXIT:
	pop		temp1
	out		SREG,temp1      ;restore the status register
	pop		temp1           ;restore temp1 register
  reti

.elif encoder_type  ==  1

EXT_INT0:
	reti
	
EXT_INT1:
	push	temp1 ;save temp1 register
	in		temp1,SREG ;save the status register
	push	temp1
	lds		temp1,EICRA
	cpi		temp1,0b00001000	; test falling edge
	breq	int15
	ldi		temp1,0b00001000	; set int1 on falling edge
	sts		EICRA,temp1
	sbis	PIND, output_a ; test PHASE
	rjmp	int11
	adwi	current_encoder_lo, 1
	rjmp	int19
int11:
	sbiw	current_encoder_lo, 1
	rjmp	int19
int15:
	ldi		temp1,0b00001100	; set int1 for rising edge
	sts		EICRA,temp1
	sbis	PIND, output_a ; test PHASE
	rjmp	int16
	sbiw	current_encoder_lo, 1
	rjmp	int19
int16:
	adwi	current_encoder_lo, 1
int19:
	pop		temp1
	out		SREG,temp1 ;restore the status register
	pop		temp1   ;restore temp1 register
	reti

.else

EXT_INT0:
	reti

EXT_INT1:
	reti

.endif

TIMER0_OVF:
; with System Clock Prescaler set to /8
; and Timer/Counter Prescaler set to /8
; and TCNT0 loaded with 125 (see below)
  push  temp1
  push  temp2
  in    temp1,  SREG
  push  temp1
  ldi   temp1,  256-125 ;	set for 1 mS - 8 uS per count
  out   TCNT0,  temp1   ;	set for next overflow
  tst   delay_1ms
  breq  TIMER0_OVF_exit
  dec   delay_1ms
TIMER0_OVF_exit:
  pop   temp1
  out   SREG,   temp1
  pop   temp2
  pop   temp1
  reti


PCI2:
  push  temp1
  in    temp1,  SREG
  push  temp1
  
  in    temp1,  PIND
  andi  temp1,  0b00000010    ; mask off PD1
  tst   temp1
  brne  PCI2_EXIT ; throw away button release interrupt
  
  mov   temp1, tune_rate
  inc   temp1
  cpi   temp1, $34
  breq  PCI20
  mov   tune_rate,  temp1
  cbi   PORTD,  led_bit ;	turn LED on
  rjmp  PCI2_EXIT
PCI20:  
  ldi   temp1,  $30   ; tune_rate
  mov   tune_rate,  temp1
  sbi   PORTD,  led_bit ; turn LED off
  ;sbi   PIND,   led_bit ; turn LED on/off - toggle

PCI2_EXIT:
  pop   temp1
  out   SREG,   temp1
  pop   temp1
  reti

get_keypad:
 sbi DDRB,0 ; make PB0 an output
 cbi PORTB,0 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop 
 ldi temp1,'1'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'2'
 sbis PINB,5 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'3'
 sbis PINB,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'A'
 sbis PINB,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 sbi PORTB,0 ; Set Bit in I/O Register
 cbi DDRB,0 ; make PB0 an input
 
 sbi DDRB,1 ; make PB1 an output
 cbi PORTB,1 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop 
 ldi temp1,'4'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'5'
 sbis PINB,5 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'6'
 sbis PINB,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'B'
 sbis PINB,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 sbi PORTB,1 ; Set Bit in I/O Register
 cbi DDRB,1 ; make PB1 an input
 
 sbi DDRB,2 ; make PB2 an output
 cbi PORTB,2 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop
 ldi temp1,'7'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'8'
 sbis PINB,5 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'9'
 sbis PINB,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'C'
 sbis PINB,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 sbi PORTB,2 ; Set Bit in I/O Register
 cbi DDRB,2 ; make PB2 an input
 
 sbi DDRB,3 ; make PB3 an output
 cbi PORTB,3 ; Clear Bit in I/O Register
 nop ; at least one 'nop' required for keypad operation
 nop 
 ldi temp1,'*'
 sbis PINB,4 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'0'
 sbis PINB,5 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'#'
 sbis PINB,6 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 ldi temp1,'D'
 sbis PINB,7 ; Skip if Bit in I/O Register is Set
 rjmp grk_exit
 sbi PORTB,3 ; Set Bit in I/O Register
 cbi DDRB,3 ; make PB3 an input
 
 clr temp1
 rjmp grk_exit2
grk_exit:
 mov  last_keypad,  temp1
 ldi delay_1ms,250 ; wait 250 milliseconds to remove bounce
 rcall WAIT
grk_exit2:
 sbi PORTB,0 ; Set Bit in I/O Register
 sbi PORTB,1 ; Set Bit in I/O Register
 sbi PORTB,2 ; Set Bit in I/O Register
 sbi PORTB,3 ; Set Bit in I/O Register
 cbi DDRB,0 ; make PA0 an input
 cbi DDRB,1 ; make PA1 an input
 cbi DDRB,2 ; make PA2 an input
 cbi DDRB,3 ; make PA3 an input
 ret
