Beaglebone … all you want to do is talk talk, talk talk.

Andrew B. Wright, S. M. ’88, Ph. D.

6/29/2017

Previous Post: Beaglebone: Outputs: Accessing GPIO off the PRU

Next Post: Beaglebone: Analog Input, the Inverted Pendulum Potentiometer

The archive

There are two methods of pulling information out of the PRU, toggling a bit and sending character data through the rpmsg system.

An embedded processor can be used to collect data and send it through the ARM.  The ARM can then send this data to the world, through bluetooth or ethernet.  This blog will build on the remoteproc communication protocol to perform this streaming.

In order to have two processors talk to each other, you need a communication protocol.  In “Hello, world!” the PRU echoed the exact data that it received.  The host sent the data and then waited (perhaps forever) for a response.  The PRU received data and then put the response onto the interface and terminated.

In this program, specific codes are going to be sent back and forth. Based on the received code, the device will undertake some action.  The same state machine concept will be used on the ARM and on the PRU.

The code sequence will consist of “af” followed by a command byte (‘S’ for start, ‘P’ for stop, ‘D’ for send data, ‘s’ for start ack, ‘p’ for stop ack, ‘d’ for data).  The next word will be an unsigned integer (N) telling how many bytes will follow (used for the data sequence in this case).  Then, the next N bytes will be the data.

So, “afS”0 would start the process and be acknowledged by “afs”0.

“afD”0 would start the transfer of data and “afd”12xxxxxxxxxxxx would represent 12 bytes of double word (4 byte) data (3 data points).

The ARM will exit when it receives this last communication.

This protocol will be used in the analog input blog to start communication and receive data.

The state machine will process a buffer of data each instruction cycle until the specific code has been received, at which point it will undertake an action.  As each byte is compared against the code, the state variable is updated (either to the next state or to the initial state).

I did something a little different in this protocol.  Normally, I would use a ringbuffer to accumulate the data.  However, in this case, I allowed the vrings to function as the ringbuffer.  So, I use the variable buffer_state to decide whether to read more data or not.

In this example, the ARM starts the PRU by sending “afS”.  When the PRU responds with “afs”, the ARM initiates a 10 word data transfer by sending “afD” followed by the two byte number 4, followed by the number of points requested (in 4 bytes).  The PRU responds with “afd” followed by the two byte number containing how many bytes will follow. The PRU then sends these bytes.  The ARM stops the PRU with “afP” and the PRU responds “afp.”

There is no error checking in this process, just to simplify the code; however, the ARM could look at responses and resend commands if the appropriate response had not been received.

The ARM-side code is talk.c:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#define CASSY_DEV "/dev/rpmsg_pru31"
int CASSY;
#define SIZE 100

unsigned char buffer[SIZE], *pbuffer;
unsigned short buffer_state=0;
int plen=0;
#define Nvalues 7
unsigned char cmdvalues[Nvalues]={'s','p','d','S','P','D','i'};

void listen(void){
 int k;
 if(buffer_state == 1) return;
 if ((plen=read(CASSY,buffer, SIZE)) > 0){
 buffer_state = 1;
 pbuffer = buffer;
 }
 return;
}

void parse_the_message(unsigned char *action_state,void *buf,int *len){
 static unsigned short command_state=0,action_state_temp;
 static unsigned N;
 static unsigned char *pbuf;
 int k;

 *action_state = 0; 
 
 if(buffer_state == 0) return;
 for(;plen > 0;plen--,pbuffer++){
 switch(command_state){
 case 0:
 if(*pbuffer == 'a'){
 command_state = 1;
 }
 break;
 case 1:
 if(*pbuffer == 'f'){
 command_state = 2;
 }
 break;
 case 2:
 for(k=0;k<Nvalues;k++){
 if(*pbuffer == cmdvalues[k]){
 command_state = 3;
 action_state_temp = *pbuffer;
 break;
 }
 }
 if(k == Nvalues) command_state = 0;
 break;
 case 3:
 N = (unsigned short)(*pbuffer);
 command_state = 4;
 break;
 case 4:
 N |= (((unsigned short)*pbuffer)<<8);
 if(N == 0){*action_state = action_state_temp; command_state = 0;}
 else{
 command_state = 5;
 pbuf = (unsigned char *)buf;
 *len = N;
 }
 break;
 case 5:
 *pbuf++ = *pbuffer;
 N--;
 if(N == 0){*action_state = action_state_temp; command_state = 0;}
 break;
 default:
 command_state = 0;
 break;
 }
 }
 buffer_state = 0;
}

