Forum: Mikrocontroller und Digitale Elektronik ATmega32 mit Timer1 (PD5) + restliche Ports als I/O nutzen


von Hack K. (hackerfleisch)


Lesenswert?

Hallo erstmal zusammen.
Ich recheriere hier schon länger im Board und bin sehr zufrieden mit der 
Kompetenz einiger User (sehr gutes Wissen), was mir schon viel in der 
Vergangenheit geholfen hatte, dafür Danke!

Jedoch stehe ich jetzt vor einem Problem:

Ich habe hier einen ATMega32 + 16MHz Quarz. -> AVR Studio 5 ("C").

Ich benutze den Timer1 für eine "PPM" (Ich benötige eine variable 
Frequenz am PD5 mit fester positiven Pulsweite [26µs]).
Das tut auch ganz gut, gesteuert wird dies via RS232.

An den restlichen Pins des ATMega32 (ausser PD4) müssen jetzt I/O's 
angeschlossen werden.
Darunter fällt u.a. auch SRAM etc.
Sollte alles kein Problem sein, jedoch stoße ich hier auf einen "Show 
Stopper".

Sobald ich den Timer1 initialisiere + starte, sind die restlichen 
doppelt belegten PIN vom µC fest für Timer vergeben.
Z.b. der Pin PD7(OC2).
Gibt es eine Möglichkeit den Timer1 zu nutzen UND alle restlichen Pins 
(ausser PD4 & 5) als Ein und Ausgänge zu nutzen?

So bald ich den Timer1 ausschalte (Prescaler auf 0) kann ich z.B. den 
Pin PD7 "High" oder "Low" setzen, jedoch nicht wenn der Timer1 läuft.

Ich wäre um jeden Tipp sehr dankbar!

Danke und Gruß

von spess53 (Gast)


Lesenswert?

Hi

>Sobald ich den Timer1 initialisiere + starte, sind die restlichen
>doppelt belegten PIN vom µC fest für Timer vergeben.
>Z.b. der Pin PD7(OC2).

Da machst du etwas falsch. Zeig mal deine Initialisierungen.

MfG Spess

von Thomas E. (thomase)


Lesenswert?

Thomas D. schrieb:
> Ich wäre um jeden Tipp sehr dankbar!
PD7 hat mit Timer1 nicht das geringste zu tun.
Richtiger Controller eingestellt?

mfg.

von Hack K. (hackerfleisch)


Lesenswert?

Hi zusammen!
Das ging ja flott! :)

Genau das wundert mich ja, warum, wenn ich nur den Timer1 verwende, Pins 
vom Timer2 sich verstellen.

Anbei mal der C-Code.
Ich verwende den Timer1 um eine "PPM" zu erzeugen. Ich muss eine 
variable Frequenz (~1KHz - 16KHz) erzeugen, jedoch mit einer 
gleichbleibenden Pulsbreite (~26µs). Zudem sollte die Frequenz sehr fein 
änderbar sein (daher 16Bit Timer).

Ich lasse den Timer im "normal Mode" laufen und schalte den direkten 
Ausgang vom OC1A und OC1B auf PD5 & PD4 ab. Timer Top = 0xFFFF
Interrupt gesteuert.
Ich lasse die beiden 16Bit Register OCR1A & OCR1B mit TCNT1 vergleichen, 
wenn "compare", dann löst es einen Interrupt aus. Dort wird dann ur der 
PD5 einmal ein und dann wieder ausgeschaltet.
Wenn ich jetzt zu Testzwecken über RS232 die vorgeladenen Register 
verändere, kann ich mit OCR1A die Frequenz und OCR1B die Pulsbreite 
einstellen.

Im einschaltzustand wird am PD5 eine Frequenz von ~14KHz und einer 
Pulsbreite von ~26µs erzeugt.

Wenn ich jetzt in der While Schleife "PORTD = (1 << PD7);" oder "PORTD = 
(0 << PD7);" schalte, passiert nix, bleibt auf "LOW".

Kann sein, das die Kommentare nicht mehr ganz passen, habe seit dem 
Fehler viel probiert.

Vielen Dank schonmal!!
Gruß


