Forum: Mikrocontroller und Digitale Elektronik C: Module in einer .h auslagern


von Hans (Gast)


Lesenswert?

Ich habe eine Frage zu Header-Dateien.

Ich möchte mein Programm nur etwas übersichtlicher gestalten, daher 
würde ich gerne ein paar Sachen in Header-Dateien auslagern.

Meine Frage ist: Muss ich hier immer eine .c und eine .h und daraus eine 
Objekt-Datei machen?

Es funktioniert ja auch, wenn ich einfach sowohl Definitionen und 
Funktionen in eine .h auslagere und diese beim Starten lade. Ist ja 
quasi ne reine Textkopie in meinen eigentlichen Quelltext. Es geht mir 
nicht darum, die Module für andere Sachen verfügbar zu machen, sondern 
die bleiben bei dem Programm hier. Ich check das mit den Headern nur 
noch nicht so. Hier die Variablen, da das, da das.

Also mal als Beispiel:
1
#include <stdint.h>
2
3
...
4
#define XYZ 100
5
#define ABC 300
6
...
7
8
void funktion_1( void );
9
int16_t funktion_2 (uint8_t beispiel);
10
...
11
12
void main( void )
13
{
14
  mache dies...;
15
  mache das...;
16
17
  funktion_1 ();
18
  ...
19
}
20
21
void funktion_1( void )
22
{
23
  mache nix;
24
}
25
26
int16_t funktion_2 (uint8_t beispiel)
27
{
28
  return beispiel++;
29
}
30
[c]
31
32
Wenn ich nun folgendes in einer .h auslagere:
33
34
[c]
35
#ifndef MYHEADER_H
36
#define MYHEADER_H
37
38
#define XYZ 100
39
#define ABC 300
40
41
void funktion_1( void );
42
int16_t funktion_2 (uint8_t beispiel);
43
44
void funktion_1( void )
45
{
46
  mache nix;
47
}
48
49
int16_t funktion_2 (uint8_t beispiel)
50
{
51
  return beispiel++;
52
}
53
54
#endif

Und dann in meinem Hauptprogramm:
1
#include <stdint.h>
2
#include "myheader.h"
3
4
void main( void )
5
{
6
  mache dies...;
7
  mache das...;
8
9
  funktion_1 ();
10
  ...
11
}

Spricht da was gegen? Ich raff halt nicht, was ich wie in den Header und 
was in die extra C-Datei schreiben muss. Das geht bestimmt schief - dazu 
dann noch Variablen mit extern usw.

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Ich habe eine Frage zu Header-Dateien.
>
> Ich möchte mein Programm nur etwas übersichtlicher gestalten, daher
> würde ich gerne ein paar Sachen in Header-Dateien auslagern.
>
> Meine Frage ist: Muss ich hier immer eine .c und eine .h und daraus eine
> Objekt-Datei machen?

Müssen tust du gar nichts.
Ein C Compiler ist ja kein Scharfrichter, der dir die Rübe abhackt.

> Es funktioniert ja auch, wenn ich einfach sowohl Definitionen und
> Funktionen in eine .h auslagere und diese beim Starten lade.

Soweit so gut. Bis dann die Projekte größer werden, du dieselbe 
Header-Datei in 2 andere *.c Files inkludieren musst und dann 
funktioniert es nämlich nicht mehr.

> die bleiben bei dem Programm hier. Ich check das mit den Headern nur
> noch nicht so.

Ist ganz einfach:

Jedes Software-Modul bekommt sein eigenes C-File, wird extra kompiliert 
und gehört zum Projekt.

Damit andere Teile des Programms bescheid wissen, was dieses Modul so an 
Funktionalität anbietet, inkludieren sie ein Header File, in dem genau 
diese Information (und nur diese Information) enthalten ist.

> Wenn ich nun folgendes in einer .h auslagere:
>
> #ifndef MYHEADER_H
> #define MYHEADER_H
>
> #define XYZ 100
> #define ABC 300
>
> void funktion_1( void );
> int16_t funktion_2 (uint8_t beispiel);

Bis hier hier ist es ok.

>
> void funktion_1( void )
> {
>   mache nix;
> }
>
> int16_t funktion_2 (uint8_t beispiel)
> {
>   return beispiel++;
> }

Die Funktionen selbst haben (ausser in Ausnahmefällen) nichts im Header 
File zu suchen. Wenn sie aber im Header File sind, müssen sie inline 
markiert sein.

> Spricht da was gegen? Ich raff halt nicht, was ich wie in den Header und
> was in die extra C-Datei schreiben muss.

ALs Grundregel:
Die Implementierungen der Funktionen gehören in ein C-File.
Im Header sind nur die Funktionsprototypen.

Und daraus folgt dann alles andere. Funktionsprototypen benötigen 
eventuell Strukturdeklarationen, also gehören die auch ins Header File, 
etc. etc.

