Gerät: dsPIC33FJ128GP802
Ich habe einige * .s-Dateien wie folgt
.global _D1
.section .speex, code
_D1:
.pword 0x66C821, 0x1B0090, 0xD96C36, 0x9B60B0, 0xDD4E36, 0xBF4E53
.pword 0xD1098B, 0x719BD9, 0x873989, 0x003B69, 0x279035, 0xED4244
.pword 0xE1403C, 0x54D439, 0x826550, 0xC59627, 0xDD0432, 0x88FA29
Ich habe das gleiche in einem * .h erklärt
extern void D1(void);
Jetzt übergebe ich den D1 an eine Tabellenlesefunktion
nowPlaying.file1 = (unsigned long) D1;
function(nowPlaying.file1);
Mein Problem ist, dass die Routine nicht korrekt ist, wenn die Adresse von D1 über 0X8000 liegt. Ich habe große und kleine Codemodelle ausprobiert, aber das Ergebnis ist das gleiche. Ich denke, das liegt an der 16-Bit-Beschränkung der Zeiger. Gibt es eine Methode, um direkt aus dem Code auf die absolute Adresse von D1 zuzugreifen? Kann so etwas wie eine eingebaute Funktion oder Makros sein.
microcontroller
pic
microchip
compiler
Saneesh AT
quelle
quelle
D1
eine Funktion oder ein Array von Daten darstellen?const short D1[]
.Antworten:
Die von Ihnen beschriebenen Daten (vollständige 24-Bit-Nutzung des Programmspeichers zum Speichern von Daten) können in C nicht definiert und initialisiert werden und können nicht direkt über C gelesen werden. Der einzige Weg, darauf zuzugreifen, ist die Kapselung in eine C-aufrufbare Assembly-Funktion oder eine intrinsische.
Hier gibt es wirklich zwei Fragen:
So spielen Sie gut mit dem Compiler, Assembler und Linker, sodass
D1
der Compiler diese Variable sehen kann , wenn Sie Ihre 24-Bit-Daten in einer Assembly-Datei als verschiebbare Daten mit einem symbolischen Namen und nicht als unbenannte Daten an einer festen Adresse definieren um seine Adresse zu bestimmenwie man auf die Daten zugreift
Die zweite Frage (Zugriff auf die Daten) wird für 33EP-Teile in DS70613C beantwortet und sollte für 33FJ-Teile in DS70204C beantwortet werden (in den Beispielen im 33FJ-Handbuch werden jedoch nur die niedrigen 16 Bit verwendet). Hier ist ein Beispiel-Code-Snippet aus dem 33EP-Referenzhandbuch, das für 33EP-Teile + für 33FJ funktioniert (ich habe kein leicht verfügbares 33FJ-Gerät):
(Anmerkung: Code Anwendungen
int
, während es besser wäre , zu verwendenuint16_t
und#include <stdint.h>
)Sie werden feststellen, dass die integrierten Funktionen
__builtin_tblrdl()
und__builtin_tblrdh()
zum Lesen der niedrigen und hohen 16-Bit-Wörter von Daten aus einem Programmspeicher__builtin_tblpage() and __builtin_tbloffset()
verwendet werden und zum Extrahieren der Seite und des Versatzes der Adresse verwendet werden können. In diesem speziellen Beispiel ist das highWord-Array immer 0, und das lowWord-Array stimmt mit den in C definierten und initialisierten prog_data überein.Bitte beachten Sie, dass hier keine Zeiger verwendet werden! Es ist zwar möglich, normale Variablen zu verwenden, die mit Tags versehen sind
const
, so dass sie vom Linker im schreibgeschützten Programmbereich lokalisiert werden und Sie den Speicher mit Standard-C-Zeigertechniken lesen können, wobei der Compiler die Paging-Register automatisch verwaltet Für Sie können Sie nur 16-Bit-Daten speichern. Sie müssen auf die integrierten Funktionen TBLRDL und TBLRDH zugreifen, um alle 24 Datenbits abzurufen.Um mit dem Compiler / Linker / etc gut zu spielen, müssen Sie den Compiler täuschen und ihm mitteilen, dass nur 16-Bit-Daten angezeigt werden. Hier ist ein Beispiel, das funktioniert hat, um an die an anderer Stelle deklarierte Variable D1 zu gelangen:
Dadurch werden 24-Bit-Werte korrekt gelesen und in den unteren 24 Bit eines uint32_t gespeichert. Die in C deklarierte externe D1-Variable ist eine Dummy-Variable, die nur verwendet wird, um an die Startadresse zu gelangen, indem die Zusammenarbeit von Compiler / Assembler / Linker ausgenutzt wird. Die eingebauten Funktionen erledigen den Rest der Arbeit.
Was ich nicht weiß, ist, wie die Größe der Daten automatisch ermittelt wird, da sie in der Assembly definiert + initialisiert sind.
quelle
Konvertieren Sie es nicht in unsigned long and back. Ärger suchen. Sie lügen im Grunde den Compiler an. Die korrekte Deklaration für nowPlaying.file1 lautet
Und ähnlich für function ():
und entfernen Sie alle Typecasts.
Oder, wenn @PeterJ vorschlägt, dass es sich um Daten handelt, sollten diese an beiden Stellen als extern short D1 [] deklariert werden: und Sie brauchen den Assembler nicht wirklich; Sie hätten alles in C als const short D1 [] = {...} deklarieren können; Der Compiler sollte es in das Codesegment einfügen, da es const ist.
quelle
Es scheint, als ob die einfache Antwort darin besteht, das Unterprogramm in Assembler zu schreiben. Wenn ich mich recht erinnere, greift C30 nicht mit 24-Bit-Zeigern auf den Programmspeicher als Daten zu. Bestenfalls kann es über das PSV-Fenster auf den Programmspeicher zugreifen, aber dann können Sie nur die niedrigen 16 Bits jedes 24-Bit-Programmspeicherworts sehen.
Es wäre sehr einfach, eine Assembler-Routine zu schreiben, die von C30 aus aufgerufen werden kann und die 24 Bit Daten bei einer 24-Bit-Programmspeicheradresse zurückgibt. Sind Ihre Daten jedoch eine Sammlung von 24-Bit-Werten oder wirklich eine Liste von Bytes, die zufällig 3 pro Wort gepackt sind? Wenn letzteres der Fall ist, ist es noch einfacher. Schreiben Sie eine Assembler-Routine, die Ihnen eine Ansicht der Byteadressen des Programmspeichers bietet. Die Adresse müsste noch 24 Bit sein, aber die Datenwerte sind jetzt nur noch 8 Bit.
Oder schreiben Sie einfach die gesamte Routine in Assembler. Wenn Sie diese Art von Low-Level-Byte-Banging und Speicherpacken ausführen, ist dies wahrscheinlich einfacher. In Assembler können Sie einfach das tun, was Sie wollen, so wie es die Maschine will. In C müssen Sie herausfinden, welche Beschwörungsformeln Sie an den Compiler murmeln müssen, damit dieser den Maschinencode für Sie schreibt. Manchmal ist es einfach einfacher, es direkt selbst zu tun. Die dsPIC-Architektur ist besonders einfach zu schreiben, definitiv einfacher als ein PIC 16.
quelle
__builtin_tblrdX()
Funktionen verwenden. Ich stimme Ihnen zu + habe einen Kick aus Ihrer Formulierung "In C müssen Sie herausfinden, welche Beschwörungsformeln Sie dem Compiler murmeln müssen". Ironischerweise sind die__builtin()
Funktionen manchmal besser , wenn Sie wirklich versuchen, die maximale Leistung zu erzielen, da der Compiler die Art und Weise, wie Code generiert wird, optimieren kann, während er handcodierte Assembly-Funktionen als Black Box behandeln muss, die er nicht ändern kann .