Forum: Mikrocontroller und Digitale Elektronik ATtiny13: UART Problem mit External-Interrupt


von binaer f. (binaerfreund)


Angehängte Dateien:

Lesenswert?

Hallo!

Das Thema war zwar schon sehr oft Inhalt, aber ich komme mit meinem 
Problem einfach nicht weiter. Da ich hier schon sehr viel hilfreiche 
Tipps sammeln konnte, hoffe ich, dass mir vielleicht jemand helfen kann.

Ich versuche gerade, mich ein Bisschen mit einem ATtiny13 zu 
beschäftigen und diesem die grundlegendsten Funktionen einer 
Software-UART zu programmieren, damit ich diesem zukünftig Befehle 
senden kann, um z.B. eine AD-Wandlung zu starten. Das Ergebnis kann dann 
am Rechner weiterverarbeitet werden.

Der u-Controller ist über den internen Oszillator auf 9,6 MHz, ohne 
Vorverteiler, getaktet.

Das Programm ist aktuell noch sehr simpel und soll auf PORTB-Pin 2 ein 
Byte empfangen und dieses auf PORTB-Pin 1 wieder ausgeben.
Einfach, um UART, den u-Controller und Interrupts kennenzulernen.

Das Zeichen zum Testen sende ich mit Putty.
Hier ist das "Lokale Echo", "Local Line Editing" und die Flusskontrolle 
abgeschaltet, damit wirklich nur das eingegebene Zeichen gesendet und 
das ankommende Zeichen ausgegeben wird (ohne Carriage Return und Line 
Feed).

Soweit funktioniert das auch. Allerdings scheint mir, als wird die 
Interrupt-Routine zweimal ausgeführt, da ich zwei Zeichen zurück 
bekomme.
Einmal das zum u-Controller gesendete Zeichen "a" und einmal 255_DEC, 
also LOW auf RX.

Neben dem Quellcode habe ich einen Screenshot vom "Messergebnis" mit dem 
OpenLogicSniffer angefügt.
Der Interrupt sollte, wenn ich mich im Datenblatt nicht verlesen habe, 
bei steigender Flanke an PORTB-Pin 2 ausgelöst werden und seine Routine 
abarbeiten....wie gesagt, passiert dies leider zweimal.
Auf PIN 2 gibt es nach dem Einlesen eines Bytes keine steigende Flanke 
mehr. Normalerweise sollte die Interruptroutine in diesem Beispiel kein 
zweites Mal aufgerufen werden.
Ich gehe davon aus, dass während die ISR abgearbeitet wird, kein 
weiteres Interrupt-Ereignis eingereiht wird. Um dies zu prüfen, habe ich 
die Funktionen cli() und sei() am Anfang und Ende von ISR() aufgerufen, 
was aber zum selben Ergebnis geführt hatte...

Hat vielleicht jemand von euch eine Idee, wo das Problem liegt? 
Vielleicht habe ich etwas übersehen, missverstanden oder einen Fehler im 
Code.


main.c
1
#include <avr/io.h
2
#include <avr/iotn13.h>
3
#include <avr/interrupt.h>
4
#include "uart.h"
5
6
unsigned char c;
7
8
9
ISR( PCINT0_vect );              // Interrupt Service Routine      External Interrupt Request
10
11
12
13
int main()
14
{
15
  DDRB |= ( 1 << PB1 );
16
17
  PCMSK |= (1<<PCINT2);          // PIN Change Mask Register        External Interrupt at PINB2
18
  MCUCR |= (1<<ISC01) | (1<<ISC00);    // MCU General Control Register      Rising Edge LOW to HIGH (RS232 Start-Bit)
19
  GIMSK |= (1<<PCIE);            // General Interrupt MaSK Register      External Interrupt
20
  sei();                  // Enable Interrupt
21
22
23
  while(1)                // Main Routine
24
  {
25
    // Singe ein Lied
26
  }
27
28
  return 0;
29
}
30
31
32
ISR( PCINT0_vect )
33
{
34
  c = _uart_receive_byte();
35
  _uart_send_byte( c );
36
}

uart.h
1
#ifndef _UART_H_
2
#define _UART_H_
3
4
#include <avr/io.h>
5
#include <avr/iotn13.h>
6
#include <util/delay.h>
7
8
// Prototypen:
9
10
int _uart_send_byte(unsigned char c);
11
unsigned char _uart_receive_byte();
12
13
#endif

uart.c
1
#include "uart.h"
2
3
//Funktionen:
4
5
int _uart_send_byte(unsigned char c )
6
{
7
  uint8_t i = 0;
8
9
  PORTB |= ( 1 << PB1 );         //START Bit  (Set PortPIN HIGH)
10
  _delay_us( 104 );
11
12
  for( i = 0; i < 8; i ++)      // Choose every bit from char LSB to MSB
13
  {
14
    if( ((c >> i ) & 0x01) )    // Check if Bit is 1
15
    {
16
      PORTB &= ~(1<<PB1);      // Bit is 1, Clear Bit (Set PortPIN LOW)
17
    }
18
    else
19
    {
20
      PORTB |= (1<<PB1);      // Bit is 0, Set Bit (Set PortPIN HIGH)
21
    }
22
    _delay_us( 104 );
23
  }
24
25
  PORTB &= ~(1<<PB1);          // STOP Bit (Set PortPIN LOW)
26
  _delay_us( 104 );
27
28
  return 0;
29
}
30
31
unsigned char _uart_receive_byte()
32
{
33
  unsigned char c=0x00;
34
  uint8_t i = 0;
35
36
  _delay_us( 156 );          // Wait 1,5 Bits after rising edge of START-Bit
37
38
  for( i = 0; i < 8; i++ )
39
  {
40
    if( ((PINB >> PINB2) & 1) )
41
    {
42
      c &= ~(1<<i);
43
    }
44
    else
45
    {
46
      c |= (1<<i);
47
    }
48
    _delay_us( 104 );
49
  }
50
51
  _delay_us( 104 );          // STOP Bit
52
53
  return c;
54
}

von Hannes L. (hannes)


Lesenswert?

binaer freund schrieb:
> Ich gehe davon aus, dass während die ISR abgearbeitet wird, kein
> weiteres Interrupt-Ereignis eingereiht wird.

Das ist aber nicht (ganz) der Fall. Die nächste entsprechende Flanke 
setzt das Interrupt-Pending-Flag erneut, so dass nach Beenden der ISR 
diese erneut aufgerufen wird. Abhilfe schafft das Löschen des 
Interrupt-Pending-Flags vor Verlassen der ISR, also das Schreiben einer 
Eins in das entsprechende Bit des Registers GIFR.

...

von binaer f. (binaerfreund)


Lesenswert?

Vielen Dank für die schnelle Hilfe!

Jetzt hat sich für mich der Sinn des GIF-Registers auch erschlossen.
Damit funktioniert es wunderbar und ich habe wieder etwas dazugelernt.

Danke!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.