von Hans M. (Firma: mayer) (oe1smc) Benutzerseite


Lesenswert?

hallo Hans

in eine .h datei schreibt man keinen programm-code.
das schreibst du in eine .c datei.
hast also 2 zusaetzliche dateien, z.b.
teilprog1.c
teilprog1.h

und in teilprog1.h definierst du nur die unterprgramme mit extern
extern void funktion_1( void );

und im haupt-teil wird nur das .h included

#include "teilprog1.h"

that's it.

schoene gruesse
hans

--

von Andreas B. (Gast)


Lesenswert?

Ein #include fügt effektiv den Inhalt der angegebenen Datei an dieser 
Stelle im Quelltext ein. Also wenn der Inhalt an die Stelle kopiert 
funktionieren würde, funktioniert es auch so.

Nur ist das nicht dafür gedacht, Quelltext aufzuteilen. Dafür packt man 
das in mehrere C Quelltexte, die separat übersetzt und dann zusammen 
gelinkt werden. Dann braucht man natürlich wieder Header für die 
gemeinsamen Deklarationen. Aber ein Header, der nur für eine einzige C 
Datei existiert, sollte eigentlich nicht existieren.

von Hans (Gast)


Lesenswert?

OK Karl-Heinz, danke schonmal für die Antwort.

Darf ich dich evtl. mal mit einm Beispiel belästigen, damit ich es evtl. 
ein für allemal verstanden habe?

Also ich habe für meinen ADC eine State-Machine geschrieben, die mir 
nebenbei die Daten aktualisiert. Sie benötigt aber halt im 
Gesamtprogramm auch ein paar Definitionen. Folgendes gehört dazu:

1
#include <stdint.h>
2
#include "msp430x24x.h"
3
4
//...
5
6
// #################################################### MCU-SPECIFIC DEFINITIONS
7
#define DAC_TX_BUFFER            UCB1TXBUF
8
#define DAC_RX_BUFFER            UCBITXBUF
9
#define DAC_ENABLE_RX_IR         (UC1IE  |=  UCB1RXIE)
10
#define DAC_DISABLE_RX_IR        (UC1IE  &= ~UCB1RXIE)
11
#define DAC_TX_BUFFER_FREE       (UC1IFG &   UCB1TXIFG)
12
#define DAC_FORCE_FREE_TX_BUFFER (UC1IFG |=  UCB1TXIFG)
13
#define DAC_RX_IFG               (UC1IFG &   UCB1RXIFG)
14
#define DAC_CLEAR_RX_IFG         (UC1IFG &= ~UCB1RXIFG)
15
#define DAC_LATCH_LOW            (P5OUT  &= ~DAC_LATCH)
16
#define DAC_LATCH_HIGH           (P5OUT  |=  DAC_LATCH)
17
// =============================================================================
18
19
20
// STATE-MACHINE
21
#define DAC_HOLD_SM             0
22
#define DAC_START_SM            1
23
#define DAC_BUILD_TRANSMIT_DATA 2
24
#define DAC_START_TRANSMISSION  3
25
#define DAC_TRANSMITTING        4
26
27
volatile struct
28
{
29
  uint8_t  state;                 // Actual state of state-machine
30
  uint32_t dac_value;             // Value to be written to DAC
31
  uint8_t  send_bytes[3];         // Buffer for bytes to send
32
  uint8_t  bytecounter;           // Counter for interrupt-driven communication
33
  uint8_t  hold_state_machine;    // If set, SM stops after last transmission
34
  uint8_t  tx_buffer_error;       // Counts errors if TX-buffer not empty
35
} DAC_SM = {DAC_START_SM, 0x8000, {0, 0, 0}, 0, FALSE, 0}; // Init SM
36
37
38
//...
39
void dac_update( void );
40
void dac_RX_SM( void );
41
//...
42
43
44
void main( void )
45
{
46
  //...
47
  dac_update();
48
  //...
49
}
50
51
52
void dac_update( void )
53
{
54
  switch( DAC_SM.state )                         // Determine actual state of SM
55
  {
56
    case DAC_HOLD_SM: break;                     // DAC-SM in hold-state
57
    
58
    case DAC_START_SM:                           // Restart state-machine
59
    {
60
      DAC_SM.tx_buffer_error = 0;                // Reset error-counter
61
      DAC_FORCE_FREE_TX_BUFFER;                  // Force TX-buffer to be free
62
      DAC_SM.state = DAC_BUILD_TRANSMIT_DATA;    // Change state
63
    }
64
    
65
    case DAC_BUILD_TRANSMIT_DATA:                // Build data-bytes
66
    {
67
      DAC_SM.send_bytes[0] = ((DAC_SM.dac_value >> 16) & 0xFF);
68
      DAC_SM.send_bytes[1] = ((DAC_SM.dac_value >> 8) & 0xFF);
69
      DAC_SM.send_bytes[2] = (DAC_SM.dac_value & 0xFF);
70
      
71
      DAC_SM.state = DAC_START_TRANSMISSION;     // Change state-machine state
72
    }
73
    
74
    case DAC_START_TRANSMISSION:                 // Start transmission of data
75
//...
76
  }
77
}
78
79
80
// ISR
81
void dac_RX_SM( void )
82
{
83
  switch( DAC_SM.state )                           // Determine state of RX-SM
84
  {
85
    case DAC_TRANSMITTING:                         // Trasnmission is active
86
    {
87
      DAC_SM.bytecounter++;                        // Advance bytecounter
88
      
89
      if( DAC_SM.bytecounter < 3 )                 // Not all bytes sent yet
90
      {
91
       // ...
92
      }
93
    }
94
  }
95
}

