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(&amp;height, &amp;latitude, &amp;longitude, &amp;altitude, &amp;speed, &amp;X, &amp;Y, &amp;Z, &amp;engine, &amp;rudder, &amp;elevator, &amp;ailerons);

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

// initialize actuators
g_esc.init(&amp;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 &amp;&amp; joining main thread
        std::thread t_antenna(&amp;Telecommunications::run, &amp;g_antenna);

        // launching sensors &amp;&amp; joining main thread

// std::thread t_gps(&Gps::run, &g_gps);

        // launching actuators &amp;&amp; joining main thread
        std::thread t_engine(&amp;Esc::run, &amp;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&lt;int&gt; *engine_ptr, int esc_pin){
    this-&gt;engine = engine_ptr;

    this-&gt;output_chip = gpiod_chip_open_by_number(0);
    this-&gt;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, &quot;Esc&quot;, GPIOD_LINE_ACTIVE_STATE_HIGH);

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

void run(){
    while (!this-&gt;stop){
        try {
            // read telecomunications is writing values between 0 and 180 
            int speed = this-&gt;engine-&gt;load();
            if (speed &lt; 0) speed = 0;
            if (speed &gt; 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 &amp;e){
            printf(&quot;Error on Esc: %s\n&quot;, e.what());
            continue;
        }
    }
    this-&gt;close_gpio();
    printf(&quot;Ending Thread! Esc\n&quot;);
}

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

Toni M.
  • 23
  • 1
  • 3

1 Answers1

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);

} }

joan
  • 71,852
  • 5
  • 76
  • 108