Forum: Mikrocontroller und Digitale Elektronik MSP430 Gleitkommazahl in Flash speichern


von A. H. (dernetteeddie1978)


Angehängte Dateien:

Lesenswert?

Hallo Kollegen,

ich möchte eine größere Anzahl Gleitkommazahlen im Flash ablegen.
Hierzu hab ich angehängte Datei von der TI Homepage genommen und zum 
experimentieren erstmal modifiziert.

Für den Anfang will ich nur einen Wert von 18.4 speichern.

Ich bin noch sehr neu im ganzen Thema Mikrokontrollerprogrammierung und 
bin mir gar nicht sicher ob das mit Gleitkommazahlen überhaupt so ohne 
weiteres geht. Ich hatte die Hoffnung der Compiler rechnet die Zahl 
selbstständig in die entsprechende Darstellung mit Mantisse etc. um und 
schiebt sie dann in den Flash.

Leider wird immer nur der Ganzzahlanteil, also 18, in den Speicher 
geschrieben. Binär:

11111111111111111111111100010010 statt
0 10000011 00100110011001100110011



Hier erstmal der Code, Chip ist ein MSP430G2452 auf Launchpad. Compiler 
der CCS:
1
#include <msp430.h>
2
3
float value;                                // 8-bit value to write to segment A
4
5
// Function prototypes
6
void write_SegC (float value);
7
8
9
int main(void)
10
{
11
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
12
  if (CALBC1_1MHZ==0xFF)          // If calibration constant erased
13
  {
14
    while(1);                               // do not load, trap CPU!!
15
  }
16
  DCOCTL = 0;                               // Select lowest DCOx and MODx settings
17
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
18
  DCOCTL = CALDCO_1MHZ;
19
  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator
20
  value = 18.4;                                // initialize value
21
22
  write_SegC(value);                    // Write segment C, increment value
23
}
24
25
void write_SegC (float value)
26
{
27
  char *Flash_ptr;                          // Flash pointer
28
  unsigned int i;
29
30
  Flash_ptr =  (char*) 0x1040;              // Initialize Flash pointer
31
  FCTL1 = FWKEY + ERASE;                    // Set Erase bit
32
  FCTL3 = FWKEY;                            // Clear Lock bit
33
  *Flash_ptr = 0;                           // Dummy write to erase Flash segment
34
35
  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
36
37
  for (i=0; i<16; i++)
38
  {
39
    *Flash_ptr = value;                   // Write value to flash
40
     Flash_ptr = Flash_ptr + 4;
41
  }
42
43
  FCTL1 = FWKEY;                            // Clear WRT bit
44
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
45
}

Interessant ist noch, dass wenn ich einen Breakpoint in der 
Unterfunktion setze, der Debugger für die Variable Value immer den Wert 
13107.0 zeigt.

Nachdem aus der Unterfunktion wieder rausgesprungen wird, enthält die 
Variable wieder den korrekten Wert.

Weiß jemand Rat?

Lg
Eddie

von es kommt noch mehr (Gast)


Lesenswert?

A. H. schrieb:
> Weiß jemand Rat?

Ja, ändere den Aufrauf

  write_SegC(18.4);

von es kommt noch mehr (Gast)


Lesenswert?

und dann   char *Flash_ptr;                          // Flash pointer

nach float *Flash_ptr;

von es kommt später mehr (Gast)


Lesenswert?

und      Flash_ptr = Flash_ptr + 4;
zu Flash_ptr++;

von A. H. (dernetteeddie1978)


Lesenswert?

Okay, habs grad versucht. Keine Änderung.

>und dann   char *Flash_ptr;                          // Flash pointer

>nach float *Flash_ptr;

Hatte ich im Vorfeld schon probiert, dann nimmt er aber die 
Adresszuweisung für den Pointer nicht mehr.


>und      Flash_ptr = Flash_ptr + 4;
>zu Flash_ptr++;

hatte ich geändert weil ja die Gleitkommazahl in einen 32Bit Wert 
umgerechnet werden hätte umgerechnet werden müssen.

von ________________________ (Gast)


Lesenswert?

A. H. schrieb:
> Adresszuweisung für den Pointer nicht mehr

Flash_ptr =  (float*) 0x1040;

von A. H. (dernetteeddie1978)


Lesenswert?

