Skip to content

Commit

Permalink
async analog read
Browse files Browse the repository at this point in the history
  • Loading branch information
t0mpr1c3 committed Oct 1, 2023
1 parent fd6c7ad commit 55e9d34
Show file tree
Hide file tree
Showing 21 changed files with 1,004 additions and 242 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
active = true
path = lib/avr-libc
url = https://github.com/avrdudes/avr-libc.git
[submodule "lib/AnalogReadAsync"]
path = lib/AnalogReadAsync
url = https://github.com/boothinator/AnalogReadAsync
1 change: 1 addition & 0 deletions lib/AnalogReadAsync
Submodule AnalogReadAsync added at b97e43
39 changes: 39 additions & 0 deletions src/ayab/analogReadAsyncWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*!
* \file analogReadAsyncWrapper.cpp
* \brief Class containing methods to actuate a analogReadAsyncWrapper connected
* to PIEZO_PIN.
*
* This file is part of AYAB.
*
* AYAB 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.
*
* AYAB 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 AYAB. If not, see <http://www.gnu.org/licenses/>.
*
* Original Work Copyright 2013 Christian Obersteiner, Andreas Müller
* Modified Work Copyright 2020-3 Sturla Lange, Tom Price
* http://ayab-knitting.com
*/

#include "analogReadAsyncWrapper.h"

/*!
* \brief Wrapper for analogReadAsync
*/
void AnalogReadAsyncWrapper::analogReadAsyncWrapped(uint8_t pin, analogReadCompleteCallback_t cb, const void *data) {
#ifndef AYAB_TESTS
analogReadAsync(pin, cb, data);
#else
(void) pin;
(void) cb;
(void) data;
#endif
}
64 changes: 64 additions & 0 deletions src/ayab/analogReadAsyncWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*!
* \file analogReadAsyncWrapper.h
*
* This file is part of AYAB.
*
* AYAB 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.
*
* AYAB 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 AYAB. If not, see <http://www.gnu.org/licenses/>.
*
* Original Work Copyright 2013 Christian Obersteiner, Andreas Müller
* Modified Work Copyright 2020-3 Sturla Lange, Tom Price
* http://ayab-knitting.com
*/

#ifndef ANALOGREADASYNCWRAPPER_H_
#define ANALOGREADASYNCWRAPPER_H_

#include <Arduino.h>
#include <analogReadAsync.h>

class AnalogReadAsyncWrapperInterface {
public:
virtual ~AnalogReadAsyncWrapperInterface() = default;

// any methods that need to be mocked should go here
virtual void analogReadAsyncWrapped(uint8_t pin, analogReadCompleteCallback_t cb, const void *data) = 0;
};

// Container class for the static method analogReadAsync.
// Dependency injection is enabled using a pointer to a global instance of
// either `AnalogReadAsyncWrapper` or `AnalogReadAsyncWrapperMock`,
// both of which classes implement the
// pure virtual methods of `AnalogReadAsyncWrapperInterface`.

class GlobalAnalogReadAsyncWrapper final {
private:
// singleton class so private constructor is appropriate
GlobalAnalogReadAsyncWrapper() = default;

public:
// pointer to global instance whose methods are implemented
static AnalogReadAsyncWrapperInterface *m_instance;

static void analogReadAsyncWrapped(uint8_t pin, analogReadCompleteCallback_t cb = nullptr, const void *data = nullptr);
};

/*!
* \brief Wrapper for analogReadAsync method
*/
class AnalogReadAsyncWrapper : public AnalogReadAsyncWrapperInterface {
public:
void analogReadAsyncWrapped(uint8_t pin, analogReadCompleteCallback_t cb = nullptr, const void *data = nullptr) final;
};

#endif // ANALOGREADASYNCWRAPPER_H_
193 changes: 109 additions & 84 deletions src/ayab/encoders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <Arduino.h>

#include "encoders.h"
#include "analogReadAsyncWrapper.h"