Was mache ich jetzt in eine .h und was in eine .c? Ich habe am meisten 
Bedenken bei dem struct. Da habe ich im iNet gelesen, ich müsse hier 
oder da ein extern voranstellen?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:

> Was mache ich jetzt in eine .h und was in eine .c?

Fang damit an, dich zu fragen, welche Funktionen von ausserhalb 
zugänglich sein müssen. Die stellst du erst mal in das Header File

Ich hab mal das hier identifiziert
1
#ifndef DAC_INCLUDED
2
#define DAC_INCLUDED
3
4
void dac_update( void );
5
void dac_RX_SM( void );
6
7
#endif

mehr muss main nicht wissen, um die State Machine einsetzen zu können. 
Insbesondere muss main nicht wissen, dass es eine Struktur gibt, oder 
dass du da mittels #define irgendwelche Konstanten festgelegt hast.

In den Funktionsprototypen kommen auch keine Argumente vor, die 
irgendwelche Strukturen oder sonstiges Wissen erfordern.

Damit ist dieses Header File in sich vollständig.
Wenn in main.c dieses Header File inkludiert wird, dann hat man in main 
alles was man braucht.

Alles andere bleibt beim DAC Code im eigenen C-File.
Warum? Weil das ausserhalb dieses C-Files niemanden etwas angeht.
1
#include "dac.h"
2
3
// #################################################### MCU-SPECIFIC DEFINITIONS
4
#define DAC_TX_BUFFER            UCB1TXBUF
5
#define DAC_RX_BUFFER            UCBITXBUF
6
#define DAC_ENABLE_RX_IR         (UC1IE  |=  UCB1RXIE)
7
#define DAC_DISABLE_RX_IR        (UC1IE  &= ~UCB1RXIE)
8
#define DAC_TX_BUFFER_FREE       (UC1IFG &   UCB1TXIFG)
9
#define DAC_FORCE_FREE_TX_BUFFER (UC1IFG |=  UCB1TXIFG)
10
#define DAC_RX_IFG               (UC1IFG &   UCB1RXIFG)
11
#define DAC_CLEAR_RX_IFG         (UC1IFG &= ~UCB1RXIFG)
12
#define DAC_LATCH_LOW            (P5OUT  &= ~DAC_LATCH)
13
#define DAC_LATCH_HIGH           (P5OUT  |=  DAC_LATCH)
14
15
// STATE-MACHINE
16
#define DAC_HOLD_SM             0
17
#define DAC_START_SM            1
18
#define DAC_BUILD_TRANSMIT_DATA 2
19
#define DAC_START_TRANSMISSION  3
20
#define DAC_TRANSMITTING        4
21
22
volatile struct
23
{
24
  uint8_t  state;                 // Actual state of state-machine
25
  uint32_t dac_value;             // Value to be written to DAC
26
  uint8_t  send_bytes[3];         // Buffer for bytes to send
27
  uint8_t  bytecounter;           // Counter for interrupt-driven communication
28
  uint8_t  hold_state_machine;    // If set, SM stops after last transmission
29
  uint8_t  tx_buffer_error;       // Counts errors if TX-buffer not empty
30
} DAC_SM = {DAC_START_SM, 0x8000, {0, 0, 0}, 0, FALSE, 0}; // Init SM
31
32
void dac_update( void )
33
{
34
  switch( DAC_SM.state )                         // Determine actual state of SM
35
  {
36
    case DAC_HOLD_SM: break;                     // DAC-SM in hold-state
37
    
38
    case DAC_START_SM:                           // Restart state-machine
39
    {
40
      DAC_SM.tx_buffer_error = 0;                // Reset error-counter
41
      DAC_FORCE_FREE_TX_BUFFER;                  // Force TX-buffer to be free
42
      DAC_SM.state = DAC_BUILD_TRANSMIT_DATA;    // Change state
43
    }
44
    
45
    case DAC_BUILD_TRANSMIT_DATA:                // Build data-bytes
46
    {
47
      DAC_SM.send_bytes[0] = ((DAC_SM.dac_value >> 16) & 0xFF);
48
      DAC_SM.send_bytes[1] = ((DAC_SM.dac_value >> 8) & 0xFF);
49
      DAC_SM.send_bytes[2] = (DAC_SM.dac_value & 0xFF);
50
      
51
      DAC_SM.state = DAC_START_TRANSMISSION;     // Change state-machine state
52
    }
53
    
54
    case DAC_START_TRANSMISSION:                 // Start transmission of data
55
//...
56
  }
57
}
58
59
60
// ISR
61
void dac_RX_SM( void )
62
{
63
  switch( DAC_SM.state )                           // Determine state of RX-SM
64
  {
65
    case DAC_TRANSMITTING:                         // Trasnmission is active
66
    {
67
      DAC_SM.bytecounter++;                        // Advance bytecounter
68
      
69
      if( DAC_SM.bytecounter < 3 )                 // Not all bytes sent yet
70
      {
71
       // ...
72
      }
73
    }
74
  }
75
}


