Home  Products Prices Directory Order Contact New Books Files Links Other
DonTronics Home Page AVR AT90S8515 getting started code.
Bottom of Page
 
DonTronics Pages Duplicated:
DonTronics goes to great lengths to provide new and interesting material for visitors. It has been found in recent months that other Webmasters are duplicating material that has already been presented here. This may be with, or without the permission of the original Author. If you see a copy of the following files or pages, you will know DonTronics was responsible for the initial  release, and the others are simply copies. It would be nice if these people did their own ground work, and put in the time to get new material, and not benefit from the time and effort that DonTronics has already invested to make this a better site.
AVR AT90S8515 getting started code. by David VanHorn Original Copy 4-Sep-98
PIC Source Book/Disk by Scott Edwards Electronics. Free public release. Original Copy 3-Apr-98


By David VanHorn  dvanhorn@cedar.net
STARTUP  ZIP  Download the full files listed in ZIP format     
8515DEF  INC  (Included in ZIP file)  
BUGS     TXT         
EQUATES  INC  (Included in ZIP file)         
INIT     ASM        
ISR      ASM       
MAIN     ASM      
MAIN     HEX  (Included in ZIP file)         
MEMORY   ASM         
RANDOM   ASM         
SERIAL   ASM         
SERVO    ASM        
STEPPER  ASM         
TABLES   ASM         
WATCHDOG ASM
This application is designed to help you get started painlessly with the AVR8515 on the Atmel Development board. It's such a nice board, with the I/O all open for you, that I use it to drive every project I've done with the chip. I just put matching headers on my project board, and use 10 pin jumpers to connect the dev board to my project. Since the dev board has a programmer built in, it makes for very fast prototyping and debugging.

As it sits, this application could be used to control a small robot, or radio-controlled device. It initializes the chip, and provides code for reading the switches and lighting the LEDs. It handles the on-chip watchdog timer, and it is trivially easy to add an external watchdog. It includes a 19 bit PseudoNoise generator, useful for encryption, or making "coin toss" decisions. It provides buffered, interrupt-driven serial communications on the uart, just point at the transmit string and call the routine.

It provides eight R/C servo output channels, each programmable from 1-2mS which corresponds to full travel in each direction. Also, the frame rate is programmable from 0-255mS, with 20 being typical. Servo outputs can also be disabled under program control.

It provides a stepper motor output for a unipolar stepper motor (6 wire type). This output can be selected as full or half-step, forward, reverse or stop, and 0-255mS per step.

(You will have to provide a proper electrical interface to these devices, I was only concerned with the software aspect. The AVR will not drive any stepper motor directly, and I would reccomend at least a buffer or 1k resistor in the servo outputs to protect the chip)

All of this activity is interrupt driven, leaving a large amount of CPU time available for other tasks. Each interrupt vector is handled. In the case of unused vectors, they contain code to turn off that interrupt. If you decide to use an int that is currently unused, just replace my turnoff code with your int handler. I like having the turnoff code in place, in case an int mask gets munged or I goof in the main code.

In this application you have working example code for properly initializing the chip, setting the stack, using the watchdog timer, Timer 0, and timer 1, the baud rate generator and uart, and all the I/O ports as input or output. You also have working examples of the features availalable in the assembler, using equates, defines, and inline math to set variables dependent on other variables. If you've never written in assembler before, you can learn a lot from this code. If you haven't worked with the 8515 before, then this code will get you past some of the parts that are a little difficult to pick up from the manual. If you're doing projects regularly with the 8515, then this may give you a worthwhile "template" to begin a project with, having most of the system up and running.


BUGS     TXT 




   None known at this time. Serial comms needs some help, too many ways to get hung in there and not return to the system for a long while.. If the output buffer is full, for example.
