Forum: Mikrocontroller und Digitale Elektronik AVRStudio Probleme mit C und überschriebenen Variablen


von Lars Schwanitz (Gast)


Lesenswert?

Nabend,

tja, jetzt ist es soweit und ich probiere ich mich an meinen ersten
ernsthaften C-Projekt. Bis jetzt habe ich immer mit ASM gearbeitet
und hatte nie Probleme.
Im Code den ich hier Eingefügt habe werden über Portexpander
(74HC595 für Out und 74HC165 für IN) 8 Kanäle zu je einem Byte für 
Eingang
und Ausgang realisiert. Die Kanäle werden immer paarweise angesprochen
(16Bit In/Out). Die Auswahl der Kanäle erfolgt über einen Hardware 
Decoder
(74HC139) mit der Funktion PE_Select(). Gelesen wird mit PE_In() und
geschrieben mit PE_Out(). Ich habe zum Test in main einfach Eingang auf
Ausgang kopiert.
Wenn ein Bit am Eingang gesetzt wird, wird dieses auch am Ausgang 
korrekt
gesetzt. Aber im benachbarten Kanal wird es auch gesetzt und toggelt 
dort.
(Die Bitnummer ist die gleiche.) Dieses Programm habe ich vorher
in ASM realisiert und dort funktioniert alles so wie es soll.
Ich habe so eine Ahnung das der Compiler die Variablen etwas am mixen 
ist?
Das ganze läuft auf einen MEGA16 und programmieren tue ich unter AVR 
Studio
mit WinAVR als C Compiler.

Wenn jemand eine Ahnung/Idee hat wo mein Problem liegt wäre ich für
einen Hinweis echt dankbar!

mfg

Lars


Code
------------------------------------------------------------------------

#include <avr/io.h>
#include <stdint.h>
#include "eeprom_default.h"

#define PE_Demux_A0    PD2    // Ausgang
#define PE_Demux_A1    PD3    // Ausgang
#define PE_Demux_A2    PD4    // Ausgang
#define PE_CLK       PD7    // Ausgang
#define PE_Enable     PD6    // Ausgang
#define PE_SData_Out   PD5    // Ausgang
#define PE_Port     PORTD

#define PE_SData_In    PINB4  // Eingang
#define PE_Read_Write  PB3    // Ausgang
#define PE_Modul_Enable  PB1    // Ausgang    //Achtung - Signal ist 
invertiert
#define PE_PortB     PORTB


//###################################################################### 
#########################
/*
    Funktionsdeklarationen
*/

void GPIO_INIT(void);
void PE_Select(volatile uint8_t Port);
void PE_Out(volatile uint8_t Byte);
volatile uint8_t PE_In(void);


//###################################################################### 
#########################

int main(void)
{  //start main
  GPIO_INIT();

        while(1)
  {

     PE_Select(0);
    PE_Out(PE_In());

    PE_Select(1);
    PE_Out(PE_In());

    PE_Select(2);
    PE_Out(PE_In());

    PE_Select(3);
    PE_Out(PE_In());



  }
}  //end main


//###################################################################### 
#########################
/*
    Initialisierung
*/

void GPIO_INIT(void)
{
  // Adress Port für den Adressdemultiplexer konfigurieren.
  DDRD |= (1 << DDD2 | 1 << DDD3 | 1 << DDD4);

  // Portexpander Steuerports konfigurieren.
  DDRD |= (1 << DDD5 | 1 << DDD6 | 1 << DDD7);
  DDRB |= (1 << DDB1 | 1 << DDB3);
  PE_PortB |= (1<<PE_Modul_Enable);  // IO Modul deaktivieren
}


//###################################################################### 
#########################
/*
    Portexpander Funktionen
*/


/*
Diese Funktion setzt den gewünschten Portexpander.
Die Aufrufvariable ist ein uint8_t mit den Bereich von 0-7 (andere Werte 
werden ignoriert).
Pro IO-Modul werden 2 Portexpander Adr. benutzt.
IO-Modul 0 = 0 & 1; IO-Modul 1 = 2 & 3; IO-Modul 2 = 4 & 5; IO-Modul 3 = 
6 & 7;
*/

