PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : XC8 inline Assembler



Siro
11.02.2017, 02:08
Einen schönen guten Morgen,
Ich programmiere mit dem XC8 Compiler und wollte was in Assembler einfügen:

asm("movlw 255");
asm("movwf FSR0L");
asm("delay_a:");
asm("decf FSR0L,F");
asm("btfss STATUS,Z"); hier liegt wohl irgend ein Problem vor ?
asm("goto delay_a");

Der Inline Assembler kennt aber die Statusflags C,Z,....nicht...

Hab schon ne Stunde gegoogelt und auch in der XC8 Anleitung gestöbert.
Keine Ahnung wo das Problem liegt.
Direkt in Assembler hab ich das zig tausendfach gemacht.

Kommt davon wenn man auf "C" umsteigt.....selber schuld......:)

Siro

Siro
11.02.2017, 10:40
Habe jetzt den Code geändert, dann brauche ich das Statusbit Z nicht abfragen. :wink:

asm("decfsz FSR0L,f");
asm("goto delay_a");

aber irgendwie muss man ja auch auf die Statusbits zugreifen können...aber wie ?


warum ich Assembler benötige ?
Eine definierte Laufzeit, das geht nicht in "C" sondern nur in Assembler und die Timer sind alle vergeben....

shedepe
11.02.2017, 12:18
Doofe Frage. Hast du dem Compiler gesagt, dass du Inline Assembler verwenden willst? Wenn ich mich nicht täusche muss man das bei den meistens Toolchains explizit aktivieren.

witkatz
11.02.2017, 13:42
Eine definierte Laufzeit, das geht nicht in "C" sondern nur in Assembler und die Timer sind alle vergeben....

für eine definierte Wartezeit von x Zyklen würde ich die Funktion _delay(x) verwenden.


_delay(1020);
ist mMn lesbarer als

#asm
movlw 255
movwf FSR0L
delay_a:
decf FSR0L,F
btfss STATUS,2
goto delay_a
#endasm

Und dann gibts noch die zeitbezogenen __delay_us(x) und __delay_ms(x) für die man lediglich _XTAL_FREQ definieren muss.

Siro
11.02.2017, 19:22
Guten Abend,
erstmal Danke für euro Infos.

@shedepe
Bei dem XC8 Compiler muss man "eigentlich" nichts extra einstellen, hab mir das nochmal genau angesehen.

@witkatz
so wollte ich das eigentlich haben, dann habe ich irgendwo gelesen, dass man jede Zeile separat als asm (" "); schreiben soll.

ich habe jetzt deine Variante probiert: siehe Screenshoot:
32410

irgendwie mag er das nicht:

unable to resolve identifier movlw
unable to resolve identifier movwf
unable to resolve identifier delay_a unexpected token
unable to resolve identifier decfsz
unable to resolve identifier delay_a
unable to resolve identifier nop

unexpected token }


Jetzt das erstaunliche, er hat trotzdem ein Compilat erstellt.....
ich glaub ich muss die ganze IDE neu installieren, da ist doch was faul.


Habe komplett neu installiert. IDE und Compiler.
Das ändert aber nichts. Sehr merkwürdig.

Aber der Hinweis mit dem __delay_us habe ich grad ausprobiert.
Man muss nur ein define _XTAL_FREQ definieren und dann erzeugt der Compiler selbst den Code für die Warteschleife.
Supi, das wuste ich noch nicht. Ist ja auch viel übersichtlicher. Danke Dir witkatz

shedepe
12.02.2017, 08:34
http://ww1.microchip.com/downloads/en/DeviceDoc/52053B.pdf
Abschnitt 5.12..3.2
Du hast ein #include <xc.h> in deinem File?.

witkatz
12.02.2017, 18:39
Du hast ein #include <xc.h> in deinem File?.
Ich kann nicht für Siro antworten, aber ich habe für meinen oben geposteten Beispielcode die xc.h als einzigen Header eingebunden.
Die Fehlermeldungen im Editor kommen bei mir auch. Es ist wohl das normale Verhalten des Preparsers, so steht es zumindest im MPLAB X IDE User's Guide Chap. 9.7.7 (http://ww1.microchip.com/downloads/en/DeviceDoc/50002027D.pdf#G12.687133)
Als Workaround könnte man die Multiline Schreibweise benutzen, allerdings dann ohne die Möglichkeit den Assemblecode zu debuggen

