Il y a quelques temps, je cherchais à lancer une fonction toutes les n millisecondes avec une bonne précision.
Le but de tout ceci étant de pouvoir faire un dérivation des données collectées en fonction du temps. Et au lieu de mesurer le temps entre chaque donnée, je fixe l’intervalle de temps pour simplifier les calculs.
Ce que je faisais au début: compter le nombre de cycles de la fonction et ajouter des boucles pour perdre du temps. Ça avait un petit désavantage, car dans ma fonction, avec des conditions, le temps d’exécution pouvait être variable, de pas grand chose, certes, mais 50ns sur un nombre important d’itérations, ça peut faire beaucoup, donc jouer sur la précision.
Dans les bouts de code dans les exemples de texane ici: https://github.com/texane/pru_sdk il y a quelquechose qui y ressemble https://github.com/texane/pru_sdk/tree/master/example/pruss_iep
Un extrait du code:
IEP_WAIT_EVENT: // TODO: cf enable IEP interrupt source TODO, then use SLP 1 LBCO r2, CONST_IEP, IEP_REG_CMP_STATUS, 4 QBBC IEP_WAIT_EVENT, r2, 0 SBCO 0, CONST_IEP, IEP_REG_CMP_STATUS, 4
Comme on peut le voir, il fait du polling, en allant regarder dans le registre ou en est le compteur.
Il y a une deuxième façon de faire: en attendant l’interruption.
Donc, j’ai trouvé ici: https://github.com/jstampfl/PruIEP_Int dans l’exemple iepx.p.
TB2: qbbc TB2,r31.t30 // spin here for interrupt
C’est aussi du polling, mais sur le bit d’interruption.
Dans son code, j’aime moins la façon dont il va paramétrer les registres car les adresses sont codées en dur au lieu d’être mises en constantes, une affaire de goût.
J’ai fait un test rapide en faisant un petit mix des deux.
Ce bout de code va générer une interruption toutes les secondes, incrémenter deux compteur, générer une interruption pour le programme en C tournant sur le Beagle Bone, qui va ensuite aller chercher ces compteurs et les afficher.
Ça donne ceci pour le coté PRUSS:
.setcallreg r2.w0 // Going to use r30 #include #include // am335xPruReferenceGuide, table 25 #define PRUSS_PRU_CTRL_REG_WAKEUP_EN 0x08 // am335xPruReferenceGuide, table 190 // offsets from CONST_IEP #define IEP_REG_GLOBAL_CFG 0x00 #define IEP_REG_GLOBAL_STATUS 0x04 #define IEP_REG_COMPEN 0x08 #define IEP_REG_COUNT 0x0c #define IEP_REG_CMP_CFG 0x40 #define IEP_REG_CMP_STATUS 0x44 #define IEP_REG_CMP0 0x48 .origin 0 .entrypoint MAIN MAIN: LBCO r0, CONST_PRUCFG, 4, 4 CLR r0, r0, 4 SBCO r0, CONST_PRUCFG, 4, 4 // // prepare PRU shared memory access MOV r0, 0x000000120 MOV r1, CTPPR_0 ST32 r0, r1 MOV r0, 0x00100000 MOV r1, CTPPR_1 ST32 r0, r1 // // setup IEP module // INITIALIZE IEP INTERRUPTS mov r14,0xBEBC200 //For CMP0, compare trigger sbco r14,c26,0x48,4 mov r14,0x3 // enable CMP0, and enable sbco r14,c26,0x40,4 // counter reset on event mov r14,0x1 sbco r14,c26,0x44,4 // clear status for CMP0 lbco r14,c26,0x4,4 sbco r14,c26,0x4,4 // clear GLOBAL status, overflow mov r14,0x111 // enable IEP counter, inc 1 sbco r14,c26,0,4 // DONE WITH IEP SETUP // SETUP CHANNEL MAP // map SysEvent to Channel 0, leave 16 - 23 alone set by Linux mov r15,0x400 //set up Channel map mov r14,0x09090909 // first map all unused events to sbco r14,c0,r15,4 // Channel 9 mov r15,0x408 sbco r14,c0,r15,4 mov r15,0x40C // skiping offsets 410 & 414, they sbco r14,c0,r15,4 // were set by the C program via prussdrv mov r18,0x43C // end for loop mov r15,0x414 // start -4 for loop TB43: add r15,r15,4 sbco r14,c0,r15,4 qbgt TB43,r15,r18 mov r14,0x00090909 // map SysEvt 7 to channel 0 mov r15,0x404 // now do 404, which has the sbco r14,c0,r15,4 // entries for 4,5,6,7 // Done with Channel Map, Host Interrupt Map now // Host Interrupt 0 - 3 were setup by Linux mov r14,0x09090909 // map channels 4,5,6,7 to Host Int 9 mov r15,0x804 sbco r14,c0,r15,4 mov r14,0x00000909 // map channel 8 & 9 to Host Int 9 mov r15,0x808 sbco r14,c0,r15,4 ldi r15, 0x24 //clear all events call ALLEVT ldi r15,0x28 // enable all events call ALLEVT CALL RSET // // r4 starts with 0, and incremented by 1 every IEP event MOV r4, 0 MOV r5, 0 JMP DO_STORE // // wait for IEP events IEP_WAIT_EVENT: qbbc IEP_WAIT_EVENT,r31.t30 // spin here for interrupt MOV r31.b0, PRU0_ARM_INTERRUPT + 16 CALL RSET // increment and store uint32_t into shared ram (4 bytes) ADD r4, r4, 1 ADD r5, r5, 2 DO_STORE: SBCO r4, CONST_PRUSHAREDRAM, 0, 8 JMP IEP_WAIT_EVENT // // never reached MOV r31.b0, PRU0_ARM_INTERRUPT + 16 HALT RSET: // Routine to clear & enable system events, also host interrupts mov r24,r2 // Save return address // so can call ALLEVT lbco r14,c26,0x4,4 // clear GLOBAL_STATUS sbco r14,c26,0x4,4 lbco r14,c26,0x44,4 // clear CMP_STATUS sbco r14,c26,0x44,4 lbco r14,c26,0x44,4 mov r15,0x24 // to clear system event call ALLEVT mov r15,0x28 // to enable system event call ALLEVT mov r2,r24 // restore return address ret ALLEVT: ldi r14,0x7 sbco r14, c0 ,r15,4 ret
et coté BBB:
#include #include #include #include #include #include "prussdrv.h" #include "pruss_intc_mapping.h" #include "../common/mio.h" /* host pru shared memory */ static int read_words(uint32_t x[2]) { static const size_t sharedram_offset = 2048; volatile uint32_t* p; prussdrv_map_prumem(4, (void**)&p); x[0] = p[sharedram_offset + 0]; x[1] = p[sharedram_offset + 1]; return 0; } /* sigint handler */ static volatile unsigned int is_sigint = 0; static void on_sigint(int x) { is_sigint = 1; } /* main */ int main(int ac, char** av) { tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; uint32_t x[2]; prussdrv_init(); if (prussdrv_open(PRU_EVTOUT_0)) { printf("prussdrv_open open failed\n"); return -1; } prussdrv_pruintc_init(&pruss_intc_initdata); /* execute code on pru0 */ #define PRU_NUM 0 prussdrv_exec_program(PRU_NUM, "./iep.bin"); prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); signal(SIGINT, on_sigint); while (is_sigint == 0) { prussdrv_pru_wait_event (PRU_EVTOUT_0); printf("\tINFO: PRU completed transfer.\r\n"); prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); read_words(x); printf("0x%08x, 0x%08x\n", x[0], x[1]); } /* disable pru and close memory mapping */ prussdrv_pru_disable(PRU_NUM); prussdrv_exit(); return 0; }
C’est fait en vitesse, donc pas très beau, mais c’est fonctionnel.
J’essayerais de formater le code un peu plus tard.
This article was written by Cédric