Path: Home => AVR overview => Applications => Steppermotor controller => Source code    (Diese Seite in Deutsch: Flag DE) Logo
Steppermotor controller small

Source code of the Steppermotor controller with an ATtiny13



; ****************************************************
; * Steppermotor-Driver with an ATtiny13 - Version 1 *
; * (C)2007 by http://www.avr-asm-tutorial.net       *
; ****************************************************
;
; Debugging switches
;
.equ debug_calc = 0
.equ debug_const = 0
.equ debug_out = 0
;
.nolist
.include "tn13def.inc"
.list ;                           ______
;                                /      |
; Hardware:                     |       |
;              _______          .       .     +12V black
;      ___    /       |         |       |  ___  |
; +5V-|___|--|RES  VCC|--+5V B3-|I4   O4|-|___|-+Q4 red
;            |        |         |       |  ___  |
;        B3--|PB3  PB2|---------|I5   O5|-|___|-+Q2 brown
;            |        |         |       |  ___  |
; Analog-In--|PB4  PB1|---------|I6   O6|-|___|-+Q3 green
;            |        |         |       |  ___  |
;         |--|GND  PB0|---------|I7   O7|-|___|-+Q1 white
;            |________|         |       |       |
;             ATtiny13        |-|GND  CD|-------+
;                               |_______|
;                                ULN2003
;
; Funktioning:
;   A stepper motor is controled by an analog input voltage
;     on pin 3. The input voltage ranges from 0 Volt to
;     the operating voltage of the processor. The number
;     of steps of the motor over that input range are
;     adjusted by the constant cSmSteps (1...65535 steps).
;   Stepmotor drive sequence and encoding:
;     Portbit PB0 PB2 PB1 PB3 |         |
;     Color    wt  bn  gn  rd | Portbit | Byte
;     Step     Q4  Q3  Q2  Q1 | 3 2 1 0 |
;     ------------------------+---------+------
;        1      1   1   0   0 | 0 1 0 1 |  05
;        2      0   1   1   0 | 0 1 1 0 |  06
;        3      0   0   1   1 | 1 0 1 0 |  0A
;        4      1   0   0   1 | 1 0 0 1 |  09
;     results in the word table: .dw 0x0605,0x090A
;
; Timer TC0:
;   The timer runs with the processor clock of 1.2 Mcs/s
;     with a prescaler value of 1024 in normal CTC mode,
;     with the CompareA register as TOP value. Interrupt
;     at CompareA match resp. at CTC reset.
;   The interrupt service routine compares the actual
;     value of motor steps with the target value. If the
;     actual value is too low, the actual value is
;     increased one step, if it is too high the actual
;     step value is decreased. If actual and target value
;     are equal, the coils of the motor are switched off
;     after a certain pre-selectable delay.
;   Timing: step frequency = 1.2 Mcs/s / 1024 / CompA,
;     with CompA = 255: step frequency = 4.57 cs/s,
;     with CompA = 8: step frequency = 146 cs/s
; ADC: 
;   Conversion of the analogue voltage on pin 3 / PB4 /ADC2, 
;     summing up 64 measuring results to average results,
;     conversion to the target value for the stepper motor
;   Timing: clock divider for conversion = 128,
;     1,2 Mcs/s / 128 = 9,375 kcs/s = 106,7 us clock rate,
;   Conversion = 13 cycles = 1,387 ms per conversion
;              = 721 conversions per second
;   Averaging over 64 conversions = 88,75 ms = 11,3 results
;     per second
;
; Constants
;
.equ cSmSteps = 1276 ; 2552/2, number of steps for full range
.equ cSmFreq = 145 ; frequency stepper motor
;                    Minimum: 5 cs/s, Maximum: 1171 cs/s
.equ cSmDelay = 390 ; number of cycles prior to switching off
;
; Derived constants
;
.equ clock = 1200000 ; clock frequency Tiny13 internal
.equ Tc0Ctc = 1024 ; prescaler value TC0
.equ cCmpA = clock / 1024 / cSmFreq ; CompareA value
;
; Checking the constants
;
.IF cCmpA > 255
	.ERROR "Steppermotor frequency too low!"
	.ENDIF