void update_action_state(unsigned char *action_state, unsigned *buf,int *len){
 listen();
 parse_the_message(action_state,(void *)buf, len);
}
void process_signals(unsigned char *action_state,unsigned char *signal){
 if(*action_state ==0){
 *action_state = *signal;
 *signal = 0;
 }
}
int compose_message(char cmd,char *cmdstr,unsigned N){
 int i=0;
 *cmdstr++ = 'a'; i++;
 *cmdstr++ = 'f'; i++;
 *cmdstr++ = cmd; i++;
 *cmdstr++ = (char)((unsigned char)N); i++;
 *cmdstr++ = (char)((unsigned char)(N>>8)); i++;
 return(i);
}

int act_on_command(unsigned char action_state,unsigned *buf, int len,int N){
 int k,i,n=N;
 char cmdstr[20];

 switch(action_state){
 case 'i':
 printf("initializing\n");
 i=compose_message('S',cmdstr,0);
 write(CASSY, cmdstr, i); break;
 case 's':
 printf("started\n");
 i= compose_message('D',cmdstr,4);
 write(CASSY,cmdstr,i);
 write(CASSY,&n,4);
 break;
 case 'd':
 printf("data received\n");
 i=compose_message('P',cmdstr,0);
 write(CASSY,cmdstr,i);
 break;
 case 'p':
 printf("stopped\n");
 len = len/sizeof(unsigned);
 printf("len = %d\n",len);
 for(k=0;k<len;k++) printf("%x\n",buf[k]);
 return(0);
 break;
 default:
 break;
 }
 return(1);
}

int main(int argc, char **argv)
{
 int flags=1;
 unsigned char action_state,signal='i';
 unsigned buf[30];
 int len,i,N;

 if(argc < 2){
 printf("syntax is './talk N' where N is the number of data points to display.\n");
 N = 1;
 }else{
 N = atoi(*(argv+1));
 if(N < 1) N = 1;
 if(N > 10) N = 10;
 } 
 
 if((CASSY = open(CASSY_DEV, O_RDWR)) < 0){ printf("cannot open file\n"); exit (0);}
 flags = fcntl(CASSY, F_GETFL, 0);
 fcntl(CASSY, F_SETFL, flags | O_NONBLOCK);
 while(flags){
 update_action_state(&action_state,buf,&len);
 process_signals(&action_state,&signal);
 flags = act_on_command(action_state,buf,len,N);
 }
 close(CASSY);
 return 0;
}

The PRU-side code is main.c

#include <stdint.h>
#include <string.h>
#include <pru_cfg.h>
#include <pru_ctrl.h>


volatile register unsigned __R30;
volatile register unsigned __R31;

unsigned short sendwords(char *buf,unsigned short len);
void update_action_state(unsigned char *action_state,unsigned *buf,int *len);
int compose_message(char cmd,char *cmdstr,unsigned N);

#define LOOP 5000 //5 ns * 5000 = 25 x 10-6 sec cycle time (40 kHz)

enum run_states {IDLE,INIT,START,STOP,EXPERIMENT};
unsigned char CASSY_STATE=IDLE;

void act_on_command(unsigned char action_state,unsigned N){ 
 unsigned x;
 char *px=(char *)(&x);
 // unsigned short N=12;
 char cmdstr[30];
 int i,k;
 
 switch(action_state){
 case 'S':
 CASSY_STATE = INIT;
 i=compose_message('s',cmdstr,0);
 sendwords(cmdstr,i);
 break;
 case 'P':
 CASSY_STATE = STOP;
 i=compose_message('p',cmdstr,0);
 sendwords(cmdstr,i);
 break;
 case 'D':
 i=compose_message('d',cmdstr,N*4);
 sendwords(cmdstr,i);
 for(k=0,x=1;k<N;k++,x++) sendwords(px,4);
 break;
 }
 switch(CASSY_STATE){
 case INIT:
 CASSY_STATE = START;
 break;
 case START:
 break;
 case STOP:
 CASSY_STATE = IDLE;
 break;
 case IDLE:
 break;
 }
}

