简体   繁体   中英

Program call delay - C & Linux

I'm working on new hardware for active prosthetic legs. My system has a BeagleBone Black RevC embedded computer and some custom boards. The BeagleBone Black (BBB) runs Debian Linux.

I wrote a C console app to talk to my other boards from Linux. From the terminal I can send command like "./plan execute_1 set_pid_gains 10 50 0" to change the gains of a control loop running on my motor driver. The "plan" function is written in C. It sends the message via SPI.

The program works fine on its own, I can send all the commands I want. However, when we started testing it from Python (using Popen to call the C program) we realised that it wasn't executing as fast as we wanted.

To replicate and isolate the problem I wrote a shell script (fxa_test_z_1) that sends 3 commands on my network:

#!/bin/bash
# Places FlexSEA in Impedance control mode
# Use with care!

# Set control mode to 'z' (4)
./plan execute_1 set_control 4

# Current loop gains:
./plan execute_1 set_current_gains 10 50 0

# Choose from one of these:
./plan execute_1 set_z_gains 1 0 0
echo "FlexSEA in Stiffness mode"

There is a 14ms delay between each function (measured with an oscilloscope). I ran many small experiments to isolate the problem. Opening & closing the SPI port, sending SPI commands and parsing the agv[] are not the problem. If I call them multiple times in the same program call the delay is in the order of 700us between each serial packet.

Calling "nice -n -19 ./fxa_test_z_1" didn't change anything.

==

What can I do to make those function calls happen faster? Is there any hope I can get them to go sub-ms?

Right now I'm trying to avoid doing major modifications to my code as we want to test our control loops tomorrow.

Thanks!

Jeff

You can make it faster by having plan do more work before it has to be run again. Starting a process is a lot of work, so you don't want to do it more often than necessary.

To have plan do more work, you can feed it a list of commands on stdin or from a file. The tricky part is then parsing each line to extract individual commands and parameters. Since your existing code already reads commands from argv, it's easy enough to parse the commands into an argv-like array using strtok :

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

#define MAX_COMMAND_LEN 256
#define MAX_ARGS 64
char *fake_argv[MAX_ARGS];  /* For compatibility with existing parser. */
const char *delims = " \n";

int main(int argc, char *argv[])
{
    char command[MAX_COMMAND_LEN];

    while( fgets(command, sizeof(command), stdin) )
    {
        int fake_argc = 0;
        fake_argv[fake_argc] = strtok(command, delims);
        while( fake_argv[fake_argc] != NULL )
        {
            fake_argv[++fake_argc] = strtok(NULL, delims);      
        }

        { int i;  /* debug print... you can remove this block */
          printf("Handling command: [%s], argc %d\n", command, fake_argc);
          for( i = 0; i < fake_argc; ++i ) { printf("  arg: [%s]\n", fake_argv[i]); }
        }
        /* ... do the stuff you were already doing except now with fake_argv */
    }

    return 0;
}

Then your bash script can just be a set of lines that you feed your program:

./plan << END_COMMAND_LIST
execute_1 set_control 4
execute_1 set_current_gains 10 50 0
execute_1 set_z_gains 1 0 0
END_COMMAND_LIST

Now the plan process is created once and it runs 3 commands before exiting.

I merged indiv's code with my main() and it works, I can send new commands every 760us (18x faster than before!)

My code might not be as elegant but others might benefit from a complete solution, so here it is:

//****************************************************************************
// main: FlexSEA Plan project: console app to control FlexSEA slaves
//****************************************************************************

//****************************************************************************
// Include(s)
//****************************************************************************

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../inc/flexsea_console.h"
#include "../inc/plan_spi.h"
#include "flexsea_local.h"

//****************************************************************************
// Local variable(s) & definitions
//****************************************************************************

int analog0 = 0;

//Choose between single multiple commands console app:
//#define SINGLE_COMMAND
#define MULTIPLE_COMMANDS

#ifdef SINGLE_COMMAND
    #ifdef MULTIPLE_COMMANDS
        #error "Pick one Command option!"
    #endif
#endif


#define MAX_COMMAND_LEN 256
#define MAX_ARGS 8
char *fake_argv[MAX_ARGS];
const char *delims = " \n";

//****************************************************************************
// External variable(s)
//****************************************************************************

extern unsigned char execute_1_data[];

//****************************************************************************
// Function(s)
//****************************************************************************

int main(int argc, char *argv[])
{
    #ifdef MULTIPLE_COMMANDS
    char command[MAX_COMMAND_LEN];
    char string1[20], string2[20] = "quit";
    char default_argv[] = "";
    int i = 0;
    #endif  //MULTIPLE_COMMANDS

    //Open SPI:
    flexsea_spi_open();

    #ifdef MULTIPLE_COMMANDS
    while(fgets(command, sizeof(command), stdin))
    {
        int fake_argc = 1;

        //Fills fake_argv with empty strings to avoid sending old values with new commands
        for(i = 0; i < MAX_ARGS; i++)
        {
            fake_argv[i] = default_argv;
        }

        //First argument
        fake_argv[fake_argc] = strtok(command, delims);

        //Other arguments
        while( fake_argv[fake_argc] != NULL )
        {
            fake_argv[++fake_argc] = strtok(NULL, delims);
        }

        //Enable for terminal debug only:
        /*
        for(i = 0; i < MAX_ARGS; i++)
        {
            printf("fake_argv[%i] = %s\n", i, fake_argv[i]);
        }
        */

        //Do we want to exit? (exit when "quit" is received)
        strcpy(string1, fake_argv[1]);
        if(!strcmp(string1, string2))
        {
            printf("Quitting.\n");
            break;
        }
        else
        {
            //Parser for console commands:
            flexsea_console_parser(fake_argc, fake_argv);

            //Can we decode what we received?
            decode_spi_rx();
        }
    }
    #endif  //MULTIPLE_COMMANDS

    #ifdef SINGLE_COMMAND

    //Parser for console commands:
    flexsea_console_parser(argc, argv);

    //Can we decode what we received?
    decode_spi_rx();

    #endif  //SINGLE_COMMAND

    //Close SPI:
    flexsea_spi_close();

    return 0;
}

My test shell script is:

#!/bin/bash
# How quickly can we send serial commands?

./plan << END_COMMAND_LIST
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
END_COMMAND_LIST

Thanks A LOT for the help!

Jeff

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM