mirror of
https://github.com/grblHAL/core.git
synced 2026-08-17 16:41:45 +08:00
Expanded ioports API with some configuration and PWM related functions.
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
## grblHAL changelog
|
||||
|
||||
<a name="20230525"/>Build 20230525
|
||||
|
||||
Core:
|
||||
|
||||
* Expanded ioports API with some configuration and PWM related functions.
|
||||
|
||||
Drivers:
|
||||
|
||||
* RP2040: added low-level ioports driver layer for analog output \(PWM\). NOTE: not yet used by any board map files.
|
||||
|
||||
---
|
||||
|
||||
<a name="20230521"/>20230521
|
||||
|
||||
Drivers:
|
||||
|
||||
+7
-3
@@ -343,6 +343,8 @@ typedef enum {
|
||||
PinGroup_StepperStep,
|
||||
PinGroup_StepperDir,
|
||||
PinGroup_AuxOutput,
|
||||
PinGroup_AuxInputAnalog,
|
||||
PinGroup_AuxOutputAnalog,
|
||||
PinGroup_SdCard,
|
||||
PinGroup_MotorChipSelect,
|
||||
PinGroup_MotorUART,
|
||||
@@ -409,9 +411,9 @@ typedef enum {
|
||||
#endif
|
||||
#define PINMODE_PULLUP (PullMode_Up<<3)
|
||||
#define PINMODE_PULLDOWN (PullMode_Down<<3)
|
||||
#define PINMODE_REMAP (1U<<10)
|
||||
#define PINMODE_PWM (1U<<11)
|
||||
#define PINMODE_ANALOG (1U<<12)
|
||||
#define PINMODE_PWM (1U<<10)
|
||||
#define PINMODE_ANALOG (1U<<11)
|
||||
#define PINMODE_REMAP (1U<<14)
|
||||
|
||||
typedef union {
|
||||
uint16_t mask;
|
||||
@@ -433,6 +435,8 @@ typedef union {
|
||||
//! /a cfg_data argument to /a xbar_config_ptr for PWM pins
|
||||
typedef struct {
|
||||
float freq_hz; //
|
||||
float min;
|
||||
float max;
|
||||
float off_value; // percent of period
|
||||
float min_value; // percent of period
|
||||
float max_value; // percent of period
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#else
|
||||
#define GRBL_VERSION "1.1f"
|
||||
#endif
|
||||
#define GRBL_BUILD 20230519
|
||||
#define GRBL_BUILD 20230524
|
||||
|
||||
#define GRBL_URL "https://github.com/grblHAL"
|
||||
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
* and advanced functionality simplifying plugin code that uses them.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
static int16_t digital_in = -1, digital_out = -1, analog_in = -1, analog_out = -1;
|
||||
static int16_t digital_in = -1, digital_out = -1, analog_in = -1, analog_out = -1;
|
||||
|
||||
static uint8_t ioports_count (io_port_type_t type, io_port_direction_t dir)
|
||||
{
|
||||
@@ -126,3 +128,147 @@ bool ioport_can_claim_explicit (void)
|
||||
{
|
||||
return !(hal.port.claim == NULL || hal.port.get_pin_info == NULL);
|
||||
}
|
||||
|
||||
/* experimental code follows */
|
||||
|
||||
static char *get_pnum (io_ports_data_t *ports, uint8_t port)
|
||||
{
|
||||
return ports->pnum ? (ports->pnum + (port * 3) + (port > 9 ? port - 10 : 0)) : NULL;
|
||||
}
|
||||
|
||||
bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uint8_t n_out)
|
||||
{
|
||||
uint_fast8_t n_ports;
|
||||
|
||||
ports->get_pnum = get_pnum;
|
||||
|
||||
if(type == Port_Digital) {
|
||||
|
||||
if(n_in) {
|
||||
ports->n_in_start = hal.port.num_digital_in;
|
||||
hal.port.num_digital_in += (ports->n_in = n_in);
|
||||
ports->in_map = malloc(ports->n_in * sizeof(ports->n_in));
|
||||
}
|
||||
|
||||
if(n_out) {
|
||||
ports->n_out_start = hal.port.num_digital_out;
|
||||
hal.port.num_digital_out += (ports->n_out = n_out);
|
||||
ports->out_map = malloc(ports->n_out * sizeof(ports->n_out));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if(n_in) {
|
||||
ports->n_in_start = hal.port.num_analog_in;
|
||||
hal.port.num_analog_in += (ports->n_in = n_in);
|
||||
ports->in_map = malloc(ports->n_in * sizeof(ports->n_in));
|
||||
}
|
||||
|
||||
if(n_out) {
|
||||
ports->n_out_start = hal.port.num_analog_out;
|
||||
hal.port.num_analog_out += (ports->n_out = n_out);
|
||||
ports->out_map = malloc(ports->n_out * sizeof(ports->n_out));
|
||||
}
|
||||
}
|
||||
|
||||
if((n_ports = max(ports->n_in, ports->n_out)) > 0) {
|
||||
|
||||
char *pn;
|
||||
uint_fast8_t i;
|
||||
|
||||
if((ports->pnum = pn = malloc((3 * n_ports + (n_ports > 9 ? n_ports - 10 : 0)) + 1)))
|
||||
for(i = 0; i < n_ports; i++) {
|
||||
|
||||
if(pn) {
|
||||
*pn = type == Port_Digital ? 'P' : 'E';
|
||||
strcpy(pn + 1, uitoa(i));
|
||||
}
|
||||
|
||||
if(ports->n_in && i < ports->n_in) {
|
||||
if(ports->in_map)
|
||||
ports->in_map[i] = i;
|
||||
if(i < 8) {
|
||||
strcat(ports->input_ports, i == 0 ? "Aux " : ",Aux ");
|
||||
strcat(ports->input_ports, uitoa(i));
|
||||
}
|
||||
}
|
||||
|
||||
if(ports->n_out && i < ports->n_out) {
|
||||
if(ports->out_map)
|
||||
ports->out_map[i] = i;
|
||||
if(i < 8) {
|
||||
ports->out.mask = (ports->out.mask << 1) + 1;
|
||||
strcat(ports->output_ports, i == 0 ? "Aux " : ",Aux ");
|
||||
strcat(ports->output_ports, uitoa(i));
|
||||
}
|
||||
}
|
||||
|
||||
if(pn)
|
||||
pn += i > 9 ? 4 : 3;
|
||||
}
|
||||
}
|
||||
|
||||
return n_ports > 0;
|
||||
}
|
||||
|
||||
/*! \brief calculate inverted pwm value if configured
|
||||
\param pwm_data pointer t a \a spindle_pwm_t structure.
|
||||
\param pwm_value non inverted PWM value.
|
||||
\returns the inverted PWM value to use.
|
||||
*/
|
||||
static inline uint_fast16_t invert_pwm (ioports_pwm_t *pwm_data, uint_fast16_t pwm_value)
|
||||
{
|
||||
return pwm_data->invert_pwm ? pwm_data->period - pwm_value - 1 : pwm_value;
|
||||
}
|
||||
|
||||
/*! \brief Precompute PWM values for faster conversion.
|
||||
\param config pointer to a \ref pwm_config_t structure.
|
||||
\param pwm_data pointer to a \a ioports_pwm_t structure, to hold the precomputed values.
|
||||
\param clock_hz timer clock frequency used for PWM generation.
|
||||
\returns \a true if successful, \a false if no PWM range possible - driver should then revert to simple on/off control.
|
||||
*/
|
||||
bool ioports_precompute_pwm_values (pwm_config_t *config, ioports_pwm_t *pwm_data, uint32_t clock_hz)
|
||||
{
|
||||
if(config->max > config->min) {
|
||||
pwm_data->min = config->min;
|
||||
pwm_data->period = (uint_fast16_t)((float)clock_hz / config->freq_hz);
|
||||
if(config->off_value == 0.0f)
|
||||
pwm_data->off_value = pwm_data->invert_pwm ? pwm_data->period : 0;
|
||||
else
|
||||
pwm_data->off_value = invert_pwm(pwm_data, (uint_fast16_t)(pwm_data->period * config->off_value / 100.0f));
|
||||
pwm_data->min_value = (uint_fast16_t)(pwm_data->period * config->min_value / 100.0f);
|
||||
pwm_data->max_value = (uint_fast16_t)(pwm_data->period * config->max_value / 100.0f); // + pwm_data->offset;
|
||||
pwm_data->pwm_gradient = (float)(pwm_data->max_value - pwm_data->min_value) / (config->max - config->min);
|
||||
pwm_data->always_on = config->off_value != 0.0f;
|
||||
}
|
||||
|
||||
return config->max > config->min;
|
||||
}
|
||||
|
||||
/*! \brief Analog value to PWM conversion.
|
||||
\param pwm_data pointer to a \a ioports_pwm_t structure.
|
||||
\param value analog value to be converted.
|
||||
\returns the PWM value to use.
|
||||
|
||||
__NOTE:__ \a ioports_precompute_pwm_values() must be called to precompute values before this function is called.
|
||||
Typically this is done by the ioports initialization code.
|
||||
*/
|
||||
uint_fast16_t ioports_compute_pwm_value (ioports_pwm_t *pwm_data, float value)
|
||||
{
|
||||
uint_fast16_t pwm_value;
|
||||
|
||||
if(value > pwm_data->min) {
|
||||
|
||||
pwm_value = (uint_fast16_t)floorf((value - pwm_data->min) * pwm_data->pwm_gradient) + pwm_data->min_value;
|
||||
|
||||
if(pwm_value >= pwm_data->max_value)
|
||||
pwm_value = pwm_data->max_value;
|
||||
else if(pwm_value < pwm_data->min_value)
|
||||
pwm_value = pwm_data->min_value;
|
||||
|
||||
pwm_value = invert_pwm(pwm_data, pwm_value);
|
||||
} else
|
||||
pwm_value = value == 0.0f ? pwm_data->off_value : invert_pwm(pwm_data, pwm_data->min_value);
|
||||
|
||||
return pwm_value;
|
||||
}
|
||||
|
||||
@@ -124,4 +124,35 @@ uint8_t ioports_available (io_port_type_t type, io_port_direction_t dir);
|
||||
bool ioport_claim (io_port_type_t type, io_port_direction_t dir, uint8_t *port, const char *description);
|
||||
bool ioport_can_claim_explicit (void);
|
||||
|
||||
//
|
||||
|
||||
struct io_ports_data;
|
||||
|
||||
typedef struct io_ports_data {
|
||||
char *pnum;
|
||||
uint8_t n_in_start, n_in, n_out_start, n_out, *in_map, *out_map;
|
||||
char input_ports[50], output_ports[50];
|
||||
ioport_bus_t out;
|
||||
char *(*get_pnum)(struct io_ports_data *data, uint8_t port);
|
||||
} io_ports_data_t;
|
||||
|
||||
//!* \brief Precalculated values that may be set/used by HAL driver to speed up analog input to PWM conversions. */
|
||||
typedef struct {
|
||||
uint_fast16_t period;
|
||||
uint_fast16_t off_value; //!< NOTE: this value holds the inverted version if software PWM inversion is enabled by the driver.
|
||||
uint_fast16_t min_value;
|
||||
uint_fast16_t max_value;
|
||||
float min; //!< Minimum analog input value.
|
||||
float pwm_gradient;
|
||||
bool invert_pwm; //!< NOTE: set (by driver) when inversion is done in code
|
||||
bool always_on;
|
||||
} ioports_pwm_t;
|
||||
|
||||
bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uint8_t n_out);
|
||||
#define iports_get_pnum(type, port) type.get_pnum(&type, port)
|
||||
#define ioports_map_input(type, port) ( type.in_map ? type.in_map[port] : port )
|
||||
#define ioports_map_output(type, port) ( type.out_map ? type.out_map[port] : port )
|
||||
bool ioports_precompute_pwm_values (pwm_config_t *config, ioports_pwm_t *pwm_data, uint32_t clock_hz);
|
||||
uint_fast16_t ioports_compute_pwm_value (ioports_pwm_t *pwm_data, float value);
|
||||
|
||||
/*EOF*/
|
||||
|
||||
Reference in New Issue
Block a user