Expanded ioports API with some configuration and PWM related functions.

This commit is contained in:
Terje Io
2023-05-25 08:57:10 +02:00
parent 334ca8f8fa
commit 5249bc66fa
5 changed files with 198 additions and 5 deletions
+12
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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"
+147 -1
View File
@@ -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;
}
+31
View File
@@ -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*/