asm("\
movlw 0x9D; \
movwf FSR0L; \
delay_a: \
decfsz FSR0L,F; \
goto delay_a; \
nop; \
");

Ich persönlich mag das mischen von C und ASM nicht. Ich habe bis jetzt inline Assembler gemieden und möchte dabei auch bleiben. Wenn ich um Assembler nicht herum kommen sollte, würde ich lieber das ganze Projekt oder zumindest ein Modul komplett in Assembler machen wollen.

Siro
12.02.2017, 20:07
So, da bin ich wieder,

Ich habe auch als "einzigne include Datei XC.h" bei mir drin.

Jo, Du hast recht witkatz, da steht was im MPLAB UDE User Guide.....Bei mir Kapitel 7.5.2
Fehler ignorierenm, es wird trotzdem ein Code erzeugt :)
Okay, dann ist das nicht nur bei mir so....

Die Mischung mit Assembler und C ist ja meist nicht erforderlich.
Ich wuste ja auch nicht, dass es dieses __delay_us Macro gibt.
Ich hab die Taktzyklen selbst berechnet, Naja ich bin mal ehrlich mit dem Simulator angepasst....:p

Die eigentliche Frage mit den Statusbits abfragen bei der asm("xxx") Abfrage bleibt aber eigentlich noch offen.
Das müste ja auch irgendwie gehen, wenngleich ich das ja jetzt nicht mehr brauche.

witkatz
12.02.2017, 22:17
Die eigentliche Frage mit den Statusbits abfragen bei der asm("xxx") Abfrage bleibt aber eigentlich noch offen.
Bei mir funktionierte es mit
asm("btfss STATUS,2");
... weil in dem zu dem PIC dazugehörenden Header pic16fxxx.h der STATUS Register dem inline Assembler per asm("STATUS equ 03h") definiert wird. Dieser Header wird über die xc.h eingebunden und dadrin sind die SFR für den Inline Assembler definiert - aber nicht die Bits. Bei MPASM sind die SFR und die einzelnen Bits in der p16fxxx.inc definiert. Man kann die Definition der Bits für den Inline Assembler ergänzen, dann funktioniert die Zeroflag Abrage auch symbolisch

asm("Z EQU 0x0002");
//...
asm(" btfss STATUS,Z ");

Nachtrag:

Eine definierte Laufzeit, das geht nicht in "C" sondern nur in Assembler...
Darauf sollte man sich im Inline Assembler nicht verlassen, der vom XC8 compiliert und - wenn man es ihm nicht verbietet - optimiert wird. Ich wollte mal interessenhalber deine erste Delay-Routine als separates ASM Modul einbinden s.Bild und schwups wird die DECF BTFSS Sequenz zu DECFSZ optimiert. Ich habe mich nur gewundert, warum eine ASM Routine, die vorher mit asm() Anweisungen in C genau 1ms gebraucht hat (Simulation für PIC16F88 ), als ASM Modul um 25% schneller läuft. Gut zu wissen, dass XC8 auch innerhalb von ASM optimiert, aber für deterministische Laufzeiten ist das nicht so gut.


32415

Siro
13.02.2017, 14:28
Gut zu wissen, dass XC8 auch innerhalb von ASM optimiert, aber für deterministische Laufzeiten ist das nicht so gut.

Upps, das kann natürlich nach hinten losgehen. Wär nicht das erste Mal, dass mir der C-Compiler "wichtigen Code" wegoptimiert ;)
Auch wenn alle Optimierungen ausgeschaltet werden, sind alle mir bekannten C Compiler der Meinung immer noch was wegzuoptimieren....
Grausam, aber leider nicht zu ändern. Naja ich war noch nie ein "C" Freund und da hat sich bis heute auch nix dran geändert.
"C" ver"C"ehnfacht halt die Entwicklungszeit.