INIT     ASM



    ;  ; File Name             :'INIT.asm" ; Title                 :8515 Machine initialization ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ; ; DESCRIPTION ; This module contains the machine initialization ; ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ;       0.02    98.08.05  dvh   Fixed bit inits for demo board. ;       0.03    98.08.06  dvh   Documented better :) ;       0,04    98.08.20  dvh   Added servo inits ;       0.05    98.08.20  dvh   Added FRAME_DELAY init for SERVO.ASM frame rate ;                               Changed Frame_Delay to SRAM rather than register ;       0.06    98.08.30  dvh   Added watchdog timer support ; ;*************************************************************************** ;* ;* Initialize the machine ;* ;*************************************************************************** ; ;INIT_Machine: ;Set Watchdog Inactive  Databook 5-43 ;Gotta handle this before it gets a chance to bite me. ;                ldi     TEMP,$1F        ;00011011                out     WDTCR,TEMP      ;         ldi     TEMP,$17        ;00010111                                 ;XXX1XXXX Watch dog turn off enable, must be 1 to wrt D3                                 ;XXXX0XXX WDE 0=disable 1=enable                                 ;XXXXX111 000 =   16mS WD prescaler bits                                 ;         001 =   32mS                                 ;         010 =   64mS                                 ;         011 =  128mS                                 ;         100 =  256mS                                 ;         101 =  512mS                                 ;         110 = 1024mS                                 ;         111 = 2048mS         out     WDTCR,TEMP      ;         ldi     TEMP,Dog_Chow   ;Stock up on dog food         sts     Dog_Food,Temp   ;Decremented by Timer 0 ISR ; ;Set the stack pointer to 025F, stack grows down.  ;Databook 5-24 ;Can't call any subroutines till we have a stack! ;         ldi     TEMP,0x5f       ;         out     SPL,TEMP        ;init Stack Pointer Low              ldi     temp,0x02       ;         out     SPL+1,TEMP      ;init Stack Pointer High ;This section allows ints which are apparently  ;caused by reset, to occur harmlessly. ;Databook 5-23, 5-29         ldi     TEMP,$40        ;                                 ;0XXXXXXX 1= enable int 1                                 ;X1XXXXXX 1= enable int 0         out     GIMSK,TEMP      ;Enable int 0,1                  ldi     TEMP,$02        ;                                 ;0XXXXXXX TC1 overflow int enable                                 ;X0XXXXXX TC1 OCA Match int enable                                 ;XX0XXXXX TC1 OCB Match int enable                                 ;XXX0XXXX Reserved =0                                 ;XXXX0XXX TC1 input capture int enable                                 ;XXXXX0XX Reserved =0                                 ;XXXXXX1X TC0 overflow int enable                                 ;XXXXXXX0 Reserved =0         out     TIMSK,TEMP      ;Enable all timer ints                  in      TEMP,SREG       ;(5-23)         ori     TEMP,$80        ;Global int enable         out     SREG,TEMP       ;         nop         nop         nop     ;Let the ints happen, so they         nop     ;clear out         nop         nop         nop     ;NOTE: I have not exhaustively researched the need          nop     ;for this EI-NOP-DI section, but it is working well.         nop         in      TEMP,SREG       ;         andi    TEMP,$7F        ;Turn off all ints (5-23)         out     SREG,TEMP       ;         ldi     TEMP,$00        ;Disable all timer ints (5-29)         out     TIMSK,TEMP      ;         ldi     TEMP,$00        ;Disable all ints. (5-29)                                 ;1XXXXXXX enable int 1                                 ;X1XXXXXX enable int 0         out     GIMSK,TEMP      ;         ;         ;At this point, we should have a clean machine, in that there         ;are no bogus pending ints from powerup. Later, when we enable         ;various ints for real, they will only trigger after an event          ;has occured, rather than because the int logic was scrambled         ;at powerup. ;******************************************************************* ; ;Set the I/O pins to their appropriate state. ;Mung as needed for your app, this is all set to the dev board ;Always a good idea to set unused pins to be outputs. ;         ;On the development board, port A is the open header         ldi     TEMP,$FF        ;OOOOOOOO         out     DDRA,TEMP       ;Set port A data direction (5-59)         ;On the development board, port B is the LEDS         ;Databook 5-61         ;LEDs are lit by outputting a low level         ldi     TEMP,$FF        ;OOOOOOOO         out     DDRB,TEMP       ;Set port B data direction (5-61)         ;On the development board, port C is the open header         ;Databook 5-66         ldi     TEMP,$FF        ;OOOOOOOO (All servo outputs)         out     DDRC,TEMP       ;Set port C data direction (5-56)         ;On the development board, port D is the buttons         ;Databook 5-68         ;Buttons pressed present a LOW to the AVR         ldi     TEMP,$00        ;IIIIIIII         out     DDRD,TEMP       ;Set port D data direction (5-68)         out     PORTD,TEMP      ;De-Activate the pullups (supplied on board)         ; ; ;******************************************************************* ; ;Set up the analog comparator to disabled (5-57) ;         ldi     TEMP,$90        ;10010000 Comparator Disabled                                 ;1XXXXXXX Comparator disable, 1=off                                 ;X0XXXXXX Reserved =0                                 ;XX0XXXXX Analog comp output                                 ;XXX1XXXX Int flag, writing 1 clears it                                 ;XXXX0XXX Int enable, 0=disable                                 ;XXXXX0XX Input capture enable 0=disable                                 ;XXXXXX00 Input mode select                                 ;      00 = int on output toggle                                 ;      01 = Reserved                                 ;      10 = int on falling edge                                  ;      11 = int on rising edge                                 ;NOTE disable comp ints before changing XXXXXX00         out     ACSR,TEMP       ; ; ;******************************************************************* ; ;Set up uart (5-54) ;         ldi     TEMP,$D8        ;11011000                                 ;1XXXXXXX RX complete int enable                                 ;X1XXXXXX TX complete int enable                                 ;XX0XXXXX DR Empty int enable (CONSTANT INT WHILE EMPTY!)                                 ;XXX1XXXX RX Enable                                 ;XXXX1XXX TX Enable                                 ;XXXXX0XX 9 bit chars 1=enable                                 ;XXXXXX0X RX DB8                                 ;XXXXXXX0 TX DB8         out     UCR,TEMP        ; ; ;******************************************************************* ; ;Set SPI inactive (5-49) ;         ldi     TEMP,$1B        ;00011011                                 ;0XXXXXXX SPI int enable 1=enable                                 ;X0XXXXXX SPI enable     1=enable                                 ;XX0XXXXX Data order     1=LSB first                                 ;XXX1XXXX Master/slave   1=master                                 ;XXXX1XXX Clock polarity 1=SCK high at idle                                 ;XXXXX0XX Clock phase    (see 5-48)                                 ;XXXXXX11 Clock rate select                                 ;      00 = Fcl/4                                 ;      01 = Fcl/16                                 ;      10 = Fcl/64                                 ;      11 = Fcl/128         out     SPCR,TEMP       ; ; ;******************************************************************* ; ;Set EEProm Inactive (5-44) ;         ldi     TEMP,$00        ;00000000                                 ;0XXXXXXX Reserved =0                                  ;X0XXXXXX Reserved =0                                  ;XX0XXXXX Reserved =0                                 ;XXX0XXXX Reserved =0                                 ;XXXX0XXX Reserved =0                                 ;XXXXX0XX Master Write enable                                 ;XXXXXX0X Write enable                                 ;XXXXXXX0 Read enable         out     EECR,TEMP       ; ; ;******************************************************************* ; ;Set up the MCU Control register (5-31) ;         ldi     TEMP,$02        ;0XXXXXXX External Sram enable if 1                                 ;X0XXXXXX External Sram wait state if 1                                 ;XX0XXXXX Sleep Enable if 1                                 ;XXX0XXXX Sleep Mode                                 ;XXXX00XX Low level activates INT1                                 ;XXXXXX10 Falling edge int0 ADC         out     MCUCR,TEMP      ;High edge enabled ; ;******************************************************************* ; ;Set timer zero running (5-34)         ;ldi    TEMP,T0_DIV1024 ;         ;ldi    TEMP,T0_DIV256  ;         ldi     TEMP,T0_DIV64   ;This plus reloading T0 to 255-(T0DIV) gives us 1ms         ;ldi    TEMP,T0_DIV8    ;         ;ldi    TEMP,T0_DIV1    ;         out     TCCR0,TEMP      ;Set the prescaler         ;Databook 5-35         ldi     TEMP,T0DIV      ;Define the reload value         out     TCNT0,TEMP      ;Put that in T0 ; ;******************************************************************* ; ;Set timer one for servo use (5-38) ;         ldi     TEMP,$00        ;Disable PWM modes         out     TCCR1A,TEMP     ;00XXXXXX Select output pin action after compare match                                 ;         00 = Disconnected from OC1A                                 ;         01 = Toggle OC1A                                 ;         10 = Clear OC1A                                 ;         11 = Set OC1A                                 ;XX00XXXX Select output action for pin B                                 ;         00 = Disconnected from OC1B                                 ;         01 = Toggle OC1B                                 ;         10 = Clear OC1B                                 ;         11 = Set OC1B                                 ;XXXX00XX Reserved                                 ;XXXXXX00 PWM Select bits                                 ;         00 = No PWM                                 ;         01 = 8 bit PWM                                 ;         10 = 9 bit PWM                                 ;         11 = 10 bit PWM                  ldi     TEMP,$00        ;0XXXXXXX Disable noise canceler         out     TCCR1B,TEMP     ;X0XXXXXX Input capture edge 0=fall 1=rise                                 ;XX00XXXX Reserved                                 ;XXXX0XXX 1=clear counter on compareA match                                 ;XXXXX000 Prescaler select                                 ;         000 = Stop                                 ;         001 = CK                                 ;         010 = CK/8                                 ;         011 = CK/64                                 ;         100 = CK/256                                 ;         101 = CK/1024                                 ;         110 = External pin T1 falling edge                                 ;         111 = External pin T1 rising edge ;******************************************************************* ; ;Go clean up the ram area. This isn't STRICTLY needed, but it makes ;debugging easier, since it loads the buffers with recognizable data. ;         cli         rcall   Ram_Init        ;Clean up the ram, and flag buffers ; ;******************************************************************* ; ;Start the timer ints, I use the "dumb" timer for system ints, and ;leaving the "smart" timer for servo control ;         ;Databook 5-29         ldi     TEMP,$02        ;Enable timer 0 ints         out     TIMSK,TEMP      ;         in      TEMP,TIMSK      ;         ori     TEMP,$80        ;Turn on timer 1 overflow int         out     TIMSK,TEMP      ;for servo control                  ;Databook 5-23         in      TEMP,SREG       ;         ori     TEMP,$80        ;Enable global ints (for real)         out     SREG,TEMP       ; ; ;******************************************************************* ; ;This section is not chip inits, just seeding the software PN generator ;         ldi     TEMP,$AA        ;Init the random number generator          mov     RAND1,TEMP      ;since a 00,00,00 state will not          mov     RAND2,TEMP      ;progress.         mov     RAND3,TEMP      ; ; ;******************************************************************* ; ;Initial states for RC servo control using timer 2 ;         rcall   Servo_Set               ;Set initial widths for the servos (SERVO.ASM)                                         ;When there's a real application running, this                                         ;might be redundant.         ldi     TEMP,$FF                ;Turn off the lights         out     PORTB,TEMP              ;         ldi     TEMP,$00                ;         out     PORTA,TEMP              ;Turn off the other outputs.         out     PORTC,TEMP              ;Turn off the servos         ldi     TEMP,$01                ;         sts     Servo_Control,TEMP      ;Set servo 0(1) active         ldi     TEMP,FRAME_RATE         ;         sts     FRAME_DELAY,TEMP        ;Set the frame rate delay         ldi     TEMP,$00                ;         out     PORTC,TEMP              ;Set servo outputs inactive ; ;******************************************************************* ; ;Initial states for watchdog timer ;         ldi     TEMP,Dog_Chow           ;Start a countdown for resetting the watchdog         sts     Dog_Food,TEMP           ; ; ;******************************************************************* ; ;Initial states for stepper motor control ;         ldi     TEMP,$00                ;         sts     Step_State,TEMP         ;Initial state for the stepper motor         ldi     TEMP,$01                ;         sts     Step_Dir,TEMP           ;Set forward as default direction.         ;ldi    TEMP,$00                ;         ;sts    Step_Dir,TEMP           ;Set reverse as default direction.         ;ldi    TEMP,$02                ;         ;sts    Step_Dir,TEMP           ;Set stopped as default direction.         ldi     TEMP,$01         sts     Step_Mode,TEMP          ;Set half step as default mode.         ;ldi    TEMP,$00         ;sts    Step_Mode,TEMP          ;Set full step as default mode.         ldi     TEMP,Step_Default       ;Default step rate                      ;         sts     Step_Rate,TEMP          ;Establish a step rate         sts     Step_Time,TEMP          ;and set it running         ;Start the watchdog, if desired (5-43)         ;Make sure that the timeout selected here is more than 1mS larger than          ;the value selected for Dog_Food in EQUATES.INC         ;Dog_Food = 255, timer= 256 seems to work.but beware.         ;The timer as implemented could be as much as 1mS off, since events         ;can begin just after a reload.         ;         ;ldi    TEMP,$0F                ;000XXXXX Reserved                                         ;XXX0XXXX Watchdog turnoff enable                                         ;XXXX1XXX Watchdog enable bit 1=enable                                         ;XXXXX000 (8) Watchdog timeout,  16mS                                         ;XXXXX001 (9) Watchdog timeout.  32mS                                         ;XXXXX010 (A) Watchdog timeout.  64mS                                         ;XXXXX011 (B) Watchdog timeout. 128mS                                         ;XXXXX100 (C) Watchdog timeout, 256mS                                         ;XXXXX101 (D) Watchdog timeout, 512mS                                         ;XXXXX110 (E) Watchdog timeout,1024mS                                         ;XXXXX111 (F) Watchdog timeout,2048mS         ;out    WDTCR,TEMP              ; ; ;******************************************************************* ; ;Serial comms initialization         rcall   Init_Buffers            ;Make the buffers empty         ;98.07.27 Added this to initialize the HS status before         ;enabling int driven comms. Note that we do not SEND xon          ;here, just establishing a known state.           ;This MUST be done prior to enabling int driven comms         ;         ldi     TEMP,$FF        ;Set serial input state to XON         sts     HS_FLAG,TEMP    ;         ldi     TEMP,$00        ;Set the inter-char timer to zero         sts     Char_Delay,TEMP ;         rcall   Set_Baud_9600           ;Or something else if you prefer (SERIAL.ASM)         in      TEMP,UCR                ;Enable TX and RX ints         ori     TEMP,$C0                ;         out     UCR,TEMP                ;         ;Say howdy!         ldi     ZL,low (Signon_Message*2) ;Point at a signon message (in TABLES.ASM)         ldi     ZH,high (Signon_Message*2) ;         rcall   String_Serout           ;Send it to the serial output buffer         rjmp    IDLE
