Hallo,
ich versuche nun schon seit mehreren Stunden einen Atmega8 (oder auch
Atmega328P) dazu zu überreden als SPI Slave zu laufen, aber es will
einfach nicht funktionieren.
Wenn ich den !SS Pin manuell auf GND lege, funktioniert die Hardware-SPI
wie gewünscht, nur eben nicht wenn sie selber den !SS Pin auswerten
soll.
Minimal Beispiel Hardware-SPI (nur Empfang):
1 | #include <avr/io.h>
|
2 | #include <avr/interrupt.h>
|
3 | #include <inttypes.h>
|
4 |
|
5 | #define SPI_SS PB2
|
6 | #define SPI_MOSI PB3
|
7 | #define SPI_MISO PB4
|
8 | #define SPI_CLK PB5
|
9 |
|
10 | volatile uint8_t BUFFER[256];
|
11 | volatile uint8_t r, w;
|
12 |
|
13 |
|
14 | void init_uart(uint32_t baud){
|
15 | uint16_t ubrr = ( (F_CPU + baud * 8) / (16 * baud) ) - 1;
|
16 | UBRRH = (uint8_t)(ubrr >> 8);
|
17 | UBRRL = (uint8_t)(ubrr & 0xFF);
|
18 | UCSRB = (1 << TXEN);
|
19 | // Asynchron 8N1
|
20 | UCSRC = (1 << URSEL) | (0 << UMSEL) | (0 << UPM1) | (0 << UPM0) | (0 << USBS) | (0 << UCSZ2) | (1 << UCSZ1) | (1 << UCSZ0);
|
21 | }
|
22 |
|
23 | static inline uint8_t send_byte(uint8_t data) {
|
24 | // Senden, wenn UDR frei ist
|
25 | if (UCSRA & (1 << UDRE)) {
|
26 | UDR = data;
|
27 | return 1;
|
28 | }
|
29 | return 0;
|
30 | }
|
31 |
|
32 | ISR(SPI_STC_vect) {
|
33 | BUFFER[w++] = SPDR;
|
34 | }
|
35 |
|
36 | int main(void) {
|
37 | init_uart(115200);
|
38 | // MISO Output, Rest Inputs (aber eigentlich egal)
|
39 | DRB = (1 << SPI_MISO);
|
40 | // SPI data output register (MISO)
|
41 | SPDR = 0x00;
|
42 | // Enable SPI Interrupt, enable SPI, MSB first, SPI Slave, CPOL 1, CPHA 1
|
43 | SPCR = (1 << SPIE) | (1 << SPE) | (0 << DORD) | (0 << MSTR) | (1 << CPOL) | (1 << CPHA);
|
44 | sei();
|
45 | while(1) {
|
46 | if ( r != w ) if ( send_byte(BUFFER[r]) ) r++;
|
47 | };
|
48 | return 0;
|
49 | }
|
Das dass SS Signal nicht richtig funktioniert kann ich ausschließen, da
eine schnell zusammengestrickte Software-SPI (auch nur Empfang)
anstandslos funktioniert:
1 | #include <avr/io.h>
|
2 | #include <avr/interrupt.h>
|
3 | #include <inttypes.h>
|
4 |
|
5 | #define SPI_SS PB2
|
6 | #define SPI_MOSI PB3
|
7 | #define SPI_MISO PB4
|
8 | #define SPI_CLK PB5
|
9 |
|
10 | volatile uint8_t BUFFER[256];
|
11 | volatile uint8_t r, w;
|
12 |
|
13 |
|
14 | void init_uart(uint32_t baud){
|
15 | uint16_t ubrr = ( (F_CPU + baud * 8) / (16 * baud) ) - 1;
|
16 | UBRRH = (uint8_t)(ubrr >> 8);
|
17 | UBRRL = (uint8_t)(ubrr & 0xFF);
|
18 | UCSRB = (1 << TXEN);
|
19 | // Asynchron 8N1
|
20 | UCSRC = (1 << URSEL) | (0 << UMSEL) | (0 << UPM1) | (0 << UPM0) | (0 << USBS) | (0 << UCSZ2) | (1 << UCSZ1) | (1 << UCSZ0);
|
21 | }
|
22 |
|
23 | static inline uint8_t send_byte(uint8_t data) {
|
24 | // Senden, wenn UDR frei ist
|
25 | if (UCSRA & (1 << UDRE)) {
|
26 | UDR = data;
|
27 | return 1;
|
28 | }
|
29 | return 0;
|
30 | }
|
31 |
|
32 | int main(void) {
|
33 | uint8_t temp;
|
34 | uint8_t data = 0;
|
35 | uint8_t bitpos = 0;
|
36 | uint8_t old_clk = 1;
|
37 | init_uart(115200);
|
38 | // MISO Output, Rest Inputs (aber eigentlich egal)
|
39 | DDRB = (1 << SPI_MISO);
|
40 | while(1) {
|
41 | temp = PINB;
|
42 | // SS high, Reset der bereits empfangenen Daten und des Bitzählers
|
43 | if ( temp & (1 << SPI_SS) ) {
|
44 | bitpos = 0;
|
45 | data = 0;
|
46 | old_clk = 1;
|
47 | if ( r != w ) if ( send_byte(BUFFER[r]) ) r++;
|
48 | }
|
49 | // SS low
|
50 | else {
|
51 | // CLK high
|
52 | if ( temp & (1 << SPI_CLK) ) {
|
53 | // steigende Flanke?
|
54 | if ( !old_clk ) {
|
55 | if ( temp & (1 << SPI_MOSI) ) data |= 0x01;
|
56 | if ( ++bitpos > 7 ) {
|
57 | bitpos = 0;
|
58 | BUFFER[w++] = data;
|
59 | data = 0;
|
60 | }
|
61 | else {
|
62 | data <<= 1;
|
63 | }
|
64 | old_clk = 1;
|
65 | }
|
66 | }
|
67 | // CLK low
|
68 | else {
|
69 | old_clk = 0;
|
70 | if ( r != w ) if ( send_byte(BUFFER[r]) ) r++;
|
71 | }
|
72 | }
|
73 | };
|
74 | return 0;
|
75 | }
|
Das Einzige was ich mir irgendwie vorstellen kann, ist das der SS vor
der 1. SPI_CLK zu spät runter oder nach der letzten Clock zu schnell
wieder hoch geht, hab dafür aber keine Information irgendwo gefunden,
wie lange er vor der ersten SPI_CLK mindestens schon auf low stehen muss
oder nach der letzten SPI_CLK noch low bleiben muss.
F_CPU ist 14.745600 MHz (Baudraten Quarz)
SPI CLK ist etwa 175 kHz
SS geht 2 SPI_CLK vor der 1. positiven SPI_CLK Flanke auf low und 2
SPI_CLK nach der letzten positiven SPI_CLK Flanke auf high.
Falls irgendwer dazu Ideen oder Informationen hat, bitte gerne.