Fertig.


> Ich habe am meisten
> Bedenken bei dem struct. Da habe ich im iNet gelesen, ich müsse hier
> oder da ein extern voranstellen?

Nachdenken. Dein Leitfaden ist nicht das iNet, sondern die 
Fragestellung: Wer muss ausserhalb des C-Files (mit der Implementierung) 
was wissen?
In deinem Fall muss ein Verwender des DAC nur die beiden Funktionen 
kennen. Alles andere muss er nicht wissen. Das bleibt daher beim DAC 
Code im eigenen C-File. Das C-File bildet sowas wie eine Hülle, eine 
Grenze, an der die Sichtbarkeit von Dingen endet. Enden sollte; aber 
dazu müsste man zb File-globale Variablen static machen. Das sieht dann 
so aus
1
.....
2
struct dacData
3
{
4
  uint8_t  state;                 // Actual state of state-machine
5
  uint32_t dac_value;             // Value to be written to DAC
6
  uint8_t  send_bytes[3];         // Buffer for bytes to send
7
  uint8_t  bytecounter;           // Counter for interrupt-driven communication
8
  uint8_t  hold_state_machine;    // If set, SM stops after last transmission
9
  uint8_t  tx_buffer_error;       // Counts errors if TX-buffer not empty
10
};
11
12
static volatile struct dacData DAC_SM = {DAC_START_SM, 0x8000, {0, 0, 0}, 0, FALSE, 0}; // Init SM
13
....

Jetzt ist die Sichtbarkeit der Strukturvariablen auf das C-File 
begrenzt. Es ist nun nicht mehr möglich sich von ausserhalb des DAC.C 
auf diese Variable zu beziehen und an ihr herumzupfuschen. Und genau so 
soll es ja auch sein.

(Und gewöhn dir diese anonymen Strukturen und das definieren von 
Variablen zusammen mit der Strukturdefinition gleich wieder ab. Das ist 
es nicht wert und in 95% aller Fälle kann man dieses Schema sowieso so 
nicht einsetzen).

Den Teil mit den MCU-SPECIFIC DEFINITIONS hab ich ebenfalls in DAC.C 
verschoben, weil dort etwas davon verwendet wird. Wenn du diese Makros 
in main.c auch brauchen würdest, dann würde man diese #define wiederrum 
in ein eigenes Header File verfrachten und dieses dann in DAC.C und in 
MAIN.C inkludieren.



Das Prinzip heißt 'Information Hiding'. Du willst soviel Information wie 
möglich in einem C-File 'verstecken' und nur über das Header File das 
preisgeben, was du unbedingt preisgeben musst, damit anderer Code diese 
Funktionalität benutzen kann. Alles was für diesen Zweck nicht notwendig 
ist, hat im Header File erst mal nichts verloren.

von Karl H. (kbuchegg)


Lesenswert?


von Hans (Gast)


Lesenswert?

Hallo Karl-Heinz!

Vielen Dank für deine Ausführliche Erklärung!

Karl Heinz Buchegger schrieb:
> Es ist nun nicht mehr möglich sich von ausserhalb des DAC.C
> auf diese Variable zu beziehen und an ihr herumzupfuschen.

Ich benötige jedoch Zugriff auf dieses struct, da ich den Wert 
"dac_value" ja in das struct eintragen muss, damit es übertragen wird. 
Kann ich die Variable denn dann auch nicht mehr beschreiben?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Hallo Karl-Heinz!
>
> Vielen Dank für deine Ausführliche Erklärung!
>
> Karl Heinz Buchegger schrieb:
>> Es ist nun nicht mehr möglich sich von ausserhalb des DAC.C
>> auf diese Variable zu beziehen und an ihr herumzupfuschen.
>
> Ich benötige jedoch Zugriff auf dieses struct, da ich den Wert
> "dac_value" ja in das struct eintragen muss, damit es übertragen wird.
> Kann ich die Variable denn dann auch nicht mehr beschreiben?

