Hallo liebe MC-net Gemeinde, ich habe eine Frage an die C cracks.
Ich arbeite mich in die Programmierung des NRF51422 ( BT4 & ANT+ device
) ein. Ich habe ein Teil via I2C and den NRF angeschlossen und kann auch
mit dem reden. Die I2C Routinen verwenden einen Zeiger auf einen Buffer
um die via I2C gelesenen Daten zu buffern:
1
staticuint8_tldc_p_buffer[2]={0,0};// buffer for read back values from LDC
2
ldc_register_address=LDC_DATA_MSB_CH1;
Es werden "transfers" und "transactions" definiert und dann an einen
Scheduler geschickt, da der Core nebenbei noch die wireless protokolle
bearbeitet und daher saemtlicher user-code nur scheduled und
"irgendwann" ausgefuehrt wird. Vermutlich deshalb werden alle Parameter
auch "static" definiert, der code kann jederzeit durch eine ISR des
wireless-teils unterbrochen werden:
1
staticapp_twi_transfer_tconsttransfers[]=// transfer definition
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi,&transaction));// read the LDC Data MSB
Und jetzt zum Mysterium:
Ich schreibe die zwei Bytes aus dem "ldc_p_buffer" dann in ein unit_16 :
1
ldc_msb=((uint16_t)ldc_p_buffer[0]<<8)|ldc_p_buffer[1];// assemble the MSB
Dann gehe ich zur neachsten registeradresse des I2C bauteils...
1
ldc_register_address=ldc_register_address+1;// next register address to read the LSB
und schedule den naechsten Transer.
1
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi,&transaction));// read the LSB. We can modify the register address without creating a new instance as it is accessed by a pointer.
Das geht auch alles, allerdings ueberschreibt der zweite I2C transfer
die Daten in "ldc_msb" mit den neuen Werten. und DAS verstehe ich gar
nicht.
Daher: Wenn ich den zweiten I2C transfer nicht schedule, bekomme ich
die erwarteten 2 Bytes in "ldc_msb". wenn ich den 2-ten Transfer
schedule bekomme ich diese Daten vom I2C device in "ldc_msb". Wieso ?
Klar, "ldc_p_buffer" wird im I2C transfer als Pointer gesehen und somit
wird der Inhalt an der Speicherstelle beim 2-ten I2C Transfer
ueberschrieben. Aber ich weise das ja dann an "ldc_msb" zu. WARUM wird
der Inhalt von "ldc_msb" ueberschrieben wenn die I2C funktion auf den
Speicherbereich von "ldc_p_buffer" zugreift ?
Leider ist in der Nordic Softwarewelt alles unglaublich zerpflueckt,
alles ist hinter mehreren Stufen von macros, #defines und noch mehr
macros versteckt....
Jeder Tip is willkommen...
Danke und Gruss,
Wolfgang
>
Du hast aber an dieser Stelle keine Garantie, wann genau die Transaktion
ausgeführt wird.
> Das geht auch alles, allerdings ueberschreibt der zweite I2C transfer> die Daten in "ldc_msb" mit den neuen Werten. und DAS verstehe ich gar> nicht.
Warum nicht?
Wenn du nur 1 Stück Papier hast, auf dem du eine Nachricht aufschreiben
kannst, dann musst du eben warten, bis dein Gegenüber die Nachricht
gelesen hast. Radierst du vorher die Nachricht aus und schreibst eine
neue drauf, dann kriegt dein Gegenüber die erste Nachricht nie zu
Gesicht.
> der Inhalt von "ldc_msb" ueberschrieben wenn die I2C funktion auf den> Speicherbereich von "ldc_p_buffer" zugreift ?
Weil ein Scheduler sich einfach nur die Transaktion merkt und sie zur
Bearbeitung in einen Puffer stellt. Teil deiner Transaktion ist der
Datenpuffer, in dem du deine Nnutzdaten hast. Diesen Puffer gibt es aber
nur ein einziges mal bei dir und der Scheduler legt sich offenbar keine
Kopie davon an. Was ja auch nicht seine Aufgabe ist.
Du wirst also mehrere derartige Puffer brauchen, für die du eine
Verwaltung benötigst, die jeden derartigen Puffer als belegt markiert,
bis du vom Scheduler die Nachricht erhältst, dass die Transaktion
abgeschlossen wurde. Erst dann kannst du diesen Puffer wieder verwenden.
Wenn garantiert ist, dass der Scheduler die Transkationen in genau der
von dir definierten Reihenfolge abarbeitet (was wohl der Normalfall sein
wird), dann reicht eine einfache Queue, mit der du deine
Nachrichtenpuffer verwaltest.
Hallo Karl Heinz,
was du schreibst scheint sich auf "ldc_p_buffer" zu beziehen, da erwarte
ich das auch. Das ist der Buffer fuer die I2C transaktionen und wird
natuerlich bei jeder Transaktion ueberschrieben. Aber genau darum mach
ich ja:
1
ldc_msb=((uint16_t)ldc_p_buffer[0]<<8)|ldc_p_buffer[1];// assemble the MSB
"ldc_msb" sollte doch eine Kopie des Inhalts von der Speicherstelle sein
auf die "ldc_p_buffer" zeigt, oder ? Oder genauer, es sollte
"ldc_p_buffer" byte1 und "Ldc_p_buffer" byte2 beinhalten, und zwar als
Kopie.
Warum aendert sich dieser Wert dann wenn ich "ldc_p_buffer"
ueberschreibe ? "ldc_msb" ist ja eben kein Zeiger.
Gruss,
Wolfgang
Karl-Heinz,
meinst du dass erst die zweite Transaktion ausgefuehrt wird und dann
die erste, so dass ich einfach zum falschen Zeitpunkt "ldc_p_buffer"
kopiere ?
Beobachtung ist aber dass ich, wenn ich die 2-te Transaktion (nach der
ersten scheduled) mache, ich NUR die Werte der 2-ten Transaktion in
"ldc_msb" habe.
Das kann es also nicht sein. Aber trotzdem Danke fuer die Anregung, ev.
verursacht der Start der 2ten Transaktion ja einen Abbruch der 1-ten
o.ae. Das wuerde das Verhalten erklaeren.
Auf dem Oszilloskop kann ich allerdings beide Transaktionen sehen und
dank I2C Bus decoder auch verifizieren dass "ldc_msb" tatsaechlich die
korrekten Werte enthaelt, nur halt ENTWEDER den von der 1-ten
Transaktion ODER den von der 2-ten, obwohl es immer nur der von der
1-ten sein sollte.
Fuer die 2-te gibt es "ldc_lsb".
So sieht das Ganze komplett aus, mit zwei getrennten "Ergebnisvariablen"
fuer die beiden Transfers:
1
/////////// do the LDC communication
2
voidldc_read_all(void){
3
ldc_register_address=LDC_DATA_MSB_CH1;
4
uint32_tLdcRawData=0;
5
constuint8_tldc_filter_length=5;// we filter over 4 values. This does cut off ldc_filter_length-1 bits from the result. Why ?
6
staticuint8_tldc_p_buffer[2]={0,0};// buffer for read back values from LDC
7
staticuint16_tldc_msb=0;
8
staticuint16_tldc_lsb=0;
9
10
staticapp_twi_transfer_tconsttransfers[]=// transfer definition
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi,&transaction));// read the LDC Data MSB
23
ldc_msb=((uint16_t)ldc_p_buffer[0]<<8)|ldc_p_buffer[1];// assemble the MSB
24
25
ldc_register_address=ldc_register_address+1;// next register address to read the LSB
26
27
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi,&transaction));// read the LSB. We can modify the register address without creating a new instance as it is accessed by a pointer.
28
ldc_lsb=((uint16_t)ldc_p_buffer[0]<<8)|ldc_p_buffer[1];// assemble the LSB into the long variable
29
30
LdcRawData=(ldc_msb<<16)|ldc_lsb;
31
LdcAverage[0]=LdcRawData;// debug
32
//LdcAverage[0] = ((ldc_filter_length - 1)*LdcAverage[0]+LdcRawData)/ldc_filter_length; // channel 0 average
33
}
Wenn ich das zweite "app_twi_schedule" auskommentiere bekomme ich meine
erwarteten 2 bytes in "ldc_msb", wenn ich es drin lasse bekomme ich
dessen 2 bytes in "ldc_msb" und dazu noch in "ldc_lsb" wo sie auch hin
gehoeren...
Gruss,
Wolfgang
Hmm, ich hab nicht alles im Detail gelesen, aber ist das nicht ein wenig
Overkill für so ein bischen LCD? Und vor allem wozu muss man da was
zurücklesen? Das braucht man ja nun weiß Gott SEHR selten, meist ist es
deutlich einfacher und schneller, einfach eine Kopie im lokalen RAM zu
halten und dort nachsehen, was zuletzt geschrieben wurde.
Callback-Handler und PiPaPo um ZWEI Bytes zurückzulesen? Neee!
Wolfgang E. schrieb:> APP_ERROR_CHECK(app_twi_schedule(&m_app_twi, &transaction)); //> read the LDC Data MSB> ldc_msb = ((uint16_t)ldc_p_buffer[0] << 8)| ldc_p_buffer[1]; //> assemble the MSB
Moment mal.
Wie verhält sich denn jetzt der Scheduler?
Arbeitet er die Transaktion gleich ab, oder passiert das irgendwann.
Im Eröffnungsposting jast du noch gesagt 'irgendwann'.
Wann genau kehrt die Funtkion app_twi_schedule zum Aufrufer zurück?
Nachdem die Transaktion durchgeführt wurde oder nachdem die die
Transaktion zur Bearbeitung entgegen genommen hat?
Wenn du hier sowieso zwingend darauf wartest, dass das Ergebnis
vorliegen muss, dann brauchst du doch auch keinen Scheduler. IMHO.
@ Wolfgang Ebersbach (wolfgang2)
>Nein kein LCD ! Ein LDC ! Kein Schreibfehler sondern ein nettes Teil
Ohh, Freudsche Fehlleistung ;-)
Trotzdem erscheint mir der Aufwand zum Auslesen von ZWEI Bytes ein wenig
hoch.
Na ja, ich weiss nicht WANN das stattfindet.
Den Scheduler moechte ich ja nicht, er wird mir aufgezwungen. Wie
gesagt, der M0 core muss noch die wireless protokoll Geschichten
abarbeiten und das macht er mit Priority. Daher auch der Scheduler, es
ist verboten einfach zu einem beliebigen Zeitpunkt eine Operation zu
starten die den Core blockiert. Man muss alles ueber Scheduler
"anfragen".
Die Frage reduziert sich also zu: Wenn ich zwei Tasks schedule, werden
die dann auch in Reihenfolge abgearbeitet ?
Kein Ahnung. Muss ich rausfinden...
Trotzdem:
* Ich schedule einen Task.
* Ich lese sofort danach ( das koennte das Problem sein..) zwei Bytes
aus einem Buffer der fuer jeden schedulten Task wiederverwendet wird
("ldc_p_buffer") und schreibe sie in die Variable "ldc_msb".
* Ich schedule den naechsten Task
* Ich lese (sofort danach) zwei Bytes aus dem o.g. immer gleichen Buffer
und schreibe sie in "ldc_lsb"
* Ich habe in "ldc_msb" UND in "ldc_lsb" die zwei bytes des 2-ten Tasks
stehen.
Wenn ich den 2-ten Task nicht schedule, habe ich in "ldc_msb" die
erwarteten 2 Bytes des 1-ten Tasks stehen und in "ldc_lsb" nichts (0).
Ich glaube inzwischen dass ich da ein timing-problem habe. Das das Ganze
ja endlos wiederholt wird, kann es schon sein dass ich im Buffer einen
Wert habe der nicht von diesem task kommt sondern von einem
"aelteren".
Gruss,
Wolfgang
@Wolfgang Ebersbach (wolfgang2)
>Die Frage reduziert sich also zu: Wenn ich zwei Tasks schedule,
. . . rollen sich nicht nur bei Germanisten die Fußnägel hoch!
>* Ich schedule einen Task.
du schedulst
er schedult
wir schedulen
ihr schedult
sie schedulen
(Goethe hilf!)
Goethe ist Kuer, sobald die Funktion das tut was sie soll.
Kultur kann man sich nur leisten wenn man was zu Essen hat... Genau wie
Umlaute :-)
Soll ich "eintakten" schreiben ? So wie Kibibyte ? Oder "drahtlose
kommunikationsvorrichtung" ?
Ich hab jetzt versucht in der Callback routine ein Flag zu setzen und
mit dem Lesen des Buffers auf das Flag zu warten, mit dem Ergebnis dass
ich im Buffer ( Oha, "Zwischenspeicher" ) jetzt immer eine "0" finde...
Gruss,
Goethe...
Da hast du aber etwas falsch verstanden. Der Scheduler wird einen
keineswegs aufgezwungen, das geht auch sehr wohl ohne.
Dein eigentliches Problem ist aber, dass du die Funktionsweise des
Schedulers nicht verstanden hast. Der Scheduler ist dazu da, um z.B.
längere Berechnungen von einen Interrupt Kontext in den Main Kontext zu
verlagern. Ausgeführt werden diese Funktionen sobald app_sched_execute()
aufgerufen wird (was normalerweise irgendwo in der main Schleife ist).
Du übergibst dem Scheduler jetzt eine Funktion, liest anschließend
sofort den Puffer auf, übergibst die nächste Funktion und liest gleich
noch mal den Puffer aus. Die beiden Funktionen wurden da aber noch gar
nicht ausgeführt, deswegen liest du in beiden Fällen die Daten des
zuletzt statt gefundenen Transfers aus, und das ist eben der zweite
Transfer des vorhergehenden Durchgangs.
Du darfst die Daten natürlich erst dann auslesen, wenn der Transfer
statt gefunden hat, also in ldc_twi_callback_func.
Hallo Fritz,
doch, das mit dem Scheduler ist mir schon klar, auch dass er noetig ist
um deterministisches Verhalten fuer die wirelss - protokollebene
herzustellen und sich daher die user programme unterzuordnen haben.
Nur: Der beruehmte Schlauch ! Auf dem stand ich halt mit all meinem
Gewicht.
Nachdem ich jetzt korrekt sowohl das Lesen des Buffers als auch das
Hochzaehlen der Registeradresse !! in der callback routine mache, also
wenn der "schedulte (1)" I2C transfer auch wirklich fertig ist, geht
alles wie erwartet.
(1) : Extra fuer Falk !
Danke dafuer dass Ihr mich von dem Schlach geschubst habt :-)
Wolfgang