Nimmt er leider auch nicht :(

Aber trotzdem schonmal dickes Danke für die Mühe!

von wv (Gast)


Angehängte Dateien:

Lesenswert?

Ich kann jetzt auch nicht sagen, warum er nicht auf (float *) casten 
will, aber ich hab mich vor ein paar Wochen mal mit der ganzen 
Flash-Problematik auseinandergesetzt.

Die TI-Beispiele sind da sehr primitiv, sie sollen ja auch nur 
verdeutlichen wie das prinzipiell geht. Da wird eine Variable auf dem 
RAM angelegt, dann der Zeiger verbogen und ins Flash geschrieben. 
Außerdem ist das Löschen des Segments und das Schreiben der Variablen in 
ein und derselben Funktion, d.h. Du kannst immer nur die eine Variable 
schreiben, der Rest ist futsch.

In meiner flash.c habe ich also eine Funktion zum Löschen und 
verschiedene Funktionen zum Schreiben und hab jetzt mal eben eine 
Funktion für float dazugebastelt. Also man löscht erst das Segment, und 
dann kann man nach belieben Schreiben.

Außerdem lege ich mit einer Compiler-Direktive die Variable direkt ins 
Flash. Das hat den Vorteil, daß man jederzeit, ohne eine Funktion 
benutzten zu müssen direkt auf die Variable zurgreifen kann, allerdings 
funktionier eine normale Wertzuweisung value=18.5 natürlich nicht mehr, 
stattdessen muß man schreiben:

writeFloat(&value , 18.5)

und auf diesen Speicherplatz darf seit dem letzten Löschen noch nicht 
geschrieben worden sein.

Ach und nochwas: daß beim Debuggen der Wert der Variablen nicht oder 
falsch angezeigt wird, ist normal. Wahrscheinlich liegt das daran, daß 
während des Schreibens kein Lese- oder sonstiger Zugriff aufs Flash 
erlaubt ist, vermutlich funktioniert dadurch auch das Debuggen nicht.

Also bei mir funktioniert das Beispiel in der main.c (CCS 5.5), die 
Werte stehen danach korrekt im Flash.

Gruß wv

von A. H. (dernetteeddie1978)


Lesenswert?

Hi,

schonmal danke für eure umfangreichen Mühen! Ich habs jetzt hinbekommen. 
Es ging so wie ihrs mir schon beschrieben habt, per cast. Offenbar hatte 
ich beim ersten Versuch irgendwo was anderes falsch gemacht.
1
#include <msp430.h>
2
3
float value;                                // 8-bit value to write to segment A
4
5
// Function prototypes
6
void write_SegC (float value);
7
8
9
int main(void)
10
{
11
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
12
  if (CALBC1_1MHZ==0xFF)          // If calibration constant erased
13
  {
14
    while(1);                               // do not load, trap CPU!!
15
  }
16
  DCOCTL = 0;                               // Select lowest DCOx and MODx settings
17
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
18
  DCOCTL = CALDCO_1MHZ;
19
  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator
20
  value = 18.4;                                // initialize value
21
22
  write_SegC(value);                    // Write segment C, increment value
23
}
24
25
void write_SegC (float value)
26
{
27
  float *Flash_ptr;                          // Flash pointer
28
  unsigned int i;
29
30
  Flash_ptr =  (float*) 0x1040;              // Initialize Flash pointer
31
  FCTL1 = FWKEY + ERASE;                    // Set Erase bit
32
  FCTL3 = FWKEY;                            // Clear Lock bit
33
  *Flash_ptr = 0;                           // Dummy write to erase Flash segment
34
35
  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
36
37
  for (i=0; i<16; i++)
38
  {
39
    *Flash_ptr = value;                   // Write value to flash
40
     Flash_ptr = Flash_ptr + 4;
41
  }
42
43
  FCTL1 = FWKEY;                            // Clear WRT bit
44
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
45
}

: Bearbeitet durch User
von A. H. (dernetteeddie1978)


Angehängte Dateien:

Lesenswert?

Ich hab nun mein Programm etwas geändert. Nun wollte ich denselben 
Float-Wert in den Haupt Flash bringen. Hierfür habe ich erstmal die 
ersten zwei Segmente gelöscht und schreibe dann 1 KB ins zweite Segment.

Interessant ist, dass ich nun den Start des Pointers auf das erste Byte 
setzen muss also auf 0xFBFC statt auf 0cFBFF sonst wird der Wert falsch 
in den Flash geschrieben. Hier nochmal zur Erläuterung:

1.KB im Flash geht von 0xFFFF bis 0xFBC0
2.KB im Flash geht von 0xFBFF bis 0xF800

Im ersten Codebeispiel habe ich den Wert in den Information Memory 
geschrieben und den Pointer auf das hinterste der 4 Byte gesetzt.
Hier im Main Flash muss der Pointer offenbar auf das erste der 4 Byte 
zeigen.

Hier richtig:
1
/* --COPYRIGHT--,BSD_EX
2
 * Copyright (c) 2012, Texas Instruments Incorporated
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 *
9
 * *  Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 *
12
 * *  Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * *  Neither the name of Texas Instruments Incorporated nor the names of
17
 *    its contributors may be used to endorse or promote products derived
18
 *    from this software without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
 *
32
 *******************************************************************************
33
 *
34
 *                       MSP430 CODE EXAMPLE DISCLAIMER
35
 *
36
 * MSP430 code examples are self-contained low-level programs that typically
37
 * demonstrate a single peripheral function or device feature in a highly
38
 * concise manner. For this the code may rely on the device's power-on default
39
 * register values and settings such as the clock configuration and care must
40
 * be taken when combining code from several examples to avoid potential side
41
 * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware
42
 * for an API functional library-approach to peripheral configuration.
43
 *
44
 * --/COPYRIGHT--*/
45
//******************************************************************************
46
//  MSP430G2xx3 Demo - Flash In-System Programming, Copy SegC to SegD
47
//
48
//  Description: This program first erases flash seg C, then it increments all
49
//  values in seg C, then it erases seg D, then copies seg C to seg D.
50
//  Assumed MCLK 771kHz - 1428kHz.
51
//  //* Set Breakpoint on NOP in the Mainloop to avoid Stressing Flash *//
52
//
53
//               MSP430G2xx3
54
//            -----------------
55
//        /|\|              XIN|-
56
//         | |                 |
57
//         --|RST          XOUT|-
58
//           |                 |
59
//
60
//  D. Dang
61
//  Texas Instruments Inc.
62
//  December 2010
63
//   Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10
64
//******************************************************************************
65
66
#include <msp430.h>
67
68
 float value;                             // 8-bit value to write to segment A
69
70
void write_SegC (float value);
71
72
int main(void)
73
{
74
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
75
  if (CALBC1_1MHZ==0xFF)          // If calibration constant erased
76
  {
77
    while(1);                               // do not load, trap CPU!!
78
  }
79
  DCOCTL = 0;                               // Select lowest DCOx and MODx settings
80
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
81
  DCOCTL = CALDCO_1MHZ;
82
  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator
83
  value = 18.4;                             // initialize value
84
85
  write_SegC(value);                      // Write segment C, increment value
86
}
87
88
void write_SegC (float value)
89
{
90
  float *Flash_ptr;                          // Flash pointer
91
  unsigned int i;
92
93
  Flash_ptr =  (float*) 0xFFFF;             // Initialize Flash pointer
94
  for (i=0;i<2048;i+=64)
95
  {
96
    while (FCTL3&BUSY);
97
    FCTL1 = FWKEY + ERASE;                   // Set Erase bit
98
    FCTL3 = FWKEY;                           // Clear Lock bit
99
   *Flash_ptr = 0;                           // Dummy write to erase Flash segment
100
    Flash_ptr = (float*) (0xFFFF-i);
101
  }
102
103
  Flash_ptr =  (float*) 0xFBFC;
104
  for (i=0; i<256; i++)
105
  {
106
   while (FCTL3&BUSY);
107
   FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
108
    *Flash_ptr = value;                     // Write value to flash
109
     while (FCTL3&BUSY);
110
     Flash_ptr--;
111
  }
112
113
  FCTL1 = FWKEY;                            // Clear WRT bit
114
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
115
}

Anfangs hatte ich den Pointer auf das letzte der 4 Byte gesetzt, also 
auf 0xFBFF und dann wurden die 4 Byte genau drum herum verteilt.

D.h. die ersten zwei gingen nach 0xF800 und 0xF801 und die anderen zwei 
Byte gingen nach 0xF7FC und 0xF7FD. Siehe hierzu auch mal das angehängte 
Dokument.

Gruß
Eddie

: Bearbeitet durch User
von wv (Gast)


Lesenswert?

Die Segmente im "Haupt"-flash sind 512 Byte groß, es reichen also 4 
Löschvorgänge um 2kB zu löschen. Außerdem stehen im obersten Segment von 
0xFE0 bis 0xFFFF die Interuptvektoren, dieses Segment würde ich auf 
keinen Fall löschen, sonst wunderst Du Dich später, wenn ein Interrupt 
im Nirwana landet.

Gruß wv

von Tipp (Gast)


Lesenswert?

Am Anfang bist du über die Überdeckung der Variablen value gestolpert. 
Du solltest eine der beiden umbennen.
Andere den Namen von value
   float value;
Und passe den Aufruf an
   write_SegC(value);

von A. H. (dernetteeddie1978)


Angehängte Dateien:

Lesenswert?

Hi,


>Am Anfang bist du über die Überdeckung der Variablen value gestolpert.

Ich weiß, aber eine Änderung der Benamung hat nichts geändert, daher hab 
ichs ersmal wieder so gelassen.

>Außerdem stehen im obersten Segment von
>0xFE0 bis 0xFFFF die Interuptvektoren

Ookay - muss ich die nun da wieder reinschreiben oder initialisiert sich 
das irgendwie von selbst?
Ich bin noch ziemlich neu in all dem und hab mit den Interrupts noch 
nicht viel gemacht.

Nachdem ich nach mehreren erfolglosen Versuchen das Segment A des 
I.Flash`s endlich erfolgreich beschreiben konnte, durfte ich dananch 
auch feststellen, dass ich mir die Kalibrierkonstanten damit 
überschrieben hatte.

Damit war der G2553 des Launchpads schon erstmal futsch. Irgendwann muss 
ich mal den Crystal aufs Board auflöten und dann gibt`s im Netz ja div. 
Programme die die da wieder reinschreiben. Zum Glück war ja noch der 
G2452 beim Launchpad dabei.

Wo steht denn eigentlich genau geschrieben wo welche Speicherbereiche im 
Chip sind? Die Doku von TI ist da irgendwie sehr vage.

Im Datenblatt des Chips ist nur ne kleine Tabelle (siehe Seite 11).
In der Family description ist auch nur ne prinzipielle Angabe (siehe 
Seite 310).

Hab die Datenblätter mal angehängt.

LG
Eddie

: Bearbeitet durch User
von wv (Gast)


Lesenswert?

Hallo Eddie,

das mit den Interruptvektoren ist nicht so schlimm, beim nächsten Laden 
des Programms sind sie wieder da. Nur funktioniert so ein Programm 
nicht, wenn man Interrupts benutzt. Wenn man also unbedingt während des 
Programmlaufs in das oberste Segment was schreiben will, muß man vorher 
die Vektoren sichern und danach wieder reinschreiben.

Anders verhält es sich mit den Kalibrierdaten, die werden bei der 
Herstellung des Chips reingeschrieben und sind dann da bis Du sie 
löschst...

Die Anordnung der Speicherbereiche findest Du im Datenblatt des 
jeweiligen Chips, da ja die Größe von Flash und Ram unterschiedlich ist. 
Das Schema ist aber immer das gleiche:

0x0000 - 0x000F CPU - Register
0x0010 - 0x00FF 8 Bit Periferie z.B. IO Ports
0x0100 - 0x01FF 16 Bit Periferie z.B. Timer
0x0200 bis je nachdem wieviel Ram der Chip hat, dort sitzen
       dei Variablen, Stackpointer usw.                       RAM
                                     ------------------------------------
                je nach Ram-Größe ist hier ein Loch
                                     ------------------------------------
                                                              FLASH
0x1000 InfoD
0x1040 InfoC
0x1080 InfoB
0x10C0 InfoA

                         --------------------
                             Loch
                         --------------------

je nach Größe des Flash
beginnt dieser nun hier irgendwo
und geht immer bis

0xFFFF                                              FLASH ENDE
                                                   --------------

Dann lohnt sich auch ein Blick in das Linker - Command - File, da steht 
die Information für den Linker, wo welche Speicherbereiche im Chip zur 
Verfügung stehen. Das File steht im Haupverzeichnis deines Projekts, und 
heißt lnk_msp430gxxxx.cmd, je nach verwendetem Chip.

Außerdem entsteht nach jedem Build deines Projekts im Unterverzeichnis 
\Debug deines Projekts eine Datei, die meinProjekt.map heißt. Das ist 
die Ausgabe des Linkers, dort kann man genau sehen, wo welche Variablen 
gelandet sind.

Allerdings weiß der Linker natürlich nichts von Deinen verbogenen 
Zeigern, und daran sieht man auch die ganze Problematik dieser 
Geschichte. Ich weiß, daß Du ein Beispiel von TI hier modifiziert hast. 
Das ist auch soweit ok, um sich mit den ganzen Funktionen rund ums Flash 
vertraut zu machen. Allerdings funktioniert das alles nicht mehr, wenn 
das Programm größer wird, wie Du am Beispiel der Interruptvektoren 
gesehen hast, denn solange Du sie nicht benutzt, merkst Du auch nicht 
wenn sie weg sind. Wenn Du aber später irgendeine Funktion hinzufügts, 
die sie benutzt, wird Dir das ganze mit sehr merkwürdigen Effekten 
abschmieren.

Genauso kann das passieren, wenn Du einfach Zeiger verbiegst, und 
irgendwelche Segmente löschst. An Anfang ist Dein Programm klein, die 
oberen Segmente sind alle leer (bis auf die besagten Interruptvektoren), 
und dann erweiterst Du Dein Programm immer mehr, und dann wird 
irgendwann, wenn Du nicht mehr an diese Funktion denkst, löscht Dir 
deine eigene Funktion Teile Deines Programms.....

Ich finde es auch nicht gerade professionell von TI, so ein Beispiel in 
die Welt zu setzen, ohne das weiter zu kommentieren oder wenigstens 
einen Anhaltspunkt zu geben, wie man das vernünftig angeht.

Gruß wv

von A. H. (dernetteeddie1978)


Lesenswert?

Hey super, vielen Dank. Ich habs jetzt auch erstmal so programmiert dass 
das erste (eigentlich letzte Segment bis 0xFFFF) nicht gelöscht wird 
sondern die zwei Segmente oben drüber.

Danke für die vielen Mühen von überall.
Beim nächsten Mal muss ich das dann mal analog deiner Beispiele machen. 
Dann ist auf jeden Fall übersichticher und man bringt sich nicht so 
schnell selbst aus dem Tritt.

Auch die viele Adressenrechnerei im HEX-Format kann einen ganz schön 
durcheinander bringen. So hab ich mich auch erstmal voll vertan als ich 
die Adresse des zweiten Segmentes ausrechnen wollte und hab doch glatt 
0xFFFF-512 statt - des Hexwertes davon  - also 0x200 - gerechnet und 
mich dann gewundert wieso ich nicht genau auf dem letzten Byte des 
nächsten Segmentes lande. Aber gut, dann hab ichs doch noch geschnallt:)

