1
0
mirror of https://github.com/pConst/basic_verilog.git synced 2025-01-28 07:02:55 +08:00

1197 lines
59 KiB
Plaintext

;KCPSM3 Program - FC_CTRL
;
;Frequency Generator Control
;Calculates and displays results on the 16x2 LCD display.
;
;
; Version : 1.12
; Date : 28th February 2006
;
; Ken Chapman
; Xilinx Ltd
;
; chapman@xilinx.com
;
;
;
;**************************************************************************************
;Port definitions
;**************************************************************************************
;
CONSTANT A_count0_port, 00 ;32-bit A-counter (LSByte first)
CONSTANT A_count1_port, 10
CONSTANT A_count2_port, 20
CONSTANT A_count3_port, 30
;
CONSTANT B_count0_port, 40 ;32-bit B-counter (LSByte first)
CONSTANT B_count1_port, 50
CONSTANT B_count2_port, 60
CONSTANT B_count3_port, 70
;
CONSTANT status_port, 80 ;4 switches and counter status
CONSTANT switch0, 01 ; Switches SW0 - bit0
CONSTANT switch1, 02 ; active High SW1 - bit1
CONSTANT switch2, 04 ; SW2 - bit2
CONSTANT switch3, 08 ; SW3 - bit3
CONSTANT AB_switch, 10 ; 0=A-count enabled 1=B-count enabled
;
;
CONSTANT count_resetport, 02 ;Reset frequency counter controls
CONSTANT a_count_reset, 01 ; A-count = bit0
CONSTANT b_count_reset, 02 ; B-count = bit1
;
CONSTANT LED_port, 01 ;8 simple LEDs - active high
;
;
CONSTANT source_control_port, 08 ;Select and control test sources
CONSTANT source_sel0, 01 ; 00 = SMA clock 01=50MHz
CONSTANT source_sel1, 02 ; 10 = DCM Osc 11=Ring Osc
CONSTANT ring_reset, 40 ; active High rest of ring osc - bit6
CONSTANT dcm_kick, 80 ; DCM kick start signal - bit7
;
;
;LCD interface ports
;
;The master enable signal is not used by the LCD display itself
;but may be required to confirm that LCD communication is active.
;This is required on the Spartan-3E Starter Kit if the StrataFLASH
;is used because it shares the same data pins and conflicts must be avoided.
;
CONSTANT LCD_output_port, 04 ;LCD character module output data and control
CONSTANT LCD_E, 01 ; active High Enable E - bit0
CONSTANT LCD_RW, 02 ; Read=1 Write=0 RW - bit1
CONSTANT LCD_RS, 04 ; Instruction=0 Data=1 RS - bit2
CONSTANT LCD_drive, 08 ; Master enable (active High) - bit3
CONSTANT LCD_DB4, 10 ; 4-bit Data DB4 - bit4
CONSTANT LCD_DB5, 20 ; interface Data DB5 - bit5
CONSTANT LCD_DB6, 40 ; Data DB6 - bit6
CONSTANT LCD_DB7, 80 ; Data DB7 - bit7
;
;
CONSTANT LCD_input_port, 09 ;LCD character module input data
CONSTANT LCD_read_spare0, 01 ; Spare bits - bit0
CONSTANT LCD_read_spare1, 02 ; are zero - bit1
CONSTANT LCD_read_spare2, 04 ; - bit2
CONSTANT LCD_read_spare3, 08 ; - bit3
CONSTANT LCD_read_DB4, 10 ; 4-bit Data DB4 - bit4
CONSTANT LCD_read_DB5, 20 ; interface Data DB5 - bit5
CONSTANT LCD_read_DB6, 40 ; Data DB6 - bit6
CONSTANT LCD_read_DB7, 80 ; Data DB7 - bit7
;
;
;
;Special Register usage
;
;
;
;**************************************************************************************
;Scratch Pad Memory Locations
;**************************************************************************************
;
;
CONSTANT count0, 00 ;last 32-bit counter value (LSByte first)
CONSTANT count1, 01
CONSTANT count2, 02
CONSTANT count3, 03
;
CONSTANT ISR_count, 04 ;count number of interrupts for a clean start
;
CONSTANT decimal0, 11 ;10 digit decimal value up to 4,294,967,295
CONSTANT decimal1, 12
CONSTANT decimal2, 13
CONSTANT decimal3, 14
CONSTANT decimal4, 15
CONSTANT decimal5, 16
CONSTANT decimal6, 17
CONSTANT decimal7, 18
CONSTANT decimal8, 19
CONSTANT decimal9, 1A
;
;
;
CONSTANT preserve_s0, 30 ;place to save register contents
CONSTANT preserve_s1, 31
CONSTANT preserve_s2, 32
CONSTANT preserve_s3, 33
CONSTANT preserve_s4, 34
CONSTANT preserve_s5, 35
CONSTANT preserve_s6, 36
CONSTANT preserve_s7, 37
CONSTANT preserve_s8, 38
CONSTANT preserve_s9, 39
CONSTANT preserve_sA, 3A
CONSTANT preserve_sB, 3B
CONSTANT preserve_sC, 3C
CONSTANT preserve_sD, 3D
CONSTANT preserve_sE, 3E
CONSTANT preserve_sF, 3F
;
;
;
;**************************************************************************************
;Useful data constants
;**************************************************************************************
;
;
;
;Constant to define a software delay of 1us. This must be adjusted to reflect the
;clock applied to KCPSM3. Every instruction executes in 2 clock cycles making the
;calculation highly predictable. The '6' in the following equation even allows for
;'CALL delay_1us' instruction in the initiating code.
;
; delay_1us_constant = (clock_rate - 6)/4 Where 'clock_rate' is in MHz
;
;Example: For a 50MHz clock the constant value is (10-6)/4 = 11 (0B Hex).
;For clock rates below 10MHz the value of 1 must be used and the operation will
;become lower than intended.
;
CONSTANT delay_1us_constant, 0B
;
;
;
;ASCII table
;
CONSTANT character_a, 61
CONSTANT character_b, 62
CONSTANT character_c, 63
CONSTANT character_d, 64
CONSTANT character_e, 65
CONSTANT character_f, 66
CONSTANT character_g, 67
CONSTANT character_h, 68
CONSTANT character_i, 69
CONSTANT character_j, 6A
CONSTANT character_k, 6B
CONSTANT character_l, 6C
CONSTANT character_m, 6D
CONSTANT character_n, 6E
CONSTANT character_o, 6F
CONSTANT character_p, 70
CONSTANT character_q, 71
CONSTANT character_r, 72
CONSTANT character_s, 73
CONSTANT character_t, 74
CONSTANT character_u, 75
CONSTANT character_v, 76
CONSTANT character_w, 77
CONSTANT character_x, 78
CONSTANT character_y, 79
CONSTANT character_z, 7A
CONSTANT character_A, 41
CONSTANT character_B, 42
CONSTANT character_C, 43
CONSTANT character_D, 44
CONSTANT character_E, 45
CONSTANT character_F, 46
CONSTANT character_G, 47
CONSTANT character_H, 48
CONSTANT character_I, 49
CONSTANT character_J, 4A
CONSTANT character_K, 4B
CONSTANT character_L, 4C
CONSTANT character_M, 4D
CONSTANT character_N, 4E
CONSTANT character_O, 4F
CONSTANT character_P, 50
CONSTANT character_Q, 51
CONSTANT character_R, 52
CONSTANT character_S, 53
CONSTANT character_T, 54
CONSTANT character_U, 55
CONSTANT character_V, 56
CONSTANT character_W, 57
CONSTANT character_X, 58
CONSTANT character_Y, 59
CONSTANT character_Z, 5A
CONSTANT character_0, 30
CONSTANT character_1, 31
CONSTANT character_2, 32
CONSTANT character_3, 33
CONSTANT character_4, 34
CONSTANT character_5, 35
CONSTANT character_6, 36
CONSTANT character_7, 37
CONSTANT character_8, 38
CONSTANT character_9, 39
CONSTANT character_colon, 3A
CONSTANT character_stop, 2E
CONSTANT character_semi_colon, 3B
CONSTANT character_minus, 2D
CONSTANT character_divide, 2F ;'/'
CONSTANT character_plus, 2B
CONSTANT character_comma, 2C
CONSTANT character_less_than, 3C
CONSTANT character_greater_than, 3E
CONSTANT character_equals, 3D
CONSTANT character_space, 20
CONSTANT character_CR, 0D ;carriage return
CONSTANT character_question, 3F ;'?'
CONSTANT character_dollar, 24
CONSTANT character_exclaim, 21 ;'!'
CONSTANT character_BS, 08 ;Back Space command character
;
;
;
;
;
;**************************************************************************************
;Initialise the system
;**************************************************************************************
;
cold_start: CALL LCD_reset ;initialise LCD display
LOAD s0, 00 ;Turn off LEDs
OUTPUT s0, LED_port
;
;
;Write welcome message to LCD display
;
LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
CALL disp_PicoBlaze ;Display 'PicoBlaze Inside'
CALL delay_1s ;wait 3 seconds
CALL delay_1s
CALL delay_1s
LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
CALL disp_Frequency ;Display 'Frequency Counter V1.00'
LOAD s5, 21 ;Line 2 position 1
CALL LCD_cursor
CALL disp_Counter
LOAD s5, 2B ;Line 2 position 11
CALL LCD_cursor
CALL disp_version
CALL delay_1s ;wait 5 seconds
CALL delay_1s
CALL delay_1s
CALL delay_1s
CALL delay_1s
CALL LCD_clear ;Clear display
;
;Kick start the DCM oscillator.
; Just requires a few cyles of activity
;
LOAD s0, FF
LOAD s1, 00
kick_loop: OUTPUT s1, source_control_port
XOR s1, dcm_kick ;toggle kick start signal
SUB s0, 01
JUMP NZ, kick_loop
;
;clear all scratch pad memory locations
;
LOAD s1, 3F
LOAD s0, 00
clear_spm: STORE s0, (s1)
SUB s1, 01
JUMP NC, clear_spm
;
ENABLE INTERRUPT
;
;**************************************************************************************
;Main Program
;**************************************************************************************
;
;The task of the main program is just to read the most recent values from
;scratch pad memory and display them as fast as it can.
;
;It also reads the slide switches controls the selection of the source frequency to
;be measured.
;
warm_start: LOAD s5, 21 ;Line 2 position 1
CALL LCD_cursor
INPUT sF, status_port ;select source based on switches
COMPARE sF, 00 ;test for no switches active
AND sF, 0F ;isolate switches
JUMP NZ, test_SMA
CALL disp_menu
JUMP warm_start
test_SMA: COMPARE sF, switch0
JUMP NZ, test_50M
CALL disp_SMA_input
LOAD sF, 00
JUMP select_source
test_50M: COMPARE sF, switch1
JUMP NZ, test_DCM
CALL disp_50MHz_Crystal
LOAD sF, 01
JUMP select_source
test_DCM: COMPARE sF, switch2
JUMP NZ, test_Ring
CALL disp_DCM_Oscillator
LOAD sF, 02
JUMP select_source
test_Ring: COMPARE sF, switch3
JUMP Z, Ring_select
CALL disp_menu ;more than one switch is set
JUMP warm_start
Ring_select: CALL disp_Ring_Oscillator
LOAD sF, 03
select_source: OUTPUT sF, source_control_port ;select source control
;
;Read the most recent values from display on LCD.
;
;Interrupts will be disabled during the reading of values to ensure a clean
;value is obtained when reading multi-byte values.
;
;
;Display the count value in the top right of the LCD display
;Up to 999,999,999
;
DISABLE INTERRUPT ;copy cycle count to register set [s5,s4,s3,s2]
FETCH s2, count0
FETCH s3, count1
FETCH s4, count2
FETCH s5, count3
ENABLE INTERRUPT
CALL integer_to_BCD ;convert last 32-bit value to BCD digits
LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
LOAD s6, decimal8 ;up to 999,999,999 Hz
CALL disp_digits
JUMP warm_start
;
;
;
;**************************************************************************************
; Display frequency value on LCD display
;**************************************************************************************
;
;
;Display value on the LCD display at current position.
;The values to be displayed must be stored in scratch pad memory
;locations 'decimal0' to 'decimal9' which must be in ascending locations.
;
;The routing performs leading zero suppression and scales to Hz, KHz or MHz ranges.
;
;Registers used s0,s1,s4,s5,sE,sF
;
disp_digits: LOAD sF, FF ;set blanking flag
LOAD sE, character_space ;scaling character for MHz, KHz or Hz
FETCH s5, decimal8 ;100MHz digit
CALL zero_test
CALL disp_digit
FETCH s5, decimal7 ;10MHz digit
CALL zero_test
CALL disp_digit
FETCH s5, decimal6 ;1MHz digit
CALL zero_test
CALL disp_digit
COMPARE sF, FF ;check if any MHz were active
JUMP Z, khz_space
LOAD s5, character_stop
CALL LCD_write_data
LOAD sE, character_M
JUMP khz_digits
khz_space: LOAD s5, character_space
CALL LCD_write_data
khz_digits: FETCH s5, decimal5 ;100KHz digit
CALL zero_test
CALL disp_digit
FETCH s5, decimal4 ;10KHz digit
CALL zero_test
CALL disp_digit
FETCH s5, decimal3 ;1KHz digit
CALL zero_test
CALL disp_digit
COMPARE sE, character_M ;check if any MHz were active
JUMP Z, hz_space
COMPARE sF, FF ;check if any KHz were active
JUMP Z, hz_space
LOAD s5, character_stop
CALL LCD_write_data
LOAD sE, character_K
JUMP hz_digits
hz_space: LOAD s5, character_space
CALL LCD_write_data
hz_digits: FETCH s5, decimal2 ;100KHz digit
CALL zero_test
CALL disp_digit
FETCH s5, decimal1 ;10KHz digit
CALL zero_test
CALL disp_digit
FETCH s5, decimal0 ;1KHz digit (always displayed)
ADD s5, character_0 ;convert number to ASCII
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, sE
CALL LCD_write_data
LOAD s5, character_H
CALL LCD_write_data
LOAD s5, character_z
CALL LCD_write_data
LOAD s5, character_space ;ensure end of line is clear
CALL LCD_write_data
RETURN
;
;Check digit for zero. If not zero then clear
;blanking flag (sF=00)
zero_test: COMPARE s5, 00
RETURN Z
LOAD sF, 00
RETURN
;
;Display single digit at current position
;or space if blanking (sF=FF) is active
;
disp_digit: COMPARE sF, FF
JUMP Z, blank_digit
ADD s5, character_0 ;convert number to ASCII
CALL LCD_write_data
RETURN
blank_digit: LOAD s5, character_space
CALL LCD_write_data
RETURN
;
;
;
;**************************************************************************************
; 32-bit integer to BCD conversion
;**************************************************************************************
;
;Convert the 32 bit value in register set [s5,s4,s3,s2]
;into the BCD decimal equivalent located in the scratch pad memory
;locations 'decimal0' to 'decimal9' which must be in ascending locations.
;
;Each digit is formed in turn starting with the least significant.
;
;Registers used s0,s2,s3,s4,s5,s6,s7,s8,s9,sA,sB,sC,sD,sE,sF
;
integer_to_BCD: LOAD sE, 0A ;10 digits to be formed from value upto 4294967295
LOAD sF, decimal0 ;pointer for LS-Digit
int_to_BCD_loop: CALL divide_32bit_by_10
STORE s1, (sF) ;remainder becomes digit value
ADD sF, 01 ;move to next most significant digit
SUB sE, 01 ;one less digit to compute
JUMP NZ, int_to_BCD_loop
RETURN
;
;Divide 32-bit binary integer by 10
;
;The value to be divided is held in register set [s5,s4,s3,s2]
;and this is where the result is returned to.
;
;At then end of the integer division the remainder in the range 0 to 9
;will be in register s1.
;
;Registers used s0, s2,s3,s4,s5,s6,s7,s8,s9,sA,sB,sC,sD
;
divide_32bit_by_10: LOAD sA, s2 ;copy input value to set [sD,sC,sB,sA]
LOAD sB, s3
LOAD sC, s4
LOAD sD, s5
LOAD s2, 00 ;clear result
LOAD s3, 00
LOAD s4, 00
LOAD s5, 00
LOAD s9, A0 ;initialise '10' value into msb's of set [s9,s8,s7,s6]
LOAD s8, 00
LOAD s7, 00
LOAD s6, 00
LOAD s0, 1D ;29 subtract and shift iterations to be performed
div10_loop: SUB sA, s6 ;perform 32-bit subtract [sD,sC,sB,sA]-[s9,s8,s7,s6]
SUBCY sB, s7
SUBCY sC, s8
SUBCY sD, s9
JUMP C, div10_restore
SL1 s2 ;shift '1' into result
JUMP div10_shifts
div10_restore: ADD sA, s6 ;perform 32-bit addition [sD,sC,sB,sA]+[s9,s8,s7,s6]
ADDCY sB, s7
ADDCY sC, s8
ADDCY sD, s9
SL0 s2 ;shift '0' into result
div10_shifts: SLA s3 ;complete 32-bit shift left
SLA s4
SLA s5
SR0 s9 ;divide '10' value by 2 (shift right 1 place)
SRA s8
SRA s7
SRA s6
SUB s0, 01 ;count iterations
JUMP NZ, div10_loop
LOAD s1, sA ;remainder of division
RETURN
;
;
;
;
;**************************************************************************************
;LCD text messages
;**************************************************************************************
;
;
;Display 'PicoBlaze' on LCD at current cursor position
;
;
disp_PicoBlaze: LOAD s5, character_P
CALL LCD_write_data
LOAD s5, character_i
CALL LCD_write_data
LOAD s5, character_c
CALL LCD_write_data
LOAD s5, character_o
CALL LCD_write_data
LOAD s5, character_B
CALL LCD_write_data
LOAD s5, character_l
CALL LCD_write_data
LOAD s5, character_a
CALL LCD_write_data
LOAD s5, character_z
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
RETURN
;
;
;Display 'Frequency' on LCD at current cursor position
;
;
disp_Frequency: LOAD s5, character_F
CALL LCD_write_data
LOAD s5, character_r
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
LOAD s5, character_q
CALL LCD_write_data
LOAD s5, character_u
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_c
CALL LCD_write_data
LOAD s5, character_y
CALL LCD_write_data
RETURN
;
;
;Display 'Counter' on LCD at current cursor position
;
;
disp_Counter: LOAD s5, character_C
CALL LCD_write_data
LOAD s5, character_o
CALL LCD_write_data
LOAD s5, character_u
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_t
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
LOAD s5, character_r
CALL LCD_write_data
RETURN
;
;Display version number on LCD at current cursor position
;
;
disp_version: LOAD s5, character_v
CALL LCD_write_data
LOAD s5, character_1
CALL LCD_write_data
LOAD s5, character_stop
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
RETURN
;
;
;Display 'SMA input' at current cursor position
;
;
disp_SMA_input: LOAD s5, character_S
CALL LCD_write_data
LOAD s5, character_M
CALL LCD_write_data
LOAD s5, character_A
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_i
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_p
CALL LCD_write_data
LOAD s5, character_u
CALL LCD_write_data
LOAD s5, character_t
CALL LCD_write_data
LOAD sF, 06
JUMP disp_spaces
;
;
;
;Display '50MHz Crystal' at current cursor position
;
;
disp_50MHz_Crystal: LOAD s5, character_5
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_M
CALL LCD_write_data
LOAD s5, character_H
CALL LCD_write_data
LOAD s5, character_z
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_C
CALL LCD_write_data
LOAD s5, character_r
CALL LCD_write_data
LOAD s5, character_y
CALL LCD_write_data
LOAD s5, character_s
CALL LCD_write_data
LOAD s5, character_t
CALL LCD_write_data
LOAD s5, character_a
CALL LCD_write_data
LOAD s5, character_l
CALL LCD_write_data
LOAD sF, 02
JUMP disp_spaces
;
;
;
;Display 'DCM oscillator' at current cursor position
;
;
disp_DCM_Oscillator: LOAD s5, character_D
CALL LCD_write_data
LOAD s5, character_C
CALL LCD_write_data
LOAD s5, character_M
CALL LCD_write_data
disp_Oscillator: LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_O
CALL LCD_write_data
LOAD s5, character_s
CALL LCD_write_data
LOAD s5, character_c
CALL LCD_write_data
LOAD s5, character_i
CALL LCD_write_data
LOAD s5, character_l
CALL LCD_write_data
CALL LCD_write_data
LOAD s5, character_a
CALL LCD_write_data
LOAD s5, character_t
CALL LCD_write_data
LOAD s5, character_o
CALL LCD_write_data
LOAD s5, character_r
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
RETURN
;
;
;
;Display 'Ring oscillator' at current cursor position
;
;
disp_Ring_Oscillator: LOAD s5, character_R
CALL LCD_write_data
LOAD s5, character_i
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_g
CALL LCD_write_data
JUMP disp_Oscillator
;
;
;Display spaces at current cursor position
;Number of spaces to be specified in register sF
;
disp_spaces: COMPARE sF, 00
RETURN Z
LOAD s5, character_space
CALL LCD_write_data
SUB sF, 01
JUMP disp_spaces
;
;Display switch setting menu on entire display.
;
disp_menu: LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
LOAD s5, character_R
CALL LCD_write_data
LOAD s5, character_i
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_g
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_D
CALL LCD_write_data
LOAD s5, character_C
CALL LCD_write_data
LOAD s5, character_M
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_5
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_M
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_S
CALL LCD_write_data
LOAD s5, character_M
CALL LCD_write_data
LOAD s5, character_A
CALL LCD_write_data
LOAD s5, 20 ;Line 2 position 0
CALL LCD_cursor
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_S
CALL LCD_write_data
LOAD s5, character_W
CALL LCD_write_data
LOAD s5, character_3
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_S
CALL LCD_write_data
LOAD s5, character_W
CALL LCD_write_data
LOAD s5, character_2
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_S
CALL LCD_write_data
LOAD s5, character_W
CALL LCD_write_data
LOAD s5, character_1
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_S
CALL LCD_write_data
LOAD s5, character_W
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
RETURN
;
;
;
;
;**************************************************************************************
;Software delay routines
;**************************************************************************************
;
;
;
;Delay of 1us.
;
;Constant value defines reflects the clock applied to KCPSM3. Every instruction
;executes in 2 clock cycles making the calculation highly predictable. The '6' in
;the following equation even allows for 'CALL delay_1us' instruction in the initiating code.
;
; delay_1us_constant = (clock_rate - 6)/4 Where 'clock_rate' is in MHz
;
;Registers used s0
;
delay_1us: LOAD s0, delay_1us_constant
wait_1us: SUB s0, 01
JUMP NZ, wait_1us
RETURN
;
;Delay of 40us.
;
;Registers used s0, s1
;
delay_40us: LOAD s1, 28 ;40 x 1us = 40us
wait_40us: CALL delay_1us
SUB s1, 01
JUMP NZ, wait_40us
RETURN
;
;
;Delay of 1ms.
;
;Registers used s0, s1, s2
;
delay_1ms: LOAD s2, 19 ;25 x 40us = 1ms
wait_1ms: CALL delay_40us
SUB s2, 01
JUMP NZ, wait_1ms
RETURN
;
;Delay of 20ms.
;
;Delay of 20ms used during initialisation.
;
;Registers used s0, s1, s2, s3
;
delay_20ms: LOAD s3, 14 ;20 x 1ms = 20ms
wait_20ms: CALL delay_1ms
SUB s3, 01
JUMP NZ, wait_20ms
RETURN
;
;Delay of approximately 1 second.
;
;Registers used s0, s1, s2, s3, s4
;
delay_1s: LOAD s4, 32 ;50 x 20ms = 1000ms
wait_1s: CALL delay_20ms
SUB s4, 01
JUMP NZ, wait_1s
RETURN
;
;
;
;**************************************************************************************
;LCD Character Module Routines
;**************************************************************************************
;
;LCD module is a 16 character by 2 line display but all displays are very similar
;The 4-wire data interface will be used (DB4 to DB7).
;
;The LCD modules are relatively slow and software delay loops are used to slow down
;KCPSM3 adequately for the LCD to communicate. The delay routines are provided in
;a different section (see above in this case).
;
;
;Pulse LCD enable signal 'E' high for greater than 230ns (1us is used).
;
;Register s4 should define the current state of the LCD output port.
;
;Registers used s0, s4
;
LCD_pulse_E: XOR s4, LCD_E ;E=1
OUTPUT s4, LCD_output_port
CALL delay_1us
XOR s4, LCD_E ;E=0
OUTPUT s4, LCD_output_port
RETURN
;
;Write 4-bit instruction to LCD display.
;
;The 4-bit instruction should be provided in the upper 4-bits of register s4.
;Note that this routine does not release the master enable but as it is only
;used during initialisation and as part of the 8-bit instruction write it
;should be acceptable.
;
;Registers used s4
;
LCD_write_inst4: AND s4, F8 ;Enable=1 RS=0 Instruction, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
CALL LCD_pulse_E
RETURN
;
;
;Write 8-bit instruction to LCD display.
;
;The 8-bit instruction should be provided in register s5.
;Instructions are written using the following sequence
; Upper nibble
; wait >1us
; Lower nibble
; wait >40us
;
;Registers used s0, s1, s4, s5
;
LCD_write_inst8: LOAD s4, s5
AND s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OR s4, LCD_drive ;Enable=1
CALL LCD_write_inst4 ;write upper nibble
CALL delay_1us ;wait >1us
LOAD s4, s5 ;select lower nibble with
SL1 s4 ;Enable=1
SL0 s4 ;RS=0 Instruction
SL0 s4 ;RW=0 Write
SL0 s4 ;E=0
CALL LCD_write_inst4 ;write lower nibble
CALL delay_40us ;wait >40us
LOAD s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;Release master enable
RETURN
;
;
;
;Write 8-bit data to LCD display.
;
;The 8-bit data should be provided in register s5.
;Data bytes are written using the following sequence
; Upper nibble
; wait >1us
; Lower nibble
; wait >40us
;
;Registers used s0, s1, s4, s5
;
LCD_write_data: LOAD s4, s5
AND s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OR s4, 0C ;Enable=1 RS=1 Data, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
CALL LCD_pulse_E ;write upper nibble
CALL delay_1us ;wait >1us
LOAD s4, s5 ;select lower nibble with
SL1 s4 ;Enable=1
SL1 s4 ;RS=1 Data
SL0 s4 ;RW=0 Write
SL0 s4 ;E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
CALL LCD_pulse_E ;write lower nibble
CALL delay_40us ;wait >40us
LOAD s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;Release master enable
RETURN
;
;
;
;
;Read 8-bit data from LCD display.
;
;The 8-bit data will be read from the current LCD memory address
;and will be returned in register s5.
;It is advisable to set the LCD address (cursor position) before
;using the data read for the first time otherwise the display may
;generate invalid data on the first read.
;
;Data bytes are read using the following sequence
; Upper nibble
; wait >1us
; Lower nibble
; wait >40us
;
;Registers used s0, s1, s4, s5
;
LCD_read_data8: LOAD s4, 0E ;Enable=1 RS=1 Data, RW=1 Read, E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
XOR s4, LCD_E ;E=1
OUTPUT s4, LCD_output_port
CALL delay_1us ;wait >260ns to access data
INPUT s5, LCD_input_port ;read upper nibble
XOR s4, LCD_E ;E=0
OUTPUT s4, LCD_output_port
CALL delay_1us ;wait >1us
XOR s4, LCD_E ;E=1
OUTPUT s4, LCD_output_port
CALL delay_1us ;wait >260ns to access data
INPUT s0, LCD_input_port ;read lower nibble
XOR s4, LCD_E ;E=0
OUTPUT s4, LCD_output_port
AND s5, F0 ;merge upper and lower nibbles
SR0 s0
SR0 s0
SR0 s0
SR0 s0
OR s5, s0
LOAD s4, 04 ;Enable=0 RS=1 Data, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;Stop reading 5V device and release master enable
CALL delay_40us ;wait >40us
RETURN
;
;
;Reset and initialise display to communicate using 4-bit data mode
;Includes routine to clear the display.
;
;Requires the 4-bit instructions 3,3,3,2 to be sent with suitable delays
;following by the 8-bit instructions to set up the display.
;
; 28 = '001' Function set, '0' 4-bit mode, '1' 2-line, '0' 5x7 dot matrix, 'xx'
; 06 = '000001' Entry mode, '1' increment, '0' no display shift
; 0C = '00001' Display control, '1' display on, '0' cursor off, '0' cursor blink off
; 01 = '00000001' Display clear
;
;Registers used s0, s1, s2, s3, s4
;
LCD_reset: CALL delay_20ms ;wait more that 15ms for display to be ready
LOAD s4, 30
CALL LCD_write_inst4 ;send '3'
CALL delay_20ms ;wait >4.1ms
CALL LCD_write_inst4 ;send '3'
CALL delay_1ms ;wait >100us
CALL LCD_write_inst4 ;send '3'
CALL delay_40us ;wait >40us
LOAD s4, 20
CALL LCD_write_inst4 ;send '2'
CALL delay_40us ;wait >40us
LOAD s5, 28 ;Function set
CALL LCD_write_inst8
LOAD s5, 06 ;Entry mode
CALL LCD_write_inst8
LOAD s5, 0C ;Display control
CALL LCD_write_inst8
LCD_clear: LOAD s5, 01 ;Display clear
CALL LCD_write_inst8
CALL delay_1ms ;wait >1.64ms for display to clear
CALL delay_1ms
RETURN
;
;Position the cursor ready for characters to be written.
;The display is formed of 2 lines of 16 characters and each
;position has a corresponding address as indicated below.
;
; Character position
; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
;
; Line 1 - 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
; Line 2 - C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
;
;This routine will set the cursor position using the value provided
;in register s5. The upper nibble will define the line and the lower
;nibble the character position on the line.
; Example s5 = 2B will position the cursor on line 2 position 11
;
;Registers used s0, s1, s2, s3, s4
;
LCD_cursor: TEST s5, 10 ;test for line 1
JUMP Z, set_line2
AND s5, 0F ;make address in range 80 to 8F for line 1
OR s5, 80
CALL LCD_write_inst8 ;instruction write to set cursor
RETURN
set_line2: AND s5, 0F ;make address in range C0 to CF for line 2
OR s5, C0
CALL LCD_write_inst8 ;instruction write to set cursor
RETURN
;
;
;
;**************************************************************************************
;Interrupt Service Routine
;**************************************************************************************
;
;
;Each interrupt means that there is a new count value to be read.
;However the first 4 interrupts are ignored other than to clear the counter to
;ensure that even the first reading is for one complete period.
;
;After reading the active counter, all calculations are performed and values stored
;in scratch pad memory are updated to reflect the new values.
;
;Registers are preserved and restored by the ISR so main program is unaffected.
;
ISR: STORE s0, preserve_s0 ;preserve registers
STORE s1, preserve_s1
STORE s2, preserve_s2
STORE s3, preserve_s3
STORE s4, preserve_s4
STORE s5, preserve_s5
STORE s6, preserve_s6
STORE s7, preserve_s7
STORE s8, preserve_s8
STORE s9, preserve_s9
STORE sA, preserve_sA
STORE sB, preserve_sB
STORE sC, preserve_sC
STORE sD, preserve_sD
STORE sE, preserve_sE
STORE sF, preserve_sF
;
;Ignore the first 4 interrupts except to clear the counter.
;This will ensure a clean start up after reset.
;
FETCH s0, ISR_count ;test to see if more that 4 interrupts have occurred
COMPARE s0, 04
JUMP Z, normal_isr
ADD s0, 01 ;increment ISR counter until reaching 4
STORE s0, ISR_count
INPUT s0, status_port ;Check which counter to clear
TEST s0, AB_switch ;if bit0 is Low then A is counting
JUMP Z, clear_B_count
clear_A_count: LOAD s0, a_count_reset ;clear the active counter
JUMP clear_counter
clear_B_count: LOAD s0, b_count_reset ;clear the active counter
clear_counter: OUTPUT s0, count_resetport ;reset counter with pulse
LOAD s0, 00 ;end reset pulse to either counter
OUTPUT s0, count_resetport
JUMP restore_reg
;
;Normal ISR Routine
;
;
;Read the new counter value and then clear it ready to start again
;
;
normal_isr: INPUT s0, status_port ;test for active counter
TEST s0, AB_switch ;if bit is low then A is counting
JUMP Z, capture_B_count
capture_A_count: LOAD s0, F0 ;set LEDs to indicate active counter
OUTPUT s0, LED_port
INPUT sC, A_count0_port ;read counter A into [sF,sE,SD,sC]
INPUT sD, A_count1_port
INPUT sE, A_count2_port
INPUT sF, A_count3_port
LOAD s0, a_count_reset ;reset counter A
OUTPUT s0, count_resetport
JUMP counters_read
;
capture_B_count: LOAD s0, 0F ;set LEDs to indicate active counter
OUTPUT s0, LED_port
INPUT sC, B_count0_port ;read counter A into [sF,sE,SD,sC]
INPUT sD, B_count1_port
INPUT sE, B_count2_port
INPUT sF, B_count3_port
LOAD s0, b_count_reset ;reset counter B
OUTPUT s0, count_resetport
counters_read: LOAD s0, 00 ;end reset pulse to either counter
OUTPUT s0, count_resetport
;
STORE sC, count0 ;store new counter value
STORE sD, count1
STORE sE, count2
STORE sF, count3
;
;
;
;Restore registers and end ISR
;
restore_reg: FETCH sF, preserve_sF ;restore registers
FETCH sE, preserve_sE
FETCH sD, preserve_sD
FETCH sC, preserve_sC
FETCH sB, preserve_sB
FETCH sA, preserve_sA
FETCH s9, preserve_s9
FETCH s8, preserve_s8
FETCH s7, preserve_s7
FETCH s6, preserve_s6
FETCH s5, preserve_s5
FETCH s4, preserve_s4
FETCH s3, preserve_s3
FETCH s2, preserve_s2
FETCH s1, preserve_s1
FETCH s0, preserve_s0
RETURNI ENABLE
;
;
;Interrupt vector
;
ADDRESS 3FF
JUMP ISR
;
;
;**************************************************************************************
;End of Program
;**************************************************************************************
;
;