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

1528 lines
89 KiB
Plaintext

; KCPSM3 Program - Control and calculation for Frequency Generator design using the
; Spartan-3E Starter Kit.
;
; Interfaces with the rotary encoder and LCD display to enable a frequency to be set.
; Converts the BCD frequency value into a binary integer and then performs the high
; precision calculation necessary to derive the control numbers required by the high
; performance Direct Digital Synthesis (DDS) circuit implemented in hardware.
;
; LEDs are connected and used as edit mode indicators.
;
; Substantial comments are included in line with the code below and should be used
; in conjunction with the documentation provided with the complete reference design.
;
;
;
; Ken Chapman - Xilinx Ltd
;
; Version v1.00 - 13th July 2006
;
;**************************************************************************************
;Port definitions
;**************************************************************************************
;
;
;
CONSTANT LED_port, 80 ;8 simple LEDs
CONSTANT LED0, 01 ; LED 0 - bit0
CONSTANT LED1, 02 ; 1 - bit1
CONSTANT LED2, 04 ; 2 - bit2
CONSTANT LED3, 08 ; 3 - bit3
CONSTANT LED4, 10 ; 4 - bit4
CONSTANT LED5, 20 ; 5 - bit5
CONSTANT LED6, 40 ; 6 - bit6
CONSTANT LED7, 80 ; 7 - bit7
;
;
CONSTANT rotary_port, 00 ;Read status of rotary encoder
CONSTANT rotary_left, 01 ; Direction of last move Left=1 Right=0 - bit0
CONSTANT rotary_press, 02 ; Centre press contact (active High) - bit1
;
;
;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, 40 ;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, 01 ;LCD character module input data
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
;
;
;
;DDS control ports
;
;DDS control word is 32-bits
;
CONSTANT DDS_control0_port, 02 ; dds_control_word(7:0)
CONSTANT DDS_control1_port, 04 ; dds_control_word(15:8)
CONSTANT DDS_control2_port, 08 ; dds_control_word(23:16)
CONSTANT DDS_control3_port, 10 ; dds_control_word(31:24)
;
;Frequency scaling control word is 5-bits
;
CONSTANT DDS_scaling_port, 20 ; dds_scaling_word(4:0)
;
;
;**************************************************************************************
;Special Register usage
;**************************************************************************************
;
;**************************************************************************************
;Scratch Pad Memory Locations
;**************************************************************************************
;
CONSTANT rotary_status, 00 ;Status of rotary encoder
CONSTANT rotary_event, 80 ; flag set by interrupt in 'rotary_status' - bit7
;
CONSTANT ISR_preserve_s0, 01 ;Preserve s0 contents during ISR
;
CONSTANT LED_pattern, 02 ;LED pattern used in rotation mode
;
;
;BCD digits representing selected and displayed frequency
;
CONSTANT BCD_digit0, 03 ; value 1
CONSTANT BCD_digit1, 04 ; 10
CONSTANT BCD_digit2, 05 ; 100
CONSTANT BCD_digit3, 06 ; 1,000
CONSTANT BCD_digit4, 07 ; 10,000
CONSTANT BCD_digit5, 08 ; 100,000
CONSTANT BCD_digit6, 09 ; 1,000,000
CONSTANT BCD_digit7, 0A ; 10,000,000
CONSTANT BCD_digit8, 0B ; 100,000,000
;
;
;Binary integer representation of BCD value
;
CONSTANT frequency0, 0C ;LS byte
CONSTANT frequency1, 0D
CONSTANT frequency2, 0E
CONSTANT frequency3, 0F ;MS byte
;
;
;Control of frequency selection values
;
CONSTANT cursor_position, 10 ; Pointer to edit position on LCD
CONSTANT edit_digit_pointer, 11 ; BCD digit to be changed
;
;
;
;80-bit product resulting from 32-bit frequency x 48-bit scaling constant
;
CONSTANT product0, 12 ;LS byte
CONSTANT product1, 13
CONSTANT product2, 14
CONSTANT product3, 15
CONSTANT product4, 16
CONSTANT product5, 17
CONSTANT product6, 18
CONSTANT product7, 19
CONSTANT product8, 1A
CONSTANT product9, 1B ;MS byte
;
;Local copies of the DDS control word and DDS scaling word
;
CONSTANT DDS_control0, 1C ; dds_control_word(7:0)
CONSTANT DDS_control1, 1D ; dds_control_word(15:8)
CONSTANT DDS_control2, 1E ; dds_control_word(23:16)
CONSTANT DDS_control3, 1F ; dds_control_word(31:24)
CONSTANT DDS_scaling, 20 ; dds_scaling_word(4:0)
;
;**************************************************************************************
; Useful data constants
;**************************************************************************************
;
; To convert the frequency into a DDS control value a high precision scaling
; factor is used. This is a 48-bit number which converts the frequency presented
; as an 32-bit integer into the 32-bit value required by the phase accumulator
; to synthesize the desired frequency. The scaling factor is derived using the
; following method. First I will consider the scaling factor which results in the
; desired frequency being generated directly at the output of the phase accumulator
; which is suitable for low frequencies in which a few ns of jitter is acceptable.
;
; 'Fpa' is frequency generated by the MSB of the phase accumulator.
; 'p' is number of phase accumulator which in this case is 32 bits.
; 'clk' is the input clock frequency to the phase accumulator which is 200MHz.
; 'N' is the DDS control word value which is also 'p' bits (32 in this case).
;
; Frequency at MSB of phase accumulator is then
;
; Fpa = clk x N / (2^p)
;
; Note that the maximum value allowed for 'N' is (2^p)/2 which results in Fpa=clk/2.
; for 'N' greater than that value 'Fpa' would decrease in frequency (aliasing).
;
;
; By simple reorganisation of the equation we can compute 'N'
;
; N = Fpa x (2^p) / clk
;
;
; Now it is easier to approach the next step using specific example.
;
; So for a frequency of Fpa = 1MHz then
; N = 1MHz x (2^32)/200MHz = 21474836.48
;
; We must use the nearest 32-bit integer value 21474836 and this in turn
; is best viewed as the 32-bit hexadecimal value 0147AE14.
;
; In this case the value we have to work with is a 32-bit integer frequency
; value of 1 million which is 000F4240.
;
; So now we need to translate the value 000F4240 into 0147AE14. This is
; where a 48-bit scaling value is used together with a full precision multiplier
; as this ensures adequate accuracy of the final frequency.
;
; 32-bit frequency value ffffffff
; 48-bit scaling value x ssssssssssss
; --------------------
; 80-bit product nnnnnnnnnnnnnnnnnnnn
;
; The art is to organise the scaling factor into the range where the most is made of
; the 48-bit resolution available but which will result in the correct 32-bit output.
; The way this is achieved is the select an appropriate 32-bits from the available 80-bit
; product for use as 'N' and truncate 'y' least significant bits.
;
; From this we can deduce that for a target frequency 'Ft' at the input then the
; scaling value 'S' is given by
;
; S = N x (2^y) / Ft with the condition that S < 2^48 but as large as possible
;
; For best accuracy we calculate 'S' using the full precision value of 'N' divided
; by Ft and then multiply continuously by 2 until we reach the biggest value less
; that 2^48. The number of multiplications by 2 indicating the value of 'y'.
;
; In this case we find that 'y' is 43.....
; S = 21474836.48 x (2^43) / 1000000 = 21.47483648 x (2^43)
; = 188894659314785.80854784
;
; ...round to nearest integer and convert to hexadecimal S = ABCC77118462
;
; N will be taken from the 80 bit product by removing the 43 LSBs and the 5 MSBs
; to leave the 32 active bits required. This is best achieved by shifting left
; by 5 places (multiply by 2^5=32) and keeping the upper 32-bits.
;
;
; Sanity check....
; Note that most calculators do not support >64 bit values to you will either
; need to decompose your calculation and perform some of it manually or trust
; the PicoBlaze implementation :-)
;
; Ft = 1MHz = 000F4240
; S = x ABCC77118462
; --------------------
; 000A3D70A3D70A405C80
;
; shift left 5 places x 20
; --------------------
; 0147AE147AE1480B9000
;
; As expected, the most significant 32-bit (4 bytes) are 0147AE14 hex which is
; the DDS control word for 1MHz calculated previously.
;
; ***
;
; Now I will consider how this needs to be modified for the circuit presented
; which has a second DCM connected to the output of the phase accumulator to
; multiply the synthesized frequency and reduce cycle to cycle jitter at
; the same time. There is then a clock divider circuit connected to the output
; of the DCM which allows lower frequencies to be formed a different way (more of
; that later). As a minimum that divider circuit will divide by 2 which ensures that
; a square wave is presented to the clocked put pin. So in this circuit the fundamental
; multiplication factor is 8 formed by a 16 times multiplication by the DCM (256/16) and
; then a divide by 2.
;
; The overall multiplication factor of this sebsequent circuit means that for final
; output from the DCM to be the desired frequency, the output from the phase accumulator
; needs to be the same number of times smaller. This is not a bad thing because the
; percentage jitter of waveforms produced by the phase accumulator is better for lower
; frequencies made from more clock cycles.
;
; So we modify the basic equation to
;
; Fout = Frequency at output of DCM
; M = Multiplying factor of DCM
;
; Fout = M x Fpa = M x clk x N / (2^p)
;
;
; By simple reorganisation of the equation we can compute 'N'
;
; N = Fout x (2^p) / (clk x M)
;
;
; In this design M=8, p=32, clk=200MHz
;
; So now consider generating a nominal maximum frequency of 100MHz which will require
; the frequency synthesized by the phase accumulator to be 12.5MHz.
;
; N = 100MHz x (2^32) / (200MHz x 8) = 268435456 = 10000000 Hex
;
; This all seems like a very convenient number but it simply reflects that 12.5MHz
; is a perfect division of the 200MHz clock and that that output from the phase
; accumulator will be formed perfectly of 16 of the 200MHz clock periods every time
; (8 Low and 8 High) with no additional jitter.
;
; So now we work out the scaling factor with the same rules as used previously that
; the scaling factor should be as large as possible within the 48-bits allocated.
;
; S = N x (2^y) / Ft with the condition that S < 2^48 but as large as possible
;
; In this case Ft = 100MHz = 055FE100 and the biggest value for S is found when using
; y=46
;
; S = 268435456 x (2^46) / 100000000 = 2.68435456 x (2^46)
; = 188894659314785.80854784
;
; round to 188894659314786 = ABCC77118462
;
; Actually this is the exact same scaling constant as previously because the
; frequency to be synthesized by the phase accumulator is 8 times smaller but the
; value of 'S' is deliberate scaled to be as large as possible. In fact, 'S' in this
; case has been scaled up by a factor of 8 to arrive at the same value. So after
; using the scaling constant to form the 80 bit product, this time we will remove
; the 46 LSBs and the 2 MSBs to leave the 32 active bits required. This is best
; achieved by shifting left by 2 places (multiply by 2^2=4) and keeping the upper
; 32-bits (last time we multiplied by 32 which was 8 times more).
;
;
; Sanity check....
;
; Ft = 100MHz = 055FE100
; S = x ABCC77118462
; --------------------
; 04000000000001242200
;
; shift left 5 places x 20
; --------------------
; 1000000000001C908800
;
; As expected, the most significant 32-bit (4 bytes) are 10000000 hex which is
; the DDS control word for 12.5MHz at the phase accumulator output calculated
; previously.
;
;
; ********
;
;
; 48-bit Scaling factor constant to generate the phase accumulator control word
; from the integer frequency value.
;
; S = AB CC 77 11 84 62
;
; Notes
;
; The 80-bit product must be shifted left 5 times and then most significant 32-bits
; used to provide DDS control word if the frequency required is to be synthesized
; directly at the output of the phase accumulator.
;
; The 80-bit product must be shifted left 2 times and then most significant 32-bits
; used to provide DDS control word if the frequency required is to be synthesized
; by the phase accumulator followed by a multiplying DCM and divider with overall
; frequency gain of 8 times.
;
CONSTANT scale_constant0, 62 ;LS byte
CONSTANT scale_constant1, 84
CONSTANT scale_constant2, 11
CONSTANT scale_constant3, 77
CONSTANT scale_constant4, CC
CONSTANT scale_constant5, AB ;MS byte
;
;
;
; ************************
;
;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
;
;Write 'Frequency Generator' to LCD display and display for 4 seconds
;
LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
CALL disp_Frequency
LOAD s5, 22 ;Line 2 position 2
CALL LCD_cursor
CALL disp_Generator
CALL delay_1s ;wait 4 seconds
CALL delay_1s
CALL delay_1s
CALL delay_1s
CALL LCD_clear ;clear screen
;
;
;Initial frequency of 100MHz
;
LOAD s0, 00
LOAD s1, 01
STORE s0, BCD_digit0
STORE s0, BCD_digit1
STORE s0, BCD_digit2
STORE s0, BCD_digit3
STORE s0, BCD_digit4
STORE s0, BCD_digit5
STORE s0, BCD_digit6
STORE s0, BCD_digit7
STORE s1, BCD_digit8
;
LOAD s0, 04 ;Start position for editing frequency is 1MHz digit
STORE s0, cursor_position
LOAD s0, BCD_digit6
STORE s0, edit_digit_pointer
;
;
ENABLE INTERRUPT ;interrupts are used to detect rotary controller
CALL delay_1ms
LOAD s0, 00 ;clear the status of any spurious rotary events
STORE s0, rotary_status ; as a result of system turning on.
;
;**************************************************************************************
; Main program
;**************************************************************************************
;
; The main program is centred on the task of editing the frequency. It waits until the
; rotary control is used and then makes the appropriate changes. If the actual digit
; digit value is changed then the calculation to drive the DDS is performed each time.
;
; The start state is that of allowing the edit cursor position to be moved. Rotary
; inputs are detected by the interrupt service routine and set a flag bit which the
; main program then uses to adjust the cursor position and pointer to the corresponding
; BCD digit in memory.
;
; A press of the rotary control is detected by polling and used to change to the digit
; editing mode.
;
;
move_mode: CALL compute_DDS_words ;compute DDS control values
CALL display_freq ;refresh display with cursor position shown
LOAD s0, LED0 ;indicate move mode on LEDs
OUTPUT s0, LED_port
move_wait: INPUT s0, rotary_port ;read rotary encoder
TEST s0, rotary_press ;test for press of button which changes mode
JUMP NZ, edit_mode
FETCH s0, rotary_status ;check for any rotation of rotary control
TEST s0, rotary_event
JUMP Z, move_wait
;
AND s0, 7F ;clear flag now that action it is being processed
STORE s0, rotary_status
FETCH sA, cursor_position ;read current position
FETCH sB, edit_digit_pointer
TEST s0, rotary_left ;determine direction to move cursor
JUMP Z, move_right
;
move_left: COMPARE sB, BCD_digit8 ;can not move left of 100MHz digit
JUMP Z, move_mode
ADD sB, 01 ;move to next higher BCD digit
SUB sA, 01 ;move cursor to match digit to be edited
COMPARE sA, 09 ;must skip over space separator
JUMP Z, skip_left
COMPARE sA, 05 ;must skip over decimal point
JUMP NZ, edit_point_update
skip_left: SUB sA, 01 ;move cursor further left
JUMP edit_point_update
;
move_right: COMPARE sB, BCD_digit0 ;can not move right of 1Hz digit
JUMP Z, move_mode
SUB sB, 01 ;move to next lower BCD digit
ADD sA, 01 ;move cursor to match digit to be edited
COMPARE sA, 09 ;must skip over space separator
JUMP Z, skip_right
COMPARE sA, 05 ;must skip over decimal point
JUMP NZ, edit_point_update
skip_right: ADD sA, 01 ;move cursor further right
;
edit_point_update: STORE sA, cursor_position ;update edit value in memory
STORE sB, edit_digit_pointer
JUMP move_mode
;
;
; The edit mode is reached by pressing the rotary control. Since this is a simple switch
; a software de-bounce delay is used to wait for the knob to be released fully before
; entering the digit editing mode fully.
;
; In this mode rotations of the detected by the interrupt service routine are used to
; increment or decrement the digit value at the cursor position with carry/borrow to
; the left.
;
; A new press of the rotary control is detected by polling and used to change back to the
; cursor moving mode.
;
;
edit_mode: CALL wait_switch_release ;wait for switch press to end
edit_display: CALL compute_DDS_words ;compute DDS control values
CALL display_freq ;refresh display with new values
LOAD s0, LED1 ;indicate edit mode on LEDs
OUTPUT s0, LED_port
edit_wait: INPUT s0, rotary_port ;read rotary encoder
TEST s0, rotary_press ;test for press of button which changes mode
JUMP NZ, end_edit_mode
FETCH s0, rotary_status ;check for any rotation of rotary control
TEST s0, rotary_event
JUMP Z, edit_wait
;
AND s0, 7F ;clear flag now that action it is being processed
STORE s0, rotary_status
FETCH sB, edit_digit_pointer ;read pointer to BCD digit for initial change
TEST s0, rotary_left ;determine direction to increment or decrement
JUMP Z, inc_digit
;
; Decrement the value starting at the current position and borrowing from the left.
; However the value needs to bottom out at all 0's from the editing position.
;
;
dec_digit: FETCH sA, (sB) ;read digit value at pointer position
SUB sA, 01 ;decrement digit
COMPARE sA, FF ;test for borrow from next digit
JUMP Z, dec_borrow
STORE sA, (sB) ;store decremented digit value
JUMP edit_display ;decrement task complete
dec_borrow: LOAD sA, 09 ;current digit rolls over to nine
STORE sA, (sB) ;store '9' digit value
COMPARE sB, BCD_digit8 ;check if working on 100MHz digit
JUMP Z, set_min_value
ADD sB, 01 ;increment pointer to next most significant digit
JUMP dec_digit ;decrement next digit up.
;
set_min_value: FETCH sB, edit_digit_pointer ;Must fill digits from insert to MS-Digit with 000...
LOAD sA, 00
fill_min: STORE sA, (sB)
COMPARE sB, BCD_digit8 ;check if filled to 100MHz digit
JUMP Z, edit_display
ADD sB, 01 ;fill next higher digit
JUMP fill_min
;
; Increment the value starting at the current position and carrying to the left.
; However the value needs to saturate to all 9's from the editing position.
;
inc_digit: FETCH sA, (sB) ;read digit value at pointer position
ADD sA, 01 ;increment digit
COMPARE sA, 0A ;test for carry to next digit
JUMP Z, inc_carry
STORE sA, (sB) ;store incremented digit value
JUMP edit_display ;increment task complete
inc_carry: LOAD sA, 00 ;current digit rolls over to zero
STORE sA, (sB) ;store zero digit value
COMPARE sB, BCD_digit8 ;check if working on 100MHz digit
JUMP Z, set_max_value
ADD sB, 01 ;increment pointer to next most significant digit
JUMP inc_digit ;increment next digit up.
;
set_max_value: FETCH sB, edit_digit_pointer ;Must fill digits from insert to MS-Digit with 999...
LOAD sA, 09
fill_max: STORE sA, (sB)
COMPARE sB, BCD_digit8 ;check if filled to 100MHz digit
JUMP Z, edit_display
ADD sB, 01 ;fill next higher digit
JUMP fill_max
;
end_edit_mode: CALL wait_switch_release ;wait for end of switch press
JUMP move_mode ;then go to move cursor mode
;
;
; Routine to poll the press switch with de-bounce delay and wait for it to be
; released. Any rotation inputs detected by the interrupt
; service routine are cleared before returning.
;
wait_switch_release: CALL delay_20ms ;delay to aid switch de-bounce
INPUT s0, rotary_port ;read rotary encoder
TEST s0, rotary_press ;test if button is still being pressed
JUMP NZ, wait_switch_release
LOAD s0, 00 ;clear flag indicating any rotary events
STORE s0, rotary_status
RETURN
;
;**************************************************************************************
; Compute DDS control words from currently display frequency value
;**************************************************************************************
;
; This routine reads the current BCD value and converts it into a 32-bit binary
; integer. It then multiplies it by a 48-bit scaling factor (see notes in the
; constants section above) to form a full precision 80-bit product.
;
; From this product the 32-bit DDS control word must be extracted. For frequencies of
; 50MHz or above the DDS control word is formed by shifting the product left by 2 places
; (multiply by 4) and then keeping only the most significant 32-bits (4 bytes).
;
; Also for frequencies of 50MHz and above, there is no additional division performed
; after the DCM which multiplies frequency and reduces the jitter. Therefore the DDS_scaling
; word will be set to zero and the output of the DCM will divide by 2.
;
; Freq DDS control word DDS Scaling Synthesized Frequency
; of Phase Accumulator
;
; 50MHz 08000000 00 6.25MHz
; 100MHz 10000000 00 12.50MHz
;
; You will notice that for frequencies of 50MHz and above, the upper byte of the
; DDS control word is 08 hex or greater. In other words, bit3 and/or bit4 of that byte
; are High (bits 27 and/or 28 of the full 32-bit word). This is the indication that
; the control words are complete.
;
; For frequencies below 50MHz an additional process is required. The reason for this
; becomes clear if we think about the lowest frequency of 1Hz. In that case the 80-bit
; product is the same as the 48-bit scaling constant 00000000ABCC77118462. Once this
; has been multiplied by 4 (shifted left 2 places) it becomes 00000002AF31DC461188 and the
; most significant 32-bits are only 00000002 hex. If we put this back into the basic
; equations for the phase accumulator we find that the output frequency of the phase
; accumulator would be
;
; Fout = M x clk x N / (2^p)
;
; = 8 x 200MHz x 2 / (2^32) = 0.745 Hz
;
; There are two important observations we can make. Firstly we have lost accuracy because
; the resolution of the DDS control word has become too granular at low amplitudes.
; Secondly this would never even work because the frequency synthesized by the phase
; accumulator would be 0.745/8 = 0.0931 Hz which is seriously slow and a way below the
; frequency at which the DCM can even work.
;
; The solution to both of these issues is to ensure that the DDS control word is always
; formed to be in the range that would result in an output of 50MHz or above. In other
; words to keep the phase accumulator output in the range 6.25MHz to 12.5MHz such that
; the DCM is able to work and only has to deal with one octave of input variation. This
; can be achieved by shifting the 80-bit product left more times until bits 27 and 28
; of the most significant 32-bits are not zero.
;
; For each shift left the synthesized frequency is being doubled and therefore the final
; output from the DCM must be divided by a further factor of 2. This is achieved using
; a multiplexer which is guided to select the appropriate output from a simple binary
; counter.
;
; Returning to the example of 1Hz, the 80-bit product will be shifted left by the default
; 2 places (multiplied by 4), but will then need to be shifted left by a further 26 places
; which is like multiplying by 67108864 (04000000 hex).
;
; 00000000ABCC77118462
; x 4
; --------------------
; 00000002AF31DC461188
;
;
; x 04000000
; --------------------
; 0ABCC771184620000000
;
; So now the DDS control word is 0ABCC771 (180143985 decimal)and the frequency synthesized
; by the phase accumulator will be....
;
; Fpa = clk x N / (2^p) = 200MHz x 180143985 / (2^32) = 8388608Hz
;
; The DCM will multiply this by a factor of 16 to give 134217728Hz and this will then
; be divided by the counter of which the 26th bit selected (26 decimal = 1A hex).
;
; Fout = Fpa x 16 / (2^(D+1)) = 8388608Hz x 16 / (2^(26+1)) = 0.99999999947 Hz
;
; 'D' is the DDS Scaling factor
; Note that bit0 of a counter is a clock division by 2 and hence the 'D+1'
;
; Clearly this implementation style has provided much greater accuracy and enables
; the DCM to work for all desired frequencies.
;
;
; Freq DDS control word DDS Scaling Synthesized Frequency
; of Phase Accumulator
;
; 100 MHz 10000000 00 12.50MHz
; 50 MHz 08000000 00 6.25MHz
; 25 MHz 08000000 01 6.25MHz
; 12.5 MHz 08000000 02 6.25MHz
;
; 1Hz 0ABCC771 1A 8.388608 MHz
;
;
;
; In order to ensure the DCM is always provided with a frequency in an acceptable
; range, the value of absolute zero is never implemented and instead just a very low
; frequency is produced.
; 6.25MHz x 16 / (2^31+1) = 0.0233 Hz
; which is 1 cycle every 43 seconds and that is pretty slow :-)
;
;
;
;
compute_DDS_words: CALL BCD_to_integer ;convert BCD display value to 32-bit value
CALL scale_frequency ;80-bit product of 32-bit frequency x 48-bit scaling value
FETCH sA, product9 ;read the upper part of the 80-bit product into [sA,s9,s8,s7,s6,s5,s4]
FETCH s9, product8 ; The least significant 24-bits of the 80-bit product will never
FETCH s8, product7 ; be used for frequencies above 1Hz.
FETCH s7, product6 ;The final 32-bit DDS control word will be formed in
FETCH s6, product5 ; [sA,s9,s8,s7]
FETCH s5, product4
FETCH s4, product3
CALL shift80_left ;multiply DDS control word by 4 to achieve default value
CALL shift80_left
LOAD sB, 00 ;default scaling factor is 2 (select counter bit0)
normalise_loop: TEST sA, 18 ;Test bits 27 and 28 of 32-bit DDS control word
JUMP NZ, store_DDS_words ;DDS control word is normalised to above 50MHz output
CALL shift80_left ;multiply DDS control word by 2
ADD sB, 01 ;Divide final value by 2 to compensate
COMPARE sB, 1F ;Test for maximum division factor
JUMP NZ, normalise_loop
LOAD sA, 08 ;Set for minimum frequency
LOAD s9, 00 ; with phase accumulator set to generate 6.25MHz
LOAD s8, 00
LOAD s7, 00
store_DDS_words: STORE s7, DDS_control0 ;store local copy of control word
STORE s8, DDS_control1 ;store local copy of control word
STORE s9, DDS_control2 ;store local copy of control word
STORE sA, DDS_control3 ;store local copy of control word
STORE sB, DDS_scaling
CALL drive_DDS_words ;output control words to DDS circuit
RETURN
;
shift80_left: SL0 s4 ;shift (most of the) 80-bit value in
SLA s5 ; [sA,s9,s8,s7,s6,s5,s4] left 1 place
SLA s6
SLA s7
SLA s8
SLA s9
SLA sA
RETURN
;
;**************************************************************************************
; Set DDS control words
;**************************************************************************************
;
; Because multiple ports are used, the idea is to update all of them in
; rapid succession to avoid too much disturbance in the frequency synthesis.
;
; dds_control_word should be supplied in register set [sA,s9,s8,s7]
; dds_scaling_word should be supplied in register s6.
;
drive_DDS_words: FETCH s7, DDS_control0
FETCH s8, DDS_control1
FETCH s9, DDS_control2
FETCH sA, DDS_control3
FETCH s6, DDS_scaling
OUTPUT s7, DDS_control0_port
OUTPUT s8, DDS_control1_port
OUTPUT s9, DDS_control2_port
OUTPUT sA, DDS_control3_port
OUTPUT s6, DDS_scaling_port
RETURN
;
;
;**************************************************************************************
; Display frequency on top line of the LCD and DDS data on the lower line
;**************************************************************************************
;
; The BCD value should be stored in scratch pad memory in 9 ascending locations
; called BCD_digit0 to BCD_digit8.
;
; The value is displayed in the format xxx.xxx xxxMHz
;
; However, the most significant 2 digits will be blanked if zero.
;
; registers used s0,s1,s2,s3,s4,s5,s6,s7
;
;
display_freq: CALL display_DDS_data ;display DDS information on lower line
LOAD s5, 12 ;Line 1 position 2
CALL LCD_cursor
FETCH s5, BCD_digit8 ;read 100MHz digit
COMPARE s5, 00 ;test for blanking
JUMP Z, blank_100M_digit
CALL display_digit ;display non zero digit
FETCH s5, BCD_digit7 ;read 10MHz digit and display
CALL display_digit
JUMP disp_1M_digit
;
blank_100M_digit: CALL display_space ;blank 100MHz digit
FETCH s5, BCD_digit7 ;read 10MHz digit
COMPARE s5, 00 ;test for blanking
JUMP Z, blank_10M_digit
CALL display_digit ;display non zero digit
JUMP disp_1M_digit
;
blank_10M_digit: CALL display_space ;blank 10MHz digit
;
disp_1M_digit: FETCH s5, BCD_digit6 ;read 1MHz digit and display
CALL display_digit
LOAD s5, character_stop ;display decimal point
CALL LCD_write_data
;
LOAD s2, BCD_digit5 ;set pointer to 100KHz digit
CALL display_3_digits
CALL display_space
LOAD s2, BCD_digit2 ;set pointer to 100Hz digit
CALL display_3_digits
LOAD s5, character_M ;display 'MHz'
CALL LCD_write_data
LOAD s5, character_H
CALL LCD_write_data
LOAD s5, character_z
CALL LCD_write_data
;
FETCH s5, cursor_position ;reposition edit cursor on display
ADD s5, 10 ;on line 1
CALL LCD_cursor
RETURN
;
display_3_digits: LOAD s3, 03 ;3 digits to display
threedigit_loop: FETCH s5, (s2)
CALL display_digit
SUB s2, 01 ;decrement digit pointer
SUB s3, 01 ;count digits displayed
JUMP NZ, threedigit_loop
RETURN
;
display_digit: ADD s5, 30 ;convert BCD to ASCII character
CALL LCD_write_data
RETURN
;
display_space: LOAD s5, character_space
CALL LCD_write_data
RETURN
;
;
;**************************************************************************************
; Convert 9 digit BCD frequency into 32-bit binary integer
;**************************************************************************************
;
;Both values are stored in scratch pad memory
; BCD values in ascending locations BCD_digit0 to BCD_digit8
; Binary frequency in ascending locations frequency0 to frequency3
;
;Each digit is read in turn and its value is determined by repeated
;decrement until reaching zero. Each decrement causes a value to be added
;to the memory locations forming the frequency value as binary integer.
;The process requires approximately 1600 instructions to convert the highest
;value 999,999,999 which is approximately 64us at 50MHz clock rate.
;
;Registers used s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,sA,sB
;
BCD_to_integer: LOAD s2, 09 ;9 digits to convert
LOAD s0, 00 ;clear frequency value ready to accumulate result
STORE s0, frequency0
STORE s0, frequency1
STORE s0, frequency2
STORE s0, frequency3
LOAD sB, 00 ;initialise BCD digit weighting [sB,sA,s9,s8] to 1
LOAD sA, 00
LOAD s9, 00
LOAD s8, 01
LOAD s3, BCD_digit0 ;locate LS-digit
next_BCD_to_int_digit: FETCH s1, (s3)
BCD_digit_convert: COMPARE s1, 00 ;test for zero
JUMP Z, next_digit_value
FETCH s0, frequency0 ;add 32-bit digit weighting to memory value
ADD s0, s8
STORE s0, frequency0
FETCH s0, frequency1
ADDCY s0, s9
STORE s0, frequency1
FETCH s0, frequency2
ADDCY s0, sA
STORE s0, frequency2
FETCH s0, frequency3
ADDCY s0, sB
STORE s0, frequency3
SUB s1, 01 ;decrement digit value
JUMP BCD_digit_convert
;Increase weighting by 10x
next_digit_value: LOAD s7, sB ;copy existing weighting
LOAD s6, sA
LOAD s5, s9
LOAD s4, s8
SL0 s8 ;multiply weight by 4x (shift left 2 places)
SLA s9
SLA sA
SLA sB
SL0 s8
SLA s9
SLA sA
SLA sB
ADD s8, s4 ;add previous weight to form 5x multiplication
ADDCY s9, s5
ADDCY sA, s6
ADDCY sB, s7
SL0 s8 ;multiply weight by 2x (shift left 1 places)
SLA s9
SLA sA
SLA sB ;weight value is now 10x previous value
ADD s3, 01 ;move to next digit for conversion
SUB s2, 01
JUMP NZ, next_BCD_to_int_digit
RETURN
;
;
;**************************************************************************************
; 32-bit x 48-bit multiply to scale the integer frequency
;**************************************************************************************
;
;Multiply the 32-bit frequency binary integer by the 48-bit scaling factor
;to form a full precision 80-bit product.
;
;The frequency binary integer is stored in scratch pad memory using ascending
;locations frequency0 to frequency3
;
;The product will be stored in scratch pad memory using ascending
;locations product0 to product9
;
;The scaling factor is provided directly as constants
; scale_constant0 to scale_constant5
;
;The multiplication is performed as a 32-bit 'shift and add' process in which the
;integer frequency is examined LSB first using a register set [sB,sA,s9,s8] and
;a scaling accumulator is formed directly in the 'product' memory locations.
;
;The process requires up to 1772 instructions which is 3544 clock cycle or
;approximately 71us at 50MHz clock rate.
;
;Registers used s0,s1,s8,s9,sA,sB (s1,s8,s9,sA,sB clear on return)
;
scale_frequency: LOAD s0, 00 ;clear accumulator section of 'product'
STORE s0, product9
STORE s0, product8
STORE s0, product7
STORE s0, product6
STORE s0, product5
STORE s0, product4
FETCH sB, frequency3 ;read frequency integer value
FETCH sA, frequency2
FETCH s9, frequency1
FETCH s8, frequency0
LOAD s1, 20 ;32-bit multiply
scale_mult_bit: SR0 sB ;shift right frequency integer
SRA sA
SRA s9
SRA s8
JUMP NC, product_shift ;no add if bit is zero (note carry is zero)
FETCH s0, product4 ;addition of scaling factor to most significant bits of product
ADD s0, scale_constant0
STORE s0, product4
FETCH s0, product5
ADDCY s0, scale_constant1
STORE s0, product5
FETCH s0, product6
ADDCY s0, scale_constant2
STORE s0, product6
FETCH s0, product7
ADDCY s0, scale_constant3
STORE s0, product7
FETCH s0, product8
ADDCY s0, scale_constant4
STORE s0, product8
FETCH s0, product9
ADDCY s0, scale_constant5
STORE s0, product9 ;carry holds any overflow of addition
product_shift: FETCH s0, product9 ;Divide product by 2 (shift right by 1)
SRA s0 ;overflow of addition included in shift
STORE s0, product9
FETCH s0, product8
SRA s0
STORE s0, product8
FETCH s0, product7
SRA s0
STORE s0, product7
FETCH s0, product6
SRA s0
STORE s0, product6
FETCH s0, product5
SRA s0
STORE s0, product5
FETCH s0, product4
SRA s0
STORE s0, product4
FETCH s0, product3
SRA s0
STORE s0, product3
FETCH s0, product2
SRA s0
STORE s0, product2
FETCH s0, product1
SRA s0
STORE s0, product1
FETCH s0, product0
SRA s0
STORE s0, product0
SUB s1, 01 ;move to next bit
JUMP NZ, scale_mult_bit
RETURN
;
;**************************************************************************************
; Display DDS control information on the lower line of the LCD display.
;**************************************************************************************
;
;Display the 32-bit DDS control word and 8-bit DDS scaling word.
;
display_DDS_data: LOAD s5, 20 ;Line 2 position 0
CALL LCD_cursor
LOAD s5, character_N
CALL LCD_write_data
LOAD s5, character_equals
CALL LCD_write_data
LOAD s7, DDS_control3 ;pointer to most significant byte in memory
CALL display_hex_32_bit
CALL display_space
LOAD s5, character_D
CALL LCD_write_data
LOAD s5, character_equals
CALL LCD_write_data
FETCH s0, DDS_scaling
CALL display_hex_byte
RETURN
;
;**************************************************************************************
; Routines to display hexadecimal values on LCD display
;**************************************************************************************
;
;
; Convert hexadecimal value provided in register s0 into ASCII characters
;
; The value provided must can be any value in the range 00 to FF and will be converted into
; two ASCII characters.
; The upper nibble will be represented by an ASCII character returned in register s3.
; The lower nibble will be represented by an ASCII character returned in register s2.
;
; The ASCII representations of '0' to '9' are 30 to 39 hexadecimal which is simply 30 hex
; added to the actual decimal value. The ASCII representations of 'A' to 'F' are 41 to 46
; hexadecimal requiring a further addition of 07 to the 30 already added.
;
; Registers used s0, s2 and s3.
;
hex_byte_to_ASCII: LOAD s2, s0 ;remember value supplied
SR0 s0 ;isolate upper nibble
SR0 s0
SR0 s0
SR0 s0
CALL hex_to_ASCII ;convert
LOAD s3, s0 ;upper nibble value in s3
LOAD s0, s2 ;restore complete value
AND s0, 0F ;isolate lower nibble
CALL hex_to_ASCII ;convert
LOAD s2, s0 ;lower nibble value in s2
RETURN
;
; Convert hexadecimal value provided in register s0 into ASCII character
;
;Register used s0
;
hex_to_ASCII: SUB s0, 0A ;test if value is in range 0 to 9
JUMP C, number_char
ADD s0, 07 ;ASCII char A to F in range 41 to 46
number_char: ADD s0, 3A ;ASCII char 0 to 9 in range 30 to 40
RETURN
;
;
; Display the two character HEX value of the register contents 's0' on the LCD
; at the current cursor position.
;
; Registers used s0, s1, s2, s3, s4, s5
;
display_hex_byte: CALL hex_byte_to_ASCII
LOAD s5, s3
CALL LCD_write_data
LOAD s5, s2
CALL LCD_write_data
RETURN
;
;
;
; Display the 32-bit value stored in 4 ascending memory locations as an 8 character
; HEX value at the current cursor position. Register s7 must contain the memory
; location of the most significant byte (which is also the highest address).
;
; Registers used s0, s1, s2, s3, s4, s5, s6, s7
;
display_hex_32_bit: LOAD s6, 04 ;4 bytes to display
disp32_loop: FETCH s0, (s7) ;read byte
CALL display_hex_byte ;display byte
SUB s7, 01 ;decrement pointer
SUB s6, 01 ;count bytes displayed
RETURN Z
JUMP disp32_loop
;
;
;**************************************************************************************
;LCD text messages
;**************************************************************************************
;
;
;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 'Generator' on LCD at current cursor position
;
disp_Generator: LOAD s5, character_G
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
LOAD s5, character_r
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
CALL display_space
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_2
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
; 0E = '00001' Display control, '1' display on, '1' 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, 0E ;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
;
;This routine will shift the complete display one position to the left.
;The cursor position and LCD memory contents will not change.
;
;
;Registers used s0, s1, s2, s3, s4, s5
;
LCD_shift_left: LOAD s5, 18 ;shift display left
CALL LCD_write_inst8
RETURN
;
;**************************************************************************************
;Interrupt Service Routine (ISR)
;**************************************************************************************
;
;Interrupts occur when the rotary control has been moved.
;
;The ISR captures the state of the direction which it writes to scratch pad memory (SPM).
;The most significant bit is also set at this location to provide a 'flag' to the
;main body of the program.
;
;
ISR: STORE s0, ISR_preserve_s0 ;preserve s0
INPUT s0, rotary_port ;read rotary encoder
OR s0, rotary_event ;set flag
STORE s0, rotary_status ;put result in SCM
FETCH s0, ISR_preserve_s0 ;restore s0
RETURNI ENABLE
;
;
;**************************************************************************************
;Interrupt Vector
;**************************************************************************************
;
ADDRESS 3FF
JUMP ISR
;
;