Hier nochmal mein fertiger Code:
1
/* --COPYRIGHT--,BSD_EX
2
 * Copyright (c) 2012, Texas Instruments Incorporated
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 *
9
 * *  Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 *
12
 * *  Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * *  Neither the name of Texas Instruments Incorporated nor the names of
17
 *    its contributors may be used to endorse or promote products derived
18
 *    from this software without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
 *
32
 *******************************************************************************
33
 *
34
 *                       MSP430 CODE EXAMPLE DISCLAIMER
35
 *
36
 * MSP430 code examples are self-contained low-level programs that typically
37
 * demonstrate a single peripheral function or device feature in a highly
38
 * concise manner. For this the code may rely on the device's power-on default
39
 * register values and settings such as the clock configuration and care must
40
 * be taken when combining code from several examples to avoid potential side
41
 * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware
42
 * for an API functional library-approach to peripheral configuration.
43
 *
44
 * --/COPYRIGHT--*/
45
//******************************************************************************
46
//  MSP430G2xx3 Demo - Flash In-System Programming, Copy SegC to SegD
47
//
48
//  Description: This program first erases flash seg C, then it increments all
49
//  values in seg C, then it erases seg D, then copies seg C to seg D.
50
//  Assumed MCLK 771kHz - 1428kHz.
51
//  //* Set Breakpoint on NOP in the Mainloop to avoid Stressing Flash *//
52
//
53
//               MSP430G2xx3
54
//            -----------------
55
//        /|\|              XIN|-
56
//         | |                 |
57
//         --|RST          XOUT|-
58
//           |                 |
59
//
60
//  D. Dang
61
//  Texas Instruments Inc.
62
//  December 2010
63
//   Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10
64
//******************************************************************************
65
66
#include <msp430.h>
67
68
 float value;                             // 8-bit value to write to segment A