void main(void)
{
 unsigned char action_state=0;
 unsigned buf[20];
 int len;

 CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
#ifdef PRU0
 PRU0_CTRL.CTRL_bit.CTR_EN = 1; //turn on cycle counter
#endif
#ifdef PRU1
 PRU1_CTRL.CTRL_bit.CTR_EN = 1; //turn on cycle counter
#endif
 while (1) {
 //here's where you insert real-time code
 update_action_state(&action_state,buf,&len);
 act_on_command(action_state,buf[0]);
 #ifdef PRU0
 while(PRU0_CTRL.CYCLE < LOOP);
 PRU0_CTRL.CYCLE = 0; //reset the cycle counter/stall counter
 #endif
 #ifdef PRU1
 while(PRU1_CTRL.CYCLE < LOOP);
 PRU1_CTRL.CYCLE = 0; //reset the cycle counter/stall counter
 #endif
 }
}

This example uses the old method of passing user input via the command line.  So, it needs to be executed with

./talk N

where N is the number of data points desired (from 1 to 10).

The communication routines are located in pru_comm.c

#include <stdint.h>
#include <string.h>
#include <pru_cfg.h>
#include <pru_intc.h>
#include <pru_rpmsg.h>
#include "resource_table.h"

register volatile unsigned __R31;
#define VIRTIO_CONFIG_S_DRIVER_OK 4

struct pru_rpmsg_transport transport;
unsigned short rpmsg_src,rpmsg_dst;
unsigned char buffer[RPMSG_BUF_SIZE], *pbuffer;
unsigned short buffer_state=0,plen=0;

unsigned short rpmsg_init(void){
 volatile unsigned char *status;
 static unsigned short rpmsg_state=0;

 switch (rpmsg_state){
 case 0:
 CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
 rpmsg_state = 1;
 break;
 case 1:
 status = &resourceTable.rpmsg_vdev.status;
 if((*status & VIRTIO_CONFIG_S_DRIVER_OK)){ 

 pru_rpmsg_init(&transport, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1, TO_ARM_HOST, FROM_ARM_HOST);
 rpmsg_state = 2;
 }
 break;
 case 2:
 if (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME, CHAN_DESC, CHAN_PORT) == PRU_RPMSG_SUCCESS){ rpmsg_state = 3;
 }
 break;
 }
 return(rpmsg_state);
}

void listen(void){
 if(rpmsg_init() != 3){ buffer_state = 0; return;}
 if(buffer_state == 1) return;
 if(! (__R31 & HOST_INT)) return;
 if (pru_rpmsg_receive(&transport, &rpmsg_src, &rpmsg_dst, buffer, &plen) == PRU_RPMSG_SUCCESS){
 buffer_state = 1;
 pbuffer = buffer; 
 return;
 }
 CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST; 
 return;
}

void parse_the_message(unsigned char *action_state,void *buf,int *len){
 static unsigned short command_state=0,N,action_state_temp;
 static unsigned char *pbuf;
 
 *action_state = 0;
 if(buffer_state == 0) return;
 for(;plen > 0;plen--,pbuffer++){
 switch(command_state){
 case 0:
 if(*pbuffer == 'a') command_state = 1;
 break;
 case 1:
 if(*pbuffer == 'f') command_state = 2;
 break;
 case 2:
 if(*pbuffer == 'S' || *pbuffer == 'P' || *pbuffer == 'D'){
 action_state_temp = *pbuffer;
 command_state = 3;
 }else command_state = 0;
 break;
 case 3: // first byte of N
 command_state = 4;
 N = (unsigned short)(*pbuffer);
 break;
 case 4: // second byte of N
 N |= (((unsigned short)(*pbuffer))<<8);
 if(N == 0){
 *action_state = action_state_temp;
 command_state = 0;
 }
 else{
 command_state = 5;
 pbuf = (unsigned char *)buf;
 *len = N;
 }

 break;
 case 5:
 *pbuf++ = *pbuffer;
 N--;
 if(N == 0){*action_state = action_state_temp; command_state = 0;}
 break;
 default:
 command_state = 0;
 break;

 }
 }
 buffer_state = 0;
}

void update_action_state(unsigned char *action_state,unsigned *buf,int *len){
 listen();
 parse_the_message(action_state,(void *)buf,len);
}

int compose_message(char cmd,char *cmdstr,unsigned N){
 int i=0;
 *cmdstr++ = 'a'; i++;
 *cmdstr++ = 'f'; i++;
 *cmdstr++ = cmd; i++;
 *cmdstr++ = (char)((unsigned char)N); i++;
 *cmdstr++ = (char)((unsigned char)(N>>8)); i++;
 return(i);
}