Nein, kannst du nicht, sollst du auch nicht.

Schreib dir eine Funktion, die das tut.

von Hans (Gast)


Lesenswert?

So Karl-Heinz, ich hoffe du schaust hier nochmal rein. Ich habe jetzt 
mal einiges umgemodelt.

Eine Frage habe ich vorab jedoch noch:

Wenn ich in dieser .h-Datei jetzt folgendes stehen habe:
1
#ifndef DAC_FUNCTIONS_H
2
#define DAC_FUNCTIONS_H
3
4
void dac_update( void );
5
void dac_rx_sm( void );
6
uint8_t dac_update_value( uint32_t dac_value );
7
void dac_start_sm( void );
8
void dac_stop_sm( void );
9
uint8_t dac_return_sm_status( void );
10
11
#endif // DAC_FUNCTIONS_H
Hier muss ich doch jtzt ebenfalls die <stdint.h> includieren, da ich 
uint8_t nutze, oder? Einmal mehr includieren stört ja eh keinen, 
richtig? Solange die doppelte Einbindung nicht vorkommt.

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:

> Wenn ich in dieser .h-Datei jetzt folgendes stehen habe:
>
1
> #ifndef DAC_FUNCTIONS_H
2
> #define DAC_FUNCTIONS_H
3
> 
4
> void dac_update( void );
5
> void dac_rx_sm( void );
6
> uint8_t dac_update_value( uint32_t dac_value );
7
> void dac_start_sm( void );
8
> void dac_stop_sm( void );
9
> uint8_t dac_return_sm_status( void );
10
> 
11
> #endif // DAC_FUNCTIONS_H
12
>
> Hier muss ich doch jtzt ebenfalls die <stdint.h> includieren, da ich
> uint8_t nutze, oder?

Ganz genau.
Jedes Header File soll in sich vollständig sein und selbst alles 
inkludieren was es braucht. Du entlastest damit denjenigen, der dac.h 
inkludiert. Er braucht sich dann nicht darüber Gedanken zu machen, 
welche anderen Header Vorraussetzung zum inkludieren von dac.h sind.

Wobei man bei Systemheadern meistens eine Ausnahme macht. Zumindest 
solange, solange man davon ausgehen kann, dass dieser Systemheader 
sowieso schon inkludiert sein wird. stdint.h könnte man da dazurechnen. 
Schadet aber auch nicht, wenn du ihn im Header File inkludierst.
Denn: Wenn dein Header File damit beginnt, dass es selbst erst mal 268 
andere Files included, dann
* dient das nicht gerade der Übersicht
* läuft man Gefahr, ungewollt einen Kreisbezug zu erhalten:

        A.h   includiert  B.h
   und  B.h   includiert  A.h

> Einmal mehr includieren stört ja eh keinen,
> richtig? Solange die doppelte Einbindung nicht vorkommt.

Dieses potentielle Problem wird ja durch die Include Guards entschärft.

von Hans (Gast)


Lesenswert?

OK, also dann hier mal mein endgültiger Text. Sorry, dass das jetzt so 
lang ist, aber es geht ja eigentlich nur um meine includes.

main
1
#include "dac_functions.h"