69
70
void write_SegC (float value);
71
72
int main(void)
73
{
74
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
75
  if (CALBC1_1MHZ==0xFF)          // If calibration constant erased
76
  {
77
    while(1);                               // do not load, trap CPU!!
78
  }
79
  DCOCTL = 0;                               // Select lowest DCOx and MODx settings
80
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
81
  DCOCTL = CALDCO_1MHZ;
82
  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator
83
  value = 18.4;                             // initialize value
84
85
  write_SegC(value);                      // Write segment C, increment value
86
}
87
88
void write_SegC (float value)
89
{
90
  float *Flash_ptr;                          // Flash pointer
91
  unsigned int i;
92
93
  Flash_ptr =  (float*) 0xFDFC;             // Initialize Flash pointer
94
  for (i=0;i<1024;i+=512)
95
  {
96
    while (FCTL3&BUSY);
97
    FCTL1 = FWKEY + ERASE;                   // Set Erase bit
98
    FCTL3 = FWKEY;                           // Clear Lock bit
99
   *Flash_ptr = 0;                           // Dummy write to erase Flash segment
100
    Flash_ptr = (float*) (0xFDFC-i);
101
  }
102
103
  Flash_ptr =  (float*) 0xFDFC;
104
  for (i=0; i<256; i++)
105
  {
106
   while (FCTL3&BUSY);
107
   FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
108
    *Flash_ptr = value;                     // Write value to flash
109
     while (FCTL3&BUSY);
110
     Flash_ptr--;
111
  }
112
113
  FCTL1 = FWKEY;                            // Clear WRT bit
114
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
115
}

