; SPI funktioniert zuverlässig ; die Steuerung von sechs angeschlossenen Servos muss noch getestet werden ;------------------------------------------------------------------------------------------------------------------------------ ; Steuerung von 12 Servos durch einen Master und zwei Slaves über SPI ; abgewandelte Testversion: anstelle der Servos wird ein LCD angeschlossen um die Übertragung der Pulslängen zu prüfen ; in Abwandlung des Myavr-originals wird Port D7 durch D1 ersetzt ;------------------------------------------------------------------------------------------------------------------------------ ; Funktion : ATMega8 ist SPI-Master; wählt durch SoftwareSlaveSelect einen der ATTinys ; als Slave aus (PIN B0), dieser antwortet über Port B1; dadurch kontrollierte ; Datenübertragung per SPI ; Format der Datenübertragung: erstes Byte: oberes Nibbel=Servonummer, unteres ; Nibbel=Highbyte der zugehörigen Servopulsdauer, zweites Byte Lowbyte ; Speicherorganisation: die Positionen der Servos werden immer in der Reihenfolge Low-High ; abgelegt, somit ergibt sich die Low-Speicherstelle des Servos Nr. x (für x=1 bis 6) nach: Ramstart+2*(x-1) ; Schaltung : SPI: MOSI, SCK - ATMega8 zu ATTinys lt. Datenblatt (ohne MISO) ; ATMega8 - ATTiny2313 ; C1 - B0 (Slave 1) ; C2 - B0 (Slave 2) ; C3 - B1 ; ATtiny2313 auf Steckboard verbunden mit ATMega8 auf MYAVR-Board ; verbunden über SPI, ATMega als Master, ATTinys als Slave ; ATtiny mit Servos an D1 bis D6 ; Verbindung Steckboard - MYAVR-Board: +,-,MOSI, SCK, Software-SS, Bereit (2313 Pins 20,10,17,19,12,13) ;------------------------------------------------------------------------------------------------------------------------------ ; Prozessor : ATtiny2313 ; Takt : 1.000.000 Hz ; Sprache : Assembler (GNU) ; Datum : 6.12.2012 ; Version : 1.5 ; Autor : U.S. ;------------------------------------------------------------------------------------------------------------------------------ ; ; Registerfestlegungen: R1 Servonummer ; R2 Servopuls Highbyte ; R4 Sicherung des Statusregisters während ISR ; R5 Servonummer nur zur Programmierung ; R6 Lowbyte nur zur Programmierung ; R7 Highbyte nur zur Programmierung ; R16 temporär ; R17 temporär und Zwischenspeicher für empfangene Daten ; R18 reserviert für ISR ; R19 reserviert für ISR ; R20 reserviert für Servomaske (ISR) ; R21 Zählervergleichswert low (ISR) ; R22 Zählervergleichswert high (ISR) ; r24 für lcd-Ausgabe nur zur Programmierung ; .include "avr.h" ;------------------------------------------------------------------------------------------------------------------------------ begin: rjmp main ; RESET External Reset, Power-on Reset and Watchdog Reset reti ; INT0 External Interrupt Request 0 reti ; INT1 External Interrupt Request 1 reti ; TIMER1 CAPT Timer/Counter1 Capture Event rjmp ISR_Vergl_A ; TIMER1 COMPA Timer/Counter1 Compare Match A reti ; TIMER1 OVF Timer/Counter1 Overflow reti ; TIMER0 OVF Timer/Counter0 Overflow reti ; USART, RX USART, Rx Complete reti ; USART, UDRE USART Data Register Empty reti ; USART, TX USART, Tx Complete reti ; ANA COMP Analog Comparator reti ; PCINT rjmp ISR_Vergl_B ; TIMER1 COMPB reti ; TIMER0 COMPA reti ; TIMER0 COMPB reti ; USI START USI Start Condition reti ; USI OVERFLOW USI Overflow reti ; EEPROM Ready reti ; WDT OVERFLOW Watchdog Timer Overflow ;------------------------------------------------------------------------------------------------------------------------------- main: ldi R16,lo8(RAMEND) out SPL,R16 ; Set Stack Pointer to top of RAM ldi ZL,lo8(RAMSTART) ; das Z-Register wird für Speicherzugriffe im Hauptprogramm benutzt ldi YL,lo8(RAMSTART) ; das Y-Register für Interrupts ; ldi R16,0b01111110 ; out DDRD,R16 ; Port D.1 bis D.6 als Ausgang (für Steuerimpulse) ldi R16,lo8(1500) ; Timerstartwert liegt bei 1500µS (Mittelstellung) ldi R17,hi8(1500) ; errechnet sich nach (= Impulsdauer * Prozessorfrequenz / Timerprescaler) st Z+,r16 ; jetzt wird die Tabelle für jeden Servo mit dem Wert für die Mittelstellung geladen st Z+,r17 st Z+,r16 ; Low-Byte st Z+,r17 ; High-Byte st Z+,r16 st Z+,r17 st Z+,r16 st Z+,r17 st Z+,r16 st Z+,r17 st Z+,r16 st Z,r17 ldi R16,lo8(20000) ; Timerendwert liegt bei 20ms ldi R17,hi8(20000) out OCR1AH,R17 out OCR1AL,R16 ldi R16,0b01100000 ; Interrupt OCIE1A und OCIE1B eingeschaltet out TIMSK,R16 ldi R16,0b00001001 ; CTC-Mode, Prescaler 1 out TCCR1B,R16 cbi DDRB,0 ; B0 als Eingang für Slaveselect cbi DDRB,1 ; B1 als Eingang temporär sbi PORTB,1 rcall SPI_init ; MCU als SPI-Slave initialisieren rcall warte100ms ; Warten bis alle anderen MCUs vollständig initialisiert sind rcall lcdInit ; nur zur Programmierung sei mainloop: ;wdr sbic PINB,0 ; warten bis Slaveselect, low-Pegel an SS startet den Ablauf rjmp mainloop sbi DDRB,1 ; B1 als Ausgang für die Dauer der Datenübertragung rcall SPI_Start cbi PORTB,1 ; Pegeländerung an B1 zeigt dem Master die Bereitschaft an rcall SPI_Empfang mov R16,R17 ; oberes Nibbel Servonummer, unteres Nibbel Highbyte der Pulsdauer swap R17 ; Nibbel vertauschen andi R16,0b00001111 ; oberes Nibbel ausblenden, es verbleibt das Highbyte der Servopulsdauer andi R17,0b00001111 ; vertauschtes oberes Nibbel ausblenden, es verbleibt die Servonummer mov R1,R17 ; Servonummer mov R2,R16 ; Highbyte der Servopulsdauer sbi PORTB,1 ; Pegeländerung: fertig ml2: sbic PINB,0 ; warten bis Slaveselect erneut rjmp ml2 rcall SPI_Start cbi PORTB,1 ; Pegeländerung an B1 zeigt dem Master die Bereitschaft an rcall SPI_Empfang ldi ZL,lo8(RAMSTART) dec R1 ; Nummer x des angesprochenen Servos (abgelegt in Register R1) add ZL,R1 ; Speicheradresse des Servos ist Ramstart+2*(x-1) add ZL,R1 st Z+,R17 ; Low-Byte st Z,r2 ; High-Byte speichern sbi PORTB,1 ; Pegeländerung: fertig cbi DDRB,1 ; B1 wieder als Eingang mov R6,r17 ; nur zur Programmierung mov R5,r1 ; nur zur Programmierung mov R7,r2 ; nur zur Programmierung rcall LCD_Ausgabe ; empfangene Werte ausgeben auf LCD nur zur Programmierung rjmp mainloop ;----------------------------------------------------------------------------------------------------------------------------- ; SPI-Initialisierung als Slave ;----------------------------------------------------------------------------------------------------------------------------- SPI_init: cbi DDRB,5 ; B5 als Eingang für MOSI cbi DDRB,7 ; B7 als Eingang für UCSK ldi R16,0b00011000 ; (1< Ausgabe sub R6,R16 ; ja -> subtrahieren sbc R7,R17 inc R0 ; Dezimalstelle erhöhen rjmp LCD_WiD_Div LCD_WiD_Aus:mov r16,r0 rcall lcdData ret