1
#include <avr/io.h>
2
#include <stdlib.h>
3
#include <avr/interrupt.h>
4
#include <avr/iom32.h>
5
#include <stdio.h>
6
#include <util/delay.h>
7
8
#define F_CPU 16000000
9
#define UART_BAUD 19200
10
#define MaxPuffer 2 //Maximaler Ringpuffer für USART (100 Zeilen á 8 Bit breit)
11
12
uint8_t puffer[MaxPuffer];
13
uint8_t* read_ptr;
14
uint8_t* write_ptr;
15
uint8_t USART_Byte;
16
17
18
void controler_init() 
19
{
20
//Zu Testzwecken Port A, B, C und D als Ausgang und alle Ausgänge auf LOW
21
   DDRA = 0xFF;
22
    PORTA |= (0 << PA0) | (0 << PA1) | (0 << PA2) | (0 << PA3) | (0 << PA4) | (0 << PA5) | (0 << PA6) | (0 << PA7);
23
   DDRB = 0xFF;
24
    PORTB |= (0 << PB0) | (0 << PB1) | (0 << PB2) | (0 << PB3) | (0 << PB4) | (0 << PB5) | (0 << PB6) | (0 << PB7);
25
   DDRC = 0xFF;
26
    PORTC |= (0 << PC0) | (0 << PC1) | (0 << PC2) | (0 << PC3) | (0 << PC4) | (0 << PC5) | (0 << PC6) | (0 << PC7);
27
   DDRD = 0xFF;
28
    PORTD |= (0 << PD0) | (0 << PD1) | (0 << PD2) | (0 << PD3) | (0 << PD4) | (0 << PD5) | (0 << PD6) | (0 << PD7);
29
}
30
31
void usart_init(void)
32
{
33
  read_ptr = puffer;
34
  write_ptr = puffer;
35
  UBRRH  = 0;             // Highbyte ist 0
36
  UBRRL = (F_CPU / (16UL * UART_BAUD)) - 1;
37
  UCSRB |= (1 << RXCIE)|( 1 << TXEN )|( 1 << RXEN);          // UART TX einschalten
38
  UCSRC |= ( 1 << URSEL )|( 1<<UCSZ0 )|( 1<<UCSZ1 )|( 0<<UCSZ2 );  // Asynchron 8N1
39
}
40
41
void timer1_init(void)
42
{
43
  //Timer1 (16 Bit) Mode 0
44
  TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) | (0 << FOC1A) | (0 << FOC1B) | (1 << WGM11) | (1 << WGM10); //Normal port operation, OC1A/OC1B disconnected; Normal Timer Operation TOP = 0xFFFF
45
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10); //WGM13&WGM12&WGM11&WGM10 = Waveform Mode 0; CS12&CS11&CS10 = Timer On, Prescale = 8
46
  TIMSK = (1 << OCIE1A) | (1 << OCIE1B);
47
48
  //OCR1A = 0 -> 16MHz; OCR1A = 65535 -> 61Hz @ toggle the output | fPCPWM = F_CPU/(2*N*TOP) [N = Prescale, TOP = OCR1A Register] i.e. -> 16MHz/(2*1*65535) = 122Hz (122Hz High, 122Hz Low @ Toggle Pin => 61Hz)
49
  //See Datasheet Page 100
50
  //Minimum allowed is OCR1A = 0x0003 (2 Bit), Maximum is OCR1A = 0xFFFF (16 Bit)
51
52
  OCR1A = 141; //or OCR1A = 0xFFFF;
53
  OCR1B = 52;
54
}
55
56
57
58
void uart_write(USART_Byte);
59
int USART_get_Byte(void);
60
61
62
int main(void)
63
{
64
  controler_init();
65
  usart_init();
66
  timer1_init();  
67
  sei();
68
;    while(1)
69
    {
70
    _delay_us(1);
71
    if (write_ptr != read_ptr)
72
    {
73
      USART_Byte = USART_get_Byte(); //Neue Daten holen
74
      UDR = USART_Byte;
75
      //uart_write(test);
76
      //UDR = USART_Byte; //Echo schicken
77
      //USART_Echo(USART_Byte);
78
      
79
      if (USART_Byte == '7') OCR1A++;
80
      if (USART_Byte == '4') OCR1A--;
81
      
82
      if (USART_Byte == '9') OCR1B++;
83
      if (USART_Byte == '6') OCR1B--;
84
85
        }      
86
    }
87
}
88
89
90
int USART_get_Byte(void)
91
{
92
  uint8_t Byte = 0;
93
94
  Byte = *read_ptr;
95
  read_ptr++;
96
  if (read_ptr == puffer+MaxPuffer) read_ptr = puffer;
97
98
  return Byte;
99
}
100
101
void uart_putchar(char c)
102
{  
103
  while (!(UCSRA & (1 << UDRE))); // wait for buffer ready
104
  UDR = c;
105
}
106
107
void uart_write(char *str)
108
{  
109
  while(*str) {uart_putchar(*str); str++;}
110
}
111
112
// void USART_Echo(int Byte)
113
// {
114
//   UDR = Byte; //Echo schicken
115
//   while (!(UCSRA & (1<<UDRE))); //Warte bis bereit
116
//   if (Byte == 13) UDR = 10; //Wenn "CR" geschickt wurde dann sende "LF"
117
// }
118
119
120
121
ISR(TIMER1_COMPA_vect)
122
{
123
  PORTD = (1 << PD5);
124
}
125
126
ISR(TIMER1_COMPB_vect)
127
{
128
  PORTD = (0 << PD5);
129
}
130
131
ISR(USART_RXC_vect)
132
{  
133
  *write_ptr = UDR;
134
  write_ptr++;
135
  if (write_ptr == puffer + MaxPuffer) write_ptr = puffer;
136
}