Übrigens  - was sind eigentlich Compilerdirektiven?

: Bearbeitet durch User
von wv (Gast)


Lesenswert?

A. H. schrieb:
> Übrigens  - was sind eigentlich Compilerdirektiven?

grob gesagt, alles was mit #pragma anfängt.

Normalerweise "weiß" der Compiler, was er mit einem .c und einem .h file 
anzufangen hat, genauso der Linker. Diese Direktiven dienen dazu, dieses 
Verhalten zu beeinflussen, wo es notwendig ist.

In unserem Beispiel bedeutet die Deklatation der Variablen value

float value;

für den Compiler, daß er Speicherplatz für eine float-Variable 
bereitstellen muß. Nun schaut er in der Runtime-Support-Library nach, 
wie groß eine float auf diesem System ist, und reserviert eine zunächst 
virtuelle Adresse und die drei folgenden (insgesamt 4) Byte. Dem Linker 
wurde der zur Verfügung stehende Speicher über das Linker-Command-File 
mitgeteilt. Da es sich um ein variable Größe handelt, wird ihr nun eine 
feste Adresse im RAM zugewiesen.

Nun wollten wir aber eigentlich Speicherplatz auf dem Flash haben, und 
wollen das dem Linker mitteilen, also schreiben wir:

#pragma DATA_SECTION(value , ".infoC")
float value;