unsigned short sendwords(char *buf,unsigned short len){
 return(pru_rpmsg_send(&transport,rpmsg_dst,rpmsg_src,buf,len));
}

 

The Makefile is

talk: talk.c
 gcc talk.c -o talk

CODE_ROOT:=/root/talk/
LINKER_CMD_FILE:= talk.cmd

PRU_RPMSG_ROOT:= /usr/lib/ti/pru-software-support-package/
PRU_INCLUDE:= --include_path=/usr/include/arm-linux-gnueabihf/ --include_path=$(PRU_RPMSG_ROOT)include/ --include_path=$(PRU_RPMSG_ROOT)include/am335x/
PRU0_ROOT:= $(CODE_ROOT)pru0/
PRU1_ROOT:= $(CODE_ROOT)pru1/
PRU_TOOLS:=/usr/bin/

CFLAGS=-v3 -O2 --endian=little --hardware_mac=on

LDFLAGS+= -L.


$(PRU0_ROOT)pru_comm0.object: $(CODE_ROOT)pru_comm.c
 $(PRU_TOOLS)clpru $(CFLAGS) $(PRU_INCLUDE) -ppd -ppa -fe $(PRU0_ROOT)pru_comm0.object $(CODE_ROOT)pru_comm.c -D PRU0

$(PRU0_ROOT)main0.object: $(CODE_ROOT)main.c
 $(PRU_TOOLS)clpru $(CFLAGS) $(PRU_INCLUDE) -ppd -ppa -fe $(PRU0_ROOT)main0.object $(CODE_ROOT)main.c -D PRU0

am335x-pru0-fw: $(PRU0_ROOT)main0.object $(PRU0_ROOT)pru_comm0.object
 $(PRU_TOOLS)clpru -z $(LINKER_CMD_FILE) -o $(PRU0_ROOT)am335x-pru0-fw $(PRU0_ROOT)main0.object $(PRU0_ROOT)pru_comm0.object -l$(PRU_RPMSG_ROOT)lib/rpmsg_lib.lib

$(PRU1_ROOT)pru_comm1.object: $(CODE_ROOT)pru_comm.c
 $(PRU_TOOLS)clpru $(CFLAGS) $(PRU_INCLUDE) -ppd -ppa -fe $(PRU1_ROOT)pru_comm1.object $(CODE_ROOT)pru_comm.c -D PRU1

$(PRU1_ROOT)main1.object: $(CODE_ROOT)main.c
 $(PRU_TOOLS)clpru $(CFLAGS) $(PRU_INCLUDE) -ppd -ppa -fe $(PRU1_ROOT)main1.object $(CODE_ROOT)main.c -D PRU1

am335x-pru1-fw: $(PRU1_ROOT)main1.object $(PRU1_ROOT)pru_comm1.object
 $(PRU_TOOLS)clpru -z $(LINKER_CMD_FILE) -o $(PRU1_ROOT)am335x-pru1-fw $(PRU1_ROOT)main1.object $(PRU1_ROOT)pru_comm1.object -l$(PRU_RPMSG_ROOT)lib/rpmsg_lib.lib

install: am335x-pru0-fw am335x-pru1-fw
 cp $(PRU0_ROOT)am335x-pru0-fw /lib/firmware
 cp $(PRU1_ROOT)am335x-pru1-fw /lib/firmware
 rmmod -f pru_rproc
 modprobe pru_rproc
clean:
 rm $(PRU0_ROOT)am335x-pru0-fw
 rm $(PRU1_ROOT)am335x-pru1-fw
 rm $(PRU0_ROOT)*.object
 rm $(PRU1_ROOT)*.object
 rm *.pp

The resource_table is the same as earlier examples.

I’m not sure at this point how fast data can be pushed through the remoteproc interface.  I see nothing that would lead me to believe that it would be significantly slower than the uio interface or the edma interface.  All access to the ARM will suffer from the same nondeterministic problems when the system gets busy regardless of the interface.  So, I would assume that transfers will have up to 100 msec of maximum latency at the ARM’s side with a more average latency of 50 msec and build an interface that accommodates these issues.

Why does speed matter?  One application of the beaglebone is to take sensor data from a robot and stream it to a host computer for live display.  If you could update the display at 30 frames per second, it would not be visually noticeable.  That’s a 33 msec update rate.

I would like to use a 20 msec sample rate on the beaglebone.  I doubt that I need this fast a sample rate; however, it’s the rate that I used when I was involved in gas turbine engine control.  If I can design a system that allows that level of performance, I can deploy it in more stringent applications.