dac_functions.h
1
#ifndef DAC_FUNCTIONS_H
2
#define DAC_FUNCTIONS_H
3
4
#include <stdint.h>
5
6
7
8
/*******************************************************************************
9
* void dac_update( void ) *                                                    *
10
***************************                                                    *
11
* Description:                                                                 *
12
*                                                                              *
13
* The function is part of the state-machine that controls the interrupt-       *
14
* driven communication with the DAC. The function has to be placed into        *
15
* the main-loop so that it is called every loop-cycle. Together with the       *
16
* function "dac_rx_sm" it runs in the background and automatically updates     *
17
* the DAC. The initial value is 0x4000 (16384) which configures the DAC for    *
18
* an output of ~4mA. This value can be changed by using the function           *
19
* "dac_update_value".                                                          *
20
*------------------------------------------------------------------------------*
21
* Function-Argument: none                                                      *
22
* Return-value:      none                                                      *
23
*******************************************************************************/
24
void dac_update( void );
25
26
27
28
/*******************************************************************************
29
* void dac_rx_sm( void ) *                                                     *
30
**************************                                                     *
31
* Description:                                                                 *
32
*                                                                              *
33
* This function has to be called everytime the usci-state-machine has received *
34
* a complete character. Although the DAC only gets data without responding to  *
35
* it - a complete transfer can generate an receive-interrupt, too. It is safer *
36
* to trigger on a complete transmission by waiting for the receive-interrupt.  *
37
* This function is in interaction with "dac_update" and controls the updates   *
38
* of the DAC.                                                                  *
39
*------------------------------------------------------------------------------*
40
* Function-Argument: none                                                      *
41
* Return-value:      none                                                      *
42
*******************************************************************************/
43
void dac_rx_sm( void );
44
45
46
47
/*******************************************************************************
48
* uint8_t dac_update_value( uint32_t dac_value ) *                             *
49
**************************************************                             *
50
* Description:                                                                 *
51
*                                                                              *
52
* The data for the DAC is updated by this function. The function has to be     *
53
* called with the new DAC-value as it's argument. This argument can be a value *
54
* between 0x3C00 (15360) which produces an output-current of ~3.75mA to a      *
55
* maximum of 0x18000 (98304) which results in an output-current of ~24mA.      *
56
* Between this two values the functions returns '0' for an accepted und        *
57
* updated value. If the new dac-value is smaller, '1' is returned an the       *
58
* current is set to ~4mA. A higher value returns '2' and programs an output-   *
59
* current of ~24mA.                                                            *
60
*------------------------------------------------------------------------------*
61
* Function-Argument: "uint32_t dac_value" <- value between 0x3C00 and 0x18000  *
62
* Return-value:      '0' <- OK | '1' <- too low | '2' <- too high              *
63
*******************************************************************************/
64
uint8_t dac_update_value( uint32_t dac_value );
65
66
67
68
/*******************************************************************************
69
* void dac_start_sm( void ) *                                                  *
70
*****************************                                                  *
71
* Description:                                                                 *
72
*                                                                              *
73
* The function (re-)starts the DAC-state-machine to run in the backround. Any  *
74
* currently running transmissions are aborted.                                 *
75
*------------------------------------------------------------------------------*
76
* Function-Argument: none                                                      *
77
* Return-value:      none                                                      *
78
*******************************************************************************/
79
void dac_start_sm( void );
80
81
82
83
/*******************************************************************************
84
* void dac_stop_sm( void ) *                                                   *
85
****************************                                                   *
86
* Description:                                                                 *
87
*                                                                              *
88
* The function stops the DAC-state-machine. A currently running transmission   *
89
* is finished first. After that the state-machine is in the hold-state and can *
90
* be restarted by the function "dac_start_sm".                                 *
91
*------------------------------------------------------------------------------*
92
* Function-Argument: none                                                      *
93
* Return-value:      none                                                      *
94
*******************************************************************************/
95
void dac_stop_sm( void );
96
97
98
99
/*******************************************************************************
100
* uint8_t dac_return_sm_status( void ) *                                       *
101
****************************************                                       *
102
* Description:                                                                 *
103
*                                                                              *
104
* The function returns the current status of the DAC-state-machine. If it is   *
105
* currently active, the function returns '1', otherwise it returns '0'.        *
106
*------------------------------------------------------------------------------*
107
* Function-Argument: none                                                      *
108
* Return-value:      '1' <- active | '0' <- stopped                            *
109
*******************************************************************************/
110
uint8_t dac_return_sm_status( void );
111
112
#endif // DAC_FUNCTIONS_H