ISR      ASM  






   ;  ; File Name             :'ISR.asm" ; Title                 :8515 Interrupt Handlers ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ; ; DESCRIPTION ; All interrupt handlers reside here ; ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ;       0.02    98.08.20  dvh   Added Timer 1 int for servo control, a little cleanup ;                               and better documentation elsewhere ;       0.03    98.08.21  dvh   Added support in TIM0_OVF for a FRAME_DELAY ;                               variable used in SERVO.ASM.  ;                               Converted Frame_Delay to SRAM from register ;       0.04    98.08.23  dvh   Regularized to XL,XH notation instead of R26.R27 etc ; ;*************************************************************************** ;Fixed jump vectors ; ;$000         rjmp    INIT_Machine    ;Restart vector, points to the beginning of the code.  ;$001         rjmp    EXT_INT0        ;IRQ 0 ;$002         rjmp    EXT_INT1        ;IRQ 1  ;$003            rjmp    TIM1_CAPT       ;Timer 1 capture  ;$004         rjmp    TIM1_COMPA      ;Timer 1 compare A ;$005         rjmp    TIM1_COMPB      ;Timer 1 compare B ;$006         rjmp    TIM1_OVF        ;Timer 1 overflow ;$007            rjmp    TIM0_OVF        ;Timer 0 overflow ;$008            rjmp    SPI_Handler     ;Serial transfer complete ;$009         rjmp    UART_RXC        ;Uart RX complete ;$00A         rjmp    UART_DRE        ;Uart data register empty ;$00B         rjmp    UART_TXC        ;Uart TX complete ;$00C            rjmp    ANA_COMP        ;Analog comparator ; ;************************************************************** ; ;All my ISRs use TTEMP and TTEMP2 to maximize speed. You could push ;TEMP, and pop it back..  ; ;Each int vector has, at minimum, code that turns off that int.  ;This is a "belts and suspenders" method that protects me from ;any ints I forgot to turn off, or accidentally enabled. ; ;If you decide to use one of the disabled ints, enable it in the ;code when appropriate, and replace these "turn-off" handlers ;with whatever it is you want done. ; ;Each ISR saves SREG to TTEMP2, which is dedicated to this purpose. ;I could have pushed and popped it, but push and pop take two cycles, ;and in and out only take one. If you get squeezed for registers, then ;you can always do the pushpop dance. It's an extra 500nS at 8MHz ; ;MAKE ABSOLUTELY SURE YOU DO NOT USE "TEMP" IN ANY ISR! (unless you push and pop it!) ; ; ;************************************************************** ; ;Unused at this point.  ; ; EXT_INT0:                in      TTEMP2,SREG     ;Saves the status register INT0_OFF:         in      TTEMP,GIMSK     ;If done, then turn off the int!         andi    TTEMP,$BF       ;         out     GIMSK,TTEMP     ; INT0_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti ; ;*************************************************************** ; ; EXT_INT1:         in      TTEMP2,SREG     ;Saves the status register INT1_OFF:         in      TTEMP,GIMSK     ;Get the int enables         andi    TTEMP,$7F       ;Mask off INT1         out     GIMSK,TTEMP     ;Send it         ldi     TTEMP,$80       ;         out     GIFR,TTEMP      ;Manual clear          INT1_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti                    ; ; ;*************************************************************** ; ; TIM1_CAPT:                      ;         in      TTEMP2,SREG     ;Saves the status register TIM1_OFF:         in      TTEMP,TIMSK     ;Shut off this int         andi    TTEMP,$F7       ;         out     TIMSK,TTEMP     ; TIM1_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti            ; ; ;*************************************************************** ; ; ; TIM1_COMPA:                     ;         in      TTEMP2,SREG     ;Saves the status register TC1A_OFF:         in      TTEMP,TIMSK     ;         andi    TTEMP,$BF       ;         out     TIMSK,TTEMP     ; TC1A_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti                    ; ;        ;*************************************************************** ; ; TIM1_COMPB:                      in      TTEMP2,SREG     ;Saves the status register TC1B_OFF:         in      TTEMP,TIMSK     ;         andi    TTEMP,$DF       ;         out     TIMSK,TTEMP     ; TC1B_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti                    ; ; ;*************************************************************** ; ;Timer 1 gives us servo output timings from 1mS to 2mS, and allows ;the CPU to go do other things, rather than sitting there spinning  ;NOPS. It's a 16 bit counter, so I start it with a base value of 1mS, ;plus the servo width control byte (SERVO_X) We then have 1-2mS pulses, ;with full 8 bit resolution (4uS) ; ;Each servo out is activated sequentially in the main code. The activation ;code loads a value into Timer1, and sets the prescaler to /8, and sets the ;desired servo bit high. This int occurs when the servo's time expires, and ;we just shut off the output, and stop the timer. (Fast eh? :) ; TIM1_OVF:         in      TTEMP2,SREG     ;Saves the status register         ;**************************************************         ;Support for the R/C servo driver in SERVO.ASM         ;**************************************************         ;No matter who was running, shut off all servo outputs         ldi     TTEMP,$00       ;         out     PORTC,TTEMP     ;         in      TTEMP,TCCR1B    ;Get the timer control register         andi    TTEMP,$F8       ;Set the prescaler to "off"         out     TCCR1B,TTEMP    ;Make it so.         ;This test could instead mask and look for a particular         ;bit to reduce the number of active servos.         lds     TTEMP,Servo_Control     ;         ;To make the servos run from 1 to 8, enable this         lsl     TTEMP                   ;Set next servo to run         brcc    Servo_TB                ;If no carry then done, else         ldi     TTEMP,$01               ;start over on servo 1(0)         ;To make the servos run from 8 to 1, enable this         ;lsr    TTEMP                   ;Set next servo to run         ;brcc   Servo_TB                ;If no carry then done, else         ;ldi    TTEMP,$80               ;start over on servo 1(0)         ;If there was a ROL without carry, then I wouldn't have needed         ;to test for carry and reload like that. can't use LSL Servo_TB:         sts     Servo_Control,TTEMP     ;Set the next active servo bit on. TIM1O_EXIT:         out     SREG,TTEMP2             ;restore the status register         reti                            ; ; ;*************************************************************** ; ;       Main 1mS Opsys tick ; TIM0_OVF:         in      TTEMP2,SREG     ;Saves the status register         ;**************************************************         ;Reload timer 0 now to preserve accuracy         ;**************************************************         ldi     TTEMP,T0DIV     ;Reload early, avoid the latency         out     TCNT0,TTEMP     ;         ;**************************************************         ;Support for the stepper motor handler         ;************************************************** T0_Step:lds     TTEMP,Step_Time ;Get delay timer from SRAM         and     TTEMP,TTEMP     ;Is it zero?         breq    T0_Dog          ;leave it alone if so         dec     TTEMP           ;Otherwise dec         sts     Step_Time,TTEMP ;and store         ;**************************************************         ;Support for the timed watchdog handler         ;************************************************** T0_Dog: lds     TTEMP,Dog_Food  ;Get delay timer from SRAM         and     TTEMP,TTEMP     ;Is it zero?         breq    T0_Char         ;leave it alone if so         dec     TTEMP           ;Otherwise dec         sts     Dog_Food,TTEMP  ;and store         ;**************************************************         ;Support for the timed charachter wait         ;************************************************** T0_Char:lds     TTEMP,Char_Time ;Get delay timer from SRAM         and     TTEMP,TTEMP     ;Is it zero?         breq    T0_RC           ;leave it alone if so         dec     TTEMP           ;Otherwise dec         sts     Char_Time,TTEMP ;and store         ;**************************************************         ;Support for the R/C servo driver in SERVO.ASM         ;************************************************** T0_RC:  lds     TTEMP,Frame_Delay;Get delay timer from SRAM         and     TTEMP,TTEMP     ;Is it zero?         breq    T0_SER          ;leave it alone if so         dec     TTEMP           ;Otherwise dec         sts     Frame_Delay,TTEMP;and store         ;**************************************************         ;Support for the inter-char delay feature in SERIAL.ASM         ;************************************************** T0_SER: lds     TTEMP,Char_Delay        ;         and     TTEMP,TTEMP             ;         breq    TIM0_EXIT               ;If already zero, nevermind         dec     TTEMP                   ;else dec, and if NOW zero         sts     Char_Delay,TTEMP        ;         brne    TIM0_EXIT               ;         ;re-enable UART DRE INT.         in      TTEMP,UCR               ;         ori     TTEMP,$20               ;         out     UCR,TTEMP               ;         ;**************************************************         ;All done, seeya!         ;************************************************** TIM0_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti                    ;Bye! ; ;*************************************************************** ; ;SPI is unused at this point ; SPI_Handler:                    ;         in      TTEMP2,SREG     ;Saves the status register SPI_OFF:         ldi     TTEMP,$3F       ;Disable int, disable SPI         out     SPCR,TTEMP      ; SPI_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti                    ; ; ;*************************************************************** ; ;Take a char and put it in the Serial input buffer.  ; UART_RXC:                       ;         in      TTEMP2,SREG     ;Saves the status register         push    TEMP            ;Have to use TEMP because Store_Serin uses temp         ;**************************************************         ;Loading incoming UART data into the serial input buffer         ;**************************************************         in      TEMP,UDR        ;Get the char         rcall   Store_Serin     ;Store it in the serial in buffer (USES TEMP) UART_RXC_Exit:         pop     TEMP            ;         out     sreg,TTEMP2     ;restore the status register         reti                    ; ; ;*************************************************************** ; ;Ready to transmit the next byte ; UART_DRE:                       ;         in      TTEMP2,SREG     ;Saves the status register         push    YL         push    YH         push    ZL         push    ZH         ldi     ZL,low(SERIAL_OUT_BUF)  ;         ldi     ZH,high(SERIAL_OUT_BUF);         lds     YL,(SERIAL_OUT_TAIL)    ;         lds     YH,(SERIAL_OUT_TAIL+1)  ;         push    TEMP                    ;         rcall   Space_Check             ;Anything to say?         pop     TEMP                    ;         brne    UART_DRE_TX             ;If non-zero, then let it continue         in      TTEMP,UCR               ;         andi    TTEMP,$DF               ;Turn off DRE, I'm DONE         out     UCR,TTEMP               ;         rjmp    UART_DRE_EXIT           ; UART_DRE_TX:         lds     TTEMP,(Serial_OUT_BUF+1);Load a byte to temp from SERIAL_OUT_BUF         ;         ; Time------------>         ;  S L X X X X X M S S         ;  T S X X X X X S P T          ;  A B X X X X X B C P         ;1 0 0 0 0 0 0 0 0 0 1  (Line state)         ;         ;Comment both of the following out for 8N1         andi    TTEMP,$7F               ;7 bit Space parity         ;ori    TTEMP,$80               ;7 bit Mark  parity         out     UDR,TTEMP               ;Load uart from TEMP         ldi     TTEMP,SER_DLY           ;Stupid inter-char delay         sts     Char_Delay,TTEMP                ;         ;disable Uart_DRE_int.         in      TTEMP,UCR               ;Get the UCR         andi    TTEMP,$DF               ;Shut off DRE ints till the T0OV int re-enables them         out     UCR,TTEMP               ;Output it to the UCR         ldi     ZL,low(SERIAL_OUT_BUF)  ;         ldi     ZH,high(SERIAL_OUT_BUF);         lds     YL,(SERIAL_OUT_TAIL)    ;         lds     YH,(SERIAL_OUT_TAIL+1)  ;         push    TEMP                    ;         rcall   Kill_Head               ;Kill the sent byte         pop     TEMP                    ;         sts     (SERIAL_OUT_TAIL),YL    ;         sts     (SERIAL_OUT_TAIL+1),YH  ; UART_DRE_EXIT:                  Pop     ZH              ;         pop     ZL              ;         pop     YH              ;         pop     YL              ;         out     SREG,TTEMP2     ;restore the status register         reti                    ; ; ;*************************************************************** ; ;Uart TXC is unused at this point ; UART_TXC:         in      TTEMP2,SREG     ;Saves the status register UTXC_OFF:         in      TTEMP,UCR       ;         andi    TTEMP,$BF       ;Disable the TXC int.         out     UCR,TTEMP       ; UTXC_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti                    ; ; ;*************************************************************** ; ;Analog comparator is unused at this point. ; ANA_COMP:         in      TTEMP2,SREG     ;Saves the status register ANAC_OFF:         ldi     TTEMP,$83       ;Shut off power to the comparator         out     ACSR,TTEMP      ;and the comparator ints ANAC_EXIT:         out     SREG,TTEMP2     ;restore the status register         reti ; ;*************************************************************** ;
MAIN     ASM  