Jetzt weiß der Linker (dem Compiler ist das egal), daß er diesen 
Speicher im Flash, und zwar genau im infoC reservieren muß. Es geht also 
ganz ohne Hex-Rechnerei, das erledigt nämlich der Linker, der dann auch 
darauf achtet, daß sich nichts in die Quere kommt.

Man kann so auch mehrere Variablen oder ganze Bereiche im Flash 
reservieren. Wenn Du also ein ganzes Segment vollschreiben willst:

#pragma DATA_SECTION(value , ".infoC")
float value[16];

Ich bleibe erstmal absichtlich beim InfoMem, da gehts etwas einfacher, 
weil diese Bereiche im Linker-Command-File vordefiniert sind, der 
restliche Bereich des Flash ist dort ein ganzer Block. Es geht aber 
auch, dazu später.

Jetzt können wir die Funktion aus dem TI-Beispiel nehmen (siehe meine 
flash.c, flash.h) aber immer Löschen und Schreiben trennen. Du willst ja 
später sicherlich irgendwelche Sensorenwerte lesen und speichern, die so 
nach und nach eintrudeln, dann kannst Du nicht bei jedem Aufruf das 
Flash wieder löschen.

----
1
//Prototypes
2
void writeFloat(float *Flash_ptr, float value);
3
void eraseSegment (void *pointer);
4
5
//Array-Variable im Flash
6
#pragma DATA_SECTION(value , ".infoC")
7
float value[16];
8
int i;
9
10
11
12
int main(void)
13
{
14
  //Deine Initialisierung ist soweit richtig
15
  //..
16
  
17
  eraseSegment(&value);
18
19
  for (i=0 ; i<16 ; i++)
20
  {
21
    writeFloat(&value[i], 18.5);
22
  }
23
}
24
25
void writeFloat(float *Flash_ptr, float value)
26
{
27
  FCTL3 = FWKEY; // Clear Lock bit
28
  FCTL1 = FWKEY + WRT; // Set WRT bit for write operation
29
  *Flash_ptr = value; // Write value to flash
30
  FCTL1 = FWKEY; // Clear WRT bit
31
  FCTL3 = FWKEY + LOCK; // Set LOCK bit
32
}
33
34
void eraseSegment (void *pointer)
35
{
36
  char *seg_pointer;
37
38
  seg_pointer = pointer;
39
  FCTL1 = FWKEY + ERASE; // Set Erase bit
40
  FCTL3 = FWKEY; // Clear Lock bit
41
  *seg_pointer = 0; // Dummy write to erase Flash segment
42
  FCTL1 = FWKEY; // Clear Erase bit
43
  FCTL3 = FWKEY + LOCK; // Set LOCK bit
44
}

Du siehst, es geht alles auch ohne Hex-Rechnerei. Die Funktionen sind 
für alle Info-Segmente D,B,C anwendbar, mit einer kleinen Änderung am 
Linker-Command-File geht's auch in den anderen Segmenten, da muß man 
dann doch ein bißchen rechnen.

von A. H. (dernetteeddie1978)


Lesenswert?

Die spannende Frage hierbei ist noch:

Ich möchte Sinuswerte im Flash ablegen. Dazu hab ich sie in einem Float 
Array definiert und schreibe sie dann in den Flash. Das funktioniert 
soweit auch, aber wenn ich sie doch eh im Array festlege, wieso schreibe 
ich sie dann nochmal in den Flash? Das ist ja nur doppelter 
Speicherverbrauch. Einmal im Flash und einmal im RAM. Aber ein anderes 
mittel gibts ja auch nicht. Egal ob ich deine oder die Methode aus dem 
TI Beispiel benutze, ich muss die werte ja immer erst mal in irgendeiner 
Art von Variablen/ Constanten ablegen damit ich sie in den Flash 
schieben kann.

LG

von Ich raffs nett (Gast)


Lesenswert?

A. H. schrieb:
> Ich möchte Sinuswerte im Flash ablegen.

Wenn du die Werte bereits zur Übersetzung kennst, kannst du sie direkt 
als Konstantanten ablegen. Sie landen dann nur im Flash. Du kannst sie 
dann direkt aus dem Flash ausgeben, oder nach einander über eine einzige 
Variable einzeln laden und verarbeiten.