void PE_Select(volatile uint8_t Port)
{
  switch(Port)
  {
    case 0:
    {
      PE_Port &= ~(1 << PE_Demux_A0 | 1 << PE_Demux_A1 | 1 << 
PE_Demux_A2);
      break;
    }
    case 1:
    {
      PE_Port |= (1 << PE_Demux_A0);
      PE_Port &= ~( 1 << PE_Demux_A1 | 1 << PE_Demux_A2);
      break;
    }
    case 2:
    {
      PE_Port |= (1 << PE_Demux_A1);
      PE_Port &= ~( 1 << PE_Demux_A0 | 1 << PE_Demux_A2);
      break;
    }
    case 3:
    {
      PE_Port |= (1 << PE_Demux_A0 | 1 << PE_Demux_A1);
      PE_Port &= ~(1 << PE_Demux_A2);
      break;
    }
    case 4:
    {
      PE_Port |= (1 << PE_Demux_A2);
      PE_Port &= ~( 1 << PE_Demux_A0 | 1 << PE_Demux_A1);
      break;
    }
    case 5:
    {
      PE_Port |= (1 << PE_Demux_A0 | 1 << PE_Demux_A2);
      PE_Port &= ~(1 << PE_Demux_A1);
      break;
    }
    case 6:
    {
      PE_Port |= (1 << PE_Demux_A1 | 1 << PE_Demux_A2);
      PE_Port &= ~(1 << PE_Demux_A0);
      break;
    }
    case 7:
    {
      PE_Port |= (1 << PE_Demux_A0 | 1 << PE_Demux_A1 | 1 << 
PE_Demux_A2);
      break;
    }
  }
} // PE_Select


/*
Das Ausgabewort wird mit dem MSB zuerst ausgegeben.
*/

void PE_Out(volatile uint8_t Byte_Out)
{
  volatile uint8_t i,temp = 0;
  PE_PortB &= ~(1<<PE_Read_Write);  // Schreibmodus aktivieren
  PE_PortB &= ~(1<<PE_Modul_Enable);  // IO Modul aktivieren
  for (i=0; i<=7; i++)
  {
    temp = Byte_Out & 0b10000000;
    if ( temp==0) PE_Port &= ~(1<< PE_SData_Out);
    else PE_Port |= (1<<PE_SData_Out);
    PE_Port |= (1<<PE_CLK);
    Byte_Out <<= 1;
    asm volatile ("nop");
    PE_Port &= ~(1<<PE_CLK);
  }
  PE_Port |= (1<<PE_Enable);      // Enable Impuls an
  PE_Port &= ~(1<< PE_SData_Out);   // PE_SData_Out auf 0 setzten
  asm volatile ("nop");
  PE_Port &= ~(1<<PE_Enable);      // Enable Impuls aus
  PE_PortB |= (1<<PE_Modul_Enable);  // IO Modul deaktivieren
}



volatile uint8_t PE_In(void)
{
  volatile uint8_t i,Byte_In = 0;
  PE_PortB |= (1<<PE_Read_Write);    // Lesemodus aktivieren
  PE_PortB &= ~(1<<PE_Modul_Enable);  // IO Modul aktivieren
   PE_Port |= (1<<PE_Enable);      // Enable Impuls an
  asm volatile ("nop");
  PE_Port &= ~(1<<PE_Enable);      // Enable Impuls aus
  for (i=0; i<=6; i++)
  {
    if (PINB & (1<<PE_SData_In)) { Byte_In = Byte_In | 0b00000001; }
    PE_Port |= (1<<PE_CLK);
    asm volatile ("nop");
    PE_Port &= ~(1<<PE_CLK);
    Byte_In <<= 1;
  }
  if (PINB & (1<<PE_SData_In)) { Byte_In = Byte_In | 0b00000001; }
  PE_PortB |= (1<<PE_Modul_Enable);  // IO Modul deaktivieren
  return Byte_In;
}

von Karl H. (kbuchegg)


Lesenswert?

> Ich habe so eine Ahnung das der Compiler die Variablen etwas
> am mixen ist?

Von einem kannst du mit 99.9% Sicherheit ausgehen.
Es ist kein Compilerfehler, sondern ein Fehler des
Prgrammierers.