;*************************************************************************** ;  ; File Name             :'MAIN.asm" ; Title                 : ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Support Snail         ;1104 E 13th St, Muncie IN 47302 ; Target MCU            :AT90S8515 ;  ;***************************************************************************; ;       D E S C R I P T I O N ; ; This is an application I put together to help people get started on the  ;ATMEL AVR 8515 processor. It's not complete by any means, I dont' think it's ;possible to use EVERY feature and mode.  Basically, I wanted to get you started, ;and help you avoid some sneaky traps that you can fall into.  ; ;Legal issues: I retain ownership of this software. I grant you the right to use ;the software to educate yourself on the intricacies of the 8515 processor, and  ;to use it for personal, non-commercial use. Anything else, email me. If you think ;this is a cool thing and want to encourage me to write more stuff, feel free to  ;send a donation. ; ;This application uses most (all?) of the features of the assembler itself, in ;terms of equates, defines, inline math, multiple file includes.. ; ;This application uses timer 0 and timer 1, the UART and baud rate generator, ;all the I/O ports, and the watchdog timer. ; ;Active features: ; ;       Pseudorandom number (PN) generator, This is implemented with a 19 bit ;       maximal length shift register. Each time the routine is called, the ;       bit pattern is shifted. The seed value is programmable, and the routine ;       leaves you randomized bits in three registers (Could easily be put in SRAM)  ; ;       1mS system "tick" (T0) Drives timeouts and timed events ; ;       Watchdog timer support, coded for the internal watchdog, but a simple ;       hack to drive external doggies. There is an equate to set the watchdog ;       "feed" interval, driven from the 1mS opsys tick ; ;       R/C Servo outputs. (T1) There is a programmable width for each servo, from ;       0-255 (actually 250, the math for 255 is JUST TOO UGLY!) Eight or less ;       output channels are supported. Each channel has a byte in ram that sets ;       it's position.  There is a debug routine in the servo logic that provides ;       some action on channels 1-3 so you can see it do something immediately. ;       WARNING: Buffer the signal with at least a 1k resistor out to the servos. ; ;       Switch inputs (ok, so it's not hard..) ;       LED outputs, driven from the PN generator. Ditto ; ;       Stepper motor control. A single channel output for unipolar motors, with ;       flag bytes in RAM setting full or half step mode, and forward or reverse ;       direction, as well as speed in mS per step (or half-step) ;       WARNING: You're on your own for drivers, the 8515 won't run even the  ;       wimpiest stepper directly, don't even try. At a minimum, four NPN transistors ;       should do the trick. ; ;       Buffered serial input and output comms at popular baud rates. Also break ;       generation, and Xon/Xoff handshake. Coding in hardware handshake would be ;       trivial. There is a receive and a transmit buffer of configurable size. ; ;       Inter-Charachter delay on transmit. Not pacing with nulls, this is a true ;       delay, driven by the T0 interrupt. I've needed this from time to time to ;       talk to other devices that couldn't really handle full speed comms. ;        ; ;This application does not use the I2C interface, or the on-chip EEPROM. ;Feel free to do something with them and send me a module to include in the package. ; ;PLEASE DO NOT HACK THIS AND PASS IT ON. ;Hack it for yourself all you like, but the main idea is to give newcomers software ;that does something, and that actually WORKS.. Your code might work, but the next ;guy might not be so clever. If you use this to create something else, please call it ;something else, and make it clear to whoever you give it to that it's YOUR creation. ; ;Old software rule #1: You modify it, you're on your own. ;  ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ;       0.02    98.08.20  dvh   Adding RC servo outputs on PORTC ;       0.03    98.08.30  dvh   Added watchdog timer support ;       0.04    98.08.30  dvh   Added stepper motor support ;       0.05    98.08.31  dvh   Exported Watchdog support to it's own file ;       0.10    98.09.02  dvh   Couldn't think of anything else to add, so I shipped it. ; ;******************************************************************** ;This application consists of twelve files. They should all be in the ;same directory, or you will have to edit the .include directives  ;below to make them point to the right locations. ; ;Main.asm (You're looking at it)  The main structure of the program ;8515def.inc    Port and pin definitions, supplied by ATMEL ;Equates.inc    Where constants get defined as words, to make life easier ;Isr.asm        Interrupt Service Routines ;Init.asm       Machine initialization ;Memory.asm     Buffer management ;Serial.asm     Serial comms using the on-board uart ;Servo.asm      Servo control code for eight standard R/C servos on PORTC  ;Stepper.asm    Stepper motor control for one unipolar motor on port A0-A3 ;Random.asm     A 19 bit PseudoNoise generator ;Watchdog.asm   Watchdog timer code ;Tables.asm     Example lookup table, plus code to get data out of it. ; ;******************************************************************** ;Physical resources. ; ;This application is designed for an AVR demo board, with 8.00 MHz crystal. ;For bonus points, modify it to use a 4 MHz xtal, without changing the speed  ;that anything runs at. That's a good first project! ; ;A0-3   Stepper motor output, 4 phase unipolar motor ;A4-7   Unused (A7 has an optional activity "ping" in MAIN) ;B0-7   Demo board LEDs ;D0-7   Demo board switches ;C0-7   R/C Servo outputs, connect to servos through a 1k resistor. ;T0     1mS opsys interrupt ;T1     Servo width ;UART   Interrupt driven buffered comms, both directions ;Wdog   Internal watchdog timer is active. ; ;******************************************************************** ; ;Logical equates to make life easier ; .include "EQUATES.INC" ;Lots of settings in here to play with. ; ;******************************************************************** ; ;The actual beginning of the code. ;INIT the machine, start the ISRs, and exit into idle ;On reset, the 8515 jumps to 0000, which contains a jump vector (see ISR.ASM) ; .cseg ;This tells the assembler that what follows is code, and goes in ROMspace ; .include "Isr.asm"      ;Restart vector and Interrupt service routines are here .include "Init.asm"     ;Initialization of timers,ports,and ext hdw. ; ;*********************************************************************** ; Idle:            ;         ;Spin the random number generator.         ;         rcall   Random                  ;Result in RAND1,RAND2,and RAND3         out     PORTB,RAND1             ;Output to the lights         ;Without a scope, you'll just see lights that are half-bright.         ;         ;Get the switches and light the lights (currently turned off)         ;The switches are used in SERVO.ASM to control some of the servo outputs.         ;in     TEMP,PIND               ;Input the switches         ;out    PORTB,TEMP              ;Output to the lights         ;NOTE! The switch inputs are active low, an un-pushed switch         ;gives a "1" input. This results in LED off, if the pin status         ;is fed out to PORTB, due to how the LEDs are wired.         ;NOTE! A LOW output state lights the LEDs. NOT a HIGH!!!         rcall   Servo_Frame_Check       ;Move the servos, if it's time (Servo.asm)         rcall   Step_Motor              ;Step the stepper, if it's time (Stepper.asm         rcall   Timed_Smack             ;Feed the watchdog, if it's needed (Watchdog.asm)         ;You could put something here to react to RS-232 input, and/or create RS-232         ;output based on switch inputs, the random generator, send messages.. Up to you!         ;You could write a subsumption based robot controller, I've given you a bunch          ;of bot-useful I/O functions that really work, and a ton of free CPU cycles.         ;The step motor driver needs an interface capable of handling the load, but         ;the output pins are wiggling properly, just don't try to connect a stepper         ;directly to the chip. A set of four NPN transistors will work acceptably          ;well for small motors with the motor common leads (2 usually) tied to a         ;suitable power supply. Don't try to run steppers off the development board         ;power supply, you'll glitch the CPU.          ldi     TEMP,$01                ;A pimg every time we loop         rcall   Ping                    ;         rjmp    Idle                    ; ; ;******************************************************************** ; ;A terribly useful little diagnostic hack, provided you've got a scope. ;Just call Ping with a number in TEMP, and it will give you N pulses. ;This is very useful for figuring which branch of a program is taken  ;on live hardware, when simulation isn't really an option. ; Ping:         sbi     PORTA,7                 ;I hope your scope can resolve to 125nS         cbi     PORTA,7                 ;         dec     TEMP                    ;One less ping         brne    Ping                    ;Are we done? If not, then ping again         ret                             ;If so, bye! ;******************************************************************** ;External Routines ; .include "Servo.asm"    ;RC Servo drivers, 8 channels on port C using timer 1 .include "Stepper.asm"  ;5/6 wire unipolar motor driver .include "Memory.asm"   ;Memory allocation, buffer handlers .include "Serial.asm"   ;Serial port I/O .include "Random.asm"   ;Pseudorandom generator, Specifically, a 19                         ;bit maximal length generator. .include "Watchdog.asm" ;Watchdog timer handler ; ;*************************************************************************** ;Lookup tables.. Stuff you need ; .include "Tables.asm" ; .db     "COPYRIGHT 1997-1998 David VanHorn",CR,LF .db     "ALL RIGHTS RESERVED",CR,LF ; ;***************************************************************************
MEMORY ASM






