; -----------------------------------------------------------------------------
; 300 bytes brushless motor control
;
; Copyright (c) Matthias Kramm, 2008
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following condition:
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
; -----------------------------------------------------------------------------
; This code demonstrates sensorless commutation of a brushless dc motor, as
; well as twi bus communication and current limiting (emergency shutdown).
; It's been designed to work with the "mikrokopter" motor control hardware,
; which can be obtained from http://www.mikrokopter.de/
; (In particular, the TWI communication is compatible with the communication
; protocol employed by the flight control used on mikrokopters, and
; the microcontroller is assumed to be an atmel atmega8)
.include "atmega8.inc"
; ------------- registers -------------------
.def TMP1 = r16
.def TMP2 = r17
.def WAITFORLEVELSWITCH = r24
.def ZERO = r0
.def EIGHT = r1
.def DELAY = r26
.def CURRENT = r28
.def PWM_MAX = r29 ; right now always 255
.def PHASE = r18
.def STEPPTRL = r30
.def STEPPTRH = r31
; irlr mosfets
;.equ CURRENT_MAX1 = 120
;.equ CURRENT_MAX2 = 200
; standard mosfets
.equ CURRENT_MAX1 = 65
.equ CURRENT_MAX2 = 130
; ------------- interrupts -------------------
; atmega8
.org 0 ; program start address
ldi r29, 0xff
out SPL, r29 ; init stack
clr ZERO
out SPH, ZERO
rjmp init
; lookup table for motor commutation
steps:
.db 0x08,0x10,2,32, 0x08,0x20,1,0, 0x04,0x20,0,32, 0x04,0x08,2,0, 0x02,0x08,1,32, 0x02,0x10,0,0
.org 0x11
; twi
twiint:
in r20, SREG
slaveint:
ldi r19, (1<<TWEN) | (1<<TWINT) | (1<<TWEA) | (1<<TWIE)
in r21, TWSR
andi r21, 0xf8
; states not explicitly handled:
; 0x60,0x68,0x70,0x78 all mean we received a +w
out PORTC, ZERO ; clear red led
cpi r21, 0xd0
brsh twerror
cpi r21, 0x60
brsh noerror
; All states < 0x60 or > 0xd0 mean that either there's a bus error (twsr=0x00,0xf8)
; or we accidently switched into a master transmitter/receiver mode.
; Send a stop to free/reset the line.
twerror:ori r19, (1<<TWSTO)
noerror:
cpi r21, 0x80
brne nodata ; 0x80 means we got a data byte
in r22, TWDR
cp r22, PWM_MAX
brlo noclamp
mov r22, PWM_MAX
noclamp:
out OCR1AL, r22
out OCR1BL, r22
out OCR2, r22
nodata:
cpi r21, 0xa8
brne nosend1
; 0xa8 means we can send our first byte
; (with ea=1 to notify twi that more are to come)
out TWDR, CURRENT
ldi r19, (1<<TWEN) | (1<<TWINT) | (1<<TWIE) | (1<<TWEA)
nosend1:
cpi r21, 0xb8
brne nosend2
; send our second byte
; (with ea=0 to signal the end of the transmission)
out TWDR, PWM_MAX
ldi r19, (1<<TWEN) | (1<<TWINT) | (1<<TWIE)
nosend2:
done:
out TWCR, r19
leaveint:
out SREG, r20
reti
init:
ldi r16, 0x08
mov EIGHT, r16
out SFIOR,EIGHT ; switch on adc multiplexer
ldi r16, 0x40 ; store initial motor pwm
out OCR1AL, r16
out OCR1BL, r16
out OCR2, r16
ldi r16, 0xb8
out DDRD, r16
ldi r16, 2<<COM1A0|2<<COM1B0|1<<WGM10 ; 0xa1
out TCCR1A, r16
ldi r16, 1<<CS10 ; 0x01
out TCCR1B, r16
ldi r16, 1<<WGM20|2<<COM20|1<<CS20 ; 0x61
out TCCR2, r16
out PORTD, ZERO
out DDRC, EIGHT
out PORTC, EIGHT
; set twi speed
; ldi r16, 0xff
; out TWBR, r16
; out TWSR, ZERO
; read out motor address, store as twi address
ldi r16, 0xc0
out PORTB, r16
wait: dec r16
brne wait
ldi r16, (0x28+4)<<TWA0|0<<TWGCE ; twi: my address+0x28, no general calls
sbis PINB, 7
subi r16, 2
sbis PINB, 6
subi r16, 4
ldi r16, (0x28+1)*2
out TWAR, r16
sei
ldi r16, (1<<TWEN) | (1<<TWINT) | (1<<TWEA) | (1<<TWIE) ; switch twi on
out TWCR, r16
turn1:
ldi ZL, LOW(steps*2)
ldi ZH, HIGH(steps*2)
ldi r18, 6 ; 6 phases
startmotor:
ldi r26,255 ; wait 255 loops before commutation
ldi r24,16 ; not running counter
work: dec r18
brmi turn1
lpm r16, Z+
out DDRB, r16
lpm r16, Z+
out PORTD, r16
;lpm r16, Z+
;out ACSR, r16 ; only needed for interrupts
; check current flow
powerlevel:
out SFIOR, ZERO ; switch off acomp
ldi r16, 0xe6
out ADMUX, r16
ldi r16, 1<<ADEN|1<<ADSC|1<<ADIF|3<<ADPS0 ; single shot conversion, 8 prescale
out ADCSRA, r16
;sbi ADCSRA, ADIF
;ldi r16, 6
;out ADMUX, r16
;sbi ADCSRA, ADSC
waitforadccomplete:
sbis ADCSRA, ADIF
rjmp waitforadccomplete
in r16, ADCH
lsl r16
lsl r16
cp r28, r16
brsh noinc
inc r28
noinc: cp r16, r28
brsh nodec
dec r28
nodec:
; decrease maximum pwm if current to high
cpi r28, CURRENT_MAX1
brlo currentok
cpi r28, CURRENT_MAX2
brlo nopowerdown
out OCR1AL, ZERO
out OCR1BL, ZERO
out OCR2, ZERO
nopowerdown:
tst r29
breq nochange
revert: dec r29
rjmp nochange
currentok:
inc r29
breq revert
nochange:
out ADCSRA, ZERO ; back to analog comparator
out SFIOR, EIGHT
lpm r16, Z+
out ADMUX, r16
lpm r17, Z+
clr r27
clr r15
wait3: in r16, ACSR
andi r16, 32
cp r16, r17
brne continue
ldi r16, 0x10
add r27, r16
brcc nooverflow
ldi r27, 0xff
nooverflow:
mov r26, r27 ; store new delay time, use this if we miss a commutation
ldi r24, 16 ; reset counter
rjmp work
continue:
dec r15
brne wait3
inc r27
cp r27, r26
brne wait3
dec r24
brne work
; if we didn't sense a commutation 16 times in a row, slow down our
; "startup" speed
rjmp startmotor