Delaying a program call - C & Linux
I am working on new equipment 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 a command as "./plan execute_1 set_pid_gains 10 50 0" to change the gain of the control loop running on my motor driver. The "plan" function is written in C. It sends a message via SPI.
The program works fine by itself, I can send all the commands I want. However, when we started testing it with Python (using Popen to invoke the C program), we realized that it was not 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 to 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 have done a lot of small experiments to isolate the problem. Opening and closing the SPI port, sending SPI commands and parsing agv [] is not a problem. If I call them multiple times in the same program call, the delay is on the order of 700us between each sequential packet.
Calling "nice -n -19./fxa_test_z_1" didn't change anything.
==
What can I do to make these function calls run faster? Is there any hope that I can get them to go to sub-ms?
I am currently trying to avoid major changes to my code, as we want to test our control loops tomorrow.
Thank!
Jeff
source to share
You can do it faster if you plan
do more work before it starts up again. Starting a process is a lot of work, so you don't want to do it more often than necessary.
To do plan
more work, you can feed it a list of commands to stdin or from a file. The tricky part then parses each line to extract individual commands and parameters. Since your existing code is already reading commands from argv, it is 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 could just be a bunch 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 process plan
is created once and it runs 3 commands before exiting.
source to share
I have combined the indiv code with my main () and it works, I can send new commands every 760us (18x faster than before!)
My code may not be as elegant, but others may benefit from the 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:
#!/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
source to share