Hallo allerseits!
Es geht um die im Bild dargestellte Schaltung, wobei ich nur den Teil
darstelle, der meiner Meinung nach relevant ist.
Der Mikrocontroller (Atmega8L) soll über SPI sowohl mit einem EEPROM als
auch mit dem Display EA DOGS102 kommunizieren. Dazu wird das jeweilige
Bauteil per CS-Leitung aktiviert. Solange ich nur mit dem Display
"spreche", funkioniert das ganze auch problemlos. Kommt allerdings ein
einzelner Zugriff auf das EEPROM hinzu, dann werden keine Informationen
mehr auf dem Display dargestellt.
Nun der Reihe nach:
1 | int main(void)
|
2 | {
|
3 | spi_init();
|
4 | EEPROM_INIT();
|
5 | EEPROM_READ_DATA(0);
|
6 | LCD_Init();
|
7 | zeile=7;
|
8 | spalte=16;
|
9 | LCD_Clear();
|
10 | write_zeichen8('T');
|
11 | write_zeichen8('e');
|
12 | write_zeichen8('s');
|
13 | write_zeichen8('t');
|
14 | while(1) asm volatile("nop");
|
15 | }
|
Ich mache es mal ganz ausführlich. Zunächst wird das SPI initialisiert:
1 | void spi_init(void)
|
2 | {
|
3 | //SPI Data Direction Register
|
4 | DDR_SPI |= (1<<MOSI);
|
5 | DDR_SPI |= (1<<SCK);
|
6 | DDR_SPI &=~(1<<MISO);
|
7 |
|
8 | //SPI Control Register
|
9 | SPCR |= (1<<SPE);
|
10 | SPCR |= (1<<MSTR);
|
11 | SPCR |= (1<<CPOL);
|
12 | SPCR |= (1<<CPHA);
|
13 | SPCR &=~ (1<<DORD);
|
14 | SPCR &=~ (1<<SPR1);
|
15 | SPCR &=~ (1<<SPR0);
|
16 |
|
17 | //SPI Status Register
|
18 | SPSR |= (1<<SPI2X);
|
19 | }
|
Nachfolgend kann das EEPROM initialisiert (lediglich deselektiert)
werden (man kann die Funktion mit der SPI-Init tauschen, das macht
keinen Unterschied):
1 | void EEPROM_INIT(void)
|
2 | {
|
3 | DDR_EEPROM |= (1<<CS_EEPROM); //Ausgang
|
4 | PORT_EEPROM |= (1<<CS_EEPROM); //deselect
|
5 | }
|
Nun soll ein Zugriff darauf erfolgen, dies geschieht mit den folgenden
Funktionen:
1 | uint8_t EEPROM_READ_DATA(uint16_t adresse)
|
2 | {
|
3 | EEPROM_check_busy();
|
4 |
|
5 | PORT_EEPROM &=~ (1<<CS_EEPROM); //select
|
6 | EEPROM_RW(READ);
|
7 | EEPROM_RW((uint8_t)(adresse>>8));
|
8 | EEPROM_RW((uint8_t)(adresse&0b0000000011111111));
|
9 | uint8_t ret_val=EEPROM_RW(0);
|
10 | PORT_EEPROM |= (1<<CS_EEPROM); //deselect
|
11 | return ret_val;
|
12 | }
|
13 |
|
14 | void EEPROM_check_busy(void)
|
15 | {
|
16 | while (READ_SREG()&0b00000001) _delay_ms(1);
|
17 | }
|
18 |
|
19 | uint8_t EEPROM_RW(uint8_t data)
|
20 | {
|
21 | SPI_SCK_IDLE_LOW();
|
22 | asm volatile("nop");
|
23 | uint8_t ret_val=spi_send_and_receive(data);
|
24 | asm volatile("nop");
|
25 | return ret_val;
|
26 | }
|
27 |
|
28 | uint8_t READ_SREG(void)
|
29 | {
|
30 | PORT_EEPROM &=~ (1<<CS_EEPROM); //select
|
31 | EEPROM_RW(RDSR);
|
32 | uint8_t ret_val=EEPROM_RW(0);
|
33 | PORT_EEPROM |= (1<<CS_EEPROM); //deselect
|
34 | return ret_val;
|
35 | }
|
36 |
|
37 | inline void SPI_SCK_IDLE_LOW(void)
|
38 | {
|
39 | SPCR &=~ ((1<<CPOL)|(1<<CPHA));
|
40 | }
|
41 |
|
42 | char spi_send_and_receive (char data)
|
43 | {
|
44 | SPDR = data;
|
45 | while (!(SPSR & (1<<SPIF)));
|
46 | data = SPDR;
|
47 | return data;
|
48 | }
|
Jetzt folgt der Zugriff auf das Display. Hierzu wird es zuerst
initialisiert:
1 | void LCD_Init(void)
|
2 | {
|
3 | EA_DDR|=(1<<EA_CS)|(1<<EA_CD)|(1<<EA_RST);
|
4 | EA_PORT|=(1<<EA_CS);
|
5 | //Reset
|
6 | EA_PORT&=~(1<<EA_RST);
|
7 | _delay_ms(1);
|
8 | EA_PORT|=(1<<EA_RST);
|
9 | _delay_ms(5);
|
10 |
|
11 | //LCD-Init
|
12 | write_command(0b01000000); //Display start Line 0
|
13 | write_command(0b10100001); //SEG
|
14 | write_command(0b11000000); //COM0-63
|
15 | write_command(0b10100100); //Set all Pixels to on
|
16 | write_command(0b10100110); //Display normal/inverse off
|
17 | write_command(0b10100010); //1/9 (1/65)
|
18 | write_command(0b00101111); //Booster
|
19 | write_command(0b00100111); //Set VLCD Resistor Ratio
|
20 | write_command(0b10000001); //Contrast
|
21 | write_command(0b00010000); //Contrast
|
22 | write_command(0b11111010); //Temperatur Compensation
|
23 | write_command(0b10010000); //Temperatur Compensation
|
24 | write_command(0b10101111); //Display on
|
25 | //Ende Init.
|
26 | }
|
27 |
|
28 | void write_command(uint8_t cmd)
|
29 | {
|
30 | SPI_SCK_IDLE_HIGH();
|
31 | EA_PORT&=~(1<<EA_CS);
|
32 | EA_PORT&=~(1<<EA_CD); //Command
|
33 | spi_send_and_receive(cmd);
|
34 | EA_PORT|=(1<<EA_CS);
|
35 | }
|
Ich erspare mir an dieser Stelle die genauen Funktionen zum designen der
Schrift. Letztendlich läuft es auch dort auf diese Funktion hinaus:
1 | void write_data(uint8_t data)
|
2 | {
|
3 | SPI_SCK_IDLE_HIGH();
|
4 | EA_PORT&=~(1<<EA_CS);
|
5 | EA_PORT|=(1<<EA_CD); //Data
|
6 | spi_send_and_receive(data);
|
7 | EA_PORT|=(1<<EA_CS);
|
8 | }
|
Wie gesagt, lässt man den Zugriff auf das EEPROM sein, so funktioniert
die ganze Sache sehr gut. Dazu braucht man nur
auskommentieren.
Ich will natürlich den Zugriff auf das EEPROM ermöglichen. Ich komme
jedoch in der Fehlersuche nicht mehr weiter. Ich kann am Oszi keine
Überschneidung des CS-Signals erkennen (wäre ja denkbar). Ebenfall
scheint das Programm sich nicht in der EEPROM-Funktion aufzhängen, es
erscheinen erwartungsgemäß 6 SPI-Zugriffe und dann nach ca. 6 ms die
Zugriffe auf das Display.
Interessanterweise kommt es zu einem zweiten Reset der Schaltung nach
etwa 150 ms, wenn man den Programmierstecker dran lässt und einen
elektrischen Reset durchführt (auch in der funktionstüchtigen
Programmvariante).
Vielleicht hat jemand von euch ja mal eine ähnliche Schaltung aufgebaut
und auch das Problem gehabt und gelöst, bzw kann mir anderweitig helfen?
Ich bedanke mich schon mal für konstruktive Antworten!