dac_functions.c
1
// DAC-functions
2
3
#include "dac_functions.h"
4
#include "dac_mcu_specific_definitions.h"
5
#include "general_definitions.h"
6
#include <stdint.h>
7
8
9
10
#define DAC_HOLD_SM             0
11
#define DAC_START_SM            1
12
#define DAC_BUILD_TRANSMIT_DATA 2
13
#define DAC_START_TRANSMISSION  3
14
#define DAC_TRANSMITTING        4
15
16
struct dac_sm
17
{
18
  uint8_t  state;                 // Actual state of state-machine
19
  uint32_t dac_value;             // Value to be written to DAC
20
  uint8_t  send_bytes[3];         // Buffer for bytes to send
21
  uint8_t  bytecounter;           // Counter for interrupt-driven communication
22
  uint8_t  hold_state_machine;    // If set, SM stops after last transmission
23
  uint8_t  tx_buffer_error;       // Counts errors if TX-buffer not empty
24
};
25
26
static volatile struct dac_sm DAC_SM = {DAC_START_SM, 0x4000, {0, 0, 0}, 0, FALSE, 0};
27
28
29
30
// ################################################################## DAC-UPDATE
31
void dac_update( void )
32
{
33
  switch( DAC_SM.state )                         // Determine actual state of SM
34
  {
35
    case DAC_HOLD_SM: break;                     // DAC-SM in hold-state
36
    
37
    case DAC_START_SM:                           // Restart state-machine
38
    {
39
      DAC_DISABLE_RX_IR;                         // Disable interrupts for RX-SM
40
      DAC_SM.hold_state_machine = FALSE;         // Reset hold-flag
41
      DAC_SM.tx_buffer_error = 0;                // Reset error-counter
42
      DAC_FORCE_FREE_TX_BUFFER;                  // Force TX-buffer to be free
43
      DAC_SM.state = DAC_BUILD_TRANSMIT_DATA;    // Change state
44
    }
45
    
46
    case DAC_BUILD_TRANSMIT_DATA:                // Build data-bytes
47
    {
48
      DAC_SM.send_bytes[0] = ((DAC_SM.dac_value >> 16) & 0xFF);
49
      DAC_SM.send_bytes[1] = ((DAC_SM.dac_value >> 8) & 0xFF);
50
      DAC_SM.send_bytes[2] = (DAC_SM.dac_value & 0xFF);
51
      
52
      DAC_SM.state = DAC_START_TRANSMISSION;     // Change state-machine state
53
    }
54
    
55
    case DAC_START_TRANSMISSION:                 // Start transmission of data
56
    {
57
      if( DAC_TX_BUFFER_FREE )                   // Wait for free TX-buffer
58
      {
59
        DAC_SM.tx_buffer_error = 0;              // Reset error-counter
60
        DAC_LATCH_LOW;                           // Pull DAC-latch 'low'
61
        DAC_CLEAR_RX_IFG;                        // Clear RX-IFG
62
        DAC_ENABLE_RX_IR;                        // Enable interrupts for RX-SM
63
        DAC_SM.bytecounter = 0;                  // Reset bytecounter
64
        DAC_SM.state = DAC_TRANSMITTING;         // Change state
65
        DAC_TX_BUFFER = DAC_SM.send_bytes[DAC_SM.bytecounter]; // Send 1st byte
66
      }
67
      else                                       // TX-buffer not free
68
      {
69
        DAC_SM.tx_buffer_error++;                // Advance error-counter
70
        
71
        if( DAC_SM.tx_buffer_error > 10 )        // TX-buffer still not free
72
        {
73
          DAC_SM.state = DAC_START_SM;           // Reset state-machine
74
        }
75
      }
76
      
77
      break;
78
    }
79
    
80
    case DAC_TRANSMITTING: break;                // RX-SM is transmitting data
81
    
82
    default:
83
    {
84
      DAC_DISABLE_RX_IR;                         // Disable interrupts for RX-SM
85
      DAC_SM.state = DAC_BUILD_TRANSMIT_DATA;    // Change state
86
    }
87
  }
88
}
89
//  ============================================================================
90
91
92
93
// ################################################### DAC RECEIVE-STATE-MACHINE
94
void dac_rx_sm( void )
95
{
96
  switch( DAC_SM.state )                           // Determine state of RX-SM
97
  {
98
    case DAC_TRANSMITTING:                         // Trasnmission is active
99
    {
100
      DAC_SM.bytecounter++;                        // Advance bytecounter
101
      
102
      if( DAC_SM.bytecounter < 3 )                 // Not all bytes sent yet
103
      {
104
        DAC_TX_BUFFER = DAC_SM.send_bytes[DAC_SM.bytecounter]; // Send next byte
105
      }
106
      else                                         // All bytes transmitted
107
      {
108
        DAC_DISABLE_RX_IR;                         // Disable RX-SM interrupts
109
        DAC_LATCH_HIGH;                            // Pull DAC-latch 'high'
110
        
111
        if( DAC_SM.hold_state_machine )            // Hold-flag is set
112
        {
113
          DAC_SM.state = DAC_HOLD_SM;              // Stop state-machine
114
        }
115
        else
116
        {
117
          DAC_SM.state = DAC_BUILD_TRANSMIT_DATA;  // Change state
118
        }
119
      }
120
      
121
      break;
122
    }
123
    
124
    default:
125
    {
126
      DAC_DISABLE_RX_IR;                        // Disable interrupts for RX-SM
127
      DAC_SM.state = DAC_START_TRANSMISSION;    // Change state-machine state
128
      
129
      break;
130
    }
131
  }
132
}
133
// =============================================================================
134
135
136
137
// ########################################################## DAC CURRENT-UPDATE
138
void dac_update_value( uint32_t dac_value )
139
{
140
  if( dac_value < 0x00003C00 )         // Current is higher than 3.75mA
141
  {
142
    DAC_SM.dac_value = 0x00003C00      // Set current to 3.75mA
143
    
144
    return 1;
145
  }
146
147
  else if( dac_value > 0x00018000 )    // Current is higher than 24mA
148
  {
149
    DAC_SM.dac_value = 0x00018000;     // Set current to 24mA
150
    
151
    return 2;
152
  }
153
154
  else
155
  {
156
    DAC_SM.dac_value = dac_value;
157
  }
158
  
159
  return 0;
160
}
161
// =============================================================================
162
163
164
165
// ########################################################## DAC CURRENT-UPDATE
166
void dac_start_sm( void )
167
{
168
  DAC_SM.state = DAC_START_SM;
169
}
170
// =============================================================================
171
172
173
174
// ########################################################## DAC CURRENT-UPDATE
175
void dac_stop_sm( void )
176
{
177
  DAC_SM.hold_state_machine = TRUE;
178
}
179
// =============================================================================
180
181
182
183
// ########################################################## DAC CURRENT-UPDATE
184
uint8_t dac_return_sm_status( void )
185
{
186
  if (DAC_SM.hold_state_machine)
187
  {
188
    return FALSE;
189
  }
190
  
191
  return TRUE;
192
}
193
// =============================================================================

