Skip to content
Merged
231 changes: 117 additions & 114 deletions Inc/HALAL/Services/Encoder/Encoder.hpp
Original file line number Diff line number Diff line change
@@ -1,114 +1,117 @@
#pragma once

#include "HALAL/Models/TimerDomain/TimerDomain.hpp"

#ifdef HAL_TIM_MODULE_ENABLED

#define NANO_SECOND 1000000000.0
#define CLOCK_MAX_VALUE 4294967295 // here goes the tim23 counter period

namespace ST_LIB {

template <const TimerDomain::Timer& dev> struct TimerWrapper;

template <const TimerDomain::Timer& dev> class Encoder {
static_assert(
dev.e.pin_count == 2,
"Encoder must have exactly 2 encoder pins, as it uses the whole timer"
);
static_assert(dev.e.pins[0].af == TimerAF::Encoder, "Pin 0 must be declared as encoder");
static_assert(dev.e.pins[1].af == TimerAF::Encoder, "Pin 1 must be declared as encoder");
static_assert(
dev.e.pins[0].channel != dev.e.pins[1].channel,
"Pins must be of different channels"
);

inline static TimerWrapper<dev>* timer;
inline static bool is_on = false;

public:
Encoder(TimerWrapper<dev>* tim) {
if (timer == nullptr) {
init(tim);
}
}

static void init(TimerWrapper<dev>* tim) {
timer = tim;
TIM_Encoder_InitTypeDef sConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

tim->instance->hal_tim->Init.Prescaler = 5;
tim->instance->hal_tim->Init.CounterMode = TIM_COUNTERMODE_UP;
tim->instance->hal_tim->Init.Period = 55000;
tim->instance->hal_tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
tim->instance->hal_tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 0;
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 0;

if (HAL_TIM_Encoder_Init(tim->instance->hal_tim, &sConfig) != HAL_OK) {
ErrorHandler("Unable to init encoder");
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(tim->instance->hal_tim, &sMasterConfig) !=
HAL_OK) {
ErrorHandler("Unable to config master synchronization in encoder");
}
}

static void turn_on() {
if (is_on)
return;

if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) {
ErrorHandler("Unable to get state from encoder");
return;
}
if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) {
ErrorHandler("Unable to start encoder");
return;
}
is_on = true;
reset();
}

static void turn_off() {
if (!is_on)
return;
if (HAL_TIM_Encoder_Stop(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) {
ErrorHandler("Unable to stop encoder");
}
is_on = false;
}

static inline void reset() { timer->instance->tim->CNT = UINT32_MAX / 2; }

static inline uint32_t get_counter() { return timer->instance->tim->CNT; }

static inline bool get_direction() { return ((timer->instance->tim->CR1 & 0b10000) >> 4); }

static inline uint32_t get_initial_counter_value() { return timer->instance->tim->ARR / 2; }

static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) {
int64_t delta_clock = clock_time - last_clock_time;
if (clock_time < last_clock_time) { // overflow handle
delta_clock = clock_time +
CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() -
last_clock_time;
}
return delta_clock;
}
};

} // namespace ST_LIB
#endif
#pragma once

#include "HALAL/Models/TimerDomain/TimerDomain.hpp"

#ifdef HAL_TIM_MODULE_ENABLED

#define NANO_SECOND 1000000000.0
#define CLOCK_MAX_VALUE 4294967295 // here goes the tim23 counter period