Beschreib einmal dein Projekt.

von A. H. (dernetteeddie1978)


Lesenswert?

Ich möchte per PWM einen Sinus erzeugen. Dazu möchte ich einen 
Viertel-Sinus als Werte speichern und dann entsprechend für den duty 
cycle verwenden.

Nun hatte ich überall gelesen dass die Leute die Werte in den Flash 
legen und von da abfragen. Also wollte ich das gleich tun. Darauf 
bezieht sich der obige Thread. Nun, mittlerweile hab ichs hinbekommen, 
frag mich aber halt wieviel Sinn das macht, da ich die Werte ja eh als 
Konstanten im RAM liegen haben muss um sie von da in den Flash schreiben 
zu können.

LG

von Ich raffs nett (Gast)


Lesenswert?

A. H. schrieb:
> frag mich aber halt wieviel Sinn das macht, da ich die Werte ja eh als
> Konstanten im RAM liegen haben muss um sie von da in den Flash schreiben
> zu können.

Nein, du brauchst die Variablen nicht im RAM! Du kannst das array als 
Konstanten vollständig im Flash ablegen.

Zu deinem Verständnis: Wie kommen die Werte zur Laufzeit ins RAM? 
Natürlich aus dem Flash. Bei deiner Variante hast du dreifachen Speicher 
verbraucht.
- Flash zu RAM Initialisierung
- Werte im RAM
- Flash zur Laufzeit beschrieben

Puhhhh ...

von A. H. (dernetteeddie1978)


Lesenswert?

Okay,

d.h. wenn ich sie ganz normal in ein Array deklariere, dann sind sie 
doch erst mal im RAM dachte ich.

von Ich raffs nett (Gast)


Lesenswert?

A. H. schrieb:
> wenn ich sie ganz normal in ein Array deklariere

Das stimmt. Du musst dem Linker sagen, wo es lang geht:
http://processors.wiki.ti.com/index.php/Placing_Variables_in_Specific_Memory_Location_-_MSP430

von wv (Gast)


Lesenswert?

A. H. schrieb:
> wenn ich sie ganz normal in ein Array deklariere, dann sind sie
> doch erst mal im RAM dachte ich.

Jein.

float myArray[];          ---> RAM

const float myArray[] = {0.12 , 0.17 ... usw};    ---> Flash

Wenn es sich um konstante Werte handelt, wie bei Deiner Sinus-Tabelle, 
dann definiert man sie einfach als Konstante. Dadurch landen sie 
automatisch im Flash.

Die ganzen anderen Funktionen, die auch teilweise spezielle 
Linker-Anweisungen benötigen, braucht man nur, wenn man während der 
Laufzeit ins Flash schreiben will, beispielsweise um Sensorenwerte zu 
loggen.

Gruß wv

von Max G. (l0wside) Benutzerseite


Lesenswert?

wv schrieb:
> float myArray[];          ---> RAM
>
> const float myArray[] = {0.12 , 0.17 ... usw};    ---> Flash

Und ganz fies:
float myArray[] = {0.12 , 0.17 ... usw};    ---> RAM

Wird beim Starten initialisiert. Wenn man Pech hat, schlägt in dieser 
Zeit schon der Watchdog zu.
Dann muss man in _system_pre_init() den Watchdog ausschalten. BTDT.

Max

von A. H. (dernetteeddie1978)


Lesenswert?

Okay, danke. Dann hab ich die jetzt erstmal per const in den Flash 
geschickt.

Das Ganze soll ja ne Sinus Pwm werden. Häng da jetzt grad an der 
nächsten Stelle. Aber da mach ich mal nen neuen Fred auf. Ist jetzt ein 
anderes Thema.

Schonmal riesen danke an alle Helfer

von Dieter B. (barth)


Lesenswert?

Hallo zusammen.

dieser Thread hat mir schon sehr viel weiter geholfen.
Ich habe versucht in einem individuellen Flash Sektor 0x100 groß
einen const struct zu initialisieren.

#pragma DATA_SECTION(Ueberstrom , ".Config")
const TMenueItem Ueberstrom = {'Ueberstrom', 1, 10, 1000}

die Initialisierung funktioniert mit dem Vorsatz const leider nicht.
Dann liegt die Variable zwar im Flash, ich kann aber mit der Funktion

void writeLong(Long *Flash_ptr, Long value)
{
  FCTL3 = FWKEY; // Clear Lock bit
  FCTL1 = FWKEY + WRT; // Set WRT bit for write operation
  *Flash_ptr = value; // Write value to flash
  FCTL1 = FWKEY; // Clear WRT bit
  FCTL3 = FWKEY + LOCK; // Set LOCK bit
  return;
}

