Hi,
ich arbeite gerade an einem CAN Bootloader, der auf einem atmega32
laufen soll, welcher dazu einen MCP2515 via SPI verwendet, um mit dem
Bus zu kommunizieren.
Um die zum Flashen erforderlichen Daten auf den Bus zu bekommen verwende
ich einen atmega168, der ebenfalls einen MCP2515 via SPI verwendet,
mittels UART die Daten von meinem Computer gesendet bekommt und somit
sozusagen ein Gateway darstellt.
Momentan schreibe ich den "Bootloader" noch in den ganz normalen
Programmspeicher, da ich mich bis jetzt primär um das implementieren der
Datenübertragung gekümmert habe. Da die Datenübertragung nun aber
fehlerfrei funktioniert wollte ich mich an die eigentlichen Funktionen
eines Bootloaders machen, nämlich dem schreiben in den Speicher.
Da ich bis jetzt immer mit -O0 die Optimierung deaktiviert habe, hat
sich mein Code auf 7kbyte aufgebläht. Um die verfügbaren 4kbyte Boot
Section des atmega32 nicht zu überschreiten möchte ich den Schalter -Os
verwenden um mit Fokus auf Speicherplatz zu optimieren.
Durch diese Änderung entstehen jedoch einige Probleme, die ich als
zugegebener Amateur im Bereich Mikrocontroller und C nicht ganz
nachvollziehen kann. Mir ist bekannt, dass durch die Optimierung in
vielen Fällen Dinge übergangen werden, weil der Compiler denkt, dass
gewisse Sachen sich nicht ändern können usw. Bis jetzt bin ich aber
davon ausgegangen, dass sich dies nur auf globale Variablen in
Verbindung mit Interrupts beschränkt. Im Anhang hab ich mal den gesamten
Code angehangen, durch ausprobieren konnte ich aber schon feststellen,
dass es in folgenden Funktionen / Prozeduren hapert.
Ich kann mir aber leider nicht erklären, wo genau die Fehler sind und
hoffe, dass ihr mir dabei helfen könnt.
Folgende Prozedur verwende ich im Bootloader um während des Empfangens
der Daten, Daten an das UART-CAN Gateway zurück zu senden, die dann auf
der UART Schnittstelle ausgegeben werden. In erster Linie dient es in
der Testphase zum überprüfen der Daten. Im Moment verwende ich es jedoch
um einfach die Funktionalität der Kommunikation über CAN zu testen.
Leider funktioniert es im Moment nicht richtig.
In meinem Terminal werden korrekterweise die ersten 7 Zeichen aus
'input' ausgegeben, dann bleibt die Prozedur jedoch in der
'while(escape_flag == 0)' schleife stecken.
Zum Verständnis muss ich noch sagen, dass ich mir damit zuerst das
erfolgreiche empfangen und Verarbeiten der Daten vom UART-Gateway
quittieren lasse um einen Buffer-Overflow im CAN Controller des UART-CAN
Gateways zu verhindern, woraufhin weiter gesendet wird.
Dies geschieht durch das Empfangen einer CAN-Botschaft (das empfangen
einer Nachricht signalisiert der CAN-Controller über das auf LOW ziehen
von 'PD7') mit der ID = 1 und dem Wert
'BOOTLOADER_MESSAGE_CLEAR_TO_SEND' im ersten Datenfeld der
CAN-Botschaft.
1 | void write_bootloader_message_buffered_to_can(uint16_t length, char *input){
| 2 | static uint16_t offset = 0;
| 3 | uint8_t loops = (length / 7)+1;
| 4 |
| 5 | for(uint8_t i=0;i<loops;i++){
| 6 | uint8_t len = 7;
| 7 | if(i+1 == loops){
| 8 | len = length % 7;
| 9 | }
| 10 |
| 11 | char *data = malloc(sizeof(char)*(len+1));
| 12 | memcpy(data+1,input+offset, (size_t)len);
| 13 | data[0] = BOOTLOADER_MESSAGE;
| 14 | offset += len;
| 15 |
| 16 | write_message_to_can(DEVICE_ID,len+1,data);
| 17 | free(data);
| 18 |
| 19 | uint8_t escape_flag = 0;
| 20 | while(escape_flag == 0){
| 21 |
| 22 | if((PIND & (1<<PD7)) == 0){
| 23 | Message *message;
| 24 | handle_interrupt(message);
| 25 | //escape_flag = 1;
| 26 | if((message->id == 1) && (message->data[0] == BOOTLOADER_MESSAGE_CLEAR_TO_SEND)){
| 27 | escape_flag = 1;
| 28 | }
| 29 | }
| 30 | }
| 31 | }
| 32 | offset = 0;
| 33 | }
|
(File: can_interface.c Line:63)
Genau hier tritt jedoch das Problem auf. Die ID wird fälschlicherweise
mit einem wert von 30-40 angegeben (kann es leider nicht genau sagen, da
ich es mir durch eine blinkende LED ausgeben lassen musste), den Inhalt
von 'data[0]' habe ich mir gar nicht erst anzeigen lassen.
Ich vermute, dass in der 'handle_interrupt(message);' welche die Daten
per SPI aus dem MCP2515 ausließt ein Konflikt mit der Optimierung
auftaucht, da das Programm bis zu diesem Punkt problemlos arbeitet.
Die 'handle_interrupt(message);' sieht wie folgt aus und verwendet
einige weitere Funktionen / Prozeduren aus 'can2515.c':
1 | uint8_t handle_interrupt(Message *message)
| 2 | {
| 3 | uint8_t status = read_rx_status();
| 4 |
| 5 | set_cs(LOW);
| 6 |
| 7 | if(status & (1<<3)){
| 8 | message->rtr = 1;
| 9 | }
| 10 | if (status & (1 << 6)) {
| 11 | spi_putc(SPI_READ_RX);
| 12 | get_message_from_buffer(0, message);
| 13 | set_cs(HIGH);
| 14 | return MESSAGE_IN_BUFFER_0;
| 15 | } else if (status & (1 << 7)) {
| 16 | spi_putc(SPI_READ_RX | 0x04);
| 17 | get_message_from_buffer(1, message);
| 18 | set_cs(HIGH);
| 19 | return MESSAGE_IN_BUFFER_1;
| 20 | }else{
| 21 | set_cs(HIGH);
| 22 | return NO_MESSAGE;
| 23 | }
| 24 |
| 25 | }
|
(File: can2515.c Line: 114)
Da ich 'can2515.c' für Testzwecke auch in anderen Programmen verwendet
habe und z.B. auch Grundstein des CAN-UART Gateway Programms ist,
befinden sich die Dateien 'can2515.c/h', 'spi.c/h' und 'regs.h' nicht
direkt in diesem Projekt sondern werden gesondert als Library gebaut und
dann eingebunden. Ich hab auch das Makefile dafür in den Anhang gepackt.
Ich arbeite auf einem Linux System und und verwende avr-gcc 4.3.5.
Ich hoffe ich habe bei dem vielen Text nichts wichtiges vergessen und
bedanke mich jetzt schon mal fürs lesen.
Ich poste natürlich gerne weitere wissenswerte Informationen.
Gruß,
toti
1 | Message *message;
| 2 | handle_interrupt(message);
|
Du hast gar keinen Speicher für deine Message reserviert. message ist
ein Pointer, der einfach irgendwo in den Speicher zeigt. Wenn das mit
-O0 funktioniert hat, war das purer Zufall.
Richtig wäre z.B.: 1 | Message message;
| 2 | handle_interrupt(&message);
|
Sowas sollte der Compiler eigentlich anzeigen. Warnungen ignoriert?
Noch eine kleine Extrabemerkung:
Wozu das umständliche malloc/free für data? Warum nicht einfach
data[8]? Ist doch egal, wenn im letzten Schleifendurchlauf nicht alle 8
gebraucht werden.
Hi,
so viel zum Thema Amateur in C ;) Vielen Dank, das wars wirklich. Ich
weiß nicht wirklich was mich da geritten hat. Ich glaube ich bin
dummerweise davon ausgegangen, dass der Compiler bei einem 'struct'
anders handelt und den Speicher reserviert. Dummer Gedanke, der dann
durch den Erfolg beim Verzicht auf Optimierung auch noch gefördert
wurde.
Bezüglich Warnungen war dar Hinweis darauf, dass ohne Optimierung die
Funktionen aus dem Header 'delay.h' nicht funktionieren würden das
einzige. Beim Compilen mit -Os lief der Compiler ohne eine einzige
Warnung durch. An der Stelle würde mich jetzt auch interessieren, hätte
der Compiler ein 'Warning' ausgeben müssen?
Was mich auch noch interessieren würde ist, ob es wirklich reiner Zufall
war, dass es ohne Optimierung funktioniert hat oder ob es daran liegt,
dass der Compiler um Rechenzeit während des Compilens zu sparen einfach
pauschal immer Speicher mit anlegt? Würde ja eigentlich auch keinen Sinn
machen oder?
EDIT: Bezüglich des mallocs/free stimme ich völlig zu. Ich hatte vor an
möglichst allen Stellen darauf zu verzichten um mir den Speicherplatz
von malloc() ganz zu sparen. Der soll ja auch nicht wenig sein.
Nochmal vielen Dank,
toti
Torsten W. schrieb:
> Beim Compilen mit -Os lief der Compiler ohne eine einzige
> Warnung durch. An der Stelle würde mich jetzt auch interessieren, hätte
> der Compiler ein 'Warning' ausgeben müssen?
Kompilierst du mit -Wall -Wextra (falls gcc, sonst halt entsprechende
Optionen bei deinem Compiler)?
Mit -Wall wird es mir jetzt angezeigt. Sogar noch drei weitere Warnings.
Danke
Torsten W. schrieb:
> Was mich auch noch interessieren würde ist, ob es wirklich reiner Zufall
> war, dass es ohne Optimierung funktioniert hat
Ja, war es. Wahrscheinlich hat dort der Pointer einfach nur in einen
ansonsten unbenutzten Bereich des RAM gezeigt.
Torsten W. schrieb:
> Mit -Wall wird es mir jetzt angezeigt. Sogar noch drei weitere Warnings.
Daraus kann man messerscharf schließen, daß es generell eine gute Idee
ist, mit -Wall und am besten auch gleich mit -Wextra zu kompilieren,
und sich um die Warnungen auch zu kümmern bzw. sie mit Sinn und
Verstand zu beseitigen.
Z.B. wird bei deinem Programm (-teil) nicht so recht klar, wieso
du für normale Strings unsigned char[] nimmst.
De Verwendung von signed/unsigned-Varianten für ganze Zahlen ist
ebenfalls immer eine Überlegung wert.
Weiterhin übergibst du einen nichtkonstanten Zeiger für den String,
was sich mir nicht erschließt.
Gleich vorweg: "funktioniert doch so" ist die dämlichste Ausrede :-)
Hi,
auf "funktioniert doch" wollte ich mich heute mal nicht rausreden ;) .
Mein Problem bezüglich der Datentypen ist, dass ich programmiertechnisch
eher aus dem Bereich Java komme, wo man sich über solche Sachen ja eher
weniger Gedanken macht und einem als Programmierer auch eine ganze Menge
Arbeit abgenommen wird.
Aus diesem Grund tue ich mich mit der Menge an Datentypen (im Vergleich
zu Java), die einem bei c zur Verfügung stehen noch etwas schwer und
muss noch reichlich Erfahrung sammeln was das angeht.
Die Erfahrung, dass man mit 'Warnings' nicht scherzen sollte habe ich im
Vorfeld bereits gemacht und angefangen diese sehr ernst zu nehmen. Das
Vergessen von -Wall war dabei eher ein Versehen aus Mangel an Erfahrung,
da dies auch mein erstes aufwändigeres Projekt in C ist.
Was genau meinst du im Bezug auf die Verwendung von 'unsigned char[]'
und in welchem Teil genau?
Im Hinblick auf das Übergeben des Pointers für den String hätte ich doch
bei einer Deklaration des Parameters als 'const' den Vorteil, dass ich
wegen der Read-Only Übergabe nicht aus Versehen in den Daten
rumschreiben könnte oder? Gibt es noch andere Aspekte in aus deren Sich
das sinnvoll ist? In diesem Punkt muss ich aber leider zugeben, dass ich
da bis jetzt mit der Motivation "funktioniert doch so" gehandelt habe ;)
Gruß,
toti
Torsten W. schrieb:
> Was genau meinst du im Bezug auf die Verwendung von 'unsigned char[]'
> und in welchem Teil genau?
Sorry, das hatte ich mit einem anderen Thread verwechselt :-(
Der Sinn meiner Aussage bleibt aber natürlich ...
Torsten W. schrieb:
> Im Hinblick auf das Übergeben des Pointers für den String hätte ich doch
> bei einer Deklaration des Parameters als 'const' den Vorteil, dass ich
> wegen der Read-Only Übergabe nicht aus Versehen in den Daten
> rumschreiben könnte oder? Gibt es noch andere Aspekte in aus deren Sich
> das sinnvoll ist?
Ein weiterer Vorteil ist, daß der Compielr von Fall zu Fall
deutlich besseren Code erzeugen kann.
Außerdem kann man eine Funktion, die selber durch const zusichert,
einen String nicht zu ändern, wiederum aus einer anderen aufrufen,
die selbiges zusichert.
Ein const-String beim Aufrufer dürfte nämlich strenggenommen nicht
als Parameter an eine Funktion übergeben werden, die den als
nicht-const bekommt.
Torsten W. schrieb:
> In diesem Punkt muss ich aber leider zugeben, dass ich
> da bis jetzt mit der Motivation "funktioniert doch so" gehandelt habe ;)
Wie gesagt, meinte ich dich eigentlich gar nicht, aber war trotzdem
ein Treffer :-)
Klaus Wachtler schrieb:
> Torsten W. schrieb:
>> Im Hinblick auf das Übergeben des Pointers für den String hätte ich doch
>> bei einer Deklaration des Parameters als 'const' den Vorteil, dass ich
>> wegen der Read-Only Übergabe nicht aus Versehen in den Daten
>> rumschreiben könnte oder? Gibt es noch andere Aspekte in aus deren Sich
>> das sinnvoll ist?
>
> Ein weiterer Vorteil ist, daß der Compielr von Fall zu Fall
> deutlich besseren Code erzeugen kann.
>
> Außerdem kann man eine Funktion, die selber durch const zusichert,
> einen String nicht zu ändern, wiederum aus einer anderen aufrufen,
> die selbiges zusichert.
> Ein const-String beim Aufrufer dürfte nämlich strenggenommen nicht
> als Parameter an eine Funktion übergeben werden, die den als
> nicht-const bekommt.
Nur mal so nebenbei:
Das gilt natürlich nicht nur für Strings, sondern für jede Art von
Pointer, egal worauf er zeigt.
das hab ich mir schon gedacht und es gleich mal bei allen Funktionen die
nur lesend auf die ihnen übergebenen Zeiger zugreifen angepasst.
Danke nochmal.
Torsten W. schrieb:
> nur lesend auf die ihnen übergebenen Zeiger zugreifen
zur Sicherheit: ich rede von einem const für das, worauf
die Zeiger zeigen, nicht für die Zeiger: 1 | void f( char *s /* Zeiger auf char */ )
| 2 | {
| 3 | ++s; // s darf verändert werden
| 4 | ++(*s); // *s auch
| 5 | }
| 6 |
| 7 | void fconst( const char *s /* Zeiger auf konstante char */ )
| 8 | {
| 9 | ++s; // s darf verändert werden
| 10 | // ++(*s); // FALSCH! *s ist konstant
| 11 | }
| 12 |
| 13 | ...
| 14 | char tmp[10] = "Hallo"; // 10 änderbare char
| 15 | f( tmp ); // ok
| 16 | fconst( tmp ); // ok (Funktion dürfte Zeichen ändern,
| 17 | // tut es aber nicht)
| 18 | f(( "Hallo" ); // FALSCH! "Hallo" ist eine Konstante,
| 19 | // f() sichert aber nicht zu, den String
| 20 | // nicht zu ändern!
| 21 | fconst( "Hallo" ); // ok
|
Ja, so meinte ich das auch ;) Danke.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|