0

I'm working on an electronic phone-line emulation for a raspberry pi. For this, I’m in need of a function, which is called each 20 ms (=50Hz, half of the ringing voltage-frequency), checks the electrical current and updates the duty-cycle of a HW PWM. To get there, I set up a signal-handler on an according timer (as I’d hang it into the scheduler in a pure embedded environment) and let it do its work. Note, that the code below is shortened just in order to kfocus on the issue.

This already works pretty well. I was amazed, how accurate the timing is kept (the jitter is below 10µs, measured with an oscilloscope).

However, there’s up to now one flaw: When working like that, the main-loop is actually not required. I cannot return from it, since that would kill the process. When I just let it be an empty loop, all is working fine. But I’m thinking about unnecessarily consumed CPU load.

I tried sleep, wait (both of which should not be used anymore, I know), and sigsuspend. But all of these let to the alarm-handler not being called anymore.

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <sys/time.h>
#include <bcm2835.h>

#define PIN_PWM RPI_V2_GPIO_P1_35

bool b_Alive;

/** Signal-Handlers: ******************************************************************/

void SIG_Alarm (int signum) {
    /** Just toggle a pin for now:                                                    */
    static bool bOn;
    if (bOn) {
        bOn = 0;
        bcm2835_gpio_write(PIN_PWM, LOW);
    }else{
        bOn = 1;
        bcm2835_gpio_write(PIN_PWM, HIGH);
    }
}

void SIG_Quit (int signum) {
    /** Shutdown the bcm-library:                                                     */
    bcm2835_close();
    /** Close the sys-log handler:                                                    */
    syslog(LOG_NOTICE + LOG_DAEMON, "Received SigInt and closed.");
    closelog();
    b_Alive = false;    
}

/** Main-Function: ********************************************************************/


int main(void) {
    /** Variables:                                                                    */
    struct sigaction sa;
    struct itimerval timer;
    /** Open syslog instead: */
    openlog( "PhoneLined", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_LOCAL0 );
    /** Setup pins:                                                                   */
    if (!bcm2835_init()) return 1;
    bcm2835_gpio_fsel(PIN_PWM, BCM2835_GPIO_FSEL_OUTP);
    /** Setup signal handler for kill:                                                */
    memset(&sa, 0, sizeof (sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = &SIG_Quit;
    sigaction(SIGINT , &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
    sigaction(SIGKILL, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
    /** Setup signal handler for timer:                                               */
    memset(&sa, 0, sizeof (sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = &SIG_Alarm;
    sigaction(SIGVTALRM, &sa, NULL);
    /** Configure the timer of a start- and cycle-time of 20ms (=50Hz):               */
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 20000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 20000;
    setitimer(ITIMER_VIRTUAL, &timer, NULL);
    /** Prepare main-loop:                                                            */
    b_Alive = true;
    syslog(LOG_NOTICE + LOG_DAEMON, "Sucessfully initialized.");
    /** ... and do nothing, while the timer works:                                    */
    while (b_Alive) {
        //pause(), suspend, wait or anything?
    }
    exit(EXIT_SUCCESS);
}

Are there any hints, how to proceed, when nothing is actually to be done in this loop?

Jay.Dee.S
  • 23
  • 6
  • `while (b_Alive) { ...}` could be optimised out without volatile. And `pause()` is the way to go. – wildplasser Feb 10 '19 at 15:47
  • I tried that. However, with a **pause();** the signal handler for tha alarm does not get called anymore. I put down the cycle-time of the timer to 500ms (which should be ages), and added an output in the log. Neither thin pin toggled, nor was the log-entry written. – Jay.Dee.S Feb 10 '19 at 16:01
  • 2
    Make sure you have read http://man7.org/linux/man-pages/man7/signal-safety.7.html – hyde Feb 10 '19 at 16:32
  • Suggest doing the job very similarly to writing a deamon. [write a deamon](https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux) – user3629249 Feb 10 '19 at 17:24
  • @user3629249: That is the exact plan, and the original code is well encapsulated in this. But this still leaves the '_main-while_' to be filled, and as soon as I add pause or sleep, I don't get the alarm anymore. – Jay.Dee.S Feb 10 '19 at 17:38
  • @hyde: Iignored the(not) signal safe part. Since it is only on exit (I know:UB is UB, but...) – wildplasser Feb 12 '19 at 00:26

1 Answers1

2

at least add volatile to your flag's definition, or the loop will be removed by the optimiser (for-O2, or higher)

(check with cc -Wall -O4 -S signal.c)


#include <stdbool.h>
// volatile sig_atomic_t
volatile bool
        b_Alive=false;

main():

...
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = 20000;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 20000;
setitimer(ITIMER_REAL, &timer, NULL);

// Prepare main-loop:            

b_Alive = true;
    syslog(LOG_NOTICE + LOG_DAEMON, "Sucessfully initialized.");
    /** ... and do nothing, while the timer works:                                    */
    while (b_Alive) {
        pause(); //, suspend, wait or anything?
    }
    exit(EXIT_SUCCESS);

And, the timertypes (from the fine manual):


   ITIMER_REAL    This timer counts down in real (i.e., wall clock) time.  At each expiration, a SIGALRM signal is generated.

   ITIMER_VIRTUAL This timer counts down against the user-mode CPU time consumed by the process.  (The measurement includes CPU time consumed by all threads in the process.)  At each
                  expiration, a SIGVTALRM signal is generated.

And, since pause() does not consume any CPU ticks, the timer would never expire when using ITIMER_VIRTUAL. (also: a different signal is delivered)

wildplasser
  • 38,231
  • 6
  • 56
  • 94
  • Thank you for the follow-up. And, yes, you are definetely right about the **volatile**, and I added it. However, the alarm-signal handler is still not triggered as soon as I add the _pause()_. Could it be, that _pause()_ also suspends the timer? – Jay.Dee.S Feb 10 '19 at 17:31
  • btw: `ITIMER_REAL` chould probably be preferred over `ITIMER_VIRTUAL` – wildplasser Feb 10 '19 at 17:47
  • Damned, I should've seen it: It WAS the wrong timer! I used ITIMER_REAL, and **pause();** just works beautiful now. Many thanks for the support! :-) – Jay.Dee.S Feb 12 '19 at 07:00