;*************************************************************************** ;  ; File Name             :'MEMORY.asm" ; Title                 : ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ; ; DESCRIPTION ; ; All routines that put data in or out of memory ;  ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ;       0.02    98.08.28  dvh   Regularized to XL,XH notation instead of R26.R27 etc ;       0.03    98.09.01  dvh   STORE_SEROUT now enables UDRIE, so we start transmitting. ;                               The UDRIE ISR will turn it off when we're done talking. ; ;*************************************************************************** ; ;Tail pointing at head means empty ;Head will contain a byte always, which is junk, or previous data. ;Adding data means inserting the data at tail, then incrementing tail. ; ;       HEAD    Head+1  Head+2  Tail ;       junk    AA      55      A5 ; ;To use a byte from head, you take data from HEAD+1 ;then call Kill_HEAD, which moves the data as follows: ; ;       HEAD    Head+1  Head+2  Tail ;       junk    AA      55      A5 ;       AA<-----/       | ;               55<-----/ ;                       A5 ; ;Tail becomes Tail-1, AA was the last entity used, and 55 will be the next ; ;       HEAD    Head+1  Tail ;       AA      55      A5 INIT_Buffers:         ldi     ZL,low(SERIAL_IN_BUF)   ;The low byte of the address of the buffer         ldi     ZH,high(SERIAL_IN_BUF)  ;high byte         sts     SERIAL_IN_TAIL,ZL       ;Store the address of the head, in the tail         sts     SERIAL_IN_TAIL+1,ZH     ;         ldi     ZL,low(SERIAL_OUT_BUF)  ;         ldi     ZH,high(SERIAL_OUT_BUF) ;         sts     SERIAL_OUT_TAIL,ZL      ;         sts     SERIAL_OUT_TAIL+1,ZH    ;         ret ; ;************************************************************ ; ;This routine takes a string, pointed to by Z, and places it in the serial tx buf. ;It will return when the string has been placed, which may be a while if there ;is a backup and the buffer is full. ;The calling routine points Z at the string ; String_Serout:         lpm                     ;Get the byte into R0 and incr Z         and     R0,R0           ;Is it null         breq    STR_Serout_Exit ;Yes, all done         mov     TEMP,R0         ;         rcall   Str_Ser_Loop    ;This will wait forever, until the buffer can take                                 ;more data, so no worries about over-writing it.         ld      TEMP,Z+         ;Inc Z, toss the data.         rjmp    String_Serout   ; STR_Serout_Exit:         ;rcall  WT4Clear        ;Forced delay, till serial empty         ret ; ; ;Hands a single byte off to the storage routine, and keeps trying till it's taken ; STR_SER_LOOP:         push    TEMP            ;         rcall   Timed_Smack     ;Since we could be looping here for a while         pop     TEMP            ;         rcall   Store_Serout    ;Take this byte and shove it         and     TEMP2,TEMP2     ;Did it go?         brne    STR_SER_LOOP    ;Nope, try again!         ret      ;Buffer store code. Checks buffer for space available, dosen't ;store if no space available. Calling routine places data in ;TEMP, and gets a flag back in TEMP, FF if no store, 00 if  ;stored. It's up to the calling routine to decide what to do ;about it when there's no more room at the inn. ; ;Input is in TEMP ;No output ; Store_Serout:         push    YL                      ;Save the pointers         push    YH                      ;         push    ZL                      ;         push    ZH                      ;         push    TEMP                    ;A copy of the data on the stack         ldi     ZL,low(SERIAL_OUT_BUF)  ;Take the head         ldi     ZH,high(SERIAL_OUT_BUF) ;         lds     YL,(SERIAL_OUT_TAIL)    ;and the tail         lds     YH,(SERIAL_OUT_TAIL+1)  ;                rcall   Space_Check             ;returns # of stored bytes in TEMP Store_Ser_out_A:         cpi     TEMP,SEROUT_SIZE        ;Is it full?         brne    Store_Ser_out_B         ;Nope, just store         ldi     TEMP2,$FF               ;Flag for no more incoming data.         pop     TEMP                    ;Balance Stack         rjmp    Store_Ser_out_Done      ;Bail, can't do it. Store_Ser_out_B:         ldi     TEMP2,$00               ;Flag success         lds     YL,(SERIAL_OUT_TAIL)    ;Stored pointer to tail         lds     YH,(SERIAL_OUT_TAIL+1)  ;         ld      TEMP,Y+                 ;Tail +1                 pop     TEMP                    ;Get the data back         st      Y,TEMP                  ;Store the byte         sts     (SERIAL_OUT_TAIL),YL    ;Store new tail         sts     (SERIAL_OUT_TAIL+1),YH  ;         in      TEMP,UCR                ;Get the current int status         ori     TEMP,$20                ;Enable UDRIE (turned off by empty buffer in         out     UCR,TEMP                ;UDRIE ISR) Store_Ser_out_Done:         ;Temp was popped above, or in out_A         pop     ZH         pop     ZL         pop     YH         pop     YL         ret ; ;************************************************************ ; ;This routine is called by the serial input ISR ; Store_Serin:         push    YL         push    YH         push    ZL         push    ZH         push    TEMP         ldi     ZL,low(SERIAL_IN_BUF)   ;         ldi     ZH,high(SERIAL_IN_BUF)  ;         lds     YL,(SERIAL_IN_TAIL)     ;Stored pointer to tail         lds     YH,(SERIAL_IN_TAIL+1)   ;         rcall   Space_Check             ;See what's stored         cpi     TEMP,(SERIN_SIZE-3)     ;Is it almost full?         breq    Store_Ser_in_Full       ;Store, but set handshake off         cpi     TEMP,(SERIN_SIZE-2)     ;Is it full?         breq    Store_Ser_in_bad        ;Yes, then just exit, can't take more          Store_Ser_in_B:         lds     YL,(SERIAL_IN_TAIL)     ;Stored pointer to tail         lds     YH,(SERIAL_IN_TAIL+1)   ;         ld      TEMP,Y+                 ;         pop     TEMP                    ;                st      Y,TEMP                  ;Store the byte         sts     (SERIAL_IN_TAIL),YL     ;         sts     (SERIAL_IN_TAIL+1),YH   ;         rcall   HS_XON                  ;Sets Xon if not already         ldi     TEMP,$00                ;Flag success         rjmp    Store_Ser_in_Done       ; Store_Ser_in_Full:         lds     YL,(SERIAL_IN_TAIL)     ;Stored pointer to tail         lds     YH,(SERIAL_IN_TAIL+1)   ;         ld      TEMP,Y+                 ;         pop     TEMP                    ;         st      Y+,TEMP                 ;Store the byte         sts     (SERIAL_IN_TAIL),YL     ;         sts     (SERIAL_IN_TAIL+1),YH   ;         rcall   HS_XOFF         ;I'm full         ldi     TEMP,$00                ;         rjmp    Store_Ser_in_done       ; Store_Ser_in_Bad:         pop     TEMP                    ;Balance stack         ldi     TEMP,$FF                ;Bad return flag                 ;NOTE: There is currently no check for this flag, but it's there. Store_Ser_in_Done:         ;THERE IS NO POP TEMP HERE! THAT IS CORRECT         pop     ZH         pop     ZL         pop     YH         pop     YL         ret ; ;************************************************************* ; ;R28,29 are buffer tail pointer R30,31 are buffer head ;Return number of chars stored between R28,30 ;Carry flag on exit says no more  ; Space_Check:         sub     YH,ZH         sbc     YL,ZL         mov     TEMP,YL ;pointing at head means empty         ret ; ; ; ;************************************************************* ; ;Used to delete a single char off the head of a buffer. ;Point R30,31 to beginning of buffer Head ;Point R28,29 to current end (Next place a byte could go) ;R27,26 will point to the destination during shuffle-down ;Returns junk in temp ;and decremented tail pointer in R30.31 ; ; ;Move head+1 to head ;inc head ;Head=tail? done, else loop ;if Z<y then we have nothing in the buffer, just exit ;If Z=Y then we have 1 char in the buffer, kill it, dec Z ;If Z>Y then we have lots, shuffle down, dec Z ; Kill_Head:         push    XL         push    XH         push    TEMP         cp      ZH,YH           ;If these are <> no sense checking the rest         brne    Kill_Head_A     ;If <> then go shuffle         cp      ZL,YL           ;If these are = too, then the buffer's empty         breq    Kill_Head_Out   ;         ;Check if the buffer is zero len.         ;could use space check, but I'd have to reload after Kill_Head_A:         mov     XL,ZL           ;Point 26,27 at head         mov     XH,ZH           ;         ;R26,27 (X) working pair pointing at head         ;R28,29 (Y) pointing at tail         ;R30,31 (Z) Pointing at head         ld      temp,Z+         ;X now dest, Y src         ;R26,27 (X) working pair pointing at head         ;R28,29 (Y) pointing at tail         ;R30,31 (Z) currently pointing at head+1 Kill_Head_B:         ld      TEMP,Z+         ;Head+1 > TEMP, Now pointing at head +2         st      X+,TEMP         ;TEMP > HEAD, now pointing at head+1         cp      XL,YL           ;         brne    Kill_Head_B     ;If so, we're done         cp      XH,YH           ;         brne    Kill_Head_B     ; Kill_Head_Done:                 ;Data is tossed         ldi     TEMP,$00        ;         st      Y,TEMP          ;Store a zero in the old tail location         ld      TEMP,-Y         ;Decrement Z, pointing to old-tail-1, which may be head. Kill_Head_Out:         pop     TEMP            ;         pop     XH              ;         pop     XL              ;         ret                     ; ; ;************************************************************* ; ;Used to delete a single char off the tail of a buffer ;Point R30,31 to beginning of buffer ;Point R28,29 to current end ;Returns junk in temp ;and decremented pointer in R30.31 ; Kill_Tail:         ;Make sure we aren't backing up past the beginning!         cp      ZL,YL         brne    Kill_Tail_B         cp      ZH,YH         brne    Kill_Tail_B         rjmp    Kill_Tail_Done Kill_Tail_B:         ld      TEMP,-Y         ;Decrement Y Kill_Tail_Done:                 ;Data is tossed         ret                     ; ; ;*************************************************************************** ; Ram_Init:       ;Load all variables with harmless or nonsense values         rcall   Clean_Ram               ;Go nuke the ram space         rcall   FLAG_RAM                ;         ret ;********************************************************************************** ;Debug only, stores distinctive data into each buffer, making it easy to see them ;in the simulator. ; Flag_Ram: ;        F_1:         ldi     ZH,high(SERIAL_IN_BUF)  ;         ldi     ZL,low(SERIAL_IN_BUF)   ;         ldi     TEMP,$22                ;         ldi     TEMP2,SER_SIZE          ;         rcall   Fill_Buf                ; F_2:             ldi     ZH,high(Serial_OUT_BUF) ;         ldi     ZL,low(Serial_OUT_BUF)  ;         ldi     TEMP,$33                ;         ldi     TEMP2,SER_SIZE          ;         rcall   Fill_Buf                ; F_Fin:           ret                             ;Done. ; ;Fill a given buffer to a given size with  ;given data. ; Fill_Buf:         st      Z+,TEMP                 ;Fill a byte         dec     TEMP2                   ;One less to fill         brne    Fill_Buf                ;Are we done?         ret                             ;yes, exit ;*************************************************************************** ; ;This routine flushes any previous junk out of our workspace. ;Avoids any pattern-sensitivity or programmer headspace errors with ;uninitialized variables. ; Clean_Ram:         ldi     ZH,$00                  ;Point @ $0060         ldi     ZL,$60                  ;         clr     TEMP                    ; CleanLoop:         st      Z+,TEMP                 ;Store $00 at each location         cpi     ZH,$02                  ;If not at least here, then go do it again         brne    CleanLoop               ;         cpi     ZL,$5C                  ;DONT EAT THE STACK!         brne    CleanLoop               ;Getting close!         ret                             ;Done. ;
RANDOM ASM






;***************************************************************************
; 
; File Name             :'random.asm"
; Title                 :
; Date                  :
; Version               :
; Support telephone     :765 287 1987  David B. VanHorn
; Support fax           :765 287 1989
; Support Email         :dvanhorn@cedar.net
; Target MCU            :AT90S8515
;
; DESCRIPTION
;
; DEFINITIONS
;
; 
;***************************************************************************;
;       M O D I F I C A T I O N   H I S T O R Y 
;
;
;       rev.      date    who   why
;       ----    --------  ---   ------------------------------------------
;       0.01    97.09.30  dvh   Creation
;       0.02    98.08.05  dvh   Making sure it works, plus tightening up.
;                               6uS at 8 Mhz.
;       0.03    98.08.23  dvh   Removed some un-needed CLC instructions and
;                               replaced roX with lsX's 
;

Random:
        ;Maximal legnth 19 bit shift register sequence,
        push    R16             ;Make workspace
        push    R17             ;

        ;  RAND3    RAND2    RAND1
        ;22222111 11111110 00000000
        ;43210987 65432109 87654321

        mov     R16,RAND1       ;Make copy
        andi    R16,$13         ;Bits 5 and 2 and 1

        mov     R17,RAND3       ;Make copy
        andi    R17,$04         ;Bit 19

        ;clc                    ;not needed, because we will overwrite bit 0 later
        rol     RAND1           ;Shift the bits D7->Carry
        rol     RAND2           ;Carry->D0 D7->Carry
        rol     RAND3           ;Carry->D0 D7->Carry, but that will be fixed.

        ;Now we have to Xor the bits to see what
        ;goes in to bit 1

        rol     R17             ;Move bit 19->20
        andi    R17,$08         ;Important that D1,0 be zero

        andi    R16,$13         ;Mask out irrelevants, protecting 19
        or      R17,R16         ;Get bits 5->21 2->18 1->17

        andi    R17,$18         ;Nuke bits 18,17
        lsr     R17             ;19->19 5->20
        lsr     R17             ;19->18 5->19
        lsr     R17             ;19->17 5->18

        eor     R17,R16         ;First xor, result in R17 (5x2 in 02 and 19x1 in 01)
        mov     R16,R17         ;Make a copy
        ror     R16             ;Move the 5x2 result to 01
        andi    R17,$01         ;Mask off everything else
        andi    R16,$01         ;in both
        eor     R17,R16         ;
        brne    D_One           ;If one, do that, else 

