Help with the PRU to measure the duration of a pulse

Hello, I would like to know how to use the PRU to measure the duration of a pulse sent by an ultrasonic sensor, I must do
this since Linux is not able to use real hard interrupts, so it won’t be able to get acurate data off them.

I would like to know if this is possible using the on board PRU , and where i could find some documentation on them (how to program them,
how they send data to the beaglebone). I am asking for help since most newbie tutorials are just focused on GPIO and PWM.

Thanks in advance!

hi,

I’ll place this info here just for reference. The code is “rough” and based off the examples. I was a newbie to this stuff to, and I still am, but I made some progress.

I used the PRU to quickly scan p8_16 for input coming from an IR Sensor (IRM-3438). The PRU will read the p8_16 at close to 10ns. After comparing the input to a PWM output, I would say either the PWM has precision issues or the GPIO/PRU can’t read at 10ns, but it’s still significantly less than 1microsecond, iirc.

IR wiring:
BBB IR Sensor
3.3V_sys → 100 ohm → vcc
gnd → gnd
p8_16 → 4.7 kOhm → ir output

Typical output for IRM-3438 with a yk-001 remote:

`

  • 58984 53340
  • 59031 53364
  • 59066 53316
  • 59013 53283
  • 59132 53353
  • 59005 51042
  • 61333 53336
  • 59060 165465
  • 59151 163160
  • 61480 163057
  • 61537 165442
  • 59152 163270
  • 61293 165502
  • 59129 165392
  • 59225 163179
  • 61368 163249
  • 61318 53408

`

Values close to 60000±10000 are 0’s, where values close to 160000±10000 are 1’s. Start value is 909000 ±10000 is a code start value. Code size is 64, however all codes for the yk-001 can be used by just looking at the “on” times: 60000 for 0, 160000 for 1, 10000 tolerance.

Regards,
Rene Robichaud

BB-BONE-PRU-00A0.dts:

`
/*

  • pru dts file BB-BONE-PRU-00A0.dts
    */
    /dts-v1/;
    /plugin/;

/ {
compatible = “ti,beaglebone”, “ti,beaglebone-black”;

/* identification */
part-number = “BB-BONE-PRU”;
version = “00A0”;

exclusive-use =
“pru0”,
“P8.16”;

fragment@0 {
target = <&am33xx_pinmux>;
overlay {
mygpio: pinmux_mygpio{
pinctrl-single,pins =
<
0x038 0x2e /* p8_16 input gpio1_14 mode, no pullup/down */

;
};
};
};

fragment@1 {
target = <&ocp>;
overlay {
test_helper: helper {
compatible = “bone-pinmux-helper”;
pinctrl-names = “default”;
pinctrl-0 = <&mygpio>;
status = “okay”;
};
};
};

fragment@2{
target = <&pruss>;
overlay {
status = “okay”;
};
};
};
`

pru_p8_16.p:

`

.origin 0
.entrypoint start

#define GPIO1 0x4804c000
#define GPIO2 0x481ac000
#define GPIO_OE 0x134
#define GPIO_DATAIN 0x138

// Refer to this mapping in the file - \prussdrv\include\pruss_intc_mapping.h
#define PRU0_PRU1_INTERRUPT 17
#define PRU1_PRU0_INTERRUPT 18
#define PRU0_ARM_INTERRUPT 19
#define PRU1_ARM_INTERRUPT 20
#define ARM_PRU0_INTERRUPT 21
#define ARM_PRU1_INTERRUPT 22

#define CONST_PRUCFG C4
#define CONST_PRUDRAM C24
#define CONST_PRUSHAREDRAM C28
#define CONST_DDR C31

// Address for the Constant table Block Index Register (CTBIR)
#define CTBIR 0x22020

// Address for the Constant table Programmable Pointer Register 0(CTPPR_0)
#define CTPPR_0 0x22028

// Address for the Constant table Programmable Pointer Register 1(CTPPR_1)
#define CTPPR_1 0x2202C

///////////////////////////////////////////////////////////////////////////////
// start
///////////////////////////////////////////////////////////////////////////////

start:

// Enable OCP master port
lbco r0, CONST_PRUCFG, 4, 4
clr r0, r0, 4 // Clear SYSCFG[STANDBY_INIT] to enable OCP master port
sbco r0, CONST_PRUCFG, 4, 4

// Configure the programmable pointer register for PRU0 by setting c28_pointer[15:0]
// field to 0x0120. This will make C28 point to 0x00012000 (PRU shared RAM).
mov r0, 0x00000120
mov r1, CTPPR_0
sbbo r0, r1, 0, 4

// Configure the programmable pointer register for PRU0 by setting c31_pointer[15:0]
// field to 0x0010. This will make C31 point to 0x80001000 (DDR memory).
mov r0, 0x00100000
mov r1, CTPPR_1
sbbo r0, r1, 0, 4

//Load values from external DDR Memory into Registers R0/R1/R2
//lbco r0, CONST_DDR, 0, 16

reset:
mov r1, 0 // off counter
mov r2, 0 // on counter
mov r3, 0 // period counter

// off loop
loop_off:
add r1, r1, 1 // increment off counter
qbbc loop_off, r31, 14 // while ( r31.t14 == 0 )

// check to see if arm processor signals exit
qbbs exit, r31, 30 // r31.t30

// on loop
loop_on:
add r2, r2, 1 // inc on counter
qbbs loop_on, r31, 14 // while ( r31.t14 == 1 )

send_data:
// store values of registers r0, r1, r2 and r3 into PRU shared RAM
sbco r0, CONST_PRUSHAREDRAM, 0, 32

// notify program
mov r31.b0, PRU0_ARM_INTERRUPT+16
jmp reset

exit:
// notify program
mov r31.b0, PRU0_ARM_INTERRUPT+16

halt

`