/*!
* \brief Initialize machine type.
Expand Down Expand Up @@ -75,6 +76,111 @@ void Encoders::isr() {
m_oldState = currentState;
}

/*!
* \brief Callback from interrupt service subroutine.
*
* Hall value is used to detect whether carriage is in front of Left Hall sensor
*/
void Encoders::hallLeftCallback(uint16_t hallValue, void *data) {
(void) data; // unused

// Update direction
m_direction = digitalRead(ENC_PIN_B) ? Direction_t::Right : Direction_t::Left;

// Update carriage position
if ((Direction_t::Right == m_direction) && (m_position < END_RIGHT[static_cast<uint8_t>(m_machineType)])) {
m_position = m_position + 1;
}

if ((hallValue < FILTER_L_MIN[static_cast<uint8_t>(m_machineType)]) ||
(hallValue > FILTER_L_MAX[static_cast<uint8_t>(m_machineType)])) {
// In front of Left Hall Sensor
m_hallActive = Direction_t::Left;

Carriage detected_carriage = Carriage_t::NoCarriage;
uint8_t start_position = END_LEFT_PLUS_OFFSET[static_cast<uint8_t>(m_machineType)];

if (hallValue >= FILTER_L_MIN[static_cast<uint8_t>(m_machineType)]) {
detected_carriage = Carriage_t::Knit;
} else {
detected_carriage = Carriage_t::Lace;
}

if (m_machineType == Machine_t::Kh270) {
// KH270 uses Knit carriage only
m_carriage = Carriage_t::Knit;

// Belt shift is ignored for KH270

// The first magnet on the carriage looks like Lace, the second looks like Knit
if (detected_carriage == Carriage_t::Knit) {
m_position = start_position + MAGNET_DISTANCE_270;
} else {
m_position = start_position;
}
} else if ((m_carriage != Carriage_t::NoCarriage) && (m_carriage != detected_carriage) && (m_position > start_position)) {
// Garter carriage detected
m_carriage = Carriage_t::Garter;

// Belt shift and start position were set when the first magnet passed
// the sensor and we assumed we were working with a standard carriage.
return;
} else {
// Knit or Lace carriage detected, machine is not KH270
m_carriage = detected_carriage;

// Belt shift signal only decided in front of Hall sensor
m_beltShift = digitalRead(ENC_PIN_C) ? BeltShift::Regular : BeltShift::Shifted;

// Carriage is known to be in front of the Hall sensor so overwrite position
m_position = start_position;
}
}
}

/*!
* \brief Callback from interrupt service subroutine.
*
* Hall value is used to detect whether carriage is in front of Right Hall sensor
*/
void Encoders::hallRightCallback(uint16_t hallValue, void *data) {
(void) data; // unused

// Update direction
m_direction = digitalRead(ENC_PIN_B) ? Direction_t::Left : Direction_t::Right;

// Update carriage position
if ((Direction_t::Left == m_direction) && (m_position > END_LEFT[static_cast<uint8_t>(m_machineType)])) {
m_position = m_position - 1;
}

// Avoid 'comparison of unsigned expression < 0 is always false'
// by being explicit about that behaviour being expected.
bool hallValueSmall = false;
hallValueSmall = (hallValue < FILTER_R_MIN[static_cast<uint8_t>(m_machineType)]);

if (hallValueSmall || (hallValue > FILTER_R_MAX[static_cast<uint8_t>(m_machineType)])) {
// In front of Right Hall Sensor
m_hallActive = Direction_t::Right;

// The Garter carriage has a second set of magnets that are going to
// pass the sensor and will reset state incorrectly if allowed to
// continue.
if (hallValueSmall && (m_carriage != Carriage_t::Garter)) {
m_carriage = Carriage_t::Knit;
}

// Belt shift is ignored for KH270
if (m_machineType != Machine_t::Kh270) {
// Belt shift signal only decided in front of Hall sensor
m_beltShift = digitalRead(ENC_PIN_C) ? BeltShift::Shifted : BeltShift::Regular;
}

// Known position of the carriage -> overwrite position
m_position = END_RIGHT_MINUS_OFFSET[static_cast<uint8_t>(m_machineType)];
}
}

/*!
* \brief Read Hall sensor on left and right.
* \param pSensor Which sensor to read (left or right).
Expand Down Expand Up @@ -132,7 +238,7 @@ Machine_t Encoders::getMachineType() {
return m_machineType;
}

// Private Methods
// Private methods

/*!
* \brief Interrupt service subroutine.
Expand All @@ -142,14 +248,6 @@ Machine_t Encoders::getMachineType() {
* Bounds on `m_machineType` not checked.
*/
void Encoders::encA_rising() {
// Update direction
m_direction = digitalRead(ENC_PIN_B) ? Direction_t::Right : Direction_t::Left;

// Update carriage position
if ((Direction_t::Right == m_direction) && (m_position < END_RIGHT[static_cast<uint8_t>(m_machineType)])) {
m_position = m_position + 1;
}

// The garter carriage has a second set of magnets that are going to
// pass the sensor and will reset state incorrectly if allowed to
// continue.
Expand All @@ -163,46 +261,7 @@ void Encoders::encA_rising() {
}

// Hall value is used to detect whether carriage is in front of Left Hall sensor
uint16_t hallValue = analogRead(EOL_PIN_L);

if ((hallValue < FILTER_L_MIN[static_cast<uint8_t>(m_machineType)]) ||
(hallValue > FILTER_L_MAX[static_cast<uint8_t>(m_machineType)])) {
// In front of Left Hall Sensor
m_hallActive = Direction_t::Left;

Carriage detected_carriage = Carriage_t::NoCarriage;
uint8_t start_position = END_LEFT_PLUS_OFFSET[static_cast<uint8_t>(m_machineType)];

if (hallValue >= FILTER_L_MIN[static_cast<uint8_t>(m_machineType)]) {
detected_carriage = Carriage_t::Knit;
} else {
detected_carriage = Carriage_t::Lace;
}

if (m_machineType == Machine_t::Kh270) {
m_carriage = Carriage_t::Knit;

// The first magnet on the carriage looks like Lace, the second looks like Knit
if (detected_carriage == Carriage_t::Knit) {
start_position = start_position + MAGNET_DISTANCE_270;
}
} else if ((m_carriage != Carriage_t::NoCarriage) && (m_carriage != detected_carriage) && (m_position > start_position)) {
m_carriage = Carriage_t::Garter;

// Belt shift and start position were set when the first magnet passed
// the sensor and we assumed we were working with a standard carriage.
return;
} else {
m_carriage = detected_carriage;

// Belt shift signal only decided in front of Hall sensor
// Belt shift is ignored for KH270
m_beltShift = digitalRead(ENC_PIN_C) ? BeltShift::Regular : BeltShift::Shifted;
}

// Known position of the carriage -> overwrite position
m_position = start_position;
}
GlobalAnalogReadAsyncWrapper::analogReadAsyncWrapped(EOL_PIN_L, (analogReadCompleteCallback_t)GlobalEncoders::hallLeftCallback);
}