D_Zero: 
        ;Set a zero in the lsb of the low byte
        mov     R16,RAND1       ;Get the low byte
        andi    R16,$FE         ;Make the LSB zero
        mov     RAND1,R16       ;Put it back
        Rjmp    D_Exit          ;Bye bye

D_One:  
        ;Set a one in the lsb of the low byte
        mov     R16,RAND1       ;Get the low byte
        ori     R16,$01         ;Make the LSB one
        mov     RAND1,R16       ;Put it back

D_Exit:
        pop     R17             ;Put everything back where I got it.
        pop     R16             ;
        ret








SERIAL ASM






;*************************************************************************** ;  ; File Name             :'SERIAL.asm" ; Title                 :Serial I/O ; Date                  :98.07.21 ; Version               :0.01 ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ; ; DESCRIPTION ; ;*************************************************************************** ;       M O D I F I C A T I O N   H I S T O R Y  ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ;       0,02    98.08.29  dvh   Added break and "wait for char" ;       0.03    98.09.01  dvh   Working on making it more interrupt driven ; ;*************************************************************************** ;I'm not really satisfied with this yet, I'm not having a lot of luck modeling ;a fully interrupt-driven comm scheme along with things like the "wait for char" ;feature. Also, there's a conflict if the output buffer fills up. There is just ;no good way to handle that as far as I can see. I always end up with a construct ;that loops forever and dosen't return to the main system.  ; ;Your input is most welcome here :) ;*************************************************************************************** ; Set_Baud_2400:         ldi     TEMP,207        ;         rcall   SET_BAUD        ;         ret Set_Baud_4800:         ldi     TEMP,103        ;         rcall   SET_BAUD        ;         ret Set_Baud_9600:         ldi     TEMP,51         ;         rcall   SET_BAUD        ;         ret Set_Baud_14400:         ldi     TEMP,34         ;         rcall   SET_BAUD        ;         ret Set_Baud_19200:         ldi     TEMP,25         ;         rcall   SET_BAUD        ;         ret Set_Baud_28800:         ldi     TEMP,16         ;Error is 2.1%         rcall   SET_BAUD        ;         ret Set_Baud_57600:         ldi     TEMP,8          ;Error is 3.7%         rcall   SET_BAUD        ;         ret Set_Baud_115200:         ldi     TEMP,3          ;Error is 7.8%         rcall   SET_BAUD        ;         ret Set_Baud:         mov     LOOP,TEMP       ;Save it for a moment         rcall   WT4TXMT         ;Wait till all prev sent data is gone         out     UBRR,LOOP       ;Set the baud rate         ret                     ; ; ;************************************************************ ; ;This one waits for TX buffer empty ; WT4TXMT:         rcall   Timed_Smack             ;Reset the watchdog if needed         ldi     ZL,low(SERIAL_OUT_BUF)  ;Look at the serial output         ldi     ZH,high(SERIAL_OUT_BUF);buffer.         lds     YL,(SERIAL_OUT_TAIL)    ;If tail = head,then there's         lds     YH,(SERIAL_OUT_TAIL+1)  ;nothing to translate         rcall   Space_Check             ;returns bytes stored, in TEMP         brne    WT4TXMT                 ;         ret ; ;This one waits till the current char is done transmitting ; WT4XCHR:         rcall   Timed_Smack     ;Reset watchdog if needed         in      TEMP,USR        ;         andi    TEMP,$40        ;Get the TX Complete bit         breq    WT4XCHR         ;         ret ; ;************************************************************ ; Check_Serial_IN:         ldi     ZL,low(SERIAL_IN_BUF)   ;Absolute pointer to head         ldi     ZH,high(SERIAl_IN_BUF)  ;         lds     YL,(SERIAL_IN_TAIL)     ;Stored pointer to tail         lds     YH,(SERIAL_IN_TAIL+1)   ;         rcall   Space_Check             ;returns bytes stored, in TEMP         breq    Check_Serial_IN_Done    ;         ldi     ZL,low(SERIAL_IN_BUF)   ;Absolute pointer to head         ldi     ZH,high(SERIAl_IN_BUF)  ;         ld      TEMP,Z+                 ;The head byte is junk         ld      TEMP,Z                  ;If non-empty, then take a byte out of                                         ;serial_in_buf, and use it somehow         and     TEMP,TEMP               ;If non zero         brne    Check_Serial_in_Full    ;then let the serial in buffer fill         ;If zero returned, then the byte was stored, so kill it from         ;the serial in buf.         ldi     ZL,low(SERIAL_IN_BUF)         ldi     ZH,high(SERIAL_IN_BUF)         lds     YL,(SERIAL_IN_TAIL)         lds     YH,(SERIAL_IN_TAIL+1)         rcall   Kill_Head               ;Kill the sent byte         sts     (SERIAL_IN_TAIL),YL     ;         sts     (SERIAL_IN_TAIL+1),YH   ;         rcall   HS_XON                  ;Since we ate a char, set HS to true if it was false         ldi     TEMP,0                  ;         ret Check_Serial_In_Full:         ldi     TEMP,$FF        ;Failed exit Check_Serial_IN_Done:         ret ; ;************************************************************ ; ;Set output handshaking. ;Toggled based on current status ;Jam XON or XOFF into output buffer ; HS_XOFF:         lds     TEMP,HS_FLAG    ;         and     TEMP,TEMP       ;         breq    Ser_HS_Done     ;         ldi     TEMP,XOFF       ;Jam XOFF into serial out buffer         rcall   Store_Serout    ;Enable transmit (if not already)         in      TEMP,UCR        ;         andi    TEMP,$7F        ;         out     UCR,TEMP        ;Make sure no more data         in      TEMP,UCR        ;         ori     TEMP,$20        ;Turn on UDRIE         out     UCR,TEMP        ;in case it's off, so xoff and xon get sent         ldi     TEMP,$00        ;         sts     HS_FLAG,TEMP    ;         rjmp    SER_HS_Done     ; ; ;************************************************************ ; HS_XON:         lds     TEMP,HS_FLAG    ;Get current status         and     TEMP,TEMP       ;         brne    Ser_HS_Done     ;If non-zero, then it's already cleared         ldi     TEMP,XON        ;Jam XON into serial out buffer         rcall   Store_Serout    ;Enable transmit if not already         in      TEMP,UCR        ;         ori     TEMP,$80        ;         out     UCR,TEMP        ;         in      TEMP,UCR        ;         ori     TEMP,$20        ;Turn on UDRIE         out     UCR,TEMP        ;in case it's off, so xoff and xon get sent         ldi     TEMP,$FF        ;         sts     HS_FLAG,TEMP    ; Ser_HS_Done:         ret ; ;************************************************************ ; SEND_BREAK:         ;Assumption, that you want anything placed in the serial buffer         ;to be sent before the break.          rcall   WT4TXMT         ;Wait for serial buffer empty         rcall   WT4XCHR         ;Wait for the last char to get out         ;Shift the baud rate slightly lower.         in      TEMP,UBRR       ;Get the current baud rate (51 @ 9600)         push    TEMP            ;Save it         mov     LOOP,TEMP       ;Make a copy         clc                     ;         lsr     TEMP            ;divide it by 8         lsr     TEMP            ;         lsr     TEMP            ;         add     LOOP,TEMP       ;Add that to the old value         out     UBRR,LOOP       ;Set the new baudrate, two "bits" slower         ldi     TEMP,NUL        ;Load a null, 8 bits of zero         out     UDR,TEMP        ;Start Breaking now.             rcall   WT4XCHR         ;Wait till the char is sent         pop     TEMP            ;Get the old baud rate         out     UBRR,TEMP       ;Restore the original baud rate         ret ; ;************************************************************ ; ;Input: Char to get in temp, Delay in TEMP2 ;Output: TEMP2=0 for ok, or FF for timeout or wrong char ; WT4CHAR: ;This starts the Serial_Wait engine to look for an incoming char.                  sts     Char_Time,TEMP2 ;System will decrement on 1mS system ticks         sts     Watch_Char,TEMP ;save temp for compare         ;Turn off inbound serial ints         ret                     ; ;Char_Time = 0, Not waiting, or timed out. ;Char_Time <> 0, actively waiting. ;Watch_Char = byte value waiting on. ; Serial_Wait:         lds     TEMP,Char_Time  ;Are we waiting on a char (0=no)         and     TEMP,TEMP       ;Is Char_Time=0?         brne    WT4CHAR_ACTIVE  ;If not, then we're waiting         ret                     ;Else return         ;If we're waiting, then see if it's happened WT4CHAR_ACTIVE:         in      TEMP,USR        ;Did we get a char yet?         andi    TEMP,$80        ;Check bit 7         brne    WT4CHAR_GOTIT   ;         ret                     ;Nope, just keep waiting WT4CHAR_GOTIT:         in      TEMP2,UDR       ;Get the Char         andi    TEMP2,$7F       ;Mask off parity(?)         lds     TEMP,Watch_Char ;What were we looking for?         cp      TEMP,TEMP2      ;Is this it?         breq    WT4CHAR_SUCCESS ; WT4CHAR_SUCCESS:         ret ; ;*************************************************************************************** ;
SERVO ASM







