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