Aber bitte jetzt keine Diskussionen . Das ist ja nur meine persönliche Meinung zu "C" :)

Hab nochmal vielen Dank für die Info, damit hätte ich sicher nicht gerechnet, dass der Inline Code auch optimiert wird.

Siro
22.02.2018, 22:06
Hallo zusammen,
ich wollte "mal wieder" etwas Assembler in meinen XC8 Code bringen,
dafür wollte ich aber keinen neuen Thread öffnen, daher führe ich das mal hier weiter:

Der XC8 inline Assembler scheint irgendwie überhaupt nicht mit dem XC8-Compiler zu harmonieren.
Ich kann weder auf Variablen noch auf Konstanten zugreifen, es gibt ständig nur Fehlermeldungen.

Hier mal ein Auschnitt des Screenshoots: ich hab mal den Fehlermeldungen daneben geschrieben

33294

Hier ein Ausschnitt des inline Codes:


#define LED_COUNT 24
unsigned char LedArray[LED_COUNT];
unsigned char byteCount;
unsigned char bitCount;

void LedShiftOut(void)
{
asm("BANKSEL LedArray");
asm("LFSR FSR0,LedArray");

asm("BANKSELECT byteCount");
asm("movlw LED_COUNT");
asm("movwf byteCount");

asm("ByteLoop:");
asm("movlw 8");
asm("movwf bitCount");
asm("movwi fsr0++");

asm("BitLoop:");
asm("RRF WREG,F");
asm("btfss STATUS,Z");
asm("goto isOne");

asm("isZero:");
asm("bcf LATA,5");
asm("NOP");
asm("NOP");
asm("goto BitLoop");

asm("isOne:");
asm("bsf LATA,5");
asm("NOP");
asm("NOP");

asm("decfsz bitCount,F");
asm("goto BitLoop");

asm("decfsz byteCount,F");
asm("goto ByteLoop");

// 50us Pause

}

was mache ich denn falsch ?, das kann doch nicht so kompliziert sein.....

Es wird mir wieder Mal nix anderes übrig bleiben als den gesamten Code in Assembler zu programmieren.:(

[edit]
auf dei Variablen kann ich evtl. zugreifen wenn ich einen Unterstrich benutze, zumindest meckert der Compiler dann nicht.
Es bleiben aber noch genug Probleme übrig...:)

Siro
23.02.2018, 07:20
Guten Morgen liebe Gemeinde,

ich werde mal eine der vielen Fragen konkretisieren:


#define ZAEHLER 8

