Forum: Mikrocontroller und Digitale Elektronik Für Experten: Durch den Einsatz von UART werden Variablen außerhalb der Hauptschleife manipuliert?


von Askan Simon (Gast)


Lesenswert?

Hallo Zusammen,

ich habe jetzt ein ganz interessantes Problem, das eigentlich 
unglaublich aber auch reproduzierbar ist.

Ich habe eine Gyro Werte den ich über Uart ausgeben möchte.
Für das Probgramm brauche ich noch einen Backup-Gyro Wert
den ich, gleich beim Start vor der Hauptschleife definiere.

Jetzt das unglaubliche, start ich die Uart Funktion Putchars
ändert sich der Backup Wert. Es sieht so aus aus würde dieser
immer wieder aufs neue, mit dem aktuellen Gyro-Wert überschrieben
werden.... (????)

Kommtiere ich die Putchars() aus, ändert sich der Wert nicht.

Ist das ein Compilerfehler?

Ich habe jetzt mal das Probramm soweit abgespeckt das man
das Programm hoffentlich leichter verstehen kann.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "lcd_routines.h"
4
#include <stdio.h>
5
#include <stdint.h>
6
#include <stdlib.h>
7
#include <util/delay.h>
8
9
// Uart
10
#define F_CPU 16000000UL
11
#define BAUD 9600UL          // Baudrate
12
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
13
14
// clear ADC enable & ADC Start Conversion & ADC Interrupt Enable bit
15
#define ADC_Disable() (ADCSRA &= ~((1<<ADEN)|(1<<ADSC)|(1<<ADIE)))
16
#define ADC_Enable()  (ADCSRA |=   (1<<ADEN)|(1<<ADSC)|(1<<ADIE))
17
18
19
volatile int sensor_wert = 0;
20
int sensor_wert_backup = 0;
21
22
volatile int32_t i = 0;
23
24
// ADC Interrupt
25
ISR (ADC_vect) {
26
27
  ADC_Disable();
28
29
  sensor_wert = ADC
30
  
31
  ADC_Enable();
32
}
33
34
// Uart Funktionen
35
int put_char (char c) {
36
  while(!(UCSR0A & (1 << UDRE))) {
37
  }
38
  //loop_until_bit_is_set(UCSR0A, UDRE);
39
  UDR0 = c;
40
  return (0);
41
}
42
void put_chars (char *s) {
43
  while(*s) {
44
    put_char(*s);
45
    s++;
46
  }
47
}
48
void put_number(int32_t variable) {
49
   char Buffer[20];
50
   itoa(variable, Buffer, 10);
51
   put_chars(Buffer);
52
}
53
54
int main(void) {
55
56
  // Uart Initalisieren
57
  UCSR0B  = (1 << TXEN) | (1 << RXEN);
58
  UCSR0B |= (1<<RXCIE); // Interrupt freigeben
59
  UCSR0B |= (1<<TXCIE);
60
  UBRR0L  = UBRR_VAL; // Teiler setzen
61
62
  // ADC initialization
63
  ADMUX  = 0x00 & 0xff;
64
  ADCSRA = 0x8F;
65
66
  PORTF = 0x00; // Sensor auf Port
67
  DDRF  = 0x00;
68
69
  sei();
70
  lcd_init();
71
72
  // ACD Start
73
  ADMUX   = ADMUX | 1;
74
  ADCSRA |= 0x40; // ADSC = 1
75
76
  sensor_wert_backup = sensor_wert; // Variable abspeichern
77
  
78
  while (1) {
79
80
    // Debug
81
    i++;
82
    if (i > 250) { 
83
      i = 0;
84
85
      // LCD Ausgabe
86
      lcd_clear();
87
      set_cursor(0,1);
88
      lcd_number(sensor_wert_backup); // Zur Kontrolle...
89
    
90
      // Uart Ausgabe
91
      put_number(0);  // Sobald diese auskommentiert bleibt
92
                      // die Variable sensor_wert_backup
93
                      // unverändert
94
    }
95
    }
96
}

Ich arbeite mit einem Atmega64, compiliere mit WinAVR.

Ich sitzt seit heut Nachmittag an dem Problem...

Über Hilfe würde ich mich sehr freuen.

Frohe Weihnachten, vielen Dank
Viele Grüße
Askan

von Der Unbekannte #2 (Gast)


Lesenswert?

Dein Programm resetet, sobald das erste Zeichen gesendet wird, weil Du 
keine Interrupt-Routine für das UART-Senden deklariert hast, aber 
dennoch die RX/TX-Interrupts einschaltest.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Keine ISRs für RX und TX Interrupts.

von Askan Simon (Gast)


Lesenswert?

Hi,

vielen Dank für Eure Hilfe. !!!
Wär ich selbst nie drauf gekommen.