dac_mcu_specific_definitions.h
1
// DEFINITIONS FOR MICROCONTROLLER-SPECIFIC COMMANDS
2
3
#ifndef DAC_MCU_SPECIFIC_DEFINITIONS_H
4
#define DAC_MCU_SPECIFIC_DEFINITIONS_H
5
6
#include "msp430x24x.h"
7
8
#define DAC_TX_BUFFER            UCB1TXBUF
9
#define DAC_RX_BUFFER            UCBITXBUF
10
#define DAC_ENABLE_RX_IR         (UC1IE  |=  UCB1RXIE )
11
#define DAC_DISABLE_RX_IR        (UC1IE  &= ~UCB1RXIE )
12
#define DAC_TX_BUFFER_FREE       (UC1IFG &   UCB1TXIFG)
13
#define DAC_FORCE_FREE_TX_BUFFER (UC1IFG |=  UCB1TXIFG)
14
#define DAC_RX_IFG               (UC1IFG &   UCB1RXIFG)
15
#define DAC_CLEAR_RX_IFG         (UC1IFG &= ~UCB1RXIFG)
16
#define DAC_LATCH_LOW            (P5OUT  &= ~DAC_LATCH)
17
#define DAC_LATCH_HIGH           (P5OUT  |=  DAC_LATCH)
18
19
#endif // DAC_MCU_SPECIFIC_DEFINITIONS_H

Und zu guter letzt die general_definitions.h
1
#ifndef GENERAL_DEFINITIONS_H
2
#define GENERAL_DEFINITIONS_H
3
4
#define FALSE 0
5
#define TRUE  1
6
7
#define BIT_0  0x01 // Wird hier noch nicht benoetigt
8
#define BIT_1  0x02
9
#define BIT_2  0x04
10
#define BIT_3  0x08
11
#define BIT_4  0x10
12
#define BIT_5  0x20
13
#define BIT_6  0x40
14
#define BIT_7  0x80
15
16
#endif // GENERAL_DEFINITIONS_H

Ich hoffe, ich habe es jetzt dann so einigermaßen verstanden.
Jetzt muss ich die nurnoch einzeln compilieren, mal sehen, wie das 
klappt.

Wieso muss in der dac_functions.c die dac_functions.h eingebunden 
werden?

von Karl H. (kbuchegg)


Lesenswert?

Die case-Durchfaller bei

    case DAC_START_SM:                           // Restart 
state-machine

und

    case DAC_BUILD_TRANSMIT_DATA:                // Build data-bytes

sind Absicht?

In solchen Fällen schreib ich mir das in einem Kommentar dazu, dass hier 
der break absichtlich fehlt. Denn in 2 Monaten weiß ich nicht mehr, ob 
ich den einfach nur vergessen habe oder ob ich ihn absichtlich 
weggelassen habe, weil die Behandlung eines case Punktes auch die 
Abarbeitung des nächsten case-Punktes beinhaltet. Das ist nämlich 
besonders dann interessant, wenn ein Schlauberger anfängt die cases 
umzusortieren. Dann landet der Fallthrough plötzlich in einem ganz 
anderen case.


> Wieso muss in der dac_functions.c die dac_functions.h
> eingebunden werden?

Du musst nicht.
Aber stell dir vor was passiert, wenn du im Header File hast

void foo( double i );

und in der IMplementierung steht

void foo( int i )
{
  ...
}

Inkludiert das C-File sein eigenes Header File, dann kann der Compiler 
das prüfen und gegebenenfalls melden. Includierst du es nicht, dann hast 
du gerade deine Sicherung gegen solche Fehler weggeworfen.

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die case-Durchfaller

Ja, in diesem Fall sind sie Absicht, weil es danach direkt losgehen 
kann. Aber hast evtl. Recht, das könnte man dazu schreiben.

Karl Heinz Buchegger schrieb:
> Inkludiert das C-File sein eigenes Header File, dann kann der Compiler
> das prüfen und gegebenenfalls melden. Includierst du es nicht, dann hast
> du gerade deine Sicherung gegen solche Fehler weggeworfen.

OK, alles klar! Vielen Dank!!!!!!

von Hans (Gast)


Lesenswert?

OK, eigentlich können die Cases da ganz weg! ...

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.