/*!
Expand All @@ -213,40 +272,6 @@ void Encoders::encA_rising() {
* Bounds on `m_machineType` not checked.
*/
void Encoders::encA_falling() {
// Update direction
m_direction = digitalRead(ENC_PIN_B) ? Direction_t::Left : Direction_t::Right;

// Update carriage position
if ((Direction_t::Left == m_direction) && (m_position > END_LEFT[static_cast<uint8_t>(m_machineType)])) {
m_position = m_position - 1;
}

// Hall value is used to detect whether carriage is in front of Right Hall sensor
uint16_t hallValue = analogRead(EOL_PIN_R);

// Avoid 'comparison of unsigned expression < 0 is always false'
// by being explicit about that behaviour being expected.
bool hallValueSmall = false;
hallValueSmall = (hallValue < FILTER_R_MIN[static_cast<uint8_t>(m_machineType)]);

if (hallValueSmall || (hallValue > FILTER_R_MAX[static_cast<uint8_t>(m_machineType)])) {
// In front of Right Hall Sensor
m_hallActive = Direction_t::Right;

// The Garter carriage has a second set of magnets that are going to
// pass the sensor and will reset state incorrectly if allowed to
// continue.
if (hallValueSmall && (m_carriage != Carriage_t::Garter)) {
m_carriage = Carriage_t::Knit;
}

// Belt shift is ignored for KH270
if (m_machineType != Machine_t::Kh270) {
// Belt shift signal only decided in front of Hall sensor
m_beltShift = digitalRead(ENC_PIN_C) ? BeltShift::Shifted : BeltShift::Regular;
}

// Known position of the carriage -> overwrite position
m_position = END_RIGHT_MINUS_OFFSET[static_cast<uint8_t>(m_machineType)];
}
GlobalAnalogReadAsyncWrapper::analogReadAsyncWrapped(EOL_PIN_R, (analogReadCompleteCallback_t)GlobalEncoders::hallRightCallback);
}
8 changes: 7 additions & 1 deletion src/ayab/encoders.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
#ifndef ENCODERS_H_
#define ENCODERS_H_

#include "board.h"
#include <Arduino.h>
#include "board.h"

// Enumerated constants

Expand Down Expand Up @@ -123,6 +123,8 @@ class EncodersInterface {
virtual void init(Machine_t machineType) = 0;
virtual void setUpInterrupt() = 0;
virtual void isr() = 0;
virtual void hallLeftCallback(uint16_t hallValue, void *data) = 0;
virtual void hallRightCallback(uint16_t hallValue, void *data) = 0;
virtual uint16_t getHallValue(Direction_t pSensor) = 0;
virtual Machine_t getMachineType() = 0;
virtual BeltShift_t getBeltShift() = 0;
Expand Down Expand Up @@ -151,6 +153,8 @@ class GlobalEncoders final {
//#ifndef AYAB_TESTS
static void isr();
//#endif
static void hallLeftCallback(uint16_t hallValue, void *data);
static void hallRightCallback(uint16_t hallValue, void *data);
static uint16_t getHallValue(Direction_t pSensor);
static Machine_t getMachineType();
static BeltShift_t getBeltShift();
Expand All @@ -167,6 +171,8 @@ class Encoders : public EncodersInterface {
void init(Machine_t machineType) final;
void setUpInterrupt() final;
void isr() final;
void hallLeftCallback(uint16_t hallValue, void *data) final;
void hallRightCallback(uint16_t hallValue, void *data) final;
uint16_t getHallValue(Direction_t pSensor) final;
Machine_t getMachineType() final;
BeltShift_t getBeltShift() final;
Expand Down
Loading

0 comments on commit 55e9d34

Please sign in to comment.