.IF cCmpA < 1
	.ERROR "Steppermotor frequency too high!"
	.ENDIF
;
; SRAM (not used, only for stack operation)
;
; Registers
;
; used: R0 for table reading
; used: R8:R1 for calculation of target value
; free: R10:R8
.def rAdcCL = R11 ; ADC calculation value LSB
.def rAdcCH = R12 ; dto., MSB
.def rAdcRL = R13 ; ADC transfer value LSB
.def rAdcRH = R14 ; dto., MSB
.def rSreg = R15 ; status save/restore register
.def rmp = R16 ; Multipurpose outside Int
.def rimp = R17 ; Multipurpose inside Int
.def rFlg = R18 ; Flags
	.equ bAdc = 0 ; Adc conversion complete flag
.def rAdcC = R19 ; ADC counter (64 Adc conversions)
.def rAdcL = R20 ; ADC Adder register LSB
.def rAdcH = R21 ; dto., MSB
.def rSmSL = R22 ; Steppermotor target value LSB
.def rSmSH = R23 ; dto., MSB
.def rSmIL = R24 ; Steppermotor actual value LSB
.def rSmIH = R25 ; dto., MSB
; used: X for activation delay of the coils
; free: Y
; used: Z for table access
;
; **********************************
;  Code Segment Start, Int - Vector
; **********************************
;
.cseg
.org $0000
;
	rjmp Main ; Reset vector
	reti ; INT0 vector
	reti ; PCINT0 vector
	reti ; TIM0_OVF vector
	reti ; EE_RDY vector
	reti ; ANA_COMP vector
	rjmp Tc0IntCA ; TIM0_COMPA vector
	reti ; TIM0_COMPB vector
	reti ; WDT vector
	rjmp AdcInt ; ADC vector
;
; **********************************
;     Interrupt Service Routines
; **********************************
;
; Timer-Counter 0 Compare A Interrupt Service Routine
;
Tc0IntCA:
	in rSreg,SREG ; save status
	cp rSmIL,rSmSL ; compare actual with target value
	cpc rSmIH,rSmSH
	breq Tc0IntCA0 ; jump if equal
	brcs Tc0IntCAF ; actual less than target value
	sbiw rSmIL,1 ; actual greater than target, one step back
	rjmp Tc0IntCAS
Tc0IntCAF:
	adiw rSmIL,1 ; one step forward
Tc0IntCAS:
	mov rimp,rSmIL ; copy actual value LSB
	andi rimp,0x03 ; isolate lowest two bit
	ldi ZH,HIGH(2*SmTab) ; point Z to table in flash memory
	ldi ZL,LOW(2*SmTab)
	add ZL,rimp ; add the two lowest bits
	ldi rimp,0 ; update upper Byte
	adc ZH,rimp
	lpm ; read next value from table to R0
	.IF debug_out == 0
		out PORTB,R0 ; write value to port
		.ENDIF
	ldi XH,HIGH(cSmDelay) ; restart delay counter
	ldi XL,LOW(cSmDelay)
	out SREG,rSreg ; restore status
	reti
Tc0IntCA0:
	sbiw XL,1 ; decrease delay counter
	brne Tc0IntCAD ; not yet zero
	ldi rimp,0 ; switch of current on coils
	out PORTB,rimp ; to output driver
	ldi XH,HIGH(cSmDelay) ; restart delay counter
	ldi XL,LOW(cSmDelay)
Tc0IntCAD:
	out SREG,rSreg ; restore status
	reti
;
SmTab:
.dw 0x0605,0x090A
;
; Adc Conversion Complete Interrupt Service Routine
;
AdcInt:
	in rSreg,SREG ; save status
	in rimp,ADCL ; read LSB of ADC result
	add rAdcL,rimp ; add to the result register
	in rimp,ADCH ; read MSB of ADC result
	adc rAdcH,rimp ; add to the result register
	dec rAdcC ; decrease counter
	brne AdcInt1 ; if not zero, go on converting
	mov rAdcRL,rAdcL ; 64 conversions, copy result
	mov rAdcRH,rAdcH
	clr rAdcH ; clear sum
	clr rAdcL
	ldi rAdcC,64 ; restart conversion counter
	sbr rFlg,1<<bAdc ; set flag
