Wallacoloo / printipi

3d printing directly through the Raspberry Pi's GPIO pins
MIT License
141 stars 43 forks source link

I succeeded to be run printipi on rpi3,But I have got other problem. #96

Open Mokutsuno opened 7 years ago

Mokutsuno commented 7 years ago

I succeeded being run this on rpi3 . I used pigpio. So,I made new own primitiveiopin.h , mitpi.h and mitpi.cpp ,which used pigpio.

And I got a problem. printpi toggles PWM ,when I send command of "G1 ....". It moves as intended. However, To toggle PWM isn't end,that is,printipi doesn't move on next command. Also "command: G1... "and"response:ok" are not outputted.

s_6019814896649

I think that printpi moves on next command ,when "exitEventLoop()" is called. So, something problem have occured, then "exitEventLoop()" haven't called.

//////////////////////////////////////////////////////////////////////
#ifndef PLATFORMS_RPI_PRIMITIVEIOPIN_H
#define PLATFORMS_RPI_PRIMITIVEIOPIN_H

#include <cassert>

#include "mitpi.h" //for GpioPin
//#include "hardwarescheduler.h" //for ability to schedule PWM
#include "platforms/auto/chronoclock.h" //for EventClockT.
//for MAX_RPI_PIN_ID
#include "compileflags.h"
#include <pigpiod_if.h>
namespace plat {
    namespace rpi2 {

        class PrimitiveIoPin {
            //logical index of this pin (0-53, I believe)
            mitpi::GpioPin pinIdx;
        public:
            //@return a pin with an invalid output
            //
            //Note that it is undefined to call any function on a null pin except for <isNull> and <id>.
            inline static PrimitiveIoPin null() {
                return PrimitiveIoPin(mitpi::NULL_GPIO_PIN);
            }
            //@return true if the pin is equal to the one constructed by the <null> function.
            inline bool isNull() const {
                return pinIdx == mitpi::NULL_GPIO_PIN;
            }
            //@pinIdx *logical* index of the pin
            //@pullUpDown direct this pin to either pull up to 3.3v, down to gnd, or no pull at all.
            //Note that this pull direction will be applied **even when operating as an output pin**.
            // The rpi's pull resistors preserve state across reboots.
            // This makes it so that when the Pi resets, its pins will still be in a defined state (no runaway heater while the Pi is still booting)
            // Of course, it's still a VERY GOOD IDEA to use hardware pull resistors as well.
            //**Also note:** @pullUpDown acts the same regardless of the <IoPin>'s read/write inversions.
        template <typename ...T> PrimitiveIoPin(mitpi::GpioPin pinIdx, mitpi::GpioPull pullUpDown = mitpi::GPIOPULL_NONE) {
                assert((pinIdx == mitpi::NULL_GPIO_PIN || pinIdx <= MAX_RPI_PIN_ID) && "Make sure to appropriately set MAX_RPI_PIN_ID (see compileflags.h) or else some pins might not behave correctly");
                if (pinIdx != mitpi::NULL_GPIO_PIN) {
                    mitpi::init();
                    //we CANNOT set the pin pull of a null gpio pin
                    mitpi::setPinPull(pinIdx, pullUpDown);
                }
        }
            //@return the logical index of the pin (for use with other platform-specific functions)
            inline mitpi::GpioPin id() const {
                return pinIdx;
            }
            //configure the pin as an output, and set its output state
            inline void makeDigitalOutput(IoLevel lev) {
                mitpi::makeOutput(pinIdx);
                digitalWrite(lev);
            }
            //configure the pin as a PWM output & set its duty cycle and period (if applicable)
            inline void makePwmOutput(float duty, EventClockT::duration desiredPeriod) {
                mitpi::makeOutput(pinIdx);
                //mitpi::pwmWrite(pinIdx, duty, desiredPeriod);
                pwmWrite(duty, desiredPeriod);
            }
            //configure the pin to be an input
            inline void makeDigitalInput() {
                mitpi::makeInput(pinIdx);
            }
            //read the pin's input value (must call makeDigitalInput beforehand)
            inline IoLevel digitalRead() const {
                return mitpi::readPinState(pinIdx);
            }
            //write a digital value to the pin. IoHigh = 3.3v, IoLow = 0v. Must call makeDigitalOutput beforehand
            inline void digitalWrite(IoLevel lev) {
                mitpi::setPinState(pinIdx, lev);
            }
            //set pwm duty cycle & period (if applicable). Must call makePwmOutput beforehand.
            inline void pwmWrite(float duty, EventClockT::duration desiredPeriod) {
                float minPeriod = std::chrono::duration_cast<std::chrono::duration<float>> (desiredPeriod).count();
                int intMinPeriod = (int)minPeriod;
                uint32_t intDuty = 1000000*duty;
                hardware_PWM(this->id(), intMinPeriod, intDuty);
                    //mitpi::pwmWrite(pin, duty, desiredPeriod);
                //HardwareScheduler().queuePwm(*this, duty, desiredPeriod);
            }
        };
    }
}
#endif
///////////////////////////////////////////////////////////////