Für die beschriebene Funktionalität ist mir allerdings dein
Programm viel zu komplex, sodass ich es nicht im Kopf durchgehen
möchte.
AVR-Studio hat aber einen guten Simulator (Debugger). Starte
dein Programm unter Simulatorkontrolle und steppe es mal
durch, dann siehst du wie die Ausgaben zustande kommen.
(Genau das Gleiche müsste ich auch tun, um dir weiter helfen
zu können. Nur hab ich den Nachteil, dass ich nicht in
deinem Projekt drinnen bin und mich erst mal orientieren
muss)

Du hast viel zu viel Code vor dem ersten Testen geschrieben.
Fang kleiner an, teste es, debugge es und mache es fehlerfrei
und erst dann wird die nächste Funktionalität ergänzt. So weiss
man immer in welchen 10 Code Zeilen (nämlich den zuletzt
hinzugefügten) ein Problem steckt und muss nicht eine größere
Menge Code analysieren um Probleme zu entdecken.

von Lars Schwanitz (Gast)


Lesenswert?

Tja, der krux ist ja, das die Funktionen einzeln funktionieren.
Ich habe ja an der Hardware direkt geschrieben und getestet.
Die PE_Out() Funktion funktioniert einwandfrei, wenn ich Ihr statische
Werte übergebe oder Werte aus einer anderen Variable. Diese vorher 
beschriebenen Symptome treten sporadisch auf sobald die PE_In() Funktion
ins Spiel kommt.
Der Umfang / Leserlichkeit des Codes kommt daher das das ganze nur die 
zusammengestückelte Runpfprogrammierung ist.

Am Anfang habe ich die PE_Out() Funktion geschrieben und getestet -> 
einwandfrei. Nur hatte ich anstatt der for noch eine while Schleife.
Dann habe ich die PE_Select() Funktion zusammen gesetzt. Hardwaremäßig
hat diese das auch getan was Sie sollte. Nur hat je nach übergeben Wert
(0 bis 7) dieses Einfluß auf die Schleifendurchläufe in der PE_Out().
Zu sehen war das über den Schiebetakt des Schieberegisters mit den Oszi.
( zB. Wert 2 -> 8 - 2 = 6 Durchläufe anstatt 8). Nachdem die die while
durch die for getauscht hatte war dieses Problem behoben. Aber wieso???

Natürlich liegen <99,xx% aller Fehler beim Programmierer. Aber dieser 
hier
wird mir nicht wirklich klar!


mfg Lars

von A.K. (Gast)


Lesenswert?

Hat nix mit dem Problem zu tun, aber mit

void PE_Select(volatile uint8_t Port)
{
    PE_Port &= ~(1<<PE_Demux_A0 | 1<<PE_Demux_A1 | 1<<PE_Demux_A2);}
    if (port & 1<<0) PE_Port |= 1<<PE_Demux_A0;
    if (port & 1<<1) PE_Port |= 1<<PE_Demux_A1;
    if (port & 1<<2) PE_Port |= 1<<PE_Demux_A2;
}

wird das Programm schon mal einen halben Meter kürzer.

Was die Funktion angeht: Bei Schaltungen ist ein Bild einer verbalen 
Beschreibung immer vorzuziehen. Lies: ich kapier's nicht, was du da wie 
verschaltet hast.

von A.K. (Gast)


Lesenswert?

Bzw.:

void PE_Select(uint8_t Port)
{
    PE_Port &= ~(1<<PE_Demux_A0 | 1<<PE_Demux_A1 | 1<<PE_Demux_A2);
    if (Port & 1<<0) PE_Port |= 1<<PE_Demux_A0;
    if (Port & 1<<1) PE_Port |= 1<<PE_Demux_A1;
    if (Port & 1<<2) PE_Port |= 1<<PE_Demux_A2;
}

denn das "volatile" ist bei einem Parameter Unfug.

von Lars Schwanitz (Gast)


Lesenswert?

@  A.K.
Dein Abkürzungsvoschlag sieht sehr vieleversprechend aus.
Werde ich morgen mal probieren. Darauf wäre ich so schnell nicht 
gekommen.

@ all
Ich werde Morgen mal eine Zeichnung machen. Das Problem kenne ich, von
anderen die Geistigen Ergüsse auf die schnelle zu verstehen ;).

Meiner Meinung nach ist das was die Funktionen im einzelnen machen, gar 
nicht so wichtig, sondern das sie sich gegenseitig beeinflussen. Obwohl 
alle
Variablen lokal in den Funktionen deklariert sind. (Und nur dort gültig 
sein
sollten!?).


Lars

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.