| Home | Products | Prices | Directory | Order | Contact | New | Books | Files | Links | Other |
| Bottom of Page |
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 ASMThis 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?
|
| Home | Products | Prices | Directory | Order | Contact | New | Books | Files | Links | Other |
mailto: don@dontronics.com_
Copyright © 1996-99 DonTronics
| Top of Page |