mirror of
https://github.com/pConst/basic_verilog.git
synced 2025-01-28 07:02:55 +08:00
1839 lines
79 KiB
Plaintext
1839 lines
79 KiB
Plaintext
|
/* Symbol Table */
|
||
|
// BCD_digit0 = CONSTANT: 3
|
||
|
// BCD_digit1 = CONSTANT: 4
|
||
|
// BCD_digit2 = CONSTANT: 5
|
||
|
// BCD_digit3 = CONSTANT: 6
|
||
|
// BCD_digit4 = CONSTANT: 7
|
||
|
// BCD_digit5 = CONSTANT: 8
|
||
|
// BCD_digit6 = CONSTANT: 9
|
||
|
// BCD_digit7 = CONSTANT: 10
|
||
|
// BCD_digit8 = CONSTANT: 11
|
||
|
// BCD_digit_convert = LABEL: 241
|
||
|
// BCD_to_integer = LABEL: 229
|
||
|
// DDS_control0 = CONSTANT: 28
|
||
|
// DDS_control0_port = CONSTANT: 2
|
||
|
// DDS_control1 = CONSTANT: 29
|
||
|
// DDS_control1_port = CONSTANT: 4
|
||
|
// DDS_control2 = CONSTANT: 30
|
||
|
// DDS_control2_port = CONSTANT: 8
|
||
|
// DDS_control3 = CONSTANT: 31
|
||
|
// DDS_control3_port = CONSTANT: 16
|
||
|
// DDS_scaling = CONSTANT: 32
|
||
|
// DDS_scaling_port = CONSTANT: 32
|
||
|
// ISR = LABEL: 568
|
||
|
// ISR_preserve_s0 = CONSTANT: 1
|
||
|
// LCD_DB4 = CONSTANT: 16
|
||
|
// LCD_DB5 = CONSTANT: 32
|
||
|
// LCD_DB6 = CONSTANT: 64
|
||
|
// LCD_DB7 = CONSTANT: 128
|
||
|
// LCD_E = CONSTANT: 1
|
||
|
// LCD_RS = CONSTANT: 4
|
||
|
// LCD_RW = CONSTANT: 2
|
||
|
// LCD_clear = LABEL: 550
|
||
|
// LCD_cursor = LABEL: 555
|
||
|
// LCD_drive = CONSTANT: 8
|
||
|
// LCD_input_port = CONSTANT: 1
|
||
|
// LCD_output_port = CONSTANT: 64
|
||
|
// LCD_pulse_E = LABEL: 466
|
||
|
// LCD_read_DB4 = CONSTANT: 16
|
||
|
// LCD_read_DB5 = CONSTANT: 32
|
||
|
// LCD_read_DB6 = CONSTANT: 64
|
||
|
// LCD_read_DB7 = CONSTANT: 128
|
||
|
// LCD_read_data8 = LABEL: 508
|
||
|
// LCD_reset = LABEL: 533
|
||
|
// LCD_shift_left = LABEL: 565
|
||
|
// LCD_write_data = LABEL: 491
|
||
|
// LCD_write_inst4 = LABEL: 472
|
||
|
// LCD_write_inst8 = LABEL: 476
|
||
|
// LED0 = CONSTANT: 1
|
||
|
// LED1 = CONSTANT: 2
|
||
|
// LED2 = CONSTANT: 4
|
||
|
// LED3 = CONSTANT: 8
|
||
|
// LED4 = CONSTANT: 16
|
||
|
// LED5 = CONSTANT: 32
|
||
|
// LED6 = CONSTANT: 64
|
||
|
// LED7 = CONSTANT: 128
|
||
|
// LED_pattern = CONSTANT: 2
|
||
|
// LED_port = CONSTANT: 128
|
||
|
// blank_100M_digit = LABEL: 190
|
||
|
// blank_10M_digit = LABEL: 196
|
||
|
// character_0 = CONSTANT: 48
|
||
|
// character_1 = CONSTANT: 49
|
||
|
// character_2 = CONSTANT: 50
|
||
|
// character_3 = CONSTANT: 51
|
||
|
// character_4 = CONSTANT: 52
|
||
|
// character_5 = CONSTANT: 53
|
||
|
// character_6 = CONSTANT: 54
|
||
|
// character_7 = CONSTANT: 55
|
||
|
// character_8 = CONSTANT: 56
|
||
|
// character_9 = CONSTANT: 57
|
||
|
// character_A = CONSTANT: 65
|
||
|
// character_B = CONSTANT: 66
|
||
|
// character_BS = CONSTANT: 8
|
||
|
// character_C = CONSTANT: 67
|
||
|
// character_CR = CONSTANT: 13
|
||
|
// character_D = CONSTANT: 68
|
||
|
// character_E = CONSTANT: 69
|
||
|
// character_F = CONSTANT: 70
|
||
|
// character_G = CONSTANT: 71
|
||
|
// character_H = CONSTANT: 72
|
||
|
// character_I = CONSTANT: 73
|
||
|
// character_J = CONSTANT: 74
|
||
|
// character_K = CONSTANT: 75
|
||
|
// character_L = CONSTANT: 76
|
||
|
// character_M = CONSTANT: 77
|
||
|
// character_N = CONSTANT: 78
|
||
|
// character_O = CONSTANT: 79
|
||
|
// character_P = CONSTANT: 80
|
||
|
// character_Q = CONSTANT: 81
|
||
|
// character_R = CONSTANT: 82
|
||
|
// character_S = CONSTANT: 83
|
||
|
// character_T = CONSTANT: 84
|
||
|
// character_U = CONSTANT: 85
|
||
|
// character_V = CONSTANT: 86
|
||
|
// character_W = CONSTANT: 87
|
||
|
// character_X = CONSTANT: 88
|
||
|
// character_Y = CONSTANT: 89
|
||
|
// character_Z = CONSTANT: 90
|
||
|
// character_a = CONSTANT: 97
|
||
|
// character_b = CONSTANT: 98
|
||
|
// character_c = CONSTANT: 99
|
||
|
// character_colon = CONSTANT: 58
|
||
|
// character_comma = CONSTANT: 44
|
||
|
// character_d = CONSTANT: 100
|
||
|
// character_divide = CONSTANT: 47
|
||
|
// character_dollar = CONSTANT: 36
|
||
|
// character_e = CONSTANT: 101
|
||
|
// character_equals = CONSTANT: 61
|
||
|
// character_exclaim = CONSTANT: 33
|
||
|
// character_f = CONSTANT: 102
|
||
|
// character_g = CONSTANT: 103
|
||
|
// character_greater_than = CONSTANT: 62
|
||
|
// character_h = CONSTANT: 104
|
||
|
// character_i = CONSTANT: 105
|
||
|
// character_j = CONSTANT: 106
|
||
|
// character_k = CONSTANT: 107
|
||
|
// character_l = CONSTANT: 108
|
||
|
// character_less_than = CONSTANT: 60
|
||
|
// character_m = CONSTANT: 109
|
||
|
// character_minus = CONSTANT: 45
|
||
|
// character_n = CONSTANT: 110
|
||
|
// character_o = CONSTANT: 111
|
||
|
// character_p = CONSTANT: 112
|
||
|
// character_plus = CONSTANT: 43
|
||
|
// character_q = CONSTANT: 113
|
||
|
// character_question = CONSTANT: 63
|
||
|
// character_r = CONSTANT: 114
|
||
|
// character_s = CONSTANT: 115
|
||
|
// character_semi_colon = CONSTANT: 59
|
||
|
// character_space = CONSTANT: 32
|
||
|
// character_stop = CONSTANT: 46
|
||
|
// character_t = CONSTANT: 116
|
||
|
// character_u = CONSTANT: 117
|
||
|
// character_v = CONSTANT: 118
|
||
|
// character_w = CONSTANT: 119
|
||
|
// character_x = CONSTANT: 120
|
||
|
// character_y = CONSTANT: 121
|
||
|
// character_z = CONSTANT: 122
|
||
|
// cold_start = LABEL: 0
|
||
|
// compute_DDS_words = LABEL: 132
|
||
|
// cursor_position = CONSTANT: 16
|
||
|
// dec_borrow = LABEL: 91
|
||
|
// dec_digit = LABEL: 85
|
||
|
// delay_1ms = LABEL: 451
|
||
|
// delay_1s = LABEL: 461
|
||
|
// delay_1us = LABEL: 442
|
||
|
// delay_1us_constant = CONSTANT: 11
|
||
|
// delay_20ms = LABEL: 456
|
||
|
// delay_40us = LABEL: 446
|
||
|
// disp32_loop = LABEL: 389
|
||
|
// disp_1M_digit = LABEL: 197
|
||
|
// disp_Frequency = LABEL: 395
|
||
|
// disp_Generator = LABEL: 414
|
||
|
// display_3_digits = LABEL: 216
|
||
|
// display_DDS_data = LABEL: 349
|
||
|
// display_digit = LABEL: 223
|
||
|
// display_freq = LABEL: 180
|
||
|
// display_hex_32_bit = LABEL: 388
|
||
|
// display_hex_byte = LABEL: 382
|
||
|
// display_space = LABEL: 226
|
||
|
// drive_DDS_words = LABEL: 169
|
||
|
// edit_digit_pointer = CONSTANT: 17
|
||
|
// edit_display = LABEL: 70
|
||
|
// edit_mode = LABEL: 69
|
||
|
// edit_point_update = LABEL: 66
|
||
|
// edit_wait = LABEL: 74
|
||
|
// end_edit_mode = LABEL: 123
|
||
|
// fill_max = LABEL: 118
|
||
|
// fill_min = LABEL: 99
|
||
|
// frequency0 = CONSTANT: 12
|
||
|
// frequency1 = CONSTANT: 13
|
||
|
// frequency2 = CONSTANT: 14
|
||
|
// frequency3 = CONSTANT: 15
|
||
|
// hex_byte_to_ASCII = LABEL: 365
|
||
|
// hex_to_ASCII = LABEL: 377
|
||
|
// inc_carry = LABEL: 110
|
||
|
// inc_digit = LABEL: 104
|
||
|
// move_left = LABEL: 47
|
||
|
// move_mode = LABEL: 31
|
||
|
// move_right = LABEL: 57
|
||
|
// move_wait = LABEL: 35
|
||
|
// next_BCD_to_int_digit = LABEL: 240
|
||
|
// next_digit_value = LABEL: 257
|
||
|
// normalise_loop = LABEL: 144
|
||
|
// number_char = LABEL: 380
|
||
|
// product0 = CONSTANT: 18
|
||
|
// product1 = CONSTANT: 19
|
||
|
// product2 = CONSTANT: 20
|
||
|
// product3 = CONSTANT: 21
|
||
|
// product4 = CONSTANT: 22
|
||
|
// product5 = CONSTANT: 23
|
||
|
// product6 = CONSTANT: 24
|
||
|
// product7 = CONSTANT: 25
|
||
|
// product8 = CONSTANT: 26
|
||
|
// product9 = CONSTANT: 27
|
||
|
// product_shift = LABEL: 316
|
||
|
// rotary_event = CONSTANT: 128
|
||
|
// rotary_left = CONSTANT: 1
|
||
|
// rotary_port = CONSTANT: 0
|
||
|
// rotary_press = CONSTANT: 2
|
||
|
// rotary_status = CONSTANT: 0
|
||
|
// s0 = REGISTER: 0
|
||
|
// s1 = REGISTER: 1
|
||
|
// s2 = REGISTER: 2
|
||
|
// s3 = REGISTER: 3
|
||
|
// s4 = REGISTER: 4
|
||
|
// s5 = REGISTER: 5
|
||
|
// s6 = REGISTER: 6
|
||
|
// s7 = REGISTER: 7
|
||
|
// s8 = REGISTER: 8
|
||
|
// s9 = REGISTER: 9
|
||
|
// sA = REGISTER: 10
|
||
|
// sB = REGISTER: 11
|
||
|
// sC = REGISTER: 12
|
||
|
// sD = REGISTER: 13
|
||
|
// sE = REGISTER: 14
|
||
|
// sF = REGISTER: 15
|
||
|
// scale_constant0 = CONSTANT: 98
|
||
|
// scale_constant1 = CONSTANT: 132
|
||
|
// scale_constant2 = CONSTANT: 17
|
||
|
// scale_constant3 = CONSTANT: 119
|
||
|
// scale_constant4 = CONSTANT: 204
|
||
|
// scale_constant5 = CONSTANT: 171
|
||
|
// scale_frequency = LABEL: 281
|
||
|
// scale_mult_bit = LABEL: 293
|
||
|
// set_line2 = LABEL: 561
|
||
|
// set_max_value = LABEL: 116
|
||
|
// set_min_value = LABEL: 97
|
||
|
// shift80_left = LABEL: 161
|
||
|
// skip_left = LABEL: 55
|
||
|
// skip_right = LABEL: 65
|
||
|
// store_DDS_words = LABEL: 154
|
||
|
// threedigit_loop = LABEL: 217
|
||
|
// wait_1ms = LABEL: 452
|
||
|
// wait_1s = LABEL: 462
|
||
|
// wait_1us = LABEL: 443
|
||
|
// wait_20ms = LABEL: 457
|
||
|
// wait_40us = LABEL: 447
|
||
|
// wait_switch_release = LABEL: 125
|
||
|
|
||
|
/* Program Code */
|
||
|
// #1: ; KCPSM3 Program - Control and calculation for Frequency Generator design using the
|
||
|
// #2: ; Spartan-3E Starter Kit.
|
||
|
// #3: ;
|
||
|
// #4: ; Interfaces with the rotary encoder and LCD display to enable a frequency to be set.
|
||
|
// #5: ; Converts the BCD frequency value into a binary integer and then performs the high
|
||
|
// #6: ; precision calculation necessary to derive the control numbers required by the high
|
||
|
// #7: ; performance Direct Digital Synthesis (DDS) circuit implemented in hardware.
|
||
|
// #8: ;
|
||
|
// #9: ; LEDs are connected and used as edit mode indicators.
|
||
|
// #10: ;
|
||
|
// #11: ; Substantial comments are included in line with the code below and should be used
|
||
|
// #12: ; in conjunction with the documentation provided with the complete reference design.
|
||
|
// #13: ;
|
||
|
// #14: ;
|
||
|
// #15: ;
|
||
|
// #16: ; Ken Chapman - Xilinx Ltd
|
||
|
// #17: ;
|
||
|
// #18: ; Version v1.00 - 13th July 2006
|
||
|
// #19: ;
|
||
|
// #20: ;**************************************************************************************
|
||
|
// #21: ;Port definitions
|
||
|
// #22: ;**************************************************************************************
|
||
|
// #23: ;
|
||
|
// #24: ;
|
||
|
// #25: ;
|
||
|
// #26: CONSTANT(LED_port,128) ;8 simple LEDs
|
||
|
// #27: CONSTANT(LED0,1) ; LED 0 - bit0
|
||
|
// #28: CONSTANT(LED1,2) ; 1 - bit1
|
||
|
// #29: CONSTANT(LED2,4) ; 2 - bit2
|
||
|
// #30: CONSTANT(LED3,8) ; 3 - bit3
|
||
|
// #31: CONSTANT(LED4,16) ; 4 - bit4
|
||
|
// #32: CONSTANT(LED5,32) ; 5 - bit5
|
||
|
// #33: CONSTANT(LED6,64) ; 6 - bit6
|
||
|
// #34: CONSTANT(LED7,128) ; 7 - bit7
|
||
|
// #35: ;
|
||
|
// #36: ;
|
||
|
// #37: CONSTANT(rotary_port,0) ;Read status of rotary encoder
|
||
|
// #38: CONSTANT(rotary_left,1) ; Direction of last move Left=1 Right=0 - bit0
|
||
|
// #39: CONSTANT(rotary_press,2) ; Centre press contact (active High) - bit1
|
||
|
// #40: ;
|
||
|
// #41: ;
|
||
|
// #42: ;LCD interface ports
|
||
|
// #43: ;
|
||
|
// #44: ;The master enable signal is not used by the LCD display itself
|
||
|
// #45: ;but may be required to confirm that LCD communication is active.
|
||
|
// #46: ;This is required on the Spartan-3E Starter Kit if the StrataFLASH
|
||
|
// #47: ;is used because it shares the same data pins and conflicts must be avoided.
|
||
|
// #48: ;
|
||
|
// #49: CONSTANT(LCD_output_port,64) ;LCD character module output data and control
|
||
|
// #50: CONSTANT(LCD_E,1) ; active High Enable E - bit0
|
||
|
// #51: CONSTANT(LCD_RW,2) ; Read=1 Write=0 RW - bit1
|
||
|
// #52: CONSTANT(LCD_RS,4) ; Instruction=0 Data=1 RS - bit2
|
||
|
// #53: CONSTANT(LCD_drive,8) ; Master enable (active High) - bit3
|
||
|
// #54: CONSTANT(LCD_DB4,16) ; 4-bit Data DB4 - bit4
|
||
|
// #55: CONSTANT(LCD_DB5,32) ; interface Data DB5 - bit5
|
||
|
// #56: CONSTANT(LCD_DB6,64) ; Data DB6 - bit6
|
||
|
// #57: CONSTANT(LCD_DB7,128) ; Data DB7 - bit7
|
||
|
// #58: ;
|
||
|
// #59: ;
|
||
|
// #60: CONSTANT(LCD_input_port,1) ;LCD character module input data
|
||
|
// #61: CONSTANT(LCD_read_DB4,16) ; 4-bit Data DB4 - bit4
|
||
|
// #62: CONSTANT(LCD_read_DB5,32) ; interface Data DB5 - bit5
|
||
|
// #63: CONSTANT(LCD_read_DB6,64) ; Data DB6 - bit6
|
||
|
// #64: CONSTANT(LCD_read_DB7,128) ; Data DB7 - bit7
|
||
|
// #65: ;
|
||
|
// #66: ;
|
||
|
// #67: ;
|
||
|
// #68: ;DDS control ports
|
||
|
// #69: ;
|
||
|
// #70: ;DDS control word is 32-bits
|
||
|
// #71: ;
|
||
|
// #72: CONSTANT(DDS_control0_port,2) ; dds_control_word(7:0)
|
||
|
// #73: CONSTANT(DDS_control1_port,4) ; dds_control_word(15:8)
|
||
|
// #74: CONSTANT(DDS_control2_port,8) ; dds_control_word(23:16)
|
||
|
// #75: CONSTANT(DDS_control3_port,16) ; dds_control_word(31:24)
|
||
|
// #76: ;
|
||
|
// #77: ;Frequency scaling control word is 5-bits
|
||
|
// #78: ;
|
||
|
// #79: CONSTANT(DDS_scaling_port,32) ; dds_scaling_word(4:0)
|
||
|
// #80: ;
|
||
|
// #81: ;
|
||
|
// #82: ;**************************************************************************************
|
||
|
// #83: ;Special Register usage
|
||
|
// #84: ;**************************************************************************************
|
||
|
// #85: ;
|
||
|
// #86: ;**************************************************************************************
|
||
|
// #87: ;Scratch Pad Memory Locations
|
||
|
// #88: ;**************************************************************************************
|
||
|
// #89: ;
|
||
|
// #90: CONSTANT(rotary_status,0) ;Status of rotary encoder
|
||
|
// #91: CONSTANT(rotary_event,128) ; flag set by interrupt in 'rotary_status' - bit7
|
||
|
// #92: ;
|
||
|
// #93: CONSTANT(ISR_preserve_s0,1) ;Preserve s0 contents during ISR
|
||
|
// #94: ;
|
||
|
// #95: CONSTANT(LED_pattern,2) ;LED pattern used in rotation mode
|
||
|
// #96: ;
|
||
|
// #97: ;
|
||
|
// #98: ;BCD digits representing selected and displayed frequency
|
||
|
// #99: ;
|
||
|
// #100: CONSTANT(BCD_digit0,3) ; value 1
|
||
|
// #101: CONSTANT(BCD_digit1,4) ; 10
|
||
|
// #102: CONSTANT(BCD_digit2,5) ; 100
|
||
|
// #103: CONSTANT(BCD_digit3,6) ; 1,000
|
||
|
// #104: CONSTANT(BCD_digit4,7) ; 10,000
|
||
|
// #105: CONSTANT(BCD_digit5,8) ; 100,000
|
||
|
// #106: CONSTANT(BCD_digit6,9) ; 1,000,000
|
||
|
// #107: CONSTANT(BCD_digit7,10) ; 10,000,000
|
||
|
// #108: CONSTANT(BCD_digit8,11) ; 100,000,000
|
||
|
// #109: ;
|
||
|
// #110: ;
|
||
|
// #111: ;Binary integer representation of BCD value
|
||
|
// #112: ;
|
||
|
// #113: CONSTANT(frequency0,12) ;LS byte
|
||
|
// #114: CONSTANT(frequency1,13)
|
||
|
// #115: CONSTANT(frequency2,14)
|
||
|
// #116: CONSTANT(frequency3,15) ;MS byte
|
||
|
// #117: ;
|
||
|
// #118: ;
|
||
|
// #119: ;Control of frequency selection values
|
||
|
// #120: ;
|
||
|
// #121: CONSTANT(cursor_position,16) ; Pointer to edit position on LCD
|
||
|
// #122: CONSTANT(edit_digit_pointer,17) ; BCD digit to be changed
|
||
|
// #123: ;
|
||
|
// #124: ;
|
||
|
// #125: ;
|
||
|
// #126: ;80-bit product resulting from 32-bit frequency x 48-bit scaling constant
|
||
|
// #127: ;
|
||
|
// #128: CONSTANT(product0,18) ;LS byte
|
||
|
// #129: CONSTANT(product1,19)
|
||
|
// #130: CONSTANT(product2,20)
|
||
|
// #131: CONSTANT(product3,21)
|
||
|
// #132: CONSTANT(product4,22)
|
||
|
// #133: CONSTANT(product5,23)
|
||
|
// #134: CONSTANT(product6,24)
|
||
|
// #135: CONSTANT(product7,25)
|
||
|
// #136: CONSTANT(product8,26)
|
||
|
// #137: CONSTANT(product9,27) ;MS byte
|
||
|
// #138: ;
|
||
|
// #139: ;Local copies of the DDS control word and DDS scaling word
|
||
|
// #140: ;
|
||
|
// #141: CONSTANT(DDS_control0,28) ; dds_control_word(7:0)
|
||
|
// #142: CONSTANT(DDS_control1,29) ; dds_control_word(15:8)
|
||
|
// #143: CONSTANT(DDS_control2,30) ; dds_control_word(23:16)
|
||
|
// #144: CONSTANT(DDS_control3,31) ; dds_control_word(31:24)
|
||
|
// #145: CONSTANT(DDS_scaling,32) ; dds_scaling_word(4:0)
|
||
|
// #146: ;
|
||
|
// #147: ;**************************************************************************************
|
||
|
// #148: ; Useful data constants
|
||
|
// #149: ;**************************************************************************************
|
||
|
// #150: ;
|
||
|
// #151: ; To convert the frequency into a DDS control value a high precision scaling
|
||
|
// #152: ; factor is used. This is a 48-bit number which converts the frequency presented
|
||
|
// #153: ; as an 32-bit integer into the 32-bit value required by the phase accumulator
|
||
|
// #154: ; to synthesize the desired frequency. The scaling factor is derived using the
|
||
|
// #155: ; following method. First I will consider the scaling factor which results in the
|
||
|
// #156: ; desired frequency being generated directly at the output of the phase accumulator
|
||
|
// #157: ; which is suitable for low frequencies in which a few ns of jitter is acceptable.
|
||
|
// #158: ;
|
||
|
// #159: ; 'Fpa' is frequency generated by the MSB of the phase accumulator.
|
||
|
// #160: ; 'p' is number of phase accumulator which in this case is 32 bits.
|
||
|
// #161: ; 'clk' is the input clock frequency to the phase accumulator which is 200MHz.
|
||
|
// #162: ; 'N' is the DDS control word value which is also 'p' bits (32 in this case).
|
||
|
// #163: ;
|
||
|
// #164: ; Frequency at MSB of phase accumulator is then
|
||
|
// #165: ;
|
||
|
// #166: ; Fpa = clk x N / (2^p)
|
||
|
// #167: ;
|
||
|
// #168: ; Note that the maximum value allowed for 'N' is (2^p)/2 which results in Fpa=clk/2.
|
||
|
// #169: ; for 'N' greater than that value 'Fpa' would decrease in frequency (aliasing).
|
||
|
// #170: ;
|
||
|
// #171: ;
|
||
|
// #172: ; By simple reorganisation of the equation we can compute 'N'
|
||
|
// #173: ;
|
||
|
// #174: ; N = Fpa x (2^p) / clk
|
||
|
// #175: ;
|
||
|
// #176: ;
|
||
|
// #177: ; Now it is easier to approach the next step using specific example.
|
||
|
// #178: ;
|
||
|
// #179: ; So for a frequency of Fpa = 1MHz then
|
||
|
// #180: ; N = 1MHz x (2^32)/200MHz = 21474836.48
|
||
|
// #181: ;
|
||
|
// #182: ; We must use the nearest 32-bit integer value 21474836 and this in turn
|
||
|
// #183: ; is best viewed as the 32-bit hexadecimal value 0147AE14.
|
||
|
// #184: ;
|
||
|
// #185: ; In this case the value we have to work with is a 32-bit integer frequency
|
||
|
// #186: ; value of 1 million which is 000F4240.
|
||
|
// #187: ;
|
||
|
// #188: ; So now we need to translate the value 000F4240 into 0147AE14. This is
|
||
|
// #189: ; where a 48-bit scaling value is used together with a full precision multiplier
|
||
|
// #190: ; as this ensures adequate accuracy of the final frequency.
|
||
|
// #191: ;
|
||
|
// #192: ; 32-bit frequency value ffffffff
|
||
|
// #193: ; 48-bit scaling value x ssssssssssss
|
||
|
// #194: ; --------------------
|
||
|
// #195: ; 80-bit product nnnnnnnnnnnnnnnnnnnn
|
||
|
// #196: ;
|
||
|
// #197: ; The art is to organise the scaling factor into the range where the most is made of
|
||
|
// #198: ; the 48-bit resolution available but which will result in the correct 32-bit output.
|
||
|
// #199: ; The way this is achieved is the select an appropriate 32-bits from the available 80-bit
|
||
|
// #200: ; product for use as 'N' and truncate 'y' least significant bits.
|
||
|
// #201: ;
|
||
|
// #202: ; From this we can deduce that for a target frequency 'Ft' at the input then the
|
||
|
// #203: ; scaling value 'S' is given by
|
||
|
// #204: ;
|
||
|
// #205: ; S = N x (2^y) / Ft with the condition that S < 2^48 but as large as possible
|
||
|
// #206: ;
|
||
|
// #207: ; For best accuracy we calculate 'S' using the full precision value of 'N' divided
|
||
|
// #208: ; by Ft and then multiply continuously by 2 until we reach the biggest value less
|
||
|
// #209: ; that 2^48. The number of multiplications by 2 indicating the value of 'y'.
|
||
|
// #210: ;
|
||
|
// #211: ; In this case we find that 'y' is 43.....
|
||
|
// #212: ; S = 21474836.48 x (2^43) / 1000000 = 21.47483648 x (2^43)
|
||
|
// #213: ; = 188894659314785.80854784
|
||
|
// #214: ;
|
||
|
// #215: ; ...round to nearest integer and convert to hexadecimal S = ABCC77118462
|
||
|
// #216: ;
|
||
|
// #217: ; N will be taken from the 80 bit product by removing the 43 LSBs and the 5 MSBs
|
||
|
// #218: ; to leave the 32 active bits required. This is best achieved by shifting left
|
||
|
// #219: ; by 5 places (multiply by 2^5=32) and keeping the upper 32-bits.
|
||
|
// #220: ;
|
||
|
// #221: ;
|
||
|
// #222: ; Sanity check....
|
||
|
// #223: ; Note that most calculators do not support >64 bit values to you will either
|
||
|
// #224: ; need to decompose your calculation and perform some of it manually or trust
|
||
|
// #225: ; the PicoBlaze implementation :-)
|
||
|
// #226: ;
|
||
|
// #227: ; Ft = 1MHz = 000F4240
|
||
|
// #228: ; S = x ABCC77118462
|
||
|
// #229: ; --------------------
|
||
|
// #230: ; 000A3D70A3D70A405C80
|
||
|
// #231: ;
|
||
|
// #232: ; shift left 5 places x 20
|
||
|
// #233: ; --------------------
|
||
|
// #234: ; 0147AE147AE1480B9000
|
||
|
// #235: ;
|
||
|
// #236: ; As expected, the most significant 32-bit (4 bytes) are 0147AE14 hex which is
|
||
|
// #237: ; the DDS control word for 1MHz calculated previously.
|
||
|
// #238: ;
|
||
|
// #239: ; ***
|
||
|
// #240: ;
|
||
|
// #241: ; Now I will consider how this needs to be modified for the circuit presented
|
||
|
// #242: ; which has a second DCM connected to the output of the phase accumulator to
|
||
|
// #243: ; multiply the synthesized frequency and reduce cycle to cycle jitter at
|
||
|
// #244: ; the same time. There is then a clock divider circuit connected to the output
|
||
|
// #245: ; of the DCM which allows lower frequencies to be formed a different way (more of
|
||
|
// #246: ; that later). As a minimum that divider circuit will divide by 2 which ensures that
|
||
|
// #247: ; a square wave is presented to the clocked put pin. So in this circuit the fundamental
|
||
|
// #248: ; multiplication factor is 8 formed by a 16 times multiplication by the DCM (256/16) and
|
||
|
// #249: ; then a divide by 2.
|
||
|
// #250: ;
|
||
|
// #251: ; The overall multiplication factor of this sebsequent circuit means that for final
|
||
|
// #252: ; output from the DCM to be the desired frequency, the output from the phase accumulator
|
||
|
// #253: ; needs to be the same number of times smaller. This is not a bad thing because the
|
||
|
// #254: ; percentage jitter of waveforms produced by the phase accumulator is better for lower
|
||
|
// #255: ; frequencies made from more clock cycles.
|
||
|
// #256: ;
|
||
|
// #257: ; So we modify the basic equation to
|
||
|
// #258: ;
|
||
|
// #259: ; Fout = Frequency at output of DCM
|
||
|
// #260: ; M = Multiplying factor of DCM
|
||
|
// #261: ;
|
||
|
// #262: ; Fout = M x Fpa = M x clk x N / (2^p)
|
||
|
// #263: ;
|
||
|
// #264: ;
|
||
|
// #265: ; By simple reorganisation of the equation we can compute 'N'
|
||
|
// #266: ;
|
||
|
// #267: ; N = Fout x (2^p) / (clk x M)
|
||
|
// #268: ;
|
||
|
// #269: ;
|
||
|
// #270: ; In this design M=8, p=32, clk=200MHz
|
||
|
// #271: ;
|
||
|
// #272: ; So now consider generating a nominal maximum frequency of 100MHz which will require
|
||
|
// #273: ; the frequency synthesized by the phase accumulator to be 12.5MHz.
|
||
|
// #274: ;
|
||
|
// #275: ; N = 100MHz x (2^32) / (200MHz x 8) = 268435456 = 10000000 Hex
|
||
|
// #276: ;
|
||
|
// #277: ; This all seems like a very convenient number but it simply reflects that 12.5MHz
|
||
|
// #278: ; is a perfect division of the 200MHz clock and that that output from the phase
|
||
|
// #279: ; accumulator will be formed perfectly of 16 of the 200MHz clock periods every time
|
||
|
// #280: ; (8 Low and 8 High) with no additional jitter.
|
||
|
// #281: ;
|
||
|
// #282: ; So now we work out the scaling factor with the same rules as used previously that
|
||
|
// #283: ; the scaling factor should be as large as possible within the 48-bits allocated.
|
||
|
// #284: ;
|
||
|
// #285: ; S = N x (2^y) / Ft with the condition that S < 2^48 but as large as possible
|
||
|
// #286: ;
|
||
|
// #287: ; In this case Ft = 100MHz = 055FE100 and the biggest value for S is found when using
|
||
|
// #288: ; y=46
|
||
|
// #289: ;
|
||
|
// #290: ; S = 268435456 x (2^46) / 100000000 = 2.68435456 x (2^46)
|
||
|
// #291: ; = 188894659314785.80854784
|
||
|
// #292: ;
|
||
|
// #293: ; round to 188894659314786 = ABCC77118462
|
||
|
// #294: ;
|
||
|
// #295: ; Actually this is the exact same scaling constant as previously because the
|
||
|
// #296: ; frequency to be synthesized by the phase accumulator is 8 times smaller but the
|
||
|
// #297: ; value of 'S' is deliberate scaled to be as large as possible. In fact, 'S' in this
|
||
|
// #298: ; case has been scaled up by a factor of 8 to arrive at the same value. So after
|
||
|
// #299: ; using the scaling constant to form the 80 bit product, this time we will remove
|
||
|
// #300: ; the 46 LSBs and the 2 MSBs to leave the 32 active bits required. This is best
|
||
|
// #301: ; achieved by shifting left by 2 places (multiply by 2^2=4) and keeping the upper
|
||
|
// #302: ; 32-bits (last time we multiplied by 32 which was 8 times more).
|
||
|
// #303: ;
|
||
|
// #304: ;
|
||
|
// #305: ; Sanity check....
|
||
|
// #306: ;
|
||
|
// #307: ; Ft = 100MHz = 055FE100
|
||
|
// #308: ; S = x ABCC77118462
|
||
|
// #309: ; --------------------
|
||
|
// #310: ; 04000000000001242200
|
||
|
// #311: ;
|
||
|
// #312: ; shift left 5 places x 20
|
||
|
// #313: ; --------------------
|
||
|
// #314: ; 1000000000001C908800
|
||
|
// #315: ;
|
||
|
// #316: ; As expected, the most significant 32-bit (4 bytes) are 10000000 hex which is
|
||
|
// #317: ; the DDS control word for 12.5MHz at the phase accumulator output calculated
|
||
|
// #318: ; previously.
|
||
|
// #319: ;
|
||
|
// #320: ;
|
||
|
// #321: ; ********
|
||
|
// #322: ;
|
||
|
// #323: ;
|
||
|
// #324: ; 48-bit Scaling factor constant to generate the phase accumulator control word
|
||
|
// #325: ; from the integer frequency value.
|
||
|
// #326: ;
|
||
|
// #327: ; S = AB CC 77 11 84 62
|
||
|
// #328: ;
|
||
|
// #329: ; Notes
|
||
|
// #330: ;
|
||
|
// #331: ; The 80-bit product must be shifted left 5 times and then most significant 32-bits
|
||
|
// #332: ; used to provide DDS control word if the frequency required is to be synthesized
|
||
|
// #333: ; directly at the output of the phase accumulator.
|
||
|
// #334: ;
|
||
|
// #335: ; The 80-bit product must be shifted left 2 times and then most significant 32-bits
|
||
|
// #336: ; used to provide DDS control word if the frequency required is to be synthesized
|
||
|
// #337: ; by the phase accumulator followed by a multiplying DCM and divider with overall
|
||
|
// #338: ; frequency gain of 8 times.
|
||
|
// #339: ;
|
||
|
// #340: CONSTANT(scale_constant0,98) ;LS byte
|
||
|
// #341: CONSTANT(scale_constant1,132)
|
||
|
// #342: CONSTANT(scale_constant2,17)
|
||
|
// #343: CONSTANT(scale_constant3,119)
|
||
|
// #344: CONSTANT(scale_constant4,CC)
|
||
|
// #345: CONSTANT(scale_constant5,AB) ;MS byte
|
||
|
// #346: ;
|
||
|
// #347: ;
|
||
|
// #348: ;
|
||
|
// #349: ; ************************
|
||
|
// #350: ;
|
||
|
// #351: ;Constant to define a software delay of 1us. This must be adjusted to reflect the
|
||
|
// #352: ;clock applied to KCPSM3. Every instruction executes in 2 clock cycles making the
|
||
|
// #353: ;calculation highly predictable. The '6' in the following equation even allows for
|
||
|
// #354: ;'CALL delay_1us' instruction in the initiating code.
|
||
|
// #355: ;
|
||
|
// #356: ; delay_1us_constant = (clock_rate - 6)/4 Where 'clock_rate' is in MHz
|
||
|
// #357: ;
|
||
|
// #358: ;Example: For a 50MHz clock the constant value is (10-6)/4 = 11 (0B Hex).
|
||
|
// #359: ;For clock rates below 10MHz the value of 1 must be used and the operation will
|
||
|
// #360: ;become lower than intended.
|
||
|
// #361: ;
|
||
|
// #362: CONSTANT(delay_1us_constant,11)
|
||
|
// #363: ;
|
||
|
// #364: ;
|
||
|
// #365: ;
|
||
|
// #366: ;ASCII table
|
||
|
// #367: ;
|
||
|
// #368: CONSTANT(character_a,97)
|
||
|
// #369: CONSTANT(character_b,98)
|
||
|
// #370: CONSTANT(character_c,99)
|
||
|
// #371: CONSTANT(character_d,100)
|
||
|
// #372: CONSTANT(character_e,101)
|
||
|
// #373: CONSTANT(character_f,102)
|
||
|
// #374: CONSTANT(character_g,103)
|
||
|
// #375: CONSTANT(character_h,104)
|
||
|
// #376: CONSTANT(character_i,105)
|
||
|
// #377: CONSTANT(character_j,106)
|
||
|
// #378: CONSTANT(character_k,107)
|
||
|
// #379: CONSTANT(character_l,108)
|
||
|
// #380: CONSTANT(character_m,109)
|
||
|
// #381: CONSTANT(character_n,110)
|
||
|
// #382: CONSTANT(character_o,111)
|
||
|
// #383: CONSTANT(character_p,112)
|
||
|
// #384: CONSTANT(character_q,113)
|
||
|
// #385: CONSTANT(character_r,114)
|
||
|
// #386: CONSTANT(character_s,115)
|
||
|
// #387: CONSTANT(character_t,116)
|
||
|
// #388: CONSTANT(character_u,117)
|
||
|
// #389: CONSTANT(character_v,118)
|
||
|
// #390: CONSTANT(character_w,119)
|
||
|
// #391: CONSTANT(character_x,120)
|
||
|
// #392: CONSTANT(character_y,121)
|
||
|
// #393: CONSTANT(character_z,122)
|
||
|
// #394: CONSTANT(character_A,65)
|
||
|
// #395: CONSTANT(character_B,66)
|
||
|
// #396: CONSTANT(character_C,67)
|
||
|
// #397: CONSTANT(character_D,68)
|
||
|
// #398: CONSTANT(character_E,69)
|
||
|
// #399: CONSTANT(character_F,70)
|
||
|
// #400: CONSTANT(character_G,71)
|
||
|
// #401: CONSTANT(character_H,72)
|
||
|
// #402: CONSTANT(character_I,73)
|
||
|
// #403: CONSTANT(character_J,74)
|
||
|
// #404: CONSTANT(character_K,75)
|
||
|
// #405: CONSTANT(character_L,76)
|
||
|
// #406: CONSTANT(character_M,77)
|
||
|
// #407: CONSTANT(character_N,78)
|
||
|
// #408: CONSTANT(character_O,79)
|
||
|
// #409: CONSTANT(character_P,80)
|
||
|
// #410: CONSTANT(character_Q,81)
|
||
|
// #411: CONSTANT(character_R,82)
|
||
|
// #412: CONSTANT(character_S,83)
|
||
|
// #413: CONSTANT(character_T,84)
|
||
|
// #414: CONSTANT(character_U,85)
|
||
|
// #415: CONSTANT(character_V,86)
|
||
|
// #416: CONSTANT(character_W,87)
|
||
|
// #417: CONSTANT(character_X,88)
|
||
|
// #418: CONSTANT(character_Y,89)
|
||
|
// #419: CONSTANT(character_Z,90)
|
||
|
// #420: CONSTANT(character_0,48)
|
||
|
// #421: CONSTANT(character_1,49)
|
||
|
// #422: CONSTANT(character_2,50)
|
||
|
// #423: CONSTANT(character_3,51)
|
||
|
// #424: CONSTANT(character_4,52)
|
||
|
// #425: CONSTANT(character_5,53)
|
||
|
// #426: CONSTANT(character_6,54)
|
||
|
// #427: CONSTANT(character_7,55)
|
||
|
// #428: CONSTANT(character_8,56)
|
||
|
// #429: CONSTANT(character_9,57)
|
||
|
// #430: CONSTANT(character_colon,58)
|
||
|
// #431: CONSTANT(character_stop,46)
|
||
|
// #432: CONSTANT(character_semi_colon,59)
|
||
|
// #433: CONSTANT(character_minus,45)
|
||
|
// #434: CONSTANT(character_divide,47) ;'/'
|
||
|
// #435: CONSTANT(character_plus,43)
|
||
|
// #436: CONSTANT(character_comma,44)
|
||
|
// #437: CONSTANT(character_less_than,60)
|
||
|
// #438: CONSTANT(character_greater_than,62)
|
||
|
// #439: CONSTANT(character_equals,61)
|
||
|
// #440: CONSTANT(character_space,32)
|
||
|
// #441: CONSTANT(character_CR,13) ;carriage return
|
||
|
// #442: CONSTANT(character_question,63) ;'?'
|
||
|
// #443: CONSTANT(character_dollar,36)
|
||
|
// #444: CONSTANT(character_exclaim,33) ;'!'
|
||
|
// #445: CONSTANT(character_BS,8) ;Back Space command character
|
||
|
// #446: ;
|
||
|
// #447: ;
|
||
|
// #448: ;
|
||
|
// #449: ;
|
||
|
// #450: ;
|
||
|
// #451: ;**************************************************************************************
|
||
|
// #452: ;Initialise the system
|
||
|
// #453: ;**************************************************************************************
|
||
|
// #454: ;
|
||
|
// @000 #455: [cold_start]
|
||
|
30215 // @000 #455: CALL(LCD_reset) ;initialise LCD display
|
||
|
// #456: ;
|
||
|
// #457: ;Write 'Frequency Generator' to LCD display and display for 4 seconds
|
||
|
// #458: ;
|
||
|
00510 // @001 #459: LOAD(s5,16) ;Line 1 position 0
|
||
|
3022b // @002 #460: CALL(LCD_cursor)
|
||
|
3018b // @003 #461: CALL(disp_Frequency)
|
||
|
00522 // @004 #462: LOAD(s5,34) ;Line 2 position 2
|
||
|
3022b // @005 #463: CALL(LCD_cursor)
|
||
|
3019e // @006 #464: CALL(disp_Generator)
|
||
|
301cd // @007 #465: CALL(delay_1s) ;wait 4 seconds
|
||
|
301cd // @008 #466: CALL(delay_1s)
|
||
|
301cd // @009 #467: CALL(delay_1s)
|
||
|
301cd // @00a #468: CALL(delay_1s)
|
||
|
30226 // @00b #469: CALL(LCD_clear) ;clear screen
|
||
|
// #470: ;
|
||
|
// #471: ;
|
||
|
// #472: ;Initial frequency of 100MHz
|
||
|
// #473: ;
|
||
|
00000 // @00c #474: LOAD(s0,0)
|
||
|
00101 // @00d #475: LOAD(s1,1)
|
||
|
2e003 // @00e #476: STORE(s0,BCD_digit0)
|
||
|
2e004 // @00f #477: STORE(s0,BCD_digit1)
|
||
|
2e005 // @010 #478: STORE(s0,BCD_digit2)
|
||
|
2e006 // @011 #479: STORE(s0,BCD_digit3)
|
||
|
2e007 // @012 #480: STORE(s0,BCD_digit4)
|
||
|
2e008 // @013 #481: STORE(s0,BCD_digit5)
|
||
|
2e009 // @014 #482: STORE(s0,BCD_digit6)
|
||
|
2e00a // @015 #483: STORE(s0,BCD_digit7)
|
||
|
2e10b // @016 #484: STORE(s1,BCD_digit8)
|
||
|
// #485: ;
|
||
|
00004 // @017 #486: LOAD(s0,4) ;Start position for editing frequency is 1MHz digit
|
||
|
2e010 // @018 #487: STORE(s0,cursor_position)
|
||
|
00009 // @019 #488: LOAD(s0,BCD_digit6)
|
||
|
2e011 // @01a #489: STORE(s0,edit_digit_pointer)
|
||
|
// #490: ;
|
||
|
// #491: ;
|
||
|
3c001 // @01b #492: ENABLE(INTERRUPT) ;interrupts are used to detect rotary controller
|
||
|
301c3 // @01c #493: CALL(delay_1ms)
|
||
|
00000 // @01d #494: LOAD(s0,0) ;clear the status of any spurious rotary events
|
||
|
2e000 // @01e #495: STORE(s0,rotary_status) ; as a result of system turning on.
|
||
|
// #496: ;
|
||
|
// #497: ;**************************************************************************************
|
||
|
// #498: ; Main program
|
||
|
// #499: ;**************************************************************************************
|
||
|
// #500: ;
|
||
|
// #501: ; The main program is centred on the task of editing the frequency. It waits until the
|
||
|
// #502: ; rotary control is used and then makes the appropriate changes. If the actual digit
|
||
|
// #503: ; digit value is changed then the calculation to drive the DDS is performed each time.
|
||
|
// #504: ;
|
||
|
// #505: ; The start state is that of allowing the edit cursor position to be moved. Rotary
|
||
|
// #506: ; inputs are detected by the interrupt service routine and set a flag bit which the
|
||
|
// #507: ; main program then uses to adjust the cursor position and pointer to the corresponding
|
||
|
// #508: ; BCD digit in memory.
|
||
|
// #509: ;
|
||
|
// #510: ; A press of the rotary control is detected by polling and used to change to the digit
|
||
|
// #511: ; editing mode.
|
||
|
// #512: ;
|
||
|
// #513: ;
|
||
|
// @01f #514: [move_mode]
|
||
|
30084 // @01f #514: CALL(compute_DDS_words) ;compute DDS control values
|
||
|
300b4 // @020 #515: CALL(display_freq) ;refresh display with cursor position shown
|
||
|
00001 // @021 #516: LOAD(s0,LED0) ;indicate move mode on LEDs
|
||
|
2c080 // @022 #517: OUTPUT(s0,LED_port)
|
||
|
// @023 #518: [move_wait]
|
||
|
04000 // @023 #518: INPUT(s0,rotary_port) ;read rotary encoder
|
||
|
12002 // @024 #519: TEST(s0,rotary_press) ;test for press of button which changes mode
|
||
|
35445 // @025 #520: JUMP(NZ,edit_mode)
|
||
|
06000 // @026 #521: FETCH(s0,rotary_status) ;check for any rotation of rotary control
|
||
|
12080 // @027 #522: TEST(s0,rotary_event)
|
||
|
35023 // @028 #523: JUMP(Z,move_wait)
|
||
|
// #524: ;
|
||
|
0a07f // @029 #525: AND(s0,127) ;clear flag now that action it is being processed
|
||
|
2e000 // @02a #526: STORE(s0,rotary_status)
|
||
|
06a10 // @02b #527: FETCH(sA,cursor_position) ;read current position
|
||
|
06b11 // @02c #528: FETCH(sB,edit_digit_pointer)
|
||
|
12001 // @02d #529: TEST(s0,rotary_left) ;determine direction to move cursor
|
||
|
35039 // @02e #530: JUMP(Z,move_right)
|
||
|
// #531: ;
|
||
|
// @02f #532: [move_left]
|
||
|
14b0b // @02f #532: COMPARE(sB,BCD_digit8) ;can not move left of 100MHz digit
|
||
|
3501f // @030 #533: JUMP(Z,move_mode)
|
||
|
18b01 // @031 #534: ADD(sB,1) ;move to next higher BCD digit
|
||
|
1ca01 // @032 #535: SUB(sA,1) ;move cursor to match digit to be edited
|
||
|
14a09 // @033 #536: COMPARE(sA,9) ;must skip over space separator
|
||
|
35037 // @034 #537: JUMP(Z,skip_left)
|
||
|
14a05 // @035 #538: COMPARE(sA,5) ;must skip over decimal point
|
||
|
35442 // @036 #539: JUMP(NZ,edit_point_update)
|
||
|
// @037 #540: [skip_left]
|
||
|
1ca01 // @037 #540: SUB(sA,1) ;move cursor further left
|
||
|
34042 // @038 #541: JUMP(edit_point_update)
|
||
|
// #542: ;
|
||
|
// @039 #543: [move_right]
|
||
|
14b03 // @039 #543: COMPARE(sB,BCD_digit0) ;can not move right of 1Hz digit
|
||
|
3501f // @03a #544: JUMP(Z,move_mode)
|
||
|
1cb01 // @03b #545: SUB(sB,1) ;move to next lower BCD digit
|
||
|
18a01 // @03c #546: ADD(sA,1) ;move cursor to match digit to be edited
|
||
|
14a09 // @03d #547: COMPARE(sA,9) ;must skip over space separator
|
||
|
35041 // @03e #548: JUMP(Z,skip_right)
|
||
|
14a05 // @03f #549: COMPARE(sA,5) ;must skip over decimal point
|
||
|
35442 // @040 #550: JUMP(NZ,edit_point_update)
|
||
|
// @041 #551: [skip_right]
|
||
|
18a01 // @041 #551: ADD(sA,1) ;move cursor further right
|
||
|
// #552: ;
|
||
|
// @042 #553: [edit_point_update]
|
||
|
2ea10 // @042 #553: STORE(sA,cursor_position) ;update edit value in memory
|
||
|
2eb11 // @043 #554: STORE(sB,edit_digit_pointer)
|
||
|
3401f // @044 #555: JUMP(move_mode)
|
||
|
// #556: ;
|
||
|
// #557: ;
|
||
|
// #558: ; The edit mode is reached by pressing the rotary control. Since this is a simple switch
|
||
|
// #559: ; a software de-bounce delay is used to wait for the knob to be released fully before
|
||
|
// #560: ; entering the digit editing mode fully.
|
||
|
// #561: ;
|
||
|
// #562: ; In this mode rotations of the detected by the interrupt service routine are used to
|
||
|
// #563: ; increment or decrement the digit value at the cursor position with carry/borrow to
|
||
|
// #564: ; the left.
|
||
|
// #565: ;
|
||
|
// #566: ; A new press of the rotary control is detected by polling and used to change back to the
|
||
|
// #567: ; cursor moving mode.
|
||
|
// #568: ;
|
||
|
// #569: ;
|
||
|
// @045 #570: [edit_mode]
|
||
|
3007d // @045 #570: CALL(wait_switch_release) ;wait for switch press to end
|
||
|
// @046 #571: [edit_display]
|
||
|
30084 // @046 #571: CALL(compute_DDS_words) ;compute DDS control values
|
||
|
300b4 // @047 #572: CALL(display_freq) ;refresh display with new values
|
||
|
00002 // @048 #573: LOAD(s0,LED1) ;indicate edit mode on LEDs
|
||
|
2c080 // @049 #574: OUTPUT(s0,LED_port)
|
||
|
// @04a #575: [edit_wait]
|
||
|
04000 // @04a #575: INPUT(s0,rotary_port) ;read rotary encoder
|
||
|
12002 // @04b #576: TEST(s0,rotary_press) ;test for press of button which changes mode
|
||
|
3547b // @04c #577: JUMP(NZ,end_edit_mode)
|
||
|
06000 // @04d #578: FETCH(s0,rotary_status) ;check for any rotation of rotary control
|
||
|
12080 // @04e #579: TEST(s0,rotary_event)
|
||
|
3504a // @04f #580: JUMP(Z,edit_wait)
|
||
|
// #581: ;
|
||
|
0a07f // @050 #582: AND(s0,127) ;clear flag now that action it is being processed
|
||
|
2e000 // @051 #583: STORE(s0,rotary_status)
|
||
|
06b11 // @052 #584: FETCH(sB,edit_digit_pointer) ;read pointer to BCD digit for initial change
|
||
|
12001 // @053 #585: TEST(s0,rotary_left) ;determine direction to increment or decrement
|
||
|
35068 // @054 #586: JUMP(Z,inc_digit)
|
||
|
// #587: ;
|
||
|
// #588: ; Decrement the value starting at the current position and borrowing from the left.
|
||
|
// #589: ; However the value needs to bottom out at all 0's from the editing position.
|
||
|
// #590: ;
|
||
|
// #591: ;
|
||
|
// @055 #592: [dec_digit]
|
||
|
07ab0 // @055 #592: FETCH(sA,sB) ;read digit value at pointer position
|
||
|
1ca01 // @056 #593: SUB(sA,1) ;decrement digit
|
||
|
14aff // @057 #594: COMPARE(sA,FF) ;test for borrow from next digit
|
||
|
3505b // @058 #595: JUMP(Z,dec_borrow)
|
||
|
2fab0 // @059 #596: STORE(sA,sB) ;store decremented digit value
|
||
|
34046 // @05a #597: JUMP(edit_display) ;decrement task complete
|
||
|
// @05b #598: [dec_borrow]
|
||
|
00a09 // @05b #598: LOAD(sA,9) ;current digit rolls over to nine
|
||
|
2fab0 // @05c #599: STORE(sA,sB) ;store '9' digit value
|
||
|
14b0b // @05d #600: COMPARE(sB,BCD_digit8) ;check if working on 100MHz digit
|
||
|
35061 // @05e #601: JUMP(Z,set_min_value)
|
||
|
18b01 // @05f #602: ADD(sB,1) ;increment pointer to next most significant digit
|
||
|
34055 // @060 #603: JUMP(dec_digit) ;decrement next digit up.
|
||
|
// #604: ;
|
||
|
// @061 #605: [set_min_value]
|
||
|
06b11 // @061 #605: FETCH(sB,edit_digit_pointer) ;Must fill digits from insert to MS-Digit with 000...
|
||
|
00a00 // @062 #606: LOAD(sA,0)
|
||
|
// @063 #607: [fill_min]
|
||
|
2fab0 // @063 #607: STORE(sA,sB)
|
||
|
14b0b // @064 #608: COMPARE(sB,BCD_digit8) ;check if filled to 100MHz digit
|
||
|
35046 // @065 #609: JUMP(Z,edit_display)
|
||
|
18b01 // @066 #610: ADD(sB,1) ;fill next higher digit
|
||
|
34063 // @067 #611: JUMP(fill_min)
|
||
|
// #612: ;
|
||
|
// #613: ; Increment the value starting at the current position and carrying to the left.
|
||
|
// #614: ; However the value needs to saturate to all 9's from the editing position.
|
||
|
// #615: ;
|
||
|
// @068 #616: [inc_digit]
|
||
|
07ab0 // @068 #616: FETCH(sA,sB) ;read digit value at pointer position
|
||
|
18a01 // @069 #617: ADD(sA,1) ;increment digit
|
||
|
14a0a // @06a #618: COMPARE(sA,10) ;test for carry to next digit
|
||
|
3506e // @06b #619: JUMP(Z,inc_carry)
|
||
|
2fab0 // @06c #620: STORE(sA,sB) ;store incremented digit value
|
||
|
34046 // @06d #621: JUMP(edit_display) ;increment task complete
|
||
|
// @06e #622: [inc_carry]
|
||
|
00a00 // @06e #622: LOAD(sA,0) ;current digit rolls over to zero
|
||
|
2fab0 // @06f #623: STORE(sA,sB) ;store zero digit value
|
||
|
14b0b // @070 #624: COMPARE(sB,BCD_digit8) ;check if working on 100MHz digit
|
||
|
35074 // @071 #625: JUMP(Z,set_max_value)
|
||
|
18b01 // @072 #626: ADD(sB,1) ;increment pointer to next most significant digit
|
||
|
34068 // @073 #627: JUMP(inc_digit) ;increment next digit up.
|
||
|
// #628: ;
|
||
|
// @074 #629: [set_max_value]
|
||
|
06b11 // @074 #629: FETCH(sB,edit_digit_pointer) ;Must fill digits from insert to MS-Digit with 999...
|
||
|
00a09 // @075 #630: LOAD(sA,9)
|
||
|
// @076 #631: [fill_max]
|
||
|
2fab0 // @076 #631: STORE(sA,sB)
|
||
|
14b0b // @077 #632: COMPARE(sB,BCD_digit8) ;check if filled to 100MHz digit
|
||
|
35046 // @078 #633: JUMP(Z,edit_display)
|
||
|
18b01 // @079 #634: ADD(sB,1) ;fill next higher digit
|
||
|
34076 // @07a #635: JUMP(fill_max)
|
||
|
// #636: ;
|
||
|
// @07b #637: [end_edit_mode]
|
||
|
3007d // @07b #637: CALL(wait_switch_release) ;wait for end of switch press
|
||
|
3401f // @07c #638: JUMP(move_mode) ;then go to move cursor mode
|
||
|
// #639: ;
|
||
|
// #640: ;
|
||
|
// #641: ; Routine to poll the press switch with de-bounce delay and wait for it to be
|
||
|
// #642: ; released. Any rotation inputs detected by the interrupt
|
||
|
// #643: ; service routine are cleared before returning.
|
||
|
// #644: ;
|
||
|
// @07d #645: [wait_switch_release]
|
||
|
301c8 // @07d #645: CALL(delay_20ms) ;delay to aid switch de-bounce
|
||
|
04000 // @07e #646: INPUT(s0,rotary_port) ;read rotary encoder
|
||
|
12002 // @07f #647: TEST(s0,rotary_press) ;test if button is still being pressed
|
||
|
3547d // @080 #648: JUMP(NZ,wait_switch_release)
|
||
|
00000 // @081 #649: LOAD(s0,0) ;clear flag indicating any rotary events
|
||
|
2e000 // @082 #650: STORE(s0,rotary_status)
|
||
|
2a000 // @083 #651: RETURN
|
||
|
// #652: ;
|
||
|
// #653: ;**************************************************************************************
|
||
|
// #654: ; Compute DDS control words from currently display frequency value
|
||
|
// #655: ;**************************************************************************************
|
||
|
// #656: ;
|
||
|
// #657: ; This routine reads the current BCD value and converts it into a 32-bit binary
|
||
|
// #658: ; integer. It then multiplies it by a 48-bit scaling factor (see notes in the
|
||
|
// #659: ; constants section above) to form a full precision 80-bit product.
|
||
|
// #660: ;
|
||
|
// #661: ; From this product the 32-bit DDS control word must be extracted. For frequencies of
|
||
|
// #662: ; 50MHz or above the DDS control word is formed by shifting the product left by 2 places
|
||
|
// #663: ; (multiply by 4) and then keeping only the most significant 32-bits (4 bytes).
|
||
|
// #664: ;
|
||
|
// #665: ; Also for frequencies of 50MHz and above, there is no additional division performed
|
||
|
// #666: ; after the DCM which multiplies frequency and reduces the jitter. Therefore the DDS_scaling
|
||
|
// #667: ; word will be set to zero and the output of the DCM will divide by 2.
|
||
|
// #668: ;
|
||
|
// #669: ; Freq DDS control word DDS Scaling Synthesized Frequency
|
||
|
// #670: ; of Phase Accumulator
|
||
|
// #671: ;
|
||
|
// #672: ; 50MHz 08000000 00 6.25MHz
|
||
|
// #673: ; 100MHz 10000000 00 12.50MHz
|
||
|
// #674: ;
|
||
|
// #675: ; You will notice that for frequencies of 50MHz and above, the upper byte of the
|
||
|
// #676: ; DDS control word is 08 hex or greater. In other words, bit3 and/or bit4 of that byte
|
||
|
// #677: ; are High (bits 27 and/or 28 of the full 32-bit word). This is the indication that
|
||
|
// #678: ; the control words are complete.
|
||
|
// #679: ;
|
||
|
// #680: ; For frequencies below 50MHz an additional process is required. The reason for this
|
||
|
// #681: ; becomes clear if we think about the lowest frequency of 1Hz. In that case the 80-bit
|
||
|
// #682: ; product is the same as the 48-bit scaling constant 00000000ABCC77118462. Once this
|
||
|
// #683: ; has been multiplied by 4 (shifted left 2 places) it becomes 00000002AF31DC461188 and the
|
||
|
// #684: ; most significant 32-bits are only 00000002 hex. If we put this back into the basic
|
||
|
// #685: ; equations for the phase accumulator we find that the output frequency of the phase
|
||
|
// #686: ; accumulator would be
|
||
|
// #687: ;
|
||
|
// #688: ; Fout = M x clk x N / (2^p)
|
||
|
// #689: ;
|
||
|
// #690: ; = 8 x 200MHz x 2 / (2^32) = 0.745 Hz
|
||
|
// #691: ;
|
||
|
// #692: ; There are two important observations we can make. Firstly we have lost accuracy because
|
||
|
// #693: ; the resolution of the DDS control word has become too granular at low amplitudes.
|
||
|
// #694: ; Secondly this would never even work because the frequency synthesized by the phase
|
||
|
// #695: ; accumulator would be 0.745/8 = 0.0931 Hz which is seriously slow and a way below the
|
||
|
// #696: ; frequency at which the DCM can even work.
|
||
|
// #697: ;
|
||
|
// #698: ; The solution to both of these issues is to ensure that the DDS control word is always
|
||
|
// #699: ; formed to be in the range that would result in an output of 50MHz or above. In other
|
||
|
// #700: ; words to keep the phase accumulator output in the range 6.25MHz to 12.5MHz such that
|
||
|
// #701: ; the DCM is able to work and only has to deal with one octave of input variation. This
|
||
|
// #702: ; can be achieved by shifting the 80-bit product left more times until bits 27 and 28
|
||
|
// #703: ; of the most significant 32-bits are not zero.
|
||
|
// #704: ;
|
||
|
// #705: ; For each shift left the synthesized frequency is being doubled and therefore the final
|
||
|
// #706: ; output from the DCM must be divided by a further factor of 2. This is achieved using
|
||
|
// #707: ; a multiplexer which is guided to select the appropriate output from a simple binary
|
||
|
// #708: ; counter.
|
||
|
// #709: ;
|
||
|
// #710: ; Returning to the example of 1Hz, the 80-bit product will be shifted left by the default
|
||
|
// #711: ; 2 places (multiplied by 4), but will then need to be shifted left by a further 26 places
|
||
|
// #712: ; which is like multiplying by 67108864 (04000000 hex).
|
||
|
// #713: ;
|
||
|
// #714: ; 00000000ABCC77118462
|
||
|
// #715: ; x 4
|
||
|
// #716: ; --------------------
|
||
|
// #717: ; 00000002AF31DC461188
|
||
|
// #718: ;
|
||
|
// #719: ;
|
||
|
// #720: ; x 04000000
|
||
|
// #721: ; --------------------
|
||
|
// #722: ; 0ABCC771184620000000
|
||
|
// #723: ;
|
||
|
// #724: ; So now the DDS control word is 0ABCC771 (180143985 decimal)and the frequency synthesized
|
||
|
// #725: ; by the phase accumulator will be....
|
||
|
// #726: ;
|
||
|
// #727: ; Fpa = clk x N / (2^p) = 200MHz x 180143985 / (2^32) = 8388608Hz
|
||
|
// #728: ;
|
||
|
// #729: ; The DCM will multiply this by a factor of 16 to give 134217728Hz and this will then
|
||
|
// #730: ; be divided by the counter of which the 26th bit selected (26 decimal = 1A hex).
|
||
|
// #731: ;
|
||
|
// #732: ; Fout = Fpa x 16 / (2^(D+1)) = 8388608Hz x 16 / (2^(26+1)) = 0.99999999947 Hz
|
||
|
// #733: ;
|
||
|
// #734: ; 'D' is the DDS Scaling factor
|
||
|
// #735: ; Note that bit0 of a counter is a clock division by 2 and hence the 'D+1'
|
||
|
// #736: ;
|
||
|
// #737: ; Clearly this implementation style has provided much greater accuracy and enables
|
||
|
// #738: ; the DCM to work for all desired frequencies.
|
||
|
// #739: ;
|
||
|
// #740: ;
|
||
|
// #741: ; Freq DDS control word DDS Scaling Synthesized Frequency
|
||
|
// #742: ; of Phase Accumulator
|
||
|
// #743: ;
|
||
|
// #744: ; 100 MHz 10000000 00 12.50MHz
|
||
|
// #745: ; 50 MHz 08000000 00 6.25MHz
|
||
|
// #746: ; 25 MHz 08000000 01 6.25MHz
|
||
|
// #747: ; 12.5 MHz 08000000 02 6.25MHz
|
||
|
// #748: ;
|
||
|
// #749: ; 1Hz 0ABCC771 1A 8.388608 MHz
|
||
|
// #750: ;
|
||
|
// #751: ;
|
||
|
// #752: ;
|
||
|
// #753: ; In order to ensure the DCM is always provided with a frequency in an acceptable
|
||
|
// #754: ; range, the value of absolute zero is never implemented and instead just a very low
|
||
|
// #755: ; frequency is produced.
|
||
|
// #756: ; 6.25MHz x 16 / (2^31+1) = 0.0233 Hz
|
||
|
// #757: ; which is 1 cycle every 43 seconds and that is pretty slow :-)
|
||
|
// #758: ;
|
||
|
// #759: ;
|
||
|
// #760: ;
|
||
|
// #761: ;
|
||
|
// @084 #762: [compute_DDS_words]
|
||
|
300e5 // @084 #762: CALL(BCD_to_integer) ;convert BCD display value to 32-bit value
|
||
|
30119 // @085 #763: CALL(scale_frequency) ;80-bit product of 32-bit frequency x 48-bit scaling value
|
||
|
06a1b // @086 #764: FETCH(sA,product9) ;read the upper part of the 80-bit product into [sA,s9,s8,s7,s6,s5,s4]
|
||
|
0691a // @087 #765: FETCH(s9,product8) ; The least significant 24-bits of the 80-bit product will never
|
||
|
06819 // @088 #766: FETCH(s8,product7) ; be used for frequencies above 1Hz.
|
||
|
06718 // @089 #767: FETCH(s7,product6) ;The final 32-bit DDS control word will be formed in
|
||
|
06617 // @08a #768: FETCH(s6,product5) ; [sA,s9,s8,s7]
|
||
|
06516 // @08b #769: FETCH(s5,product4)
|
||
|
06415 // @08c #770: FETCH(s4,product3)
|
||
|
300a1 // @08d #771: CALL(shift80_left) ;multiply DDS control word by 4 to achieve default value
|
||
|
300a1 // @08e #772: CALL(shift80_left)
|
||
|
00b00 // @08f #773: LOAD(sB,0) ;default scaling factor is 2 (select counter bit0)
|
||
|
// @090 #774: [normalise_loop]
|
||
|
12a18 // @090 #774: TEST(sA,24) ;Test bits 27 and 28 of 32-bit DDS control word
|
||
|
3549a // @091 #775: JUMP(NZ,store_DDS_words) ;DDS control word is normalised to above 50MHz output
|
||
|
300a1 // @092 #776: CALL(shift80_left) ;multiply DDS control word by 2
|
||
|
18b01 // @093 #777: ADD(sB,1) ;Divide final value by 2 to compensate
|
||
|
14b1f // @094 #778: COMPARE(sB,31) ;Test for maximum division factor
|
||
|
35490 // @095 #779: JUMP(NZ,normalise_loop)
|
||
|
00a08 // @096 #780: LOAD(sA,8) ;Set for minimum frequency
|
||
|
00900 // @097 #781: LOAD(s9,0) ; with phase accumulator set to generate 6.25MHz
|
||
|
00800 // @098 #782: LOAD(s8,0)
|
||
|
00700 // @099 #783: LOAD(s7,0)
|
||
|
// @09a #784: [store_DDS_words]
|
||
|
2e71c // @09a #784: STORE(s7,DDS_control0) ;store local copy of control word
|
||
|
2e81d // @09b #785: STORE(s8,DDS_control1) ;store local copy of control word
|
||
|
2e91e // @09c #786: STORE(s9,DDS_control2) ;store local copy of control word
|
||
|
2ea1f // @09d #787: STORE(sA,DDS_control3) ;store local copy of control word
|
||
|
2eb20 // @09e #788: STORE(sB,DDS_scaling)
|
||
|
300a9 // @09f #789: CALL(drive_DDS_words) ;output control words to DDS circuit
|
||
|
2a000 // @0a0 #790: RETURN
|
||
|
// #791: ;
|
||
|
// @0a1 #792: [shift80_left]
|
||
|
20406 // @0a1 #792: SL0(s4) ;shift (most of the) 80-bit value in
|
||
|
20500 // @0a2 #793: SLA(s5) ; [sA,s9,s8,s7,s6,s5,s4] left 1 place
|
||
|
20600 // @0a3 #794: SLA(s6)
|
||
|
20700 // @0a4 #795: SLA(s7)
|
||
|
20800 // @0a5 #796: SLA(s8)
|
||
|
20900 // @0a6 #797: SLA(s9)
|
||
|
20a00 // @0a7 #798: SLA(sA)
|
||
|
2a000 // @0a8 #799: RETURN
|
||
|
// #800: ;
|
||
|
// #801: ;**************************************************************************************
|
||
|
// #802: ; Set DDS control words
|
||
|
// #803: ;**************************************************************************************
|
||
|
// #804: ;
|
||
|
// #805: ; Because multiple ports are used, the idea is to update all of them in
|
||
|
// #806: ; rapid succession to avoid too much disturbance in the frequency synthesis.
|
||
|
// #807: ;
|
||
|
// #808: ; dds_control_word should be supplied in register set [sA,s9,s8,s7]
|
||
|
// #809: ; dds_scaling_word should be supplied in register s6.
|
||
|
// #810: ;
|
||
|
// @0a9 #811: [drive_DDS_words]
|
||
|
0671c // @0a9 #811: FETCH(s7,DDS_control0)
|
||
|
0681d // @0aa #812: FETCH(s8,DDS_control1)
|
||
|
0691e // @0ab #813: FETCH(s9,DDS_control2)
|
||
|
06a1f // @0ac #814: FETCH(sA,DDS_control3)
|
||
|
06620 // @0ad #815: FETCH(s6,DDS_scaling)
|
||
|
2c702 // @0ae #816: OUTPUT(s7,DDS_control0_port)
|
||
|
2c804 // @0af #817: OUTPUT(s8,DDS_control1_port)
|
||
|
2c908 // @0b0 #818: OUTPUT(s9,DDS_control2_port)
|
||
|
2ca10 // @0b1 #819: OUTPUT(sA,DDS_control3_port)
|
||
|
2c620 // @0b2 #820: OUTPUT(s6,DDS_scaling_port)
|
||
|
2a000 // @0b3 #821: RETURN
|
||
|
// #822: ;
|
||
|
// #823: ;
|
||
|
// #824: ;**************************************************************************************
|
||
|
// #825: ; Display frequency on top line of the LCD and DDS data on the lower line
|
||
|
// #826: ;**************************************************************************************
|
||
|
// #827: ;
|
||
|
// #828: ; The BCD value should be stored in scratch pad memory in 9 ascending locations
|
||
|
// #829: ; called BCD_digit0 to BCD_digit8.
|
||
|
// #830: ;
|
||
|
// #831: ; The value is displayed in the format xxx.xxx xxxMHz
|
||
|
// #832: ;
|
||
|
// #833: ; However, the most significant 2 digits will be blanked if zero.
|
||
|
// #834: ;
|
||
|
// #835: ; registers used s0,s1,s2,s3,s4,s5,s6,s7
|
||
|
// #836: ;
|
||
|
// #837: ;
|
||
|
// @0b4 #838: [display_freq]
|
||
|
3015d // @0b4 #838: CALL(display_DDS_data) ;display DDS information on lower line
|
||
|
00512 // @0b5 #839: LOAD(s5,18) ;Line 1 position 2
|
||
|
3022b // @0b6 #840: CALL(LCD_cursor)
|
||
|
0650b // @0b7 #841: FETCH(s5,BCD_digit8) ;read 100MHz digit
|
||
|
14500 // @0b8 #842: COMPARE(s5,0) ;test for blanking
|
||
|
350be // @0b9 #843: JUMP(Z,blank_100M_digit)
|
||
|
300df // @0ba #844: CALL(display_digit) ;display non zero digit
|
||
|
0650a // @0bb #845: FETCH(s5,BCD_digit7) ;read 10MHz digit and display
|
||
|
300df // @0bc #846: CALL(display_digit)
|
||
|
340c5 // @0bd #847: JUMP(disp_1M_digit)
|
||
|
// #848: ;
|
||
|
// @0be #849: [blank_100M_digit]
|
||
|
300e2 // @0be #849: CALL(display_space) ;blank 100MHz digit
|
||
|
0650a // @0bf #850: FETCH(s5,BCD_digit7) ;read 10MHz digit
|
||
|
14500 // @0c0 #851: COMPARE(s5,0) ;test for blanking
|
||
|
350c4 // @0c1 #852: JUMP(Z,blank_10M_digit)
|
||
|
300df // @0c2 #853: CALL(display_digit) ;display non zero digit
|
||
|
340c5 // @0c3 #854: JUMP(disp_1M_digit)
|
||
|
// #855: ;
|
||
|
// @0c4 #856: [blank_10M_digit]
|
||
|
300e2 // @0c4 #856: CALL(display_space) ;blank 10MHz digit
|
||
|
// #857: ;
|
||
|
// @0c5 #858: [disp_1M_digit]
|
||
|
06509 // @0c5 #858: FETCH(s5,BCD_digit6) ;read 1MHz digit and display
|
||
|
300df // @0c6 #859: CALL(display_digit)
|
||
|
0052e // @0c7 #860: LOAD(s5,character_stop) ;display decimal point
|
||
|
301eb // @0c8 #861: CALL(LCD_write_data)
|
||
|
// #862: ;
|
||
|
00208 // @0c9 #863: LOAD(s2,BCD_digit5) ;set pointer to 100KHz digit
|
||
|
300d8 // @0ca #864: CALL(display_3_digits)
|
||
|
300e2 // @0cb #865: CALL(display_space)
|
||
|
00205 // @0cc #866: LOAD(s2,BCD_digit2) ;set pointer to 100Hz digit
|
||
|
300d8 // @0cd #867: CALL(display_3_digits)
|
||
|
0054d // @0ce #868: LOAD(s5,character_M) ;display 'MHz'
|
||
|
301eb // @0cf #869: CALL(LCD_write_data)
|
||
|
00548 // @0d0 #870: LOAD(s5,character_H)
|
||
|
301eb // @0d1 #871: CALL(LCD_write_data)
|
||
|
0057a // @0d2 #872: LOAD(s5,character_z)
|
||
|
301eb // @0d3 #873: CALL(LCD_write_data)
|
||
|
// #874: ;
|
||
|
06510 // @0d4 #875: FETCH(s5,cursor_position) ;reposition edit cursor on display
|
||
|
18510 // @0d5 #876: ADD(s5,16) ;on line 1
|
||
|
3022b // @0d6 #877: CALL(LCD_cursor)
|
||
|
2a000 // @0d7 #878: RETURN
|
||
|
// #879: ;
|
||
|
// @0d8 #880: [display_3_digits]
|
||
|
00303 // @0d8 #880: LOAD(s3,3) ;3 digits to display
|
||
|
// @0d9 #881: [threedigit_loop]
|
||
|
07520 // @0d9 #881: FETCH(s5,s2)
|
||
|
300df // @0da #882: CALL(display_digit)
|
||
|
1c201 // @0db #883: SUB(s2,1) ;decrement digit pointer
|
||
|
1c301 // @0dc #884: SUB(s3,1) ;count digits displayed
|
||
|
354d9 // @0dd #885: JUMP(NZ,threedigit_loop)
|
||
|
2a000 // @0de #886: RETURN
|
||
|
// #887: ;
|
||
|
// @0df #888: [display_digit]
|
||
|
18530 // @0df #888: ADD(s5,48) ;convert BCD to ASCII character
|
||
|
301eb // @0e0 #889: CALL(LCD_write_data)
|
||
|
2a000 // @0e1 #890: RETURN
|
||
|
// #891: ;
|
||
|
// @0e2 #892: [display_space]
|
||
|
00520 // @0e2 #892: LOAD(s5,character_space)
|
||
|
301eb // @0e3 #893: CALL(LCD_write_data)
|
||
|
2a000 // @0e4 #894: RETURN
|
||
|
// #895: ;
|
||
|
// #896: ;
|
||
|
// #897: ;**************************************************************************************
|
||
|
// #898: ; Convert 9 digit BCD frequency into 32-bit binary integer
|
||
|
// #899: ;**************************************************************************************
|
||
|
// #900: ;
|
||
|
// #901: ;Both values are stored in scratch pad memory
|
||
|
// #902: ; BCD values in ascending locations BCD_digit0 to BCD_digit8
|
||
|
// #903: ; Binary frequency in ascending locations frequency0 to frequency3
|
||
|
// #904: ;
|
||
|
// #905: ;Each digit is read in turn and its value is determined by repeated
|
||
|
// #906: ;decrement until reaching zero. Each decrement causes a value to be added
|
||
|
// #907: ;to the memory locations forming the frequency value as binary integer.
|
||
|
// #908: ;The process requires approximately 1600 instructions to convert the highest
|
||
|
// #909: ;value 999,999,999 which is approximately 64us at 50MHz clock rate.
|
||
|
// #910: ;
|
||
|
// #911: ;Registers used s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,sA,sB
|
||
|
// #912: ;
|
||
|
// @0e5 #913: [BCD_to_integer]
|
||
|
00209 // @0e5 #913: LOAD(s2,9) ;9 digits to convert
|
||
|
00000 // @0e6 #914: LOAD(s0,0) ;clear frequency value ready to accumulate result
|
||
|
2e00c // @0e7 #915: STORE(s0,frequency0)
|
||
|
2e00d // @0e8 #916: STORE(s0,frequency1)
|
||
|
2e00e // @0e9 #917: STORE(s0,frequency2)
|
||
|
2e00f // @0ea #918: STORE(s0,frequency3)
|
||
|
00b00 // @0eb #919: LOAD(sB,0) ;initialise BCD digit weighting [sB,sA,s9,s8] to 1
|
||
|
00a00 // @0ec #920: LOAD(sA,0)
|
||
|
00900 // @0ed #921: LOAD(s9,0)
|
||
|
00801 // @0ee #922: LOAD(s8,1)
|
||
|
00303 // @0ef #923: LOAD(s3,BCD_digit0) ;locate LS-digit
|
||
|
// @0f0 #924: [next_BCD_to_int_digit]
|
||
|
07130 // @0f0 #924: FETCH(s1,s3)
|
||
|
// @0f1 #925: [BCD_digit_convert]
|
||
|
14100 // @0f1 #925: COMPARE(s1,0) ;test for zero
|
||
|
35101 // @0f2 #926: JUMP(Z,next_digit_value)
|
||
|
0600c // @0f3 #927: FETCH(s0,frequency0) ;add 32-bit digit weighting to memory value
|
||
|
19080 // @0f4 #928: ADD(s0,s8)
|
||
|
2e00c // @0f5 #929: STORE(s0,frequency0)
|
||
|
0600d // @0f6 #930: FETCH(s0,frequency1)
|
||
|
1b090 // @0f7 #931: ADDCY(s0,s9)
|
||
|
2e00d // @0f8 #932: STORE(s0,frequency1)
|
||
|
0600e // @0f9 #933: FETCH(s0,frequency2)
|
||
|
1b0a0 // @0fa #934: ADDCY(s0,sA)
|
||
|
2e00e // @0fb #935: STORE(s0,frequency2)
|
||
|
0600f // @0fc #936: FETCH(s0,frequency3)
|
||
|
1b0b0 // @0fd #937: ADDCY(s0,sB)
|
||
|
2e00f // @0fe #938: STORE(s0,frequency3)
|
||
|
1c101 // @0ff #939: SUB(s1,1) ;decrement digit value
|
||
|
340f1 // @100 #940: JUMP(BCD_digit_convert)
|
||
|
// #941: ;Increase weighting by 10x
|
||
|
// @101 #942: [next_digit_value]
|
||
|
017b0 // @101 #942: LOAD(s7,sB) ;copy existing weighting
|
||
|
016a0 // @102 #943: LOAD(s6,sA)
|
||
|
01590 // @103 #944: LOAD(s5,s9)
|
||
|
01480 // @104 #945: LOAD(s4,s8)
|
||
|
20806 // @105 #946: SL0(s8) ;multiply weight by 4x (shift left 2 places)
|
||
|
20900 // @106 #947: SLA(s9)
|
||
|
20a00 // @107 #948: SLA(sA)
|
||
|
20b00 // @108 #949: SLA(sB)
|
||
|
20806 // @109 #950: SL0(s8)
|
||
|
20900 // @10a #951: SLA(s9)
|
||
|
20a00 // @10b #952: SLA(sA)
|
||
|
20b00 // @10c #953: SLA(sB)
|
||
|
19840 // @10d #954: ADD(s8,s4) ;add previous weight to form 5x multiplication
|
||
|
1b950 // @10e #955: ADDCY(s9,s5)
|
||
|
1ba60 // @10f #956: ADDCY(sA,s6)
|
||
|
1bb70 // @110 #957: ADDCY(sB,s7)
|
||
|
20806 // @111 #958: SL0(s8) ;multiply weight by 2x (shift left 1 places)
|
||
|
20900 // @112 #959: SLA(s9)
|
||
|
20a00 // @113 #960: SLA(sA)
|
||
|
20b00 // @114 #961: SLA(sB) ;weight value is now 10x previous value
|
||
|
18301 // @115 #962: ADD(s3,1) ;move to next digit for conversion
|
||
|
1c201 // @116 #963: SUB(s2,1)
|
||
|
354f0 // @117 #964: JUMP(NZ,next_BCD_to_int_digit)
|
||
|
2a000 // @118 #965: RETURN
|
||
|
// #966: ;
|
||
|
// #967: ;
|
||
|
// #968: ;**************************************************************************************
|
||
|
// #969: ; 32-bit x 48-bit multiply to scale the integer frequency
|
||
|
// #970: ;**************************************************************************************
|
||
|
// #971: ;
|
||
|
// #972: ;Multiply the 32-bit frequency binary integer by the 48-bit scaling factor
|
||
|
// #973: ;to form a full precision 80-bit product.
|
||
|
// #974: ;
|
||
|
// #975: ;The frequency binary integer is stored in scratch pad memory using ascending
|
||
|
// #976: ;locations frequency0 to frequency3
|
||
|
// #977: ;
|
||
|
// #978: ;The product will be stored in scratch pad memory using ascending
|
||
|
// #979: ;locations product0 to product9
|
||
|
// #980: ;
|
||
|
// #981: ;The scaling factor is provided directly as constants
|
||
|
// #982: ; scale_constant0 to scale_constant5
|
||
|
// #983: ;
|
||
|
// #984: ;The multiplication is performed as a 32-bit 'shift and add' process in which the
|
||
|
// #985: ;integer frequency is examined LSB first using a register set [sB,sA,s9,s8] and
|
||
|
// #986: ;a scaling accumulator is formed directly in the 'product' memory locations.
|
||
|
// #987: ;
|
||
|
// #988: ;The process requires up to 1772 instructions which is 3544 clock cycle or
|
||
|
// #989: ;approximately 71us at 50MHz clock rate.
|
||
|
// #990: ;
|
||
|
// #991: ;Registers used s0,s1,s8,s9,sA,sB (s1,s8,s9,sA,sB clear on return)
|
||
|
// #992: ;
|
||
|
// @119 #993: [scale_frequency]
|
||
|
00000 // @119 #993: LOAD(s0,0) ;clear accumulator section of 'product'
|
||
|
2e01b // @11a #994: STORE(s0,product9)
|
||
|
2e01a // @11b #995: STORE(s0,product8)
|
||
|
2e019 // @11c #996: STORE(s0,product7)
|
||
|
2e018 // @11d #997: STORE(s0,product6)
|
||
|
2e017 // @11e #998: STORE(s0,product5)
|
||
|
2e016 // @11f #999: STORE(s0,product4)
|
||
|
06b0f // @120 #1000: FETCH(sB,frequency3) ;read frequency integer value
|
||
|
06a0e // @121 #1001: FETCH(sA,frequency2)
|
||
|
0690d // @122 #1002: FETCH(s9,frequency1)
|
||
|
0680c // @123 #1003: FETCH(s8,frequency0)
|
||
|
00120 // @124 #1004: LOAD(s1,32) ;32-bit multiply
|
||
|
// @125 #1005: [scale_mult_bit]
|
||
|
20b0e // @125 #1005: SR0(sB) ;shift right frequency integer
|
||
|
20a08 // @126 #1006: SRA(sA)
|
||
|
20908 // @127 #1007: SRA(s9)
|
||
|
20808 // @128 #1008: SRA(s8)
|
||
|
35d3c // @129 #1009: JUMP(NC,product_shift) ;no add if bit is zero (note carry is zero)
|
||
|
06016 // @12a #1010: FETCH(s0,product4) ;addition of scaling factor to most significant bits of product
|
||
|
18062 // @12b #1011: ADD(s0,scale_constant0)
|
||
|
2e016 // @12c #1012: STORE(s0,product4)
|
||
|
06017 // @12d #1013: FETCH(s0,product5)
|
||
|
1a084 // @12e #1014: ADDCY(s0,scale_constant1)
|
||
|
2e017 // @12f #1015: STORE(s0,product5)
|
||
|
06018 // @130 #1016: FETCH(s0,product6)
|
||
|
1a011 // @131 #1017: ADDCY(s0,scale_constant2)
|
||
|
2e018 // @132 #1018: STORE(s0,product6)
|
||
|
06019 // @133 #1019: FETCH(s0,product7)
|
||
|
1a077 // @134 #1020: ADDCY(s0,scale_constant3)
|
||
|
2e019 // @135 #1021: STORE(s0,product7)
|
||
|
0601a // @136 #1022: FETCH(s0,product8)
|
||
|
1a0cc // @137 #1023: ADDCY(s0,scale_constant4)
|
||
|
2e01a // @138 #1024: STORE(s0,product8)
|
||
|
0601b // @139 #1025: FETCH(s0,product9)
|
||
|
1a0ab // @13a #1026: ADDCY(s0,scale_constant5)
|
||
|
2e01b // @13b #1027: STORE(s0,product9) ;carry holds any overflow of addition
|
||
|
// @13c #1028: [product_shift]
|
||
|
0601b // @13c #1028: FETCH(s0,product9) ;Divide product by 2 (shift right by 1)
|
||
|
20008 // @13d #1029: SRA(s0) ;overflow of addition included in shift
|
||
|
2e01b // @13e #1030: STORE(s0,product9)
|
||
|
0601a // @13f #1031: FETCH(s0,product8)
|
||
|
20008 // @140 #1032: SRA(s0)
|
||
|
2e01a // @141 #1033: STORE(s0,product8)
|
||
|
06019 // @142 #1034: FETCH(s0,product7)
|
||
|
20008 // @143 #1035: SRA(s0)
|
||
|
2e019 // @144 #1036: STORE(s0,product7)
|
||
|
06018 // @145 #1037: FETCH(s0,product6)
|
||
|
20008 // @146 #1038: SRA(s0)
|
||
|
2e018 // @147 #1039: STORE(s0,product6)
|
||
|
06017 // @148 #1040: FETCH(s0,product5)
|
||
|
20008 // @149 #1041: SRA(s0)
|
||
|
2e017 // @14a #1042: STORE(s0,product5)
|
||
|
06016 // @14b #1043: FETCH(s0,product4)
|
||
|
20008 // @14c #1044: SRA(s0)
|
||
|
2e016 // @14d #1045: STORE(s0,product4)
|
||
|
06015 // @14e #1046: FETCH(s0,product3)
|
||
|
20008 // @14f #1047: SRA(s0)
|
||
|
2e015 // @150 #1048: STORE(s0,product3)
|
||
|
06014 // @151 #1049: FETCH(s0,product2)
|
||
|
20008 // @152 #1050: SRA(s0)
|
||
|
2e014 // @153 #1051: STORE(s0,product2)
|
||
|
06013 // @154 #1052: FETCH(s0,product1)
|
||
|
20008 // @155 #1053: SRA(s0)
|
||
|
2e013 // @156 #1054: STORE(s0,product1)
|
||
|
06012 // @157 #1055: FETCH(s0,product0)
|
||
|
20008 // @158 #1056: SRA(s0)
|
||
|
2e012 // @159 #1057: STORE(s0,product0)
|
||
|
1c101 // @15a #1058: SUB(s1,1) ;move to next bit
|
||
|
35525 // @15b #1059: JUMP(NZ,scale_mult_bit)
|
||
|
2a000 // @15c #1060: RETURN
|
||
|
// #1061: ;
|
||
|
// #1062: ;**************************************************************************************
|
||
|
// #1063: ; Display DDS control information on the lower line of the LCD display.
|
||
|
// #1064: ;**************************************************************************************
|
||
|
// #1065: ;
|
||
|
// #1066: ;Display the 32-bit DDS control word and 8-bit DDS scaling word.
|
||
|
// #1067: ;
|
||
|
// @15d #1068: [display_DDS_data]
|
||
|
00520 // @15d #1068: LOAD(s5,32) ;Line 2 position 0
|
||
|
3022b // @15e #1069: CALL(LCD_cursor)
|
||
|
0054e // @15f #1070: LOAD(s5,character_N)
|
||
|
301eb // @160 #1071: CALL(LCD_write_data)
|
||
|
0053d // @161 #1072: LOAD(s5,character_equals)
|
||
|
301eb // @162 #1073: CALL(LCD_write_data)
|
||
|
0071f // @163 #1074: LOAD(s7,DDS_control3) ;pointer to most significant byte in memory
|
||
|
30184 // @164 #1075: CALL(display_hex_32_bit)
|
||
|
300e2 // @165 #1076: CALL(display_space)
|
||
|
00544 // @166 #1077: LOAD(s5,character_D)
|
||
|
301eb // @167 #1078: CALL(LCD_write_data)
|
||
|
0053d // @168 #1079: LOAD(s5,character_equals)
|
||
|
301eb // @169 #1080: CALL(LCD_write_data)
|
||
|
06020 // @16a #1081: FETCH(s0,DDS_scaling)
|
||
|
3017e // @16b #1082: CALL(display_hex_byte)
|
||
|
2a000 // @16c #1083: RETURN
|
||
|
// #1084: ;
|
||
|
// #1085: ;**************************************************************************************
|
||
|
// #1086: ; Routines to display hexadecimal values on LCD display
|
||
|
// #1087: ;**************************************************************************************
|
||
|
// #1088: ;
|
||
|
// #1089: ;
|
||
|
// #1090: ; Convert hexadecimal value provided in register s0 into ASCII characters
|
||
|
// #1091: ;
|
||
|
// #1092: ; The value provided must can be any value in the range 00 to FF and will be converted into
|
||
|
// #1093: ; two ASCII characters.
|
||
|
// #1094: ; The upper nibble will be represented by an ASCII character returned in register s3.
|
||
|
// #1095: ; The lower nibble will be represented by an ASCII character returned in register s2.
|
||
|
// #1096: ;
|
||
|
// #1097: ; The ASCII representations of '0' to '9' are 30 to 39 hexadecimal which is simply 30 hex
|
||
|
// #1098: ; added to the actual decimal value. The ASCII representations of 'A' to 'F' are 41 to 46
|
||
|
// #1099: ; hexadecimal requiring a further addition of 07 to the 30 already added.
|
||
|
// #1100: ;
|
||
|
// #1101: ; Registers used s0, s2 and s3.
|
||
|
// #1102: ;
|
||
|
// @16d #1103: [hex_byte_to_ASCII]
|
||
|
01200 // @16d #1103: LOAD(s2,s0) ;remember value supplied
|
||
|
2000e // @16e #1104: SR0(s0) ;isolate upper nibble
|
||
|
2000e // @16f #1105: SR0(s0)
|
||
|
2000e // @170 #1106: SR0(s0)
|
||
|
2000e // @171 #1107: SR0(s0)
|
||
|
30179 // @172 #1108: CALL(hex_to_ASCII) ;convert
|
||
|
01300 // @173 #1109: LOAD(s3,s0) ;upper nibble value in s3
|
||
|
01020 // @174 #1110: LOAD(s0,s2) ;restore complete value
|
||
|
0a00f // @175 #1111: AND(s0,15) ;isolate lower nibble
|
||
|
30179 // @176 #1112: CALL(hex_to_ASCII) ;convert
|
||
|
01200 // @177 #1113: LOAD(s2,s0) ;lower nibble value in s2
|
||
|
2a000 // @178 #1114: RETURN
|
||
|
// #1115: ;
|
||
|
// #1116: ; Convert hexadecimal value provided in register s0 into ASCII character
|
||
|
// #1117: ;
|
||
|
// #1118: ;Register used s0
|
||
|
// #1119: ;
|
||
|
// @179 #1120: [hex_to_ASCII]
|
||
|
1c00a // @179 #1120: SUB(s0,10) ;test if value is in range 0 to 9
|
||
|
3597c // @17a #1121: JUMP(C,number_char)
|
||
|
18007 // @17b #1122: ADD(s0,7) ;ASCII char A to F in range 41 to 46
|
||
|
// @17c #1123: [number_char]
|
||
|
1803a // @17c #1123: ADD(s0,58) ;ASCII char 0 to 9 in range 30 to 40
|
||
|
2a000 // @17d #1124: RETURN
|
||
|
// #1125: ;
|
||
|
// #1126: ;
|
||
|
// #1127: ; Display the two character HEX value of the register contents 's0' on the LCD
|
||
|
// #1128: ; at the current cursor position.
|
||
|
// #1129: ;
|
||
|
// #1130: ; Registers used s0, s1, s2, s3, s4, s5
|
||
|
// #1131: ;
|
||
|
// @17e #1132: [display_hex_byte]
|
||
|
3016d // @17e #1132: CALL(hex_byte_to_ASCII)
|
||
|
01530 // @17f #1133: LOAD(s5,s3)
|
||
|
301eb // @180 #1134: CALL(LCD_write_data)
|
||
|
01520 // @181 #1135: LOAD(s5,s2)
|
||
|
301eb // @182 #1136: CALL(LCD_write_data)
|
||
|
2a000 // @183 #1137: RETURN
|
||
|
// #1138: ;
|
||
|
// #1139: ;
|
||
|
// #1140: ;
|
||
|
// #1141: ; Display the 32-bit value stored in 4 ascending memory locations as an 8 character
|
||
|
// #1142: ; HEX value at the current cursor position. Register s7 must contain the memory
|
||
|
// #1143: ; location of the most significant byte (which is also the highest address).
|
||
|
// #1144: ;
|
||
|
// #1145: ; Registers used s0, s1, s2, s3, s4, s5, s6, s7
|
||
|
// #1146: ;
|
||
|
// @184 #1147: [display_hex_32_bit]
|
||
|
00604 // @184 #1147: LOAD(s6,4) ;4 bytes to display
|
||
|
// @185 #1148: [disp32_loop]
|
||
|
07070 // @185 #1148: FETCH(s0,s7) ;read byte
|
||
|
3017e // @186 #1149: CALL(display_hex_byte) ;display byte
|
||
|
1c701 // @187 #1150: SUB(s7,1) ;decrement pointer
|
||
|
1c601 // @188 #1151: SUB(s6,1) ;count bytes displayed
|
||
|
2b000 // @189 #1152: RETURN(Z)
|
||
|
34185 // @18a #1153: JUMP(disp32_loop)
|
||
|
// #1154: ;
|
||
|
// #1155: ;
|
||
|
// #1156: ;**************************************************************************************
|
||
|
// #1157: ;LCD text messages
|
||
|
// #1158: ;**************************************************************************************
|
||
|
// #1159: ;
|
||
|
// #1160: ;
|
||
|
// #1161: ;Display 'Frequency' on LCD at current cursor position
|
||
|
// #1162: ;
|
||
|
// @18b #1163: [disp_Frequency]
|
||
|
00546 // @18b #1163: LOAD(s5,character_F)
|
||
|
301eb // @18c #1164: CALL(LCD_write_data)
|
||
|
00572 // @18d #1165: LOAD(s5,character_r)
|
||
|
301eb // @18e #1166: CALL(LCD_write_data)
|
||
|
00565 // @18f #1167: LOAD(s5,character_e)
|
||
|
301eb // @190 #1168: CALL(LCD_write_data)
|
||
|
00571 // @191 #1169: LOAD(s5,character_q)
|
||
|
301eb // @192 #1170: CALL(LCD_write_data)
|
||
|
00575 // @193 #1171: LOAD(s5,character_u)
|
||
|
301eb // @194 #1172: CALL(LCD_write_data)
|
||
|
00565 // @195 #1173: LOAD(s5,character_e)
|
||
|
301eb // @196 #1174: CALL(LCD_write_data)
|
||
|
0056e // @197 #1175: LOAD(s5,character_n)
|
||
|
301eb // @198 #1176: CALL(LCD_write_data)
|
||
|
00563 // @199 #1177: LOAD(s5,character_c)
|
||
|
301eb // @19a #1178: CALL(LCD_write_data)
|
||
|
00579 // @19b #1179: LOAD(s5,character_y)
|
||
|
301eb // @19c #1180: CALL(LCD_write_data)
|
||
|
2a000 // @19d #1181: RETURN
|
||
|
// #1182: ;
|
||
|
// #1183: ;Display 'Generator' on LCD at current cursor position
|
||
|
// #1184: ;
|
||
|
// @19e #1185: [disp_Generator]
|
||
|
00547 // @19e #1185: LOAD(s5,character_G)
|
||
|
301eb // @19f #1186: CALL(LCD_write_data)
|
||
|
00565 // @1a0 #1187: LOAD(s5,character_e)
|
||
|
301eb // @1a1 #1188: CALL(LCD_write_data)
|
||
|
0056e // @1a2 #1189: LOAD(s5,character_n)
|
||
|
301eb // @1a3 #1190: CALL(LCD_write_data)
|
||
|
00565 // @1a4 #1191: LOAD(s5,character_e)
|
||
|
301eb // @1a5 #1192: CALL(LCD_write_data)
|
||
|
00572 // @1a6 #1193: LOAD(s5,character_r)
|
||
|
301eb // @1a7 #1194: CALL(LCD_write_data)
|
||
|
00561 // @1a8 #1195: LOAD(s5,character_a)
|
||
|
301eb // @1a9 #1196: CALL(LCD_write_data)
|
||
|
00574 // @1aa #1197: LOAD(s5,character_t)
|
||
|
301eb // @1ab #1198: CALL(LCD_write_data)
|
||
|
0056f // @1ac #1199: LOAD(s5,character_o)
|
||
|
301eb // @1ad #1200: CALL(LCD_write_data)
|
||
|
00572 // @1ae #1201: LOAD(s5,character_r)
|
||
|
301eb // @1af #1202: CALL(LCD_write_data)
|
||
|
300e2 // @1b0 #1203: CALL(display_space)
|
||
|
00576 // @1b1 #1204: LOAD(s5,character_v)
|
||
|
301eb // @1b2 #1205: CALL(LCD_write_data)
|
||
|
00531 // @1b3 #1206: LOAD(s5,character_1)
|
||
|
301eb // @1b4 #1207: CALL(LCD_write_data)
|
||
|
0052e // @1b5 #1208: LOAD(s5,character_stop)
|
||
|
301eb // @1b6 #1209: CALL(LCD_write_data)
|
||
|
00532 // @1b7 #1210: LOAD(s5,character_2)
|
||
|
301eb // @1b8 #1211: CALL(LCD_write_data)
|
||
|
2a000 // @1b9 #1212: RETURN
|
||
|
// #1213: ;
|
||
|
// #1214: ;
|
||
|
// #1215: ;
|
||
|
// #1216: ;
|
||
|
// #1217: ;**************************************************************************************
|
||
|
// #1218: ;Software delay routines
|
||
|
// #1219: ;**************************************************************************************
|
||
|
// #1220: ;
|
||
|
// #1221: ;
|
||
|
// #1222: ;
|
||
|
// #1223: ;Delay of 1us.
|
||
|
// #1224: ;
|
||
|
// #1225: ;Constant value defines reflects the clock applied to KCPSM3. Every instruction
|
||
|
// #1226: ;executes in 2 clock cycles making the calculation highly predictable. The '6' in
|
||
|
// #1227: ;the following equation even allows for 'CALL delay_1us' instruction in the initiating code.
|
||
|
// #1228: ;
|
||
|
// #1229: ; delay_1us_constant = (clock_rate - 6)/4 Where 'clock_rate' is in MHz
|
||
|
// #1230: ;
|
||
|
// #1231: ;Registers used s0
|
||
|
// #1232: ;
|
||
|
// @1ba #1233: [delay_1us]
|
||
|
0000b // @1ba #1233: LOAD(s0,delay_1us_constant)
|
||
|
// @1bb #1234: [wait_1us]
|
||
|
1c001 // @1bb #1234: SUB(s0,1)
|
||
|
355bb // @1bc #1235: JUMP(NZ,wait_1us)
|
||
|
2a000 // @1bd #1236: RETURN
|
||
|
// #1237: ;
|
||
|
// #1238: ;Delay of 40us.
|
||
|
// #1239: ;
|
||
|
// #1240: ;Registers used s0, s1
|
||
|
// #1241: ;
|
||
|
// @1be #1242: [delay_40us]
|
||
|
00128 // @1be #1242: LOAD(s1,40) ;40 x 1us = 40us
|
||
|
// @1bf #1243: [wait_40us]
|
||
|
301ba // @1bf #1243: CALL(delay_1us)
|
||
|
1c101 // @1c0 #1244: SUB(s1,1)
|
||
|
355bf // @1c1 #1245: JUMP(NZ,wait_40us)
|
||
|
2a000 // @1c2 #1246: RETURN
|
||
|
// #1247: ;
|
||
|
// #1248: ;
|
||
|
// #1249: ;Delay of 1ms.
|
||
|
// #1250: ;
|
||
|
// #1251: ;Registers used s0, s1, s2
|
||
|
// #1252: ;
|
||
|
// @1c3 #1253: [delay_1ms]
|
||
|
00219 // @1c3 #1253: LOAD(s2,25) ;25 x 40us = 1ms
|
||
|
// @1c4 #1254: [wait_1ms]
|
||
|
301be // @1c4 #1254: CALL(delay_40us)
|
||
|
1c201 // @1c5 #1255: SUB(s2,1)
|
||
|
355c4 // @1c6 #1256: JUMP(NZ,wait_1ms)
|
||
|
2a000 // @1c7 #1257: RETURN
|
||
|
// #1258: ;
|
||
|
// #1259: ;Delay of 20ms.
|
||
|
// #1260: ;
|
||
|
// #1261: ;Delay of 20ms used during initialisation.
|
||
|
// #1262: ;
|
||
|
// #1263: ;Registers used s0, s1, s2, s3
|
||
|
// #1264: ;
|
||
|
// @1c8 #1265: [delay_20ms]
|
||
|
00314 // @1c8 #1265: LOAD(s3,20) ;20 x 1ms = 20ms
|
||
|
// @1c9 #1266: [wait_20ms]
|
||
|
301c3 // @1c9 #1266: CALL(delay_1ms)
|
||
|
1c301 // @1ca #1267: SUB(s3,1)
|
||
|
355c9 // @1cb #1268: JUMP(NZ,wait_20ms)
|
||
|
2a000 // @1cc #1269: RETURN
|
||
|
// #1270: ;
|
||
|
// #1271: ;Delay of approximately 1 second.
|
||
|
// #1272: ;
|
||
|
// #1273: ;Registers used s0, s1, s2, s3, s4
|
||
|
// #1274: ;
|
||
|
// @1cd #1275: [delay_1s]
|
||
|
00432 // @1cd #1275: LOAD(s4,50) ;50 x 20ms = 1000ms
|
||
|
// @1ce #1276: [wait_1s]
|
||
|
301c8 // @1ce #1276: CALL(delay_20ms)
|
||
|
1c401 // @1cf #1277: SUB(s4,1)
|
||
|
355ce // @1d0 #1278: JUMP(NZ,wait_1s)
|
||
|
2a000 // @1d1 #1279: RETURN
|
||
|
// #1280: ;
|
||
|
// #1281: ;
|
||
|
// #1282: ;
|
||
|
// #1283: ;**************************************************************************************
|
||
|
// #1284: ;LCD Character Module Routines
|
||
|
// #1285: ;**************************************************************************************
|
||
|
// #1286: ;
|
||
|
// #1287: ;LCD module is a 16 character by 2 line display but all displays are very similar
|
||
|
// #1288: ;The 4-wire data interface will be used (DB4 to DB7).
|
||
|
// #1289: ;
|
||
|
// #1290: ;The LCD modules are relatively slow and software delay loops are used to slow down
|
||
|
// #1291: ;KCPSM3 adequately for the LCD to communicate. The delay routines are provided in
|
||
|
// #1292: ;a different section (see above in this case).
|
||
|
// #1293: ;
|
||
|
// #1294: ;
|
||
|
// #1295: ;Pulse LCD enable signal 'E' high for greater than 230ns (1us is used).
|
||
|
// #1296: ;
|
||
|
// #1297: ;Register s4 should define the current state of the LCD output port.
|
||
|
// #1298: ;
|
||
|
// #1299: ;Registers used s0, s4
|
||
|
// #1300: ;
|
||
|
// @1d2 #1301: [LCD_pulse_E]
|
||
|
0e401 // @1d2 #1301: XOR(s4,LCD_E) ;E=1
|
||
|
2c440 // @1d3 #1302: OUTPUT(s4,LCD_output_port)
|
||
|
301ba // @1d4 #1303: CALL(delay_1us)
|
||
|
0e401 // @1d5 #1304: XOR(s4,LCD_E) ;E=0
|
||
|
2c440 // @1d6 #1305: OUTPUT(s4,LCD_output_port)
|
||
|
2a000 // @1d7 #1306: RETURN
|
||
|
// #1307: ;
|
||
|
// #1308: ;Write 4-bit instruction to LCD display.
|
||
|
// #1309: ;
|
||
|
// #1310: ;The 4-bit instruction should be provided in the upper 4-bits of register s4.
|
||
|
// #1311: ;Note that this routine does not release the master enable but as it is only
|
||
|
// #1312: ;used during initialisation and as part of the 8-bit instruction write it
|
||
|
// #1313: ;should be acceptable.
|
||
|
// #1314: ;
|
||
|
// #1315: ;Registers used s4
|
||
|
// #1316: ;
|
||
|
// @1d8 #1317: [LCD_write_inst4]
|
||
|
0a4f8 // @1d8 #1317: AND(s4,F8) ;Enable=1 RS=0 Instruction, RW=0 Write, E=0
|
||
|
2c440 // @1d9 #1318: OUTPUT(s4,LCD_output_port) ;set up RS and RW >40ns before enable pulse
|
||
|
301d2 // @1da #1319: CALL(LCD_pulse_E)
|
||
|
2a000 // @1db #1320: RETURN
|
||
|
// #1321: ;
|
||
|
// #1322: ;
|
||
|
// #1323: ;Write 8-bit instruction to LCD display.
|
||
|
// #1324: ;
|
||
|
// #1325: ;The 8-bit instruction should be provided in register s5.
|
||
|
// #1326: ;Instructions are written using the following sequence
|
||
|
// #1327: ; Upper nibble
|
||
|
// #1328: ; wait >1us
|
||
|
// #1329: ; Lower nibble
|
||
|
// #1330: ; wait >40us
|
||
|
// #1331: ;
|
||
|
// #1332: ;Registers used s0, s1, s4, s5
|
||
|
// #1333: ;
|
||
|
// @1dc #1334: [LCD_write_inst8]
|
||
|
01450 // @1dc #1334: LOAD(s4,s5)
|
||
|
0a4f0 // @1dd #1335: AND(s4,F0) ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
|
||
|
0c408 // @1de #1336: OR(s4,LCD_drive) ;Enable=1
|
||
|
301d8 // @1df #1337: CALL(LCD_write_inst4) ;write upper nibble
|
||
|
301ba // @1e0 #1338: CALL(delay_1us) ;wait >1us
|
||
|
01450 // @1e1 #1339: LOAD(s4,s5) ;select lower nibble with
|
||
|
20407 // @1e2 #1340: SL1(s4) ;Enable=1
|
||
|
20406 // @1e3 #1341: SL0(s4) ;RS=0 Instruction
|
||
|
20406 // @1e4 #1342: SL0(s4) ;RW=0 Write
|
||
|
20406 // @1e5 #1343: SL0(s4) ;E=0
|
||
|
301d8 // @1e6 #1344: CALL(LCD_write_inst4) ;write lower nibble
|
||
|
301be // @1e7 #1345: CALL(delay_40us) ;wait >40us
|
||
|
004f0 // @1e8 #1346: LOAD(s4,F0) ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
|
||
|
2c440 // @1e9 #1347: OUTPUT(s4,LCD_output_port) ;Release master enable
|
||
|
2a000 // @1ea #1348: RETURN
|
||
|
// #1349: ;
|
||
|
// #1350: ;
|
||
|
// #1351: ;
|
||
|
// #1352: ;Write 8-bit data to LCD display.
|
||
|
// #1353: ;
|
||
|
// #1354: ;The 8-bit data should be provided in register s5.
|
||
|
// #1355: ;Data bytes are written using the following sequence
|
||
|
// #1356: ; Upper nibble
|
||
|
// #1357: ; wait >1us
|
||
|
// #1358: ; Lower nibble
|
||
|
// #1359: ; wait >40us
|
||
|
// #1360: ;
|
||
|
// #1361: ;Registers used s0, s1, s4, s5
|
||
|
// #1362: ;
|
||
|
// @1eb #1363: [LCD_write_data]
|
||
|
01450 // @1eb #1363: LOAD(s4,s5)
|
||
|
0a4f0 // @1ec #1364: AND(s4,F0) ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
|
||
|
0c40c // @1ed #1365: OR(s4,12) ;Enable=1 RS=1 Data, RW=0 Write, E=0
|
||
|
2c440 // @1ee #1366: OUTPUT(s4,LCD_output_port) ;set up RS and RW >40ns before enable pulse
|
||
|
301d2 // @1ef #1367: CALL(LCD_pulse_E) ;write upper nibble
|
||
|
301ba // @1f0 #1368: CALL(delay_1us) ;wait >1us
|
||
|
01450 // @1f1 #1369: LOAD(s4,s5) ;select lower nibble with
|
||
|
20407 // @1f2 #1370: SL1(s4) ;Enable=1
|
||
|
20407 // @1f3 #1371: SL1(s4) ;RS=1 Data
|
||
|
20406 // @1f4 #1372: SL0(s4) ;RW=0 Write
|
||
|
20406 // @1f5 #1373: SL0(s4) ;E=0
|
||
|
2c440 // @1f6 #1374: OUTPUT(s4,LCD_output_port) ;set up RS and RW >40ns before enable pulse
|
||
|
301d2 // @1f7 #1375: CALL(LCD_pulse_E) ;write lower nibble
|
||
|
301be // @1f8 #1376: CALL(delay_40us) ;wait >40us
|
||
|
004f0 // @1f9 #1377: LOAD(s4,F0) ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
|
||
|
2c440 // @1fa #1378: OUTPUT(s4,LCD_output_port) ;Release master enable
|
||
|
2a000 // @1fb #1379: RETURN
|
||
|
// #1380: ;
|
||
|
// #1381: ;
|
||
|
// #1382: ;
|
||
|
// #1383: ;
|
||
|
// #1384: ;Read 8-bit data from LCD display.
|
||
|
// #1385: ;
|
||
|
// #1386: ;The 8-bit data will be read from the current LCD memory address
|
||
|
// #1387: ;and will be returned in register s5.
|
||
|
// #1388: ;It is advisable to set the LCD address (cursor position) before
|
||
|
// #1389: ;using the data read for the first time otherwise the display may
|
||
|
// #1390: ;generate invalid data on the first read.
|
||
|
// #1391: ;
|
||
|
// #1392: ;Data bytes are read using the following sequence
|
||
|
// #1393: ; Upper nibble
|
||
|
// #1394: ; wait >1us
|
||
|
// #1395: ; Lower nibble
|
||
|
// #1396: ; wait >40us
|
||
|
// #1397: ;
|
||
|
// #1398: ;Registers used s0, s1, s4, s5
|
||
|
// #1399: ;
|
||
|
// @1fc #1400: [LCD_read_data8]
|
||
|
0040e // @1fc #1400: LOAD(s4,14) ;Enable=1 RS=1 Data, RW=1 Read, E=0
|
||
|
2c440 // @1fd #1401: OUTPUT(s4,LCD_output_port) ;set up RS and RW >40ns before enable pulse
|
||
|
0e401 // @1fe #1402: XOR(s4,LCD_E) ;E=1
|
||
|
2c440 // @1ff #1403: OUTPUT(s4,LCD_output_port)
|
||
|
301ba // @200 #1404: CALL(delay_1us) ;wait >260ns to access data
|
||
|
04501 // @201 #1405: INPUT(s5,LCD_input_port) ;read upper nibble
|
||
|
0e401 // @202 #1406: XOR(s4,LCD_E) ;E=0
|
||
|
2c440 // @203 #1407: OUTPUT(s4,LCD_output_port)
|
||
|
301ba // @204 #1408: CALL(delay_1us) ;wait >1us
|
||
|
0e401 // @205 #1409: XOR(s4,LCD_E) ;E=1
|
||
|
2c440 // @206 #1410: OUTPUT(s4,LCD_output_port)
|
||
|
301ba // @207 #1411: CALL(delay_1us) ;wait >260ns to access data
|
||
|
04001 // @208 #1412: INPUT(s0,LCD_input_port) ;read lower nibble
|
||
|
0e401 // @209 #1413: XOR(s4,LCD_E) ;E=0
|
||
|
2c440 // @20a #1414: OUTPUT(s4,LCD_output_port)
|
||
|
0a5f0 // @20b #1415: AND(s5,F0) ;merge upper and lower nibbles
|
||
|
2000e // @20c #1416: SR0(s0)
|
||
|
2000e // @20d #1417: SR0(s0)
|
||
|
2000e // @20e #1418: SR0(s0)
|
||
|
2000e // @20f #1419: SR0(s0)
|
||
|
0d500 // @210 #1420: OR(s5,s0)
|
||
|
00404 // @211 #1421: LOAD(s4,4) ;Enable=0 RS=1 Data, RW=0 Write, E=0
|
||
|
2c440 // @212 #1422: OUTPUT(s4,LCD_output_port) ;Stop reading 5V device and release master enable
|
||
|
301be // @213 #1423: CALL(delay_40us) ;wait >40us
|
||
|
2a000 // @214 #1424: RETURN
|
||
|
// #1425: ;
|
||
|
// #1426: ;
|
||
|
// #1427: ;Reset and initialise display to communicate using 4-bit data mode
|
||
|
// #1428: ;Includes routine to clear the display.
|
||
|
// #1429: ;
|
||
|
// #1430: ;Requires the 4-bit instructions 3,3,3,2 to be sent with suitable delays
|
||
|
// #1431: ;following by the 8-bit instructions to set up the display.
|
||
|
// #1432: ;
|
||
|
// #1433: ; 28 = '001' Function set, '0' 4-bit mode, '1' 2-line, '0' 5x7 dot matrix, 'xx'
|
||
|
// #1434: ; 06 = '000001' Entry mode, '1' increment, '0' no display shift
|
||
|
// #1435: ; 0E = '00001' Display control, '1' display on, '1' cursor off, '0' cursor blink off
|
||
|
// #1436: ; 01 = '00000001' Display clear
|
||
|
// #1437: ;
|
||
|
// #1438: ;Registers used s0, s1, s2, s3, s4
|
||
|
// #1439: ;
|
||
|
// @215 #1440: [LCD_reset]
|
||
|
301c8 // @215 #1440: CALL(delay_20ms) ;wait more that 15ms for display to be ready
|
||
|
00430 // @216 #1441: LOAD(s4,48)
|
||
|
301d8 // @217 #1442: CALL(LCD_write_inst4) ;send '3'
|
||
|
301c8 // @218 #1443: CALL(delay_20ms) ;wait >4.1ms
|
||
|
301d8 // @219 #1444: CALL(LCD_write_inst4) ;send '3'
|
||
|
301c3 // @21a #1445: CALL(delay_1ms) ;wait >100us
|
||
|
301d8 // @21b #1446: CALL(LCD_write_inst4) ;send '3'
|
||
|
301be // @21c #1447: CALL(delay_40us) ;wait >40us
|
||
|
00420 // @21d #1448: LOAD(s4,32)
|
||
|
301d8 // @21e #1449: CALL(LCD_write_inst4) ;send '2'
|
||
|
301be // @21f #1450: CALL(delay_40us) ;wait >40us
|
||
|
00528 // @220 #1451: LOAD(s5,40) ;Function set
|
||
|
301dc // @221 #1452: CALL(LCD_write_inst8)
|
||
|
00506 // @222 #1453: LOAD(s5,6) ;Entry mode
|
||
|
301dc // @223 #1454: CALL(LCD_write_inst8)
|
||
|
0050e // @224 #1455: LOAD(s5,14) ;Display control
|
||
|
301dc // @225 #1456: CALL(LCD_write_inst8)
|
||
|
// @226 #1457: [LCD_clear]
|
||
|
00501 // @226 #1457: LOAD(s5,1) ;Display clear
|
||
|
301dc // @227 #1458: CALL(LCD_write_inst8)
|
||
|
301c3 // @228 #1459: CALL(delay_1ms) ;wait >1.64ms for display to clear
|
||
|
301c3 // @229 #1460: CALL(delay_1ms)
|
||
|
2a000 // @22a #1461: RETURN
|
||
|
// #1462: ;
|
||
|
// #1463: ;Position the cursor ready for characters to be written.
|
||
|
// #1464: ;The display is formed of 2 lines of 16 characters and each
|
||
|
// #1465: ;position has a corresponding address as indicated below.
|
||
|
// #1466: ;
|
||
|
// #1467: ; Character position
|
||
|
// #1468: ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||
|
// #1469: ;
|
||
|
// #1470: ; Line 1 - 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
|
||
|
// #1471: ; Line 2 - C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
|
||
|
// #1472: ;
|
||
|
// #1473: ;This routine will set the cursor position using the value provided
|
||
|
// #1474: ;in register s5. The upper nibble will define the line and the lower
|
||
|
// #1475: ;nibble the character position on the line.
|
||
|
// #1476: ; Example s5 = 2B will position the cursor on line 2 position 11
|
||
|
// #1477: ;
|
||
|
// #1478: ;Registers used s0, s1, s2, s3, s4
|
||
|
// #1479: ;
|
||
|
// @22b #1480: [LCD_cursor]
|
||
|
12510 // @22b #1480: TEST(s5,16) ;test for line 1
|
||
|
35231 // @22c #1481: JUMP(Z,set_line2)
|
||
|
0a50f // @22d #1482: AND(s5,15) ;make address in range 80 to 8F for line 1
|
||
|
0c580 // @22e #1483: OR(s5,128)
|
||
|
301dc // @22f #1484: CALL(LCD_write_inst8) ;instruction write to set cursor
|
||
|
2a000 // @230 #1485: RETURN
|
||
|
// @231 #1486: [set_line2]
|
||
|
0a50f // @231 #1486: AND(s5,15) ;make address in range C0 to CF for line 2
|
||
|
0c5c0 // @232 #1487: OR(s5,C0)
|
||
|
301dc // @233 #1488: CALL(LCD_write_inst8) ;instruction write to set cursor
|
||
|
2a000 // @234 #1489: RETURN
|
||
|
// #1490: ;
|
||
|
// #1491: ;This routine will shift the complete display one position to the left.
|
||
|
// #1492: ;The cursor position and LCD memory contents will not change.
|
||
|
// #1493: ;
|
||
|
// #1494: ;
|
||
|
// #1495: ;Registers used s0, s1, s2, s3, s4, s5
|
||
|
// #1496: ;
|
||
|
// @235 #1497: [LCD_shift_left]
|
||
|
00518 // @235 #1497: LOAD(s5,24) ;shift display left
|
||
|
301dc // @236 #1498: CALL(LCD_write_inst8)
|
||
|
2a000 // @237 #1499: RETURN
|
||
|
// #1500: ;
|
||
|
// #1501: ;**************************************************************************************
|
||
|
// #1502: ;Interrupt Service Routine (ISR)
|
||
|
// #1503: ;**************************************************************************************
|
||
|
// #1504: ;
|
||
|
// #1505: ;Interrupts occur when the rotary control has been moved.
|
||
|
// #1506: ;
|
||
|
// #1507: ;The ISR captures the state of the direction which it writes to scratch pad memory (SPM).
|
||
|
// #1508: ;The most significant bit is also set at this location to provide a 'flag' to the
|
||
|
// #1509: ;main body of the program.
|
||
|
// #1510: ;
|
||
|
// #1511: ;
|
||
|
// @238 #1512: [ISR]
|
||
|
2e001 // @238 #1512: STORE(s0,ISR_preserve_s0) ;preserve s0
|
||
|
04000 // @239 #1513: INPUT(s0,rotary_port) ;read rotary encoder
|
||
|
0c080 // @23a #1514: OR(s0,rotary_event) ;set flag
|
||
|
2e000 // @23b #1515: STORE(s0,rotary_status) ;put result in SCM
|
||
|
06001 // @23c #1516: FETCH(s0,ISR_preserve_s0) ;restore s0
|
||
|
38001 // @23d #1517: RETURNI(ENABLE)
|
||
|
// #1518: ;
|
||
|
// #1519: ;
|
||
|
// #1520: ;**************************************************************************************
|
||
|
// #1521: ;Interrupt Vector
|
||
|
// #1522: ;**************************************************************************************
|
||
|
// #1523: ;
|
||
|
@3ff // #1524: ADDRESS(1023)
|
||
|
34238 // @3ff #1525: JUMP(ISR)
|
||
|
// #1526: ;
|
||
|
// #1527: ;
|