den Wert nicht ändern und die Initialiserung wird nicht übernommen.
Habt ihr hierfür auch noch eine Idee?

Danke

von Frank (Gast)


Lesenswert?

Andere Frage, für was brauchst du bei einer Sinustabelle für die PWM 
Register eigentlich float?

von Helfer (Gast)


Lesenswert?

Du musst den Flashbereich natürlich vorm Schreiben löschen.

Im Code oben findest du die FKT. ereaseSegment ...

von Dieter B. (barth)


Lesenswert?

Hallo,

das habe ich auch herausgefunden. Allerdings ist ja dann der ganze 
Flashbereich gelöscht. gibt es auch eine Möglichkeit nur einen Wert zu 
ändern?
Wie macht man das sonst.
512 Byte im Ram zwischenpeichern und dann alles umkopieren und den 
gewünschten Wert ändern?

von wv (Gast)


Lesenswert?

Hallo Dieter,

man muß tatsächlich immer einen ganzen Block löschen, also muß man auch 
alles vorher ins RAM sichern, den geänderten Wert austauschen und wieder 
zurückschreiben.

Zeig doch mal den Ausschnitt von Deinem Linker-Command-File, wo du 
.Config definiert hast. Da muß Du nämlich verhindern, daß Dein struct 
über eine Blockgrenze geht, denn dann müßtest Du beide Blöcke sichern 
und löschen.

const muß bei der initialisierung weg, denn das ist eine 
Compileranweisung, den Wert nicht mehr zu ändern. Das #pragma 
DATA_SECTION legt die Variable bereits ins Flash.

Ob man jetzt wirklich jedesmal den ganzen Block sichern und 
zurückschreiben muß, ist eine Frage der Erfordernisse und der richtigen 
Strategie. Eventuell sind ja in Deinem TMenueItem Dinge die sich während 
der Laufzeit nicht ändern. Dann könnte man das ja aufteilen in eine 
Struktur, die wirklich konstant bleibt und eine weitere, eventuell 
kleinere Struktur mit veränderlichen Werten, die dann vielleicht ins 
InfoMem paßt, da braucht man dann nur 64 byte sichern und wieder 
zurückschreiben.

Gruß wv

von Helfer (Gast)


Lesenswert?

Strategie ist ein gutes Stichwort. Wenn immer nur ein oder wenige Werte 
gespeichert werden, kann man einen Rinbuffer aufbauen. Dann wird nur 
noch bei Überlauf gelöscht.

von Dieter B. (barth)


Lesenswert?

Hallo

danke für die Antworten.
Also TMenueItem ist wie der Name schon sagt ein Eintrag eines Menüs.
Also z.b. Überstrom. Hier ist eben der Name des Menüs, der Parent, Min, 
Max und eben der eigentliche Wert definiert.

Der Benutzer soll hier während der Laufzeit den eigentlichen Wert 
ändern.

Das Problem ist das das ganze Menü auf diesen Typen basiert und man sich 
mit Parent und Child durch das Menü durchhangelt. Funktioniert soweit 
auch wunderbar.

Dann werde ich mir jetzt eine Funktion basteln in der ich 512 byte in 
den Ram schiebe und dann wert veränder und dann wieder zurück.
Wenn jemand einen Ressourcen schonenden Vorschlag hat, nur her damit.
---------
Es handelt sich bei mir um den MSP430G2955
Erstes Flashsegment liegt hier bei 0x2100 Die länge habe ich hier mit 
0x200
definiert. Wenn ich mich nicht verlesen habe, sollte das ein Segment 
sein.
Hier würde ich dann alle Menüeinträge ablegen welche zur Laufzeit 
verändert werden können.

---------
Das Problem wenn ich const weg lasse, wird meine Variable nicht 
initialisiert.
Es steht in jedem Long dann -1 und das Char array wird auch nicht 
initialisiert.

--------
Den Vorschlag mit dem Ringpuffer habe ich noch nicht ganz verstanden.
Nur angenommen ich würde in TMenueItem.Value nicht den Wert selbst 
ablegen sondern nur einen Zeiger auf den Werte, würde ich zum einen mehr 
Speicher benötigen und zum anderen muss dieser Pointer ja auch irgendwie 
wider geschrieben werden und das ist ja in dem Fall auch wieder im 
Flash.
Aber vielleicht habe ich dich auch missverstanden.


Danke für eure Hilfe

von Helfer (Gast)


Lesenswert?

Mit dem Ringpuffer geht es nur für einzelne Werte. Man reserviert 
einfach das mehrfache an Speicherplatz. Der freie Speicher hat immer 
0xFFFF. Den kannst du mit einzelnen Werfern überscvhreibern. Den letzten 
Eintrag findest du durch die Suche nach 0xFFFF. Wenn alle pufferplätze 
voll sind, wird das Segment gelöscht und man fängt wieder am 
Pufferanfang an.

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.