Hallo,
bei meinem momentanen Projekt mit dem Attiny13 (SoftSPI Slave) hat sich
irgendwo ein Fehler eingeschlichen, welchen ich auch nach mehreren
Versuchen nicht finden kann.
Versuchsaufbau:
- Arduino Uno (SoftSPI.ino) als Master, welcher das über SPI empfangene
Byte im seriellen Monitor ausgibt
- Attiny13 (main.c) als Slave, welcher ständig 193 per SPI sendet
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
#define MOSI PB4
5
#define MISO PB2
6
#define SCK PB1 //INTO
7
#define SS PB3 //PCINT3
8
9
#define Data 0b11000001
10
11
volatileuint8_tSPI_input=0;
12
volatileuint8_tSPI_output=Data;
13
14
/*SPI MODE = 2*/
15
16
ISR(INT0_vect){
17
/*Wenn sinkende Flanke auf SCK, dann MOSI auslesen und als LSB in SPI_input speichern, dann eins nach links Schieben (MSB-Modus)
18
Danach MSB von SPI_output auslesen und auf MISO legen, SPI_output eins nach links schieben*/
19
if(PINB&(1<<MOSI)){//Wenn MOSI == HIGH
20
SPI_input|=(1<<0);
21
}
22
SPI_input<<1;
23
if(SPI_output&(1<<7)){//Wenn MSB SPI_output == 1
24
PORTB|=(1<<MISO);
25
}else{
26
PORTB&=~(1<<MISO);
27
}
28
SPI_output<<1;
29
}
30
31
voidSPI(){
32
SPI_output=Data;//Wichtig! SPI_output nach schieben wiederherstellen (Anwendungsspezifisch)
33
}
34
35
ISR(PCINT0_vect){
36
/*Wenn SS auf Low gezogen wird INT0 aktivieren, wenn SS auf HIGH gezogen wird INT0 deaktivieren und SPI() ausführen*/
37
if(PINB&(1<<SS)){/*Wenn SS == HIGH --> SPI()*/
38
GIMSK&=~(1<<INT0);
39
SPI();
40
}else{//Wenn SS == LOW --> INT0
41
GIMSK|=(1<<INT0);
42
}
43
}
44
45
voidinit_SPI(){
46
/*PCINT3 aktivieren und INT0 auf sinkende Flanke umstellen*/
47
PCMSK|=(1<<PCINT3);
48
MCUSR|=(1<<ISC01);
49
GIMSK|=(1<<PCIE);
50
}
51
52
intmain(void){
53
DDRB|=(1<<MISO);
54
init_SPI();
55
sei();//Interrupts aktivieren!
56
while(1){
57
//nix
58
}
59
}
Problem: Bei einem Byte beginnend mit 0/1 gibt der Arduino 0/255
unabhängig des restlichen Bytes aus.
Die Verkabelung habe ich schon mehrmals geprüft, ist also soweit korrekt
(MISO -- MISO, MOSI -- MOSI, ...).
Kann jemand denn Fehler im Arduino-/Attiny-Programm finden?
Armin W. schrieb:> void setup() {> Serial.begin(9600);> // put your setup code here, to run once:> pinMode(SS, OUTPUT);> pinMode(10, OUTPUT);> digitalWrite(SS, HIGH);> }
In setup() fehlt SPI.begin() Aufruf.
Danke für den Tipp, das Ergebnis ist leider immer noch das gleiche.
Kann es sein das der Arduino Uno einfach nicht langsam genug kann für
den 1,2MHz Attiny13?
Und kurze Verständnisfrage: Im SPI-Modus 2 legt der Master seine Daten
schon bei der ersten fallenden Flanke von SCK an MOSI, bei einer
steigenden Flanke übernimmt er die Daten von MISO, oder?
Sonst würde ich noch einen SoftSPI-Master für den Attiny13 schreiben...
Armin W. schrieb:> Danke für den Tipp, das Ergebnis ist leider immer noch das gleiche.
:(
mach doch am UNO mal eine Verbindung MISO-MOSI, dann solltest du das
gesendete zurückbekommen. Damit weißt du schon mal das der Master
funktioniert - auch wenn du eine fertige Implementierung verwendest.
> Kann es sein das der Arduino Uno einfach nicht langsam genug kann für> den 1,2MHz Attiny13?
naja, bei 100Hz Clock sollte das reichen - da kannst du ja fast schon
mitschreiben. Vorrausgesetzt der Master geht überhaupt auf einen so
kleinen wert einzustellen?
> Und kurze Verständnisfrage: Im SPI-Modus 2 legt der Master seine Daten> schon bei der ersten fallenden Flanke von SCK an MOSI, bei einer> steigenden Flanke übernimmt er die Daten von MISO, oder?
ja - sollte auch mit deinem Code passen
> Sonst würde ich noch einen SoftSPI-Master für den Attiny13 schreiben...
zwei Master??
Sascha
Sascha W. schrieb:> Armin W. schrieb:>> Danke für den Tipp, das Ergebnis ist leider immer noch das gleiche.> :(> mach doch am UNO mal eine Verbindung MISO-MOSI, dann solltest du das> gesendete zurückbekommen. Damit weißt du schon mal das der Master> funktioniert - auch wenn du eine fertige Implementierung verwendest.>>> Kann es sein das der Arduino Uno einfach nicht langsam genug kann für>> den 1,2MHz Attiny13?> naja, bei 100Hz Clock sollte das reichen - da kannst du ja fast schon> mitschreiben. Vorrausgesetzt der Master geht überhaupt auf einen so> kleinen wert einzustellen?>>> Und kurze Verständnisfrage: Im SPI-Modus 2 legt der Master seine Daten>> schon bei der ersten fallenden Flanke von SCK an MOSI, bei einer>> steigenden Flanke übernimmt er die Daten von MISO, oder?> ja - sollte auch mit deinem Code passen>>> Sonst würde ich noch einen SoftSPI-Master für den Attiny13 schreiben...> zwei Master??>> Sascha
1. Hab ich getestet, funktioniert
2. Der minimale SPI-Takt des Arduino Uno beträgt 16000000 Hz / 128 =
125000Hz. Vieleicht doch etwas zu viel(Interrupt-Latenz, Overhead, ...)?
3. Ich meinte einen Attiny13 SPI-Master für meinen Attiny13 SPI-Slave.
Werde ich mal versuchen, denn die Taktfrequenz wäre ja dafür relativ
egal.
Armin W. schrieb:> 2. Der minimale SPI-Takt des Arduino Uno beträgt 16000000 Hz / 128 => 125000Hz. Vieleicht doch etwas zu viel(Interrupt-Latenz, Overhead, ...)?
Dann hast du am tn13 ja nur 10Takte pro Bit - das wird nichts.
Sascha
/*MSB von SPI_output auf MOSI geben, SCK auf Low ziehen, SPI_output eins nach links schieben*/
30
if(SPI_Flag==0){
31
if(SPI_output&(1<<7)){//Wenn MSB von SPI_output gesetzt
32
PORTB|=(1<<MOSI);//MOSI HIGH
33
}else{
34
PORTB&=~(1<<MOSI);
35
}
36
PORTB&=~(1<<SCK);//Fallende Flanke an SCK
37
SPI_output<<=1;
38
SPI_Flag=1;
39
}else{//wichtig
40
/*ZUERST SPI_input eins nach links schieben, MISO als LSB von SPI_input machen*/
41
if(SPI_Flag==1){
42
SPI_input<<=1;
43
PORTB|=(1<<SCK);//Steigende Flanke
44
if(PINB&(1<<MISO)){//Wenn MISO HIGH
45
SPI_input|=(1<<0);//LSB von SPI_input setzen
46
}
47
SPI_Flag=0;
48
}
49
}
50
}
51
52
voidinit_SPI(){
53
/*TimerOVF-Interrupt aktivieren*/
54
TIMSK0|=(1<<TOIE0);
55
}
56
57
uint8_tSPI(){
58
/*Timer aktivieren (Prescaler == 1)*/
59
PORTB&=~(1<<SS);
60
TCCR0B|=(1<<CS00);
61
while(SPI_Flag!=2){
62
//nix
63
}
64
SPI_Flag=0;
65
//PORTB |= (1 << PB0); //Debug
66
PORTB|=(1<<SS);
67
}
68
69
intmain(void){
70
uint8_tDaten=0;
71
DDRB|=(1<<MOSI)|(1<<SCK)|(1<<PB0)|(1<<SS);
72
PORTB|=(1<<SCK)|(1<<SS);
73
init_SPI();
74
sei();//Interrupts aktivieren!
75
SPI_output=Ausgabe;
76
SPI();
77
if(SPI_input==Eingabe){
78
PORTB|=(1<<PB0);
79
}else{
80
PORTB&=~(1<<PB0);
81
}
82
while(1){
83
//nix
84
}
85
}
Slave:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
#define MOSI PB4
5
#define MISO PB2
6
#define SCK PB1 //INTO
7
#define SS PB3 //PCINT3
8
9
#define Eingabe 67
10
#define Ausgabe 123
11
12
volatileuint8_tSPI_input=0;
13
volatileuint8_tSPI_output=Ausgabe;
14
15
/*SPI MODE = 2*/
16
17
ISR(INT0_vect){
18
/*Wenn sinkende Flanke auf SCK, dann ZUERST SPI_inputeins nach links schieben, MOSI auslesen und als LSB in SPI_input speichern (MSB-Modus) Danach MSB von SPI_output auslesen und auf MISO legen, SPI_output eins nach links schieben*/
19
SPI_input<<=1;
20
if(PINB&(1<<MOSI)){//Wenn MOSI == HIGH
21
SPI_input|=(1<<0);
22
}
23
if(SPI_output&(1<<7)){//Wenn MSB SPI_output == 1
24
PORTB|=(1<<MISO);
25
}else{
26
PORTB&=~(1<<MISO);
27
}
28
SPI_output<<=1;
29
}
30
31
voidSPI(){
32
SPI_output=Ausgabe;//Wichtig! SPI_output nach schieben wiederherstellen (Anwendungsspezifisch)
33
if(SPI_input==Eingabe){
34
PORTB|=(1<<PB0);
35
}
36
}
37
38
ISR(PCINT0_vect){
39
/*Wenn SS auf Low gezogen wird INT0 aktivieren, wenn SS auf HIGH gezogen wird INT0 deaktivieren und SPI() ausführen*/
40
if(PINB&(1<<SS)){/*Wenn SS == HIGH --> SPI()*/
41
GIMSK&=~(1<<INT0);
42
SPI();
43
//Wichtig! SPI_output nach schieben wiederherstellen (Anwendungsspezifisch)
44
}else{//Wenn SS == LOW --> INT0
45
GIFR|=(1<<INTF0);
46
GIMSK|=(1<<INT0);
47
}
48
}
49
50
voidinit_SPI(){
51
/*PCINT3 aktivieren und INT0 auf sinkende Flanke umstellen*/
52
PCMSK|=(1<<PCINT3);
53
MCUCR|=(1<<ISC01);
54
GIMSK|=(1<<PCIE);
55
}
56
57
intmain(void){
58
DDRB|=(1<<MISO)|(1<<PB0);
59
//PORTB |= (1 << SS);
60
init_SPI();
61
sei();//Interrupts aktivieren!
62
while(1){
63
//nix
64
}
65
}
Dabei hat sich ein Attiny13, nachdem avrdude einen content mismatch
gemeldet hat, nicht mehr beim Programmer gemeldet :-( R.I.P.
Da ich jedoch noch einen Attiny13 von einem früheren Projekt hatte,
konnte ich das ganze trotzdem noch überprüfen.
Zuletzt stellt sich mir nur noch die Frage wiso eine deaktivierung des
Timers mit
1
TCCR0B&=~(1<<CS00);
keinen anschließenden Timer-Overflow-Interrupt verhindert
(dazugehöhrige Flag wird parallel dazu deaktiviert). Erst mit
Armin W. schrieb:> Zuletzt stellt sich mir nur noch die Frage wiso eine deaktivierung des> Timers mit
1
TCCR0B&=~(1<<CS00);
> keinen anschließenden> Timer-Overflow-Interrupt verhindert (dazugehöhrige Flag wird parallel> dazu deaktiviert).> Erst mit
1
cli();
> kann ich das Verhindern.
Also mit dem Deaktivieren des Clocks stoppt der Timer und löst auch
keine INT's mehr aus. Das TOV0 von Hand löschen zu wollen ist
überflüssig, da dieses mit Eintritt in die ISR automatisch gelöscht
wird.
Im übrigen verwendet man bei diesen Registern ein einfaches Write!
1
TIFR0=(1<<TOV0);
Ein Read-Modify-Write wie bei dir würde im Zweifelsfall dazu führen, das
ein weiteres gesetztes Flag wie z.B. OCF0A dann ebenfalls gelöscht
würde!
Wie merkst du denn das die ISR weiterhin aufgerufen wird?
Sascha
Eine Debug-Led (PORTB |= (1 << LEDPIN);) leuchtet nicht (Befehl sollte
nach SPI-Transfer ausgeführt werden).
Liegt wohl daran, das ich das Register verodere.