Forum: Compiler & IDEs Output Pins festlegen und verwenden (Anfänger)


von Kaan A. (deranfaenger)


Lesenswert?

Hi Leute,

vor ab will ich sagen, dass ich schon etwas herumgegooglet habe und es 
nach meinem jetzigen Stand der Verständnis so funktionieren müsste.

Folgendes Problem:

Ich muss einen Mikrocontroller programmieren, der je nach 
Eingangssignalen PWM-Signale erzeugt und diese an bestimmten Pins 
ausgibt.
Die Ausgangspins habe ich global in einem Array definiert sodass ich 
einfachen zugriff drauf habe:

static const unsigned char *OUTPUT_ARRAY[] = {
  (unsigned char *) 0x05, (unsigned char *) 6, // PB6
  (unsigned char *) 0x05, (unsigned char *) 7, // PB7
  (unsigned char *) 0x0B, (unsigned char *) 5, // PD5
  (unsigned char *) 0x0B, (unsigned char *) 6, // PD6
  (unsigned char *) 0x0B, (unsigned char *) 7, // PD7
  (unsigned char *) 0x05, (unsigned char *) 0  // PB0
};


Nun muss ich in der Timerinterruptroutine nach gewissen Bedingungen den 
Ausgang an diesen Pins 0 bzw. 1 setzen:

(*OUTPUT_ARRAY[i*2]) |= (1 << (int)OUTPUT_ARRAY[i*2 + 1]);

Das funktioniert aber nicht ich bekomme folgende Fehlermeldung:
assignment of read-only location '*OUTPUT_ARRAY[(int)i * 2]'


Kann jemand weiterhelfen?
Is bestimmt ein einfaches Problem :/

von Tom Thomsen (Gast)


Lesenswert?

Kaan Ayhan schrieb:
> Kann jemand weiterhelfen?
> Is bestimmt ein einfaches Problem :/

Nun mal ganz langsam. An welchem Mikrocontroller versuchst du dich 
überhaupt? Vielleicht kann der das gar nicht.

von tictactoe (Gast)


Lesenswert?

Ich erklär dir hier mal nur die Fehlermeldung.

Kaan Ayhan schrieb:
> Die Ausgangspins habe ich global in einem Array definiert sodass ich
> einfachen zugriff drauf habe:
>
> static const unsigned char *OUTPUT_ARRAY[] = {
> ...
> };
>
> (*OUTPUT_ARRAY[i*2]) |= (1 << (int)OUTPUT_ARRAY[i*2 + 1]);
>
> Das funktioniert aber nicht ich bekomme folgende Fehlermeldung:
> assignment of read-only location '*OUTPUT_ARRAY[(int)i * 2]'

Schauen wir mal, was du hier hast:
1
static const unsigned char *OUTPUT_ARRAY[] = { ... }
2
                            ^OUTPUT_ARRAY ist ein
3
                                        ^Array von
4
                           ^Pointern auf
5
       ^nur-lesbare
6
             ^unsigned char
Also, das, worauf die Pointer zeigen, ist "const" -- nur lesbar. Jetzt 
müsstest die Fehlermeldung eigentlich klipp und klar sein.

Am einfachsten ist sicherlich, das "const" wegzulassen. Das 
funktioniert, weil die "Pointer", die du zuweist, eh alle non-const 
sind:
1
(unsigned char *) 0x05, (unsigned char *) 6, // PB6

Vielleicht hast du aber auch folgendes gemeint:
1
static unsigned char * const OUTPUT_ARRAY[] = { ... }
2
                             ^OUTPUT_ARRAY ist ein
3
                       ^nur-lesbares
4
                                         ^Array von
5
                     ^Pointern auf
6
       ^unsigned char
Denn du hast sicher nicht vor, nach dem Initialisieren OUTPUT_ARRAY 
selbst zu verändern. Daher kannst du es "const" machen. Aber das muss 
man ganz in der Nähe des Namens hinschreiben. Aber das, worauf die 
Pointer zeigen, bleibt beschreibbar -- genau was du brauchst.

Apropos Namen: Es wäre gut, wenn du dir angewöhnst, Namen aus nur 
Grossbuchstaben für Macros zu reservieren.

von Peter D. (peda)


Lesenswert?

Du kannst ganz massiv Code und Zeit sparen, wenn Du bei nur 6 Pins ein 
switch/case nimmst.
Dann kann der Compiler CBI/SBI draus machen und muß nicht erst 
umständlich Pointer aufdröseln und Bitmasken shiften.
Insbesondere im Interrupt spart man damit auch Push/Pop-Orgien.