int main(void)
{
asm("movlw ZAEHLER"); // undefined symbol ZAEHLER

was ist hier so Besonderes, dass es nicht funktioniert ?

so funktioniert es ja:

asm("movlw 8");



Nun habe ich grad etwas Neues ausprobiert und das geht sogar:

// #define ZAEHLER 8 // so geht es nicht

asm("ZAEHLER EQU 8"); // aber so geht es

int main(void)
{
asm("movlw ZAEHLER");



Siro

Klebwax
23.02.2018, 08:33
was mache ich denn falsch ?, das kann doch nicht so kompliziert sein.....

Es wird mir wieder Mal nix anderes übrig bleiben als den gesamten Code in Assembler zu programmieren.

Was gibt es für einen Grund, hier Assembler zu verwenden? Seitdem es C für die PICS gibt, hab ich kein Assembler mehr verwendet. Und ich kann auch in deinem Code nichts erkennen, daß sich nicht in C formulieren lässt.

Wenn ich deinen Code richtig verstanden habe, soll er, ein Array von unsigned chars bitweise auf einem Port ausgeben.


#include <xc.h>
#include <stdint.h>

#define LED_COUNT 24
uint8_t LedArray[LED_COUNT];

void LedShiftOut(uint8_t *leds, int8_t count) {
uint8_t one_byte;
int8_t bit_count;

while (count) {
one_byte = *leds++; // next Byte
for (bit_count = 0; bit_count < 8; bit_count++) {
LATA5 = one_byte & 0x01; // lowest Bit
one_byte >>= 1;
}
count--;
}
}

void main(void) {
LedShiftOut(LedArray, LED_COUNT);
return;
}


Eigentlich ist das Ganze aber eine Aufgabe für das SPI Modul, wobei man MISO und CLK nicht verwendet.

Und zu deiner Frage mit

#define ZAEHLER 8

Der Compiler kennt ZAEHLER nicht, alle Zeilen, die mit einem # beginnen, werden vom Präprozessor bearbeitet. Die bekommt der Compiler garnicht zu sehen. Das "#define" tut das Gleiche, was "suchen und ersetzten" in deinem Editor macht, jedesmal vor dem Compilieren.

Wenn ich lese, was du hier in diesem Thread so schreibst, wirst du mit deinem Ansatz nicht wirklich glücklich werden. Entweder du schreibst alles in Assembler, wozu es IMHO keine Notwendigkeit gibt, oder du programmierst in C. Die Mischung ist eigentlich immer Pfusch und führt am Ende zu unwartbarem Code. Die Arduino-Leute sind noch einen Schritt weiter gegangen und verwenden C++.

MfG Klebwax

Siro
23.02.2018, 09:21
Moin Klebwax,
ersteinmal Danke für deine Mühe und Informationen.

Im Prinzip hast Du recht, man braucht "meistens" keinen Assembler, wenn man schon in "C" programmiert.
Aber manchmal gibt es doch einige Ausnahmen.
Ich möchte die RGB Leds Typ WS2812B ansteuern und die haben/wollen leider ein sehr genaues Timing haben. +/- 150ns laut Datenblatt.
Da bleibt nicht viel Freiraum für einen "nicht optimierten" C-Code (Free Version :-))

Im Prinzip will ich nur die Daten in dem "richtigen" Timing ausschieben, der Rest sollte in "C" sein.
Auch richtig erkannt, dass man dafür das SPI Interface nehmen könnte, wobei ich hier noch nicht sicher bin ob das
mit dem Timing hinhaut. Bin mometan noch etwas in der Experimentierphase.

Generell sollte es aber auch möglich sein, Assembler mit "C" zu mischen und ich bin quasi am erforschen
wie das am besten oder überhaupt geht. Komme ja auch grad etwas weiter....


Mit dem Preprocessor ist mir eigentlich klar, aber anscheinend nur "eigentlich"
Wenn der Preprozssor alle #defines vorher einsetzt bevor er compiliert und/oder assembliert,
dann müsste die Assemblerzeile doch auch richtig aussehen.

#define ZAEHLER 8
sagt aus, daß überall im Code wo ZAEHLER auftaucht die 8 eingetragen wird (wie Du auch geschrieben hast: suchen/ersetzen).
Erst dann wird compiliert/assembliert und demnach steht doch dann in meiner Assemblerzeile
anstelle von
asm("movlw ZAEHLER");
asm("movlw 8");
oder habe ich das falsch verstanden ?

[Edit] mir ist grad ein Licht aufgegangen:
Ein define wird NICHT ersetzt wenn er in Ausdrücken mit Hochkomma steht.
Der identifier wird nur ersetzt, wenn er ein Token bildet. Der identifier wird also nicht ersetzt, wenn er in einem Kommentar, in einer Zeichenfolge oder als Teil eines längeren Bezeichners erscheint ...

Siro

(Ziel ist es mit einen kleinen 8 Füssler (momentan PIC12F1840) 24 RGB Leds anzusteuern)
Das gibt es übrigens auch schon fertig in Assembler:
http://www.picalic.de/PIC2WS2812/picsolution.html

Klebwax
23.02.2018, 10:40
Generell sollte es aber auch möglich sein, Assembler mit "C" zu mischen ...

es sollen schon Leute für weniger gefeuert worden sein ;)


#define ZAEHLER 8
sagt aus, daß überall im Code
im C-Code
wo ZAEHLER auftaucht die 8 eingetragen wird (wie Du auch geschrieben hast: suchen/ersetzen).
Erst dann wird compiliert/assembliert und demnach steht doch dann in meiner Assemblerzeile
anstelle von
asm("movlw ZAEHLER");
asm("movlw 8");
oder habe ich das falsch verstanden ?
und nicht in Strings "... ZAEHLER"