About pwmWrite(), I haven't understood very much yet.

You said you not actively developing Printipi ,so please reply to me, if it's OK with you..

Wallacoloo commented 7 years ago

You haven't given me much information to work on, here. I would suggest you run Printipi under a debugger (like gdb). You could interrupt it when it hangs (with Ctrl+C) and then get a stack trace by typing bt. This might reveal if it's stuck in a loop / blocking call, etc.

One thing I would also do is check the return code of hardware_PWM. I'm not sure which parts of the code you've edited, but you should make sure that the original hardwarescheduler.cpp isn't being compiled - it also uses DMA and might conflict with the DMA channel that pigpio's hardware_PWM function is using.

Wallacoloo commented 7 years ago

I just dug up the documentation for hardware_PWM. It looks to me like this is calling into a separate daemon to manage the gpios. As such, if you haven't started the pigpio daemon before running printipi, it's quite possible that their code is written in a way where it stalls until the daemon is started.

Mokutsuno commented 7 years ago

Thank you for your reply.

printipi doesn't hang. printipi executes first command (G1 Y0.1 F6000 e.g.),but doesn't execute next command. It keeps outputting "[inf0] RCThermistor2Pin read timeout" during running it.

The original hardwarescheduler.cpp isn't being compiled. The pigpio daemon has been start since boot the raspberry pi 3.