von Stefan E. (sternst)


Lesenswert?

1
ISR(TIMER1_COMPA_vect)
2
{
3
  PORTD = (1 << PD5);
4
}
5
6
ISR(TIMER1_COMPB_vect)
7
{
8
  PORTD = (0 << PD5);
9
}
Scherzkeks. Du selbst bist doch derjenige, der alle Pins von Port D 
laufend "überbügelt".

von Uwe (Gast)


Lesenswert?

> PORTD = (1 << PD5);
Da kannst du gleich schreiben PORTD = 0b00100000.
Also alle anderen Pins außer PD5 werden auf 0V gesetzt.

Und hier:
>  PORTD = (0 << PD5);
PORTD = 0b00000000

von Peter D. (peda)


Lesenswert?

Thomas D. schrieb:
> PORTA |= (0 << PA0) | (0 << PA1) | (0 << PA2) | (0 << PA3) | (0 << PA4) | (0 << 
PA5) | (0 << PA6) | (0 << PA7);

Scherzkeks, ein NOP geht auch einfacher.

Geh an die Tafel und schreibe 100 mal:
Ich lerne erst, was |= bedeutet, ehe ich es verwende!

Peter

von Hack K. (hackerfleisch)


Lesenswert?

OMG!

Du hast recht!
Ich sollte wohl eher
1
ISR(TIMER1_COMPA_vect)
2
{
3
  PORTD |= (1 << PD5);
4
}
5
6
ISR(TIMER1_COMPB_vect)
7
{
8
  PORTD &= ~(1 << PD5);
9
}

schreiben, oder? :)
Manchmal sieht man vor lauter Bits das Byte nicht mehr...
Ok, sorry fürs stören.
Die Initialisierung sollte jedoch passen oder?
Ich muss an einer bestehenden PCB anknüpfen, dort sind alle Pins belegt 
(Port C = Datenbus von SRAM, Port A & B = Adressbus SRAM, Port D = 
Timer, 2 Bits für einen Multiplexer [für die umschaltung von den insg. 4 
SRAMs]) etc.

Eine Frage hätte ich jedoch noch:
Warum "muss" ich in der While schleife ein delay von 1 µs (oder weniger 
haben) damit die Daten von RS232 erkannt werden? Ich prüfe ja bei jedem 
Durchgang ob der lesepupperPointer != schreibepufferPointer ist, wenn ja 
= Datan vorhanden, wenn nicht, keine Daten vorhanden.

Das gleiche wenn ich stings senden will, wird wohl in der func 
"uart_write" der Pointer überlaufen:
wenn ich ein String sende z.B. "Test" kommt der am Terminal an, wenn ich 
jetzt nur ein Zeichen sende, kommt "Müll"+das Zeichen an. Leeren von der 
Variable str in der func "uart_write" hat leider ncihts gebracht.

Gruß
Thomas

von Hack K. (hackerfleisch)


Lesenswert?

@peda
Lese mal mein Kommentar im ersten Code Beispiel. Da ich dem Fehler nicht 
auf die schliche kam, probiert mal auch dinge die eigentlich einfacher 
tun.

Ich werde mich natürlich gleich an die Strafarbeit setzen... :)

von Peter D. (peda)


Lesenswert?

Thomas D. schrieb:
> Da ich dem Fehler nicht
> auf die schliche kam, probiert mal auch dinge die eigentlich einfacher
> tun.

Nö, Du probierst Dinge, die garnichts tun.
"x |= 0;" ist Code ohne Effekt.
Sowas macht man nicht, erst recht nicht, um Fehler zu suchen.


Peter

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas D. schrieb:
> Eine Frage hätte ich jedoch noch:
> Warum "muss" ich in der While schleife ein delay von 1 µs (oder weniger
> haben) damit die Daten von RS232 erkannt werden? Ich prüfe ja bei jedem
> Durchgang ob der lesepupperPointer != schreibepufferPointer ist, wenn ja
> = Datan vorhanden, wenn nicht, keine Daten vorhanden.

Da sehe ich direkt 2 Fehler:

1. Die globalen Variablen, die Du in der ISR änderst, müssen volatile
   deklariert werden, damit Du sie in main() benutzen darfst.