AdcInt1:
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; restart conversion
	out SREG,rSreg ; restore status
	reti
;
; **********************************
;    Main program Init and Loop
; **********************************
;
Main:
	; Stack init
	ldi rmp, LOW(RAMEND)
	out SPL,rmp
	; Init register
	clr rFlg ; clear flag register
	clr rSmIL ; clear actual value
	clr rSmIH
	clr rSmSL ; clear target value
	clr rSmSH
	ldi XH,HIGH(cSmDelay) ; restart delay counter
	ldi XL,LOW(cSmDelay)
	; Init output port
	ldi rmp,0x0F ; output on portbits 0..3
	out DDRB,rmp
	ldi rmp,0x05 ; start motor at step 1
	out PORTB,rmp
	; Debugging session
	.IF debug_calc
		.equ adc_result = 128
		ldi rmp,HIGH(adc_result)
		mov rAdcRH,rmp
		ldi rmp,LOW(adc_result)
		mov rAdcRL,rmp
		rjmp dcalc
		.ENDIF
	.IF debug_const
		.equ const = cMSteps
		ldi rmp,HIGH(const)
		mov rSmSH,rmp
		ldi rmp,LOW(const)
		mov rSmSL,rmp
		.ENDIF
	; Init ADC
	ldi rAdcC,64 ; restart ADC conversion counter
	clr rAdcL ; clear ADC sum value
	clr rAdcH
	ldi rmp,1<<ADC2D ; Digital Input Disable on channel 2
	out DIDR0,rmp
	ldi rmp,1<<MUX1 ; ADC mux on channel 2
	out ADMUX,rmp
	ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; start conversion
	; TC0 init
	ldi rmp,cCmpA ; set CTC value
	out OCR0A,rmp
	ldi rmp,1<<WGM01 ; set CTC mode
	out TCCR0A,rmp
	ldi rmp,(1<<CS02)|(1<<CS00) ; prescaler = 1024
	out TCCR0B,rmp
	ldi rmp,1<<OCIE0A ; Interrupt Compare Match A enable
	out TIMSK0,rmp
	; Sleep Mode and Int Enable
	ldi rmp,1<<SE ; sleep enable
	out MCUCR,rmp
	sei ; Int enable
Loop:
	sleep ; sleep
	nop ; Dummy for wakeup
	sbrc rFlg,bAdc ; ADC flag set?
	rcall AdcRdy ; convert ADC result
	rjmp Loop ; sleep again
;
; **********************************
;        Calculation routine
; **********************************
;
; ADC conversion complete calculate target value
;
AdcRdy:
	cbr rFlg,1<<bAdc ; clear ADC flag
.IF debug_const
	ret
	.ENDIF
dcalc:
	mov rAdcCH,rAdcRH ; copy sum value
	mov rAdcCL,rAdcRL
	ldi rmp,LOW(cSmSteps) ; number of steps to R4:R3:R2:R1
	mov R1,rmp
	ldi rmp,HIGH(cSmSteps)
	mov R2,rmp
	clr R3
	clr R4
	clr R5 ; clear result in R8:R7:R6:R5
	clr R6
	clr R7
	clr R8
AdcRdy1:
	lsr rAdcCH ; shift lowest bit to carry flag
	ror rAdcCL
	brcc AdcRdy2 ; do not add
	add R5,R1 ; add to result
	adc R6,R2
	adc R7,R3
	adc R8,R4
AdcRdy2:
	lsl R1 ; multiply multiplicator by two
	rol R2
	rol R3
	rol R4
	mov rmp,rAdcCL ; = zero?
	or rmp,rAdcCH
	brne AdcRdy1 ; go on multiplying
	ldi rmp,0x80 ; round up
	add R5,rmp
	adc R6,rmp
	ldi rmp,0
	adc R7,rmp
	adc R8,rmp
	cli ; disable interrupts
	mov rSmSL,R7 ; set target value LSB
	mov rSmSH,R8 ; dto., set MSB
.IF debug_out
	out PORTB,rSmSL
	.ENDIF
	sei ; enable interrupts again
	ret
;
; End of Source Code
;



©2007 by http://www.avr-asm-tutorial.net