; .\common.SRC generated from: .\common.c ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ; The following lines are included to check the compilability of this file % $NOMOD51 ; disable predefined 8051 registers $INCLUDE (At89c55.INC) ; include CPU definition file for Atmel 89C55 ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ; ;!!!!%%% Put the wait for good sentence back in the serial port when test is done! ; ; Bit Registers ; NAME COMMON ?PR?_asm_set_servo?COMMON SEGMENT CODE ?DT?_asm_set_servo?COMMON SEGMENT DATA OVERLAYABLE ?PR?asm_get_waypoint?COMMON SEGMENT CODE ?PR?asm_send_waypoint?COMMON SEGMENT CODE ?PR?asm_init?COMMON SEGMENT CODE ?PR?asm_lcd_print?COMMON SEGMENT CODE ?DT?asm_lcd_print?COMMON SEGMENT DATA OVERLAYABLE PUBLIC asm_lcd_print PUBLIC asm_init PUBLIC asm_send_waypoint PUBLIC asm_get_waypoint PUBLIC _asm_set_servo PUBLIC d_lon PUBLIC d_lat PUBLIC message RSEG ?DT?_asm_set_servo?COMMON ?_asm_set_servo?BYTE: command_byte: DS 1 ; (Servo) Holds the command byte [CCCCCAAA] servo_data: DS 1 ; (Servo) Holds data for this command bit_count: DS 1 ; (Servo) Counter for number of bits being sent buffer_h: DS 1 ; (Servo) Buffer high byte (initially contains servo data) buffer_l: DS 1 ; (Servo) Buffer low byte (initially contains command & address) RSEG ?DT?asm_lcd_print?COMMON ?asm_lcd_print?BYTE: data_ptr?643: DS 1 ;------------------------------------------------------------------------------ ; DATA SEGMENT--Reserves space in DATA RAM (lower 128 bytes)-- ;------------------------------------------------------------------------------ VARS SEGMENT DATA ; segment for DATA RAM. RSEG VARS ; switch to this data segment temp_sum_h: DS 1 ; (several) Stores the temporary sums for calculations in various routines temp_sum_l: DS 1 M SEGMENT DATA RSEG M message: DS 16 ; String for lcd_display ;------------------------------------------------------------------------------ ; IDATA SEGMENT--Reserves space in INDIRECT DATA RAM (upper 128 bytes)-- ;------------------------------------------------------------------------------ ISEG AT 0x80 w0: DS 1 w1: DS 1 w2: DS 1 w3: DS 1 DSEG AT 0x7C d_lon: DS 2 ; Desired longitude DSEG AT 0x7E d_lat: DS 2 ; Desired latitude ;------------------------------------------------------------------------------ ; BIT SEGMENT--Reserves space in BIT RAM ;------------------------------------------------------------------------------ bit_flags SEGMENT BIT ; segment for BIT RAM. RSEG bit_flags ; switch to this bit segment ipc_data: DBIT 1 ; (ipc) This is the data bit we've just received or sent ;------------------------------------------------------------------------------ ; CONSTANTS (typeless) ;------------------------------------------------------------------------------ YGM EQU P0_5 ; (Servo) YGM (output) "You've Got Mail" line going to Motorola chip DAT EQU P0_4 ; (Servo) DAT (output) Data bit going to the Motorola chip ACK EQU P0_3 ; (Servo) ACK (input) from the Motorola chip rs EQU P0_1 ; (LCD) Port P0.1 RS line (1 = data, 0 = instructions) e_line EQU P0_0 ; (LCD) Port P0.0 Enable line (1 = enable) lcd_data EQU P1 ; (LCD) Port P1 (P1.4 - P1.7) are LCD data lines IPC_DAT EQU P2_0 ; Interprocessor comm line IPC_ACK EQU P2_1 ; Interprocessor comm line IPC_CLK EQU P2_2 ; Interprocessor comm line compass_port EQU P2 ; Port used for digital compass (P2.7 = N, P2.6 = E, P2.5 = S, P2.4 = W) ;====================================================================================================== ; /* SET_SERVO: ; cccccaaa: Commands: 0=set position ; Data: 0 = Hard Left (CCW) 255 = Hard Right (CW) ; Address: Address of servo for this call (0-7)*/ ; ; void asm_set_servo(unsigned char c_a, unsigned char servo_data) { RSEG ?PR?_asm_set_servo?COMMON _asm_set_servo: USING 0 MOV command_byte,R7 ; Move command/Adr byte into RAM MOV servo_data,R5 ; Move Data byte into RAM CALL send_package ; Send the package to the servo controller ?C0001: RET ;(servo) send_package subroutine ;------------------------------------------------- ; Used to send a 16 bit package to the Motorola 68HC705 servo controller ; Assumes the data to be sent is already in command_byte and servo_data ; ; Sends 16 bits, high bit of buffer_h first (data) through low bit of buffer_l last (command / address). ; send_package: MOV bit_count,#0x11 ; Make sure bit counter starts with 17 (ie 16 bits to transfer.) MOV buffer_h,servo_data ; Copy the stuff that's going out (so that the last servo data, MOV buffer_l,command_byte ; address & command sent are always available if needed) send_loop: MOV C,ACK ; Get the ACK bit JNC ckYGM_1 ; If ACK is clear, go check YGM (we'll either wait, or send a new bit) ; To get here, ACK must be set. ; If YGM is clear, we need to wait for servo controller chip to clear its ACK. ; If YGM is set, then ball's in our court. We need to clear YGM. ; Either way, we can clear YGM and go back to top. CLR YGM ; Clear YGM. SJMP send_loop ; So, ACK set & YGM clear. Now we're just waiting for the ACK line to go low. ckYGM_1: MOV C,YGM ; So, ACK is clear. Get YGM bit JNC send_bit ; If YGM clear (& ACK is clear), then OK to send next bit. ; ACK clear & YGM is set. Means we're waiting for servo controller to ACK our data. SJMP send_loop ; Don't need to do anything. Just wait for servo controller to ACK our last bit. send_bit: DJNZ bit_count,send_bit_1 ; Check the number of bits still to send. Is it zero? RET ; If so, we're done!! send_bit_1: MOV A,buffer_l ; Still more bits to send. ACK clear, YGM clear. We're OK to send next bit. CLR C ; Clear out the carry flag RLC A ; Move 0 into lsb of buffer_l, and msb of buffer_l into carry MOV buffer_l,A ; Save new buffer_l value MOV A,buffer_h ; Get buffer_h RLC A ; Move msb of buffer_l (was in carry) into lsb of buffer_h, and msb of buffer_h into carry MOV DAT,C ; Put our bit on the DAT line. MOV buffer_h,A ; Save modified copy of buffer_h SETB YGM ; Set the "You've got mail" line. SJMP send_loop ;(servo) long_delay routine. ;-------------------------- ; To use: Load variable R1 with some number (FF = approx 4 seconds) ; then call this subroutine. Routine will return after specified amount of time. ; Number of cycles for each instruction shown in comments long_delay: MOV R0,#0xFF ;1 = 375ns outer_lp: MOV R2,#80 ;1 = 375ns inner_lp: DJNZ R2,inner_lp ;2 = 750ns (do this 80 times, total of 60us) DJNZ R0,outer_lp ;2 = 750ns (do this 256 times, total of 15.648ms) DJNZ R1,long_delay ;2 = 750ns (do this R1 times) RET ;2 = 750ns ; END OF _asm_set_servo ;====================================================================================================== ; !!! now void / void !!!! ; /* GET_WAYPOINT : blocks for navigator to send new waypoint. */ ; void asm_get_waypoint(void) { RSEG ?PR?asm_get_waypoint?COMMON asm_get_waypoint: MOV R2,#0x20 ; Counter (load w/32) (Matt's "temp") ipc_wait: JNB IPC_ACK,ipc_wait ; Make ipc_data equal what is actually on the data line CLR ipc_data JNB IPC_DAT,data_was_clear SETB ipc_data data_was_clear: SETB IPC_CLK ipc_wait2: JB IPC_ACK,ipc_wait2 CLR IPC_CLK ; Bit received, shift it into register... CLR C JNB ipc_data,ipc_clear SETB C ipc_clear: MOV R0,#w0 ; Get pointer d_lon MOV A,@R0 RLC A MOV @R0,A INC R0 MOV A,@R0 RLC A MOV @R0,A INC R0 MOV A,@R0 RLC A MOV @R0,A INC R0 MOV A,@R0 RLC A MOV @R0,A INC R0 DEC R2 ;Decrement Matt's temp CJNE R2,#0x00,ipc_wait ; fix the stupid C / ASM bit differences MOV R0, #w0 MOV d_lon+1, @R0 INC R0 MOV d_lon, @R0 INC R0 MOV d_lat+1, @R0 INC R0 MOV d_lat, @R0 ?C0003: RET ; END OF asm_get_waypoint ;====================================================================================================== ; !!! now void / void !!!! ; /* SEND_WAYPOINT : Sends current location to navigator ; unsigned char the_data <- A pointer to the pos struct*/ ; void asm_send_waypoint(unsigned char the_data) { RSEG ?PR?asm_send_waypoint?COMMON asm_send_waypoint: USING 0 MOV R0, #w0 MOV @R0, d_lon+1 INC R0 MOV @R0, d_lon INC R0 MOV @R0, d_lat+1 INC R0 MOV @R0, d_lat MOV R2,#0x20 ; Counter (load w/32) (Matt's "temp") ipc_wait12: JB IPC_ACK,ipc_wait12 snd_nxt_bit: DEC R2 MOV R0,#d_lon ; Get pointer d_lon CLR C MOV A,@R0 RLC A MOV @R0,A INC R0 MOV A,@R0 RLC A MOV @R0,A INC R0 MOV A,@R0 RLC A MOV @R0,A INC R0 MOV A,@R0 RLC A MOV @R0,A INC R0 ; At this point, C has MSB of c_lat JNC or_a_zero MOV R0,#d_lon INC @R0 ; Set the LSB, since we know it was a zero or_a_zero: JB IPC_ACK,or_a_zero ;Make sure ACK is clear before proceeding JC data_line_set CLR IPC_DAT data_line_set: ; Make ipc_data equal what is actually on the data line SETB IPC_CLK ipc_wait13: JNB IPC_ACK,ipc_wait13 ;Wait for ACK SETB IPC_DAT CLR IPC_CLK CJNE R2,#0x00,ipc_wait12 ?C0004: RET ; END OF _asm_send_waypoint ;====================================================================================================== ; /* Initialize everything EXCEPT THE STACK! */ ; void asm_init(void) { ; ; NOTE: This routine hangs until at least one good GPS sentence is received! RSEG ?PR?asm_init?COMMON asm_init: ; Make sure IPC_DAT pin is an input SETB IPC_DAT CLR IPC_CLK ; Initialization for Servo routine CLR YGM ; Clear the output lines CLR DAT MOV command_byte,#0x00 MOV servo_data,#0x00 MOV bit_count,#0x11 ; Must be 1 more than the number of bits to send (ie decimal 17) MOV R1,#0x80 ; Wait about 2 seconds for Motorola chip to stabilize CALL long_delay ; End of Initialization for Servo routine ; Initialization for LCD routine: CLR e_line CLR rs CLR P1_4 CLR P1_5 CLR P1_6 CLR P1_7 ; Initialize the LCD Display ; Wait for 15ms MOV R1,#150 ; Set delay time CALL var_delay ; Send init command MOV lcd_data,#0x30 SETB e_line NOP ; Min cycle time is 500ns CLR e_line ; Wait for 4.1ms MOV R1,#41 CALL var_delay ; Send init command MOV lcd_data,#0x30 SETB e_line NOP ; Min cycle time is 500ns CLR e_line ; Wait for 100us MOV R1,1 CALL var_delay ; Send init command MOV A,#0x30 CALL lcd_write ; Send function set command ; 4-bit bus, 2 rows, 5x7 dots MOV A,#0x20 CALL lcd_write MOV A,#0x20 CALL lcd_write MOV A,#0x80 CALL lcd_write ; Send display control command ; Display on, cursor off, no blinking MOV A,#0x00 CALL lcd_write MOV A,#0xC0 CALL lcd_write ; Send clear display command ; Clear display, Cursor address = 0 MOV A,#0x00 CALL lcd_write MOV R1,#16 CALL var_delay MOV A,#0x10 CALL lcd_write MOV R1,#16 CALL var_delay ; Send entry mode command ; Increment, no display shift MOV A,0x00 CALL lcd_write MOV A,#0x60 CALL lcd_write ; End of initialization for LCD Display Routine ?C0005: RET ; END OF asm_init ;====================================================================================================== ; // LCD_PRINT ; // Passes a pointer (in R7) to a null-terminated string the C code would like ASM to print ; void asm_lcd_print(unsigned char data_ptr) { RSEG ?PR?asm_lcd_print?COMMON asm_lcd_print: ;MOV data_ptr?643,R7 ;For some reason, it doesn't like: MOV R1,R7 MOV R1,#message ; Assumes pointer to null-terminated string to display is in R1 CALL show_em ; Update the LCD display ?C0007: RET ;-------------------------------------------------- ; LCD Subroutines ;-------------------------------------------------- ;var_delay routine. ;-------------------------- ; To use: Load variable R1 with number of tenths of milliseconds you want to delay, ; then call this subroutine. Routine will return after specified amount of time. ; Min. delay time (R1 = 1) is 100us, max. delay time (R1 = 0) is 25.6ms. ; Assumes a 32MHz crystal, for an internal cycle time of 375ns (internal divide-by-12) ; Formula is delay = R1 * 100us (if R1 = 1, get a 100us delay, if R1 = 10, get a 1ms delay) ; The timing is close, but not exact. See following chart: ; R1 = 1, Delay Error = +0.875us (100.875us) ; R1 = 10, Delay Error = +1.25us (1001.25us) ; R1 = 100, Delay Error = +13.3us (10.0133ms) ; R1 = 0, Delay Error = +32.75us (25.63275ms) ; Number of cycles for each instruction shown in comments var_delay: MOV R0,#132 ;1 = 375ns L1: DJNZ R0,L1 ;2 = 750ns (do this 132 times) DJNZ R1,var_delay ;2 = 750ns (do this R1 times) RET ;2 = 750ns ;lcd_write routine - Sends data to the LCD lcd_write: MOV lcd_data,A ; Upper 4 bits connect to LCD SETB e_line ; Toggle enable line NOP ; 500ns minimum cycle time CLR e_line MOV R0,#53 ;Simple 40us delay loop for LCD L2: DJNZ R0,L2 ; RET ;lcd_addr - Routine to send LCD address for start of characters lcd_addr: CLR rs ; Put LCD in command mode MOV lcd_data,A SETB e_line ; Clock in the data NOP ; 500ns minimum cycle time CLR e_line MOV R0,#53 ;Simple 40us delay loop for LCD L4: DJNZ R0,L4 ; SETB rs ; Put LCD in data mode RET ; Assumes that R1 points to the null terminated string to display show_em: MOV A,#0x80 ; Addr = 80H MSB (remember, we send 4 bits at a time!) CALL lcd_addr ; (See Optrex spec sheet for address format) MOV A,#0x00 ; Addr = 80H LSB CALL lcd_addr L3: MOV A,@R1 ; Get character (for high nibble) JZ outmsg ; Are we done displaying message? CALL lcd_write ANL A,#0x0F ; Mask high nibble, so rotate works like shift RL A RL A RL A RL A CALL lcd_write INC R1 ; Increment index to point to next character SJMP L3 outmsg: RET ; END OF asm_lcd_print ;------------------------------------------------------------------------------ ; The END directive is ALWAYS required. ;------------------------------------------------------------------------------ END ; End of everything