Files
grblHAL/probe.c
Terje Io b435b48ec1 Changed real time reporting to allow per connection status for which report elements to output.
Added pragmas to shut up warning from the ESP32 compiler.
2026-02-04 11:28:20 +01:00

228 lines
7.2 KiB
C

/*
probe.c - An embedded CNC Controller with rs274/ngc (g-code) support
Part of grblHAL
Copyright (c) 2025-2026 Terje Io
grblHAL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
grblHAL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hal.h"
typedef struct {
probe_id_t probe_id;
probe_flags_t flags;
probeflags_t inverted_bit;
uint8_t port;
void *input;
get_probe_input_ptr get_input;
} probe_t;
static probe_state_t probe_state = { .connected = On };
static probe_t probes[N_PROBE_MAX], *probe = NULL;
static void probe_irq_handler (uint8_t port, bool state)
{
if(probe_state.is_probing) {
probe_state.triggered = On;
if(!probe_state.was_probing) {
probe_state.was_probing = true;
}
} else {
control_signals_t signals = { .mask = hal.control.get_state().mask };
signals.probe_triggered = On;
hal.control.interrupt_callback(signals);
}
}
// Toggle probe connected status.
static void probe_connected_toggle (void)
{
if(!probe_state.is_probing) {
if((probe->flags.connected = probe_state.connected = !probe_state.connected)) {
if(probe->flags.watchable && settings.probe.enable_protection)
probe->flags.guarded = probe_state.irq_enabled = ioport_enable_irq(probe->port, IRQ_Mode_Change, probe_irq_handler);
} else if(probe->flags.guarded && ioport_enable_irq(probe->port, IRQ_Mode_None, probe_irq_handler))
probe->flags.guarded = probe_state.irq_enabled = Off;
if(settings.probe.enable_protection)
report_add_realtime(Report_ProbeProtect);
}
}
// Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
static void probe_configure (bool is_probe_away, bool probing)
{
bool invert = !!(settings.probe.value & probe->inverted_bit.value);
probe_state.inverted = is_probe_away ? !invert : invert;
if(probing) {
if(probe->flags.latchable) {
probe_state.is_probing = probe_state.was_probing = Off;
probe_state.triggered = hal.probe.get_state().triggered;
probe_state.irq_mode = probe_state.triggered ? IRQ_Mode_None : (probe_state.inverted ? IRQ_Mode_Falling : IRQ_Mode_Rising);
}
} else
probe_state.irq_mode = probe->flags.connected && probe->flags.watchable && settings.probe.enable_protection ? IRQ_Mode_Change : IRQ_Mode_None;
if(ioport_enable_irq(probe->port, (pin_irq_mode_t)probe_state.irq_mode, probe_irq_handler)) {
probe_state.irq_enabled = probe_state.irq_mode != IRQ_Mode_None;
probe->flags.guarded = !probing && probe_state.irq_enabled;
}
if(settings.probe.enable_protection)
report_add_realtime(Report_ProbeProtect);
if(!probe_state.irq_enabled)
probe_state.triggered = Off;
probe_state.is_probing = probing;
}
static bool probe_select (probe_id_t probe_id)
{
uint_fast8_t idx = 0;
probe_t *selected_probe = NULL;
do {
if(probes[idx].probe_id == probe_id && probes[idx].get_input)
selected_probe = &probes[idx];
} while(selected_probe == NULL && ++idx < N_PROBE_MAX);
if(!probe_state.is_probing && selected_probe && selected_probe != probe) {
if(probe_state.irq_mode != IRQ_Mode_None)
ioport_enable_irq(probe->port, IRQ_Mode_None, probe_irq_handler);
probe = selected_probe;
hal.probe.configure(false, false);
}
return probe == selected_probe;
}
static probe_flags_t probe_get_caps (probe_id_t probe_id)
{
uint_fast8_t idx = 0;
probe_t *probe = NULL;
do {
if(probes[idx].probe_id == probe_id && probes[idx].get_input)
probe = &probes[idx];
} while(probe == NULL && ++idx < N_PROBE_MAX);
return probe ? probe->flags : (probe_flags_t){0};
}
static bool is_triggered (probe_id_t probe_id)
{
uint_fast8_t idx = 0;
probe_t *probe = NULL;
do {
if(probes[idx].probe_id == probe_id && probes[idx].get_input)
probe = &probes[idx];
} while(probe == NULL && ++idx < N_PROBE_MAX);
return !!probe && probe->get_input(probe->input) ^ !!(settings.probe.value & probe->inverted_bit.value);
}
// Returns the probe connected and triggered pin states.
static probe_state_t get_state (void)
{
probe_state_t state = { .value = probe_state.value };
state.probe_id = probe->probe_id;
state.connected = probe->flags.connected;
if(probe_state.is_probing && probe_state.irq_enabled)
state.triggered = probe_state.triggered;
else
state.triggered = probe->get_input(probe->input) ^ probe_state.inverted;
return state;
}
bool probe_add (probe_id_t probe_id, uint8_t port, pin_irq_mode_t irq_mode, void *input, get_probe_input_ptr get_input)
{
static uint_fast8_t n_probes = 0;
if(get_input == NULL || n_probes >= N_PROBE_MAX)
return false;
bool can_latch;
if(!(can_latch = (irq_mode & IRQ_Mode_RisingFalling) == IRQ_Mode_RisingFalling))
hal.signals_cap.probe_triggered = Off;
else if(n_probes == 0)
hal.signals_cap.probe_triggered = On;
probes[n_probes].probe_id = probe_id;
probes[n_probes].port = port;
probes[n_probes].flags.available = On;
probes[n_probes].flags.connected = probe_state.connected;
probes[n_probes].flags.latchable = can_latch;
probes[n_probes].flags.watchable = !!(irq_mode & IRQ_Mode_Change);
probes[n_probes].input = input;
probes[n_probes].get_input = get_input;
switch(probe_id) {
case Probe_Toolsetter:
probes[n_probes].inverted_bit.invert_toolsetter_input = On;
break;
case Probe_2:
probes[n_probes].inverted_bit.invert_probe2_input = On;
break;
default: // Probe_Default
probes[n_probes].inverted_bit.invert_probe_pin = On;
break;
}
hal.driver_cap.probe_pull_up = On;
hal.probe.configure = probe_configure;
hal.probe.connected_toggle = probe_connected_toggle;
hal.probe.get_caps = probe_get_caps;
hal.probe.get_state = get_state;
hal.probe.is_triggered = is_triggered;
if(probe == NULL || probe_id == Probe_Default)
probe = &probes[n_probes];
if(++n_probes == 2)
hal.probe.select = probe_select;
return true;
}
void probe_connected_event (void *data)
{
if(hal.probe.connected_toggle) {
if((uintptr_t)data == 2)
hal.probe.connected_toggle();
else {
probe_state_t state = hal.probe.get_state();
if(state.probe_id == Probe_Default && hal.driver_cap.probe && state.connected != !!data)
hal.probe.connected_toggle();
}
}
}