One way to maintain a high update rate despite occasional throughput jams is to buffer the data.  As long as the buffer is long enough that it doesn’t fill during the window where data is being continuously transmitted,  then an update rate that is faster than the average transmission speed can be managed.  There will be an initial delay, and the data presented will not be live; however, the time delay only has to be short enough that it does not cut into reaction time of a human.

To continue this example, let’s say that the beaglebone is producing data at 20 msec and that the average transmission rate is 50 msec.  How long would the buffer have to be to allow 1 minute worth of data to pass without glitch?

In this case, for every 5 samples collected, 2 are transmitted.  The number of samples to be transmitted are 60 seconds/.02 seconds, for a total of 3000 samples.

Draining 2 samples out of this buffer every 100 msec reduces the the needed size by 20 samples per second.  Therefore, the buffer can be 3000 – 20*60 = 1800 samples.  At 10 bytes per sample, this would be a buffer of about 18 kilobytes.  PRU DRAM is 8 kilobytes per PRU.  There is a 12 kilobyte shared buffer.  This gives a total possible buffer of 28 kilobytes, which would be sufficient for this application; however, it would require some engineering work.


Connect IP potentiometer to analog input (1 channel).  Start program.  Move pot back and forth.  Collect on ARM.  Store in CSV file.  Read and plot in excel.


I want to use this with the audio system and fft.  But, that is too intense for this tutorial.  Probably update those blogs and refer back to this one for how to do data acquisition.  Also, the sample rate for audio processing is going to be in the microseconds.  So, storing minutes of data is probably out side the capabilities of the PRU.


NOTES


The TI examples (/usr/lib/ti/pru-software-support-package/examples) have many methods of sharing data between ARM, PRU0, and PRU1: PRU_PRUtoARM_Interrupt, PRU_ARMtoPRU_Interrupt, PRU_Direct_Connect0, PRU_Direct_Connect1.  NOTE: PRU_PRUtoARM_Interrupt, PRU_ARMtoPRU_Interrupt appear to use the uio driver.  Examples described on TI’s web page are obsolete and come from the early days of PRU development.


There is a good description of the linker command file at http://processors.wiki.ti.com/index.php/Linker_Command_File_Primer.

The PRU Optimizing C/C++ Compiler v2.1 User’s Guide describes the syntax needed to access the PRU’s shared memory.

The line in the Linker Command file

PRU_SHAREDMEM: o=0x00010000 l=0x00003000 CREGISTER = 28

provides access to the shared memory from a pointer, declared in the main.c file:

#define PRU_SRAM __far __attribute__((cregister(“PRU_SHAREDMEM”, near)))

PRU_SRAM volatile uint32_t shared_freq_1;

PRU_SRAM volatile uint32_t shared_freq_2;

PRU_SRAM volatile uint32_t shared_freq_3;

The line in the Linker Command file

DDR     : org = 0x80000000 len = 0x00000100 CREGISTER=31

provides access to the ddr memory from a pointer, declared in the main.c file:

volatile far uint32_t CT_DDR __attribute__((cregister(“DDR”, near), peripheral));

The line in the Linker Command file

L3OCMC : org = 0x40000000 len = 0x00010000 CREGISTER=30

provides access to the ddr memory from a pointer, declared in the main.c file:

volatile far uint32_t CT_L3 __attribute__((cregister(“L3OCMC”, near), peripheral));

The example, PRU_Direct_Connect0 shows how to use a resource table with fw_rsc_custom_ints to map the INTC interrupt, channel set up.  This is also seen in lab_4 of the TI PRU Training.

Here’s a useful intrinsic that needs further investigation:

void __xout ( unsigned int device_id , XFR unsigned int base_register , unsigned int use_remapping , void& object );

Digging through the remoteproc source code seems to indicate that the only mode and function of the resource table is to tell the remoteproc driver how to set up the INTC and the vrings.  There may be some memory allocation built in, although that’s not clear.


Some thoughts regarding /dev/uio and /dev/rproc:

Getting data across the pru/arm divide uses INTC.  I’m trying to see how to deal with INTC from the ARM side, but that appears to be a kernel module thing.  There are two kernel modules: uio and rproc.   Both of these modules use INTC.  Presumably, since they may want to configure the same resources, you can only use one at a time.

The rproc wants to configure things using the resource_table.  The uio wants to set things up directly in INTC registers.