von Eric B. (beric)


Lesenswert?

Kaan Ayhan schrieb:
> Is bestimmt ein einfaches Problem :/
1
typedef struct
2
{
3
  unsigned char * port_address;
4
  unsigned char   pin_bit;
5
} tPinConfig;
6
7
static const tPinConfig output_pin[] =
8
{
9
  { (unsigned char *) 0x05, 1u << 6u },
10
  /* ... insert other ports here ... */
11
  { (unsigned char *) 0x05, 1u << 0u },
12
};
13
14
/* ON */
15
*(output_pin[X].port_address) |= output_pin[X].pin_bit;
16
17
/* OFF */
18
*(output_pin[X].port_address) &= ~output_pin[X].pin_bit;
19
20
/* TOGGLE */
21
*(output_pin[X].port_address) ^= output_pin[X].pin_bit;

: Bearbeitet durch User
von Kaan A. (deranfaenger)


Lesenswert?

Hi,

erstmal danke für die schnellen Antworten.

tom, sry dachte habe das dazugeschrieben..  es handelt sich um einen 
ATMega88pa

tictactoe, danke für die super Erklärung. Jetzt funktioniert es genau so 
wie ich es haben möchte :)

Peter, ja du hast wohl Recht... die Interruptroutine dauert zu lange, 
sodass der nächste Interrupt zu früh kommt. Wird wohl an meinem 
Amateurcode liegen.
Wie meinst du das mit der switch-Anweisung? Ich kann mir das nicht so 
genau vorstellen wie das aussehen soll.

Eric, wäre deine schreibweise performanter, oder ist das einfach nur die 
schönere/professionellere Variante?
So muss ich das ja trotzdem durch eine for-Schleife jagen... die sind 
aber wie es ausschaut zu zeitintensiv.

Die Funktion des µC sieht so aus, dass ich 6 digitale In- und Outputpins 
und 2 analoge Inputpins habe. An den analogen Eingängen wird die Phase 
und das Tastverhältnis eingestellt. Über die digitalen Inputpins kann 
der Benutzer festlegen für welche PWM-Ausgänge die Einstellungen 
übernommen werden sollen und in meinen 2 Arrays speichere ich mir eben 
diese Adressen.