namespace ST_LIB {

template <const TimerDomain::Timer& dev> struct TimerWrapper;

template <const TimerDomain::Timer& dev> class Encoder {
friend struct TimerWrapper<dev>;

static_assert(
dev.e.pin_count == 2,
"Encoder must have exactly 2 encoder pins, as it uses the whole timer"
);
static_assert(dev.e.pins[0].af == TimerAF::Encoder, "Pin 0 must be declared as encoder");
static_assert(dev.e.pins[1].af == TimerAF::Encoder, "Pin 1 must be declared as encoder");
static_assert(
dev.e.pins[0].channel != dev.e.pins[1].channel,
"Pins must be of different channels"
);

inline static TimerWrapper<dev>* timer;
inline static bool is_on = false;

Encoder(TimerWrapper<dev>* tim) {
if (timer == nullptr) {
init(tim);
}
}

public:
static void init(TimerWrapper<dev>* tim) {
TIM_Encoder_InitTypeDef sConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 0;
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 0;

if (HAL_TIM_Encoder_Init(tim->instance->hal_tim, &sConfig) != HAL_OK) {
ErrorHandler("Unable to init encoder");
return;
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(tim->instance->hal_tim, &sMasterConfig) !=
HAL_OK) {
ErrorHandler("Unable to config master synchronization in encoder");
return;
}

tim->instance->tim->PSC = 5;
tim->instance->tim->ARR = 55000;
timer = tim;
}

static void turn_on() {
if (is_on)
return;

if (HAL_TIM_Encoder_GetState(timer->instance->hal_tim) == HAL_TIM_STATE_RESET) {
ErrorHandler("Unable to get state from encoder");
return;
}
if (HAL_TIM_Encoder_Start(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) {
ErrorHandler("Unable to start encoder");
return;
}
is_on = true;
reset();
}

static void turn_off() {
if (!is_on)
return;

if (HAL_TIM_Encoder_Stop(timer->instance->hal_tim, TIM_CHANNEL_ALL) != HAL_OK) {
ErrorHandler("Unable to stop encoder");
return;
}
is_on = false;
}

static inline void reset() { timer->instance->tim->CNT = get_initial_counter_value(); }

static inline uint32_t get_counter() { return timer->instance->tim->CNT; }

static inline bool get_direction() { return ((timer->instance->tim->CR1 & 0b10000) >> 4); }

static inline uint32_t get_initial_counter_value() { return timer->instance->tim->ARR / 2; }

static int64_t get_delta_clock(uint64_t clock_time, uint64_t last_clock_time) {
int64_t delta_clock = clock_time - last_clock_time;
if (clock_time < last_clock_time) { // overflow handle
delta_clock = clock_time +
CLOCK_MAX_VALUE * NANO_SECOND / timer->get_clock_frequency() -
last_clock_time;
}
return delta_clock;
}
};

} // namespace ST_LIB
#endif
4 changes: 3 additions & 1 deletion Inc/HALAL/Services/PWM/DualPWM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ template <
const ST_LIB::TimerPin pin,
const ST_LIB::TimerPin negated_pin>
class DualPWM {
friend TimerWrapper<dev>;

static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) {
switch (ch) {
case TimerChannel::CHANNEL_1:
Expand Down Expand Up @@ -70,7 +72,6 @@ class DualPWM {
bool is_on_positive = false;
bool is_on_negative = false;

public:
DualPWM(
TimerWrapper<dev>* tim,
uint32_t polarity,
Expand Down Expand Up @@ -98,6 +99,7 @@ class DualPWM {
timer->template set_output_compare_preload_enable<pin.channel>();
}

public:
inline void turn_on() {
turn_on_positive();
turn_on_negative();
Expand Down
4 changes: 3 additions & 1 deletion Inc/HALAL/Services/PWM/PWM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace ST_LIB {
template <const TimerDomain::Timer& dev> struct TimerWrapper;

template <const TimerDomain::Timer& dev, const ST_LIB::TimerPin pin> class PWM {
friend TimerWrapper<dev>;

static consteval uint8_t get_channel_state_idx(const ST_LIB::TimerChannel ch) {
switch (ch) {
case TimerChannel::CHANNEL_1:
Expand Down Expand Up @@ -65,7 +67,6 @@ template <const TimerDomain::Timer& dev, const ST_LIB::TimerPin pin> class PWM {
float* duty_cycle = nullptr;
bool is_on = false;

public:
PWM(TimerWrapper<dev>* tim,
uint32_t polarity,
uint32_t negated_polarity,
Expand All @@ -90,6 +91,7 @@ template <const TimerDomain::Timer& dev, const ST_LIB::TimerPin pin> class PWM {
timer->template set_output_compare_preload_enable<pin.channel>();
}

public:
void turn_on() {
if (this->is_on)
return;
Expand Down
17 changes: 8 additions & 9 deletions Inc/ST-LIB_LOW/Sensors/EncoderSensor/NewEncoderSensor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
*/
#pragma once

#include "HALAL/HALAL.hpp"
// #include "HALAL/Services/Encoder/NewEncoder.hpp"
#include "C++Utilities/CppUtils.hpp"

namespace ST_LIB {

template <typename EncoderType, size_t SAMPLES> struct EncoderSensor {
enum Direction : uint8_t { FORWARD = 0, BACKWARDS = 1 };

private:
constexpr static int64_t START_COUNTER{UINT32_MAX / 2};
constexpr static size_t WINDOW_SIZE{(SAMPLES / 2) * 2};
static_assert(WINDOW_SIZE >= 2, "EncoderSensor requires at least two samples");

const double counter_distance_m;
const double sample_time_s;
Expand All @@ -41,7 +41,7 @@ template <typename EncoderType, size_t SAMPLES> struct EncoderSensor {
)
: counter_distance_m(counter_distance_m), sample_time_s(sample_time_s), encoder(enc),
direction(direction), position(position), speed(speed), acceleration(acceleration) {
for (size_t i{0}; i < SAMPLES; i++)
for (size_t i{0}; i < WINDOW_SIZE; i++)
past_delta_counters.push(0);
}

Expand All @@ -50,15 +50,15 @@ template <typename EncoderType, size_t SAMPLES> struct EncoderSensor {

void reset() {
encoder.reset();
for (size_t i{0}; i < SAMPLES; ++i)
for (size_t i{0}; i < WINDOW_SIZE; ++i)
past_delta_counters.push_pop(0);
}

// must be called on equally spaced time periods
void read() {
uint32_t counter{encoder.get_counter()};

int64_t delta_counter{(int64_t)counter - START_COUNTER};
int64_t delta_counter{(int64_t)counter - (int64_t)encoder.get_initial_counter_value()};
const int64_t& previous_delta_counter{
past_delta_counters[past_delta_counters.size() / 2 - 1]
};
Expand All @@ -71,13 +71,12 @@ template <typename EncoderType, size_t SAMPLES> struct EncoderSensor {
// https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference
*speed = ((3.0 * delta_counter / 2.0) - (2.0 * previous_delta_counter) +
(previous_previous_delta_counter / 2.0)) *
counter_distance_m / (sample_time_s * past_delta_counters.size() / 2);
counter_distance_m / (sample_time_s * WINDOW_SIZE / 2);

*acceleration =
(delta_counter - (2.0 * previous_delta_counter) + previous_previous_delta_counter) *
counter_distance_m /
((sample_time_s * past_delta_counters.size() / 2) *
(sample_time_s * past_delta_counters.size() / 2));
((sample_time_s * WINDOW_SIZE / 2) * (sample_time_s * WINDOW_SIZE / 2));

*direction = encoder.get_direction() ? FORWARD : BACKWARDS;

Expand Down
1 change: 1 addition & 0 deletions Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ message(STATUS "Generating test executable for ST-LIB")
add_executable(${STLIB_TEST_EXECUTABLE}
${CMAKE_CURRENT_LIST_DIR}/../Src/HALAL/Models/SPI/SPI2.cpp
${CMAKE_CURRENT_LIST_DIR}/../Src/HALAL/Models/DMA/DMA2.cpp
${CMAKE_CURRENT_LIST_DIR}/Time/encoder_test.cpp
${CMAKE_CURRENT_LIST_DIR}/Time/scheduler_test.cpp
${CMAKE_CURRENT_LIST_DIR}/Time/timer_wrapper_test.cpp
${CMAKE_CURRENT_LIST_DIR}/adc_test.cpp
Expand Down
Loading