mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-02 19:02:35 +02:00
Finished implementing PWM and UART pin users
This commit is contained in:
84
Grbl_Esp32/src/PinUsers/PwmPin.cpp
Normal file
84
Grbl_Esp32/src/PinUsers/PwmPin.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "PwmPin.h"
|
||||
|
||||
#include "../Pin.h"
|
||||
#include "../Assert.h"
|
||||
#include "LimitedResource.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace PinUsers {
|
||||
class NativePwm : public PwmDetail {
|
||||
static LimitedResource<16>& PwmChannelResources() {
|
||||
// The ESP32 chip has 16 PWM channels.
|
||||
|
||||
static LimitedResource<16> instances_;
|
||||
return instances_;
|
||||
}
|
||||
|
||||
Pin pin_;
|
||||
int pwmChannel_;
|
||||
uint32_t frequency_;
|
||||
uint32_t maxDuty_;
|
||||
uint8_t resolutionBits_;
|
||||
|
||||
/*
|
||||
Calculate the highest precision of a PWM based on the frequency in bits
|
||||
|
||||
80,000,000 / freq = period
|
||||
determine the highest precision where (1 << precision) < period
|
||||
*/
|
||||
uint8_t calculatePwmPrecision(uint32_t freq) {
|
||||
uint8_t precision = 0;
|
||||
|
||||
// increase the precision (bits) until it exceeds allow by frequency the max or is 16
|
||||
// TODO is there a named value for the 80MHz?
|
||||
while ((1 << precision) < (uint32_t)(80000000 / freq) && precision <= 16) {
|
||||
precision++;
|
||||
}
|
||||
|
||||
return precision - 1;
|
||||
}
|
||||
|
||||
public:
|
||||
NativePwm(Pin pin, uint32_t frequency, uint32_t maxDuty) : frequency_(frequency), maxDuty_(maxDuty) {
|
||||
auto native = pin.getNative(Pin::Capabilities::PWM | Pin::Capabilities::Native);
|
||||
|
||||
pwmChannel_ = PwmChannelResources().tryClaim();
|
||||
Assert(pwmChannel_ != -1, "PWM Channel could not be claimed. Are all PWM channels in use?");
|
||||
|
||||
resolutionBits_ = calculatePwmPrecision(frequency);
|
||||
|
||||
ledcSetup(pwmChannel_, frequency, resolutionBits_);
|
||||
ledcAttachPin(native, pwmChannel_);
|
||||
ledcWrite(pwmChannel_, 0);
|
||||
|
||||
// grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "PWM Output:%d on Pin:%s Freq:%0.0fHz", _number, _pin.name().c_str(), _pwm_frequency);
|
||||
}
|
||||
|
||||
uint32_t getFrequency() const override { return frequency_; }
|
||||
uint32_t getMaxDuty() const override { return maxDuty_; }
|
||||
|
||||
void setValue(float value) {
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
} else if (value > 1) {
|
||||
value = 1;
|
||||
}
|
||||
|
||||
auto duty = value * (uint32_t(1) << int(resolutionBits_));
|
||||
ledcWrite(pwmChannel_, uint32_t(duty));
|
||||
}
|
||||
|
||||
~NativePwm() override {
|
||||
ledcWrite(pwmChannel_, 0);
|
||||
PwmChannelResources().release(pwmChannel_);
|
||||
}
|
||||
};
|
||||
|
||||
PwmPin::PwmPin(Pin pin, uint32_t frequency, uint32_t maxDuty) {
|
||||
Assert(pin.capabilities().has(Pin::Capabilities::PWM | Pin::Capabilities::Native), "Pin does not support PWM");
|
||||
|
||||
// For now, we only support Native pins. In the future, we might support other pins.
|
||||
_detail = new NativePwm(pin, frequency, maxDuty);
|
||||
}
|
||||
}
|
36
Grbl_Esp32/src/PinUsers/PwmPin.h
Normal file
36
Grbl_Esp32/src/PinUsers/PwmPin.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Pin.h"
|
||||
#include "../Assert.h"
|
||||
|
||||
namespace PinUsers {
|
||||
class PwmDetail {
|
||||
public:
|
||||
virtual uint32_t getFrequency() const = 0;
|
||||
virtual uint32_t getMaxDuty() const = 0;
|
||||
|
||||
// Sets the PWM value from 0..1.
|
||||
virtual void setValue(float value) = 0;
|
||||
|
||||
virtual ~PwmDetail() {}
|
||||
};
|
||||
|
||||
class PwmPin {
|
||||
Pin _pin;
|
||||
PwmDetail* _detail;
|
||||
|
||||
public:
|
||||
PwmPin() : _pin(Pin::UNDEFINED), _detail(nullptr) {}
|
||||
PwmPin(Pin pin, uint32_t frequency, uint32_t maxDuty);
|
||||
|
||||
// Returns actual frequency which might not be exactly the same as requested(nearest supported value)
|
||||
inline uint32_t getFrequency() const { return _detail->getFrequency(); }
|
||||
|
||||
// Returns actual maxDuty which might not be exactly the same as requested(nearest supported value)
|
||||
inline uint32_t getMaxDuty() const { return _detail->getMaxDuty(); }
|
||||
|
||||
inline void setValue(float value) const { return _detail->setValue(value); }
|
||||
|
||||
inline ~PwmPin() { delete _detail; }
|
||||
};
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
#include "../Pins/PinOptionsParser.h"
|
||||
#include "../Assert.h"
|
||||
#include "LimitedResource.h"
|
||||
|
||||
#include <driver/uart.h>
|
||||
|
||||
namespace PinUsers {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Pin.h"
|
||||
#include <WString.h>
|
||||
#include "../Assert.h"
|
||||
|
||||
#include <WString.h>
|
||||
|
||||
// TODO FIXME: Uart also suffers from the way settings works: What you would want is 2 phases,
|
||||
// so (1) validation and preparation, and (2) building the actual uart. Now, it's all combined
|
||||
// in the single constructor, which works, but could throw an exception.
|
||||
|
Reference in New Issue
Block a user