pru_p8_16.c

`
// modified pru example to read p8_16 at close to 10ns
// Rene Robichaud

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

// Driver header file
#include <prussdrv.h>
#include <pruss_intc_mapping.h>

#define PRU_NUM 0

#define DDR_BASEADDR 0x80000000
#define OFFSET_DDR 0x00001000

#define OFFSET_SHAREDRAM 2048 // 0x00002000
#define ADDEND1 0x98765400u
#define ADDEND2 0x12345678u
#define ADDEND3 0x00000001u
#define ADDEND4 0x87654321u

#define PRUSS0_SHARED_DATARAM 4

static int mem_fd;
static void *ddrMem, *sharedMem;
static unsigned int *sharedMem_uint;
int g_interrupted = 0;

//=============================================================================

static int user_pru_init( unsigned short pru_num )
{
void *DDR_regaddr1, *DDR_regaddr2, *DDR_regaddr3, *DDR_regaddr4;

/* open the device */
mem_fd = open( “/dev/mem”, O_RDWR );
if (mem_fd < 0)
{
printf( “Failed to open /dev/mem (%s)\n”, strerror( errno ) );
return -1;
}

/* map the DDR memory */
ddrMem = mmap( 0, 0x0FFFFFFF, PROT_WRITE | PROT_READ, MAP_SHARED, mem_fd, DDR_BASEADDR );
if ( ddrMem == NULL )
{
printf(“Failed to map the device (%s)\n”, strerror(errno));
close(mem_fd);
return -1;
}

/* Store Addends in DDR memory location */
DDR_regaddr1 = ddrMem + OFFSET_DDR;
DDR_regaddr2 = ddrMem + OFFSET_DDR + 0x00000004;
DDR_regaddr3 = ddrMem + OFFSET_DDR + 0x00000008;
DDR_regaddr4 = ddrMem + OFFSET_DDR + 0x0000000c;

(unsigned int) DDR_regaddr1 = ADDEND1;
(unsigned int) DDR_regaddr2 = ADDEND2;
(unsigned int) DDR_regaddr3 = ADDEND3;
(unsigned int) DDR_regaddr4 = ADDEND4;

return(0);
}

//=============================================================================

static unsigned short user_pru_recv_data( unsigned short pru_num )
{
/* Allocate Shared PRU memory. /
prussdrv_map_prumem( PRUSS0_SHARED_DATARAM, &sharedMem );
sharedMem_uint = (unsigned int
) sharedMem;

return 1;

}

//=============================================================================

int main( int argc, char* argv )
{
unsigned int ret;
tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
int i;
int update = 0;

printf(“\nINFO: Starting %s example.\r\n”, “PRU_p8_12”);
/* Initialize the PRU */
prussdrv_init ();

/* Open PRU Interrupt */
ret = prussdrv_open( PRU_EVTOUT_0 );
if (ret)
{
printf(“prussdrv_open open failed\n”);
return (ret);
}

/* Get the interrupt initialized */
prussdrv_pruintc_init(&pruss_intc_initdata);

/* Initialize example */
printf(“\tINFO: Initializing example.\r\n”);
user_pru_init( PRU_NUM );

/* Execute example on PRU */
printf(“\tINFO: Executing example.\r\n”);

// EXECUTE
prussdrv_exec_program (PRU_NUM, “./pru_p8_16.bin”);

/* Wait until PRU0 has finished execution */
printf(“\tINFO: Waiting for HALT command.\r\n”);

while ( !g_interrupted )
{
// wait for interrupt
prussdrv_pru_wait_event ( PRU_EVTOUT_0 );
prussdrv_pru_clear_event ( PRU_EVTOUT_0, PRU0_ARM_INTERRUPT );

// process pru shared memory
user_pru_recv_data( PRU_NUM );

update = !update;
printf( "%c ", update ? ‘+’ : ‘-’ );
for ( i = 1; i < 3; i++ )
{

printf( "%10u ", sharedMem_uint[ OFFSET_SHAREDRAM + i ] );

}

printf( “\n” );
}

prussdrv_pru_wait_event ( PRU_EVTOUT_0 );
prussdrv_pru_clear_event ( PRU_EVTOUT_0, PRU0_ARM_INTERRUPT );

/* Disable PRU and close memory mapping*/
prussdrv_pru_disable( PRU_NUM );
prussdrv_exit ();
munmap( ddrMem, 0x0FFFFFFF );
close( mem_fd );

return 0;
}

`

Find infos on how to use the PRUSS in the PRU FAQ.

I’d try the inbuild eCAP device. It’s connected to header P9_42, mode 3 (pr1_ecap0_ecap_cap_in_apwm_o).