;*************************************************************************** ;  ; File Name             :'SERVO.asm" ; Title                 : ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ;  ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.08.20  dvh   Creation ;       0.02    98.08.21  dvh   Added code for a frame rate, driven by timer 0 ;                               (the 1ms opsys tick) This is set to XX milliseconds ;                               in EQUATES.INC, 20 is typical, Less seems ok, down to ;                               zero if 8 channels are active. If less are active, there ;                               will need to be some delay time set in FRAME_RATE ;                               Changed Frame_Delay from register to SRAM ;                               Regularized to XL,XH notation instead of R26.R27 etc ; ;******************************************************************** ;DEBUGGING NOTES ; ;Problem: The servo outs are working, but only up to a value of 64-ish, then it ;appears that the top two bits are output on the subsequent channel. This is independent ;of the channel, The top 2 of 1 appear on 2, 3 appears on 4...  ; ;I've tried dedicating an entire set of registers, solely to this application. ;I've tried making the width data directly, rather than bringing it in from SRAM ;I've tried dedicating a special register to the ISR ;I've tried changing the order the servos operate in. ;I've tried pushing and popping all my temps in this routine ;I've tried turning off the call to RANDOM in Main ;I've tried turning off serial buffer code in the ISRs ; ;Finally found it, RTFM bug. Page 5-39 says that you must  ;load the high byte of timer 1 first because of how the system ;loads the 16 bit value. From the above, you can see that a  ;simple oversight can lead to much frustration. ; ;******************************************************************** ; ;This routine drives eight standard RC servos connected to port C (easily changed) ;The number of servos could be reduced fairly easily by simply changing the test ;in the TIM1_OVF of ISR.ASM ; ;The servo control bytes are stored as SERVO_X(1-8) in SRAM, Modify as you like, ;the servo position will be updated when it's turn comes up next. ; ;Servo outputs are activated sequentially, so that no more than one servo is active ;at any time. The servos may take some time to physically slew to their new positions, ;but we are only talking to one of them at a time. This is also how standard R/C  ;equipment works, so there's nothing new here. ; ;PORTC's current status tells us wether a servo is currently active, there should only ;ever be a single high bit on this port. ; ;Servo_Control determines wether any servos should be active. Every time timer1 expires, ;it shifts the Servo_Control byte. If the active bit shifts into carry, then it re-inits ;Servo_Control. Setting Servo_Control to zero will cause the system to quit outputting  ;servo pulses. ; ;Frame_Delay is a timer byte, decremented by the Timer0 int every millisecond. It is  ;initialized by the timer1 ISR to some value (set in EQUATES.INC). Before we output the ;first servo pulse, we must see Frame_Delay decrement to zero. This assures a constant ;frame rate, no matter what the widths of the servo pulses is, since the timers run  ;concurrently. ; ; ; Servo_Frame_Check:         ;         ;The Servo_Control byte tells us which servo output will be active.         ;If it's zero, then no servos are active, and we don't send any output.         ;Otherwise, it's just a matter of picking which one to send a signal to.         ;Without any pulses, the servos go to idle and may be pushed by external forces.         ;First, we check if there are any servo outs currently running, if so,         ;we want to let them expire (TIM1_OVF in ISR.ASM) before doing anything else.         ;This means that the vast majority of the time, we just do this test         ;and bail, preserving CPU time for other things! :)         in      TEMP,PORTC              ;Get current servo outputs         and     TEMP,TEMP               ;Anything busy?         brne    Servo_Pass              ;Yes, just leave them alone!                  ;Now, we check wether ANY servos SHOULD be active. There's only one         ;bit on in here at any time, starting with bit zero. (Servo_1)         ;If no bits are active, then we can just bail, since we aren't          ;outputting any servos at this time (servos are free-floating then,         ;and can be moved since they aren't seeking any position actively)         lds     TEMP,Servo_Control      ;Get from SRAM         and     TEMP,TEMP               ;Is it zero?         breq    Servo_Pass              ;Nope, just pass         ;We've determined that a servo should be active         ;If it's servo 1, then we want to check if the frame delay has expired.         ;FRAME_DELAY is decremented to zero by timer 0 (the 1ms opsys tick)         ;If it hasn't expired, then again we bail out. If it has then we go ahead         ;and start servo 1, and reload FRAME_DELAY. Note that this test is only done         ;before SERVO_1 is output, it's not a delay between all channels, just between          ;SERVO_8 and SERVO_1.         cpi     TEMP,$01                ;Is it servo 1         brne    Servo_Test              ;If not, just continue                  lds     TEMP,Frame_Delay        ;Get the frame delay byte         and     TEMP,TEMP               ;If so, check if the servo frame delay has expired         brne    Servo_Pass              ;If not, then don't do anything till it does.         ;If this is servo 1, and FRAME_DELAY has expired, then reload FRAME_DELAY, and         ;go ahead with the servo pulses         ldi     TEMP,FRAME_RATE         ;Dec'd by Timer 0         sts     Frame_Delay,TEMP        ;Put it back                  ;DEBUG  This routine fakes some different outputs on the servo channels.         rcall   Servo_Debug             ;Some different outputs on channels 1-4 Servo_Test:         ;Figure out which servo time to pick up, 0-7 (in temp)         ldi     TEMP2,0                 ;Init TEMP2         lds     TEMP,Servo_Control      ;         ;As we shift our copy of the Servo_Control right twoard carry, Temp2 is          ;incremented to form an offset, which we will add to the base of the          ;servo time widths in RAM to obtain the right width for this servo         ; Servo_PTR:                              ;         lsr     TEMP                    ;Shift the servo bit twoard carry         brcs    Servo_Point             ;If carry, then we have our servo                inc     TEMP2                   ;Otherwise inc, and          rjmp    Servo_PTR               ;try again         ;Now we have the index, set the main pointer in R30,31, and grab the          ;proper servo width byte from SRAM Servo_Point:         ;Point at the base of the servo times         ldi     ZL,low(SERVO_1) ;Point at the lowest servo         ldi     ZH,high(SERVO_1)        ;         ;Add the offset to pick SERVO_X         clc                             ;                                adc     ZL,TEMP2                ;Add the servo number (0-7)         ;Handle carry if the location crosses a boundary         brcc    Servo_Point_B           ;No carry, we're done         inc     ZH                      ;else inc the high byte of the pointer                  ;Retrieve the servo width data Servo_Point_B:         ld      TEMP3,Z                 ;Get the servo time         ;At 8 mhz, /8 prescaler gives us a granularity of 1uS, we want 4uS         ;The servo base is 1mS and we want 0-255 to represent 1-2mS, so         ;we need a base value of 1mS (250*4) and add to that the servo         ;width data (0-255)*4. Complicating this, the counter counts UP to         ;zero, so to simplify things, I load a pair of registers with FF         ;and subtract each value 4 times, then load that value into the timer.                  ;Set to $FFFF so we can subtract down.         ldi     TEMP2,$FF               ;         ldi     TEMP,$FF                ;         ;mov    TEMP,TEMP2              ;TEMP and TEMP2 have FFFF, TEMP3 has servo value         ;Apply the SERVO_X byte as a width         rcall   Servo_Sub               ;16 bit subtract TEMP3 4 times from TEMP2,TEMP         ;Subtract for 1mS fixed in addition         ldi     TEMP3,SERVO_BASE        ;And the fixed base value         rcall   Servo_Sub               ;16 bit subtract TEMP3 4 times from TEMP2,TEMP         ;Load timer 1, Timer high byte MUST be loaded first,         ;per databook 5-39, else wierdness will ensue.         ;         out     TCNT1H,TEMP2            ;Timer high byte         out     TCNT1L,TEMP             ;Timer low byte         ;Start timer 1         in      TEMP,TCCR1B             ;Get the timer control register         andi    TEMP,$F8                ;Mask out the timer bits         ori     TEMP,$02                ;Set the prescaler to "/8" (1MHz at 8.0 MHz)         out     TCCR1B,TEMP             ;Make it so.                  ;Start the servo output active         lds     TEMP,Servo_Control      ;Get the active servo bit         out     PORTC,TEMP              ;Start the output Servo_Pass:     ;Either nothing to do, or it's done         ret                             ; ;*************************************************************************** ; ;Effectively, multiply an 8 bit value by 4, and perform a subtract against ;a 16 bit value in TEMP2(H) and TEMP(L) ;A loop would be shorter, but slower, and have to have control code. ;16 bit math would be 16 bit math.. ;Input in TEMP3 ; Servo_Sub:         clc                             ;Clear previous carry         sbc     TEMP,TEMP3              ;Subtract it once         brcc    Servo_1A                ;If carry, then dec the high byte         ;These next two shouldn't be necessary.         dec     TEMP2                   ;Then fix the high byte         clc                             ; Servo_1A:         sbc     TEMP,TEMP3              ;         brcc    Servo_1B                ;         dec     TEMP2                   ;         clc                             ; Servo_1B:         sbc     TEMP,TEMP3              ;         brcc    Servo_1C                ;         dec     TEMP2                   ;         clc                             ; Servo_1C:         sbc     TEMP,TEMP3              ;                brcc    Servo_1D                ;         dec     TEMP2                   ; Servo_1D:         ret ; ;******************************************************************* ; ;Mix, take a servo width, and apply some amount of it to another channel. ; Servo_Mix:         ;First I need to know the source channel         ;then the destination         ;Then I read the source and the destination.         ;         ;Servo outs are generally seen as + or - around zero (128)         ;So, I need to look at it as signed data.         ;Ex: 175 would be +50, and 75 would be -50         ;A mix ratio of 50% would give +25 and -25 respectively,         ;and then I need to add or subtract these amounts to the          ;destination channel, regardless of it's original position,         ;but if the operation causes rollover (carry) then I need to         ;set the channel to maximum travel in that direction, (0 or 255)         ;rather than allowing it to wrap across to the opposite side!         ;         ;Then apply the mix ratio to the source,         ;and to the destination. Both could be positive or negative.         ;         ;         ret ; ;******************************************************************* ; ;Add a bit of randomness to the servo outputs ; ;From observing the servos, it seems that they only resolve to about 6 bits ;of real precision. Any changes smaller than that don't seem to produce shaft ;output. This is called "deadband", and keeps the servo from drawing excessive ;power seeking around for a precise position.  ; ;I thought it might be interesting to have the servo take up positions that are ;slightly different from the signalled position, so I made this little quicky, which ;substitutes some bits of the random bytes into the servo byte. ; ;Input, servo width in temp, Output, altered servo width in temp. ; Servo_Dither:                 andi    TEMP,$F8        ;Mask out the lowest 3 bits                 mov     TEMP2,RAND2     ;Get some randomness                 ori     TEMP2,$07       ;We'll use these bits                 or      TEMP,TEMP2      ;Replace the low 3 bits with the random ones.         ret ; ;******************************************************************* ; ;Just a debug routine to fake in some default data ; Servo_Set:         ldi     TEMP,$FF        ;         sts     Servo_1,TEMP    ;Set a full wide pulse         ldi     TEMP,$80;         sts     Servo_2,TEMP    ;Set a 1/2 wide pulse         ldi     TEMP,$40        ;         sts     Servo_3,TEMP    ;Set a 1/4 wide pulse         ldi     TEMP,$20        ;         sts     Servo_4,TEMP    ;Set a 1/8 wide pulse         ldi     TEMP,$10        ;         sts     Servo_5,TEMP    ;Set a 1/16 wide pulse         ldi     TEMP,$08        ;         sts     Servo_6,TEMP    ;Set a 1/32 wide pulse         ldi     TEMP,$04        ;         sts     Servo_7,TEMP    ;Set a 1/64 wide pulse         ldi     TEMP,$00        ;         sts     Servo_8,TEMP    ;Set a minimum pulse         ret ; ; ;Just some code to make the servos twitch, it's called before servo1 each time ;Frame_Delay rolls to zero ; Servo_Debug:         ;         ;DEBUG  This code will smoothly ramp a given servo up to FF         ;         ;Normally, this code will not be used         lds     TEMP,Servo_1            ;Change this to SERVO_X as desired         inc     TEMP                    ;         sts     SERVO_1,TEMP            ;         ;or for manual control..         in      TEMP,PIND               ;Input the switches         ;sts    SERVO_1,TEMP            ;Set servo 1 from the buttons!         sts     SERVO_2,TEMP            ;Set servo 2 from the buttons!         ;sts    SERVO_3,TEMP            ;Set servo 3 from the buttons!         ;sts    SERVO_4,TEMP            ;Set servo 4 from the buttons!         ;sts    SERVO_5,TEMP            ;Set servo 5 from the buttons!         ;sts    SERVO_6,TEMP            ;Set servo 6 from the buttons!         ;sts    SERVO_7,TEMP            ;Set servo 7 from the buttons!         ;sts    SERVO_8,TEMP            ;Set servo 8 from the buttons!         ;Normally, this code will not be here         sts     SERVO_3,RAND1           ;Pick a new random position         ret                             ;

STEPPER ASM






;*************************************************************************** ;  ; File Name             :'STEPPER.asm" ; Title                 : ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ;  ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.08.30  dvh   Creation ; ;******************************************************************** ; ; ;A simple routine to control a stepper motor.  ;Full or half-step, outputs designed for unipolar motors. ; ;Full stepping ; ;Phase  Current ; ;0000   1       (off) ;1000   1       1st state ;0100   1 ;0010   1 ;0001   1       4th state ; ;Half-Stepping ; ;Phase  Current ; ;0000   0       (off) ;1000   1       1st state ;1100   0 ;0100   1 ;0110   0 ;0010   1 ;0011   0 ;0001   1 ;1001   0       8th state ; ; ;Going forward, step up the table, going backward, step back :) ;Up to 1mS per step (or half-step) with a simple control routine based on the 1mS ISR ; ;Ram variables ; ;Step_Dir       Forward or reverse (0=Reverse, 1=forward, anything else= no step)? ;Step_Time      mS per phase (0-255) ;Step_State     Where are we now? A pointer into the stepper output table ;Step_Mode      Full or half step (0=full 1=half) ;Step_Speed     Reload value for Step_Time. Your application can change this and  ;               alter the stepper speed. Beware, steppers don't like large changes ;               in velocity. ; Step_Motor: ;First, see if we're stepping this time         lds     TEMP,Step_Time  ;Is it time to step?         cpi     TEMP,$00        ;         breq    Step_Direction  ;If so, then check direction, else         ret                     ;just bail Step_Direction: ;Then determine the direction         lds     TEMP,Step_Dir   ;Get the current direction         and     TEMP,TEMP       ;         breq    Step_Reverse    ;         cpi     TEMP,$01        ;Is it forward?         breq    Step_Forward    ;Go if so.         rjmp    Step_Done       ;else it's no step Step_Forward: ;Then wether full or half (forward)         lds     TEMP,Step_Mode  ;Full or half         and     TEMP,TEMP       ;         breq    Step_FF         ;         rjmp    Step_FH         ; Step_Reverse: ;Or wether full or half (reverse)         lds     TEMP,Step_Mode  ;Full or half?         and     TEMP,TEMP       ;         breq    Step_RF         ;         rjmp    Step_RH         ; ; ;*************************************************************************************** ; ;Step forard, a full step ; Step_FF:         lds     TEMP,Step_State                 ;Get the current state         inc     TEMP                            ;advance one step on the full step table         cpi     TEMP,$04                        ;         brne    Step_FFA                        ;         ldi     TEMP,$00                        ; Step_FFA:         ldi     ZL,low(Full_Step_Table*2)       ;Make the Z reg point at the table         ldi     ZH,high(Full_Step_Table*2)      ;preparing for the LPM instruction         rjmp    Step_Output                     ; ; ;Step forward, a half step ; Step_FH:         lds     TEMP,Step_State                 ;Get the current state         inc     TEMP                            ;advance one step on the full step table         cpi     TEMP,$08                        ;         brne    Step_FHA                        ;         ldi     TEMP,$00                        ; Step_FHA:         ldi     ZL,low(Half_Step_Table*2)       ;Make the Z reg point at the table         ldi     ZH,high(Half_Step_Table*2)      ;preparing for the LPM instruction         rjmp    Step_Output                     ; ; ;Step reverse a full step ; Step_RF:         lds     TEMP,Step_State                 ;Get the current state         dec     TEMP                            ;advance one step on the full step table         cpi     TEMP,$FF                        ;         brne    Step_RFA                        ;         ldi     TEMP,$03                        ; Step_RFA:         ldi     ZL,low(Full_Step_Table*2)       ;Make the Z reg point at the table         ldi     ZH,high(Full_Step_Table*2)      ;preparing for the LPM instruction         rjmp    Step_Output                     ; ; ;Step reverse a half step ; Step_RH:         lds     TEMP,Step_State                 ;Get the current state         dec     TEMP                            ;advance one step on the full step table         cpi     TEMP,$FF                        ;         brne    Step_RHA                        ;         ldi     TEMP,$07                        ; Step_RHA:         ldi     ZL,low(Half_Step_Table*2)       ;Make the Z reg point at the table         ldi     ZH,high(Half_Step_Table*2)      ;preparing for the LPM instruction         rjmp    Step_Output                     ; ; ;Whichever and how far, output the motor states ; Step_Output:         sts     Step_State,TEMP                 ;         lsl     TEMP                            ;Mult x 2, because we store words in rom         add     ZL,TEMP                         ;Add the calculated offset         brcc    STO_A                           ;If no carry, then we're done         inc     ZH                              ;Handle carry STO_A:  lpm                                     ;look up character         mov     TEMP,R0                         ;         andi    TEMP,$0F                        ;Mask off non-stepper bits         in      TEMP2,PORTA                     ;         andi    TEMP2,$F0                       ;Kill old stepper bits         or      TEMP2,TEMP                      ;         out     PORTA,TEMP2                     ; Step_Done:         lds     TEMP,Step_Rate                  ;Restart the timer for the next step         sts     Step_Time,TEMP                  ;         ret                                     ;
TABLES ASM






;*************************************************************************** ;  ; File Name             :"TABLES.asm" ; Title                 :Constant data ; Date                  :98.07.21 ; Version               :0.01 ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ; ;*************************************************************************** ; ;       M O D I F I C A T I O N   H I S T O R Y  ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ; ;************************************************************************* ;The provided example could be implemented as an algorithm, simply adding  ;30h to the input byte, but in the table implementation, any value can be  ;used. This is good for functions where the translation algorithm is unknown ;or arbitrary. Mainly I wanted a clear example of table based translation. ;************************************************************************* ; ;.org $F00      ;Arbitrary point in romspace                 ;Optionally, comment out the .org directive, and                 ;the table will float at the end of the code                 ;provided it is the LAST FILE included in MAIN                 ;                 ;Nothing prevents you from splattering your tables                 ;throughout the rom space, but it's neater this way :) .cseg ; ;************************************************************************* ; ;Just an example table for now. ; ; BIN2ASC_TAB: ;        0   1   2   3   4   5   6   7   8  .db     $30,$31,$32,$33,$34,$35,$36,$37,$38 ;        9   A   B   C   D   E   F .db     $39,$41,$42,$43,$44,$45,$46 ; ;************************************************************************* ; ;Example table-reading code. Converts a binary value in TEMP to ASCII. ;To keep it simple we assume that the calling routine will only pass us ;values 0-F, otherwise we will read past the end of the table ; ;Note that this routine does not care where the table is located. ; ;Read_Table: ;       ldi     R30,low(BIN2ASC_TAB*2)  ;Make the Z reg point at the table ;       ldi     R31,high(BIN2ASC_TAB*2) ;preparing for the LPM instruction ;       add     R30,TEMP                ;Add the calculated offset ;       brcc    RT_A                    ;If no carry, then we're done ;       inc     R31                     ;Handle carry ; ;RT_A:   ;       lpm                             ;look up character ;       ret ; ;I assume external hardware will take care of current control in half step mode ; ; BIG HAIRY NOTE!  Because of the way that the assembler works, and ;the fact that EPROM is 16 bit words, tables built as follows are  ;actually allocating one word per byte, Have a look at the read routine ;in STEPPER.ASM for these tables as opposed to the one above. Both  ;work, but putting more than one byte per .db statement results in ;more efficient use of the romspace. ; Full_Step_Table: .db     $08     ;1000 Full current .db     $04     ;0100 Full current .db     $02     ;0010 Full current .db     $01     ;0001 Full current Half_Step_Table: .db     $08     ;1000 Full current .db     $0C     ;1100 Half current .db     $04     ;0100 Full current .db     $06     ;0110 Half current .db     $02     ;0010 Full current .db     $03     ;0011 Half current .db     $01     ;0001 Full current .db     $09     ;1001 Half current Signon_Message: ;        00000000011111111112222222222333    3          3       3          3        3  3  3   ;        12345678901234567890123456789012    3          4       5          6        7  8  9 .db     "AVR8515 Startup program version ","MAJOR_REV",".","Minor_Rev","Minor_RevB",CR,LF,NUL
WATCHDOG.ASM






;*************************************************************************** ;  ; File Name             :'Watchdog.asm" ; Title                 : ; Date                  : ; Version               : ; Support telephone     :765 287 1987  David B. VanHorn ; Support fax           :765 287 1989 ; Support Email         :dvanhorn@cedar.net ; Target MCU            :AT90S8515 ;  ;***************************************************************************; ;       M O D I F I C A T I O N   H I S T O R Y  ; ; ;       rev.      date    who   why ;       ----    --------  ---   ------------------------------------------ ;       0.01    98.07.29  dvh   Creation ; ;******************************************************************** ; Timed_Smack:         ;Whatever you need to satisfy the internal or external watchdog         ;The internal watchdog can be set to 16,32,64,128,256,512,1024,or 2048mS         ;This scheme for timed reset is limited to 255mS between resets, but it can         ;be used with any of the timeouts above. Resetting a watchdog more frequently         ;than is needed, is mostly harmless.         ;Just make sure that the value you set the timer to at the end of INIT is         ;GREATER than the value you assign for Dog_Chow in EQUATES.INC         ;The code to set the watchdog to a particular interval is in INIT.ASM         lds     TEMP,Dog_Food           ;How many milliseconds before reset is needed?         and     TEMP,TEMP               ;Is it zero?         brne    Smack_Done              ;Nope, just exit         ldi     TEMP,Dog_Chow           ;Reload the timer for the next occurence         sts     Dog_Food,TEMP           ;         wdr     ;This code will reset the internal watchdog         ldi     TEMP,$0D                ;000XXXXX Reserved                                         ;XXX0XXXX Watchdog turnoff enable                                         ;XXXX1XXX Watchdog enable bit 1=enable                                         ;XXXXX000 (8) Watchdog timeout,  16mS                                         ;XXXXX001 (9) Watchdog timeout.  32mS                                         ;XXXXX010 (A) Watchdog timeout.  64mS                                         ;XXXXX011 (B) Watchdog timeout. 128mS                                         ;XXXXX100 (C) Watchdog timeout, 256mS                                         ;XXXXX101 (D) Watchdog timeout, 512mS                                         ;XXXXX110 (E) Watchdog timeout,1024mS                                         ;XXXXX111 (F) Watchdog timeout,2048mS         out     WDTCR,TEMP              ; Smack_Done:         ret
What's Really HOT at DonTronics? 
Don's Download Dungeon
 
Home  Products Prices Directory Order Contact New Books Files Links Other
 

DonTronics Home Page

mailto: don@dontronics.com_
Copyright © 1996-99 DonTronics
 
 
 
Top of Page