Die WS2812 mit ihrem Timing sind ein besonderes Thema. Eigentlich benötigt man dafür ein HW-Modul im Prozessor wie SPI oder PWM. Die Lösungen mit SPI sind eher suboptimal. Aber wenn Assembler, dann würd ich nicht mischen sondern die wirklich notwendigen Funktionen komplett in Assembler schreiben und in einen extra File packen. Im XC8 Manual in Kapitel 5.12 findet sich da eine Anleitung.

MfG Klebwax

witkatz
23.02.2018, 10:54
...
unsigned char LedArray[LED_COUNT];

void LedShiftOut(void)
{
asm("BANKSEL LedArray");
...

MPLAB_XC8_C_Compiler_User_Guide.pdf:

Most C symbols map to an corresponding assembly equivalent.
...
The name of a C function maps to an assembly label that will have the same name, but
with an underscore prepended. So the function main() will define an assembly label
_main.
nach meinem Wissen gilt das für Funktionen als auch für Variablen, also folgendes müsste gehen:
asm("BANKSEL _LedArray");

Siro
23.02.2018, 13:47
Du hast völlig Recht Witkatz, mit dem Unterstrich kann ich auf die Variablen zugreifen
ebenso auf die Funktionsnamen.
Bei BANKSEL geht das auch, obwohl der Operand _LedArray dabei sogar in Hochkomma steht.


#define LED_COUNT 24
unsigned char LedArray[LED_COUNT];

unsigned char count;

void MeineFunc(void)
{ volatile unsigned char x;
x++;
}

int main(void)
{
asm("movf _count,W"); // geht Variable laden mit Unterstrich
asm("movwf _count"); // geht Variable speichern mit Unterstrich
asm("call _MeineFunc"); // geht Funktionsaufruf mit Unterstrich
asm("BANKSEL _LedArray"); // geht mit Unterstrich

// asm("LFSR FSR0,_LedArray"); // geht leider nicht

Ich werde sicher noch einiges propbieren, das Wochende fängt ja erst an.
und wünsche Euch allen ein schönes Wochenende und danke, dass ihr meine "Code Eskapaden" ertragt....:)

witkatz
23.02.2018, 18:54
// asm("LFSR FSR0,_LedArray"); // geht leider nicht
Kennt dein PIC den Befehl überhaupt? Ist nämlich ein PIC18 Befehl, so hab ich's zumindest im MPASM Quick Chart (http://ww1.microchip.com/downloads/en/DeviceDoc/30400g.pdf) gefunden.

Siro
23.02.2018, 19:19
Ich lach mich kaputt.....
Du hast ja sowas von recht Witkatz

Oh Mann, das tut ja weh :p, vielen Dank für die Info
--------------------------------------------------

Ich habe jetzt einen laufenden Code, fast ausschließlich in "C" , basierend auf der Funktion von Klebwax(dessen Code ich geklaut habe).




// so viele LEDs sollen angesteuert werden:
#define LED_COUNT 3

/* Jede LED hat 3 Bytes insgesamt also 24 Bits */
typedef struct // __pack weil wir keinen Speicher verschwenden wollen ????
{
U8 green; /* 8 Bit fuer die Helligkeit */
U8 red; /* 8 Bit fuer die Helligkeit */
U8 blue; /* 8 Bit fuer die Helligkeit */
} TLed; /* Type Bezeichner ist TLed */

TLed LedArray[LED_COUNT];

void LedShiftOut(U8* leds, U8 count)
{
U8 one_byte;
U8 bit_count;

count *=3; // 3 Bytes pro Led RGB
while (count) {
one_byte = *leds++; // next Byte
for (bit_count = 0; bit_count < 8; bit_count++)
{
if (one_byte & 0x01) // lowest Bit
{
LATA5 = 1;
asm("NOP");
asm("NOP");
asm("NOP");
LATA5 = 0;
} else
{
LATA5 = 1;
asm("NOP");
LATA5 = 0;
}
one_byte >>= 1;
}
count--;
}

Delay_ms(1);
}

Das Timing stimmt eigentlich nicht, aber das scheint wesentlich unkritischer zu sein als das Datenblatt vorgibt.
Die Low Phasen dürfen nämlich viel länger sein, das scheint nicht zu stören. Die sind bei mir alle so ca. 2us.
Die High-Phasen müssen lediglich stimmen, dann ist die Welt in Ordnung.
Ich habe zwischen den Bytes sogar 5us Pause, das stellt kein Problem dar.

33295

Nochmal einen DANK an alle Die mir hilfreiche Informationen gegeben haben (beonders Witkatz + Klebwax)

Klebwax
23.02.2018, 21:27
@Siro

Jetzt könnte man noch die Lbraryfunktion

NOP();

statt

asm("NOP");

verwenden und alles wäre plain C.

MfG Klebwax

witkatz
23.02.2018, 22:00
Jetzt könnte man noch die Lbraryfunktion
NOP();
Oder etwas lesbarer _delay(1); statt NOP(); bzw. _delay(3); statt NOP();NOP();NOP();

Siro
24.02.2018, 16:07
Hallo nochmal, kurzer Endbericht:

die Assembler nops habe ich durch "NOP()" ersetzt und das funktioniert erwartungsgemäß einwandfrei.

Mit den Delay wäre etwas problematisch, da ein NOP in meinem Falle bei 32MHz Clock 125ns entspricht.
die delay funktion ist in Millisekunden.
es soll wohl delay_ms und delay_us geben, die meckert er mich an, da fehlt wohl eine Bibliothek.

Aber nichts desto trotz, es laufen grad 50 LEDs einwandfrei, aber das hatte auch noch etwas gedauert, denn

man sollte während des Ausschiebens der Daten alle Interrrupts sperren, sonst hat man Randomize Rainbow :)

