2

I am trying to control a Servo in c ++ using the libgpiod library.

My problem is in defining the 50Hz pwm pulse on the pin that the servo is connected to.

The servo signal cable is connected to pin 33 which corresponds to GPIO 13 (pwm1).

#define ESC_PIN 13

Then I get the chip that controls the line and configure its operation as well as add a description

this-> output_chip = gpiod_chip_open_by_number (0);
this-> esc_line = gpiod_chip_get_line (output_chip, esc_pin);
gpiod_line_request_output (esc_line, "Esc", GPIOD_LINE_ACTIVE_STATE_HIGH);

With the following lines of code I can control the state of the pin but I don't know how to define a pwm pulse without using the sleep function

gpiod_line_set_value (this-> esc_line, 1);
std :: this_thread :: sleep_for (std :: chrono :: milliseconds (2));
gpiod_line_set_value (this-> esc_line, 0);
std :: this_thread :: sleep_for (std :: chrono :: milliseconds (18));

main.cpp (trimmed code)


#include "Telecommunications.h"
#include "Gps.h"
#include "Esc.h"
#include <thread>
#include <atomic>
#include <csignal>

#define ESC_PIN 13

// I'm not a fan of globals but with this way we can catch signals and close files accordingly
Telecommunications g_antenna;
Esc g_esc;

// creating signal handler to close gps, GPIO & I2C
void ctl_c(int s){
    printf("Ending threads\n");
    g_antenna.stop = true;
//    g_gps.stop = true;
    g_esc.stop = true;
    sleep(2); // let the threads end the loops and finally exit
    printf("Bye!\n");
    exit(s);
}

int main(int argc, char** argv)
{
    // initialize sensors variables
    std::atomic<int> height = 0, X = 0, Y = 0, Z = 0;
    std::atomic<double> latitude = 0.0, longitude = 0.0, altitude = 0.0, speed = 0.0;
    // initialize actuators variables
    std::atomic<int> engine = 0, rudder = 0, elevator = 0, ailerons = 0;

    // initialize Telecommunications with Reciver
    g_antenna.init(&height, &latitude, &longitude, &altitude, &speed, &X, &Y, &Z, &engine, &rudder, &elevator, &ailerons);

    // initialize sensors
    Gps g_gps; // = new Gps(&latitude, &longitude, &altitude, &speed);
    g_gps.init(&latitude, &longitude, &altitude, &speed);

    // initialize actuators
    g_esc.init(&engine, ESC_PIN);

    // read signal events so we can terminate the threads and close the files
    signal(SIGINT, ctl_c);
    signal(SIGKILL, ctl_c);
    signal(SIGSTOP, ctl_c);

    // never stop the program
    while (true){
        try{
            // launching telecommunications && joining main thread
            std::thread t_antenna(&Telecommunications::run, &g_antenna);

            // launching sensors && joining main thread
//            std::thread t_gps(&Gps::run, &g_gps);

            // launching actuators && joining main thread
            std::thread t_engine(&Esc::run, &g_esc);

            t_antenna.join();
//            t_gps.join();
            t_engine.join();
        } catch (const std::exception &e){ // catch any exception
            // log the exceptions on the DB
            printf("Exception on main: %s", e.what());
            continue;
        }
    }
    // we do no expect to end the loop until a Ctl+C is send and the function ctl_c will exit()
    return 0;
}

Esc.h

//
// Created by X on 17/12/2020.
//

#ifndef ROHA_ESC_H
#define ROHA_ESC_H

#include <gpiod.h>
#include <atomic>
#include <chrono>

class Esc {
public:
    bool stop = false;
    explicit Esc(std::atomic<int> *engine_ptr, int esc_pin){
        this->init(engine_ptr, esc_pin);
    }
    Esc() = default;

    void init(std::atomic<int> *engine_ptr, int esc_pin){
        this->engine = engine_ptr;

        this->output_chip = gpiod_chip_open_by_number(0);
        this->esc_line = gpiod_chip_get_line(output_chip, esc_pin);

        // request the ESC pin, set a description and define it as ACTIVE_STATE_HIGH
        gpiod_line_request_output(esc_line, "Esc", GPIOD_LINE_ACTIVE_STATE_HIGH);

        // write ZERO signal to pin
        gpiod_line_set_value(esc_line, 0);
    }

    void run(){
        while (!this->stop){
            try {
                // read telecomunications is writing values between 0 and 180 
                int speed = this->engine->load();
                if (speed < 0) speed = 0;
                if (speed > 180) speed = 180;

                // pending, map values to the expected HIGH state ms

                // aproach
                gpiod_line_set_value(esc_line, 1);
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
                gpiod_line_set_value(esc_line, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(19));
                
            } catch (const std::exception &e){
                printf("Error on Esc: %s\n", e.what());
                continue;
            }
        }
        this->close_gpio();
        printf("Ending Thread! Esc\n");
    }
private:
    std::atomic<int> *engine {};
    struct gpiod_chip *output_chip{};
    struct gpiod_line *esc_line{};
    void close_gpio(){
        gpiod_line_release(this->esc_line);
        gpiod_chip_close(this->output_chip);
    }
};

#endif //ROHA_ESC_H

This program is for learning C ++ to control electronics from a rpi. Any improvement or bad practice that you see in the code will be appreciated if you comment it

1 Answer 1

1

You will have to use delays/sleeps to implement the required 50 Hz pulses. The /dev/gpiochip interface has no built-in support for PWM. You will need two delays per pulse: high (delay from pulse start to pulse end), low (delay for remainder of period).

I would discourage the use of software timed PWM with servos. You will shorten their lifespan as they will likely overheat because of timing jitter.

Here is an example using my lgpio library which wraps /dev/gpiochip.

/*
tx_servo.c
2020-12-21
Public Domain

http://abyz.me.uk/lg/lgpio.html

gcc -Wall -o tx_servo tx_servo.c -llgpio

./tx_servo
*/

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

#include <lgpio.h>

#define SERVO 21

#define LFLAGS 0

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

   h = lgGpiochipOpen(0);

   if (h >= 0)
   {
      if (lgGpioClaimOutput(h, LFLAGS, SERVO, 0) == LG_OKAY)
      {
         lgTxServo(h, SERVO, 1500, 50, 0, 0); /* 1500 micros, 50 Hz */
         lguSleep(60);
      }

      lgGpiochipClose(h);
   }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.