I used gdb. As a result,hardware_PWM() did not return 0. It returned -95. I noticed that hardware_PWM()(pwmWrite()) was called twice only. First,it is called about V1_GPIO_P1_08 (probably FAN). Second,called about V1_GPIO_P1_10 (probably HOTEND). Both return -95. hardware_PWM()(pwmWrite()) isn't called, when I input commands. I can't understand why GPIO toggle (But as above, it doesn't work as intended). There are no functions to make PWM other than hardware_PWM()(pwmWrite()) in printipi. Why?

I followed https://github.com/Wallacoloo/printipi/wiki/Adding-support-for-a-new-CPU-architecture There are "chronoclock.cpp","chronoclock.h","mitpi.cpp","mitpi.h","primitiveiopin.h" and "hardwarescheduler.cpp". under platform/rpi2 directory. "chronoclock.cpp" and "chronoclock.h" are the same as https://github.com/Wallacoloo/printipi/blob/master/src/platforms/rpi/chronoclock.cpp https://github.com/Wallacoloo/printipi/blob/master/src/platforms/rpi/chronoclock.h I changed "primitiveiopin.h" a little.

mitpi.cpp

#include "mitpi.h"
#include <sys/mman.h> //for mmap
#include <sys/time.h> 
#include <time.h> //for nanosleep / usleep (if have -std=gnu99)
#include <unistd.h> //for usleep
#include <stdlib.h> //for exit
#include <cassert> //for assert
#include <fcntl.h> //for file opening
#include "common/logging.h"
#include "platforms/auto/primitiveiopin.h"
#include <pigpiod_if.h>
#include <sys/time.h>
#include "platforms/auto/chronoclock.h" //for EventClockT
namespace mitpi {
#define PI_INPUT 0
#define PI_OUTPUT 1
#define PI_LOW 0
#define PI_HIGH 1
    static volatile uint32_t *gpioBaseMem = nullptr;
    static volatile uint32_t *timerBaseMem = nullptr;
    static volatile uint32_t *gpioPadBaseMem = nullptr;

    static void assertValidPin(PinIntType pin) {
        (void)pin; //unused when assertions are disabled.
        //printf("%d\n",(int)pin);
        assert(pin >= 0 && pin < 64);
    }

    bool init() {
        // Connect to pigpiod
        int status = pigpio_start(0, 0);
        if (status < 0){ LOG("Failed \pigpio_start\n");}

        return 0; //init OK
    }

    void makeOutput(PinIntType pin) {
        assertValidPin(pin);
        set_mode(pin, PI_OUTPUT);
    }
    void makeInput(PinIntType pin) {
        assertValidPin(pin);
        //set_mode(pin, PI_INPUT);
    }
    void setPinHigh(PinIntType pin) {
        assertValidPin(pin);
        gpio_write(pin, PI_HIGH); // 1 is HIGH
    }
    void setPinLow(PinIntType pin) {
        assertValidPin(pin);
        gpio_write(pin, PI_LOW); // 0 is LOW
    }

    void setPinState(PinIntType pin, bool state) {
        state ? setPinHigh(pin) : setPinLow(pin);
    }
    bool readPinState(PinIntType pin) {
        assertValidPin(pin);
        gpio_read(pin);
    }

    void setPinPull(PinIntType pin, GpioPull pull) {
        assertValidPin(pin);
        set_pull_up_down(pin, pull);
    }

    void setPadProperties(uint32_t flags, int bank) {

    }

    void usleep(unsigned int us) {
        //explicitly exposed to allow users of the library access to a usleep function
        ::usleep(us);
    }
struct timeval sysTimeMicro;
    uint64_t readSysTime() {
        gettimeofday(&sysTimeMicro,NULL);
        return ((uint64_t)(sysTimeMicro.tv_usec));
    }

}

mitpi.h

#ifndef PLATFORMS_RPI_MITPI_H
#define PLATFORMS_RPI_MITPI_H

//for uint32_t
#include <sys/time.h>
#include <stdint.h>
/*
 * Mitpi is a small library used for interfacing with the Raspberry Pi's peripherals, including GPIO and timers,
 *   licensed under the extremely permissive MIT license.
 *
 * Please note that most of its features require superuser priviledges, so you may have to launch any executable which uses this library with `sudo'
 */

namespace mitpi {

//Map physical pin locations to logical pin ids used by the processor
enum GpioPin {
    //There are 2 board revisions (and now the model A+/B+), which have slightly different I/O wiring.
    //P1 header (2x13 pins) for version 1 (i.e. before Sept 2012:
    //looking down at the board with the P1 header in the upper right,
    //The upper-left pin is P1_01,
    //The upper-right pin is P1_02 (so, odds are left, evens are right)
    //numbering increases as you go vertically down 
    V1_GPIO_P1_03     =  0, //(i.e., physical pin #3 is addressed in software as #0)
    V1_GPIO_P1_05     =  1,
    V1_GPIO_P1_07     =  4,
    V1_GPIO_P1_08     = 14,
    V1_GPIO_P1_10     = 15,
    V1_GPIO_P1_11     = 17,
    V1_GPIO_P1_12     = 18,
    V1_GPIO_P1_13     = 21,
    V1_GPIO_P1_15     = 22,
    V1_GPIO_P1_16     = 23,
    V1_GPIO_P1_18     = 24,
    V1_GPIO_P1_19     = 10,
    V1_GPIO_P1_21     =  9,
    V1_GPIO_P1_22     = 25,
    V1_GPIO_P1_23     = 11,
    V1_GPIO_P1_24     =  8,
    V1_GPIO_P1_26     =  7,

    //The P1 header for revision 2:
    //same physical numbering as before
    V2_GPIO_P1_03  =  2,
    V2_GPIO_P1_05  =  3,
    V2_GPIO_P1_07  =  4,
    V2_GPIO_P1_08  = 14,
    V2_GPIO_P1_10  = 15,
    V2_GPIO_P1_11  = 17,
    V2_GPIO_P1_12  = 18,
    V2_GPIO_P1_13  = 27,
    V2_GPIO_P1_15  = 22,
    V2_GPIO_P1_16  = 23,
    V2_GPIO_P1_18  = 24,
    V2_GPIO_P1_19  = 10,
    V2_GPIO_P1_21  =  9,
    V2_GPIO_P1_22  = 25,
    V2_GPIO_P1_23  = 11,
    V2_GPIO_P1_24  =  8,
    V2_GPIO_P1_26  =  7,

    //Revision 2 has another 2x4 header called 'P5'.
    //This doesn't have any pins soldered on by default, so (trivial) physical modification is required to use them
    //Physical numbering is ordered ... how?
    V2_GPIO_P5_03  = 28,
    V2_GPIO_P5_04  = 29,
    V2_GPIO_P5_05  = 30,
    V2_GPIO_P5_06  = 31,

    //The addition of the model A+ and B+ brought a 40-pin GPIO header.
    //The first 26 pins are identical to the model A/B Revision 2, with the latter 14 exposing additional GPIOs
    PLUS_GPIO_P1_03  =  2,
    PLUS_GPIO_P1_05  =  3,
    PLUS_GPIO_P1_07  =  4,
    PLUS_GPIO_P1_08  = 14,
    PLUS_GPIO_P1_10  = 15,
    PLUS_GPIO_P1_11  = 17,
    PLUS_GPIO_P1_12  = 18,
    PLUS_GPIO_P1_13  = 27,
    PLUS_GPIO_P1_15  = 22,
    PLUS_GPIO_P1_16  = 23,
    PLUS_GPIO_P1_18  = 24,
    PLUS_GPIO_P1_19  = 10,
    PLUS_GPIO_P1_21  =  9,
    PLUS_GPIO_P1_22  = 25,
    PLUS_GPIO_P1_23  = 11,
    PLUS_GPIO_P1_24  =  8,
    PLUS_GPIO_P1_26  =  7,
    PLUS_GPIO_P1_29  =  5,
    PLUS_GPIO_P1_31  =  6,
    PLUS_GPIO_P1_32  = 12,
    PLUS_GPIO_P1_33  = 13,
    PLUS_GPIO_P1_35  = 19,
    PLUS_GPIO_P1_36  = 16,
    PLUS_GPIO_P1_37  = 26,
    PLUS_GPIO_P1_38  = 20,
    PLUS_GPIO_P1_40  = 21,

    NULL_GPIO_PIN = 127,
};

//Internal pull-up / down resistors
enum GpioPull {
    GPIOPULL_NONE = 0,
    GPIOPULL_DOWN = 1,
    GPIOPULL_UP   = 2,
};

//Each bank of pins can be set to have different drive strengths, hysteresis and slew.
//pass a bitwise-or'd collection of flags to <setPadProperties>
enum GpioPadProperties {
    //Per-pin drive strength. Defaults to 8 mA at boot.
    //Note: avoid drawing a total of > 50 mA across ALL gpio pins / 3v3 supply
    PAD_DRIVE_2MA  = 0,
    PAD_DRIVE_4MA  = 1,
    PAD_DRIVE_6MA  = 2,
    PAD_DRIVE_8MA  = 3,
    PAD_DRIVE_10MA = 4,
    PAD_DRIVE_12MA = 5,
    PAD_DRIVE_14MA = 6,
    PAD_DRIVE_16MA = 7,
    //Hysteresis settings:
    PAD_HYSTERESIS_DIS = 0,
    PAD_HYSTERESIS_EN  = (1 << 3),
    //Slew rate limit
    //From Wikipedia: Slew rate is defined as the maximum rate of change of output voltage per unit of time and is expressed as volt per second.
    PAD_SLEW_LIMIT =   0,
    PAD_SLEW_NO_LIMIT = (1 << 4),
};

typedef unsigned int PinIntType;

bool init();
void makeOutput(PinIntType pin);
void makeInput(PinIntType pin);
void setPinHigh(PinIntType pin);
void setPinLow(PinIntType pin);
void setPinState(PinIntType pin, bool state);
bool readPinState(PinIntType pin);
void setPinPull(PinIntType pin, GpioPull pull);
//Set the drive strength, hysteresis and slew limits for a set of pins.
//@flags bitwise-or of GpioPadProperties flags
//@bank 0 for pins 0-27, 1 -> pins 28-45, 2 -> pins 46-53
void setPadProperties(uint32_t flags, int bank);
void usleep(unsigned int us);
uint64_t readSysTime();

struct InitMitpiType {
    inline InitMitpiType() {
        init();
    }
};

}
#endif

hardwarescheduler.h

#ifndef PLATFORMS_GENERIC_HARDWARESCHEDULER_H
#define PLATFORMS_GENERIC_HARDWARESCHEDULER_H

#include <cassert> //for assert

#include "platforms/auto/chronoclock.h" //for EventClockT
#include "platforms/auto/primitiveiopin.h"
#include "schedulerbase.h" //for OnIdleCpuIntervalT (cannot forward-declare an enum)
#include "common/logging.h"

//forward declare for class defined in outputevent.h
class OutputEvent;

namespace plat {
    namespace rpi2 {

        struct HardwareScheduler {
            //add this event to the hardware queue, waiting until schedTime(evt.time()) if necessary
            inline void queue(OutputEvent evt) {
                evt.primitiveIoPin().digitalWrite(evt.state());
            }
            //Set the given pin to a pwm duty-cycle of `ratio` using a maximum period of maxPeriod (irrelevant if using PCM algorithm). 
            //E.g. queuePwm(5, 0.4) sets pin #5 to a 40% duty cycle.
            inline void queuePwm(const PrimitiveIoPin &pin, float ratio, float idealPeriod) {
            //  plat::rpi2::PrimitiveIoPin::queuePwmWrite(&pin, ratio, idealPeriod);
            }
            //If an event needs to occur at evtTime, this function should return the earliest time at which it can be scheduled.
            inline EventClockT::time_point schedTime(EventClockT::time_point evtTime) const {
                return evtTime;
            }
            //Can be used to perform routine resource management when there's free cpu.
            //Avoid spending more than a few hundred microseconds in this function, or event scheduling might be impacted
            //@return true if we request more cpu time.
            inline bool onIdleCpu(OnIdleCpuIntervalT interval) {
                (void)interval; //unused
                return false; //no more cpu needed
            }
        };
    }
}

#endif

primitiveiopin.h

#ifndef PLATFORMS_RPI_PRIMITIVEIOPIN_H
#define PLATFORMS_RPI_PRIMITIVEIOPIN_H

#include <cassert>
#include <stdio.h>
#include "mitpi.h" //for GpioPin
//#include "hardwarescheduler.h" //for ability to schedule PWM
#include "platforms/auto/chronoclock.h" //for EventClockT.
//for MAX_RPI_PIN_ID
#include "compileflags.h"
#include <pigpiod_if.h>
namespace plat {
    namespace rpi2 {

        class PrimitiveIoPin {
            //logical index of this pin (0-53, I believe)
            mitpi::GpioPin pinIdx;
        public:
            //@return a pin with an invalid output
            //
            //Note that it is undefined to call any function on a null pin except for <isNull> and <id>.
            inline static PrimitiveIoPin null() {
                return PrimitiveIoPin(mitpi::NULL_GPIO_PIN);
            }
            //@return true if the pin is equal to the one constructed by the <null> function.
            inline bool isNull() const {
                return pinIdx == mitpi::NULL_GPIO_PIN;
            }
            //@pinIdx *logical* index of the pin
            //@pullUpDown direct this pin to either pull up to 3.3v, down to gnd, or no pull at all.
            //Note that this pull direction will be applied **even when operating as an output pin**.
            // The rpi's pull resistors preserve state across reboots.
            // This makes it so that when the Pi resets, its pins will still be in a defined state (no runaway heater while the Pi is still booting)
            // Of course, it's still a VERY GOOD IDEA to use hardware pull resistors as well.
            //**Also note:** @pullUpDown acts the same regardless of the <IoPin>'s read/write inversions.
            inline PrimitiveIoPin(mitpi::GpioPin pinIdx, mitpi::GpioPull pullUpDown = mitpi::GPIOPULL_NONE)
                : pinIdx(pinIdx) {
                assert((pinIdx == mitpi::NULL_GPIO_PIN || pinIdx <= MAX_RPI_PIN_ID) && "Make sure to appropriately set MAX_RPI_PIN_ID (see compileflags.h) or else some pins might not behave correctly");
                if (pinIdx != mitpi::NULL_GPIO_PIN) {
                    mitpi::init();
                    //we CANNOT set the pin pull of a null gpio pin
                    mitpi::setPinPull(pinIdx, pullUpDown);
                }
            }
            //@return the logical index of the pin (for use with other platform-specific functions)
            inline mitpi::GpioPin id() const {
                return pinIdx;
            }
            //configure the pin as an output, and set its output state
            inline void makeDigitalOutput(IoLevel lev) {
                mitpi::makeOutput(pinIdx);
                digitalWrite(lev);
            }
            //configure the pin as a PWM output & set its duty cycle and period (if applicable)
            inline void makePwmOutput(float duty, EventClockT::duration desiredPeriod) {
                mitpi::makeOutput(pinIdx);
                //mitpi::pwmWrite(pinIdx, duty, desiredPeriod);
                pwmWrite(duty, desiredPeriod);
            }
            //configure the pin to be an input
            inline void makeDigitalInput() {
                mitpi::makeInput(pinIdx);
            }
            //read the pin's input value (must call makeDigitalInput beforehand)
            inline IoLevel digitalRead() const {
                return mitpi::readPinState(pinIdx);
            }
            //write a digital value to the pin. IoHigh = 3.3v, IoLow = 0v. Must call makeDigitalOutput beforehand
            inline void digitalWrite(IoLevel lev) {
                mitpi::setPinState(pinIdx, lev);
            }
            //set pwm duty cycle & period (if applicable). Must call makePwmOutput beforehand.
            inline void pwmWrite(float duty, EventClockT::duration desiredPeriod) {
                float minPeriod = std::chrono::duration_cast<std::chrono::duration<float>> (desiredPeriod).count();
                int intMinPeriod = (int)minPeriod;
                uint32_t intDuty = 1000000*duty;
                int flag = hardware_PWM(this->id(), intMinPeriod, intDuty);
                printf("%d",flag);
            }
        };

    }
}
#endif
Mokutsuno commented 7 years ago

s_6031948969552