Zudem waren ab LED 21 die Farben falsch. Zuerst dachte ich an ein Speicherproblem, aber das war es nicht.
Da war doch tatsächlich die 21te LED auf dem Band defekt. Zum Glück hatte ich noch einzelne und habe sie ausgetauscht.
Nun leuchten alle 50 wie sie sollen.
Bei voller Ansteuerung in weiss kommt schon so einiges an Strom zusammen.
Das sind dann 20mA * 3 Leds = 60mA * 50 Leds = 3,05 Ampere.

Achja, noch was: Bei kurzzeitiger Unterbrechung der Versorgungsspannung, gehen die LED zwar aus, es bleiben aber die Daten in den 50 LEDs erhalten.
So 3 bis 5 Sekunden habe ich ausprobiert, ohne dass ich erenut Daten reinschieben muss.

witkatz
24.02.2018, 16:44
die delay funktion ist in Millisekunden.
Nö. In Millisekunden ist die _delay_ms(), die _delay() ist in Prozessortakten, _delay(1); ersetzt also ein NOP(); und _delay(3); ersetzt NOP();NOP();NOP();
Die Unterstriche sind dabei wichtig. delay() kennt der XC8 nicht, _delay() schon.
Ich finde halt eine Zahl lesbarer als eine NOP-Kette. Ist aber letztendlich wurscht.

- - - Aktualisiert - - -


Nun leuchten alle 50 wie sie sollen.
Nun warten wir auf ein Foto oder Video :cool:
Glückwunsch

Siro
24.02.2018, 19:16
Das mit den delay muss ich mir nochmal anschauen.
Danke für die Info Witkatz:

Das mit dem Video macht sich garnicht gut, man erkennt nicht annähernd die schönen Farben, das sieht immer weiss aus...
Ich hab es aber mal im Dunkeln gefilmt, nicht schön, besser wie nix.

https://www.dropbox.com/s/mr6gkga5mxkh98k/RGB.MOV?dl=0


hier noch die komplexe1 Schaltung: :)
33302
rot Plus
blau Minus
grün Datenleitung zu den Leds2


Siro