Ich hab hier mal eine leicht abgespeckte Version des Codes (Die Funktion 
chooseMag wird hier zwar nicht verwendet aber vielleicht wird für euch 
dadurch deutlicher was ich meine)
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define NR_OF_MAGS  6
5
6
// Define new Type for Magnetron
7
struct Magnetron {
8
  float frequency;
9
  float threshold;
10
  float phase;
11
  int currentTime;  // PWM-Time in Timer-frequency
12
};
13
// Declare Array of Type Magnetron
14
struct Magnetron mag[NR_OF_MAGS];
15
// initialize Magnetrons
16
void initMag(struct Magnetron *mag){
17
  mag->frequency = 500;
18
  mag->threshold = 300;
19
  mag->phase = 0;
20
  mag->currentTime = 0;
21
}
22
23
// Magnetron PWM Output Pin addresses
24
static unsigned char * const output_array[] = {
25
  (unsigned char *) 0x25, (unsigned char *) 6, // PB6
26
  (unsigned char *) 0x25, (unsigned char *) 7, // PB7
27
  (unsigned char *) 0x2B, (unsigned char *) 5, // PD5
28
  (unsigned char *) 0x2B, (unsigned char *) 6, // PD6
29
  (unsigned char *) 0x2B, (unsigned char *) 7, // PD7
30
  (unsigned char *) 0x25, (unsigned char *) 0  // PB0
31
};
32
33
// Magnetron Enable Pin addresses
34
static const unsigned char * ENABLE_ARRAY[] = {
35
  (unsigned char *) 0x25, (unsigned char *) 1, // PB1
36
  (unsigned char *) 0x25, (unsigned char *) 2, // PB2
37
  (unsigned char *) 0x25, (unsigned char *) 3, // PB3
38
  (unsigned char *) 0x2B, (unsigned char *) 2, // PD2
39
  (unsigned char *) 0x2B, (unsigned char *) 3, // PD3
40
  (unsigned char *) 0x2B, (unsigned char *) 4  // PD4
41
};
42
43
// check witch Magnetrons are choosen to be updated
44
void chooseMag(char magEnabled[]){
45
  for (unsigned char i = 0; i < sizeof(magEnabled); i++){
46
    magEnabled[i] = (*ENABLE_ARRAY[i*2]) & (1 << (int)ENABLE_ARRAY[i*2 + 1]);
47
  }
48
}
49
50
void setOutput(int scaledTime, unsigned char i){
51
  if((mag[i].phase + mag[i].threshold) > mag[i].frequency){
52
    if(scaledTime <= ((mag[i].phase + mag[i].threshold) - mag[i].frequency)) {*output_array[i*2] |= (1<<(int)output_array[i*2+1]);};
53
  }else{
54
    (((scaledTime - mag[i].phase) >= 0)&&((scaledTime - mag[i].phase) < mag[i].threshold)) ? (*output_array[i*2] |= (1<<(int)output_array[i*2+1])) : (*output_array[i*2] &= ~(1<<(int)output_array[i*2+1]));
55
  }
56
}
57
58
//
59
void checkPWM(){
60
  for (unsigned char i=0; i<NR_OF_MAGS; i++){
61
    int scaledTime;
62
    scaledTime = mag[i].currentTime / (float) 3906.25 * mag[i].frequency; // scale currentTime from Timer frequency to desired PWM  frequency
63
    if(scaledTime > mag[i].frequency) {mag[i].currentTime = 0;};
64
    setOutput(scaledTime, i);
65
  }
66
}
67
68
// for each Magnetron increase PWM-Time
69
void stepPWM(){
70
  for (unsigned char i=0; i<NR_OF_MAGS; i++){
71
    mag[i].currentTime += 1;
72
  }
73
}
74
75
//Timer Interrupt
76
ISR(TIMER0_OVF_vect){
77
  stepPWM();
78
  checkPWM();
79
}
80
81
// Initialize Data Direction Registers
82
void initDDR(){
83
  DDRB = 0b11000001;
84
  DDRC = 0b00000000;
85
  DDRD = 0b11100011;
86
}
87
88
int main(void){
89
90
  // Initialize Analog Digital Converter
91
  PRR &= 0xfe; // Power reduction off -> ADC enable
92
  ADMUX &= ~(1<<ADLAR); // result is right adjusted
93
  
94
  // Initialize Pin directions
95
  initDDR();
96
  
97
  // initialize Magnetron
98
  for (unsigned char i=0; i<NR_OF_MAGS; i++){
99
    initMag(&mag[i]);
100
  }
101
  
102
  // Initialize Timer Interrupt
103
  // System Clock runs in default with 1MHz --> 0.000001 seconds per Tick
104
  // Interrupt at Overflow --> 0.000001 * 256 = 0.000256 seconds per Interrupt  =>  3.90625 kHz
105
  sei(); // enable global interrupts
106
  TCCR0A = 0x00; // disable Compare Output Mode,  set Timer Mode of Operation to Normal
107
  TCCR0B = 0x01; // disable Force Output Mode, set Timer Mode of Operation to Normal, set Clock Source to I/O Clock without prescaler
108
  TIMSK0 |= 1 << TOIE0; // enable Timer Interrupt at overflow
109
  
110
    while(1){}; // es wird im Moment nur die Timerinterrupt Routine getestet
111
    
112
  return 0;
113
}
Wenn das ganze funktioniert, werde ich die Funktionen noch etwas 
verschönern und aufteilen, aber erstmal ist mir natürlich die 
Funktionalität wichtig.

Ich hoffe ihr könnt mir helfen, den Code zu optimieren.

MFG der Anfänger

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Kaan Ayhan schrieb:
> Wie meinst du das mit der switch-Anweisung?
1
#include "sbit.h"
2
3
void pin_out( uint8_t pinno, uint8_t level )
4
{
5
  if( level )
6
    switch( pinno ){
7
      case 0: PORT_B6 = 1; break;
8
      case 1: PORT_B7 = 1; break;
9
      case 2: PORT_D5 = 1; break;
10
      case 3: PORT_D6 = 1; break;
11
      case 4: PORT_D7 = 1; break;
12
      case 5: PORT_B0 = 1; break;
13
    }
14
  else
15
    switch( pinno ){
16
      case 0: PORT_B6 = 0; break;
17
      case 1: PORT_B7 = 0; break;
18
      case 2: PORT_D5 = 0; break;
19
      case 3: PORT_D6 = 0; break;
20
      case 4: PORT_D7 = 0; break;
21
      case 5: PORT_B0 = 0; break;
22
    }
23
}

