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

1331 lines
71 KiB
Plaintext

;KCPSM3 Program - SPI Control of Amplifier and A/D converter on Spartan-3E Starter Kit.
;
;
;Ken Chapman - Xilinx Ltd
;
;Version v1.00 - 21th December 2005
;
;This program uses an 8KHz interrupt to generate test waveforms on the
;4 analogue outputs provided by the Linear Technology LTC2624 device.
;
;As well as the port connections vital to communication with the UART and the SPI
;FLASH memory, there are additional port connections used to disable the other
;devices sharing the SPI bus on the Starter Kit board. Although these could have been
;controlled at the hardware level, they are included in this code to aid
;future investigations of communication with the other SPI devices using PicoBlaze.
;
;Connections to the LEDs, switches and press buttons are provided to aid
;development and enable further experiments. Otherwise know as having fun!
;
;Port definitions
;
;
CONSTANT SPI_control_port, 08 ;SPI clock and chip selects
CONSTANT SPI_sck, 01 ; SCK - bit0
CONSTANT SPI_rom_cs, 02 ; serial rom select - bit1
CONSTANT SPI_spare_control, 04 ; spare - bit2
CONSTANT SPI_amp_cs, 08 ; amplifier select - bit3
CONSTANT SPI_adc_conv, 10 ; A/D convert - bit4
CONSTANT SPI_dac_cs, 20 ; D/A select - bit5
CONSTANT SPI_amp_shdn, 40 ; amplifier SHDN - bit6
CONSTANT SPI_dac_clr, 80 ; D/A clear - bit7
;
CONSTANT SPI_output_port, 04 ;SPI data output
CONSTANT SPI_sdo, 80 ; SDO - bit7
;
CONSTANT SPI_input_port, 01 ;SPI data input
CONSTANT SPI_sdi, 80 ; SDI - bit7
CONSTANT SPI_amp_sdi, 40 ; amplifier SDI - bit6
;
;
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 switch_port, 00 ;Read switches and press buttons
CONSTANT BTN_north, 01 ; Buttons North - bit0
CONSTANT BTN_east, 02 ; East - bit1
CONSTANT BTN_south, 04 ; South - bit2
CONSTANT BTN_west, 08 ; West - bit3
CONSTANT switch0, 10 ; Switches 0 - bit4
CONSTANT switch1, 20 ; 1 - bit5
CONSTANT switch2, 40 ; 2 - bit6
CONSTANT switch3, 80 ; 3 - bit7
;
;LCD interface ports
;
;The master enable signal is not used by the LCD display itself
;but may be required to confirm that LCD communication is active.
;This is required on the Spartan-3E Starter Kit if the StrataFLASH
;is used because it shares the same data pins and conflicts must be avoided.
;
CONSTANT LCD_output_port, 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, 02 ;LCD character module input data
CONSTANT LCD_read_spare0, 01 ; Spare bits - bit0
CONSTANT LCD_read_spare1, 02 ; are zero - bit1
CONSTANT LCD_read_spare2, 04 ; - bit2
CONSTANT LCD_read_spare3, 08 ; - bit3
CONSTANT LCD_read_DB4, 10 ; 4-bit Data DB4 - bit4
CONSTANT LCD_read_DB5, 20 ; interface Data DB5 - bit5
CONSTANT LCD_read_DB6, 40 ; Data DB6 - bit6
CONSTANT LCD_read_DB7, 80 ; Data DB7 - bit7
;
;
;
;
;Special Register usage
;
;
;
;Scratch Pad Memory Locations
;
;Values read from the A/D converter
;
CONSTANT ADC0_lsb, 00 ;ADC Channel 0 value LS-Byte
CONSTANT ADC0_msb, 01 ; MS-Byte
;
CONSTANT ADC1_lsb, 02 ;ADC Channel 1 value LS-Byte
CONSTANT ADC1_msb, 03 ; MS-Byte
;
;Amplifier gain settings.
;
;Stored value is the 4-bit code for gain setting
; Code 1 2 3 4 5 6 7
; Gain -1 -2 -5 -10 -20 -50 -100
CONSTANT amp_A_gain, 04 ;Amplifier A gain value
CONSTANT amp_B_gain, 05 ;Amplifier B gain value
;
;Sample counter used to give activity indication on LEDs
;
CONSTANT sample_count, 06 ;8-bit counter LS-Byte
;
CONSTANT decimal0, 07 ;5 digit decimal value
CONSTANT decimal1, 08
CONSTANT decimal2, 09
CONSTANT decimal3, 0A
CONSTANT decimal4, 0B
;
;
;
;
;Useful data constants
;
CONSTANT VREF_lsb, 72 ;Reference voltage in milli-volts
CONSTANT VREF_msb, 06 ;Nominal value 1.65v so value is 1650 (0672 hex)
;
;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 SPI_init ;initialise SPI bus ports
CALL LCD_reset ;initialise LCD display
;
;Write welcome message to LCD display
;
LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
CALL disp_PicoBlaze ;Display 'PicoBlaze Inside'
LOAD s5, 23 ;Line 2 position 3
CALL LCD_cursor
CALL disp_ADC_Control
CALL delay_1s ;wait 5 seconds
CALL delay_1s
CALL delay_1s
CALL delay_1s
CALL delay_1s
CALL LCD_clear ;Clear display
;
LOAD s0, 00 ;clear event counter
STORE s0, sample_count
;
;
;
;
LOAD s0, 01 ;set initial amplifier gain to 1 on both channels
STORE s0, amp_A_gain
STORE s0, amp_B_gain
JUMP new_gain_set ;set, display the initial gain and enable interrupts
;
;
;The program is interrupt driven to maintain an 8KHz sample rate. The main body
;of the program waits for an interrupt to occur. The interrupt updates all four
;analogue outputs with values stored in scratch pad memory. This takes approximately
;58us of the 125us available between interrupts. The main program then prepares
;new values for the analogue outputs (in less than 67us) before waiting for the
;next interrupt.
;
;
warm_start: LOAD sF, FF ;flag set and wait for interrupt to be serviced
ENABLE INTERRUPT ;normal operation
wait_int: INPUT sE, switch_port ;test for button press changes to amplifier gain
TEST sE, BTN_north ;sE used as this in not effected by ISR
JUMP NZ, gain_increase
TEST sE, BTN_south
JUMP NZ, gain_decrease
COMPARE sF, FF ;wait for interrupt
JUMP Z, wait_int ;interrupt clears the flag
;
;
;
;Drive LEDs with simple binary count of the samples to indicate
;that the design is active.
;
FETCH s0, sample_count ;increment counter
ADD s0, 01
STORE s0, sample_count
OUTPUT s0, LED_port ;count increments at 1Hz
;
;
;Display the A/D Channel 0 value as hex on LCD
;
LOAD s5, 2C ;Line 2 position 12
CALL LCD_cursor
FETCH s0, ADC0_msb
CALL disp_hex_byte
FETCH s0, ADC0_lsb
CALL disp_hex_byte
;
;
;
;Convert A/D channel 0 value to decimal voltage
;
;The 14-bit signed value from the A/D (sign extended to 16-bits)
;relates to a voltage in the range -1.25v to +1.25v at the input
;to the A/D converter relative to the 1.65v mid-rail reference point.
;
;The 14-bit value can be translated into the -1.25v to +1.25v using the
;simple equation...
;
; ADin = AD_value x 1.25/8192
;
;It is possible to scale the AD_value by 1.25/8192 using a fixed point
;representation.
;
;However, it is also possible to scale it by another factor at the
;same time which nicely converts to a binary value which is readily
;converted to decimal. This can be achieved by example...
;
;For an input to the A/D converter of +1.25v relative to the reference,
;the A/D will output the maximum conversion of 1FFF (+8191).
;
;In this case we would like to have the result value +1.250v which can be represented
;by the integer value +1250 with appropiate positioning of the decimal point.
;The constant to achieve this conversion is +1250/8191=+0.152606...
;Also a number requiring fixed point representation but how many bits to use?
;
;The way to resolve this is to realise that a multiplication will be
;performed and it would be nice if the +1250 result ended up in a register pair.
;So if we perform a 16x16-bit multiplication such that the upper 16-bits of
;the 32-bit result is the required value, then everything will resolve itself.
;
;Hence the constant required is actually (1250x(2^16))/8191=+10001 (2711 hex).
;
;Using the example 1FFF x 2711 = 04E1F8EF
; of which the upper 16-bits = 04E1 (+1249 decimal)
;
;Likewise the other limit case is E000 x 2711 = FB1DE000
; of which the upper 16-bits = FB1D (-1251 decimal)
;
;The values can be made perfect by rounding before truncation
;
FETCH s2, ADC0_lsb ;Read A/D channel 0 value
FETCH s3, ADC0_msb
LOAD s0, 11 ;scaling value for input to A/D converter
LOAD s1, 27
CALL mult_16x16s ;[s7,s6,s5,s4]=[s3,s2]x[s1,s0]
SL0 s5 ;round value before truncation
ADDCY s6, 00
ADDCY s7, 00
;
;The register pair [s7,s6] now holds the binary value
;representing the input level to the A/D converter in milli-volts.
;This is now displayed on the LCD. Negative values need to be converted to
;signed magnitude for display.
;
LOAD s5, 20 ;Line 2 position 0
CALL LCD_cursor
CALL disp_AD ;display A/D=
TEST s7, 80 ;test sign bit of value
JUMP NZ, neg_AD
LOAD s5, character_plus
JUMP AD_sign
neg_AD: XOR s6, FF ;complement [s7,s6] to make positive
XOR s7, FF
ADD s6, 01
ADDCY s7, 00
LOAD s5, character_minus
AD_sign: CALL LCD_write_data ;display sign of value
CALL disp_volts ;display 4 digit value as X.XXXv
;
;Convert A/D channel 0 value to display the VINA decimal voltage
;
;The same fundamental technique can be used to convert the 14-bit
;A/D value into the level at the VINA input except that two more factors
;must be considered.
;
;The first is that the amplifier inverts and has gain. Therefore the
;VINA input level is opposite polarity and could be a smaller deviation
;from the mid rail 1.65v reference.
;
;Secondly, to display the actual voltage level at the VINA terminal
;the 1.65v offset must be added.
;
;The voltage at the VINA input is therefore...
;
; VINA = [AD_value x (1.25/(8192 x G))]+1.65
;
;Following the same methodology as for the A/D value, it means that there
;is a set of scaling factors to deal with the negative gain values.
;
; K = (+1250 x (2^16)) / (8191 x G)
;
; G K (K Hex)
; -1 -10001 (D8EF)
; -2 -5001 (EC77)
; -5 -2000 (F830)
; -10 -1000 (FC18)
; -20 -500 (FE0C)
; -50 -200 (FF38)
; -100 -100 (FF9C)
;
FETCH s2, ADC0_lsb ;Read A/D channel 0 value
FETCH s3, ADC0_msb
FETCH s4, amp_A_gain ;read A gain and select appropiate gain setting
LOAD s0, EF ;scaling value for amplifier gain of -1
LOAD s1, D8
COMPARE s4, 01
JUMP Z, mult_VINA
LOAD s0, 77 ;scaling value for amplifier gain of -2
LOAD s1, EC
COMPARE s4, 02
JUMP Z, mult_VINA
LOAD s0, 30 ;scaling value for amplifier gain of -5
LOAD s1, F8
COMPARE s4, 03
JUMP Z, mult_VINA
LOAD s0, 18 ;scaling value for amplifier gain of -10
LOAD s1, FC
COMPARE s4, 05
JUMP Z, mult_VINA
LOAD s0, 0C ;scaling value for amplifier gain of -20
LOAD s1, FE
COMPARE s4, 06
JUMP Z, mult_VINA
LOAD s0, 38 ;scaling value for amplifier gain of -50
LOAD s1, FF
COMPARE s4, 01
JUMP Z, mult_VINA
LOAD s0, 9C ;scaling value for amplifier gain of -100
LOAD s1, FF
mult_VINA: CALL mult_16x16s ;[s7,s6,s5,s4]=[s3,s2]x[s1,s0]
SL0 s5 ;round value before truncation
ADDCY s6, 00
ADDCY s7, 00
ADD s6, VREF_lsb ;add 1.65v offset represented at 1650 (0672 hex)
ADDCY s7, VREF_msb
;
;The register pair [s7,s6] now holds the binary value
;representing the VINA input level in milli-volts.
;This must be a positive value due to the offset of 1.65v
;being greater than the maximum relative range of -1.25v to +1.25v.
;This binary value can now be converted to a decimal digits
;and displayed on the LCD.
;
;If the A/D value is maximum negative (E000) or maximum positive (1FFF)
;then an indication of the actual value being applied being greater or
;less than that computed will be made.
;
LOAD s5, 17 ;Line 1 position 7
CALL LCD_cursor
CALL disp_VA ;display VA=
FETCH s2, ADC0_lsb ;Read A/D channel 0 value
FETCH s3, ADC0_msb
COMPARE s3, E0 ;test for maximum negative
JUMP NZ, test_max_pos
COMPARE s2, 00
JUMP NZ, test_max_pos
LOAD s5, character_greater_than ;display >
CALL LCD_write_data
JUMP disp_VINA_volts
test_max_pos: COMPARE s3, 1F ;test for maximum positive
JUMP NZ, disp_VINA_volts
COMPARE s2, FF
JUMP NZ, disp_VINA_volts
LOAD s5, character_less_than ;display <
CALL LCD_write_data
disp_VINA_volts: CALL disp_volts ;display 4 digit value as X.XXXv
JUMP warm_start
;
;
;**************************************************************************************
;Display voltage level at in the form X.XXX on the LCD at current cursor position
;**************************************************************************************
;
;Value to be displayed must be unsigned (positive) in the
;[s7,s6] register pair. Only the lower 4 digits are displayed.
;
disp_volts: CALL integer16_to_BCD ;convert [s7,s6] to BCD in scratch pad memory
FETCH s5, decimal3
ADD s5, 30 ;convert to ASCII
CALL LCD_write_data
LOAD s5, character_stop
CALL LCD_write_data
FETCH s5, decimal2
ADD s5, 30 ;convert to ASCII
CALL LCD_write_data
FETCH s5, decimal1
ADD s5, 30 ;convert to ASCII
CALL LCD_write_data
FETCH s5, decimal0
ADD s5, 30 ;convert to ASCII
CALL LCD_write_data
LOAD s5, character_space ;ensure next position is cleared
CALL LCD_write_data
RETURN
;
;**************************************************************************************
;Changing amplifier gain using press buttons
;**************************************************************************************
;
;Possible gain values are
; Gain Amplifier
; code
; -1 1
; -2 2
; -5 3
; -10 4
; -20 5
; -50 6
; -100 7
;
gain_increase: DISABLE INTERRUPT ;stop normal operation
FETCH s0, amp_A_gain ;read current gain
ADD s0, 01
COMPARE s0, 08 ;test for too big
JUMP NZ, new_gain_set
LOAD s0, 07 ;maximum gain
JUMP new_gain_set
gain_decrease: DISABLE INTERRUPT ;stop normal operation
FETCH s0, amp_A_gain ;read current gain
SUB s0, 01
JUMP NZ, new_gain_set
LOAD s0, 01 ;minimum gain
new_gain_set: STORE s0, amp_A_gain ;store new value
FETCH s2, amp_B_gain ;form the amplifier control byte
SL0 s2 ;B amplifier set by upper 4 bits
SL0 s2
SL0 s2
SL0 s2
OR s2, s0 ;A amplifier set by lower
CALL set_amp ;set SPI amplifier
;display gain setting on LCD
LOAD s5, 10 ;Line 1 position 0
CALL LCD_cursor
LOAD s5, character_G
CALL LCD_write_data
LOAD s5, character_equals
CALL LCD_write_data
LOAD s5, character_minus
CALL LCD_write_data
FETCH s0, amp_A_gain ;read A gain setting
COMPARE s0, 01 ;determine actual gain value
JUMP NZ, test_A2
LOAD s5, character_1 ;gain is -1
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
JUMP wait_no_press
test_A2: COMPARE s0, 02
JUMP NZ, test_A3
LOAD s5, character_2 ;gain is -2
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
JUMP wait_no_press
test_A3: COMPARE s0, 03
JUMP NZ, test_A4
LOAD s5, character_5 ;gain is -5
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
JUMP wait_no_press
test_A4: COMPARE s0, 04
JUMP NZ, test_A5
LOAD s5, character_1 ;gain is -10
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
JUMP wait_no_press
test_A5: COMPARE s0, 05
JUMP NZ, test_A6
LOAD s5, character_2 ;gain is -20
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
JUMP wait_no_press
test_A6: COMPARE s0, 06
JUMP NZ, gain_A7
LOAD s5, character_5 ;gain is -50
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
JUMP wait_no_press
gain_A7: LOAD s5, character_1 ;gain is -100
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
LOAD s5, character_0
CALL LCD_write_data
wait_no_press: CALL delay_20ms ;delay to help avoid switch bounce
INPUT s0, switch_port ;check for release of press buttons
TEST s0, 05 ;north and south buttons
JUMP NZ, wait_no_press
JUMP warm_start
;
;**************************************************************************************
;16-bit by 16-bit Signed multiplier
;**************************************************************************************
;
;16 bit signed multiplication using shift and add technique.
;The full precision 32-bit product is returned.
;
;The key to signed multiplication is to think of all bits of the second operand
;[s1,s0] as being positive except for the most significant bit. This means that
;the first operand is added to the result in all cases when there is a '1' in the
;second operand except for the MSB case when the first operand is subtracted if there
;is a '1'.
;
;[s7,s6,s5,s4]=[s3,s2]x[s1,s0]
;
;Registers used s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,sA
;
mult_16x16s: LOAD s7, 00 ;clear accumulator
LOAD s6, 00
LOAD s5, 00 ;Set bit 14 to act as a bit shift counter
LOAD s4, 00
LOAD s8, 00 ;sign extend [s3,s2] to form [s9,s8,s3,s2]
TEST s3, 80 ;test sign of first operand
JUMP Z, m16s_pos
LOAD s8, FF
m16s_pos: LOAD s9, s8 ;[s9,s8,s3,s2]=0000xxxx or FFFFxxxx as required
LOAD sA, 0F ;15 positive shift and add operations to perform
m16s_loop: SR0 s1 ;shift right operand [s1,s0]
SRA s0
JUMP NC, m16s_noadd ;test for a '1'
ADD s4, s2 ;32-bit addition [s7,s6,s5,s4]=[s7,s6,s5,s4]+[s9,s8,s3,s2]
ADDCY s5, s3
ADDCY s6, s8
ADDCY s7, s9
m16s_noadd: SL0 s2 ;multiply first operand by 2
SLA s3
SLA s8
SLA s9
SUB sA, 01
JUMP NZ, m16s_loop ;move to next unsigned bit
TEST s0, 01 ;test sign bit of operand [s1,s0]
JUMP NC, m16s_nosub
SUB s4, s2 ;32-bit subtraction [s7,s6,s5,s4]=[s7,s6,s5,s4]-[s9,s8,s3,s2]
SUBCY s5, s3
SUBCY s6, s8
SUBCY s7, s9
m16s_nosub: RETURN
;
;
;
;**************************************************************************************
;16-bit positive integer to 5 digit decimal conversion
;**************************************************************************************
;
;Convert the 16 bit value in register set [s7,s6]
;into the BCD decimal equivalent located in the scratch pad memory
;locations 'decimal0' to 'decimal4' which must be in ascending locations.
;
;Register set [s9,s8,s7,s6] are preserved.
;
;
;Each digit is formed in turn starting with the least significant.
;
;Registers used s0,s1,s2,s3,s4,s5,s6,s7,s8
;
integer16_to_BCD: LOAD s0, 05 ;5 digits to be formed from value up to 65535
LOAD s8, decimal0 ;pointer for LS-Digit
int_to_BCD_loop: CALL divide_16bit_by_10 ;[s7,s6]=[s7,s6]/10 with remainder in s4
STORE s4, (s8) ;remainder becomes digit value
ADD s8, 01 ;move to next most significant digit
SUB s0, 01 ;one less digit to compute
JUMP NZ, int_to_BCD_loop
RETURN
;
;Divide 16-bit binary integer by 10
;
;The value to be divided is held in register set [s7,s6]
;and this is where the result is returned to.
;
;At then end of the integer division the remainder in the range 0 to 9
;will be in register s4.
;
;Registers used s1,s2,s3,s4,s5,s6,s7
;Other registers are used but are preserved
;
divide_16bit_by_10: LOAD s4, s6 ;copy input value to [s5,s4]
LOAD s5, s7
LOAD s6, 00 ;clear result
LOAD s7, 00
LOAD s2, 00 ;initialise '10' value into msb's of set [s3,s2]
LOAD s3, A0
LOAD s1, 0D ;13 subtract and shift iterations to be performed
div10_loop: SUB s4, s2 ;perform 16-bit subtract [s5,s4]-[s3,s2]
SUBCY s5, s3
JUMP C, div10_restore
SL1 s6 ;shift '1' into result because subtract was possible
JUMP div10_shifts
div10_restore: ADD s4, s2 ;perform 32-bit addition [s5,s4]+[s3,s2]
ADDCY s5, s3 ;to restore value
SL0 s6 ;shift '0' into result because subtract was not possible
div10_shifts: SLA s7 ;complete 16-bit shift left
SR0 s3 ;divide '10' value by 2 (shift right 1 place)
SRA s2
SUB s1, 01 ;count iterations
JUMP NZ, div10_loop
RETURN
;
;
;**************************************************************************************
;SPI communication routines for Spartan-3E Starter Kit
;**************************************************************************************
;
;These routines will work with two output ports and one input port which should be
;defined as follows using CONSTANT directives.
; (replace 'pp' with appropriate port address in each case)
;In the list of CONSTANT directives, there are ports associated with all the SPI devices
;provided on the board. Even if some devices are not used, it is vital that the remaining
;devices are disabled. Leaving all signals connected and use of these routines will ensure
;that all other devices are disabled when communicating with a particular device.
;
;
;
;CONSTANT SPI_control_port, pp ;SPI clock and chip selects
;CONSTANT SPI_sck, 01 ; SCK - bit0
;CONSTANT SPI_rom_cs, 02 ; serial rom select - bit1
;CONSTANT SPI_spare_control, 04 ; spare - bit2
;CONSTANT SPI_amp_cs, 08 ; amplifier select - bit3
;CONSTANT SPI_adc_conv, 10 ; A/D convert - bit4
;CONSTANT SPI_dac_cs, 20 ; D/A select - bit5
;CONSTANT SPI_amp_shdn, 40 ; amplifier SHDN - bit6
;CONSTANT SPI_dac_clr, 80 ; D/A clear - bit7
;
;CONSTANT SPI_output_port, pp ;SPI data output
;CONSTANT SPI_sdo, 80 ; SDO - bit7
;
;CONSTANT SPI_input_port, pp ;SPI data input
;CONSTANT SPI_sdi, 80 ; SDI - bit7
;CONSTANT SPI_amp_sdi, 40 ; amplifier SDI - bit6
;
;
;
;
;Initialise SPI bus
;
;This routine should be used to initialise the SPI bus.
;The SCK clock is made low.
;Device selections are made inactive as follows
; SPI_sck = 0 Clock is Low (required)
; SPI_rom_cs = 1 Deselect ROM
; spare = 1 spare control bit
; SPI_amp_cs = 1 Deselect amplifier
; SPI_adc_conv = 0 A/D convert ready to apply positive pulse
; SPI_dac_cs = 1 Deselect D/A
; SPI_amp_shdn = 0 Amplifier active and available
; SPI_dac_clr = 1 D/A clear off
;
SPI_init: LOAD s0, AE ;normally AE
OUTPUT s0, SPI_control_port
RETURN
;
;
;
;
;**************************************************************************************
;SPI communication routines for Programmable Amplifier
;**************************************************************************************
;
;
;Set the A and B channel gain of the Dual Amplifier (LTC6912-1).
;
;The gain value should be provided in the s2 register with the upper nibble
;defining the gain for the B channel and lower nibble the gain for the A channel.
; 0000 = 0 hex = Gain 0 with input hi-Z and output driving
; 0001 = 1 hex = Gain -1
; 0010 = 2 hex = Gain -2
; 0011 = 3 hex = Gain -5
; 0100 = 4 hex = Gain -10
; 0101 = 5 hex = Gain -20
; 0110 = 6 hex = Gain -50
; 0111 = 7 hex = Gain -100
; 1000 = 8 hex = software shutdown (power on default). Hi-Z output.
;
;On return, the s2, register will contain the response from the LTC6912-1 amplifier.
;This will be the same format and indicate the previous setting of the amplifier.
;The response is obtained from the dedicated AMP_SDI signal since the LTC6912 output
;is always active and can not be on a shared SPI bus.
;
set_amp: CALL SPI_init ;ensure known state of bus and s0 register
XOR s0, SPI_amp_cs ;select low on Amplifier chip select
OUTPUT s0, SPI_control_port
LOAD s1, 08 ;8-bits to transmit and receive
next_amp_SPI_bit: OUTPUT s2, SPI_output_port ;output data bit
XOR s0, SPI_sck ;clock High (bit0)
OUTPUT s0, SPI_control_port ;drive clock High
INPUT s3, SPI_input_port ;read input bit
TEST s3, SPI_amp_sdi ;detect state of received bit
SLA s2 ;shift new data into result and move to next transmit bit
XOR s0, SPI_sck ;clock Low (bit0)
OUTPUT s0, SPI_control_port ;drive clock Low
SUB s1, 01 ;count bits
JUMP NZ, next_amp_SPI_bit ;repeat until finished
XOR s0, SPI_amp_cs ;deselect the amplifier
OUTPUT s0, SPI_control_port
RETURN
;
;
;
;**************************************************************************************
;SPI communication routines for A/D Converter
;**************************************************************************************
;
;
;
;Sample A/D converter (LTC1407A-1) and return results.
;
;Note there is a latency of one read to obtain the value. Each read results in the
;the analogue inputs being sampled and converted but this value will only be transmitted
;during the next read and conversion cycle.
;
;The results are returned as follows.
; Channel 0 in registers [s9,s8]
; Channel 1 in registers [s7,s6]
;Where each is a 14-bit twos complement value sign extended to 16-bits.
;
;Each 14-bit value represents the analogue voltage in the range -1.25v to +1.25v
;relative to the reference voltage of 1.65v (3.3v/2). Hence the actual input voltage
;range is 0.4v to 2.9v. Since the input to the A/D is supplied via the programmable
;amplifier, the VINA and VINB inputs are inverted and may cover a smaller range if ;
;desired.
;
;Examples
; VINA = 0.65v with gain=-1 means input to A/D = 2.65v
; This is equivalent to +1.00v which is value (8192/1.25)*1 = 6553 (1999 hex)
;
; VINA = 2.65v with gain=-1 means input to A/D = 0.65v
; This is equivalent to -1.00v which is value (2048/1.25)*-1 = -6553 (E667 hex)
;
;
;Although the A/D converter claims to be an SPI device, it really
;does not conform to the normal specification of the 4-wire interface.
;
;Firstly the CONV signal is only pulsed High and does not behave like
;a normal active low select signal. Secondly, the communication is
;34 bits which does not fit a byte boundary, and thirdly, the data output
;to its SDO pin changes as a result of rising edges of SCK clock which
;is not the same as the falling edge used by other devices.
;
adc_read: CALL SPI_init ;ensure known state of bus and s0 register
XOR s0, SPI_adc_conv ;Pulse AD-CONV High to take sample and start
OUTPUT s0, SPI_control_port ; conversion and transmission of data.
XOR s0, SPI_adc_conv ;AD-CONV Low
OUTPUT s0, SPI_control_port
LOAD s1, 22 ;34 clocks to read all data
next_adc_bit: XOR s0, SPI_sck ;clock High (bit0)
OUTPUT s0, SPI_control_port ;drive clock High
XOR s0, SPI_sck ;clock Low (bit0)
OUTPUT s0, SPI_control_port ;drive clock Low
INPUT s3, SPI_input_port ;read input bit
TEST s3, SPI_sdi ;detect state of received bit
SLA s6 ;shift new data into result registers
SLA s7
SLA s8
SLA s9
SUB s1, 01 ;count bits
JUMP NZ, next_adc_bit ;repeat until finished
SRX s9 ;sign extend 14-bit result in [s9,s8]
SRA s8
SRX s9
SRA s8
SRX s7 ;sign extend 14-bit result in [s7,s6]
SRA s6
SRX s7
SRA s6
RETURN
;
;
;**************************************************************************************
;LCD text messages
;**************************************************************************************
;
;
;Display 'PicoBlaze' on LCD at current cursor position
;
;
disp_PicoBlaze: LOAD s5, character_P
CALL LCD_write_data
LOAD s5, character_i
CALL LCD_write_data
LOAD s5, character_c
CALL LCD_write_data
LOAD s5, character_o
CALL LCD_write_data
LOAD s5, character_B
CALL LCD_write_data
LOAD s5, character_l
CALL LCD_write_data
LOAD s5, character_a
CALL LCD_write_data
LOAD s5, character_z
CALL LCD_write_data
LOAD s5, character_e
CALL LCD_write_data
RETURN
;
;
;Display 'ADC Control' on LCD at current cursor position
;
;
disp_ADC_Control: LOAD s5, character_A
CALL LCD_write_data
LOAD s5, character_D
CALL LCD_write_data
LOAD s5, character_C
CALL LCD_write_data
LOAD s5, character_space
CALL LCD_write_data
LOAD s5, character_C
CALL LCD_write_data
LOAD s5, character_o
CALL LCD_write_data
LOAD s5, character_n
CALL LCD_write_data
LOAD s5, character_t
CALL LCD_write_data
LOAD s5, character_r
CALL LCD_write_data
LOAD s5, character_o
CALL LCD_write_data
LOAD s5, character_l
CALL LCD_write_data
RETURN
;
;
;Display 'VA=' on LCD at current cursor position
;
;
disp_VA: LOAD s5, character_V
CALL LCD_write_data
LOAD s5, character_A
CALL LCD_write_data
LOAD s5, character_equals
CALL LCD_write_data
RETURN
;
;
;Display 'A/D' on LCD at current cursor position
;
;
disp_AD: LOAD s5, character_A
CALL LCD_write_data
LOAD s5, character_divide
CALL LCD_write_data
LOAD s5, character_D
CALL LCD_write_data
LOAD s5, character_equals
CALL LCD_write_data
RETURN
;
;
;
;**************************************************************************************
;Value to ASCII Conversions and 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 s2.
; The lower nibble will be represented by an ASCII character returned in register s1.
;
;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, s1 and s2.
;
hex_byte_to_ASCII: LOAD s1, s0 ;remember value supplied
SR0 s0 ;isolate upper nibble
SR0 s0
SR0 s0
SR0 s0
CALL hex_to_ASCII ;convert
LOAD s2, s0 ;upper nibble value in s2
LOAD s0, s1 ;restore complete value
AND s0, 0F ;isolate lower nibble
CALL hex_to_ASCII ;convert
LOAD s1, s0 ;lower nibble value in s1
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 display at the current cursor position.
;
;Registers used s0, s1, s2, s4, s5, s6
;
disp_hex_byte: CALL hex_byte_to_ASCII
LOAD s6, s1 ;remember lower hex character
LOAD s5, s2 ;display upper hex character
CALL LCD_write_data
LOAD s5, s6 ;display lower hex character
CALL LCD_write_data
RETURN
;
;
;**************************************************************************************
;Software delay routines
;**************************************************************************************
;
;
;
;Delay of 1us.
;
;Constant value defines reflects the clock applied to KCPSM3. Every instruction
;executes in 2 clock cycles making the calculation highly predictable. The '6' in
;the following equation even allows for 'CALL delay_1us' instruction in the initiating code.
;
; delay_1us_constant = (clock_rate - 6)/4 Where 'clock_rate' is in MHz
;
;Registers used s0
;
delay_1us: LOAD s0, delay_1us_constant
wait_1us: SUB s0, 01
JUMP NZ, wait_1us
RETURN
;
;Delay of 40us.
;
;Registers used s0, s1
;
delay_40us: LOAD s1, 28 ;40 x 1us = 40us
wait_40us: CALL delay_1us
SUB s1, 01
JUMP NZ, wait_40us
RETURN
;
;
;Delay of 1ms.
;
;Registers used s0, s1, s2
;
delay_1ms: LOAD s2, 19 ;25 x 40us = 1ms
wait_1ms: CALL delay_40us
SUB s2, 01
JUMP NZ, wait_1ms
RETURN
;
;Delay of 20ms.
;
;Delay of 20ms used during initialisation.
;
;Registers used s0, s1, s2, s3
;
delay_20ms: LOAD s3, 14 ;20 x 1ms = 20ms
wait_20ms: CALL delay_1ms
SUB s3, 01
JUMP NZ, wait_20ms
RETURN
;
;Delay of approximately 1 second.
;
;Registers used s0, s1, s2, s3, s4
;
delay_1s: LOAD s4, 32 ;50 x 20ms = 1000ms
wait_1s: CALL delay_20ms
SUB s4, 01
JUMP NZ, wait_1s
RETURN
;
;
;
;**************************************************************************************
;LCD Character Module Routines
;**************************************************************************************
;
;LCD module is a 16 character by 2 line display but all displays are very similar
;The 4-wire data interface will be used (DB4 to DB7).
;
;The LCD modules are relatively slow and software delay loops are used to slow down
;KCPSM3 adequately for the LCD to communicate. The delay routines are provided in
;a different section (see above in this case).
;
;
;Pulse LCD enable signal 'E' high for greater than 230ns (1us is used).
;
;Register s4 should define the current state of the LCD output port.
;
;Registers used s0, s4
;
LCD_pulse_E: XOR s4, LCD_E ;E=1
OUTPUT s4, LCD_output_port
CALL delay_1us
XOR s4, LCD_E ;E=0
OUTPUT s4, LCD_output_port
RETURN
;
;Write 4-bit instruction to LCD display.
;
;The 4-bit instruction should be provided in the upper 4-bits of register s4.
;Note that this routine does not release the master enable but as it is only
;used during initialisation and as part of the 8-bit instruction write it
;should be acceptable.
;
;Registers used s4
;
LCD_write_inst4: AND s4, F8 ;Enable=1 RS=0 Instruction, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
CALL LCD_pulse_E
RETURN
;
;
;Write 8-bit instruction to LCD display.
;
;The 8-bit instruction should be provided in register s5.
;Instructions are written using the following sequence
; Upper nibble
; wait >1us
; Lower nibble
; wait >40us
;
;Registers used s0, s1, s4, s5
;
LCD_write_inst8: LOAD s4, s5
AND s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OR s4, LCD_drive ;Enable=1
CALL LCD_write_inst4 ;write upper nibble
CALL delay_1us ;wait >1us
LOAD s4, s5 ;select lower nibble with
SL1 s4 ;Enable=1
SL0 s4 ;RS=0 Instruction
SL0 s4 ;RW=0 Write
SL0 s4 ;E=0
CALL LCD_write_inst4 ;write lower nibble
CALL delay_40us ;wait >40us
LOAD s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;Release master enable
RETURN
;
;
;
;Write 8-bit data to LCD display.
;
;The 8-bit data should be provided in register s5.
;Data bytes are written using the following sequence
; Upper nibble
; wait >1us
; Lower nibble
; wait >40us
;
;Registers used s0, s1, s4, s5
;
LCD_write_data: LOAD s4, s5
AND s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OR s4, 0C ;Enable=1 RS=1 Data, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
CALL LCD_pulse_E ;write upper nibble
CALL delay_1us ;wait >1us
LOAD s4, s5 ;select lower nibble with
SL1 s4 ;Enable=1
SL1 s4 ;RS=1 Data
SL0 s4 ;RW=0 Write
SL0 s4 ;E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
CALL LCD_pulse_E ;write lower nibble
CALL delay_40us ;wait >40us
LOAD s4, F0 ;Enable=0 RS=0 Instruction, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;Release master enable
RETURN
;
;
;
;
;Read 8-bit data from LCD display.
;
;The 8-bit data will be read from the current LCD memory address
;and will be returned in register s5.
;It is advisable to set the LCD address (cursor position) before
;using the data read for the first time otherwise the display may
;generate invalid data on the first read.
;
;Data bytes are read using the following sequence
; Upper nibble
; wait >1us
; Lower nibble
; wait >40us
;
;Registers used s0, s1, s4, s5
;
LCD_read_data8: LOAD s4, 0E ;Enable=1 RS=1 Data, RW=1 Read, E=0
OUTPUT s4, LCD_output_port ;set up RS and RW >40ns before enable pulse
XOR s4, LCD_E ;E=1
OUTPUT s4, LCD_output_port
CALL delay_1us ;wait >260ns to access data
INPUT s5, LCD_input_port ;read upper nibble
XOR s4, LCD_E ;E=0
OUTPUT s4, LCD_output_port
CALL delay_1us ;wait >1us
XOR s4, LCD_E ;E=1
OUTPUT s4, LCD_output_port
CALL delay_1us ;wait >260ns to access data
INPUT s0, LCD_input_port ;read lower nibble
XOR s4, LCD_E ;E=0
OUTPUT s4, LCD_output_port
AND s5, F0 ;merge upper and lower nibbles
SR0 s0
SR0 s0
SR0 s0
SR0 s0
OR s5, s0
LOAD s4, 04 ;Enable=0 RS=1 Data, RW=0 Write, E=0
OUTPUT s4, LCD_output_port ;Stop reading 5V device and release master enable
CALL delay_40us ;wait >40us
RETURN
;
;
;Reset and initialise display to communicate using 4-bit data mode
;Includes routine to clear the display.
;
;Requires the 4-bit instructions 3,3,3,2 to be sent with suitable delays
;following by the 8-bit instructions to set up the display.
;
; 28 = '001' Function set, '0' 4-bit mode, '1' 2-line, '0' 5x7 dot matrix, 'xx'
; 06 = '000001' Entry mode, '1' increment, '0' no display shift
; 0C = '00001' Display control, '1' display on, '0' cursor off, '0' cursor blink off
; 01 = '00000001' Display clear
;
;Registers used s0, s1, s2, s3, s4
;
LCD_reset: CALL delay_20ms ;wait more that 15ms for display to be ready
LOAD s4, 30
CALL LCD_write_inst4 ;send '3'
CALL delay_20ms ;wait >4.1ms
CALL LCD_write_inst4 ;send '3'
CALL delay_1ms ;wait >100us
CALL LCD_write_inst4 ;send '3'
CALL delay_40us ;wait >40us
LOAD s4, 20
CALL LCD_write_inst4 ;send '2'
CALL delay_40us ;wait >40us
LOAD s5, 28 ;Function set
CALL LCD_write_inst8
LOAD s5, 06 ;Entry mode
CALL LCD_write_inst8
LOAD s5, 0C ;Display control
CALL LCD_write_inst8
LCD_clear: LOAD s5, 01 ;Display clear
CALL LCD_write_inst8
CALL delay_1ms ;wait >1.64ms for display to clear
CALL delay_1ms
RETURN
;
;Position the cursor ready for characters to be written.
;The display is formed of 2 lines of 16 characters and each
;position has a corresponding address as indicated below.
;
; Character position
; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
;
; Line 1 - 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
; Line 2 - C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
;
;This routine will set the cursor position using the value provided
;in register s5. The upper nibble will define the line and the lower
;nibble the character position on the line.
; Example s5 = 2B will position the cursor on line 2 position 11
;
;Registers used s0, s1, s2, s3, s4
;
LCD_cursor: TEST s5, 10 ;test for line 1
JUMP Z, set_line2
AND s5, 0F ;make address in range 80 to 8F for line 1
OR s5, 80
CALL LCD_write_inst8 ;instruction write to set cursor
RETURN
set_line2: AND s5, 0F ;make address in range C0 to CF for line 2
OR s5, C0
CALL LCD_write_inst8 ;instruction write to set cursor
RETURN
;
;
;**************************************************************************************
;Interrupt Service Routine (ISR)
;**************************************************************************************
;
;Interrupts occur at 1 second intervals.
;
;Each interrupt is used to take analogue samples and store them in scratch pad memory.
;The interrupt clears a 'flag' in register sF so that the main program can advance.
;
ISR: CALL adc_read ;read A/D Converter
STORE s8, ADC0_lsb ;store ADC Channel 0
STORE s9, ADC0_msb
STORE s6, ADC1_lsb ;store ADC Channel 1
STORE s7, ADC1_msb
;
LOAD sF, 00 ;clear flag
RETURNI ENABLE
;
;
;**************************************************************************************
;Interrupt Vector
;**************************************************************************************
;
ADDRESS 3FF
JUMP ISR
;
;