2. Die Variable write_ptr besteht aus jeweils 2 Bytes.
   Du musst in main() gewährleisten, dass der Zugriff atomar ist und
   nicht durch die ISR, welche zwischendurch aufgerufen werden könnte,
   geändert werden kann.

Dein Delay versteckt nur den Fehler.

Ich schlage vor, statt Pointer Indices zu verwenden. Dann kommst Du mit 
1 Byte pro index aus (read_idx & write_idx) und kannst Punkt 2 abhaken. 
Ausserdem geht der Zugriff auf Deinen Buffer über Indices im allgemeinen 
flotter als mit Pointern - jedenfalls auf einem µC.

von Hack K. (hackerfleisch)


Lesenswert?

Ich weis, jedoch hatte ich mir alle Bits von den Ports einzeln 
aufgeschrieben, um jeweils ein Bit zu ändern wenns nötig ist (also 
einfach am entsprechenden Bit eine 1 statt der 0 eintragen).
Aber wurst, ich habs ja verstanden was Du meintest.

Hat einer eine Idee mit dem delay in der while, warum das dann erst tut? 
Würde gerne komplett ohne delay arbeiten.

Gruß

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas D. schrieb:
> Hat einer eine Idee mit dem delay in der while, warum das dann erst tut?

Siehe mein Posting über Deinem.

von Hack K. (hackerfleisch)


Lesenswert?

@UKW

Danke für Deine Antwort!!
Klingt logisch was Du geschrieben hast. Ich werde mich mal in Indices 
einlesen, bin da noch grün hinter den Ohren.

Zwecks den variablen und volatile.
Ich dachte ich brauche das nur, wenn ich eine Variable im Interrupt UND 
in der Main ändere.
Hier tue ich das ja nicht, ich frage höchsten die beiden pointer auf 
ungleichheut ab, oder irre ich mich damit?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas D. schrieb:
> Klingt logisch was Du geschrieben hast. Ich werde mich mal in Indices
> einlesen, bin da noch grün hinter den Ohren.

Komisch, eigentlich lernt man erst den Umgang mit buffer[idx] statt mit 
Pointern ;-)

> Zwecks den variablen und volatile.
> Ich dachte ich brauche das nur, wenn ich eine Variable im Interrupt UND
> in der Main ändere.

Nein, auch wenn Du lesend drauf zugreifst. Der Compiler könnte sonst 
write_ptr in ein Register(-Paar) packen, wenn er sieht, dass main() 
niemals beendet wird und keine Unterfunktion aufgerufen wird, die diese 
Variable ändert. Die ISR() wird ja nirgends explizit aufgerufen.

> Hier tue ich das ja nicht, ich frage höchsten die beiden pointer auf
> ungleichheut ab, oder irre ich mich damit?

read_ptr wird nicht in der ISR verwendet, ist daher kein Problem. 
write_ptr aber schon.

Benutze aber besser:
1
uint8_t read_idx;
2
volatile uint8_t write_idx;
3
...
4
ISR(USART_RXC_vect)
5
{  
6
  puffer[write_idx] = UDR;
7
  write_idx++;
8
  if (write_idx == MaxPuffer) write_idx = 0;
9
}

Rest analog.

MaxPuffer würde ich als MAX_PUFFER schreiben - wie es sich für eine 
Preprocessor-Konstante gehört. Ausserdem ist sie mit dem Wert 2 
unterirdisch klein ;-)

von Hack K. (hackerfleisch)


Lesenswert?

Hi UKW!

Danke für Deine Hilfe!
Ich werde mir morgen mal das ganze anschauen und dann posten wie es 
lief. :)

Immer toll wenn man ein Forum hat indem es Leute gibt die einem weiter 
helfen, ich will ja kein code kopieren der komplett fertig ist o.ä. ich 
will das lernen um selber damit arbeiten zu können.

Werde mir auch morgen mal Dein Beispiel dazu anschauen und versuchen das 
zu verwenden.

Zwecks dem MAX_PUFFER und dem inhalt. Der lag eigentlich bei 100, habe 
ihn vergessen wieder auf 100 zu setzen. Aber danke, hätte ich glatt 
übersehen. :)

Gruß
Thomas

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Was mir noch auffällt:
1
      USART_Byte = USART_get_Byte(); //Neue Daten holen
2
      UDR = USART_Byte;

Die zweite Zeile ist hoffentlich nur ein Test-Echo??? Denn hier 
schreibst Du in das Senderegister ohne Rücksicht auf Verluste. Wenn 
USART_Byte tatsächlich gesendet werden soll, dann benutze bitte Deine(!) 
Funktion uart_putchar(). Die ist dafür gedacht: Sie wartet, bis das 
Senderegister frei ist und beschreibt erst dann UDR.

So, wie Du das oben machst, geht das in die Hose.

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.