Viele Grüße aus Neuss
Askan

von Askan Simon (Gast)


Lesenswert?

Hallo,

da im Tutorial nich so viel steht, reicht dann einfach
1
ISR (USART_RXC_vect) {
2
}
3
ISR (USART_TXC_vect) {
4
}

oder muß der Uart grundsätzlich anders aufgebaut werden,
1
ISR(USART_RXC_vect) {
2
  PORTB=0xF0;
3
  register unsigned char akku;
4
  akku = UDR;
5
  while (!(UCSRA & (1<<UDRE)));     // warten bis Senden moeglich
6
  UDR = akku;                          // sende Zeichen
7
}

Ich habe diesen Teil ja schon in den put_char() Funktionen
gelößt.

Kenn mich hier leider nicht so gut aus.

Vielen Dank & Viele Grüße
Askan

von 6789 (Gast)


Lesenswert?

Nein, der Interrupt kommt wenn der puffer leer, resp voll ist, da muss 
man dann nicht mehr warten. DSondern gleich das zeichen in das UART 
schieben, resp ausm UART lesen. Speziell aufpassen muss man am Ende des 
zu sendenden Blockes, dann kommt der Interrupt, man schiebt allerdicngs 
kein neues Zeichen rein. Dann muss man gemaess datenblatt abbrechen.

von crazy horse (Gast)


Lesenswert?

Interrupt-Betrieb ist (fast) immer vorzuziehen:

/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.6 Standard
Chip type           : ATmega32
Program type        : Application
Clock frequency     : 16,000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 512
*****************************************************/

#include <mega32.h>

#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

// USART Receiver buffer
#define RX_BUFFER_SIZE 16
char rx_buffer[RX_BUFFER_SIZE];

#if RX_BUFFER_SIZE<256
unsigned char rx_wr_index,rx_rd_index,rx_counter;
#else
unsigned int rx_wr_index,rx_rd_index,rx_counter;
#endif

// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow;

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   rx_buffer[rx_wr_index]=data;
   if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
   if (++rx_counter == RX_BUFFER_SIZE)
      {
      rx_counter=0;
      rx_buffer_overflow=1;
      };
   };
}

#ifndef DEBUG_TERMINAL_IO
// Get a character from the USART Receiver buffer
#define ALTERNATE_GETCHAR
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index];
if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif

// USART Transmitter buffer
#define TX_BUFFER_SIZE 16
char tx_buffer[TX_BUFFER_SIZE];

#if TX_BUFFER_SIZE<256
unsigned char tx_wr_index,tx_rd_index,tx_counter;
#else
unsigned int tx_wr_index,tx_rd_index,tx_counter;
#endif

// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
if (tx_counter)
   {
   --tx_counter;
   UDR=tx_buffer[tx_rd_index];
   if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;
   };
}

#ifndef DEBUG_TERMINAL_IO
// Write a character to the USART Transmitter buffer
#define ALTERNATE_PUTCHAR
#pragma used+
void putchar(char c)
{
while (tx_counter == TX_BUFFER_SIZE);
#asm("cli")
if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
   {
   tx_buffer[tx_wr_index]=c;
   if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;
   ++tx_counter;
   }
else
   UDR=c;
#asm("sei")
}
#pragma used-
#endif

// Standard Input/Output functions
#include <stdio.h>


// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600
UCSRA=0x00;
UCSRB=0xD8;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x67;



// Global enable interrupts
#asm("sei")

while (1)
      {
      // Place your code here

      };
}

von Sven P. (Gast)


Lesenswert?

Askan S. wrote:
> Hallo,
>
> da im Tutorial nich so viel steht, reicht dann einfach
Ist schon ok so.

> Ich habe diesen Teil ja schon in den put_char() Funktionen
> gelößt.
Eben. Wenn du das Versenden einfach in einer Schleife machst, dann 
schalte doch den TX-Interrupt einfach ab, den brauchste doch dann 
garnicht -- schließlich prüfst du ja schon in der Schleife, ob der UART 
bereit ist. Und das Interruptflag, das du da prüfst, wird auch gesetzt, 
wenn der Interrupt nicht freigegeben ist.

Und Interrupts sind nicht generell immer vorzuziehen. Im Hauptprogramm 
ists einfach unnötig, die Senderoutine nochmal mit Interrupts 
aufzubrechen, solange du sowieso nix Andres zu tun hast.
Ansonsten rumpuffern, wie der wilde Gaul vorgeschlagen hat :-)

von Askan Simon (Gast)


Lesenswert?

Hi,

vielen Dank für die Hilfe. Ich denke ich komme jetzt, auch vielen
Dank für das Beispiel, hatte hier schon lange gesucht.

Frohe Weihnachten, Viele Grüße
Askan

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.