Using the uio, the pruss_drv allowed you to access a pointer into PRU memory from the ARM side.  The ARM could look directly at this memory.  Is this wise? I cannot say.  But, I’m thinking not.  Also, it’s difficult to determine if ARM <-> uio <-> pru is faster or slower than ARM <->rproc<->pru.

I would like to be able to look at the INTC registers to see how everything is configured.  I’m pretty sure this can be done from the PRU.

The memory map can be seen at /proc/iomem.

There was an interface to use /dev/mem and mmap.  I’m pretty sure that this would need a driver (eg. uio) to talk to pru mem directly.  It also appears to be a bad idea.

There is a reference to intc at /sys/devices/platform/ocp/4a300000.pruss/4a320000.intc

Another method of transfer which perhaps gets around all of this is the Enhanced Direct Memory Access (edma) module.  Can the PRU set up a back-and-forth transfer between PRU and ARM.  The big ticket problem in all of this seems to be getting an address in ARM (user) memory space where a write could be made.

NOTE:  a useful linux command “find dir -name foo”  allows you to find any file named foo under directory dir.

Some of the PRU examples clearly use remoteproc.  Some appear to be tied to the uio driver.  I suspect there will be a future code clean-up that either fixes or deletes the uio examples.

When I poked through the remoteproc source code much earlier, it appears that the only thing a resource table does is INTC mapping.


Use the following for the PRU’s c-code:

#include “pru_cfg.h”

volatile pruCfg C4 __attribute__((cregister(“PRU_ICSS_CFG”,near),peripheral));

volatile register unsigned int __R31; //connected to PRU’s input pins and INTC controller

volatile register unsigned int __R30; //connected to PRU’s output pins

far volatile unsigned DATA_XFER_SPACE[64] __attribute__ ((location(0x10000));

void main()

{

C4.SYSCFG_bit.STANDBY_INIT = 0;

int k;

DATA_XFER_SPACE[0] = (unsigned)((&DATA_XFER_SPACE[0])&0xffff);

DATA_XFER_SPACE[1]= (unsigned)((&DATA_XFER_SPACE[1])>>16);

for(k=2;k<64;k++) DATA_XFER_SPACE[k] = k;

__R31 = 0×24;

__halt();

}

“Two weeks for that?” you say.  The address location 0x10000 can’t be stuffed into a 16 bit value, so the far keyword is used in defining DATA_XFER_SPACE.  The number of unsigned’s in the buffer was chosen as 64, so that the length of DATA_XFER_SPACE is 0x100 bytes.  Nice round number in hex.

The location attribute puts DATA_XFER_SPACE right at 0x10000 which is the start of PRU_SHAREDMEM in the linker command file.

This address will be used by the host side code.  If the pru side address and the host side address don’t match, then one will be reading/writing to the wrong block of memory.

This could be accomplished indirectly in the linker command file by assigning a SECTION to the desired memory location (e.g. DATA_XFER: > PRU_SHAREDMEM) and then allocating a variable that is exactly the size of the section.  That may lead to potential hinkiness if you accidentally want to put something else in that SECTION. It also requires the use of the #pragma DATA_SECTION(DATA_XFER_SPACE,DATA_XFER) in the pru c-code.

The ARM program is

#include <stdio.h>

#include <prussdrv.h>

#include <pruss_intc_mapping.h>

static volatile unsigned int *pSRAM;

int main (void)

{

unsigned int ret;

void *p;

int k;

tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;

prussdrv_init ();

if (ret = prussdrv_open(PRU_EVTOUT_1)) return (ret);

prussdrv_pruintc_init(&pruss_intc_initdata);

if( ret=prussdrv_load_datafile(PRU0, “./data.bin”)) return (ret);

if(ret=prussdrv_exec_program (PRU0, “./text.bin”)) return ret;

prussdrv_pru_wait_event (PRU_EVTOUT_1);

prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, &p); // get pointer to PRU_SHAREDMEM

pSRAM = (volatile unsigned int *)p;

printf(“The physical memory address is %x\n”,prussdrv_get_phys_addr(p)); //printf the physical address for kicks

for(k=0;k<64;k++) {

printf(“%d, %x\n”,k,*(pSRAM+k)); // read and print the word

}

prussdrv_pru_clear_event (PRU_EVTOUT_1, PRU0_ARM_INTERRUPT);

prussdrv_pru_disable(PRU0);

prussdrv_exit ();

return(0);

}

Posted in: Robotics

Comments are closed.