Sollte schneller gehen, als mit Pointer.

Einfacher wärs natürlich, wenn alle Outputpins auf dem selben Port 
wären.

: Bearbeitet durch User
von Kaan A. (deranfaenger)


Lesenswert?

Danke jetzt scheint es mir logisch..

Aber ich kann die bib nicht einbinden. Wenn ich #include <sbit.h> mache, 
kommt die Fehlermeldung: No such file or directory
Ich hab die Datei im gleichen Ordner, wie das Projekt.

Achja und ich benutze AtmelStudio 6.2.

von Eric B. (beric)


Lesenswert?

Kaan Ayhan schrieb:
> Eric, wäre deine schreibweise performanter, oder ist das einfach nur die
> schönere/professionellere Variante?

* Es spart ein paar bytes,
* Es ist besser lesbar,
* Es braucht keine unnötige casts von Pointer auf normale ints,
* Der von dir beschriebene Fehler tritt nicht auf.

von Markus F. (mfro)


Lesenswert?

Kaan Ayhan schrieb:
> Wenn ich #include <sbit.h> mache,
...
> Ich hab die Datei im gleichen Ordner, wie das Projekt.

Dann brauchst Du
1
#include "sbit.h"
Sonst wird im System-Include-Pfad gesucht.

von Peter D. (peda)


Lesenswert?

Kaan Ayhan schrieb:
1
scaledTime = mag[i].currentTime / (float) 3906.25 * mag[i].frequency;

Der Pinzugriff ist jetzt nicht mehr das Problem.
Du hast float im Interrupt und dazu noch float Division.
Wenn das ne PWM werden soll, dann aber nur gaanz laangsaam.

von Kaan A. (deranfaenger)


Lesenswert?

Danke für den Tipp, Markus.

Und Peter, ja ich habe auch gemerkt dass das Problem in dieser Zeile 
liegt, doch wie kann ich das Problem lösen? Einfach einen Integer daraus 
zu machen wird das Problem ja nicht lösen denke ich... das Problem ist, 
dass es ganz ohne Division auch nicht geht. Denn es werden 6 PWM-Signale 
erzeugt. Alle werden in der Timer-Frequenz(Timer Interrupt bei Overflow 
mit 8bit Timer und 1MHz für eine höhere Frequenz könnte ich natürlich 
auch den Interrupt früher setzen) hochgezählt. Nun muss ich ja um die 
PWM zu berechnen das Ganze umskalieren. Sprich geteilt durch aktuelle 
Frequenz mal erwünschte Frequenz.

Um mit dem Interrupt kein Problem mehr zu haben, hab ich die Funktion 
checkPWM() in die main gezogen. Im Interrupt wird nur noch die Variable 
currentTime pro Ausgang hochgezählt ( stepPWM() ).

Weiß jemand wie ich das besser machen kann?

Wichtig ist, dass Frequenz und Tastverhältnis für alle Ausgänge 
unabhängig von einander variabel sein sollten.
Wenn ich die Frequenz für alle Ausgänge gleich hätte, wäre das ja kein 
Problem.

von Peter D. (peda)


Lesenswert?

Schau Dir mal andere SW-PWM Varianten an, die machen überhaupt keine 
Berechnungen im Interrupt.
Nur Arrayzugriffe, um den nächsten Zählwert laden.
Die Berechnung eines neuen PWM-Wertes erfolgt einmalig in der Mainloop.

Kaan Ayhan schrieb:
> Wichtig ist, dass Frequenz und Tastverhältnis für alle Ausgänge
> unabhängig von einander variabel sein sollten.

Das kling recht crazy.
Mags Du mal die Anwendung schildern, warum die Frequenz sich ändern muß.
Allgemein ist PWM ja nur Änderung des Mittelwertes.

von Kaan A. (deranfaenger)


Lesenswert?

Danke für den Tipp ich werde es mir morgen anschauen.

Ja der Mikrocontroller soll die Ansteuerung der Netzteile von Magnetrons 
übernehmen um den Erhitzungsprozess in einem Mikrowellenofen zu 
optimieren. So grob erklärt.

Meint ihr, wenn ich die Einstellung der Frequenz auf "vor den Start" 
begrenze, wäre es besser? Wenn man denn keinen anderen Weg findet wäre 
das meine Lösung :/

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.