From b1961e8a2944b4b3a917970ea410beb20d8366df Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 24 Aug 2020 00:50:23 -0400 Subject: [PATCH 001/124] Add timers for sections --- Firmware/MotorControl/axis.cpp | 14 ++++++++++ Firmware/MotorControl/axis.hpp | 42 +++++++++++++++++++++++++++-- Firmware/MotorControl/low_level.cpp | 8 ++++++ Firmware/MotorControl/utils.cpp | 7 +++++ Firmware/MotorControl/utils.hpp | 1 + Firmware/odrive-interface.yaml | 19 +++++++++++++ 6 files changed, 89 insertions(+), 2 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 04a802c6..2f211a03 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -201,11 +201,23 @@ bool Axis::do_updates() { for (ThermistorCurrentLimiter* thermistor : thermistors_) { thermistor->update(); } + task_times_.thermistor_update = sample_TIM13(); + encoder_.update(); + task_times_.encoder_update = sample_TIM13(); + sensorless_estimator_.update(); + task_times_.sensorless_update = sample_TIM13(); + min_endstop_.update(); + task_times_.min_endstop_update = sample_TIM13(); + max_endstop_.update(); + task_times_.max_endstop_update = sample_TIM13(); + bool ret = check_for_errors(); + task_times_.axis_error_check = sample_TIM13(); + odCAN->send_heartbeat(this); return ret; } @@ -347,10 +359,12 @@ bool Axis::run_closed_loop_control_loop() { float torque_setpoint; if (!controller_.update(&torque_setpoint)) return error_ |= ERROR_CONTROLLER_FAILED, false; + task_times_.controller_update = sample_TIM13(); float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) return false; // set_error should update axis.error_ + task_times_.motor_update = sample_TIM13(); return true; }); diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 446958d6..71610259 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -21,6 +21,25 @@ public: bool finish_on_enc_idx = false; }; + struct TaskTimes_t { + uint16_t thermistor_update = 0; + uint16_t encoder_update = 0; + uint16_t sensorless_update = 0; + uint16_t min_endstop_update = 0; + uint16_t max_endstop_update = 0; + uint16_t axis_update = 0; + uint16_t axis_error_check = 0; + + uint16_t controller_update = 0; + uint16_t motor_update = 0; + uint16_t update_handler = 0; + + uint16_t brake_update = 0; + uint16_t adc_cb = 0; + uint16_t control_loop = 0; + uint32_t total = 0; + }; + static LockinConfig_t default_calibration(); static LockinConfig_t default_sensorless(); static LockinConfig_t default_lockin(); @@ -140,14 +159,18 @@ public: // go to zero. // // @tparam T Must be a callable type that takes no arguments and returns a bool + uint16_t start = 0; template void run_control_loop(const T& update_handler) { while (requested_state_ == AXIS_STATE_UNDEFINED) { + start = sample_TIM13(); // look for errors at axis level and also all subcomponents bool checks_ok = do_checks(); + // Update all estimators // Note: updates run even if checks fail - bool updates_ok = do_updates(); + bool updates_ok = do_updates(); + task_times_.axis_update = sample_TIM13(); // make sure the watchdog is being fed. bool watchdog_ok = watchdog_check(); @@ -162,10 +185,24 @@ public: // Run main loop function, defer quitting for after wait // TODO: change arming logic to arm after waiting bool main_continue = update_handler(); - + task_times_.update_handler = sample_TIM13(); // Check we meet deadlines after queueing ++loop_counter_; + task_times_.control_loop = sample_TIM13() - task_times_.update_handler; + task_times_.update_handler -= task_times_.axis_update; + task_times_.axis_update -= start; + task_times_.motor_update -= task_times_.controller_update; + task_times_.controller_update -= task_times_.axis_error_check; + task_times_.axis_error_check -= task_times_.max_endstop_update; + task_times_.max_endstop_update -= task_times_.min_endstop_update; + task_times_.min_endstop_update -= task_times_.sensorless_update; + task_times_.sensorless_update -= task_times_.encoder_update; + task_times_.encoder_update -= task_times_.thermistor_update; + task_times_.thermistor_update -= start; + task_times_.total = sample_TIM13() - start; + + // Wait until the current measurement interrupt fires if (!wait_for_current_meas()) { // maybe the interrupt handler is dead, let's be @@ -206,6 +243,7 @@ public: TrapezoidalTrajectory& trap_traj_; Endstop& min_endstop_; Endstop& max_endstop_; + TaskTimes_t task_times_; // List of current_limiters and thermistors to // provide easy iteration. diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index d447539d..1402fbc7 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -492,6 +492,7 @@ static void decode_hall_samples(Encoder& enc, uint16_t GPIO_samples[num_GPIO]) { // This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion. // TODO: Document how the phasing is done, link to timing diagram void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { + auto start = sample_TIM13(); #define calib_tau 0.2f //@TOTO make more easily configurable constexpr float calib_filter_k = CURRENT_MEAS_PERIOD / calib_tau; @@ -587,6 +588,9 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { axis.motor_.DC_calib_.phC += (current - axis.motor_.DC_calib_.phC) * calib_filter_k; } } + auto end = (sample_TIM13() - start); + axes[0]->task_times_.adc_cb = end; + axes[1]->task_times_.adc_cb = end; } void tim_update_cb(TIM_HandleTypeDef* htim) { @@ -620,6 +624,7 @@ void tim_update_cb(TIM_HandleTypeDef* htim) { // @brief Sums up the Ibus contribution of each motor and updates the // brake resistor PWM accordingly. void update_brake_current() { + auto start = sample_TIM13(); float Ibus_sum = 0.0f; for (size_t i = 0; i < AXIS_COUNT; ++i) { if (axes[i]->motor_.armed_state_ == Motor::ARMED_STATE_ARMED) { @@ -666,6 +671,9 @@ void update_brake_current() { int low_off = high_on - TIM_APB1_DEADTIME_CLOCKS; if (low_off < 0) low_off = 0; safety_critical_apply_brake_resistor_timings(low_off, high_on); + + auto end = sample_TIM13() - start; + axes[0]->task_times_.brake_update = end; } diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 579a47a7..f089ba4e 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -5,6 +5,8 @@ #include #include +#include + int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { int Sextant; @@ -203,3 +205,8 @@ void delay_us(uint32_t us) __ASM("nop"); } } + +uint16_t sample_TIM13(){ + constexpr uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); + return clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config +} \ No newline at end of file diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 49f9434d..8dcf09aa 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -121,6 +121,7 @@ int is_in_the_future(uint32_t time_ms); uint32_t micros(void); void delay_us(uint32_t us); +uint16_t sample_TIM13(); float our_arm_sin_f32(float x); float our_arm_cos_f32(float x); diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 2c9060eb..df4365d6 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -364,12 +364,31 @@ interfaces: trap_traj: TrapezoidalTrajectory min_endstop: Endstop max_endstop: Endstop + task_times: TaskTime functions: watchdog_feed: doc: Feed the watchdog to prevent watchdog timeouts. clear_errors: doc: Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. + ODrive.Axis.TaskTime: + c_is_class: False + attributes: + thermistor_update: uint16 + encoder_update: uint16 + sensorless_update: uint16 + min_endstop_update: uint16 + max_endstop_update: uint16 + axis_update: uint16 + axis_error_check: uint16 + controller_update: uint16 + motor_update: uint16 + control_loop: uint16 + update_handler: uint16 + brake_update: uint16 + adc_cb: uint16 + total: uint32 + ODrive.Axis.LockinConfig: c_is_class: False attributes: From 05bf4da3f5549df814ebd9b44e0de8195559a435 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 24 Aug 2020 18:07:03 +0200 Subject: [PATCH 002/124] Revert "remove test artifact" This reverts commit dc47edf450e5f3ac39e0e2bf63a49bbd2ae345be. --- tools/odrive/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 0add96dc..e1ba95c2 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -77,7 +77,7 @@ def dump_errors(odrv, clear=False): module_decode_map = [ (name, odrv, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), ('motor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), - ('fet_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('fet_thermistorr', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), ('motor_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), ('encoder', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), ('controller', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), From 41597141525d1109647134abd206d59ceffe1b43 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 24 Aug 2020 13:58:22 +0200 Subject: [PATCH 003/124] Revert "improve compatibility of dump_errors()" This reverts commit 21357115126258b0afa7b3a5a77ecabb919c7da8. --- tools/odrive/utils.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index e1ba95c2..04fa2d15 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -75,20 +75,18 @@ def dump_errors(odrv, clear=False): # Flatten axis and submodules # (name, remote_obj, errorcode) module_decode_map = [ - (name, odrv, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), - ('motor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), - ('fet_thermistorr', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('motor_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('encoder', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), - ('controller', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), + ('axis', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), + ('motor', axis.motor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), + ('fet_thermistor', axis.fet_thermistor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('motor_thermistor', axis.motor_thermistor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('encoder', axis.encoder, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), + ('controller', axis.controller, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), ] # Module error decode for name, remote_obj, errorcodes in module_decode_map: - prefix = ' '*2 + name.strip('0123456789') + ": " - if not hasattr(remote_obj, name): - print(prefix + _VT100Colors['yellow'] + "not found" + _VT100Colors['default']) - elif getattr(remote_obj, name).error != 0: + prefix = ' '*2 + name + ": " + if (remote_obj.error != 0): foundError = False print(prefix + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default']) errorcodes_dict = {val: name for name, val in errorcodes.items() if 'ERROR_' in name} From 2bb990937a533b2749edcdfdc019804e3d61981b Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 25 Aug 2020 21:40:24 -0400 Subject: [PATCH 004/124] Add TaskTimer class --- Firmware/MotorControl/axis.cpp | 23 +++++++---- Firmware/MotorControl/axis.hpp | 62 ++++++++++++++-------------- Firmware/MotorControl/controller.cpp | 2 - Firmware/MotorControl/low_level.cpp | 16 +++---- Firmware/MotorControl/odrive_main.h | 3 +- Firmware/MotorControl/taskTimer.hpp | 27 ++++++++++++ Firmware/MotorControl/utils.cpp | 4 -- Firmware/MotorControl/utils.hpp | 1 - Firmware/odrive-interface.yaml | 40 +++++++++++------- 9 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 Firmware/MotorControl/taskTimer.hpp diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index a69fc140..b44f0b8c 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -198,25 +198,29 @@ bool Axis::do_checks() { // @brief Update all esitmators bool Axis::do_updates() { // Sub-components should use set_error which will propegate to this error_ + task_times_.thermistor_update.beginTimer(); for (ThermistorCurrentLimiter* thermistor : thermistors_) { thermistor->update(); } - task_times_.thermistor_update = sample_TIM13(); + task_times_.thermistor_update.stopTimer(); + task_times_.encoder_update.beginTimer(); encoder_.update(); - task_times_.encoder_update = sample_TIM13(); + task_times_.encoder_update.stopTimer(); + task_times_.sensorless_update.beginTimer(); sensorless_estimator_.update(); - task_times_.sensorless_update = sample_TIM13(); + task_times_.sensorless_update.stopTimer(); + task_times_.min_endstop_update.beginTimer(); min_endstop_.update(); - task_times_.min_endstop_update = sample_TIM13(); + task_times_.min_endstop_update.stopTimer(); + task_times_.max_endstop_update.beginTimer(); max_endstop_.update(); - task_times_.max_endstop_update = sample_TIM13(); + task_times_.max_endstop_update.stopTimer(); bool ret = check_for_errors(); - task_times_.axis_error_check = sample_TIM13(); odCAN->send_heartbeat(this); return ret; @@ -356,15 +360,18 @@ bool Axis::run_closed_loop_control_loop() { set_step_dir_active(config_.enable_step_dir); run_control_loop([this](){ // Note that all estimators are updated in the loop prefix in run_control_loop + + task_times_.controller_update.beginTimer(); float torque_setpoint; if (!controller_.update(&torque_setpoint)) return error_ |= ERROR_CONTROLLER_FAILED, false; - task_times_.controller_update = sample_TIM13(); + task_times_.controller_update.stopTimer(); + task_times_.motor_update.beginTimer(); float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) return false; // set_error should update axis.error_ - task_times_.motor_update = sample_TIM13(); + task_times_.motor_update.stopTimer(); return true; }); diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 3cf5dd91..13c631d8 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -11,6 +11,7 @@ class Axis; #include "low_level.h" #include "utils.hpp" #include "communication/interface_uart.h" // TODO: remove once uart_poll() is gone +#include "taskTimer.hpp" #include @@ -29,22 +30,24 @@ public: }; struct TaskTimes_t { - uint16_t thermistor_update = 0; - uint16_t encoder_update = 0; - uint16_t sensorless_update = 0; - uint16_t min_endstop_update = 0; - uint16_t max_endstop_update = 0; - uint16_t axis_update = 0; - uint16_t axis_error_check = 0; + TaskTimer thermistor_update; + TaskTimer encoder_update; + TaskTimer sensorless_update; + TaskTimer min_endstop_update; + TaskTimer max_endstop_update; + TaskTimer axis_update; + TaskTimer axis_error_check; - uint16_t controller_update = 0; - uint16_t motor_update = 0; - uint16_t update_handler = 0; + TaskTimer controller_update; + TaskTimer motor_update; + TaskTimer update_handler; - uint16_t brake_update = 0; - uint16_t adc_cb = 0; - uint16_t control_loop = 0; - uint32_t total = 0; + TaskTimer brake_update; + TaskTimer adc_cb; + TaskTimer control_loop; + TaskTimer total; + + TaskTimer uart_poll; }; static LockinConfig_t default_calibration(); @@ -166,18 +169,21 @@ public: // go to zero. // // @tparam T Must be a callable type that takes no arguments and returns a bool - uint16_t start = 0; template void run_control_loop(const T& update_handler) { while (requested_state_ == AXIS_STATE_UNDEFINED) { - start = sample_TIM13(); + task_times_.control_loop.beginTimer(); + // look for errors at axis level and also all subcomponents + task_times_.axis_error_check.beginTimer(); bool checks_ok = do_checks(); + task_times_.axis_error_check.stopTimer(); // Update all estimators // Note: updates run even if checks fail + task_times_.axis_update.beginTimer(); bool updates_ok = do_updates(); - task_times_.axis_update = sample_TIM13(); + task_times_.axis_update.stopTimer(); // make sure the watchdog is being fed. bool watchdog_ok = watchdog_check(); @@ -191,29 +197,21 @@ public: // Run main loop function, defer quitting for after wait // TODO: change arming logic to arm after waiting + task_times_.update_handler.beginTimer(); bool main_continue = update_handler(); - task_times_.update_handler = sample_TIM13(); + task_times_.update_handler.stopTimer(); if (axis_num_ == 0) { + task_times_.uart_poll.beginTimer(); uart_poll(); // TODO: move to board-level control loop once it exists + task_times_.uart_poll.stopTimer(); } // Check we meet deadlines after queueing ++loop_counter_; - task_times_.control_loop = sample_TIM13() - task_times_.update_handler; - task_times_.update_handler -= task_times_.axis_update; - task_times_.axis_update -= start; - task_times_.motor_update -= task_times_.controller_update; - task_times_.controller_update -= task_times_.axis_error_check; - task_times_.axis_error_check -= task_times_.max_endstop_update; - task_times_.max_endstop_update -= task_times_.min_endstop_update; - task_times_.min_endstop_update -= task_times_.sensorless_update; - task_times_.sensorless_update -= task_times_.encoder_update; - task_times_.encoder_update -= task_times_.thermistor_update; - task_times_.thermistor_update -= start; - task_times_.total = sample_TIM13() - start; - + task_times_.control_loop.stopTimer(); + task_times_.total.stopTimer(); // Wait until the current measurement interrupt fires if (!wait_for_current_meas()) { @@ -225,6 +223,8 @@ public: break; } + task_times_.total.beginTimer(); + if (!main_continue) break; } diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 1524dce6..73e5fc00 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -2,8 +2,6 @@ #include "odrive_main.h" #include -#include - bool Controller::apply_config() { config_.parent = this; update_filter_gains(); diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 0f9da174..c4495e5e 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -382,7 +382,8 @@ void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { // This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion. // TODO: Document how the phasing is done, link to timing diagram void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { - auto start = sample_TIM13(); + axes[0].task_times_.adc_cb.beginTimer(); + axes[1].task_times_.adc_cb.beginTimer(); #define calib_tau 0.2f //@TOTO make more easily configurable constexpr float calib_filter_k = CURRENT_MEAS_PERIOD / calib_tau; @@ -478,15 +479,15 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { axis.motor_.DC_calib_.phC += (current - axis.motor_.DC_calib_.phC) * calib_filter_k; } } - auto end = (sample_TIM13() - start); - axes[0]->task_times_.adc_cb = end; - axes[1]->task_times_.adc_cb = end; + axes[0].task_times_.adc_cb.stopTimer(); + axes[1].task_times_.adc_cb.stopTimer(); } // @brief Sums up the Ibus contribution of each motor and updates the // brake resistor PWM accordingly. void update_brake_current() { - auto start = sample_TIM13(); + axes[0].task_times_.brake_update.beginTimer(); + axes[1].task_times_.brake_update.beginTimer(); float Ibus_sum = 0.0f; for (size_t i = 0; i < AXIS_COUNT; ++i) { if (axes[i].motor_.armed_state_ == Motor::ARMED_STATE_ARMED) { @@ -533,9 +534,8 @@ void update_brake_current() { int low_off = high_on - TIM_APB1_DEADTIME_CLOCKS; if (low_off < 0) low_off = 0; safety_critical_apply_brake_resistor_timings(low_off, high_on); - - auto end = sample_TIM13() - start; - axes[0]->task_times_.brake_update = end; + axes[0].task_times_.brake_update.stopTimer(); + axes[1].task_times_.brake_update.stopTimer(); } diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 2a5c9f5c..41fecc58 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -128,9 +128,8 @@ inline ENUMTYPE &operator &= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast(reinterpret_cast&>(a) ^= static_cast>(b)); } \ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast(~static_cast>(a)); } - - #include "autogen/interfaces.hpp" +#include // ODrive specific includes #include diff --git a/Firmware/MotorControl/taskTimer.hpp b/Firmware/MotorControl/taskTimer.hpp new file mode 100644 index 00000000..b9363aa8 --- /dev/null +++ b/Firmware/MotorControl/taskTimer.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +inline uint16_t sample_TIM13(){ + constexpr uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); + return clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config +} + +struct TaskTimer { + uint32_t startTime = 0; + uint32_t endTime = 0; + uint32_t length = 0; + uint32_t maxLength = 0; + + void beginTimer(){ + startTime = sample_TIM13(); + } + + void stopTimer(){ + endTime = sample_TIM13(); + length = endTime - startTime; + maxLength = std::max(maxLength, length); + } +}; \ No newline at end of file diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 3dada0c8..a267aeea 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -206,7 +206,3 @@ void delay_us(uint32_t us) } } -uint16_t sample_TIM13(){ - constexpr uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); - return clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config -} \ No newline at end of file diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 8dcf09aa..49f9434d 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -121,7 +121,6 @@ int is_in_the_future(uint32_t time_ms); uint32_t micros(void); void delay_us(uint32_t us); -uint16_t sample_TIM13(); float our_arm_sin_f32(float x); float our_arm_cos_f32(float x); diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 3f991961..c1ff609b 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -454,30 +454,38 @@ interfaces: trap_traj: TrapezoidalTrajectory min_endstop: Endstop max_endstop: Endstop - task_times: TaskTime + task_times: TaskTimes functions: watchdog_feed: doc: Feed the watchdog to prevent watchdog timeouts. clear_errors: doc: Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. - ODrive.Axis.TaskTime: + ODrive.Axis.TaskTimes: c_is_class: False attributes: - thermistor_update: uint16 - encoder_update: uint16 - sensorless_update: uint16 - min_endstop_update: uint16 - max_endstop_update: uint16 - axis_update: uint16 - axis_error_check: uint16 - controller_update: uint16 - motor_update: uint16 - control_loop: uint16 - update_handler: uint16 - brake_update: uint16 - adc_cb: uint16 - total: uint32 + thermistor_update: TaskTimer + encoder_update: TaskTimer + sensorless_update: TaskTimer + min_endstop_update: TaskTimer + max_endstop_update: TaskTimer + axis_update: TaskTimer + axis_error_check: TaskTimer + controller_update: TaskTimer + motor_update: TaskTimer + control_loop: TaskTimer + update_handler: TaskTimer + brake_update: TaskTimer + adc_cb: TaskTimer + total: TaskTimer + + ODrive.TaskTimer: + c_is_class: False + attributes: + startTime: readonly uint32 + endTime: readonly uint32 + length: readonly uint32 + maxLength: readonly uint32 ODrive.Axis.LockinConfig: c_is_class: False From 499dd64d87426e15814ba9ec7d837f21d78548b4 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 26 Aug 2020 01:42:03 -0400 Subject: [PATCH 005/124] Ensure sample trigger occurs at the right spot, and only once. --- Firmware/MotorControl/axis.hpp | 3 ++- Firmware/MotorControl/low_level.cpp | 26 ++++++++++++++++++++------ Firmware/MotorControl/low_level.h | 1 + Firmware/MotorControl/odrive_main.h | 1 + Firmware/MotorControl/taskTimer.cpp | 4 ++++ Firmware/MotorControl/taskTimer.hpp | 29 +++++++++++++++++++---------- Firmware/odrive-interface.yaml | 1 + Firmware/sampler.py | 11 ++++++++++- 8 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 Firmware/MotorControl/taskTimer.cpp diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 13c631d8..32f64dbd 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -212,6 +212,8 @@ public: task_times_.control_loop.stopTimer(); task_times_.total.stopTimer(); + if(axis_num_ == 1) + TaskTimer::sample_next = false; // Wait until the current measurement interrupt fires if (!wait_for_current_meas()) { @@ -222,7 +224,6 @@ public: error_ |= ERROR_CURRENT_MEASUREMENT_TIMEOUT; break; } - task_times_.total.beginTimer(); if (!main_continue) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index c4495e5e..e248232d 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -31,6 +31,7 @@ constexpr float adc_ref_voltage = 3.3f; // Arbitrary non-zero inital value to avoid division by zero if ADC reading is late float vbus_voltage = 12.0f; float ibus_ = 0.0f; // exposed for monitoring only +bool task_timers_armed = false; bool brake_resistor_armed = false; bool brake_resistor_saturated = false; /* Private constant data -----------------------------------------------------*/ @@ -382,8 +383,8 @@ void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { // This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion. // TODO: Document how the phasing is done, link to timing diagram void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { - axes[0].task_times_.adc_cb.beginTimer(); - axes[1].task_times_.adc_cb.beginTimer(); + + adc_timestamp = sample_TIM13(); #define calib_tau 0.2f //@TOTO make more easily configurable constexpr float calib_filter_k = CURRENT_MEAS_PERIOD / calib_tau; @@ -404,10 +405,12 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { bool current_meas_not_DC_CAL = !counting_down; // Check the timing of the sequencing - if (current_meas_not_DC_CAL) + if (current_meas_not_DC_CAL) { axis.motor_.log_timing(TIMING_LOG_ADC_CB_I); - else + } + else { axis.motor_.log_timing(TIMING_LOG_ADC_CB_DC); + } bool update_timings = false; if (hadc == &hadc2) { @@ -452,6 +455,19 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { } float current = axis.motor_.phase_current_from_adcval(ADCValue); + + if(current_meas_not_DC_CAL && axis_num == 0 && hadc == &hadc2){ + if (task_timers_armed) { + TaskTimer::sample_next = true; + task_timers_armed = false; + axes[0].task_times_.adc_cb.startTime = adc_timestamp; // Start of ADC2 + } + } + + if(current_meas_not_DC_CAL && axis_num == 0 && hadc == &hadc3){ + axes[0].task_times_.adc_cb.stopTimer(); // End of ADC3 + } + if (current_meas_not_DC_CAL) { // ADC2 and ADC3 record the phB and phC currents concurrently, // and their interrupts should arrive on the same clock cycle. @@ -479,8 +495,6 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { axis.motor_.DC_calib_.phC += (current - axis.motor_.DC_calib_.phC) * calib_filter_k; } } - axes[0].task_times_.adc_cb.stopTimer(); - axes[1].task_times_.adc_cb.stopTimer(); } // @brief Sums up the Ibus contribution of each motor and updates the diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index e02ef5c2..a1e5a470 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -19,6 +19,7 @@ extern const float adc_ref_voltage; /* Exported variables --------------------------------------------------------*/ extern float vbus_voltage; extern float ibus_; +extern bool task_timers_armed; extern bool brake_resistor_armed; extern bool brake_resistor_saturated; extern uint16_t adc_measurements_[ADC_CHANNEL_COUNT]; diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 41fecc58..35b10dee 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -217,6 +217,7 @@ public: const uint8_t fw_version_revision_ = ::fw_version_revision_; const uint8_t fw_version_unreleased_ = ::fw_version_unreleased_; // 0 for official releases, 1 otherwise + bool& task_timers_armed_ = ::task_timers_armed; bool& brake_resistor_armed_ = ::brake_resistor_armed; // TODO: make this the actual variable bool& brake_resistor_saturated_ = ::brake_resistor_saturated; // TODO: make this the actual variable diff --git a/Firmware/MotorControl/taskTimer.cpp b/Firmware/MotorControl/taskTimer.cpp new file mode 100644 index 00000000..bc55573b --- /dev/null +++ b/Firmware/MotorControl/taskTimer.cpp @@ -0,0 +1,4 @@ +#include "taskTimer.hpp" + +bool TaskTimer::sample_next = false; +volatile uint32_t adc_timestamp = 0; diff --git a/Firmware/MotorControl/taskTimer.hpp b/Firmware/MotorControl/taskTimer.hpp index b9363aa8..8a2e17e4 100644 --- a/Firmware/MotorControl/taskTimer.hpp +++ b/Firmware/MotorControl/taskTimer.hpp @@ -1,12 +1,14 @@ #pragma once -#include #include +#include + #include -inline uint16_t sample_TIM13(){ + +inline uint16_t sample_TIM13() { constexpr uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); - return clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config + return clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config } struct TaskTimer { @@ -15,13 +17,20 @@ struct TaskTimer { uint32_t length = 0; uint32_t maxLength = 0; - void beginTimer(){ - startTime = sample_TIM13(); + static bool sample_next; + + void beginTimer() { + if (sample_next) + startTime = sample_TIM13(); } - void stopTimer(){ - endTime = sample_TIM13(); - length = endTime - startTime; - maxLength = std::max(maxLength, length); + void stopTimer() { + if (sample_next) { + endTime = sample_TIM13(); + length = endTime - startTime; + maxLength = std::max(maxLength, length); + } } -}; \ No newline at end of file +}; + +extern volatile uint32_t adc_timestamp; \ No newline at end of file diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index c1ff609b..276a96ee 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -13,6 +13,7 @@ interfaces: The odrv0, odrv1, ... objects that appear in odrivetool implement this toplevel interface. attributes: + task_timers_armed: bool vbus_voltage: type: readonly float32 unit: V diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 70a59115..c3f7c353 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -88,6 +88,7 @@ if __name__ == '__main__': total = 0 countmap = { } pcmap = { } + funcmap = { } start = time.time() try: @@ -101,6 +102,9 @@ if __name__ == '__main__': func, addr = sampler.func(pc) + if(func == 'ADC_IRQ_Dispatch'): + funcmap[pc] = 1 + if not addr: continue @@ -112,7 +116,12 @@ if __name__ == '__main__': total += 1 cur = time.time() - if cur - start > 1.0: + if cur - start > 5.0: + + # tmp = sorted(funcmap) + # for k in tmp: + # print(hex(k)) + tmp = sorted(countmap.items(), key=operator.itemgetter(1)) #, reverse=True) for k, v in tmp: print('{:05.2f}% {}'.format((v * 100.) / total, k)) From c834533be69dc83ab063b21c05222400b497af17 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 26 Aug 2020 02:23:57 -0400 Subject: [PATCH 006/124] Add missing taskTimer compile command --- Firmware/Tupfile.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 040db512..9fb16b98 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -194,6 +194,7 @@ sources = { 'MotorControl/trapTraj.cpp', 'MotorControl/pwm_input.cpp', 'MotorControl/main.cpp', + 'MotorControl/taskTimer.cpp', 'Drivers/STM32/stm32_system.cpp', 'Drivers/STM32/stm32_gpio.cpp', 'Drivers/STM32/stm32_nvm.c', From ea03574ce3df68c74ccf617e32fc4dfd63583682 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 27 Aug 2020 18:09:44 -0400 Subject: [PATCH 007/124] Add FOC_Current timer --- Firmware/MotorControl/axis.hpp | 1 + Firmware/MotorControl/motor.cpp | 2 ++ Firmware/odrive-interface.yaml | 1 + 3 files changed, 4 insertions(+) diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 32f64dbd..12904f09 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -48,6 +48,7 @@ public: TaskTimer total; TaskTimer uart_poll; + TaskTimer FOC_Current; }; static LockinConfig_t default_calibration(); diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 9bdbaa75..5eff545c 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -292,6 +292,7 @@ bool Motor::FOC_voltage(float v_d, float v_q, float pwm_phase) { } bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase, float phase_vel) { + axis_->task_times_.FOC_Current.beginTimer(); // Syntactic sugar CurrentControl_t& ictrl = current_control_; @@ -404,6 +405,7 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_pha } } + axis_->task_times_.FOC_Current.stopTimer(); return true; } diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 276a96ee..105b0d4c 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -479,6 +479,7 @@ interfaces: brake_update: TaskTimer adc_cb: TaskTimer total: TaskTimer + FOC_Current: TaskTimer ODrive.TaskTimer: c_is_class: False From 5661a3b5ed93591d3bfcd81dc355e8cd4079e2d7 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 1 Sep 2020 00:02:54 +0200 Subject: [PATCH 008/124] Refactor control loop. Please see https://github.com/madcowswe/ODrive/issues/472 for a detailed description. --- CHANGELOG.md | 34 + Firmware/Board/v3/Inc/board.h | 15 +- Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h | 2 +- Firmware/Board/v3/Inc/stm32f4xx_it.h | 2 +- Firmware/Board/v3/Src/adc.c | 10 +- Firmware/Board/v3/Src/can.c | 8 +- Firmware/Board/v3/Src/dma.c | 10 +- Firmware/Board/v3/Src/i2c.c | 4 +- Firmware/Board/v3/Src/spi.c | 6 +- Firmware/Board/v3/Src/stm32f4xx_hal_msp.c | 2 +- Firmware/Board/v3/Src/stm32f4xx_it.c | 4 + Firmware/Board/v3/Src/tim.c | 19 +- Firmware/Board/v3/Src/usart.c | 2 +- Firmware/Board/v3/Src/usbd_conf.c | 2 +- Firmware/Board/v3/board.cpp | 264 ++++-- Firmware/Drivers/DRV8301/drv8301.cpp | 314 +++---- Firmware/Drivers/DRV8301/drv8301.hpp | 445 ++-------- Firmware/Drivers/STM32/stm32_gpio.cpp | 18 +- Firmware/Drivers/STM32/stm32_gpio.hpp | 8 + Firmware/Drivers/STM32/stm32_spi_arbiter.cpp | 6 +- Firmware/Drivers/STM32/stm32_spi_arbiter.hpp | 1 - Firmware/Drivers/gate_driver.hpp | 13 +- Firmware/MotorControl/async_estimator.cpp | 48 ++ Firmware/MotorControl/async_estimator.hpp | 36 + Firmware/MotorControl/axis.cpp | 408 ++++----- Firmware/MotorControl/axis.hpp | 116 +-- Firmware/MotorControl/component.hpp | 20 + Firmware/MotorControl/controller.cpp | 65 +- Firmware/MotorControl/controller.hpp | 11 +- Firmware/MotorControl/encoder.cpp | 179 ++-- Firmware/MotorControl/encoder.hpp | 5 +- Firmware/MotorControl/foc.cpp | 160 ++++ Firmware/MotorControl/foc.hpp | 67 ++ Firmware/MotorControl/low_level.cpp | 265 +----- Firmware/MotorControl/low_level.h | 6 +- Firmware/MotorControl/main.cpp | 193 ++++- Firmware/MotorControl/motor.cpp | 788 +++++++++++------- Firmware/MotorControl/motor.hpp | 139 ++- Firmware/MotorControl/odrive_main.h | 41 +- .../MotorControl/open_loop_controller.cpp | 27 + .../MotorControl/open_loop_controller.hpp | 32 + Firmware/MotorControl/oscilloscope.cpp | 29 + Firmware/MotorControl/oscilloscope.hpp | 29 + Firmware/MotorControl/phase_control_law.hpp | 87 ++ .../MotorControl/sensorless_estimator.cpp | 28 +- .../MotorControl/sensorless_estimator.hpp | 4 +- Firmware/MotorControl/task_timer.hpp | 65 ++ Firmware/Tupfile.lua | 4 + Firmware/communication/can_simple.cpp | 10 +- Firmware/communication/communication.cpp | 3 - Firmware/communication/interface_can.cpp | 2 +- Firmware/freertos_vars.h | 2 - Firmware/odrive-interface.yaml | 339 +++++--- Firmware/syscalls.c | 9 +- docs/commands.md | 5 +- docs/encoders.md | 2 +- tools/odrive/enums.py | 53 +- tools/odrive/shell.py | 1 + tools/odrive/tests/analog_input_test.py | 2 +- tools/odrive/tests/calibration_test.py | 19 +- tools/odrive/tests/can_test.py | 3 +- tools/odrive/tests/closed_loop_test.py | 20 +- tools/odrive/tests/encoder_test.py | 9 +- tools/odrive/tests/integration_test.py | 13 +- tools/odrive/tests/test_runner.py | 27 +- tools/odrive/utils.py | 101 ++- tools/setup_hall_as_index.py | 1 - 67 files changed, 2587 insertions(+), 2075 deletions(-) create mode 100644 Firmware/MotorControl/async_estimator.cpp create mode 100644 Firmware/MotorControl/async_estimator.hpp create mode 100644 Firmware/MotorControl/component.hpp create mode 100644 Firmware/MotorControl/foc.cpp create mode 100644 Firmware/MotorControl/foc.hpp create mode 100644 Firmware/MotorControl/open_loop_controller.cpp create mode 100644 Firmware/MotorControl/open_loop_controller.hpp create mode 100644 Firmware/MotorControl/oscilloscope.cpp create mode 100644 Firmware/MotorControl/oscilloscope.hpp create mode 100644 Firmware/MotorControl/phase_control_law.hpp create mode 100644 Firmware/MotorControl/task_timer.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f7831d4..736558ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ Please add a note of your changes below this heading if you make a Pull Request. * Make NVM configuration code more dynamic so that the layout doesn't have to be known at compile time. * GPIO initialization logic was changed. GPIOs now need to be explicitly set to the mode corresponding to the feature that they are used by. See `.config.gpioX_mode`. * Previously, if two components used the same interrupt pin (e.g. step input for axis0 and axis1) then the one that was configured later would override the other one. Now this is no longer the case (the old component remains the owner of the pin). +* New control loop architecture: + 1. TIM8 update interrupt handler (CNT = 0) runs at a high priority and invokes the system level function `sample_cb()` to sample all timing critical inputs (currently only encoder state). + 2. TIM8 update interrupt handler (CNT = 0) raises an NVIC flag to kick off a lower priority interrupt. + 3. The control loop interrupt handler checks if all ADC measurements are ready and informs both motor objects about the current measurements. + 4. The control loop interrupt handler invokes the system level function `control_loop_cb()` which updates all components (encoders, estimators, torque controllers, etc). The data paths between the components are configured by the Axis threads based on the requested state. This replaces the previous architecture where the components were updated inside the Axis threads in `Axis::run_control_loop()`. + 5. Meanwhile the TIM1 and TIM8 updates for CNT = 3500 will have fired. The control loop interrupt handler thus reads the new ADC measurements and informs both motor objects that a DC calibration event has happened. + 6. Finally, the control loop interrupt invokes `pwm_update_cb` on both motors to make them update their PWM timing registers. +* Components that need low level control over PWM timings are implemented by inheriting from the `PhaseControlLaw` interface. Three components currently inherit this interface: `FieldOrientedController`, `ResistanceMeasurementControlLaw` and `InductanceMeasurementControlLaw`. +* The FOC algorithm is now found in foc.cpp and and is presumably capable of running at a different frequency than the main control tasks (not relevant for ODrive v3). +* Async estimator was consolidated into a separate component `.async_estimator`. +* The Automatic Output Enable (AOE) flag of TIM1/TIM8 is used to achieve glitch-free motor arming. +* Sensorless mode was merged into closed loop control mode. Use `.enable_sensorless_mode` to disable the use of an encoder. +* More informative profiling instrumentation was added. +* A system-level error property was introduced. ### API Miration Notes @@ -14,6 +28,26 @@ Please add a note of your changes below this heading if you make a Pull Request. * `enable_i2c_instead_of_can` was replaced by the separate settings `enable_i2c0` and `enable_can0`. * `.motor.gate_driver` was moved to `.gate_driver`. * `.min_endstop.pullup` and `.max_endstop.pullup` were removed. Use `.config.gpioX_mode = GPIO_MODE_DIGITAL / GPIO_MODE_DIGITAL_PULL_UP / GPIO_MODE_DIGITAL_PULL_DOWN` instead. +* `.get_oscilloscope_val()` was moved to `.oscilloscope.get_val()`. +* Several error flags from `..error` were removed. Some were moved to `.error` and some are no longer relevant because implementation details changed. +* Several error flags from `..motor.error` were removed. Some were moved to `.error` and some are no longer relevant because implementation details changed. +* `.lockin_state` was removed as the lockin implementation was replaced by a more general open loop control block (currently not exposed on the API). +* `AXIS_STATE_SENSORLESS_CONTROL` was removed. Use `AXIS_STATE_CLOSED_LOOP_CONTROL` instead with `.enable_sensorless_mode = True`. +* `.config.startup_sensorless_control` was removed. Use `.config.startup_closed_loop_control` instead with `.enable_sensorless_mode = True`. +* `.clear_errors()` was replaced by the system-wide function `.clear_errors()`. +* `.armed_state` was replaced by `.is_armed`. +* Several properties in `.motor.current_control` were changed to read-only. +* `.motor.current_control.Ibus` was moved to `.motor.I_bus`. +* `.motor.current_control.max_allowed_current` was moved to `.motor.max_allowed_current`. +* `.motor.current_control.overcurrent_trip_level` was removed. +* `.motor.current_control.acim_rotor_flux` was moved to `.async_estimator.rotor_flux`. +* `.motor.current_control.async_phase_vel` was moved to `.async_estimator.stator_phase_vel`. +* `.motor.current_control.async_phase_offset` was moved to `.async_estimator.phase`. +* `.motor.timing_log` was removed in favor of `.task_times` and `..task_times`. +* `.motor.config.direction` was moved to `.encoder.config.direction`. +* `.motor.config.acim_slip_velocity` was moved to `.async_estimator.config.slip_velocity`. +* `.encoder.config.idx_search_unidirectional` was removed. Offset calibration direction is fully defined by the sign of `.encoder.config.calib_scan_omega` and how the motor is wired up. +* The unit of `.sensorless_estimator.vel_estimate` was changed from `rad/s` to `turns/s`. # Release Candidate ## [0.5.1] - Date TBD diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h index 931286d5..f8c01d87 100644 --- a/Firmware/Board/v3/Inc/board.h +++ b/Firmware/Board/v3/Inc/board.h @@ -62,6 +62,15 @@ #define TIM_TIME_BASE TIM14 +// Run control loop at the same frequency as the current measurements. +#define CONTROL_TIMER_PERIOD_TICKS (2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) + +#define TIM1_INIT_COUNT (TIM_1_8_PERIOD_CLOCKS / 2 - 1 * 128) // TODO: explain why this offset + +// The delta from the control loop timestamp to the current sense timestamp is +// exactly 0 for M0 and TIM1_INIT_COUNT for M1. +#define MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA (TIM_1_8_PERIOD_CLOCKS / 2 + 1 * 128) + #ifdef __cplusplus #include #include @@ -84,7 +93,6 @@ extern Stm32Gpio gpios[GPIO_COUNT]; struct GpioFunction { int mode = 0; uint8_t alternate_function = 0xff; }; extern std::array alternate_functions[GPIO_COUNT]; -extern PCD_HandleTypeDef& usb_pcd_handle; extern USBD_HandleTypeDef& usb_dev_handle; extern Stm32SpiArbiter& ext_spi_arbiter; @@ -112,6 +120,10 @@ static const int current_meas_hz = CURRENT_MEAS_HZ; #error "unknown board voltage" #endif +// Linear range of the DRV8301 opamp output: 0.3V...5.7V. We set the upper limit +// to 3.0V so that it's symmetric around the center point of 1.65V. +#define CURRENT_SENSE_MIN_VOLT 0.3f +#define CURRENT_SENSE_MAX_VOLT 3.0f // This board has no board-specific user configurations static inline bool board_read_config() { return true; } @@ -121,5 +133,6 @@ static inline bool board_apply_config() { return true; } void system_init(); bool board_init(); +void start_timers(); #endif // __BOARD_CONFIG_H diff --git a/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h b/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h index ed26bf39..75e7b9da 100644 --- a/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h +++ b/Firmware/Board/v3/Inc/stm32f4xx_hal_conf.h @@ -158,7 +158,7 @@ * @brief This is the HAL system configuration section */ #define VDD_VALUE ((uint32_t)3300U) /*!< Value of VDD in mv */ -#define TICK_INT_PRIORITY ((uint32_t)0U) /*!< tick interrupt priority */ +#define TICK_INT_PRIORITY ((uint32_t)6U) /*!< tick interrupt priority */ #define USE_RTOS 0U #define PREFETCH_ENABLE 1U #define INSTRUCTION_CACHE_ENABLE 1U diff --git a/Firmware/Board/v3/Inc/stm32f4xx_it.h b/Firmware/Board/v3/Inc/stm32f4xx_it.h index 784a3333..3910f40c 100644 --- a/Firmware/Board/v3/Inc/stm32f4xx_it.h +++ b/Firmware/Board/v3/Inc/stm32f4xx_it.h @@ -58,7 +58,7 @@ void DMA1_Stream0_IRQHandler(void); void DMA1_Stream2_IRQHandler(void); void DMA1_Stream4_IRQHandler(void); void DMA1_Stream5_IRQHandler(void); -void ADC_IRQHandler(void); +//void ADC_IRQHandler(void); void CAN1_TX_IRQHandler(void); void CAN1_RX0_IRQHandler(void); void CAN1_RX1_IRQHandler(void); diff --git a/Firmware/Board/v3/Src/adc.c b/Firmware/Board/v3/Src/adc.c index 69c7fdb9..52e00b72 100644 --- a/Firmware/Board/v3/Src/adc.c +++ b/Firmware/Board/v3/Src/adc.c @@ -278,9 +278,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); - /* ADC1 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ @@ -314,9 +311,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /* ADC2 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -341,8 +335,8 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* ADC3 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); + //HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); // must be on the same level as control loop + //HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/can.c b/Firmware/Board/v3/Src/can.c index 011150ff..18086b27 100644 --- a/Firmware/Board/v3/Src/can.c +++ b/Firmware/Board/v3/Src/can.c @@ -105,13 +105,13 @@ void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* CAN1 interrupt Init */ - HAL_NVIC_SetPriority(CAN1_TX_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_TX_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_TX_IRQn); - HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); - HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn); - HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 6, 0); + HAL_NVIC_SetPriority(CAN1_SCE_IRQn, 9, 0); HAL_NVIC_EnableIRQ(CAN1_SCE_IRQn); /* USER CODE BEGIN CAN1_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/dma.c b/Firmware/Board/v3/Src/dma.c index 813c735a..30798c83 100644 --- a/Firmware/Board/v3/Src/dma.c +++ b/Firmware/Board/v3/Src/dma.c @@ -72,16 +72,18 @@ void MX_DMA_Init(void) /* DMA interrupt init */ /* DMA1_Stream0_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 4, 0); // SPI RX - must have lower priority than SPI TX + // and higher priority than the control loop handler HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); /* DMA1_Stream2_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn); /* DMA1_Stream4_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); /* DMA1_Stream5_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0); + HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 3, 0); // SPI TX - must have higher priority than SPI RX + // and higher priority than the control loop handler HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); /* DMA2_Stream0_IRQn interrupt configuration */ // Dear STM, no we _don't_ want to fire an interrupt for this DMA diff --git a/Firmware/Board/v3/Src/i2c.c b/Firmware/Board/v3/Src/i2c.c index ad0324a1..c4fca942 100644 --- a/Firmware/Board/v3/Src/i2c.c +++ b/Firmware/Board/v3/Src/i2c.c @@ -131,9 +131,9 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) __HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c1_tx); /* I2C1 interrupt Init */ - HAL_NVIC_SetPriority(I2C1_EV_IRQn, 5, 0); + HAL_NVIC_SetPriority(I2C1_EV_IRQn, 9, 0); HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); - HAL_NVIC_SetPriority(I2C1_ER_IRQn, 5, 0); + HAL_NVIC_SetPriority(I2C1_ER_IRQn, 9, 0); HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); /* USER CODE BEGIN I2C1_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/spi.c b/Firmware/Board/v3/Src/spi.c index a3a3311e..e49524f6 100644 --- a/Firmware/Board/v3/Src/spi.c +++ b/Firmware/Board/v3/Src/spi.c @@ -125,7 +125,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) } hdma_spi3_tx.Init.Mode = DMA_NORMAL; - hdma_spi3_tx.Init.Priority = DMA_PRIORITY_MEDIUM; + hdma_spi3_tx.Init.Priority = DMA_PRIORITY_HIGH; // SPI TX must have higher priority than SPI RX hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK) { @@ -158,8 +158,8 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi3_rx); /* SPI3 interrupt Init */ - HAL_NVIC_SetPriority(SPI3_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(SPI3_IRQn); + //HAL_NVIC_SetPriority(SPI3_IRQn, 3, 0); + //HAL_NVIC_EnableIRQ(SPI3_IRQn); /* USER CODE BEGIN SPI3_MspInit 1 */ /* USER CODE END SPI3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c b/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c index e49480c0..d8c864e0 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c +++ b/Firmware/Board/v3/Src/stm32f4xx_hal_msp.c @@ -74,7 +74,7 @@ void HAL_MspInit(void) /* UsageFault_IRQn interrupt configuration */ HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0); /* SVCall_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0); + HAL_NVIC_SetPriority(SVCall_IRQn, 3, 0); /* DebugMonitor_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0); /* PendSV_IRQn interrupt configuration */ diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index 1e69bcb9..874abcfc 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -85,6 +85,10 @@ void get_regs(void** stack_ptr) { void* volatile pc __attribute__((unused)) = stack_ptr[6]; // Program counter void* volatile psr __attribute__((unused)) = stack_ptr[7]; // Program status register + void* volatile cfsr __attribute__((unused)) = (void*)SCB->CFSR; // Configurable fault status register + void* volatile cpacr __attribute__((unused)) = (void*)SCB->CPACR; + void* volatile fpccr __attribute__((unused)) = (void*)FPU->FPCCR; + volatile int stay_looping = 1; while(stay_looping); } diff --git a/Firmware/Board/v3/Src/tim.c b/Firmware/Board/v3/Src/tim.c index a8268085..40c5c829 100644 --- a/Firmware/Board/v3/Src/tim.c +++ b/Firmware/Board/v3/Src/tim.c @@ -383,10 +383,6 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) /* USER CODE END TIM1_MspInit 0 */ /* TIM1 clock enable */ __HAL_RCC_TIM1_CLK_ENABLE(); - - /* TIM1 interrupt Init */ - HAL_NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); /* USER CODE BEGIN TIM1_MspInit 1 */ /* USER CODE END TIM1_MspInit 1 */ @@ -398,10 +394,6 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle) /* USER CODE END TIM13_MspInit 0 */ /* TIM13 clock enable */ __HAL_RCC_TIM13_CLK_ENABLE(); - - /* TIM13 interrupt Init */ - HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn); /* USER CODE BEGIN TIM13_MspInit 1 */ /* USER CODE END TIM13_MspInit 1 */ @@ -429,12 +421,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle) /* USER CODE END TIM8_MspInit 0 */ /* TIM8 clock enable */ __HAL_RCC_TIM8_CLK_ENABLE(); - - /* TIM8 interrupt Init */ - HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn); - HAL_NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn); /* USER CODE BEGIN TIM8_MspInit 1 */ /* USER CODE END TIM8_MspInit 1 */ @@ -482,7 +468,7 @@ void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle) __HAL_RCC_TIM5_CLK_ENABLE(); /* TIM5 interrupt Init */ - HAL_NVIC_SetPriority(TIM5_IRQn, 5, 0); + HAL_NVIC_SetPriority(TIM5_IRQn, 1, 0); HAL_NVIC_EnableIRQ(TIM5_IRQn); /* USER CODE BEGIN TIM5_MspInit 1 */ @@ -599,7 +585,6 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle) __HAL_RCC_TIM1_CLK_DISABLE(); /* TIM1 interrupt Deinit */ - HAL_NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); /* USER CODE BEGIN TIM1_MspDeInit 1 */ /* USER CODE END TIM1_MspDeInit 1 */ @@ -657,8 +642,6 @@ void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle) */ /* HAL_NVIC_DisableIRQ(TIM8_UP_TIM13_IRQn); */ /* USER CODE END TIM8:TIM8_UP_TIM13_IRQn disable */ - - HAL_NVIC_DisableIRQ(TIM8_TRG_COM_TIM14_IRQn); /* USER CODE BEGIN TIM8_MspDeInit 1 */ /* USER CODE END TIM8_MspDeInit 1 */ diff --git a/Firmware/Board/v3/Src/usart.c b/Firmware/Board/v3/Src/usart.c index 93bac536..ab44aa0d 100644 --- a/Firmware/Board/v3/Src/usart.c +++ b/Firmware/Board/v3/Src/usart.c @@ -129,7 +129,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) __HAL_LINKDMA(uartHandle,hdmatx,hdma_uart4_tx); /* UART4 interrupt Init */ - HAL_NVIC_SetPriority(UART4_IRQn, 5, 0); + HAL_NVIC_SetPriority(UART4_IRQn, 10, 0); HAL_NVIC_EnableIRQ(UART4_IRQn); /* USER CODE BEGIN UART4_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/usbd_conf.c b/Firmware/Board/v3/Src/usbd_conf.c index c219c4d8..ff1a4f17 100644 --- a/Firmware/Board/v3/Src/usbd_conf.c +++ b/Firmware/Board/v3/Src/usbd_conf.c @@ -114,7 +114,7 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); /* Peripheral interrupt init */ - HAL_NVIC_SetPriority(OTG_FS_IRQn, 5, 0); + HAL_NVIC_SetPriority(OTG_FS_IRQn, 6, 0); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); /* USER CODE BEGIN USB_OTG_FS_MspInit 1 */ diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index 5f883809..04d3e4bf 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -15,8 +15,14 @@ #include #include +// this should technically be in task_timer.cpp but let's not make a one-line file +bool TaskTimer::enabled = false; + extern "C" void SystemClock_Config(void); // defined in main.c generated by CubeMX +#define ControlLoop_IRQHandler OTG_HS_IRQHandler +#define ControlLoop_IRQn OTG_HS_IRQn + Stm32SpiArbiter spi3_arbiter{&hspi3}; Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; @@ -27,14 +33,14 @@ UART_HandleTypeDef* uart2 = nullptr; Drv8301 m0_gate_driver{ &spi3_arbiter, {M0_nCS_GPIO_Port, M0_nCS_Pin}, // nCS - {EN_GATE_GPIO_Port, EN_GATE_Pin}, // EN pin (shared between both motors) + {}, // EN pin (shared between both motors, therefore we actuate it outside of the drv8301 driver) {nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors) }; Drv8301 m1_gate_driver{ &spi3_arbiter, {M1_nCS_GPIO_Port, M1_nCS_Pin}, // nCS - {EN_GATE_GPIO_Port, EN_GATE_Pin}, // EN pin (shared between both motors) + {}, // EN pin (shared between both motors, therefore we actuate it outside of the drv8301 driver) {nFAULT_GPIO_Port, nFAULT_Pin} // nFAULT pin (shared between both motors) }; @@ -61,14 +67,14 @@ OnboardThermistorCurrentLimiter fet_thermistors[AXIS_COUNT] = { Motor motors[AXIS_COUNT] = { { &htim1, // timer - TIM_1_8_PERIOD_CLOCKS, // control_deadline + 0b110, // current_sensor_mask 1.0f / SHUNT_RESISTANCE, // shunt_conductance [S] m0_gate_driver, // gate_driver m0_gate_driver // opamp }, { &htim8, // timer - (3 * TIM_1_8_PERIOD_CLOCKS) / 2, // control_deadline + 0b110, // current_sensor_mask 1.0f / SHUNT_RESISTANCE, // shunt_conductance [S] m1_gate_driver, // gate_driver m1_gate_driver // opamp @@ -244,8 +250,6 @@ PwmInput pwm0_input{&htim5, {0, 0, 0, 4}}; // 0 means not in use PwmInput pwm0_input{&htim5, {1, 2, 3, 4}}; #endif -extern PCD_HandleTypeDef hpcd_USB_OTG_FS; // defined in usbd_conf.c -PCD_HandleTypeDef& usb_pcd_handle = hpcd_USB_OTG_FS; extern USBD_HandleTypeDef hUsbDeviceFS; USBD_HandleTypeDef& usb_dev_handle = hUsbDeviceFS; @@ -274,6 +278,28 @@ bool board_init() { MX_TIM5_Init(); MX_TIM13_Init(); + // External interrupt lines are individually enabled in stm32_gpio.cpp + HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI0_IRQn); + HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI1_IRQn); + HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI2_IRQn); + HAL_NVIC_SetPriority(EXTI3_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI3_IRQn); + HAL_NVIC_SetPriority(EXTI4_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI4_IRQn); + HAL_NVIC_SetPriority(EXTI9_5_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); + HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); + + HAL_NVIC_SetPriority(ControlLoop_IRQn, 5, 0); // must be on the same level as ADC interrupt + HAL_NVIC_EnableIRQ(ControlLoop_IRQn); + + HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn); + HAL_UART_DeInit(uart0); uart0->Init.BaudRate = odrv.config_.uart0_baudrate; HAL_UART_Init(uart0); @@ -308,29 +334,92 @@ bool board_init() { __HAL_DBGMCU_FREEZE_TIM8(); __HAL_DBGMCU_FREEZE_TIM13(); - /* - * Initial intention of the synchronization: - * Synchronize TIM1, TIM8 and TIM13 such that: - * 1. The triangle waveform of TIM1 leads the triangle waveform of TIM8 by a - * 90° phase shift. - * 2. The timer update events of TIM1 and TIM8 are symmetrically interleaved. - * 3. Each TIM13 reload coincides with a TIM1 lower update event. - * - * However right now this synchronization only ensures point (1) and (3) but because - * TIM1 and TIM3 only trigger an update on every third reload, this does not - * allow for (2). - * - * TODO: revisit the timing topic in general. - * - */ - Stm32Timer::start_synchronously<3>( - {&htim1, &htim8, &htim13}, - {TIM_1_8_PERIOD_CLOCKS / 2 - 1 * 128 /* TODO: explain why this offset */, 0, TIM_1_8_PERIOD_CLOCKS / 2 - 1 * 128} - ); + Stm32Gpio drv_enable_gpio = {EN_GATE_GPIO_Port, EN_GATE_Pin}; + + // Reset both DRV chips. The enable pin also controls the SPI interface, not + // only the driver stages. + drv_enable_gpio.write(false); + delay_us(40); // mimumum pull-down time for full reset: 20us + drv_enable_gpio.write(true); + delay_us(20000); // mimumum pull-down time for full reset: 20us return true; } +void start_timers() { + CRITICAL_SECTION() { + // Temporarily disable ADC triggers so they don't trigger as a side + // effect of starting the timers. + hadc1.Instance->CR2 &= ~(ADC_CR2_JEXTEN); + hadc2.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN); + hadc3.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN); + + /* + * Initial intention of the synchronization: + * Synchronize TIM1, TIM8 and TIM13 such that: + * 1. The triangle waveform of TIM1 leads the triangle waveform of TIM8 by a + * 90° phase shift. + * 2. The timer update events of TIM1 and TIM8 are symmetrically interleaved. + * 3. Each TIM13 reload coincides with a TIM1 lower update event. + * + * However right now this synchronization only ensures point (1) and (3) but because + * TIM1 and TIM3 only trigger an update on every third reload, this does not + * allow for (2). + * + * TODO: revisit the timing topic in general. + * + */ + Stm32Timer::start_synchronously<3>( + {&htim1, &htim8, &htim13}, + {TIM1_INIT_COUNT, 0, TIM1_INIT_COUNT / 2 /* TIM13 is on a clock that's only have as fast as TIM1 */} + ); + + hadc1.Instance->CR2 |= (ADC_EXTERNALTRIGINJECCONVEDGE_RISING); + hadc2.Instance->CR2 |= (ADC_EXTERNALTRIGCONVEDGE_RISING | ADC_EXTERNALTRIGINJECCONVEDGE_RISING); + hadc3.Instance->CR2 |= (ADC_EXTERNALTRIGCONVEDGE_RISING | ADC_EXTERNALTRIGINJECCONVEDGE_RISING); + + __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_JEOC); + __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_JEOC); + __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_JEOC); + __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_EOC); + __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_EOC); + __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_EOC); + __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_OVR); + __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_OVR); + __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_OVR); + __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); + + // it's sufficient to enable interrupts for one ADC only because they all trigger simultaneously + //__HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_JEOC); + //__HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_EOC); + + __HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE); + } +} + +static bool fetch_and_reset_adcs(float* current0_phB, float* current0_phC, float* current1_phB, float* current1_phC) { + bool all_adcs_done = (ADC1->SR & ADC_SR_JEOC) == ADC_SR_JEOC + && (ADC2->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC) + && (ADC3->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC); + if (!all_adcs_done) { + return false; + } + + bool m0_current_valid = m0_gate_driver.is_ready(); + bool m1_current_valid = m1_gate_driver.is_ready(); + + vbus_sense_adc_cb(ADC1->JDR1); + *current0_phB = m0_current_valid ? motors[0].phase_current_from_adcval(ADC2->JDR1) : NAN; + *current0_phC = m0_current_valid ? motors[0].phase_current_from_adcval(ADC3->JDR1) : NAN; + *current1_phB = m1_current_valid ? motors[1].phase_current_from_adcval(ADC2->DR) : NAN; + *current1_phC = m1_current_valid ? motors[1].phase_current_from_adcval(ADC3->DR) : NAN; + + ADC1->SR = ~(ADC_SR_JEOC); + ADC2->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR); + ADC3->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR); + + return true; +} extern "C" { @@ -348,51 +437,98 @@ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { } } - - -void TIM1_UP_TIM10_IRQHandler(void) { - COUNT_IRQ(TIM1_UP_TIM10_IRQn); - __HAL_TIM_CLEAR_IT(&htim1, TIM_IT_UPDATE); - motors[0].tim_update_cb(); -} - -void TIM8_UP_TIM13_IRQHandler(void) { - COUNT_IRQ(TIM8_UP_TIM13_IRQn); - __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); - motors[1].tim_update_cb(); -} - void TIM5_IRQHandler(void) { COUNT_IRQ(TIM5_IRQn); pwm0_input.on_capture(); } -void ADC_IRQ_Dispatch(ADC_HandleTypeDef* hadc, void(*callback)(ADC_HandleTypeDef* hadc, bool injected)) { - // Injected measurements - uint32_t JEOC = __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_JEOC); - uint32_t JEOC_IT_EN = __HAL_ADC_GET_IT_SOURCE(hadc, ADC_IT_JEOC); - if (JEOC && JEOC_IT_EN) { - callback(hadc, true); - __HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_JSTRT | ADC_FLAG_JEOC)); +volatile uint32_t timestamp_ = 0; +volatile bool counting_down_ = false; + +void TIM8_UP_TIM13_IRQHandler(void) { + // Entry into this function happens at 21-23 clock cycles after the timer + // update event. + __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); + + // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current + // If we are counting down, we just sampled in SVM vector 7, with zero current + bool counting_down = TIM8->CR1 & TIM_CR1_DIR; + + bool timer_update_missed = (counting_down_ == counting_down); + if (timer_update_missed) { + motors[0].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED); + motors[1].disarm_with_error(Motor::ERROR_TIMER_UPDATE_MISSED); + return; } - // Regular measurements - uint32_t EOC = __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOC); - uint32_t EOC_IT_EN = __HAL_ADC_GET_IT_SOURCE(hadc, ADC_IT_EOC); - if (EOC && EOC_IT_EN) { - callback(hadc, false); - __HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_STRT | ADC_FLAG_EOC)); + counting_down_ = counting_down; + + timestamp_ += TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1); + + if (!counting_down) { + TaskTimer::enabled = odrv.task_timers_armed_; + // Run sampling handlers and kick off control tasks when TIM8 is + // counting up. + odrv.sampling_cb(); + NVIC->STIR = ControlLoop_IRQn; + } else { + // Tentatively reset all PWM outputs to 50% duty cycles. If the control + // loop handler finishes in time then these values will be overridden + // before they go into effect. + TIM1->CCR1 = + TIM1->CCR2 = + TIM1->CCR3 = + TIM8->CCR1 = + TIM8->CCR2 = + TIM8->CCR3 = + TIM_1_8_PERIOD_CLOCKS / 2; } } -void ADC_IRQHandler(void) { - COUNT_IRQ(ADC_IRQn); - - // The HAL's ADC handling mechanism adds many clock cycles of overhead - // So we bypass it and handle the logic ourselves. - //@TODO add vbus measurement on adc1 here - ADC_IRQ_Dispatch(&hadc1, &vbus_sense_adc_cb); - ADC_IRQ_Dispatch(&hadc2, &pwm_trig_adc_cb); - ADC_IRQ_Dispatch(&hadc3, &pwm_trig_adc_cb); +void ControlLoop_IRQHandler(void) { + COUNT_IRQ(ControlLoop_IRQn); + uint32_t timestamp = timestamp_; + + // Ensure that all the ADCs are done + float current0_phB; + float current0_phC; + float current1_phB; + float current1_phC; + + if (!fetch_and_reset_adcs(¤t0_phB, ¤t0_phC, ¤t1_phB, ¤t1_phC)) { + motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); + motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); + } + + motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, {-current0_phB - current0_phC, current0_phB, current0_phC}); + motors[1].current_meas_cb(timestamp, {-current1_phB - current1_phC, current1_phB, current1_phC}); + + odrv.control_loop_cb(timestamp); + + // By this time the ADCs for both M0 and M1 should have fired again. But + // let's wait for them just to be sure. + while (!(ADC2->SR & ADC_SR_EOC)); + + if (!fetch_and_reset_adcs(¤t0_phB, ¤t0_phC, ¤t1_phB, ¤t1_phC)) { + motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); + motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); + } + + motors[0].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT, {-current0_phB - current0_phC, current0_phB, current0_phC}); + motors[1].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1), {-current1_phB - current1_phC, current1_phB, current1_phC}); + + motors[0].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT); + motors[1].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)); + + // If we did everything right, the TIM8 update handler should have been + // called exactly once between the start of this function and now. + + if (timestamp_ != timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) { + motors[0].disarm_with_error(Motor::ERROR_CONTROL_DEADLINE_MISSED); + motors[1].disarm_with_error(Motor::ERROR_CONTROL_DEADLINE_MISSED); + } + + odrv.task_timers_armed_ = odrv.task_timers_armed_ && !TaskTimer::enabled; + TaskTimer::enabled = false; } void I2C1_EV_IRQHandler(void) { @@ -405,12 +541,10 @@ void I2C1_ER_IRQHandler(void) { HAL_I2C_ER_IRQHandler(&hi2c1); } +extern PCD_HandleTypeDef hpcd_USB_OTG_FS; // defined in usbd_conf.c void OTG_FS_IRQHandler(void) { COUNT_IRQ(OTG_FS_IRQn); - // Mask interrupt, and signal processing of interrupt by usb_cmd_thread - // The thread will re-enable the interrupt when all pending irqs are clear. - HAL_NVIC_DisableIRQ(OTG_FS_IRQn); - osSemaphoreRelease(sem_usb_irq); + HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); } } diff --git a/Firmware/Drivers/DRV8301/drv8301.cpp b/Firmware/Drivers/DRV8301/drv8301.cpp index e2fb20c2..05001a6b 100644 --- a/Firmware/Drivers/DRV8301/drv8301.cpp +++ b/Firmware/Drivers/DRV8301/drv8301.cpp @@ -1,49 +1,8 @@ -/* --COPYRIGHT--,BSD - * Copyright (c) 2015, Texas Instruments Incorporated - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Texas Instruments Incorporated nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * --/COPYRIGHT--*/ -//! \file drivers/drvic/drv8301/src/32b/f28x/f2806x/drv8301.c -//! \brief Contains the various functions related to the DRV8301 object -//! -//! (C) Copyright 2015, Texas Instruments, Inc. - -// ************************************************************************** -// the includes #include "drv8301.hpp" #include "utils.hpp" - #include "cmsis_os.h" -#include -#include -#include +#include "board.h" const SPI_InitTypeDef Drv8301::spi_config_ = { .Mode = SPI_MODE_MASTER, @@ -59,119 +18,135 @@ const SPI_InitTypeDef Drv8301::spi_config_ = { .CRCPolynomial = 10, }; -bool Drv8301::init() { - enable_gpio_.write(true); - - // Wait for driver to come online - osDelay(10); +bool Drv8301::config(float requested_gain, float* actual_gain) { + // Calculate gain setting: Snap down to have equal or larger range as + // requested or largest possible range otherwise - // Make sure the Fault bit is not set during startup - uint16_t reg; - while (!read_spi(RegName_Status_1, ®) || (reg & DRV8301_STATUS1_FAULT_BITS)) - ; // TODO: don't spin - - // Wait for the DRV8301 registers to update - osDelay(1); - - return true; -} - -Drv8301::FaultType_e Drv8301::get_error() { - uint16_t readWord; - FaultType_e faultType = FaultType_NoFault; - - // read the data - if (!read_spi(RegName_Status_1, &readWord)) { - return (FaultType_e)0xffff; - } - - if (readWord & DRV8301_STATUS1_FAULT_BITS) { - faultType = (FaultType_e)(readWord & DRV8301_FAULT_TYPE_MASK); - - if (faultType == FaultType_NoFault) { - // read the data - if (!read_spi(RegName_Status_2, &readWord)) { - return (FaultType_e)0xffff; - } - - if (readWord & DRV8301_STATUS2_GVDD_OV_BITS) { - faultType = FaultType_GVDD_OV; - } - } - } - - return faultType; -} - -bool Drv8301::set_gain(float requested_gain, float* actual_gain) { // for reference: // 20V/V on 500uOhm gives a range of +/- 150A // 40V/V on 500uOhm gives a range of +/- 75A // 20V/V on 666uOhm gives a range of +/- 110A // 40V/V on 666uOhm gives a range of +/- 55A - // Snap down to have equal or larger range as requested or largest possible range otherwise - - // Decoding array for snapping gain - std::array, 4> gain_choices = { - std::make_pair(10.0f, ShuntAmpGain_10VpV), - std::make_pair(20.0f, ShuntAmpGain_20VpV), - std::make_pair(40.0f, ShuntAmpGain_40VpV), - std::make_pair(80.0f, ShuntAmpGain_80VpV) - }; - - // We use lower_bound in reverse because it snaps up by default, we want to snap down. - auto gain_snap_down = std::lower_bound(gain_choices.crbegin(), gain_choices.crend(), requested_gain, - [](std::pair pair, float val){ - return (bool)(pair.first > val); - }); - - // If we snap to outside the array, clip to smallest val - if (gain_snap_down == gain_choices.crend()) - --gain_snap_down; - - Registers_t regs; - if (!read_regs(®s)) { - return false; - } - - regs.Ctrl_Reg_1.OC_MODE = OcMode_LatchShutDown; - // Overcurrent set to approximately 150A at 100degC. This may need tweaking. - regs.Ctrl_Reg_1.OC_ADJ_SET = VdsLevel_0p730_V; - regs.Ctrl_Reg_2.GAIN = gain_snap_down->second; - - if (!write_regs(®s)) { - return false; + uint16_t gain_setting = 3; + float gain_choices[] = {10.0f, 20.0f, 40.0f, 80.0f}; + while (gain_setting && (gain_choices[gain_setting] > requested_gain)) { + gain_setting--; } if (actual_gain) { - *actual_gain = gain_snap_down->first; + *actual_gain = gain_choices[gain_setting]; + } + + RegisterFile new_config; + + new_config.control_register_1 = + (21 << 6) // Overcurrent set to approximately 150A at 100degC. This may need tweaking. + | (0b01 << 4) // OCP_MODE: latch shut down + | (0b0 << 3) // 6x PWM mode + | (0b0 << 2) // don't reset latched faults + | (0b00 << 0); // gate-drive peak current: 1.7A + + new_config.control_register_2 = + (0b0 << 6) // OC_TOFF: cycle by cycle + | (0b00 << 4) // calibration off (normal operation) + | (gain_setting << 2) // select gain + | (0b00 << 0); // report both over temperature and over current on nOCTW pin + + bool regs_equal = (regs_.control_register_1 == new_config.control_register_1) + && (regs_.control_register_2 == new_config.control_register_2); + + if (!regs_equal) { + regs_ = new_config; + state_ = kStateUninitialized; + enable_gpio_.write(false); } return true; } -bool Drv8301::check_fault() { - if (nfault_gpio_) { - return nfault_gpio_.read(); - } else { +bool Drv8301::init() { + uint16_t val; + + if (state_ == kStateReady) { return true; } + + // Reset DRV chip. The enable pin also controls the SPI interface, not only + // the driver stages. + enable_gpio_.write(false); + delay_us(40); // mimumum pull-down time for full reset: 20us + state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up + enable_gpio_.write(true); + osDelay(20); // t_spi_ready, max = 10ms + + // Write current configuration + bool did_write_regs = write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) + && write_reg(kRegNameControl1, regs_.control_register_1) // the write operation tends to be ignored if only done once (not sure why) + && write_reg(kRegNameControl2, regs_.control_register_2); + if (!did_write_regs) { + return false; + } + + // Wait for configuration to be applied + delay_us(100); + state_ = kStateStartupChecks; + + bool did_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1) + && read_reg(kRegNameControl2, &val) && (val == regs_.control_register_2); + if (!did_read_regs) { + return false; + } + + if (get_error() != FaultType_NoFault) { + return false; + } + + // There could have been an nFAULT edge meanwhile. In this case we shouldn't + // consider the driver ready. + CRITICAL_SECTION() { + if (state_ == kStateStartupChecks) { + state_ = kStateReady; + } + } + + return state_ == kStateReady; +} + +void Drv8301::do_checks() { + if (state_ != kStateUninitialized && !nfault_gpio_.read()) { + state_ = kStateUninitialized; + } } -bool Drv8301::read_spi(const RegName_e regName, uint16_t* data) { +bool Drv8301::is_ready() { + return state_ == kStateReady; +} + +Drv8301::FaultType_e Drv8301::get_error() { + uint16_t fault1, fault2; + + if (!read_reg(kRegNameStatus1, &fault1) || + !read_reg(kRegNameStatus2, &fault2)) { + return (FaultType_e)0xffffffff; + } + + return (FaultType_e)((uint32_t)fault1 | ((uint32_t)(fault2 & 0x0080) << 16)); +} + +bool Drv8301::read_reg(const RegName_e regName, uint16_t* data) { tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0); if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { return false; } - - // Datasheet says you don't have to pulse the nCS between transfers, (16 - // clocks should commit the transfer) but for some reason you actually need - // to pulse it. + delay_us(1); - tx_buf_ = 0; - rx_buf_ = 0xbeef; + tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0); + rx_buf_ = 0xffff; if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) { return false; } @@ -183,13 +158,13 @@ bool Drv8301::read_spi(const RegName_e regName, uint16_t* data) { } if (data) { - *data = rx_buf_ & DRV8301_DATA_MASK; + *data = rx_buf_ & 0x07FF; } - + return true; } -bool Drv8301::write_spi(const RegName_e regName, const uint16_t data) { +bool Drv8301::write_reg(const RegName_e regName, const uint16_t data) { // Do blocking write tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Write, regName, data); if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { @@ -199,76 +174,3 @@ bool Drv8301::write_spi(const RegName_e regName, const uint16_t data) { return true; } - -bool Drv8301::write_regs(Registers_t *regs) { - uint16_t ctrl1 = regs->Ctrl_Reg_1.DRV8301_CURRENT | - regs->Ctrl_Reg_1.DRV8301_RESET | - regs->Ctrl_Reg_1.PWM_MODE | - regs->Ctrl_Reg_1.OC_MODE | - regs->Ctrl_Reg_1.OC_ADJ_SET; - - uint16_t ctrl2 = regs->Ctrl_Reg_2.OCTW_SET | - regs->Ctrl_Reg_2.GAIN | - regs->Ctrl_Reg_2.DC_CAL_CH1p2 | - regs->Ctrl_Reg_2.OC_TOFF; - - return write_spi(RegName_Control_1, ctrl1) - && write_spi(RegName_Control_2, ctrl2); -} - -bool Drv8301::read_regs(Registers_t *regs) { - bool success = true; - uint16_t drvDataNew; - - // Update Status Register 1 - if (read_spi(RegName_Status_1, &drvDataNew)) { - regs->Stat_Reg_1.FAULT = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FAULT_BITS); - regs->Stat_Reg_1.GVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_GVDD_UV_BITS); - regs->Stat_Reg_1.PVDD_UV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_PVDD_UV_BITS); - regs->Stat_Reg_1.OTSD = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTSD_BITS); - regs->Stat_Reg_1.OTW = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_OTW_BITS); - regs->Stat_Reg_1.FETHA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHA_OC_BITS); - regs->Stat_Reg_1.FETLA_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLA_OC_BITS); - regs->Stat_Reg_1.FETHB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHB_OC_BITS); - regs->Stat_Reg_1.FETLB_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLB_OC_BITS); - regs->Stat_Reg_1.FETHC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETHC_OC_BITS); - regs->Stat_Reg_1.FETLC_OC = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS1_FETLC_OC_BITS); - regs->Stat_Reg_1_Value = drvDataNew; - } else { - success = false; - } - - // Update Status Register 2 - if (read_spi(RegName_Status_2, &drvDataNew)) { - regs->Stat_Reg_2.GVDD_OV = (bool)(drvDataNew & (uint16_t)DRV8301_STATUS2_GVDD_OV_BITS); - regs->Stat_Reg_2.DeviceID = (uint16_t)(drvDataNew & (uint16_t)DRV8301_STATUS2_ID_BITS); - regs->Stat_Reg_2_Value = drvDataNew; - } else { - success = false; - } - - // Update Control Register 1 - if (read_spi(RegName_Control_1, &drvDataNew)) { - regs->Ctrl_Reg_1.DRV8301_CURRENT = (PeakCurrent_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_CURRENT_BITS); - regs->Ctrl_Reg_1.DRV8301_RESET = (Reset_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_GATE_RESET_BITS); - regs->Ctrl_Reg_1.PWM_MODE = (PwmMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_PWM_MODE_BITS); - regs->Ctrl_Reg_1.OC_MODE = (OcMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_MODE_BITS); - regs->Ctrl_Reg_1.OC_ADJ_SET = (VdsLevel_e)(drvDataNew & (uint16_t)DRV8301_CTRL1_OC_ADJ_SET_BITS); - regs->Ctrl_Reg_1_Value = drvDataNew; - } else { - success = false; - } - - // Update Control Register 2 - if (read_spi(RegName_Control_2, &drvDataNew)) { - regs->Ctrl_Reg_2.OCTW_SET = (OcTwMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OCTW_SET_BITS); - regs->Ctrl_Reg_2.GAIN = (ShuntAmpGain_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_GAIN_BITS); - regs->Ctrl_Reg_2.DC_CAL_CH1p2 = (DcCalMode_e)(drvDataNew & (uint16_t)(DRV8301_CTRL2_DC_CAL_1_BITS | DRV8301_CTRL2_DC_CAL_2_BITS)); - regs->Ctrl_Reg_2.OC_TOFF = (OcOffTimeMode_e)(drvDataNew & (uint16_t)DRV8301_CTRL2_OC_TOFF_BITS); - regs->Ctrl_Reg_2_Value = drvDataNew; - } else { - success = false; - } - - return success; -} diff --git a/Firmware/Drivers/DRV8301/drv8301.hpp b/Firmware/Drivers/DRV8301/drv8301.hpp index cf0cc847..45f693bd 100644 --- a/Firmware/Drivers/DRV8301/drv8301.hpp +++ b/Firmware/Drivers/DRV8301/drv8301.hpp @@ -1,192 +1,20 @@ -/* --COPYRIGHT--,BSD - * Copyright (c) 2015, Texas Instruments Incorporated - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of Texas Instruments Incorporated nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * --/COPYRIGHT--*/ -#ifndef _DRV8301_HPP_ -#define _DRV8301_HPP_ - -//! \file drivers/drvic/drv8301/src/32b/f28x/f2806x/drv8301.h -//! \brief Contains public interface to various functions related -//! to the DRV8301 object -//! -//! (C) Copyright 2015, Texas Instruments, Inc. - - -// ************************************************************************** -// the includes +#ifndef __DRV8301_HPP +#define __DRV8301_HPP #include "stdbool.h" #include "stdint.h" -// drivers - -#include "stm32f4xx_hal.h" - #include #include #include -#ifdef __cplusplus -extern "C" { -#endif - - -// ************************************************************************** -// the defines - -//! \brief Defines the address mask -//! -#define DRV8301_ADDR_MASK (0x7800) - - -//! \brief Defines the data mask -//! -#define DRV8301_DATA_MASK (0x07FF) - - -//! \brief Defines the R/W mask -//! -#define DRV8301_RW_MASK (0x8000) - - -//! \brief Defines the R/W mask -//! -#define DRV8301_FAULT_TYPE_MASK (0x07FF) - - -//! \brief Defines the location of the FETLC_OC (FET Low side, Phase C Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETLC_OC_BITS (1 << 0) - -//! \brief Defines the location of the FETLC_OC (FET High side, Phase C Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETHC_OC_BITS (1 << 1) - -//! \brief Defines the location of the FETLC_OC (FET Low side, Phase B Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETLB_OC_BITS (1 << 2) - -//! \brief Defines the location of the FETLC_OC (FET High side, Phase B Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETHB_OC_BITS (1 << 3) - -//! \brief Defines the location of the FETLC_OC (FET Low side, Phase A Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETLA_OC_BITS (1 << 4) - -//! \brief Defines the location of the FETLC_OC (FET High side, Phase A Over Current) bits in the Status 1 register -//! -#define DRV8301_STATUS1_FETHA_OC_BITS (1 << 5) - -//! \brief Defines the location of the OTW (Over Temperature Warning) bits in the Status 1 register -//! -#define DRV8301_STATUS1_OTW_BITS (1 << 6) - -//! \brief Defines the location of the OTSD (Over Temperature Shut Down) bits in the Status 1 register -//! -#define DRV8301_STATUS1_OTSD_BITS (1 << 7) - -//! \brief Defines the location of the PVDD_UV (Power supply Vdd, Under Voltage) bits in the Status 1 register -//! -#define DRV8301_STATUS1_PVDD_UV_BITS (1 << 8) - -//! \brief Defines the location of the GVDD_UV (DRV8301 Vdd, Under Voltage) bits in the Status 1 register -//! -#define DRV8301_STATUS1_GVDD_UV_BITS (1 << 9) - -//! \brief Defines the location of the FAULT bits in the Status 1 register -//! -#define DRV8301_STATUS1_FAULT_BITS (1 << 10) - - -//! \brief Defines the location of the Device ID bits in the Status 2 register -//! -#define DRV8301_STATUS2_ID_BITS (15 << 0) - -//! \brief Defines the location of the GVDD_OV (DRV8301 Vdd, Over Voltage) bits in the Status 2 register -//! -#define DRV8301_STATUS2_GVDD_OV_BITS (1 << 7) - - -//! \brief Defines the location of the GATE_CURRENT bits in the Control 1 register -//! -#define DRV8301_CTRL1_GATE_CURRENT_BITS (3 << 0) - -//! \brief Defines the location of the GATE_RESET bits in the Control 1 register -//! -#define DRV8301_CTRL1_GATE_RESET_BITS (1 << 2) - -//! \brief Defines the location of the PWM_MODE bits in the Control 1 register -//! -#define DRV8301_CTRL1_PWM_MODE_BITS (1 << 3) - -//! \brief Defines the location of the OC_MODE bits in the Control 1 register -//! -#define DRV8301_CTRL1_OC_MODE_BITS (3 << 4) - -//! \brief Defines the location of the OC_ADJ bits in the Control 1 register -//! -#define DRV8301_CTRL1_OC_ADJ_SET_BITS (31 << 6) - - -//! \brief Defines the location of the OCTW_SET bits in the Control 2 register -//! -#define DRV8301_CTRL2_OCTW_SET_BITS (3 << 0) - -//! \brief Defines the location of the GAIN bits in the Control 2 register -//! -#define DRV8301_CTRL2_GAIN_BITS (3 << 2) - -//! \brief Defines the location of the DC_CAL_1 bits in the Control 2 register -//! -#define DRV8301_CTRL2_DC_CAL_1_BITS (1 << 4) - -//! \brief Defines the location of the DC_CAL_2 bits in the Control 2 register -//! -#define DRV8301_CTRL2_DC_CAL_2_BITS (1 << 5) - -//! \brief Defines the location of the OC_TOFF bits in the Control 2 register -//! -#define DRV8301_CTRL2_OC_TOFF_BITS (1 << 6) - - -#ifdef __cplusplus -} -#endif // extern "C" - - class Drv8301 : public GateDriverBase, public OpAmpBase { public: typedef enum { FaultType_NoFault = (0 << 0), //!< No fault + + // Status Register 1 FaultType_FETLC_OC = (1 << 0), //!< FET Low side, Phase C Over Current fault FaultType_FETHC_OC = (1 << 1), //!< FET High side, Phase C Over Current fault FaultType_FETLB_OC = (1 << 2), //!< FET Low side, Phase B Over Current fault @@ -197,24 +25,59 @@ public: FaultType_OTSD = (1 << 7), //!< Over Temperature Shut Down fault FaultType_PVDD_UV = (1 << 8), //!< Power supply Vdd Under Voltage fault FaultType_GVDD_UV = (1 << 9), //!< DRV8301 Vdd Under Voltage fault - FaultType_GVDD_OV = (1 << 10) //!< DRV8301 Vdd Over Voltage fault + FaultType_FAULT = (1 << 10), + + // Status Register 2 + FaultType_GVDD_OV = (1 << 23) //!< DRV8301 Vdd Over Voltage fault } FaultType_e; Drv8301(Stm32SpiArbiter* spi_arbiter, Stm32Gpio ncs_gpio, Stm32Gpio enable_gpio, Stm32Gpio nfault_gpio) - : spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio), - enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {} + : spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio), + enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {} + + /** + * @brief Prepares the gate driver's configuration. + * + * If the gate driver was in ready state and the new configuration is + * different from the old one then the gate driver will exit ready state. + * + * In any case cnahges to the configuration only take effect with a call to + * init(). + */ + bool config(float requested_gain, float* actual_gain); /** - * @brief Initializes the gate driver to a hardcoded default configuration. + * @brief Initializes the gate driver to the configuration prepared with + * config(). + * * Returns true on success or false otherwise (e.g. if the gate driver is - * not connected). + * not connected or not powered or if config() was not yet called). */ bool init(); - - bool set_gain(float requested_gain, float* actual_gain) final; - bool check_fault() final; + + /** + * @brief Monitors the nFAULT pin. + * + * This must be run at an interval of <8ms from the moment the init() + * functions starts to run, otherwise it's possible that a temporary power + * loss is missed, leading to unwanted register values. + * In case of power loss the nFAULT pin can be low for as little as 8ms. + */ + void do_checks(); + + /** + * @brief Returns true if and only if the DRV8301 chip is in an initialized + * state and ready to do switching and current sensor opamp operation. + */ + bool is_ready() final; + + /** + * @brief This has no effect on this driver chip because the drive stages are + * always enabled while the chip is initialized + */ bool set_enabled(bool enabled) final { return true; } + FaultType_e get_error(); float get_midpoint() final { @@ -232,209 +95,51 @@ private: }; enum RegName_e { - RegName_Status_1 = 0 << 11, //!< Status Register 1 - RegName_Status_2 = 1 << 11, //!< Status Register 2 - RegName_Control_1 = 2 << 11, //!< Control Register 1 - RegName_Control_2 = 3 << 11 //!< Control Register 2 + kRegNameStatus1 = 0 << 11, //!< Status Register 1 + kRegNameStatus2 = 1 << 11, //!< Status Register 2 + kRegNameControl1 = 2 << 11, //!< Control Register 1 + kRegNameControl2 = 3 << 11 //!< Control Register 2 }; - //! \brief Enumeration for the DC calibration modes - enum DcCalMode_e { - DcCalMode_Ch1_Load = (0 << 4), //!< Shunt amplifier 1 connected to load via input pins - DcCalMode_Ch1_NoLoad = (1 << 4), //!< Shunt amplifier 1 disconnected from load and input pins are shorted - DcCalMode_Ch2_Load = (0 << 5), //!< Shunt amplifier 2 connected to load via input pins - DcCalMode_Ch2_NoLoad = (1 << 5) //!< Shunt amplifier 2 disconnected from load and input pins are shorted + struct RegisterFile { + uint16_t control_register_1; + uint16_t control_register_2; }; - //! \brief Enumeration for the Over Current modes - enum OcMode_e { - OcMode_CurrentLimit = 0 << 4, //!< current limit when OC detected - OcMode_LatchShutDown = 1 << 4, //!< latch shut down when OC detected - OcMode_ReportOnly = 2 << 4, //!< report only when OC detected - OcMode_Disabled = 3 << 4 //!< OC protection disabled - }; - - //! \brief Enumeration for the Over Current Off Time modes - enum OcOffTimeMode_e { - OcOffTimeMode_Normal = 0 << 6, //!< normal CBC operation - OcOffTimeMode_Ctrl = 1 << 6 //!< off time control during OC - }; - - //! \brief Enumeration for the Over Current, Temperature Warning modes - enum OcTwMode_e { - OcTwMode_Both = 0 << 0, //!< report both OT and OC at /OCTW pin - OcTwMode_OT_Only = 1 << 0, //!< report only OT at /OCTW pin - OcTwMode_OC_Only = 2 << 0 //!< report only OC at /OCTW pin - }; - - //! \brief Enumeration for the drv8301 peak current levels - enum PeakCurrent_e { - PeakCurrent_1p70_A = 0 << 0, //!< drv8301 driver peak current 1.70A - PeakCurrent_0p70_A = 1 << 0, //!< drv8301 driver peak current 0.70A - PeakCurrent_0p25_A = 2 << 0 //!< drv8301 driver peak current 0.25A - }; - - //! \brief Enumeration for the PWM modes - enum PwmMode_e { - PwmMode_Six_Inputs = 0 << 3, //!< six independent inputs - PwmMode_Three_Inputs = 1 << 3 //!< three independent nputs - }; - - //! \brief Enumeration for the shunt amplifier gains - enum Reset_e { - Reset_Normal = 0 << 2, //!< normal - Reset_All = 1 << 2 //!< reset all - }; - - //! \brief Enumeration for the shunt amplifier gains - enum ShuntAmpGain_e { - ShuntAmpGain_10VpV = 0 << 2, //!< 10 V per V - ShuntAmpGain_20VpV = 1 << 2, //!< 20 V per V - ShuntAmpGain_40VpV = 2 << 2, //!< 40 V per V - ShuntAmpGain_80VpV = 3 << 2 //!< 80 V per V - }; - - //! \brief Enumeration for the shunt amplifier number - enum ShuntAmpNumber_e { - ShuntAmpNumber_1 = 1, //!< Shunt amplifier number 1 - ShuntAmpNumber_2 = 2 //!< Shunt amplifier number 2 - }; - - //! \brief Enumeration for the Vds level for th over current adjustment - enum VdsLevel_e { - VdsLevel_0p060_V = 0 << 6, //!< Vds = 0.060 V - VdsLevel_0p068_V = 1 << 6, //!< Vds = 0.068 V - VdsLevel_0p076_V = 2 << 6, //!< Vds = 0.076 V - VdsLevel_0p086_V = 3 << 6, //!< Vds = 0.086 V - VdsLevel_0p097_V = 4 << 6, //!< Vds = 0.097 V - VdsLevel_0p109_V = 5 << 6, //!< Vds = 0.109 V - VdsLevel_0p123_V = 6 << 6, //!< Vds = 0.123 V - VdsLevel_0p138_V = 7 << 6, //!< Vds = 0.138 V - VdsLevel_0p155_V = 8 << 6, //!< Vds = 0.155 V - VdsLevel_0p175_V = 9 << 6, //!< Vds = 0.175 V - VdsLevel_0p197_V = 10 << 6, //!< Vds = 0.197 V - VdsLevel_0p222_V = 11 << 6, //!< Vds = 0.222 V - VdsLevel_0p250_V = 12 << 6, //!< Vds = 0.250 V - VdsLevel_0p282_V = 13 << 6, //!< Vds = 0.282 V - VdsLevel_0p317_V = 14 << 6, //!< Vds = 0.317 V - VdsLevel_0p358_V = 15 << 6, //!< Vds = 0.358 V - VdsLevel_0p403_V = 16 << 6, //!< Vds = 0.403 V - VdsLevel_0p454_V = 17 << 6, //!< Vds = 0.454 V - VdsLevel_0p511_V = 18 << 6, //!< Vds = 0.511 V - VdsLevel_0p576_V = 19 << 6, //!< Vds = 0.576 V - VdsLevel_0p648_V = 20 << 6, //!< Vds = 0.648 V - VdsLevel_0p730_V = 21 << 6, //!< Vds = 0.730 V - VdsLevel_0p822_V = 22 << 6, //!< Vds = 0.822 V - VdsLevel_0p926_V = 23 << 6, //!< Vds = 0.926 V - VdsLevel_1p043_V = 24 << 6, //!< Vds = 1.403 V - VdsLevel_1p175_V = 25 << 6, //!< Vds = 1.175 V - VdsLevel_1p324_V = 26 << 6, //!< Vds = 1.324 V - VdsLevel_1p491_V = 27 << 6, //!< Vds = 1.491 V - VdsLevel_1p679_V = 28 << 6, //!< Vds = 1.679 V - VdsLevel_1p892_V = 29 << 6, //!< Vds = 1.892 V - VdsLevel_2p131_V = 30 << 6, //!< Vds = 2.131 V - VdsLevel_2p400_V = 31 << 6 //!< Vds = 2.400 V - }; - - struct Registers_t { - struct { - bool FAULT; - bool GVDD_UV; - bool PVDD_UV; - bool OTSD; - bool OTW; - bool FETHA_OC; - bool FETLA_OC; - bool FETHB_OC; - bool FETLB_OC; - bool FETHC_OC; - bool FETLC_OC; - } Stat_Reg_1; - - struct { - bool GVDD_OV; - uint16_t DeviceID; - } Stat_Reg_2; - - struct { - PeakCurrent_e DRV8301_CURRENT; - Reset_e DRV8301_RESET; - PwmMode_e PWM_MODE; - OcMode_e OC_MODE; - VdsLevel_e OC_ADJ_SET; - } Ctrl_Reg_1; - - struct { - OcTwMode_e OCTW_SET; - ShuntAmpGain_e GAIN; - DcCalMode_e DC_CAL_CH1p2; - OcOffTimeMode_e OC_TOFF; - } Ctrl_Reg_2; - - uint16_t Stat_Reg_1_Value; - uint16_t Stat_Reg_2_Value; - uint16_t Ctrl_Reg_1_Value; - uint16_t Ctrl_Reg_2_Value; - }; - - //! \brief Builds the control word - //! \param[in] ctrlMode The control mode - //! \param[in] regName The register name - //! \param[in] data The data - //! \return The control word static inline uint16_t build_ctrl_word(const CtrlMode_e ctrlMode, - const RegName_e regName, - const uint16_t data) { - return ctrlMode | regName | (data & DRV8301_DATA_MASK); - } // end of DRV8301_buildCtrlWord() function + const RegName_e regName, + const uint16_t data) { + return ctrlMode | regName | (data & 0x07FF); + } - //! \brief Reads data from the DRV8301 register - //! \param[in] regName The register name - //! \return The data value - bool read_spi(const RegName_e regName, uint16_t* data); + /** @brief Reads data from a DRV8301 register */ + bool read_reg(const RegName_e regName, uint16_t* data); - //! \brief Writes data to the DRV8301 register - //! \param[in] regName The register name - //! \param[in] data The data value - bool write_spi(const RegName_e regName, const uint16_t data); + /** @brief Writes data to a DRV8301 register. There is no check if the write succeeded. */ + bool write_reg(const RegName_e regName, const uint16_t data); - //! \brief Interface to all 8301 SPI variables - //! - //! \details Call this function periodically to be able to read the DRV8301 Status1, Status2, - //! Control1, and Control2 registers and write the Control1 and Control2 registers. - //! This function updates the members of the structure Registers_t. - //! How to use in Setup - //! Code - //! Add the structure declaration Registers_t to your code - //! Make sure the SPI and 8301 EN_Gate GPIO are setup for the 8301 by using HAL_init and HAL_setParams - //! During code setup, call HAL_enableDrv and HAL_setupDrvSpi - //! In background loop, call DRV8301_writeData and DRV8301_readData - //! How to use in Runtime - //! Watch window - //! Add the structure, declared by Registers_t above, to the watch window - //! Runtime - //! Pull down the menus from the Registers_t strcuture to the desired setting - //! Set SndCmd to send the settings to the DRV8301 - //! If a read of the DRV8301 registers is required, se RcvCmd - //! - //! \param[in] regs The (Registers_t) structure that contains all DRV8301 Status/Control register options - bool write_regs(Registers_t *regs); - - //! \param[in] regs The (Registers_t) structure that contains all DRV8301 Status/Control register options - bool read_regs(Registers_t *regs); + static const SPI_InitTypeDef spi_config_; + // Configuration Stm32SpiArbiter* spi_arbiter_; Stm32Gpio ncs_gpio_; Stm32Gpio enable_gpio_; Stm32Gpio nfault_gpio_; + RegisterFile regs_; //!< Current configuration. If is_ready_ is + //!< true then this can be considered consistent + //!< with the actual file on the DRV8301 chip. + // We don't put these buffers on the stack because we place the stack in // a RAM section which cannot be used by DMA. - uint16_t tx_buf_; - uint16_t rx_buf_; + uint16_t tx_buf_, rx_buf_; - static const SPI_InitTypeDef spi_config_; + enum { + kStateUninitialized, + kStateStartupChecks, + kStateReady, + } state_ = kStateUninitialized; }; -#endif // _DRV8301_HPP_ +#endif // __DRV8301_HPP diff --git a/Firmware/Drivers/STM32/stm32_gpio.cpp b/Firmware/Drivers/STM32/stm32_gpio.cpp index 0d09b174..36cb4c0b 100644 --- a/Firmware/Drivers/STM32/stm32_gpio.cpp +++ b/Firmware/Drivers/STM32/stm32_gpio.cpp @@ -92,14 +92,11 @@ bool Stm32Gpio::subscribe(bool rising_edge, bool falling_edge, void (*callback)( struct subscription_t& subscription = subscriptions[pin_number]; - void (*no_port)(void*) = nullptr; + GPIO_TypeDef* no_port = nullptr; if (!__atomic_compare_exchange_n(&subscription.port, &no_port, port_, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { return false; // already in use } - subscription.ctx = ctx; - subscription.callback = callback; - // The following code is mostly taken from HAL_GPIO_Init __HAL_RCC_SYSCFG_CLK_ENABLE(); @@ -126,10 +123,9 @@ bool Stm32Gpio::subscribe(bool rising_edge, bool falling_edge, void (*callback)( // Clear any previous triggers __HAL_GPIO_EXTI_CLEAR_IT(pin_mask_); - // Enable interrupt - // TODO: use configurable priority - HAL_NVIC_SetPriority(get_irq_number(pin_number), 0, 0); - HAL_NVIC_EnableIRQ(get_irq_number(pin_number)); + + subscription.ctx = ctx; + subscription.callback = callback; return true; } @@ -141,8 +137,12 @@ void Stm32Gpio::unsubscribe() { struct subscription_t& subscription = subscriptions[pin_number]; - HAL_NVIC_DisableIRQ(get_irq_number(pin_number)); + if (subscription.port != port_) { + return; // the subscription was not for this GPIO + } + EXTI->IMR |= (uint32_t)pin_mask_; + __HAL_GPIO_EXTI_CLEAR_IT(pin_mask_); // At this point no more interrupts will be triggered for this GPIO diff --git a/Firmware/Drivers/STM32/stm32_gpio.hpp b/Firmware/Drivers/STM32/stm32_gpio.hpp index d8cd15f6..1e1199f9 100644 --- a/Firmware/Drivers/STM32/stm32_gpio.hpp +++ b/Firmware/Drivers/STM32/stm32_gpio.hpp @@ -37,6 +37,8 @@ public: * Before calling this function the gpio should most likely be configured as * input (however this is not mandatory, the interrupt works in output mode * too). + * Also you need to enable the EXTIx_IRQn interrupt vectors in the NVIC, + * otherwise the subscription won't have any effect. * * Only one subscription is allowed per pin number. I.e. it is not possible * to set up a subscription for both PA0 and PB0 at the same time. @@ -51,9 +53,15 @@ public: /** * @brief Unsubscribes from external interrupt on the specified GPIO. * + * If no subscription was active for this GPIO, calling this function has no + * effect. + * * This function is thread-safe with respect to all other public functions * of this class, however it must not be called from an interrupt routine * running at a higher priority than the interrupt that is being unsubscribed. + * + * After this function returns the callback given to subscribe() will no + * longer be invoked. */ void unsubscribe(); diff --git a/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp b/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp index 922d4c62..00c65dcf 100644 --- a/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp +++ b/Firmware/Drivers/STM32/stm32_spi_arbiter.cpp @@ -40,7 +40,11 @@ bool Stm32SpiArbiter::start() { task.ncs_gpio.write(false); HAL_StatusTypeDef status = HAL_ERROR; - if (task.tx_buf && task.rx_buf) { + + if (hspi_->hdmatx->State != HAL_DMA_STATE_READY || hspi_->hdmarx->State != HAL_DMA_STATE_READY) { + // This can happen if the DMA or interrupt priorities are not configured properly. + status = HAL_BUSY; + } else if (task.tx_buf && task.rx_buf) { status = HAL_SPI_TransmitReceive_DMA(hspi_, (uint8_t*)task.tx_buf, task.rx_buf, task.length); } else if (task.tx_buf) { status = HAL_SPI_Transmit_DMA(hspi_, (uint8_t*)task.tx_buf, task.length); diff --git a/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp b/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp index 79d54c3c..d6883fc2 100644 --- a/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp +++ b/Firmware/Drivers/STM32/stm32_spi_arbiter.hpp @@ -89,7 +89,6 @@ private: SPI_HandleTypeDef* hspi_; SpiTask* task_list_ = nullptr; - SpiTask* current_task_ = nullptr; }; #endif // __STM32_SPI_ARBITER_HPP \ No newline at end of file diff --git a/Firmware/Drivers/gate_driver.hpp b/Firmware/Drivers/gate_driver.hpp index a95ffaf6..99f7fc1f 100644 --- a/Firmware/Drivers/gate_driver.hpp +++ b/Firmware/Drivers/gate_driver.hpp @@ -13,17 +13,20 @@ struct GateDriverBase { virtual bool set_enabled(bool enabled) = 0; /** - * @brief Checks for a fault condition. Returns false if the driver is in a - * fault state and true if it is in a nominal state. + * @brief Returns false if the gate driver is in a state where the output + * drive stages are disarmed or not properly configured (e.g. because they + * are not initialized or there was a fault condition). */ - virtual bool check_fault() = 0; + virtual bool is_ready() = 0; }; struct OpAmpBase { /** - * @brief Tries to set the OpAmp gain to the specified value or lower. + * @brief Returns false if the opamp is in a state where it's not operating + * with the latest configured gain (e.g. because it was not initialized or + * there was a fault condition). */ - virtual bool set_gain(float requested_gain, float* actual_gain) = 0; + virtual bool is_ready() = 0; /** * @brief Returns the neutral voltage of the OpAmp in Volts diff --git a/Firmware/MotorControl/async_estimator.cpp b/Firmware/MotorControl/async_estimator.cpp new file mode 100644 index 00000000..24541b56 --- /dev/null +++ b/Firmware/MotorControl/async_estimator.cpp @@ -0,0 +1,48 @@ + +#include "async_estimator.hpp" +#include + +void AsyncEstimator::update(uint32_t timestamp) { + float rotor_phase = rotor_phase_src_ ? *rotor_phase_src_ : NAN; + float rotor_phase_vel = rotor_phase_vel_src_ ? *rotor_phase_vel_src_ : NAN; + float id = id_src_ ? *id_src_ : NAN; + float iq = iq_src_ ? *iq_src_ : NAN; + + if (std::isnan(rotor_phase) || std::isnan(rotor_phase_vel)) { + stator_phase_vel_ = NAN; + stator_phase_ = NAN; + active_ = false; + return; + } + + if (!active_) { + last_timestamp_ = timestamp; + stator_phase_vel_ = 0.0f; + stator_phase_ = 0.0f; + active_ = true; + return; + } + + last_timestamp_ = timestamp; + + float dt = (float)(timestamp - last_timestamp_) / (float)TIM_1_8_CLOCK_HZ; + + // Note that the effect of the current commands on the real currents is actually 1.5 PWM cycles later + // However the rotor time constant is (usually) so slow that it doesn't matter + // So we elect to write it as if the effect is immediate, to have cleaner code + + // acim_rotor_flux is normalized to units of [A] tracking Id; rotor inductance is unspecified + float dflux_by_dt = config_.slip_velocity * (id - rotor_flux_); + rotor_flux_ += dflux_by_dt * dt; + float slip_velocity = config_.slip_velocity * (iq / rotor_flux_); + // Check for issues with small denominator. Polarity of check to catch NaN too + bool acceptable_vel = fabsf(slip_velocity) <= 0.1f / dt; + if (!acceptable_vel) + slip_velocity = 0.0f; + slip_vel_ = slip_velocity; // reporting only + stator_phase_vel_ = rotor_phase_vel + slip_velocity; + + phase_offset_ += slip_velocity * dt; + phase_offset_ = wrap_pm_pi(phase_offset_); + stator_phase_ = wrap_pm_pi(rotor_phase + phase_offset_); +} diff --git a/Firmware/MotorControl/async_estimator.hpp b/Firmware/MotorControl/async_estimator.hpp new file mode 100644 index 00000000..ea0189f7 --- /dev/null +++ b/Firmware/MotorControl/async_estimator.hpp @@ -0,0 +1,36 @@ +#ifndef __ASYNC_ESTIMATOR_HPP +#define __ASYNC_ESTIMATOR_HPP + +#include +#include + +class AsyncEstimator : public ComponentBase { +public: + struct Config_t { + float slip_velocity = 14.706f; // [rad/s electrical] = 1/rotor_tau + }; + + void update(uint32_t timestamp) final; + + // Config + Config_t config_; + + // Inputs + float* rotor_phase_src_ = nullptr; + float* rotor_phase_vel_src_ = nullptr; + float* id_src_ = nullptr; + float* iq_src_ = nullptr; + + // State variables + float active_ = false; + uint32_t last_timestamp_ = 0; + float rotor_flux_ = 0.0f; // [A] + float slip_vel_ = 0.0f; // [rad/s electrical] + float phase_offset_ = 0.0f; // [rad electrical] + + // Outputs + float stator_phase_vel_ = NAN; // [rad/s] rotor flux angular velocity estimate + float stator_phase_ = NAN; // [rad] rotor flux phase angle estimate +}; + +#endif // __ASYNC_ESTIMATOR_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 5ff333fd..208eec07 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -97,12 +97,6 @@ void Axis::clear_config() { config_.can_node_id = axis_num_; } -// @brief Sets up all components of the axis, -// such as gate driver and encoder hardware. -bool Axis::setup() { - return motor_.setup(); -} - static void run_state_machine_loop_wrapper(void* ctx) { reinterpret_cast(ctx)->run_state_machine_loop(); reinterpret_cast(ctx)->thread_id_valid_ = false; @@ -115,17 +109,15 @@ void Axis::start_thread() { thread_id_valid_ = true; } -// @brief Unblocks the control loop thread. -// This is called from the current sense interrupt handler. -void Axis::signal_current_meas() { - if (thread_id_valid_) - osSignalSet(thread_id_, M_SIGNAL_PH_CURRENT_MEAS); -} - -// @brief Blocks until a current measurement is completed -// @returns True on success, false otherwise -bool Axis::wait_for_current_meas() { - return osSignalWait(M_SIGNAL_PH_CURRENT_MEAS, PH_CURRENT_MEAS_TIMEOUT).status == osEventSignal; +/** + * @brief Blocks until at least one complete control loop has been executed. + */ +bool Axis::wait_for_control_iteration() { + uint16_t control_iteration_num = odrv.n_evt_control_loop_; + while (odrv.n_evt_control_loop_ == control_iteration_num) { + osDelay(1); + } + return true; } // step/direction interface @@ -164,23 +156,13 @@ void Axis::set_step_dir_active(bool active) { // @brief Do axis level checks and call subcomponent do_checks // Returns true if everything is ok. -bool Axis::do_checks() { - if (!brake_resistor_armed) - error_ |= ERROR_BRAKE_RESISTOR_DISARMED; - if ((current_state_ != AXIS_STATE_IDLE) && (motor_.armed_state_ == Motor::ARMED_STATE_DISARMED)) - // motor got disarmed in something other than the idle loop - error_ |= ERROR_MOTOR_DISARMED; - if (!(vbus_voltage >= odrv.config_.dc_bus_undervoltage_trip_level)) - error_ |= ERROR_DC_BUS_UNDER_VOLTAGE; - if (!(vbus_voltage <= odrv.config_.dc_bus_overvoltage_trip_level)) - error_ |= ERROR_DC_BUS_OVER_VOLTAGE; - +bool Axis::do_checks(uint32_t timestamp) { // Sub-components should use set_error which will propegate to this error_ motor_.effective_current_lim(); for (ThermistorCurrentLimiter* thermistor : thermistors_) { thermistor->do_checks(); } - motor_.do_checks(); + motor_.do_checks(timestamp); // encoder_.do_checks(); // sensorless_estimator_.do_checks(); // controller_.do_checks(); @@ -195,21 +177,6 @@ bool Axis::do_checks() { return check_for_errors(); } -// @brief Update all esitmators -bool Axis::do_updates() { - // Sub-components should use set_error which will propegate to this error_ - for (ThermistorCurrentLimiter* thermistor : thermistors_) { - thermistor->update(); - } - encoder_.update(); - sensorless_estimator_.update(); - min_endstop_.update(); - max_endstop_.update(); - bool ret = check_for_errors(); - odCAN->send_heartbeat(this); - return ret; -} - // @brief Feed the watchdog to prevent watchdog timeouts. void Axis::watchdog_feed() { watchdog_current_value_ = get_watchdog_reset(); @@ -230,131 +197,169 @@ bool Axis::watchdog_check() { } bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) { - // Spiral up current for softer rotor lock-in - lockin_state_ = LOCKIN_STATE_RAMP; - float x = 0.0f; - run_control_loop([&]() { - float phase = wrap_pm_pi(lockin_config.ramp_distance * x); - float I_mag = lockin_config.current * x; - x += current_meas_period / lockin_config.ramp_time; - if (!motor_.update(I_mag, phase, 0.0f)) - return false; - return x < 1.0f; - }); - - // Spin states - float distance = lockin_config.ramp_distance; - float phase = wrap_pm_pi(distance); - float vel = distance / lockin_config.ramp_time; + CRITICAL_SECTION() { + // Reset state variables + open_loop_controller_.Id_setpoint_ = NAN; + open_loop_controller_.Iq_setpoint_ = NAN; + open_loop_controller_.Vd_setpoint_ = NAN; + open_loop_controller_.Vq_setpoint_ = NAN; + open_loop_controller_.phase_ = 0.0f; + open_loop_controller_.phase_vel_ = NAN; - // Function of states to check if we are done - auto spin_done = [&](bool vel_override = false) -> bool { - bool done = false; - if (lockin_config.finish_on_vel || vel_override) - done = done || std::abs(vel) >= std::abs(lockin_config.vel); - if (lockin_config.finish_on_distance) - done = done || std::abs(distance) >= std::abs(lockin_config.finish_distance); - if (lockin_config.finish_on_enc_idx) - done = done || encoder_.index_found_; - return done; - }; + open_loop_controller_.max_current_ramp_ = lockin_config.current / lockin_config.ramp_time; + open_loop_controller_.max_voltage_ramp_ = lockin_config.current / lockin_config.ramp_time; + open_loop_controller_.max_phase_vel_ramp_ = lockin_config.accel; + open_loop_controller_.target_current_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? lockin_config.current : 0.0f; + open_loop_controller_.target_voltage_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : lockin_config.current; + open_loop_controller_.target_vel_ = lockin_config.vel; + open_loop_controller_.total_distance_ = 0.0f; - // Accelerate - lockin_state_ = LOCKIN_STATE_ACCELERATE; - run_control_loop([&]() { - vel += lockin_config.accel * current_meas_period; - distance += vel * current_meas_period; - phase = wrap_pm_pi(phase + vel * current_meas_period); + motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL; + motor_.current_control_.Id_setpoint_src_ = &open_loop_controller_.Id_setpoint_; + motor_.current_control_.Iq_setpoint_src_ = &open_loop_controller_.Iq_setpoint_; + motor_.current_control_.Vd_setpoint_src_ = &open_loop_controller_.Vd_setpoint_; + motor_.current_control_.Vq_setpoint_src_ = &open_loop_controller_.Vq_setpoint_; + motor_.current_control_.phase_src_ = + async_estimator_.rotor_phase_src_ = + &open_loop_controller_.phase_; + motor_.phase_vel_src_ = + motor_.current_control_.phase_vel_src_ = + async_estimator_.rotor_phase_vel_src_ = + &open_loop_controller_.phase_vel_; + } + wait_for_control_iteration(); - if (!motor_.update(lockin_config.current, phase, vel)) - return false; - return !spin_done(true); //vel_override to go to next phase - }); + motor_.arm(&motor_.current_control_); - if (!encoder_.index_found_) - encoder_.set_idx_subscribe(true); + bool did_subscribe_to_idx = false; + bool success = false; + float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f; - // Constant speed - if (!spin_done()) { - lockin_state_ = LOCKIN_STATE_CONST_VEL; - vel = lockin_config.vel; // reset to actual specified vel to avoid small integration error - run_control_loop([&]() { - distance += vel * current_meas_period; - phase = wrap_pm_pi(phase + vel * current_meas_period); + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) { + bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_ - lockin_config.vel) <= std::numeric_limits::epsilon(); + bool reached_target_dist = open_loop_controller_.total_distance_ * dir >= lockin_config.finish_distance * dir; - if (!motor_.update(lockin_config.current, phase, vel)) - return false; - return !spin_done(); - }); + // Check if terminal condition is reached + bool terminal_condition = (reached_target_vel && lockin_config.finish_on_vel) + || (reached_target_dist && lockin_config.finish_on_distance) + || (encoder_.index_found_ && lockin_config.finish_on_enc_idx); + if (terminal_condition) { + success = true; + break; + } + + // Activate index pin as soon as target velocity was reached. This is + // to avoid hitting the index from the wrong direction. + if (reached_target_vel && !encoder_.index_found_ && !did_subscribe_to_idx) { + encoder_.set_idx_subscribe(true); + did_subscribe_to_idx = true; + } + + osDelay(1); } - lockin_state_ = LOCKIN_STATE_INACTIVE; - return check_for_errors(); + motor_.disarm(); + + return success; } -// Note run_sensorless_control_loop and run_closed_loop_control_loop are very similar and differ only in where we get the estimate from. -bool Axis::run_sensorless_control_loop() { - controller_.pos_estimate_linear_src_ = nullptr; - controller_.pos_estimate_circular_src_ = nullptr; - controller_.pos_estimate_valid_src_ = nullptr; - controller_.vel_estimate_src_ = &sensorless_estimator_.vel_estimate_; - controller_.vel_estimate_valid_src_ = &sensorless_estimator_.vel_estimate_valid_; - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; - if (!motor_.update(torque_setpoint, sensorless_estimator_.phase_, sensorless_estimator_.vel_estimate_)) - return false; // set_error should update axis.error_ - return true; - }); +bool Axis::start_closed_loop_control() { + bool sensorless_mode = config_.enable_sensorless_mode; + + if (sensorless_mode) { + // TODO: restart if desired + if (!run_lockin_spin(config_.sensorless_ramp)) { + return false; + } + } + + // Hook up the data paths between the components + CRITICAL_SECTION() { + if (sensorless_mode) { + controller_.pos_estimate_linear_src_ = nullptr; + controller_.pos_estimate_circular_src_ = nullptr; + controller_.pos_wrap_src_ = nullptr; + controller_.vel_estimate_src_ = &sensorless_estimator_.vel_estimate_; + } else if (controller_.config_.load_encoder_axis < AXIS_COUNT) { + Axis* ax = &axes[controller_.config_.load_encoder_axis]; + controller_.pos_estimate_circular_src_ = &ax->encoder_.pos_circular_; + controller_.pos_wrap_src_ = &controller_.config_.circular_setpoint_range; + controller_.pos_estimate_linear_src_ = &ax->encoder_.pos_estimate_; + controller_.vel_estimate_src_ = &ax->encoder_.vel_estimate_; + } else { + controller_.pos_estimate_circular_src_ = nullptr; + controller_.pos_estimate_linear_src_ = nullptr; + controller_.pos_wrap_src_ = nullptr; + controller_.vel_estimate_src_ = nullptr; + controller_.set_error(Controller::ERROR_INVALID_LOAD_ENCODER); + return false; + } + + // To avoid any transient on startup, we intialize the setpoint to be the current position + // note - input_pos_ is not set here. It is set to 0 earlier in this method and velocity control is used. + if (controller_.config_.control_mode >= Controller::CONTROL_MODE_POSITION_CONTROL) { + float* pos_init_src = controller_.config_.circular_setpoints ? + controller_.pos_estimate_circular_src_ : + controller_.pos_estimate_linear_src_; + if (!pos_init_src) { + return false; + } else { + controller_.pos_setpoint_ = *pos_init_src; + controller_.input_pos_ = *pos_init_src; + } + } + controller_.input_pos_updated(); + + // Avoid integrator windup issues + controller_.vel_integrator_torque_ = 0.0f; + + motor_.torque_setpoint_src_ = &controller_.torque_output_; + motor_.direction_ = sensorless_mode ? 1.0f : encoder_.config_.direction; + + motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL; + motor_.current_control_.Id_setpoint_src_ = &motor_.Id_setpoint_; + motor_.current_control_.Iq_setpoint_src_ = &motor_.Iq_setpoint_; + motor_.current_control_.Vd_setpoint_src_ = &motor_.Vd_setpoint_; + motor_.current_control_.Vq_setpoint_src_ = &motor_.Vq_setpoint_; + motor_.current_control_.phase_src_ = + async_estimator_.rotor_phase_src_ = + sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_; + motor_.phase_vel_src_ = + motor_.current_control_.phase_vel_src_ = + async_estimator_.rotor_phase_vel_src_ = + sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_; + } + wait_for_control_iteration(); + + motor_.arm(&motor_.current_control_); + + if (sensorless_mode) { + // call to controller.reset() that happend when arming means that vel_setpoint + // is zeroed. So we make the setpoint the spinup target for smooth transition. + controller_.input_vel_ = config_.sensorless_ramp.vel / (2 * M_PI); + controller_.vel_setpoint_ = config_.sensorless_ramp.vel / (2 * M_PI); + } + + return true; +} + +bool Axis::stop_closed_loop_control() { + motor_.disarm(); return check_for_errors(); } bool Axis::run_closed_loop_control_loop() { - if (!controller_.select_encoder(controller_.config_.load_encoder_axis)) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - - // To avoid any transient on startup, we intialize the setpoint to be the current position - if (controller_.config_.circular_setpoints) { - if (!controller_.pos_estimate_circular_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_circular_src_; - controller_.input_pos_ = *controller_.pos_estimate_circular_src_; - } - } - else { - if (!controller_.pos_estimate_linear_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_; - controller_.input_pos_ = *controller_.pos_estimate_linear_src_; - } - } - controller_.input_pos_updated(); - - // Avoid integrator windup issues - controller_.vel_integrator_torque_ = 0.0f; - + start_closed_loop_control(); set_step_dir_active(config_.enable_step_dir); - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; - float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; - if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) - return false; // set_error should update axis.error_ + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) { + osDelay(1); + } - return true; - }); set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); + stop_closed_loop_control(); + return check_for_errors(); } @@ -381,44 +386,14 @@ bool Axis::run_homing() { homing_.is_homed = false; - if (!controller_.select_encoder(controller_.config_.load_encoder_axis)) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - - // To avoid any transient on startup, we intialize the setpoint to be the current position - // note - input_pos_ is not set here. It is set to 0 earlier in this method and velocity control is used. - if (controller_.config_.circular_setpoints) { - if (!controller_.pos_estimate_circular_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_circular_src_; - } - } - else { - if (!controller_.pos_estimate_linear_src_) { - return error_ |= ERROR_CONTROLLER_FAILED, false; - } - else { - controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_; - } + start_closed_loop_control(); + + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_ && !min_endstop_.get_state()) { + osDelay(1); } - // Avoid integrator windup issues - controller_.vel_integrator_torque_ = 0.0f; + stop_closed_loop_control(); - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; - - float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; - if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) - return false; // set_error should update axis.error_ - - return !min_endstop_.get_state(); - }); error_ &= ~ERROR_MIN_ENDSTOP_PRESSED; // clear this error since we deliberately drove into the endstop // pos_setpoint is the starting position for the trap_traj so we need to set it. @@ -436,18 +411,13 @@ bool Axis::run_homing() { controller_.input_vel_ = 0.0f; controller_.input_torque_ = 0.0f; - run_control_loop([this](){ - // Note that all estimators are updated in the loop prefix in run_control_loop - float torque_setpoint; - if (!controller_.update(&torque_setpoint)) - return error_ |= ERROR_CONTROLLER_FAILED, false; + start_closed_loop_control(); - float phase_vel = (2*M_PI) * encoder_.vel_estimate_ * motor_.config_.pole_pairs; - if (!motor_.update(torque_setpoint, encoder_.phase_, phase_vel)) - return false; // set_error should update axis.error_ + while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_ && !controller_.trajectory_done_) { + osDelay(1); + } - return !controller_.trajectory_done_; - }); + stop_closed_loop_control(); controller_.config_.control_mode = stored_control_mode; controller_.config_.input_mode = stored_input_mode; @@ -457,21 +427,29 @@ bool Axis::run_homing() { } bool Axis::run_idle_loop() { - // run_control_loop ignores missed modulation timing updates - // if and only if we're in AXIS_STATE_IDLE - safety_critical_disarm_motor_pwm(motor_); set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); - run_control_loop([this]() { - return true; - }); + while (requested_state_ == AXIS_STATE_UNDEFINED) { + motor_.setup(); + osDelay(1); + } return check_for_errors(); } // Infinite loop that does calibration and enters main control loop as appropriate void Axis::run_state_machine_loop() { - // arm! - motor_.arm(); + // Wait for up to 2s for motor to become ready to allow for error-free + // startup. This delay gives the current sensor calibration time to + // converge. If the DRV chip is unpowered, the motor will not become ready + // but we still enter idle state. + for (size_t i = 0; i < 2000; ++i) { + bool motor_is_ready = std::isnan(motor_.current_meas_.phA) + && std::isnan(motor_.current_meas_.phB) + && std::isnan(motor_.current_meas_.phC); + if (motor_is_ready) { + break; + } + } for (;;) { // Load the task chain if a specific request is pending @@ -488,8 +466,6 @@ void Axis::run_state_machine_loop() { task_chain_[pos++] = AXIS_STATE_HOMING; if (config_.startup_closed_loop_control) task_chain_[pos++] = AXIS_STATE_CLOSED_LOOP_CONTROL; - else if (config_.startup_sensorless_control) - task_chain_[pos++] = AXIS_STATE_SENSORLESS_CONTROL; task_chain_[pos++] = AXIS_STATE_IDLE; } else if (requested_state_ == AXIS_STATE_FULL_CALIBRATION_SEQUENCE) { task_chain_[pos++] = AXIS_STATE_MOTOR_CALIBRATION; @@ -520,8 +496,6 @@ void Axis::run_state_machine_loop() { case AXIS_STATE_ENCODER_INDEX_SEARCH: { if (!motor_.is_calibrated_) goto invalid_state_label; - if (encoder_.config_.idx_search_unidirectional && motor_.config_.direction==0) - goto invalid_state_label; status = encoder_.run_index_search(); } break; @@ -544,27 +518,13 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_LOCKIN_SPIN: { - if (!motor_.is_calibrated_ || motor_.config_.direction==0) + if (!motor_.is_calibrated_ || encoder_.config_.direction==0) goto invalid_state_label; status = run_lockin_spin(config_.general_lockin); } break; - case AXIS_STATE_SENSORLESS_CONTROL: { - if (!motor_.is_calibrated_ || motor_.config_.direction==0) - goto invalid_state_label; - status = run_lockin_spin(config_.sensorless_ramp); // TODO: restart if desired - if (status) { - // call to controller.reset() that happend when arming means that vel_setpoint - // is zeroed. So we make the setpoint the spinup target for smooth transition. - controller_.vel_setpoint_ = config_.sensorless_ramp.vel; - status = run_sensorless_control_loop(); - } - } break; - case AXIS_STATE_CLOSED_LOOP_CONTROL: { - if (!motor_.is_calibrated_ || motor_.config_.direction==0) - goto invalid_state_label; - if (!encoder_.is_ready_) + if (!motor_.is_calibrated_ || (encoder_.config_.direction==0 && !config_.enable_sensorless_mode)) goto invalid_state_label; watchdog_feed(); status = run_closed_loop_control_loop(); @@ -572,7 +532,7 @@ void Axis::run_state_machine_loop() { case AXIS_STATE_IDLE: { run_idle_loop(); - status = motor_.arm(); // done with idling - try to arm the motor + status = true; } break; default: diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index f9e4eb7d..3b46baa4 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -4,13 +4,15 @@ class Axis; #include "encoder.hpp" +#include "async_estimator.hpp" #include "sensorless_estimator.hpp" #include "controller.hpp" +#include "open_loop_controller.hpp" #include "trapTraj.hpp" #include "endstop.hpp" #include "low_level.h" #include "utils.hpp" -#include "communication/interface_uart.h" // TODO: remove once uart_poll() is gone +#include "task_timer.hpp" #include @@ -28,6 +30,22 @@ public: bool finish_on_enc_idx = false; }; + struct TaskTimes { + TaskTimer thermistor_update; + TaskTimer encoder_update; + TaskTimer sensorless_estimator_update; + TaskTimer endstop_update; + TaskTimer can_heartbeat; + TaskTimer controller_update; + TaskTimer open_loop_controller_update; + TaskTimer async_estimator_update; + TaskTimer motor_update; + TaskTimer current_controller_update; + TaskTimer dc_calib; + TaskTimer current_sense; + TaskTimer pwm_update; + }; + static LockinConfig_t default_calibration(); static LockinConfig_t default_sensorless(); static LockinConfig_t default_lockin(); @@ -38,7 +56,6 @@ public: // this only has an effect if encoder.config.use_index is also true bool startup_encoder_offset_calibration = false; // - void run_control_loop(const T& update_handler) { - while (requested_state_ == AXIS_STATE_UNDEFINED) { - // look for errors at axis level and also all subcomponents - bool checks_ok = do_checks(); - // Update all estimators - // Note: updates run even if checks fail - bool updates_ok = do_updates(); - - // make sure the watchdog is being fed. - bool watchdog_ok = watchdog_check(); - - if (!checks_ok || !updates_ok || !watchdog_ok) { - // It's not useful to quit idle since that is the safe action - // Also leaving idle would rearm the motors - if (current_state_ != AXIS_STATE_IDLE) - break; - } - - // Run main loop function, defer quitting for after wait - // TODO: change arming logic to arm after waiting - bool main_continue = update_handler(); - - if (axis_num_ == 0) { - uart_poll(); // TODO: move to board-level control loop once it exists - } - - // Check we meet deadlines after queueing - ++loop_counter_; - - // Wait until the current measurement interrupt fires - if (!wait_for_current_meas()) { - // maybe the interrupt handler is dead, let's be - // safe and float the phases - safety_critical_disarm_motor_pwm(motor_); - update_brake_current(); - error_ |= ERROR_CURRENT_MEASUREMENT_TIMEOUT; - break; - } - - if (!main_continue) - break; - } - } - + bool start_closed_loop_control(); + bool stop_closed_loop_control(); bool run_lockin_spin(const LockinConfig_t &lockin_config); - bool run_sensorless_control_loop(); bool run_closed_loop_control_loop(); bool run_homing(); bool run_idle_loop(); @@ -212,14 +150,17 @@ public: Config_t config_; Encoder& encoder_; + AsyncEstimator async_estimator_; SensorlessEstimator& sensorless_estimator_; Controller& controller_; + OpenLoopController open_loop_controller_; OnboardThermistorCurrentLimiter& fet_thermistor_; OffboardThermistorCurrentLimiter& motor_thermistor_; Motor& motor_; TrapezoidalTrajectory& trap_traj_; Endstop& min_endstop_; Endstop& max_endstop_; + TaskTimes task_times_; // List of current_limiters and thermistors to // provide easy iteration. @@ -242,7 +183,6 @@ public: std::array task_chain_ = { AXIS_STATE_UNDEFINED }; AxisState& current_state_ = task_chain_.front(); uint32_t loop_counter_ = 0; - LockinState lockin_state_ = LOCKIN_STATE_INACTIVE; Homing_t homing_; uint32_t last_heartbeat_ = 0; diff --git a/Firmware/MotorControl/component.hpp b/Firmware/MotorControl/component.hpp new file mode 100644 index 00000000..cfaeb828 --- /dev/null +++ b/Firmware/MotorControl/component.hpp @@ -0,0 +1,20 @@ +#ifndef __COMPONENT_HPP +#define __COMPONENT_HPP + +#include + +class ComponentBase { +public: + /** + * @brief Shall run the update action of this component. + * + * This function gets called in a low priority interrupt context and is + * allowed to call CMSIS functions. + * + * @param timestamp: The timestamp (in HCLK ticks) for which this update + * is run. + */ + virtual void update(uint32_t timestamp) = 0; +}; + +#endif // __COMPONENT_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 1524dce6..a93acb51 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -19,7 +19,6 @@ void Controller::reset() { void Controller::set_error(Error error) { error_ |= error; - axis_->error_ |= Axis::ERROR_CONTROLLER_FAILED; } //-------------------------------- @@ -27,21 +26,6 @@ void Controller::set_error(Error error) { //-------------------------------- -bool Controller::select_encoder(size_t encoder_num) { - if (encoder_num < AXIS_COUNT) { - Axis* ax = &axes[encoder_num]; - pos_estimate_circular_src_ = &ax->encoder_.pos_circular_; - pos_wrap_src_ = &config_.circular_setpoint_range; - pos_estimate_linear_src_ = &ax->encoder_.pos_estimate_; - pos_estimate_valid_src_ = &ax->encoder_.pos_estimate_valid_; - vel_estimate_src_ = &ax->encoder_.vel_estimate_; - vel_estimate_valid_src_ = &ax->encoder_.vel_estimate_valid_; - return true; - } else { - return set_error(Controller::ERROR_INVALID_LOAD_ENCODER), false; - } -} - void Controller::move_to_pos(float goal_point) { axis_->trap_traj_.planTrapezoidal(goal_point, pos_setpoint_, vel_setpoint_, axis_->trap_traj_.config_.vel_limit, @@ -114,18 +98,19 @@ static float limitVel(const float vel_limit, const float vel_estimate, const flo return std::clamp(torque, Tmin, Tmax); } -bool Controller::update(float* torque_setpoint_output) { - float* pos_estimate_linear = (pos_estimate_valid_src_ && *pos_estimate_valid_src_) - ? pos_estimate_linear_src_ : nullptr; - float* pos_estimate_circular = (pos_estimate_valid_src_ && *pos_estimate_valid_src_) - ? pos_estimate_circular_src_ : nullptr; - float* vel_estimate_src = (vel_estimate_valid_src_ && *vel_estimate_valid_src_) - ? vel_estimate_src_ : nullptr; +bool Controller::update() { + float pos_estimate_linear = pos_estimate_linear_src_ ? *pos_estimate_linear_src_ : NAN; + float pos_estimate_circular = pos_estimate_circular_src_ ? *pos_estimate_circular_src_ : NAN; + float pos_wrap = pos_wrap_src_ ? *pos_wrap_src_ : NAN; + float vel_estimate = vel_estimate_src_ ? *vel_estimate_src_ : NAN; + + // Reset output just in case the controller fails for any reason + torque_output_ = NAN; // Calib_anticogging is only true when calibration is occurring, so we can't block anticogging_pos float anticogging_pos = axis_->encoder_.pos_estimate_ / axis_->encoder_.getCoggingRatio(); if (config_.anticogging.calib_anticogging) { - if (!axis_->encoder_.pos_estimate_valid_ || !axis_->encoder_.vel_estimate_valid_) { + if (std::isnan(axis_->encoder_.pos_estimate_) || std::isnan(axis_->encoder_.vel_estimate_)) { set_error(ERROR_INVALID_ESTIMATE); return false; } @@ -225,21 +210,21 @@ bool Controller::update(float* torque_setpoint_output) { float pos_err; if (config_.circular_setpoints) { - if(!pos_estimate_circular) { + if (std::isnan(pos_estimate_circular) || std::isnan(pos_wrap)) { set_error(ERROR_INVALID_ESTIMATE); return false; } // Keep pos setpoint from drifting pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap_src_); // Circular delta - pos_err = pos_setpoint_ - *pos_estimate_circular; - pos_err = wrap_pm(pos_err, 0.5f * *pos_wrap_src_); + pos_err = pos_setpoint_ - pos_estimate_circular; + pos_err = wrap_pm(pos_err, 0.5f * pos_wrap); } else { - if(!pos_estimate_linear) { + if (std::isnan(pos_estimate_linear)) { set_error(ERROR_INVALID_ESTIMATE); return false; } - pos_err = pos_setpoint_ - *pos_estimate_linear; + pos_err = pos_setpoint_ - pos_estimate_linear; } vel_des += config_.pos_gain * pos_err; @@ -258,11 +243,11 @@ bool Controller::update(float* torque_setpoint_output) { // Check for overspeed fault (done in this module (controller) for cohesion with vel_lim) if (config_.enable_overspeed_error) { // 0.0f to disable - if (!vel_estimate_src) { + if (std::isnan(vel_estimate)) { set_error(ERROR_INVALID_ESTIMATE); return false; } - if (std::abs(*vel_estimate_src) > config_.vel_limit_tolerance * vel_lim) { + if (std::abs(vel_estimate) > config_.vel_limit_tolerance * vel_lim) { set_error(ERROR_OVERSPEED); return false; } @@ -273,7 +258,7 @@ bool Controller::update(float* torque_setpoint_output) { float vel_gain = config_.vel_gain; float vel_integrator_gain = config_.vel_integrator_gain; if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - float effective_flux = axis_->motor_.current_control_.acim_rotor_flux; + float effective_flux = axis_->async_estimator_.rotor_flux_; float minflux = axis_->motor_.config_.acim_gain_min_flux; if (fabsf(effective_flux) < minflux) effective_flux = std::copysignf(minflux, effective_flux); @@ -295,12 +280,12 @@ bool Controller::update(float* torque_setpoint_output) { float v_err = 0.0f; if (config_.control_mode >= CONTROL_MODE_VELOCITY_CONTROL) { - if (!vel_estimate_src) { + if (std::isnan(vel_estimate)) { set_error(ERROR_INVALID_ESTIMATE); return false; } - v_err = vel_des - *vel_estimate_src; + v_err = vel_des - vel_estimate; torque += (vel_gain * gain_scheduling_multiplier) * v_err; // Velocity integral action before limiting @@ -309,11 +294,11 @@ bool Controller::update(float* torque_setpoint_output) { // Velocity limiting in current mode if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL && config_.enable_current_mode_vel_limit) { - if (!vel_estimate_src) { + if (std::isnan(vel_estimate)) { set_error(ERROR_INVALID_ESTIMATE); return false; } - torque = limitVel(config_.vel_limit, *vel_estimate_src, vel_gain, torque); + torque = limitVel(config_.vel_limit, vel_estimate, vel_gain, torque); } // Torque limiting @@ -341,6 +326,12 @@ bool Controller::update(float* torque_setpoint_output) { } } - if (torque_setpoint_output) *torque_setpoint_output = torque; + torque_output_ = torque; + + // TODO: this is inconsistent with the other errors which are sticky. + // However if we make ERROR_INVALID_ESTIMATE sticky then it will be + // confusing that a normal sequence of motor calibration + encoder + // calibration would leave the controller in an error state. + error_ &= ~ERROR_INVALID_ESTIMATE; return true; } diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index f1009f6e..483dc316 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -38,7 +38,7 @@ public: bool enable_current_mode_vel_limit = true; // enable velocity limit in current control mode (requires a valid velocity estimator) uint8_t axis_to_mirror = -1; float mirror_ratio = 1.0f; - uint8_t load_encoder_axis = -1; // default depends on Axis number and is set in load_configuration() + uint8_t load_encoder_axis = -1; // default depends on Axis number and is set in load_configuration(). Set to -1 to select sensorless estimator. // custom setters Controller* parent; @@ -67,21 +67,19 @@ public: bool anticogging_calibration(float pos_estimate, float vel_estimate); void update_filter_gains(); - bool update(float* torque_setpoint); + bool update(); Config_t config_; Axis* axis_ = nullptr; // set by Axis constructor Error error_ = ERROR_NONE; + // Inputs float* pos_estimate_linear_src_ = nullptr; float* pos_estimate_circular_src_ = nullptr; - bool* pos_estimate_valid_src_ = nullptr; float* vel_estimate_src_ = nullptr; - bool* vel_estimate_valid_src_ = nullptr; float* pos_wrap_src_ = nullptr; - float pos_setpoint_ = 0.0f; // [turns] float vel_setpoint_ = 0.0f; // [turn/s] // float vel_setpoint = 800.0f; @@ -100,6 +98,9 @@ public: bool anticogging_valid_ = false; + // Outputs + float torque_output_ = NAN; + // custom setters void set_input_pos(float value) { input_pos_ = value; input_pos_updated(); } diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 26c7c7d9..f4899cc6 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -64,7 +64,6 @@ void Encoder::set_error(Error error) { vel_estimate_valid_ = false; pos_estimate_valid_ = false; error_ |= error; - axis_->error_ |= Axis::ERROR_ENCODER_FAILED; } bool Encoder::do_checks(){ @@ -166,9 +165,6 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) { bool Encoder::run_index_search() { config_.use_index = true; index_found_ = false; - if (!config_.idx_search_unidirectional && axis_->motor_.config_.direction == 0) { - axis_->motor_.config_.direction = 1; - } set_idx_subscribe(); bool status = axis_->run_lockin_spin(axis_->config_.calibration_lockin); @@ -177,7 +173,6 @@ bool Encoder::run_index_search() { bool Encoder::run_direction_find() { int32_t init_enc_val = shadow_count_; - axis_->motor_.config_.direction = 1; // Must test spin forwards for direction detect logic Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds @@ -190,12 +185,12 @@ bool Encoder::run_direction_find() { // Check response and direction if (shadow_count_ > init_enc_val + 8) { // motor same dir as encoder - axis_->motor_.config_.direction = 1; + config_.direction = 1; } else if (shadow_count_ < init_enc_val - 8) { // motor opposite dir as encoder - axis_->motor_.config_.direction = -1; + config_.direction = -1; } else { - axis_->motor_.config_.direction = 0; + config_.direction = 0; } } @@ -205,10 +200,8 @@ bool Encoder::run_direction_find() { // @brief Turns the motor in one direction for a bit and then in the other // direction in order to find the offset between the electrical phase 0 // and the encoder state 0. -// TODO: Do the scan with current, not voltage! bool Encoder::run_offset_calibration() { const float start_lock_duration = 1.0f; - const int num_steps = (int)(config_.calib_scan_distance / config_.calib_scan_omega * (float)current_meas_hz); // Require index found if enabled if (config_.use_index && !index_found_) { @@ -220,55 +213,85 @@ bool Encoder::run_offset_calibration() { // Therefore we have to sync them for calibration shadow_count_ = count_in_cpr_; - float voltage_magnitude; - if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_HIGH_CURRENT) - voltage_magnitude = axis_->motor_.config_.calibration_current * axis_->motor_.config_.phase_resistance; - else if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) - voltage_magnitude = axis_->motor_.config_.calibration_current; - else - return false; + CRITICAL_SECTION() { + // Reset state variables + axis_->open_loop_controller_.Id_setpoint_ = NAN; + axis_->open_loop_controller_.Iq_setpoint_ = NAN; + axis_->open_loop_controller_.Vd_setpoint_ = NAN; + axis_->open_loop_controller_.Vq_setpoint_ = NAN; + axis_->open_loop_controller_.phase_ = 0.0f; + axis_->open_loop_controller_.phase_vel_ = NAN; + + float max_current_ramp = axis_->motor_.config_.calibration_current / start_lock_duration * 2.0f; + axis_->open_loop_controller_.max_current_ramp_ = max_current_ramp; + axis_->open_loop_controller_.max_voltage_ramp_ = max_current_ramp; + axis_->open_loop_controller_.max_phase_vel_ramp_ = INFINITY; + axis_->open_loop_controller_.target_current_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? axis_->motor_.config_.calibration_current : 0.0f; + axis_->open_loop_controller_.target_voltage_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : axis_->motor_.config_.calibration_current; + axis_->open_loop_controller_.target_vel_ = 0.0f; + axis_->open_loop_controller_.total_distance_ = 0.0f; + + axis_->motor_.current_control_.enable_current_control_src_ = (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL); + axis_->motor_.current_control_.Id_setpoint_src_ = &axis_->open_loop_controller_.Id_setpoint_; + axis_->motor_.current_control_.Iq_setpoint_src_ = &axis_->open_loop_controller_.Iq_setpoint_; + axis_->motor_.current_control_.Vd_setpoint_src_ = &axis_->open_loop_controller_.Vd_setpoint_; + axis_->motor_.current_control_.Vq_setpoint_src_ = &axis_->open_loop_controller_.Vq_setpoint_; + axis_->motor_.current_control_.phase_src_ = + axis_->async_estimator_.rotor_phase_src_ = + &axis_->open_loop_controller_.phase_; + axis_->motor_.phase_vel_src_ = + axis_->motor_.current_control_.phase_vel_src_ = + axis_->async_estimator_.rotor_phase_vel_src_ = + &axis_->open_loop_controller_.phase_vel_; + } + axis_->wait_for_control_iteration(); + + axis_->motor_.arm(&axis_->motor_.current_control_); // go to motor zero phase for start_lock_duration to get ready to scan - int i = 0; - axis_->run_control_loop([&](){ - if (!axis_->motor_.enqueue_voltage_timings(voltage_magnitude, 0.0f)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB); - return ++i < start_lock_duration * current_meas_hz; - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; + for (size_t i = 0; i < (size_t)(start_lock_duration * 1000.0f); ++i) { + if (!axis_->motor_.is_armed_) { + return false; // TODO: return "disarmed" error code + } + if (axis_->requested_state_ != Axis::AXIS_STATE_UNDEFINED) { + axis_->motor_.disarm(); + return false; // TODO: return "aborted" error code + } + osDelay(1); + } + int32_t init_enc_val = shadow_count_; + uint32_t num_steps = 0; int64_t encvaluesum = 0; - // scan forward - i = 0; - axis_->run_control_loop([&]() { - float phase = wrap_pm_pi(config_.calib_scan_distance * (float)i / (float)num_steps - config_.calib_scan_distance / 2.0f); - float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); - float v_beta = voltage_magnitude * our_arm_sin_f32(phase); - if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB); + CRITICAL_SECTION() { + axis_->open_loop_controller_.target_vel_ = config_.calib_scan_omega; + axis_->open_loop_controller_.total_distance_ = 0.0f; + } + // scan forward + while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { + bool reached_target_dist = axis_->open_loop_controller_.total_distance_ >= config_.calib_scan_distance; + if (reached_target_dist) { + break; + } encvaluesum += shadow_count_; - - return ++i < num_steps; - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; + num_steps++; + osDelay(1); + } // Check response and direction if (shadow_count_ > init_enc_val + 8) { // motor same dir as encoder - axis_->motor_.config_.direction = 1; + config_.direction = 1; } else if (shadow_count_ < init_enc_val - 8) { // motor opposite dir as encoder - axis_->motor_.config_.direction = -1; + config_.direction = -1; } else { // Encoder response error set_error(ERROR_NO_RESPONSE); + axis_->motor_.disarm(); return false; } @@ -279,25 +302,31 @@ bool Encoder::run_offset_calibration() { calib_scan_response_ = std::abs(shadow_count_ - init_enc_val); if (std::abs(calib_scan_response_ - expected_encoder_delta) / expected_encoder_delta > config_.calib_range) { set_error(ERROR_CPR_POLEPAIRS_MISMATCH); + axis_->motor_.disarm(); return false; } - // scan backwards - i = 0; - axis_->run_control_loop([&]() { - float phase = wrap_pm_pi(-config_.calib_scan_distance * (float)i / (float)num_steps + config_.calib_scan_distance / 2.0f); - float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); - float v_beta = voltage_magnitude * our_arm_sin_f32(phase); - if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(TIMING_LOG_ENC_CALIB); + CRITICAL_SECTION() { + axis_->open_loop_controller_.target_vel_ = -config_.calib_scan_omega; + } + // scan backwards + while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { + bool reached_target_dist = axis_->open_loop_controller_.total_distance_ <= 0.0f; + if (reached_target_dist) { + break; + } encvaluesum += shadow_count_; - - return ++i < num_steps; - }); - if (axis_->error_ != Axis::ERROR_NONE) + num_steps++; + osDelay(1); + } + + // Motor disarmed because of an error + if (!axis_->motor_.is_armed_) { return false; + } + + axis_->motor_.disarm(); config_.offset = encvaluesum / (num_steps * 2); int32_t residual = encvaluesum - ((int64_t)config_.offset * (int64_t)(num_steps * 2)); @@ -339,7 +368,7 @@ void Encoder::sample_now() { case MODE_SPI_ABS_AEAT: case MODE_SPI_ABS_RLS: { - axis_->motor_.log_timing(TIMING_LOG_SAMPLE_NOW); + abs_spi_start_transaction(); // Do nothing } break; @@ -368,10 +397,8 @@ void Encoder::decode_hall_samples() { | (read_sampled_gpio(hallC_gpio_) ? 4 : 0); } -bool Encoder::abs_spi_start_transaction(){ +bool Encoder::abs_spi_start_transaction() { if (mode_ & MODE_FLAG_ABS){ - axis_->motor_.log_timing(TIMING_LOG_SPI_START); - if (Stm32SpiArbiter::acquire_task(&spi_task_)) { spi_task_.ncs_gpio = abs_spi_cs_gpio_; spi_task_.tx_buf = (uint8_t*)abs_spi_dma_tx_; @@ -411,8 +438,6 @@ void Encoder::abs_spi_cb(bool success) { goto done; } - axis_->motor_.log_timing(TIMING_LOG_SPI_END); - switch (mode_) { case MODE_SPI_ABS_AMS: { uint16_t rawVal = abs_spi_dma_rx_[0]; @@ -476,6 +501,7 @@ bool Encoder::update() { } break; case MODE_HALL: { + decode_hall_samples(); int32_t hall_cnt; if (decode_hall(hall_state_, &hall_cnt)) { delta_enc = hall_cnt - count_in_cpr_; @@ -485,6 +511,11 @@ bool Encoder::update() { } else { if (!config_.ignore_illegal_hall_state) { set_error(ERROR_ILLEGAL_HALL_STATE); + pos_estimate_ = NAN; + pos_cpr_ = NAN; + vel_estimate_ = NAN; + phase_ = NAN; + phase_vel_ = NAN; return false; } } @@ -508,8 +539,15 @@ bool Encoder::update() { if (abs_spi_pos_updated_ == false) { // Low pass filter the error spi_error_rate_ += current_meas_period * (1.0f - spi_error_rate_); - if (spi_error_rate_ > 0.005f) + if (spi_error_rate_ > 0.005f) { set_error(ERROR_ABS_SPI_COM_FAIL); + pos_estimate_ = NAN; + pos_cpr_ = NAN; + vel_estimate_ = NAN; + phase_ = NAN; + phase_vel_ = NAN; + return false; + } } else { // Low pass filter the error spi_error_rate_ += current_meas_period * (0.0f - spi_error_rate_); @@ -524,7 +562,12 @@ bool Encoder::update() { }break; default: { - set_error(ERROR_UNSUPPORTED_ENCODER_MODE); + set_error(ERROR_UNSUPPORTED_ENCODER_MODE); + pos_estimate_ = NAN; + pos_cpr_ = NAN; + vel_estimate_ = NAN; + phase_ = NAN; + phase_vel_ = NAN; return false; } break; } @@ -589,9 +632,13 @@ bool Encoder::update() { float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); float ph = elec_rad_per_enc * (interpolated_enc - config_.offset_float); // ph = fmodf(ph, 2*M_PI); - phase_ = wrap_pm_pi(ph); + if (is_ready_) { + phase_ = wrap_pm_pi(ph) * config_.direction; + phase_vel_ = (2*M_PI) * vel_estimate_ * axis_->motor_.config_.pole_pairs * config_.direction; + } else { + phase_ = NAN; + phase_vel_ = NAN; + } - vel_estimate_valid_ = true; - pos_estimate_valid_ = true; return true; } diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index a3810f21..ec2e8f7a 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -22,13 +22,13 @@ public: int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, int32_t offset = 0; // Offset between encoder count and rotor electrical phase float offset_float = 0.0f; // Sub-count phase alignment offset + int32_t direction = 0.0f; // direction with respect to motor bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state float calib_range = 0.02f; // Accuracy required to pass encoder cpr check float calib_scan_distance = 16.0f * M_PI; // rad electrical float calib_scan_omega = 4.0f * M_PI; // rad/s electrical float bandwidth = 1000.0f; bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state - bool idx_search_unidirectional = false; // Only allow index search in known direction bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; @@ -85,7 +85,8 @@ public: int32_t shadow_count_ = 0; int32_t count_in_cpr_ = 0; float interpolation_ = 0.0f; - float phase_ = 0.0f; // [count] + float phase_ = 0.0f; // [rad] + float phase_vel_ = 0.0f; // [rad/s] float pos_estimate_counts_ = 0.0f; // [count] float pos_cpr_counts_ = 0.0f; // [count] float vel_estimate_counts_ = 0.0f; // [count/s] diff --git a/Firmware/MotorControl/foc.cpp b/Firmware/MotorControl/foc.cpp new file mode 100644 index 00000000..a1b2d3df --- /dev/null +++ b/Firmware/MotorControl/foc.cpp @@ -0,0 +1,160 @@ + +#include "foc.hpp" +#include + +Motor::Error AlphaBetaFrameController::on_measurement( + float vbus_voltage, std::array currents, + uint32_t input_timestamp) { + // Clarke transform + float Ialpha = currents[0]; + float Ibeta = one_by_sqrt3 * (currents[1] - currents[2]); + return on_measurement(vbus_voltage, Ialpha, Ibeta, input_timestamp); +} + +Motor::Error AlphaBetaFrameController::get_output( + uint32_t output_timestamp, float (&pwm_timings)[3], float* ibus) { + float mod_alpha = NAN; + float mod_beta = NAN; + + Motor::Error status = get_alpha_beta_output(output_timestamp, &mod_alpha, &mod_beta, ibus); + + if (status != Motor::ERROR_NONE) { + return status; + } else if (std::isnan(mod_alpha) || std::isnan(mod_alpha)) { + return Motor::ERROR_MODULATION_IS_NAN; + } else if (SVM(mod_alpha, mod_beta, &pwm_timings[0], &pwm_timings[1], &pwm_timings[2]) != 0) { + return Motor::ERROR_MODULATION_MAGNITUDE; + } + + return Motor::ERROR_NONE; +} + +void FieldOrientedController::reset() { + v_current_control_integral_d_ = 0.0f; + v_current_control_integral_q_ = 0.0f; + vbus_voltage_measured_ = NAN; + Ialpha_measured_ = NAN; + Ibeta_measured_ = NAN; +} + +Motor::Error FieldOrientedController::on_measurement( + float vbus_voltage, float Ialpha, float Ibeta, + uint32_t input_timestamp) { + // Store the measurements for later processing. + i_timestamp_ = input_timestamp; + vbus_voltage_measured_ = vbus_voltage; + Ialpha_measured_ = Ialpha; + Ibeta_measured_ = Ibeta; + + return Motor::ERROR_NONE; +} + +ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output( + uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) { + + if (std::isnan(vbus_voltage_measured_) || std::isnan(Ialpha_measured_) || std::isnan(Ibeta_measured_)) { + // FOC didn't receive a current measurement yet. + return Motor::ERROR_CONTROLLER_INITIALIZING; + } else if (abs((int32_t)(i_timestamp_ - ctrl_timestamp_)) > MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA) { + // Data from control loop and current measurement are too far apart. + return Motor::ERROR_BAD_TIMING; + } + + // TODO: improve efficiency in case PWM updates are requested at a higher + // rate than current sensor updates. In this case we can reuse mod_d and + // mod_q from a previous iteration. + + // Fetch member variables into local variables to make the optimizer's life easier. + float vbus_voltage = vbus_voltage_measured_; + float Ialpha = Ialpha_measured_; + float Ibeta = Ibeta_measured_; + float Vd = Vd_setpoint_; + float Vq = Vq_setpoint_; + float Id_setpoint = Id_setpoint_; + float Iq_setpoint = Iq_setpoint_; + float phase = phase_; + float phase_vel = phase_vel_; + + if (std::isnan(phase) || std::isnan(phase_vel)) { + return Motor::ERROR_UNKNOWN_PHASE; + } + + // Park transform + float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); + float c_I = our_arm_cos_f32(I_phase); + float s_I = our_arm_sin_f32(I_phase); + float Id = c_I * Ialpha + s_I * Ibeta; + float Iq = c_I * Ibeta - s_I * Ialpha; + Iq_measured_ += I_measured_report_filter_k_ * (Iq - Iq_measured_); + Id_measured_ += I_measured_report_filter_k_ * (Id - Id_measured_); + + // Current error + float Ierr_d = Id_setpoint - Id; + float Ierr_q = Iq_setpoint - Iq; + + + if (enable_current_control_) { + // Check for current sense saturation + if (std::isnan(Ierr_d) || std::isnan(Ierr_q)) { + return Motor::ERROR_UNKNOWN_CURRENT; + } + + // Apply PI control (V{d,q}_setpoint act as feed-forward terms in this mode) + Vd += v_current_control_integral_d_ + Ierr_d * p_gain_; + Vq += v_current_control_integral_q_ + Ierr_q * p_gain_; + } + + if (std::isnan(vbus_voltage)) { + return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE; + } + + float mod_to_V = (2.0f / 3.0f) * vbus_voltage; + float V_to_mod = 1.0f / mod_to_V; + float mod_d = V_to_mod * Vd; + float mod_q = V_to_mod * Vq; + + if (enable_current_control_) { + // Vector modulation saturation, lock integrator if saturated + // TODO make maximum modulation configurable + float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / sqrtf(mod_d * mod_d + mod_q * mod_q); + if (mod_scalefactor < 1.0f) { + mod_d *= mod_scalefactor; + mod_q *= mod_scalefactor; + // TODO make decayfactor configurable + v_current_control_integral_d_ *= 0.99f; + v_current_control_integral_q_ *= 0.99f; + } else { + v_current_control_integral_d_ += Ierr_d * (i_gain_ * current_meas_period); + v_current_control_integral_q_ += Ierr_q * (i_gain_ * current_meas_period); + } + } + + // Inverse park transform + float pwm_phase = phase_ + phase_vel_ * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); + float c_p = our_arm_cos_f32(pwm_phase); + float s_p = our_arm_sin_f32(pwm_phase); + float mod_alpha_temp = c_p * mod_d - s_p * mod_q; + float mod_beta_temp = c_p * mod_q + s_p * mod_d; + + // Report final applied voltage in stationary frame (for sensorless estimator) + final_v_alpha_ = mod_to_V * mod_alpha_temp; + final_v_beta_ = mod_to_V * mod_beta_temp; + + *mod_alpha = mod_alpha_temp; + *mod_beta = mod_beta_temp; + *ibus = mod_d * Id + mod_q * Iq; + return Motor::ERROR_NONE; +} + +void FieldOrientedController::update(uint32_t timestamp) { + CRITICAL_SECTION() { + ctrl_timestamp_ = timestamp; + enable_current_control_ = enable_current_control_src_; + Id_setpoint_ = Id_setpoint_src_ ? *Id_setpoint_src_ : NAN; + Iq_setpoint_ = Iq_setpoint_src_ ? *Iq_setpoint_src_ : NAN; + Vd_setpoint_ = Vd_setpoint_src_ ? *Vd_setpoint_src_ : NAN; + Vq_setpoint_ = Vq_setpoint_src_ ? *Vq_setpoint_src_ : NAN; + phase_ = phase_src_ ? *phase_src_ : NAN; + phase_vel_ = phase_vel_src_ ? *phase_vel_src_ : NAN; + } +} diff --git a/Firmware/MotorControl/foc.hpp b/Firmware/MotorControl/foc.hpp new file mode 100644 index 00000000..ba2b3ec7 --- /dev/null +++ b/Firmware/MotorControl/foc.hpp @@ -0,0 +1,67 @@ +#ifndef __FOC_HPP +#define __FOC_HPP + +#include "phase_control_law.hpp" +#include "component.hpp" + +/** + * @brief Field oriented controller. + * + * This controller can run in either current control mode or voltage control + * mode. + */ +class FieldOrientedController : public AlphaBetaFrameController, public ComponentBase { +public: + void update(uint32_t timestamp) final; + + void reset() final; + + ODriveIntf::MotorIntf::Error on_measurement( + float vbus_voltage, float Ialpha, float Ibeta, uint32_t input_timestamp) final; + + ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) final; + + // Config - these values are set while this controller is inactive + float p_gain_ = NAN; // [V/A] should be auto set after resistance and inductance measurement + float i_gain_ = NAN; // [V/As] should be auto set after resistance and inductance measurement + float I_measured_report_filter_k_ = 1.0f; + + // Inputs + bool enable_current_control_src_ = false; + float* Id_setpoint_src_ = nullptr; + float* Iq_setpoint_src_ = nullptr; + float* Vd_setpoint_src_ = nullptr; + float* Vq_setpoint_src_ = nullptr; + float* phase_src_ = nullptr; + float* phase_vel_src_ = nullptr; + + // These values are set atomically by the update() function and read by the + // calculate() function in an interrupt context. + uint32_t ctrl_timestamp_; // [HCLK ticks] + bool enable_current_control_ = false; // true: FOC runs in current control mode using I{dq}_setpoint, false: FOC runs in voltage control mode using V{dq}_setpoint + float Id_setpoint_; // [A] only used if enable_current_control_ == true + float Iq_setpoint_; // [A] only used if enable_current_control_ == true + float Vd_setpoint_; // [V] acts as input if enable_current_control_ == false and as output otherwise + float Vq_setpoint_; // [V] acts as input if enable_current_control_ == false and as output otherwise + float phase_; // [rad] + float phase_vel_; // [rad/s] + + // These values (or some of them) are updated inside on_measurement() and get_alpha_beta_output() + uint32_t i_timestamp_; + float vbus_voltage_measured_ = NAN; // [V] + float Ialpha_measured_ = NAN; // [A] + float Ibeta_measured_ = NAN; // [A] + float Id_measured_ = 0.0f; // [A] + float Iq_measured_ = 0.0f; // [A] + float v_current_control_integral_d_ = 0.0f; // [V] + float v_current_control_integral_q_ = 0.0f; // [V] + //float mod_to_V_ = 0.0f; + //float mod_d_ = 0.0f; + //float mod_q_ = 0.0f; + //float ibus_ = 0.0f; + float final_v_alpha_ = 0.0f; // [V] + float final_v_beta_ = 0.0f; // [V] +}; + +#endif // __FOC_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index a40d2ac6..3fbad122 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -72,84 +72,14 @@ bool brake_resistor_saturated = false; * at a high rate. */ -// @brief Floats ALL phases immediately and disarms both motors and the brake resistor. -void low_level_fault(Motor::Error error) { - // Disable all motors NOW! - for (size_t i = 0; i < AXIS_COUNT; ++i) { - safety_critical_disarm_motor_pwm(axes[i].motor_); - axes[i].motor_.error_ |= error; - } - - safety_critical_disarm_brake_resistor(); -} - -// @brief Kicks off the arming process of the motor. -// All calls to this function must clearly originate -// from user input. -void safety_critical_arm_motor_pwm(Motor& motor) { - uint32_t mask = cpu_enter_critical(); - if (brake_resistor_armed) { - motor.armed_state_ = Motor::ARMED_STATE_WAITING_FOR_TIMINGS; - } - cpu_exit_critical(mask); -} - -// @brief Disarms the motor PWM. -// After calling this function, it is guaranteed that all three -// motor phases are floating and will not be enabled again until -// safety_critical_arm_motor_phases is called. -// @returns true if the motor was in a state other than disarmed before -bool safety_critical_disarm_motor_pwm(Motor& motor) { - uint32_t mask = cpu_enter_critical(); - bool was_armed = motor.armed_state_ != Motor::ARMED_STATE_DISARMED; - motor.armed_state_ = Motor::ARMED_STATE_DISARMED; - __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(motor.timer_); - cpu_exit_critical(mask); - return was_armed; -} - -// @brief Updates the phase timings unless the motor is disarmed. -// -// If this is called at a rate higher than the motor's timer period, -// the actual PMW timings on the pins can be undefined for up to one -// timer period. -void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3]) { - uint32_t mask = cpu_enter_critical(); - if (!brake_resistor_armed) { - motor.armed_state_ = Motor::ARMED_STATE_DISARMED; - } - - motor.timer_->Instance->CCR1 = timings[0]; - motor.timer_->Instance->CCR2 = timings[1]; - motor.timer_->Instance->CCR3 = timings[2]; - - if (motor.armed_state_ == Motor::ARMED_STATE_WAITING_FOR_TIMINGS) { - // timings were just loaded into the timer registers - // the timer register are buffered, so they won't have an effect - // on the output just yet so we need to wait until the next - // interrupt before we actually enable the output - motor.armed_state_ = Motor::ARMED_STATE_WAITING_FOR_UPDATE; - } else if (motor.armed_state_ == Motor::ARMED_STATE_WAITING_FOR_UPDATE) { - // now we waited long enough. Enter armed state and - // enable the actual PWM outputs. - motor.armed_state_ = Motor::ARMED_STATE_ARMED; - __HAL_TIM_MOE_ENABLE(motor.timer_); // enable pwm outputs - } else if (motor.armed_state_ == Motor::ARMED_STATE_ARMED) { - // nothing to do, PWM is running, all good - } else { - // unknown state oh no - safety_critical_disarm_motor_pwm(motor); - } - cpu_exit_critical(mask); -} // @brief Arms the brake resistor void safety_critical_arm_brake_resistor() { - uint32_t mask = cpu_enter_critical(); - brake_resistor_armed = true; - htim2.Instance->CCR3 = 0; - htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; - cpu_exit_critical(mask); + CRITICAL_SECTION() { + brake_resistor_armed = true; + htim2.Instance->CCR3 = 0; + htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; + } } // @brief Disarms the brake resistor and by extension @@ -157,40 +87,48 @@ void safety_critical_arm_brake_resistor() { // After calling this, the brake resistor can only be armed again // by calling safety_critical_arm_brake_resistor(). void safety_critical_disarm_brake_resistor() { - uint32_t mask = cpu_enter_critical(); - brake_resistor_armed = false; - htim2.Instance->CCR3 = 0; - htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; - for (size_t i = 0; i < AXIS_COUNT; ++i) { - safety_critical_disarm_motor_pwm(axes[i].motor_); + bool brake_resistor_was_armed = brake_resistor_armed; + + CRITICAL_SECTION() { + brake_resistor_armed = false; + htim2.Instance->CCR3 = 0; + htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; + } + + // Check necessary to prevent infinite recursion + if (brake_resistor_was_armed) { + for (auto& axis: axes) { + axis.motor_.disarm(); + } } - cpu_exit_critical(mask); } // @brief Updates the brake resistor PWM timings unless // the brake resistor is disarmed. void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t high_on) { - if (high_on - low_off < TIM_APB1_DEADTIME_CLOCKS) - low_level_fault(Motor::ERROR_BRAKE_DEADTIME_VIOLATION); - uint32_t mask = cpu_enter_critical(); - if (brake_resistor_armed) { - // Safe update of low and high side timings - // To avoid race condition, first reset timings to safe state - // ch3 is low side, ch4 is high side - htim2.Instance->CCR3 = 0; - htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; - htim2.Instance->CCR3 = low_off; - htim2.Instance->CCR4 = high_on; + if (high_on - low_off < TIM_APB1_DEADTIME_CLOCKS) { + odrv.disarm_with_error(ODrive::ERROR_BRAKE_DEADTIME_VIOLATION); + } + + CRITICAL_SECTION() { + if (brake_resistor_armed) { + // Safe update of low and high side timings + // To avoid race condition, first reset timings to safe state + // ch3 is low side, ch4 is high side + htim2.Instance->CCR3 = 0; + htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; + htim2.Instance->CCR3 = low_off; + htim2.Instance->CCR4 = high_on; + } } - cpu_exit_critical(mask); } /* Function implementations --------------------------------------------------*/ void start_adc_pwm() { // Disarm motors - for (size_t i = 0; i < AXIS_COUNT; ++i) { - safety_critical_disarm_motor_pwm(axes[i].motor_); + for (auto& axis: axes) { + axis.motor_.disarm(); } for (Motor& motor: motors) { @@ -215,26 +153,9 @@ void start_adc_pwm() { __HAL_ADC_ENABLE(&hadc3); // Warp field stabilize. osDelay(2); - __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_JEOC); - __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_JEOC); - __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_JEOC); - __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_EOC); - __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_EOC); - __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_OVR); - __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_OVR); - __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_OVR); - __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_JEOC); - __HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_JEOC); - __HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_JEOC); - __HAL_ADC_ENABLE_IT(&hadc2, ADC_IT_EOC); - __HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_EOC); - for (Motor& motor: motors) { - // Enable the update interrupt (used to coherently sample GPIO) - __HAL_TIM_CLEAR_IT(motor.timer_, TIM_IT_UPDATE); - __HAL_TIM_ENABLE_IT(motor.timer_, TIM_IT_UPDATE); - } + start_timers(); // Start brake resistor PWM in floating output configuration @@ -372,111 +293,9 @@ float get_adc_voltage_channel(uint16_t channel) // IRQ Callbacks //-------------------------------- -void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { +void vbus_sense_adc_cb(uint32_t adc_value) { constexpr float voltage_scale = adc_ref_voltage * VBUS_S_DIVIDER_RATIO / adc_full_scale; - // Only one conversion in sequence, so only rank1 - uint32_t ADCValue = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1); - vbus_voltage = ADCValue * voltage_scale; -} - -// This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion. -// TODO: Document how the phasing is done, link to timing diagram -void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { -#define calib_tau 0.2f //@TOTO make more easily configurable - constexpr float calib_filter_k = CURRENT_MEAS_PERIOD / calib_tau; - - // Ensure ADCs are expected ones to simplify the logic below - if (!(hadc == &hadc2 || hadc == &hadc3)) { - low_level_fault(Motor::ERROR_ADC_FAILED); - return; - }; - - // Motor 0 is on Timer 1, which triggers ADC 2 and 3 on an injected conversion - // Motor 1 is on Timer 8, which triggers ADC 2 and 3 on a regular conversion - // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current - // If we are counting down, we just sampled in SVM vector 7, with zero current - Axis& axis = injected ? axes[0] : axes[1]; - int axis_num = injected ? 0 : 1; - Axis& other_axis = injected ? axes[1] : axes[0]; - bool counting_down = axis.motor_.timer_->Instance->CR1 & TIM_CR1_DIR; - bool current_meas_not_DC_CAL = !counting_down; - - // Check the timing of the sequencing - if (current_meas_not_DC_CAL) - axis.motor_.log_timing(TIMING_LOG_ADC_CB_I); - else - axis.motor_.log_timing(TIMING_LOG_ADC_CB_DC); - - bool update_timings = false; - if (hadc == &hadc2) { - if (&axis == &axes[1] && counting_down) - update_timings = true; // update timings of M0 - else if (&axis == &axes[0] && !counting_down) - update_timings = true; // update timings of M1 - - // TODO: this is out of place here. However when moving it somewhere - // else we have to consider the timing requirements to prevent the SPI - // transfers of axis0 and axis1 from conflicting. - // Also see comment on sync_timers. - if((current_meas_not_DC_CAL && !axis_num) || - (axis_num && !current_meas_not_DC_CAL)){ - axis.encoder_.abs_spi_start_transaction(); - } - } - - // Load next timings for the motor that we're not currently sampling - if (update_timings) { - if (!other_axis.motor_.next_timings_valid_) { - // the motor control loop failed to update the timings in time - // we must assume that it died and therefore float all phases - bool was_armed = safety_critical_disarm_motor_pwm(other_axis.motor_); - if (was_armed) { - other_axis.motor_.error_ |= Motor::ERROR_CONTROL_DEADLINE_MISSED; - } - } else { - other_axis.motor_.next_timings_valid_ = false; - safety_critical_apply_motor_pwm_timings( - other_axis.motor_, other_axis.motor_.next_timings_ - ); - } - update_brake_current(); - } - - uint32_t ADCValue; - if (injected) { - ADCValue = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1); - } else { - ADCValue = HAL_ADC_GetValue(hadc); - } - float current = axis.motor_.phase_current_from_adcval(ADCValue); - - if (current_meas_not_DC_CAL) { - // ADC2 and ADC3 record the phB and phC currents concurrently, - // and their interrupts should arrive on the same clock cycle. - // We dispatch the callbacks in order, so ADC2 will always be processed before ADC3. - // Therefore we store the value from ADC2 and signal the thread that the - // measurement is ready when we receive the ADC3 measurement - - // return or continue - if (hadc == &hadc2) { - axis.motor_.current_meas_.phB = current - axis.motor_.DC_calib_.phB; - return; - } else { - axis.motor_.current_meas_.phC = current - axis.motor_.DC_calib_.phC; - } - // Prepare hall readings - // TODO move this to inside encoder update function - axis.encoder_.decode_hall_samples(); - // Trigger axis thread - axis.signal_current_meas(); - } else { - // DC_CAL measurement - if (hadc == &hadc2) { - axis.motor_.DC_calib_.phB += (current - axis.motor_.DC_calib_.phB) * calib_filter_k; - } else { - axis.motor_.DC_calib_.phC += (current - axis.motor_.DC_calib_.phC) * calib_filter_k; - } - } + vbus_voltage = adc_value * voltage_scale; } // @brief Sums up the Ibus contribution of each motor and updates the @@ -484,8 +303,8 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { void update_brake_current() { float Ibus_sum = 0.0f; for (size_t i = 0; i < AXIS_COUNT; ++i) { - if (axes[i].motor_.armed_state_ == Motor::ARMED_STATE_ARMED) { - Ibus_sum += axes[i].motor_.current_control_.Ibus; + if (axes[i].motor_.is_armed_) { + Ibus_sum += axes[i].motor_.I_bus_; } } @@ -499,7 +318,7 @@ void update_brake_current() { if (std::isnan(brake_duty)) { // Shuts off all motors AND brake resistor, sets error code on all motors. - low_level_fault(Motor::ERROR_BRAKE_DUTY_CYCLE_NAN); + odrv.disarm_with_error(ODrive::ERROR_BRAKE_DUTY_CYCLE_NAN); return; } @@ -516,11 +335,11 @@ void update_brake_current() { ibus_ += odrv.ibus_report_filter_k_ * (Ibus_sum - ibus_); if (Ibus_sum > odrv.config_.dc_max_positive_current) { - low_level_fault(Motor::ERROR_DC_BUS_OVER_CURRENT); + odrv.disarm_with_error(ODrive::ERROR_DC_BUS_OVER_CURRENT); return; } if (Ibus_sum < odrv.config_.dc_max_negative_current) { - low_level_fault(Motor::ERROR_DC_BUS_OVER_REGEN_CURRENT); + odrv.disarm_with_error(ODrive::ERROR_DC_BUS_OVER_REGEN_CURRENT); return; } diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index e02ef5c2..6701a079 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -25,17 +25,13 @@ extern uint16_t adc_measurements_[ADC_CHANNEL_COUNT]; /* Exported macro ------------------------------------------------------------*/ /* Exported functions --------------------------------------------------------*/ -void safety_critical_arm_motor_pwm(Motor& motor); -bool safety_critical_disarm_motor_pwm(Motor& motor); -void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3]); void safety_critical_arm_brake_resistor(); void safety_critical_disarm_brake_resistor(); void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t high_on); // called from STM platform code extern "C" { -void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected); -void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected); +void vbus_sense_adc_cb(uint32_t adc_value); void pwm_in_cb(TIM_HandleTypeDef *htim); } diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 1befd9cf..d11d8aec 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -17,9 +17,6 @@ osSemaphoreId sem_usb_rx; osSemaphoreId sem_usb_tx; osSemaphoreId sem_can; -osThreadId usb_irq_thread; -const uint32_t stack_size_usb_irq_thread = 2048; // Bytes - #if defined(STM32F405xx) // Place FreeRTOS heap in core coupled memory for better performance __attribute__((section(".ccmram"))) @@ -150,28 +147,25 @@ void ODrive::enter_dfu_mode() { } } -static void usb_deferred_interrupt_thread(void * ctx) { - (void) ctx; // unused parameter - - for (;;) { - // Wait for signalling from USB interrupt (OTG_FS_IRQHandler) - osStatus semaphore_status = osSemaphoreWait(sem_usb_irq, osWaitForever); - if (semaphore_status == osOK) { - // We have a new incoming USB transmission: handle it - HAL_PCD_IRQHandler(&usb_pcd_handle); - // Let the irq (OTG_FS_IRQHandler) fire again. - HAL_NVIC_EnableIRQ((usb_pcd_handle.Instance == USB_OTG_FS) ? OTG_FS_IRQn : OTG_HS_IRQn); - } +void ODrive::clear_errors() { + for (auto& axis: axes) { + axis.motor_.error_ = Motor::ERROR_NONE; + axis.controller_.error_ = Controller::ERROR_NONE; + axis.sensorless_estimator_.error_ = SensorlessEstimator::ERROR_NONE; + axis.encoder_.error_ = Encoder::ERROR_NONE; + axis.encoder_.spi_error_rate_ = 0.0f; + axis.error_ = Axis::ERROR_NONE; } + error_ = ERROR_NONE; } extern "C" { void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName) { - for(auto& axis : axes){ - safety_critical_disarm_motor_pwm(axis.motor_); + for(auto& axis: axes){ + axis.motor_.disarm(); } - safety_critical_disarm_brake_resistor(); + safety_critical_disarm_brake_resistor(); for (;;); // TODO: safe action } @@ -184,7 +178,6 @@ void vApplicationIdleHook(void) { odrv.system_stats_.min_stack_space_axis = *std::min_element(std::begin(min_stack_space), std::end(min_stack_space)); odrv.system_stats_.min_stack_space_usb = uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t); odrv.system_stats_.min_stack_space_uart = uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_usb_irq = uxTaskGetStackHighWaterMark(usb_irq_thread) * sizeof(StackType_t); odrv.system_stats_.min_stack_space_startup = uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t); odrv.system_stats_.min_stack_space_can = uxTaskGetStackHighWaterMark(odCAN->thread_id_) * sizeof(StackType_t); @@ -192,7 +185,6 @@ void vApplicationIdleHook(void) { odrv.system_stats_.stack_usage_axis = axes[0].stack_size_ - odrv.system_stats_.min_stack_space_axis; odrv.system_stats_.stack_usage_usb = stack_size_usb_thread - odrv.system_stats_.min_stack_space_usb; odrv.system_stats_.stack_usage_uart = stack_size_uart_thread - odrv.system_stats_.min_stack_space_uart; - odrv.system_stats_.stack_usage_usb_irq = stack_size_usb_irq_thread - odrv.system_stats_.min_stack_space_usb_irq; odrv.system_stats_.stack_usage_startup = stack_size_default_task - odrv.system_stats_.min_stack_space_startup; odrv.system_stats_.stack_usage_can = odCAN->stack_size_ - odrv.system_stats_.min_stack_space_can; } @@ -200,6 +192,140 @@ void vApplicationIdleHook(void) { } +/** + * @brief Runs system-level checks that need to be as real-time as possible. + * + * This function is called after every current measurement of every motor. + * It should finish as quickly as possible. + */ +void ODrive::do_fast_checks() { + if (!(vbus_voltage >= config_.dc_bus_undervoltage_trip_level)) + disarm_with_error(ERROR_DC_BUS_UNDER_VOLTAGE); + if (!(vbus_voltage <= config_.dc_bus_overvoltage_trip_level)) + disarm_with_error(ERROR_DC_BUS_OVER_VOLTAGE); +} + +/** + * @brief Floats all power phases on the system (all motors and brake resistors). + * + * This should be called if a system level exception ocurred that makes it + * unsafe to run power through the system in general. + */ +void ODrive::disarm_with_error(Error error) { + CRITICAL_SECTION() { + for (auto& axis: axes) { + axis.motor_.disarm_with_error(Motor::ERROR_SYSTEM_LEVEL); + } + safety_critical_disarm_brake_resistor(); + error_ |= error; + } +} + +/** + * @brief Runs the periodic sampling tasks + * + * All components that need to sample real-world data should do it in this + * function as it runs on a high interrupt priority and provides lowest possible + * timing jitter. + * + * All function called from this function should adhere to the following rules: + * - Try to use the same number of CPU cycles in every iteration. + * (reason: Tasks that run later in the function still want lowest possible timing jitter) + * - Use as few cycles as possible. + * (reason: The interrupt blocks other important interrupts (TODO: which ones?)) + * - Not call any FreeRTOS functions. + * (reason: The interrupt priority is higher than the max allowed priority for syscalls) + * + * Time consuming and undeterministic logic/arithmetic should live on + * control_loop_cb() instead. + */ +void ODrive::sampling_cb() { + n_evt_sampling_++; + + MEASURE_TIME(task_times_.sampling) { + for (auto& axis: axes) { + axis.encoder_.sample_now(); + } + } +} + +/** + * @brief Runs the periodic control loop. + * + * This function is executed in a low priority interrupt context and is allowed + * to call CMSIS functions. + * + * Yet it runs at a higher priority than communication workloads. + * + * @param update_cnt: The true count of update events (wrapping around at 16 + * bits). This is used for timestamp calculation in the face of + * potentially missed timer update interrupts. Therefore this counter + * must not rely on any interrupts. + */ +void ODrive::control_loop_cb(uint32_t timestamp) { + last_update_timestamp_ = timestamp; + n_evt_control_loop_++; + + // TODO: use a configurable component list for most of the following things + + MEASURE_TIME(task_times_.control_loop_misc) { + uart_poll(); + odrv.oscilloscope_.update(); + } + + MEASURE_TIME(task_times_.control_loop_checks) { + for (auto& axis: axes) { + // look for errors at axis level and also all subcomponents + bool checks_ok = axis.do_checks(timestamp); + + // make sure the watchdog is being fed. + bool watchdog_ok = axis.watchdog_check(); + + if (!checks_ok || !watchdog_ok) { + axis.motor_.disarm(); + } + } + } + + for (auto& axis: axes) { + // Sub-components should use set_error which will propegate to this error_ + MEASURE_TIME(axis.task_times_.thermistor_update) { + for (ThermistorCurrentLimiter* thermistor : axis.thermistors_) { + thermistor->update(); + } + } + + MEASURE_TIME(axis.task_times_.encoder_update) + axis.encoder_.update(); + + MEASURE_TIME(axis.task_times_.sensorless_estimator_update) + axis.sensorless_estimator_.update(); + + MEASURE_TIME(axis.task_times_.endstop_update) { + axis.min_endstop_.update(); + axis.max_endstop_.update(); + } + + MEASURE_TIME(axis.task_times_.can_heartbeat) + odCAN->send_heartbeat(&axis); + + MEASURE_TIME(axis.task_times_.controller_update) + axis.controller_.update(); // uses position and velocity from encoder + + MEASURE_TIME(axis.task_times_.open_loop_controller_update) + axis.open_loop_controller_.update(timestamp); + + MEASURE_TIME(axis.task_times_.async_estimator_update) + axis.async_estimator_.update(timestamp); + + MEASURE_TIME(axis.task_times_.motor_update) + axis.motor_.update(); // uses torque from controller and phase_vel from encoder + + MEASURE_TIME(axis.task_times_.current_controller_update) + axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or async_estimator_ + } +} + /** @brief For diagnostics only */ uint32_t ODrive::get_interrupt_status(int32_t irqn) { @@ -259,30 +385,20 @@ static void rtos_main(void*) { // must happen after communication is initialized pwm0_input.init(); - // Set up hardware for all components - for (size_t i = 0; i < AXIS_COUNT; ++i) { - if (!axes[i].setup()) { - for (;;) { - osDelay(10); // TODO: proper error handling - } - } + // Try to initialized gate drivers for fault-free startup. + // If this does not succeed, a fault will be raised and the idle loop will + // periodically attempt to reinit the gate driver. + for(auto& axis: axes){ + axis.motor_.setup(); } - for(auto& axis : axes){ + for(auto& axis: axes){ axis.encoder_.setup(); } // Start PWM and enable adc interrupts/callbacks start_adc_pwm(); - // This delay serves two purposes: - // - Let the current sense calibration converge (the current - // sense interrupts are firing in background by now) - // - Allow a user to interrupt the code, e.g. by flashing a new code, - // before it does anything crazy - // TODO make timing a function of calibration filter tau - osDelay(1500); - // Start state machine threads. Each thread will go through various calibration // procedures and then run the actual controller loops. // TODO: generalize for AXIS_COUNT != 2 @@ -537,11 +653,6 @@ extern "C" int main(void) { sem_can = osSemaphoreCreate(osSemaphore(sem_can), 1); osSemaphoreWait(sem_can, 0); - // Start USB interrupt handler thread - osThreadDef(task_usb_pump, usb_deferred_interrupt_thread, osPriorityAboveNormal, 0, stack_size_usb_irq_thread / sizeof(StackType_t)); - usb_irq_thread = osThreadCreate(osThread(task_usb_pump), NULL); - - // Construct all objects. odCAN = new ODriveCAN(can_config, &hcan1); diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 9bdbaa75..a5ac87d0 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -6,49 +6,254 @@ #include +#define CURRENT_ADC_LOWER_BOUND (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MIN_VOLT / 3.3f) +#define CURRENT_ADC_UPPER_BOUND (uint32_t)((float)(1 << 12) * CURRENT_SENSE_MAX_VOLT / 3.3f) + +/** + * @brief This control law adjusts the output voltage such that a predefined + * current is tracked. A hardcoded integrator gain is used for this. + * + * TODO: this might as well be implemented using the FieldOrientedController. + */ +struct ResistanceMeasurementControlLaw : AlphaBetaFrameController { + void reset() final { + test_voltage_ = 0.0f; + test_mod_ = NAN; + } + + ODriveIntf::MotorIntf::Error on_measurement( + float vbus_voltage, float Ialpha, float Ibeta, + uint32_t input_timestamp) final + { + actual_current_ = Ialpha; + test_voltage_ += (kI * current_meas_period) * (target_current_ - actual_current_); + + if (std::abs(test_voltage_) > max_voltage_) { + test_voltage_ = NAN; + return Motor::ERROR_PHASE_RESISTANCE_OUT_OF_RANGE; + } else if (std::isnan(vbus_voltage)) { + return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE; + } else { + float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); + test_mod_ = test_voltage_ * vfactor; + return Motor::ERROR_NONE; + } + } + + ODriveIntf::MotorIntf::Error get_alpha_beta_output(uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) { + if (std::isnan(test_mod_)) { + return Motor::ERROR_CONTROLLER_INITIALIZING; + } else { + *mod_alpha = test_mod_; + *mod_beta = 0.0f; + *ibus = test_mod_ * actual_current_; + return Motor::ERROR_NONE; + } + } + + float get_resistance() { + return test_voltage_ / target_current_; + } + + const float kI = 10.0f; // [(V/s)/A] + float max_voltage_ = 0.0f; + float actual_current_ = 0.0f; + float target_current_ = 0.0f; + float test_voltage_ = 0.0f; + float test_mod_ = NAN; +}; + +/** + * @brief This control law toggles rapidly between positive and negative output + * voltage. By measuring how large the current ripples are, the phase inductance + * can be determined. + * + * TODO: this method assumes a certain synchronization between current measurement and output application + */ +struct InductanceMeasurementControlLaw : AlphaBetaFrameController { + void reset() final { + attached_ = false; + } + + ODriveIntf::MotorIntf::Error on_measurement(float vbus_voltage, + float Ialpha, float Ibeta, uint32_t input_timestamp) final + { + if (std::isnan(Ialpha) || std::isnan(vbus_voltage)) { + return {Motor::ERROR_UNKNOWN_VBUS_VOLTAGE}; + } + + if (attached_) { + float sign = test_voltage_ >= 0.0f ? 1.0f : -1.0f; + deltaI_ += -sign * (Ialpha - last_Ialpha_); + } else { + start_timestamp_ = input_timestamp; + attached_ = true; + } + + last_Ialpha_ = Ialpha; + last_input_timestamp_ = input_timestamp; + + return Motor::ERROR_NONE; + } + + ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) final + { + test_voltage_ *= -1.0f; + float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); + *mod_alpha = test_voltage_ * vfactor; + *mod_beta = 0.0f; + *ibus = 0.0f; + return Motor::ERROR_NONE; + } + + float get_inductance() { + // Note: A more correct formula would also take into account that there is a finite timestep. + // However, the discretisation in the current control loop inverts the same discrepancy + float dt = (float)(last_input_timestamp_ - start_timestamp_) / (float)TIM_1_8_CLOCK_HZ; // at 216MHz this overflows after 19 seconds + return std::abs(test_voltage_) / (deltaI_ / dt); + } + + // Config + float test_voltage_ = 0.0f; + + // State + bool attached_ = false; + float sign_ = 0; + + // Outputs + uint32_t start_timestamp_ = 0; + float last_Ialpha_ = NAN; + uint32_t last_input_timestamp_ = 0; + float deltaI_ = 0.0f; +}; + + Motor::Motor(TIM_HandleTypeDef* timer, - uint16_t control_deadline, + uint8_t current_sensor_mask, float shunt_conductance, TGateDriver& gate_driver, TOpAmp& opamp) : timer_(timer), - control_deadline_(control_deadline), + current_sensor_mask_(current_sensor_mask), shunt_conductance_(shunt_conductance), gate_driver_(gate_driver), opamp_(opamp) { apply_config(); } -// @brief Arms the PWM outputs that belong to this motor. -// -// Note that this does not yet activate the PWM outputs, it just unlocks them. -// -// While the motor is armed, the control loop must set new modulation timings -// between any two interrupts (that is, enqueue_modulation_timings must be executed). -// If the control loop fails to do so, the next interrupt handler floats the -// phases. Once this happens, missed_control_deadline is set to true and -// the motor can be considered disarmed. -// -// @returns: True on success, false otherwise -bool Motor::arm() { +/** + * @brief Arms the PWM outputs that belong to this motor. + * + * Note that this does not activate the PWM outputs immediately, it just sets + * a flag so they will be enabled later. + * + * The sequence goes like this: + * - Motor::arm() sets the is_armed_ flag. + * - On the next timer update event Motor::timer_update_cb() gets called in an + * interrupt context + * - Motor::timer_update_cb() runs specified control law to determine PWM values + * - Motor::timer_update_cb() calls Motor::apply_pwm_timings() + * - Motor::apply_pwm_timings() sets the output compare registers and the AOE + * (automatic output enable) bit. + * - On the next update event the timer latches the configured values into the + * active shadow register and enables the outputs at the same time. + * + * The sequence can be aborted at any time by calling Motor::disarm(). + * + * @param control_law: An control law that is called at the frequency of current + * measurements. The function must return as quickly as possible + * such that the resulting PWM timings are available before the next + * timer update event. + * @returns: True on success, false otherwise + */ +bool Motor::arm(PhaseControlLaw<3>* control_law) { + CRITICAL_SECTION() { + control_law_ = control_law; - // Reset controller states, integrators, setpoints, etc. - axis_->controller_.reset(); - reset_current_control(); + // Reset controller states, integrators, setpoints, etc. + axis_->controller_.reset(); + axis_->async_estimator_.rotor_flux_ = 0.0f; + if (control_law_) { + control_law_->reset(); + } + + if (brake_resistor_armed) { + is_armed_ = true; + } + } - // Wait until the interrupt handler triggers twice. This gives - // the control loop the correct time quota to set up modulation timings. - if (!axis_->wait_for_current_meas()) - return axis_->error_ |= Axis::ERROR_CURRENT_MEASUREMENT_TIMEOUT, false; - next_timings_valid_ = false; - safety_critical_arm_motor_pwm(*this); return true; } -void Motor::reset_current_control() { - current_control_.v_current_control_integral_d = 0.0f; - current_control_.v_current_control_integral_q = 0.0f; - current_control_.acim_rotor_flux = 0.0f; +/** + * @brief Updates the phase PWM timings unless the motor is disarmed. + * + * If the motor is armed, the PWM timings come into effect at the next update + * event (and are enabled if they weren't already), unless the motor is disarmed + * prior to that. + * + * @param tentative: If true, the update is not counted as "refresh". + */ +void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) { + CRITICAL_SECTION() { + if (!brake_resistor_armed) { + disarm_with_error(ERROR_BRAKE_RESISTOR_DISARMED); + } + + TIM_HandleTypeDef* htim = timer_; + TIM_TypeDef* tim = htim->Instance; + tim->CCR1 = timings[0]; + tim->CCR2 = timings[1]; + tim->CCR3 = timings[2]; + + if (!tentative) { + if (is_armed_) { + // Set the Automatic Output Enable so that the Master Output Enable + // bit will be automatically enabled on the next update event. + tim->BDTR |= TIM_BDTR_AOE; + } + } + + // If a timer update event occurred just now while we were updating the + // timings, we can't be sure what values the shadow registers now contain, + // so we must disarm the motor. + // (this also protects against the case where the update interrupt has too + // low priority, but that should not happen) + //if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE)) { + // disarm_with_error(ERROR_CONTROL_DEADLINE_MISSED); + //} + } +} + +/** + * @brief Disarms the motor PWM. + * + * After this function returns, it is guaranteed that all three + * motor phases are floating and will not be enabled again until + * arm() is called. + */ +bool Motor::disarm(bool* was_armed) { + bool dummy; + was_armed = was_armed ? was_armed : &dummy; + CRITICAL_SECTION() { + *was_armed = is_armed_; + if (is_armed_) { + gate_driver_.set_enabled(false); + } + is_armed_ = false; + TIM_HandleTypeDef* timer = timer_; + timer->Instance->BDTR &= ~TIM_BDTR_AOE; // prevent the PWMs from automatically enabling at the next update + __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(timer); + control_law_ = nullptr; + } + + // Check necessary to prevent infinite recursion + if (was_armed) { + update_brake_current(); + } + + return true; } // @brief Tune the current controller based on phase resistance and inductance @@ -56,9 +261,9 @@ void Motor::reset_current_control() { // TODO: allow update on user-request or update automatically via hooks void Motor::update_current_controller_gains() { // Calculate current control gains - current_control_.p_gain = config_.current_control_bandwidth * config_.phase_inductance; + current_control_.p_gain_ = config_.current_control_bandwidth * config_.phase_inductance; float plant_pole = config_.phase_resistance / config_.phase_inductance; - current_control_.i_gain = plant_pole * current_control_.p_gain; + current_control_.i_gain_ = plant_pole * current_control_.p_gain_; } bool Motor::apply_config() { @@ -70,43 +275,41 @@ bool Motor::apply_config() { // @brief Set up the gate drivers bool Motor::setup() { - if (!gate_driver_.init()) { - return false; - } - // Solve for exact gain, then snap down to have equal or larger range as requested // or largest possible range otherwise constexpr float kMargin = 0.90f; - constexpr float kTripMargin = 1.0f; // Trip level is at edge of linear range of amplifer constexpr float max_output_swing = 1.35f; // [V] out of amplifier float max_unity_gain_current = kMargin * max_output_swing * shunt_conductance_; // [A] float requested_gain = max_unity_gain_current / config_.requested_current_range; // [V/V] float actual_gain = NAN; - bool success = opamp_.set_gain(requested_gain, &actual_gain); - if (!success) + if (!gate_driver_.config(requested_gain, &actual_gain)) return false; // Values for current controller phase_current_rev_gain_ = 1.0f / actual_gain; // Clip all current control to actual usable range - current_control_.max_allowed_current = max_unity_gain_current * phase_current_rev_gain_; - // Set trip level - current_control_.overcurrent_trip_level = (kTripMargin / kMargin) * current_control_.max_allowed_current; + max_allowed_current_ = max_unity_gain_current * phase_current_rev_gain_; + + max_dc_calib_ = 0.1f * max_allowed_current_; + + if (!gate_driver_.init()) + return true; return true; } -void Motor::set_error(Motor::Error error){ +void Motor::disarm_with_error(Motor::Error error){ error_ |= error; - axis_->error_ |= Axis::ERROR_MOTOR_FAILED; - safety_critical_disarm_motor_pwm(*this); + disarm(); update_brake_current(); } -bool Motor::do_checks() { - if (!gate_driver_.check_fault()) { - set_error(ERROR_DRV_FAULT); +bool Motor::do_checks(uint32_t timestamp) { + gate_driver_.do_checks(); + + if (!gate_driver_.is_ready()) { + disarm_with_error(ERROR_DRV_FAULT); return false; } @@ -120,7 +323,7 @@ float Motor::effective_current_lim() { if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { current_lim = std::min(current_lim, 0.98f*one_by_sqrt3*vbus_voltage); //gimbal motor is voltage control } else { - current_lim = std::min(current_lim, axis_->motor_.current_control_.max_allowed_current); + current_lim = std::min(current_lim, axis_->motor_.max_allowed_current_); } // Apply axis current limiters @@ -137,27 +340,22 @@ float Motor::effective_current_lim() { //Note - for ACIM motors, available torque is allowed to be 0. float Motor::max_available_torque() { if (config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - float max_torque = effective_current_lim_ * config_.torque_constant * current_control_.acim_rotor_flux; + float max_torque = effective_current_lim_ * config_.torque_constant * axis_->async_estimator_.rotor_flux_; max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim); return max_torque; - } - else { + } else { float max_torque = effective_current_lim_ * config_.torque_constant; max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim); return max_torque; } } -void Motor::log_timing(TimingLog_t log_idx) { - static const uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); - uint16_t timing = clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config - - if (log_idx < TIMING_LOG_NUM_SLOTS) { - timing_log_[log_idx] = timing; - } -} - float Motor::phase_current_from_adcval(uint32_t ADCValue) { + // Make sure the measurements don't come too close to the current sensor's hardware limitations + if (ADCValue < CURRENT_ADC_LOWER_BOUND || ADCValue > CURRENT_ADC_UPPER_BOUND) { + disarm_with_error(ERROR_CURRENT_SENSE_SATURATION); + } + int adcval_bal = (int)ADCValue - (1 << 11); float amp_out_volt = (3.3f / (float)(1 << 12)) * (float)adcval_bal; float shunt_volt = amp_out_volt * phase_current_rev_gain_; @@ -171,81 +369,83 @@ float Motor::phase_current_from_adcval(uint32_t ADCValue) { // TODO check Ibeta balance to verify good motor connection bool Motor::measure_phase_resistance(float test_current, float max_voltage) { - static const float kI = 10.0f; // [(V/s)/A] - static const int num_test_cycles = (int)(3.0f / CURRENT_MEAS_PERIOD); // Test runs for 3s - float test_voltage = 0.0f; + ResistanceMeasurementControlLaw control_law; + control_law.target_current_ = test_current; + control_law.max_voltage_ = max_voltage; + + arm(&control_law); + + for (size_t i = 0; i < 3000; ++i) { + if (!((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_)) { + break; + } + osDelay(1); + } + + bool success = is_armed_; + + //// De-energize motor + //if (!enqueue_voltage_timings(motor, 0.0f, 0.0f)) + // return false; // error set inside enqueue_voltage_timings + + disarm(); + + config_.phase_resistance = control_law.get_resistance(); + if (std::isnan(config_.phase_resistance)) { + // TODO: the motor is already disarmed at this stage. This is an error + // that only pretains to the measurement and its result so it should + // just be a return value of this function. + disarm_with_error(ERROR_PHASE_RESISTANCE_OUT_OF_RANGE); + success = false; + } + + return success; +} + + +bool Motor::measure_phase_inductance(float test_voltage) { + InductanceMeasurementControlLaw control_law; + control_law.test_voltage_ = test_voltage; + + arm(&control_law); + + for (size_t i = 0; i < 1250; ++i) { + if (!((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_)) { + break; + } + osDelay(1); + } + + bool success = is_armed_; + + //// De-energize motor + //if (!enqueue_voltage_timings(motor, 0.0f, 0.0f)) + // return false; // error set inside enqueue_voltage_timings + + disarm(); + + config_.phase_inductance = control_law.get_inductance(); - size_t i = 0; - axis_->run_control_loop([&](){ - float Ialpha = -(current_meas_.phB + current_meas_.phC); - test_voltage += (kI * current_meas_period) * (test_current - Ialpha); - if (test_voltage > max_voltage || test_voltage < -max_voltage) - return set_error(ERROR_PHASE_RESISTANCE_OUT_OF_RANGE), false; - - // Test voltage along phase A - if (!enqueue_voltage_timings(test_voltage, 0.0f)) - return false; // error set inside enqueue_voltage_timings - log_timing(TIMING_LOG_MEAS_R); - - return ++i < num_test_cycles; - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; - - //// De-energize motor - //if (!enqueue_voltage_timings(motor, 0.0f, 0.0f)) - // return false; // error set inside enqueue_voltage_timings - - float R = test_voltage / test_current; - config_.phase_resistance = R; - return true; // if we ran to completion that means success -} - -bool Motor::measure_phase_inductance(float voltage_low, float voltage_high) { - float test_voltages[2] = {voltage_low, voltage_high}; - float Ialphas[2] = {0.0f}; - static const int num_cycles = 5000; - - size_t t = 0; - axis_->run_control_loop([&](){ - int i = t & 1; - Ialphas[i] += -current_meas_.phB - current_meas_.phC; - - // Test voltage along phase A - if (!enqueue_voltage_timings(test_voltages[i], 0.0f)) - return false; // error set inside enqueue_voltage_timings - log_timing(TIMING_LOG_MEAS_L); - - return ++t < (num_cycles << 1); - }); - if (axis_->error_ != Axis::ERROR_NONE) - return false; - - //// De-energize motor - //if (!enqueue_voltage_timings(motor, 0.0f, 0.0f)) - // return false; // error set inside enqueue_voltage_timings - - float v_L = 0.5f * (voltage_high - voltage_low); - // Note: A more correct formula would also take into account that there is a finite timestep. - // However, the discretisation in the current control loop inverts the same discrepancy - float dI_by_dt = (Ialphas[1] - Ialphas[0]) / (current_meas_period * (float)num_cycles); - float L = v_L / dI_by_dt; - - config_.phase_inductance = L; // TODO arbitrary values set for now - if (L < 2e-6f || L > 4000e-6f) - return set_error(ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE), false; - return true; + if (!(config_.phase_inductance >= 2e-6f && config_.phase_inductance <= 4000e-6f)) { + error_ |= ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE; + success = false; + } + + return success; } +// TODO: motor calibration should only be a utility function that's called from +// the UI on explicit user request. It should take its parameters as input +// arguments and return the measured results without modifying any config values. bool Motor::run_calibration() { float R_calib_max_voltage = config_.resistance_calib_max_voltage; if (config_.motor_type == MOTOR_TYPE_HIGH_CURRENT || config_.motor_type == MOTOR_TYPE_ACIM) { if (!measure_phase_resistance(config_.calibration_current, R_calib_max_voltage)) return false; - if (!measure_phase_inductance(-R_calib_max_voltage, R_calib_max_voltage)) + if (!measure_phase_inductance(R_calib_max_voltage)) return false; } else if (config_.motor_type == MOTOR_TYPE_GIMBAL) { // no calibration needed @@ -259,224 +459,196 @@ bool Motor::run_calibration() { return true; } -bool Motor::enqueue_modulation_timings(float mod_alpha, float mod_beta) { - if (std::isnan(mod_alpha) || std::isnan(mod_alpha)) - return set_error(ERROR_MODULATION_IS_NAN), false; - float tA, tB, tC; - if (SVM(mod_alpha, mod_beta, &tA, &tB, &tC) != 0) - return set_error(ERROR_MODULATION_MAGNITUDE), false; - next_timings_[0] = (uint16_t)(tA * (float)TIM_1_8_PERIOD_CLOCKS); - next_timings_[1] = (uint16_t)(tB * (float)TIM_1_8_PERIOD_CLOCKS); - next_timings_[2] = (uint16_t)(tC * (float)TIM_1_8_PERIOD_CLOCKS); - next_timings_valid_ = true; - return true; -} +void Motor::update() { + float torque = torque_setpoint_src_ ? *torque_setpoint_src_ : NAN; + float phase_vel = phase_vel_src_ ? *phase_vel_src_ : NAN; -bool Motor::enqueue_voltage_timings(float v_alpha, float v_beta) { - float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); - float mod_alpha = vfactor * v_alpha; - float mod_beta = vfactor * v_beta; - if (!enqueue_modulation_timings(mod_alpha, mod_beta)) - return false; - log_timing(TIMING_LOG_FOC_VOLTAGE); - return true; -} + // Reset output just in case the controller fails for any reason + Iq_setpoint_ = NAN; + // Id_setpoint_ = NAN; // this doubles as a state variable so we can't reset it -// We should probably make FOC Current call FOC Voltage to avoid duplication. -bool Motor::FOC_voltage(float v_d, float v_q, float pwm_phase) { - float c = our_arm_cos_f32(pwm_phase); - float s = our_arm_sin_f32(pwm_phase); - float v_alpha = c*v_d - s*v_q; - float v_beta = c*v_q + s*v_d; - return enqueue_voltage_timings(v_alpha, v_beta); -} + float vd = 0.0f; + float vq = 0.0f; + float id = Id_setpoint_; + float iq; -bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase, float phase_vel) { - // Syntactic sugar - CurrentControl_t& ictrl = current_control_; - - // For Reporting - ictrl.Iq_setpoint = Iq_des; - - // Check for current sense saturation - if (std::abs(current_meas_.phB) > ictrl.overcurrent_trip_level || std::abs(current_meas_.phC) > ictrl.overcurrent_trip_level) { - set_error(ERROR_CURRENT_SENSE_SATURATION); - return false; + // Convert torque to current + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { + iq = torque / (axis_->motor_.config_.torque_constant * fmax(axis_->async_estimator_.rotor_flux_, config_.acim_gain_min_flux)); + } else { + iq = torque / axis_->motor_.config_.torque_constant; } - // Clarke transform - float Ialpha = -current_meas_.phB - current_meas_.phC; - float Ibeta = one_by_sqrt3 * (current_meas_.phB - current_meas_.phC); + iq *= direction_; - // Park transform - float c_I = our_arm_cos_f32(I_phase); - float s_I = our_arm_sin_f32(I_phase); - float Id = c_I * Ialpha + s_I * Ibeta; - float Iq = c_I * Ibeta - s_I * Ialpha; - ictrl.Iq_measured += ictrl.I_measured_report_filter_k * (Iq - ictrl.Iq_measured); - ictrl.Id_measured += ictrl.I_measured_report_filter_k * (Id - ictrl.Id_measured); + // TODO: 2-norm vs independent clamping (current could be sqrt(2) bigger) + float ilim = axis_->motor_.effective_current_lim_; + id = std::clamp(id, -ilim, ilim); + iq = std::clamp(iq, -ilim, ilim); - // Check for violation of current limit - float I_trip = effective_current_lim_ + config_.current_lim_margin; - if (SQ(Id) + SQ(Iq) > SQ(I_trip)) { - set_error(ERROR_CURRENT_LIMIT_VIOLATION); - return false; + if ((axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) && config_.acim_autoflux_enable) { + float abs_iq = fabsf(iq); + float gain = abs_iq > id ? config_.acim_autoflux_attack_gain : config_.acim_autoflux_decay_gain; + id += gain * (abs_iq - id) * current_meas_period; + id = std::clamp(id, config_.acim_autoflux_min_Id, ilim); } - // Current error - float Ierr_d = Id_des - Id; - float Ierr_q = Iq_des - Iq; - - // Apply PI control - float Vd = ictrl.v_current_control_integral_d + Ierr_d * ictrl.p_gain; - float Vq = ictrl.v_current_control_integral_q + Ierr_q * ictrl.p_gain; - if (config_.R_wL_FF_enable) { - Vd -= phase_vel * config_.phase_inductance * Iq_des; - Vq += phase_vel * config_.phase_inductance * Id_des; - Vd += config_.phase_resistance * Id_des; - Vq += config_.phase_resistance * Iq_des; + vd -= phase_vel * config_.phase_inductance * iq; + vq += phase_vel * config_.phase_inductance * id; + vd += config_.phase_resistance * id; + vq += config_.phase_resistance * iq; } if (config_.bEMF_FF_enable) { - Vq += phase_vel * (2.0f/3.0f) * (config_.torque_constant / config_.pole_pairs); + vq += phase_vel * (2.0f/3.0f) * (config_.torque_constant / config_.pole_pairs); } - float mod_to_V = (2.0f / 3.0f) * vbus_voltage; - float V_to_mod = 1.0f / mod_to_V; - float mod_d = V_to_mod * Vd; - float mod_q = V_to_mod * Vq; + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { + // reinterpret current as voltage + vd += id; + vq += iq; + id = NAN; + iq = NAN; + } - // Vector modulation saturation, lock integrator if saturated - // TODO make maximum modulation configurable - float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / sqrtf(mod_d * mod_d + mod_q * mod_q); - if (mod_scalefactor < 1.0f) { - mod_d *= mod_scalefactor; - mod_q *= mod_scalefactor; - // TODO make decayfactor configurable - ictrl.v_current_control_integral_d *= 0.99f; - ictrl.v_current_control_integral_q *= 0.99f; + Vd_setpoint_ = vd; + Vq_setpoint_ = vq; + Id_setpoint_ = id; + Iq_setpoint_ = iq; +} + + +/** + * @brief Called when the underlying hardware timer triggers an update event. + */ +void Motor::current_meas_cb(uint32_t timestamp, Iph_ABC_t current) { + // TODO: this is platform specific + //const float current_meas_period = static_cast(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ; + TaskTimerContext tmr{axis_->task_times_.current_sense}; + + bool current_valid = !std::isnan(current.phA) + && !std::isnan(current.phB) + && !std::isnan(current.phC); + + n_evt_current_measurement_++; + + bool dc_calib_valid = (dc_calib_running_since_ >= config_.dc_calib_tau * 7.5f) + && (abs(DC_calib_.phA) < max_dc_calib_) + && (abs(DC_calib_.phB) < max_dc_calib_) + && (abs(DC_calib_.phC) < max_dc_calib_); + + if (current_valid && dc_calib_valid) { + current.phA -= DC_calib_.phA; + current.phB -= DC_calib_.phB; + current.phC -= DC_calib_.phC; + I_leak_ = current.phA + current.phB + current.phC; // sum should be close to 0 + current_meas_.phA = current.phA - I_leak_ / 3.0f; + current_meas_.phB = current.phB - I_leak_ / 3.0f; + current_meas_.phC = current.phC - I_leak_ / 3.0f; } else { - ictrl.v_current_control_integral_d += Ierr_d * (ictrl.i_gain * current_meas_period); - ictrl.v_current_control_integral_q += Ierr_q * (ictrl.i_gain * current_meas_period); + I_leak_ = NAN; + current_meas_.phA = NAN; + current_meas_.phB = NAN; + current_meas_.phC = NAN; } - // Compute estimated bus current - ictrl.Ibus = mod_d * Id + mod_q * Iq; - - // Inverse park transform - float c_p = our_arm_cos_f32(pwm_phase); - float s_p = our_arm_sin_f32(pwm_phase); - float mod_alpha = c_p * mod_d - s_p * mod_q; - float mod_beta = c_p * mod_q + s_p * mod_d; - - // Report final applied voltage in stationary frame (for sensorles estimator) - ictrl.final_v_alpha = mod_to_V * mod_alpha; - ictrl.final_v_beta = mod_to_V * mod_beta; - - // Apply SVM - if (!enqueue_modulation_timings(mod_alpha, mod_beta)) - return false; // error set inside enqueue_modulation_timings - log_timing(TIMING_LOG_FOC_CURRENT); - - if (axis_->axis_num_ == 0) { - - // Edit these to suit your capture needs - float trigger_data = ictrl.v_current_control_integral_d; - float trigger_threshold = 0.5f; - float sample_data = Ialpha; - - static bool ready = false; - static bool capturing = false; - if (trigger_data < trigger_threshold) { - ready = true; - } - if (ready && trigger_data >= trigger_threshold) { - capturing = true; - ready = false; - } - if (capturing) { - oscilloscope[oscilloscope_pos] = sample_data; - if (++oscilloscope_pos >= OSCILLOSCOPE_SIZE) { - oscilloscope_pos = 0; - capturing = false; - } - } + if (abs(I_leak_) > config_.I_leak_max) { + disarm_with_error(ERROR_I_LEAK_OUT_OF_RANGE); } - return true; + // Run system-level checks (e.g. overvoltage/undervoltage condition) + // The motor might be disarmed in this function. In this case the + // handler will continue to run until the end but it won't have an + // effect on the PWM. + odrv.do_fast_checks(); + + // Check for violation of current limit + // If Ia + Ib + Ic == 0 holds then we have: + // Inorm^2 = Id^2 + Iq^2 = Ialpha^2 + Ibeta^2 = 2/3 * (Ia^2 + Ib^2 + Ic^2) + float Itrip = effective_current_lim_ + config_.current_lim_margin; + if (2.0f / 3.0f * (SQ(current_meas_.phA) + SQ(current_meas_.phB) + SQ(current_meas_.phC)) > SQ(Itrip)) { + disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION); + } + + if (control_law_) { + Error err = control_law_->on_measurement(vbus_voltage, + {current_meas_.phA, current_meas_.phB, current_meas_.phC}, + timestamp); + if (err != ERROR_NONE) { + disarm_with_error(err); + } + } } -// torque_setpoint [Nm] -// phase [rad electrical] -// phase_vel [rad/s electrical] -bool Motor::update(float torque_setpoint, float phase, float phase_vel) { - float current_setpoint = 0.0f; - phase *= config_.direction; - phase_vel *= config_.direction; +/** + * @brief Called when the underlying hardware timer triggers an update event. + */ +void Motor::dc_calib_cb(uint32_t timestamp, Iph_ABC_t current) { + const float dc_calib_period = static_cast(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ; + TaskTimerContext tmr{axis_->task_times_.dc_calib}; - if (config_.motor_type == MOTOR_TYPE_ACIM) { - current_setpoint = torque_setpoint / (config_.torque_constant * fmax(current_control_.acim_rotor_flux, config_.acim_gain_min_flux)); + bool current_valid = !std::isnan(current.phA) + && !std::isnan(current.phB) + && !std::isnan(current.phC); + + if (current_valid) { + const float calib_filter_k = std::min(dc_calib_period / config_.dc_calib_tau, 1.0f); + DC_calib_.phA += (current.phA - DC_calib_.phA) * calib_filter_k; + DC_calib_.phB += (current.phB - DC_calib_.phB) * calib_filter_k; + DC_calib_.phC += (current.phC - DC_calib_.phC) * calib_filter_k; + dc_calib_running_since_ += dc_calib_period; + } else { + DC_calib_.phA = 0.0f; + DC_calib_.phB = 0.0f; + DC_calib_.phC = 0.0f; + dc_calib_running_since_ = 0.0f; } - else { - current_setpoint = torque_setpoint / config_.torque_constant; +} + + +void Motor::pwm_update_cb(uint32_t output_timestamp) { + TaskTimerContext tmr{axis_->task_times_.pwm_update}; + n_evt_pwm_update_++; + + Error control_law_status = ERROR_CONTROLLER_FAILED; + float pwm_timings[3] = {NAN, NAN, NAN}; + float i_bus = 0.0f; + + if (control_law_) { + control_law_status = control_law_->get_output( + output_timestamp, pwm_timings, &i_bus); } - current_setpoint *= config_.direction; - // TODO: 2-norm vs independent clamping (current could be sqrt(2) bigger) - float ilim = effective_current_lim_; - float id = std::clamp(current_control_.Id_setpoint, -ilim, ilim); - float iq = std::clamp(current_setpoint, -ilim, ilim); + // Apply control law to calculate PWM duty cycles + if (is_armed_ && control_law_status == ERROR_NONE) { + uint16_t next_timings[] = { + (uint16_t)(pwm_timings[0] * (float)TIM_1_8_PERIOD_CLOCKS), + (uint16_t)(pwm_timings[1] * (float)TIM_1_8_PERIOD_CLOCKS), + (uint16_t)(pwm_timings[2] * (float)TIM_1_8_PERIOD_CLOCKS) + }; - if (config_.motor_type == MOTOR_TYPE_ACIM) { - // Note that the effect of the current commands on the real currents is actually 1.5 PWM cycles later - // However the rotor time constant is (usually) so slow that it doesn't matter - // So we elect to write it as if the effect is immediate, to have cleaner code - - if (config_.acim_autoflux_enable) { - float abs_iq = fabsf(iq); - float gain = abs_iq > id ? config_.acim_autoflux_attack_gain : config_.acim_autoflux_decay_gain; - id += gain * (abs_iq - id) * current_meas_period; - id = std::clamp(id, config_.acim_autoflux_min_Id, ilim); - current_control_.Id_setpoint = id; + apply_pwm_timings(next_timings, false); + } else if (is_armed_) { + i_bus = 0.0f; + if (!(timer_->Instance->BDTR & TIM_BDTR_MOE) && (control_law_status == ERROR_CONTROLLER_INITIALIZING)) { + // If the PWM output is armed in software but not yet in + // hardware we tolerate the "initializing" error. + } else { + disarm_with_error(control_law_status); } - - // acim_rotor_flux is normalized to units of [A] tracking Id; rotor inductance is unspecified - float dflux_by_dt = config_.acim_slip_velocity * (id - current_control_.acim_rotor_flux); - current_control_.acim_rotor_flux += dflux_by_dt * current_meas_period; - float slip_velocity = config_.acim_slip_velocity * (iq / current_control_.acim_rotor_flux); - // Check for issues with small denominator. Polarity of check to catch NaN too - bool acceptable_vel = fabsf(slip_velocity) <= 0.1f * (float)current_meas_hz; - if (!acceptable_vel) - slip_velocity = 0.0f; - phase_vel += slip_velocity; - // reporting only: - current_control_.async_phase_vel = slip_velocity; - - current_control_.async_phase_offset += slip_velocity * current_meas_period; - current_control_.async_phase_offset = wrap_pm_pi(current_control_.async_phase_offset); - phase += current_control_.async_phase_offset; - phase = wrap_pm_pi(phase); } - float pwm_phase = phase + 1.5f * current_meas_period * phase_vel; - - // Execute current command - switch(config_.motor_type){ - case MOTOR_TYPE_HIGH_CURRENT: return FOC_current(id, iq, phase, pwm_phase, phase_vel); break; - case MOTOR_TYPE_ACIM: return FOC_current(id, iq, phase, pwm_phase, phase_vel); break; - case MOTOR_TYPE_GIMBAL: return FOC_voltage(id, iq, pwm_phase); break; - default: set_error(ERROR_NOT_IMPLEMENTED_MOTOR_TYPE); return false; break; + // If something above failed, reset I_bus to 0A. + if (!is_armed_) { + i_bus = 0.0f; } - return true; -} -void Motor::tim_update_cb() { - // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current - // If we are counting down, we just sampled in SVM vector 7, with zero current - bool counting_down = timer_->Instance->CR1 & TIM_CR1_DIR; - if (counting_down) - return; + I_bus_ = i_bus; - axis_->encoder_.sample_now(); + if (i_bus < config_.I_bus_hard_min || i_bus > config_.I_bus_hard_max) { + disarm_with_error(ERROR_I_BUS_OUT_OF_RANGE); + } + + update_brake_current(); } diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index 3f61725a..27834014 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -5,53 +5,17 @@ class Axis; // declared in axis.hpp class Motor; #include - #include - -enum TimingLog_t { - TIMING_LOG_GENERAL, - TIMING_LOG_ADC_CB_I, - TIMING_LOG_ADC_CB_DC, - TIMING_LOG_MEAS_R, - TIMING_LOG_MEAS_L, - TIMING_LOG_ENC_CALIB, - TIMING_LOG_IDX_SEARCH, - TIMING_LOG_FOC_VOLTAGE, - TIMING_LOG_FOC_CURRENT, - TIMING_LOG_SPI_START, - TIMING_LOG_SAMPLE_NOW, - TIMING_LOG_SPI_END, - TIMING_LOG_NUM_SLOTS -}; +#include "foc.hpp" class Motor : public ODriveIntf::MotorIntf { public: - struct Iph_BC_t { + struct Iph_ABC_t { + float phA; float phB; float phC; }; - struct CurrentControl_t{ - float p_gain; // [V/A] - float i_gain; // [V/As] - float v_current_control_integral_d; // [V] - float v_current_control_integral_q; // [V] - float Ibus; // DC bus current [A] - // Voltage applied at end of cycle: - float final_v_alpha; // [V] - float final_v_beta; // [V] - float Id_setpoint; // [A] - float Iq_setpoint; // [A] - float Iq_measured; // [A] - float Id_measured; // [A] - float I_measured_report_filter_k; - float max_allowed_current; // [A] - float overcurrent_trip_level; // [A] - float acim_rotor_flux; // [A] - float async_phase_vel; // [rad/s electrical] - float async_phase_offset; // [rad electrical] - }; - // NOTE: for gimbal motors, all units of Nm are instead V. // example: vel_gain is [V/(turn/s)] instead of [Nm/(turn/s)] // example: current_lim and calibration_current will instead determine the maximum voltage applied to the motor. @@ -63,7 +27,6 @@ public: float phase_inductance = 0.0f; // to be set by measure_phase_inductance float phase_resistance = 0.0f; // to be set by measure_phase_resistance float torque_constant = 0.04f; // [Nm/A] for PM motors, [Nm/A^2] for induction motors. Equal to 8.27/Kv of the motor - int32_t direction = 0; // 1 or -1 (0 = unspecified) MotorType motor_type = MOTOR_TYPE_HIGH_CURRENT; // Read out max_allowed_current to see max supported value for current_lim. // float current_lim = 70.0f; //[A] @@ -75,15 +38,22 @@ public: float current_control_bandwidth = 1000.0f; // [rad/s] float inverter_temp_limit_lower = 100; float inverter_temp_limit_upper = 120; - float acim_slip_velocity = 14.706f; // [rad/s electrical] = 1/rotor_tau + float acim_gain_min_flux = 10; // [A] float acim_autoflux_min_Id = 10; // [A] bool acim_autoflux_enable = false; float acim_autoflux_attack_gain = 10.0f; float acim_autoflux_decay_gain = 1.0f; + bool R_wL_FF_enable = false; // Enable feedforwards for R*I and w*L*I terms bool bEMF_FF_enable = false; // Enable feedforward for bEMF + float I_bus_hard_min = -INFINITY; + float I_bus_hard_max = INFINITY; + float I_leak_max = 0.1f; + + float dc_calib_tau = 0.2f; + // custom property setters Motor* parent = nullptr; void set_pre_calibrated(bool value) { @@ -96,37 +66,36 @@ public: }; Motor(TIM_HandleTypeDef* timer, - uint16_t control_deadline, + uint8_t current_sensor_mask, float shunt_conductance, TGateDriver& gate_driver, TOpAmp& opamp); - bool arm(); - void disarm(); + bool arm(PhaseControlLaw<3>* control_law); + void apply_pwm_timings(uint16_t timings[3], bool tentative); + bool disarm(bool* was_armed = nullptr); bool apply_config(); bool setup(); - void reset_current_control(); void update_current_controller_gains(); - void set_error(Error error); - bool do_checks(); + void disarm_with_error(Error error); + bool do_checks(uint32_t timestamp); float effective_current_lim(); float max_available_torque(); - void log_timing(TimingLog_t log_idx); float phase_current_from_adcval(uint32_t ADCValue); bool measure_phase_resistance(float test_current, float max_voltage); - bool measure_phase_inductance(float voltage_low, float voltage_high); + bool measure_phase_inductance(float test_voltage); bool run_calibration(); - bool enqueue_modulation_timings(float mod_alpha, float mod_beta); - bool enqueue_voltage_timings(float v_alpha, float v_beta); - bool FOC_voltage(float v_d, float v_q, float pwm_phase); - bool FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase, float phase_vel); - bool update(float current_setpoint, float phase, float phase_vel); - void tim_update_cb(); + void update(); + + // These functions are called as appropriate from the board.cpp file. + void current_meas_cb(uint32_t timestamp, Iph_ABC_t current); + void dc_calib_cb(uint32_t timestamp, Iph_ABC_t current); + void pwm_update_cb(uint32_t output_timestamp); // hardware config TIM_HandleTypeDef* const timer_; - const uint16_t control_deadline_; + const uint8_t current_sensor_mask_; const float shunt_conductance_; TGateDriver& gate_driver_; TOpAmp& opamp_; @@ -136,49 +105,37 @@ public: //private: - uint16_t next_timings_[3] = { - TIM_1_8_PERIOD_CLOCKS / 2, - TIM_1_8_PERIOD_CLOCKS / 2, - TIM_1_8_PERIOD_CLOCKS / 2 - }; - bool next_timings_valid_ = false; - uint16_t last_cpu_time_ = 0; - int timing_log_index_ = 0; - struct { - uint16_t& operator[](size_t idx) { return content[idx]; } - uint16_t& get(size_t idx) { return content[idx]; } - uint16_t content[TIMING_LOG_NUM_SLOTS]; - } timing_log_; + uint32_t n_evt_current_measurement_ = 0; + uint32_t n_evt_pwm_update_ = 0; // variables exposed on protocol Error error_ = ERROR_NONE; // Do not write to this variable directly! // It is for exclusive use by the safety_critical_... functions. - ArmedState armed_state_ = ARMED_STATE_DISARMED; + bool is_armed_ = false; bool is_calibrated_ = config_.pre_calibrated; - Iph_BC_t current_meas_ = {0.0f, 0.0f}; - Iph_BC_t DC_calib_ = {0.0f, 0.0f}; + Iph_ABC_t current_meas_ = {NAN, NAN, NAN}; + Iph_ABC_t DC_calib_ = {0.0f, 0.0f, 0.0f}; + float dc_calib_running_since_ = 0.0f; // current sensor calibration needs some time to settle + float I_leak_ = NAN; // close to zero if only two current sensors are available + float I_bus_ = 0.0f; // this motors contribution to the bus current + bool current_meas_valid_ = false; // if false, the measured current values must not be used for control float phase_current_rev_gain_ = 0.0f; // Reverse gain for ADC to Amps (to be set by DRV8301_setup) - CurrentControl_t current_control_ = { - .p_gain = 0.0f, // [V/A] should be auto set after resistance and inductance measurement - .i_gain = 0.0f, // [V/As] should be auto set after resistance and inductance measurement - .v_current_control_integral_d = 0.0f, - .v_current_control_integral_q = 0.0f, - .Ibus = 0.0f, - .final_v_alpha = 0.0f, - .final_v_beta = 0.0f, - .Id_setpoint = 0.0f, - .Iq_setpoint = 0.0f, - .Iq_measured = 0.0f, - .Id_measured = 0.0f, - .I_measured_report_filter_k = 1.0f, - .max_allowed_current = 0.0f, - .overcurrent_trip_level = 0.0f, - .acim_rotor_flux = 0.0f, - .async_phase_vel = 0.0f, - .async_phase_offset = 0.0f, - }; + FieldOrientedController current_control_; float effective_current_lim_ = 10.0f; // [A] + float max_allowed_current_ = 0.0f; // [A] set in setup() + float max_dc_calib_ = 0.0f; // [A] set in setup() + + float* torque_setpoint_src_ = nullptr; // Usually points to the Controller object's output + float* phase_vel_src_ = nullptr; // Usually points to the Encoder object's output + float direction_ = 0.0f; // if -1 then positive torque is converted to negative Iq + float Vd_setpoint_ = NAN; // fed to the FOC + float Vq_setpoint_ = NAN; // fed to the FOC + float Id_setpoint_ = 0.0f; // fed to the FOC + float Iq_setpoint_ = NAN; // fed to the FOC + + PhaseControlLaw<3>* control_law_; }; + #endif // __MOTOR_HPP diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 2a5c9f5c..ca74b553 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -9,15 +9,13 @@ #include #include #include +#include extern "C" { #endif // OS includes #include -//default timeout waiting for phase measurement signals -#define PH_CURRENT_MEAS_TIMEOUT 2 // [ms] - // extern const float elec_rad_per_enc; extern uint32_t _reboot_cookie; @@ -34,14 +32,12 @@ typedef struct { uint32_t min_stack_space_axis; // minimum remaining space since startup [Bytes] uint32_t min_stack_space_usb; uint32_t min_stack_space_uart; - uint32_t min_stack_space_usb_irq; uint32_t min_stack_space_startup; uint32_t min_stack_space_can; uint32_t stack_usage_axis; uint32_t stack_usage_usb; uint32_t stack_usage_uart; - uint32_t stack_usage_usb_irq; uint32_t stack_usage_startup; uint32_t stack_usage_can; @@ -105,6 +101,13 @@ struct BoardConfig_t { PWMMapping_t analog_mappings[GPIO_COUNT]; }; +struct TaskTimes { + TaskTimer sampling; + TaskTimer control_loop_misc; + TaskTimer control_loop_checks; +}; + + // Forward Declarations class Axis; class Motor; @@ -112,11 +115,6 @@ class ODriveCAN; extern ODriveCAN *odCAN; -// if you use the oscilloscope feature you can bump up this value -#define OSCILLOSCOPE_SIZE 4096 -extern float oscilloscope[OSCILLOSCOPE_SIZE]; -extern size_t oscilloscope_pos; - // TODO: move // this is technically not thread-safe but practically it might be #define DEFINE_ENUM_FLAG_OPERATORS(ENUMTYPE) \ @@ -143,6 +141,7 @@ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast(~static_c #include #include #include +#include #include // Defined in autogen/version.c based on git-derived version numbers @@ -164,10 +163,7 @@ public: void erase_configuration() override; void reboot() override { NVIC_SystemReset(); } void enter_dfu_mode() override; - - float get_oscilloscope_val(uint32_t index) override { - return oscilloscope[index]; - } + void clear_errors() override; float get_adc_voltage(uint32_t gpio) override { return ::get_adc_voltage(get_gpio(gpio)); @@ -178,12 +174,18 @@ public: return cnt += delta; } + void do_fast_checks(); + void sampling_cb(); + void control_loop_cb(uint32_t timestamp); + Axis& get_axis(int num) { return axes[num]; } ODriveCAN& get_can() { return *odCAN; } uint32_t get_interrupt_status(int32_t irqn); uint32_t get_dma_status(uint8_t stream_num); + void disarm_with_error(Error error); + Error error_ = ERROR_NONE; float& vbus_voltage_ = ::vbus_voltage; // TODO: make this the actual variable float& ibus_ = ::ibus_; // TODO: make this the actual variable float ibus_report_filter_k_ = 1.0f; @@ -222,12 +224,23 @@ public: bool& brake_resistor_saturated_ = ::brake_resistor_saturated; // TODO: make this the actual variable SystemStats_t system_stats_; + Oscilloscope oscilloscope_{ + &axes[0].motor_.current_control_.v_current_control_integral_d_, // trigger_src + 0.5f, // trigger_threshold + &axes[0].motor_.current_control_.Ialpha_measured_ // data_src + }; BoardConfig_t config_; uint32_t user_config_loaded_ = 0; bool misconfigured_ = false; uint32_t test_property_ = 0; + + uint32_t last_update_timestamp_ = 0; + uint32_t n_evt_sampling_ = 0; + uint32_t n_evt_control_loop_ = 0; + bool task_timers_armed_ = false; + TaskTimes task_times_; }; extern ODrive odrv; // defined in main.cpp diff --git a/Firmware/MotorControl/open_loop_controller.cpp b/Firmware/MotorControl/open_loop_controller.cpp new file mode 100644 index 00000000..b111f41c --- /dev/null +++ b/Firmware/MotorControl/open_loop_controller.cpp @@ -0,0 +1,27 @@ + +#include "open_loop_controller.hpp" +#include + +void OpenLoopController::update(uint32_t timestamp) { + if (std::isnan(Id_setpoint_) || std::isnan(Id_setpoint_) || std::isnan(phase_) || std::isnan(phase_vel_)) { + Id_setpoint_ = 0.0f; + Iq_setpoint_ = 0.0f; + Vd_setpoint_ = 0.0f; + Vq_setpoint_ = 0.0f; + phase_ = 0.0f; + phase_vel_ = 0.0f; + timestamp_ = timestamp; + } + + float dt = (float)(timestamp - timestamp_) / (float)TIM_1_8_CLOCK_HZ; + + Id_setpoint_ = std::clamp(target_current_, Id_setpoint_ - max_current_ramp_ * dt, Id_setpoint_ + max_current_ramp_ * dt); + Iq_setpoint_ = 0.0f; + Vd_setpoint_ = std::clamp(target_voltage_, Vd_setpoint_ - max_voltage_ramp_ * dt, Vd_setpoint_ + max_voltage_ramp_ * dt); + Vq_setpoint_ = 0.0f; + + phase_vel_ = std::clamp(target_vel_, phase_vel_ - max_phase_vel_ramp_ * dt, phase_vel_ + max_phase_vel_ramp_ * dt); + phase_ = wrap_pm_pi(phase_ + phase_vel_ * dt); + total_distance_ += phase_vel_ * dt; + timestamp_ = timestamp; +} diff --git a/Firmware/MotorControl/open_loop_controller.hpp b/Firmware/MotorControl/open_loop_controller.hpp new file mode 100644 index 00000000..143a42e5 --- /dev/null +++ b/Firmware/MotorControl/open_loop_controller.hpp @@ -0,0 +1,32 @@ +#ifndef __OPEN_LOOP_CONTROLLER_HPP +#define __OPEN_LOOP_CONTROLLER_HPP + +#include "component.hpp" +#include + +class OpenLoopController : public ComponentBase { +public: + void update(uint32_t timestamp) final; + + // Config + float max_current_ramp_ = INFINITY; // [A/s] + float max_voltage_ramp_ = INFINITY; // [V/s] + float max_phase_vel_ramp_ = INFINITY; // [rad/s^2] + + // Inputs + float target_vel_ = NAN; + float target_current_ = NAN; + float target_voltage_ = NAN; + + // State/Outputs + uint32_t timestamp_ = 0; + float Id_setpoint_ = NAN; + float Iq_setpoint_ = NAN; + float Vd_setpoint_ = NAN; + float Vq_setpoint_ = NAN; + float phase_ = NAN; + float phase_vel_ = NAN; + float total_distance_ = NAN; +}; + +#endif // __OPEN_LOOP_CONTROLLER_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/oscilloscope.cpp b/Firmware/MotorControl/oscilloscope.cpp new file mode 100644 index 00000000..05d2038f --- /dev/null +++ b/Firmware/MotorControl/oscilloscope.cpp @@ -0,0 +1,29 @@ + +#include "oscilloscope.hpp" + +// if you use the oscilloscope feature you can bump up this value +#define OSCILLOSCOPE_SIZE 4096 + +void Oscilloscope::update() { + // Edit these to suit your capture needs + float trigger_data = trigger_src_ ? *trigger_src_ : 0.0f; + float trigger_threshold = trigger_threshold_; + float sample_data = data_src_ ? *data_src_ : 0.0f; + + static bool ready = false; + static bool capturing = false; + if (trigger_data < trigger_threshold) { + ready = true; + } + if (ready && trigger_data >= trigger_threshold) { + capturing = true; + ready = false; + } + if (capturing) { + data_[pos_] = sample_data; + if (++pos_ >= OSCILLOSCOPE_SIZE) { + pos_ = 0; + capturing = false; + } + } +} diff --git a/Firmware/MotorControl/oscilloscope.hpp b/Firmware/MotorControl/oscilloscope.hpp new file mode 100644 index 00000000..b1a5e016 --- /dev/null +++ b/Firmware/MotorControl/oscilloscope.hpp @@ -0,0 +1,29 @@ +#ifndef __OSCILLOSCOPE_HPP +#define __OSCILLOSCOPE_HPP + +#include + +// if you use the oscilloscope feature you can bump up this value +#define OSCILLOSCOPE_SIZE 4096 + +class Oscilloscope : public ODriveIntf::OscilloscopeIntf { +public: + Oscilloscope(float* trigger_src, float trigger_threshold, float* data_src) + : trigger_src_(trigger_src), trigger_threshold_(trigger_threshold), data_src_(data_src) {} + + float get_val(uint32_t index) override { + return index < OSCILLOSCOPE_SIZE ? data_[index] : NAN; + } + + void update(); + + const uint32_t size_ = OSCILLOSCOPE_SIZE; + const float* trigger_src_; + const float trigger_threshold_; + const float* data_src_; + + float data_[OSCILLOSCOPE_SIZE] = {0}; + size_t pos_ = 0; +}; + +#endif // __OSCILLOSCOPE_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/phase_control_law.hpp b/Firmware/MotorControl/phase_control_law.hpp new file mode 100644 index 00000000..bda2ccdc --- /dev/null +++ b/Firmware/MotorControl/phase_control_law.hpp @@ -0,0 +1,87 @@ +#ifndef __PHASE_CONTROL_LAW_HPP +#define __PHASE_CONTROL_LAW_HPP + +#include +#include + +template +class PhaseControlLaw { +public: + /** + * @brief Called when this controller becomes the active controller. + */ + virtual void reset() = 0; + + /** + * @brief Informs the control law about a new set of measurements. + * + * This function gets called in a high priority interrupt context and should + * run fast. + * + * Beware that all inputs can be NAN. + * + * @param vbus_voltage: The most recently measured DC link voltage. NAN if + * the measurement is not available or valid for some reason. + * @param currents: The most recently measured (or inferred) phase currents + * in Amps. Any of the values can be NAN if the measurement is not + * available or valid for some reason. + * @param input_timestamp: The timestamp (in HCLK ticks) corresponding to + * the vbus_voltage and current measurement. + */ + virtual ODriveIntf::MotorIntf::Error on_measurement(float vbus_voltage, + std::array currents, uint32_t input_timestamp) = 0; + + /** + * @brief Shall calculate the PWM timings for the specified target time. + * + * This function gets called in a high priority interrupt context and should + * run fast. + * + * Beware that this function can be called before a call to on_measurement(). + * + * @param output_timestamp: The timestamp (in HCLK ticks) corresponding to + * the middle of the time span during which the output will be + * active. + * @param pwm_timings: This array referenced by this argument shall be + * filled with the desired PWM timings. Each item corresponds to one + * phase and must lie in [0.0f, 1.0f]. + * The function is not required to return valid PWM timings in case + * of an error. + * @param ibus: The variable pointed to by this argument is set to the + * estimated DC current around the output timestamp when the desired + * PWM timings get applied. + * The function is not required to return a valid I_bus estimate in + * case of an error. + * + * @returns: An error code or ERROR_NONE. If the function returns an error + * the motor gets disarmed with one exception: If the controller + * never returned valid PWM timings since it became active then it + * is allowed to return ERROR_CONTROLLER_INITIALIZING without + * triggering a motor disarm. In this phase the PWMs will not yet + * be truly active. + */ + virtual ODriveIntf::MotorIntf::Error get_output(uint32_t output_timestamp, + float (&pwm_timings)[N_PHASES], + float* ibus) = 0; +}; + +class AlphaBetaFrameController : public PhaseControlLaw<3> { +private: + ODriveIntf::MotorIntf::Error on_measurement(float vbus_voltage, + std::array currents, uint32_t input_timestamp) final; + + ODriveIntf::MotorIntf::Error get_output(uint32_t output_timestamp, + float (&pwm_timings)[3], + float* ibus) final; + +protected: + virtual ODriveIntf::MotorIntf::Error on_measurement( + float vbus_voltage, float Ialpha, float Ibeta, uint32_t input_timestamp) = 0; + + virtual ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, + float* mod_alpha, float* mod_beta, + float* ibus) = 0; +}; + +#endif // __PHASE_CONTROL_LAW_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/sensorless_estimator.cpp b/Firmware/MotorControl/sensorless_estimator.cpp index d70c7d2d..2bbe6e8d 100644 --- a/Firmware/MotorControl/sensorless_estimator.cpp +++ b/Firmware/MotorControl/sensorless_estimator.cpp @@ -10,14 +10,21 @@ bool SensorlessEstimator::update() { // is the one computed two cycles ago. To get the correct measurement, it was stored twice: // once by final_v_alpha/final_v_beta in the current control reporting, and once by V_alpha_beta_memory. + if (std::isnan(flux_state_[0]) || std::isnan(flux_state_[1]) || std::isnan(pll_pos_)) { + // Automatically reset state if it becomes NAN. The state becomes NAN + // when invalid current measurements are processed (e.g. because of the + // opamp being uninitialized). + flux_state_[0] = 0.0f; + flux_state_[1] = 0.0f; + pll_pos_ = 0.0f; + phase_vel_ = 0.0f; + } + // Clarke transform float I_alpha_beta[2] = { -axis_->motor_.current_meas_.phB - axis_->motor_.current_meas_.phC, one_by_sqrt3 * (axis_->motor_.current_meas_.phB - axis_->motor_.current_meas_.phC)}; - // Swap sign of I_beta if motor is reversed - I_alpha_beta[1] *= axis_->motor_.config_.direction; - // alpha-beta vector operations float eta[2]; for (int i = 0; i <= 1; ++i) { @@ -49,8 +56,8 @@ bool SensorlessEstimator::update() { } // Flux state estimation done, store V_alpha_beta for next timestep - V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha; - V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta * axis_->motor_.config_.direction; + V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha_; + V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta_; // PLL // TODO: the PLL part has some code duplication with the encoder PLL @@ -61,19 +68,22 @@ bool SensorlessEstimator::update() { // Check that we don't get problems with discrete time approximation if (!(current_meas_period * pll_kp < 1.0f)) { error_ |= ERROR_UNSTABLE_GAIN; - vel_estimate_valid_ = false; + pll_pos_ = NAN; + phase_ = NAN; + vel_estimate_ = NAN; return false; } // predict PLL phase with velocity - pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * vel_estimate_); + pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * phase_vel_); // update PLL phase with observer permanent magnet phase phase_ = fast_atan2(eta[1], eta[0]); float delta_phase = wrap_pm_pi(phase_ - pll_pos_); pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * pll_kp * delta_phase); // update PLL velocity - vel_estimate_ += current_meas_period * pll_ki * delta_phase; + phase_vel_ += current_meas_period * pll_ki * delta_phase; + + vel_estimate_ = phase_vel_ / (2 * M_PI); - vel_estimate_valid_ = true; return true; }; diff --git a/Firmware/MotorControl/sensorless_estimator.hpp b/Firmware/MotorControl/sensorless_estimator.hpp index 9f59b28a..b15aef25 100644 --- a/Firmware/MotorControl/sensorless_estimator.hpp +++ b/Firmware/MotorControl/sensorless_estimator.hpp @@ -18,8 +18,8 @@ public: Error error_ = ERROR_NONE; float phase_ = 0.0f; // [rad] float pll_pos_ = 0.0f; // [rad] - float vel_estimate_ = 0.0f; // [rad/s] - bool vel_estimate_valid_ = false; + float phase_vel_ = 0.0f; // [rad/s] + float vel_estimate_ = 0.0f; // [turns/s] // float pll_kp_ = 0.0f; // [rad/s / rad] // float pll_ki_ = 0.0f; // [(rad/s^2) / rad] float flux_state_[2] = {0.0f, 0.0f}; // [Vs] diff --git a/Firmware/MotorControl/task_timer.hpp b/Firmware/MotorControl/task_timer.hpp new file mode 100644 index 00000000..a6506de4 --- /dev/null +++ b/Firmware/MotorControl/task_timer.hpp @@ -0,0 +1,65 @@ +#ifndef __TASK_TIMER_HPP +#define __TASK_TIMER_HPP + +#include +#include + +#define MEASURE_START_TIME +#define MEASURE_END_TIME +#define MEASURE_LENGTH +#define MEASURE_MAX_LENGTH + +inline uint16_t sample_TIM13() { + constexpr uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); + return clocks_per_cnt * TIM13->CNT; // TODO: Use a hw_config +} + +struct TaskTimer { + uint32_t start_time_ = 0; + uint32_t end_time_ = 0; + uint32_t length_ = 0; + uint32_t max_length_ = 0; + + static bool enabled; + + uint32_t start() { + return sample_TIM13(); + } + + void stop(uint32_t start_time) { + uint32_t end_time = sample_TIM13(); + uint32_t length = end_time - start_time; + + if (enabled) { +#ifdef MEASURE_START_TIME + start_time_ = start_time; +#endif +#ifdef MEASURE_END_TIME + end_time_ = end_time; +#endif +#ifdef MEASURE_LENGTH + length_ = length; +#endif + } +#ifdef MEASURE_MAX_LENGTH + max_length_ = std::max(max_length_, length); +#endif + } +}; + +struct TaskTimerContext { + TaskTimerContext(const TaskTimerContext&) = delete; + TaskTimerContext(const TaskTimerContext&&) = delete; + void operator=(const TaskTimerContext&) = delete; + void operator=(const TaskTimerContext&&) = delete; + TaskTimerContext(TaskTimer& timer) : timer_(timer), start_time(timer.start()) {} + ~TaskTimerContext() { timer_.stop(start_time); } + + TaskTimer& timer_; + uint32_t start_time; + bool exit_ = false; +}; + +#define MEASURE_TIME(timer) for (TaskTimerContext __task_timer_ctx{timer}; !__task_timer_ctx.exit_; __task_timer_ctx.exit_ = true) + +#endif // __TASK_TIMER_HPP \ No newline at end of file diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 040db512..e8faf520 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -189,7 +189,11 @@ sources = { 'MotorControl/thermistor.cpp', 'MotorControl/encoder.cpp', 'MotorControl/endstop.cpp', + 'MotorControl/async_estimator.cpp', 'MotorControl/controller.cpp', + 'MotorControl/foc.cpp', + 'MotorControl/open_loop_controller.cpp', + 'MotorControl/oscilloscope.cpp', 'MotorControl/sensorless_estimator.cpp', 'MotorControl/trapTraj.cpp', 'MotorControl/pwm_input.cpp', diff --git a/Firmware/communication/can_simple.cpp b/Firmware/communication/can_simple.cpp index a6222cdc..16fac7fd 100644 --- a/Firmware/communication/can_simple.cpp +++ b/Firmware/communication/can_simple.cpp @@ -329,16 +329,16 @@ void CANSimple::get_iq_callback(Axis* axis, can_Message_t& msg) { txmsg.len = 8; uint32_t floatBytes; - static_assert(sizeof axis->motor_.current_control_.Iq_setpoint == sizeof floatBytes); - std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_setpoint, sizeof floatBytes); + static_assert(sizeof axis->motor_.current_control_.Iq_setpoint_ == sizeof floatBytes); + std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_setpoint_, sizeof floatBytes); txmsg.buf[0] = floatBytes; txmsg.buf[1] = floatBytes >> 8; txmsg.buf[2] = floatBytes >> 16; txmsg.buf[3] = floatBytes >> 24; - static_assert(sizeof floatBytes == sizeof axis->motor_.current_control_.Iq_measured); - std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_measured, sizeof floatBytes); + static_assert(sizeof floatBytes == sizeof axis->motor_.current_control_.Iq_measured_); + std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_measured_, sizeof floatBytes); txmsg.buf[4] = floatBytes; txmsg.buf[5] = floatBytes >> 8; txmsg.buf[6] = floatBytes >> 16; @@ -379,7 +379,7 @@ void CANSimple::get_vbus_voltage_callback(Axis* axis, can_Message_t& msg) { } void CANSimple::clear_errors_callback(Axis* axis, can_Message_t& msg) { - axis->clear_errors(); + odrv.clear_errors(); // TODO: might want to clear axis errors only } void CANSimple::send_heartbeat(Axis* axis) { diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index faac6f59..877ffd8d 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -30,9 +30,6 @@ uint64_t serial_number; char serial_number_str[13]; // 12 digits + null termination -float oscilloscope[OSCILLOSCOPE_SIZE] = {0}; -size_t oscilloscope_pos = 0; - /* Private constant data -----------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ diff --git a/Firmware/communication/interface_can.cpp b/Firmware/communication/interface_can.cpp index cf612231..f8fdcd9c 100644 --- a/Firmware/communication/interface_can.cpp +++ b/Firmware/communication/interface_can.cpp @@ -179,7 +179,7 @@ void ODriveCAN::set_error(Error error) { void ODriveCAN::send_heartbeat(Axis *axis) { // Handle heartbeat message if (axis->config_.can_heartbeat_rate_ms > 0) { - uint32_t now = osKernelSysTick(); + uint32_t now = HAL_GetTick(); if ((now - axis->last_heartbeat_) >= axis->config_.can_heartbeat_rate_ms) { switch (config_.protocol) { case PROTOCOL_SIMPLE: diff --git a/Firmware/freertos_vars.h b/Firmware/freertos_vars.h index 64a7e784..e2ba1e46 100644 --- a/Firmware/freertos_vars.h +++ b/Firmware/freertos_vars.h @@ -12,8 +12,6 @@ extern osSemaphoreId sem_usb_tx; extern osSemaphoreId sem_can; extern osThreadId defaultTaskHandle; -extern osThreadId usb_irq_thread; -extern const uint32_t stack_size_usb_irq_thread; extern const uint32_t stack_size_default_task; #endif /* __FREERTOS_H */ \ No newline at end of file diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 700d485d..d5df666f 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -13,6 +13,59 @@ interfaces: The odrv0, odrv1, ... objects that appear in odrivetool implement this toplevel interface. attributes: + error: + nullflag: 'None' + flags: + ControlIterationMissed: + brief: At least one control iteration was missed. + doc: | + The main control loop is supposed to runs at a fixed frequency. + If the device is computationally overloaded (e.g. too many active + components) it's possible that one or more control iterations + are skipped. + DcBusUnderVoltage: + brief: The DC voltage fell below the limit configured in `config.dc_bus_undervoltage_trip_level`. + doc: | + Confirm that your power leads are connected securely. For initial + testing a 12V PSU which can supply a couple of amps should be + sufficient while the use of low current ‘wall wart’ plug packs may + lead to inconsistent behaviour and is not recommended. + + You can monitor your PSU voltage using liveplotter in odrivetool + by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If + you see your votlage drop below `config.dc_bus_undervoltage_trip_level` + (default: ~ 8V) then you will trip this error. Even a relatively + small motor can draw multiple kW momentary and so unless you have + a very large PSU or are running of a battery you may encounter + this error when executing high speed movements with a high current + limit. To limit your PSU power draw you can limit your motor + current and/or velocity limit `controller.config.vel_limit` and + `motor.config.current_lim`. + DcBusOverVoltage: + brief: The DC voltage exceeded the limit configured in `config.dc_bus_overvoltage_trip_level`. + doc: | + Confirm that you have a brake resistor of the correct value + connected securely and that `config.brake_resistance` is set to + the value of your brake resistor. + + You can monitor your PSU voltage using liveplotter in odrivetool + by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If + during a move you see the voltage rise above your PSU’s nominal + set voltage then you have your brake resistance set too low. This + may happen if you are using long wires or small gauge wires to + connect your brake resistor to your odrive which will added extra + resistance. This extra resistance needs to be accounted for to + prevent this voltage spike. If you have checked all your + connections you can also try increasing your brake resistance by + ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake + resistor value. + DcBusOverRegenCurrent: {doc: too much current pushed into the power supply} + DcBusOverCurrent: {doc: too much current pulled out of the power supply} + BrakeDeadtimeViolation: + BrakeDutyCycleNan: +# BrakeResistorDisarmed: +# doc: The brake resistor was unexpectedly disarmed. + vbus_voltage: type: readonly float32 unit: V @@ -46,6 +99,22 @@ interfaces: doc: 0 for official releases, 1 otherwise brake_resistor_armed: readonly bool brake_resistor_saturated: bool + + # Diagnostics & performance monitoring + n_evt_sampling: {type: readonly uint32, doc: Number of input sampling events since startup (modulo 2^32)} + n_evt_control_loop: {type: readonly uint32, doc: Number of control loop iterations since startup (modulo 2^32)} + task_timers_armed: + type: bool + doc: | + Set by a profiling application to trigger sampling of a single + control iteration. Cleared by the device as soon as the sampling + is complete. + task_times: + c_is_class: False + attributes: + sampling: TaskTimer + control_loop_misc: TaskTimer + control_loop_checks: TaskTimer system_stats: c_is_class: False attributes: @@ -55,12 +124,10 @@ interfaces: min_stack_space_usb: readonly uint32 min_stack_space_uart: readonly uint32 min_stack_space_can: readonly uint32 - min_stack_space_usb_irq: readonly uint32 min_stack_space_startup: readonly uint32 stack_usage_axis: readonly uint32 stack_usage_usb: readonly uint32 stack_usage_uart: readonly uint32 - stack_usage_usb_irq: readonly uint32 stack_usage_startup: readonly uint32 stack_usage_can: readonly uint32 usb: @@ -76,6 +143,7 @@ interfaces: addr_match_cnt: readonly uint32 rx_cnt: readonly uint32 error_cnt: readonly uint32 + config: c_is_class: False attributes: @@ -209,6 +277,7 @@ interfaces: gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} user_config_loaded: readonly uint32 misconfigured: + # TODO: make this a system error type: readonly bool doc: | If this property is true, something is bad in the configuration. The @@ -230,12 +299,12 @@ interfaces: axis0: {type: Axis, c_name: get_axis(0)} axis1: {type: Axis, c_name: get_axis(1)} + oscilloscope: {type: Oscilloscope} can: {type: Can, c_name: get_can()} test_property: uint32 functions: test_function: {in: {delta: int32}, out: {cnt: int32}} - get_oscilloscope_val: {in: {index: uint32}, out: {val: float32}} get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}, doc: Reads the ADC voltage of the specified GPIO. The GPIO should be in `GPIO_MODE_ANALOG_IN`.} save_configuration: erase_configuration: @@ -263,6 +332,8 @@ interfaces: bits 1:0: priority (3 is highest priority) 0xffffffff if the specified number is not a valid DMA stream number. doc: Returns information about the specified DMA stream. + clear_errors: + doc: Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. ODrive.Can: c_is_class: True @@ -298,56 +369,7 @@ interfaces: tried to run encoder calibration or closed loop control before the motor was calibrated, or you tried to run closed loop control before the encoder was calibrated. - DcBusUnderVoltage: - brief: The DC voltage fell below the limit configured in `config.dc_bus_undervoltage_trip_level`. - doc: | - Confirm that your power leads are connected securely. For initial - testing a 12V PSU which can supply a couple of amps should be - sufficient while the use of low current ‘wall wart’ plug packs may - lead to inconsistent behaviour and is not recommended. - - You can monitor your PSU voltage using liveplotter in odrivetool - by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If - you see your votlage drop below `config.dc_bus_undervoltage_trip_level` - (default: ~ 8V) then you will trip this error. Even a relatively - small motor can draw multiple kW momentary and so unless you have - a very large PSU or are running of a battery you may encounter - this error when executing high speed movements with a high current - limit. To limit your PSU power draw you can limit your motor - current and/or velocity limit `controller.config.vel_limit` and - `motor.config.current_lim`. - DcBusOverVoltage: - brief: The DC voltage exceeded the limit configured in `config.dc_bus_overvoltage_trip_level`. - doc: | - Confirm that you have a brake resistor of the correct value - connected securely and that `config.brake_resistance` is set to - the value of your brake resistor. - - You can monitor your PSU voltage using liveplotter in odrivetool - by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If - during a move you see the voltage rise above your PSU’s nominal - set voltage then you have your brake resistance set too low. This - may happen if you are using long wires or small gauge wires to - connect your brake resistor to your odrive which will added extra - resistance. This extra resistance needs to be accounted for to - prevent this voltage spike. If you have checked all your - connections you can also try increasing your brake resistance by - ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake - resistor value. - CurrentMeasurementTimeout: - BrakeResistorDisarmed: - doc: The brake resistor was unexpectedly disarmed. - MotorDisarmed: - doc: The motor was unexpectedly disarmed. - MotorFailed: - doc: Check `motor.error` for more information. - SensorlessEstimatorFailed: - EncoderFailed: - doc: Check `encoder.error` for more information. - ControllerFailed: - PosCtrlDuringSensorless: - status: deprecated - WatchdogTimerExpired: + WatchdogTimerExpired: {bit: 11} MinEndstopPressed: MaxEndstopPressed: EstopRequested: @@ -360,13 +382,6 @@ interfaces: current_state: readonly AxisState requested_state: AxisState loop_counter: readonly uint32 - lockin_state: - typeargs: {fibre.Property.mode: readonly} - values: - Inactive: - Ramp: - Accelerate: - ConstVel: is_homed: {type: bool, c_name: homing_.is_homed} config: c_is_class: False @@ -383,9 +398,6 @@ interfaces: startup_closed_loop_control: type: bool doc: enable closed loop control after calibration/startup - startup_sensorless_control: - type: bool - doc: enable sensorless control after calibration/startup startup_homing: type: bool doc: enable homing after calibration/startup @@ -399,6 +411,7 @@ interfaces: This is ignored if enable_step_dir is false. This setting only takes effect on a state transition into idle or out of closed loop control. + enable_sensorless_mode: bool turns_per_step: float32 watchdog_timeout: type: float32 @@ -450,15 +463,30 @@ interfaces: motor: Motor controller: Controller encoder: Encoder + async_estimator: AsyncEstimator sensorless_estimator: SensorlessEstimator trap_traj: TrapezoidalTrajectory min_endstop: Endstop max_endstop: Endstop + task_times: + c_is_class: False + attributes: + thermistor_update: TaskTimer + encoder_update: TaskTimer + sensorless_estimator_update: TaskTimer + endstop_update: TaskTimer + can_heartbeat: TaskTimer + controller_update: TaskTimer + open_loop_controller_update: TaskTimer + async_estimator_update: TaskTimer + motor_update: TaskTimer + current_controller_update: TaskTimer + dc_calib: TaskTimer + current_sense: TaskTimer + pwm_update: TaskTimer functions: watchdog_feed: doc: Feed the watchdog to prevent watchdog timeouts. - clear_errors: - doc: Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. ODrive.Axis.LockinConfig: c_is_class: False @@ -492,7 +520,10 @@ interfaces: c_is_class: True attributes: error: ThermistorCurrentLimiter.Error - temperature: readonly float32 + temperature: + type: readonly float32 + unit: °C + doc: NaN while the ODrive is initializing. config: c_is_class: False attributes: @@ -508,7 +539,10 @@ interfaces: c_is_class: True attributes: error: ThermistorCurrentLimiter.Error - temperature: readonly float32 + temperature: + type: readonly float32 + unit: °C + doc: NaN while the ODrive is initializing. config: c_is_class: False attributes: @@ -566,8 +600,8 @@ interfaces: brief: The measured motor phase inductance is outside of the plausible range. doc: | See `PhaseResistanceOutOfRange` for details. - AdcFailed: DrvFault: + bit: 3 brief: The gate driver chip reported an error. doc: | The ODrive v3.4 is known to have a hardware issue whereby the @@ -584,9 +618,8 @@ interfaces: [this post](https://discourse.odriverobotics.com/t/drv-fault-on-odrive-v3-4/558) for instructions for a hardware fix. ControlDeadlineMissed: - NotImplementedMotorType: - BrakeCurrentOutOfRange: ModulationMagnitude: + bit: 7 doc: | The bus voltage was insufficent to push the requested current through the motor. @@ -597,63 +630,71 @@ interfaces: For gimbal motors, it is recommended to set the `config.calibration_current` and `config.current_lim` to half your bus voltage, or less. - BrakeDeadtimeViolation: - UnexpectedTimerCallback: - CurrentSenseSaturation: + CurrentSenseSaturation: {bit: 10} CurrentLimitViolation: {bit: 12} - BrakeDutyCycleNan: - DcBusOverRegenCurrent: {doc: too much current pushed into the power supply} - DcBusOverCurrent: {doc: too much current pulled out of the power supply} - ModulationIsNan: - armed_state: - typeargs: {fibre.Property.mode: readonly} - values: - Disarmed: - WaitingForTimings: - WaitingForUpdate: - Armed: + ModulationIsNan: {bit: 16} + TimerUpdateMissed: {doc: A timer update event was missed. Perhaps the previous timer update took too much time. This is not expected in official release firmware.} + CurrentMeasurementUnavailable: {doc: The phase current measurement is not available. The ADC failed to sample the current sensor in time. This is not expected in official release firmware.} + ControllerFailed: {doc: The motor was disarmed because the underlying controller failed. Usually this is the FOC controller.} + ILeakOutOfRange: {doc: '`i_leak` exceeded `config.max_leak_current`. This can happen if there is a short from a motor phase to DC- or DC+.'} + IBusOutOfRange: + doc: | + The DC current sourced/sunk by this motor exceeded the configured + hard limits. More specifically `i_bus` fell outside of the range + `config.i_bus_hard_min` ... `config.i_bus_hard_max`. + BrakeResistorDisarmed: {doc: An attempt was made to run the motor PWM while the brake resistor was enabled but disarmed.} + SystemLevel: + doc: | + The motor had to be disarmed because of a system level error. + See `ODrive.Error` for more details. + BadTiming: {doc: The main control loop got out of sync with the motor control loop. This could indicate that the main control loop got stuck.} + UnknownPhase: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} + UnknownCurrent: {doc: The current controller did not get a valid current measurement or setpoint. Maybe you didn't configure the controller correctly or there is a low level system issue.} + UnknownVbusVoltage: {doc: The current controller did not get a valid `vbus_voltage` measurement.} + ControllerInitializing: {doc: Internal value used while the controller is not yet ready to generate PWM timings.} + is_armed: readonly bool is_calibrated: readonly bool + current_meas_phA: {type: readonly float32, c_name: current_meas_.phA} current_meas_phB: {type: readonly float32, c_name: current_meas_.phB} current_meas_phC: {type: readonly float32, c_name: current_meas_.phC} + DC_calib_phA: {type: float32, c_name: DC_calib_.phA} DC_calib_phB: {type: float32, c_name: DC_calib_.phB} DC_calib_phC: {type: float32, c_name: DC_calib_.phC} + I_leak: {type: readonly float32, unit: A} + I_bus: {type: readonly float32, unit: A} phase_current_rev_gain: float32 effective_current_lim: readonly float32 + max_allowed_current: + type: readonly float32 + unit: A + doc: | + Indicates the maximum current that can be measured by the current + sensors in the current hardware configuration. This value depends on + `config.requested_current_range`. + max_dc_calib: {type: readonly float32, unit: A} current_control: - c_is_class: False + c_is_class: True attributes: p_gain: float32 i_gain: float32 + I_measured_report_filter_k: float32 + Id_setpoint: readonly float32 + Iq_setpoint: readonly float32 + Vd_setpoint: readonly float32 + Vq_setpoint: readonly float32 + phase: readonly float32 + phase_vel: readonly float32 + Ialpha_measured: readonly float32 + Ibeta_measured: readonly float32 + Id_measured: readonly float32 + Iq_measured: readonly float32 v_current_control_integral_d: float32 v_current_control_integral_q: float32 - Ibus: float32 - final_v_alpha: float32 - final_v_beta: float32 - Id_setpoint: float32 - Iq_setpoint: readonly float32 - Iq_measured: float32 - Id_measured: float32 - I_measured_report_filter_k: float32 - max_allowed_current: readonly float32 - overcurrent_trip_level: readonly float32 - acim_rotor_flux: float32 - async_phase_vel: readonly float32 - async_phase_offset: float32 - timing_log: - c_is_class: False - attributes: - general: {type: readonly uint16, c_name: 'get(0)'} - adc_cb_i: {type: readonly uint16, c_name: 'get(1)'} - adc_cb_dc: {type: readonly uint16, c_name: 'get(2)'} - meas_r: {type: readonly uint16, c_name: 'get(3)'} - meas_l: {type: readonly uint16, c_name: 'get(4)'} - enc_calib: {type: readonly uint16, c_name: 'get(5)'} - idx_search: {type: readonly uint16, c_name: 'get(6)'} - foc_voltage: {type: readonly uint16, c_name: 'get(7)'} - foc_current: {type: readonly uint16, c_name: 'get(8)'} - spi_start: {type: readonly uint16, c_name: 'get(9)'} - sample_now: {type: readonly uint16, c_name: 'get(10)'} - spi_end: {type: readonly uint16, c_name: 'get(11)'} + final_v_alpha: readonly float32 + final_v_beta: readonly float32 + n_evt_current_measurement: {type: readonly uint32, doc: Number of current measurement events since startup (modulo 2^32)} + n_evt_pwm_update: {type: readonly uint32, doc: Number of PWM update events since startup (modulo 2^32)} + config: c_is_class: False attributes: @@ -664,7 +705,6 @@ interfaces: phase_inductance: {type: float32, c_setter: set_phase_inductance} phase_resistance: {type: float32, c_setter: set_phase_resistance} torque_constant: float32 - direction: int32 motor_type: MotorType current_lim: float32 current_lim_margin: float32 @@ -673,7 +713,6 @@ interfaces: inverter_temp_limit_upper: float32 requested_current_range: float32 current_control_bandwidth: {type: float32, c_setter: set_current_control_bandwidth} - acim_slip_velocity: float32 acim_gain_min_flux: float32 acim_autoflux_min_Id: float32 acim_autoflux_enable: bool @@ -681,6 +720,54 @@ interfaces: acim_autoflux_decay_gain: float32 R_wL_FF_enable: bool bEMF_FF_enable: bool + I_bus_hard_min: + type: float32 + unit: A + doc: | + If the controller fails to keep this motor's DC current (`I_bus`) + above this value the motor gets disarmed immediately. Most likely + you want a negative value here. Set to -inf to disable. Take noise + into account when chosing a value. + I_bus_hard_max: + type: float32 + unit: A + doc: | + If the controller fails to keep this motor's DC current (`I_bus`) + below this value the motor gets disarmed immediately. Usually this + is set in conjunction with `I_bus_hard_min`. Set to inf to disable. + Take noise into account when chosing a value. + I_leak_max: + type: float32 + unit: A + doc: | + In almost all scenarios, the currents on phase A, B and C should + add up to zero. A small amount of measurement noise is expected. + However if the sum of A, B, C currents exceeds this configuration + value, the motor gets disarmed immediately. + + Note that this feature is only works on devices with three current + sensors. + dc_calib_tau: float32 + + ODrive.Oscilloscope: + c_is_class: True + attributes: + size: readonly uint32 + functions: + get_val: {in: {index: uint32}, out: {val: float32}} + + ODrive.AsyncEstimator: + c_is_class: True + attributes: + rotor_flux: {type: readonly float32, unit: A, doc: estimated magnitude of the rotor flux} + slip_vel: {type: readonly float32, unit: rad/s, doc: estimated slip between physical and electrical angular velocity} + phase_offset: {type: readonly float32, unit: rad, doc: estimate offset between physical and electrical angular position} + stator_phase_vel: {type: readonly float32, unit: rad/s, doc: calculated setpoint for the electrical velocity} + stator_phase: {type: readonly float32, unit: rad, doc: calculated setpoint for the electrical phase} + config: + c_is_class: False + attributes: + slip_velocity: float32 ODrive.Controller: c_is_class: True @@ -856,12 +943,12 @@ interfaces: offset: int32 pre_calibrated: {type: bool, c_setter: set_pre_calibrated} offset_float: float32 + direction: int32 enable_phase_interpolation: bool bandwidth: {type: float32, c_setter: set_bandwidth} calib_range: float32 calib_scan_distance: float32 calib_scan_omega: float32 - idx_search_unidirectional: bool ignore_illegal_hall_state: bool sincos_gpio_pin_sin: type: uint16 @@ -880,9 +967,10 @@ interfaces: nullflag: None flags: UnstableGain: - phase: float32 - pll_pos: float32 - vel_estimate: float32 + phase: {type: float32, unit: rad} + pll_pos: {type: float32, unit: rad} + phase_vel: {type: float32, unit: rad/s} + vel_estimate: {type: float32, unit: turns/s} # pll_kp: float32 # pll_ki: float32 config: @@ -917,6 +1005,13 @@ interfaces: is_active_high: bool debounce_ms: {type: uint32, c_setter: set_debounce_ms} + ODrive.TaskTimer: + c_is_class: True + attributes: + start_time: readonly uint32 + end_time: readonly uint32 + length: readonly uint32 + max_length: uint32 valuetypes: ODrive.GpioMode: @@ -969,14 +1064,10 @@ valuetypes: don't have to run the motor calibration on the next start up. * This modifies the variables `motor.config.phase_resistance` and `motor.config.phase_inductance`. - SensorlessControl: - brief: Run sensorless control. - doc: | - * The motor must be calibrated (`motor.is_calibrated`) - * `controller.config.control_mode` must be `True`. EncoderIndexSearch: brief: Turn the motor in one direction until the encoder index is traversed. doc: This state can only be entered if `encoder.config.use_index` is `True`. + value: 6 EncoderOffsetCalibration: brief: Turn the motor in one direction for a few seconds and then back to measure the offset between the encoder position and the electrical phase. doc: | diff --git a/Firmware/syscalls.c b/Firmware/syscalls.c index eff893dc..1c21082c 100644 --- a/Firmware/syscalls.c +++ b/Firmware/syscalls.c @@ -5,11 +5,8 @@ ****************************************************************************** */ -#include -#include #include -#include -#include +#include //int _read(int file, char *data, int len) {} @@ -41,8 +38,8 @@ void* heap_end_ptr = 0; */ intptr_t _sbrk(size_t size) { intptr_t ptr; - vTaskSuspendAll(); { + uint32_t mask = cpu_enter_critical(); if (!heap_end_ptr) heap_end_ptr = _end_ptr; if (heap_end_ptr + size > _heap_end_max_ptr) { @@ -51,8 +48,8 @@ intptr_t _sbrk(size_t size) { ptr = (intptr_t)heap_end_ptr; heap_end_ptr += size; } + cpu_exit_critical(mask); } - (void)xTaskResumeAll(); return ptr; } diff --git a/docs/commands.md b/docs/commands.md index 1b41b30f..f19d5f33 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -31,7 +31,6 @@ The ODrive will sequence all enabled startup actions selected in the order shown * `.config.startup_encoder_index_search` * `.config.startup_encoder_offset_calibration` * `.config.startup_closed_loop_control` -* `.config.startup_sensorless_control` See [here](api/odrive.axis.axisstate) for a description of each state. @@ -91,11 +90,11 @@ odrv0.axis0.controller.config.vel_gain = 0.01 odrv0.axis0.controller.config.vel_integrator_gain = 0.05 odrv0.axis0.controller.config.control_mode = 2 odrv0.axis0.controller.input_vel = 400 -odrv0.axis0.motor.config.direction = 1 odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / ( * ) +odrv0.axis0.config.enable_sensorless_mode = True ``` To start the motor: ``` -.requested_state = AXIS_STATE_SENSORLESS_CONTROL +.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL ``` diff --git a/docs/encoders.md b/docs/encoders.md index 06cfb12b..8af94f43 100644 --- a/docs/encoders.md +++ b/docs/encoders.md @@ -21,7 +21,7 @@ To verify everything went well, check the following variables: * `.error` should be 0. * `.encoder.config.offset` - This should print a number, like -326 or 1364. - * `.motor.config.direction` - This should print 1 or -1. + * `.encoder.config.direction` - This should print 1 or -1. ### Encoder with index signal If you have an encoder with an index (Z) signal, you can avoid doing the offset calibration on every startup, and instead use the index signal to re-sync the encoder to a stored calibration. diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index cff886bd..6445a530 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -28,7 +28,6 @@ AXIS_STATE_IDLE = 1 AXIS_STATE_STARTUP_SEQUENCE = 2 AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3 AXIS_STATE_MOTOR_CALIBRATION = 4 -AXIS_STATE_SENSORLESS_CONTROL = 5 AXIS_STATE_ENCODER_INDEX_SEARCH = 6 AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7 AXIS_STATE_CLOSED_LOOP_CONTROL = 8 @@ -70,6 +69,16 @@ MOTOR_TYPE_HIGH_CURRENT = 0 MOTOR_TYPE_GIMBAL = 2 MOTOR_TYPE_ACIM = 3 +# ODrive.Error +ODRIVE_ERROR_NONE = 0x00000000 +ODRIVE_ERROR_CONTROL_ITERATION_MISSED = 0x00000001 +ODRIVE_ERROR_DC_BUS_UNDER_VOLTAGE = 0x00000002 +ODRIVE_ERROR_DC_BUS_OVER_VOLTAGE = 0x00000004 +ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT = 0x00000008 +ODRIVE_ERROR_DC_BUS_OVER_CURRENT = 0x00000010 +ODRIVE_ERROR_BRAKE_DEADTIME_VIOLATION = 0x00000020 +ODRIVE_ERROR_BRAKE_DUTY_CYCLE_NAN = 0x00000040 + # ODrive.Can.Error CAN_ERROR_NONE = 0x00000000 CAN_ERROR_DUPLICATE_CAN_IDS = 0x00000001 @@ -77,16 +86,6 @@ CAN_ERROR_DUPLICATE_CAN_IDS = 0x00000001 # ODrive.Axis.Error AXIS_ERROR_NONE = 0x00000000 AXIS_ERROR_INVALID_STATE = 0x00000001 -AXIS_ERROR_DC_BUS_UNDER_VOLTAGE = 0x00000002 -AXIS_ERROR_DC_BUS_OVER_VOLTAGE = 0x00000004 -AXIS_ERROR_CURRENT_MEASUREMENT_TIMEOUT = 0x00000008 -AXIS_ERROR_BRAKE_RESISTOR_DISARMED = 0x00000010 -AXIS_ERROR_MOTOR_DISARMED = 0x00000020 -AXIS_ERROR_MOTOR_FAILED = 0x00000040 -AXIS_ERROR_SENSORLESS_ESTIMATOR_FAILED = 0x00000080 -AXIS_ERROR_ENCODER_FAILED = 0x00000100 -AXIS_ERROR_CONTROLLER_FAILED = 0x00000200 -AXIS_ERROR_POS_CTRL_DURING_SENSORLESS = 0x00000400 AXIS_ERROR_WATCHDOG_TIMER_EXPIRED = 0x00000800 AXIS_ERROR_MIN_ENDSTOP_PRESSED = 0x00001000 AXIS_ERROR_MAX_ENDSTOP_PRESSED = 0x00002000 @@ -94,36 +93,28 @@ AXIS_ERROR_ESTOP_REQUESTED = 0x00004000 AXIS_ERROR_HOMING_WITHOUT_ENDSTOP = 0x00020000 AXIS_ERROR_OVER_TEMP = 0x00040000 -# ODrive.Axis.LockinState -LOCKIN_STATE_INACTIVE = 0 -LOCKIN_STATE_RAMP = 1 -LOCKIN_STATE_ACCELERATE = 2 -LOCKIN_STATE_CONST_VEL = 3 - # ODrive.Motor.Error MOTOR_ERROR_NONE = 0x00000000 MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE = 0x00000001 MOTOR_ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE = 0x00000002 -MOTOR_ERROR_ADC_FAILED = 0x00000004 MOTOR_ERROR_DRV_FAULT = 0x00000008 MOTOR_ERROR_CONTROL_DEADLINE_MISSED = 0x00000010 -MOTOR_ERROR_NOT_IMPLEMENTED_MOTOR_TYPE = 0x00000020 -MOTOR_ERROR_BRAKE_CURRENT_OUT_OF_RANGE = 0x00000040 MOTOR_ERROR_MODULATION_MAGNITUDE = 0x00000080 -MOTOR_ERROR_BRAKE_DEADTIME_VIOLATION = 0x00000100 -MOTOR_ERROR_UNEXPECTED_TIMER_CALLBACK = 0x00000200 MOTOR_ERROR_CURRENT_SENSE_SATURATION = 0x00000400 MOTOR_ERROR_CURRENT_LIMIT_VIOLATION = 0x00001000 -MOTOR_ERROR_BRAKE_DUTY_CYCLE_NAN = 0x00002000 -MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT = 0x00004000 -MOTOR_ERROR_DC_BUS_OVER_CURRENT = 0x00008000 MOTOR_ERROR_MODULATION_IS_NAN = 0x00010000 - -# ODrive.Motor.ArmedState -ARMED_STATE_DISARMED = 0 -ARMED_STATE_WAITING_FOR_TIMINGS = 1 -ARMED_STATE_WAITING_FOR_UPDATE = 2 -ARMED_STATE_ARMED = 3 +MOTOR_ERROR_TIMER_UPDATE_MISSED = 0x00020000 +MOTOR_ERROR_CURRENT_MEASUREMENT_UNAVAILABLE = 0x00040000 +MOTOR_ERROR_CONTROLLER_FAILED = 0x00080000 +MOTOR_ERROR_I_LEAK_OUT_OF_RANGE = 0x00100000 +MOTOR_ERROR_I_BUS_OUT_OF_RANGE = 0x00200000 +MOTOR_ERROR_BRAKE_RESISTOR_DISARMED = 0x00400000 +MOTOR_ERROR_SYSTEM_LEVEL = 0x00800000 +MOTOR_ERROR_BAD_TIMING = 0x01000000 +MOTOR_ERROR_UNKNOWN_PHASE = 0x02000000 +MOTOR_ERROR_UNKNOWN_CURRENT = 0x04000000 +MOTOR_ERROR_UNKNOWN_VBUS_VOLTAGE = 0x08000000 +MOTOR_ERROR_CONTROLLER_INITIALIZING = 0x10000000 # ODrive.Controller.Error CONTROLLER_ERROR_NONE = 0x00000000 diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index a146871a..e6fcb072 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -87,6 +87,7 @@ def launch_shell(args, logger, app_shutdown_token): 'oscilloscope_dump': oscilloscope_dump, 'dump_interrupts': dump_interrupts, 'dump_dma': dump_dma, + 'dump_timing': dump_timing, 'BulkCapture': BulkCapture, 'step_and_plot': step_and_plot, 'calculate_thermistor_coeffs': calculate_thermistor_coeffs, diff --git a/tools/odrive/tests/analog_input_test.py b/tools/odrive/tests/analog_input_test.py index 3e4041e9..85bd70ae 100644 --- a/tools/odrive/tests/analog_input_test.py +++ b/tools/odrive/tests/analog_input_test.py @@ -106,7 +106,7 @@ class TestAnalogInput(): full_range = abs(max_val - min_val) slope, offset, fitted_curve = fit_sawtooth(data, min_val, max_val, sigma=30) test_assert_eq(slope, (max_val - min_val) / period, accuracy=0.005) - test_curve_fit(data, fitted_curve, max_mean_err = full_range * 0.02, inlier_range = full_range * 0.05, max_outliers = len(data[:,0]) * 0.02) + test_curve_fit(data, fitted_curve, max_mean_err = full_range * 0.03, inlier_range = full_range * 0.05, max_outliers = len(data[:,0]) * 0.02) diff --git a/tools/odrive/tests/calibration_test.py b/tools/odrive/tests/calibration_test.py index ff2585bf..283aa1a4 100644 --- a/tools/odrive/tests/calibration_test.py +++ b/tools/odrive/tests/calibration_test.py @@ -35,7 +35,7 @@ class TestMotorCalibration(): axis_ctx.handle.motor.config.pre_calibrated = False axis_ctx.handle.config.enable_watchdog = False - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run calibration request_state(axis_ctx, AXIS_STATE_MOTOR_CALIBRATION) @@ -69,13 +69,12 @@ class TestDisconnectedMotorCalibration(): axis_ctx.handle.motor.config.phase_inductance = 0.0 axis_ctx.handle.motor.config.pre_calibrated = False - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_MOTOR_CALIBRATION) time.sleep(6) test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) - test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_MOTOR_FAILED) test_assert_eq(axis_ctx.handle.motor.error, MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE) @@ -107,10 +106,10 @@ class TestEncoderDirFind(): axis_ctx.handle.motor.config.pre_calibrated = True # Set calibration settings - axis_ctx.handle.motor.config.direction = 0 + axis_ctx.handle.encoder.config.direction = 0 axis_ctx.handle.config.calibration_lockin.vel = 12.566 # 2 electrical revolutions per second - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_ENCODER_DIR_FIND) @@ -120,7 +119,7 @@ class TestEncoderDirFind(): test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) - test_assert_eq(axis_ctx.handle.motor.config.direction in [-1, 1], True) + test_assert_eq(axis_ctx.handle.encoder.config.direction in [-1, 1], True) class TestEncoderOffsetCalibration(): @@ -151,12 +150,12 @@ class TestEncoderOffsetCalibration(): axis_ctx.handle.motor.config.pre_calibrated = True # Set calibration settings - axis_ctx.handle.motor.config.direction = 0 + axis_ctx.handle.encoder.config.direction = 0 axis_ctx.handle.encoder.config.use_index = False axis_ctx.handle.encoder.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second axis_ctx.handle.encoder.config.calib_scan_distance = 50.265 # 8 revolutions - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_ENCODER_OFFSET_CALIBRATION) @@ -167,7 +166,7 @@ class TestEncoderOffsetCalibration(): test_assert_no_error(axis_ctx) test_assert_eq(axis_ctx.handle.encoder.is_ready, True) - test_assert_eq(axis_ctx.handle.motor.config.direction in [-1, 1], True) + test_assert_eq(axis_ctx.handle.encoder.config.direction in [-1, 1], True) class TestEncoderIndexSearch(): @@ -208,7 +207,7 @@ class TestEncoderIndexSearch(): # Set calibration settings axis_ctx.handle.config.calibration_lockin.vel = 12.566 # 2 electrical revolutions per second - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() # run test request_state(axis_ctx, AXIS_STATE_ENCODER_INDEX_SEARCH) diff --git a/tools/odrive/tests/can_test.py b/tools/odrive/tests/can_test.py index a9275e24..201fd4fc 100644 --- a/tools/odrive/tests/can_test.py +++ b/tools/odrive/tests/can_test.py @@ -116,7 +116,7 @@ class TestSimpleCAN(): axis = odrive.handle.axis0 axis.config.enable_watchdog = False - axis.clear_errors() + odrive.handle.clear_errors() axis.config.can_node_id = node_id axis.config.can_node_id_extended = extended_id time.sleep(0.1) @@ -125,6 +125,7 @@ class TestSimpleCAN(): def my_req(cmd_name, **kwargs): return asyncio.run(request(canbus.handle, node_id, extended_id, cmd_name, **kwargs)) def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was sent + logger.debug('sending request...') test_assert_eq(my_req('get_vbus_voltage')['vbus_voltage'], odrive.handle.vbus_voltage, accuracy=0.01) my_cmd('set_node_id', node_id=node_id+20) diff --git a/tools/odrive/tests/closed_loop_test.py b/tools/odrive/tests/closed_loop_test.py index 5e684d02..200e9258 100644 --- a/tools/odrive/tests/closed_loop_test.py +++ b/tools/odrive/tests/closed_loop_test.py @@ -39,14 +39,14 @@ class TestClosedLoopControlBase(): axis_ctx.handle.motor.config.pre_calibrated = True # Set calibration settings - axis_ctx.handle.motor.config.direction = 0 + axis_ctx.handle.encoder.config.direction = 0 axis_ctx.handle.encoder.config.use_index = False axis_ctx.handle.encoder.config.calib_scan_omega = 12.566 # 2 electrical revolutions per second axis_ctx.handle.encoder.config.calib_scan_distance = 50.265 # 8 revolutions axis_ctx.handle.encoder.config.bandwidth = 1000 - axis_ctx.handle.clear_errors() + axis_ctx.parent.handle.clear_errors() logger.debug('Calibrating encoder offset...') request_state(axis_ctx, AXIS_STATE_ENCODER_OFFSET_CALIBRATION) @@ -76,7 +76,7 @@ class TestClosedLoopControl(TestClosedLoopControlBase): def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): - nominal_rps = 1.0 + nominal_rps = 3.0 nominal_vel = nominal_rps logger.debug(f'Testing closed loop velocity control at {nominal_rps} rounds/s...') @@ -85,6 +85,8 @@ class TestClosedLoopControl(TestClosedLoopControlBase): axis_ctx.handle.controller.input_vel = 0 request_state(axis_ctx, AXIS_STATE_CLOSED_LOOP_CONTROL) + axis_ctx.handle.controller.config.vel_limit = nominal_vel + axis_ctx.handle.controller.config.vel_limit_tolerance = 2 axis_ctx.handle.controller.input_vel = nominal_vel data = record_log(lambda: [axis_ctx.handle.encoder.vel_estimate, axis_ctx.handle.encoder.pos_estimate], duration=5.0) @@ -109,6 +111,7 @@ class TestClosedLoopControl(TestClosedLoopControlBase): axis_ctx.handle.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL axis_ctx.handle.controller.input_pos = 0 + axis_ctx.handle.controller.input_vel = 0 # turn off velocity feed-forward axis_ctx.handle.controller.config.vel_limit = 5.0 # max 5 rps axis_ctx.handle.encoder.set_linear_count(0) @@ -177,7 +180,7 @@ class TestRegenProtection(TestClosedLoopControlBase): def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): nominal_rps = 15.0 - max_current = 30.0 + max_current = 20.0 # Accept a bit of noise on Ibus axis_ctx.parent.handle.config.dc_max_negative_current = -0.5 @@ -209,10 +212,11 @@ class TestRegenProtection(TestClosedLoopControlBase): # ... and brake axis_ctx.parent.handle.config.dc_max_negative_current = -0.2 - axis_ctx.handle.controller.input_vel = 0 # this should fail almost instantaneously + axis_ctx.handle.controller.input_vel = 10 # this should fail almost instantaneously time.sleep(0.1) - test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_MOTOR_DISARMED | AXIS_ERROR_BRAKE_RESISTOR_DISARMED) - test_assert_eq(axis_ctx.handle.motor.error, MOTOR_ERROR_DC_BUS_OVER_REGEN_CURRENT) + test_assert_eq(axis_ctx.parent.handle.error, ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT) + test_assert_eq(axis_ctx.handle.motor.error & MOTOR_ERROR_SYSTEM_LEVEL, MOTOR_ERROR_SYSTEM_LEVEL) + test_assert_eq(axis_ctx.handle.error, 0) class TestVelLimitInTorqueControl(TestClosedLoopControlBase): @@ -231,7 +235,7 @@ class TestVelLimitInTorqueControl(TestClosedLoopControlBase): axis_ctx.handle.controller.config.vel_gain /= 10 # reduce the slope to make it easier to see what's going on vel_gain = axis_ctx.handle.controller.config.vel_gain - direction = axis_ctx.handle.motor.config.direction + direction = axis_ctx.handle.encoder.config.direction logger.debug(f'vel gain is {vel_gain}') axis_ctx.handle.controller.config.vel_limit = max_vel diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index d6ade57e..29cbc794 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -50,11 +50,6 @@ class TestEncoderBase(): test_assert_eq(slope, true_cps, accuracy=0.005) test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) - # encoder.phase - slope, offset, fitted_curve = fit_sawtooth(data[:,(0,3)], pi if reverse else -pi, -pi if reverse else pi, sigma=5) - test_assert_eq(slope / 7, 2*pi*true_rps, accuracy=0.05) - test_curve_fit(data[:,(0,3)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) - # encoder.pos_estimate slope, offset, fitted_curve = fit_line(data[:,(0,4)]) test_assert_eq(slope, true_cps, accuracy=0.005) @@ -68,7 +63,7 @@ class TestEncoderBase(): # encoder.vel_estimate slope, offset, fitted_curve = fit_line(data[:,(0,6)]) test_assert_eq(slope, 0.0, range = true_cpr * abs(true_rps) * 0.01) - test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.02) + test_assert_eq(offset, true_cpr * true_rps, accuracy = 0.03) test_curve_fit(data[:,(0,6)], fitted_curve, max_mean_err = true_cpr * 0.05, inlier_range = true_cpr * 0.05 * noise, max_outliers = len(data[:,0]) * 0.05) @@ -189,11 +184,11 @@ class TestSinCosEncoder(TestEncoderBase): enc.parent.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN enc.parent.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN enc.handle.config.mode = ENCODER_MODE_SINCOS + enc.handle.config.bandwidth = 100 enc.parent.save_config_and_reboot() else: time.sleep(1.0) # wait for PLLs to stabilize - enc.handle.config.bandwidth = 100 self.run_generic_encoder_test(enc.handle, 6283, 1.0, 2.0) diff --git a/tools/odrive/tests/integration_test.py b/tools/odrive/tests/integration_test.py index da9464e9..a062af2e 100644 --- a/tools/odrive/tests/integration_test.py +++ b/tools/odrive/tests/integration_test.py @@ -109,7 +109,7 @@ class TestSimpleCANClosedLoop(): # run calibration axis_ctx.handle.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE - while axis_ctx.handle.current_state != AXIS_STATE_IDLE: + while axis_ctx.handle.requested_state != AXIS_STATE_UNDEFINED or axis_ctx.handle.current_state != AXIS_STATE_IDLE: time.sleep(1) test_assert_eq(axis_ctx.handle.current_state, AXIS_STATE_IDLE) test_assert_no_error(axis_ctx) @@ -143,20 +143,13 @@ class TestSimpleCANClosedLoop(): # this test is a sanity check to make sure that closed loop operation works # actual testing of closed loop functionality should be tested using closed_loop_test.py - # make sure no gpio input is overwriting our values - odrive.disable_mappings() - odrive.handle.config.gpio15_mode = GPIO_MODE_CAN0 - odrive.handle.config.gpio16_mode = GPIO_MODE_CAN0 - odrive.handle.config.enable_can0 = True - odrive.save_config_and_reboot() - with self.prepare(odrive, canbus, axis_ctx, motor_ctx, enc_ctx, node_id, extended_id, logger): def my_cmd(cmd_name, **kwargs): command(canbus.handle, node_id, extended_id, cmd_name, **kwargs) def my_req(cmd_name, **kwargs): return asyncio.run(request(canbus.handle, node_id, extended_id, cmd_name, **kwargs)) def fence(): my_req('get_vbus_voltage') # fence to ensure the CAN command was sent axis_ctx.handle.config.enable_watchdog = False - axis_ctx.handle.clear_errors() + odrive.handle.clear_errors() axis_ctx.handle.config.can_node_id = node_id axis_ctx.handle.config.can_node_id_extended = extended_id time.sleep(0.1) @@ -173,7 +166,7 @@ class TestSimpleCANClosedLoop(): vel_limit = 15.0 nominal_vel = 10.0 axis_ctx.handle.controller.config.vel_limit = vel_limit - axis_ctx.handle.motor.config.current_lim = 30.0 + axis_ctx.handle.motor.config.current_lim = 20.0 my_cmd('set_requested_state', requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL) fence() diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index 4f8c807b..ea093d7b 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -7,6 +7,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) import stat import odrive from odrive.enums import * +import odrive.utils import fibre from fibre import Logger, Event import argparse @@ -617,24 +618,16 @@ def request_state(axis_ctx: ODriveAxisComponent, state, expect_success=True): test_assert_eq(axis_ctx.handle.error, AXIS_ERROR_INVALID_STATE) axis_ctx.handle.error = AXIS_ERROR_NONE # reset error -def get_errors(axis_ctx: ODriveAxisComponent): - errors = [] - if axis_ctx.handle.motor.error != 0: - errors.append("motor failed with error 0x{:04X}".format(axis_ctx.handle.motor.error)) - if axis_ctx.handle.encoder.error != 0: - errors.append("encoder failed with error 0x{:04X}".format(axis_ctx.handle.encoder.error)) - if axis_ctx.handle.sensorless_estimator.error != 0: - errors.append("sensorless_estimator failed with error 0x{:04X}".format(axis_ctx.handle.sensorless_estimator.error)) - if axis_ctx.handle.error != 0: - errors.append("axis failed with error 0x{:04X}".format(axis_ctx.handle.error)) - elif len(errors) > 0: - errors.append("and by the way: axis reports no error even though there is one") - return errors - def test_assert_no_error(axis_ctx: ODriveAxisComponent): - errors = get_errors(axis_ctx) - if len(errors) > 0: - raise TestFailed("\n".join(errors)) + any_error = (axis_ctx.handle.motor.error | + axis_ctx.handle.encoder.error | + axis_ctx.handle.sensorless_estimator.error | + axis_ctx.handle.error) != 0 + + if any_error: + lines = [] + odrive.utils.dump_errors(axis_ctx.parent.handle, printfunc = lines.append) + raise TestFailed("\n".join(lines)) def run_shell(command_line, logger, env=None, timeout=None): """ diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 329cda81..e6208816 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -69,44 +69,52 @@ def set_motor_thermistor_coeffs(axis, Rload, R_25, Beta, Tmin, TMax): axis.motor_thermistor.config.poly_coefficient_2 = float(coeffs[1]) axis.motor_thermistor.config.poly_coefficient_3 = float(coeffs[0]) -def dump_errors(odrv, clear=False): +def dump_errors(odrv, clear=False, printfunc = print): axes = [(name, axis) for name, axis in odrv._remote_attributes.items() if 'axis' in name] axes.sort() + + def dump_errors_for_module(indent, name, obj, path, errorcodes): + prefix = indent + name.strip('0123456789') + ": " + for elem in path.split('.'): + if not hasattr(obj, elem): + printfunc(prefix + _VT100Colors['yellow'] + "not found" + _VT100Colors['default']) + return + parent = obj + obj = getattr(obj, elem) + if obj != 0: + printfunc(indent + name + ": " + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default']) + for bit in range(64): + if obj & (1 << bit) != 0: + printfunc(indent + " " + errorcodes.get((1 << bit), 'UNKNOWN ERROR: 0x{:08X}'.format(1 << bit))) + if clear: + setattr(parent, elem, 0) + else: + printfunc(indent + name + ": " + _VT100Colors['green'] + "no error" + _VT100Colors['default']) + + system_error_codes = {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("ODRIVE_ERROR_")} + dump_errors_for_module("", "system", odrv, 'error', system_error_codes) + for name, axis in axes: - print(name) + printfunc(name) # Flatten axis and submodules - # (name, remote_obj, errorcode) + # (name, obj, path, errorcode) module_decode_map = [ - (name, odrv, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), - ('motor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), - ('fet_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('motor_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('encoder', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), - ('controller', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), + ('axis', axis, 'error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), + ('motor', axis, 'motor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), + ('fet_thermistor', axis, 'fet_thermistor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('motor_thermistor', axis, 'motor_thermistor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('encoder', axis, 'encoder.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), + ('controller', axis, 'controller.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), ] - # Module error decode - for name, remote_obj, errorcodes in module_decode_map: - prefix = ' '*2 + name.strip('0123456789') + ": " - if not hasattr(remote_obj, name): - print(prefix + _VT100Colors['yellow'] + "not found" + _VT100Colors['default']) - elif getattr(remote_obj, name).error != 0: - foundError = False - print(prefix + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default']) - errorcodes_dict = {val: name for name, val in errorcodes.items() if 'ERROR_' in name} - for bit in range(64): - if getattr(remote_obj, name).error & (1 << bit) != 0: - print(" " + errorcodes_dict.get((1 << bit), 'UNKNOWN ERROR: 0x{:08X}'.format(1 << bit))) - if clear: - getattr(remote_obj, name).error = 0 - else: - print(prefix + _VT100Colors['green'] + "no error" + _VT100Colors['default']) + for name, obj, path, errorcodes in module_decode_map: + dump_errors_for_module(" ", name, obj, path, errorcodes) def oscilloscope_dump(odrv, num_vals, filename='oscilloscope.csv'): with open(filename, 'w') as f: for x in range(num_vals): - f.write(str(odrv.get_oscilloscope_val(x))) + f.write(str(odrv.oscilloscope.get_val(x))) f.write('\n') data_rate = 100 @@ -296,7 +304,7 @@ def show_oscilloscope(odrv): size = 18000 values = [] for i in range(size): - values.append(odrv.get_oscilloscope_val(i)) + values.append(odrv.oscilloscope.get_val(i)) import matplotlib.pyplot as plt plt.plot(values) @@ -520,4 +528,43 @@ def dump_dma(odrv): ("(" + ch_name + ")").ljust(30), "*" if (status & 0x80000000) else " ")) +def dump_timing(odrv, n_samples=100, path='/tmp/timings.png'): + timings = [] + + for attr in dir(odrv.task_times): + if not attr.startswith('_'): + timings.append((attr, getattr(odrv.task_times, attr), [], [])) # (name, obj, start_times, lengths) + for attr in dir(odrv.axis0.task_times): + if not attr.startswith('_'): + timings.append(('axis0.' + attr, getattr(odrv.axis0.task_times, attr), [], [])) # (name, obj, start_times, lengths) + for attr in dir(odrv.axis1.task_times): + if not attr.startswith('_'): + timings.append(('axis1.' + attr, getattr(odrv.axis1.task_times, attr), [], [])) # (name, obj, start_times, lengths) + # Take a couple of samples + print("sampling...") + for i in range(n_samples): + odrv.task_timers_armed = True # Trigger sample and wait for it to finish + while odrv.task_timers_armed: pass + for name, obj, start_times, lengths in timings: + start_times.append(obj.start_time) + lengths.append(obj.length) + print("done") + + # sort by start time + timings = sorted(timings, key = lambda x: np.mean(x[2])) + + plt.rcParams['figure.figsize'] = 21, 9 + plt.figure() + plt.grid('both') + plt.barh( + [-i for i in range(len(timings))], # y positions + [np.mean(lengths) for name, obj, start_times, lengths in timings], # lengths + left = [np.mean(start_times) for name, obj, start_times, lengths in timings], # starts + xerr = ( + [np.std(lengths) for name, obj, start_times, lengths in timings], # error bars to the left side + [(min(obj.max_length, 20100) - np.mean(lengths)) for name, obj, start_times, lengths in timings], # error bars to the right side - TODO: remove artificial min() + ), + tick_label = [name for name, obj, start_times, lengths in timings], # labels + ) + plt.savefig(path, bbox_inches='tight') diff --git a/tools/setup_hall_as_index.py b/tools/setup_hall_as_index.py index 8ba871a8..12dc7b58 100644 --- a/tools/setup_hall_as_index.py +++ b/tools/setup_hall_as_index.py @@ -28,7 +28,6 @@ for ax in axes: ax.encoder.config.cpr = 4096 ax.encoder.config.use_index = True ax.encoder.config.find_idx_on_lockin_only = True - ax.encoder.config.idx_search_unidirectional = True ax.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL ax.controller.config.vel_limit = 10000 From 44adb60f67ebdeff938334134b605fb4b1082aed Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 1 Sep 2020 14:57:21 +0200 Subject: [PATCH 009/124] make sensorless spinup transition smooth --- Firmware/MotorControl/axis.cpp | 29 +++++++++++++++++------------ Firmware/MotorControl/axis.hpp | 2 +- Firmware/MotorControl/encoder.cpp | 4 ++-- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 208eec07..2b8c062b 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -196,7 +196,7 @@ bool Axis::watchdog_check() { } } -bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) { +bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed) { CRITICAL_SECTION() { // Reset state variables open_loop_controller_.Id_setpoint_ = NAN; @@ -258,7 +258,9 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) { osDelay(1); } - motor_.disarm(); + if (!success || !remain_armed) { + motor_.disarm(); + } return success; } @@ -269,7 +271,7 @@ bool Axis::start_closed_loop_control() { if (sensorless_mode) { // TODO: restart if desired - if (!run_lockin_spin(config_.sensorless_ramp)) { + if (!run_lockin_spin(config_.sensorless_ramp, true)) { return false; } } @@ -329,16 +331,19 @@ bool Axis::start_closed_loop_control() { motor_.current_control_.phase_vel_src_ = async_estimator_.rotor_phase_vel_src_ = sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_; + + if (sensorless_mode) { + // Make the final velocity of the loĉk-in spin the setpoint of the + // closed loop controller to allow for smooth transition. + controller_.input_vel_ = config_.sensorless_ramp.vel / (2 * M_PI); + controller_.vel_setpoint_ = config_.sensorless_ramp.vel / (2 * M_PI); + } } - wait_for_control_iteration(); - motor_.arm(&motor_.current_control_); - - if (sensorless_mode) { - // call to controller.reset() that happend when arming means that vel_setpoint - // is zeroed. So we make the setpoint the spinup target for smooth transition. - controller_.input_vel_ = config_.sensorless_ramp.vel / (2 * M_PI); - controller_.vel_setpoint_ = config_.sensorless_ramp.vel / (2 * M_PI); + // In sensorless mode the motor is already armed. + if (!motor_.is_armed_) { + wait_for_control_iteration(); + motor_.arm(&motor_.current_control_); } return true; @@ -520,7 +525,7 @@ void Axis::run_state_machine_loop() { case AXIS_STATE_LOCKIN_SPIN: { if (!motor_.is_calibrated_ || encoder_.config_.direction==0) goto invalid_state_label; - status = run_lockin_spin(config_.general_lockin); + status = run_lockin_spin(config_.general_lockin, false); } break; case AXIS_STATE_CLOSED_LOOP_CONTROL: { diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 3b46baa4..1dce2112 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -131,7 +131,7 @@ public: bool start_closed_loop_control(); bool stop_closed_loop_control(); - bool run_lockin_spin(const LockinConfig_t &lockin_config); + bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed); bool run_closed_loop_control_loop(); bool run_homing(); bool run_idle_loop(); diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index f4899cc6..9b25a353 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -167,7 +167,7 @@ bool Encoder::run_index_search() { index_found_ = false; set_idx_subscribe(); - bool status = axis_->run_lockin_spin(axis_->config_.calibration_lockin); + bool status = axis_->run_lockin_spin(axis_->config_.calibration_lockin, false); return status; } @@ -179,7 +179,7 @@ bool Encoder::run_direction_find() { lockin_config.finish_on_distance = true; lockin_config.finish_on_enc_idx = false; lockin_config.finish_on_vel = false; - bool status = axis_->run_lockin_spin(lockin_config); + bool status = axis_->run_lockin_spin(lockin_config, false); if (status) { // Check response and direction From 5c4e4374f02bc858051c57715188bcacf07da52f Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 2 Sep 2020 22:22:09 -0400 Subject: [PATCH 010/124] Update to FreeRTOS v10 --- .../Third_Party/FreeRTOS/Source/croutine.c | 94 +- .../FreeRTOS/Source/event_groups.c | 205 +-- .../FreeRTOS/Source/include/FreeRTOS.h | 406 ++++- .../Source/include/FreeRTOSConfig_template.h | 173 -- .../FreeRTOS/Source/include/StackMacros.h | 96 +- .../FreeRTOS/Source/include/atomic.h | 414 +++++ .../FreeRTOS/Source/include/croutine.h | 94 +- .../Source/include/deprecated_definitions.h | 92 +- .../FreeRTOS/Source/include/event_groups.h | 122 +- .../FreeRTOS/Source/include/list.h | 117 +- .../FreeRTOS/Source/include/message_buffer.h | 803 ++++++++++ .../FreeRTOS/Source/include/mpu_prototypes.h | 297 ++-- .../FreeRTOS/Source/include/mpu_wrappers.h | 142 +- .../FreeRTOS/Source/include/portable.h | 150 +- .../FreeRTOS/Source/include/projdefs.h | 101 +- .../FreeRTOS/Source/include/queue.h | 283 +--- .../FreeRTOS/Source/include/semphr.h | 127 +- .../FreeRTOS/Source/include/stack_macros.h | 129 ++ .../FreeRTOS/Source/include/stdint.readme | 27 + .../FreeRTOS/Source/include/stream_buffer.h | 859 ++++++++++ .../FreeRTOS/Source/include/task.h | 610 +++++-- .../FreeRTOS/Source/include/timers.h | 165 +- .../Third_Party/FreeRTOS/Source/list.c | 122 +- .../Source/portable/GCC/ARM_CM4F/port.c | 250 +-- .../Source/portable/GCC/ARM_CM4F/portmacro.h | 105 +- .../Source/portable/MemMang/ReadMe.url | 5 + .../FreeRTOS/Source/portable/MemMang/heap_4.c | 152 +- .../Third_Party/FreeRTOS/Source/queue.c | 913 +++++++---- .../Third_Party/FreeRTOS/Source/readme.txt | 17 + .../FreeRTOS/Source/stream_buffer.c | 1263 +++++++++++++++ .../Third_Party/FreeRTOS/Source/tasks.c | 1411 +++++++++++------ .../Third_Party/FreeRTOS/Source/timers.c | 377 +++-- 32 files changed, 7162 insertions(+), 2959 deletions(-) delete mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt create mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c index 993e09b2..9ce50030 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #include "FreeRTOS.h" #include "task.h" @@ -302,7 +260,7 @@ CRCB_t *pxCRCB; ( void ) uxListRemove( &( pxCRCB->xGenericListItem ) ); /* Is the co-routine waiting on an event also? */ - if( pxCRCB->xEventListItem.pvContainer ) + if( pxCRCB->xEventListItem.pxContainer ) { ( void ) uxListRemove( &( pxCRCB->xEventListItem ) ); } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c index b23ecb1c..bf4ec246 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* Standard includes. */ #include @@ -81,11 +39,11 @@ task.h is included from an application file. */ #include "timers.h" #include "event_groups.h" -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e961, e750 and e9021 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021 See comment above. */ /* The following bit fields convey control information in a task's event list item value. It is important they don't clash with the @@ -102,7 +60,7 @@ taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */ #define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL #endif -typedef struct xEventGroupDefinition +typedef struct EventGroupDef_t { EventBits_t uxEventBits; List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */ @@ -126,7 +84,7 @@ typedef struct xEventGroupDefinition * wait condition is met if any of the bits set in uxBitsToWait for are also set * in uxCurrentEventBits. */ -PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ); +static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ @@ -139,8 +97,18 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux /* A StaticEventGroup_t object must be provided. */ configASSERT( pxEventGroupBuffer ); + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticEventGroup_t equals the size of the real + event group structure. */ + volatile size_t xSize = sizeof( StaticEventGroup_t ); + configASSERT( xSize == sizeof( EventGroup_t ) ); + } /*lint !e529 xSize is referenced if configASSERT() is defined. */ + #endif /* configASSERT_DEFINED */ + /* The user has provided a statically allocated event group - use it. */ - pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 EventGroup_t and StaticEventGroup_t are guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ + pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t and StaticEventGroup_t are deliberately aliased for data hiding purposes and guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ if( pxEventBits != NULL ) { @@ -160,10 +128,13 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux } else { + /* xEventGroupCreateStatic should only ever be called with + pxEventGroupBuffer pointing to a pre-allocated (compile time + allocated) StaticEventGroup_t variable. */ traceEVENT_GROUP_CREATE_FAILED(); } - return ( EventGroupHandle_t ) pxEventBits; + return pxEventBits; } #endif /* configSUPPORT_STATIC_ALLOCATION */ @@ -175,8 +146,20 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux { EventGroup_t *pxEventBits; - /* Allocate the event group. */ - pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); + /* Allocate the event group. Justification for MISRA deviation as + follows: pvPortMalloc() always ensures returned memory blocks are + aligned per the requirements of the MCU stack. In this case + pvPortMalloc() must return a pointer that is guaranteed to meet the + alignment requirements of the EventGroup_t structure - which (if you + follow it through) is the alignment requirements of the TickType_t type + (EventBits_t being of TickType_t itself). Therefore, whenever the + stack alignment requirements are greater than or equal to the + TickType_t alignment requirements the cast is safe. In other cases, + where the natural word size of the architecture is less than + sizeof( TickType_t ), the TickType_t variables will be accessed in two + or more reads operations, and the alignment requirements is only that + of each individual read. */ + pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */ if( pxEventBits != NULL ) { @@ -196,10 +179,10 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux } else { - traceEVENT_GROUP_CREATE_FAILED(); + traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */ } - return ( EventGroupHandle_t ) pxEventBits; + return pxEventBits; } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ @@ -208,7 +191,7 @@ PRIVILEGED_FUNCTION static BaseType_t prvTestWaitCondition( const EventBits_t ux EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) { EventBits_t uxOriginalBitValue, uxReturn; -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; BaseType_t xAlreadyYielded; BaseType_t xTimeoutOccurred = pdFALSE; @@ -259,6 +242,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; /* The rendezvous bits were not set, but no block time was specified - just return the current event bit value. */ uxReturn = pxEventBits->uxEventBits; + xTimeoutOccurred = pdTRUE; } } } @@ -317,13 +301,16 @@ BaseType_t xTimeoutOccurred = pdFALSE; traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ); + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + return uxReturn; } /*-----------------------------------------------------------*/ EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; EventBits_t uxReturn, uxControlBits = 0; BaseType_t xWaitConditionMet, xAlreadyYielded; BaseType_t xTimeoutOccurred = pdFALSE; @@ -368,6 +355,7 @@ BaseType_t xTimeoutOccurred = pdFALSE; /* The wait condition has not been met, but no block time was specified, so just return the current value. */ uxReturn = uxCurrentEventBits; + xTimeoutOccurred = pdTRUE; } else { @@ -449,11 +437,9 @@ BaseType_t xTimeoutOccurred = pdFALSE; { mtCOVERAGE_TEST_MARKER(); } + xTimeoutOccurred = pdTRUE; } taskEXIT_CRITICAL(); - - /* Prevent compiler warnings when trace macros are not used. */ - xTimeoutOccurred = pdFALSE; } else { @@ -465,13 +451,16 @@ BaseType_t xTimeoutOccurred = pdFALSE; } traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ); + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + return uxReturn; } /*-----------------------------------------------------------*/ EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; EventBits_t uxReturn; /* Check the user is not attempting to clear the bits used by the kernel @@ -503,7 +492,7 @@ EventBits_t uxReturn; BaseType_t xReturn; traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); - xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ return xReturn; } @@ -514,7 +503,7 @@ EventBits_t uxReturn; EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) { UBaseType_t uxSavedInterruptStatus; -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t const * const pxEventBits = xEventGroup; EventBits_t uxReturn; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); @@ -524,16 +513,16 @@ EventBits_t uxReturn; portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); return uxReturn; -} +} /*lint !e818 EventGroupHandle_t is a typedef used in other functions to so can't be pointer to const. */ /*-----------------------------------------------------------*/ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) { ListItem_t *pxListItem, *pxNext; ListItem_t const *pxListEnd; -List_t *pxList; +List_t const * pxList; EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits; -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; BaseType_t xMatchFound = pdFALSE; /* Check the user is not attempting to set the bits used by the kernel @@ -542,7 +531,7 @@ BaseType_t xMatchFound = pdFALSE; configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); pxList = &( pxEventBits->xTasksWaitingForBits ); - pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ vTaskSuspendAll(); { traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); @@ -602,7 +591,7 @@ BaseType_t xMatchFound = pdFALSE; eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows that is was unblocked due to its required bits matching, rather than because it timed out. */ - ( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); + vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); } /* Move onto the next list item. Note pxListItem->pxNext is not @@ -623,7 +612,7 @@ BaseType_t xMatchFound = pdFALSE; void vEventGroupDelete( EventGroupHandle_t xEventGroup ) { -EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventGroup_t *pxEventBits = xEventGroup; const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); vTaskSuspendAll(); @@ -633,9 +622,9 @@ const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) { /* Unblock the task, returning 0 as the event list is being deleted - and cannot therefore have any bits set. */ - configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); - ( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); + and cannot therefore have any bits set. */ + configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); + vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); } #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) @@ -667,7 +656,7 @@ const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); an interrupt. */ void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) { - ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); + ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ } /*-----------------------------------------------------------*/ @@ -675,7 +664,7 @@ void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet an interrupt. */ void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) { - ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); + ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ } /*-----------------------------------------------------------*/ @@ -721,7 +710,7 @@ BaseType_t xWaitConditionMet = pdFALSE; BaseType_t xReturn; traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); - xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ return xReturn; } @@ -734,7 +723,7 @@ BaseType_t xWaitConditionMet = pdFALSE; UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) { UBaseType_t xReturn; - EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; + EventGroup_t const *pxEventBits = ( EventGroup_t * ) xEventGroup; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ if( xEventGroup == NULL ) { @@ -748,5 +737,17 @@ BaseType_t xWaitConditionMet = pdFALSE; return xReturn; } -#endif +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vEventGroupSetNumber( void * xEventGroup, UBaseType_t uxEventGroupNumber ) + { + ( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h index f81172db..ceb469a7 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef INC_FREERTOS_H #define INC_FREERTOS_H @@ -126,6 +84,10 @@ extern "C" { #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. #endif +#if configMAX_PRIORITIES < 1 + #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. +#endif + #ifndef configUSE_PREEMPTION #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif @@ -142,10 +104,6 @@ extern "C" { #error Missing definition: configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif -#ifndef configMAX_PRIORITIES - #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. -#endif - #ifndef configUSE_CO_ROUTINES #define configUSE_CO_ROUTINES 0 #endif @@ -198,6 +156,10 @@ extern "C" { #define INCLUDE_uxTaskGetStackHighWaterMark 0 #endif +#ifndef INCLUDE_uxTaskGetStackHighWaterMark2 + #define INCLUDE_uxTaskGetStackHighWaterMark2 0 +#endif + #ifndef INCLUDE_eTaskGetState #define INCLUDE_eTaskGetState 0 #endif @@ -279,6 +241,26 @@ extern "C" { #define configASSERT_DEFINED 1 #endif +/* configPRECONDITION should be defined as configASSERT. +The CBMC proofs need a way to track assumptions and assertions. +A configPRECONDITION statement should express an implicit invariant or +assumption made. A configASSERT statement should express an invariant that must +hold explicit before calling the code. */ +#ifndef configPRECONDITION + #define configPRECONDITION( X ) configASSERT(X) + #define configPRECONDITION_DEFINED 0 +#else + #define configPRECONDITION_DEFINED 1 +#endif + +#ifndef portMEMORY_BARRIER + #define portMEMORY_BARRIER() +#endif + +#ifndef portSOFTWARE_BARRIER + #define portSOFTWARE_BARRIER() +#endif + /* The timers module relies on xTaskGetSchedulerState(). */ #if configUSE_TIMERS == 1 @@ -396,6 +378,14 @@ extern "C" { #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) #endif +#ifndef traceBLOCKING_ON_QUEUE_PEEK + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_PEEK( pxQueue ) +#endif + #ifndef traceBLOCKING_ON_QUEUE_SEND /* Task is about to block because it cannot write to a queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore @@ -408,6 +398,14 @@ extern "C" { #define configCHECK_FOR_STACK_OVERFLOW 0 #endif +#ifndef configRECORD_STACK_HIGH_ADDRESS + #define configRECORD_STACK_HIGH_ADDRESS 0 +#endif + +#ifndef configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H + #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 +#endif + /* The following event macros are embedded in the kernel API calls. */ #ifndef traceMOVED_TASK_TO_READY_STATE @@ -474,6 +472,10 @@ extern "C" { #define traceQUEUE_PEEK( pxQueue ) #endif +#ifndef traceQUEUE_PEEK_FAILED + #define traceQUEUE_PEEK_FAILED( pxQueue ) +#endif + #ifndef traceQUEUE_PEEK_FROM_ISR #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) #endif @@ -658,6 +660,58 @@ extern "C" { #define traceTASK_NOTIFY_GIVE_FROM_ISR() #endif +#ifndef traceSTREAM_BUFFER_CREATE_FAILED + #define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE_STATIC_FAILED + #define traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE + #define traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_DELETE + #define traceSTREAM_BUFFER_DELETE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RESET + #define traceSTREAM_BUFFER_RESET( xStreamBuffer ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_SEND + #define traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND + #define traceSTREAM_BUFFER_SEND( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FAILED + #define traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FROM_ISR + #define traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_RECEIVE + #define traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE + #define traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FAILED + #define traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FROM_ISR + #define traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ) +#endif + #ifndef configGENERATE_RUN_TIME_STATS #define configGENERATE_RUN_TIME_STATS 0 #endif @@ -708,6 +762,10 @@ extern "C" { #define configUSE_TICKLESS_IDLE 0 #endif +#ifndef configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING + #define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( x ) +#endif + #ifndef configPRE_SLEEP_PROCESSING #define configPRE_SLEEP_PROCESSING( x ) #endif @@ -724,6 +782,14 @@ extern "C" { #define portTASK_USES_FLOATING_POINT() #endif +#ifndef portALLOCATE_SECURE_CONTEXT + #define portALLOCATE_SECURE_CONTEXT( ulSecureStackSize ) +#endif + +#ifndef portDONT_DISCARD + #define portDONT_DISCARD +#endif + #ifndef configUSE_TIME_SLICING #define configUSE_TIME_SLICING 1 #endif @@ -768,6 +834,10 @@ extern "C" { #define configUSE_TASK_NOTIFICATIONS 1 #endif +#ifndef configUSE_POSIX_ERRNO + #define configUSE_POSIX_ERRNO 0 +#endif + #ifndef portTICK_TYPE_IS_ATOMIC #define portTICK_TYPE_IS_ATOMIC 0 #endif @@ -782,6 +852,19 @@ extern "C" { #define configSUPPORT_DYNAMIC_ALLOCATION 1 #endif +#ifndef configSTACK_DEPTH_TYPE + /* Defaults to uint16_t for backward compatibility, but can be overridden + in FreeRTOSConfig.h if uint16_t is too restrictive. */ + #define configSTACK_DEPTH_TYPE uint16_t +#endif + +#ifndef configMESSAGE_BUFFER_LENGTH_TYPE + /* Defaults to size_t for backward compatibility, but can be overridden + in FreeRTOSConfig.h if lengths will always be less than the number of bytes + in a size_t. */ + #define configMESSAGE_BUFFER_LENGTH_TYPE size_t +#endif + /* Sanity check the configuration. */ #if( configUSE_TICKLESS_IDLE != 0 ) #if( INCLUDE_vTaskSuspend != 1 ) @@ -797,6 +880,10 @@ extern "C" { #error configUSE_MUTEXES must be set to 1 to use recursive mutexes #endif +#ifndef configINITIAL_TICK_COUNT + #define configINITIAL_TICK_COUNT 0 +#endif + #if( portTICK_TYPE_IS_ATOMIC == 0 ) /* Either variables of tick type cannot be read atomically, or portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when @@ -820,6 +907,32 @@ V8 if desired. */ #define configENABLE_BACKWARD_COMPATIBILITY 1 #endif +#ifndef configPRINTF + /* configPRINTF() was not defined, so define it away to nothing. To use + configPRINTF() then define it as follows (where MyPrintFunction() is + provided by the application writer): + + void MyPrintFunction(const char *pcFormat, ... ); + #define configPRINTF( X ) MyPrintFunction X + + Then call like a standard printf() function, but placing brackets around + all parameters so they are passed as a single parameter. For example: + configPRINTF( ("Value = %d", MyVariable) ); */ + #define configPRINTF( X ) +#endif + +#ifndef configMAX + /* The application writer has not provided their own MAX macro, so define + the following generic implementation. */ + #define configMAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef configMIN + /* The application writer has not provided their own MAX macro, so define + the following generic implementation. */ + #define configMIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + #if configENABLE_BACKWARD_COMPATIBILITY == 1 #define eTaskStateGet eTaskGetState #define portTickType TickType_t @@ -840,6 +953,7 @@ V8 if desired. */ #define pcTimerGetTimerName pcTimerGetName #define pcQueueGetQueueName pcQueueGetName #define vTaskGetTaskInfo vTaskGetInfo + #define xTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter /* Backward compatibility within the scheduler code only - these definitions are not really required but are included for completeness. */ @@ -847,6 +961,10 @@ V8 if desired. */ #define pdTASK_CODE TaskFunction_t #define xListItem ListItem_t #define xList List_t + + /* For libraries that break the list data hiding, and access list structure + members directly (which is not supposed to be done). */ + #define pxContainer pvContainer #endif /* configENABLE_BACKWARD_COMPATIBILITY */ #if( configUSE_ALTERNATIVE_API != 0 ) @@ -861,6 +979,75 @@ point support. */ #define configUSE_TASK_FPU_SUPPORT 1 #endif +/* Set configENABLE_MPU to 1 to enable MPU support and 0 to disable it. This is +currently used in ARMv8M ports. */ +#ifndef configENABLE_MPU + #define configENABLE_MPU 0 +#endif + +/* Set configENABLE_FPU to 1 to enable FPU support and 0 to disable it. This is +currently used in ARMv8M ports. */ +#ifndef configENABLE_FPU + #define configENABLE_FPU 1 +#endif + +/* Set configENABLE_TRUSTZONE to 1 enable TrustZone support and 0 to disable it. +This is currently used in ARMv8M ports. */ +#ifndef configENABLE_TRUSTZONE + #define configENABLE_TRUSTZONE 1 +#endif + +/* Set configRUN_FREERTOS_SECURE_ONLY to 1 to run the FreeRTOS ARMv8M port on +the Secure Side only. */ +#ifndef configRUN_FREERTOS_SECURE_ONLY + #define configRUN_FREERTOS_SECURE_ONLY 0 +#endif + +/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using + * dynamically allocated RAM, in which case when any task is deleted it is known + * that both the task's stack and TCB need to be freed. Sometimes the + * FreeRTOSConfig.h settings only allow a task to be created using statically + * allocated RAM, in which case when any task is deleted it is known that neither + * the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h + * settings allow a task to be created using either statically or dynamically + * allocated RAM, in which case a member of the TCB is used to record whether the + * stack and/or TCB were allocated statically or dynamically, so when a task is + * deleted the RAM that was allocated dynamically is freed again and no attempt is + * made to free the RAM that was allocated statically. + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a + * task to be created using either statically or dynamically allocated RAM. Note + * that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with + * a statically allocated stack and a dynamically allocated TCB. + * + * The following table lists various combinations of portUSING_MPU_WRAPPERS, + * configSUPPORT_DYNAMIC_ALLOCATION and configSUPPORT_STATIC_ALLOCATION and + * when it is possible to have both static and dynamic allocation: + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | MPU | Dynamic | Static | Available Functions | Possible Allocations | Both Dynamic and | Need Free | + * | | | | | | Static Possible | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | 0 | 0 | 1 | xTaskCreateStatic | TCB - Static, Stack - Static | No | No | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 0 | xTaskCreate | TCB - Dynamic, Stack - Dynamic | No | Yes | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic | 2. TCB - Static, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 0 | 1 | xTaskCreateStatic, | TCB - Static, Stack - Static | No | No | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 0 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateRestricted | 2. TCB - Dynamic, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic, | 2. TCB - Dynamic, Stack - Static | | | + * | | | | xTaskCreateRestricted, | 3. TCB - Static, Stack - Static | | | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + */ +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \ + ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) ) + /* * In line with software engineering best practice, FreeRTOS implements a strict * data hiding policy, so the real structures used by FreeRTOS to maintain the @@ -873,25 +1060,40 @@ point support. */ */ struct xSTATIC_LIST_ITEM { - TickType_t xDummy1; - void *pvDummy2[ 4 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void *pvDummy3[ 4 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy4; + #endif }; typedef struct xSTATIC_LIST_ITEM StaticListItem_t; /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ struct xSTATIC_MINI_LIST_ITEM { - TickType_t xDummy1; - void *pvDummy2[ 2 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void *pvDummy3[ 2 ]; }; typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ typedef struct xSTATIC_LIST { - UBaseType_t uxDummy1; - void *pvDummy2; - StaticMiniListItem_t xDummy3; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + UBaseType_t uxDummy2; + void *pvDummy3; + StaticMiniListItem_t xDummy4; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy5; + #endif } StaticList_t; /* @@ -917,7 +1119,7 @@ typedef struct xSTATIC_TCB UBaseType_t uxDummy5; void *pxDummy6; uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; - #if ( portSTACK_GROWTH > 0 ) + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) void *pxDummy8; #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) @@ -945,10 +1147,16 @@ typedef struct xSTATIC_TCB uint32_t ulDummy18; uint8_t ucDummy19; #endif - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) uint8_t uxDummy20; #endif + #if( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDummy21; + #endif + #if ( configUSE_POSIX_ERRNO == 1 ) + int iDummy22; + #endif } StaticTask_t; /* @@ -1043,18 +1251,42 @@ typedef struct xSTATIC_TIMER void *pvDummy1; StaticListItem_t xDummy2; TickType_t xDummy3; - UBaseType_t uxDummy4; - void *pvDummy5[ 2 ]; + void *pvDummy5; + TaskFunction_t pvDummy6; #if( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy6; - #endif - - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucDummy7; + UBaseType_t uxDummy7; #endif + uint8_t ucDummy8; } StaticTimer_t; +/* +* In line with software engineering best practice, especially when supplying a +* library that is likely to change in future versions, FreeRTOS implements a +* strict data hiding policy. This means the stream buffer structure used +* internally by FreeRTOS is not accessible to application code. However, if +* the application writer wants to statically allocate the memory required to +* create a stream buffer then the size of the stream buffer object needs to be +* know. The StaticStreamBuffer_t structure below is provided for this purpose. +* Its size and alignment requirements are guaranteed to match those of the +* genuine structure, no matter which architecture is being used, and no matter +* how the values in FreeRTOSConfig.h are set. Its contents are somewhat +* obfuscated in the hope users will recognise that it would be unwise to make +* direct use of the structure members. +*/ +typedef struct xSTATIC_STREAM_BUFFER +{ + size_t uxDummy1[ 4 ]; + void * pvDummy2[ 3 ]; + uint8_t ucDummy3; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy4; + #endif +} StaticStreamBuffer_t; + +/* Message buffers are built on stream buffers. */ +typedef StaticStreamBuffer_t StaticMessageBuffer_t; + #ifdef __cplusplus } #endif diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h deleted file mode 100644 index 2662d227..00000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOSConfig_template.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ - - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ - -/* Ensure stdint is only used by the compiler, and not the assembler. */ -#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) - #include - extern uint32_t SystemCoreClock; -#endif - -#define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configCPU_CLOCK_HZ (SystemCoreClock) -#define configTICK_RATE_HZ ((TickType_t)1000) -#define configMAX_PRIORITIES (7) -#define configMINIMAL_STACK_SIZE ((uint16_t)128) -#define configTOTAL_HEAP_SIZE ((size_t)(15 * 1024)) -#define configMAX_TASK_NAME_LEN (16) -#define configUSE_TRACE_FACILITY 1 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configQUEUE_REGISTRY_SIZE 8 -#define configCHECK_FOR_STACK_OVERFLOW 0 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_MALLOC_FAILED_HOOK 0 -#define configUSE_APPLICATION_TASK_TAG 0 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configGENERATE_RUN_TIME_STATS 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES (2) - -/* Software timer definitions. */ -#define configUSE_TIMERS 0 -#define configTIMER_TASK_PRIORITY (2) -#define configTIMER_QUEUE_LENGTH 10 -#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) - -/* Set the following definitions to 1 to include the API function, or zero -to exclude the API function. */ -#define INCLUDE_vTaskPrioritySet 1 -#define INCLUDE_uxTaskPriorityGet 1 -#define INCLUDE_vTaskDelete 1 -#define INCLUDE_vTaskCleanUpResources 0 -#define INCLUDE_vTaskSuspend 1 -#define INCLUDE_vTaskDelayUntil 0 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 1 - -/* Cortex-M specific definitions. */ -#ifdef __NVIC_PRIO_BITS - /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ - #define configPRIO_BITS __NVIC_PRIO_BITS -#else - #define configPRIO_BITS 4 /* 15 priority levels */ -#endif - -/* The lowest interrupt priority that can be used in a call to a "set priority" -function. */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf - -/* The highest interrupt priority that can be used by any interrupt service -routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL -INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER -PRIORITY THAN THIS! (higher priorities are lower numeric values. */ -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 - -/* Interrupt priorities used by the kernel port layer itself. These are generic -to all Cortex-M ports, and do not rely on any particular library functions. */ -#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) -/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! -See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ -#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -/* Normal assert() semantics without relying on the provision of an assert.h -header file. */ -#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } - -/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS - standard names. */ -#define vPortSVCHandler SVC_Handler -#define xPortPendSVHandler PendSV_Handler - -/* IMPORTANT: This define MUST be commented when used with STM32Cube firmware, - to prevent overwriting SysTick_Handler defined within STM32Cube HAL */ -/* #define xPortSysTickHandler SysTick_Handler */ - -#endif /* FREERTOS_CONFIG_H */ - diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h index 13c6b829..56439917 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h @@ -1,75 +1,37 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef STACK_MACROS_H #define STACK_MACROS_H +#ifndef _MSC_VER /* Visual Studio doesn't support #warning. */ + #warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in future released. +#endif + /* * Call the stack overflow hook function if the stack of the task being swapped * out is currently overflowed, or looks like it might have overflowed in the diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h new file mode 100644 index 00000000..ceca6960 --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h @@ -0,0 +1,414 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/** + * @file atomic.h + * @brief FreeRTOS atomic operation support. + * + * This file implements atomic functions by disabling interrupts globally. + * Implementations with architecture specific atomic instructions can be + * provided under each compiler directory. + */ + +#ifndef ATOMIC_H +#define ATOMIC_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include atomic.h" +#endif + +/* Standard includes. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Port specific definitions -- entering/exiting critical section. + * Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h + * + * Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with + * ATOMIC_ENTER_CRITICAL(). + * + */ +#if defined( portSET_INTERRUPT_MASK_FROM_ISR ) + + /* Nested interrupt scheme is supported in this port. */ + #define ATOMIC_ENTER_CRITICAL() \ + UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR() + + #define ATOMIC_EXIT_CRITICAL() \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType ) + +#else + + /* Nested interrupt scheme is NOT supported in this port. */ + #define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL() + #define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL() + +#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */ + +/* + * Port specific definition -- "always inline". + * Inline is compiler specific, and may not always get inlined depending on your + * optimization level. Also, inline is considered as performance optimization + * for atomic. Thus, if portFORCE_INLINE is not provided by portmacro.h, + * instead of resulting error, simply define it away. + */ +#ifndef portFORCE_INLINE + #define portFORCE_INLINE +#endif + +#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */ +#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */ + +/*----------------------------- Swap && CAS ------------------------------*/ + +/** + * Atomic compare-and-swap + * + * @brief Performs an atomic compare-and-swap operation on the specified values. + * + * @param[in, out] pulDestination Pointer to memory location from where value is + * to be loaded and checked. + * @param[in] ulExchange If condition meets, write this value to memory. + * @param[in] ulComparand Swap condition. + * + * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped. + * + * @note This function only swaps *pulDestination with ulExchange, if previous + * *pulDestination value equals ulComparand. + */ +static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination, + uint32_t ulExchange, + uint32_t ulComparand ) +{ +uint32_t ulReturnValue; + + ATOMIC_ENTER_CRITICAL(); + { + if( *pulDestination == ulComparand ) + { + *pulDestination = ulExchange; + ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; + } + else + { + ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; + } + } + ATOMIC_EXIT_CRITICAL(); + + return ulReturnValue; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic swap (pointers) + * + * @brief Atomically sets the address pointed to by *ppvDestination to the value + * of *pvExchange. + * + * @param[in, out] ppvDestination Pointer to memory location from where a pointer + * value is to be loaded and written back to. + * @param[in] pvExchange Pointer value to be written to *ppvDestination. + * + * @return The initial value of *ppvDestination. + */ +static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination, + void * pvExchange ) +{ +void * pReturnValue; + + ATOMIC_ENTER_CRITICAL(); + { + pReturnValue = *ppvDestination; + *ppvDestination = pvExchange; + } + ATOMIC_EXIT_CRITICAL(); + + return pReturnValue; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic compare-and-swap (pointers) + * + * @brief Performs an atomic compare-and-swap operation on the specified pointer + * values. + * + * @param[in, out] ppvDestination Pointer to memory location from where a pointer + * value is to be loaded and checked. + * @param[in] pvExchange If condition meets, write this value to memory. + * @param[in] pvComparand Swap condition. + * + * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped. + * + * @note This function only swaps *ppvDestination with pvExchange, if previous + * *ppvDestination value equals pvComparand. + */ +static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination, + void * pvExchange, + void * pvComparand ) +{ +uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; + + ATOMIC_ENTER_CRITICAL(); + { + if( *ppvDestination == pvComparand ) + { + *ppvDestination = pvExchange; + ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; + } + } + ATOMIC_EXIT_CRITICAL(); + + return ulReturnValue; +} + + +/*----------------------------- Arithmetic ------------------------------*/ + +/** + * Atomic add + * + * @brief Atomically adds count to the value of the specified pointer points to. + * + * @param[in,out] pulAddend Pointer to memory location from where value is to be + * loaded and written back to. + * @param[in] ulCount Value to be added to *pulAddend. + * + * @return previous *pulAddend value. + */ +static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend, + uint32_t ulCount ) +{ + uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend += ulCount; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic subtract + * + * @brief Atomically subtracts count from the value of the specified pointer + * pointers to. + * + * @param[in,out] pulAddend Pointer to memory location from where value is to be + * loaded and written back to. + * @param[in] ulCount Value to be subtract from *pulAddend. + * + * @return previous *pulAddend value. + */ +static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend, + uint32_t ulCount ) +{ + uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend -= ulCount; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic increment + * + * @brief Atomically increments the value of the specified pointer points to. + * + * @param[in,out] pulAddend Pointer to memory location from where value is to be + * loaded and written back to. + * + * @return *pulAddend value before increment. + */ +static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend ) +{ +uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend += 1; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic decrement + * + * @brief Atomically decrements the value of the specified pointer points to + * + * @param[in,out] pulAddend Pointer to memory location from where value is to be + * loaded and written back to. + * + * @return *pulAddend value before decrement. + */ +static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend ) +{ +uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend -= 1; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} + +/*----------------------------- Bitwise Logical ------------------------------*/ + +/** + * Atomic OR + * + * @brief Performs an atomic OR operation on the specified values. + * + * @param [in, out] pulDestination Pointer to memory location from where value is + * to be loaded and written back to. + * @param [in] ulValue Value to be ORed with *pulDestination. + * + * @return The original value of *pulDestination. + */ +static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination, + uint32_t ulValue ) +{ +uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination |= ulValue; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic AND + * + * @brief Performs an atomic AND operation on the specified values. + * + * @param [in, out] pulDestination Pointer to memory location from where value is + * to be loaded and written back to. + * @param [in] ulValue Value to be ANDed with *pulDestination. + * + * @return The original value of *pulDestination. + */ +static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination, + uint32_t ulValue ) +{ +uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination &= ulValue; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic NAND + * + * @brief Performs an atomic NAND operation on the specified values. + * + * @param [in, out] pulDestination Pointer to memory location from where value is + * to be loaded and written back to. + * @param [in] ulValue Value to be NANDed with *pulDestination. + * + * @return The original value of *pulDestination. + */ +static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination, + uint32_t ulValue ) +{ +uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination = ~( ulCurrent & ulValue ); + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} +/*-----------------------------------------------------------*/ + +/** + * Atomic XOR + * + * @brief Performs an atomic XOR operation on the specified values. + * + * @param [in, out] pulDestination Pointer to memory location from where value is + * to be loaded and written back to. + * @param [in] ulValue Value to be XORed with *pulDestination. + * + * @return The original value of *pulDestination. + */ +static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination, + uint32_t ulValue ) +{ +uint32_t ulCurrent; + + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination ^= ulValue; + } + ATOMIC_EXIT_CRITICAL(); + + return ulCurrent; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ATOMIC_H */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h index 4f003a0b..8d7069c0 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef CO_ROUTINE_H #define CO_ROUTINE_H @@ -199,7 +157,7 @@ BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPri } // Alternatively, if you do not require any other part of the idle task to - // execute, the idle task hook can call vCoRoutineScheduler() within an + // execute, the idle task hook can call vCoRoutineSchedule() within an // infinite loop. void vApplicationIdleHook( void ) { diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h index 4ea816cc..21657b9d 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef DEPRECATED_DEFINITIONS_H #define DEPRECATED_DEFINITIONS_H diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h index cab9d59e..a87fdf37 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef EVENT_GROUPS_H #define EVENT_GROUPS_H @@ -120,7 +78,8 @@ extern "C" { * \defgroup EventGroupHandle_t EventGroupHandle_t * \ingroup EventGroup */ -typedef void * EventGroupHandle_t; +struct EventGroupDef_t; +typedef struct EventGroupDef_t * EventGroupHandle_t; /* * The type that holds event bits always matches TickType_t - therefore the @@ -185,7 +144,7 @@ typedef TickType_t EventBits_t; * \ingroup EventGroup */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) -PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreate( void ) ; + EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; #endif /** @@ -238,7 +197,7 @@ PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreate( void ) ; */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) -PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ); + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; #endif /** @@ -333,7 +292,7 @@ PRIVILEGED_FUNCTION EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup * \defgroup xEventGroupWaitBits xEventGroupWaitBits * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ); +EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -390,7 +349,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGr * \defgroup xEventGroupClearBits xEventGroupClearBits * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ); +EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -446,7 +405,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventG * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) - PRIVILEGED_FUNCTION BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; #else #define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ) #endif @@ -523,7 +482,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventG * \defgroup xEventGroupSetBits xEventGroupSetBits * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); +EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -598,7 +557,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGro * \ingroup EventGroup */ #if( configUSE_TRACE_FACILITY == 1 ) - PRIVILEGED_FUNCTION BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ); + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; #else #define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ) #endif @@ -727,7 +686,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGro * \defgroup xEventGroupSync xEventGroupSync * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ); +EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** @@ -763,7 +722,7 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR * \ingroup EventGroup */ -PRIVILEGED_FUNCTION EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); +EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; /** * event_groups.h @@ -777,15 +736,16 @@ PRIVILEGED_FUNCTION EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xE * * @param xEventGroup The event group being deleted. */ -PRIVILEGED_FUNCTION void vEventGroupDelete( EventGroupHandle_t xEventGroup ); +void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; /* For internal use only. */ -PRIVILEGED_FUNCTION void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ); -PRIVILEGED_FUNCTION void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ); +void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION; +void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; #if (configUSE_TRACE_FACILITY == 1) - PRIVILEGED_FUNCTION UBaseType_t uxEventGroupGetNumber( void* xEventGroup ); + UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) PRIVILEGED_FUNCTION; + void vEventGroupSetNumber( void* xEventGroup, UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION; #endif #ifdef __cplusplus diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h index e552625e..a3e30249 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* * This is the list implementation used by the scheduler. While it is tailored @@ -178,6 +136,7 @@ use of FreeRTOS.*/ /* * Definition of the only type of object that a list can contain. */ +struct xLIST; struct xLIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -185,7 +144,7 @@ struct xLIST_ITEM struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ - void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ + struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */ listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ }; typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ @@ -205,7 +164,7 @@ typedef struct xMINI_LIST_ITEM MiniListItem_t; typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - configLIST_VOLATILE UBaseType_t uxNumberOfItems; + volatile UBaseType_t uxNumberOfItems; ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -224,7 +183,7 @@ typedef struct xLIST * Access macro to get the owner of a list item. The owner of a list item * is the object (usually a TCB) that contains the list item. * - * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER * \ingroup LinkedList */ #define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner ) @@ -266,7 +225,7 @@ typedef struct xLIST #define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext ) /* - * Return the list item at the head of the list. + * Return the next list item. * * \page listGET_NEXT listGET_NEXT * \ingroup LinkedList @@ -288,7 +247,7 @@ typedef struct xLIST * \page listLIST_IS_EMPTY listLIST_IS_EMPTY * \ingroup LinkedList */ -#define listLIST_IS_EMPTY( pxList ) ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ) +#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) /* * Access macro to return the number of items in the list. @@ -356,7 +315,7 @@ List_t * const pxConstList = ( pxList ); \ * @param pxListItem The list item we want to know if is in the list. * @return pdTRUE if the list item is in the list, otherwise pdFALSE. */ -#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) ) +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) ) /* * Return the list a list item is contained within (referenced from). @@ -364,7 +323,7 @@ List_t * const pxConstList = ( pxList ); \ * @param pxListItem The list item being queried. * @return A pointer to the List_t object that references the pxListItem */ -#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer ) +#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer ) /* * This provides a crude means of knowing if a list has been initialised, as @@ -383,7 +342,7 @@ List_t * const pxConstList = ( pxList ); \ * \page vListInitialise vListInitialise * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInitialise( List_t * const pxList ); +void vListInitialise( List_t * const pxList ) PRIVILEGED_FUNCTION; /* * Must be called before a list item is used. This sets the list container to @@ -394,7 +353,7 @@ PRIVILEGED_FUNCTION void vListInitialise( List_t * const pxList ); * \page vListInitialiseItem vListInitialiseItem * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInitialiseItem( ListItem_t * const pxItem ); +void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION; /* * Insert a list item into a list. The item will be inserted into the list in @@ -407,7 +366,7 @@ PRIVILEGED_FUNCTION void vListInitialiseItem( ListItem_t * const pxItem ); * \page vListInsert vListInsert * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ); +void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; /* * Insert a list item into a list. The item will be inserted in a position @@ -428,7 +387,7 @@ PRIVILEGED_FUNCTION void vListInsert( List_t * const pxList, ListItem_t * const * \page vListInsertEnd vListInsertEnd * \ingroup LinkedList */ -PRIVILEGED_FUNCTION void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ); +void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; /* * Remove an item from a list. The list item has a pointer to the list that @@ -443,7 +402,7 @@ PRIVILEGED_FUNCTION void vListInsertEnd( List_t * const pxList, ListItem_t * con * \page uxListRemove uxListRemove * \ingroup LinkedList */ -PRIVILEGED_FUNCTION UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ); +UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION; #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h new file mode 100644 index 00000000..0c3edb9c --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h @@ -0,0 +1,803 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +/* + * Message buffers build functionality on top of FreeRTOS stream buffers. + * Whereas stream buffers are used to send a continuous stream of data from one + * task or interrupt to another, message buffers are used to send variable + * length discrete messages from one task or interrupt to another. Their + * implementation is light weight, making them particularly suited for interrupt + * to task and core to core communication scenarios. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xMessageBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xMessageBufferRead()) inside a critical section and set the receive + * timeout to 0. + * + * Message buffers hold variable length messages. To enable that, when a + * message is written to the message buffer an additional sizeof( size_t ) bytes + * are also written to store the message's length (that happens internally, with + * the API function). sizeof( size_t ) is typically 4 bytes on a 32-bit + * architecture, so writing a 10 byte message to a message buffer on a 32-bit + * architecture will actually reduce the available space in the message buffer + * by 14 bytes (10 byte are used by the message, and 4 bytes to hold the length + * of the message). + */ + +#ifndef FREERTOS_MESSAGE_BUFFER_H +#define FREERTOS_MESSAGE_BUFFER_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include message_buffer.h" +#endif + +/* Message buffers are built onto of stream buffers. */ +#include "stream_buffer.h" + +#if defined( __cplusplus ) +extern "C" { +#endif + +/** + * Type by which message buffers are referenced. For example, a call to + * xMessageBufferCreate() returns an MessageBufferHandle_t variable that can + * then be used as a parameter to xMessageBufferSend(), xMessageBufferReceive(), + * etc. + */ +typedef void * MessageBufferHandle_t; + +/*-----------------------------------------------------------*/ + +/** + * message_buffer.h + * +
+MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
+
+ * + * Creates a new message buffer using dynamically allocated memory. See + * xMessageBufferCreateStatic() for a version that uses statically allocated + * memory (memory that is allocated at compile time). + * + * configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in + * FreeRTOSConfig.h for xMessageBufferCreate() to be available. + * + * @param xBufferSizeBytes The total number of bytes (not messages) the message + * buffer will be able to hold at any one time. When a message is written to + * the message buffer an additional sizeof( size_t ) bytes are also written to + * store the message's length. sizeof( size_t ) is typically 4 bytes on a + * 32-bit architecture, so on most 32-bit architectures a 10 byte message will + * take up 14 bytes of message buffer space. + * + * @return If NULL is returned, then the message buffer cannot be created + * because there is insufficient heap memory available for FreeRTOS to allocate + * the message buffer data structures and storage area. A non-NULL value being + * returned indicates that the message buffer has been created successfully - + * the returned value should be stored as the handle to the created message + * buffer. + * + * Example use: +
+
+void vAFunction( void )
+{
+MessageBufferHandle_t xMessageBuffer;
+const size_t xMessageBufferSizeBytes = 100;
+
+    // Create a message buffer that can hold 100 bytes.  The memory used to hold
+    // both the message buffer structure and the messages themselves is allocated
+    // dynamically.  Each message added to the buffer consumes an additional 4
+    // bytes which are used to hold the lengh of the message.
+    xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
+
+    if( xMessageBuffer == NULL )
+    {
+        // There was not enough heap memory space available to create the
+        // message buffer.
+    }
+    else
+    {
+        // The message buffer was created successfully and can now be used.
+    }
+
+
+ * \defgroup xMessageBufferCreate xMessageBufferCreate + * \ingroup MessageBufferManagement + */ +#define xMessageBufferCreate( xBufferSizeBytes ) ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE ) + +/** + * message_buffer.h + * +
+MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
+                                                  uint8_t *pucMessageBufferStorageArea,
+                                                  StaticMessageBuffer_t *pxStaticMessageBuffer );
+
+ * Creates a new message buffer using statically allocated memory. See + * xMessageBufferCreate() for a version that uses dynamically allocated memory. + * + * @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the + * pucMessageBufferStorageArea parameter. When a message is written to the + * message buffer an additional sizeof( size_t ) bytes are also written to store + * the message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit + * architecture, so on most 32-bit architecture a 10 byte message will take up + * 14 bytes of message buffer space. The maximum number of bytes that can be + * stored in the message buffer is actually (xBufferSizeBytes - 1). + * + * @param pucMessageBufferStorageArea Must point to a uint8_t array that is at + * least xBufferSizeBytes + 1 big. This is the array to which messages are + * copied when they are written to the message buffer. + * + * @param pxStaticMessageBuffer Must point to a variable of type + * StaticMessageBuffer_t, which will be used to hold the message buffer's data + * structure. + * + * @return If the message buffer is created successfully then a handle to the + * created message buffer is returned. If either pucMessageBufferStorageArea or + * pxStaticmessageBuffer are NULL then NULL is returned. + * + * Example use: +
+
+// Used to dimension the array used to hold the messages.  The available space
+// will actually be one less than this, so 999.
+#define STORAGE_SIZE_BYTES 1000
+
+// Defines the memory that will actually hold the messages within the message
+// buffer.
+static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
+
+// The variable used to hold the message buffer structure.
+StaticMessageBuffer_t xMessageBufferStruct;
+
+void MyFunction( void )
+{
+MessageBufferHandle_t xMessageBuffer;
+
+    xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucBufferStorage ),
+                                                 ucBufferStorage,
+                                                 &xMessageBufferStruct );
+
+    // As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
+    // parameters were NULL, xMessageBuffer will not be NULL, and can be used to
+    // reference the created message buffer in other message buffer API calls.
+
+    // Other code that uses the message buffer can go here.
+}
+
+
+ * \defgroup xMessageBufferCreateStatic xMessageBufferCreateStatic + * \ingroup MessageBufferManagement + */ +#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer ) + +/** + * message_buffer.h + * +
+size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
+                           const void *pvTxData,
+                           size_t xDataLengthBytes,
+                           TickType_t xTicksToWait );
+
+ *
+ * Sends a discrete message to the message buffer.  The message can be any
+ * length that fits within the buffer's free space, and is copied into the
+ * buffer.
+ *
+ * ***NOTE***:  Uniquely among FreeRTOS objects, the stream buffer
+ * implementation (so also the message buffer implementation, as message buffers
+ * are built on top of stream buffers) assumes there is only one task or
+ * interrupt that will write to the buffer (the writer), and only one task or
+ * interrupt that will read from the buffer (the reader).  It is safe for the
+ * writer and reader to be different tasks or interrupts, but, unlike other
+ * FreeRTOS objects, it is not safe to have multiple different writers or
+ * multiple different readers.  If there are to be multiple different writers
+ * then the application writer must place each call to a writing API function
+ * (such as xMessageBufferSend()) inside a critical section and set the send
+ * block time to 0.  Likewise, if there are to be multiple different readers
+ * then the application writer must place each call to a reading API function
+ * (such as xMessageBufferRead()) inside a critical section and set the receive
+ * block time to 0.
+ *
+ * Use xMessageBufferSend() to write to a message buffer from a task.  Use
+ * xMessageBufferSendFromISR() to write to a message buffer from an interrupt
+ * service routine (ISR).
+ *
+ * @param xMessageBuffer The handle of the message buffer to which a message is
+ * being sent.
+ *
+ * @param pvTxData A pointer to the message that is to be copied into the
+ * message buffer.
+ *
+ * @param xDataLengthBytes The length of the message.  That is, the number of
+ * bytes to copy from pvTxData into the message buffer.  When a message is
+ * written to the message buffer an additional sizeof( size_t ) bytes are also
+ * written to store the message's length.  sizeof( size_t ) is typically 4 bytes
+ * on a 32-bit architecture, so on most 32-bit architecture setting
+ * xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
+ * bytes (20 bytes of message data and 4 bytes to hold the message length).
+ *
+ * @param xTicksToWait The maximum amount of time the calling task should remain
+ * in the Blocked state to wait for enough space to become available in the
+ * message buffer, should the message buffer have insufficient space when
+ * xMessageBufferSend() is called.  The calling task will never block if
+ * xTicksToWait is zero.  The block time is specified in tick periods, so the
+ * absolute time it represents is dependent on the tick frequency.  The macro
+ * pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into
+ * a time specified in ticks.  Setting xTicksToWait to portMAX_DELAY will cause
+ * the task to wait indefinitely (without timing out), provided
+ * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h.  Tasks do not use any
+ * CPU time when they are in the Blocked state.
+ *
+ * @return The number of bytes written to the message buffer.  If the call to
+ * xMessageBufferSend() times out before there was enough space to write the
+ * message into the message buffer then zero is returned.  If the call did not
+ * time out then xDataLengthBytes is returned.
+ *
+ * Example use:
+
+void vAFunction( MessageBufferHandle_t xMessageBuffer )
+{
+size_t xBytesSent;
+uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
+char *pcStringToSend = "String to send";
+const TickType_t x100ms = pdMS_TO_TICKS( 100 );
+
+    // Send an array to the message buffer, blocking for a maximum of 100ms to
+    // wait for enough space to be available in the message buffer.
+    xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
+
+    if( xBytesSent != sizeof( ucArrayToSend ) )
+    {
+        // The call to xMessageBufferSend() times out before there was enough
+        // space in the buffer for the data to be written.
+    }
+
+    // Send the string to the message buffer.  Return immediately if there is
+    // not enough space in the buffer.
+    xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // The string could not be added to the message buffer because there was
+        // not enough free space in the buffer.
+    }
+}
+
+ * \defgroup xMessageBufferSend xMessageBufferSend + * \ingroup MessageBufferManagement + */ +#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) xStreamBufferSend( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) + +/** + * message_buffer.h + * +
+size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
+                                  const void *pvTxData,
+                                  size_t xDataLengthBytes,
+                                  BaseType_t *pxHigherPriorityTaskWoken );
+
+ *
+ * Interrupt safe version of the API function that sends a discrete message to
+ * the message buffer.  The message can be any length that fits within the
+ * buffer's free space, and is copied into the buffer.
+ *
+ * ***NOTE***:  Uniquely among FreeRTOS objects, the stream buffer
+ * implementation (so also the message buffer implementation, as message buffers
+ * are built on top of stream buffers) assumes there is only one task or
+ * interrupt that will write to the buffer (the writer), and only one task or
+ * interrupt that will read from the buffer (the reader).  It is safe for the
+ * writer and reader to be different tasks or interrupts, but, unlike other
+ * FreeRTOS objects, it is not safe to have multiple different writers or
+ * multiple different readers.  If there are to be multiple different writers
+ * then the application writer must place each call to a writing API function
+ * (such as xMessageBufferSend()) inside a critical section and set the send
+ * block time to 0.  Likewise, if there are to be multiple different readers
+ * then the application writer must place each call to a reading API function
+ * (such as xMessageBufferRead()) inside a critical section and set the receive
+ * block time to 0.
+ *
+ * Use xMessageBufferSend() to write to a message buffer from a task.  Use
+ * xMessageBufferSendFromISR() to write to a message buffer from an interrupt
+ * service routine (ISR).
+ *
+ * @param xMessageBuffer The handle of the message buffer to which a message is
+ * being sent.
+ *
+ * @param pvTxData A pointer to the message that is to be copied into the
+ * message buffer.
+ *
+ * @param xDataLengthBytes The length of the message.  That is, the number of
+ * bytes to copy from pvTxData into the message buffer.  When a message is
+ * written to the message buffer an additional sizeof( size_t ) bytes are also
+ * written to store the message's length.  sizeof( size_t ) is typically 4 bytes
+ * on a 32-bit architecture, so on most 32-bit architecture setting
+ * xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
+ * bytes (20 bytes of message data and 4 bytes to hold the message length).
+ *
+ * @param pxHigherPriorityTaskWoken  It is possible that a message buffer will
+ * have a task blocked on it waiting for data.  Calling
+ * xMessageBufferSendFromISR() can make data available, and so cause a task that
+ * was waiting for data to leave the Blocked state.  If calling
+ * xMessageBufferSendFromISR() causes a task to leave the Blocked state, and the
+ * unblocked task has a priority higher than the currently executing task (the
+ * task that was interrupted), then, internally, xMessageBufferSendFromISR()
+ * will set *pxHigherPriorityTaskWoken to pdTRUE.  If
+ * xMessageBufferSendFromISR() sets this value to pdTRUE, then normally a
+ * context switch should be performed before the interrupt is exited.  This will
+ * ensure that the interrupt returns directly to the highest priority Ready
+ * state task.  *pxHigherPriorityTaskWoken should be set to pdFALSE before it
+ * is passed into the function.  See the code example below for an example.
+ *
+ * @return The number of bytes actually written to the message buffer.  If the
+ * message buffer didn't have enough free space for the message to be stored
+ * then 0 is returned, otherwise xDataLengthBytes is returned.
+ *
+ * Example use:
+
+// A message buffer that has already been created.
+MessageBufferHandle_t xMessageBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+size_t xBytesSent;
+char *pcStringToSend = "String to send";
+BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
+
+    // Attempt to send the string to the message buffer.
+    xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
+                                            ( void * ) pcStringToSend,
+                                            strlen( pcStringToSend ),
+                                            &xHigherPriorityTaskWoken );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // The string could not be added to the message buffer because there was
+        // not enough free space in the buffer.
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xMessageBufferSendFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR + * \ingroup MessageBufferManagement + */ +#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferSendFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) + +/** + * message_buffer.h + * +
+size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
+                              void *pvRxData,
+                              size_t xBufferLengthBytes,
+                              TickType_t xTicksToWait );
+
+ * + * Receives a discrete message from a message buffer. Messages can be of + * variable length and are copied out of the buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xMessageBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xMessageBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xMessageBufferReceive() to read from a message buffer from a task. Use + * xMessageBufferReceiveFromISR() to read from a message buffer from an + * interrupt service routine (ISR). + * + * @param xMessageBuffer The handle of the message buffer from which a message + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received message is + * to be copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData + * parameter. This sets the maximum length of the message that can be received. + * If xBufferLengthBytes is too small to hold the next message then the message + * will be left in the message buffer and 0 will be returned. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for a message, should the message buffer be empty. + * xMessageBufferReceive() will return immediately if xTicksToWait is zero and + * the message buffer is empty. The block time is specified in tick periods, so + * the absolute time it represents is dependent on the tick frequency. The + * macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds + * into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will + * cause the task to wait indefinitely (without timing out), provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any + * CPU time when they are in the Blocked state. + * + * @return The length, in bytes, of the message read from the message buffer, if + * any. If xMessageBufferReceive() times out before a message became available + * then zero is returned. If the length of the message is greater than + * xBufferLengthBytes then the message will be left in the message buffer and + * zero is returned. + * + * Example use: +
+void vAFunction( MessageBuffer_t xMessageBuffer )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
+
+    // Receive the next message from the message buffer.  Wait in the Blocked
+    // state (so not using any CPU processing time) for a maximum of 100ms for
+    // a message to become available.
+    xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
+                                            ( void * ) ucRxData,
+                                            sizeof( ucRxData ),
+                                            xBlockTime );
+
+    if( xReceivedBytes > 0 )
+    {
+        // A ucRxData contains a message that is xReceivedBytes long.  Process
+        // the message here....
+    }
+}
+
+ * \defgroup xMessageBufferReceive xMessageBufferReceive + * \ingroup MessageBufferManagement + */ +#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) xStreamBufferReceive( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) + + +/** + * message_buffer.h + * +
+size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
+                                     void *pvRxData,
+                                     size_t xBufferLengthBytes,
+                                     BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * An interrupt safe version of the API function that receives a discrete + * message from a message buffer. Messages can be of variable length and are + * copied out of the buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xMessageBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xMessageBufferRead()) inside a critical section and set the receive + * block time to 0. + * + * Use xMessageBufferReceive() to read from a message buffer from a task. Use + * xMessageBufferReceiveFromISR() to read from a message buffer from an + * interrupt service routine (ISR). + * + * @param xMessageBuffer The handle of the message buffer from which a message + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received message is + * to be copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData + * parameter. This sets the maximum length of the message that can be received. + * If xBufferLengthBytes is too small to hold the next message then the message + * will be left in the message buffer and 0 will be returned. + * + * @param pxHigherPriorityTaskWoken It is possible that a message buffer will + * have a task blocked on it waiting for space to become available. Calling + * xMessageBufferReceiveFromISR() can make space available, and so cause a task + * that is waiting for space to leave the Blocked state. If calling + * xMessageBufferReceiveFromISR() causes a task to leave the Blocked state, and + * the unblocked task has a priority higher than the currently executing task + * (the task that was interrupted), then, internally, + * xMessageBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE. + * If xMessageBufferReceiveFromISR() sets this value to pdTRUE, then normally a + * context switch should be performed before the interrupt is exited. That will + * ensure the interrupt returns directly to the highest priority Ready state + * task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is + * passed into the function. See the code example below for an example. + * + * @return The length, in bytes, of the message read from the message buffer, if + * any. + * + * Example use: +
+// A message buffer that has already been created.
+MessageBuffer_t xMessageBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
+
+    // Receive the next message from the message buffer.
+    xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
+                                                  ( void * ) ucRxData,
+                                                  sizeof( ucRxData ),
+                                                  &xHigherPriorityTaskWoken );
+
+    if( xReceivedBytes > 0 )
+    {
+        // A ucRxData contains a message that is xReceivedBytes long.  Process
+        // the message here....
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xMessageBufferReceiveFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR + * \ingroup MessageBufferManagement + */ +#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferReceiveFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) + +/** + * message_buffer.h + * +
+void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
+
+ * + * Deletes a message buffer that was previously created using a call to + * xMessageBufferCreate() or xMessageBufferCreateStatic(). If the message + * buffer was created using dynamic memory (that is, by xMessageBufferCreate()), + * then the allocated memory is freed. + * + * A message buffer handle must not be used after the message buffer has been + * deleted. + * + * @param xMessageBuffer The handle of the message buffer to be deleted. + * + */ +#define vMessageBufferDelete( xMessageBuffer ) vStreamBufferDelete( ( StreamBufferHandle_t ) xMessageBuffer ) + +/** + * message_buffer.h +
+BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer ) );
+
+ * + * Tests to see if a message buffer is full. A message buffer is full if it + * cannot accept any more messages, of any size, until space is made available + * by a message being removed from the message buffer. + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return If the message buffer referenced by xMessageBuffer is full then + * pdTRUE is returned. Otherwise pdFALSE is returned. + */ +#define xMessageBufferIsFull( xMessageBuffer ) xStreamBufferIsFull( ( StreamBufferHandle_t ) xMessageBuffer ) + +/** + * message_buffer.h +
+BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer ) );
+
+ * + * Tests to see if a message buffer is empty (does not contain any messages). + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return If the message buffer referenced by xMessageBuffer is empty then + * pdTRUE is returned. Otherwise pdFALSE is returned. + * + */ +#define xMessageBufferIsEmpty( xMessageBuffer ) xStreamBufferIsEmpty( ( StreamBufferHandle_t ) xMessageBuffer ) + +/** + * message_buffer.h +
+BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
+
+ * + * Resets a message buffer to its initial empty state, discarding any message it + * contained. + * + * A message buffer can only be reset if there are no tasks blocked on it. + * + * @param xMessageBuffer The handle of the message buffer being reset. + * + * @return If the message buffer was reset then pdPASS is returned. If the + * message buffer could not be reset because either there was a task blocked on + * the message queue to wait for space to become available, or to wait for a + * a message to be available, then pdFAIL is returned. + * + * \defgroup xMessageBufferReset xMessageBufferReset + * \ingroup MessageBufferManagement + */ +#define xMessageBufferReset( xMessageBuffer ) xStreamBufferReset( ( StreamBufferHandle_t ) xMessageBuffer ) + + +/** + * message_buffer.h +
+size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) );
+
+ * Returns the number of bytes of free space in the message buffer. + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return The number of bytes that can be written to the message buffer before + * the message buffer would be full. When a message is written to the message + * buffer an additional sizeof( size_t ) bytes are also written to store the + * message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit + * architecture, so if xMessageBufferSpacesAvailable() returns 10, then the size + * of the largest message that can be written to the message buffer is 6 bytes. + * + * \defgroup xMessageBufferSpaceAvailable xMessageBufferSpaceAvailable + * \ingroup MessageBufferManagement + */ +#define xMessageBufferSpaceAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) +#define xMessageBufferSpacesAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) /* Corrects typo in original macro name. */ + +/** + * message_buffer.h +
+ size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer ) );
+ 
+ * Returns the length (in bytes) of the next message in a message buffer. + * Useful if xMessageBufferReceive() returned 0 because the size of the buffer + * passed into xMessageBufferReceive() was too small to hold the next message. + * + * @param xMessageBuffer The handle of the message buffer being queried. + * + * @return The length (in bytes) of the next message in the message buffer, or 0 + * if the message buffer is empty. + * + * \defgroup xMessageBufferNextLengthBytes xMessageBufferNextLengthBytes + * \ingroup MessageBufferManagement + */ +#define xMessageBufferNextLengthBytes( xMessageBuffer ) xStreamBufferNextMessageLengthBytes( ( StreamBufferHandle_t ) xMessageBuffer ) PRIVILEGED_FUNCTION; + +/** + * message_buffer.h + * +
+BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is sent to a message buffer or stream buffer. If there was a task that + * was blocked on the message or stream buffer waiting for data to arrive then + * the sbSEND_COMPLETED() macro sends a notification to the task to remove it + * from the Blocked state. xMessageBufferSendCompletedFromISR() does the same + * thing. It is provided to enable application writers to implement their own + * version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer to which data was + * written. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xMessageBufferSendCompletedFromISR(). If calling + * xMessageBufferSendCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xMessageBufferSendCompletedFromISR xMessageBufferSendCompletedFromISR + * \ingroup StreamBufferManagement + */ +#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferSendCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) + +/** + * message_buffer.h + * +
+BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is read out of a message buffer or stream buffer. If there was a task + * that was blocked on the message or stream buffer waiting for data to arrive + * then the sbRECEIVE_COMPLETED() macro sends a notification to the task to + * remove it from the Blocked state. xMessageBufferReceiveCompletedFromISR() + * does the same thing. It is provided to enable application writers to + * implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT + * ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer from which data was + * read. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xMessageBufferReceiveCompletedFromISR(). If calling + * xMessageBufferReceiveCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xMessageBufferReceiveCompletedFromISR xMessageBufferReceiveCompletedFromISR + * \ingroup StreamBufferManagement + */ +#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferReceiveCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) + +#if defined( __cplusplus ) +} /* extern "C" */ +#endif + +#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h index 8f7500b0..a21b7a66 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* * When the MPU is used the standard (non MPU) API functions are mapped to @@ -79,99 +37,124 @@ #ifndef MPU_PROTOTYPES_H #define MPU_PROTOTYPES_H -/* MPU versions of tasks.h API function. */ -BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask ); -TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer ); -BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ); -void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ); -void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ); -void MPU_vTaskDelay( const TickType_t xTicksToDelay ); -void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ); -BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ); -UBaseType_t MPU_uxTaskPriorityGet( TaskHandle_t xTask ); -eTaskState MPU_eTaskGetState( TaskHandle_t xTask ); -void MPU_vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ); -void MPU_vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); -void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ); -void MPU_vTaskResume( TaskHandle_t xTaskToResume ); -void MPU_vTaskStartScheduler( void ); -void MPU_vTaskSuspendAll( void ); -BaseType_t MPU_xTaskResumeAll( void ); -TickType_t MPU_xTaskGetTickCount( void ); -UBaseType_t MPU_uxTaskGetNumberOfTasks( void ); -char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ); -TaskHandle_t MPU_xTaskGetHandle( const char *pcNameToQuery ); -UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); -void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ); -TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ); -void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ); -void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ); -BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ); -TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ); -UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); -void MPU_vTaskList( char * pcWriteBuffer ); -void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ); -BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ); -BaseType_t MPU_xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); -uint32_t MPU_ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); -BaseType_t MPU_xTaskNotifyStateClear( TaskHandle_t xTask ); -BaseType_t MPU_xTaskIncrementTick( void ); -TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ); -void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ); -BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ); -void MPU_vTaskMissedYield( void ); -BaseType_t MPU_xTaskGetSchedulerState( void ); +/* MPU versions of tasks.h API functions. */ +BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelay( const TickType_t xTicksToDelay ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskPriorityGet( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +eTaskState MPU_eTaskGetState( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskResume( TaskHandle_t xTaskToResume ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskStartScheduler( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSuspendAll( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskResumeAll( void ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTaskGetTickCount( void ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) FREERTOS_SYSTEM_CALL; +char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetHandle( const char *pcNameToQuery ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +configSTACK_DEPTH_TYPE MPU_uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) FREERTOS_SYSTEM_CALL; +TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) FREERTOS_SYSTEM_CALL; +void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskList( char * pcWriteBuffer ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskNotifyStateClear( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskIncrementTick( void ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskMissedYield( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) FREERTOS_SYSTEM_CALL; -/* MPU versions of queue.h API function. */ -BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ); -BaseType_t MPU_xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ); -UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ); -UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ); -void MPU_vQueueDelete( QueueHandle_t xQueue ); -QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ); -QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ); -QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ); -QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ); -void* MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ); -BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ); -BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ); -void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ); -void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ); -const char * MPU_pcQueueGetName( QueueHandle_t xQueue ); -QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ); -QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ); -QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ); -BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); -BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); -QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ); -BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ); -void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ); -UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ); -uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ); +/* MPU versions of queue.h API functions. */ +BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueDelete( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +const char * MPU_pcQueueGetName( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL; +QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of timers.h API functions. */ +TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) FREERTOS_SYSTEM_CALL; +TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, StaticTimer_t *pxTimerBuffer ) FREERTOS_SYSTEM_CALL; +void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +void MPU_vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +void MPU_vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerCreateTimerTask( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of event_group.h API functions. */ +EventGroupHandle_t MPU_xEventGroupCreate( void ) FREERTOS_SYSTEM_CALL; +EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxEventGroupGetNumber( void* xEventGroup ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of message/stream_buffer.h API functions. */ +size_t MPU_xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL; +StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer ) FREERTOS_SYSTEM_CALL; +StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, uint8_t * const pucStreamBufferStorageArea, StaticStreamBuffer_t * const pxStaticStreamBuffer ) FREERTOS_SYSTEM_CALL; -/* MPU versions of timers.h API function. */ -TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ); -TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, StaticTimer_t *pxTimerBuffer ); -void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ); -void MPU_vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); -BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ); -TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ); -BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); -const char * MPU_pcTimerGetName( TimerHandle_t xTimer ); -TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ); -TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ); -BaseType_t MPU_xTimerCreateTimerTask( void ); -BaseType_t MPU_xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ); -/* MPU versions of event_group.h API function. */ -EventGroupHandle_t MPU_xEventGroupCreate( void ); -EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ); -EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ); -EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ); -EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); -EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ); -void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ); -UBaseType_t MPU_uxEventGroupGetNumber( void* xEventGroup ); #endif /* MPU_PROTOTYPES_H */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h index 1a05c9fd..5f63d4f2 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef MPU_WRAPPERS_H #define MPU_WRAPPERS_H @@ -109,6 +67,7 @@ only for ports that are using the MPU. */ #define pcTaskGetName MPU_pcTaskGetName #define xTaskGetHandle MPU_xTaskGetHandle #define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark + #define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2 #define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag #define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag #define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer @@ -118,10 +77,13 @@ only for ports that are using the MPU. */ #define uxTaskGetSystemState MPU_uxTaskGetSystemState #define vTaskList MPU_vTaskList #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats + #define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter #define xTaskGenericNotify MPU_xTaskGenericNotify #define xTaskNotifyWait MPU_xTaskNotifyWait #define ulTaskNotifyTake MPU_ulTaskNotifyTake #define xTaskNotifyStateClear MPU_xTaskNotifyStateClear + #define ulTaskNotifyValueClear MPU_ulTaskNotifyValueClear + #define xTaskCatchUpTicks MPU_xTaskCatchUpTicks #define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle #define vTaskSetTimeOutState MPU_vTaskSetTimeOutState @@ -130,7 +92,9 @@ only for ports that are using the MPU. */ /* Map standard queue.h API functions to the MPU equivalents. */ #define xQueueGenericSend MPU_xQueueGenericSend - #define xQueueGenericReceive MPU_xQueueGenericReceive + #define xQueueReceive MPU_xQueueReceive + #define xQueuePeek MPU_xQueuePeek + #define xQueueSemaphoreTake MPU_xQueueSemaphoreTake #define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting #define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable #define vQueueDelete MPU_vQueueDelete @@ -164,6 +128,8 @@ only for ports that are using the MPU. */ #define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle #define xTimerPendFunctionCall MPU_xTimerPendFunctionCall #define pcTimerGetName MPU_pcTimerGetName + #define vTimerSetReloadMode MPU_vTimerSetReloadMode + #define uxTimerGetReloadMode MPU_uxTimerGetReloadMode #define xTimerGetPeriod MPU_xTimerGetPeriod #define xTimerGetExpiryTime MPU_xTimerGetExpiryTime #define xTimerGenericCommand MPU_xTimerGenericCommand @@ -177,25 +143,35 @@ only for ports that are using the MPU. */ #define xEventGroupSync MPU_xEventGroupSync #define vEventGroupDelete MPU_vEventGroupDelete - /* Remove the privileged function macro. */ + /* Map standard message/stream_buffer.h API functions to the MPU + equivalents. */ + #define xStreamBufferSend MPU_xStreamBufferSend + #define xStreamBufferReceive MPU_xStreamBufferReceive + #define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes + #define vStreamBufferDelete MPU_vStreamBufferDelete + #define xStreamBufferIsFull MPU_xStreamBufferIsFull + #define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty + #define xStreamBufferReset MPU_xStreamBufferReset + #define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable + #define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable + #define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel + #define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate + #define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic + + + /* Remove the privileged function macro, but keep the PRIVILEGED_DATA + macro so applications can place data in privileged access sections + (useful when using statically allocated objects). */ #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + #define FREERTOS_SYSTEM_CALL #else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ /* Ensure API functions go in the privileged execution section. */ -#if defined(__ICCARM__) - -#define PRIVILEGED_FUNCTION _Pragma("location= \"privileged_functions\"") -#define PRIVILEGED_DATA _Pragma("location= \"privileged_data\"") -#define PRIVILEGED_INITIALIZED_DATA _Pragma("location= \"privileged_initialized_data\"") - -#else - -#define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) -#define PRIVILEGED_DATA __attribute__((section("privileged_data"))) -#define PRIVILEGED_INITIALIZED_DATA PRIVILEGED_DATA - -#endif + #define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + #define FREERTOS_SYSTEM_CALL __attribute__((section( "freertos_system_calls"))) #endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ @@ -203,7 +179,7 @@ only for ports that are using the MPU. */ #define PRIVILEGED_FUNCTION #define PRIVILEGED_DATA - #define PRIVILEGED_INITIALIZED_DATA + #define FREERTOS_SYSTEM_CALL #define portUSING_MPU_WRAPPERS 0 #endif /* portUSING_MPU_WRAPPERS */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h index 00a22086..a2099c33 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /*----------------------------------------------------------- * Portable layer API. Each function must be defined for each port. @@ -126,6 +84,14 @@ must be set in the compiler's include path. */ #define portNUM_CONFIGURABLE_REGIONS 1 #endif +#ifndef portHAS_STACK_OVERFLOW_CHECKING + #define portHAS_STACK_OVERFLOW_CHECKING 0 +#endif + +#ifndef portARCH_NAME + #define portARCH_NAME NULL +#endif + #ifdef __cplusplus extern "C" { #endif @@ -139,18 +105,39 @@ extern "C" { * */ #if( portUSING_MPU_WRAPPERS == 1 ) - PRIVILEGED_FUNCTION StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) ; + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #endif #else - PRIVILEGED_FUNCTION StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) ; + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; + #else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; + #endif #endif -/* Used by heap_5.c. */ +/* Used by heap_5.c to define the start address and size of each memory region +that together comprise the total FreeRTOS heap space. */ typedef struct HeapRegion { uint8_t *pucStartAddress; size_t xSizeInBytes; } HeapRegion_t; +/* Used to pass information about the heap out of vPortGetHeapStats(). */ +typedef struct xHeapStats +{ + size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */ + size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */ + size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */ + size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */ +} HeapStats_t; + /* * Used to define multiple heap regions for use by heap_5.c. This function * must be called before any calls to pvPortMalloc() - not creating a task, @@ -162,30 +149,35 @@ typedef struct HeapRegion * terminated by a HeapRegions_t structure that has a size of 0. The region * with the lowest start address must appear first in the array. */ -PRIVILEGED_FUNCTION void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ); +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION; +/* + * Returns a HeapStats_t structure filled with information about the current + * heap state. + */ +void vPortGetHeapStats( HeapStats_t *pxHeapStats ); /* * Map to the memory management routines required for the port. */ -PRIVILEGED_FUNCTION void *pvPortMalloc( size_t xSize ); -PRIVILEGED_FUNCTION void vPortFree( void *pv ); -PRIVILEGED_FUNCTION void vPortInitialiseBlocks( void ); -PRIVILEGED_FUNCTION size_t xPortGetFreeHeapSize( void ); -PRIVILEGED_FUNCTION size_t xPortGetMinimumEverFreeHeapSize( void ); +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void *pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; +size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION; /* * Setup the hardware ready for the scheduler to take control. This generally * sets up a tick interrupt and sets timers for the correct tick frequency. */ -PRIVILEGED_FUNCTION BaseType_t xPortStartScheduler( void ); +BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION; /* * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so * the hardware is left in its original condition after the scheduler stops * executing. */ -PRIVILEGED_FUNCTION void vPortEndScheduler( void ); +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; /* * The structures and methods of manipulating the MPU are contained within the @@ -196,7 +188,7 @@ PRIVILEGED_FUNCTION void vPortEndScheduler( void ); */ #if( portUSING_MPU_WRAPPERS == 1 ) struct xMEMORY_REGION; - PRIVILEGED_FUNCTION void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth ); + void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth ) PRIVILEGED_FUNCTION; #endif #ifdef __cplusplus diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h index 0b63fd8a..0d95130b 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef PROJDEFS_H #define PROJDEFS_H @@ -152,8 +110,13 @@ itself. */ /* The following endian values are used by FreeRTOS+ components, not FreeRTOS itself. */ -#define pdFREERTOS_LITTLE_ENDIAN 0 -#define pdFREERTOS_BIG_ENDIAN 1 +#define pdFREERTOS_LITTLE_ENDIAN 0 +#define pdFREERTOS_BIG_ENDIAN 1 + +/* Re-defining endian values for generic naming. */ +#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN +#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN + #endif /* PROJDEFS_H */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h index a64640c6..52ccca55 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef QUEUE_H @@ -79,27 +37,29 @@ extern "C" { #endif +#include "task.h" /** * Type by which queues are referenced. For example, a call to xQueueCreate() * returns an QueueHandle_t variable that can then be used as a parameter to * xQueueSend(), xQueueReceive(), etc. */ -typedef void * QueueHandle_t; +struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ +typedef struct QueueDefinition * QueueHandle_t; /** * Type by which queue sets are referenced. For example, a call to * xQueueCreateSet() returns an xQueueSet variable that can then be used as a * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. */ -typedef void * QueueSetHandle_t; +typedef struct QueueDefinition * QueueSetHandle_t; /** * Queue sets can contain both queues and semaphores, so the * QueueSetMemberHandle_t is defined as a type to be used where a parameter or * return value can be either an QueueHandle_t or an SemaphoreHandle_t. */ -typedef void * QueueSetMemberHandle_t; +typedef struct QueueDefinition * QueueSetMemberHandle_t; /* For internal use only. */ #define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) @@ -282,8 +242,6 @@ typedef void * QueueSetMemberHandle_t; ); *
* - * This is a macro that calls xQueueGenericSend(). - * * Post an item to the front of a queue. The item is queued by copy, not by * reference. This function must not be called from an interrupt service * routine. See xQueueSendFromISR () for an alternative which may be used @@ -689,19 +647,17 @@ typedef void * QueueSetMemberHandle_t; * \defgroup xQueueSend xQueueSend * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ); +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; /** * queue. h *
  BaseType_t xQueuePeek(
 							 QueueHandle_t xQueue,
-							 void *pvBuffer,
+							 void * const pvBuffer,
 							 TickType_t xTicksToWait
 						 );
* - * This is a macro that calls the xQueueGenericReceive() function. - * * Receive an item from a queue without removing the item from the queue. * The item is received by copy so a buffer of adequate size must be * provided. The number of bytes copied into the buffer was defined when @@ -782,10 +738,10 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const vo // ... Rest of task code. }
- * \defgroup xQueueReceive xQueueReceive + * \defgroup xQueuePeek xQueuePeek * \ingroup QueueManagement */ -#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) +BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * queue. h @@ -818,7 +774,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const vo * \defgroup xQueuePeekFromISR xQueuePeekFromISR * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ); +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; /** * queue. h @@ -829,8 +785,6 @@ PRIVILEGED_FUNCTION BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * c TickType_t xTicksToWait );
* - * This is a macro that calls the xQueueGenericReceive() function. - * * Receive an item from a queue. The item is received by copy so a buffer of * adequate size must be provided. The number of bytes copied into the buffer * was defined when the queue was created. @@ -911,106 +865,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * c * \defgroup xQueueReceive xQueueReceive * \ingroup QueueManagement */ -#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) - - -/** - * queue. h - *
- BaseType_t xQueueGenericReceive(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   TickType_t	xTicksToWait
-									   BaseType_t	xJustPeek
-									);
- * - * It is preferred that the macro xQueueReceive() be used rather than calling - * this function directly. - * - * Receive an item from a queue. The item is received by copy so a buffer of - * adequate size must be provided. The number of bytes copied into the buffer - * was defined when the queue was created. - * - * This function must not be used in an interrupt service routine. See - * xQueueReceiveFromISR for an alternative that can. - * - * @param xQueue The handle to the queue from which the item is to be - * received. - * - * @param pvBuffer Pointer to the buffer into which the received item will - * be copied. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for an item to receive should the queue be empty at the time - * of the call. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * xQueueGenericReceive() will return immediately if the queue is empty and - * xTicksToWait is 0. - * - * @param xJustPeek When set to true, the item received from the queue is not - * actually removed from the queue - meaning a subsequent call to - * xQueueReceive() will return the same item. When set to false, the item - * being received from the queue is also removed from the queue. - * - * @return pdTRUE if an item was successfully received from the queue, - * otherwise pdFALSE. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive - * \ingroup QueueManagement - */ -PRIVILEGED_FUNCTION BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ); +BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1025,7 +880,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ); +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1042,7 +897,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQue * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ); +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1056,7 +911,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQue * \defgroup vQueueDelete vQueueDelete * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION void vQueueDelete( QueueHandle_t xQueue ); +void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1429,7 +1284,7 @@ uint32_t ulVarToSend, ulValReceived; // name of the yield function required is port specific. if( xHigherPriorityTaskWokenByPost ) { - taskYIELD_YIELD_FROM_ISR(); + portYIELD_FROM_ISR(); } }
@@ -1437,8 +1292,8 @@ uint32_t ulVarToSend, ulValReceived; * \defgroup xQueueSendFromISR xQueueSendFromISR * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ); -PRIVILEGED_FUNCTION BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ); +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * queue. h @@ -1527,15 +1382,15 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR * \ingroup QueueManagement */ -PRIVILEGED_FUNCTION BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ); +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /* * Utilities to query queues that are safe to use from an ISR. These utilities * should be used only from witin an ISR, or within a critical section. */ -PRIVILEGED_FUNCTION BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ); -PRIVILEGED_FUNCTION BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ); -PRIVILEGED_FUNCTION UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ); +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /* * The functions defined above are for passing data to and from tasks. The @@ -1556,18 +1411,20 @@ BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTi * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling * these functions directly. */ -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ); -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ); -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ); -PRIVILEGED_FUNCTION QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ); -PRIVILEGED_FUNCTION void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ); +QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; /* * For internal use only. Use xSemaphoreTakeMutexRecursive() or * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. */ -PRIVILEGED_FUNCTION BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ); -PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ); +BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) PRIVILEGED_FUNCTION; /* * Reset a queue back to its original empty state. The return value is now @@ -1598,7 +1455,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * preferably in ROM/Flash), not on the stack. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - PRIVILEGED_FUNCTION void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif /* @@ -1612,7 +1469,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * @param xQueue The handle of the queue being removed from the registry. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - PRIVILEGED_FUNCTION void vQueueUnregisterQueue( QueueHandle_t xQueue ); + void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; #endif /* @@ -1627,7 +1484,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * returned. */ #if( configQUEUE_REGISTRY_SIZE > 0 ) - PRIVILEGED_FUNCTION const char *pcQueueGetName( QueueHandle_t xQueue ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #endif /* @@ -1636,7 +1493,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * RTOS objects that use the queue structure as their base. */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ); + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif /* @@ -1645,7 +1502,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * RTOS objects that use the queue structure as their base. */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ); + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; #endif /* @@ -1696,7 +1553,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) * @return If the queue set is created successfully then a handle to the created * queue set is returned. Otherwise NULL is returned. */ -PRIVILEGED_FUNCTION QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ); +QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; /* * Adds a queue or semaphore to a queue set that was previously created by a @@ -1720,7 +1577,7 @@ PRIVILEGED_FUNCTION QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQ * queue set because it is already a member of a different queue set then pdFAIL * is returned. */ -PRIVILEGED_FUNCTION BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); +BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; /* * Removes a queue or semaphore from a queue set. A queue or semaphore can only @@ -1739,7 +1596,7 @@ PRIVILEGED_FUNCTION BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSe * then pdPASS is returned. If the queue was not in the queue set, or the * queue (or semaphore) was not empty, then pdFAIL is returned. */ -PRIVILEGED_FUNCTION BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); +BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; /* * xQueueSelectFromSet() selects from the members of a queue set a queue or @@ -1775,19 +1632,19 @@ PRIVILEGED_FUNCTION BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueu * in the queue set that is available, or NULL if no such queue or semaphore * exists before before the specified block time expires. */ -PRIVILEGED_FUNCTION QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ); +QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /* * A version of xQueueSelectFromSet() that can be used from an ISR. */ -PRIVILEGED_FUNCTION QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ); +QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; /* Not public API functions. */ -PRIVILEGED_FUNCTION void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) ; -PRIVILEGED_FUNCTION BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ); -PRIVILEGED_FUNCTION void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ); -PRIVILEGED_FUNCTION UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ); -PRIVILEGED_FUNCTION uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ); +void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; +void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; #ifdef __cplusplus diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h index a674b02a..787c7912 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef SEMAPHORE_H #define SEMAPHORE_H @@ -328,7 +286,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup xSemaphoreTake xSemaphoreTake * \ingroup Semaphores */ -#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) ) /** * semphr. h @@ -392,23 +350,23 @@ typedef QueueHandle_t SemaphoreHandle_t; // ... // For some reason due to the nature of the code further calls to - // xSemaphoreTakeRecursive() are made on the same mutex. In real - // code these would not be just sequential calls as this would make - // no sense. Instead the calls are likely to be buried inside - // a more complex call structure. + // xSemaphoreTakeRecursive() are made on the same mutex. In real + // code these would not be just sequential calls as this would make + // no sense. Instead the calls are likely to be buried inside + // a more complex call structure. xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // The mutex has now been 'taken' three times, so will not be - // available to another task until it has also been given back - // three times. Again it is unlikely that real code would have - // these calls sequentially, but instead buried in a more complex - // call structure. This is just for illustrative purposes. + // available to another task until it has also been given back + // three times. Again it is unlikely that real code would have + // these calls sequentially, but instead buried in a more complex + // call structure. This is just for illustrative purposes. + xSemaphoreGiveRecursive( xMutex ); + xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); - xSemaphoreGiveRecursive( xMutex ); - xSemaphoreGiveRecursive( xMutex ); - // Now the mutex can be taken by other tasks. + // Now the mutex can be taken by other tasks. } else { @@ -1154,6 +1112,17 @@ typedef QueueHandle_t SemaphoreHandle_t; */ #define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) +/** + * semphr.h + *
TaskHandle_t xSemaphoreGetMutexHolderFromISR( SemaphoreHandle_t xMutex );
+ * + * If xMutex is indeed a mutex type semaphore, return the current mutex holder. + * If xMutex is not a mutex type semaphore, or the mutex is available (not held + * by a task), return NULL. + * + */ +#define xSemaphoreGetMutexHolderFromISR( xSemaphore ) xQueueGetMutexHolderFromISR( ( xSemaphore ) ) + /** * semphr.h *
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h new file mode 100644 index 00000000..b5bac083 --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h @@ -0,0 +1,129 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef STACK_MACROS_H +#define STACK_MACROS_H + +/* + * Call the stack overflow hook function if the stack of the task being swapped + * out is currently overflowed, or looks like it might have overflowed in the + * past. + * + * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check + * the current stack state only - comparing the current top of stack value to + * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 + * will also cause the last few stack bytes to be checked to ensure the value + * to which the bytes were set when the task was created have not been + * overwritten. Note this second test does not guarantee that an overflowed + * stack will always be recognised. + */ + +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ + const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ + \ + if( ( pulStack[ 0 ] != ulCheckValue ) || \ + ( pulStack[ 1 ] != ulCheckValue ) || \ + ( pulStack[ 2 ] != ulCheckValue ) || \ + ( pulStack[ 3 ] != ulCheckValue ) ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ + static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +/* Remove stack overflow macro if not being used. */ +#ifndef taskCHECK_FOR_STACK_OVERFLOW + #define taskCHECK_FOR_STACK_OVERFLOW() +#endif + + + +#endif /* STACK_MACROS_H */ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme new file mode 100644 index 00000000..4414c29e --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme @@ -0,0 +1,27 @@ + +#ifndef FREERTOS_STDINT +#define FREERTOS_STDINT + +/******************************************************************************* + * THIS IS NOT A FULL stdint.h IMPLEMENTATION - It only contains the definitions + * necessary to build the FreeRTOS code. It is provided to allow FreeRTOS to be + * built using compilers that do not provide their own stdint.h definition. + * + * To use this file: + * + * 1) Copy this file into the directory that contains your FreeRTOSConfig.h + * header file, as that directory will already be in the compilers include + * path. + * + * 2) Rename the copied file stdint.h. + * + */ + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef long int32_t; +typedef unsigned long uint32_t; + +#endif /* FREERTOS_STDINT */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h new file mode 100644 index 00000000..a8b68ad6 --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h @@ -0,0 +1,859 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * Stream buffers are used to send a continuous stream of data from one task or + * interrupt to another. Their implementation is light weight, making them + * particularly suited for interrupt to task and core to core communication + * scenarios. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferReceive()) inside a critical section section and set the + * receive block time to 0. + * + */ + +#ifndef STREAM_BUFFER_H +#define STREAM_BUFFER_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include stream_buffer.h" +#endif + +#if defined( __cplusplus ) +extern "C" { +#endif + +/** + * Type by which stream buffers are referenced. For example, a call to + * xStreamBufferCreate() returns an StreamBufferHandle_t variable that can + * then be used as a parameter to xStreamBufferSend(), xStreamBufferReceive(), + * etc. + */ +struct StreamBufferDef_t; +typedef struct StreamBufferDef_t * StreamBufferHandle_t; + + +/** + * message_buffer.h + * +
+StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes );
+
+ * + * Creates a new stream buffer using dynamically allocated memory. See + * xStreamBufferCreateStatic() for a version that uses statically allocated + * memory (memory that is allocated at compile time). + * + * configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in + * FreeRTOSConfig.h for xStreamBufferCreate() to be available. + * + * @param xBufferSizeBytes The total number of bytes the stream buffer will be + * able to hold at any one time. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * + * @return If NULL is returned, then the stream buffer cannot be created + * because there is insufficient heap memory available for FreeRTOS to allocate + * the stream buffer data structures and storage area. A non-NULL value being + * returned indicates that the stream buffer has been created successfully - + * the returned value should be stored as the handle to the created stream + * buffer. + * + * Example use: +
+
+void vAFunction( void )
+{
+StreamBufferHandle_t xStreamBuffer;
+const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10;
+
+    // Create a stream buffer that can hold 100 bytes.  The memory used to hold
+    // both the stream buffer structure and the data in the stream buffer is
+    // allocated dynamically.
+    xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
+
+    if( xStreamBuffer == NULL )
+    {
+        // There was not enough heap memory space available to create the
+        // stream buffer.
+    }
+    else
+    {
+        // The stream buffer was created successfully and can now be used.
+    }
+}
+
+ * \defgroup xStreamBufferCreate xStreamBufferCreate + * \ingroup StreamBufferManagement + */ +#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE ) + +/** + * stream_buffer.h + * +
+StreamBufferHandle_t xStreamBufferCreateStatic( size_t xBufferSizeBytes,
+                                                size_t xTriggerLevelBytes,
+                                                uint8_t *pucStreamBufferStorageArea,
+                                                StaticStreamBuffer_t *pxStaticStreamBuffer );
+
+ * Creates a new stream buffer using statically allocated memory. See + * xStreamBufferCreate() for a version that uses dynamically allocated memory. + * + * configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h for + * xStreamBufferCreateStatic() to be available. + * + * @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the + * pucStreamBufferStorageArea parameter. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * + * @param pucStreamBufferStorageArea Must point to a uint8_t array that is at + * least xBufferSizeBytes + 1 big. This is the array to which streams are + * copied when they are written to the stream buffer. + * + * @param pxStaticStreamBuffer Must point to a variable of type + * StaticStreamBuffer_t, which will be used to hold the stream buffer's data + * structure. + * + * @return If the stream buffer is created successfully then a handle to the + * created stream buffer is returned. If either pucStreamBufferStorageArea or + * pxStaticstreamBuffer are NULL then NULL is returned. + * + * Example use: +
+
+// Used to dimension the array used to hold the streams.  The available space
+// will actually be one less than this, so 999.
+#define STORAGE_SIZE_BYTES 1000
+
+// Defines the memory that will actually hold the streams within the stream
+// buffer.
+static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
+
+// The variable used to hold the stream buffer structure.
+StaticStreamBuffer_t xStreamBufferStruct;
+
+void MyFunction( void )
+{
+StreamBufferHandle_t xStreamBuffer;
+const size_t xTriggerLevel = 1;
+
+    xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ),
+                                               xTriggerLevel,
+                                               ucBufferStorage,
+                                               &xStreamBufferStruct );
+
+    // As neither the pucStreamBufferStorageArea or pxStaticStreamBuffer
+    // parameters were NULL, xStreamBuffer will not be NULL, and can be used to
+    // reference the created stream buffer in other stream buffer API calls.
+
+    // Other code that uses the stream buffer can go here.
+}
+
+
+ * \defgroup xStreamBufferCreateStatic xStreamBufferCreateStatic + * \ingroup StreamBufferManagement + */ +#define xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer ) + +/** + * stream_buffer.h + * +
+size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
+                          const void *pvTxData,
+                          size_t xDataLengthBytes,
+                          TickType_t xTicksToWait );
+
+ * + * Sends bytes to a stream buffer. The bytes are copied into the stream buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferReceive()) inside a critical section and set the receive + * block time to 0. + * + * Use xStreamBufferSend() to write to a stream buffer from a task. Use + * xStreamBufferSendFromISR() to write to a stream buffer from an interrupt + * service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer to which a stream is + * being sent. + * + * @param pvTxData A pointer to the buffer that holds the bytes to be copied + * into the stream buffer. + * + * @param xDataLengthBytes The maximum number of bytes to copy from pvTxData + * into the stream buffer. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for enough space to become available in the stream + * buffer, should the stream buffer contain too little space to hold the + * another xDataLengthBytes bytes. The block time is specified in tick periods, + * so the absolute time it represents is dependent on the tick frequency. The + * macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds + * into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will + * cause the task to wait indefinitely (without timing out), provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. If a task times out + * before it can write all xDataLengthBytes into the buffer it will still write + * as many bytes as possible. A task does not use any CPU time when it is in + * the blocked state. + * + * @return The number of bytes written to the stream buffer. If a task times + * out before it can write all xDataLengthBytes into the buffer it will still + * write as many bytes as possible. + * + * Example use: +
+void vAFunction( StreamBufferHandle_t xStreamBuffer )
+{
+size_t xBytesSent;
+uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
+char *pcStringToSend = "String to send";
+const TickType_t x100ms = pdMS_TO_TICKS( 100 );
+
+    // Send an array to the stream buffer, blocking for a maximum of 100ms to
+    // wait for enough space to be available in the stream buffer.
+    xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
+
+    if( xBytesSent != sizeof( ucArrayToSend ) )
+    {
+        // The call to xStreamBufferSend() times out before there was enough
+        // space in the buffer for the data to be written, but it did
+        // successfully write xBytesSent bytes.
+    }
+
+    // Send the string to the stream buffer.  Return immediately if there is not
+    // enough space in the buffer.
+    xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // The entire string could not be added to the stream buffer because
+        // there was not enough free space in the buffer, but xBytesSent bytes
+        // were sent.  Could try again to send the remaining bytes.
+    }
+}
+
+ * \defgroup xStreamBufferSend xStreamBufferSend + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
+                                 const void *pvTxData,
+                                 size_t xDataLengthBytes,
+                                 BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * Interrupt safe version of the API function that sends a stream of bytes to + * the stream buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferReceive()) inside a critical section and set the receive + * block time to 0. + * + * Use xStreamBufferSend() to write to a stream buffer from a task. Use + * xStreamBufferSendFromISR() to write to a stream buffer from an interrupt + * service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer to which a stream is + * being sent. + * + * @param pvTxData A pointer to the data that is to be copied into the stream + * buffer. + * + * @param xDataLengthBytes The maximum number of bytes to copy from pvTxData + * into the stream buffer. + * + * @param pxHigherPriorityTaskWoken It is possible that a stream buffer will + * have a task blocked on it waiting for data. Calling + * xStreamBufferSendFromISR() can make data available, and so cause a task that + * was waiting for data to leave the Blocked state. If calling + * xStreamBufferSendFromISR() causes a task to leave the Blocked state, and the + * unblocked task has a priority higher than the currently executing task (the + * task that was interrupted), then, internally, xStreamBufferSendFromISR() + * will set *pxHigherPriorityTaskWoken to pdTRUE. If + * xStreamBufferSendFromISR() sets this value to pdTRUE, then normally a + * context switch should be performed before the interrupt is exited. This will + * ensure that the interrupt returns directly to the highest priority Ready + * state task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it + * is passed into the function. See the example code below for an example. + * + * @return The number of bytes actually written to the stream buffer, which will + * be less than xDataLengthBytes if the stream buffer didn't have enough free + * space for all the bytes to be written. + * + * Example use: +
+// A stream buffer that has already been created.
+StreamBufferHandle_t xStreamBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+size_t xBytesSent;
+char *pcStringToSend = "String to send";
+BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
+
+    // Attempt to send the string to the stream buffer.
+    xBytesSent = xStreamBufferSendFromISR( xStreamBuffer,
+                                           ( void * ) pcStringToSend,
+                                           strlen( pcStringToSend ),
+                                           &xHigherPriorityTaskWoken );
+
+    if( xBytesSent != strlen( pcStringToSend ) )
+    {
+        // There was not enough free space in the stream buffer for the entire
+        // string to be written, ut xBytesSent bytes were written.
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xStreamBufferSendFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xStreamBufferSendFromISR xStreamBufferSendFromISR + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
+                             void *pvRxData,
+                             size_t xBufferLengthBytes,
+                             TickType_t xTicksToWait );
+
+ * + * Receives bytes from a stream buffer. + * + * ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer + * implementation (so also the message buffer implementation, as message buffers + * are built on top of stream buffers) assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). It is safe for the + * writer and reader to be different tasks or interrupts, but, unlike other + * FreeRTOS objects, it is not safe to have multiple different writers or + * multiple different readers. If there are to be multiple different writers + * then the application writer must place each call to a writing API function + * (such as xStreamBufferSend()) inside a critical section and set the send + * block time to 0. Likewise, if there are to be multiple different readers + * then the application writer must place each call to a reading API function + * (such as xStreamBufferReceive()) inside a critical section and set the receive + * block time to 0. + * + * Use xStreamBufferReceive() to read from a stream buffer from a task. Use + * xStreamBufferReceiveFromISR() to read from a stream buffer from an + * interrupt service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer from which bytes are to + * be received. + * + * @param pvRxData A pointer to the buffer into which the received bytes will be + * copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the + * pvRxData parameter. This sets the maximum number of bytes to receive in one + * call. xStreamBufferReceive will return as many bytes as possible up to a + * maximum set by xBufferLengthBytes. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for data to become available if the stream buffer is + * empty. xStreamBufferReceive() will return immediately if xTicksToWait is + * zero. The block time is specified in tick periods, so the absolute time it + * represents is dependent on the tick frequency. The macro pdMS_TO_TICKS() can + * be used to convert a time specified in milliseconds into a time specified in + * ticks. Setting xTicksToWait to portMAX_DELAY will cause the task to wait + * indefinitely (without timing out), provided INCLUDE_vTaskSuspend is set to 1 + * in FreeRTOSConfig.h. A task does not use any CPU time when it is in the + * Blocked state. + * + * @return The number of bytes actually read from the stream buffer, which will + * be less than xBufferLengthBytes if the call to xStreamBufferReceive() timed + * out before xBufferLengthBytes were available. + * + * Example use: +
+void vAFunction( StreamBuffer_t xStreamBuffer )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
+
+    // Receive up to another sizeof( ucRxData ) bytes from the stream buffer.
+    // Wait in the Blocked state (so not using any CPU processing time) for a
+    // maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be
+    // available.
+    xReceivedBytes = xStreamBufferReceive( xStreamBuffer,
+                                           ( void * ) ucRxData,
+                                           sizeof( ucRxData ),
+                                           xBlockTime );
+
+    if( xReceivedBytes > 0 )
+    {
+        // A ucRxData contains another xRecievedBytes bytes of data, which can
+        // be processed here....
+    }
+}
+
+ * \defgroup xStreamBufferReceive xStreamBufferReceive + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
+                                    void *pvRxData,
+                                    size_t xBufferLengthBytes,
+                                    BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * An interrupt safe version of the API function that receives bytes from a + * stream buffer. + * + * Use xStreamBufferReceive() to read bytes from a stream buffer from a task. + * Use xStreamBufferReceiveFromISR() to read bytes from a stream buffer from an + * interrupt service routine (ISR). + * + * @param xStreamBuffer The handle of the stream buffer from which a stream + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received bytes are + * copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the + * pvRxData parameter. This sets the maximum number of bytes to receive in one + * call. xStreamBufferReceive will return as many bytes as possible up to a + * maximum set by xBufferLengthBytes. + * + * @param pxHigherPriorityTaskWoken It is possible that a stream buffer will + * have a task blocked on it waiting for space to become available. Calling + * xStreamBufferReceiveFromISR() can make space available, and so cause a task + * that is waiting for space to leave the Blocked state. If calling + * xStreamBufferReceiveFromISR() causes a task to leave the Blocked state, and + * the unblocked task has a priority higher than the currently executing task + * (the task that was interrupted), then, internally, + * xStreamBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE. + * If xStreamBufferReceiveFromISR() sets this value to pdTRUE, then normally a + * context switch should be performed before the interrupt is exited. That will + * ensure the interrupt returns directly to the highest priority Ready state + * task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is + * passed into the function. See the code example below for an example. + * + * @return The number of bytes read from the stream buffer, if any. + * + * Example use: +
+// A stream buffer that has already been created.
+StreamBuffer_t xStreamBuffer;
+
+void vAnInterruptServiceRoutine( void )
+{
+uint8_t ucRxData[ 20 ];
+size_t xReceivedBytes;
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
+
+    // Receive the next stream from the stream buffer.
+    xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer,
+                                                  ( void * ) ucRxData,
+                                                  sizeof( ucRxData ),
+                                                  &xHigherPriorityTaskWoken );
+
+    if( xReceivedBytes > 0 )
+    {
+        // ucRxData contains xReceivedBytes read from the stream buffer.
+        // Process the stream here....
+    }
+
+    // If xHigherPriorityTaskWoken was set to pdTRUE inside
+    // xStreamBufferReceiveFromISR() then a task that has a priority above the
+    // priority of the currently executing task was unblocked and a context
+    // switch should be performed to ensure the ISR returns to the unblocked
+    // task.  In most FreeRTOS ports this is done by simply passing
+    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+    // variables value, and perform the context switch if necessary.  Check the
+    // documentation for the port in use for port specific instructions.
+    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+}
+
+ * \defgroup xStreamBufferReceiveFromISR xStreamBufferReceiveFromISR + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Deletes a stream buffer that was previously created using a call to + * xStreamBufferCreate() or xStreamBufferCreateStatic(). If the stream + * buffer was created using dynamic memory (that is, by xStreamBufferCreate()), + * then the allocated memory is freed. + * + * A stream buffer handle must not be used after the stream buffer has been + * deleted. + * + * @param xStreamBuffer The handle of the stream buffer to be deleted. + * + * \defgroup vStreamBufferDelete vStreamBufferDelete + * \ingroup StreamBufferManagement + */ +void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see if it is full. A stream buffer is full if it + * does not have any free space, and therefore cannot accept any more data. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return If the stream buffer is full then pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferIsFull xStreamBufferIsFull + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see if it is empty. A stream buffer is empty if + * it does not contain any data. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return If the stream buffer is empty then pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferIsEmpty xStreamBufferIsEmpty + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Resets a stream buffer to its initial, empty, state. Any data that was in + * the stream buffer is discarded. A stream buffer can only be reset if there + * are no tasks blocked waiting to either send to or receive from the stream + * buffer. + * + * @param xStreamBuffer The handle of the stream buffer being reset. + * + * @return If the stream buffer is reset then pdPASS is returned. If there was + * a task blocked waiting to send to or read from the stream buffer then the + * stream buffer is not reset and pdFAIL is returned. + * + * \defgroup xStreamBufferReset xStreamBufferReset + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see how much free space it contains, which is + * equal to the amount of data that can be sent to the stream buffer before it + * is full. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return The number of bytes that can be written to the stream buffer before + * the stream buffer would be full. + * + * \defgroup xStreamBufferSpacesAvailable xStreamBufferSpacesAvailable + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer );
+
+ * + * Queries a stream buffer to see how much data it contains, which is equal to + * the number of bytes that can be read from the stream buffer before the stream + * buffer would be empty. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return The number of bytes that can be read from the stream buffer before + * the stream buffer would be empty. + * + * \defgroup xStreamBufferBytesAvailable xStreamBufferBytesAvailable + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel );
+
+ * + * A stream buffer's trigger level is the number of bytes that must be in the + * stream buffer before a task that is blocked on the stream buffer to + * wait for data is moved out of the blocked state. For example, if a task is + * blocked on a read of an empty stream buffer that has a trigger level of 1 + * then the task will be unblocked when a single byte is written to the buffer + * or the task's block time expires. As another example, if a task is blocked + * on a read of an empty stream buffer that has a trigger level of 10 then the + * task will not be unblocked until the stream buffer contains at least 10 bytes + * or the task's block time expires. If a reading task's block time expires + * before the trigger level is reached then the task will still receive however + * many bytes are actually available. Setting a trigger level of 0 will result + * in a trigger level of 1 being used. It is not valid to specify a trigger + * level that is greater than the buffer size. + * + * A trigger level is set when the stream buffer is created, and can be modified + * using xStreamBufferSetTriggerLevel(). + * + * @param xStreamBuffer The handle of the stream buffer being updated. + * + * @param xTriggerLevel The new trigger level for the stream buffer. + * + * @return If xTriggerLevel was less than or equal to the stream buffer's length + * then the trigger level will be updated and pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferSetTriggerLevel xStreamBufferSetTriggerLevel + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is sent to a message buffer or stream buffer. If there was a task that + * was blocked on the message or stream buffer waiting for data to arrive then + * the sbSEND_COMPLETED() macro sends a notification to the task to remove it + * from the Blocked state. xStreamBufferSendCompletedFromISR() does the same + * thing. It is provided to enable application writers to implement their own + * version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer to which data was + * written. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xStreamBufferSendCompletedFromISR(). If calling + * xStreamBufferSendCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xStreamBufferSendCompletedFromISR xStreamBufferSendCompletedFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * +
+BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+
+ * + * For advanced users only. + * + * The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when + * data is read out of a message buffer or stream buffer. If there was a task + * that was blocked on the message or stream buffer waiting for data to arrive + * then the sbRECEIVE_COMPLETED() macro sends a notification to the task to + * remove it from the Blocked state. xStreamBufferReceiveCompletedFromISR() + * does the same thing. It is provided to enable application writers to + * implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT + * ANY OTHER TIME. + * + * See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for + * additional information. + * + * @param xStreamBuffer The handle of the stream buffer from which data was + * read. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xStreamBufferReceiveCompletedFromISR(). If calling + * xStreamBufferReceiveCompletedFromISR() removes a task from the Blocked state, + * and the task has a priority above the priority of the currently running task, + * then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a + * context switch should be performed before exiting the ISR. + * + * @return If a task was removed from the Blocked state then pdTRUE is returned. + * Otherwise pdFALSE is returned. + * + * \defgroup xStreamBufferReceiveCompletedFromISR xStreamBufferReceiveCompletedFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/* Functions below here are not part of the public API. */ +StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer ) PRIVILEGED_FUNCTION; + +StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) PRIVILEGED_FUNCTION; + +size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +#if( configUSE_TRACE_FACILITY == 1 ) + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxStreamBufferNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; +#endif + +#if defined( __cplusplus ) +} +#endif + +#endif /* !defined( STREAM_BUFFER_H ) */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h index cfa065eb..b0cc60b6 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef INC_TASK_H @@ -85,10 +43,18 @@ extern "C" { * MACROS AND DEFINITIONS *----------------------------------------------------------*/ -#define tskKERNEL_VERSION_NUMBER "V9.0.0" -#define tskKERNEL_VERSION_MAJOR 9 -#define tskKERNEL_VERSION_MINOR 0 -#define tskKERNEL_VERSION_BUILD 0 +#define tskKERNEL_VERSION_NUMBER "V10.3.1" +#define tskKERNEL_VERSION_MAJOR 10 +#define tskKERNEL_VERSION_MINOR 3 +#define tskKERNEL_VERSION_BUILD 1 + +/* MPU region parameters passed in ulParameters + * of MemoryRegion_t struct. */ +#define tskMPU_REGION_READ_ONLY ( 1UL << 0UL ) +#define tskMPU_REGION_READ_WRITE ( 1UL << 1UL ) +#define tskMPU_REGION_EXECUTE_NEVER ( 1UL << 2UL ) +#define tskMPU_REGION_NORMAL_MEMORY ( 1UL << 3UL ) +#define tskMPU_REGION_DEVICE_MEMORY ( 1UL << 4UL ) /** * task. h @@ -100,7 +66,8 @@ extern "C" { * \defgroup TaskHandle_t TaskHandle_t * \ingroup Tasks */ -typedef void * TaskHandle_t; +struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock* TaskHandle_t; /* * Defines the prototype to which the application task hook function must @@ -116,7 +83,7 @@ typedef enum eBlocked, /* The task being queried is in the Blocked state. */ eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ - eInvalid /* Used as an 'invalid state' value. */ + eInvalid /* Used as an 'invalid state' value. */ } eTaskState; /* Actions that can be performed when vTaskNotify() is called. */ @@ -155,11 +122,14 @@ typedef struct xTASK_PARAMETERS { TaskFunction_t pvTaskCode; const char * const pcName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - uint16_t usStackDepth; + configSTACK_DEPTH_TYPE usStackDepth; void *pvParameters; UBaseType_t uxPriority; StackType_t *puxStackBuffer; MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; + #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + StaticTask_t * const pxTaskBuffer; + #endif } TaskParameters_t; /* Used with the uxTaskGetSystemState() function to return the state of each task @@ -174,7 +144,7 @@ typedef struct xTASK_STATUS UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ - uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ } TaskStatus_t; /* Possible return values for eTaskConfirmSleepModeStatus(). */ @@ -269,7 +239,7 @@ is used in assert() statements. */ BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, const char * const pcName, - uint16_t usStackDepth, + configSTACK_DEPTH_TYPE usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask @@ -344,25 +314,25 @@ is used in assert() statements. */ // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time // the new task attempts to access it. xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle ); - configASSERT( xHandle ); + configASSERT( xHandle ); // Use the handle to delete the task. - if( xHandle != NULL ) - { - vTaskDelete( xHandle ); - } + if( xHandle != NULL ) + { + vTaskDelete( xHandle ); + } } * \defgroup xTaskCreate xTaskCreate * \ingroup Tasks */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint16_t usStackDepth, + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; #endif /** @@ -414,9 +384,9 @@ is used in assert() statements. */ * memory to be allocated dynamically. * * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will - * be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer - * are NULL then the task will not be created and - * errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned. + * be created and a handle to the created task is returned. If either + * pxStackBuffer or pxTaskBuffer are NULL then the task will not be created and + * NULL is returned. * * Example usage:
@@ -473,13 +443,13 @@ is used in assert() statements. */
  * \ingroup Tasks
  */
 #if( configSUPPORT_STATIC_ALLOCATION == 1 )
-	PRIVILEGED_FUNCTION TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
-									const char * const pcName,
+	TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
+									const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
 									const uint32_t ulStackDepth,
 									void * const pvParameters,
 									UBaseType_t uxPriority,
 									StackType_t * const puxStackBuffer,
-									StaticTask_t * const pxTaskBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
+									StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION;
 #endif /* configSUPPORT_STATIC_ALLOCATION */
 
 /**
@@ -487,6 +457,8 @@ is used in assert() statements. */
  *
  BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
* + * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. + * * xTaskCreateRestricted() should only be used in systems that include an MPU * implementation. * @@ -494,6 +466,9 @@ is used in assert() statements. */ * The function parameters define the memory regions and associated access * permissions allocated to the task. * + * See xTaskCreateRestrictedStatic() for a version that does not use any + * dynamic memory allocation. + * * @param pxTaskDefinition Pointer to a structure that contains a member * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API * documentation) plus an optional stack buffer and the memory region @@ -523,9 +498,9 @@ static const TaskParameters_t xCheckTaskParameters = // for full information. { // Base address Length Parameters - { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, - { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, - { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } } }; @@ -550,7 +525,95 @@ TaskHandle_t xHandle; * \ingroup Tasks */ #if( portUSING_MPU_WRAPPERS == 1 ) - PRIVILEGED_FUNCTION BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ); + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * + * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. + * + * xTaskCreateRestrictedStatic() should only be used in systems that include an + * MPU implementation. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreateRestricted() then the stack is provided by the application writer, + * and the memory used to hold the task's data structure is automatically + * dynamically allocated inside the xTaskCreateRestricted() function. If a task + * is created using xTaskCreateRestrictedStatic() then the application writer + * must provide the memory used to hold the task's data structures too. + * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be + * created without using any dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure + * contains an additional member, which is used to point to a variable of type + * StaticTask_t - which is then used to hold the task's data structure. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+// Create an TaskParameters_t structure that defines the task to be created.
+// The StaticTask_t variable is only included in the structure when
+// configSUPPORT_STATIC_ALLOCATION is set to 1.  The PRIVILEGED_DATA macro can
+// be used to force the variable into the RTOS kernel's privileged data area.
+static PRIVILEGED_DATA StaticTask_t xTaskBuffer;
+static const TaskParameters_t xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{
+		// Base address					Length	Parameters
+		{ cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+		{ cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+		{ cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+
+	&xTaskBuffer; // Holds the task's data structure.
+};
+
+int main( void )
+{
+TaskHandle_t xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// and/or timer task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic + * \ingroup Tasks + */ +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; #endif /** @@ -599,7 +662,7 @@ void vATask( void *pvParameters ) * \defgroup xTaskCreateRestricted xTaskCreateRestricted * \ingroup Tasks */ -PRIVILEGED_FUNCTION void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ); +void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; /** * task. h @@ -640,7 +703,7 @@ PRIVILEGED_FUNCTION void vTaskAllocateMPURegions( TaskHandle_t xTask, const Memo * \defgroup vTaskDelete vTaskDelete * \ingroup Tasks */ -PRIVILEGED_FUNCTION void vTaskDelete( TaskHandle_t xTaskToDelete ); +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * TASK CONTROL API @@ -692,7 +755,7 @@ PRIVILEGED_FUNCTION void vTaskDelete( TaskHandle_t xTaskToDelete ); * \defgroup vTaskDelay vTaskDelay * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskDelay( const TickType_t xTicksToDelay ); +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; /** * task. h @@ -751,7 +814,7 @@ PRIVILEGED_FUNCTION void vTaskDelay( const TickType_t xTicksToDelay ); * \defgroup vTaskDelayUntil vTaskDelayUntil * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ); +void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; /** * task. h @@ -768,6 +831,11 @@ PRIVILEGED_FUNCTION void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, * task will leave the Blocked state, and return from whichever function call * placed the task into the Blocked state. * + * There is no 'FromISR' version of this function as an interrupt would need to + * know which object a task was blocked on in order to know which actions to + * take. For example, if the task was blocked on a queue the interrupt handler + * would then need to know if the queue was locked. + * * @param xTask The handle of the task to remove from the Blocked state. * * @return If the task referenced by xTask was not in the Blocked state then @@ -776,11 +844,11 @@ PRIVILEGED_FUNCTION void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, * \defgroup xTaskAbortDelay xTaskAbortDelay * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION BaseType_t xTaskAbortDelay( TaskHandle_t xTask ); +BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h - *
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
+ *
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
* * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. * See the configuration section for more information. @@ -823,15 +891,15 @@ PRIVILEGED_FUNCTION BaseType_t xTaskAbortDelay( TaskHandle_t xTask ); * \defgroup uxTaskPriorityGet uxTaskPriorityGet * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ); +UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h - *
UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask );
+ *
UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask );
* * A version of uxTaskPriorityGet() that can be used from an ISR. */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ); +UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h @@ -849,7 +917,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ); * state of the task might change between the function being called, and the * functions return value being tested by the calling task. */ -PRIVILEGED_FUNCTION eTaskState eTaskGetState( TaskHandle_t xTask ); +eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /** * task. h @@ -905,7 +973,7 @@ PRIVILEGED_FUNCTION eTaskState eTaskGetState( TaskHandle_t xTask ); * \defgroup vTaskGetInfo vTaskGetInfo * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ); +void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) PRIVILEGED_FUNCTION; /** * task. h @@ -947,7 +1015,7 @@ PRIVILEGED_FUNCTION void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskS * \defgroup vTaskPrioritySet vTaskPrioritySet * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); +void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; /** * task. h @@ -998,7 +1066,7 @@ PRIVILEGED_FUNCTION void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNew * \defgroup vTaskSuspend vTaskSuspend * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskSuspend( TaskHandle_t xTaskToSuspend ); +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; /** * task. h @@ -1047,7 +1115,7 @@ PRIVILEGED_FUNCTION void vTaskSuspend( TaskHandle_t xTaskToSuspend ); * \defgroup vTaskResume vTaskResume * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION void vTaskResume( TaskHandle_t xTaskToResume ); +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /** * task. h @@ -1076,7 +1144,7 @@ PRIVILEGED_FUNCTION void vTaskResume( TaskHandle_t xTaskToResume ); * \defgroup vTaskResumeFromISR vTaskResumeFromISR * \ingroup TaskCtrl */ -PRIVILEGED_FUNCTION BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ); +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * SCHEDULER CONTROL @@ -1109,7 +1177,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ); * \defgroup vTaskStartScheduler vTaskStartScheduler * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION void vTaskStartScheduler( void ); +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1165,7 +1233,7 @@ PRIVILEGED_FUNCTION void vTaskStartScheduler( void ); * \defgroup vTaskEndScheduler vTaskEndScheduler * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION void vTaskEndScheduler( void ); +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1216,7 +1284,7 @@ PRIVILEGED_FUNCTION void vTaskEndScheduler( void ); * \defgroup vTaskSuspendAll vTaskSuspendAll * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION void vTaskSuspendAll( void ); +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1270,7 +1338,7 @@ PRIVILEGED_FUNCTION void vTaskSuspendAll( void ); * \defgroup xTaskResumeAll xTaskResumeAll * \ingroup SchedulerControl */ -PRIVILEGED_FUNCTION BaseType_t xTaskResumeAll( void ); +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; /*----------------------------------------------------------- * TASK UTILITIES @@ -1285,7 +1353,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskResumeAll( void ); * \defgroup xTaskGetTickCount xTaskGetTickCount * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION TickType_t xTaskGetTickCount( void ); +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1301,7 +1369,7 @@ PRIVILEGED_FUNCTION TickType_t xTaskGetTickCount( void ); * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION TickType_t xTaskGetTickCountFromISR( void ); +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1315,7 +1383,7 @@ PRIVILEGED_FUNCTION TickType_t xTaskGetTickCountFromISR( void ); * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetNumberOfTasks( void ); +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1328,7 +1396,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxTaskGetNumberOfTasks( void ); * \defgroup pcTaskGetName pcTaskGetName * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION char *pcTaskGetName( TaskHandle_t xTaskToQuery ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +char *pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task. h @@ -1344,7 +1412,7 @@ PRIVILEGED_FUNCTION char *pcTaskGetName( TaskHandle_t xTaskToQuery ); /*lint !e9 * \defgroup pcTaskGetHandle pcTaskGetHandle * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task.h @@ -1358,6 +1426,12 @@ PRIVILEGED_FUNCTION TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); /* * a value of 1 means 4 bytes) since the task started. The smaller the returned * number the closer the task has come to overflowing its stack. * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * * @param xTask Handle of the task associated with the stack to be checked. * Set xTask to NULL to check the stack of the calling task. * @@ -1365,7 +1439,34 @@ PRIVILEGED_FUNCTION TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); /* * actual spaces on the stack rather than bytes) since the task referenced by * xTask was created. */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /* When using trace macros it is sometimes necessary to include task.h before FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, @@ -1383,15 +1484,26 @@ constant. */ * Passing xTask as NULL has the effect of setting the calling tasks hook * function. */ - PRIVILEGED_FUNCTION void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ); + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; /** * task.h *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
* - * Returns the pxHookFunction value assigned to the task xTask. + * Returns the pxHookFunction value assigned to the task xTask. Do not + * call from an interrupt service routine - call + * xTaskGetApplicationTaskTagFromISR() instead. */ - PRIVILEGED_FUNCTION TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ); + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + + /** + * task.h + *
void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. Can + * be called from an interrupt service routine. + */ + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ #endif /* ifdef configUSE_APPLICATION_TASK_TAG */ @@ -1402,8 +1514,8 @@ constant. */ kernel does not use the pointers itself, so the application writer can use the pointers for any purpose they wish. The following two functions are used to set and query a pointer respectively. */ - PRIVILEGED_FUNCTION void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ); - PRIVILEGED_FUNCTION void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ); + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) PRIVILEGED_FUNCTION; + void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) PRIVILEGED_FUNCTION; #endif @@ -1418,7 +1530,7 @@ constant. */ * wants. The return value is the value returned by the task hook function * registered by the user. */ -PRIVILEGED_FUNCTION BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ); +BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; /** * xTaskGetIdleTaskHandle() is only available if @@ -1427,7 +1539,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, * Simply returns the handle of the idle task. It is not valid to call * xTaskGetIdleTaskHandle() before the scheduler has been started. */ -PRIVILEGED_FUNCTION TaskHandle_t xTaskGetIdleTaskHandle( void ); +TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; /** * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for @@ -1526,7 +1638,7 @@ PRIVILEGED_FUNCTION TaskHandle_t xTaskGetIdleTaskHandle( void ); }
*/ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); +UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) PRIVILEGED_FUNCTION; /** * task. h @@ -1573,7 +1685,7 @@ PRIVILEGED_FUNCTION UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTas * \defgroup vTaskList vTaskList * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION void vTaskList( char * pcWriteBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task. h @@ -1627,7 +1739,37 @@ PRIVILEGED_FUNCTION void vTaskList( char * pcWriteBuffer ); /*lint !e971 Unquali * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats * \ingroup TaskUtils */ -PRIVILEGED_FUNCTION void vTaskGetRunTimeStats( char *pcWriteBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** +* task. h +*
uint32_t ulTaskGetIdleRunTimeCounter( void );
+* +* configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS +* must both be defined as 1 for this function to be available. The application +* must also then provide definitions for +* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() +* to configure a peripheral timer/counter and return the timers current count +* value respectively. The counter should be at least 10 times the frequency of +* the tick count. +* +* Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total +* accumulated execution time being stored for each task. The resolution +* of the accumulated time value depends on the frequency of the timer +* configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. +* While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total +* execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() +* returns the total execution time of just the idle task. +* +* @return The total run time of the idle task. This is the amount of time the +* idle task has actually been executing. The unit of time is dependent on the +* frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and +* portGET_RUN_TIME_COUNTER_VALUE() macros. +* +* \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter +* \ingroup TaskUtils +*/ +uint32_t ulTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -1708,7 +1850,7 @@ PRIVILEGED_FUNCTION void vTaskGetRunTimeStats( char *pcWriteBuffer ); /*lint !e9 * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ); +BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) PRIVILEGED_FUNCTION; #define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL ) #define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) @@ -1799,7 +1941,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, u * \defgroup xTaskNotify xTaskNotify * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ); +BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; #define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) #define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) @@ -1876,7 +2018,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNo * \defgroup xTaskNotifyWait xTaskNotifyWait * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); +BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * task. h @@ -1977,7 +2119,7 @@ PRIVILEGED_FUNCTION BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, u * \defgroup xTaskNotifyWait xTaskNotifyWait * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ); +void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * task. h @@ -2046,7 +2188,7 @@ PRIVILEGED_FUNCTION void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, Bas * \defgroup ulTaskNotifyTake ulTaskNotifyTake * \ingroup TaskNotifications */ -PRIVILEGED_FUNCTION uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); +uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * task. h @@ -2064,6 +2206,121 @@ PRIVILEGED_FUNCTION uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, Tic */ BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); +/** +* task. h +*
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
+* +* Clears the bits specified by the ulBitsToClear bit mask in the notification +* value of the task referenced by xTask. +* +* Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit architectures) to clear +* the notification value to 0. Set ulBitsToClear to 0 to query the task's +* notification value without clearing any bits. +* +* @return The value of the target task's notification value before the bits +* specified by ulBitsToClear were cleared. +* \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear +* \ingroup TaskNotifications +*/ +uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut )
+ * + * Capture the current time for future use with xTaskCheckForTimeOut(). + * + * @param pxTimeOut Pointer to a timeout object into which the current time + * is to be captured. The captured time includes the tick count and the number + * of times the tick count has overflowed since the system first booted. + * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState + * \ingroup TaskCtrl + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );
+ * + * Determines if pxTicksToWait ticks has passed since a time was captured + * using a call to vTaskSetTimeOutState(). The captured time includes the tick + * count and the number of times the tick count has overflowed. + * + * @param pxTimeOut The time status as captured previously using + * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated + * to reflect the current time status. + * @param pxTicksToWait The number of ticks to check for timeout i.e. if + * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by + * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. + * If the timeout has not occurred, pxTIcksToWait is updated to reflect the + * number of remaining ticks. + * + * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is + * returned and pxTicksToWait is updated to reflect the number of remaining + * ticks. + * + * @see https://www.freertos.org/xTaskCheckForTimeOut.html + * + * Example Usage: + *
+	// Driver library function used to receive uxWantedBytes from an Rx buffer
+	// that is filled by a UART interrupt. If there are not enough bytes in the
+	// Rx buffer then the task enters the Blocked state until it is notified that
+	// more data has been placed into the buffer. If there is still not enough
+	// data then the task re-enters the Blocked state, and xTaskCheckForTimeOut()
+	// is used to re-calculate the Block time to ensure the total amount of time
+	// spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This
+	// continues until either the buffer contains at least uxWantedBytes bytes,
+	// or the total amount of time spent in the Blocked state reaches
+	// MAX_TIME_TO_WAIT – at which point the task reads however many bytes are
+	// available up to a maximum of uxWantedBytes.
+
+	size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )
+	{
+	size_t uxReceived = 0;
+	TickType_t xTicksToWait = MAX_TIME_TO_WAIT;
+	TimeOut_t xTimeOut;
+
+		// Initialize xTimeOut.  This records the time at which this function
+		// was entered.
+		vTaskSetTimeOutState( &xTimeOut );
+
+		// Loop until the buffer contains the wanted number of bytes, or a
+		// timeout occurs.
+		while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
+		{
+			// The buffer didn't contain enough data so this task is going to
+			// enter the Blocked state. Adjusting xTicksToWait to account for
+			// any time that has been spent in the Blocked state within this
+			// function so far to ensure the total amount of time spent in the
+			// Blocked state does not exceed MAX_TIME_TO_WAIT.
+			if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
+			{
+				//Timed out before the wanted number of bytes were available,
+				// exit the loop.
+				break;
+			}
+
+			// Wait for a maximum of xTicksToWait ticks to be notified that the
+			// receive interrupt has placed more data into the buffer.
+			ulTaskNotifyTake( pdTRUE, xTicksToWait );
+		}
+
+		// Attempt to read uxWantedBytes from the receive buffer into pucBuffer.
+		// The actual number of bytes read (which might be less than
+		// uxWantedBytes) is returned.
+		uxReceived = UART_read_from_receive_buffer( pxUARTInstance,
+													pucBuffer,
+													uxWantedBytes );
+
+		return uxReceived;
+	}
+ 
+ * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut + * \ingroup TaskCtrl + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; + /*----------------------------------------------------------- * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES *----------------------------------------------------------*/ @@ -2083,7 +2340,7 @@ BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); * + Time slicing is in use and there is a task of equal priority to the * currently running task. */ -PRIVILEGED_FUNCTION BaseType_t xTaskIncrementTick( void ); +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN @@ -2116,8 +2373,8 @@ PRIVILEGED_FUNCTION BaseType_t xTaskIncrementTick( void ); * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time * period. */ -PRIVILEGED_FUNCTION void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ); -PRIVILEGED_FUNCTION void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ); +void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN @@ -2130,7 +2387,7 @@ PRIVILEGED_FUNCTION void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, c * indefinitely, whereas vTaskPlaceOnEventList() does. * */ -PRIVILEGED_FUNCTION void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ); +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN @@ -2141,14 +2398,14 @@ PRIVILEGED_FUNCTION void vTaskPlaceOnEventListRestricted( List_t * const pxEvent * Removes a task from both the specified event list and the list of blocked * tasks, and places it on a ready queue. * - * xTaskRemoveFromEventList()/xTaskRemoveFromUnorderedEventList() will be called + * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called * if either an event occurs to unblock a task, or the block timeout period * expires. * * xTaskRemoveFromEventList() is used when the event list is in task priority * order. It removes the list item from the head of the event list as that will * have the highest priority owning task of all the tasks on the event list. - * xTaskRemoveFromUnorderedEventList() is used when the event list is not + * vTaskRemoveFromUnorderedEventList() is used when the event list is not * ordered and the event list items hold something other than the owning tasks * priority. In this case the event list item value is updated to the value * passed in the xItemValue parameter. @@ -2156,8 +2413,8 @@ PRIVILEGED_FUNCTION void vTaskPlaceOnEventListRestricted( List_t * const pxEvent * @return pdTRUE if the task being removed has a higher priority than the task * making the call, otherwise pdFALSE. */ -PRIVILEGED_FUNCTION BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ); -PRIVILEGED_FUNCTION BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ); +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) PRIVILEGED_FUNCTION; /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY @@ -2167,64 +2424,63 @@ PRIVILEGED_FUNCTION BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * p * Sets the pointer to the current TCB to the TCB of the highest priority task * that is ready to run. */ -PRIVILEGED_FUNCTION void vTaskSwitchContext( void ); +portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; /* * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY * THE EVENT BITS MODULE. */ -PRIVILEGED_FUNCTION TickType_t uxTaskResetEventItemValue( void ); +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; /* * Return the handle of the calling task. */ -PRIVILEGED_FUNCTION TaskHandle_t xTaskGetCurrentTaskHandle( void ); - -/* - * Capture the current time status for future reference. - */ -PRIVILEGED_FUNCTION void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ); - -/* - * Compare the time status now with that previously captured to see if the - * timeout has expired. - */ -PRIVILEGED_FUNCTION BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ); +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; /* * Shortcut used by the queue implementation to prevent unnecessary call to * taskYIELD(); */ -PRIVILEGED_FUNCTION void vTaskMissedYield( void ); +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; /* * Returns the scheduler state as taskSCHEDULER_RUNNING, * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. */ -PRIVILEGED_FUNCTION BaseType_t xTaskGetSchedulerState( void ); +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; /* * Raises the priority of the mutex holder to that of the calling task should * the mutex holder have a priority less than the calling task. */ -PRIVILEGED_FUNCTION void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ); +BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; /* * Set the priority of a task back to its proper priority in the case that it * inherited a higher priority while it was holding a semaphore. */ -PRIVILEGED_FUNCTION BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ); +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * If a higher priority task attempting to obtain a mutex caused a lower + * priority task to inherit the higher priority task's priority - but the higher + * priority task then timed out without obtaining the mutex, then the lower + * priority task will disinherit the priority again - but only down as far as + * the highest priority task that is still waiting for the mutex (if there were + * more than one task waiting for the mutex). + */ +void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; /* * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. */ -PRIVILEGED_FUNCTION UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ); +UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; /* * Set the uxTaskNumber of the task referenced by the xTask parameter to * uxHandle. */ -PRIVILEGED_FUNCTION void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ); +void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; /* * Only available when configUSE_TICKLESS_IDLE is set to 1. @@ -2234,10 +2490,23 @@ PRIVILEGED_FUNCTION void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType * to date with the actual execution time by being skipped forward by a time * equal to the idle period. */ -PRIVILEGED_FUNCTION void vTaskStepTick( const TickType_t xTicksToJump ); +void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; + +/* Correct the tick count value after the application code has held +interrupts disabled for an extended period. xTicksToCatchUp is the number +of tick interrupts that have been missed due to interrupts being disabled. +Its value is not computed automatically, so must be computed by the +application writer. + +This function is similar to vTaskStepTick(), however, unlike +vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a +time at which a task should be removed from the blocked state. That means +tasks may have to be removed from the blocked state as the tick count is +moved. */ +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; /* - * Only avilable when configUSE_TICKLESS_IDLE is set to 1. + * Only available when configUSE_TICKLESS_IDLE is set to 1. * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port * specific sleep function to determine if it is ok to proceed with the sleep, * and if it is ok to proceed, if it is ok to sleep indefinitely. @@ -2250,13 +2519,20 @@ PRIVILEGED_FUNCTION void vTaskStepTick( const TickType_t xTicksToJump ); * critical section between the timer being stopped and the sleep mode being * entered to ensure it is ok to proceed into the sleep mode. */ -PRIVILEGED_FUNCTION eSleepModeStatus eTaskConfirmSleepModeStatus( void ); +eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; /* * For internal use only. Increment the mutex held count when a mutex is * taken and return the handle of the task that has taken the mutex. */ -PRIVILEGED_FUNCTION void *pvTaskIncrementMutexHeldCount( void ); +TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as vTaskSetTimeOutState(), but without a critial + * section. + */ +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h index 2f430f37..1b6d7f97 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef TIMERS_H @@ -75,10 +33,10 @@ #error "include FreeRTOS.h must appear in source files before include timers.h" #endif -/*lint -e537 This headers are only multiply included if the application code +/*lint -save -e537 This headers are only multiply included if the application code happens to also be including task.h. */ #include "task.h" -/*lint +e537 */ +/*lint -restore */ #ifdef __cplusplus extern "C" { @@ -115,7 +73,8 @@ or interrupt version of the queue send function should be used. */ * reference the subject timer in calls to other software timer API functions * (for example, xTimerStart(), xTimerReset(), etc.). */ -typedef void * TimerHandle_t; +struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tmrTimerControl * TimerHandle_t; /* * Defines the prototype to which timer callback functions must conform. @@ -266,11 +225,11 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * @endverbatim */ #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION TimerHandle_t xTimerCreate( const char * const pcTimerName, + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; #endif /** @@ -396,12 +355,12 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * @endverbatim */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - PRIVILEGED_FUNCTION TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; #endif /* configSUPPORT_STATIC_ALLOCATION */ /** @@ -424,7 +383,7 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * * See the xTimerCreate() API function example usage scenario. */ -PRIVILEGED_FUNCTION void *pvTimerGetTimerID( const TimerHandle_t xTimer ); +void *pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); @@ -445,7 +404,7 @@ PRIVILEGED_FUNCTION void *pvTimerGetTimerID( const TimerHandle_t xTimer ); * * See the xTimerCreate() API function example usage scenario. */ -PRIVILEGED_FUNCTION void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); +void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; /** * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); @@ -482,7 +441,7 @@ PRIVILEGED_FUNCTION void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) * } * @endverbatim */ -PRIVILEGED_FUNCTION BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); @@ -490,7 +449,7 @@ PRIVILEGED_FUNCTION BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); * Simply returns the handle of the timer service/daemon task. It it not valid * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. */ -PRIVILEGED_FUNCTION TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) PRIVILEGED_FUNCTION; /** * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); @@ -1225,7 +1184,7 @@ PRIVILEGED_FUNCTION TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); * } * @endverbatim */ -PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ); +BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, @@ -1259,7 +1218,7 @@ PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t x * timer daemon task, otherwise pdFALSE is returned. * */ -PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); +BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * const char * const pcTimerGetName( TimerHandle_t xTimer ); @@ -1270,7 +1229,38 @@ PRIVILEGED_FUNCTION BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctio * * @return The name assigned to the timer specified by the xTimer parameter. */ -PRIVILEGED_FUNCTION const char * pcTimerGetName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ); + * + * Updates a timer to be either an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being updated. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the timer's period (see the + * xTimerPeriodInTicks parameter of the xTimerCreate() API function). If + * uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + */ +void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) PRIVILEGED_FUNCTION; + +/** +* UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); +* +* Queries a timer to determine if it is an auto-reload timer, in which case the timer +* automatically resets itself each time it expires, or a one-shot timer, in +* which case the timer will only expire once unless it is manually restarted. +* +* @param xTimer The handle of the timer being queried. +* +* @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise +* pdFALSE is returned. +*/ +UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); @@ -1281,7 +1271,7 @@ PRIVILEGED_FUNCTION const char * pcTimerGetName( TimerHandle_t xTimer ); /*lint * * @return The period of the timer in ticks. */ -PRIVILEGED_FUNCTION TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /** * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); @@ -1296,14 +1286,19 @@ PRIVILEGED_FUNCTION TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); * will next expire is returned. If the timer is not running then the return * value is undefined. */ -PRIVILEGED_FUNCTION TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /* * Functions beyond this part are not part of the public API and are intended * for use by the kernel only. */ -PRIVILEGED_FUNCTION BaseType_t xTimerCreateTimerTask( void ); -PRIVILEGED_FUNCTION BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ); +BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#if( configUSE_TRACE_FACILITY == 1 ) + void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +#endif #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c index 5e207c16..7618ee8b 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #include @@ -81,7 +39,7 @@ void vListInitialise( List_t * const pxList ) /* The list structure contains a list item which is used to mark the end of the list. To initialise the list the list end is inserted as the only list entry. */ - pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ /* The list end value is the highest possible value in the list to ensure it remains at the end of the list. */ @@ -89,8 +47,8 @@ void vListInitialise( List_t * const pxList ) /* The list end next and previous pointers point to itself so we know when the list is empty. */ - pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ - pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ pxList->uxNumberOfItems = ( UBaseType_t ) 0U; @@ -104,7 +62,7 @@ void vListInitialise( List_t * const pxList ) void vListInitialiseItem( ListItem_t * const pxItem ) { /* Make sure the list item is not recorded as being on a list. */ - pxItem->pvContainer = NULL; + pxItem->pxContainer = NULL; /* Write known values into the list item if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -136,7 +94,7 @@ ListItem_t * const pxIndex = pxList->pxIndex; pxIndex->pxPrevious = pxNewListItem; /* Remember which list the item is in. */ - pxNewListItem->pvContainer = ( void * ) pxList; + pxNewListItem->pxContainer = pxList; ( pxList->uxNumberOfItems )++; } @@ -156,7 +114,7 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* Insert the new list item into the list, sorted in xItemValue order. If the list already contains a list item with the same item value then the - new list item should be placed after it. This ensures that TCB's which are + new list item should be placed after it. This ensures that TCBs which are stored in ready lists (all of which have the same xItemValue value) get a share of the CPU. However, if the xItemValue is the same as the back marker the iteration loop below will not end. Therefore the value is checked @@ -169,18 +127,18 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; { /* *** NOTE *********************************************************** If you find your application is crashing here then likely causes are - listed below. In addition see http://www.freertos.org/FAQHelp.html for + listed below. In addition see https://www.freertos.org/FAQHelp.html for more tips, and ensure configASSERT() is defined! - http://www.freertos.org/a00110.html#configASSERT + https://www.freertos.org/a00110.html#configASSERT 1) Stack overflow - - see http://www.freertos.org/Stacks-and-stack-overflow-checking.html + see https://www.freertos.org/Stacks-and-stack-overflow-checking.html 2) Incorrect interrupt priority assignment, especially on Cortex-M parts where numerically high priority values denote low actual interrupt priorities, which can seem counter intuitive. See - http://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition + https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition of configMAX_SYSCALL_INTERRUPT_PRIORITY on - http://www.freertos.org/a00110.html + https://www.freertos.org/a00110.html 3) Calling an API function from within a critical section or when the scheduler is suspended, or calling an API function that does not end in "FromISR" from an interrupt. @@ -189,7 +147,7 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; before vTaskStartScheduler() has been called?). **********************************************************************/ - for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */ { /* There is nothing to do here, just iterating to the wanted insertion position. */ @@ -203,7 +161,7 @@ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* Remember which list the item is in. This allows fast removal of the item later. */ - pxNewListItem->pvContainer = ( void * ) pxList; + pxNewListItem->pxContainer = pxList; ( pxList->uxNumberOfItems )++; } @@ -213,7 +171,7 @@ UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) { /* The list item knows which list it is in. Obtain the list from the list item. */ -List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; +List_t * const pxList = pxItemToRemove->pxContainer; pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; @@ -231,7 +189,7 @@ List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; mtCOVERAGE_TEST_MARKER(); } - pxItemToRemove->pvContainer = NULL; + pxItemToRemove->pxContainer = NULL; ( pxList->uxNumberOfItems )--; return pxList->uxNumberOfItems; diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c index d5feca9e..89a912c0 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /*----------------------------------------------------------- * Implementation of functions defined in portable.h for the ARM CM4F port. @@ -129,7 +87,7 @@ r0p1 port. */ /* Constants required to set up the initial stack. */ #define portINITIAL_XPSR ( 0x01000000 ) -#define portINITIAL_EXEC_RETURN ( 0xfffffffd ) +#define portINITIAL_EXC_RETURN ( 0xfffffffd ) /* The systick is a 24-bit counter. */ #define portMAX_24_BIT_NUMBER ( 0xffffffUL ) @@ -152,10 +110,6 @@ debugger. */ #define portTASK_RETURN_ADDRESS prvTaskExitError #endif -/* Each task maintains its own interrupt status in the critical nesting -variable. */ -static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; - /* * Setup the timer to generate the tick interrupts. The implementation in this * file is weak to allow application writers to change the timer used to @@ -187,10 +141,14 @@ static void prvTaskExitError( void ); /*-----------------------------------------------------------*/ +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + /* * The number of SysTick increments that make up one tick period. */ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) static uint32_t ulTimerCountsForOneTick = 0; #endif /* configUSE_TICKLESS_IDLE */ @@ -198,7 +156,7 @@ static void prvTaskExitError( void ); * The maximum number of tick periods that can be suppressed is limited by the * 24 bit resolution of the SysTick timer. */ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) static uint32_t xMaximumPossibleSuppressedTicks = 0; #endif /* configUSE_TICKLESS_IDLE */ @@ -206,7 +164,7 @@ static void prvTaskExitError( void ); * Compensate for the CPU cycles that pass while the SysTick is stopped (low * power functionality only. */ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) static uint32_t ulStoppedTimerCompensation = 0; #endif /* configUSE_TICKLESS_IDLE */ @@ -215,7 +173,7 @@ static void prvTaskExitError( void ); * FreeRTOS API functions are not called from interrupts that have been assigned * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. */ -#if ( configASSERT_DEFINED == 1 ) +#if( configASSERT_DEFINED == 1 ) static uint8_t ucMaxSysCallPriority = 0; static uint32_t ulMaxPRIGROUPValue = 0; static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; @@ -248,7 +206,7 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px /* A save method is being used that requires each task to maintain its own exec return value. */ pxTopOfStack--; - *pxTopOfStack = portINITIAL_EXEC_RETURN; + *pxTopOfStack = portINITIAL_EXC_RETURN; pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ @@ -258,6 +216,8 @@ StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t px static void prvTaskExitError( void ) { +volatile uint32_t ulDummy = 0; + /* A function that implements a task must not exit or attempt to return to its caller as there is nothing to return to. If a task wants to exit it should instead call vTaskDelete( NULL ). @@ -266,7 +226,16 @@ static void prvTaskExitError( void ) defined, then stop here so application writers can catch the error. */ configASSERT( uxCriticalNesting == ~0UL ); portDISABLE_INTERRUPTS(); - for( ;; ); + while( ulDummy == 0 ) + { + /* This file calls prvTaskExitError() after the scheduler has been + started to remove a compiler warning about the function being defined + but never called. ulDummy is used purely to quieten other warnings + about code appearing after this function is called - making ulDummy + volatile makes the compiler think the function could return and + therefore not output an 'unreachable code' warning for code that appears + after it. */ + } } /*-----------------------------------------------------------*/ @@ -291,11 +260,17 @@ void vPortSVCHandler( void ) static void prvPortStartFirstTask( void ) { + /* Start the first task. This also clears the bit that indicates the FPU is + in use in case the FPU was used before the scheduler was started - which + would otherwise result in the unnecessary leaving of space in the SVC stack + for lazy saving of FPU registers. */ __asm volatile( " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ " ldr r0, [r0] \n" " ldr r0, [r0] \n" " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */ + " msr control, r0 \n" " cpsie i \n" /* Globally enable interrupts. */ " cpsie f \n" " dsb \n" @@ -354,6 +329,24 @@ BaseType_t xPortStartScheduler( void ) ucMaxPriorityValue <<= ( uint8_t ) 0x01; } + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + /* Shift the priority group value back to its position within the AIRCR register. */ ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; @@ -388,7 +381,10 @@ BaseType_t xPortStartScheduler( void ) /* Should never get here as the tasks will now be executing! Call the task exit error function to prevent compiler warnings about a static function not being called in the case that the application writer overrides this - functionality by defining configTASK_RETURN_ADDRESS. */ + functionality by defining configTASK_RETURN_ADDRESS. Call + vTaskSwitchContext() so link time optimisation does not remove the + symbol. */ + vTaskSwitchContext(); prvTaskExitError(); /* Should not get here! */ @@ -449,10 +445,9 @@ void xPortPendSVHandler( void ) " vstmdbeq r0!, {s16-s31} \n" " \n" " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ - " \n" " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ " \n" - " stmdb sp!, {r3} \n" + " stmdb sp!, {r0, r3} \n" " mov r0, %0 \n" " msr basepri, r0 \n" " dsb \n" @@ -460,7 +455,7 @@ void xPortPendSVHandler( void ) " bl vTaskSwitchContext \n" " mov r0, #0 \n" " msr basepri, r0 \n" - " ldmia sp!, {r3} \n" + " ldmia sp!, {r0, r3} \n" " \n" " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ " ldr r0, [r1] \n" @@ -510,11 +505,11 @@ void xPortSysTickHandler( void ) } /*-----------------------------------------------------------*/ -#if configUSE_TICKLESS_IDLE == 1 +#if( configUSE_TICKLESS_IDLE == 1 ) __attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) { - uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; TickType_t xModifiableIdleTime; /* Make sure the SysTick reload value does not overflow the counter. */ @@ -540,7 +535,7 @@ void xPortSysTickHandler( void ) /* Enter a critical section but don't use the taskENTER_CRITICAL() method as that will mask interrupts that should exit sleep mode. */ - __asm volatile( "cpsid i" ); + __asm volatile( "cpsid i" ::: "memory" ); __asm volatile( "dsb" ); __asm volatile( "isb" ); @@ -561,7 +556,7 @@ void xPortSysTickHandler( void ) /* Re-enable interrupts - see comments above the cpsid instruction() above. */ - __asm volatile( "cpsie i" ); + __asm volatile( "cpsie i" ::: "memory" ); } else { @@ -581,32 +576,50 @@ void xPortSysTickHandler( void ) should not be executed again. However, the original expected idle time variable must remain unmodified, so a copy is taken. */ xModifiableIdleTime = xExpectedIdleTime; - configPRE_SLEEP_PROCESSING( &xModifiableIdleTime ); + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); if( xModifiableIdleTime > 0 ) { - __asm volatile( "dsb" ); + __asm volatile( "dsb" ::: "memory" ); __asm volatile( "wfi" ); __asm volatile( "isb" ); } - configPOST_SLEEP_PROCESSING( &xExpectedIdleTime ); + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); - /* Stop SysTick. Again, the time the SysTick is stopped for is - accounted for as best it can be, but using the tickless mode will - inevitably result in some tiny drift of the time maintained by the - kernel with respect to calendar time. */ - ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; - portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + /* Re-enable interrupts to allow the interrupt that brought the MCU + out of sleep mode to execute immediately. see comments above + __disable_interrupt() call above. */ + __asm volatile( "cpsie i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); - /* Re-enable interrupts - see comments above the cpsid instruction() - above. */ - __asm volatile( "cpsie i" ); + /* Disable interrupts again because the clock is about to be stopped + and interrupts that execute while the clock is stopped will increase + any slippage between the time maintained by the RTOS and calendar + time. */ + __asm volatile( "cpsid i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); - if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + /* Disable the SysTick clock without reading the + portNVIC_SYSTICK_CTRL_REG register to ensure the + portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, + the time the SysTick is stopped for is accounted for as best it can + be, but using the tickless mode will inevitably result in some tiny + drift of the time maintained by the kernel with respect to calendar + time*/ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT ); + + /* Determine if the SysTick clock has already counted to zero and + been set back to the current reload value (the reload back being + correct for the entire expected idle time) or if the SysTick is yet + to count to zero (in which case an interrupt other than the SysTick + must have brought the system out of sleep mode). */ + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) { uint32_t ulCalculatedLoadValue; - /* The tick interrupt has already executed, and the SysTick - count reloaded with ulReloadValue. Reset the + /* The tick interrupt is already pending, and the SysTick count + reloaded with ulReloadValue. Reset the portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick period. */ ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); @@ -621,11 +634,9 @@ void xPortSysTickHandler( void ) portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; - /* The tick interrupt handler will already have pended the tick - processing in the kernel. As the pending tick will be - processed as soon as this function exits, the tick value - maintained by the tick is stepped forward by one less than the - time spent waiting. */ + /* As the pending tick will be processed as soon as this + function exits, the tick value maintained by the tick is stepped + forward by one less than the time spent waiting. */ ulCompleteTickPeriods = xExpectedIdleTime - 1UL; } else @@ -647,17 +658,14 @@ void xPortSysTickHandler( void ) /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, then set portNVIC_SYSTICK_LOAD_REG back to its standard - value. The critical section is used to ensure the tick interrupt - can only execute once in the case that the reload register is near - zero. */ + value. */ portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; - portENTER_CRITICAL(); - { - portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; - vTaskStepTick( ulCompleteTickPeriods ); - portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; - } - portEXIT_CRITICAL(); + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrupts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); } } @@ -671,7 +679,7 @@ void xPortSysTickHandler( void ) __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) { /* Calculate the constants required to configure the tick interrupt. */ - #if configUSE_TICKLESS_IDLE == 1 + #if( configUSE_TICKLESS_IDLE == 1 ) { ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; @@ -679,6 +687,10 @@ __attribute__(( weak )) void vPortSetupTimerInterrupt( void ) } #endif /* configUSE_TICKLESS_IDLE */ + /* Stop and clear the SysTick. */ + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + /* Configure SysTick to interrupt at the requested rate. */ portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); @@ -708,7 +720,7 @@ static void vPortEnableVFP( void ) uint8_t ucCurrentPriority; /* Obtain the number of the currently executing interrupt. */ - __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); /* Is the interrupt number a user defined interrupt? */ if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) @@ -754,7 +766,7 @@ static void vPortEnableVFP( void ) devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the scheduler. Note however that some vendor specific peripheral libraries assume a non-zero priority group setting, in which cases using a value - of zero will result in unpredicable behaviour. */ + of zero will result in unpredictable behaviour. */ configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h index d44fc922..d0a566a7 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #ifndef PORTMACRO_H @@ -125,7 +83,7 @@ typedef unsigned long UBaseType_t; \ /* Barriers are normally not required but do ensure the code is completely \ within the specified behaviour for the architecture. */ \ - __asm volatile( "dsb" ); \ + __asm volatile( "dsb" ::: "memory" ); \ __asm volatile( "isb" ); \ } @@ -173,7 +131,7 @@ not necessary for to use this port. They are defined so the common demo files { uint8_t ucReturn; - __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) ); + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" ); return ucReturn; } @@ -214,7 +172,7 @@ uint32_t ulCurrentInterrupt; BaseType_t xReturn; /* Obtain the number of the currently executing interrupt. */ - __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); if( ulCurrentInterrupt == 0 ) { @@ -240,7 +198,7 @@ uint32_t ulNewBASEPRI; " msr basepri, %0 \n" \ " isb \n" \ " dsb \n" \ - :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) + :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" ); } @@ -257,7 +215,7 @@ uint32_t ulOriginalBASEPRI, ulNewBASEPRI; " msr basepri, %1 \n" \ " isb \n" \ " dsb \n" \ - :"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) + :"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" ); /* This return will not be reached but is necessary to prevent compiler @@ -270,11 +228,12 @@ portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) { __asm volatile ( - " msr basepri, %0 " :: "r" ( ulNewMaskValue ) + " msr basepri, %0 " :: "r" ( ulNewMaskValue ) : "memory" ); } /*-----------------------------------------------------------*/ +#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" ) #ifdef __cplusplus } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url new file mode 100644 index 00000000..6c23737d --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,2 +[InternetShortcut] +URL=http://www.freertos.org/a00111.html +IDList= diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c index e7c7ade6..eaf443f4 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* * A sample implementation of pvPortMalloc() and vPortFree() that combines @@ -139,10 +97,12 @@ static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( p /* Create a couple of list links to mark the start and end of the list. */ static BlockLink_t xStart, *pxEnd = NULL; -/* Keeps track of the number of free bytes remaining, but says nothing about -fragmentation. */ +/* Keeps track of the number of calls to allocate and free memory as well as the +number of free bytes remaining, but says nothing about fragmentation. */ static size_t xFreeBytesRemaining = 0U; static size_t xMinimumEverFreeBytesRemaining = 0U; +static size_t xNumberOfSuccessfulAllocations = 0; +static size_t xNumberOfSuccessfulFrees = 0; /* Gets set to the top bit of an size_t type. When this bit in the xBlockSize member of an BlockLink_t structure is set then the block belongs to the @@ -263,6 +223,7 @@ void *pvReturn = NULL; by the application and has no "next" block. */ pxBlock->xBlockSize |= xBlockAllocatedBit; pxBlock->pxNextFreeBlock = NULL; + xNumberOfSuccessfulAllocations++; } else { @@ -334,6 +295,7 @@ BlockLink_t *pxLink; xFreeBytesRemaining += pxLink->xBlockSize; traceFREE( pv, pxLink->xBlockSize ); prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + xNumberOfSuccessfulFrees++; } ( void ) xTaskResumeAll(); } @@ -475,4 +437,56 @@ uint8_t *puc; mtCOVERAGE_TEST_MARKER(); } } +/*-----------------------------------------------------------*/ + +void vPortGetHeapStats( HeapStats_t *pxHeapStats ) +{ +BlockLink_t *pxBlock; +size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */ + + vTaskSuspendAll(); + { + pxBlock = xStart.pxNextFreeBlock; + + /* pxBlock will be NULL if the heap has not been initialised. The heap + is initialised automatically when the first allocation is made. */ + if( pxBlock != NULL ) + { + do + { + /* Increment the number of blocks and record the largest block seen + so far. */ + xBlocks++; + + if( pxBlock->xBlockSize > xMaxSize ) + { + xMaxSize = pxBlock->xBlockSize; + } + + if( pxBlock->xBlockSize < xMinSize ) + { + xMinSize = pxBlock->xBlockSize; + } + + /* Move to the next block in the chain until the last block is + reached. */ + pxBlock = pxBlock->pxNextFreeBlock; + } while( pxBlock != pxEnd ); + } + } + xTaskResumeAll(); + + pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize; + pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize; + pxHeapStats->xNumberOfFreeBlocks = xBlocks; + + taskENTER_CRITICAL(); + { + pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining; + pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations; + pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees; + pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining; + } + taskEXIT_CRITICAL(); +} diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c index 056d4be1..b3203b80 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ #include #include @@ -83,11 +41,11 @@ task.h is included from an application file. */ #include "croutine.h" #endif -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ /* Constants used with the cRxLock and cTxLock structure members. */ @@ -98,17 +56,26 @@ privileged Vs unprivileged linkage and placement. */ pcTail members are used as pointers into the queue storage area. When the Queue_t structure is used to represent a mutex pcHead and pcTail pointers are not necessary, and the pcHead pointer is set to NULL to indicate that the -pcTail pointer actually points to the mutex holder (if any). Map alternative -names to the pcHead and pcTail structure members to ensure the readability of -the code is maintained despite this dual use of two structure members. An -alternative implementation would be to use a union, but use of a union is -against the coding standard (although an exception to the standard has been -permitted where the dual use also significantly changes the type of the -structure member). */ -#define pxMutexHolder pcTail +structure instead holds a pointer to the mutex holder (if any). Map alternative +names to the pcHead and structure member to ensure the readability of the code +is maintained. The QueuePointers_t and SemaphoreData_t types are used to form +a union as their usage is mutually exclusive dependent on what the queue is +being used for. */ #define uxQueueType pcHead #define queueQUEUE_IS_MUTEX NULL +typedef struct QueuePointers +{ + int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ +} QueuePointers_t; + +typedef struct SemaphoreData +{ + TaskHandle_t xMutexHolder; /*< The handle of the task that holds the mutex. */ + UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ +} SemaphoreData_t; + /* Semaphores do not actually store or copy data, so have an item size of zero. */ #define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 ) @@ -125,18 +92,17 @@ zero. */ /* * Definition of the queue used by the scheduler. * Items are queued by copy, not reference. See the following link for the - * rationale: http://www.freertos.org/Embedded-RTOS-Queues.html + * rationale: https://www.freertos.org/Embedded-RTOS-Queues.html */ -typedef struct QueueDefinition +typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { int8_t *pcHead; /*< Points to the beginning of the queue storage area. */ - int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */ - union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */ + union { - int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ - UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ + QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */ + SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */ } u; List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ @@ -205,46 +171,46 @@ typedef xQUEUE Queue_t; * to indicate that a task may require unblocking. When the queue in unlocked * these lock counts are inspected, and the appropriate action taken. */ -PRIVILEGED_FUNCTION static void prvUnlockQueue( Queue_t * const pxQueue ); +static void prvUnlockQueue( Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; /* * Uses a critical section to determine if there is any data in a queue. * * @return pdTRUE if the queue contains no items, otherwise pdFALSE. */ -PRIVILEGED_FUNCTION static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ); +static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; /* * Uses a critical section to determine if there is any space in a queue. * * @return pdTRUE if there is no space, otherwise pdFALSE; */ -PRIVILEGED_FUNCTION static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ); +static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; /* * Copies an item into the queue, either at the front of the queue or the * back of the queue. */ -PRIVILEGED_FUNCTION static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ); +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) PRIVILEGED_FUNCTION; /* * Copies an item out of a queue. */ -PRIVILEGED_FUNCTION static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ); +static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; #if ( configUSE_QUEUE_SETS == 1 ) /* * Checks to see if a queue is a member of a queue set, and if so, notifies * the queue set that the queue contains data. */ - PRIVILEGED_FUNCTION static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ); + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; #endif /* * Called after a Queue_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -PRIVILEGED_FUNCTION static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ); +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; /* * Mutexes are a special type of queue. When a mutex is created, first the @@ -252,9 +218,19 @@ PRIVILEGED_FUNCTION static void prvInitialiseNewQueue( const UBaseType_t uxQueue * as a mutex. */ #if( configUSE_MUTEXES == 1 ) - PRIVILEGED_FUNCTION static void prvInitialiseMutex( Queue_t *pxNewQueue ); + static void prvInitialiseMutex( Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; #endif +#if( configUSE_MUTEXES == 1 ) + /* + * If a task waiting for a mutex causes the mutex holder to inherit a + * priority, but the waiting task times out, then the holder should + * disinherit the priority - but only down to the highest priority of any + * other tasks that are waiting for the same mutex. This function returns + * that priority. + */ + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; +#endif /*-----------------------------------------------------------*/ /* @@ -278,16 +254,16 @@ PRIVILEGED_FUNCTION static void prvInitialiseNewQueue( const UBaseType_t uxQueue BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) { -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); taskENTER_CRITICAL(); { - pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; pxQueue->pcWriteTo = pxQueue->pcHead; - pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ pxQueue->cRxLock = queueUNLOCKED; pxQueue->cTxLock = queueUNLOCKED; @@ -353,13 +329,14 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; the real queue and semaphore structures. */ volatile size_t xSize = sizeof( StaticQueue_t ); configASSERT( xSize == sizeof( Queue_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ } #endif /* configASSERT_DEFINED */ /* The address of a statically allocated queue was passed in, use it. The address of a statically allocated storage area was also passed in but is already set. */ - pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ if( pxNewQueue != NULL ) { @@ -374,6 +351,11 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } return pxNewQueue; } @@ -391,25 +373,28 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); - if( uxItemSize == ( UBaseType_t ) 0 ) - { - /* There is not going to be a queue storage area. */ - xQueueSizeInBytes = ( size_t ) 0; - } - else - { - /* Allocate enough space to hold the maximum number of items that - can be in the queue at any time. */ - xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - } + /* Allocate enough space to hold the maximum number of items that + can be in the queue at any time. It is valid for uxItemSize to be + zero in the case the queue is used as a semaphore. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + /* Allocate the queue and storage area. Justification for MISRA + deviation as follows: pvPortMalloc() always ensures returned memory + blocks are aligned per the requirements of the MCU stack. In this case + pvPortMalloc() must return a pointer that is guaranteed to meet the + alignment requirements of the Queue_t structure - which in this case + is an int8_t *. Therefore, whenever the stack alignment requirements + are greater than or equal to the pointer to char requirements the cast + is safe. In other cases alignment requirements are not strict (one or + two bytes). */ + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); /*lint !e9087 !e9079 see comment above. */ if( pxNewQueue != NULL ) { /* Jump past the queue structure to find the location of the queue storage area. */ - pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); + pucQueueStorage = ( uint8_t * ) pxNewQueue; + pucQueueStorage += sizeof( Queue_t ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) { @@ -422,6 +407,11 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } return pxNewQueue; } @@ -481,11 +471,11 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT correctly for a generic queue, but this function is creating a mutex. Overwrite those members that need to be set differently - in particular the information required for priority inheritance. */ - pxNewQueue->pxMutexHolder = NULL; + pxNewQueue->u.xSemaphore.xMutexHolder = NULL; pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; /* In case this is a recursive mutex. */ - pxNewQueue->u.uxRecursiveCallCount = 0; + pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; traceCREATE_MUTEX( pxNewQueue ); @@ -505,13 +495,13 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) { - Queue_t *pxNewQueue; + QueueHandle_t xNewQueue; const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; - pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); - prvInitialiseMutex( pxNewQueue ); + xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); - return pxNewQueue; + return xNewQueue; } #endif /* configUSE_MUTEXES */ @@ -521,17 +511,17 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) { - Queue_t *pxNewQueue; + QueueHandle_t xNewQueue; const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; /* Prevent compiler warnings about unused parameters if configUSE_TRACE_FACILITY does not equal 1. */ ( void ) ucQueueType; - pxNewQueue = ( Queue_t * ) xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); - prvInitialiseMutex( pxNewQueue ); + xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); - return pxNewQueue; + return xNewQueue; } #endif /* configUSE_MUTEXES */ @@ -539,9 +529,10 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT #if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) - void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) + TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) { - void *pxReturn; + TaskHandle_t pxReturn; + Queue_t * const pxSemaphore = ( Queue_t * ) xSemaphore; /* This function is called by xSemaphoreGetMutexHolder(), and should not be called directly. Note: This is a good way of determining if the @@ -550,9 +541,9 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT following critical section exiting and the function returning. */ taskENTER_CRITICAL(); { - if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + if( pxSemaphore->uxQueueType == queueQUEUE_IS_MUTEX ) { - pxReturn = ( void * ) ( ( Queue_t * ) xSemaphore )->pxMutexHolder; + pxReturn = pxSemaphore->u.xSemaphore.xMutexHolder; } else { @@ -567,6 +558,32 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT #endif /*-----------------------------------------------------------*/ +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + + configASSERT( xSemaphore ); + + /* Mutexes cannot be used in interrupt service routines, so the mutex + holder should not change in an ISR, and therefore a critical section is + not required here. */ + if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = ( ( Queue_t * ) xSemaphore )->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + +#endif +/*-----------------------------------------------------------*/ + #if ( configUSE_RECURSIVE_MUTEXES == 1 ) BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) @@ -576,25 +593,25 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT configASSERT( pxMutex ); - /* If this is the task that holds the mutex then pxMutexHolder will not + /* If this is the task that holds the mutex then xMutexHolder will not change outside of this task. If this task does not hold the mutex then pxMutexHolder can never coincidentally equal the tasks handle, and as this is the only condition we are interested in it does not matter if pxMutexHolder is accessed simultaneously by another task. Therefore no mutual exclusion is required to test the pxMutexHolder variable. */ - if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) { traceGIVE_MUTEX_RECURSIVE( pxMutex ); - /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to + /* uxRecursiveCallCount cannot be zero if xMutexHolder is equal to the task handle, therefore no underflow check is required. Also, uxRecursiveCallCount is only modified by the mutex holder, and as there can only be one, no mutual exclusion is required to modify the uxRecursiveCallCount member. */ - ( pxMutex->u.uxRecursiveCallCount )--; + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )--; /* Has the recursive call count unwound to 0? */ - if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ) + if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 ) { /* Return the mutex. This will automatically unblock any other task that might be waiting to access the mutex. */ @@ -636,21 +653,21 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT traceTAKE_MUTEX_RECURSIVE( pxMutex ); - if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) { - ( pxMutex->u.uxRecursiveCallCount )++; + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; xReturn = pdPASS; } else { - xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE ); + xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait ); /* pdPASS will only be returned if the mutex was successfully obtained. The calling task may have entered the Blocked state before reaching here. */ if( xReturn != pdFAIL ) { - ( pxMutex->u.uxRecursiveCallCount )++; + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; } else { @@ -724,7 +741,7 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQ { BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; TimeOut_t xTimeOut; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -736,9 +753,9 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; #endif - /* This function relaxes the coding standard somewhat to allow return - statements within the function itself. This is done in the interest - of execution time efficiency. */ + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ for( ;; ) { taskENTER_CRITICAL(); @@ -750,13 +767,23 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) { traceQUEUE_SEND( pxQueue ); - xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); #if ( configUSE_QUEUE_SETS == 1 ) { + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + was overwritten in the queue so the number of items + in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to @@ -803,6 +830,8 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } #else /* configUSE_QUEUE_SETS */ { + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + /* If there was a task waiting for data to arrive on the queue then unblock it now. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) @@ -855,7 +884,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { /* The queue was full and a block time was specified so configure the timeout structure. */ - vTaskSetTimeOutState( &xTimeOut ); + vTaskInternalSetTimeOutState( &xTimeOut ); xEntryTimeSet = pdTRUE; } else @@ -882,8 +911,8 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); /* Unlocking the queue means queue events can effect the - event list. It is possible that interrupts occurring now - remove this task from the event list again - but as the + event list. It is possible that interrupts occurring now + remove this task from the event list again - but as the scheduler is suspended the task will go onto the pending ready last instead of the actual ready list. */ prvUnlockQueue( pxQueue ); @@ -914,7 +943,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; traceQUEUE_SEND_FAILED( pxQueue ); return errQUEUE_FULL; } - } + } /*lint -restore */ } /*-----------------------------------------------------------*/ @@ -922,7 +951,7 @@ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pv { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -954,6 +983,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) { const int8_t cTxLock = pxQueue->cTxLock; + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; traceQUEUE_SEND_FROM_ISR( pxQueue ); @@ -972,7 +1002,14 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + was overwritten in the queue so the number of items + in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to @@ -1045,6 +1082,9 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { mtCOVERAGE_TEST_MARKER(); } + + /* Not used in this path. */ + ( void ) uxPreviousMessagesWaiting; } #endif /* configUSE_QUEUE_SETS */ } @@ -1073,7 +1113,7 @@ BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherP { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; /* Similar to xQueueGenericSendFromISR() but used with semaphores where the item size is 0. Don't directly wake a task that was blocked on a queue @@ -1090,7 +1130,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; /* Normally a mutex would not be given from an interrupt, especially if there is a mutex holder, as priority inheritance makes no sense for an interrupts, only tasks. */ - configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->pxMutexHolder != NULL ) ) ); + configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->u.xSemaphore.xMutexHolder != NULL ) ) ); /* RTOS ports that support interrupt nesting have the concept of a maximum system call (or maximum API call) interrupt priority. Interrupts that are @@ -1127,7 +1167,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; can be assumed there is no mutex holder and no need to determine if priority disinheritance is needed. Simply increase the count of messages (semaphores) available. */ - pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; /* The event list is not altered if the queue is locked. This will be done when the queue is unlocked later. */ @@ -1137,7 +1177,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) != pdFALSE ) + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) { /* The semaphore is a member of a queue set, and posting to the queue set caused a higher priority @@ -1234,25 +1274,30 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } /*-----------------------------------------------------------*/ -BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ) +BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) { BaseType_t xEntryTimeSet = pdFALSE; TimeOut_t xTimeOut; -int8_t *pcOriginalReadPosition; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; - configASSERT( pxQueue ); - configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) { configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } #endif - /* This function relaxes the coding standard somewhat to allow return - statements within the function itself. This is done in the interest - of execution time efficiency. */ + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ for( ;; ) { taskENTER_CRITICAL(); @@ -1263,44 +1308,19 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; must be the highest priority task wanting to access the queue. */ if( uxMessagesWaiting > ( UBaseType_t ) 0 ) { - /* Remember the read position in case the queue is only being - peeked. */ - pcOriginalReadPosition = pxQueue->u.pcReadFrom; - + /* Data available, remove one item. */ prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_RECEIVE( pxQueue ); + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; - if( xJustPeeking == pdFALSE ) + /* There is now space in the queue, were any tasks waiting to + post to the queue? If so, unblock the highest priority waiting + task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) { - traceQUEUE_RECEIVE( pxQueue ); - - /* Actually removing data, not just peeking. */ - pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; - - #if ( configUSE_MUTEXES == 1 ) + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) { - if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) - { - /* Record the information required to implement - priority inheritance should it become necessary. */ - pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_MUTEXES */ - - if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) - { - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) - { - queueYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + queueYIELD_IF_USING_PREEMPTION(); } else { @@ -1309,30 +1329,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } else { - traceQUEUE_PEEK( pxQueue ); - - /* The data is not being removed, so reset the read - pointer. */ - pxQueue->u.pcReadFrom = pcOriginalReadPosition; - - /* The data is being left in the queue, so see if there are - any other tasks waiting for the data. */ - if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) - { - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) - { - /* The task waiting has a higher priority than this task. */ - queueYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + mtCOVERAGE_TEST_MARKER(); } taskEXIT_CRITICAL(); @@ -1352,7 +1349,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { /* The queue was empty and a block time was specified so configure the timeout structure. */ - vTaskSetTimeOutState( &xTimeOut ); + vTaskInternalSetTimeOutState( &xTimeOut ); xEntryTimeSet = pdTRUE; } else @@ -1373,6 +1370,181 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; /* Update the timeout state to see if it has expired yet. */ if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { + /* The timeout has not expired. If the queue is still empty place + the task on the list of tasks waiting to receive from the queue. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The queue contains data again. Loop back to try and read the + data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. If there is no data in the queue exit, otherwise loop + back and attempt to read the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +Queue_t * const pxQueue = xQueue; + +#if( configUSE_MUTEXES == 1 ) + BaseType_t xInheritanceOccurred = pdFALSE; +#endif + + /* Check the queue pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* Check this really is a semaphore, in which case the item size will be + 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Semaphores are queues with an item size of 0, and where the + number of messages in the queue is the semaphore's count value. */ + const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxSemaphoreCount > ( UBaseType_t ) 0 ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Semaphores are queues with a data size of zero and where the + messages waiting is the semaphore's count. Reduce the count. */ + pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1; + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + + /* Check to see if other tasks are blocked waiting to give the + semaphore, and if so, unblock the highest priority such task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* For inheritance to have occurred there must have been an + initial timeout, and an adjusted timeout cannot become 0, as + if it were 0 the function would have exited. */ + #if( configUSE_MUTEXES == 1 ) + { + configASSERT( xInheritanceOccurred == pdFALSE ); + } + #endif /* configUSE_MUTEXES */ + + /* The semaphore count was 0 and no block time is specified + (or the block time has expired) so exit now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The semaphore count was 0 and a block time was specified + so configure the timeout structure ready to block. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can give to and take from the semaphore + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* A block time is specified and not expired. If the semaphore + count is 0 then enter the Blocked state to wait for a semaphore to + become available. As semaphores are implemented with queues the + queue being empty is equivalent to the semaphore count being 0. */ if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); @@ -1383,7 +1555,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; { taskENTER_CRITICAL(); { - vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder ); } taskEXIT_CRITICAL(); } @@ -1407,18 +1579,48 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; } else { - /* Try again. */ + /* There was no timeout and the semaphore count was not 0, so + attempt to take the semaphore again. */ prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } else { + /* Timed out. */ prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); + /* If the semaphore count is 0 exit now as the timeout has + expired. Otherwise return to attempt to take the semaphore that is + known to be available. As semaphores are implemented by queues the + queue being empty is equivalent to the semaphore count being 0. */ if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { + #if ( configUSE_MUTEXES == 1 ) + { + /* xInheritanceOccurred could only have be set if + pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to + test the mutex type again to check it is actually a mutex. */ + if( xInheritanceOccurred != pdFALSE ) + { + taskENTER_CRITICAL(); + { + UBaseType_t uxHighestWaitingPriority; + + /* This task blocking on the mutex caused another + task to inherit this task's priority. Now this task + has timed out the priority should be disinherited + again, but only as low as the next highest priority + task that is waiting for the same mutex. */ + uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue ); + vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority ); + } + taskEXIT_CRITICAL(); + } + } + #endif /* configUSE_MUTEXES */ + traceQUEUE_RECEIVE_FAILED( pxQueue ); return errQUEUE_EMPTY; } @@ -1427,7 +1629,156 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; mtCOVERAGE_TEST_MARKER(); } } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +int8_t *pcOriginalReadPosition; +Queue_t * const pxQueue = xQueue; + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember the read position so it can be reset after the data + is read from the queue as this function is only peeking the + data, not removing it. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_PEEK( pxQueue ); + + /* The data is not being removed, so reset the read pointer. */ + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure ready to enter the blocked + state. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* Timeout has not expired yet, check to see if there is data in the + queue now, and if not enter the Blocked state to wait for data. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_PEEK( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There is data in the queue now, so don't enter the blocked + state, instead return to try and obtain the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. If there is still no data in the queue + exit, otherwise go back and try to read the data again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ } /*-----------------------------------------------------------*/ @@ -1435,7 +1786,7 @@ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, Ba { BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -1468,7 +1819,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); prvCopyDataFromQueue( pxQueue, pvBuffer ); - pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; /* If the queue is locked the event list will not be modified. Instead update the lock count so the task that unlocks the queue @@ -1527,7 +1878,7 @@ BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) BaseType_t xReturn; UBaseType_t uxSavedInterruptStatus; int8_t *pcOriginalReadPosition; -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); @@ -1558,9 +1909,9 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; /* Remember the read position so it can be reset as nothing is actually being removed from the queue. */ - pcOriginalReadPosition = pxQueue->u.pcReadFrom; + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; prvCopyDataFromQueue( pxQueue, pvBuffer ); - pxQueue->u.pcReadFrom = pcOriginalReadPosition; + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; xReturn = pdPASS; } @@ -1595,9 +1946,8 @@ UBaseType_t uxReturn; UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) { UBaseType_t uxReturn; -Queue_t *pxQueue; +Queue_t * const pxQueue = xQueue; - pxQueue = ( Queue_t * ) xQueue; configASSERT( pxQueue ); taskENTER_CRITICAL(); @@ -1613,10 +1963,10 @@ Queue_t *pxQueue; UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) { UBaseType_t uxReturn; +Queue_t * const pxQueue = xQueue; - configASSERT( xQueue ); - - uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + configASSERT( pxQueue ); + uxReturn = pxQueue->uxMessagesWaiting; return uxReturn; } /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ @@ -1624,7 +1974,7 @@ UBaseType_t uxReturn; void vQueueDelete( QueueHandle_t xQueue ) { -Queue_t * const pxQueue = ( Queue_t * ) xQueue; +Queue_t * const pxQueue = xQueue; configASSERT( pxQueue ); traceQUEUE_DELETE( pxQueue ); @@ -1694,6 +2044,33 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue; #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ +#if( configUSE_MUTEXES == 1 ) + + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) + { + UBaseType_t uxHighestPriorityOfWaitingTasks; + + /* If a task waiting for a mutex causes the mutex holder to inherit a + priority, but the waiting task times out, then the holder should + disinherit the priority - but only down to the highest priority of any + other tasks that are waiting for the same mutex. For this purpose, + return the priority of the highest priority task that is waiting for the + mutex. */ + if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0U ) + { + uxHighestPriorityOfWaitingTasks = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) ); + } + else + { + uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; + } + + return uxHighestPriorityOfWaitingTasks; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) { BaseType_t xReturn = pdFALSE; @@ -1710,8 +2087,8 @@ UBaseType_t uxMessagesWaiting; if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { /* The mutex is no longer being held. */ - xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder ); - pxQueue->pxMutexHolder = NULL; + xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder ); + pxQueue->u.xSemaphore.xMutexHolder = NULL; } else { @@ -1722,9 +2099,9 @@ UBaseType_t uxMessagesWaiting; } else if( xPosition == queueSEND_TO_BACK ) { - ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. */ - pxQueue->pcWriteTo += pxQueue->uxItemSize; - if( pxQueue->pcWriteTo >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ + pxQueue->pcWriteTo += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ { pxQueue->pcWriteTo = pxQueue->pcHead; } @@ -1735,11 +2112,11 @@ UBaseType_t uxMessagesWaiting; } else { - ( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - pxQueue->u.pcReadFrom -= pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e9087 !e418 MISRA exception as the casts are only redundant for some ports. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. Assert checks null pointer only used when length is 0. */ + pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ { - pxQueue->u.pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); } else { @@ -1767,7 +2144,7 @@ UBaseType_t uxMessagesWaiting; } } - pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; return xReturn; } @@ -1777,16 +2154,16 @@ static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer { if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) { - pxQueue->u.pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ { - pxQueue->u.pcReadFrom = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } - ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. */ + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ } } /*-----------------------------------------------------------*/ @@ -1812,7 +2189,7 @@ static void prvUnlockQueue( Queue_t * const pxQueue ) { if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) != pdFALSE ) + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to unblock. @@ -1935,9 +2312,10 @@ BaseType_t xReturn; BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) { BaseType_t xReturn; +Queue_t * const pxQueue = xQueue; - configASSERT( xQueue ); - if( ( ( Queue_t * ) xQueue )->uxMessagesWaiting == ( UBaseType_t ) 0 ) + configASSERT( pxQueue ); + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) { xReturn = pdTRUE; } @@ -1974,9 +2352,10 @@ BaseType_t xReturn; BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) { BaseType_t xReturn; +Queue_t * const pxQueue = xQueue; - configASSERT( xQueue ); - if( ( ( Queue_t * ) xQueue )->uxMessagesWaiting == ( ( Queue_t * ) xQueue )->uxLength ) + configASSERT( pxQueue ); + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) { xReturn = pdTRUE; } @@ -1994,7 +2373,7 @@ BaseType_t xReturn; BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ) { BaseType_t xReturn; - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* If the queue is already full we may have to block. A critical section is required to prevent an interrupt removing something from the queue @@ -2071,7 +2450,7 @@ BaseType_t xReturn; BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ) { BaseType_t xReturn; - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* If the queue is already empty we may have to block. A critical section is required to prevent an interrupt adding something to the queue @@ -2108,17 +2487,17 @@ BaseType_t xReturn; if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) { /* Data is available from the queue. */ - pxQueue->u.pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) { - pxQueue->u.pcReadFrom = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } --( pxQueue->uxMessagesWaiting ); - ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); xReturn = pdPASS; @@ -2160,7 +2539,7 @@ BaseType_t xReturn; BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ) { - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* Cannot block within an ISR so if there is no space on the queue then exit without doing anything. */ @@ -2209,24 +2588,24 @@ BaseType_t xReturn; BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxCoRoutineWoken ) { BaseType_t xReturn; - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* We cannot block from an ISR, so check there is data available. If not then just leave without doing anything. */ if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) { /* Copy the data from the queue. */ - pxQueue->u.pcReadFrom += pxQueue->uxItemSize; - if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) { - pxQueue->u.pcReadFrom = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } else { mtCOVERAGE_TEST_MARKER(); } --( pxQueue->uxMessagesWaiting ); - ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); if( ( *pxCoRoutineWoken ) == pdFALSE ) { @@ -2316,7 +2695,7 @@ BaseType_t xReturn; } return pcReturn; - } + } /*lint !e818 xQueue cannot be a pointer to const because it is a typedef. */ #endif /* configQUEUE_REGISTRY_SIZE */ /*-----------------------------------------------------------*/ @@ -2357,7 +2736,7 @@ BaseType_t xReturn; void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) { - Queue_t * const pxQueue = ( Queue_t * ) xQueue; + Queue_t * const pxQueue = xQueue; /* This function should not be called by application code hence the 'Restricted' in its name. It is not part of the public API. It is @@ -2395,7 +2774,7 @@ BaseType_t xReturn; { QueueSetHandle_t pxQueue; - pxQueue = xQueueGenericCreate( uxEventQueueLength, sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); + pxQueue = xQueueGenericCreate( uxEventQueueLength, ( UBaseType_t ) sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); return pxQueue; } @@ -2478,7 +2857,7 @@ BaseType_t xReturn; { QueueSetMemberHandle_t xReturn = NULL; - ( void ) xQueueGenericReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait, pdFALSE ); /*lint !e961 Casting from one typedef to another is not redundant. */ + ( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait ); /*lint !e961 Casting from one typedef to another is not redundant. */ return xReturn; } @@ -2500,7 +2879,7 @@ BaseType_t xReturn; #if ( configUSE_QUEUE_SETS == 1 ) - static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) { Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer; BaseType_t xReturn = pdFALSE; @@ -2517,7 +2896,7 @@ BaseType_t xReturn; traceQUEUE_SEND( pxQueueSetContainer ); /* The data copied is the handle of the queue that contains data. */ - xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, xCopyPosition ); + xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK ); if( cTxLock == queueUNLOCKED ) { diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt new file mode 100644 index 00000000..58480c56 --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt @@ -0,0 +1,17 @@ +Each real time kernel port consists of three files that contain the core kernel +components and are common to every port, and one or more files that are +specific to a particular microcontroller and or compiler. + ++ The FreeRTOS/Source directory contains the three files that are common to +every port - list.c, queue.c and tasks.c. The kernel is contained within these +three files. croutine.c implements the optional co-routine functionality - which +is normally only used on very memory limited systems. + ++ The FreeRTOS/Source/Portable directory contains the files that are specific to +a particular microcontroller and or compiler. + ++ The FreeRTOS/Source/include directory contains the real time kernel header +files. + +See the readme file in the FreeRTOS/Source/Portable directory for more +information. \ No newline at end of file diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c new file mode 100644 index 00000000..7ad5d54a --- /dev/null +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c @@ -0,0 +1,1263 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* Standard includes. */ +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "stream_buffer.h" + +#if( configUSE_TASK_NOTIFICATIONS != 1 ) + #error configUSE_TASK_NOTIFICATIONS must be set to 1 to build stream_buffer.c +#endif + +/* Lint e961, e9021 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + +/* If the user has not provided application specific Rx notification macros, +or #defined the notification macros away, them provide default implementations +that uses task notifications. */ +/*lint -save -e9026 Function like macros allowed and needed here so they can be overidden. */ +#ifndef sbRECEIVE_COMPLETED + #define sbRECEIVE_COMPLETED( pxStreamBuffer ) \ + vTaskSuspendAll(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ + { \ + ( void ) xTaskNotify( ( pxStreamBuffer )->xTaskWaitingToSend, \ + ( uint32_t ) 0, \ + eNoAction ); \ + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ + } \ + } \ + ( void ) xTaskResumeAll(); +#endif /* sbRECEIVE_COMPLETED */ + +#ifndef sbRECEIVE_COMPLETED_FROM_ISR + #define sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, \ + pxHigherPriorityTaskWoken ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + \ + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ + { \ + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, \ + ( uint32_t ) 0, \ + eNoAction, \ + pxHigherPriorityTaskWoken ); \ + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ + } \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } +#endif /* sbRECEIVE_COMPLETED_FROM_ISR */ + +/* If the user has not provided an application specific Tx notification macro, +or #defined the notification macro away, them provide a default implementation +that uses task notifications. */ +#ifndef sbSEND_COMPLETED + #define sbSEND_COMPLETED( pxStreamBuffer ) \ + vTaskSuspendAll(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ + { \ + ( void ) xTaskNotify( ( pxStreamBuffer )->xTaskWaitingToReceive, \ + ( uint32_t ) 0, \ + eNoAction ); \ + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ + } \ + } \ + ( void ) xTaskResumeAll(); +#endif /* sbSEND_COMPLETED */ + +#ifndef sbSEND_COMPLETE_FROM_ISR + #define sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + \ + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ + { \ + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, \ + ( uint32_t ) 0, \ + eNoAction, \ + pxHigherPriorityTaskWoken ); \ + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ + } \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } +#endif /* sbSEND_COMPLETE_FROM_ISR */ +/*lint -restore (9026) */ + +/* The number of bytes used to hold the length of a message in the buffer. */ +#define sbBYTES_TO_STORE_MESSAGE_LENGTH ( sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) ) + +/* Bits stored in the ucFlags field of the stream buffer. */ +#define sbFLAGS_IS_MESSAGE_BUFFER ( ( uint8_t ) 1 ) /* Set if the stream buffer was created as a message buffer, in which case it holds discrete messages rather than a stream. */ +#define sbFLAGS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 2 ) /* Set if the stream buffer was created using statically allocated memory. */ + +/*-----------------------------------------------------------*/ + +/* Structure that hold state information on the buffer. */ +typedef struct StreamBufferDef_t /*lint !e9058 Style convention uses tag. */ +{ + volatile size_t xTail; /* Index to the next item to read within the buffer. */ + volatile size_t xHead; /* Index to the next item to write within the buffer. */ + size_t xLength; /* The length of the buffer pointed to by pucBuffer. */ + size_t xTriggerLevelBytes; /* The number of bytes that must be in the stream buffer before a task that is waiting for data is unblocked. */ + volatile TaskHandle_t xTaskWaitingToReceive; /* Holds the handle of a task waiting for data, or NULL if no tasks are waiting. */ + volatile TaskHandle_t xTaskWaitingToSend; /* Holds the handle of a task waiting to send data to a message buffer that is full. */ + uint8_t *pucBuffer; /* Points to the buffer itself - that is - the RAM that stores the data passed through the buffer. */ + uint8_t ucFlags; + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxStreamBufferNumber; /* Used for tracing purposes. */ + #endif +} StreamBuffer_t; + +/* + * The number of bytes available to be read from the buffer. + */ +static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) PRIVILEGED_FUNCTION; + +/* + * Add xCount bytes from pucData into the pxStreamBuffer message buffer. + * Returns the number of bytes written, which will either equal xCount in the + * success case, or 0 if there was not enough space in the buffer (in which case + * no data is written into the buffer). + */ +static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, const uint8_t *pucData, size_t xCount ) PRIVILEGED_FUNCTION; + +/* + * If the stream buffer is being used as a message buffer, then reads an entire + * message out of the buffer. If the stream buffer is being used as a stream + * buffer then read as many bytes as possible from the buffer. + * prvReadBytesFromBuffer() is called to actually extract the bytes from the + * buffer's data storage area. + */ +static size_t prvReadMessageFromBuffer( StreamBuffer_t *pxStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + size_t xBytesAvailable, + size_t xBytesToStoreMessageLength ) PRIVILEGED_FUNCTION; + +/* + * If the stream buffer is being used as a message buffer, then writes an entire + * message to the buffer. If the stream buffer is being used as a stream + * buffer then write as many bytes as possible to the buffer. + * prvWriteBytestoBuffer() is called to actually send the bytes to the buffer's + * data storage area. + */ +static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + size_t xSpace, + size_t xRequiredSpace ) PRIVILEGED_FUNCTION; + +/* + * Read xMaxCount bytes from the pxStreamBuffer message buffer and write them + * to pucData. + */ +static size_t prvReadBytesFromBuffer( StreamBuffer_t *pxStreamBuffer, + uint8_t *pucData, + size_t xMaxCount, + size_t xBytesAvailable ) PRIVILEGED_FUNCTION; + +/* + * Called by both pxStreamBufferCreate() and pxStreamBufferCreateStatic() to + * initialise the members of the newly created stream buffer structure. + */ +static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, + uint8_t * const pucBuffer, + size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + uint8_t ucFlags ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer ) + { + uint8_t *pucAllocatedMemory; + uint8_t ucFlags; + + /* In case the stream buffer is going to be used as a message buffer + (that is, it will hold discrete messages with a little meta data that + says how big the next message is) check the buffer will be large enough + to hold at least one message. */ + if( xIsMessageBuffer == pdTRUE ) + { + /* Is a message buffer but not statically allocated. */ + ucFlags = sbFLAGS_IS_MESSAGE_BUFFER; + configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); + } + else + { + /* Not a message buffer and not statically allocated. */ + ucFlags = 0; + configASSERT( xBufferSizeBytes > 0 ); + } + configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); + + /* A trigger level of 0 would cause a waiting task to unblock even when + the buffer was empty. */ + if( xTriggerLevelBytes == ( size_t ) 0 ) + { + xTriggerLevelBytes = ( size_t ) 1; + } + + /* A stream buffer requires a StreamBuffer_t structure and a buffer. + Both are allocated in a single call to pvPortMalloc(). The + StreamBuffer_t structure is placed at the start of the allocated memory + and the buffer follows immediately after. The requested size is + incremented so the free space is returned as the user would expect - + this is a quirk of the implementation that means otherwise the free + space would be reported as one byte smaller than would be logically + expected. */ + xBufferSizeBytes++; + pucAllocatedMemory = ( uint8_t * ) pvPortMalloc( xBufferSizeBytes + sizeof( StreamBuffer_t ) ); /*lint !e9079 malloc() only returns void*. */ + + if( pucAllocatedMemory != NULL ) + { + prvInitialiseNewStreamBuffer( ( StreamBuffer_t * ) pucAllocatedMemory, /* Structure at the start of the allocated memory. */ /*lint !e9087 Safe cast as allocated memory is aligned. */ /*lint !e826 Area is not too small and alignment is guaranteed provided malloc() behaves as expected and returns aligned buffer. */ + pucAllocatedMemory + sizeof( StreamBuffer_t ), /* Storage area follows. */ /*lint !e9016 Indexing past structure valid for uint8_t pointer, also storage area has no alignment requirement. */ + xBufferSizeBytes, + xTriggerLevelBytes, + ucFlags ); + + traceSTREAM_BUFFER_CREATE( ( ( StreamBuffer_t * ) pucAllocatedMemory ), xIsMessageBuffer ); + } + else + { + traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ); + } + + return ( StreamBufferHandle_t ) pucAllocatedMemory; /*lint !e9087 !e826 Safe cast as allocated memory is aligned. */ + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) + { + StreamBuffer_t * const pxStreamBuffer = ( StreamBuffer_t * ) pxStaticStreamBuffer; /*lint !e740 !e9087 Safe cast as StaticStreamBuffer_t is opaque Streambuffer_t. */ + StreamBufferHandle_t xReturn; + uint8_t ucFlags; + + configASSERT( pucStreamBufferStorageArea ); + configASSERT( pxStaticStreamBuffer ); + configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); + + /* A trigger level of 0 would cause a waiting task to unblock even when + the buffer was empty. */ + if( xTriggerLevelBytes == ( size_t ) 0 ) + { + xTriggerLevelBytes = ( size_t ) 1; + } + + if( xIsMessageBuffer != pdFALSE ) + { + /* Statically allocated message buffer. */ + ucFlags = sbFLAGS_IS_MESSAGE_BUFFER | sbFLAGS_IS_STATICALLY_ALLOCATED; + } + else + { + /* Statically allocated stream buffer. */ + ucFlags = sbFLAGS_IS_STATICALLY_ALLOCATED; + } + + /* In case the stream buffer is going to be used as a message buffer + (that is, it will hold discrete messages with a little meta data that + says how big the next message is) check the buffer will be large enough + to hold at least one message. */ + configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticStreamBuffer_t equals the size of the real + message buffer structure. */ + volatile size_t xSize = sizeof( StaticStreamBuffer_t ); + configASSERT( xSize == sizeof( StreamBuffer_t ) ); + } /*lint !e529 xSize is referenced is configASSERT() is defined. */ + #endif /* configASSERT_DEFINED */ + + if( ( pucStreamBufferStorageArea != NULL ) && ( pxStaticStreamBuffer != NULL ) ) + { + prvInitialiseNewStreamBuffer( pxStreamBuffer, + pucStreamBufferStorageArea, + xBufferSizeBytes, + xTriggerLevelBytes, + ucFlags ); + + /* Remember this was statically allocated in case it is ever deleted + again. */ + pxStreamBuffer->ucFlags |= sbFLAGS_IS_STATICALLY_ALLOCATED; + + traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ); + + xReturn = ( StreamBufferHandle_t ) pxStaticStreamBuffer; /*lint !e9087 Data hiding requires cast to opaque type. */ + } + else + { + xReturn = NULL; + traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ); + } + + return xReturn; + } + +#endif /* ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) +{ +StreamBuffer_t * pxStreamBuffer = xStreamBuffer; + + configASSERT( pxStreamBuffer ); + + traceSTREAM_BUFFER_DELETE( xStreamBuffer ); + + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) pdFALSE ) + { + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Both the structure and the buffer were allocated using a single call + to pvPortMalloc(), hence only one call to vPortFree() is required. */ + vPortFree( ( void * ) pxStreamBuffer ); /*lint !e9087 Standard free() semantics require void *, plus pxStreamBuffer was allocated by pvPortMalloc(). */ + } + #else + { + /* Should not be possible to get here, ucFlags must be corrupt. + Force an assert. */ + configASSERT( xStreamBuffer == ( StreamBufferHandle_t ) ~0 ); + } + #endif + } + else + { + /* The structure and buffer were not allocated dynamically and cannot be + freed - just scrub the structure so future use will assert. */ + ( void ) memset( pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn = pdFAIL; + +#if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxStreamBufferNumber; +#endif + + configASSERT( pxStreamBuffer ); + + #if( configUSE_TRACE_FACILITY == 1 ) + { + /* Store the stream buffer number so it can be restored after the + reset. */ + uxStreamBufferNumber = pxStreamBuffer->uxStreamBufferNumber; + } + #endif + + /* Can only reset a message buffer if there are no tasks blocked on it. */ + taskENTER_CRITICAL(); + { + if( pxStreamBuffer->xTaskWaitingToReceive == NULL ) + { + if( pxStreamBuffer->xTaskWaitingToSend == NULL ) + { + prvInitialiseNewStreamBuffer( pxStreamBuffer, + pxStreamBuffer->pucBuffer, + pxStreamBuffer->xLength, + pxStreamBuffer->xTriggerLevelBytes, + pxStreamBuffer->ucFlags ); + xReturn = pdPASS; + + #if( configUSE_TRACE_FACILITY == 1 ) + { + pxStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; + } + #endif + + traceSTREAM_BUFFER_RESET( xStreamBuffer ); + } + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; + + configASSERT( pxStreamBuffer ); + + /* It is not valid for the trigger level to be 0. */ + if( xTriggerLevel == ( size_t ) 0 ) + { + xTriggerLevel = ( size_t ) 1; + } + + /* The trigger level is the number of bytes that must be in the stream + buffer before a task that is waiting for data is unblocked. */ + if( xTriggerLevel <= pxStreamBuffer->xLength ) + { + pxStreamBuffer->xTriggerLevelBytes = xTriggerLevel; + xReturn = pdPASS; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) +{ +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xSpace; + + configASSERT( pxStreamBuffer ); + + xSpace = pxStreamBuffer->xLength + pxStreamBuffer->xTail; + xSpace -= pxStreamBuffer->xHead; + xSpace -= ( size_t ) 1; + + if( xSpace >= pxStreamBuffer->xLength ) + { + xSpace -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xSpace; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) +{ +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn; + + configASSERT( pxStreamBuffer ); + + xReturn = prvBytesInBuffer( pxStreamBuffer ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn, xSpace = 0; +size_t xRequiredSpace = xDataLengthBytes; +TimeOut_t xTimeOut; + + configASSERT( pvTxData ); + configASSERT( pxStreamBuffer ); + + /* This send function is used to write to both message buffers and stream + buffers. If this is a message buffer then the space needed must be + increased by the amount of bytes needed to store the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; + + /* Overflow? */ + configASSERT( xRequiredSpace > xDataLengthBytes ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xTicksToWait != ( TickType_t ) 0 ) + { + vTaskSetTimeOutState( &xTimeOut ); + + do + { + /* Wait until the required number of bytes are free in the message + buffer. */ + taskENTER_CRITICAL(); + { + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + + if( xSpace < xRequiredSpace ) + { + /* Clear notification state as going to wait for space. */ + ( void ) xTaskNotifyStateClear( NULL ); + + /* Should only be one writer. */ + configASSERT( pxStreamBuffer->xTaskWaitingToSend == NULL ); + pxStreamBuffer->xTaskWaitingToSend = xTaskGetCurrentTaskHandle(); + } + else + { + taskEXIT_CRITICAL(); + break; + } + } + taskEXIT_CRITICAL(); + + traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ); + ( void ) xTaskNotifyWait( ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); + pxStreamBuffer->xTaskWaitingToSend = NULL; + + } while( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xSpace == ( size_t ) 0 ) + { + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); + + if( xReturn > ( size_t ) 0 ) + { + traceSTREAM_BUFFER_SEND( xStreamBuffer, xReturn ); + + /* Was a task waiting for the data? */ + if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) + { + sbSEND_COMPLETED( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + const void *pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn, xSpace; +size_t xRequiredSpace = xDataLengthBytes; + + configASSERT( pvTxData ); + configASSERT( pxStreamBuffer ); + + /* This send function is used to write to both message buffers and stream + buffers. If this is a message buffer then the space needed must be + increased by the amount of bytes needed to store the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); + + if( xReturn > ( size_t ) 0 ) + { + /* Was a task waiting for the data? */ + if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) + { + sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + size_t xSpace, + size_t xRequiredSpace ) +{ + BaseType_t xShouldWrite; + size_t xReturn; + + if( xSpace == ( size_t ) 0 ) + { + /* Doesn't matter if this is a stream buffer or a message buffer, there + is no space to write. */ + xShouldWrite = pdFALSE; + } + else if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) == ( uint8_t ) 0 ) + { + /* This is a stream buffer, as opposed to a message buffer, so writing a + stream of bytes rather than discrete messages. Write as many bytes as + possible. */ + xShouldWrite = pdTRUE; + xDataLengthBytes = configMIN( xDataLengthBytes, xSpace ); + } + else if( xSpace >= xRequiredSpace ) + { + /* This is a message buffer, as opposed to a stream buffer, and there + is enough space to write both the message length and the message itself + into the buffer. Start by writing the length of the data, the data + itself will be written later in this function. */ + xShouldWrite = pdTRUE; + ( void ) prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) &( xDataLengthBytes ), sbBYTES_TO_STORE_MESSAGE_LENGTH ); + } + else + { + /* There is space available, but not enough space. */ + xShouldWrite = pdFALSE; + } + + if( xShouldWrite != pdFALSE ) + { + /* Writes the data itself. */ + xReturn = prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) pvTxData, xDataLengthBytes ); /*lint !e9079 Storage buffer is implemented as uint8_t for ease of sizing, alighment and access. */ + } + else + { + xReturn = 0; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; + + configASSERT( pvRxData ); + configASSERT( pxStreamBuffer ); + + /* This receive function is used by both message buffers, which store + discrete messages, and stream buffers, which store a continuous stream of + bytes. Discrete messages include an additional + sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + if( xTicksToWait != ( TickType_t ) 0 ) + { + /* Checking if there is data and clearing the notification state must be + performed atomically. */ + taskENTER_CRITICAL(); + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + /* If this function was invoked by a message buffer read then + xBytesToStoreMessageLength holds the number of bytes used to hold + the length of the next discrete message. If this function was + invoked by a stream buffer read then xBytesToStoreMessageLength will + be 0. */ + if( xBytesAvailable <= xBytesToStoreMessageLength ) + { + /* Clear notification state as going to wait for data. */ + ( void ) xTaskNotifyStateClear( NULL ); + + /* Should only be one reader. */ + configASSERT( pxStreamBuffer->xTaskWaitingToReceive == NULL ); + pxStreamBuffer->xTaskWaitingToReceive = xTaskGetCurrentTaskHandle(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + if( xBytesAvailable <= xBytesToStoreMessageLength ) + { + /* Wait for data to be available. */ + traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ); + ( void ) xTaskNotifyWait( ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); + pxStreamBuffer->xTaskWaitingToReceive = NULL; + + /* Recheck the data available after blocking. */ + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + } + + /* Whether receiving a discrete message (where xBytesToStoreMessageLength + holds the number of bytes used to store the message length) or a stream of + bytes (where xBytesToStoreMessageLength is zero), the number of bytes + available must be greater than xBytesToStoreMessageLength to be able to + read bytes from the buffer. */ + if( xBytesAvailable > xBytesToStoreMessageLength ) + { + xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable, xBytesToStoreMessageLength ); + + /* Was a task waiting for space in the buffer? */ + if( xReceivedLength != ( size_t ) 0 ) + { + traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ); + sbRECEIVE_COMPLETED( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ); + mtCOVERAGE_TEST_MARKER(); + } + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReturn, xBytesAvailable, xOriginalTail; +configMESSAGE_BUFFER_LENGTH_TYPE xTempReturn; + + configASSERT( pxStreamBuffer ); + + /* Ensure the stream buffer is being used as a message buffer. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + if( xBytesAvailable > sbBYTES_TO_STORE_MESSAGE_LENGTH ) + { + /* The number of bytes available is greater than the number of bytes + required to hold the length of the next message, so another message + is available. Return its length without removing the length bytes + from the buffer. A copy of the tail is stored so the buffer can be + returned to its prior state as the message is not actually being + removed from the buffer. */ + xOriginalTail = pxStreamBuffer->xTail; + ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempReturn, sbBYTES_TO_STORE_MESSAGE_LENGTH, xBytesAvailable ); + xReturn = ( size_t ) xTempReturn; + pxStreamBuffer->xTail = xOriginalTail; + } + else + { + /* The minimum amount of bytes in a message buffer is + ( sbBYTES_TO_STORE_MESSAGE_LENGTH + 1 ), so if xBytesAvailable is + less than sbBYTES_TO_STORE_MESSAGE_LENGTH the only other valid + value is 0. */ + configASSERT( xBytesAvailable == 0 ); + xReturn = 0; + } + } + else + { + xReturn = 0; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; + + configASSERT( pvRxData ); + configASSERT( pxStreamBuffer ); + + /* This receive function is used by both message buffers, which store + discrete messages, and stream buffers, which store a continuous stream of + bytes. Discrete messages include an additional + sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the + message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + /* Whether receiving a discrete message (where xBytesToStoreMessageLength + holds the number of bytes used to store the message length) or a stream of + bytes (where xBytesToStoreMessageLength is zero), the number of bytes + available must be greater than xBytesToStoreMessageLength to be able to + read bytes from the buffer. */ + if( xBytesAvailable > xBytesToStoreMessageLength ) + { + xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable, xBytesToStoreMessageLength ); + + /* Was a task waiting for space in the buffer? */ + if( xReceivedLength != ( size_t ) 0 ) + { + sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ); + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +static size_t prvReadMessageFromBuffer( StreamBuffer_t *pxStreamBuffer, + void *pvRxData, + size_t xBufferLengthBytes, + size_t xBytesAvailable, + size_t xBytesToStoreMessageLength ) +{ +size_t xOriginalTail, xReceivedLength, xNextMessageLength; +configMESSAGE_BUFFER_LENGTH_TYPE xTempNextMessageLength; + + if( xBytesToStoreMessageLength != ( size_t ) 0 ) + { + /* A discrete message is being received. First receive the length + of the message. A copy of the tail is stored so the buffer can be + returned to its prior state if the length of the message is too + large for the provided buffer. */ + xOriginalTail = pxStreamBuffer->xTail; + ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempNextMessageLength, xBytesToStoreMessageLength, xBytesAvailable ); + xNextMessageLength = ( size_t ) xTempNextMessageLength; + + /* Reduce the number of bytes available by the number of bytes just + read out. */ + xBytesAvailable -= xBytesToStoreMessageLength; + + /* Check there is enough space in the buffer provided by the + user. */ + if( xNextMessageLength > xBufferLengthBytes ) + { + /* The user has provided insufficient space to read the message + so return the buffer to its previous state (so the length of + the message is in the buffer again). */ + pxStreamBuffer->xTail = xOriginalTail; + xNextMessageLength = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* A stream of bytes is being received (as opposed to a discrete + message), so read as many bytes as possible. */ + xNextMessageLength = xBufferLengthBytes; + } + + /* Read the actual data. */ + xReceivedLength = prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) pvRxData, xNextMessageLength, xBytesAvailable ); /*lint !e9079 Data storage area is implemented as uint8_t array for ease of sizing, indexing and alignment. */ + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) +{ +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; +size_t xTail; + + configASSERT( pxStreamBuffer ); + + /* True if no bytes are available. */ + xTail = pxStreamBuffer->xTail; + if( pxStreamBuffer->xHead == xTail ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) +{ +BaseType_t xReturn; +size_t xBytesToStoreMessageLength; +const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + + configASSERT( pxStreamBuffer ); + + /* This generic version of the receive function is used by both message + buffers, which store discrete messages, and stream buffers, which store a + continuous stream of bytes. Discrete messages include an additional + sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + /* True if the available space equals zero. */ + if( xStreamBufferSpacesAvailable( xStreamBuffer ) <= xBytesToStoreMessageLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; + + configASSERT( pxStreamBuffer ); + + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) + { + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, + ( uint32_t ) 0, + eNoAction, + pxHigherPriorityTaskWoken ); + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) +{ +StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; + + configASSERT( pxStreamBuffer ); + + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) + { + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, + ( uint32_t ) 0, + eNoAction, + pxHigherPriorityTaskWoken ); + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, const uint8_t *pucData, size_t xCount ) +{ +size_t xNextHead, xFirstLength; + + configASSERT( xCount > ( size_t ) 0 ); + + xNextHead = pxStreamBuffer->xHead; + + /* Calculate the number of bytes that can be added in the first write - + which may be less than the total number of bytes that need to be added if + the buffer will wrap back to the beginning. */ + xFirstLength = configMIN( pxStreamBuffer->xLength - xNextHead, xCount ); + + /* Write as many bytes as can be written in the first write. */ + configASSERT( ( xNextHead + xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void* ) ( &( pxStreamBuffer->pucBuffer[ xNextHead ] ) ), ( const void * ) pucData, xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + + /* If the number of bytes written was less than the number that could be + written in the first write... */ + if( xCount > xFirstLength ) + { + /* ...then write the remaining bytes to the start of the buffer. */ + configASSERT( ( xCount - xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) pxStreamBuffer->pucBuffer, ( const void * ) &( pucData[ xFirstLength ] ), xCount - xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xNextHead += xCount; + if( xNextHead >= pxStreamBuffer->xLength ) + { + xNextHead -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxStreamBuffer->xHead = xNextHead; + + return xCount; +} +/*-----------------------------------------------------------*/ + +static size_t prvReadBytesFromBuffer( StreamBuffer_t *pxStreamBuffer, uint8_t *pucData, size_t xMaxCount, size_t xBytesAvailable ) +{ +size_t xCount, xFirstLength, xNextTail; + + /* Use the minimum of the wanted bytes and the available bytes. */ + xCount = configMIN( xBytesAvailable, xMaxCount ); + + if( xCount > ( size_t ) 0 ) + { + xNextTail = pxStreamBuffer->xTail; + + /* Calculate the number of bytes that can be read - which may be + less than the number wanted if the data wraps around to the start of + the buffer. */ + xFirstLength = configMIN( pxStreamBuffer->xLength - xNextTail, xCount ); + + /* Obtain the number of bytes it is possible to obtain in the first + read. Asserts check bounds of read and write. */ + configASSERT( xFirstLength <= xMaxCount ); + configASSERT( ( xNextTail + xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) pucData, ( const void * ) &( pxStreamBuffer->pucBuffer[ xNextTail ] ), xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + + /* If the total number of wanted bytes is greater than the number + that could be read in the first read... */ + if( xCount > xFirstLength ) + { + /*...then read the remaining bytes from the start of the buffer. */ + configASSERT( xCount <= xMaxCount ); + ( void ) memcpy( ( void * ) &( pucData[ xFirstLength ] ), ( void * ) ( pxStreamBuffer->pucBuffer ), xCount - xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Move the tail pointer to effectively remove the data read from + the buffer. */ + xNextTail += xCount; + + if( xNextTail >= pxStreamBuffer->xLength ) + { + xNextTail -= pxStreamBuffer->xLength; + } + + pxStreamBuffer->xTail = xNextTail; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) +{ +/* Returns the distance between xTail and xHead. */ +size_t xCount; + + xCount = pxStreamBuffer->xLength + pxStreamBuffer->xHead; + xCount -= pxStreamBuffer->xTail; + if ( xCount >= pxStreamBuffer->xLength ) + { + xCount -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, + uint8_t * const pucBuffer, + size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + uint8_t ucFlags ) +{ + /* Assert here is deliberately writing to the entire buffer to ensure it can + be written to without generating exceptions, and is setting the buffer to a + known value to assist in development/debugging. */ + #if( configASSERT_DEFINED == 1 ) + { + /* The value written just has to be identifiable when looking at the + memory. Don't use 0xA5 as that is the stack fill value and could + result in confusion as to what is actually being observed. */ + const BaseType_t xWriteValue = 0x55; + configASSERT( memset( pucBuffer, ( int ) xWriteValue, xBufferSizeBytes ) == pucBuffer ); + } /*lint !e529 !e438 xWriteValue is only used if configASSERT() is defined. */ + #endif + + ( void ) memset( ( void * ) pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); /*lint !e9087 memset() requires void *. */ + pxStreamBuffer->pucBuffer = pucBuffer; + pxStreamBuffer->xLength = xBufferSizeBytes; + pxStreamBuffer->xTriggerLevelBytes = xTriggerLevelBytes; + pxStreamBuffer->ucFlags = ucFlags; +} + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) + { + return xStreamBuffer->uxStreamBufferNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxStreamBufferNumber ) + { + xStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) + { + return ( xStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ); + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c index 5c68c6a2..f6a6a9b4 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* Standard includes. */ #include @@ -80,13 +38,13 @@ task.h is included from an application file. */ #include "FreeRTOS.h" #include "task.h" #include "timers.h" -#include "StackMacros.h" +#include "stack_macros.h" -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ /* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting functions but without including stdio.h here. */ @@ -117,29 +75,24 @@ functions but without including stdio.h here. */ */ #define tskSTACK_FILL_BYTE ( 0xa5U ) -/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using -dynamically allocated RAM, in which case when any task is deleted it is known -that both the task's stack and TCB need to be freed. Sometimes the -FreeRTOSConfig.h settings only allow a task to be created using statically -allocated RAM, in which case when any task is deleted it is known that neither -the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h -settings allow a task to be created using either statically or dynamically -allocated RAM, in which case a member of the TCB is used to record whether the -stack and/or TCB were allocated statically or dynamically, so when a task is -deleted the RAM that was allocated dynamically is freed again and no attempt is -made to free the RAM that was allocated statically. -tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a -task to be created using either statically or dynamically allocated RAM. Note -that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with -a statically allocated stack and a dynamically allocated TCB. */ -#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) || ( portUSING_MPU_WRAPPERS == 1 ) ) +/* Bits used to recored how a task's stack and TCB were allocated. */ #define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) #define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) #define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) +/* If any of the following are set then task stacks are filled with a known +value so the high water mark can be determined. If none of the following are +set then don't fill the stack so there is no unnecessary dependency on memset. */ +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 +#else + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 +#endif + /* * Macros used by vListTask to indicate which state a task is in. */ +#define tskRUNNING_CHAR ( 'X' ) #define tskBLOCKED_CHAR ( 'B' ) #define tskREADY_CHAR ( 'R' ) #define tskDELETED_CHAR ( 'D' ) @@ -153,6 +106,12 @@ a statically allocated stack and a dynamically allocated TCB. */ #define static #endif +/* The name allocated to the Idle task. This can be overridden by defining +configIDLE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configIDLE_TASK_NAME + #define configIDLE_TASK_NAME "IDLE" +#endif + #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is @@ -269,7 +228,7 @@ count overflows. */ * task should be used in place of the parameter. This macro simply checks to * see if the parameter is NULL and returns a pointer to the appropriate TCB. */ -#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) ) +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) /* The item value of the event list item is normally used to hold the priority of the task to which it belongs (coded to allow it to be held in reverse @@ -290,7 +249,7 @@ to its original value when it is released. */ * and stores task state information, including a pointer to the task's context * (the task's run time environment, including register values) */ -typedef struct tskTaskControlBlock +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ @@ -304,8 +263,8 @@ typedef struct tskTaskControlBlock StackType_t *pxStack; /*< Points to the start of the stack. */ char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - #if ( portSTACK_GROWTH > 0 ) - StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */ + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t *pxEndOfStack; /*< Points to the highest valid address for the stack. */ #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) @@ -327,7 +286,7 @@ typedef struct tskTaskControlBlock #endif #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; #endif #if( configGENERATE_RUN_TIME_STATS == 1 ) @@ -341,7 +300,10 @@ typedef struct tskTaskControlBlock responsible for resulting newlib operation. User must be familiar with newlib and must provide system-wide implementations of the necessary stubs. Be warned that (at the time of writing) the current newlib design - implements a system-wide malloc() that must be provided with locks. */ + implements a system-wide malloc() that must be provided with locks. + + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ struct _reent xNewLib_reent; #endif @@ -350,9 +312,9 @@ typedef struct tskTaskControlBlock volatile uint8_t ucNotifyState; #endif - /* See the comments above the definition of + /* See the comments in FreeRTOS.h with the definition of tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ #endif @@ -360,18 +322,24 @@ typedef struct tskTaskControlBlock uint8_t ucDelayAborted; #endif + #if( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif + } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name below to enable the use of older kernel aware debuggers. */ typedef tskTCB TCB_t; -/*lint -e956 A manual analysis and inspection has been used to determine which -static variables must be declared volatile. */ +/*lint -save -e956 A manual analysis and inspection has been used to determine +which static variables must be declared volatile. */ +PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; -PRIVILEGED_INITIALIZED_DATA TCB_t * volatile pxCurrentTCB = NULL; - -/* Lists for ready and blocked tasks. --------------------*/ +/* Lists for ready and blocked tasks. -------------------- +xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but +doing so breaks some kernel aware debuggers and debuggers that rely on removing +the static qualifier. */ PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ @@ -382,7 +350,7 @@ PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been r #if( INCLUDE_vTaskDelete == 1 ) PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ - PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; + PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; #endif @@ -392,17 +360,23 @@ PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been r #endif +/* Global POSIX errno. Its value is changed upon context switching to match +the errno of the currently running task. */ +#if ( configUSE_POSIX_ERRNO == 1 ) + int FreeRTOS_errno = 0; +#endif + /* Other file private variables. --------------------------------*/ -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile TickType_t xTickCount = ( TickType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; -PRIVILEGED_INITIALIZED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile BaseType_t xYieldPending = pdFALSE; -PRIVILEGED_INITIALIZED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; -PRIVILEGED_INITIALIZED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; -PRIVILEGED_INITIALIZED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ -PRIVILEGED_INITIALIZED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ +PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ /* Context switches are held pending while the scheduler is suspended. Also, interrupts must not manipulate the xStateListItem of a TCB, or any of the @@ -412,30 +386,38 @@ moves the task's event list item into the xPendingReadyList, ready for the kernel to move the task from the pending ready list into the real ready list when the scheduler is unsuspended. The pending ready list itself can only be accessed from a critical section. */ -PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; #if ( configGENERATE_RUN_TIME_STATS == 1 ) - PRIVILEGED_INITIALIZED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ - PRIVILEGED_INITIALIZED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ + /* Do not move these variables to function scope as doing so prevents the + code working with debuggers that need to remove the static qualifier. */ + PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ #endif -/*lint +e956 */ +/*lint -restore */ /*-----------------------------------------------------------*/ /* Callback function prototypes. --------------------------*/ #if( configCHECK_FOR_STACK_OVERFLOW > 0 ) + extern void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ); + #endif #if( configUSE_TICK_HOOK > 0 ) - extern void vApplicationTickHook( void ); + + extern void vApplicationTickHook( void ); /*lint !e526 Symbol not defined as it is an application callback. */ + #endif #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); + + extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /*lint !e526 Symbol not defined as it is an application callback. */ + #endif /* File private functions. --------------------------------*/ @@ -446,14 +428,16 @@ PRIVILEGED_INITIALIZED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( * is in any other state. */ #if ( INCLUDE_vTaskSuspend == 1 ) - PRIVILEGED_FUNCTION static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ); + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + #endif /* INCLUDE_vTaskSuspend */ /* * Utility to ready all the lists used by the scheduler. This is called * automatically upon the creation of the first task. */ -PRIVILEGED_FUNCTION static void prvInitialiseTaskLists( void ); +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; /* * The idle task, which as all tasks is implemented as a never ending loop. @@ -477,7 +461,7 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); */ #if ( INCLUDE_vTaskDelete == 1 ) - PRIVILEGED_FUNCTION static void prvDeleteTCB( TCB_t *pxTCB ); + static void prvDeleteTCB( TCB_t *pxTCB ) PRIVILEGED_FUNCTION; #endif @@ -486,13 +470,13 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); * in the list of tasks waiting to be deleted. If so the task is cleaned up * and its TCB deleted. */ -PRIVILEGED_FUNCTION static void prvCheckTasksWaitingTermination( void ); +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; /* * The currently executing task is entering the Blocked state. Add the task to * either the current or the overflow delayed task list. */ -PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ); +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; /* * Fills an TaskStatus_t structure with information on each task that is @@ -504,7 +488,7 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick */ #if ( configUSE_TRACE_FACILITY == 1 ) - PRIVILEGED_FUNCTION static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ); + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) PRIVILEGED_FUNCTION; #endif @@ -514,7 +498,7 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick */ #if ( INCLUDE_xTaskGetHandle == 1 ) - PRIVILEGED_FUNCTION static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ); + static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; #endif @@ -523,9 +507,9 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick * This function determines the 'high water mark' of the task stack by * determining how much of the stack remains at the original preset value. */ -#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - PRIVILEGED_FUNCTION static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ); + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; #endif @@ -540,7 +524,7 @@ PRIVILEGED_FUNCTION static void prvAddCurrentTaskToDelayedList( TickType_t xTick */ #if ( configUSE_TICKLESS_IDLE != 0 ) - PRIVILEGED_FUNCTION static TickType_t prvGetExpectedIdleTime( void ); + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; #endif @@ -556,7 +540,7 @@ static void prvResetNextTaskUnblockTime( void ); * Helper function used to pad task names with spaces when printing out * human readable tables of task information. */ - PRIVILEGED_FUNCTION static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ); + static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ) PRIVILEGED_FUNCTION; #endif @@ -564,32 +548,43 @@ static void prvResetNextTaskUnblockTime( void ); * Called after a Task_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -PRIVILEGED_FUNCTION static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, - const char * const pcName, +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; /* * Called after a new task has been created and initialised to place the task * under the control of the scheduler. */ -PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) PRIVILEGED_FUNCTION; + +/* + * freertos_tasks_c_additions_init() should only be called if the user definable + * macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is the only macro + * called by the function. + */ +#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + + static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; + +#endif /*-----------------------------------------------------------*/ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, - const char * const pcName, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, - StaticTask_t * const pxTaskBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + StaticTask_t * const pxTaskBuffer ) { TCB_t *pxNewTCB; TaskHandle_t xReturn; @@ -597,20 +592,32 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); configASSERT( puxStackBuffer != NULL ); configASSERT( pxTaskBuffer != NULL ); + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticTask_t equals the size of the real task + structure. */ + volatile size_t xSize = sizeof( StaticTask_t ); + configASSERT( xSize == sizeof( TCB_t ) ); + ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ + } + #endif /* configASSERT_DEFINED */ + + if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) { /* The memory used for the task's TCB and stack are passed into this function - use them. */ - pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ { /* Tasks can be created statically or dynamically, so note this task was created statically in case the task is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); prvAddNewTaskToReadyList( pxNewTCB ); @@ -626,7 +633,53 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); #endif /* SUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -#if( portUSING_MPU_WRAPPERS == 1 ) +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); + configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); + + if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + + return xReturn; + } + +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) { @@ -647,10 +700,14 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); /* Store the stack location in the TCB. */ pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; - /* Tasks can be created statically or dynamically, so note - this task had a statically allocated stack in case it is - later deleted. The TCB was allocated dynamically. */ - pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note + this task had a statically allocated stack in case it is + later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, pxTaskDefinition->pcName, @@ -674,11 +731,11 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, - const char * const pcName, - const uint16_t usStackDepth, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TaskHandle_t * const pxCreatedTask ) { TCB_t *pxNewTCB; BaseType_t xReturn; @@ -713,12 +770,12 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); StackType_t *pxStack; /* Allocate space for the stack used by the task being created. */ - pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ if( pxStack != NULL ) { /* Allocate space for the TCB. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ if( pxNewTCB != NULL ) { @@ -741,13 +798,13 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); if( pxNewTCB != NULL ) { - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ { /* Tasks can be created statically or dynamically, so note this task was created dynamically in case it is later deleted. */ pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; } - #endif /* configSUPPORT_STATIC_ALLOCATION */ + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); prvAddNewTaskToReadyList( pxNewTCB ); @@ -765,13 +822,13 @@ PRIVILEGED_FUNCTION static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ); /*-----------------------------------------------------------*/ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, - const char * const pcName, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const MemoryRegion_t * const xRegions ) { StackType_t *pxTopOfStack; UBaseType_t x; @@ -791,12 +848,12 @@ UBaseType_t x; #endif /* portUSING_MPU_WRAPPERS == 1 */ /* Avoid dependency on memset() if it is not required. */ - #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) { /* Fill the stack with a known value to assist debugging. */ ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); } - #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ + #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ /* Calculate the top of stack address. This depends on whether the stack grows from high memory to low (as per the 80x86) or vice versa. @@ -804,11 +861,19 @@ UBaseType_t x; by the port. */ #if( portSTACK_GROWTH < 0 ) { - pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); - pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. Checked by assert(). */ /* Check the alignment of the calculated top of stack is correct. */ configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + #if( configRECORD_STACK_HIGH_ADDRESS == 1 ) + { + /* Also record the stack's high address, which may assist + debugging. */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } + #endif /* configRECORD_STACK_HIGH_ADDRESS */ } #else /* portSTACK_GROWTH */ { @@ -824,26 +889,35 @@ UBaseType_t x; #endif /* portSTACK_GROWTH */ /* Store the task name in the TCB. */ - for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + if( pcName != NULL ) { - pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; - /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than - configMAX_TASK_NAME_LEN characters just in case the memory after the - string is not accessible (extremely unlikely). */ - if( pcName[ x ] == 0x00 ) - { - break; - } - else - { - mtCOVERAGE_TEST_MARKER(); + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + configMAX_TASK_NAME_LEN characters just in case the memory after the + string is not accessible (extremely unlikely). */ + if( pcName[ x ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } + + /* Ensure the name string is terminated in the case that the string length + was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + } + else + { + /* The task has not been given a name, so just ensure there is a NULL + terminator when it is read out. */ + pxNewTCB->pcTaskName[ 0 ] = 0x00; } - - /* Ensure the name string is terminated in the case that the string length - was greater or equal to configMAX_TASK_NAME_LEN. */ - pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; /* This is used as an array index so must ensure it's not too large. First remove the privilege bit if one is present. */ @@ -922,7 +996,9 @@ UBaseType_t x; #if ( configUSE_NEWLIB_REENTRANT == 1 ) { - /* Initialise this task's Newlib reent structure. */ + /* Initialise this task's Newlib reent structure. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); } #endif @@ -936,18 +1012,56 @@ UBaseType_t x; /* Initialize the TCB stack to look as if the task was already running, but had been interrupted by the scheduler. The return address is set to the start of the task function. Once the stack has been initialised - the top of stack variable is updated. */ + the top of stack variable is updated. */ #if( portUSING_MPU_WRAPPERS == 1 ) { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + /* If the port has capability to detect stack overflow, + pass the stack end address to the stack initialization + function as well. */ + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ } #else /* portUSING_MPU_WRAPPERS */ { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + /* If the port has capability to detect stack overflow, + pass the stack end address to the stack initialization + function as well. */ + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ } #endif /* portUSING_MPU_WRAPPERS */ - if( ( void * ) pxCreatedTask != NULL ) + if( pxCreatedTask != NULL ) { /* Pass the handle out in an anonymous way. The handle can be used to change the created task's priority, delete the created task, etc.*/ @@ -1055,7 +1169,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) being deleted. */ pxTCB = prvGetTCBFromHandle( xTaskToDelete ); - /* Remove task from the ready list. */ + /* Remove task from the ready/delayed list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -1095,6 +1209,10 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) check the xTasksWaitingTermination list. */ ++uxDeletedTasksWaitingCleanUp; + /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as + portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ + traceTASK_DELETE( pxTCB ); + /* The pre-delete hook is primarily for the Windows simulator, in which Windows specific clean up operations are performed, after which it is not possible to yield away from this task - @@ -1105,14 +1223,13 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) else { --uxCurrentNumberOfTasks; + traceTASK_DELETE( pxTCB ); prvDeleteTCB( pxTCB ); /* Reset the next expected unblock time in case it referred to the task that has just been deleted. */ prvResetNextTaskUnblockTime(); } - - traceTASK_DELETE( pxTCB ); } taskEXIT_CRITICAL(); @@ -1264,13 +1381,13 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #endif /* INCLUDE_vTaskDelay */ /*-----------------------------------------------------------*/ -#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) ) +#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) eTaskState eTaskGetState( TaskHandle_t xTask ) { eTaskState eReturn; - List_t *pxStateList; - const TCB_t * const pxTCB = ( TCB_t * ) xTask; + List_t const * pxStateList, *pxDelayedList, *pxOverflowedDelayedList; + const TCB_t * const pxTCB = xTask; configASSERT( pxTCB ); @@ -1283,11 +1400,13 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { taskENTER_CRITICAL(); { - pxStateList = ( List_t * ) listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxDelayedList = pxDelayedTaskList; + pxOverflowedDelayedList = pxOverflowDelayedTaskList; } taskEXIT_CRITICAL(); - if( ( pxStateList == pxDelayedTaskList ) || ( pxStateList == pxOverflowDelayedTaskList ) ) + if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) { /* The task being queried is referenced from one of the Blocked lists. */ @@ -1298,11 +1417,30 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) else if( pxStateList == &xSuspendedTaskList ) { /* The task being queried is referenced from the suspended - list. Is it genuinely suspended or is it block + list. Is it genuinely suspended or is it blocked indefinitely? */ if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) { - eReturn = eSuspended; + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + { + /* The task does not appear on the event list item of + and of the RTOS objects, but could still be in the + blocked state if it is waiting on its notification + rather than waiting on an object. */ + if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + { + eReturn = eBlocked; + } + else + { + eReturn = eSuspended; + } + } + #else + { + eReturn = eSuspended; + } + #endif } else { @@ -1337,15 +1475,15 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #if ( INCLUDE_uxTaskPriorityGet == 1 ) - UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ) + UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) { - TCB_t *pxTCB; + TCB_t const *pxTCB; UBaseType_t uxReturn; taskENTER_CRITICAL(); { - /* If null is passed in here then it is the priority of the that - called uxTaskPriorityGet() that is being queried. */ + /* If null is passed in here then it is the priority of the task + that called uxTaskPriorityGet() that is being queried. */ pxTCB = prvGetTCBFromHandle( xTask ); uxReturn = pxTCB->uxPriority; } @@ -1359,9 +1497,9 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #if ( INCLUDE_uxTaskPriorityGet == 1 ) - UBaseType_t uxTaskPriorityGetFromISR( TaskHandle_t xTask ) + UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) { - TCB_t *pxTCB; + TCB_t const *pxTCB; UBaseType_t uxReturn, uxSavedInterruptState; /* RTOS ports that support interrupt nesting have the concept of a @@ -1379,7 +1517,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) separate interrupt safe API to ensure interrupt entry is as fast and as simple as possible. More information (albeit Cortex-M specific) is provided on the following link: - http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + https://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR(); @@ -1515,14 +1653,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) } /* If the task is in the blocked or suspended list we need do - nothing more than change it's priority variable. However, if + nothing more than change its priority variable. However, if the task is in a ready list it needs to be removed and placed in the list appropriate to its new priority. */ if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) { - /* The task is currently in its ready list - remove before adding - it to it's new ready list. As we are in a critical section we - can do this even if the scheduler is suspended. */ + /* The task is currently in its ready list - remove before + adding it to it's new ready list. As we are in a critical + section we can do this even if the scheduler is suspended. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { /* It is known that the task is in its ready list so @@ -1597,6 +1735,17 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) } vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + { + if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task was blocked to wait for a notification, but is + now suspended, so no notification was received. */ + pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + } + } + #endif } taskEXIT_CRITICAL(); @@ -1628,7 +1777,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) /* The scheduler is not running, but the task that was pointed to by pxCurrentTCB has just been suspended and pxCurrentTCB must be adjusted to point to a different task. */ - if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ { /* No other tasks are ready, so set pxCurrentTCB back to NULL so when the next task is created pxCurrentTCB will @@ -1656,7 +1805,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) { BaseType_t xReturn = pdFALSE; - const TCB_t * const pxTCB = ( TCB_t * ) xTask; + const TCB_t * const pxTCB = xTask; /* Accesses xPendingReadyList so must be called from a critical section. */ @@ -1672,7 +1821,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { /* Is it in the suspended list because it is in the Suspended state, or because is is blocked with no timeout? */ - if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961. The cast is only redundant when NULL is used. */ { xReturn = pdTRUE; } @@ -1701,14 +1850,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) void vTaskResume( TaskHandle_t xTaskToResume ) { - TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; + TCB_t * const pxTCB = xTaskToResume; /* It does not make sense to resume the calling task. */ configASSERT( xTaskToResume ); /* The parameter cannot be NULL as it is impossible to resume the currently executing task. */ - if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) + if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) { taskENTER_CRITICAL(); { @@ -1716,12 +1865,12 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) { traceTASK_RESUME( pxTCB ); - /* As we are in a critical section we can access the ready - lists even if the scheduler is suspended. */ + /* The ready list can be accessed even if the scheduler is + suspended because this is inside a critical section. */ ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); - /* We may have just resumed a higher priority task. */ + /* A higher priority task may have just been resumed. */ if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) { /* This yield may not cause the task just resumed to run, @@ -1756,7 +1905,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) { BaseType_t xYieldRequired = pdFALSE; - TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; + TCB_t * const pxTCB = xTaskToResume; UBaseType_t uxSavedInterruptStatus; configASSERT( xTaskToResume ); @@ -1776,7 +1925,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) separate interrupt safe API to ensure interrupt entry is as fast and as simple as possible. More information (albeit Cortex-M specific) is provided on the following link: - http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + https://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); @@ -1838,10 +1987,10 @@ BaseType_t xReturn; address of the RAM then create the idle task. */ vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, - "IDLE", + configIDLE_TASK_NAME, ulIdleTaskStackSize, - ( void * ) NULL, - ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), + ( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */ + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ pxIdleTaskStackBuffer, pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ @@ -1858,9 +2007,10 @@ BaseType_t xReturn; { /* The Idle task is being created using dynamically allocated RAM. */ xReturn = xTaskCreate( prvIdleTask, - "IDLE", configMINIMAL_STACK_SIZE, + configIDLE_TASK_NAME, + configMINIMAL_STACK_SIZE, ( void * ) NULL, - ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ } #endif /* configSUPPORT_STATIC_ALLOCATION */ @@ -1880,6 +2030,15 @@ BaseType_t xReturn; if( xReturn == pdPASS ) { + /* freertos_tasks_c_additions_init() should only be called if the user + definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is + the only macro called by the function. */ + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + { + freertos_tasks_c_additions_init(); + } + #endif + /* Interrupts are turned off here, to ensure a tick does not occur before or during the call to xPortStartScheduler(). The stacks of the created tasks contain a status word with interrupts switched on @@ -1890,20 +2049,27 @@ BaseType_t xReturn; #if ( configUSE_NEWLIB_REENTRANT == 1 ) { /* Switch Newlib's _impure_ptr variable to point to the _reent - structure specific to the task that will run first. */ + structure specific to the task that will run first. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ xNextTaskUnblockTime = portMAX_DELAY; xSchedulerRunning = pdTRUE; - xTickCount = ( TickType_t ) 0U; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; /* If configGENERATE_RUN_TIME_STATS is defined then the following macro must be defined to configure the timer/counter used to generate - the run time counter time base. */ + the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS + is set to 0 and the following line fails to build then ensure you do not + have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your + FreeRTOSConfig.h file. */ portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + traceTASK_SWITCHED_IN(); + /* Setting up the timer tick is hardware specific and thus in the portable interface. */ if( xPortStartScheduler() != pdFALSE ) @@ -1947,7 +2113,18 @@ void vTaskSuspendAll( void ) BaseType_t. Please read Richard Barry's reply in the following link to a post in the FreeRTOS support forum before reporting this as a bug! - http://goo.gl/wu4acr */ + + /* portSOFRWARE_BARRIER() is only implemented for emulated/simulated ports that + do not otherwise exhibit real time behaviour. */ + portSOFTWARE_BARRIER(); + + /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment + is used to allow calls to vTaskSuspendAll() to nest. */ ++uxSchedulerSuspended; + + /* Enforces ordering for ports and optimised compilers that may otherwise place + the above increment elsewhere. */ + portMEMORY_BARRIER(); } /*----------------------------------------------------------*/ @@ -2040,7 +2217,7 @@ BaseType_t xAlreadyYielded = pdFALSE; appropriate ready list. */ while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); @@ -2073,9 +2250,9 @@ BaseType_t xAlreadyYielded = pdFALSE; not slip, and that any delayed tasks are resumed at the correct time. */ { - UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */ + TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ - if( uxPendedCounts > ( UBaseType_t ) 0U ) + if( xPendedCounts > ( TickType_t ) 0U ) { do { @@ -2087,10 +2264,10 @@ BaseType_t xAlreadyYielded = pdFALSE; { mtCOVERAGE_TEST_MARKER(); } - --uxPendedCounts; - } while( uxPendedCounts > ( UBaseType_t ) 0U ); + --xPendedCounts; + } while( xPendedCounts > ( TickType_t ) 0U ); - uxPendedTicks = 0; + xPendedTicks = 0; } else { @@ -2157,7 +2334,7 @@ UBaseType_t uxSavedInterruptStatus; system call interrupt priority. FreeRTOS maintains a separate interrupt safe API to ensure interrupt entry is as fast and as simple as possible. More information (albeit Cortex-M specific) is provided on the following - link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + link: https://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); @@ -2197,19 +2374,21 @@ TCB_t *pxTCB; TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL; UBaseType_t x; char cNextChar; + BaseType_t xBreakLoop; /* This function is called with the scheduler suspended. */ if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) { - listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ do { - listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Check each character in the name looking for a match or mismatch. */ + xBreakLoop = pdFALSE; for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) { cNextChar = pxNextTCB->pcTaskName[ x ]; @@ -2217,19 +2396,24 @@ TCB_t *pxTCB; if( cNextChar != pcNameToQuery[ x ] ) { /* Characters didn't match. */ - break; + xBreakLoop = pdTRUE; } - else if( cNextChar == 0x00 ) + else if( cNextChar == ( char ) 0x00 ) { /* Both strings terminated, a match must have been found. */ pxReturn = pxNextTCB; - break; + xBreakLoop = pdTRUE; } else { mtCOVERAGE_TEST_MARKER(); } + + if( xBreakLoop != pdFALSE ) + { + break; + } } if( pxReturn != NULL ) @@ -2310,7 +2494,7 @@ TCB_t *pxTCB; } ( void ) xTaskResumeAll(); - return ( TaskHandle_t ) pxTCB; + return pxTCB; } #endif /* INCLUDE_xTaskGetHandle */ @@ -2422,12 +2606,30 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than #endif /* configUSE_TICKLESS_IDLE */ /*----------------------------------------------------------*/ +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) +{ +BaseType_t xYieldRequired = pdFALSE; + + /* Must not be called with the scheduler suspended as the implementation + relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ + configASSERT( uxSchedulerSuspended == 0 ); + + /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when + the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ + vTaskSuspendAll(); + xPendedTicks += xTicksToCatchUp; + xYieldRequired = xTaskResumeAll(); + + return xYieldRequired; +} +/*----------------------------------------------------------*/ + #if ( INCLUDE_xTaskAbortDelay == 1 ) BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) { - TCB_t *pxTCB = ( TCB_t * ) xTask; - BaseType_t xReturn = pdFALSE; + TCB_t *pxTCB = xTask; + BaseType_t xReturn; configASSERT( pxTCB ); @@ -2437,6 +2639,8 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than it is actually in the Blocked state. */ if( eTaskGetState( xTask ) == eBlocked ) { + xReturn = pdPASS; + /* Remove the reference to the task from the blocked list. An interrupt won't touch the xStateListItem because the scheduler is suspended. */ @@ -2451,6 +2655,10 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) { ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + + /* This lets the task know it was forcibly removed from the + blocked state so it should not re-evaluate its block time and + then block again. */ pxTCB->ucDelayAborted = pdTRUE; } else @@ -2485,10 +2693,10 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than } else { - mtCOVERAGE_TEST_MARKER(); + xReturn = pdFAIL; } } - xTaskResumeAll(); + ( void ) xTaskResumeAll(); return xReturn; } @@ -2510,13 +2718,13 @@ BaseType_t xSwitchRequired = pdFALSE; { /* Minor optimisation. The tick count cannot change in this block. */ - const TickType_t xConstTickCount = xTickCount + 1; + const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; /* Increment the RTOS tick, switching the delayed and overflowed delayed lists if it wraps to 0. */ xTickCount = xConstTickCount; - if( xConstTickCount == ( TickType_t ) 0U ) + if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ { taskSWITCH_DELAYED_LISTS(); } @@ -2549,7 +2757,7 @@ BaseType_t xSwitchRequired = pdFALSE; item at the head of the delayed list. This is the time at which the task at the head of the delayed list must be removed from the Blocked state. */ - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); if( xConstTickCount < xItemValue ) @@ -2560,7 +2768,7 @@ BaseType_t xSwitchRequired = pdFALSE; state - so record the item value in xNextTaskUnblockTime. */ xNextTaskUnblockTime = xItemValue; - break; + break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */ } else { @@ -2627,7 +2835,7 @@ BaseType_t xSwitchRequired = pdFALSE; { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ - if( uxPendedTicks == ( UBaseType_t ) 0U ) + if( xPendedTicks == ( TickType_t ) 0 ) { vApplicationTickHook(); } @@ -2637,10 +2845,23 @@ BaseType_t xSwitchRequired = pdFALSE; } } #endif /* configUSE_TICK_HOOK */ + + #if ( configUSE_PREEMPTION == 1 ) + { + if( xYieldPending != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ } else { - ++uxPendedTicks; + ++xPendedTicks; /* The tick hook gets called at regular intervals, even if the scheduler is locked. */ @@ -2651,19 +2872,6 @@ BaseType_t xSwitchRequired = pdFALSE; #endif } - #if ( configUSE_PREEMPTION == 1 ) - { - if( xYieldPending != pdFALSE ) - { - xSwitchRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_PREEMPTION */ - return xSwitchRequired; } /*-----------------------------------------------------------*/ @@ -2682,13 +2890,15 @@ BaseType_t xSwitchRequired = pdFALSE; } else { - xTCB = ( TCB_t * ) xTask; + xTCB = xTask; } /* Save the hook function in the TCB. A critical section is required as the value can be accessed from an interrupt. */ taskENTER_CRITICAL(); + { xTCB->pxTaskTag = pxHookFunction; + } taskEXIT_CRITICAL(); } @@ -2699,24 +2909,17 @@ BaseType_t xSwitchRequired = pdFALSE; TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) { - TCB_t *xTCB; + TCB_t *pxTCB; TaskHookFunction_t xReturn; - /* If xTask is NULL then we are setting our own task hook. */ - if( xTask == NULL ) - { - xTCB = ( TCB_t * ) pxCurrentTCB; - } - else - { - xTCB = ( TCB_t * ) xTask; - } + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); /* Save the hook function in the TCB. A critical section is required as the value can be accessed from an interrupt. */ taskENTER_CRITICAL(); { - xReturn = xTCB->pxTaskTag; + xReturn = pxTCB->pxTaskTag; } taskEXIT_CRITICAL(); @@ -2726,6 +2929,31 @@ BaseType_t xSwitchRequired = pdFALSE; #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + TaskHookFunction_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = pxTCB->pxTaskTag; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) @@ -2736,11 +2964,11 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are calling our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB; + xTCB = pxCurrentTCB; } else { - xTCB = ( TCB_t * ) xTask; + xTCB = xTask; } if( xTCB->pxTaskTag != NULL ) @@ -2773,43 +3001,59 @@ void vTaskSwitchContext( void ) #if ( configGENERATE_RUN_TIME_STATS == 1 ) { - #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE - portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); - #else - ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); - #endif + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif - /* Add the amount of time the task has been running to the - accumulated time so far. The time the task started running was - stored in ulTaskSwitchedInTime. Note that there is no overflow - protection here so count values are only valid until the timer - overflows. The guard against negative values is to protect - against suspect run time stat counter implementations - which - are provided by the application, not the kernel. */ - if( ulTotalRunTime > ulTaskSwitchedInTime ) - { - pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - ulTaskSwitchedInTime = ulTotalRunTime; + /* Add the amount of time the task has been running to the + accumulated time so far. The time the task started running was + stored in ulTaskSwitchedInTime. Note that there is no overflow + protection here so count values are only valid until the timer + overflows. The guard against negative values is to protect + against suspect run time stat counter implementations - which + are provided by the application, not the kernel. */ + if( ulTotalRunTime > ulTaskSwitchedInTime ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + ulTaskSwitchedInTime = ulTotalRunTime; } #endif /* configGENERATE_RUN_TIME_STATS */ /* Check for stack overflow, if configured. */ taskCHECK_FOR_STACK_OVERFLOW(); + /* Before the currently running task is switched out, save its errno. */ + #if( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCB->iTaskErrno = FreeRTOS_errno; + } + #endif + /* Select a new task to run using either the generic C or port optimised asm code. */ - taskSELECT_HIGHEST_PRIORITY_TASK(); + taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ traceTASK_SWITCHED_IN(); + /* After the new task is switched in, update the global errno. */ + #if( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCB->iTaskErrno; + } + #endif + #if ( configUSE_NEWLIB_REENTRANT == 1 ) { /* Switch Newlib's _impure_ptr variable to point to the _reent - structure specific to this task. */ + structure specific to this task. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ @@ -2909,7 +3153,7 @@ BaseType_t xReturn; This function assumes that a check has already been made to ensure that pxEventList is not empty. */ - pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); @@ -2917,6 +3161,20 @@ BaseType_t xReturn; { ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); prvAddTaskToReadyList( pxUnblockedTCB ); + + #if( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + might be set to the blocked task's time out time. If the task is + unblocked for a reason other than a timeout xNextTaskUnblockTime is + normally left unchanged, because it is automatically reset to a new + value when the tick count equals xNextTaskUnblockTime. However if + tickless idling is used it might be more important to enter sleep mode + at the earliest possible time - so reset xNextTaskUnblockTime here to + ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif } else { @@ -2941,6 +3199,27 @@ BaseType_t xReturn; xReturn = pdFALSE; } + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) +{ +TCB_t *pxUnblockedTCB; + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + the event flags implementation. */ + configASSERT( uxSchedulerSuspended != pdFALSE ); + + /* Store the new item value in the event list. */ + listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Remove the event list form the event flag. Interrupts do not access + event flags. */ + pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( pxEventListItem ); + #if( configUSE_TICKLESS_IDLE != 0 ) { /* If a task is blocked on a kernel object then xNextTaskUnblockTime @@ -2955,28 +3234,6 @@ BaseType_t xReturn; } #endif - return xReturn; -} -/*-----------------------------------------------------------*/ - -BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) -{ -TCB_t *pxUnblockedTCB; -BaseType_t xReturn; - - /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by - the event flags implementation. */ - configASSERT( uxSchedulerSuspended != pdFALSE ); - - /* Store the new item value in the event list. */ - listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); - - /* Remove the event list form the event flag. Interrupts do not access - event flags. */ - pxUnblockedTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem ); - configASSERT( pxUnblockedTCB ); - ( void ) uxListRemove( pxEventListItem ); - /* Remove the task from the delayed list and add it to the ready list. The scheduler is suspended so interrupts will not be accessing the ready lists. */ @@ -2985,28 +3242,30 @@ BaseType_t xReturn; if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) { - /* Return true if the task removed from the event list has - a higher priority than the calling task. This allows - the calling task to know if it should force a context - switch now. */ - xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + /* The unblocked task has a priority above that of the calling task, so + a context switch is required. This function is called with the + scheduler suspended so xYieldPending is set so the context switch + occurs immediately that the scheduler is resumed (unsuspended). */ xYieldPending = pdTRUE; } - else - { - xReturn = pdFALSE; - } - - return xReturn; } /*-----------------------------------------------------------*/ void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) { configASSERT( pxTimeOut ); + taskENTER_CRITICAL(); + { + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + /* For internal use only as it does not use a critical section. */ pxTimeOut->xOverflowCount = xNumOfOverflows; pxTimeOut->xTimeOnEntering = xTickCount; } @@ -3023,9 +3282,10 @@ BaseType_t xReturn; { /* Minor optimisation. The tick count cannot change in this block. */ const TickType_t xConstTickCount = xTickCount; + const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; #if( INCLUDE_xTaskAbortDelay == 1 ) - if( pxCurrentTCB->ucDelayAborted != pdFALSE ) + if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) { /* The delay was aborted, which is not the same as a time out, but has the same result. */ @@ -3055,15 +3315,16 @@ BaseType_t xReturn; was called. */ xReturn = pdTRUE; } - else if( ( ( TickType_t ) ( xConstTickCount - pxTimeOut->xTimeOnEntering ) ) < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ + else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ { /* Not a genuine timeout. Adjust parameters for time remaining. */ - *pxTicksToWait -= ( xConstTickCount - pxTimeOut->xTimeOnEntering ); - vTaskSetTimeOutState( pxTimeOut ); + *pxTicksToWait -= xElapsedTime; + vTaskInternalSetTimeOutState( pxTimeOut ); xReturn = pdFALSE; } else { + *pxTicksToWait = 0; xReturn = pdTRUE; } } @@ -3084,11 +3345,11 @@ void vTaskMissedYield( void ) UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) { UBaseType_t uxReturn; - TCB_t *pxTCB; + TCB_t const *pxTCB; if( xTask != NULL ) { - pxTCB = ( TCB_t * ) xTask; + pxTCB = xTask; uxReturn = pxTCB->uxTaskNumber; } else @@ -3106,11 +3367,11 @@ void vTaskMissedYield( void ) void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) { - TCB_t *pxTCB; + TCB_t * pxTCB; if( xTask != NULL ) { - pxTCB = ( TCB_t * ) xTask; + pxTCB = xTask; pxTCB->uxTaskNumber = uxHandle; } } @@ -3136,6 +3397,11 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE SCHEDULER IS STARTED. **/ + /* In case a task that has a secure context deletes itself, in which case + the idle task is responsible for deleting the task's secure context, if + any. */ + portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); + for( ;; ) { /* See if any tasks have deleted themselves - if so then the idle task @@ -3212,6 +3478,11 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) configASSERT( xNextTaskUnblockTime >= xTickCount ); xExpectedIdleTime = prvGetExpectedIdleTime(); + /* Define the following macro to set xExpectedIdleTime to 0 + if the application does not want + portSUPPRESS_TICKS_AND_SLEEP() to be called. */ + configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { traceLOW_POWER_IDLE_BEGIN(); @@ -3243,6 +3514,8 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) const UBaseType_t uxNonApplicationTasks = 1; eSleepModeStatus eReturn = eStandardSleep; + /* This function must be called from a critical section. */ + if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) { /* A task was made ready while the scheduler was suspended. */ @@ -3284,6 +3557,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { pxTCB = prvGetTCBFromHandle( xTaskToSet ); + configASSERT( pxTCB != NULL ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; } } @@ -3369,37 +3643,22 @@ static void prvCheckTasksWaitingTermination( void ) #if ( INCLUDE_vTaskDelete == 1 ) { - BaseType_t xListIsEmpty; + TCB_t *pxTCB; - /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called - too often in the idle task. */ + /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() + being called too often in the idle task. */ while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) { - vTaskSuspendAll(); + taskENTER_CRITICAL(); { - xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; } - ( void ) xTaskResumeAll(); + taskEXIT_CRITICAL(); - if( xListIsEmpty == pdFALSE ) - { - TCB_t *pxTCB; - - taskENTER_CRITICAL(); - { - pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - --uxCurrentNumberOfTasks; - --uxDeletedTasksWaitingCleanUp; - } - taskEXIT_CRITICAL(); - - prvDeleteTCB( pxTCB ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + prvDeleteTCB( pxTCB ); } } #endif /* INCLUDE_vTaskDelete */ @@ -3421,25 +3680,6 @@ static void prvCheckTasksWaitingTermination( void ) pxTaskStatus->pxStackBase = pxTCB->pxStack; pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; - #if ( INCLUDE_vTaskSuspend == 1 ) - { - /* If the task is in the suspended list then there is a chance it is - actually just blocked indefinitely - so really it should be reported as - being in the Blocked state. */ - if( pxTaskStatus->eCurrentState == eSuspended ) - { - vTaskSuspendAll(); - { - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - pxTaskStatus->eCurrentState = eBlocked; - } - } - xTaskResumeAll(); - } - } - #endif /* INCLUDE_vTaskSuspend */ - #if ( configUSE_MUTEXES == 1 ) { pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; @@ -3460,16 +3700,42 @@ static void prvCheckTasksWaitingTermination( void ) } #endif - /* Obtaining the task state is a little fiddly, so is only done if the value - of eState passed into this function is eInvalid - otherwise the state is - just set to whatever is passed in. */ + /* Obtaining the task state is a little fiddly, so is only done if the + value of eState passed into this function is eInvalid - otherwise the + state is just set to whatever is passed in. */ if( eState != eInvalid ) { - pxTaskStatus->eCurrentState = eState; + if( pxTCB == pxCurrentTCB ) + { + pxTaskStatus->eCurrentState = eRunning; + } + else + { + pxTaskStatus->eCurrentState = eState; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a + chance it is actually just blocked indefinitely - so really + it should be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + vTaskSuspendAll(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatus->eCurrentState = eBlocked; + } + } + ( void ) xTaskResumeAll(); + } + } + #endif /* INCLUDE_vTaskSuspend */ + } } else { - pxTaskStatus->eCurrentState = eTaskGetState( xTask ); + pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); } /* Obtaining the stack space takes some time, so the xGetFreeStackSpace @@ -3499,12 +3765,12 @@ static void prvCheckTasksWaitingTermination( void ) static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) { - volatile TCB_t *pxNextTCB, *pxFirstTCB; + configLIST_VOLATILE TCB_t *pxNextTCB, *pxFirstTCB; UBaseType_t uxTask = 0; if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) { - listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Populate an TaskStatus_t structure within the pxTaskStatusArray array for each task that is referenced from @@ -3512,7 +3778,7 @@ static void prvCheckTasksWaitingTermination( void ) meaning of each TaskStatus_t structure member. */ do { - listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); uxTask++; } while( pxNextTCB != pxFirstTCB ); @@ -3528,9 +3794,9 @@ static void prvCheckTasksWaitingTermination( void ) #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) { uint32_t ulCount = 0U; @@ -3542,10 +3808,50 @@ static void prvCheckTasksWaitingTermination( void ) ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ - return ( uint16_t ) ulCount; + return ( configSTACK_DEPTH_TYPE ) ulCount; } -#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) */ +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + same except for their return type. Using configSTACK_DEPTH_TYPE allows the + user to determine the return type. It gets around the problem of the value + overflowing on 8-bit types without breaking backward compatibility for + applications that expect an 8-bit return type. */ + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + uint8_t *pucEndOfStack; + configSTACK_DEPTH_TYPE uxReturn; + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are + the same except for their return type. Using configSTACK_DEPTH_TYPE + allows the user to determine the return type. It gets around the + problem of the value overflowing on 8-bit types without breaking + backward compatibility for applications that expect an 8-bit return + type. */ + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark2 */ /*-----------------------------------------------------------*/ #if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) @@ -3586,7 +3892,9 @@ static void prvCheckTasksWaitingTermination( void ) portCLEAN_UP_TCB( pxTCB ); /* Free up the memory allocated by the scheduler for the task. It is up - to the task to free any memory allocated at the application level. */ + to the task to free any memory allocated at the application level. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) { _reclaim_reent( &( pxTCB->xNewLib_reent ) ); @@ -3600,7 +3908,7 @@ static void prvCheckTasksWaitingTermination( void ) vPortFree( pxTCB->pxStack ); vPortFree( pxTCB ); } - #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) + #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ { /* The task could have been allocated statically or dynamically, so check what was statically allocated before trying to free the @@ -3622,7 +3930,7 @@ static void prvCheckTasksWaitingTermination( void ) { /* Neither the stack nor the TCB were allocated dynamically, so nothing needs to be freed. */ - configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ) + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); mtCOVERAGE_TEST_MARKER(); } } @@ -3650,7 +3958,7 @@ TCB_t *pxTCB; the item at the head of the delayed list. This is the time at which the task at the head of the delayed list should be removed from the Blocked state. */ - ( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + ( pxTCB ) = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) ); } } @@ -3703,25 +4011,27 @@ TCB_t *pxTCB; #if ( configUSE_MUTEXES == 1 ) - void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) { - TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; + TCB_t * const pxMutexHolderTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; /* If the mutex was given back by an interrupt while the queue was - locked then the mutex holder might now be NULL. */ + locked then the mutex holder might now be NULL. _RB_ Is this still + needed as interrupts can no longer use mutexes? */ if( pxMutexHolder != NULL ) { /* If the holder of the mutex has a priority below the priority of the task attempting to obtain the mutex then it will temporarily inherit the priority of the task attempting to obtain the mutex. */ - if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ) + if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) { /* Adjust the mutex holder state to account for its new priority. Only reset the event list item value if the value is - not being used for anything else. */ - if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) { - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ } else { @@ -3730,11 +4040,14 @@ TCB_t *pxTCB; /* If the task being modified is in the ready state it will need to be moved into a new list. */ - if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) { - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { - taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + /* It is known that the task is in its ready list so + there is no need to check again and the port level + reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); } else { @@ -3742,26 +4055,45 @@ TCB_t *pxTCB; } /* Inherit the priority before being moved into the new list. */ - pxTCB->uxPriority = pxCurrentTCB->uxPriority; - prvAddTaskToReadyList( pxTCB ); + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxMutexHolderTCB ); } else { /* Just inherit the priority. */ - pxTCB->uxPriority = pxCurrentTCB->uxPriority; + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; } - traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority ); + traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); + + /* Inheritance occurred. */ + xReturn = pdTRUE; } else { - mtCOVERAGE_TEST_MARKER(); + if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) + { + /* The base priority of the mutex holder is lower than the + priority of the task attempting to take the mutex, but the + current priority of the mutex holder is not lower than the + priority of the task attempting to take the mutex. + Therefore the mutex holder must have already inherited a + priority, but inheritance would have occurred if that had + not been the case. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } } else { mtCOVERAGE_TEST_MARKER(); } + + return xReturn; } #endif /* configUSE_MUTEXES */ @@ -3771,7 +4103,7 @@ TCB_t *pxTCB; BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) { - TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; + TCB_t * const pxTCB = pxMutexHolder; BaseType_t xReturn = pdFALSE; if( pxMutexHolder != NULL ) @@ -3781,7 +4113,6 @@ TCB_t *pxTCB; interrupt, and if a mutex is given by the holding task then it must be the running state task. */ configASSERT( pxTCB == pxCurrentTCB ); - configASSERT( pxTCB->uxMutexesHeld ); ( pxTCB->uxMutexesHeld )--; @@ -3795,8 +4126,8 @@ TCB_t *pxTCB; /* A task can only have an inherited priority if it holds the mutex. If the mutex is held by a task then it cannot be given from an interrupt, and if a mutex is given by the - holding task then it must be the running state task. Remove - the holding task from the ready list. */ + holding task then it must be the running state task. Remove + the holding task from the ready/delayed list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -3848,6 +4179,111 @@ TCB_t *pxTCB; #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) + { + TCB_t * const pxTCB = pxMutexHolder; + UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; + const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; + + if( pxMutexHolder != NULL ) + { + /* If pxMutexHolder is not NULL then the holder must hold at least + one mutex. */ + configASSERT( pxTCB->uxMutexesHeld ); + + /* Determine the priority to which the priority of the task that + holds the mutex should be set. This will be the greater of the + holding task's base priority and the priority of the highest + priority task that is waiting to obtain the mutex. */ + if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) + { + uxPriorityToUse = uxHighestPriorityWaitingTask; + } + else + { + uxPriorityToUse = pxTCB->uxBasePriority; + } + + /* Does the priority need to change? */ + if( pxTCB->uxPriority != uxPriorityToUse ) + { + /* Only disinherit if no other mutexes are held. This is a + simplification in the priority inheritance implementation. If + the task that holds the mutex is also holding other mutexes then + the other mutexes may have caused the priority inheritance. */ + if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) + { + /* If a task has timed out because it already holds the + mutex it was trying to obtain then it cannot of inherited + its own priority. */ + configASSERT( pxTCB != pxCurrentTCB ); + + /* Disinherit the priority, remembering the previous + priority to facilitate determining the subject task's + state. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + uxPriorityUsedOnEntry = pxTCB->uxPriority; + pxTCB->uxPriority = uxPriorityToUse; + + /* Only reset the event list item value if the value is not + being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the running task is not the task that holds the mutex + then the task that holds the mutex could be in either the + Ready, Blocked or Suspended states. Only remove the task + from its current state list if it is in the Ready state as + the task's priority is going to change and there is one + Ready list per priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + there is no need to check again and the port level + reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) void vTaskEnterCritical( void ) @@ -3928,7 +4364,7 @@ TCB_t *pxTCB; } /* Terminate. */ - pcBuffer[ x ] = 0x00; + pcBuffer[ x ] = ( char ) 0x00; /* Return the new end of string. */ return &( pcBuffer[ x ] ); @@ -3937,12 +4373,12 @@ TCB_t *pxTCB; #endif /* ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */ /*-----------------------------------------------------------*/ -#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) void vTaskList( char * pcWriteBuffer ) { TaskStatus_t *pxTaskStatusArray; - volatile UBaseType_t uxArraySize, x; + UBaseType_t uxArraySize, x; char cStatus; /* @@ -3971,7 +4407,7 @@ TCB_t *pxTCB; /* Make sure the write buffer does not contain a string. */ - *pcWriteBuffer = 0x00; + *pcWriteBuffer = ( char ) 0x00; /* Take a snapshot of the number of tasks in case it changes while this function is executing. */ @@ -3980,7 +4416,7 @@ TCB_t *pxTCB; /* Allocate an array index for each task. NOTE! if configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will equate to NULL. */ - pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ if( pxTaskStatusArray != NULL ) { @@ -3992,6 +4428,9 @@ TCB_t *pxTCB; { switch( pxTaskStatusArray[ x ].eCurrentState ) { + case eRunning: cStatus = tskRUNNING_CHAR; + break; + case eReady: cStatus = tskREADY_CHAR; break; @@ -4004,9 +4443,10 @@ TCB_t *pxTCB; case eDeleted: cStatus = tskDELETED_CHAR; break; + case eInvalid: /* Fall through. */ default: /* Should not get here, but it is included to prevent static checking errors. */ - cStatus = 0x00; + cStatus = ( char ) 0x00; break; } @@ -4015,8 +4455,8 @@ TCB_t *pxTCB; pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); /* Write the rest of the string. */ - sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); - pcWriteBuffer += strlen( pcWriteBuffer ); + sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ } /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION @@ -4029,15 +4469,15 @@ TCB_t *pxTCB; } } -#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*----------------------------------------------------------*/ -#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) void vTaskGetRunTimeStats( char *pcWriteBuffer ) { TaskStatus_t *pxTaskStatusArray; - volatile UBaseType_t uxArraySize, x; + UBaseType_t uxArraySize, x; uint32_t ulTotalTime, ulStatsAsPercentage; #if( configUSE_TRACE_FACILITY != 1 ) @@ -4072,7 +4512,7 @@ TCB_t *pxTCB; */ /* Make sure the write buffer does not contain a string. */ - *pcWriteBuffer = 0x00; + *pcWriteBuffer = ( char ) 0x00; /* Take a snapshot of the number of tasks in case it changes while this function is executing. */ @@ -4081,7 +4521,7 @@ TCB_t *pxTCB; /* Allocate an array index for each task. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will equate to NULL. */ - pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ if( pxTaskStatusArray != NULL ) { @@ -4092,7 +4532,7 @@ TCB_t *pxTCB; ulTotalTime /= 100UL; /* Avoid divide by zero errors. */ - if( ulTotalTime > 0 ) + if( ulTotalTime > 0UL ) { /* Create a human readable table from the binary data. */ for( x = 0; x < uxArraySize; x++ ) @@ -4117,7 +4557,7 @@ TCB_t *pxTCB; { /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */ - sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); + sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ } #endif } @@ -4133,12 +4573,12 @@ TCB_t *pxTCB; { /* sizeof( int ) == sizeof( long ) so a smaller printf() library can be used. */ - sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); + sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ } #endif } - pcWriteBuffer += strlen( pcWriteBuffer ); + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ } } else @@ -4156,7 +4596,7 @@ TCB_t *pxTCB; } } -#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ TickType_t uxTaskResetEventItemValue( void ) @@ -4175,7 +4615,7 @@ TickType_t uxReturn; #if ( configUSE_MUTEXES == 1 ) - void *pvTaskIncrementMutexHeldCount( void ) + TaskHandle_t pvTaskIncrementMutexHeldCount( void ) { /* If xSemaphoreCreateMutex() is called before any tasks have been created then pxCurrentTCB will be NULL. */ @@ -4240,7 +4680,7 @@ TickType_t uxReturn; } else { - pxCurrentTCB->ulNotifiedValue = ulReturn - 1; + pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1; } } else @@ -4315,7 +4755,7 @@ TickType_t uxReturn; blocked state (because a notification was already pending) or the task unblocked because of a notification. Otherwise the task unblocked because of a timeout. */ - if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) { /* A notification was not received. */ xReturn = pdFALSE; @@ -4347,7 +4787,7 @@ TickType_t uxReturn; uint8_t ucOriginalNotifyState; configASSERT( xTaskToNotify ); - pxTCB = ( TCB_t * ) xTaskToNotify; + pxTCB = xTaskToNotify; taskENTER_CRITICAL(); { @@ -4390,6 +4830,14 @@ TickType_t uxReturn; /* The task is being notified without its notify value being updated. */ break; + + default: + /* Should not get here if all enums are handled. + Artificially force an assert by testing a value the + compiler can't assume is const. */ + configASSERT( pxTCB->ulNotifiedValue == ~0UL ); + + break; } traceTASK_NOTIFY(); @@ -4473,7 +4921,7 @@ TickType_t uxReturn; http://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - pxTCB = ( TCB_t * ) xTaskToNotify; + pxTCB = xTaskToNotify; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); { @@ -4515,6 +4963,13 @@ TickType_t uxReturn; /* The task is being notified without its notify value being updated. */ break; + + default: + /* Should not get here if all enums are handled. + Artificially force an assert by testing a value the + compiler can't assume is const. */ + configASSERT( pxTCB->ulNotifiedValue == ~0UL ); + break; } traceTASK_NOTIFY_FROM_ISR(); @@ -4546,13 +5001,11 @@ TickType_t uxReturn; { *pxHigherPriorityTaskWoken = pdTRUE; } - else - { - /* Mark that a yield is pending in case the user is not - using the "xHigherPriorityTaskWoken" parameter to an ISR - safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } + + /* Mark that a yield is pending in case the user is not + using the "xHigherPriorityTaskWoken" parameter to an ISR + safe FreeRTOS function. */ + xYieldPending = pdTRUE; } else { @@ -4596,7 +5049,7 @@ TickType_t uxReturn; http://www.freertos.org/RTOS-Cortex-M3-M4.html */ portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - pxTCB = ( TCB_t * ) xTaskToNotify; + pxTCB = xTaskToNotify; uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); { @@ -4636,13 +5089,11 @@ TickType_t uxReturn; { *pxHigherPriorityTaskWoken = pdTRUE; } - else - { - /* Mark that a yield is pending in case the user is not - using the "xHigherPriorityTaskWoken" parameter in an ISR - safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } + + /* Mark that a yield is pending in case the user is not + using the "xHigherPriorityTaskWoken" parameter in an ISR + safe FreeRTOS function. */ + xYieldPending = pdTRUE; } else { @@ -4654,7 +5105,6 @@ TickType_t uxReturn; } #endif /* configUSE_TASK_NOTIFICATIONS */ - /*-----------------------------------------------------------*/ #if( configUSE_TASK_NOTIFICATIONS == 1 ) @@ -4688,6 +5138,41 @@ TickType_t uxReturn; #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) + { + TCB_t *pxTCB; + uint32_t ulReturn; + + /* If null is passed in here then it is the calling task that is having + its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + taskENTER_CRITICAL(); + { + /* Return the notification as it was before the bits were cleared, + then clear the bit mask. */ + ulReturn = pxCurrentTCB->ulNotifiedValue; + pxTCB->ulNotifiedValue &= ~ulBitsToClear; + } + taskEXIT_CRITICAL(); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) + + uint32_t ulTaskGetIdleRunTimeCounter( void ) + { + return xIdleTaskHandle->ulRunTimeCounter; + } + +#endif +/*-----------------------------------------------------------*/ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) { @@ -4709,7 +5194,7 @@ const TickType_t xConstTickCount = xTickCount; { /* The current task must be in a ready list, so there is no need to check, and the port reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ } else { @@ -4800,8 +5285,26 @@ const TickType_t xConstTickCount = xTickCount; #endif /* INCLUDE_vTaskSuspend */ } +/* Code below here allows additional code to be inserted into this source file, +especially where access to file scope functions and data is needed (for example +when performing module tests). */ #ifdef FREERTOS_MODULE_TEST #include "tasks_test_access_functions.h" #endif + +#if( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) + + #include "freertos_tasks_c_additions.h" + + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + static void freertos_tasks_c_additions_init( void ) + { + FREERTOS_TASKS_C_ADDITIONS_INIT(); + } + #endif + +#endif + + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c index 44cb477e..00200b8f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c @@ -1,71 +1,29 @@ /* - FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. - All rights reserved - - VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. - - This file is part of the FreeRTOS distribution. - - FreeRTOS is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License (version 2) as published by the - Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. - - *************************************************************************** - >>! NOTE: The modification to the GPL is included to allow you to !<< - >>! distribute a combined work that includes FreeRTOS without being !<< - >>! obliged to provide the source code for proprietary components !<< - >>! outside of the FreeRTOS kernel. !<< - *************************************************************************** - - FreeRTOS 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. Full license text is available on the following - link: http://www.freertos.org/a00114.html - - *************************************************************************** - * * - * FreeRTOS provides completely free yet professionally developed, * - * robust, strictly quality controlled, supported, and cross * - * platform software that is more than just the market leader, it * - * is the industry's de facto standard. * - * * - * Help yourself get started quickly while simultaneously helping * - * to support the FreeRTOS project by purchasing a FreeRTOS * - * tutorial book, reference manual, or both: * - * http://www.FreeRTOS.org/Documentation * - * * - *************************************************************************** - - http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading - the FAQ page "My application does not run, what could be wrong?". Have you - defined configASSERT()? - - http://www.FreeRTOS.org/support - In return for receiving this top quality - embedded software for free we request you assist our global community by - participating in the support forum. - - http://www.FreeRTOS.org/training - Investing in training allows your team to - be as productive as possible as early as possible. Now you can receive - FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers - Ltd, and the world's leading authority on the world's leading RTOS. - - http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, - including FreeRTOS+Trace - an indispensable productivity tool, a DOS - compatible FAT file system, and our tiny thread aware UDP/IP stack. - - http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. - Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. - - http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High - Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS - licenses offer ticketed support, indemnification and commercial middleware. - - http://www.SafeRTOS.com - High Integrity Systems also provide a safety - engineered and independently SIL3 certified version for use in safety and - mission critical applications that require provable dependability. - - 1 tab == 4 spaces! -*/ + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ /* Standard includes. */ #include @@ -84,11 +42,11 @@ task.h is included from an application file. */ #error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. #endif -/* Lint e961 and e750 are suppressed as a MISRA exception justified because the -MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the -header files above, but not in this file, in order to generate the correct -privileged Vs unprivileged linkage and placement. */ -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e9021 !e961 !e750. */ /* This entire source file will be skipped if the application is not configured @@ -100,22 +58,29 @@ configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ /* Misc definitions. */ #define tmrNO_DELAY ( TickType_t ) 0U +/* The name assigned to the timer service task. This can be overridden by +defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configTIMER_SERVICE_TASK_NAME + #define configTIMER_SERVICE_TASK_NAME "Tmr Svc" +#endif + +/* Bit definitions used in the ucStatus member of a timer structure. */ +#define tmrSTATUS_IS_ACTIVE ( ( uint8_t ) 0x01 ) +#define tmrSTATUS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 0x02 ) +#define tmrSTATUS_IS_AUTORELOAD ( ( uint8_t ) 0x04 ) + /* The definition of the timers themselves. */ -typedef struct tmrTimerControl +typedef struct tmrTimerControl /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ - UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */ void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ #if( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ #endif - - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */ - #endif + uint8_t ucStatus; /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ } xTIMER; /* The old xTIMER name is maintained above then typedefed to the new Timer_t @@ -158,22 +123,25 @@ typedef struct tmrTimerQueueMessage } u; } DaemonTaskMessage_t; -/*lint -e956 A manual analysis and inspection has been used to determine which -static variables must be declared volatile. */ +/*lint -save -e956 A manual analysis and inspection has been used to determine +which static variables must be declared volatile. */ /* The list in which active timers are stored. Timers are referenced in expire time order, with the nearest expiry time at the front of the list. Only the -timer service task is allowed to access these lists. */ +timer service task is allowed to access these lists. +xActiveTimerList1 and xActiveTimerList2 could be at function scope but that +breaks some kernel aware debuggers, and debuggers that reply on removing the +static qualifier. */ PRIVILEGED_DATA static List_t xActiveTimerList1; PRIVILEGED_DATA static List_t xActiveTimerList2; PRIVILEGED_DATA static List_t *pxCurrentTimerList; PRIVILEGED_DATA static List_t *pxOverflowTimerList; /* A queue that is used to send commands to the timer service task. */ -PRIVILEGED_INITIALIZED_DATA static QueueHandle_t xTimerQueue = NULL; -PRIVILEGED_INITIALIZED_DATA static TaskHandle_t xTimerTaskHandle = NULL; +PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; +PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; -/*lint +e956 */ +/*lint -restore */ /*-----------------------------------------------------------*/ @@ -191,44 +159,44 @@ PRIVILEGED_INITIALIZED_DATA static TaskHandle_t xTimerTaskHandle = NULL; * Initialise the infrastructure used by the timer service task if it has not * been initialised already. */ -PRIVILEGED_FUNCTION static void prvCheckForValidListAndQueue( void ); +static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; /* * The timer service task (daemon). Timer functionality is controlled by this * task. Other tasks communicate with the timer service task using the * xTimerQueue queue. */ -PRIVILEGED_FUNCTION static void prvTimerTask( void *pvParameters ); +static portTASK_FUNCTION_PROTO( prvTimerTask, pvParameters ) PRIVILEGED_FUNCTION; /* * Called by the timer service task to interpret and process a command it * received on the timer queue. */ -PRIVILEGED_FUNCTION static void prvProcessReceivedCommands( void ); +static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; /* * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, * depending on if the expire time causes a timer counter overflow. */ -PRIVILEGED_FUNCTION static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ); +static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; /* * An active timer has reached its expire time. Reload the timer if it is an - * auto reload timer, then call its callback. + * auto-reload timer, then call its callback. */ -PRIVILEGED_FUNCTION static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ); +static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; /* * The tick count has overflowed. Switch the timer lists after ensuring the * current timer list does not still reference some timers. */ -PRIVILEGED_FUNCTION static void prvSwitchTimerLists( void ); +static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; /* * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE * if a tick count overflow occurred since prvSampleTimeNow() was last called. */ -PRIVILEGED_FUNCTION static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ); +static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; /* * If the timer list contains any active timers then return the expire time of @@ -236,24 +204,24 @@ PRIVILEGED_FUNCTION static TickType_t prvSampleTimeNow( BaseType_t * const pxTim * timer list does not contain any timers then return 0 and set *pxListWasEmpty * to pdTRUE. */ -PRIVILEGED_FUNCTION static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ); +static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; /* * If a timer has expired, process it. Otherwise, block the timer service task * until either a timer does expire or a command is received. */ -PRIVILEGED_FUNCTION static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ); +static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; /* * Called after a Timer_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -PRIVILEGED_FUNCTION static void prvInitialiseNewTimer( const char * const pcTimerName, +static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - Timer_t *pxNewTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + Timer_t *pxNewTimer ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ BaseType_t xTimerCreateTimerTask( void ) @@ -276,7 +244,7 @@ BaseType_t xReturn = pdFAIL; vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, - "Tmr Svc", + configTIMER_SERVICE_TASK_NAME, ulTimerTaskStackSize, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, @@ -291,7 +259,7 @@ BaseType_t xReturn = pdFAIL; #else { xReturn = xTaskCreate( prvTimerTask, - "Tmr Svc", + configTIMER_SERVICE_TASK_NAME, configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, @@ -311,44 +279,39 @@ BaseType_t xReturn = pdFAIL; #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreate( const char * const pcTimerName, + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + TimerCallbackFunction_t pxCallbackFunction ) { Timer_t *pxNewTimer; - pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); + pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of Timer_t is always a pointer to the timer's mame. */ if( pxNewTimer != NULL ) { + /* Status is thus far zero as the timer is not created statically + and has not been started. The auto-reload bit may get set in + prvInitialiseNewTimer. */ + pxNewTimer->ucStatus = 0x00; prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); - - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - /* Timers can be created statically or dynamically, so note this - timer was created dynamically in case the timer is later - deleted. */ - pxNewTimer->ucStaticallyAllocated = pdFALSE; - } - #endif /* configSUPPORT_STATIC_ALLOCATION */ } return pxNewTimer; } -#endif /* configSUPPORT_STATIC_ALLOCATION */ +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + StaticTimer_t *pxTimerBuffer ) { Timer_t *pxNewTimer; @@ -356,27 +319,25 @@ BaseType_t xReturn = pdFAIL; { /* Sanity check that the size of the structure used to declare a variable of type StaticTimer_t equals the size of the real timer - structures. */ + structure. */ volatile size_t xSize = sizeof( StaticTimer_t ); configASSERT( xSize == sizeof( Timer_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ } #endif /* configASSERT_DEFINED */ /* A pointer to a StaticTimer_t structure MUST be provided, use it. */ configASSERT( pxTimerBuffer ); - pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 !e9087 StaticTimer_t is a pointer to a Timer_t, so guaranteed to be aligned and sized correctly (checked by an assert()), so this is safe. */ if( pxNewTimer != NULL ) { - prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + /* Timers can be created statically or dynamically so note this + timer was created statically in case it is later deleted. The + auto-reload bit may get set in prvInitialiseNewTimer(). */ + pxNewTimer->ucStatus = tmrSTATUS_IS_STATICALLY_ALLOCATED; - #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - /* Timers can be created statically or dynamically so note this - timer was created statically in case it is later deleted. */ - pxNewTimer->ucStaticallyAllocated = pdTRUE; - } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); } return pxNewTimer; @@ -385,12 +346,12 @@ BaseType_t xReturn = pdFAIL; #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -static void prvInitialiseNewTimer( const char * const pcTimerName, +static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, - Timer_t *pxNewTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + Timer_t *pxNewTimer ) { /* 0 is not a valid value for xTimerPeriodInTicks. */ configASSERT( ( xTimerPeriodInTicks > 0 ) ); @@ -405,10 +366,13 @@ static void prvInitialiseNewTimer( const char * const pcTimerName, parameters. */ pxNewTimer->pcTimerName = pcTimerName; pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; - pxNewTimer->uxAutoReload = uxAutoReload; pxNewTimer->pvTimerID = pvTimerID; pxNewTimer->pxCallbackFunction = pxCallbackFunction; vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + if( uxAutoReload != pdFALSE ) + { + pxNewTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } traceTIMER_CREATE( pxNewTimer ); } } @@ -428,7 +392,7 @@ DaemonTaskMessage_t xMessage; /* Send a command to the timer service task to start the xTimer timer. */ xMessage.xMessageID = xCommandID; xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; - xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; + xMessage.u.xTimerParameters.pxTimer = xTimer; if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) { @@ -468,16 +432,61 @@ TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) { -Timer_t *pxTimer = ( Timer_t * ) xTimer; +Timer_t *pxTimer = xTimer; configASSERT( xTimer ); return pxTimer->xTimerPeriodInTicks; } /*-----------------------------------------------------------*/ +void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) +{ +Timer_t * pxTimer = xTimer; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( uxAutoReload != pdFALSE ) + { + pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD; + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) +{ +Timer_t * pxTimer = xTimer; +UBaseType_t uxReturn; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) == 0 ) + { + /* Not an auto-reload timer. */ + uxReturn = ( UBaseType_t ) pdFALSE; + } + else + { + /* Is an auto-reload timer. */ + uxReturn = ( UBaseType_t ) pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) { -Timer_t * pxTimer = ( Timer_t * ) xTimer; +Timer_t * pxTimer = xTimer; TickType_t xReturn; configASSERT( xTimer ); @@ -488,7 +497,7 @@ TickType_t xReturn; const char * pcTimerGetName( TimerHandle_t xTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { -Timer_t *pxTimer = ( Timer_t * ) xTimer; +Timer_t *pxTimer = xTimer; configASSERT( xTimer ); return pxTimer->pcTimerName; @@ -498,16 +507,16 @@ Timer_t *pxTimer = ( Timer_t * ) xTimer; static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) { BaseType_t xResult; -Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); +Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Remove the timer from the list of active timers. A check has already been performed to ensure the list is not empty. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); traceTIMER_EXPIRED( pxTimer ); - /* If the timer is an auto reload timer then calculate the next + /* If the timer is an auto-reload timer then calculate the next expiry time and re-insert the timer in the list of active timers. */ - if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { /* The timer is inserted into a list using a time relative to anything other than the current time. It will therefore be inserted into the @@ -527,6 +536,7 @@ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTi } else { + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; mtCOVERAGE_TEST_MARKER(); } @@ -535,7 +545,7 @@ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTi } /*-----------------------------------------------------------*/ -static void prvTimerTask( void *pvParameters ) +static portTASK_FUNCTION( prvTimerTask, pvParameters ) { TickType_t xNextExpireTime; BaseType_t xListWasEmpty; @@ -660,7 +670,7 @@ TickType_t xNextExpireTime; static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) { TickType_t xTimeNow; -PRIVILEGED_INITIALIZED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ +PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ xTimeNow = xTaskGetTickCount(); @@ -760,7 +770,7 @@ TickType_t xTimeNow; software timer. */ pxTimer = xMessage.u.xTimerParameters.pxTimer; - if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) + if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) /*lint !e961. The cast is only redundant when NULL is passed into the macro. */ { /* The timer is in a list, remove it. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); @@ -783,11 +793,12 @@ TickType_t xTimeNow; switch( xMessage.xMessageID ) { case tmrCOMMAND_START : - case tmrCOMMAND_START_FROM_ISR : - case tmrCOMMAND_RESET : - case tmrCOMMAND_RESET_FROM_ISR : + case tmrCOMMAND_START_FROM_ISR : + case tmrCOMMAND_RESET : + case tmrCOMMAND_RESET_FROM_ISR : case tmrCOMMAND_START_DONT_TRACE : /* Start or restart a timer. */ + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ) { /* The timer expired before it was added to the active @@ -795,7 +806,7 @@ TickType_t xTimeNow; pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); traceTIMER_EXPIRED( pxTimer ); - if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); configASSERT( xResult ); @@ -814,12 +825,13 @@ TickType_t xTimeNow; case tmrCOMMAND_STOP : case tmrCOMMAND_STOP_FROM_ISR : - /* The timer has already been removed from the active list. - There is nothing to do here. */ + /* The timer has already been removed from the active list. */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; break; case tmrCOMMAND_CHANGE_PERIOD : case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR : + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); @@ -833,29 +845,28 @@ TickType_t xTimeNow; break; case tmrCOMMAND_DELETE : - /* The timer has already been removed from the active list, - just free up the memory if the memory was dynamically - allocated. */ - #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) { - /* The timer can only have been allocated dynamically - - free it again. */ - vPortFree( pxTimer ); - } - #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) - { - /* The timer could have been allocated statically or - dynamically, so check before attempting to free the - memory. */ - if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + /* The timer has already been removed from the active list, + just free up the memory if the memory was dynamically + allocated. */ + if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ) { vPortFree( pxTimer ); } else { - mtCOVERAGE_TEST_MARKER(); + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; } } + #else + { + /* If dynamic allocation is not enabled, the memory + could not have been dynamically allocated. So there is + no need to free the memory - just mark the timer as + "not active". */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ break; @@ -884,7 +895,7 @@ BaseType_t xResult; xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); /* Remove the timer from the list. */ - pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); + pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); traceTIMER_EXPIRED( pxTimer ); @@ -893,7 +904,7 @@ BaseType_t xResult; have not yet been switched. */ pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); - if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { /* Calculate the reload value, and if the reload value results in the timer going into the same timer list then it has already expired @@ -945,10 +956,10 @@ static void prvCheckForValidListAndQueue( void ) { /* The timer queue is allocated statically in case configSUPPORT_DYNAMIC_ALLOCATION is 0. */ - static StaticQueue_t xStaticTimerQueue; - static uint8_t ucStaticTimerQueueStorage[ configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; + static StaticQueue_t xStaticTimerQueue; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ + static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ - xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); + xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); } #else { @@ -980,28 +991,32 @@ static void prvCheckForValidListAndQueue( void ) BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) { -BaseType_t xTimerIsInActiveList; -Timer_t *pxTimer = ( Timer_t * ) xTimer; +BaseType_t xReturn; +Timer_t *pxTimer = xTimer; configASSERT( xTimer ); /* Is the timer in the list of active timers? */ taskENTER_CRITICAL(); { - /* Checking to see if it is in the NULL list in effect checks to see if - it is referenced from either the current or the overflow timer lists in - one go, but the logic has to be reversed, hence the '!'. */ - xTimerIsInActiveList = ( BaseType_t ) !( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) ); + if( ( pxTimer->ucStatus & tmrSTATUS_IS_ACTIVE ) == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } } taskEXIT_CRITICAL(); - return xTimerIsInActiveList; + return xReturn; } /*lint !e818 Can't be pointer to const due to the typedef. */ /*-----------------------------------------------------------*/ void *pvTimerGetTimerID( const TimerHandle_t xTimer ) { -Timer_t * const pxTimer = ( Timer_t * ) xTimer; +Timer_t * const pxTimer = xTimer; void *pvReturn; configASSERT( xTimer ); @@ -1018,7 +1033,7 @@ void *pvReturn; void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) { -Timer_t * const pxTimer = ( Timer_t * ) xTimer; +Timer_t * const pxTimer = xTimer; configASSERT( xTimer ); @@ -1083,6 +1098,26 @@ Timer_t * const pxTimer = ( Timer_t * ) xTimer; #endif /* INCLUDE_xTimerPendFunctionCall */ /*-----------------------------------------------------------*/ +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) + { + return ( ( Timer_t * ) xTimer )->uxTimerNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) + { + ( ( Timer_t * ) xTimer )->uxTimerNumber = uxTimerNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + /* This entire source file will be skipped if the application is not configured to include software timer functionality. If you want to include software timer functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ From f6f0cf9cd3874c1feaf931e427a62b14fa0e8c67 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 2 Sep 2020 23:12:05 -0400 Subject: [PATCH 011/124] Compile with -flto --- .../FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c | 4 +++- Firmware/Tupfile.lua | 6 +++--- Firmware/communication/communication.cpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c index d5feca9e..cc8bbe09 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c @@ -302,6 +302,7 @@ static void prvPortStartFirstTask( void ) " isb \n" " svc 0 \n" /* System call to start first task. */ " nop \n" + " .ltorg \n" ); } /*-----------------------------------------------------------*/ @@ -695,7 +696,8 @@ static void vPortEnableVFP( void ) " \n" " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ " str r1, [r0] \n" - " bx r14 " + " bx r14 \n" + " .ltorg " ); } /*-----------------------------------------------------------*/ diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 040db512..77ce8389 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -136,16 +136,16 @@ FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections -- linker flags LDFLAGS += board.ldflags -LDFLAGS += '-lc -lm -lnosys' -- libs +LDFLAGS += '-flto -lc -lm -lnosys' -- libs LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' -LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' +LDFLAGS += '-Wl,--undefined=uxTopUsedPriority ' -- debug build if tup.getconfig("DEBUG") == "true" then FLAGS += '-g -gdwarf-2' OPT += '-Og' else - OPT += '-O2' + OPT += '-O2 -flto' end -- common flags for ASM, C and C++ diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index faac6f59..7845fd6f 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -57,7 +57,7 @@ void init_communication(void) { } extern "C" { -int _write(int file, const char* data, int len); +int _write(int file, const char* data, int len) __attribute__((used)); } // @brief This is what printf calls internally From 72f7d51b0740ec31e179872587c0b4b48bc1d399 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 3 Sep 2020 00:11:00 -0400 Subject: [PATCH 012/124] Responding on USB --- .../Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c | 3 ++- Firmware/Board/v3/Src/stm32f4xx_it.c | 1 + Firmware/Tupfile.lua | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c index 5c68c6a2..32c83890 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c @@ -369,7 +369,7 @@ typedef tskTCB TCB_t; /*lint -e956 A manual analysis and inspection has been used to determine which static variables must be declared volatile. */ -PRIVILEGED_INITIALIZED_DATA TCB_t * volatile pxCurrentTCB = NULL; +PRIVILEGED_INITIALIZED_DATA TCB_t * volatile pxCurrentTCB __attribute__((used)) = NULL; /* Lists for ready and blocked tasks. --------------------*/ PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ @@ -2758,6 +2758,7 @@ BaseType_t xSwitchRequired = pdFALSE; #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ +__attribute__((used)) void vTaskSwitchContext( void ) { if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index 1e69bcb9..032a25a0 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -74,6 +74,7 @@ void NMI_Handler(void) /* USER CODE END NonMaskableInt_IRQn 1 */ } +__attribute__((used)) void get_regs(void** stack_ptr) { void* volatile r0 __attribute__((unused)) = stack_ptr[0]; void* volatile r1 __attribute__((unused)) = stack_ptr[1]; diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 77ce8389..ff0cf41e 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -158,7 +158,7 @@ toolchain = GCCToolchain('arm-none-eabi-', 'build', FLAGS, LDFLAGS) -- Load list of source files Makefile that was autogenerated by CubeMX vars = parse_makefile_vars(board.dir..'/Makefile') -all_stm_sources = (vars['C_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['ASM_SOURCES'] or '') +all_stm_sources = (vars['ASM_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['C_SOURCES'] or '') for src in string.gmatch(all_stm_sources, "%S+") do stm_sources += board.dir..'/'..src end From d628fdb35caf810073b2bf6f57ab79b08090d1dd Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 3 Sep 2020 00:18:26 -0400 Subject: [PATCH 013/124] Minor cleanup, add comment to tupfile --- .../Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c | 2 +- Firmware/Tupfile.lua | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c index 32c83890..220b867b 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c +++ b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c @@ -369,7 +369,7 @@ typedef tskTCB TCB_t; /*lint -e956 A manual analysis and inspection has been used to determine which static variables must be declared volatile. */ -PRIVILEGED_INITIALIZED_DATA TCB_t * volatile pxCurrentTCB __attribute__((used)) = NULL; +__attribute__((used)) PRIVILEGED_INITIALIZED_DATA TCB_t * volatile pxCurrentTCB = NULL; /* Lists for ready and blocked tasks. --------------------*/ PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index ff0cf41e..bd59d526 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -158,6 +158,9 @@ toolchain = GCCToolchain('arm-none-eabi-', 'build', FLAGS, LDFLAGS) -- Load list of source files Makefile that was autogenerated by CubeMX vars = parse_makefile_vars(board.dir..'/Makefile') + +-- ASM sources must precede C sources due to LTO removing weak symbols which appear after strong symbols +-- in the call to the linker: https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966 all_stm_sources = (vars['ASM_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['C_SOURCES'] or '') for src in string.gmatch(all_stm_sources, "%S+") do stm_sources += board.dir..'/'..src From cfb5523001ebe1482375616c316bf2c5fff5ca17 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 3 Sep 2020 00:33:31 -0400 Subject: [PATCH 014/124] Fix dump_errors on this branch(?) --- tools/odrive/utils.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 505bba79..329cda81 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -78,18 +78,20 @@ def dump_errors(odrv, clear=False): # Flatten axis and submodules # (name, remote_obj, errorcode) module_decode_map = [ - ('axis', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), - ('motor', axis.motor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), - ('fet_thermistor', axis.fet_thermistor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('motor_thermistor', axis.motor_thermistor, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), - ('encoder', axis.encoder, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), - ('controller', axis.controller, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), + (name, odrv, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("AXIS_ERROR_")}), + ('motor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), + ('fet_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('motor_thermistor', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('encoder', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), + ('controller', axis, {k: v for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), ] # Module error decode for name, remote_obj, errorcodes in module_decode_map: - prefix = ' '*2 + name + ": " - if (remote_obj.error != 0): + prefix = ' '*2 + name.strip('0123456789') + ": " + if not hasattr(remote_obj, name): + print(prefix + _VT100Colors['yellow'] + "not found" + _VT100Colors['default']) + elif getattr(remote_obj, name).error != 0: foundError = False print(prefix + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default']) errorcodes_dict = {val: name for name, val in errorcodes.items() if 'ERROR_' in name} From a530de4fa9c4ea52257e41ab4c5665553fb00f75 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 3 Sep 2020 01:15:07 -0400 Subject: [PATCH 015/124] Add tup.config var for LTO and enable by default --- Firmware/Tupfile.lua | 9 +++++++-- Firmware/tup.config.default | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index bd59d526..cd959137 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -138,14 +138,19 @@ FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections LDFLAGS += board.ldflags LDFLAGS += '-flto -lc -lm -lnosys' -- libs LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' -LDFLAGS += '-Wl,--undefined=uxTopUsedPriority ' +LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' -- debug build if tup.getconfig("DEBUG") == "true" then FLAGS += '-g -gdwarf-2' OPT += '-Og' else - OPT += '-O2 -flto' + OPT += '-O2' +end + +if tup.getconfig("USE_LTO") == "true" then + OPT += '-flto' + LDFLAGS += '-flto' end -- common flags for ASM, C and C++ diff --git a/Firmware/tup.config.default b/Firmware/tup.config.default index b2d49106..e348cedb 100644 --- a/Firmware/tup.config.default +++ b/Firmware/tup.config.default @@ -5,6 +5,7 @@ CONFIG_USB_PROTOCOL=native CONFIG_UART_PROTOCOL=ascii CONFIG_DEBUG=false CONFIG_DOCTEST=false +CONFIG_USE_LTO=true # Uncomment this to error on compilation warnings #CONFIG_STRICT=true From 54636c764f423d3acb049a489329df2a2b90e992 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 3 Sep 2020 20:03:10 -0700 Subject: [PATCH 016/124] implement faster fmodf related functions --- Firmware/MotorControl/controller.cpp | 2 +- Firmware/MotorControl/encoder.cpp | 4 ++-- Firmware/MotorControl/utils.hpp | 30 +++++++++++++--------------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 1524dce6..ecea3c9c 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -233,7 +233,7 @@ bool Controller::update(float* torque_setpoint_output) { pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap_src_); // Circular delta pos_err = pos_setpoint_ - *pos_estimate_circular; - pos_err = wrap_pm(pos_err, 0.5f * *pos_wrap_src_); + pos_err = wrap_pm(pos_err, *pos_wrap_src_); } else { if(!pos_estimate_linear) { set_error(ERROR_INVALID_ESTIMATE); diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index d2ce992b..f0c65445 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -546,7 +546,7 @@ bool Encoder::update() { // discrete phase detector float delta_pos_counts = (float)(shadow_count_ - (int32_t)std::floor(pos_estimate_counts_)); float delta_pos_cpr_counts = (float)(count_in_cpr_ - (int32_t)std::floor(pos_cpr_counts_)); - delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, 0.5f * (float)(config_.cpr)); + delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, (float)(config_.cpr)); // pll feedback pos_estimate_counts_ += current_meas_period * pll_kp_ * delta_pos_counts; pos_cpr_counts_ += current_meas_period * pll_kp_ * delta_pos_cpr_counts; @@ -561,7 +561,7 @@ bool Encoder::update() { // Outputs from Encoder for Controller pos_estimate_ = pos_estimate_counts_ / (float)config_.cpr; vel_estimate_ = vel_estimate_counts_ / (float)config_.cpr; - pos_circular_ += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 0.5f); + pos_circular_ += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 1.0f); pos_circular_ = fmodf_pos(pos_circular_, axis_->controller_.config_.circular_setpoint_range); //// run encoder count interpolation diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 49f9434d..70bd73be 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -84,25 +84,23 @@ static const float one_by_sqrt3 = 0.57735026919f; static const float two_by_sqrt3 = 1.15470053838f; static const float sqrt3_by_2 = 0.86602540378f; -// like fmodf, but always positive +// Result depends on rounding mode +// With default rounding (round to nearest), +// the result will be in range -y/2 to y/2 +static inline float wrap_pm(float x, float y) { + int q = (int)(x/y); + return x - (float)q * y; +} + +// Same as fmodf but result is positive and y must be positive static inline float fmodf_pos(float x, float y) { - float out = fmodf(x, y); - if (out < 0.0f) - out += y; - return out; + float res = wrap_pm(x, y); + if (res < 0) res += y; + return res; } -/** - * @brief Similar to modulo operator, except that the output range is centered - * around zero. - * The returned value is always in the range [-pm_range, pm_range). - */ -static inline float wrap_pm(float x, float pm_range) { - return fmodf_pos(x + pm_range, 2.0f * pm_range) - pm_range; -} - -static inline float wrap_pm_pi(float theta) { - return wrap_pm(theta, M_PI); +static inline float wrap_pm_pi(float x) { + return wrap_pm(x, 2*M_PI); } // Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta From 805b308a869cbb0a892d52b250d87176f2af529c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 14:27:22 -0700 Subject: [PATCH 017/124] fix rounding mode, use vcvtr --- Firmware/.vscode/c_cpp_properties.json | 6 ++++-- Firmware/MotorControl/utils.hpp | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 1d5ab7cf..be9f826f 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -6,12 +6,12 @@ "${workspaceFolder}/**" ], "defines": [ + "__arm__", "STM32F405xx", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", "HW_VERSION_MINOR=6", "HW_VERSION_VOLTAGE=56", - "USB_PROTOCOL_NATIVE", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" @@ -27,6 +27,7 @@ "${workspaceFolder}/**" ], "defines": [ + "__arm__", "STM32F405xx", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", @@ -47,11 +48,12 @@ "${workspaceFolder}/**" ], "defines": [ + "__arm__", "STM32F405xx", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", "HW_VERSION_MINOR=4", - "HW_VERSION_VOLTAGE=24", + "HW_VERSION_VOLTAGE=56", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 70bd73be..42e03c8f 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -84,12 +84,24 @@ static const float one_by_sqrt3 = 0.57735026919f; static const float two_by_sqrt3 = 1.15470053838f; static const float sqrt3_by_2 = 0.86602540378f; -// Result depends on rounding mode -// With default rounding (round to nearest), +// Round to integer +// Default rounding mode: round to nearest +static inline int round_int(float x) { +#ifdef __arm__ + int res; + asm("vcvtr.s32.f32 %[res], %[x]" : [res] "=X" (res) : [x] "w" (x)); + return res; +#else + return (int)rint(x); +#endif +} + +// Wrap value to range. +// With default rounding mode (round to nearest), // the result will be in range -y/2 to y/2 static inline float wrap_pm(float x, float y) { - int q = (int)(x/y); - return x - (float)q * y; + int intval = round_int(x/y); + return x - (float)intval * y; } // Same as fmodf but result is positive and y must be positive From fc726dfa3615bb2935965fe12552637be9447bbc Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 16:09:17 -0700 Subject: [PATCH 018/124] use nearbyint on VFPv5 as it is single instruction --- Firmware/.vscode/c_cpp_properties.json | 3 +++ Firmware/MotorControl/utils.hpp | 10 +++++++--- Firmware/Tests/test_trap_traj.cpp | 2 +- Firmware/Tupfile.lua | 2 +- Firmware/fibre/cpp/include/fibre/protocol.hpp | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index be9f826f..3a82cf12 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -8,6 +8,7 @@ "defines": [ "__arm__", "STM32F405xx", + "FPU_FPV4", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", "HW_VERSION_MINOR=6", @@ -29,6 +30,7 @@ "defines": [ "__arm__", "STM32F405xx", + "FPU_FPV4", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", "HW_VERSION_MINOR=6", @@ -50,6 +52,7 @@ "defines": [ "__arm__", "STM32F405xx", + "FPU_FPV4", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", "HW_VERSION_MINOR=4", diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 42e03c8f..e70a9ccc 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -92,7 +92,7 @@ static inline int round_int(float x) { asm("vcvtr.s32.f32 %[res], %[x]" : [res] "=X" (res) : [x] "w" (x)); return res; #else - return (int)rint(x); + return (int)nearbyint(x); #endif } @@ -100,8 +100,12 @@ static inline int round_int(float x) { // With default rounding mode (round to nearest), // the result will be in range -y/2 to y/2 static inline float wrap_pm(float x, float y) { - int intval = round_int(x/y); - return x - (float)intval * y; +#ifdef FPU_FPV4 + float intval = (float)round_int(x/y); +#else + float intval = nearbyint(x/y); +#endif + return x - intval * y; } // Same as fmodf but result is positive and y must be positive diff --git a/Firmware/Tests/test_trap_traj.cpp b/Firmware/Tests/test_trap_traj.cpp index b5d30014..d95328dd 100644 --- a/Firmware/Tests/test_trap_traj.cpp +++ b/Firmware/Tests/test_trap_traj.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 040db512..d2cf3cbf 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -35,7 +35,7 @@ tup.frule{ board_v3 = { dir = 'Board/v3', sources = {'Drivers/DRV8301/drv8301.cpp', 'Board/v3/board.cpp'}, - flags = {'-DSTM32F405xx', '-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'}, + flags = {'-DSTM32F405xx', '-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-DFPU_FPV4'}, ldflags = {'-TBoard/v3/STM32F405RGTx_FLASH.ld', '-LBoard/v3/Drivers/CMSIS/Lib', '-larm_cortexM4lf_math', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'} } diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index 2a01e220..5d529c39 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -10,7 +10,7 @@ see protocol.md for the protocol specification #include #include -#include +#include //#include #include #include From 711111e6fb76594fbebf3cb641ea7cd837b28f00 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 18:23:33 -0700 Subject: [PATCH 019/124] change fmaxf to std max --- Firmware/MotorControl/low_level.cpp | 2 +- Firmware/MotorControl/motor.cpp | 2 +- Firmware/MotorControl/trapTraj.cpp | 2 +- Firmware/Tests/test_trap_traj.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index a40d2ac6..af258c29 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -494,7 +494,7 @@ void update_brake_current() { float brake_duty = brake_current * odrv.config_.brake_resistance / vbus_voltage; if (odrv.config_.enable_dc_bus_overvoltage_ramp && (odrv.config_.brake_resistance > 0.0f) && (odrv.config_.dc_bus_overvoltage_ramp_start < odrv.config_.dc_bus_overvoltage_ramp_end)) { - brake_duty += std::fmax((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); + brake_duty += std::max((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); } if (std::isnan(brake_duty)) { diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 9bdbaa75..0b0a2a31 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -416,7 +416,7 @@ bool Motor::update(float torque_setpoint, float phase, float phase_vel) { phase_vel *= config_.direction; if (config_.motor_type == MOTOR_TYPE_ACIM) { - current_setpoint = torque_setpoint / (config_.torque_constant * fmax(current_control_.acim_rotor_flux, config_.acim_gain_min_flux)); + current_setpoint = torque_setpoint / (config_.torque_constant * std::max(current_control_.acim_rotor_flux, config_.acim_gain_min_flux)); } else { current_setpoint = torque_setpoint / config_.torque_constant; diff --git a/Firmware/MotorControl/trapTraj.cpp b/Firmware/MotorControl/trapTraj.cpp index b3afd946..cb271fcd 100644 --- a/Firmware/MotorControl/trapTraj.cpp +++ b/Firmware/MotorControl/trapTraj.cpp @@ -42,7 +42,7 @@ bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, // Are we displacing enough to reach cruising speed? if (s*dX < s*dXmin) { // Short move (triangle profile) - Vr_ = s * sqrtf(std::fmax((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); + Vr_ = s * sqrtf(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_); Td_ = std::max(0.0f, -Vr_ / Dr_); Tv_ = 0.0f; diff --git a/Firmware/Tests/test_trap_traj.cpp b/Firmware/Tests/test_trap_traj.cpp index d95328dd..90d8275f 100644 --- a/Firmware/Tests/test_trap_traj.cpp +++ b/Firmware/Tests/test_trap_traj.cpp @@ -85,7 +85,7 @@ bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, // Are we displacing enough to reach cruising speed? if (s*dX < s*dXmin) { // Short move (triangle profile) - Vr_ = s * sqrtf(std::fmax((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); + Vr_ = s * sqrtf(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); //Vr_ = s * sqrtf((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_)); Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_); Td_ = std::max(0.0f, -Vr_ / Dr_); From fb7d1d2961bba82e4ede5f32a07b5ca0b4b54967 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 18:27:14 -0700 Subject: [PATCH 020/124] change fabsf to std abs --- Firmware/MotorControl/controller.cpp | 2 +- Firmware/MotorControl/motor.cpp | 4 ++-- Firmware/MotorControl/utils.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index ecea3c9c..88906cf5 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -275,7 +275,7 @@ bool Controller::update(float* torque_setpoint_output) { if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { float effective_flux = axis_->motor_.current_control_.acim_rotor_flux; float minflux = axis_->motor_.config_.acim_gain_min_flux; - if (fabsf(effective_flux) < minflux) + if (std::abs(effective_flux) < minflux) effective_flux = std::copysignf(minflux, effective_flux); vel_gain /= effective_flux; vel_integrator_gain /= effective_flux; diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 0b0a2a31..b148ea7c 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -434,7 +434,7 @@ bool Motor::update(float torque_setpoint, float phase, float phase_vel) { // So we elect to write it as if the effect is immediate, to have cleaner code if (config_.acim_autoflux_enable) { - float abs_iq = fabsf(iq); + float abs_iq = std::abs(iq); float gain = abs_iq > id ? config_.acim_autoflux_attack_gain : config_.acim_autoflux_decay_gain; id += gain * (abs_iq - id) * current_meas_period; id = std::clamp(id, config_.acim_autoflux_min_Id, ilim); @@ -446,7 +446,7 @@ bool Motor::update(float torque_setpoint, float phase, float phase_vel) { current_control_.acim_rotor_flux += dflux_by_dt * current_meas_period; float slip_velocity = config_.acim_slip_velocity * (iq / current_control_.acim_rotor_flux); // Check for issues with small denominator. Polarity of check to catch NaN too - bool acceptable_vel = fabsf(slip_velocity) <= 0.1f * (float)current_meas_hz; + bool acceptable_vel = std::abs(slip_velocity) <= 0.1f * (float)current_meas_hz; if (!acceptable_vel) slip_velocity = 0.0f; phase_vel += slip_velocity; diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index f4dc212d..3bf30baf 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -124,8 +124,8 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { // based on https://math.stackexchange.com/a/1105038/81278 float fast_atan2(float y, float x) { // a := min (|x|, |y|) / max (|x|, |y|) - float abs_y = fabsf(y); - float abs_x = fabsf(x); + float abs_y = std::abs(y); + float abs_x = std::abs(x); // inject FLT_MIN in denominator to avoid division by zero float a = MACRO_MIN(abs_x, abs_y) / (MACRO_MAX(abs_x, abs_y) + FLT_MIN); // s := a * a From 412329d71a6eb7a42a93f804e783be5361f9cc62 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 19:46:12 -0700 Subject: [PATCH 021/124] change to cmath by default --- Firmware/Drivers/DRV8301/drv8301.cpp | 2 +- Firmware/MotorControl/low_level.cpp | 2 +- Firmware/MotorControl/trapTraj.cpp | 2 +- Firmware/MotorControl/utils.cpp | 2 +- Firmware/MotorControl/utils.hpp | 2 +- Firmware/Tests/test_trap_traj.cpp | 2 +- Firmware/fibre/cpp/include/fibre/protocol.hpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Firmware/Drivers/DRV8301/drv8301.cpp b/Firmware/Drivers/DRV8301/drv8301.cpp index e2fb20c2..3e1cbf91 100644 --- a/Firmware/Drivers/DRV8301/drv8301.cpp +++ b/Firmware/Drivers/DRV8301/drv8301.cpp @@ -41,7 +41,7 @@ #include "utils.hpp" #include "cmsis_os.h" -#include +#include #include #include diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index af258c29..ea6b879b 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include diff --git a/Firmware/MotorControl/trapTraj.cpp b/Firmware/MotorControl/trapTraj.cpp index cb271fcd..6bc73e18 100644 --- a/Firmware/MotorControl/trapTraj.cpp +++ b/Firmware/MotorControl/trapTraj.cpp @@ -1,4 +1,4 @@ -#include +#include #include "odrive_main.h" #include "utils.hpp" diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 3bf30baf..313b5da0 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include #include diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index e70a9ccc..8c9143fa 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -3,7 +3,7 @@ #define __UTILS_H #include -#include +#include /** * @brief Flash size register address diff --git a/Firmware/Tests/test_trap_traj.cpp b/Firmware/Tests/test_trap_traj.cpp index 90d8275f..8d5399ff 100644 --- a/Firmware/Tests/test_trap_traj.cpp +++ b/Firmware/Tests/test_trap_traj.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index 5d529c39..2a01e220 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -10,7 +10,7 @@ see protocol.md for the protocol specification #include #include -#include +#include //#include #include #include From ec63f956f98efe7087d4dbd423de28d8c6f550e3 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 20:17:35 -0700 Subject: [PATCH 022/124] no need for special fma function, the compiler knows --- Firmware/MotorControl/utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 313b5da0..d518fa56 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -151,7 +151,7 @@ float fast_atan2(float y, float x) { float horner_fma(float x, const float *coeffs, size_t count) { float result = 0.0f; for (size_t idx = 0; idx < count; ++idx) - result = fmaf(result, x, coeffs[idx]); + result = (result * x) + coeffs[idx]; return result; } From 600832f8cff4394ecec00899d791684e7fb0e957 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 4 Sep 2020 20:31:58 -0700 Subject: [PATCH 023/124] drop flag fno-finite-math-only, and implement explicit nan check function --- Firmware/MotorControl/low_level.cpp | 14 ++++++++------ Firmware/MotorControl/motor.cpp | 9 ++++----- Firmware/MotorControl/thermistor.cpp | 2 +- Firmware/MotorControl/utils.cpp | 5 ++--- Firmware/MotorControl/utils.hpp | 11 +++++++++-- Firmware/Tupfile.lua | 2 +- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index ea6b879b..9a6f9ca2 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -296,7 +296,7 @@ void start_general_purpose_adc() { // @brief Returns the ADC voltage associated with the specified pin. // This only works if the GPIO was not used for anything else since bootup, otherwise // it must be put to analog mode first. -// Returns NaN if the pin has no associated ADC1 channel. +// Returns -1.0f if the pin has no associated ADC1 channel. // // On ODrive 3.3 and 3.4 the following pins can be used with this function: // GPIO_1, GPIO_2, GPIO_3, GPIO_4 and some pins that are connected to @@ -359,13 +359,13 @@ uint16_t channel_from_gpio(Stm32Gpio gpio) { } // @brief Given an adc channel return the measured voltage. -// returns NaN if the channel is not valid. +// returns -1.0f if the channel is not valid. float get_adc_voltage_channel(uint16_t channel) { if (channel < ADC_CHANNEL_COUNT) return ((float)adc_measurements_[channel]) * (adc_ref_voltage / adc_full_scale); else - return 0.0f / 0.0f; // NaN + return -1.0f; } //-------------------------------- @@ -497,7 +497,7 @@ void update_brake_current() { brake_duty += std::max((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); } - if (std::isnan(brake_duty)) { + if (is_nan(brake_duty)) { // Shuts off all motors AND brake resistor, sets error code on all motors. low_level_fault(Motor::ERROR_BRAKE_DUTY_CYCLE_NAN); return; @@ -510,8 +510,10 @@ void update_brake_current() { // Duty limit at 95% to allow bootstrap caps to charge brake_duty = std::clamp(brake_duty, 0.0f, 0.95f); - // Special handling to avoid the case 0.0/0.0 == NaN. - Ibus_sum += brake_duty ? (brake_duty * vbus_voltage / odrv.config_.brake_resistance) : 0.0f; + // Special handling to avoid the case 0.0/0.0 == NaN, or divide by 0. + if (odrv.config_.brake_resistance > 0.0f) { + Ibus_sum += brake_duty * vbus_voltage / odrv.config_.brake_resistance; + } ibus_ += odrv.ibus_report_filter_k_ * (Ibus_sum - ibus_); diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index b148ea7c..3e160dee 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -260,10 +260,10 @@ bool Motor::run_calibration() { } bool Motor::enqueue_modulation_timings(float mod_alpha, float mod_beta) { - if (std::isnan(mod_alpha) || std::isnan(mod_alpha)) + if (is_nan(mod_alpha) || is_nan(mod_beta)) return set_error(ERROR_MODULATION_IS_NAN), false; float tA, tB, tC; - if (SVM(mod_alpha, mod_beta, &tA, &tB, &tC) != 0) + if (!SVM(mod_alpha, mod_beta, &tA, &tB, &tC)) return set_error(ERROR_MODULATION_MAGNITUDE), false; next_timings_[0] = (uint16_t)(tA * (float)TIM_1_8_PERIOD_CLOCKS); next_timings_[1] = (uint16_t)(tB * (float)TIM_1_8_PERIOD_CLOCKS); @@ -445,9 +445,8 @@ bool Motor::update(float torque_setpoint, float phase, float phase_vel) { float dflux_by_dt = config_.acim_slip_velocity * (id - current_control_.acim_rotor_flux); current_control_.acim_rotor_flux += dflux_by_dt * current_meas_period; float slip_velocity = config_.acim_slip_velocity * (iq / current_control_.acim_rotor_flux); - // Check for issues with small denominator. Polarity of check to catch NaN too - bool acceptable_vel = std::abs(slip_velocity) <= 0.1f * (float)current_meas_hz; - if (!acceptable_vel) + // Check for issues with small denominator. + if (is_nan(slip_velocity) || std::abs(slip_velocity) > 0.1f * (float)current_meas_hz) slip_velocity = 0.0f; phase_vel += slip_velocity; // reporting only: diff --git a/Firmware/MotorControl/thermistor.cpp b/Firmware/MotorControl/thermistor.cpp index 3baf78a0..617dee75 100644 --- a/Firmware/MotorControl/thermistor.cpp +++ b/Firmware/MotorControl/thermistor.cpp @@ -42,7 +42,7 @@ float ThermistorCurrentLimiter::get_current_limit(float base_current_lim) const const float temp_margin = temp_limit_upper_ - temperature_; const float derating_range = temp_limit_upper_ - temp_limit_lower_; float thermal_current_lim = base_current_lim * (temp_margin / derating_range); - if (!(thermal_current_lim >= 0.0f)) { // Funny polarity to also catch NaN + if (thermal_current_lim < 0.0f || is_nan(thermal_current_lim)) { thermal_current_lim = 0.0f; } diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index d518fa56..24aa9569 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -5,7 +5,7 @@ #include #include -int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { +bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { int Sextant; if (beta >= 0.0f) { @@ -113,12 +113,11 @@ int SVM(float alpha, float beta, float* tA, float* tB, float* tC) { } break; } - // if any of the results becomes NaN, result_valid will evaluate to false int result_valid = *tA >= 0.0f && *tA <= 1.0f && *tB >= 0.0f && *tB <= 1.0f && *tC >= 0.0f && *tC <= 1.0f; - return result_valid ? 0 : -1; + return result_valid; } // based on https://math.stackexchange.com/a/1105038/81278 diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 8c9143fa..e31f27cf 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -77,6 +77,13 @@ std::array make_array(T head, Tail... tail) return std::array({ head, tail ... }); } +// To allow use of -ffast-math we need to have a special check for nan +// that bypasses the "ignore nan" flag +__attribute__((optimize("-fno-finite-math-only"))) +static inline bool is_nan(float x) { + return __builtin_isnan(x);; +} + extern "C" { #endif @@ -122,8 +129,8 @@ static inline float wrap_pm_pi(float x) { // Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta // as per the magnitude invariant clarke transform // The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 -// Returns 0 on success, and -1 if the input was out of range -int SVM(float alpha, float beta, float* tA, float* tB, float* tC); +// Returns true on success, and false if the input was out of range +bool SVM(float alpha, float beta, float* tA, float* tB, float* tC); float fast_atan2(float y, float x); float horner_fma(float x, const float *coeffs, size_t count); diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index d2cf3cbf..f5a78ad8 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -149,7 +149,7 @@ else end -- common flags for ASM, C and C++ -OPT += '-ffast-math -fno-finite-math-only' +OPT += '-ffast-math' tup.append_table(FLAGS, OPT) tup.append_table(LDFLAGS, OPT) From 4815e5ed19a6d99df8930a2bf15be1386a9b955b Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 00:23:58 -0400 Subject: [PATCH 024/124] Change sqrtf to std::sqrt --- Firmware/MotorControl/motor.cpp | 2 +- Firmware/MotorControl/trapTraj.cpp | 2 +- Firmware/Tests/test_trap_traj.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 3e160dee..bfee0047 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -349,7 +349,7 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_pha // Vector modulation saturation, lock integrator if saturated // TODO make maximum modulation configurable - float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / sqrtf(mod_d * mod_d + mod_q * mod_q); + float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / std::sqrt(mod_d * mod_d + mod_q * mod_q); if (mod_scalefactor < 1.0f) { mod_d *= mod_scalefactor; mod_q *= mod_scalefactor; diff --git a/Firmware/MotorControl/trapTraj.cpp b/Firmware/MotorControl/trapTraj.cpp index 6bc73e18..b4f94a37 100644 --- a/Firmware/MotorControl/trapTraj.cpp +++ b/Firmware/MotorControl/trapTraj.cpp @@ -42,7 +42,7 @@ bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, // Are we displacing enough to reach cruising speed? if (s*dX < s*dXmin) { // Short move (triangle profile) - Vr_ = s * sqrtf(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); + Vr_ = s * std::sqrt(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_); Td_ = std::max(0.0f, -Vr_ / Dr_); Tv_ = 0.0f; diff --git a/Firmware/Tests/test_trap_traj.cpp b/Firmware/Tests/test_trap_traj.cpp index 8d5399ff..8d5aad2d 100644 --- a/Firmware/Tests/test_trap_traj.cpp +++ b/Firmware/Tests/test_trap_traj.cpp @@ -85,8 +85,8 @@ bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi, // Are we displacing enough to reach cruising speed? if (s*dX < s*dXmin) { // Short move (triangle profile) - Vr_ = s * sqrtf(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); - //Vr_ = s * sqrtf((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_)); + Vr_ = s * std::sqrt(std::max((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_), 0.0f)); + //Vr_ = s * std::sqrt((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_)); Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_); Td_ = std::max(0.0f, -Vr_ / Dr_); Tv_ = 0.0f; From 9e1e054f0a741efd7282ad1e981e07fd9b36a8f0 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 00:30:12 -0400 Subject: [PATCH 025/124] Bring several functions from utils.cpp into utils.hpp for inlining --- Firmware/MotorControl/utils.cpp | 157 ------------------------ Firmware/MotorControl/utils.hpp | 209 ++++++++++++++++++++++++++------ 2 files changed, 175 insertions(+), 191 deletions(-) diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 24aa9569..8c041d55 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -1,164 +1,7 @@ #include -#include -#include -#include #include -bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { - int Sextant; - - if (beta >= 0.0f) { - if (alpha >= 0.0f) { - //quadrant I - if (one_by_sqrt3 * beta > alpha) - Sextant = 2; //sextant v2-v3 - else - Sextant = 1; //sextant v1-v2 - - } else { - //quadrant II - if (-one_by_sqrt3 * beta > alpha) - Sextant = 3; //sextant v3-v4 - else - Sextant = 2; //sextant v2-v3 - } - } else { - if (alpha >= 0.0f) { - //quadrant IV - if (-one_by_sqrt3 * beta > alpha) - Sextant = 5; //sextant v5-v6 - else - Sextant = 6; //sextant v6-v1 - } else { - //quadrant III - if (one_by_sqrt3 * beta > alpha) - Sextant = 4; //sextant v4-v5 - else - Sextant = 5; //sextant v5-v6 - } - } - - switch (Sextant) { - // sextant v1-v2 - case 1: { - // Vector on-times - float t1 = alpha - one_by_sqrt3 * beta; - float t2 = two_by_sqrt3 * beta; - - // PWM timings - *tA = (1.0f - t1 - t2) * 0.5f; - *tB = *tA + t1; - *tC = *tB + t2; - } break; - - // sextant v2-v3 - case 2: { - // Vector on-times - float t2 = alpha + one_by_sqrt3 * beta; - float t3 = -alpha + one_by_sqrt3 * beta; - - // PWM timings - *tB = (1.0f - t2 - t3) * 0.5f; - *tA = *tB + t3; - *tC = *tA + t2; - } break; - - // sextant v3-v4 - case 3: { - // Vector on-times - float t3 = two_by_sqrt3 * beta; - float t4 = -alpha - one_by_sqrt3 * beta; - - // PWM timings - *tB = (1.0f - t3 - t4) * 0.5f; - *tC = *tB + t3; - *tA = *tC + t4; - } break; - - // sextant v4-v5 - case 4: { - // Vector on-times - float t4 = -alpha + one_by_sqrt3 * beta; - float t5 = -two_by_sqrt3 * beta; - - // PWM timings - *tC = (1.0f - t4 - t5) * 0.5f; - *tB = *tC + t5; - *tA = *tB + t4; - } break; - - // sextant v5-v6 - case 5: { - // Vector on-times - float t5 = -alpha - one_by_sqrt3 * beta; - float t6 = alpha - one_by_sqrt3 * beta; - - // PWM timings - *tC = (1.0f - t5 - t6) * 0.5f; - *tA = *tC + t5; - *tB = *tA + t6; - } break; - - // sextant v6-v1 - case 6: { - // Vector on-times - float t6 = -two_by_sqrt3 * beta; - float t1 = alpha + one_by_sqrt3 * beta; - - // PWM timings - *tA = (1.0f - t6 - t1) * 0.5f; - *tC = *tA + t1; - *tB = *tC + t6; - } break; - } - - int result_valid = - *tA >= 0.0f && *tA <= 1.0f - && *tB >= 0.0f && *tB <= 1.0f - && *tC >= 0.0f && *tC <= 1.0f; - return result_valid; -} - -// based on https://math.stackexchange.com/a/1105038/81278 -float fast_atan2(float y, float x) { - // a := min (|x|, |y|) / max (|x|, |y|) - float abs_y = std::abs(y); - float abs_x = std::abs(x); - // inject FLT_MIN in denominator to avoid division by zero - float a = MACRO_MIN(abs_x, abs_y) / (MACRO_MAX(abs_x, abs_y) + FLT_MIN); - // s := a * a - float s = a * a; - // r := ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a - float r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; - // if |y| > |x| then r := 1.57079637 - r - if (abs_y > abs_x) - r = 1.57079637f - r; - // if x < 0 then r := 3.14159274 - r - if (x < 0.0f) - r = 3.14159274f - r; - // if y < 0 then r := -r - if (y < 0.0f) - r = -r; - - return r; -} - -// Evaluate polynomials using Fused Multiply Add intrisic instruction. -// coeffs[0] is highest order, as per numpy.polyfit -// p(x) = coeffs[0] * x^deg + ... + coeffs[deg], for some degree "deg" -float horner_fma(float x, const float *coeffs, size_t count) { - float result = 0.0f; - for (size_t idx = 0; idx < count; ++idx) - result = (result * x) + coeffs[idx]; - return result; -} - -// Modulo (as opposed to remainder), per https://stackoverflow.com/a/19288271 -int mod(int dividend, int divisor){ - int r = dividend % divisor; - return (r < 0) ? (r + divisor) : r; -} // @brief: Returns how much time is left until the deadline is reached. // If the deadline has already passed, the return value is 0 (except if diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index e31f27cf..9e978d3a 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -1,9 +1,8 @@ - -#ifndef __UTILS_H -#define __UTILS_H +#pragma once #include -#include +#include +#include /** * @brief Flash size register address @@ -61,8 +60,6 @@ #define SQ(x) ((x) * (x)) -#ifdef __cplusplus - #include /** @@ -71,32 +68,30 @@ * has to match exactly. Whereas initializer lists allow * less arguments. */ -template -std::array make_array(T head, Tail... tail) -{ - return std::array({ head, tail ... }); +template +std::array make_array(T head, Tail... tail) { + return std::array({head, tail...}); } // To allow use of -ffast-math we need to have a special check for nan // that bypasses the "ignore nan" flag -__attribute__((optimize("-fno-finite-math-only"))) -static inline bool is_nan(float x) { - return __builtin_isnan(x);; +__attribute__((optimize("-fno-finite-math-only"))) static inline bool is_nan(float x) { + return __builtin_isnan(x); + ; } -extern "C" { -#endif - static const float one_by_sqrt3 = 0.57735026919f; static const float two_by_sqrt3 = 1.15470053838f; static const float sqrt3_by_2 = 0.86602540378f; // Round to integer // Default rounding mode: round to nearest -static inline int round_int(float x) { +inline int round_int(float x) { #ifdef __arm__ int res; - asm("vcvtr.s32.f32 %[res], %[x]" : [res] "=X" (res) : [x] "w" (x)); + asm("vcvtr.s32.f32 %[res], %[x]" + : [ res ] "=X"(res) + : [ x ] "w"(x)); return res; #else return (int)nearbyint(x); @@ -106,35 +101,185 @@ static inline int round_int(float x) { // Wrap value to range. // With default rounding mode (round to nearest), // the result will be in range -y/2 to y/2 -static inline float wrap_pm(float x, float y) { +inline float wrap_pm(float x, float y) { #ifdef FPU_FPV4 - float intval = (float)round_int(x/y); + float intval = (float)round_int(x / y); #else - float intval = nearbyint(x/y); + float intval = nearbyint(x / y); #endif return x - intval * y; } // Same as fmodf but result is positive and y must be positive -static inline float fmodf_pos(float x, float y) { +inline float fmodf_pos(float x, float y) { float res = wrap_pm(x, y); if (res < 0) res += y; return res; } -static inline float wrap_pm_pi(float x) { - return wrap_pm(x, 2*M_PI); +inline float wrap_pm_pi(float x) { + return wrap_pm(x, 2 * M_PI); } // Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta // as per the magnitude invariant clarke transform // The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 // Returns true on success, and false if the input was out of range -bool SVM(float alpha, float beta, float* tA, float* tB, float* tC); +inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { + int Sextant; -float fast_atan2(float y, float x); -float horner_fma(float x, const float *coeffs, size_t count); -int mod(int dividend, int divisor); + if (beta >= 0.0f) { + if (alpha >= 0.0f) { + //quadrant I + if (one_by_sqrt3 * beta > alpha) + Sextant = 2; //sextant v2-v3 + else + Sextant = 1; //sextant v1-v2 + + } else { + //quadrant II + if (-one_by_sqrt3 * beta > alpha) + Sextant = 3; //sextant v3-v4 + else + Sextant = 2; //sextant v2-v3 + } + } else { + if (alpha >= 0.0f) { + //quadrant IV + if (-one_by_sqrt3 * beta > alpha) + Sextant = 5; //sextant v5-v6 + else + Sextant = 6; //sextant v6-v1 + } else { + //quadrant III + if (one_by_sqrt3 * beta > alpha) + Sextant = 4; //sextant v4-v5 + else + Sextant = 5; //sextant v5-v6 + } + } + + switch (Sextant) { + // sextant v1-v2 + case 1: { + // Vector on-times + float t1 = alpha - one_by_sqrt3 * beta; + float t2 = two_by_sqrt3 * beta; + + // PWM timings + *tA = (1.0f - t1 - t2) * 0.5f; + *tB = *tA + t1; + *tC = *tB + t2; + } break; + + // sextant v2-v3 + case 2: { + // Vector on-times + float t2 = alpha + one_by_sqrt3 * beta; + float t3 = -alpha + one_by_sqrt3 * beta; + + // PWM timings + *tB = (1.0f - t2 - t3) * 0.5f; + *tA = *tB + t3; + *tC = *tA + t2; + } break; + + // sextant v3-v4 + case 3: { + // Vector on-times + float t3 = two_by_sqrt3 * beta; + float t4 = -alpha - one_by_sqrt3 * beta; + + // PWM timings + *tB = (1.0f - t3 - t4) * 0.5f; + *tC = *tB + t3; + *tA = *tC + t4; + } break; + + // sextant v4-v5 + case 4: { + // Vector on-times + float t4 = -alpha + one_by_sqrt3 * beta; + float t5 = -two_by_sqrt3 * beta; + + // PWM timings + *tC = (1.0f - t4 - t5) * 0.5f; + *tB = *tC + t5; + *tA = *tB + t4; + } break; + + // sextant v5-v6 + case 5: { + // Vector on-times + float t5 = -alpha - one_by_sqrt3 * beta; + float t6 = alpha - one_by_sqrt3 * beta; + + // PWM timings + *tC = (1.0f - t5 - t6) * 0.5f; + *tA = *tC + t5; + *tB = *tA + t6; + } break; + + // sextant v6-v1 + case 6: { + // Vector on-times + float t6 = -two_by_sqrt3 * beta; + float t1 = alpha + one_by_sqrt3 * beta; + + // PWM timings + *tA = (1.0f - t6 - t1) * 0.5f; + *tC = *tA + t1; + *tB = *tC + t6; + } break; + } + + int result_valid = + *tA >= 0.0f && *tA <= 1.0f + && *tB >= 0.0f && *tB <= 1.0f + && *tC >= 0.0f && *tC <= 1.0f; + return result_valid; +} + + +// based on https://math.stackexchange.com/a/1105038/81278 +inline float fast_atan2(const float y, const float x) { + // a := min (|x|, |y|) / max (|x|, |y|) + float abs_y = std::abs(y); + float abs_x = std::abs(x); + // inject FLT_MIN in denominator to avoid division by zero + float a = std::min(abs_x, abs_y) / (std::max(abs_x, abs_y) + std::numeric_limits::min()); + // s := a * a + float s = a * a; + // r := ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a + float r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; + // if |y| > |x| then r := 1.57079637 - r + if (abs_y > abs_x) + r = 1.57079637f - r; + // if x < 0 then r := 3.14159274 - r + if (x < 0.0f) + r = 3.14159274f - r; + // if y < 0 then r := -r + if (y < 0.0f) + r = -r; + + return r; +} + +// Evaluate polynomials using Fused Multiply Add intrisic instruction. +// coeffs[0] is highest order, as per numpy.polyfit +// p(x) = coeffs[0] * x^deg + ... + coeffs[deg], for some degree "deg" +inline float horner_fma(float x, const float *coeffs, size_t count) { + float result = 0.0f; + for (size_t idx = 0; idx < count; ++idx) + result = (result * x) + coeffs[idx]; + return result; +} + +// Modulo (as opposed to remainder), per https://stackoverflow.com/a/19288271 +inline int mod(const int dividend, const int divisor){ + int r = dividend % divisor; + return (r < 0) ? (r + divisor) : r; +} uint32_t deadline_to_timeout(uint32_t deadline_ms); uint32_t timeout_to_deadline(uint32_t timeout_ms); @@ -143,11 +288,7 @@ int is_in_the_future(uint32_t time_ms); uint32_t micros(void); void delay_us(uint32_t us); +extern "C" { float our_arm_sin_f32(float x); float our_arm_cos_f32(float x); - -#ifdef __cplusplus -} -#endif - -#endif //__UTILS_H +} \ No newline at end of file From fc5707a1a701cdd7969f5a21d73e80ccd142e57a Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 00:34:46 -0400 Subject: [PATCH 026/124] Remove unused macros --- Firmware/MotorControl/utils.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 9e978d3a..e01afb7f 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -55,8 +55,7 @@ #endif #define M_PI (3.14159265358979323846f) -#define MACRO_MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MACRO_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#include #define SQ(x) ((x) * (x)) From 9d5e8614eb8323b8bfe8cc1f80cf6ab99013e714 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 00:36:06 -0400 Subject: [PATCH 027/124] Change SQ macro to a constexpr function --- Firmware/MotorControl/utils.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index e01afb7f..d02c9fa6 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -3,6 +3,7 @@ #include #include #include +#include /** * @brief Flash size register address @@ -55,11 +56,10 @@ #endif #define M_PI (3.14159265358979323846f) -#include - -#define SQ(x) ((x) * (x)) - -#include +template +constexpr T SQ(const T& x){ + return x * x; +} /** * @brief Small helper to make array with known size From ea08c1fe77c35042f54edab26069b35ee5541af2 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 00:43:40 -0400 Subject: [PATCH 028/124] Constexpr math constants --- Firmware/MotorControl/utils.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index d02c9fa6..7a663406 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -54,7 +54,12 @@ #ifdef M_PI #undef M_PI #endif -#define M_PI (3.14159265358979323846f) + +// Math Constants +constexpr float M_PI = 3.14159265358979323846f; +constexpr float one_by_sqrt3 = 0.57735026919f; +constexpr float two_by_sqrt3 = 1.15470053838f; +constexpr float sqrt3_by_2 = 0.86602540378f; template constexpr T SQ(const T& x){ @@ -76,13 +81,8 @@ std::array make_array(T head, Tail... tail) { // that bypasses the "ignore nan" flag __attribute__((optimize("-fno-finite-math-only"))) static inline bool is_nan(float x) { return __builtin_isnan(x); - ; } -static const float one_by_sqrt3 = 0.57735026919f; -static const float two_by_sqrt3 = 1.15470053838f; -static const float sqrt3_by_2 = 0.86602540378f; - // Round to integer // Default rounding mode: round to nearest inline int round_int(float x) { From 29332281be14e85a4d0e9ff8a8022e451225ff6f Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 00:56:59 -0400 Subject: [PATCH 029/124] Convert SVM to not use out parameters --- Firmware/MotorControl/motor.cpp | 4 +-- Firmware/MotorControl/utils.hpp | 48 +++++++++++++++++---------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index bfee0047..06493cd4 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -262,8 +262,8 @@ bool Motor::run_calibration() { bool Motor::enqueue_modulation_timings(float mod_alpha, float mod_beta) { if (is_nan(mod_alpha) || is_nan(mod_beta)) return set_error(ERROR_MODULATION_IS_NAN), false; - float tA, tB, tC; - if (!SVM(mod_alpha, mod_beta, &tA, &tB, &tC)) + auto [tA, tB, tC, success] = SVM(mod_alpha, mod_beta); + if(!success) return set_error(ERROR_MODULATION_MAGNITUDE), false; next_timings_[0] = (uint16_t)(tA * (float)TIM_1_8_PERIOD_CLOCKS); next_timings_[1] = (uint16_t)(tB * (float)TIM_1_8_PERIOD_CLOCKS); diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 7a663406..251e2cbc 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -4,6 +4,7 @@ #include #include #include +#include /** * @brief Flash size register address @@ -124,7 +125,8 @@ inline float wrap_pm_pi(float x) { // as per the magnitude invariant clarke transform // The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 // Returns true on success, and false if the input was out of range -inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { +inline auto SVM(float alpha, float beta) { + float tA, tB, tC; int Sextant; if (beta >= 0.0f) { @@ -166,9 +168,9 @@ inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t2 = two_by_sqrt3 * beta; // PWM timings - *tA = (1.0f - t1 - t2) * 0.5f; - *tB = *tA + t1; - *tC = *tB + t2; + tA = (1.0f - t1 - t2) * 0.5f; + tB = tA + t1; + tC = tB + t2; } break; // sextant v2-v3 @@ -178,9 +180,9 @@ inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t3 = -alpha + one_by_sqrt3 * beta; // PWM timings - *tB = (1.0f - t2 - t3) * 0.5f; - *tA = *tB + t3; - *tC = *tA + t2; + tB = (1.0f - t2 - t3) * 0.5f; + tA = tB + t3; + tC = tA + t2; } break; // sextant v3-v4 @@ -190,9 +192,9 @@ inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t4 = -alpha - one_by_sqrt3 * beta; // PWM timings - *tB = (1.0f - t3 - t4) * 0.5f; - *tC = *tB + t3; - *tA = *tC + t4; + tB = (1.0f - t3 - t4) * 0.5f; + tC = tB + t3; + tA = tC + t4; } break; // sextant v4-v5 @@ -202,9 +204,9 @@ inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t5 = -two_by_sqrt3 * beta; // PWM timings - *tC = (1.0f - t4 - t5) * 0.5f; - *tB = *tC + t5; - *tA = *tB + t4; + tC = (1.0f - t4 - t5) * 0.5f; + tB = tC + t5; + tA = tB + t4; } break; // sextant v5-v6 @@ -214,9 +216,9 @@ inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t6 = alpha - one_by_sqrt3 * beta; // PWM timings - *tC = (1.0f - t5 - t6) * 0.5f; - *tA = *tC + t5; - *tB = *tA + t6; + tC = (1.0f - t5 - t6) * 0.5f; + tA = tC + t5; + tB = tA + t6; } break; // sextant v6-v1 @@ -226,17 +228,17 @@ inline bool SVM(float alpha, float beta, float* tA, float* tB, float* tC) { float t1 = alpha + one_by_sqrt3 * beta; // PWM timings - *tA = (1.0f - t6 - t1) * 0.5f; - *tC = *tA + t1; - *tB = *tC + t6; + tA = (1.0f - t6 - t1) * 0.5f; + tC = tA + t1; + tB = tC + t6; } break; } int result_valid = - *tA >= 0.0f && *tA <= 1.0f - && *tB >= 0.0f && *tB <= 1.0f - && *tC >= 0.0f && *tC <= 1.0f; - return result_valid; + tA >= 0.0f && tA <= 1.0f + && tB >= 0.0f && tB <= 1.0f + && tC >= 0.0f && tC <= 1.0f; + return std::make_tuple(tA, tB, tC, result_valid); } From b38fd2be9c87eb16bb08d5ddd4968d2796497fb4 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 5 Sep 2020 15:25:32 -0700 Subject: [PATCH 030/124] Only inline short functions --- Firmware/MotorControl/thermistor.cpp | 2 +- Firmware/MotorControl/utils.cpp | 146 ++++++++++++++++- Firmware/MotorControl/utils.hpp | 188 +++------------------- Firmware/communication/interface_uart.cpp | 5 - 4 files changed, 171 insertions(+), 170 deletions(-) diff --git a/Firmware/MotorControl/thermistor.cpp b/Firmware/MotorControl/thermistor.cpp index 617dee75..df3b1d0d 100644 --- a/Firmware/MotorControl/thermistor.cpp +++ b/Firmware/MotorControl/thermistor.cpp @@ -22,7 +22,7 @@ ThermistorCurrentLimiter::ThermistorCurrentLimiter(uint16_t adc_channel, void ThermistorCurrentLimiter::update() { const float voltage = get_adc_voltage_channel(adc_channel_); const float normalized_voltage = voltage / adc_ref_voltage; - temperature_ = horner_fma(normalized_voltage, coefficients_, num_coeffs_); + temperature_ = horner_poly_eval(normalized_voltage, coefficients_, num_coeffs_); } bool ThermistorCurrentLimiter::do_checks() { diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 8c041d55..dad400fb 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -3,6 +3,150 @@ #include +// Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta +// as per the magnitude invariant clarke transform +// The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 +// Returns true on success, and false if the input was out of range +std::tuple SVM(float alpha, float beta) { + float tA, tB, tC; + int Sextant; + + if (beta >= 0.0f) { + if (alpha >= 0.0f) { + //quadrant I + if (one_by_sqrt3 * beta > alpha) + Sextant = 2; //sextant v2-v3 + else + Sextant = 1; //sextant v1-v2 + + } else { + //quadrant II + if (-one_by_sqrt3 * beta > alpha) + Sextant = 3; //sextant v3-v4 + else + Sextant = 2; //sextant v2-v3 + } + } else { + if (alpha >= 0.0f) { + //quadrant IV + if (-one_by_sqrt3 * beta > alpha) + Sextant = 5; //sextant v5-v6 + else + Sextant = 6; //sextant v6-v1 + } else { + //quadrant III + if (one_by_sqrt3 * beta > alpha) + Sextant = 4; //sextant v4-v5 + else + Sextant = 5; //sextant v5-v6 + } + } + + switch (Sextant) { + // sextant v1-v2 + case 1: { + // Vector on-times + float t1 = alpha - one_by_sqrt3 * beta; + float t2 = two_by_sqrt3 * beta; + + // PWM timings + tA = (1.0f - t1 - t2) * 0.5f; + tB = tA + t1; + tC = tB + t2; + } break; + + // sextant v2-v3 + case 2: { + // Vector on-times + float t2 = alpha + one_by_sqrt3 * beta; + float t3 = -alpha + one_by_sqrt3 * beta; + + // PWM timings + tB = (1.0f - t2 - t3) * 0.5f; + tA = tB + t3; + tC = tA + t2; + } break; + + // sextant v3-v4 + case 3: { + // Vector on-times + float t3 = two_by_sqrt3 * beta; + float t4 = -alpha - one_by_sqrt3 * beta; + + // PWM timings + tB = (1.0f - t3 - t4) * 0.5f; + tC = tB + t3; + tA = tC + t4; + } break; + + // sextant v4-v5 + case 4: { + // Vector on-times + float t4 = -alpha + one_by_sqrt3 * beta; + float t5 = -two_by_sqrt3 * beta; + + // PWM timings + tC = (1.0f - t4 - t5) * 0.5f; + tB = tC + t5; + tA = tB + t4; + } break; + + // sextant v5-v6 + case 5: { + // Vector on-times + float t5 = -alpha - one_by_sqrt3 * beta; + float t6 = alpha - one_by_sqrt3 * beta; + + // PWM timings + tC = (1.0f - t5 - t6) * 0.5f; + tA = tC + t5; + tB = tA + t6; + } break; + + // sextant v6-v1 + case 6: { + // Vector on-times + float t6 = -two_by_sqrt3 * beta; + float t1 = alpha + one_by_sqrt3 * beta; + + // PWM timings + tA = (1.0f - t6 - t1) * 0.5f; + tC = tA + t1; + tB = tC + t6; + } break; + } + + int result_valid = + tA >= 0.0f && tA <= 1.0f + && tB >= 0.0f && tB <= 1.0f + && tC >= 0.0f && tC <= 1.0f; + return {tA, tB, tC, result_valid}; +} + +// based on https://math.stackexchange.com/a/1105038/81278 +float fast_atan2(float y, float x) { + // a := min (|x|, |y|) / max (|x|, |y|) + float abs_y = std::abs(y); + float abs_x = std::abs(x); + // inject FLT_MIN in denominator to avoid division by zero + float a = std::min(abs_x, abs_y) / (std::max(abs_x, abs_y) + std::numeric_limits::min()); + // s := a * a + float s = a * a; + // r := ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a + float r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; + // if |y| > |x| then r := 1.57079637 - r + if (abs_y > abs_x) + r = 1.57079637f - r; + // if x < 0 then r := 3.14159274 - r + if (x < 0.0f) + r = 3.14159274f - r; + // if y < 0 then r := -r + if (y < 0.0f) + r = -r; + + return r; +} + // @brief: Returns how much time is left until the deadline is reached. // If the deadline has already passed, the return value is 0 (except if // the deadline is very far in the past) @@ -41,6 +185,6 @@ void delay_us(uint32_t us) { uint32_t start = micros(); while (micros() - start < (uint32_t) us) { - __ASM("nop"); + asm volatile ("nop"); } } diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 251e2cbc..8eb044f9 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -62,6 +62,23 @@ constexpr float one_by_sqrt3 = 0.57735026919f; constexpr float two_by_sqrt3 = 1.15470053838f; constexpr float sqrt3_by_2 = 0.86602540378f; +// Function prototypes for implementations in utils.cpp +std::tuple SVM(float alpha, float beta); +float fast_atan2(float y, float x); +uint32_t deadline_to_timeout(uint32_t deadline_ms); +uint32_t timeout_to_deadline(uint32_t timeout_ms); +int is_in_the_future(uint32_t time_ms); +uint32_t micros(void); +void delay_us(uint32_t us); + +extern "C" { +float our_arm_sin_f32(float x); +float our_arm_cos_f32(float x); +} + +// ---------------- +// Inline functions + template constexpr T SQ(const T& x){ return x * x; @@ -80,7 +97,8 @@ std::array make_array(T head, Tail... tail) { // To allow use of -ffast-math we need to have a special check for nan // that bypasses the "ignore nan" flag -__attribute__((optimize("-fno-finite-math-only"))) static inline bool is_nan(float x) { +__attribute__((optimize("-fno-finite-math-only"))) +inline bool is_nan(float x) { return __builtin_isnan(x); } @@ -90,8 +108,8 @@ inline int round_int(float x) { #ifdef __arm__ int res; asm("vcvtr.s32.f32 %[res], %[x]" - : [ res ] "=X"(res) - : [ x ] "w"(x)); + : [res] "=X" (res) + : [x] "w" (x) ); return res; #else return (int)nearbyint(x); @@ -121,155 +139,10 @@ inline float wrap_pm_pi(float x) { return wrap_pm(x, 2 * M_PI); } -// Compute rising edge timings (0.0 - 1.0) as a function of alpha-beta -// as per the magnitude invariant clarke transform -// The magnitude of the alpha-beta vector may not be larger than sqrt(3)/2 -// Returns true on success, and false if the input was out of range -inline auto SVM(float alpha, float beta) { - float tA, tB, tC; - int Sextant; - - if (beta >= 0.0f) { - if (alpha >= 0.0f) { - //quadrant I - if (one_by_sqrt3 * beta > alpha) - Sextant = 2; //sextant v2-v3 - else - Sextant = 1; //sextant v1-v2 - - } else { - //quadrant II - if (-one_by_sqrt3 * beta > alpha) - Sextant = 3; //sextant v3-v4 - else - Sextant = 2; //sextant v2-v3 - } - } else { - if (alpha >= 0.0f) { - //quadrant IV - if (-one_by_sqrt3 * beta > alpha) - Sextant = 5; //sextant v5-v6 - else - Sextant = 6; //sextant v6-v1 - } else { - //quadrant III - if (one_by_sqrt3 * beta > alpha) - Sextant = 4; //sextant v4-v5 - else - Sextant = 5; //sextant v5-v6 - } - } - - switch (Sextant) { - // sextant v1-v2 - case 1: { - // Vector on-times - float t1 = alpha - one_by_sqrt3 * beta; - float t2 = two_by_sqrt3 * beta; - - // PWM timings - tA = (1.0f - t1 - t2) * 0.5f; - tB = tA + t1; - tC = tB + t2; - } break; - - // sextant v2-v3 - case 2: { - // Vector on-times - float t2 = alpha + one_by_sqrt3 * beta; - float t3 = -alpha + one_by_sqrt3 * beta; - - // PWM timings - tB = (1.0f - t2 - t3) * 0.5f; - tA = tB + t3; - tC = tA + t2; - } break; - - // sextant v3-v4 - case 3: { - // Vector on-times - float t3 = two_by_sqrt3 * beta; - float t4 = -alpha - one_by_sqrt3 * beta; - - // PWM timings - tB = (1.0f - t3 - t4) * 0.5f; - tC = tB + t3; - tA = tC + t4; - } break; - - // sextant v4-v5 - case 4: { - // Vector on-times - float t4 = -alpha + one_by_sqrt3 * beta; - float t5 = -two_by_sqrt3 * beta; - - // PWM timings - tC = (1.0f - t4 - t5) * 0.5f; - tB = tC + t5; - tA = tB + t4; - } break; - - // sextant v5-v6 - case 5: { - // Vector on-times - float t5 = -alpha - one_by_sqrt3 * beta; - float t6 = alpha - one_by_sqrt3 * beta; - - // PWM timings - tC = (1.0f - t5 - t6) * 0.5f; - tA = tC + t5; - tB = tA + t6; - } break; - - // sextant v6-v1 - case 6: { - // Vector on-times - float t6 = -two_by_sqrt3 * beta; - float t1 = alpha + one_by_sqrt3 * beta; - - // PWM timings - tA = (1.0f - t6 - t1) * 0.5f; - tC = tA + t1; - tB = tC + t6; - } break; - } - - int result_valid = - tA >= 0.0f && tA <= 1.0f - && tB >= 0.0f && tB <= 1.0f - && tC >= 0.0f && tC <= 1.0f; - return std::make_tuple(tA, tB, tC, result_valid); -} - - -// based on https://math.stackexchange.com/a/1105038/81278 -inline float fast_atan2(const float y, const float x) { - // a := min (|x|, |y|) / max (|x|, |y|) - float abs_y = std::abs(y); - float abs_x = std::abs(x); - // inject FLT_MIN in denominator to avoid division by zero - float a = std::min(abs_x, abs_y) / (std::max(abs_x, abs_y) + std::numeric_limits::min()); - // s := a * a - float s = a * a; - // r := ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a - float r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a; - // if |y| > |x| then r := 1.57079637 - r - if (abs_y > abs_x) - r = 1.57079637f - r; - // if x < 0 then r := 3.14159274 - r - if (x < 0.0f) - r = 3.14159274f - r; - // if y < 0 then r := -r - if (y < 0.0f) - r = -r; - - return r; -} - -// Evaluate polynomials using Fused Multiply Add intrisic instruction. +// Evaluate polynomials in an efficient way // coeffs[0] is highest order, as per numpy.polyfit // p(x) = coeffs[0] * x^deg + ... + coeffs[deg], for some degree "deg" -inline float horner_fma(float x, const float *coeffs, size_t count) { +inline float horner_poly_eval(float x, const float *coeffs, size_t count) { float result = 0.0f; for (size_t idx = 0; idx < count; ++idx) result = (result * x) + coeffs[idx]; @@ -279,17 +152,6 @@ inline float horner_fma(float x, const float *coeffs, size_t count) { // Modulo (as opposed to remainder), per https://stackoverflow.com/a/19288271 inline int mod(const int dividend, const int divisor){ int r = dividend % divisor; - return (r < 0) ? (r + divisor) : r; + if (r < 0) r += divisor; + return r; } - -uint32_t deadline_to_timeout(uint32_t deadline_ms); -uint32_t timeout_to_deadline(uint32_t timeout_ms); -int is_in_the_future(uint32_t time_ms); - -uint32_t micros(void); -void delay_us(uint32_t us); - -extern "C" { -float our_arm_sin_f32(float x); -float our_arm_cos_f32(float x); -} \ No newline at end of file diff --git a/Firmware/communication/interface_uart.cpp b/Firmware/communication/interface_uart.cpp index 74fa02c0..6450eb50 100644 --- a/Firmware/communication/interface_uart.cpp +++ b/Firmware/communication/interface_uart.cpp @@ -18,9 +18,6 @@ static uint8_t dma_rx_buffer[UART_RX_BUFFER_SIZE]; static uint32_t dma_last_rcv_idx; -// FIXME: the stdlib doesn't know about CMSIS threads, so this is just a global variable -// static thread_local uint32_t deadline_ms = 0; - osThreadId uart_thread = 0; extern UART_HandleTypeDef* uart0; static UART_HandleTypeDef* huart_ = uart0; // defined in board.cpp. @@ -35,7 +32,6 @@ public: size_t chunk = length < UART_TX_BUFFER_SIZE ? length : UART_TX_BUFFER_SIZE; // wait for USB interface to become ready // TODO: implement ring buffer to get a more continuous stream of data - // if (osSemaphoreWait(sem_uart_dma, deadline_to_timeout(deadline_ms)) != osOK) if (osSemaphoreWait(sem_uart_dma, PROTOCOL_SERVER_TIMEOUT_MS) != osOK) return -1; // transmit chunk @@ -76,7 +72,6 @@ static void uart_server_thread(void * ctx) { continue; } - // deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); // Process bytes in one or two chunks (two in case there was a wrap) if (new_rcv_idx < dma_last_rcv_idx) { uart_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, From 33987df7e1335f2deada3eb39d9c42f529fd40c1 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 5 Sep 2020 16:15:11 -0700 Subject: [PATCH 031/124] Avoid redundant normalizing of adc voltage --- Firmware/MotorControl/encoder.cpp | 4 ++-- Firmware/MotorControl/low_level.cpp | 13 ++++++++----- Firmware/MotorControl/low_level.h | 9 ++++++--- Firmware/MotorControl/thermistor.cpp | 3 +-- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index f0c65445..4fe75689 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -330,8 +330,8 @@ void Encoder::sample_now() { } break; case MODE_SINCOS: { - sincos_sample_s_ = (get_adc_voltage(get_gpio(config_.sincos_gpio_pin_sin)) / 3.3f) - 0.5f; - sincos_sample_c_ = (get_adc_voltage(get_gpio(config_.sincos_gpio_pin_cos)) / 3.3f) - 0.5f; + sincos_sample_s_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_sin)) - 0.5f; + sincos_sample_c_ = get_adc_relative_voltage(get_gpio(config_.sincos_gpio_pin_cos)) - 0.5f; } break; case MODE_SPI_ABS_AMS: diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 9a6f9ca2..abd5fc09 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -311,8 +311,12 @@ void start_general_purpose_adc() { // The true frequency is slightly lower because of the injected vbus // measurements float get_adc_voltage(Stm32Gpio gpio) { + return get_adc_relative_voltage(gpio) * adc_ref_voltage; +} + +float get_adc_relative_voltage(Stm32Gpio gpio) { const uint16_t channel = channel_from_gpio(gpio); - return get_adc_voltage_channel(channel); + return get_adc_relative_voltage_ch(channel); } // @brief Given a GPIO_port and pin return the associated adc_channel. @@ -358,12 +362,11 @@ uint16_t channel_from_gpio(Stm32Gpio gpio) { return channel; } -// @brief Given an adc channel return the measured voltage. +// @brief Given an adc channel return the voltage as a ratio of adc_ref_voltage // returns -1.0f if the channel is not valid. -float get_adc_voltage_channel(uint16_t channel) -{ +float get_adc_relative_voltage_ch(uint16_t channel) { if (channel < ADC_CHANNEL_COUNT) - return ((float)adc_measurements_[channel]) * (adc_ref_voltage / adc_full_scale); + return (float)adc_measurements_[channel] / adc_full_scale; else return -1.0f; } diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index e02ef5c2..fdae02a1 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -46,12 +46,15 @@ void sync_timers(TIM_HandleTypeDef* htim_a, TIM_HandleTypeDef* htim_b, uint16_t TIM_CLOCKSOURCE_ITRx, uint16_t count_offset, TIM_HandleTypeDef* htim_refbase = nullptr); void start_general_purpose_adc(); -float get_adc_voltage(Stm32Gpio gpio); -uint16_t channel_from_gpio(Stm32Gpio gpio); -float get_adc_voltage_channel(uint16_t channel); void pwm_in_init(); void start_analog_thread(); +// ADC getters +uint16_t channel_from_gpio(Stm32Gpio gpio); +float get_adc_voltage(Stm32Gpio gpio); +float get_adc_relative_voltage(Stm32Gpio gpio); +float get_adc_relative_voltage_ch(uint16_t channel); + void update_brake_current(); #ifdef __cplusplus diff --git a/Firmware/MotorControl/thermistor.cpp b/Firmware/MotorControl/thermistor.cpp index df3b1d0d..4d16ae61 100644 --- a/Firmware/MotorControl/thermistor.cpp +++ b/Firmware/MotorControl/thermistor.cpp @@ -20,8 +20,7 @@ ThermistorCurrentLimiter::ThermistorCurrentLimiter(uint16_t adc_channel, } void ThermistorCurrentLimiter::update() { - const float voltage = get_adc_voltage_channel(adc_channel_); - const float normalized_voltage = voltage / adc_ref_voltage; + const float normalized_voltage = get_adc_relative_voltage_ch(adc_channel_); temperature_ = horner_poly_eval(normalized_voltage, coefficients_, num_coeffs_); } From 1e48a2b0e06d89f69a7a6af7573f8a270e0e78fd Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 5 Sep 2020 16:40:59 -0700 Subject: [PATCH 032/124] fix result valid type --- Firmware/MotorControl/utils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index dad400fb..cb1866db 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -18,7 +18,6 @@ std::tuple SVM(float alpha, float beta) { Sextant = 2; //sextant v2-v3 else Sextant = 1; //sextant v1-v2 - } else { //quadrant II if (-one_by_sqrt3 * beta > alpha) @@ -116,7 +115,7 @@ std::tuple SVM(float alpha, float beta) { } break; } - int result_valid = + bool result_valid = tA >= 0.0f && tA <= 1.0f && tB >= 0.0f && tB <= 1.0f && tC >= 0.0f && tC <= 1.0f; From 8419e0ab1705ba6f6f12372f5e93842288d0e850 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 5 Sep 2020 17:30:07 -0700 Subject: [PATCH 033/124] decode functions using nm with name demangling --- Firmware/sampler.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index a51e4c7e..6f032b32 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -45,17 +45,22 @@ class OpenOCDCMSampler(object): return 0 - def initSymbols(self, elf, readelf='arm-none-eabi-readelf'): - proc = subprocess.Popen([readelf, '-s', elf], stdout=subprocess.PIPE) + def initSymbols(self, elf, symbol_dump_cmd='arm-none-eabi-nm'): + proc = subprocess.Popen([symbol_dump_cmd, '-CS', elf], stdout=subprocess.PIPE) for line in proc.stdout.readlines(): field = line.split() - # for i,txt in enumerate(field): - # print("{}, {}".format(i, txt)) + try: - if field[3] == b'FUNC': - addr = int(field[1], 16) - 1 # For some reason readelf dumps the func addr off by 1 - func = field[7] - size = int(field[2]) + # For using nm -CS + if field[2] in ('t', 'T', 'w', 'W'): + addr = int(field[0], 16) + func = field[3] + size = int(field[1], 16) + # # For using readelf -s + # if field[3] == b'FUNC': + # addr = int(field[1], 16) - 1 # For some reason readelf dumps the func addr off by 1 + # func = field[7] + # size = int(field[2]) if addr not in self.indexes: self.table.append((addr, func, size)) self.indexes.add(addr) From d788fa71488871fc0638786b4d81e1356dea4c6f Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 5 Sep 2020 17:38:31 -0700 Subject: [PATCH 034/124] use --size-sort to force always print a size --- Firmware/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 6f032b32..694b783c 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -46,7 +46,7 @@ class OpenOCDCMSampler(object): def initSymbols(self, elf, symbol_dump_cmd='arm-none-eabi-nm'): - proc = subprocess.Popen([symbol_dump_cmd, '-CS', elf], stdout=subprocess.PIPE) + proc = subprocess.Popen([symbol_dump_cmd, '-CS', '--size-sort', elf], stdout=subprocess.PIPE) for line in proc.stdout.readlines(): field = line.split() From 7d92018ee8952998ef8d19ec342c4a898c633687 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 20:44:10 -0400 Subject: [PATCH 035/124] Fix character matching --- Firmware/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 694b783c..a033f66f 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -52,7 +52,7 @@ class OpenOCDCMSampler(object): try: # For using nm -CS - if field[2] in ('t', 'T', 'w', 'W'): + if field[2] in (b't', b'T', b'w', b'W'): addr = int(field[0], 16) func = field[3] size = int(field[1], 16) From 682f8bfada7b5ae2778584617456744a2af4915d Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 20:45:48 -0400 Subject: [PATCH 036/124] Decode function name as UTF-8 --- Firmware/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index a033f66f..3c97617c 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -129,7 +129,7 @@ if __name__ == '__main__': tmp = sorted(countmap.items(), key=operator.itemgetter(1)) #, reverse=True) for k, v in tmp: - print('{:05.2f}% {}'.format((v * 100.) / total, k)) + print('{:05.2f}% {}'.format((v * 100.) / total, k.decode('UTF-8'))) # print('{:06.2f} clocks : {}'.format((v * 10500) / total, k)) start = cur print('{} Samples'.format(total)) From 4a45314c978dc3d18259e209dff2273bb806e4ed Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 5 Sep 2020 18:09:14 -0700 Subject: [PATCH 037/124] all words in function names --- Firmware/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 3c97617c..7ad9392d 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -54,7 +54,7 @@ class OpenOCDCMSampler(object): # For using nm -CS if field[2] in (b't', b'T', b'w', b'W'): addr = int(field[0], 16) - func = field[3] + func = ' '.join(field[3:-1]) size = int(field[1], 16) # # For using readelf -s # if field[3] == b'FUNC': From f9fa06c86fe1720568c6e57f4bd93fecaec4fc8b Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 5 Sep 2020 21:15:45 -0400 Subject: [PATCH 038/124] Fix all words in func names --- Firmware/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 7ad9392d..4fc25a97 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -54,7 +54,7 @@ class OpenOCDCMSampler(object): # For using nm -CS if field[2] in (b't', b'T', b'w', b'W'): addr = int(field[0], 16) - func = ' '.join(field[3:-1]) + func = b' '.join(field[3:]) size = int(field[1], 16) # # For using readelf -s # if field[3] == b'FUNC': From 062c3978474b2d46ca9f210913c20feebc1c3912 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 7 Sep 2020 20:54:12 +0200 Subject: [PATCH 039/124] implement CI check against std::isnan --- .github/workflows/compile.yaml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index a433132f..f8468433 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -108,9 +108,22 @@ jobs: mv tup_build.sh tup_build.bat # in reality this is a .bat script on windows .\tup_build.bat - #code-checks: - # runs-on: ubuntu-latest - # steps: + code-checks: + strategy: + fail-fast: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Check that we're not using std::isnan + run: | + cd ${{ github.workspace }}/Firmware + + if grep 'std::isnan' -R .; then + echo "Don't use std::isnan because it's not compatible with '-ffast-math'. Use is_nan() instead." + return 1; + fi + # TODO: # - check if enums.py is consistent with yaml # - clang-format check From 4b83470bda5cf13ad23c3259ec55ac63feef7f2c Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 1 Sep 2020 15:08:54 +0200 Subject: [PATCH 040/124] backport changes to ODrives older than v3.5 --- Firmware/Board/v3/Src/adc.c | 3 --- Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c | 9 --------- Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c | 9 --------- 3 files changed, 21 deletions(-) diff --git a/Firmware/Board/v3/Src/adc.c b/Firmware/Board/v3/Src/adc.c index 52e00b72..ede9c05f 100644 --- a/Firmware/Board/v3/Src/adc.c +++ b/Firmware/Board/v3/Src/adc.c @@ -334,9 +334,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /* ADC3 interrupt Init */ - //HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); // must be on the same level as control loop - //HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c index bc2ddddf..8444ff78 100644 --- a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c +++ b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_2.c @@ -215,9 +215,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); - /* ADC1 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ @@ -253,9 +250,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /* ADC2 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -279,9 +273,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /* ADC3 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ diff --git a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c index 31ce77d0..5f1115f5 100644 --- a/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c +++ b/Firmware/Board/v3/Src/prev_board_ver/adc_V3_4.c @@ -214,9 +214,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); - /* ADC1 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ @@ -251,9 +248,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /* ADC2 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -277,9 +271,6 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - /* ADC3 interrupt Init */ - HAL_NVIC_SetPriority(ADC_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); /* USER CODE BEGIN ADC3_MspInit 1 */ /* USER CODE END ADC3_MspInit 1 */ From 7fd0806d493edd5eacb569d82dfcf2836435f984 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 23 Sep 2020 16:20:10 +0200 Subject: [PATCH 041/124] introduce InputPort and OutputPort, don't use NAN The InputPort/OutputPort infrastructure facilitates safer data paths between components: OutputPorts store a value and the age of the value measured in number of control loop iterations. InputPorts can be connected to various sources, for instance an OutputPort. InputPorts expose the values to consumers in the form of std::optional to reflect the fact that an InputPort can be dangling or connected to a stale OutputPort. --- CHANGELOG.md | 1 + Firmware/Board/v3/board.cpp | 45 ++-- Firmware/MotorControl/async_estimator.cpp | 33 ++- Firmware/MotorControl/async_estimator.hpp | 16 +- Firmware/MotorControl/axis.cpp | 101 ++++---- Firmware/MotorControl/component.hpp | 162 +++++++++++++ Firmware/MotorControl/controller.cpp | 61 +++-- Firmware/MotorControl/controller.hpp | 11 +- Firmware/MotorControl/encoder.cpp | 61 ++--- Firmware/MotorControl/encoder.hpp | 11 +- Firmware/MotorControl/foc.cpp | 184 ++++++++------- Firmware/MotorControl/foc.hpp | 40 ++-- Firmware/MotorControl/main.cpp | 42 +++- Firmware/MotorControl/motor.cpp | 215 ++++++++++-------- Firmware/MotorControl/motor.hpp | 30 +-- Firmware/MotorControl/odrive_main.h | 2 +- .../MotorControl/open_loop_controller.cpp | 34 +-- .../MotorControl/open_loop_controller.hpp | 19 +- Firmware/MotorControl/phase_control_law.hpp | 44 ++-- .../MotorControl/sensorless_estimator.cpp | 72 +++--- .../MotorControl/sensorless_estimator.hpp | 13 +- Firmware/Tupfile.lua | 2 + Firmware/communication/ascii_protocol.cpp | 4 +- Firmware/communication/can_simple.cpp | 30 ++- Firmware/fibre/cpp/interfaces_template.j2 | 2 + Firmware/fibre/tools/interface_generator.py | 6 +- Firmware/odrive-interface.yaml | 83 ++++--- tools/odrive/enums.py | 23 +- tools/odrive/tests/encoder_test.py | 2 +- tools/odrive/tests/test_runner.py | 2 +- tools/odrive/utils.py | 1 + 31 files changed, 833 insertions(+), 519 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aad81ce5..04633143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Please add a note of your changes below this heading if you make a Pull Request. * `.motor.config.acim_slip_velocity` was moved to `.async_estimator.config.slip_velocity`. * `.encoder.config.idx_search_unidirectional` was removed. Offset calibration direction is fully defined by the sign of `.encoder.config.calib_scan_omega` and how the motor is wired up. * The unit of `.sensorless_estimator.vel_estimate` was changed from `rad/s` to `turns/s`. +* Several properties were changed to readonly. # Release Candidate ## [0.5.1] - Date TBD diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index 2f0c7a10..447264a4 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -400,22 +400,33 @@ void start_timers() { } } -static bool fetch_and_reset_adcs(float* current0_phB, float* current0_phC, float* current1_phB, float* current1_phC) { +static bool fetch_and_reset_adcs( + std::optional* current0, + std::optional* current1) { bool all_adcs_done = (ADC1->SR & ADC_SR_JEOC) == ADC_SR_JEOC && (ADC2->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC) && (ADC3->SR & (ADC_SR_EOC | ADC_SR_JEOC)) == (ADC_SR_EOC | ADC_SR_JEOC); if (!all_adcs_done) { return false; } - - bool m0_current_valid = m0_gate_driver.is_ready(); - bool m1_current_valid = m1_gate_driver.is_ready(); vbus_sense_adc_cb(ADC1->JDR1); - *current0_phB = m0_current_valid ? motors[0].phase_current_from_adcval(ADC2->JDR1) : NAN; - *current0_phC = m0_current_valid ? motors[0].phase_current_from_adcval(ADC3->JDR1) : NAN; - *current1_phB = m1_current_valid ? motors[1].phase_current_from_adcval(ADC2->DR) : NAN; - *current1_phC = m1_current_valid ? motors[1].phase_current_from_adcval(ADC3->DR) : NAN; + + if (m0_gate_driver.is_ready()) { + std::optional phB = motors[0].phase_current_from_adcval(ADC2->JDR1); + std::optional phC = motors[0].phase_current_from_adcval(ADC3->JDR1); + if (phB.has_value() && phC.has_value()) { + *current0 = {-*phB - *phC, *phB, *phC}; + } + } + + if (m1_gate_driver.is_ready()) { + std::optional phB = motors[1].phase_current_from_adcval(ADC2->DR); + std::optional phC = motors[1].phase_current_from_adcval(ADC3->DR); + if (phB.has_value() && phC.has_value()) { + *current1 = {-*phB - *phC, *phB, *phC}; + } + } ADC1->SR = ~(ADC_SR_JEOC); ADC2->SR = ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_OVR); @@ -492,18 +503,16 @@ void ControlLoop_IRQHandler(void) { uint32_t timestamp = timestamp_; // Ensure that all the ADCs are done - float current0_phB; - float current0_phC; - float current1_phB; - float current1_phC; + std::optional current0; + std::optional current1; - if (!fetch_and_reset_adcs(¤t0_phB, ¤t0_phC, ¤t1_phB, ¤t1_phC)) { + if (!fetch_and_reset_adcs(¤t0, ¤t1)) { motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); } - motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, {-current0_phB - current0_phC, current0_phB, current0_phC}); - motors[1].current_meas_cb(timestamp, {-current1_phB - current1_phC, current1_phB, current1_phC}); + motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, current0); + motors[1].current_meas_cb(timestamp, current1); odrv.control_loop_cb(timestamp); @@ -511,13 +520,13 @@ void ControlLoop_IRQHandler(void) { // let's wait for them just to be sure. while (!(ADC2->SR & ADC_SR_EOC)); - if (!fetch_and_reset_adcs(¤t0_phB, ¤t0_phC, ¤t1_phB, ¤t1_phC)) { + if (!fetch_and_reset_adcs(¤t0, ¤t1)) { motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); } - motors[0].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT, {-current0_phB - current0_phC, current0_phB, current0_phC}); - motors[1].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1), {-current1_phB - current1_phC, current1_phB, current1_phC}); + motors[0].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT, current0); + motors[1].dc_calib_cb(timestamp + TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1), current1); motors[0].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1) - TIM1_INIT_COUNT); motors[1].pwm_update_cb(timestamp + 3 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)); diff --git a/Firmware/MotorControl/async_estimator.cpp b/Firmware/MotorControl/async_estimator.cpp index 24541b56..88cad20a 100644 --- a/Firmware/MotorControl/async_estimator.cpp +++ b/Firmware/MotorControl/async_estimator.cpp @@ -3,30 +3,28 @@ #include void AsyncEstimator::update(uint32_t timestamp) { - float rotor_phase = rotor_phase_src_ ? *rotor_phase_src_ : NAN; - float rotor_phase_vel = rotor_phase_vel_src_ ? *rotor_phase_vel_src_ : NAN; - float id = id_src_ ? *id_src_ : NAN; - float iq = iq_src_ ? *iq_src_ : NAN; + std::optional rotor_phase = rotor_phase_src_.get_current(); + std::optional rotor_phase_vel = rotor_phase_vel_src_.get_current(); + std::optional idq = idq_src_.get_current(); - if (std::isnan(rotor_phase) || std::isnan(rotor_phase_vel)) { - stator_phase_vel_ = NAN; - stator_phase_ = NAN; + if (!rotor_phase.has_value() || !rotor_phase_vel.has_value() || !idq.has_value()) { active_ = false; return; } + auto [id, iq] = *idq; + + float dt = (float)(timestamp - last_timestamp_) / (float)TIM_1_8_CLOCK_HZ; + last_timestamp_ = timestamp; + if (!active_) { - last_timestamp_ = timestamp; - stator_phase_vel_ = 0.0f; - stator_phase_ = 0.0f; + // Skip first iteration and use it to reset state + rotor_flux_ = 0.0f; + phase_offset_ = 0.0f; active_ = true; return; } - last_timestamp_ = timestamp; - - float dt = (float)(timestamp - last_timestamp_) / (float)TIM_1_8_CLOCK_HZ; - // Note that the effect of the current commands on the real currents is actually 1.5 PWM cycles later // However the rotor time constant is (usually) so slow that it doesn't matter // So we elect to write it as if the effect is immediate, to have cleaner code @@ -40,9 +38,8 @@ void AsyncEstimator::update(uint32_t timestamp) { if (!acceptable_vel) slip_velocity = 0.0f; slip_vel_ = slip_velocity; // reporting only - stator_phase_vel_ = rotor_phase_vel + slip_velocity; - phase_offset_ += slip_velocity * dt; - phase_offset_ = wrap_pm_pi(phase_offset_); - stator_phase_ = wrap_pm_pi(rotor_phase + phase_offset_); + stator_phase_vel_ = *rotor_phase_vel + slip_velocity; + phase_offset_ = wrap_pm_pi(phase_offset_ + slip_velocity * dt); + stator_phase_ = wrap_pm_pi(*rotor_phase + phase_offset_); } diff --git a/Firmware/MotorControl/async_estimator.hpp b/Firmware/MotorControl/async_estimator.hpp index ea0189f7..3505ce53 100644 --- a/Firmware/MotorControl/async_estimator.hpp +++ b/Firmware/MotorControl/async_estimator.hpp @@ -3,6 +3,7 @@ #include #include +#include class AsyncEstimator : public ComponentBase { public: @@ -16,21 +17,20 @@ public: Config_t config_; // Inputs - float* rotor_phase_src_ = nullptr; - float* rotor_phase_vel_src_ = nullptr; - float* id_src_ = nullptr; - float* iq_src_ = nullptr; + InputPort rotor_phase_src_; + InputPort rotor_phase_vel_src_; + InputPort idq_src_; // State variables float active_ = false; uint32_t last_timestamp_ = 0; float rotor_flux_ = 0.0f; // [A] - float slip_vel_ = 0.0f; // [rad/s electrical] - float phase_offset_ = 0.0f; // [rad electrical] + float phase_offset_ = 0.0f; // [A] // Outputs - float stator_phase_vel_ = NAN; // [rad/s] rotor flux angular velocity estimate - float stator_phase_ = NAN; // [rad] rotor flux phase angle estimate + OutputPort slip_vel_ = 0.0f; // [rad/s electrical] + OutputPort stator_phase_vel_ = 0.0f; // [rad/s] rotor flux angular velocity estimate + OutputPort stator_phase_ = 0.0f; // [rad] rotor flux phase angle estimate }; #endif // __ASYNC_ESTIMATOR_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 99aa7b80..a0b5ef8e 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -202,10 +202,8 @@ bool Axis::watchdog_check() { bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed) { CRITICAL_SECTION() { // Reset state variables - open_loop_controller_.Id_setpoint_ = NAN; - open_loop_controller_.Iq_setpoint_ = NAN; - open_loop_controller_.Vd_setpoint_ = NAN; - open_loop_controller_.Vq_setpoint_ = NAN; + open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f}; + open_loop_controller_.Vdq_setpoint_ = {0.0f, 0.0f}; open_loop_controller_.phase_ = 0.0f; open_loop_controller_.phase_vel_ = NAN; @@ -218,17 +216,15 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme open_loop_controller_.total_distance_ = 0.0f; motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL; - motor_.current_control_.Id_setpoint_src_ = &open_loop_controller_.Id_setpoint_; - motor_.current_control_.Iq_setpoint_src_ = &open_loop_controller_.Iq_setpoint_; - motor_.current_control_.Vd_setpoint_src_ = &open_loop_controller_.Vd_setpoint_; - motor_.current_control_.Vq_setpoint_src_ = &open_loop_controller_.Vq_setpoint_; - motor_.current_control_.phase_src_ = - async_estimator_.rotor_phase_src_ = - &open_loop_controller_.phase_; - motor_.phase_vel_src_ = - motor_.current_control_.phase_vel_src_ = - async_estimator_.rotor_phase_vel_src_ = - &open_loop_controller_.phase_vel_; + motor_.current_control_.Idq_setpoint_src_.connect_to(&open_loop_controller_.Idq_setpoint_); + motor_.current_control_.Vdq_setpoint_src_.connect_to(&open_loop_controller_.Vdq_setpoint_); + + motor_.current_control_.phase_src_.connect_to(&open_loop_controller_.phase_); + async_estimator_.rotor_phase_src_.connect_to(&open_loop_controller_.phase_); + + motor_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); + motor_.current_control_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); + async_estimator_.rotor_phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); } wait_for_control_iteration(); @@ -239,8 +235,8 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f; while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) { - bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_ - lockin_config.vel) <= std::numeric_limits::epsilon(); - bool reached_target_dist = open_loop_controller_.total_distance_ * dir >= lockin_config.finish_distance * dir; + bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_.get_any().value_or(0.0f) - lockin_config.vel) <= std::numeric_limits::epsilon(); + bool reached_target_dist = open_loop_controller_.total_distance_.get_any().value_or(0.0f) * dir >= lockin_config.finish_distance * dir; // Check if terminal condition is reached bool terminal_condition = (reached_target_vel && lockin_config.finish_on_vel) @@ -282,21 +278,21 @@ bool Axis::start_closed_loop_control() { // Hook up the data paths between the components CRITICAL_SECTION() { if (sensorless_mode) { - controller_.pos_estimate_linear_src_ = nullptr; - controller_.pos_estimate_circular_src_ = nullptr; - controller_.pos_wrap_src_ = nullptr; - controller_.vel_estimate_src_ = &sensorless_estimator_.vel_estimate_; + controller_.pos_estimate_linear_src_.disconnect(); + controller_.pos_estimate_circular_src_.disconnect(); + controller_.pos_wrap_src_.disconnect(); + controller_.vel_estimate_src_.connect_to(&sensorless_estimator_.vel_estimate_); } else if (controller_.config_.load_encoder_axis < AXIS_COUNT) { Axis* ax = &axes[controller_.config_.load_encoder_axis]; - controller_.pos_estimate_circular_src_ = &ax->encoder_.pos_circular_; - controller_.pos_wrap_src_ = &controller_.config_.circular_setpoint_range; - controller_.pos_estimate_linear_src_ = &ax->encoder_.pos_estimate_; - controller_.vel_estimate_src_ = &ax->encoder_.vel_estimate_; + controller_.pos_estimate_circular_src_.connect_to(&ax->encoder_.pos_circular_); + controller_.pos_wrap_src_.connect_to(&controller_.config_.circular_setpoint_range); + controller_.pos_estimate_linear_src_.connect_to(&ax->encoder_.pos_estimate_); + controller_.vel_estimate_src_.connect_to(&ax->encoder_.vel_estimate_); } else { - controller_.pos_estimate_circular_src_ = nullptr; - controller_.pos_estimate_linear_src_ = nullptr; - controller_.pos_wrap_src_ = nullptr; - controller_.vel_estimate_src_ = nullptr; + controller_.pos_estimate_circular_src_.disconnect(); + controller_.pos_estimate_linear_src_.disconnect(); + controller_.pos_wrap_src_.disconnect(); + controller_.vel_estimate_src_.disconnect(); controller_.set_error(Controller::ERROR_INVALID_LOAD_ENCODER); return false; } @@ -304,14 +300,14 @@ bool Axis::start_closed_loop_control() { // To avoid any transient on startup, we intialize the setpoint to be the current position // note - input_pos_ is not set here. It is set to 0 earlier in this method and velocity control is used. if (controller_.config_.control_mode >= Controller::CONTROL_MODE_POSITION_CONTROL) { - float* pos_init_src = controller_.config_.circular_setpoints ? + std::optional pos_init = (controller_.config_.circular_setpoints ? controller_.pos_estimate_circular_src_ : - controller_.pos_estimate_linear_src_; - if (!pos_init_src) { + controller_.pos_estimate_linear_src_).get_any(); + if (!pos_init.has_value()) { return false; } else { - controller_.pos_setpoint_ = *pos_init_src; - controller_.input_pos_ = *pos_init_src; + controller_.pos_setpoint_ = *pos_init; + controller_.input_pos_ = *pos_init; } } controller_.input_pos_updated(); @@ -319,27 +315,28 @@ bool Axis::start_closed_loop_control() { // Avoid integrator windup issues controller_.vel_integrator_torque_ = 0.0f; - motor_.torque_setpoint_src_ = &controller_.torque_output_; + motor_.torque_setpoint_src_.connect_to(&controller_.torque_output_); motor_.direction_ = sensorless_mode ? 1.0f : encoder_.config_.direction; motor_.current_control_.enable_current_control_src_ = motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL; - motor_.current_control_.Id_setpoint_src_ = &motor_.Id_setpoint_; - motor_.current_control_.Iq_setpoint_src_ = &motor_.Iq_setpoint_; - motor_.current_control_.Vd_setpoint_src_ = &motor_.Vd_setpoint_; - motor_.current_control_.Vq_setpoint_src_ = &motor_.Vq_setpoint_; - motor_.current_control_.phase_src_ = - async_estimator_.rotor_phase_src_ = - sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_; - motor_.phase_vel_src_ = - motor_.current_control_.phase_vel_src_ = - async_estimator_.rotor_phase_vel_src_ = - sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_; + motor_.current_control_.Idq_setpoint_src_.connect_to(&motor_.Idq_setpoint_); + motor_.current_control_.Vdq_setpoint_src_.connect_to(&motor_.Vdq_setpoint_); + + OutputPort* phase_src = sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_; + motor_.current_control_.phase_src_.connect_to(phase_src); + async_estimator_.rotor_phase_src_.connect_to(phase_src); + + OutputPort* phase_vel_src = sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_; + motor_.phase_vel_src_.connect_to(phase_vel_src); + motor_.current_control_.phase_vel_src_.connect_to(phase_vel_src); + async_estimator_.rotor_phase_vel_src_.connect_to(phase_vel_src); if (sensorless_mode) { // Make the final velocity of the loĉk-in spin the setpoint of the // closed loop controller to allow for smooth transition. - controller_.input_vel_ = config_.sensorless_ramp.vel / (2 * M_PI); - controller_.vel_setpoint_ = config_.sensorless_ramp.vel / (2 * M_PI); + float vel = config_.sensorless_ramp.vel / (2.0f * M_PI * motor_.config_.pole_pairs); + controller_.input_vel_ = vel; + controller_.vel_setpoint_ = vel; } } @@ -452,14 +449,14 @@ void Axis::run_state_machine_loop() { // converge. If the DRV chip is unpowered, the motor will not become ready // but we still enter idle state. for (size_t i = 0; i < 2000; ++i) { - bool motor_is_ready = std::isnan(motor_.current_meas_.phA) - && std::isnan(motor_.current_meas_.phB) - && std::isnan(motor_.current_meas_.phC); - if (motor_is_ready) { + if (motor_.current_meas_.has_value()) { break; } + osDelay(1); } + sensorless_estimator_.error_ &= ~SensorlessEstimator::ERROR_UNKNOWN_CURRENT_MEASUREMENT; + for (;;) { // Load the task chain if a specific request is pending if (requested_state_ != AXIS_STATE_UNDEFINED) { diff --git a/Firmware/MotorControl/component.hpp b/Firmware/MotorControl/component.hpp index cfaeb828..aa156e1c 100644 --- a/Firmware/MotorControl/component.hpp +++ b/Firmware/MotorControl/component.hpp @@ -2,6 +2,8 @@ #define __COMPONENT_HPP #include +#include +#include class ComponentBase { public: @@ -17,4 +19,164 @@ public: virtual void update(uint32_t timestamp) = 0; }; + +template +class InputPort; + +/** + * @brief An output port stores a value for consumption by a connecting input + * port. + * + * Output ports are supposed to be reset at the beginning of a control loop + * iteration. This ensures that connecting input ports don't use an outdated + * value and, more importantly, ensures proper handling if the producer of the + * value is incapable of producing the value for any reason. + * + * Member functions of this class are not thread-safe unless noted otherwise. + */ +template +class OutputPort { +public: + /** + * @brief Initializes the output port with the specified value. + * + * An initialization value is required for get_any() to work properly. + * get_current() and get_previous() cannot be used to fetch the + * initialization value. + */ + OutputPort(T val) : content_(val) {} + + /** + * @brief Updates the underlying value of this output port. + */ + void operator=(T value) { + content_ = value; + age_ = 0; + } + + /** + * @brief Marks the contained value as outdated. The value is not actually + * deleted and can still be accessed through some of the member functions + * of this class. + */ + void reset() { + // This will eventually overflow to 0 so get_current() could + // theoretically return a very old value however it is very likely that + // the motor will be long disarmed by then. + age_++; + } + + /** + * @brief Returns the value from this control loop iteration or std::nullopt + * if the value was not yet set during this control loop iteration. + */ + std::optional get_current() { + if (age_ == 0) { + return content_; + } else { + return std::nullopt; + } + } + + /** + * @brief Returns the value from exactly the previous control loop iteration. + * + * If during the last iteration no value was set or the value was already + * overwritten during this control loop iteration then this function returns + * std::nullopt. + */ + std::optional get_previous() { + if (age_ == 1) { + return content_; + } else { + return std::nullopt; + } + } + + /** + * @brief Returns the value contained in this output port with disregard of + * when the value was set. + * + * This function is thread-safe if load/store operations of T are atomic. + */ + std::optional get_any() { + return content_; + } + +private: + uint32_t age_ = 2; // Age in number of control loop iterations + T content_; +}; + +/** + * @brief An input port provides a value from the source to which it's configured. + * + * The source can be one of: + * - an internally stored value + * - an externally stored value (referenced by a pointer) + * - an external OutputPort (referenced by a pointer) + * - none (all queries will return std::nullopt) + * + * Member functions of this class are not thread-safe unless otherwise noted. + */ +template +class InputPort { +public: + void connect_to(OutputPort* input_port) { + content_ = input_port; + } + + void connect_to(T* input_ptr) { + content_ = input_ptr; + } + + void disconnect() { + content_ = (OutputPort*)nullptr; + } + + std::optional get_current() { + if (content_.index() == 2) { + OutputPort* ptr = std::get<2>(content_); + return ptr ? ptr->get_current() : std::nullopt; + } else if (content_.index() == 1) { + T* ptr = std::get<1>(content_); + return ptr ? std::make_optional(*ptr) : std::nullopt; + } else { + return std::get<0>(content_); + } + } + + // TODO: probably it makes sense to let the application define that it's + // ok for this input port to fetch the value from the last iteration. + // This would provide a general way to resolve same-iteration data path cycles. + + //std::optional get_previous() { + // if (content_.index() == 2) { + // OutputPort* ptr = std::get<2>(content_); + // return ptr ? ptr->get_previous() : std::nullopt; + // } else if (content_.index() == 1) { + // T* ptr = std::get<1>(content_); + // return ptr ? std::make_optional(*ptr) : std::nullopt; + // } else { + // return std::get<0>(content_); + // } + //} + + std::optional get_any() { + if (content_.index() == 2) { + OutputPort* ptr = std::get<2>(content_); + return ptr ? ptr->get_any() : std::nullopt; + } else if (content_.index() == 1) { + T* ptr = std::get<1>(content_); + return ptr ? std::make_optional(*ptr) : std::nullopt; + } else { + return std::get<0>(content_); + } + } + +private: + std::variant*> content_; +}; + + #endif // __COMPONENT_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index a93acb51..c67c7133 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -99,23 +99,21 @@ static float limitVel(const float vel_limit, const float vel_estimate, const flo } bool Controller::update() { - float pos_estimate_linear = pos_estimate_linear_src_ ? *pos_estimate_linear_src_ : NAN; - float pos_estimate_circular = pos_estimate_circular_src_ ? *pos_estimate_circular_src_ : NAN; - float pos_wrap = pos_wrap_src_ ? *pos_wrap_src_ : NAN; - float vel_estimate = vel_estimate_src_ ? *vel_estimate_src_ : NAN; + std::optional pos_estimate_linear = pos_estimate_linear_src_.get_current(); + std::optional pos_estimate_circular = pos_estimate_circular_src_.get_current(); + std::optional pos_wrap = pos_wrap_src_.get_current(); + std::optional vel_estimate = vel_estimate_src_.get_current(); - // Reset output just in case the controller fails for any reason - torque_output_ = NAN; + std::optional anticogging_pos_estimate = axis_->encoder_.pos_estimate_.get_current(); + std::optional anticogging_vel_estimate = axis_->encoder_.vel_estimate_.get_current(); - // Calib_anticogging is only true when calibration is occurring, so we can't block anticogging_pos - float anticogging_pos = axis_->encoder_.pos_estimate_ / axis_->encoder_.getCoggingRatio(); if (config_.anticogging.calib_anticogging) { - if (std::isnan(axis_->encoder_.pos_estimate_) || std::isnan(axis_->encoder_.vel_estimate_)) { + if (!anticogging_pos_estimate.has_value() || !anticogging_vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } // non-blocking - anticogging_calibration(axis_->encoder_.pos_estimate_, axis_->encoder_.vel_estimate_); + anticogging_calibration(*anticogging_pos_estimate, *anticogging_vel_estimate); } // TODO also enable circular deltas for 2nd order filter, etc. @@ -160,8 +158,16 @@ bool Controller::update() { } break; case INPUT_MODE_MIRROR: { if (config_.axis_to_mirror < AXIS_COUNT) { - pos_setpoint_ = axes[config_.axis_to_mirror].encoder_.pos_estimate_ * config_.mirror_ratio; - vel_setpoint_ = axes[config_.axis_to_mirror].encoder_.vel_estimate_ * config_.mirror_ratio; + std::optional other_pos = axes[config_.axis_to_mirror].encoder_.pos_estimate_.get_current(); + std::optional other_vel = axes[config_.axis_to_mirror].encoder_.vel_estimate_.get_current(); + + if (!other_pos.has_value() || !other_vel.has_value()) { + set_error(ERROR_INVALID_ESTIMATE); + return false; + } + + pos_setpoint_ = *other_pos * config_.mirror_ratio; + vel_setpoint_ = *other_vel * config_.mirror_ratio; } else { set_error(ERROR_INVALID_MIRROR_AXIS); return false; @@ -193,7 +199,7 @@ bool Controller::update() { torque_setpoint_ = traj_step.Ydd * config_.inertia; axis_->trap_traj_.t_ += current_meas_period; } - anticogging_pos = pos_setpoint_; // FF the position setpoint instead of the pos_estimate + anticogging_pos_estimate = pos_setpoint_; // FF the position setpoint instead of the pos_estimate } break; default: { set_error(ERROR_INVALID_INPUT_MODE); @@ -210,21 +216,21 @@ bool Controller::update() { float pos_err; if (config_.circular_setpoints) { - if (std::isnan(pos_estimate_circular) || std::isnan(pos_wrap)) { + if (!pos_estimate_circular.has_value() || !pos_wrap.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } // Keep pos setpoint from drifting - pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap_src_); + pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap); // Circular delta - pos_err = pos_setpoint_ - pos_estimate_circular; - pos_err = wrap_pm(pos_err, 0.5f * pos_wrap); + pos_err = pos_setpoint_ - *pos_estimate_circular; + pos_err = wrap_pm(pos_err, 0.5f * *pos_wrap); } else { - if (std::isnan(pos_estimate_linear)) { + if (!pos_estimate_linear.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - pos_err = pos_setpoint_ - pos_estimate_linear; + pos_err = pos_setpoint_ - *pos_estimate_linear; } vel_des += config_.pos_gain * pos_err; @@ -243,11 +249,11 @@ bool Controller::update() { // Check for overspeed fault (done in this module (controller) for cohesion with vel_lim) if (config_.enable_overspeed_error) { // 0.0f to disable - if (std::isnan(vel_estimate)) { + if (!vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - if (std::abs(vel_estimate) > config_.vel_limit_tolerance * vel_lim) { + if (std::abs(*vel_estimate) > config_.vel_limit_tolerance * vel_lim) { set_error(ERROR_OVERSPEED); return false; } @@ -275,17 +281,22 @@ bool Controller::update() { // We get the current position and apply a current feed-forward // ensuring that we handle negative encoder positions properly (-1 == motor->encoder.encoder_cpr - 1) if (anticogging_valid_ && config_.anticogging.anticogging_enabled) { + if (!anticogging_pos_estimate.has_value()) { + set_error(ERROR_INVALID_ESTIMATE); + return false; + } + float anticogging_pos = *anticogging_pos_estimate / axis_->encoder_.getCoggingRatio(); torque += config_.anticogging.cogging_map[std::clamp(mod((int)anticogging_pos, 3600), 0, 3600)]; } float v_err = 0.0f; if (config_.control_mode >= CONTROL_MODE_VELOCITY_CONTROL) { - if (std::isnan(vel_estimate)) { + if (!vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - v_err = vel_des - vel_estimate; + v_err = vel_des - *vel_estimate; torque += (vel_gain * gain_scheduling_multiplier) * v_err; // Velocity integral action before limiting @@ -294,11 +305,11 @@ bool Controller::update() { // Velocity limiting in current mode if (config_.control_mode < CONTROL_MODE_VELOCITY_CONTROL && config_.enable_current_mode_vel_limit) { - if (std::isnan(vel_estimate)) { + if (!vel_estimate.has_value()) { set_error(ERROR_INVALID_ESTIMATE); return false; } - torque = limitVel(config_.vel_limit, vel_estimate, vel_gain, torque); + torque = limitVel(config_.vel_limit, *vel_estimate, vel_gain, torque); } // Torque limiting diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index 483dc316..bcd2a9c6 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -75,10 +75,10 @@ public: Error error_ = ERROR_NONE; // Inputs - float* pos_estimate_linear_src_ = nullptr; - float* pos_estimate_circular_src_ = nullptr; - float* vel_estimate_src_ = nullptr; - float* pos_wrap_src_ = nullptr; + InputPort pos_estimate_linear_src_; + InputPort pos_estimate_circular_src_; + InputPort vel_estimate_src_; + InputPort pos_wrap_src_; float pos_setpoint_ = 0.0f; // [turns] float vel_setpoint_ = 0.0f; // [turn/s] @@ -99,11 +99,10 @@ public: bool anticogging_valid_ = false; // Outputs - float torque_output_ = NAN; + OutputPort torque_output_ = 0.0f; // custom setters void set_input_pos(float value) { input_pos_ = value; input_pos_updated(); } - }; #endif // __CONTROLLER_HPP diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 39a7a904..3efc142e 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -215,10 +215,8 @@ bool Encoder::run_offset_calibration() { CRITICAL_SECTION() { // Reset state variables - axis_->open_loop_controller_.Id_setpoint_ = NAN; - axis_->open_loop_controller_.Iq_setpoint_ = NAN; - axis_->open_loop_controller_.Vd_setpoint_ = NAN; - axis_->open_loop_controller_.Vq_setpoint_ = NAN; + axis_->open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f}; + axis_->open_loop_controller_.Vdq_setpoint_ = {0.0f, 0.0f}; axis_->open_loop_controller_.phase_ = 0.0f; axis_->open_loop_controller_.phase_vel_ = NAN; @@ -232,17 +230,15 @@ bool Encoder::run_offset_calibration() { axis_->open_loop_controller_.total_distance_ = 0.0f; axis_->motor_.current_control_.enable_current_control_src_ = (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL); - axis_->motor_.current_control_.Id_setpoint_src_ = &axis_->open_loop_controller_.Id_setpoint_; - axis_->motor_.current_control_.Iq_setpoint_src_ = &axis_->open_loop_controller_.Iq_setpoint_; - axis_->motor_.current_control_.Vd_setpoint_src_ = &axis_->open_loop_controller_.Vd_setpoint_; - axis_->motor_.current_control_.Vq_setpoint_src_ = &axis_->open_loop_controller_.Vq_setpoint_; - axis_->motor_.current_control_.phase_src_ = - axis_->async_estimator_.rotor_phase_src_ = - &axis_->open_loop_controller_.phase_; - axis_->motor_.phase_vel_src_ = - axis_->motor_.current_control_.phase_vel_src_ = - axis_->async_estimator_.rotor_phase_vel_src_ = - &axis_->open_loop_controller_.phase_vel_; + axis_->motor_.current_control_.Idq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Idq_setpoint_); + axis_->motor_.current_control_.Vdq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Vdq_setpoint_); + + axis_->motor_.current_control_.phase_src_.connect_to(&axis_->open_loop_controller_.phase_); + axis_->async_estimator_.rotor_phase_src_.connect_to(&axis_->open_loop_controller_.phase_); + + axis_->motor_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); + axis_->motor_.current_control_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); + axis_->async_estimator_.rotor_phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); } axis_->wait_for_control_iteration(); @@ -272,7 +268,7 @@ bool Encoder::run_offset_calibration() { // scan forward while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { - bool reached_target_dist = axis_->open_loop_controller_.total_distance_ >= config_.calib_scan_distance; + bool reached_target_dist = axis_->open_loop_controller_.total_distance_.get_any().value_or(-INFINITY) >= config_.calib_scan_distance; if (reached_target_dist) { break; } @@ -312,7 +308,7 @@ bool Encoder::run_offset_calibration() { // scan backwards while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { - bool reached_target_dist = axis_->open_loop_controller_.total_distance_ <= 0.0f; + bool reached_target_dist = axis_->open_loop_controller_.total_distance_.get_any().value_or(INFINITY) <= 0.0f; if (reached_target_dist) { break; } @@ -511,10 +507,6 @@ bool Encoder::update() { } else { if (!config_.ignore_illegal_hall_state) { set_error(ERROR_ILLEGAL_HALL_STATE); - pos_estimate_ = NAN; - vel_estimate_ = NAN; - phase_ = NAN; - phase_vel_ = NAN; return false; } } @@ -540,10 +532,6 @@ bool Encoder::update() { spi_error_rate_ += current_meas_period * (1.0f - spi_error_rate_); if (spi_error_rate_ > 0.005f) { set_error(ERROR_ABS_SPI_COM_FAIL); - pos_estimate_ = NAN; - vel_estimate_ = NAN; - phase_ = NAN; - phase_vel_ = NAN; return false; } } else { @@ -561,11 +549,7 @@ bool Encoder::update() { }break; default: { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); - pos_estimate_ = NAN; - vel_estimate_ = NAN; - phase_ = NAN; - phase_vel_ = NAN; - return false; + return false; } break; } @@ -601,8 +585,14 @@ bool Encoder::update() { // Outputs from Encoder for Controller pos_estimate_ = pos_estimate_counts_ / (float)config_.cpr; vel_estimate_ = vel_estimate_counts_ / (float)config_.cpr; - pos_circular_ += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 0.5f); - pos_circular_ = fmodf_pos(pos_circular_, axis_->controller_.config_.circular_setpoint_range); + + // TODO: we should strictly require that this value is from the previous iteration + // to avoid spinout scenarios. However that requires a proper way to reset + // the encoder from error states. + float pos_circular = pos_circular_.get_any().value_or(0.0f); + pos_circular += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 0.5f); + pos_circular = fmodf_pos(pos_circular, axis_->controller_.config_.circular_setpoint_range); + pos_circular_ = pos_circular; //// run encoder count interpolation int32_t corrected_enc = count_in_cpr_ - config_.offset; @@ -628,13 +618,10 @@ bool Encoder::update() { //TODO avoid recomputing elec_rad_per_enc every time float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); float ph = elec_rad_per_enc * (interpolated_enc - config_.offset_float); - // ph = fmodf(ph, 2*M_PI); + if (is_ready_) { phase_ = wrap_pm_pi(ph) * config_.direction; - phase_vel_ = (2*M_PI) * vel_estimate_ * axis_->motor_.config_.pole_pairs * config_.direction; - } else { - phase_ = NAN; - phase_vel_ = NAN; + phase_vel_ = (2*M_PI) * *vel_estimate_.get_current() * axis_->motor_.config_.pole_pairs * config_.direction; } return true; diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index e439a104..3cfe357d 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -5,6 +5,7 @@ #include #include "utils.hpp" #include +#include "component.hpp" class Encoder : public ODriveIntf::EncoderIntf { @@ -86,8 +87,8 @@ public: int32_t shadow_count_ = 0; int32_t count_in_cpr_ = 0; float interpolation_ = 0.0f; - float phase_ = 0.0f; // [rad] - float phase_vel_ = 0.0f; // [rad/s] + OutputPort phase_ = 0.0f; // [rad] + OutputPort phase_vel_ = 0.0f; // [rad/s] float pos_estimate_counts_ = 0.0f; // [count] float pos_cpr_counts_ = 0.0f; // [count] float vel_estimate_counts_ = 0.0f; // [count/s] @@ -97,9 +98,9 @@ public: int32_t pos_abs_ = 0; float spi_error_rate_ = 0.0f; - float pos_estimate_ = 0.0f; // [turn] - float vel_estimate_ = 0.0f; // [turn/s] - float pos_circular_ = 0.0f; // [turn] + OutputPort pos_estimate_ = 0.0f; // [turn] + OutputPort vel_estimate_ = 0.0f; // [turn/s] + OutputPort pos_circular_ = 0.0f; // [turn] bool pos_estimate_valid_ = false; bool vel_estimate_valid_ = false; diff --git a/Firmware/MotorControl/foc.cpp b/Firmware/MotorControl/foc.cpp index a1b2d3df..babf683f 100644 --- a/Firmware/MotorControl/foc.cpp +++ b/Firmware/MotorControl/foc.cpp @@ -3,26 +3,34 @@ #include Motor::Error AlphaBetaFrameController::on_measurement( - float vbus_voltage, std::array currents, + std::optional vbus_voltage, + std::optional> currents, uint32_t input_timestamp) { - // Clarke transform - float Ialpha = currents[0]; - float Ibeta = one_by_sqrt3 * (currents[1] - currents[2]); - return on_measurement(vbus_voltage, Ialpha, Ibeta, input_timestamp); + + std::optional Ialpha_beta; + + if (currents.has_value()) { + // Clarke transform + Ialpha_beta = { + (*currents)[0], + one_by_sqrt3 * ((*currents)[1] - (*currents)[2]) + }; + } + + return on_measurement(vbus_voltage, Ialpha_beta, input_timestamp); } Motor::Error AlphaBetaFrameController::get_output( - uint32_t output_timestamp, float (&pwm_timings)[3], float* ibus) { - float mod_alpha = NAN; - float mod_beta = NAN; - - Motor::Error status = get_alpha_beta_output(output_timestamp, &mod_alpha, &mod_beta, ibus); + uint32_t output_timestamp, float (&pwm_timings)[3], + std::optional* ibus) { + std::optional mod_alpha_beta; + Motor::Error status = get_alpha_beta_output(output_timestamp, &mod_alpha_beta, ibus); if (status != Motor::ERROR_NONE) { return status; - } else if (std::isnan(mod_alpha) || std::isnan(mod_alpha)) { + } else if (!mod_alpha_beta.has_value() || std::isnan(mod_alpha_beta->first) || std::isnan(mod_alpha_beta->second)) { return Motor::ERROR_MODULATION_IS_NAN; - } else if (SVM(mod_alpha, mod_beta, &pwm_timings[0], &pwm_timings[1], &pwm_timings[2]) != 0) { + } else if (SVM(mod_alpha_beta->first, mod_alpha_beta->second, &pwm_timings[0], &pwm_timings[1], &pwm_timings[2]) != 0) { return Motor::ERROR_MODULATION_MAGNITUDE; } @@ -32,27 +40,26 @@ Motor::Error AlphaBetaFrameController::get_output( void FieldOrientedController::reset() { v_current_control_integral_d_ = 0.0f; v_current_control_integral_q_ = 0.0f; - vbus_voltage_measured_ = NAN; - Ialpha_measured_ = NAN; - Ibeta_measured_ = NAN; + vbus_voltage_measured_ = std::nullopt; + Ialpha_beta_measured_ = std::nullopt; } Motor::Error FieldOrientedController::on_measurement( - float vbus_voltage, float Ialpha, float Ibeta, - uint32_t input_timestamp) { + std::optional vbus_voltage, std::optional Ialpha_beta, + uint32_t input_timestamp) { // Store the measurements for later processing. i_timestamp_ = input_timestamp; vbus_voltage_measured_ = vbus_voltage; - Ialpha_measured_ = Ialpha; - Ibeta_measured_ = Ibeta; + Ialpha_beta_measured_ = Ialpha_beta; return Motor::ERROR_NONE; } ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output( - uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) { + uint32_t output_timestamp, std::optional* mod_alpha_beta, + std::optional* ibus) { - if (std::isnan(vbus_voltage_measured_) || std::isnan(Ialpha_measured_) || std::isnan(Ibeta_measured_)) { + if (!vbus_voltage_measured_.has_value() || !Ialpha_beta_measured_.has_value()) { // FOC didn't receive a current measurement yet. return Motor::ERROR_CONTROLLER_INITIALIZING; } else if (abs((int32_t)(i_timestamp_ - ctrl_timestamp_)) > MAX_CONTROL_LOOP_UPDATE_TO_CURRENT_UPDATE_DELTA) { @@ -64,56 +71,66 @@ ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output( // rate than current sensor updates. In this case we can reuse mod_d and // mod_q from a previous iteration. - // Fetch member variables into local variables to make the optimizer's life easier. - float vbus_voltage = vbus_voltage_measured_; - float Ialpha = Ialpha_measured_; - float Ibeta = Ibeta_measured_; - float Vd = Vd_setpoint_; - float Vq = Vq_setpoint_; - float Id_setpoint = Id_setpoint_; - float Iq_setpoint = Iq_setpoint_; - float phase = phase_; - float phase_vel = phase_vel_; - - if (std::isnan(phase) || std::isnan(phase_vel)) { - return Motor::ERROR_UNKNOWN_PHASE; - } - - // Park transform - float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); - float c_I = our_arm_cos_f32(I_phase); - float s_I = our_arm_sin_f32(I_phase); - float Id = c_I * Ialpha + s_I * Ibeta; - float Iq = c_I * Ibeta - s_I * Ialpha; - Iq_measured_ += I_measured_report_filter_k_ * (Iq - Iq_measured_); - Id_measured_ += I_measured_report_filter_k_ * (Id - Id_measured_); - - // Current error - float Ierr_d = Id_setpoint - Id; - float Ierr_q = Iq_setpoint - Iq; - - - if (enable_current_control_) { - // Check for current sense saturation - if (std::isnan(Ierr_d) || std::isnan(Ierr_q)) { - return Motor::ERROR_UNKNOWN_CURRENT; - } - - // Apply PI control (V{d,q}_setpoint act as feed-forward terms in this mode) - Vd += v_current_control_integral_d_ + Ierr_d * p_gain_; - Vq += v_current_control_integral_q_ + Ierr_q * p_gain_; - } - - if (std::isnan(vbus_voltage)) { + if (!Vdq_setpoint_.has_value()) { + return Motor::ERROR_UNKNOWN_VOLTAGE_COMMAND; + } else if (!phase_.has_value() || !phase_vel_.has_value()) { + return Motor::ERROR_UNKNOWN_PHASE_ESTIMATE; + } else if (!vbus_voltage_measured_.has_value()) { return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE; } + auto [Vd, Vq] = *Vdq_setpoint_; + float phase = *phase_; + float phase_vel = *phase_vel_; + float vbus_voltage = *vbus_voltage_measured_; + + std::optional Idq; + + // Park transform + if (Ialpha_beta_measured_.has_value()) { + auto [Ialpha, Ibeta] = *Ialpha_beta_measured_; + float I_phase = phase + phase_vel * ((float)(int32_t)(i_timestamp_ - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); + float c_I = our_arm_cos_f32(I_phase); + float s_I = our_arm_sin_f32(I_phase); + Idq = { + c_I * Ialpha + s_I * Ibeta, + c_I * Ibeta - s_I * Ialpha + }; + Id_measured_ += I_measured_report_filter_k_ * (Idq->first - Id_measured_); + Iq_measured_ += I_measured_report_filter_k_ * (Idq->second - Iq_measured_); + } else { + Id_measured_ = 0.0f; + Iq_measured_ = 0.0f; + } + + float mod_to_V = (2.0f / 3.0f) * vbus_voltage; float V_to_mod = 1.0f / mod_to_V; - float mod_d = V_to_mod * Vd; - float mod_q = V_to_mod * Vq; + float mod_d; + float mod_q; if (enable_current_control_) { + // Current control mode + + if (!pi_gains_.has_value()) { + return Motor::ERROR_UNKNOWN_GAINS; + } else if (!Idq.has_value()) { + return Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT; + } else if (!Idq_setpoint_.has_value()) { + return Motor::ERROR_UNKNOWN_CURRENT_COMMAND; + } + + auto [p_gain, i_gain] = *pi_gains_; + auto [Id, Iq] = *Idq; + auto [Id_setpoint, Iq_setpoint] = *Idq_setpoint_; + + float Ierr_d = Id_setpoint - Id; + float Ierr_q = Iq_setpoint - Iq; + + // Apply PI control (V{d,q}_setpoint act as feed-forward terms in this mode) + mod_d = V_to_mod * (Vd + v_current_control_integral_d_ + Ierr_d * p_gain); + mod_q = V_to_mod * (Vq + v_current_control_integral_q_ + Ierr_q * p_gain); + // Vector modulation saturation, lock integrator if saturated // TODO make maximum modulation configurable float mod_scalefactor = 0.80f * sqrt3_by_2 * 1.0f / sqrtf(mod_d * mod_d + mod_q * mod_q); @@ -124,25 +141,34 @@ ODriveIntf::MotorIntf::Error FieldOrientedController::get_alpha_beta_output( v_current_control_integral_d_ *= 0.99f; v_current_control_integral_q_ *= 0.99f; } else { - v_current_control_integral_d_ += Ierr_d * (i_gain_ * current_meas_period); - v_current_control_integral_q_ += Ierr_q * (i_gain_ * current_meas_period); + v_current_control_integral_d_ += Ierr_d * (i_gain * current_meas_period); + v_current_control_integral_q_ += Ierr_q * (i_gain * current_meas_period); } + + } else { + // Voltage control mode + mod_d = V_to_mod * Vd; + mod_q = V_to_mod * Vq; } // Inverse park transform - float pwm_phase = phase_ + phase_vel_ * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); + float pwm_phase = phase + phase_vel * ((float)(int32_t)(output_timestamp - ctrl_timestamp_) / (float)TIM_1_8_CLOCK_HZ); float c_p = our_arm_cos_f32(pwm_phase); float s_p = our_arm_sin_f32(pwm_phase); - float mod_alpha_temp = c_p * mod_d - s_p * mod_q; - float mod_beta_temp = c_p * mod_q + s_p * mod_d; + float mod_alpha = c_p * mod_d - s_p * mod_q; + float mod_beta = c_p * mod_q + s_p * mod_d; // Report final applied voltage in stationary frame (for sensorless estimator) - final_v_alpha_ = mod_to_V * mod_alpha_temp; - final_v_beta_ = mod_to_V * mod_beta_temp; + final_v_alpha_ = mod_to_V * mod_alpha; + final_v_beta_ = mod_to_V * mod_beta; - *mod_alpha = mod_alpha_temp; - *mod_beta = mod_beta_temp; - *ibus = mod_d * Id + mod_q * Iq; + *mod_alpha_beta = {mod_alpha, mod_beta}; + + if (Idq.has_value()) { + auto [Id, Iq] = *Idq; + *ibus = mod_d * Id + mod_q * Iq; + } + return Motor::ERROR_NONE; } @@ -150,11 +176,9 @@ void FieldOrientedController::update(uint32_t timestamp) { CRITICAL_SECTION() { ctrl_timestamp_ = timestamp; enable_current_control_ = enable_current_control_src_; - Id_setpoint_ = Id_setpoint_src_ ? *Id_setpoint_src_ : NAN; - Iq_setpoint_ = Iq_setpoint_src_ ? *Iq_setpoint_src_ : NAN; - Vd_setpoint_ = Vd_setpoint_src_ ? *Vd_setpoint_src_ : NAN; - Vq_setpoint_ = Vq_setpoint_src_ ? *Vq_setpoint_src_ : NAN; - phase_ = phase_src_ ? *phase_src_ : NAN; - phase_vel_ = phase_vel_src_ ? *phase_vel_src_ : NAN; + Idq_setpoint_ = Idq_setpoint_src_.get_current(); + Vdq_setpoint_ = Vdq_setpoint_src_.get_current(); + phase_ = phase_src_.get_current(); + phase_vel_ = phase_vel_src_.get_current(); } } diff --git a/Firmware/MotorControl/foc.hpp b/Firmware/MotorControl/foc.hpp index ba2b3ec7..30fc072e 100644 --- a/Firmware/MotorControl/foc.hpp +++ b/Firmware/MotorControl/foc.hpp @@ -17,43 +17,41 @@ public: void reset() final; ODriveIntf::MotorIntf::Error on_measurement( - float vbus_voltage, float Ialpha, float Ibeta, uint32_t input_timestamp) final; + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) final; ODriveIntf::MotorIntf::Error get_alpha_beta_output( - uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) final; + uint32_t output_timestamp, + std::optional* mod_alpha_beta, + std::optional* ibus) final; // Config - these values are set while this controller is inactive - float p_gain_ = NAN; // [V/A] should be auto set after resistance and inductance measurement - float i_gain_ = NAN; // [V/As] should be auto set after resistance and inductance measurement + std::optional pi_gains_; // [V/A, V/As] should be auto set after resistance and inductance measurement float I_measured_report_filter_k_ = 1.0f; // Inputs bool enable_current_control_src_ = false; - float* Id_setpoint_src_ = nullptr; - float* Iq_setpoint_src_ = nullptr; - float* Vd_setpoint_src_ = nullptr; - float* Vq_setpoint_src_ = nullptr; - float* phase_src_ = nullptr; - float* phase_vel_src_ = nullptr; + InputPort Idq_setpoint_src_; + InputPort Vdq_setpoint_src_; + InputPort phase_src_; + InputPort phase_vel_src_; // These values are set atomically by the update() function and read by the // calculate() function in an interrupt context. uint32_t ctrl_timestamp_; // [HCLK ticks] bool enable_current_control_ = false; // true: FOC runs in current control mode using I{dq}_setpoint, false: FOC runs in voltage control mode using V{dq}_setpoint - float Id_setpoint_; // [A] only used if enable_current_control_ == true - float Iq_setpoint_; // [A] only used if enable_current_control_ == true - float Vd_setpoint_; // [V] acts as input if enable_current_control_ == false and as output otherwise - float Vq_setpoint_; // [V] acts as input if enable_current_control_ == false and as output otherwise - float phase_; // [rad] - float phase_vel_; // [rad/s] + std::optional Idq_setpoint_; // [A] only used if enable_current_control_ == true + std::optional Vdq_setpoint_; // [V] feed-forward voltage term (or standalone setpoint if enable_current_control_ == false) + std::optional phase_; // [rad] + std::optional phase_vel_; // [rad/s] // These values (or some of them) are updated inside on_measurement() and get_alpha_beta_output() uint32_t i_timestamp_; - float vbus_voltage_measured_ = NAN; // [V] - float Ialpha_measured_ = NAN; // [A] - float Ibeta_measured_ = NAN; // [A] - float Id_measured_ = 0.0f; // [A] - float Iq_measured_ = 0.0f; // [A] + std::optional vbus_voltage_measured_; // [V] + std::optional Ialpha_beta_measured_; // [A, A] + float Id_measured_; // [A] + float Iq_measured_; // [A] float v_current_control_integral_d_ = 0.0f; // [V] float v_current_control_integral_q_ = 0.0f; // [V] //float mod_to_V_ = 0.0f; diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 6a9d8a48..02deeed5 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -272,6 +272,34 @@ void ODrive::control_loop_cb(uint32_t timestamp) { // TODO: use a configurable component list for most of the following things MEASURE_TIME(task_times_.control_loop_misc) { + // Reset all output ports so that we are certain about the freshness of + // all values that we use. + // If we forget to reset a value here the worst that can happen is that + // this safety check doesn't work. + // TODO: maybe we should add a check to output ports that prevents + // double-setting the value. + for (auto& axis: axes) { + axis.async_estimator_.slip_vel_.reset(); + axis.async_estimator_.stator_phase_vel_.reset(); + axis.async_estimator_.stator_phase_.reset(); + axis.controller_.torque_output_.reset(); + axis.encoder_.phase_.reset(); + axis.encoder_.phase_vel_.reset(); + axis.encoder_.pos_estimate_.reset(); + axis.encoder_.vel_estimate_.reset(); + axis.encoder_.pos_circular_.reset(); + axis.motor_.Vdq_setpoint_.reset(); + axis.motor_.Idq_setpoint_.reset(); + axis.open_loop_controller_.Idq_setpoint_.reset(); + axis.open_loop_controller_.Vdq_setpoint_.reset(); + axis.open_loop_controller_.phase_.reset(); + axis.open_loop_controller_.phase_vel_.reset(); + axis.open_loop_controller_.total_distance_.reset(); + axis.sensorless_estimator_.phase_.reset(); + axis.sensorless_estimator_.phase_vel_.reset(); + axis.sensorless_estimator_.vel_estimate_.reset(); + } + uart_poll(); odrv.oscilloscope_.update(); } @@ -300,7 +328,12 @@ void ODrive::control_loop_cb(uint32_t timestamp) { MEASURE_TIME(axis.task_times_.encoder_update) axis.encoder_.update(); + } + // Controller of either axis might use the encoder estimate of the other + // axis so we process both encoders before we continue. + + for (auto& axis: axes) { MEASURE_TIME(axis.task_times_.sensorless_estimator_update) axis.sensorless_estimator_.update(); @@ -318,11 +351,8 @@ void ODrive::control_loop_cb(uint32_t timestamp) { MEASURE_TIME(axis.task_times_.open_loop_controller_update) axis.open_loop_controller_.update(timestamp); - MEASURE_TIME(axis.task_times_.async_estimator_update) - axis.async_estimator_.update(timestamp); - MEASURE_TIME(axis.task_times_.motor_update) - axis.motor_.update(); // uses torque from controller and phase_vel from encoder + axis.motor_.update(timestamp); // uses torque from controller and phase_vel from encoder MEASURE_TIME(axis.task_times_.current_controller_update) axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or async_estimator_ @@ -406,6 +436,10 @@ static void rtos_main(void*) { axis.encoder_.setup(); } + for(auto& axis: axes){ + axis.async_estimator_.idq_src_.connect_to(&axis.motor_.Idq_setpoint_); + } + // Start PWM and enable adc interrupts/callbacks start_adc_pwm(); diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 109a7f00..f88ffe30 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -18,35 +18,43 @@ struct ResistanceMeasurementControlLaw : AlphaBetaFrameController { void reset() final { test_voltage_ = 0.0f; - test_mod_ = NAN; + test_mod_ = std::nullopt; } ODriveIntf::MotorIntf::Error on_measurement( - float vbus_voltage, float Ialpha, float Ibeta, - uint32_t input_timestamp) final - { - actual_current_ = Ialpha; - test_voltage_ += (kI * current_meas_period) * (target_current_ - actual_current_); + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) final { + + if (Ialpha_beta.has_value()) { + actual_current_ = Ialpha_beta->first; + test_voltage_ += (kI * current_meas_period) * (target_current_ - actual_current_); + } else { + actual_current_ = 0.0f; + test_voltage_ = 0.0f; + } if (std::abs(test_voltage_) > max_voltage_) { test_voltage_ = NAN; return Motor::ERROR_PHASE_RESISTANCE_OUT_OF_RANGE; - } else if (std::isnan(vbus_voltage)) { + } else if (!vbus_voltage.has_value()) { return Motor::ERROR_UNKNOWN_VBUS_VOLTAGE; } else { - float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); + float vfactor = 1.0f / ((2.0f / 3.0f) * *vbus_voltage); test_mod_ = test_voltage_ * vfactor; return Motor::ERROR_NONE; } } - ODriveIntf::MotorIntf::Error get_alpha_beta_output(uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) { - if (std::isnan(test_mod_)) { + ODriveIntf::MotorIntf::Error get_alpha_beta_output( + uint32_t output_timestamp, + std::optional* mod_alpha_beta, + std::optional* ibus) final { + if (!test_mod_.has_value()) { return Motor::ERROR_CONTROLLER_INITIALIZING; } else { - *mod_alpha = test_mod_; - *mod_beta = 0.0f; - *ibus = test_mod_ * actual_current_; + *mod_alpha_beta = {*test_mod_, 0.0f}; + *ibus = *test_mod_ * actual_current_; return Motor::ERROR_NONE; } } @@ -60,7 +68,7 @@ struct ResistanceMeasurementControlLaw : AlphaBetaFrameController { float actual_current_ = 0.0f; float target_current_ = 0.0f; float test_voltage_ = 0.0f; - float test_mod_ = NAN; + std::optional test_mod_ = NAN; }; /** @@ -75,13 +83,17 @@ struct InductanceMeasurementControlLaw : AlphaBetaFrameController { attached_ = false; } - ODriveIntf::MotorIntf::Error on_measurement(float vbus_voltage, - float Ialpha, float Ibeta, uint32_t input_timestamp) final + ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) final { - if (std::isnan(Ialpha) || std::isnan(vbus_voltage)) { - return {Motor::ERROR_UNKNOWN_VBUS_VOLTAGE}; + if (!Ialpha_beta.has_value()) { + return {Motor::ERROR_UNKNOWN_CURRENT_MEASUREMENT}; } + float Ialpha = Ialpha_beta->first; + if (attached_) { float sign = test_voltage_ >= 0.0f ? 1.0f : -1.0f; deltaI_ += -sign * (Ialpha - last_Ialpha_); @@ -97,12 +109,12 @@ struct InductanceMeasurementControlLaw : AlphaBetaFrameController { } ODriveIntf::MotorIntf::Error get_alpha_beta_output( - uint32_t output_timestamp, float* mod_alpha, float* mod_beta, float* ibus) final + uint32_t output_timestamp, std::optional* mod_alpha_beta, + std::optional* ibus) final { test_voltage_ *= -1.0f; float vfactor = 1.0f / ((2.0f / 3.0f) * vbus_voltage); - *mod_alpha = test_voltage_ * vfactor; - *mod_beta = 0.0f; + *mod_alpha_beta = {test_voltage_ * vfactor, 0.0f}; *ibus = 0.0f; return Motor::ERROR_NONE; } @@ -263,9 +275,9 @@ bool Motor::disarm(bool* was_armed) { // TODO: allow update on user-request or update automatically via hooks void Motor::update_current_controller_gains() { // Calculate current control gains - current_control_.p_gain_ = config_.current_control_bandwidth * config_.phase_inductance; + float p_gain = config_.current_control_bandwidth * config_.phase_inductance; float plant_pole = config_.phase_resistance / config_.phase_inductance; - current_control_.i_gain_ = plant_pole * current_control_.p_gain_; + current_control_.pi_gains_ = {p_gain, plant_pole * p_gain}; } bool Motor::apply_config() { @@ -352,10 +364,11 @@ float Motor::max_available_torque() { } } -float Motor::phase_current_from_adcval(uint32_t ADCValue) { +std::optional Motor::phase_current_from_adcval(uint32_t ADCValue) { // Make sure the measurements don't come too close to the current sensor's hardware limitations if (ADCValue < CURRENT_ADC_LOWER_BOUND || ADCValue > CURRENT_ADC_UPPER_BOUND) { - disarm_with_error(ERROR_CURRENT_SENSE_SATURATION); + error_ |= ERROR_CURRENT_SENSE_SATURATION; + return std::nullopt; } int adcval_bal = (int)ADCValue - (1 << 11); @@ -461,24 +474,22 @@ bool Motor::run_calibration() { return true; } -void Motor::update() { - float torque = torque_setpoint_src_ ? *torque_setpoint_src_ : NAN; - float phase_vel = phase_vel_src_ ? *phase_vel_src_ : NAN; +void Motor::update(uint32_t timestamp) { + std::optional torque = torque_setpoint_src_.get_current(); - // Reset output just in case the controller fails for any reason - Iq_setpoint_ = NAN; - // Id_setpoint_ = NAN; // this doubles as a state variable so we can't reset it + if (!torque.has_value()) { + error_ |= ERROR_UNKNOWN_TORQUE; + return; + } - float vd = 0.0f; - float vq = 0.0f; - float id = Id_setpoint_; - float iq; + auto [id, iq] = Idq_setpoint_.get_previous() + .value_or(float2D{0.0f, 0.0f}); // Id doubles as a state variable // Convert torque to current if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - iq = torque / (axis_->motor_.config_.torque_constant * fmax(axis_->async_estimator_.rotor_flux_, config_.acim_gain_min_flux)); + iq = *torque / (axis_->motor_.config_.torque_constant * fmax(axis_->async_estimator_.rotor_flux_, config_.acim_gain_min_flux)); } else { - iq = torque / axis_->motor_.config_.torque_constant; + iq = *torque / axis_->motor_.config_.torque_constant; } iq *= direction_; @@ -495,44 +506,61 @@ void Motor::update() { id = std::clamp(id, config_.acim_autoflux_min_Id, ilim); } + if (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL) { + Idq_setpoint_ = {id, iq}; + } + + // This update call is in bit a weird position because it depends on the + // Id,q setpoint but outputs the phase velocity that we depend on later + // in this function. + // A cleaner fix would be to take the feedforward calculation out of here + // and turn it into a separate component. + MEASURE_TIME(axis_->task_times_.async_estimator_update) + axis_->async_estimator_.update(timestamp); + + float vd = 0.0f; + float vq = 0.0f; + + std::optional phase_vel = phase_vel_src_.get_current(); + if (config_.R_wL_FF_enable) { - vd -= phase_vel * config_.phase_inductance * iq; - vq += phase_vel * config_.phase_inductance * id; + if (!phase_vel.has_value()) { + error_ |= ERROR_UNKNOWN_PHASE_VEL; + return; + } + + vd -= *phase_vel * config_.phase_inductance * iq; + vq += *phase_vel * config_.phase_inductance * id; vd += config_.phase_resistance * id; vq += config_.phase_resistance * iq; } if (config_.bEMF_FF_enable) { - vq += phase_vel * (2.0f/3.0f) * (config_.torque_constant / config_.pole_pairs); - } + if (!phase_vel.has_value()) { + error_ |= ERROR_UNKNOWN_PHASE_VEL; + return; + } + vq += *phase_vel * (2.0f/3.0f) * (config_.torque_constant / config_.pole_pairs); + } + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { // reinterpret current as voltage - vd += id; - vq += iq; - id = NAN; - iq = NAN; + Vdq_setpoint_ = {vd + id, vq + iq}; + } else { + Vdq_setpoint_ = {vd, vq}; } - - Vd_setpoint_ = vd; - Vq_setpoint_ = vq; - Id_setpoint_ = id; - Iq_setpoint_ = iq; } /** * @brief Called when the underlying hardware timer triggers an update event. */ -void Motor::current_meas_cb(uint32_t timestamp, Iph_ABC_t current) { +void Motor::current_meas_cb(uint32_t timestamp, std::optional current) { // TODO: this is platform specific //const float current_meas_period = static_cast(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ; TaskTimerContext tmr{axis_->task_times_.current_sense}; - bool current_valid = !std::isnan(current.phA) - && !std::isnan(current.phB) - && !std::isnan(current.phC); - n_evt_current_measurement_++; bool dc_calib_valid = (dc_calib_running_since_ >= config_.dc_calib_tau * 7.5f) @@ -540,23 +568,14 @@ void Motor::current_meas_cb(uint32_t timestamp, Iph_ABC_t current) { && (abs(DC_calib_.phB) < max_dc_calib_) && (abs(DC_calib_.phC) < max_dc_calib_); - if (current_valid && dc_calib_valid) { - current.phA -= DC_calib_.phA; - current.phB -= DC_calib_.phB; - current.phC -= DC_calib_.phC; - I_leak_ = current.phA + current.phB + current.phC; // sum should be close to 0 - current_meas_.phA = current.phA - I_leak_ / 3.0f; - current_meas_.phB = current.phB - I_leak_ / 3.0f; - current_meas_.phC = current.phC - I_leak_ / 3.0f; + if (current.has_value() && dc_calib_valid) { + current_meas_ = { + current->phA - DC_calib_.phA, + current->phB - DC_calib_.phB, + current->phC - DC_calib_.phC + }; } else { - I_leak_ = NAN; - current_meas_.phA = NAN; - current_meas_.phB = NAN; - current_meas_.phC = NAN; - } - - if (abs(I_leak_) > config_.I_leak_max) { - disarm_with_error(ERROR_I_LEAK_OUT_OF_RANGE); + current_meas_ = std::nullopt; } // Run system-level checks (e.g. overvoltage/undervoltage condition) @@ -565,17 +584,29 @@ void Motor::current_meas_cb(uint32_t timestamp, Iph_ABC_t current) { // effect on the PWM. odrv.do_fast_checks(); - // Check for violation of current limit - // If Ia + Ib + Ic == 0 holds then we have: - // Inorm^2 = Id^2 + Iq^2 = Ialpha^2 + Ibeta^2 = 2/3 * (Ia^2 + Ib^2 + Ic^2) - float Itrip = effective_current_lim_ + config_.current_lim_margin; - if (2.0f / 3.0f * (SQ(current_meas_.phA) + SQ(current_meas_.phB) + SQ(current_meas_.phC)) > SQ(Itrip)) { - disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION); + if (current_meas_.has_value()) { + // Check for violation of current limit + // If Ia + Ib + Ic == 0 holds then we have: + // Inorm^2 = Id^2 + Iq^2 = Ialpha^2 + Ibeta^2 = 2/3 * (Ia^2 + Ib^2 + Ic^2) + float Itrip = effective_current_lim_ + config_.current_lim_margin; + float Inorm_sq = 2.0f / 3.0f * (SQ(current_meas_->phA) + + SQ(current_meas_->phB) + + SQ(current_meas_->phC)); + if (Inorm_sq > SQ(Itrip)) { + disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION); + } + } else if (is_armed_) { + // Since we can't check current limits, be safe for now and disarm. + // Theoretically we could continue to operate if there is no active + // current limit. + disarm_with_error(ERROR_UNKNOWN_CURRENT_MEASUREMENT); } if (control_law_) { Error err = control_law_->on_measurement(vbus_voltage, - {current_meas_.phA, current_meas_.phB, current_meas_.phC}, + current_meas_.has_value() ? + std::make_optional(std::array{current_meas_->phA, current_meas_->phB, current_meas_->phC}) + : std::nullopt, timestamp); if (err != ERROR_NONE) { disarm_with_error(err); @@ -586,19 +617,15 @@ void Motor::current_meas_cb(uint32_t timestamp, Iph_ABC_t current) { /** * @brief Called when the underlying hardware timer triggers an update event. */ -void Motor::dc_calib_cb(uint32_t timestamp, Iph_ABC_t current) { +void Motor::dc_calib_cb(uint32_t timestamp, std::optional current) { const float dc_calib_period = static_cast(2 * TIM_1_8_PERIOD_CLOCKS * (TIM_1_8_RCR + 1)) / TIM_1_8_CLOCK_HZ; TaskTimerContext tmr{axis_->task_times_.dc_calib}; - bool current_valid = !std::isnan(current.phA) - && !std::isnan(current.phB) - && !std::isnan(current.phC); - - if (current_valid) { + if (current.has_value()) { const float calib_filter_k = std::min(dc_calib_period / config_.dc_calib_tau, 1.0f); - DC_calib_.phA += (current.phA - DC_calib_.phA) * calib_filter_k; - DC_calib_.phB += (current.phB - DC_calib_.phB) * calib_filter_k; - DC_calib_.phC += (current.phC - DC_calib_.phC) * calib_filter_k; + DC_calib_.phA += (current->phA - DC_calib_.phA) * calib_filter_k; + DC_calib_.phB += (current->phB - DC_calib_.phB) * calib_filter_k; + DC_calib_.phC += (current->phC - DC_calib_.phC) * calib_filter_k; dc_calib_running_since_ += dc_calib_period; } else { DC_calib_.phA = 0.0f; @@ -615,7 +642,7 @@ void Motor::pwm_update_cb(uint32_t output_timestamp) { Error control_law_status = ERROR_CONTROLLER_FAILED; float pwm_timings[3] = {NAN, NAN, NAN}; - float i_bus = 0.0f; + std::optional i_bus; if (control_law_) { control_law_status = control_law_->get_output( @@ -632,23 +659,27 @@ void Motor::pwm_update_cb(uint32_t output_timestamp) { apply_pwm_timings(next_timings, false); } else if (is_armed_) { - i_bus = 0.0f; if (!(timer_->Instance->BDTR & TIM_BDTR_MOE) && (control_law_status == ERROR_CONTROLLER_INITIALIZING)) { // If the PWM output is armed in software but not yet in // hardware we tolerate the "initializing" error. + i_bus = 0.0f; } else { disarm_with_error(control_law_status); } } - // If something above failed, reset I_bus to 0A. if (!is_armed_) { + // If something above failed, reset I_bus to 0A. + i_bus = 0.0f; + } else if (is_armed_ && !i_bus.has_value()) { + // If the motor is armed then i_bus must be known + disarm_with_error(ERROR_UNKNOWN_CURRENT_MEASUREMENT); i_bus = 0.0f; } - I_bus_ = i_bus; + I_bus_ = *i_bus; - if (i_bus < config_.I_bus_hard_min || i_bus > config_.I_bus_hard_max) { + if (*i_bus < config_.I_bus_hard_min || *i_bus > config_.I_bus_hard_max) { disarm_with_error(ERROR_I_BUS_OUT_OF_RANGE); } diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index 27834014..0b3c0b9f 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -10,11 +10,6 @@ class Motor; class Motor : public ODriveIntf::MotorIntf { public: - struct Iph_ABC_t { - float phA; - float phB; - float phC; - }; // NOTE: for gimbal motors, all units of Nm are instead V. // example: vel_gain is [V/(turn/s)] instead of [Nm/(turn/s)] @@ -82,15 +77,15 @@ public: bool do_checks(uint32_t timestamp); float effective_current_lim(); float max_available_torque(); - float phase_current_from_adcval(uint32_t ADCValue); + std::optional phase_current_from_adcval(uint32_t ADCValue); bool measure_phase_resistance(float test_current, float max_voltage); bool measure_phase_inductance(float test_voltage); bool run_calibration(); - void update(); + void update(uint32_t timestamp); // These functions are called as appropriate from the board.cpp file. - void current_meas_cb(uint32_t timestamp, Iph_ABC_t current); - void dc_calib_cb(uint32_t timestamp, Iph_ABC_t current); + void current_meas_cb(uint32_t timestamp, std::optional current); + void dc_calib_cb(uint32_t timestamp, std::optional current); void pwm_update_cb(uint32_t output_timestamp); // hardware config @@ -113,26 +108,23 @@ public: // Do not write to this variable directly! // It is for exclusive use by the safety_critical_... functions. bool is_armed_ = false; - bool is_calibrated_ = config_.pre_calibrated; - Iph_ABC_t current_meas_ = {NAN, NAN, NAN}; + bool is_calibrated_ = false; // Set in apply_config() + std::optional current_meas_; Iph_ABC_t DC_calib_ = {0.0f, 0.0f, 0.0f}; float dc_calib_running_since_ = 0.0f; // current sensor calibration needs some time to settle - float I_leak_ = NAN; // close to zero if only two current sensors are available float I_bus_ = 0.0f; // this motors contribution to the bus current - bool current_meas_valid_ = false; // if false, the measured current values must not be used for control float phase_current_rev_gain_ = 0.0f; // Reverse gain for ADC to Amps (to be set by DRV8301_setup) FieldOrientedController current_control_; float effective_current_lim_ = 10.0f; // [A] float max_allowed_current_ = 0.0f; // [A] set in setup() float max_dc_calib_ = 0.0f; // [A] set in setup() - float* torque_setpoint_src_ = nullptr; // Usually points to the Controller object's output - float* phase_vel_src_ = nullptr; // Usually points to the Encoder object's output + InputPort torque_setpoint_src_; // Usually points to the Controller object's output + InputPort phase_vel_src_; // Usually points to the Encoder object's output + float direction_ = 0.0f; // if -1 then positive torque is converted to negative Iq - float Vd_setpoint_ = NAN; // fed to the FOC - float Vq_setpoint_ = NAN; // fed to the FOC - float Id_setpoint_ = 0.0f; // fed to the FOC - float Iq_setpoint_ = NAN; // fed to the FOC + OutputPort Vdq_setpoint_ = {{0.0f, 0.0f}}; // fed to the FOC + OutputPort Idq_setpoint_ = {{0.0f, 0.0f}}; // fed to the FOC PhaseControlLaw<3>* control_law_; }; diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index ee17e125..bc78d7b0 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -228,7 +228,7 @@ public: Oscilloscope oscilloscope_{ &axes[0].motor_.current_control_.v_current_control_integral_d_, // trigger_src 0.5f, // trigger_threshold - &axes[0].motor_.current_control_.Ialpha_measured_ // data_src + nullptr // &axes[0].motor_.current_control_.Ialpha_measured_ // data_src TODO: change data type }; BoardConfig_t config_; diff --git a/Firmware/MotorControl/open_loop_controller.cpp b/Firmware/MotorControl/open_loop_controller.cpp index b111f41c..506e4898 100644 --- a/Firmware/MotorControl/open_loop_controller.cpp +++ b/Firmware/MotorControl/open_loop_controller.cpp @@ -3,25 +3,25 @@ #include void OpenLoopController::update(uint32_t timestamp) { - if (std::isnan(Id_setpoint_) || std::isnan(Id_setpoint_) || std::isnan(phase_) || std::isnan(phase_vel_)) { - Id_setpoint_ = 0.0f; - Iq_setpoint_ = 0.0f; - Vd_setpoint_ = 0.0f; - Vq_setpoint_ = 0.0f; - phase_ = 0.0f; - phase_vel_ = 0.0f; - timestamp_ = timestamp; - } + auto [prev_Id, prev_Iq] = Idq_setpoint_.get_previous().value_or(float2D{0.0f, 0.0f}); + auto [prev_Vd, prev_Vq] = Vdq_setpoint_.get_previous().value_or(float2D{0.0f, 0.0f}); + float phase = phase_.get_previous().value_or(0.0f); + float phase_vel = phase_vel_.get_previous().value_or(0.0f); float dt = (float)(timestamp - timestamp_) / (float)TIM_1_8_CLOCK_HZ; - Id_setpoint_ = std::clamp(target_current_, Id_setpoint_ - max_current_ramp_ * dt, Id_setpoint_ + max_current_ramp_ * dt); - Iq_setpoint_ = 0.0f; - Vd_setpoint_ = std::clamp(target_voltage_, Vd_setpoint_ - max_voltage_ramp_ * dt, Vd_setpoint_ + max_voltage_ramp_ * dt); - Vq_setpoint_ = 0.0f; - - phase_vel_ = std::clamp(target_vel_, phase_vel_ - max_phase_vel_ramp_ * dt, phase_vel_ + max_phase_vel_ramp_ * dt); - phase_ = wrap_pm_pi(phase_ + phase_vel_ * dt); - total_distance_ += phase_vel_ * dt; + Idq_setpoint_ = { + std::clamp(target_current_, prev_Id - max_current_ramp_ * dt, prev_Id + max_current_ramp_ * dt), + 0.0f + }; + Vdq_setpoint_ = { + std::clamp(target_voltage_, prev_Vd - max_voltage_ramp_ * dt, prev_Vd + max_voltage_ramp_ * dt), + 0.0f + }; + + phase_vel = std::clamp(target_vel_, phase_vel - max_phase_vel_ramp_ * dt, phase_vel + max_phase_vel_ramp_ * dt); + phase_vel_ = phase_vel; + phase_ = wrap_pm_pi(phase + phase_vel * dt); + total_distance_ = total_distance_.get_previous().value_or(0.0f) + phase_vel * dt; timestamp_ = timestamp; } diff --git a/Firmware/MotorControl/open_loop_controller.hpp b/Firmware/MotorControl/open_loop_controller.hpp index 143a42e5..82356a23 100644 --- a/Firmware/MotorControl/open_loop_controller.hpp +++ b/Firmware/MotorControl/open_loop_controller.hpp @@ -3,6 +3,7 @@ #include "component.hpp" #include +#include class OpenLoopController : public ComponentBase { public: @@ -14,19 +15,17 @@ public: float max_phase_vel_ramp_ = INFINITY; // [rad/s^2] // Inputs - float target_vel_ = NAN; - float target_current_ = NAN; - float target_voltage_ = NAN; + float target_vel_ = 0.0f; + float target_current_ = 0.0f; + float target_voltage_ = 0.0f; // State/Outputs uint32_t timestamp_ = 0; - float Id_setpoint_ = NAN; - float Iq_setpoint_ = NAN; - float Vd_setpoint_ = NAN; - float Vq_setpoint_ = NAN; - float phase_ = NAN; - float phase_vel_ = NAN; - float total_distance_ = NAN; + OutputPort Idq_setpoint_ = {{0.0f, 0.0f}}; + OutputPort Vdq_setpoint_ = {{0.0f, 0.0f}}; + OutputPort phase_ = 0.0f; + OutputPort phase_vel_ = 0.0f; + OutputPort total_distance_ = 0.0f; }; #endif // __OPEN_LOOP_CONTROLLER_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/phase_control_law.hpp b/Firmware/MotorControl/phase_control_law.hpp index bda2ccdc..1a95ac29 100644 --- a/Firmware/MotorControl/phase_control_law.hpp +++ b/Firmware/MotorControl/phase_control_law.hpp @@ -20,16 +20,20 @@ public: * * Beware that all inputs can be NAN. * - * @param vbus_voltage: The most recently measured DC link voltage. NAN if - * the measurement is not available or valid for some reason. + * @param vbus_voltage: The most recently measured DC link voltage. Can be + * std::nullopt if the measurement is not available or valid for any + * reason. * @param currents: The most recently measured (or inferred) phase currents - * in Amps. Any of the values can be NAN if the measurement is not - * available or valid for some reason. + * in Amps. Can be std::nullopt if no valid measurements are available + * (e.g. because the opamp isn't started or because the sensors were + * saturated). * @param input_timestamp: The timestamp (in HCLK ticks) corresponding to * the vbus_voltage and current measurement. */ - virtual ODriveIntf::MotorIntf::Error on_measurement(float vbus_voltage, - std::array currents, uint32_t input_timestamp) = 0; + virtual ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional> currents, + uint32_t input_timestamp) = 0; /** * @brief Shall calculate the PWM timings for the specified target time. @@ -60,28 +64,34 @@ public: * triggering a motor disarm. In this phase the PWMs will not yet * be truly active. */ - virtual ODriveIntf::MotorIntf::Error get_output(uint32_t output_timestamp, - float (&pwm_timings)[N_PHASES], - float* ibus) = 0; + virtual ODriveIntf::MotorIntf::Error get_output( + uint32_t output_timestamp, + float (&pwm_timings)[N_PHASES], + std::optional* ibus) = 0; }; class AlphaBetaFrameController : public PhaseControlLaw<3> { private: - ODriveIntf::MotorIntf::Error on_measurement(float vbus_voltage, - std::array currents, uint32_t input_timestamp) final; + ODriveIntf::MotorIntf::Error on_measurement( + std::optional vbus_voltage, + std::optional> currents, + uint32_t input_timestamp) final; - ODriveIntf::MotorIntf::Error get_output(uint32_t output_timestamp, - float (&pwm_timings)[3], - float* ibus) final; + ODriveIntf::MotorIntf::Error get_output( + uint32_t output_timestamp, + float (&pwm_timings)[3], + std::optional* ibus) final; protected: virtual ODriveIntf::MotorIntf::Error on_measurement( - float vbus_voltage, float Ialpha, float Ibeta, uint32_t input_timestamp) = 0; + std::optional vbus_voltage, + std::optional Ialpha_beta, + uint32_t input_timestamp) = 0; virtual ODriveIntf::MotorIntf::Error get_alpha_beta_output( uint32_t output_timestamp, - float* mod_alpha, float* mod_beta, - float* ibus) = 0; + std::optional* mod_alpha_beta, + std::optional* ibus) = 0; }; #endif // __PHASE_CONTROL_LAW_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/sensorless_estimator.cpp b/Firmware/MotorControl/sensorless_estimator.cpp index 70819875..c3951056 100644 --- a/Firmware/MotorControl/sensorless_estimator.cpp +++ b/Firmware/MotorControl/sensorless_estimator.cpp @@ -1,6 +1,15 @@ #include "odrive_main.h" +void SensorlessEstimator::reset() { + pll_pos_ = 0.0f; + vel_estimate_ = 0.0f; + V_alpha_beta_memory_[0] = 0.0f; + V_alpha_beta_memory_[1] = 0.0f; + flux_state_[0] = 0.0f; + flux_state_[1] = 0.0f; +} + bool SensorlessEstimator::update() { // Algorithm based on paper: Sensorless Control of Surface-Mount Permanent-Magnet Synchronous Motors Based on a Nonlinear Observer // http://cas.ensmp.fr/~praly/Telechargement/Journaux/2010-IEEE_TPEL-Lee-Hong-Nam-Ortega-Praly-Astolfi.pdf @@ -10,20 +19,33 @@ bool SensorlessEstimator::update() { // is the one computed two cycles ago. To get the correct measurement, it was stored twice: // once by final_v_alpha/final_v_beta in the current control reporting, and once by V_alpha_beta_memory. - if (std::isnan(flux_state_[0]) || std::isnan(flux_state_[1]) || std::isnan(pll_pos_)) { - // Automatically reset state if it becomes NAN. The state becomes NAN - // when invalid current measurements are processed (e.g. because of the - // opamp being uninitialized). - flux_state_[0] = 0.0f; - flux_state_[1] = 0.0f; - pll_pos_ = 0.0f; - phase_vel_ = 0.0f; + // PLL + // TODO: the PLL part has some code duplication with the encoder PLL + // Pll gains as a function of bandwidth + float pll_kp = 2.0f * config_.pll_bandwidth; + // Critically damped + float pll_ki = 0.25f * (pll_kp * pll_kp); + + // Check that we don't get problems with discrete time approximation + if (!(current_meas_period * pll_kp < 1.0f)) { + error_ |= ERROR_UNSTABLE_GAIN; + reset(); // Reset state for when the next valid current measurement comes in. + return false; + } + + // TODO: we read values here which are modified by a higher priority interrupt. + // This is not thread-safe. + auto current_meas = axis_->motor_.current_meas_; + if (!current_meas.has_value()) { + error_ |= ERROR_UNKNOWN_CURRENT_MEASUREMENT; + reset(); // Reset state for when the next valid current measurement comes in. + return false; } // Clarke transform float I_alpha_beta[2] = { - -axis_->motor_.current_meas_.phB - axis_->motor_.current_meas_.phC, - one_by_sqrt3 * (axis_->motor_.current_meas_.phB - axis_->motor_.current_meas_.phC)}; + current_meas->phA, + one_by_sqrt3 * (current_meas->phB - current_meas->phC)}; // alpha-beta vector operations float eta[2]; @@ -59,31 +81,21 @@ bool SensorlessEstimator::update() { V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha_; V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta_; - // PLL - // TODO: the PLL part has some code duplication with the encoder PLL - // Pll gains as a function of bandwidth - float pll_kp = 2.0f * config_.pll_bandwidth; - // Critically damped - float pll_ki = 0.25f * (pll_kp * pll_kp); - // Check that we don't get problems with discrete time approximation - if (!(current_meas_period * pll_kp < 1.0f)) { - error_ |= ERROR_UNSTABLE_GAIN; - pll_pos_ = NAN; - phase_ = NAN; - vel_estimate_ = NAN; - return false; - } + float phase_vel = phase_vel_.get_previous().value_or(0.0f); // predict PLL phase with velocity - pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * phase_vel_); + pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * phase_vel); // update PLL phase with observer permanent magnet phase - phase_ = fast_atan2(eta[1], eta[0]); - float delta_phase = wrap_pm_pi(phase_ - pll_pos_); + float phase = fast_atan2(eta[1], eta[0]); + float delta_phase = wrap_pm_pi(phase - pll_pos_); pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * pll_kp * delta_phase); // update PLL velocity - phase_vel_ += current_meas_period * pll_ki * delta_phase; - // convert to mechanical turns/s for controller usage. - vel_estimate_ = phase_vel_ / (std::max((float)axis_->motor_.config_.pole_pairs, 1.0f) * 2.0f * M_PI); + phase_vel += current_meas_period * pll_ki * delta_phase; + + // set outputs + phase_ = phase; + phase_vel_ = phase_vel; + vel_estimate_ = phase_vel / (std::max((float)axis_->motor_.config_.pole_pairs, 1.0f) * 2.0f * M_PI); return true; }; diff --git a/Firmware/MotorControl/sensorless_estimator.hpp b/Firmware/MotorControl/sensorless_estimator.hpp index b15aef25..3ac6f488 100644 --- a/Firmware/MotorControl/sensorless_estimator.hpp +++ b/Firmware/MotorControl/sensorless_estimator.hpp @@ -1,6 +1,8 @@ #ifndef __SENSORLESS_ESTIMATOR_HPP #define __SENSORLESS_ESTIMATOR_HPP +#include "component.hpp" + class SensorlessEstimator : public ODriveIntf::SensorlessEstimatorIntf { public: struct Config_t { @@ -9,6 +11,7 @@ public: float pm_flux_linkage = 1.58e-3f; // [V / (rad/s)] { 5.51328895422 / ( * ) } }; + void reset(); bool update(); Axis* axis_ = nullptr; // set by Axis constructor @@ -16,15 +19,13 @@ public: // TODO: expose on protocol Error error_ = ERROR_NONE; - float phase_ = 0.0f; // [rad] float pll_pos_ = 0.0f; // [rad] - float phase_vel_ = 0.0f; // [rad/s] - float vel_estimate_ = 0.0f; // [turns/s] - // float pll_kp_ = 0.0f; // [rad/s / rad] - // float pll_ki_ = 0.0f; // [(rad/s^2) / rad] float flux_state_[2] = {0.0f, 0.0f}; // [Vs] float V_alpha_beta_memory_[2] = {0.0f, 0.0f}; // [V] - bool estimator_good_ = false; + + OutputPort phase_ = 0.0f; // [rad] + OutputPort phase_vel_ = 0.0f; // [rad/s] + OutputPort vel_estimate_ = 0.0f; // [turns/s] }; #endif /* __SENSORLESS_ESTIMATOR_HPP */ diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index c6c69d8c..9f690e80 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -132,6 +132,7 @@ FLAGS += '-DUSE_HAL_DRIVER' FLAGS += '-mthumb' FLAGS += '-mfloat-abi=hard' +FLAGS += '-Wno-psabi' -- suppress unimportant note about ABI compatibility in GCC 10 FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} -- linker flags @@ -145,6 +146,7 @@ if tup.getconfig("DEBUG") == "true" then FLAGS += '-g -gdwarf-2' OPT += '-Og' else + FLAGS += '-g' OPT += '-O2' end diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index ae23a614..b5ac9ce3 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -256,8 +256,8 @@ void cmd_get_feedback(char * pStr, StreamSink& response_channel, bool use_checks } else { Axis& axis = axes[motor_number]; respond(response_channel, use_checksum, "%f %f", - (double)axis.encoder_.pos_estimate_, - (double)axis.encoder_.vel_estimate_); + (double)axis.encoder_.pos_estimate_.get_any().value_or(0.0f), + (double)axis.encoder_.vel_estimate_.get_any().value_or(0.0f)); } } diff --git a/Firmware/communication/can_simple.cpp b/Firmware/communication/can_simple.cpp index 16fac7fd..a2ac1f1f 100644 --- a/Firmware/communication/can_simple.cpp +++ b/Firmware/communication/can_simple.cpp @@ -206,16 +206,19 @@ void CANSimple::get_encoder_estimates_callback(Axis* axis, can_Message_t& msg) { // uint32_t floatBytes = *(reinterpret_cast(&(axis->encoder_.pos_estimate_))); uint32_t floatBytes; - static_assert(sizeof axis->encoder_.pos_estimate_ == sizeof floatBytes); - std::memcpy(&floatBytes, &axis->encoder_.pos_estimate_, sizeof floatBytes); + + float pos_estimate = axis->encoder_.pos_estimate_.get_any().value_or(0.0f); + static_assert(sizeof pos_estimate == sizeof floatBytes); + std::memcpy(&floatBytes, &pos_estimate, sizeof floatBytes); txmsg.buf[0] = floatBytes; txmsg.buf[1] = floatBytes >> 8; txmsg.buf[2] = floatBytes >> 16; txmsg.buf[3] = floatBytes >> 24; - static_assert(sizeof floatBytes == sizeof axis->encoder_.vel_estimate_); - std::memcpy(&floatBytes, &axis->encoder_.vel_estimate_, sizeof floatBytes); + float vel_estimate = axis->encoder_.vel_estimate_.get_any().value_or(0.0f); + static_assert(sizeof floatBytes == sizeof vel_estimate); + std::memcpy(&floatBytes, &vel_estimate, sizeof floatBytes); txmsg.buf[4] = floatBytes; txmsg.buf[5] = floatBytes >> 8; txmsg.buf[6] = floatBytes >> 16; @@ -245,8 +248,9 @@ void CANSimple::get_sensorless_estimates_callback(Axis* axis, can_Message_t& msg txmsg.buf[2] = floatBytes >> 16; txmsg.buf[3] = floatBytes >> 24; - static_assert(sizeof floatBytes == sizeof axis->sensorless_estimator_.vel_estimate_); - std::memcpy(&floatBytes, &axis->sensorless_estimator_.vel_estimate_, sizeof floatBytes); + float vel_estimate = axis->sensorless_estimator_.vel_estimate_.get_any().value_or(0.0f); + static_assert(sizeof floatBytes == sizeof vel_estimate); + std::memcpy(&floatBytes, &vel_estimate, sizeof floatBytes); txmsg.buf[4] = floatBytes; txmsg.buf[5] = floatBytes >> 8; txmsg.buf[6] = floatBytes >> 16; @@ -328,17 +332,23 @@ void CANSimple::get_iq_callback(Axis* axis, can_Message_t& msg) { txmsg.isExt = axis->config_.can_node_id_extended; txmsg.len = 8; + // TODO: read variable in a thread-safe way + std::optional Idq_setpoint = axis->motor_.current_control_.Idq_setpoint_; + if (!Idq_setpoint.has_value()) { + Idq_setpoint = {0.0f, 0.0f}; + } + uint32_t floatBytes; - static_assert(sizeof axis->motor_.current_control_.Iq_setpoint_ == sizeof floatBytes); - std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_setpoint_, sizeof floatBytes); + static_assert(sizeof Idq_setpoint->first == sizeof floatBytes); + std::memcpy(&floatBytes, &Idq_setpoint->first, sizeof floatBytes); txmsg.buf[0] = floatBytes; txmsg.buf[1] = floatBytes >> 8; txmsg.buf[2] = floatBytes >> 16; txmsg.buf[3] = floatBytes >> 24; - static_assert(sizeof floatBytes == sizeof axis->motor_.current_control_.Iq_measured_); - std::memcpy(&floatBytes, &axis->motor_.current_control_.Iq_measured_, sizeof floatBytes); + static_assert(sizeof Idq_setpoint->second == sizeof floatBytes); + std::memcpy(&floatBytes, &Idq_setpoint->second, sizeof floatBytes); txmsg.buf[4] = floatBytes; txmsg.buf[5] = floatBytes >> 8; txmsg.buf[6] = floatBytes >> 16; diff --git a/Firmware/fibre/cpp/interfaces_template.j2 b/Firmware/fibre/cpp/interfaces_template.j2 index cbf7e72f..d901327e 100644 --- a/Firmware/fibre/cpp/interfaces_template.j2 +++ b/Firmware/fibre/cpp/interfaces_template.j2 @@ -12,6 +12,8 @@ #ifndef __FIBRE_INTERFACES_HPP #define __FIBRE_INTERFACES_HPP +[[userdata.c_preamble]] + #include #pragma GCC push_options diff --git a/Firmware/fibre/tools/interface_generator.py b/Firmware/fibre/tools/interface_generator.py index 659d97f6..0b899607 100644 --- a/Firmware/fibre/tools/interface_generator.py +++ b/Firmware/fibre/tools/interface_generator.py @@ -86,6 +86,8 @@ properties: valuetypes: type: object additionalProperties: { "$ref": "#/definitions/valuetype" } + userdata: + type: object __line__: {type: object} __column__: {type: object} additionalProperties: false @@ -159,8 +161,8 @@ value_types = OrderedDict({ }) enums = OrderedDict() - interfaces = OrderedDict() +userdata = OrderedDict() # Arbitrary data passed from the definition file to the template def make_property_type(typeargs): value_type = resolve_valuetype('', typeargs['fibre.Property.type']) @@ -529,6 +531,7 @@ for definition_file in definition_files: raise Exception(err.message + '\nat ' + str(list(err.absolute_path))) interfaces.update(get_dict(file_content, 'interfaces')) value_types.update(get_dict(file_content, 'valuetypes')) + userdata.update(get_dict(file_content, 'userdata')) dictionary += file_content.get('dictionary', None) or [] @@ -660,6 +663,7 @@ template_args = { 'interfaces': interfaces, 'value_types': value_types, 'toplevel_interfaces': toplevel_interfaces, + 'userdata': userdata, 'endpoints': endpoints, 'embedded_endpoint_definitions': embedded_endpoint_definitions } diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 4d2f27ce..09800f16 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -5,6 +5,12 @@ summary: ODrive Interface Definitions dictionary: [ODrive] # Prevent the word 'ODrive' from being detected as two words 'O' and 'Drive' +userdata: + c_preamble: | + #include + using float2D = std::pair; + struct Iph_ABC_t { float phA; float phB; float phC; }; + interfaces: ODrive: c_is_class: True @@ -637,7 +643,6 @@ interfaces: TimerUpdateMissed: {doc: A timer update event was missed. Perhaps the previous timer update took too much time. This is not expected in official release firmware.} CurrentMeasurementUnavailable: {doc: The phase current measurement is not available. The ADC failed to sample the current sensor in time. This is not expected in official release firmware.} ControllerFailed: {doc: The motor was disarmed because the underlying controller failed. Usually this is the FOC controller.} - ILeakOutOfRange: {doc: '`i_leak` exceeded `config.max_leak_current`. This can happen if there is a short from a motor phase to DC- or DC+.'} IBusOutOfRange: doc: | The DC current sourced/sunk by this motor exceeded the configured @@ -649,19 +654,23 @@ interfaces: The motor had to be disarmed because of a system level error. See `ODrive.Error` for more details. BadTiming: {doc: The main control loop got out of sync with the motor control loop. This could indicate that the main control loop got stuck.} - UnknownPhase: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} - UnknownCurrent: {doc: The current controller did not get a valid current measurement or setpoint. Maybe you didn't configure the controller correctly or there is a low level system issue.} + UnknownPhaseEstimate: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} + UnknownPhaseVel: {doc: The motor controller did not get a valid phase velocity input.} + UnknownTorque: {doc: The motor controller did not get a valid torque input.} + UnknownCurrentCommand: {doc: The current controller did not get a valid current setpoint. Maybe you didn't configure the controller correctly.} + UnknownCurrentMeasurement: {doc: The current controller did not get a valid current measurement.} UnknownVbusVoltage: {doc: The current controller did not get a valid `vbus_voltage` measurement.} + UnknownVoltageCommand: {doc: The current controller did not get a valid feedforward voltage setpoint.} + UnknownGains: {doc: The current controller gains were not configured. Run motor calibration or set `config.phase_resistance` and `config.phase_inductance` manually.} ControllerInitializing: {doc: Internal value used while the controller is not yet ready to generate PWM timings.} is_armed: readonly bool is_calibrated: readonly bool - current_meas_phA: {type: readonly float32, c_name: current_meas_.phA} - current_meas_phB: {type: readonly float32, c_name: current_meas_.phB} - current_meas_phC: {type: readonly float32, c_name: current_meas_.phC} + current_meas_phA: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phA'} + current_meas_phB: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phB'} + current_meas_phC: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phC'} DC_calib_phA: {type: float32, c_name: DC_calib_.phA} DC_calib_phB: {type: float32, c_name: DC_calib_.phB} DC_calib_phC: {type: float32, c_name: DC_calib_.phC} - I_leak: {type: readonly float32, unit: A} I_bus: {type: readonly float32, unit: A} phase_current_rev_gain: float32 effective_current_lim: readonly float32 @@ -676,17 +685,17 @@ interfaces: current_control: c_is_class: True attributes: - p_gain: float32 - i_gain: float32 + p_gain: {type: readonly float32, c_getter: 'pi_gains_.value_or(float2D{0.0f, 0.0f}).first'} + i_gain: {type: readonly float32, c_getter: 'pi_gains_.value_or(float2D{0.0f, 0.0f}).second'} I_measured_report_filter_k: float32 - Id_setpoint: readonly float32 - Iq_setpoint: readonly float32 - Vd_setpoint: readonly float32 - Vq_setpoint: readonly float32 - phase: readonly float32 - phase_vel: readonly float32 - Ialpha_measured: readonly float32 - Ibeta_measured: readonly float32 + Id_setpoint: {type: readonly float32, c_getter: 'Idq_setpoint_.value_or(float2D{0.0f, 0.0f}).first'} + Iq_setpoint: {type: readonly float32, c_getter: 'Idq_setpoint_.value_or(float2D{0.0f, 0.0f}).second'} + Vd_setpoint: {type: readonly float32, c_getter: 'Vdq_setpoint_.value_or(float2D{0.0f, 0.0f}).first'} + Vq_setpoint: {type: readonly float32, c_getter: 'Vdq_setpoint_.value_or(float2D{0.0f, 0.0f}).second'} + phase: {type: readonly float32, c_getter: 'phase_.value_or(0.0f)'} + phase_vel: {type: readonly float32, c_getter: 'phase_vel_.value_or(0.0f)'} + Ialpha_measured: {type: readonly float32, c_getter: 'Ialpha_beta_measured_.value_or(float2D{0.0f, 0.0f}).first'} + Ibeta_measured: {type: readonly float32, c_getter: 'Ialpha_beta_measured_.value_or(float2D{0.0f, 0.0f}).second'} Id_measured: readonly float32 Iq_measured: readonly float32 v_current_control_integral_d: float32 @@ -761,10 +770,25 @@ interfaces: c_is_class: True attributes: rotor_flux: {type: readonly float32, unit: A, doc: estimated magnitude of the rotor flux} - slip_vel: {type: readonly float32, unit: rad/s, doc: estimated slip between physical and electrical angular velocity} - phase_offset: {type: readonly float32, unit: rad, doc: estimate offset between physical and electrical angular position} - stator_phase_vel: {type: readonly float32, unit: rad/s, doc: calculated setpoint for the electrical velocity} - stator_phase: {type: readonly float32, unit: rad, doc: calculated setpoint for the electrical phase} + slip_vel: + type: readonly float32 + unit: rad/s + doc: estimated slip between physical and electrical angular velocity} + c_getter: slip_vel_.get_any().value_or(0.0f) + phase_offset: + type: readonly float32 + unit: rad + doc: estimate offset between physical and electrical angular position} + stator_phase_vel: + type: readonly float32 + unit: rad/s + doc: calculated setpoint for the electrical velocity} + c_getter: stator_phase_vel_.get_any().value_or(0.0f) + stator_phase: + type: readonly float32 + unit: rad + doc: calculated setpoint for the electrical phase} + c_getter: stator_phase_.get_any().value_or(0.0f) config: c_is_class: False attributes: @@ -920,13 +944,13 @@ interfaces: shadow_count: readonly int32 count_in_cpr: readonly int32 interpolation: readonly float32 - phase: readonly float32 - pos_estimate: readonly float32 + phase: {type: readonly float32, c_getter: phase_.get_any().value_or(0.0f)} + pos_estimate: {type: readonly float32, c_getter: pos_estimate_.get_any().value_or(0.0f)} pos_estimate_counts: readonly float32 pos_cpr_counts: readonly float32 - pos_circular: readonly float32 + pos_circular: {type: readonly float32, c_getter: pos_circular_.get_any().value_or(0.0f)} hall_state: readonly uint8 - vel_estimate: readonly float32 + vel_estimate: {type: readonly float32, c_getter: vel_estimate_.get_any().value_or(0.0f)} vel_estimate_counts: readonly float32 calib_scan_response: readonly float32 pos_abs: int32 @@ -967,10 +991,11 @@ interfaces: nullflag: None flags: UnstableGain: - phase: {type: float32, unit: rad} - pll_pos: {type: float32, unit: rad} - phase_vel: {type: float32, unit: rad/s} - vel_estimate: {type: float32, unit: turns/s} + UnknownCurrentMeasurement: + phase: {type: readonly float32, unit: rad, c_getter: phase_.get_any().value_or(0.0f)} + pll_pos: {type: readonly float32, unit: rad} + phase_vel: {type: readonly float32, unit: rad/s, c_getter: phase_vel_.get_any().value_or(0.0f)} + vel_estimate: {type: readonly float32, unit: turns/s, c_getter: vel_estimate_.get_any().value_or(0.0f)} # pll_kp: float32 # pll_ki: float32 config: diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index c2a6e329..bd5ddd8a 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -107,15 +107,19 @@ MOTOR_ERROR_MODULATION_IS_NAN = 0x00010000 MOTOR_ERROR_TIMER_UPDATE_MISSED = 0x00020000 MOTOR_ERROR_CURRENT_MEASUREMENT_UNAVAILABLE = 0x00040000 MOTOR_ERROR_CONTROLLER_FAILED = 0x00080000 -MOTOR_ERROR_I_LEAK_OUT_OF_RANGE = 0x00100000 -MOTOR_ERROR_I_BUS_OUT_OF_RANGE = 0x00200000 -MOTOR_ERROR_BRAKE_RESISTOR_DISARMED = 0x00400000 -MOTOR_ERROR_SYSTEM_LEVEL = 0x00800000 -MOTOR_ERROR_BAD_TIMING = 0x01000000 -MOTOR_ERROR_UNKNOWN_PHASE = 0x02000000 -MOTOR_ERROR_UNKNOWN_CURRENT = 0x04000000 -MOTOR_ERROR_UNKNOWN_VBUS_VOLTAGE = 0x08000000 -MOTOR_ERROR_CONTROLLER_INITIALIZING = 0x10000000 +MOTOR_ERROR_I_BUS_OUT_OF_RANGE = 0x00100000 +MOTOR_ERROR_BRAKE_RESISTOR_DISARMED = 0x00200000 +MOTOR_ERROR_SYSTEM_LEVEL = 0x00400000 +MOTOR_ERROR_BAD_TIMING = 0x00800000 +MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE = 0x01000000 +MOTOR_ERROR_UNKNOWN_PHASE_VEL = 0x02000000 +MOTOR_ERROR_UNKNOWN_TORQUE = 0x04000000 +MOTOR_ERROR_UNKNOWN_CURRENT_COMMAND = 0x08000000 +MOTOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x10000000 +MOTOR_ERROR_UNKNOWN_VBUS_VOLTAGE = 0x20000000 +MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND = 0x40000000 +MOTOR_ERROR_UNKNOWN_GAINS = 0x80000000 +MOTOR_ERROR_CONTROLLER_INITIALIZING = 0x100000000 # ODrive.Controller.Error CONTROLLER_ERROR_NONE = 0x00000000 @@ -141,3 +145,4 @@ ENCODER_ERROR_ABS_SPI_NOT_READY = 0x00000100 # ODrive.SensorlessEstimator.Error SENSORLESS_ESTIMATOR_ERROR_NONE = 0x00000000 SENSORLESS_ESTIMATOR_ERROR_UNSTABLE_GAIN = 0x00000001 +SENSORLESS_ESTIMATOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x00000002 diff --git a/tools/odrive/tests/encoder_test.py b/tools/odrive/tests/encoder_test.py index 29cbc794..9d045650 100644 --- a/tools/odrive/tests/encoder_test.py +++ b/tools/odrive/tests/encoder_test.py @@ -48,7 +48,7 @@ class TestEncoderBase(): # encoder.count_in_cpr slope, offset, fitted_curve = fit_sawtooth(data[:,(0,2)], true_cpr if reverse else 0, 0 if reverse else true_cpr) test_assert_eq(slope, true_cps, accuracy=0.005) - test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02) + test_curve_fit(data[:,(0,2)], fitted_curve, max_mean_err = true_cpr * 0.02, inlier_range = true_cpr * 0.02, max_outliers = len(data[:,0]) * 0.02 * noise) # encoder.pos_estimate slope, offset, fitted_curve = fit_line(data[:,(0,4)]) diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index ea093d7b..770cb1c7 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -622,7 +622,7 @@ def test_assert_no_error(axis_ctx: ODriveAxisComponent): any_error = (axis_ctx.handle.motor.error | axis_ctx.handle.encoder.error | axis_ctx.handle.sensorless_estimator.error | - axis_ctx.handle.error) != 0 + axis_ctx.handle.error) != 0 # TODO: this is not the complete list of components if any_error: lines = [] diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index e6208816..339099d5 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -104,6 +104,7 @@ def dump_errors(odrv, clear=False, printfunc = print): ('motor', axis, 'motor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("MOTOR_ERROR_")}), ('fet_thermistor', axis, 'fet_thermistor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), ('motor_thermistor', axis, 'motor_thermistor.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("THERMISTOR_CURRENT_LIMITER_ERROR")}), + ('sensorless_estimator', axis, 'sensorless_estimator.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("SENSORLESS_ESTIMATOR_ERROR")}), ('encoder', axis, 'encoder.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("ENCODER_ERROR_")}), ('controller', axis, 'controller.error', {v: k for k, v in odrive.enums.__dict__ .items() if k.startswith("CONTROLLER_ERROR_")}), ] From dbb0b8f8495b060aa9c0ae28f88c1f8e4c12b140 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 23 Sep 2020 16:35:08 +0200 Subject: [PATCH 042/124] update resource usage info --- docs/resources.md | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/resources.md b/docs/resources.md index 2d6fcb8f..dac2e0c7 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -1,6 +1,8 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv0)` and `dump_dma(odrv0)` in `odrivetool`. +Take this info with a grain of salt as we might forget to update it from time to time. When in doubt check the file history. + # ODrive v3.6 ## Interrupt Vectors @@ -13,26 +15,32 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | -12 | MemoryManagement_IRQn | 0 | | -11 | BusFault_IRQn | 0 | | -10 | UsageFault_IRQn | 0 | -| -5 | SVCall_IRQn | 0 | +| -5 | SVCall_IRQn | 3 | | -4 | DebugMonitor_IRQn | 0 | | -2 | PendSV_IRQn | 15 | | -1 | SysTick_IRQn | 15 | -| 11 | DMA1_Stream0_IRQn | 5 | -| 13 | DMA1_Stream2_IRQn | 5 | -| 15 | DMA1_Stream4_IRQn | 5 | -| 16 | DMA1_Stream5_IRQn | 5 | -| 18 | ADC_IRQn | 5 | -| 19 | CAN1_TX_IRQn | 6 | -| 20 | CAN1_RX0_IRQn | 6 | -| 21 | CAN1_RX1_IRQn | 6 | -| 22 | CAN1_SCE_IRQn | 6 | -| 25 | TIM1_UP_TIM10_IRQn | 0 | +| 6 | EXTI0_IRQn | 1 | +| 7 | EXTI1_IRQn | 1 | +| 8 | EXTI2_IRQn | 1 | +| 9 | EXTI3_IRQn | 1 | +| 10 | EXTI4_IRQn | 1 | +| 11 | DMA1_Stream0_IRQn | 4 | +| 13 | DMA1_Stream2_IRQn | 10 | +| 15 | DMA1_Stream4_IRQn | 10 | +| 16 | DMA1_Stream5_IRQn | 3 | +| 19 | CAN1_TX_IRQn | 9 | +| 20 | CAN1_RX0_IRQn | 9 | +| 21 | CAN1_RX1_IRQn | 9 | +| 22 | CAN1_SCE_IRQn | 9 | +| 23 | EXTI9_5_IRQn | 1 | +| 40 | EXTI15_10_IRQn | 1 | | 44 | TIM8_UP_TIM13_IRQn | 0 | -| 45 | TIM8_TRG_COM_TIM14_IRQn | 0 | -| 50 | TIM5_IRQn | 5 | -| 51 | SPI3_IRQn | 5 | -| 52 | UART4_IRQn | 5 | -| 67 | OTG_FS_IRQn | 5 | +| 45 | TIM8_TRG_COM_TIM14_IRQn | 6 | +| 50 | TIM5_IRQn | 1 | +| 52 | UART4_IRQn | 10 | +| 67 | OTG_FS_IRQn | 6 | +| 77 | OTG_HS_IRQn (aka ControlLoop_IRQn) | 5 | + ## DMA Streams @@ -44,6 +52,6 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI | | DMA1_Stream2 | 0 | 4 (UART4_RX) | UART0 | | DMA1_Stream4 | 0 | 4 (UART4_TX) | UART0 | -| DMA1_Stream5 | 1 | 0 (SPI3_TX) | SPI | +| DMA1_Stream5 | 2 | 0 (SPI3_TX) | SPI | | DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | From bd1ee78562ab9d8d5a3ea30f20e862eafa64492a Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 23 Sep 2020 16:48:11 +0200 Subject: [PATCH 043/124] fix CI errors --- Firmware/MotorControl/open_loop_controller.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Firmware/MotorControl/open_loop_controller.cpp b/Firmware/MotorControl/open_loop_controller.cpp index 506e4898..3fe9dc24 100644 --- a/Firmware/MotorControl/open_loop_controller.cpp +++ b/Firmware/MotorControl/open_loop_controller.cpp @@ -8,6 +8,9 @@ void OpenLoopController::update(uint32_t timestamp) { float phase = phase_.get_previous().value_or(0.0f); float phase_vel = phase_vel_.get_previous().value_or(0.0f); + (void)prev_Iq; // unused + (void)prev_Vq; // unused + float dt = (float)(timestamp - timestamp_) / (float)TIM_1_8_CLOCK_HZ; Idq_setpoint_ = { From e0c34a66a43e122f3dcbc5bd29f43994252eaa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B8rn=20Ludvigsen?= Date: Sat, 19 Oct 2019 15:16:23 +0200 Subject: [PATCH 044/124] Adds MX_USART2_UART_Init() function --- Firmware/Board/v3/Inc/stm32f4xx_it.h | 3 + Firmware/Board/v3/Inc/usart.h | 2 + Firmware/Board/v3/Src/dma.c | 6 ++ Firmware/Board/v3/Src/spi.c | 2 +- Firmware/Board/v3/Src/stm32f4xx_it.c | 47 ++++++++++++- Firmware/Board/v3/Src/usart.c | 100 +++++++++++++++++++++++++-- 6 files changed, 151 insertions(+), 9 deletions(-) diff --git a/Firmware/Board/v3/Inc/stm32f4xx_it.h b/Firmware/Board/v3/Inc/stm32f4xx_it.h index 784a3333..3bdb143f 100644 --- a/Firmware/Board/v3/Inc/stm32f4xx_it.h +++ b/Firmware/Board/v3/Inc/stm32f4xx_it.h @@ -58,11 +58,14 @@ void DMA1_Stream0_IRQHandler(void); void DMA1_Stream2_IRQHandler(void); void DMA1_Stream4_IRQHandler(void); void DMA1_Stream5_IRQHandler(void); +void DMA1_Stream6_IRQHandler(void); +void DMA1_Stream7_IRQHandler(void); void ADC_IRQHandler(void); void CAN1_TX_IRQHandler(void); void CAN1_RX0_IRQHandler(void); void CAN1_RX1_IRQHandler(void); void CAN1_SCE_IRQHandler(void); +void USART2_IRQHandler(void); void TIM8_TRG_COM_TIM14_IRQHandler(void); void TIM5_IRQHandler(void); void SPI3_IRQHandler(void); diff --git a/Firmware/Board/v3/Inc/usart.h b/Firmware/Board/v3/Inc/usart.h index c987f7f2..ff3d48b8 100644 --- a/Firmware/Board/v3/Inc/usart.h +++ b/Firmware/Board/v3/Inc/usart.h @@ -62,6 +62,7 @@ /* USER CODE END Includes */ extern UART_HandleTypeDef huart4; +extern UART_HandleTypeDef huart2; /* USER CODE BEGIN Private defines */ @@ -70,6 +71,7 @@ extern UART_HandleTypeDef huart4; extern void _Error_Handler(char *, int); void MX_UART4_Init(void); +void MX_USART2_UART_Init(void); /* USER CODE BEGIN Prototypes */ diff --git a/Firmware/Board/v3/Src/dma.c b/Firmware/Board/v3/Src/dma.c index 813c735a..969827c7 100644 --- a/Firmware/Board/v3/Src/dma.c +++ b/Firmware/Board/v3/Src/dma.c @@ -83,6 +83,12 @@ void MX_DMA_Init(void) /* DMA1_Stream5_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 5, 0); HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); + /* DMA1_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); + /* DMA1_Stream7_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn); /* DMA2_Stream0_IRQn interrupt configuration */ // Dear STM, no we _don't_ want to fire an interrupt for this DMA // (it's not possible to deselect this in CubeMX) diff --git a/Firmware/Board/v3/Src/spi.c b/Firmware/Board/v3/Src/spi.c index a3a3311e..42aae784 100644 --- a/Firmware/Board/v3/Src/spi.c +++ b/Firmware/Board/v3/Src/spi.c @@ -110,7 +110,7 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) /* SPI3 DMA Init */ /* SPI3_TX Init */ - hdma_spi3_tx.Instance = DMA1_Stream5; + hdma_spi3_tx.Instance = DMA1_Stream7; hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0; hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE; diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index 1e69bcb9..eb578ec2 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -53,7 +53,10 @@ extern TIM_HandleTypeDef htim5; extern TIM_HandleTypeDef htim8; extern DMA_HandleTypeDef hdma_uart4_rx; extern DMA_HandleTypeDef hdma_uart4_tx; +extern DMA_HandleTypeDef hdma_usart2_rx; +extern DMA_HandleTypeDef hdma_usart2_tx; extern UART_HandleTypeDef huart4; +extern UART_HandleTypeDef huart2; extern TIM_HandleTypeDef htim14; @@ -242,12 +245,40 @@ void DMA1_Stream5_IRQHandler(void) /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */ COUNT_IRQ(DMA1_Stream5_IRQn); /* USER CODE END DMA1_Stream5_IRQn 0 */ - HAL_DMA_IRQHandler(&hdma_spi3_tx); + HAL_DMA_IRQHandler(&hdma_usart2_rx); /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */ /* USER CODE END DMA1_Stream5_IRQn 1 */ } +/** +* @brief This function handles DMA1 stream6 global interrupt. +*/ +void DMA1_Stream6_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */ + COUNT_IRQ(DMA1_Stream6_IRQn); + /* USER CODE END DMA1_Stream6_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_usart2_tx); + /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */ + + /* USER CODE END DMA1_Stream6_IRQn 1 */ +} + +/** +* @brief This function handles DMA1 stream7 global interrupt. +*/ +void DMA1_Stream7_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Stream7_IRQn 0 */ + COUNT_IRQ(DMA1_Stream7_IRQn); + /* USER CODE END DMA1_Stream7_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_spi3_tx); + /* USER CODE BEGIN DMA1_Stream7_IRQn 1 */ + + /* USER CODE END DMA1_Stream7_IRQn 1 */ +} + /** * @brief This function handles CAN1 TX interrupts. */ @@ -304,6 +335,20 @@ void CAN1_SCE_IRQHandler(void) /* USER CODE END CAN1_SCE_IRQn 1 */ } +/** + * @brief This function handles USART2 global interrupt. + */ +void USART2_IRQHandler(void) +{ + /* USER CODE BEGIN USART2_IRQn 0 */ + + /* USER CODE END USART2_IRQn 0 */ + HAL_UART_IRQHandler(&huart2); + /* USER CODE BEGIN USART2_IRQn 1 */ + + /* USER CODE END USART2_IRQn 1 */ +} + /** * @brief This function handles TIM8 trigger and commutation interrupts and TIM14 global interrupt. */ diff --git a/Firmware/Board/v3/Src/usart.c b/Firmware/Board/v3/Src/usart.c index 93bac536..b2c8e461 100644 --- a/Firmware/Board/v3/Src/usart.c +++ b/Firmware/Board/v3/Src/usart.c @@ -58,15 +58,18 @@ /* USER CODE END 0 */ UART_HandleTypeDef huart4; +UART_HandleTypeDef huart2; DMA_HandleTypeDef hdma_uart4_rx; DMA_HandleTypeDef hdma_uart4_tx; +DMA_HandleTypeDef hdma_usart2_rx; +DMA_HandleTypeDef hdma_usart2_tx; /* UART4 init function */ void MX_UART4_Init(void) { huart4.Instance = UART4; - huart4.Init.BaudRate = 115200; // Provisionally this can be changed to 921600 for faster transfers, the low power Arduinos will not keep up. + //huart4.Init.BaudRate = 115200; // Provisionally this can be changed to 921600 for faster transfers, the low power Arduinos will not keep up. huart4.Init.WordLength = UART_WORDLENGTH_8B; huart4.Init.StopBits = UART_STOPBITS_1; huart4.Init.Parity = UART_PARITY_NONE; @@ -78,6 +81,25 @@ void MX_UART4_Init(void) _Error_Handler(__FILE__, __LINE__); } +} +/* USART2 init function */ + +void MX_USART2_UART_Init(void) +{ + + huart2.Instance = USART2; + //huart2.Init.BaudRate = 115200; + huart2.Init.WordLength = UART_WORDLENGTH_8B; + huart2.Init.StopBits = UART_STOPBITS_1; + huart2.Init.Parity = UART_PARITY_NONE; + huart2.Init.Mode = UART_MODE_TX_RX; + huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; + huart2.Init.OverSampling = UART_OVERSAMPLING_16; + if (HAL_UART_Init(&huart2) != HAL_OK) + { + Error_Handler(); + } + } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) @@ -135,6 +157,58 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) /* USER CODE END UART4_MspInit 1 */ } + else if(uartHandle->Instance==USART2) + { + /* USER CODE BEGIN USART2_MspInit 0 */ + + /* USER CODE END USART2_MspInit 0 */ + /* USART2 clock enable */ + __HAL_RCC_USART2_CLK_ENABLE(); + + /* USART2 DMA Init */ + /* USART2_RX Init */ + hdma_usart2_rx.Instance = DMA1_Stream5; + hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4; + hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; + hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_usart2_rx.Init.Mode = DMA_CIRCULAR; + hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW; + hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) + { + _Error_Handler(__FILE__, __LINE__); + } + + __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx); + + /* USART2_TX Init */ + hdma_usart2_tx.Instance = DMA1_Stream6; + hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4; + hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_usart2_tx.Init.Mode = DMA_NORMAL; + hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW; + hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) + { + _Error_Handler(__FILE__, __LINE__); + } + + __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx); + + /* USART2 interrupt Init */ + HAL_NVIC_SetPriority(USART2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(USART2_IRQn); + /* USER CODE BEGIN USART2_MspInit 1 */ + + /* USER CODE END USART2_MspInit 1 */ + } } void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) @@ -147,12 +221,6 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) /* USER CODE END UART4_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_UART4_CLK_DISABLE(); - - /**UART4 GPIO Configuration - PA0-WKUP ------> UART4_TX - PA1 ------> UART4_RX - */ - HAL_GPIO_DeInit(GPIOA, GPIO_1_Pin|GPIO_2_Pin); /* UART4 DMA DeInit */ HAL_DMA_DeInit(uartHandle->hdmarx); @@ -164,6 +232,24 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) /* USER CODE END UART4_MspDeInit 1 */ } + else if(uartHandle->Instance==USART2) + { + /* USER CODE BEGIN USART2_MspDeInit 0 */ + + /* USER CODE END USART2_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_USART2_CLK_DISABLE(); + + /* UART4 DMA DeInit */ + HAL_DMA_DeInit(uartHandle->hdmarx); + HAL_DMA_DeInit(uartHandle->hdmatx); + + /* USART2 interrupt Deinit */ + HAL_NVIC_DisableIRQ(USART2_IRQn); + /* USER CODE BEGIN USART2_MspDeInit 1 */ + + /* USER CODE END USART2_MspDeInit 1 */ + } } /* USER CODE BEGIN 1 */ From e7f046d518a2670e025a1e09add10d593d548e9b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 24 Sep 2020 19:54:52 +0200 Subject: [PATCH 045/124] Add support for UART1 --- CHANGELOG.md | 1 + Firmware/Board/v3/board.cpp | 19 +++++++---- Firmware/communication/communication.cpp | 9 +++++- Firmware/communication/interface_uart.cpp | 7 ++-- Firmware/communication/interface_uart.h | 3 +- Firmware/odrive-interface.yaml | 23 ++++++++++--- docs/interfaces.md | 39 ++++++++++++----------- docs/resources.md | 7 +++- tools/odrive/tests/uart_ascii_test.py | 38 ++++++++++++++++++---- 9 files changed, 103 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a6c0c76..62a7a2a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * [Mechanical brake support](docs/mechanical-brakes.md) +* Support for UART1 on GPIO3 and GPIO4. UART0 (on GPIO1/2) and UART1 can currently not be enabled at the same time. ### Changed diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index d2b0afbb..c20e8568 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -21,7 +21,7 @@ Stm32SpiArbiter spi3_arbiter{&hspi3}; Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; UART_HandleTypeDef* uart0 = &huart4; -UART_HandleTypeDef* uart1 = nullptr; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 +UART_HandleTypeDef* uart1 = &huart2; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 UART_HandleTypeDef* uart2 = nullptr; Drv8301 m0_gate_driver{ @@ -219,14 +219,14 @@ std::array alternate_functions[GPIO_COUNT] = { #if HW_VERSION_MINOR >= 3 /* GPIO1: */ {{{ODrive::GPIO_MODE_UART0, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, /* GPIO2: */ {{{ODrive::GPIO_MODE_UART0, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, - /* GPIO3: */ {{{ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, + /* GPIO3: */ {{{ODrive::GPIO_MODE_UART1, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, #else /* GPIO1: */ {{}}, /* GPIO2: */ {{}}, /* GPIO3: */ {{}}, #endif - /* GPIO4: */ {{{ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, + /* GPIO4: */ {{{ODrive::GPIO_MODE_UART1, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, /* GPIO5: */ {{}}, /* GPIO6: */ {{}}, /* GPIO7: */ {{}}, @@ -273,13 +273,18 @@ bool board_init() { MX_SPI3_Init(); MX_ADC3_Init(); MX_TIM2_Init(); - MX_UART4_Init(); MX_TIM5_Init(); MX_TIM13_Init(); - HAL_UART_DeInit(uart0); - uart0->Init.BaudRate = odrv.config_.uart0_baudrate; - HAL_UART_Init(uart0); + if (odrv.config_.enable_uart0) { + uart0->Init.BaudRate = odrv.config_.uart0_baudrate; + MX_UART4_Init(); + } + + if (odrv.config_.enable_uart1) { + uart1->Init.BaudRate = odrv.config_.uart1_baudrate; + MX_USART2_UART_Init(); + } if (odrv.config_.enable_i2c0) { // Set up the direction GPIO as input diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index faac6f59..715a23eb 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -41,8 +41,15 @@ size_t oscilloscope_pos = 0; void init_communication(void) { printf("hi!\r\n"); + // Dual UART operation not supported yet + if (odrv.config_.enable_uart0 && odrv.config_.enable_uart1) { + odrv.misconfigured_ = true; + } + if (odrv.config_.enable_uart0 && uart0) { - start_uart_server(); + start_uart_server(uart0); + } else if (odrv.config_.enable_uart1 && uart1) { + start_uart_server(uart1); } start_usb_server(); diff --git a/Firmware/communication/interface_uart.cpp b/Firmware/communication/interface_uart.cpp index 74fa02c0..8eff51d8 100644 --- a/Firmware/communication/interface_uart.cpp +++ b/Firmware/communication/interface_uart.cpp @@ -22,8 +22,7 @@ static uint32_t dma_last_rcv_idx; // static thread_local uint32_t deadline_ms = 0; osThreadId uart_thread = 0; -extern UART_HandleTypeDef* uart0; -static UART_HandleTypeDef* huart_ = uart0; // defined in board.cpp. +static UART_HandleTypeDef* huart_ = nullptr; const uint32_t stack_size_uart_thread = 4096; // Bytes @@ -102,7 +101,9 @@ static void uart_server_thread(void * ctx) { } // TODO: allow multiple UART server instances -void start_uart_server() { +void start_uart_server(UART_HandleTypeDef* huart) { + huart_ = huart; + // DMA is set up to receive in a circular buffer forever. // We dont use interrupts to fetch the data, instead we periodically read // data out of the circular buffer into a parse buffer, controlled by a state machine diff --git a/Firmware/communication/interface_uart.h b/Firmware/communication/interface_uart.h index a7df55bd..83b70e66 100644 --- a/Firmware/communication/interface_uart.h +++ b/Firmware/communication/interface_uart.h @@ -9,11 +9,12 @@ extern "C" { #endif #include +#include "usart.h" extern osThreadId uart_thread; extern const uint32_t stack_size_uart_thread; -void start_uart_server(void); +void start_uart_server(UART_HandleTypeDef* huart); void uart_poll(void); #ifdef __cplusplus diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 43126cbd..cb881b5b 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -99,13 +99,24 @@ interfaces: enable_uart0: type: bool - doc: Enables/disables UART0. You also need to set the corresponding GPIOs to GPIO_MODE_UART0. Changing this requires a reboot. - enable_uart1: {type: bool, doc: Not supported on ODrive v3.x.} + brief: Enables/disables UART0. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART0. + Refer to [interfaces](interfaces.md) to see which pins support UART0. + Changing this requires a reboot. + enable_uart0: + type: bool + brief: Enables/disables UART1. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART1. + Refer to [interfaces](interfaces.md) to see which pins support UART1. + Changing this requires a reboot. enable_uart2: {type: bool, doc: Not supported on ODrive v3.x.} uart0_baudrate: type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. doc: | - Defines the baudrate used on the UART interface. Some baudrates will have a small timing error due to hardware limitations. Here's an (incomplete) list of baudrates for ODrive v3.x: @@ -127,7 +138,11 @@ interfaces: For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). - uart1_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} + uart1_baudrate: + type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. + doc: See `uart0_baudrate` for details. uart2_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} enable_can0: type: bool diff --git a/docs/interfaces.md b/docs/interfaces.md index 0b9cb6c3..5de89a3b 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -18,25 +18,25 @@ The ODrive can be controlled over various ports and protocols. If you're comfort ## Pinout -| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | -|----|---------------|------------------------|-----------------------|-------------------|------------------|------------------|------------------|------------------|------------------|------------------------| -| 0 | _not a pin_ | | | | | | | | | | -| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | PWM0.0 | | | | | mechanical brake | -| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | PWM0.1 | | | | | mechanical brake | -| 3 | GPIO3 | general purpose | **analog input** | | PWM0.2 | | | | | mechanical brake | -| 4 | GPIO4 | general purpose | **analog input** | | PWM0.3 | | | | | mechanical brake | -| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | mechanical brake | -| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | mechanical brake | -| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | mechanical brake | -| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | mechanical brake | -| 9 | M0.A | general purpose | | | | | | **ENC0.A** | | | -| 10 | M0.B | general purpose | | | | | | **ENC0.B** | | | -| 11 | M0.Z | **general purpose** | | | | | | | | | -| 12 | M1.A | general purpose | | | | | I2C.SCL | | **ENC1.A** | | -| 13 | M1.B | general purpose | | | | | I2C.SDA | | **ENC1.B** | | -| 14 | M1.Z | **general purpose** | | | | | | | | | -| 15 | _not exposed_ | general purpose | | | | **CAN0.RX** | I2C.SCL | | | | -| 16 | _not exposed_ | general purpose | | | | **CAN0.TX** | I2C.SDA | | | | +| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_UART1` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | +|----|---------------|------------------------|-----------------------|-------------------|-------------------|------------------|------------------|------------------|------------------|------------------|------------------------| +| 0 | _not a pin_ | | | | | | | | | | | +| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | | PWM0.0 | | | | | mechanical brake | +| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | | PWM0.1 | | | | | mechanical brake | +| 3 | GPIO3 | general purpose | **analog input** | | **UART1.TX** | PWM0.2 | | | | | mechanical brake | +| 4 | GPIO4 | general purpose | **analog input** | | **UART1.RX** | PWM0.3 | | | | | mechanical brake | +| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | | mechanical brake | +| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 9 | M0.A | general purpose | | | | | | | **ENC0.A** | | | +| 10 | M0.B | general purpose | | | | | | | **ENC0.B** | | | +| 11 | M0.Z | **general purpose** | | | | | | | | | | +| 12 | M1.A | general purpose | | | | | | I2C.SCL | | **ENC1.A** | | +| 13 | M1.B | general purpose | | | | | | I2C.SDA | | **ENC1.B** | | +| 14 | M1.Z | **general purpose** | | | | | | | | | | +| 15 | _not exposed_ | general purpose | | | | | **CAN0.RX** | I2C.SCL | | | | +| 16 | _not exposed_ | general purpose | | | | | **CAN0.TX** | I2C.SDA | | | | (*) ODrive v3.5 and later
@@ -50,6 +50,7 @@ Notes: * Digital mode is a general purpose mode that can be used for these functions: step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS. * You must also connect GND between ODrive and your other board. * ODrive v3.3 and onward have 5V tolerant GPIO pins. +* Simultaneous operation of UART0 and UART1 is currently not supported. ## Native Protocol diff --git a/docs/resources.md b/docs/resources.md index 2d6fcb8f..e0408179 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -21,14 +21,17 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | 13 | DMA1_Stream2_IRQn | 5 | | 15 | DMA1_Stream4_IRQn | 5 | | 16 | DMA1_Stream5_IRQn | 5 | +| 17 | DMA1_Stream6_IRQn | 5 | | 18 | ADC_IRQn | 5 | | 19 | CAN1_TX_IRQn | 6 | | 20 | CAN1_RX0_IRQn | 6 | | 21 | CAN1_RX1_IRQn | 6 | | 22 | CAN1_SCE_IRQn | 6 | | 25 | TIM1_UP_TIM10_IRQn | 0 | +| 38 | USART2_IRQn | 5 | | 44 | TIM8_UP_TIM13_IRQn | 0 | | 45 | TIM8_TRG_COM_TIM14_IRQn | 0 | +| 47 | DMA1_Stream7_IRQn | 5 | | 50 | TIM5_IRQn | 5 | | 51 | SPI3_IRQn | 5 | | 52 | UART4_IRQn | 5 | @@ -44,6 +47,8 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI | | DMA1_Stream2 | 0 | 4 (UART4_RX) | UART0 | | DMA1_Stream4 | 0 | 4 (UART4_TX) | UART0 | -| DMA1_Stream5 | 1 | 0 (SPI3_TX) | SPI | +| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART1 | +| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART1 | +| DMA1_Stream7 | 1 | 0 (SPI3_TX) | SPI | | DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | diff --git a/tools/odrive/tests/uart_ascii_test.py b/tools/odrive/tests/uart_ascii_test.py index 962f3797..7f568f3c 100644 --- a/tools/odrive/tests/uart_ascii_test.py +++ b/tools/odrive/tests/uart_ascii_test.py @@ -41,16 +41,40 @@ class TestUartAscii(): 'rx': (odrive.gpio1, True), 'tx': (odrive.gpio2, False) }, SerialPortComponent)) - yield (odrive, ports) + yield (odrive, 0, ports) - def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - logger.debug('Enabling UART...') + # Enable the line below to manually test UART1. For this you need + # to manually move to the wires go to GPIO1/2 to GPIO3/4. The ones + # that normally go to GPIO3/4 have a low pass filter. + #yield (odrive, 1, ports) + + def run_test(self, odrive: ODriveComponent, uart_num: int, port: SerialPortComponent, logger: Logger): + logger.debug('Enabling UART {}...'.format(uart_num)) + # GPIOs might be in use by something other than UART and some components # might be configured so that they would fail in the later test. - odrive.erase_config_and_reboot() - odrive.handle.config.enable_uart0 = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 - odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.disable_mappings() + odrive.handle.config.enable_uart0 = False + odrive.handle.config.uart0_baudrate = 115200 + odrive.handle.config.enable_uart1 = False + odrive.handle.config.uart1_baudrate = 115200 + odrive.handle.config.enable_uart2 = False + odrive.handle.config.uart2_baudrate = 115200 + + if uart_num == 0: + odrive.handle.config.enable_uart0 = True + odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 + odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN + odrive.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN + else: + odrive.handle.config.enable_uart1 = True + odrive.handle.config.gpio1_mode = GPIO_MODE_ANALOG_IN + odrive.handle.config.gpio2_mode = GPIO_MODE_ANALOG_IN + odrive.handle.config.gpio3_mode = GPIO_MODE_UART1 + odrive.handle.config.gpio4_mode = GPIO_MODE_UART1 + + odrive.save_config_and_reboot() with port.open(115200) as ser: # reset port to known state From 29742a1392f2a578fdcf54582eccddab40ebfad0 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Sat, 26 Sep 2020 23:17:30 -0400 Subject: [PATCH 046/124] Update top level gitignore for gui artifacts --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 66a8990f..c94fc787 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,8 @@ ODrive\.files ODrive\.includes Firmware/Tests/bin/ + +# GUI +GUI/dist_electron +GUI/node_modules +GUI/build \ No newline at end of file From 7851d20e2ba3e1558c1dff1f851c78aff38e59e8 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Sat, 26 Sep 2020 23:17:30 -0400 Subject: [PATCH 047/124] Update top level gitignore for gui artifacts --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 66a8990f..c94fc787 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,8 @@ ODrive\.files ODrive\.includes Firmware/Tests/bin/ + +# GUI +GUI/dist_electron +GUI/node_modules +GUI/build \ No newline at end of file From 074b017ab260d47fba20f1fb8d14083f1c59f181 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Sat, 26 Sep 2020 23:17:30 -0400 Subject: [PATCH 048/124] Update top level gitignore for gui artifacts --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 66a8990f..c94fc787 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,8 @@ ODrive\.files ODrive\.includes Firmware/Tests/bin/ + +# GUI +GUI/dist_electron +GUI/node_modules +GUI/build \ No newline at end of file From 9e090a07a6074575c9cb9bfc6bf72ffa68771f89 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Sat, 26 Sep 2020 23:17:30 -0400 Subject: [PATCH 049/124] Update top level gitignore for gui artifacts --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 66a8990f..c94fc787 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,8 @@ ODrive\.files ODrive\.includes Firmware/Tests/bin/ + +# GUI +GUI/dist_electron +GUI/node_modules +GUI/build \ No newline at end of file From e1c7ddbafa66b8ba08a9e2f5a225142637d072a9 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 28 Sep 2020 18:54:48 +0200 Subject: [PATCH 050/124] fix stack overflow --- Firmware/MotorControl/motor.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index f88ffe30..66ebb468 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -247,11 +247,11 @@ void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) { * motor phases are floating and will not be enabled again until * arm() is called. */ -bool Motor::disarm(bool* was_armed) { - bool dummy; - was_armed = was_armed ? was_armed : &dummy; +bool Motor::disarm(bool* p_was_armed) { + bool was_armed; + CRITICAL_SECTION() { - *was_armed = is_armed_; + was_armed = is_armed_; if (is_armed_) { gate_driver_.set_enabled(false); } @@ -267,6 +267,10 @@ bool Motor::disarm(bool* was_armed) { update_brake_current(); } + if (p_was_armed) { + *p_was_armed = was_armed; + } + return true; } @@ -316,7 +320,6 @@ bool Motor::setup() { void Motor::disarm_with_error(Motor::Error error){ error_ |= error; disarm(); - update_brake_current(); } bool Motor::do_checks(uint32_t timestamp) { From 5877ba5dd9b99abdbec7f14e27983c3dc39c382d Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 5 Oct 2020 14:08:43 +0200 Subject: [PATCH 051/124] fix enable_uart1 missing error --- Firmware/fibre/tools/interface_generator.py | 2 ++ Firmware/odrive-interface.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/fibre/tools/interface_generator.py b/Firmware/fibre/tools/interface_generator.py index 659d97f6..161ae453 100644 --- a/Firmware/fibre/tools/interface_generator.py +++ b/Firmware/fibre/tools/interface_generator.py @@ -91,6 +91,8 @@ properties: additionalProperties: false """)) +# TODO: detect duplicate keys in yaml dictionaries + # Source: https://stackoverflow.com/a/53647080/3621512 class SafeLineLoader(yaml.SafeLoader): pass diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index cb881b5b..9e4c15f6 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -104,7 +104,7 @@ interfaces: You also need to set the corresponding GPIOs to GPIO_MODE_UART0. Refer to [interfaces](interfaces.md) to see which pins support UART0. Changing this requires a reboot. - enable_uart0: + enable_uart1: type: bool brief: Enables/disables UART1. doc: | From e15ab6018463f43f1b328e5e391fc20f9618a0e3 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 15 Oct 2020 11:52:44 +0200 Subject: [PATCH 052/124] change user facing component names UART0 => UART_A UART1 => UART_B UART2 => UART_C CAN0 => CAN_A SPI0 => SPI_A I2C0 => I2C_A PWM0 => PWM --- Firmware/Board/v3/Inc/board.h | 14 +++--- Firmware/Board/v3/board.cpp | 36 +++++++------- Firmware/MotorControl/main.cpp | 30 ++++++------ Firmware/MotorControl/odrive_main.h | 16 +++--- Firmware/communication/communication.cpp | 14 +++--- Firmware/odrive-interface.yaml | 62 ++++++++++++------------ docs/interfaces.md | 46 +++++++++--------- docs/resources.md | 12 ++--- tools/odrive/enums.py | 14 +++--- tools/odrive/tests/can_test.py | 6 +-- tools/odrive/tests/integration_test.py | 6 +-- tools/odrive/tests/pwm_input_test.py | 2 +- tools/odrive/tests/uart_ascii_test.py | 48 +++++++++--------- 13 files changed, 153 insertions(+), 153 deletions(-) diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h index 931286d5..4d83f9af 100644 --- a/Firmware/Board/v3/Inc/board.h +++ b/Firmware/Board/v3/Inc/board.h @@ -43,8 +43,8 @@ #define DEFAULT_GPIO_MODES \ ODriveIntf::GPIO_MODE_DIGITAL, \ - ODriveIntf::GPIO_MODE_UART0, \ - ODriveIntf::GPIO_MODE_UART0, \ + ODriveIntf::GPIO_MODE_UART_A, \ + ODriveIntf::GPIO_MODE_UART_A, \ ODriveIntf::GPIO_MODE_ANALOG_IN, \ ODriveIntf::GPIO_MODE_ANALOG_IN, \ ODriveIntf::GPIO_MODE_ANALOG_IN, \ @@ -57,8 +57,8 @@ ODriveIntf::GPIO_MODE_ENC1, \ ODriveIntf::GPIO_MODE_ENC1, \ ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN, \ - ODriveIntf::GPIO_MODE_CAN0, \ - ODriveIntf::GPIO_MODE_CAN0, + ODriveIntf::GPIO_MODE_CAN_A, \ + ODriveIntf::GPIO_MODE_CAN_A, #define TIM_TIME_BASE TIM14 @@ -89,9 +89,9 @@ extern USBD_HandleTypeDef& usb_dev_handle; extern Stm32SpiArbiter& ext_spi_arbiter; -extern UART_HandleTypeDef* uart0; -extern UART_HandleTypeDef* uart1; -extern UART_HandleTypeDef* uart2; +extern UART_HandleTypeDef* uart_a; +extern UART_HandleTypeDef* uart_b; +extern UART_HandleTypeDef* uart_c; extern PwmInput pwm0_input; #endif diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index c20e8568..74d7b455 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -20,9 +20,9 @@ extern "C" void SystemClock_Config(void); // defined in main.c generated by Cube Stm32SpiArbiter spi3_arbiter{&hspi3}; Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; -UART_HandleTypeDef* uart0 = &huart4; -UART_HandleTypeDef* uart1 = &huart2; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 -UART_HandleTypeDef* uart2 = nullptr; +UART_HandleTypeDef* uart_a = &huart4; +UART_HandleTypeDef* uart_b = &huart2; // TODO: this could be supported in ODrive v3.6 (or similar) using STM32's USART2 +UART_HandleTypeDef* uart_c = nullptr; Drv8301 m0_gate_driver{ &spi3_arbiter, @@ -217,16 +217,16 @@ std::array alternate_functions[GPIO_COUNT] = { /* GPIO0 (inexistent): */ {{}}, #if HW_VERSION_MINOR >= 3 - /* GPIO1: */ {{{ODrive::GPIO_MODE_UART0, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, - /* GPIO2: */ {{{ODrive::GPIO_MODE_UART0, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, - /* GPIO3: */ {{{ODrive::GPIO_MODE_UART1, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, + /* GPIO1: */ {{{ODrive::GPIO_MODE_UART_A, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, + /* GPIO2: */ {{{ODrive::GPIO_MODE_UART_A, GPIO_AF8_UART4}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, + /* GPIO3: */ {{{ODrive::GPIO_MODE_UART_B, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, #else /* GPIO1: */ {{}}, /* GPIO2: */ {{}}, /* GPIO3: */ {{}}, #endif - /* GPIO4: */ {{{ODrive::GPIO_MODE_UART1, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM0, GPIO_AF2_TIM5}}}, + /* GPIO4: */ {{{ODrive::GPIO_MODE_UART_B, GPIO_AF7_USART2}, {ODrive::GPIO_MODE_PWM, GPIO_AF2_TIM5}}}, /* GPIO5: */ {{}}, /* GPIO6: */ {{}}, /* GPIO7: */ {{}}, @@ -234,11 +234,11 @@ std::array alternate_functions[GPIO_COUNT] = { /* ENC0_A: */ {{{ODrive::GPIO_MODE_ENC0, GPIO_AF2_TIM3}}}, /* ENC0_B: */ {{{ODrive::GPIO_MODE_ENC0, GPIO_AF2_TIM3}}}, /* ENC0_Z: */ {{}}, - /* ENC1_A: */ {{{ODrive::GPIO_MODE_I2C0, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}}, - /* ENC1_B: */ {{{ODrive::GPIO_MODE_I2C0, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}}, + /* ENC1_A: */ {{{ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}}, + /* ENC1_B: */ {{{ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}, {ODrive::GPIO_MODE_ENC1, GPIO_AF2_TIM4}}}, /* ENC1_Z: */ {{}}, - /* CAN_R: */ {{{ODrive::GPIO_MODE_CAN0, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C0, GPIO_AF4_I2C1}}}, - /* CAN_D: */ {{{ODrive::GPIO_MODE_CAN0, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C0, GPIO_AF4_I2C1}}}, + /* CAN_R: */ {{{ODrive::GPIO_MODE_CAN_A, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}}}, + /* CAN_D: */ {{{ODrive::GPIO_MODE_CAN_A, GPIO_AF9_CAN1}, {ODrive::GPIO_MODE_I2C_A, GPIO_AF4_I2C1}}}, }; #if HW_VERSION_MINOR <= 2 @@ -276,17 +276,17 @@ bool board_init() { MX_TIM5_Init(); MX_TIM13_Init(); - if (odrv.config_.enable_uart0) { - uart0->Init.BaudRate = odrv.config_.uart0_baudrate; + if (odrv.config_.enable_uart_a) { + uart_a->Init.BaudRate = odrv.config_.uart_a_baudrate; MX_UART4_Init(); } - if (odrv.config_.enable_uart1) { - uart1->Init.BaudRate = odrv.config_.uart1_baudrate; + if (odrv.config_.enable_uart_b) { + uart_b->Init.BaudRate = odrv.config_.uart_b_baudrate; MX_USART2_UART_Init(); } - if (odrv.config_.enable_i2c0) { + if (odrv.config_.enable_i2c_a) { // Set up the direction GPIO as input get_gpio(3).config(GPIO_MODE_INPUT, GPIO_PULLUP); get_gpio(4).config(GPIO_MODE_INPUT, GPIO_PULLUP); @@ -300,11 +300,11 @@ bool board_init() { MX_I2C1_Init(i2c_stats_.addr); } - if (odrv.config_.enable_can0) { + if (odrv.config_.enable_can_a) { // The CAN initialization will (and must) init its own GPIOs before the // GPIO modes are initialized. Therefore we ensure that the later GPIO // mode initialization won't override the CAN mode. - if (odrv.config_.gpio_modes[15] != ODriveIntf::GPIO_MODE_CAN0 || odrv.config_.gpio_modes[16] != ODriveIntf::GPIO_MODE_CAN0) { + if (odrv.config_.gpio_modes[15] != ODriveIntf::GPIO_MODE_CAN_A || odrv.config_.gpio_modes[16] != ODriveIntf::GPIO_MODE_CAN_A) { odrv.misconfigured_ = true; } else { MX_CAN1_Init(); diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 27d7be86..08d8793e 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -397,9 +397,9 @@ extern "C" int main(void) { } odrv.misconfigured_ = odrv.misconfigured_ - || (odrv.config_.enable_uart0 && !uart0) - || (odrv.config_.enable_uart1 && !uart1) - || (odrv.config_.enable_uart2 && !uart2); + || (odrv.config_.enable_uart_a && !uart_a) + || (odrv.config_.enable_uart_b && !uart_b) + || (odrv.config_.enable_uart_c && !uart_c); // Init board-specific peripherals if (!board_init()) { @@ -457,49 +457,49 @@ extern "C" int main(void) { GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; } break; - case ODriveIntf::GPIO_MODE_UART0: { + case ODriveIntf::GPIO_MODE_UART_A: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - if (!odrv.config_.enable_uart0) { + if (!odrv.config_.enable_uart_a) { odrv.misconfigured_ = true; } } break; - case ODriveIntf::GPIO_MODE_UART1: { + case ODriveIntf::GPIO_MODE_UART_B: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - if (!odrv.config_.enable_uart1) { + if (!odrv.config_.enable_uart_b) { odrv.misconfigured_ = true; } } break; - case ODriveIntf::GPIO_MODE_UART2: { + case ODriveIntf::GPIO_MODE_UART_C: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = (i == 0) ? GPIO_PULLDOWN : GPIO_PULLUP; // this is probably swapped but imitates old behavior GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - if (!odrv.config_.enable_uart2) { + if (!odrv.config_.enable_uart_c) { odrv.misconfigured_ = true; } } break; - case ODriveIntf::GPIO_MODE_CAN0: { + case ODriveIntf::GPIO_MODE_CAN_A: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - if (!odrv.config_.enable_can0) { + if (!odrv.config_.enable_can_a) { odrv.misconfigured_ = true; } } break; - case ODriveIntf::GPIO_MODE_I2C0: { + case ODriveIntf::GPIO_MODE_I2C_A: { GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - if (!odrv.config_.enable_i2c0) { + if (!odrv.config_.enable_i2c_a) { odrv.misconfigured_ = true; } } break; - //case ODriveIntf::GPIO_MODE_SPI0: { // TODO + //case ODriveIntf::GPIO_MODE_SPI_A: { // TODO //} break; - case ODriveIntf::GPIO_MODE_PWM0: { + case ODriveIntf::GPIO_MODE_PWM: { GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index a3c33a1d..acfb7ac1 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -61,14 +61,14 @@ struct BoardConfig_t { DEFAULT_GPIO_MODES }; - bool enable_uart0 = true; - bool enable_uart1 = false; - bool enable_uart2 = false; - uint32_t uart0_baudrate = 115200; - uint32_t uart1_baudrate = 115200; - uint32_t uart2_baudrate = 115200; - bool enable_can0 = true; - bool enable_i2c0 = false; + bool enable_uart_a = true; + bool enable_uart_b = false; + bool enable_uart_c = false; + uint32_t uart_a_baudrate = 115200; + uint32_t uart_b_baudrate = 115200; + uint32_t uart_c_baudrate = 115200; + bool enable_can_a = true; + bool enable_i2c_a = false; bool enable_ascii_protocol_on_usb = true; float max_regen_current = 0.0f; float brake_resistance = DEFAULT_BRAKE_RESISTANCE; diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index 715a23eb..b24a2f58 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -42,23 +42,23 @@ void init_communication(void) { printf("hi!\r\n"); // Dual UART operation not supported yet - if (odrv.config_.enable_uart0 && odrv.config_.enable_uart1) { + if (odrv.config_.enable_uart_a && odrv.config_.enable_uart_b) { odrv.misconfigured_ = true; } - if (odrv.config_.enable_uart0 && uart0) { - start_uart_server(uart0); - } else if (odrv.config_.enable_uart1 && uart1) { - start_uart_server(uart1); + if (odrv.config_.enable_uart_a && uart_a) { + start_uart_server(uart_a); + } else if (odrv.config_.enable_uart_b && uart_b) { + start_uart_server(uart_b); } start_usb_server(); - if (odrv.config_.enable_i2c0) { + if (odrv.config_.enable_i2c_a) { start_i2c_server(); } - if (odrv.config_.enable_can0) { + if (odrv.config_.enable_can_a) { odCAN->start_can_server(); } } diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 9e4c15f6..a3eb74b3 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -97,22 +97,22 @@ interfaces: gpio15_mode: {type: GpioMode, doc: Mode of GPIO15 (changes take effect after reboot), c_name: 'gpio_modes[15]'} gpio16_mode: {type: GpioMode, doc: Mode of GPIO16 (changes take effect after reboot), c_name: 'gpio_modes[16]'} - enable_uart0: + enable_uart_a: type: bool - brief: Enables/disables UART0. + brief: Enables/disables UART_A. doc: | - You also need to set the corresponding GPIOs to GPIO_MODE_UART0. - Refer to [interfaces](interfaces.md) to see which pins support UART0. + You also need to set the corresponding GPIOs to GPIO_MODE_UART_A. + Refer to [interfaces](interfaces.md) to see which pins support UART_A. Changing this requires a reboot. - enable_uart1: + enable_uart_b: type: bool - brief: Enables/disables UART1. + brief: Enables/disables UART_B. doc: | - You also need to set the corresponding GPIOs to GPIO_MODE_UART1. - Refer to [interfaces](interfaces.md) to see which pins support UART1. + You also need to set the corresponding GPIOs to GPIO_MODE_UART_B. + Refer to [interfaces](interfaces.md) to see which pins support UART_B. Changing this requires a reboot. - enable_uart2: {type: bool, doc: Not supported on ODrive v3.x.} - uart0_baudrate: + enable_uart_c: {type: bool, doc: Not supported on ODrive v3.x.} + uart_a_baudrate: type: uint32 unit: baud/s brief: Defines the baudrate used on the UART interface. @@ -138,21 +138,21 @@ interfaces: For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). - uart1_baudrate: + uart_b_baudrate: type: uint32 unit: baud/s brief: Defines the baudrate used on the UART interface. - doc: See `uart0_baudrate` for details. - uart2_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} - enable_can0: + doc: See `uart_a_baudrate` for details. + uart_c_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} + enable_can_a: type: bool doc: | Enables CAN. Changing this setting requires a reboot. - enable_i2c0: + enable_i2c_a: type: bool doc: | Enables I2C. The I2C pins on ODrive v3.x are in conflict with CAN. - This setting has no effect if `enable_can0` is also true. + This setting has no effect if `enable_can_a` is also true. This setting has no effect on ODrive v3.2 or earlier. Changing this setting requires a reboot. enable_ascii_protocol_on_usb: bool @@ -216,10 +216,10 @@ interfaces: brief: Max current the power supply can sink. doc: You most likely want a non-positive value here. Set to -INFINITY to disable. - gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} + gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} user_config_loaded: readonly uint32 @@ -234,10 +234,10 @@ interfaces: Possible causes: - A GPIO was set to a mode that it doesn't support - A GPIO was set to a mode for which the corresponding feature was - not enabled. Example: `GPIO_MODE_UART0` was used without enabling - `config.enable_uart0`. + not enabled. Example: `GPIO_MODE_UART_A` was used without enabling + `config.enable_uart_a`. - A feature was enabled which is not supported on this hardware. - Example: `config.enable_uart2` set to true on ODrive v3.x. + Example: `config.enable_uart_c` set to true on ODrive v3.x. - A GPIO was used as an interrupt input for two internal components or two GPIOs that are mutually exclusive in their interrupt capability were both used as interrupt input. @@ -962,15 +962,15 @@ valuetypes: doc: | The pin can be used for one or more of these functions: Sin/cos encoders, analog input, `get_adc_voltage`. - Uart0: {doc: See `config.enable_uart0`.} - Uart1: {doc: This mode is not supported on ODrive v3.x.} - Uart2: {doc: This mode is not supported on ODrive v3.x.} - Can0: {doc: See `config.enable_can0`.} - I2c0: {doc: See `config.enable_i2c0`.} - Spi0: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they - cannot be configured through software. Consequently, even though SPI0 + UartA: {doc: See `config.enable_uart_a`.} + UartB: {doc: This mode is not supported on ODrive v3.x.} + UartC: {doc: This mode is not supported on ODrive v3.x.} + CanA: {doc: See `config.enable_can_a`.} + I2cA: {doc: See `config.enable_i2c_a`.} + SpiA: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they + cannot be configured through software. Consequently, even though SPI_A is exposed, this mode is of no use on ODrive v3.x.} - Pwm0: {doc: See `config.gpio0_pwm_mapping`.} + Pwm: {doc: See `config.gpio0_pwm_mapping`.} Enc0: {doc: The pin is used by quadrature encoder 0.} Enc1: {doc: The pin is used by quadrature encoder 1.} Enc2: {doc: This mode is not supported on ODrive v3.x.} diff --git a/docs/interfaces.md b/docs/interfaces.md index 5de89a3b..f2eae872 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -18,25 +18,25 @@ The ODrive can be controlled over various ports and protocols. If you're comfort ## Pinout -| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_UART1` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | -|----|---------------|------------------------|-----------------------|-------------------|-------------------|------------------|------------------|------------------|------------------|------------------|------------------------| -| 0 | _not a pin_ | | | | | | | | | | | -| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | | PWM0.0 | | | | | mechanical brake | -| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | | PWM0.1 | | | | | mechanical brake | -| 3 | GPIO3 | general purpose | **analog input** | | **UART1.TX** | PWM0.2 | | | | | mechanical brake | -| 4 | GPIO4 | general purpose | **analog input** | | **UART1.RX** | PWM0.3 | | | | | mechanical brake | -| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | | mechanical brake | -| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | -| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | -| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | -| 9 | M0.A | general purpose | | | | | | | **ENC0.A** | | | -| 10 | M0.B | general purpose | | | | | | | **ENC0.B** | | | -| 11 | M0.Z | **general purpose** | | | | | | | | | | -| 12 | M1.A | general purpose | | | | | | I2C.SCL | | **ENC1.A** | | -| 13 | M1.B | general purpose | | | | | | I2C.SDA | | **ENC1.B** | | -| 14 | M1.Z | **general purpose** | | | | | | | | | | -| 15 | _not exposed_ | general purpose | | | | | **CAN0.RX** | I2C.SCL | | | | -| 16 | _not exposed_ | general purpose | | | | | **CAN0.TX** | I2C.SDA | | | | +| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART_A` | `GPIO_MODE_UART_B` | `GPIO_MODE_PWM` | `GPIO_MODE_CAN_A` | `GPIO_MODE_I2C_A` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | +|----|---------------|------------------------|-----------------------|--------------------|--------------------|------------------|------------------|-------------------|------------------|------------------|------------------------| +| 0 | _not a pin_ | | | | | | | | | | | +| 1 | GPIO1 (+) | general purpose | analog input | **UART_A.TX** | | PWM0.0 | | | | | mechanical brake | +| 2 | GPIO2 (+) | general purpose | analog input | **UART_A.RX** | | PWM0.1 | | | | | mechanical brake | +| 3 | GPIO3 | general purpose | **analog input** | | **UART_B.TX** | PWM0.2 | | | | | mechanical brake | +| 4 | GPIO4 | general purpose | **analog input** | | **UART_B.RX** | PWM0.3 | | | | | mechanical brake | +| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | | mechanical brake | +| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | | mechanical brake | +| 9 | M0.A | general purpose | | | | | | | **ENC0.A** | | | +| 10 | M0.B | general purpose | | | | | | | **ENC0.B** | | | +| 11 | M0.Z | **general purpose** | | | | | | | | | | +| 12 | M1.A | general purpose | | | | | | I2C.SCL | | **ENC1.A** | | +| 13 | M1.B | general purpose | | | | | | I2C.SDA | | **ENC1.B** | | +| 14 | M1.Z | **general purpose** | | | | | | | | | | +| 15 | _not exposed_ | general purpose | | | | | **CAN_A.RX** | I2C.SCL | | | | +| 16 | _not exposed_ | general purpose | | | | | **CAN_A.TX** | I2C.SDA | | | | (*) ODrive v3.5 and later
@@ -46,11 +46,11 @@ Notes: * Changes to the pin configuration only take effect after `odrv0.save_configuration()` and `odrv0.reboot()` * Bold font marks the default configuration. * If a GPIO is set to an unsupported mode it will be left uninitialized. -* When setting a GPIO to a special purpose mode (e.g. `GPIO_MODE_UART0`) you must also enable the corresponding feature (e.g. `.config.enable_uart`). +* When setting a GPIO to a special purpose mode (e.g. `GPIO_MODE_UART_A`) you must also enable the corresponding feature (e.g. `.config.enable_uart_a`). * Digital mode is a general purpose mode that can be used for these functions: step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS. * You must also connect GND between ODrive and your other board. * ODrive v3.3 and onward have 5V tolerant GPIO pins. -* Simultaneous operation of UART0 and UART1 is currently not supported. +* Simultaneous operation of UART_A and UART_B is currently not supported. ## Native Protocol @@ -116,7 +116,7 @@ Any of the numerical parameters that are writable from the ODrive Tool can be ho 2. If you want to control your ODrive with the PWM input without using anything else to activate the ODrive, you can configure the ODrive such that axis 0 automatically goes operational at startup. See [here](commands.md#startup-procedure) for more information. 3. In ODrive Tool, configure the PWM input mapping ``` - odrv0.config.gpio4_mode = GPIO_MODE_PWM0 + odrv0.config.gpio4_mode = GPIO_MODE_PWM odrv0.config.gpio4_pwm_mapping.min = -2 odrv0.config.gpio4_pwm_mapping.max = 2 odrv0.config.gpio4_pwm_mapping.endpoint = odrv0.axis0.controller._remote_attributes['input_pos'] @@ -166,7 +166,7 @@ If you plan to access the USB endpoints directly it is recommended that you use ### UART -UART0 is enabled by default with a baudrate of 115200 on the pins as shown in [Pinout](#pinout). Don't forget to also connect GND of the two UART devices. You can use `odrv0.config.uart0_baudrate` to change the baudrate and `odrv0.config.enable_uart0` to disable/reenable UART0. +UART_A is enabled by default with a baudrate of 115200 on the pins as shown in [Pinout](#pinout). Don't forget to also connect GND of the two UART devices. You can use `odrv0.config.uart_a_baudrate` to change the baudrate and `odrv0.config.enable_uart_a` to disable/reenable UART_A. ## CAN Simple Protocol diff --git a/docs/resources.md b/docs/resources.md index e0408179..ead55eca 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -44,11 +44,11 @@ Most information in this file can be reproduced by running `dump_interrupts(odrv | Name | Prio | Channel | High Level Func | |--------------|------|----------------------------------|-----------------| -| DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI | -| DMA1_Stream2 | 0 | 4 (UART4_RX) | UART0 | -| DMA1_Stream4 | 0 | 4 (UART4_TX) | UART0 | -| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART1 | -| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART1 | -| DMA1_Stream7 | 1 | 0 (SPI3_TX) | SPI | +| DMA1_Stream0 | 1 | 0 (SPI3_RX) | SPI_A | +| DMA1_Stream2 | 0 | 4 (UART4_RX) | UART_A | +| DMA1_Stream4 | 0 | 4 (UART4_TX) | UART_A | +| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART_B | +| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART_B | +| DMA1_Stream7 | 1 | 0 (SPI3_TX) | SPI_A | | DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index ea11aa8e..33dd1ca6 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -8,13 +8,13 @@ GPIO_MODE_DIGITAL = 0 GPIO_MODE_DIGITAL_PULL_UP = 1 GPIO_MODE_DIGITAL_PULL_DOWN = 2 GPIO_MODE_ANALOG_IN = 3 -GPIO_MODE_UART0 = 4 -GPIO_MODE_UART1 = 5 -GPIO_MODE_UART2 = 6 -GPIO_MODE_CAN0 = 7 -GPIO_MODE_I2C0 = 8 -GPIO_MODE_SPI0 = 9 -GPIO_MODE_PWM0 = 10 +GPIO_MODE_UART_A = 4 +GPIO_MODE_UART_B = 5 +GPIO_MODE_UART_C = 6 +GPIO_MODE_CAN_A = 7 +GPIO_MODE_I2C_A = 8 +GPIO_MODE_SPI_A = 9 +GPIO_MODE_PWM = 10 GPIO_MODE_ENC0 = 11 GPIO_MODE_ENC1 = 12 GPIO_MODE_ENC2 = 13 diff --git a/tools/odrive/tests/can_test.py b/tools/odrive/tests/can_test.py index a9275e24..6a0c5004 100644 --- a/tools/odrive/tests/can_test.py +++ b/tools/odrive/tests/can_test.py @@ -109,9 +109,9 @@ class TestSimpleCAN(): def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, node_id: int, extended_id: bool, logger: Logger): odrive.disable_mappings() - odrive.handle.config.gpio15_mode = GPIO_MODE_CAN0 - odrive.handle.config.gpio16_mode = GPIO_MODE_CAN0 - odrive.handle.config.enable_can0 = True + odrive.handle.config.gpio15_mode = GPIO_MODE_CAN_A + odrive.handle.config.gpio16_mode = GPIO_MODE_CAN_A + odrive.handle.config.enable_can_a = True odrive.save_config_and_reboot() axis = odrive.handle.axis0 diff --git a/tools/odrive/tests/integration_test.py b/tools/odrive/tests/integration_test.py index da9464e9..b59b77d7 100644 --- a/tools/odrive/tests/integration_test.py +++ b/tools/odrive/tests/integration_test.py @@ -145,9 +145,9 @@ class TestSimpleCANClosedLoop(): # make sure no gpio input is overwriting our values odrive.disable_mappings() - odrive.handle.config.gpio15_mode = GPIO_MODE_CAN0 - odrive.handle.config.gpio16_mode = GPIO_MODE_CAN0 - odrive.handle.config.enable_can0 = True + odrive.handle.config.gpio15_mode = GPIO_MODE_CAN_A + odrive.handle.config.gpio16_mode = GPIO_MODE_CAN_A + odrive.handle.config.enable_can_a = True odrive.save_config_and_reboot() with self.prepare(odrive, canbus, axis_ctx, motor_ctx, enc_ctx, node_id, extended_id, logger): diff --git a/tools/odrive/tests/pwm_input_test.py b/tools/odrive/tests/pwm_input_test.py index 82586638..a79d8aac 100644 --- a/tools/odrive/tests/pwm_input_test.py +++ b/tools/odrive/tests/pwm_input_test.py @@ -70,7 +70,7 @@ class TestPwmInput(): odrive.handle.config.gpio4_pwm_mapping ][odrive_gpio_num - 1] - setattr(odrive.handle.config, 'gpio' + str(odrive_gpio_num) + '_mode', GPIO_MODE_PWM0) + setattr(odrive.handle.config, 'gpio' + str(odrive_gpio_num) + '_mode', GPIO_MODE_PWM) pwm_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos'] pwm_mapping.min = min_val pwm_mapping.max = max_val diff --git a/tools/odrive/tests/uart_ascii_test.py b/tools/odrive/tests/uart_ascii_test.py index 7f568f3c..52a1c64e 100644 --- a/tools/odrive/tests/uart_ascii_test.py +++ b/tools/odrive/tests/uart_ascii_test.py @@ -43,7 +43,7 @@ class TestUartAscii(): }, SerialPortComponent)) yield (odrive, 0, ports) - # Enable the line below to manually test UART1. For this you need + # Enable the line below to manually test UART_B. For this you need # to manually move to the wires go to GPIO1/2 to GPIO3/4. The ones # that normally go to GPIO3/4 have a low pass filter. #yield (odrive, 1, ports) @@ -54,25 +54,25 @@ class TestUartAscii(): # GPIOs might be in use by something other than UART and some components # might be configured so that they would fail in the later test. odrive.disable_mappings() - odrive.handle.config.enable_uart0 = False - odrive.handle.config.uart0_baudrate = 115200 - odrive.handle.config.enable_uart1 = False - odrive.handle.config.uart1_baudrate = 115200 - odrive.handle.config.enable_uart2 = False - odrive.handle.config.uart2_baudrate = 115200 + odrive.handle.config.enable_uart_a = False + odrive.handle.config.uart_a_baudrate = 115200 + odrive.handle.config.enable_uart_b = False + odrive.handle.config.uart_b_baudrate = 115200 + odrive.handle.config.enable_uart_c = False + odrive.handle.config.uart_c_baudrate = 115200 if uart_num == 0: - odrive.handle.config.enable_uart0 = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 - odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.handle.config.enable_uart_a = True + odrive.handle.config.gpio1_mode = GPIO_MODE_UART_A + odrive.handle.config.gpio2_mode = GPIO_MODE_UART_A odrive.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN odrive.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN else: - odrive.handle.config.enable_uart1 = True + odrive.handle.config.enable_uart_b = True odrive.handle.config.gpio1_mode = GPIO_MODE_ANALOG_IN odrive.handle.config.gpio2_mode = GPIO_MODE_ANALOG_IN - odrive.handle.config.gpio3_mode = GPIO_MODE_UART1 - odrive.handle.config.gpio4_mode = GPIO_MODE_UART1 + odrive.handle.config.gpio3_mode = GPIO_MODE_UART_B + odrive.handle.config.gpio4_mode = GPIO_MODE_UART_B odrive.save_config_and_reboot() @@ -187,11 +187,11 @@ class TestUartBaudrate(): yield (odrive, ports) def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - odrive.handle.config.enable_uart0 = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 - odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.handle.config.enable_uart_a = True + odrive.handle.config.gpio1_mode = GPIO_MODE_UART_A + odrive.handle.config.gpio2_mode = GPIO_MODE_UART_A - odrive.handle.config.uart0_baudrate = 9600 + odrive.handle.config.uart_a_baudrate = 9600 odrive.save_config_and_reboot() # Control test: talk to the ODrive with the wrong baudrate @@ -211,7 +211,7 @@ class TestUartBaudrate(): response = float(ser.readline().strip()) test_assert_eq(response, odrive.handle.vbus_voltage, accuracy=0.1) - odrive.handle.config.uart0_baudrate = 115200 + odrive.handle.config.uart_a_baudrate = 115200 odrive.save_config_and_reboot() @@ -229,9 +229,9 @@ class TestUartBurnIn(): yield (odrive, ports) def run_test(self, odrive: ODriveComponent, port: SerialPortComponent, logger: Logger): - odrive.handle.config.enable_uart0 = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 - odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.handle.config.enable_uart_a = True + odrive.handle.config.gpio1_mode = GPIO_MODE_UART_A + odrive.handle.config.gpio2_mode = GPIO_MODE_UART_A with port.open(115200) as ser: with open('/dev/random', 'rb') as rand: @@ -286,9 +286,9 @@ class TestUartNoise(): noise_enable.write(False) time.sleep(0.1) - odrive.handle.config.enable_uart0 = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART0 - odrive.handle.config.gpio2_mode = GPIO_MODE_UART0 + odrive.handle.config.enable_uart_a = True + odrive.handle.config.gpio1_mode = GPIO_MODE_UART_A + odrive.handle.config.gpio2_mode = GPIO_MODE_UART_A with port.open(115200) as ser: # reset port to known state From 6c3225f811d9ef7bd02fd82ca1bfbd0bab5d0cd2 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 16 Oct 2020 12:03:46 +0200 Subject: [PATCH 053/124] add note how to use UART_B --- docs/uart.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/uart.md b/docs/uart.md index 230932af..a1e16849 100644 --- a/docs/uart.md +++ b/docs/uart.md @@ -11,3 +11,15 @@ To use UART connect it like this: The logic level of the ODrive is 3.3V. The GPIOs are 5V tolerant. You can use `odrv0.config.uart_a_baudrate` to change the baudrate and `odrv0.config.enable_uart_a` to disable/reenable UART_A. Currently the UART port runs both the [Native Protocol](native-protocol) and the [ASCII Protocol](ascii-protocol) at the same time. + +### How to use UART on GPIO3/4 + +If you need GPIO1/2 for some function other than UART you can disable UART_A and instead use UART_B on GPIO3/4. Here's how you do it: + + odrv0.config.enable_uart_a = False + odrv0.config.gpio1_mode = GPIO_MODE_DIGITAL + odrv0.config.gpio2_mode = GPIO_MODE_DIGITAL + odrv0.config.enable_uart_b = True + odrv0.config.gpio3_mode = GPIO_MODE_UART_B + odrv0.config.gpio4_mode = GPIO_MODE_UART_B + odrv0.reboot() From 284fa6797a8bd4c5f1337a337f6d7a4d9da7309e Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Fri, 30 Oct 2020 19:00:33 -0400 Subject: [PATCH 054/124] [GUI] cleanup, added automatic calib_scan_range setting for encoder config to make it more reliable --- GUI/src/assets/wizard/configTemplate.json | 6 ++-- GUI/src/views/Wizard.vue | 42 +++++++++++++++-------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/GUI/src/assets/wizard/configTemplate.json b/GUI/src/assets/wizard/configTemplate.json index e6406e96..6fbae6a0 100644 --- a/GUI/src/assets/wizard/configTemplate.json +++ b/GUI/src/assets/wizard/configTemplate.json @@ -17,7 +17,8 @@ "config": { "mode": null, "use_index": null, - "cpr": null + "cpr": null, + "calib_scan_distance": null } }, "controller": { @@ -51,7 +52,8 @@ "config": { "mode": null, "use_index": null, - "cpr": null + "cpr": null, + "calib_scan_distance": null } }, "controller": { diff --git a/GUI/src/views/Wizard.vue b/GUI/src/views/Wizard.vue index 11c96c14..ee5b2905 100644 --- a/GUI/src/views/Wizard.vue +++ b/GUI/src/views/Wizard.vue @@ -63,6 +63,7 @@ import { motorCalibration, encoderCalibration } from "../lib/odrive_utils.js"; +import {wait} from "../lib/utils.js" export default { name: "Wizard", @@ -125,8 +126,10 @@ export default { this.calibrating = true; let result = await motorCalibration(this.odrive, e.axis); this.calibrating = false; + fetchParam(this.odrive + e.axis + '.motor.is_calibrated'); // no error, we're good! if (result == 0) { + wait(250); apply(); this.calStatus = true; } else { @@ -146,15 +149,25 @@ export default { let oldCPR = getVal(this.odrive + e.axis + ".encoder.config.cpr"); console.log("oldCPR = " + oldCPR); console.log("axis = " + e.axis); + + // set up the calib_scan_distance to a value that will work + let pp = this.wizardConfig[e.axis].motor.config.pole_pairs; + + // smallest multiple of 4pi that is bigger than pole_pairs * 2pi + let scan_distance = pp % 0 ? pp * 2 * Math.PI : (pp + 1) * 2 * Math.PI; + putVal(this.odrive + e.axis + ".encoder.config.calib_scan_distance", scan_distance); putVal( this.odrive + e.axis + ".encoder.config.cpr", this.wizardConfig[e.axis].encoder.config.cpr ); + await wait(250); + this.calibrating = true; let result = await encoderCalibration(this.odrive, e.axis); this.calibrating = false; putVal(this.odrive + e.axis + ".encoder.config.cpr", oldCPR); + fetchParam(this.odrive + e.axis + '.encoder.is_ready'); if (result == 0){ // no encoder error, we're good this.choiceMade = true; @@ -167,7 +180,7 @@ export default { } } }, - choiceHandler(e) { + async choiceHandler(e) { // apply static configStub this.updateConfig(this.wizardConfig, e.configStub); @@ -179,7 +192,6 @@ export default { // ugly, but a special case. // for motors, wait for calibration to finish before giving the green light unless motor.is_calibrated == true // for encoders, wait for calibration to finish unless encoder.is_ready == true - this.choiceMade = true; if ( this.currentStep == pages.Motor_0 || this.currentStep == pages.Motor_1 @@ -187,20 +199,33 @@ export default { let axis; if (this.currentStep == pages.Motor_0) axis = "axis0"; if (this.currentStep == pages.Motor_1) axis = "axis1"; + fetchParam(this.odrive + axis + ".motor.is_calibrated"); + await wait(100); if (getVal(this.odrive + axis + ".motor.is_calibrated") == false) { this.choiceMade = false; } + else { + this.choiceMade = true; + } } - if ( + else if ( this.currentStep == pages.Encoder_0 || this.currentStep == pages.Encoder_1 ) { let axis; if (this.currentStep == pages.Encoder_0) axis = "axis0"; if (this.currentStep == pages.Encoder_1) axis = "axis1"; + fetchParam(this.odrive + axis + ".encoder.is_ready"); + await wait(100); if (getVal(this.odrive + axis + ".encoder.is_ready") == false) { this.choiceMade = false; } + else { + this.choiceMade = true; + } + } + else { + this.choiceMade = true; } this.currentStep.choiceMade = this.choiceMade; console.log(JSON.parse(JSON.stringify(this.wizardConfig))); @@ -255,17 +280,6 @@ export default { this.choiceMade = false; }, }, - created() { - // when wizard is active, we want to poll for certain values - let update = () => { - fetchParam("odrive0.axis0.motor.is_calibrated"); - fetchParam("odrive0.axis1.motor.is_calibrated"); - fetchParam("odrive0.axis0.encoder.is_ready"); - fetchParam("odrive0.axis1.encoder.is_ready"); - setTimeout(() => update(), 1000); - }; - update(); - }, beforeDestroy() { for (const page of Object.keys(pages)) { pages[page].choiceMade = false; From 746212191c53505ef7f9793828994d62ed4e7ce7 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Fri, 30 Oct 2020 21:41:28 -0400 Subject: [PATCH 055/124] Fix thermistor sampling --- Firmware/MotorControl/axis.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 79af1d3d..5f354acc 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -186,11 +186,7 @@ bool Axis::do_checks() { // @brief Update all esitmators bool Axis::do_updates() { // Sub-components should use set_error which will propegate to this error_ - task_times_.thermistor_update.beginTimer(); - for (ThermistorCurrentLimiter* thermistor : thermistors_) { - thermistor->update(); - } - task_times_.thermistor_update.stopTimer(); + task_times_.encoder_update.beginTimer(); encoder_.update(); @@ -200,9 +196,12 @@ bool Axis::do_updates() { sensorless_estimator_.update(); task_times_.sensorless_update.stopTimer(); - task_times_.min_endstop_update.beginTimer(); + task_times_.thermistor_update.beginTimer(); motor_.fet_thermistor_.update(); motor_.motor_thermistor_.update(); + task_times_.thermistor_update.stopTimer(); + + task_times_.min_endstop_update.beginTimer(); min_endstop_.update(); task_times_.min_endstop_update.stopTimer(); From 1d0fcc5fcffae2873ff8fbb499a5187c10dc11be Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 1 Nov 2020 14:36:02 -0500 Subject: [PATCH 056/124] Throw a DRV FAULT if initialization fails --- Firmware/Drivers/DRV8301/drv8301.cpp | 8 +++++++- Firmware/MotorControl/motor.cpp | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Firmware/Drivers/DRV8301/drv8301.cpp b/Firmware/Drivers/DRV8301/drv8301.cpp index 3e1cbf91..a7ed5161 100644 --- a/Firmware/Drivers/DRV8301/drv8301.cpp +++ b/Firmware/Drivers/DRV8301/drv8301.cpp @@ -67,8 +67,14 @@ bool Drv8301::init() { // Make sure the Fault bit is not set during startup uint16_t reg; + uint32_t count = 0; while (!read_spi(RegName_Status_1, ®) || (reg & DRV8301_STATUS1_FAULT_BITS)) - ; // TODO: don't spin + { + osDelay(1); + if(count++ > 10){ + return false; + } + } // Wait for the DRV8301 registers to update osDelay(1); diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 08788f60..68b44fa8 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -78,6 +78,7 @@ bool Motor::apply_config() { // @brief Set up the gate drivers bool Motor::setup() { if (!gate_driver_.init()) { + set_error(ERROR_DRV_FAULT); return false; } From 3fb2cf73af58ee001f6117b5b2d591496c378b58 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 1 Nov 2020 21:13:24 -0800 Subject: [PATCH 057/124] update sampler can use python 3 comment --- Firmware/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 4fc25a97..a2da9ecf 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -3,7 +3,7 @@ # run openocd (0.9.0) with : # $ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg &> /dev/null & # then run -# $ python2 sampler.py path_to_myelf_with_symbols +# $ python sampler.py path_to_myelf_with_symbols # ctrl-c to stop sampling. # To terminate the openocd session, enter command "fg" then do ctrl-c. From fb40ad9d62137d58e6cb4cd2fd635c8eeffab413 Mon Sep 17 00:00:00 2001 From: mdhom Date: Mon, 2 Nov 2020 14:28:26 +0100 Subject: [PATCH 058/124] added input_mode enum values --- GUI/src/views/Dashboard.vue | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/GUI/src/views/Dashboard.vue b/GUI/src/views/Dashboard.vue index 07dbae0e..81d88287 100644 --- a/GUI/src/views/Dashboard.vue +++ b/GUI/src/views/Dashboard.vue @@ -254,6 +254,40 @@ let odriveEnums = { text: "Position Control", value: 3, }, + ], + input_mode: [ + { + text: "Inactive", + value: 0 + }, + { + text: "Passthrough", + value: 1 + }, + { + text: "Velocity Ramp", + value: 2 + }, + { + text: "Position Filter", + value: 3 + }, + { + text: "Mix Channels", + value: 4 + }, + { + text: "Trapezoidal Trajectory", + value: 5 + }, + { + text: "Torque Ramp", + value: 6 + }, + { + text: "Mirror", + value: 7 + } ] } From 202d3d21e56eb275abb522fad563114e913fe7ce Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Mon, 2 Nov 2020 23:09:36 -0500 Subject: [PATCH 059/124] Rename encoder.config.offset to phase_offset --- Firmware/MotorControl/encoder.cpp | 14 +++++++------- Firmware/MotorControl/encoder.hpp | 4 ++-- Firmware/odrive-interface.yaml | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 06ee616b..856a4481 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -152,8 +152,8 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) { uint32_t prim = cpu_enter_critical(); if (update_offset) { - config_.offset += count - count_in_cpr_; - config_.offset = mod(config_.offset, config_.cpr); + config_.phase_offset += count - count_in_cpr_; + config_.phase_offset = mod(config_.phase_offset, config_.cpr); } // Update states @@ -302,9 +302,9 @@ bool Encoder::run_offset_calibration() { if (axis_->error_ != Axis::ERROR_NONE) return false; - config_.offset = encvaluesum / (num_steps * 2); - int32_t residual = encvaluesum - ((int64_t)config_.offset * (int64_t)(num_steps * 2)); - config_.offset_float = (float)residual / (float)(num_steps * 2) + 0.5f; // add 0.5 to center-align state to phase + config_.phase_offset = encvaluesum / (num_steps * 2); + int32_t residual = encvaluesum - ((int64_t)config_.phase_offset * (int64_t)(num_steps * 2)); + config_.phase_offset_float = (float)residual / (float)(num_steps * 2) + 0.5f; // add 0.5 to center-align state to phase is_ready_ = true; return true; @@ -568,7 +568,7 @@ bool Encoder::update() { pos_circular_ = fmodf_pos(pos_circular_, axis_->controller_.config_.circular_setpoint_range); //// run encoder count interpolation - int32_t corrected_enc = count_in_cpr_ - config_.offset; + int32_t corrected_enc = count_in_cpr_ - config_.phase_offset; // if we are stopped, make sure we don't randomly drift if (snap_to_zero_vel || !config_.enable_phase_interpolation) { interpolation_ = 0.5f; @@ -590,7 +590,7 @@ bool Encoder::update() { //// compute electrical phase //TODO avoid recomputing elec_rad_per_enc every time float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); - float ph = elec_rad_per_enc * (interpolated_enc - config_.offset_float); + float ph = elec_rad_per_enc * (interpolated_enc - config_.phase_offset_float); // ph = fmodf(ph, 2*M_PI); phase_ = wrap_pm_pi(ph); diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 645f3811..294609b5 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -21,8 +21,8 @@ public: // state as soon as the index is found. bool zero_count_on_find_idx = true; int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, - int32_t offset = 0; // Offset between encoder count and rotor electrical phase - float offset_float = 0.0f; // Sub-count phase alignment offset + int32_t phase_offset = 0; // Offset between encoder count and rotor electrical phase + float phase_offset_float = 0.0f; // Sub-count phase alignment offset bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state float calib_range = 0.02f; // Accuracy required to pass encoder cpr check float calib_scan_distance = 16.0f * M_PI; // rad electrical diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 49ca512f..dec37590 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -889,9 +889,9 @@ interfaces: abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin, doc: Make sure that the GPIO is in `GPIO_MODE_DIGITAL`.} zero_count_on_find_idx: bool cpr: int32 - offset: int32 + phase_offset: int32 + phase_offset_float: float32 pre_calibrated: {type: bool, c_setter: set_pre_calibrated} - offset_float: float32 enable_phase_interpolation: bool bandwidth: {type: float32, c_setter: set_bandwidth} calib_range: float32 From 9594c477cb254530b00c5466cb4c91d2eb128f7b Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Mon, 2 Nov 2020 23:36:26 -0500 Subject: [PATCH 060/124] Read thermistors at least once at startup --- Firmware/MotorControl/axis.cpp | 3 --- Firmware/MotorControl/motor.cpp | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 5f354acc..187b17f9 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -169,9 +169,6 @@ bool Axis::do_checks() { // Sub-components should use set_error which will propegate to this error_ motor_.effective_current_lim(); motor_.do_checks(); - // encoder_.do_checks(); - // sensorless_estimator_.do_checks(); - // controller_.do_checks(); // Check for endstop presses if (min_endstop_.config_.enabled && min_endstop_.rose() && !(current_state_ == AXIS_STATE_HOMING)) { diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 68b44fa8..b2944d36 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -82,6 +82,9 @@ bool Motor::setup() { return false; } + fet_thermistor_.update(); + motor_thermistor_.update(); + // Solve for exact gain, then snap down to have equal or larger range as requested // or largest possible range otherwise constexpr float kMargin = 0.90f; From 80f648fa5c2dd3d3f64f9d6df078bd62ffc7bb35 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 3 Nov 2020 18:37:26 +0100 Subject: [PATCH 061/124] don't use std::isnan --- Firmware/MotorControl/motor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 81cf31b4..557cde9c 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -423,7 +423,7 @@ bool Motor::measure_phase_resistance(float test_current, float max_voltage) { disarm(); config_.phase_resistance = control_law.get_resistance(); - if (std::isnan(config_.phase_resistance)) { + if (is_nan(config_.phase_resistance)) { // TODO: the motor is already disarmed at this stage. This is an error // that only pretains to the measurement and its result so it should // just be a return value of this function. From a1ac01e4302c297bce21ab7bc328648a9a0f0d4b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 3 Nov 2020 19:42:32 +0100 Subject: [PATCH 062/124] add dump_threads diagnostics function --- Firmware/MotorControl/main.cpp | 28 +++++++++++++++++----------- Firmware/MotorControl/odrive_main.h | 26 ++++++++++++++++---------- Firmware/odrive-interface.yaml | 25 +++++++++++++++---------- docs/resources.md | 17 ++++++++++++++++- tools/odrive/shell.py | 1 + tools/odrive/utils.py | 19 +++++++++++++++++++ 6 files changed, 84 insertions(+), 32 deletions(-) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index cd76941d..510f68ab 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -177,20 +177,26 @@ void vApplicationIdleHook(void) { if (odrv.system_stats_.fully_booted) { odrv.system_stats_.uptime = xTaskGetTickCount(); odrv.system_stats_.min_heap_space = xPortGetMinimumEverFreeHeapSize(); + uint32_t min_stack_space[AXIS_COUNT]; std::transform(axes.begin(), axes.end(), std::begin(min_stack_space), [](auto& axis) { return uxTaskGetStackHighWaterMark(axis.thread_id_) * sizeof(StackType_t); }); - odrv.system_stats_.min_stack_space_axis = *std::min_element(std::begin(min_stack_space), std::end(min_stack_space)); - odrv.system_stats_.min_stack_space_usb = uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_uart = uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_startup = uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t); - odrv.system_stats_.min_stack_space_can = uxTaskGetStackHighWaterMark(odCAN->thread_id_) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_axis = axes[0].stack_size_ - *std::min_element(std::begin(min_stack_space), std::end(min_stack_space)); + odrv.system_stats_.max_stack_usage_usb = stack_size_usb_thread - uxTaskGetStackHighWaterMark(usb_thread) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_uart = stack_size_uart_thread - uxTaskGetStackHighWaterMark(uart_thread) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_startup = stack_size_default_task - uxTaskGetStackHighWaterMark(defaultTaskHandle) * sizeof(StackType_t); + odrv.system_stats_.max_stack_usage_can = odCAN->stack_size_ - uxTaskGetStackHighWaterMark(odCAN->thread_id_) * sizeof(StackType_t); - // Actual usage, in bytes, so we don't have to math - odrv.system_stats_.stack_usage_axis = axes[0].stack_size_ - odrv.system_stats_.min_stack_space_axis; - odrv.system_stats_.stack_usage_usb = stack_size_usb_thread - odrv.system_stats_.min_stack_space_usb; - odrv.system_stats_.stack_usage_uart = stack_size_uart_thread - odrv.system_stats_.min_stack_space_uart; - odrv.system_stats_.stack_usage_startup = stack_size_default_task - odrv.system_stats_.min_stack_space_startup; - odrv.system_stats_.stack_usage_can = odCAN->stack_size_ - odrv.system_stats_.min_stack_space_can; + odrv.system_stats_.stack_size_axis = axes[0].stack_size_; + odrv.system_stats_.stack_size_usb = stack_size_usb_thread; + odrv.system_stats_.stack_size_uart = stack_size_uart_thread; + odrv.system_stats_.stack_size_startup = stack_size_default_task; + odrv.system_stats_.stack_size_can = odCAN->stack_size_; + + odrv.system_stats_.prio_axis = osThreadGetPriority(axes[0].thread_id_); + odrv.system_stats_.prio_usb = osThreadGetPriority(usb_thread); + odrv.system_stats_.prio_uart = osThreadGetPriority(uart_thread); + odrv.system_stats_.prio_startup = osThreadGetPriority(defaultTaskHandle); + odrv.system_stats_.prio_can = osThreadGetPriority(odCAN->thread_id_); } } diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 694f682d..57535b00 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -29,17 +29,23 @@ typedef struct { bool fully_booted; uint32_t uptime; // [ms] uint32_t min_heap_space; // FreeRTOS heap [Bytes] - uint32_t min_stack_space_axis; // minimum remaining space since startup [Bytes] - uint32_t min_stack_space_usb; - uint32_t min_stack_space_uart; - uint32_t min_stack_space_startup; - uint32_t min_stack_space_can; + uint32_t max_stack_usage_axis; // minimum remaining space since startup [Bytes] + uint32_t max_stack_usage_usb; + uint32_t max_stack_usage_uart; + uint32_t max_stack_usage_startup; + uint32_t max_stack_usage_can; - uint32_t stack_usage_axis; - uint32_t stack_usage_usb; - uint32_t stack_usage_uart; - uint32_t stack_usage_startup; - uint32_t stack_usage_can; + uint32_t stack_size_axis; + uint32_t stack_size_usb; + uint32_t stack_size_uart; + uint32_t stack_size_startup; + uint32_t stack_size_can; + + int32_t prio_axis; + int32_t prio_usb; + int32_t prio_uart; + int32_t prio_startup; + int32_t prio_can; USBStats_t& usb = usb_stats_; I2CStats_t& i2c = i2c_stats_; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 8dfde3e8..fd0ddf56 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -126,16 +126,21 @@ interfaces: attributes: uptime: readonly uint32 min_heap_space: readonly uint32 - min_stack_space_axis: readonly uint32 - min_stack_space_usb: readonly uint32 - min_stack_space_uart: readonly uint32 - min_stack_space_can: readonly uint32 - min_stack_space_startup: readonly uint32 - stack_usage_axis: readonly uint32 - stack_usage_usb: readonly uint32 - stack_usage_uart: readonly uint32 - stack_usage_startup: readonly uint32 - stack_usage_can: readonly uint32 + max_stack_usage_axis: readonly uint32 + max_stack_usage_usb: readonly uint32 + max_stack_usage_uart: readonly uint32 + max_stack_usage_can: readonly uint32 + max_stack_usage_startup: readonly uint32 + stack_size_axis: readonly uint32 + stack_size_usb: readonly uint32 + stack_size_uart: readonly uint32 + stack_size_startup: readonly uint32 + stack_size_can: readonly uint32 + prio_axis: readonly int32 + prio_usb: readonly int32 + prio_uart: readonly int32 + prio_startup: readonly int32 + prio_can: readonly int32 usb: c_is_class: False attributes: diff --git a/docs/resources.md b/docs/resources.md index dac2e0c7..bb35ff4b 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -1,5 +1,5 @@ -Most information in this file can be reproduced by running `dump_interrupts(odrv0)` and `dump_dma(odrv0)` in `odrivetool`. +Most information in this file can be reproduced by running `dump_interrupts(odrv0)`, `dump_dma(odrv0)` and `dump_threads(odrv0)` in `odrivetool` (minor manual postprocessing was applied to the output of those functions). Take this info with a grain of salt as we might forget to update it from time to time. When in doubt check the file history. @@ -55,3 +55,18 @@ Take this info with a grain of salt as we might forget to update it from time to | DMA1_Stream5 | 2 | 0 (SPI3_TX) | SPI | | DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | + +## Threads + + - lowest priority: -3 + - highest priority: 3 + +| Name | Stack Size [B] | Prio | +|---------|----------------|------| +| axis0 | 2048 | 3 | +| axis1 | 2048 | 2 | +| can | 1024 | 0 | +| startup | 2048 | 0 | +| uart | 4096 | 0 | +| usb | 4096 | 0 | + diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index e6fcb072..8f3274ea 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -86,6 +86,7 @@ def launch_shell(args, logger, app_shutdown_token): 'dump_errors': dump_errors, 'oscilloscope_dump': oscilloscope_dump, 'dump_interrupts': dump_interrupts, + 'dump_threads': dump_threads, 'dump_dma': dump_dma, 'dump_timing': dump_timing, 'BulkCapture': BulkCapture, diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 1f211e75..b9fc8e9b 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -487,6 +487,25 @@ def dump_interrupts(odrv): " *" if (status & 0x80000000) else " ", str((status >> 8) & 0x7fffff).rjust(7))) +def dump_threads(odrv): + prefixes = ["max_stack_usage_", "stack_size_", "prio_"] + keys = [k[len(prefix):] for k in dir(odrv.system_stats) for prefix in prefixes if k.startswith(prefix)] + good_keys = set([k for k in set(keys) if keys.count(k) == len(prefixes)]) + if len(good_keys) > len(set(keys)): + print("Warning: incomplete thread information for threads {}".format(set(keys) - good_keys)) + + print("| Name | Stack Size [B] | Max Ever Stack Usage [B] | Prio |") + print("|---------|----------------|--------------------------|------|") + for k in sorted(good_keys): + sz = getattr(odrv.system_stats, "stack_size_" + k) + use = getattr(odrv.system_stats, "max_stack_usage_" + k) + print("| {} | {} | {} | {} |".format( + k.ljust(7), + str(sz).rjust(14), + "{} ({:.1f}%)".format(use, use / sz * 100).rjust(24), + str(getattr(odrv.system_stats, "prio_" + k)).rjust(4) + )) + def dump_dma(odrv): if odrv.hw_version_major == 3: From d5838e85d3b78162c9db735ac03b796e7d2e545c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 5 Nov 2020 21:42:32 -0800 Subject: [PATCH 063/124] minor wordings and comments --- Firmware/MotorControl/encoder.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 978c9516..7c40a185 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -167,8 +167,8 @@ bool Encoder::run_index_search() { index_found_ = false; set_idx_subscribe(); - bool status = axis_->run_lockin_spin(axis_->config_.calibration_lockin, false); - return status; + bool success = axis_->run_lockin_spin(axis_->config_.calibration_lockin, false); + return success; } bool Encoder::run_direction_find() { @@ -179,9 +179,9 @@ bool Encoder::run_direction_find() { lockin_config.finish_on_distance = true; lockin_config.finish_on_enc_idx = false; lockin_config.finish_on_vel = false; - bool status = axis_->run_lockin_spin(lockin_config, false); + bool success = axis_->run_lockin_spin(lockin_config, false); - if (status) { + if (success) { // Check response and direction if (shadow_count_ > init_enc_val + 8) { // motor same dir as encoder @@ -194,7 +194,7 @@ bool Encoder::run_direction_find() { } } - return status; + return success; } // @brief Turns the motor in one direction for a bit and then in the other @@ -292,7 +292,6 @@ bool Encoder::run_offset_calibration() { return false; } - //TODO avoid recomputing elec_rad_per_enc every time // Check CPR float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); float expected_encoder_delta = config_.calib_scan_distance / elec_rad_per_enc; @@ -374,6 +373,7 @@ void Encoder::sample_now() { } break; } + // Sample all GPIO digital input data registers, used for HALL sensors for example. for (size_t i = 0; i < sizeof(ports_to_sample) / sizeof(ports_to_sample[0]); ++i) { port_samples_[i] = ports_to_sample[i]->IDR; } From 2040447174fbf1d08e5311d1601096db4de4adf1 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 5 Nov 2020 21:48:36 -0800 Subject: [PATCH 064/124] change all did_ grammar to past tense instead --- Firmware/Drivers/DRV8301/drv8301.cpp | 8 ++++---- Firmware/MotorControl/axis.cpp | 6 +++--- Firmware/fibre/python/fibre/discovery.py | 14 +++++++------- Firmware/fibre/python/fibre/serial_transport.py | 4 ++-- Firmware/fibre/python/fibre/shell.py | 8 ++++---- tools/odrive/shell.py | 6 +++--- tools/odrive/tests/test_runner.py | 8 ++++---- tools/odrive/utils.py | 4 ++-- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Firmware/Drivers/DRV8301/drv8301.cpp b/Firmware/Drivers/DRV8301/drv8301.cpp index 05001a6b..61175d73 100644 --- a/Firmware/Drivers/DRV8301/drv8301.cpp +++ b/Firmware/Drivers/DRV8301/drv8301.cpp @@ -81,13 +81,13 @@ bool Drv8301::init() { osDelay(20); // t_spi_ready, max = 10ms // Write current configuration - bool did_write_regs = write_reg(kRegNameControl1, regs_.control_register_1) + bool wrote_regs = write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) && write_reg(kRegNameControl1, regs_.control_register_1) // the write operation tends to be ignored if only done once (not sure why) && write_reg(kRegNameControl2, regs_.control_register_2); - if (!did_write_regs) { + if (!wrote_regs) { return false; } @@ -95,9 +95,9 @@ bool Drv8301::init() { delay_us(100); state_ = kStateStartupChecks; - bool did_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1) + bool is_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1) && read_reg(kRegNameControl2, &val) && (val == regs_.control_register_2); - if (!did_read_regs) { + if (!is_read_regs) { return false; } diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index b84daa32..8971e415 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -212,7 +212,7 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme motor_.arm(&motor_.current_control_); - bool did_subscribe_to_idx = false; + bool subscribed_to_idx = false; bool success = false; float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f; @@ -231,9 +231,9 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme // Activate index pin as soon as target velocity was reached. This is // to avoid hitting the index from the wrong direction. - if (reached_target_vel && !encoder_.index_found_ && !did_subscribe_to_idx) { + if (reached_target_vel && !encoder_.index_found_ && !subscribed_to_idx) { encoder_.set_idx_subscribe(true); - did_subscribe_to_idx = true; + subscribed_to_idx = true; } osDelay(1); diff --git a/Firmware/fibre/python/fibre/discovery.py b/Firmware/fibre/python/fibre/discovery.py index d8781c7c..6b789c99 100644 --- a/Firmware/fibre/python/fibre/discovery.py +++ b/Firmware/fibre/python/fibre/discovery.py @@ -48,7 +48,7 @@ def noprint(text): pass def find_all(path, serial_number, - did_discover_object_callback, + discovered_object_callback, search_cancellation_token, channel_termination_token, logger): @@ -58,9 +58,9 @@ def find_all(path, serial_number, This function is non-blocking. """ - def did_discover_channel(channel): + def discovered_channel(channel): """ - Inits an object from a given channel and then calls did_discover_object_callback + Inits an object from a given channel and then calls discovered_object_callback with the created object This queries the endpoint 0 on that channel to gain information about the interface, which is then used to init the corresponding object. @@ -132,7 +132,7 @@ def find_all(path, serial_number, logger.debug("Ignoring device with serial number {}".format(device_serial_number)) return - did_discover_object_callback(obj) + discovered_object_callback(obj) except Exception: @@ -144,7 +144,7 @@ def find_all(path, serial_number, the_rest = ':'.join(search_spec.split(':')[1:]) if prefix in channel_types: t = threading.Thread(target=channel_types[prefix], - args=(the_rest, serial_number, did_discover_channel, search_cancellation_token, channel_termination_token, logger)) + args=(the_rest, serial_number, discovered_channel, search_cancellation_token, channel_termination_token, logger)) t.daemon = True t.start() else: @@ -159,7 +159,7 @@ def find_any(path="usb", serial_number=None, """ result = [] done_signal = Event(search_cancellation_token) - def did_discover_object(obj): + def discovered_object(obj): result.append(obj) if find_multiple: if len(result) >= int(find_multiple): @@ -167,7 +167,7 @@ def find_any(path="usb", serial_number=None, else: done_signal.set() - find_all(path, serial_number, did_discover_object, done_signal, channel_termination_token, logger) + find_all(path, serial_number, discovered_object, done_signal, channel_termination_token, logger) try: done_signal.wait(timeout=timeout) except TimeoutError: diff --git a/Firmware/fibre/python/fibre/serial_transport.py b/Firmware/fibre/python/fibre/serial_transport.py index e737dfca..f3fe5180 100644 --- a/Firmware/fibre/python/fibre/serial_transport.py +++ b/Firmware/fibre/python/fibre/serial_transport.py @@ -81,7 +81,7 @@ def discover_channels(path, serial_number, callback, cancellation_token, channel return False return bool(re.match(regex, port_name)) - def did_disconnect(port_name, device): + def disconnected(port_name, device): device.close() # TODO: yes there is a race condition here in case you wonder. known_devices.pop(known_devices.index(port_name)) @@ -103,6 +103,6 @@ def discover_channels(path, serial_number, callback, cancellation_token, channel known_devices.append(port_name) else: known_devices.append(port_name) - channel._channel_broken.subscribe(lambda: did_disconnect(port_name, serial_device)) + channel._channel_broken.subscribe(lambda: disconnected(port_name, serial_device)) callback(channel) time.sleep(1) diff --git a/Firmware/fibre/python/fibre/shell.py b/Firmware/fibre/python/fibre/shell.py index c5a7257a..96cad269 100644 --- a/Firmware/fibre/python/fibre/shell.py +++ b/Firmware/fibre/python/fibre/shell.py @@ -4,7 +4,7 @@ import platform import threading import fibre -def did_discover_device(device, +def discovered_device(device, interactive_variables, discovered_devices, branding_short, branding_long, logger, app_shutdown_token): @@ -29,9 +29,9 @@ def did_discover_device(device, logger.notify("{} to {} {} as {}".format(verb, branding_long, serial_number, interactive_name)) # Subscribe to disappearance of the device - device.__channel__._channel_broken.subscribe(lambda: did_lose_device(interactive_name, logger, app_shutdown_token)) + device.__channel__._channel_broken.subscribe(lambda: lost_device(interactive_name, logger, app_shutdown_token)) -def did_lose_device(interactive_name, logger, app_shutdown_token): +def lost_device(interactive_name, logger, app_shutdown_token): """ Handles the disappearance of a device by displaying a message. @@ -58,7 +58,7 @@ def launch_shell(args, # Connect to device logger.debug("Waiting for {}...".format(branding_long)) fibre.find_all(args.path, args.serial_number, - lambda dev: did_discover_device(dev, interactive_variables, discovered_devices, branding_short, branding_long, logger, app_shutdown_token), + lambda dev: discovered_device(dev, interactive_variables, discovered_devices, branding_short, branding_long, logger, app_shutdown_token), app_shutdown_token, app_shutdown_token, logger=logger) diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index 8f3274ea..b6cd0414 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -41,7 +41,7 @@ interactive_variables = {} discovered_devices = [] -def did_discover_device(odrive, logger, app_shutdown_token): +def discovered_device(odrive, logger, app_shutdown_token): """ Handles the discovery of new devices by displaying a message and making the device available to the interactive @@ -63,9 +63,9 @@ def did_discover_device(odrive, logger, app_shutdown_token): logger.notify("{} to ODrive {:012X} as {}".format(verb, serial_number, interactive_name)) # Subscribe to disappearance of the device - odrive.__channel__._channel_broken.subscribe(lambda: did_lose_device(interactive_name, logger, app_shutdown_token)) + odrive.__channel__._channel_broken.subscribe(lambda: lost_device(interactive_name, logger, app_shutdown_token)) -def did_lose_device(interactive_name, logger, app_shutdown_token): +def lost_device(interactive_name, logger, app_shutdown_token): """ Handles the disappearance of a device by displaying a message. diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index 770cb1c7..f305858d 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -70,14 +70,14 @@ def test_assert_within(observed, lower_bound, upper_bound, accuracy=0.0): def disjoint_sets(list_of_sets: list): while len(list_of_sets): current_set, list_of_sets = list_of_sets[0], list_of_sets[1:] - did_update = True - while did_update: - did_update = False + updated = True + while updated: + updated = False for i, s in enumerate(list_of_sets): if len(current_set.intersection(s)): current_set = current_set.union(s) list_of_sets = list_of_sets[:i] + list_of_sets[(i+1):] - did_update = True + updated = True yield current_set def is_list_like(arg): diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index b9fc8e9b..68b76cc2 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -155,10 +155,10 @@ def start_liveplotter(get_var_callback): plt.ion() # Make sure the script terminates when the user closes the plotter - def did_close(evt): + def closed(evt): cancellation_token.set() fig = plt.figure() - fig.canvas.mpl_connect('close_event', did_close) + fig.canvas.mpl_connect('close_event', closed) while not cancellation_token.is_set(): plt.clf() From 19b47411058692aeb8a20fd132a391114e08dc52 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 5 Nov 2020 21:49:05 -0800 Subject: [PATCH 065/124] increase liveplotter rate --- tools/odrive/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 68b76cc2..0a6ae81e 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -116,9 +116,9 @@ def oscilloscope_dump(odrv, num_vals, filename='oscilloscope.csv'): f.write(str(odrv.oscilloscope.get_val(x))) f.write('\n') -data_rate = 100 +data_rate = 200 plot_rate = 10 -num_samples = 1000 +num_samples = 500 def start_liveplotter(get_var_callback): """ Starts a liveplotter. From 7d062e90180aafb7207f02240a2ffd7855181056 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 6 Nov 2020 22:59:56 -0800 Subject: [PATCH 066/124] initial hall polarity detection algo --- Firmware/MotorControl/axis.cpp | 19 ++++++++--- Firmware/MotorControl/axis.hpp | 3 +- Firmware/MotorControl/encoder.cpp | 56 ++++++++++++++++++++++++++++++- Firmware/MotorControl/encoder.hpp | 3 ++ Firmware/odrive-interface.yaml | 4 +++ 5 files changed, 79 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 8971e415..2942c076 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -181,7 +181,8 @@ bool Axis::watchdog_check() { } } -bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed) { +bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, + std::function const_vel_cb) { CRITICAL_SECTION() { // Reset state variables open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f}; @@ -212,7 +213,7 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme motor_.arm(&motor_.current_control_); - bool subscribed_to_idx = false; + bool subscribed_to_idx_once = false; bool success = false; float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f; @@ -231,11 +232,14 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme // Activate index pin as soon as target velocity was reached. This is // to avoid hitting the index from the wrong direction. - if (reached_target_vel && !encoder_.index_found_ && !subscribed_to_idx) { + if (reached_target_vel && !encoder_.index_found_ && !subscribed_to_idx_once) { encoder_.set_idx_subscribe(true); - subscribed_to_idx = true; + subscribed_to_idx_once = true; } + if (reached_target_vel && const_vel_cb) + const_vel_cb(); + osDelay(1); } @@ -496,6 +500,13 @@ void Axis::run_state_machine_loop() { status = encoder_.run_direction_find(); } break; + case AXIS_STATE_ENCODER_HALL_CALIBRATION: { + if (!motor_.is_calibrated_) + goto invalid_state_label; + + status = encoder_.run_hall_calibration(); + } break; + case AXIS_STATE_HOMING: { status = run_homing(); } break; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index e9477e0b..0894fd4f 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -142,7 +142,8 @@ public: bool start_closed_loop_control(); bool stop_closed_loop_control(); - bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed); + bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, + std::function const_vel_cb = {}); bool run_closed_loop_control_loop(); bool run_homing(); bool run_idle_loop(); diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 7c40a185..b15e56ef 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -1,7 +1,7 @@ #include "odrive_main.h" #include - +#include Encoder::Encoder(TIM_HandleTypeDef* timer, Stm32Gpio index_gpio, Stm32Gpio hallA_gpio, Stm32Gpio hallB_gpio, Stm32Gpio hallC_gpio, @@ -197,6 +197,60 @@ bool Encoder::run_direction_find() { return success; } + +bool Encoder::run_hall_calibration() { + + // This will run every cycle when the lockin has reached the constant speed part + int states_seen_count[8] = {0}; + auto constant_speed_cb = [this, &states_seen_count]() { + states_seen_count[hall_state_]++; + }; + + Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; + lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds + lockin_config.finish_on_distance = true; + lockin_config.finish_on_enc_idx = false; + lockin_config.finish_on_vel = false; + + bool success = axis_->run_lockin_spin(lockin_config, false, constant_speed_cb); + + if (success) { + std::bitset<8> state_seen; + std::bitset<8> state_confirmed; + for (int i = 0; i < 8; i++) { + if (states_seen_count[i] > 0) + state_seen[i] = true; + if (states_seen_count[i] > 50) + state_confirmed[i] = true; + } + if (!(state_seen == state_confirmed)) { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } + + uint8_t states = state_seen.to_ulong(); + uint8_t hall_polarity = 0; + auto flip_detect = [](uint8_t states, unsigned int idx)->bool { + return ~states == (1<<(0+idx) | 1<<(7-idx)); + }; + if (flip_detect(states, 0)) { + hall_polarity = 0b000; + } else if (flip_detect(states, 1)) { + hall_polarity = 0b001; + } else if (flip_detect(states, 2)) { + hall_polarity = 0b010; + } else if (flip_detect(states, 3)) { + hall_polarity = 0b100; + } else { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } + hall_polarity_ = hall_polarity; + } + + return success; +} + // @brief Turns the motor in one direction for a bit and then in the other // direction in order to find the offset between the electrical phase 0 // and the encoder state 0. diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 841f7a4f..2390b01d 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -65,6 +65,7 @@ public: bool run_index_search(); bool run_direction_find(); + bool run_hall_calibration(); bool run_offset_calibration(); void sample_now(); bool read_sampled_gpio(Stm32Gpio gpio); @@ -110,6 +111,8 @@ public: uint16_t port_samples_[sizeof(ports_to_sample) / sizeof(ports_to_sample[0])]; // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC + bool hall_calibration_running_ = false; + float sincos_sample_s_ = 0.0f; float sincos_sample_c_ = 0.0f; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index c8d04b05..a623f1e1 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -1159,6 +1159,10 @@ valuetypes: brief: Run axis homing function. doc: Endstops must be enabled to use this feature. + EncoderHallCalibration: + brief: Rotate the motor in lockin and calibrate hall states + doc: + The phase offset is not calibrated at this time, so the map is only relative ODrive.Encoder.Mode: values: From f930462588dd61c90067ca153d41506ede5b86e4 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 7 Nov 2020 15:11:33 -0500 Subject: [PATCH 067/124] Add enum generation to makefile --- Firmware/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/Makefile b/Firmware/Makefile index a40ae89f..d12efb32 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -12,6 +12,7 @@ OPENOCD := openocd -f interface/stlink-v2.cfg \ all: @tup --quiet --no-environ-check + @python interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py flash: all $(OPENOCD) -c init \ From b0a1ef7caa37c71759fad58576700d41870a00fa Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 7 Nov 2020 15:04:17 -0800 Subject: [PATCH 068/124] also mask the states --- Firmware/MotorControl/encoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index b15e56ef..7ca0720a 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -231,7 +231,7 @@ bool Encoder::run_hall_calibration() { uint8_t states = state_seen.to_ulong(); uint8_t hall_polarity = 0; auto flip_detect = [](uint8_t states, unsigned int idx)->bool { - return ~states == (1<<(0+idx) | 1<<(7-idx)); + return (0xFF & ~states) == (1<<(0+idx) | 1<<(7-idx)); }; if (flip_detect(states, 0)) { hall_polarity = 0b000; From eab5801a656300338460460ad04f7e469b2f21e4 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 7 Nov 2020 15:11:33 -0500 Subject: [PATCH 069/124] Add enum generation to makefile --- Firmware/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/Makefile b/Firmware/Makefile index a40ae89f..d12efb32 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -12,6 +12,7 @@ OPENOCD := openocd -f interface/stlink-v2.cfg \ all: @tup --quiet --no-environ-check + @python interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py flash: all $(OPENOCD) -c init \ From 336a8b504c0f129abfb8b670c13f9b2434721581 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 7 Nov 2020 16:41:51 -0800 Subject: [PATCH 070/124] hall polarity calibration --- Firmware/MotorControl/axis.cpp | 9 ++++-- Firmware/MotorControl/axis.hpp | 2 +- Firmware/MotorControl/encoder.cpp | 50 ++++++++++++++++++------------- Firmware/MotorControl/encoder.hpp | 5 +++- Firmware/odrive-interface.yaml | 1 + tools/odrive/enums.py | 1 + 6 files changed, 42 insertions(+), 26 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 2942c076..2c45d11c 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -182,7 +182,7 @@ bool Axis::watchdog_check() { } bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, - std::function const_vel_cb) { + std::function loop_cb) { CRITICAL_SECTION() { // Reset state variables open_loop_controller_.Idq_setpoint_ = {0.0f, 0.0f}; @@ -237,9 +237,12 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme subscribed_to_idx_once = true; } - if (reached_target_vel && const_vel_cb) - const_vel_cb(); + if (loop_cb) + if (!loop_cb(reached_target_vel)) + break; + // TODO: use new sync function instead + asm volatile ("" ::: "memory"); osDelay(1); } diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 0894fd4f..da93bedf 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -143,7 +143,7 @@ public: bool start_closed_loop_control(); bool stop_closed_loop_control(); bool run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_armed, - std::function const_vel_cb = {}); + std::function loop_cb = {} ); bool run_closed_loop_control_loop(); bool run_homing(); bool run_idle_loop(); diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 7ca0720a..b861b28f 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -199,28 +199,31 @@ bool Encoder::run_direction_find() { bool Encoder::run_hall_calibration() { - - // This will run every cycle when the lockin has reached the constant speed part - int states_seen_count[8] = {0}; - auto constant_speed_cb = [this, &states_seen_count]() { - states_seen_count[hall_state_]++; - }; - Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds lockin_config.finish_on_distance = true; lockin_config.finish_on_enc_idx = false; lockin_config.finish_on_vel = false; - bool success = axis_->run_lockin_spin(lockin_config, false, constant_speed_cb); + auto loop_cb = [this](bool const_vel) { + if (const_vel) + sample_hall_states_ = true; + // No need to cancel early + return true; + }; + + states_seen_count_.fill(0); + hall_calibrated_ = false; + bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); + sample_hall_states_ = false; if (success) { std::bitset<8> state_seen; std::bitset<8> state_confirmed; for (int i = 0; i < 8; i++) { - if (states_seen_count[i] > 0) + if (states_seen_count_[i] > 0) state_seen[i] = true; - if (states_seen_count[i] > 50) + if (states_seen_count_[i] > 50) state_confirmed[i] = true; } if (!(state_seen == state_confirmed)) { @@ -245,7 +248,7 @@ bool Encoder::run_hall_calibration() { set_error(ERROR_ILLEGAL_HALL_STATE); return false; } - hall_polarity_ = hall_polarity; + config_.hall_polarity = hall_polarity; } return success; @@ -553,16 +556,21 @@ bool Encoder::update() { case MODE_HALL: { decode_hall_samples(); - int32_t hall_cnt; - if (decode_hall(hall_state_, &hall_cnt)) { - delta_enc = hall_cnt - count_in_cpr_; - delta_enc = mod(delta_enc, 6); - if (delta_enc > 3) - delta_enc -= 6; - } else { - if (!config_.ignore_illegal_hall_state) { - set_error(ERROR_ILLEGAL_HALL_STATE); - return false; + if (sample_hall_states_) { + states_seen_count_[hall_state_]++; + } + if (hall_calibrated_) { + int32_t hall_cnt; + if (decode_hall((hall_state_ ^ config_.hall_polarity), &hall_cnt)) { + delta_enc = hall_cnt - count_in_cpr_; + delta_enc = mod(delta_enc, 6); + if (delta_enc > 3) + delta_enc -= 6; + } else { + if (!config_.ignore_illegal_hall_state) { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } } } } break; diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 2390b01d..71542649 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -32,6 +32,7 @@ public: float bandwidth = 1000.0f; bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 + uint8_t hall_polarity; uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; uint16_t sincos_gpio_pin_cos = 4; @@ -111,7 +112,9 @@ public: uint16_t port_samples_[sizeof(ports_to_sample) / sizeof(ports_to_sample[0])]; // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC - bool hall_calibration_running_ = false; + bool sample_hall_states_ = false; + bool hall_calibrated_ = false; + std::array states_seen_count_; // for hall polarity calibration float sincos_sample_s_ = 0.0f; float sincos_sample_c_ = 0.0f; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index a623f1e1..6de402ef 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -980,6 +980,7 @@ interfaces: calib_scan_response: readonly float32 pos_abs: int32 spi_error_rate: readonly float32 + hall_calibrated: readonly bool config: c_is_class: False attributes: diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 801d2e3c..2674241d 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -35,6 +35,7 @@ AXIS_STATE_CLOSED_LOOP_CONTROL = 8 AXIS_STATE_LOCKIN_SPIN = 9 AXIS_STATE_ENCODER_DIR_FIND = 10 AXIS_STATE_HOMING = 11 +AXIS_STATE_ENCODER_HALL_CALIBRATION = 12 # ODrive.Encoder.Mode ENCODER_MODE_INCREMENTAL = 0 From f923d63d167f058202c0b18b8d603dbde5f4b0ff Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 7 Nov 2020 17:03:24 -0800 Subject: [PATCH 071/124] hopefully the correct is_ready --- Firmware/MotorControl/encoder.cpp | 13 ++++++++++--- Firmware/MotorControl/encoder.hpp | 4 ++-- Firmware/odrive-interface.yaml | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index b861b28f..7bdddb0d 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -22,7 +22,9 @@ bool Encoder::apply_config(ODriveIntf::MotorIntf::MotorType motor_type) { update_pll_gains(); if (config_.pre_calibrated) { - if (config_.mode == Encoder::MODE_HALL || config_.mode == Encoder::MODE_SINCOS) + if (config_.mode == Encoder::MODE_HALL && config_.hall_calibrated) + is_ready_ = true; + if (config_.mode == Encoder::MODE_SINCOS) is_ready_ = true; if (motor_type == Motor::MOTOR_TYPE_ACIM) is_ready_ = true; @@ -213,7 +215,7 @@ bool Encoder::run_hall_calibration() { }; states_seen_count_.fill(0); - hall_calibrated_ = false; + config_.hall_calibrated = false; bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); sample_hall_states_ = false; @@ -266,6 +268,11 @@ bool Encoder::run_offset_calibration() { return false; } + if (config_.mode == MODE_HALL && !config_.hall_calibrated) { + set_error(ERROR_HALL_NOT_CALIBRATED_YET); + return false; + } + // We use shadow_count_ to do the calibration, but the offset is used by count_in_cpr_ // Therefore we have to sync them for calibration shadow_count_ = count_in_cpr_; @@ -559,7 +566,7 @@ bool Encoder::update() { if (sample_hall_states_) { states_seen_count_[hall_state_]++; } - if (hall_calibrated_) { + if (config_.hall_calibrated) { int32_t hall_cnt; if (decode_hall((hall_state_ ^ config_.hall_polarity), &hall_cnt)) { delta_enc = hall_cnt - count_in_cpr_; diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 71542649..5d3d8348 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -32,7 +32,8 @@ public: float bandwidth = 1000.0f; bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 - uint8_t hall_polarity; + uint8_t hall_polarity = 0; + bool hall_calibrated = false; uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; uint16_t sincos_gpio_pin_cos = 4; @@ -113,7 +114,6 @@ public: // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC bool sample_hall_states_ = false; - bool hall_calibrated_ = false; std::array states_seen_count_; // for hall polarity calibration float sincos_sample_s_ = 0.0f; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 6de402ef..0f8d3d14 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -980,7 +980,6 @@ interfaces: calib_scan_response: readonly float32 pos_abs: int32 spi_error_rate: readonly float32 - hall_calibrated: readonly bool config: c_is_class: False attributes: @@ -1000,6 +999,8 @@ interfaces: calib_scan_distance: float32 calib_scan_omega: float32 ignore_illegal_hall_state: bool + hall_polarity: uint8 + hall_calibrated: bool sincos_gpio_pin_sin: type: uint16 doc: Analog sine signal of a sin/cos encoder. The corresponding GPIO must be in `GPIO_MODE_ANALOG_IN`. From 5836cfb9d0f2a32e0e1c5706650cb90203a983c2 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 7 Nov 2020 20:59:56 -0800 Subject: [PATCH 072/124] hall phase calib first pass --- Firmware/MotorControl/axis.cpp | 4 +- Firmware/MotorControl/encoder.cpp | 71 ++++++++++++++++++++++++++++--- Firmware/MotorControl/encoder.hpp | 12 +++++- Firmware/odrive-interface.yaml | 3 +- tools/odrive/enums.py | 1 + 5 files changed, 81 insertions(+), 10 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 2c45d11c..27fc1cf9 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -507,7 +507,9 @@ void Axis::run_state_machine_loop() { if (!motor_.is_calibrated_) goto invalid_state_label; - status = encoder_.run_hall_calibration(); + status = encoder_.run_hall_polarity_calibration(); + if (status) + status = encoder_.run_hall_phase_calibration(); } break; case AXIS_STATE_HOMING: { diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 7bdddb0d..80c60115 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -22,7 +22,7 @@ bool Encoder::apply_config(ODriveIntf::MotorIntf::MotorType motor_type) { update_pll_gains(); if (config_.pre_calibrated) { - if (config_.mode == Encoder::MODE_HALL && config_.hall_calibrated) + if (config_.mode == Encoder::MODE_HALL && config_.hall_polarity_calibrated) is_ready_ = true; if (config_.mode == Encoder::MODE_SINCOS) is_ready_ = true; @@ -200,7 +200,7 @@ bool Encoder::run_direction_find() { } -bool Encoder::run_hall_calibration() { +bool Encoder::run_hall_polarity_calibration() { Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; lockin_config.finish_distance = lockin_config.vel * 3.0f; // run for 3 seconds lockin_config.finish_on_distance = true; @@ -214,8 +214,8 @@ bool Encoder::run_hall_calibration() { return true; }; + config_.hall_polarity_calibrated = false; states_seen_count_.fill(0); - config_.hall_calibrated = false; bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); sample_hall_states_ = false; @@ -236,7 +236,7 @@ bool Encoder::run_hall_calibration() { uint8_t states = state_seen.to_ulong(); uint8_t hall_polarity = 0; auto flip_detect = [](uint8_t states, unsigned int idx)->bool { - return (0xFF & ~states) == (1<<(0+idx) | 1<<(7-idx)); + return (~states & 0xFF) == (1<<(0+idx) | 1<<(7-idx)); }; if (flip_detect(states, 0)) { hall_polarity = 0b000; @@ -256,6 +256,39 @@ bool Encoder::run_hall_calibration() { return success; } +bool Encoder::run_hall_phase_calibration() { + Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; + lockin_config.finish_distance = lockin_config.vel * 10.0f; // run for 10 seconds + lockin_config.finish_on_distance = true; + lockin_config.finish_on_enc_idx = false; + lockin_config.finish_on_vel = false; + + auto loop_cb = [this](bool const_vel) { + if (const_vel) + sample_hall_phase_ = true; + // No need to cancel early + return true; + }; + + // TODO: There is a race condition here with the execution in Encoder::update. + // We should evaluate making thread execution synchronous with the control loops + // at least optionally. + // Perhaps the new loop_sync feature will give a loose timing guarantee that may be sufficient + calibrate_hall_phase_ = true; + config_.hall_edge_phase.fill(0); + bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); + + if (success) { + for (int i = 0; i < 6; i++) + config_.hall_edge_phase[i] /= (float)hall_phase_calib_seen_count_[i]; + } else { + config_.hall_edge_phase = hall_edge_phase_defaults; + } + + calibrate_hall_phase_ = false; + return success; +} + // @brief Turns the motor in one direction for a bit and then in the other // direction in order to find the offset between the electrical phase 0 // and the encoder state 0. @@ -268,7 +301,7 @@ bool Encoder::run_offset_calibration() { return false; } - if (config_.mode == MODE_HALL && !config_.hall_calibrated) { + if (config_.mode == MODE_HALL && !config_.hall_polarity_calibrated) { set_error(ERROR_HALL_NOT_CALIBRATED_YET); return false; } @@ -566,9 +599,35 @@ bool Encoder::update() { if (sample_hall_states_) { states_seen_count_[hall_state_]++; } - if (config_.hall_calibrated) { + if (config_.hall_polarity_calibrated) { int32_t hall_cnt; if (decode_hall((hall_state_ ^ config_.hall_polarity), &hall_cnt)) { + if (calibrate_hall_phase_) { + if (sample_hall_phase_ && last_hall_cnt_.has_value()) { + int mod_hall_cnt = (hall_cnt - last_hall_cnt_.value()) % 6; + size_t edge_idx; + if (mod_hall_cnt == 0) { goto skip; } // no count - do nothing + else if (mod_hall_cnt == 1) { // counted up + edge_idx = hall_cnt; + } else if (mod_hall_cnt == 5) { // counted down + edge_idx = last_hall_cnt_.value(); + } else { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } + + auto maybe_phase = axis_->open_loop_controller_.phase_.get_any(); + if (maybe_phase) { + config_.hall_edge_phase[edge_idx] += maybe_phase.value(); + hall_phase_calib_seen_count_[edge_idx]++; + } + } + skip: + last_hall_cnt_ = hall_cnt; + + return true; // Skip all velocity and phase estimation + } + delta_enc = hall_cnt - count_in_cpr_; delta_enc = mod(delta_enc, 6); if (delta_enc > 3) diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 5d3d8348..cea130d2 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -11,6 +11,8 @@ class Encoder : public ODriveIntf::EncoderIntf { public: static constexpr uint32_t MODE_FLAG_ABS = 0x100; + static constexpr std::array hall_edge_phase_defaults = + {0*1.0471975512f, 1*1.0471975512f, 2*1.0471975512f, 3*1.0471975512f, 4*1.0471975512f, 5*1.0471975512f}; struct Config_t { Mode mode = MODE_INCREMENTAL; @@ -33,7 +35,8 @@ public: bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 uint8_t hall_polarity = 0; - bool hall_calibrated = false; + bool hall_polarity_calibrated = false; + std::array hall_edge_phase = hall_edge_phase_defaults; uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; uint16_t sincos_gpio_pin_cos = 4; @@ -67,7 +70,8 @@ public: bool run_index_search(); bool run_direction_find(); - bool run_hall_calibration(); + bool run_hall_polarity_calibration(); + bool run_hall_phase_calibration(); bool run_offset_calibration(); void sample_now(); bool read_sampled_gpio(Stm32Gpio gpio); @@ -113,8 +117,12 @@ public: uint16_t port_samples_[sizeof(ports_to_sample) / sizeof(ports_to_sample[0])]; // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC + std::optional last_hall_cnt_ = std::nullopt; // Used to find hall edges for calibration + bool calibrate_hall_phase_ = false; bool sample_hall_states_ = false; + bool sample_hall_phase_ = false; std::array states_seen_count_; // for hall polarity calibration + std::array hall_phase_calib_seen_count_; float sincos_sample_s_ = 0.0f; float sincos_sample_c_ = 0.0f; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 0f8d3d14..68ed0def 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -964,6 +964,7 @@ interfaces: AbsSpiTimeout: AbsSpiComFail: AbsSpiNotReady: + HallNotCalibratedYet: is_ready: readonly bool index_found: readonly bool shadow_count: readonly int32 @@ -1000,7 +1001,7 @@ interfaces: calib_scan_omega: float32 ignore_illegal_hall_state: bool hall_polarity: uint8 - hall_calibrated: bool + hall_polarity_calibrated: bool sincos_gpio_pin_sin: type: uint16 doc: Analog sine signal of a sin/cos encoder. The corresponding GPIO must be in `GPIO_MODE_ANALOG_IN`. diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 2674241d..9442723a 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -140,6 +140,7 @@ ENCODER_ERROR_INDEX_NOT_FOUND_YET = 0x00000020 ENCODER_ERROR_ABS_SPI_TIMEOUT = 0x00000040 ENCODER_ERROR_ABS_SPI_COM_FAIL = 0x00000080 ENCODER_ERROR_ABS_SPI_NOT_READY = 0x00000100 +ENCODER_ERROR_HALL_NOT_CALIBRATED_YET = 0x00000200 # ODrive.SensorlessEstimator.Error SENSORLESS_ESTIMATOR_ERROR_NONE = 0x00000000 From 7ad508990d276ee9ab02e66617b4e739b620ffa9 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 8 Nov 2020 21:03:46 -0800 Subject: [PATCH 073/124] implement normalized phase corrected hall calibration --- Firmware/MotorControl/encoder.cpp | 35 ++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 80c60115..5254e1bb 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -251,6 +251,7 @@ bool Encoder::run_hall_polarity_calibration() { return false; } config_.hall_polarity = hall_polarity; + config_.hall_polarity_calibrated = true; } return success; @@ -275,12 +276,27 @@ bool Encoder::run_hall_phase_calibration() { // at least optionally. // Perhaps the new loop_sync feature will give a loose timing guarantee that may be sufficient calibrate_hall_phase_ = true; - config_.hall_edge_phase.fill(0); + config_.hall_edge_phase.fill(0.0f); + hall_phase_calib_seen_count_.fill(0); bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); + if (error_ & ERROR_ILLEGAL_HALL_STATE) + success = false; if (success) { - for (int i = 0; i < 6; i++) - config_.hall_edge_phase[i] /= (float)hall_phase_calib_seen_count_[i]; + // Check deltas to dicern rotation direction + float delta_phase = 0.0f; + for (int i = 0; i < 6; i++) { + int next_i = (i == 6) ? 0 : i+1; + delta_phase += wrap_pm_pi(config_.hall_edge_phase[next_i] - config_.hall_edge_phase[i]); + } + // Correct reverse rotation + if (delta_phase < 0.0f) + for (int i = 0; i < 6; i++) + config_.hall_edge_phase[i] = wrap_pm_pi(-config_.hall_edge_phase[i]); + // Normalize edge timing to 1st edge in sequence + float offset = config_.hall_edge_phase[0]; + for (int i = 0; i < 6; i++) + config_.hall_edge_phase[i] = wrap_pm_pi(config_.hall_edge_phase[i] - offset); } else { config_.hall_edge_phase = hall_edge_phase_defaults; } @@ -604,7 +620,7 @@ bool Encoder::update() { if (decode_hall((hall_state_ ^ config_.hall_polarity), &hall_cnt)) { if (calibrate_hall_phase_) { if (sample_hall_phase_ && last_hall_cnt_.has_value()) { - int mod_hall_cnt = (hall_cnt - last_hall_cnt_.value()) % 6; + int mod_hall_cnt = mod(hall_cnt - last_hall_cnt_.value(), 6); size_t edge_idx; if (mod_hall_cnt == 0) { goto skip; } // no count - do nothing else if (mod_hall_cnt == 1) { // counted up @@ -618,8 +634,17 @@ bool Encoder::update() { auto maybe_phase = axis_->open_loop_controller_.phase_.get_any(); if (maybe_phase) { - config_.hall_edge_phase[edge_idx] += maybe_phase.value(); + float phase = maybe_phase.value(); + // Early increment to get the right divisor in recursive average hall_phase_calib_seen_count_[edge_idx]++; + float& edge_phase = config_.hall_edge_phase[edge_idx]; + if (hall_phase_calib_seen_count_[edge_idx] == 1) + edge_phase = phase; + else { + // circularly wrapped recursive average + edge_phase += (phase - edge_phase) / hall_phase_calib_seen_count_[edge_idx]; + edge_phase = wrap_pm_pi(edge_phase); + } } } skip: From 34f55f02c1b5dcc9546f4079fdb4b2d061481acf Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 8 Nov 2020 22:00:38 -0800 Subject: [PATCH 074/124] also find direction during hall calib --- Firmware/MotorControl/encoder.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 5254e1bb..b56632af 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -290,9 +290,13 @@ bool Encoder::run_hall_phase_calibration() { delta_phase += wrap_pm_pi(config_.hall_edge_phase[next_i] - config_.hall_edge_phase[i]); } // Correct reverse rotation - if (delta_phase < 0.0f) + if (delta_phase < 0.0f) { + config_.direction = -1; for (int i = 0; i < 6; i++) config_.hall_edge_phase[i] = wrap_pm_pi(-config_.hall_edge_phase[i]); + } else { + config_.direction = 1; + } // Normalize edge timing to 1st edge in sequence float offset = config_.hall_edge_phase[0]; for (int i = 0; i < 6; i++) @@ -721,6 +725,8 @@ bool Encoder::update() { // Predict current pos pos_estimate_counts_ += current_meas_period * vel_estimate_counts_; pos_cpr_counts_ += current_meas_period * vel_estimate_counts_; + // Encoder model + // discrete phase detector float delta_pos_counts = (float)(shadow_count_ - (int32_t)std::floor(pos_estimate_counts_)); float delta_pos_cpr_counts = (float)(count_in_cpr_ - (int32_t)std::floor(pos_cpr_counts_)); From 04b4e9f84e9151a8ab8a85a056d8daf201e1edc5 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 8 Nov 2020 23:19:19 -0800 Subject: [PATCH 075/124] implement correction, not yet confirmed working --- Firmware/MotorControl/encoder.cpp | 55 +++++++++++++++++++++++-------- Firmware/MotorControl/encoder.hpp | 7 ++-- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index b56632af..56e4c842 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -276,7 +276,7 @@ bool Encoder::run_hall_phase_calibration() { // at least optionally. // Perhaps the new loop_sync feature will give a loose timing guarantee that may be sufficient calibrate_hall_phase_ = true; - config_.hall_edge_phase.fill(0.0f); + config_.hall_edge_phcnt.fill(0.0f); hall_phase_calib_seen_count_.fill(0); bool success = axis_->run_lockin_spin(lockin_config, false, loop_cb); if (error_ & ERROR_ILLEGAL_HALL_STATE) @@ -286,23 +286,25 @@ bool Encoder::run_hall_phase_calibration() { // Check deltas to dicern rotation direction float delta_phase = 0.0f; for (int i = 0; i < 6; i++) { - int next_i = (i == 6) ? 0 : i+1; - delta_phase += wrap_pm_pi(config_.hall_edge_phase[next_i] - config_.hall_edge_phase[i]); + int next_i = (i == 5) ? 0 : i+1; + delta_phase += wrap_pm_pi(config_.hall_edge_phcnt[next_i] - config_.hall_edge_phcnt[i]); } // Correct reverse rotation if (delta_phase < 0.0f) { config_.direction = -1; for (int i = 0; i < 6; i++) - config_.hall_edge_phase[i] = wrap_pm_pi(-config_.hall_edge_phase[i]); + config_.hall_edge_phcnt[i] = wrap_pm_pi(-config_.hall_edge_phcnt[i]); } else { config_.direction = 1; } - // Normalize edge timing to 1st edge in sequence - float offset = config_.hall_edge_phase[0]; - for (int i = 0; i < 6; i++) - config_.hall_edge_phase[i] = wrap_pm_pi(config_.hall_edge_phase[i] - offset); + // Normalize edge timing to 1st edge in sequence, and change units to counts + float offset = config_.hall_edge_phcnt[0]; + for (int i = 0; i < 6; i++) { + float& phcnt = config_.hall_edge_phcnt[i]; + phcnt = fmodf_pos((6.0f / (2.0f * M_PI)) * (phcnt - offset), 6.0f); + } } else { - config_.hall_edge_phase = hall_edge_phase_defaults; + config_.hall_edge_phcnt = hall_edge_defaults; } calibrate_hall_phase_ = false; @@ -601,6 +603,28 @@ void Encoder::abs_spi_cs_pin_init(){ abs_spi_cs_gpio_.write(true); } +// Note that this may return counts +1 or -1 without any wrapping +int32_t Encoder::hall_model(float internal_pos) { + int32_t base_cnt = (int32_t)std::floor(internal_pos); + + float pos_in_range = fmodf_pos(internal_pos, 6.0f); + int pos_idx = (int)pos_in_range; + if (pos_idx == 6) pos_idx = 5; // in case of rounding error + int next_i = (pos_idx == 5) ? 0 : pos_idx+1; + + float below_edge = config_.hall_edge_phcnt[pos_idx]; + float above_edge = config_.hall_edge_phcnt[next_i]; + + // if we are blow the "below" edge, we are the count under + if (wrap_pm(pos_in_range - below_edge, 6.0f) < 0.0f) + return base_cnt - 1; + // if we are above the "above" edge, we are the count over + else if (wrap_pm(pos_in_range - above_edge, 6.0f) > 0.0f) + return base_cnt + 1; + // otherwise we are in the nominal count (or completely lost) + return base_cnt; +} + bool Encoder::update() { // update internal encoder state. int32_t delta_enc = 0; @@ -641,7 +665,7 @@ bool Encoder::update() { float phase = maybe_phase.value(); // Early increment to get the right divisor in recursive average hall_phase_calib_seen_count_[edge_idx]++; - float& edge_phase = config_.hall_edge_phase[edge_idx]; + float& edge_phase = config_.hall_edge_phcnt[edge_idx]; if (hall_phase_calib_seen_count_[edge_idx] == 1) edge_phase = phase; else { @@ -726,10 +750,15 @@ bool Encoder::update() { pos_estimate_counts_ += current_meas_period * vel_estimate_counts_; pos_cpr_counts_ += current_meas_period * vel_estimate_counts_; // Encoder model - + auto encoder_model = [this](float internal_pos)->int32_t { + if (config_.mode == MODE_HALL) + return hall_model(internal_pos); + else + return (int32_t)std::floor(internal_pos); + }; // discrete phase detector - float delta_pos_counts = (float)(shadow_count_ - (int32_t)std::floor(pos_estimate_counts_)); - float delta_pos_cpr_counts = (float)(count_in_cpr_ - (int32_t)std::floor(pos_cpr_counts_)); + float delta_pos_counts = (float)(shadow_count_ - encoder_model(pos_estimate_counts_)); + float delta_pos_cpr_counts = (float)(count_in_cpr_ - encoder_model(pos_cpr_counts_)); delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, (float)(config_.cpr)); // pll feedback pos_estimate_counts_ += current_meas_period * pll_kp_ * delta_pos_counts; diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index cea130d2..2f384d7b 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -11,8 +11,8 @@ class Encoder : public ODriveIntf::EncoderIntf { public: static constexpr uint32_t MODE_FLAG_ABS = 0x100; - static constexpr std::array hall_edge_phase_defaults = - {0*1.0471975512f, 1*1.0471975512f, 2*1.0471975512f, 3*1.0471975512f, 4*1.0471975512f, 5*1.0471975512f}; + static constexpr std::array hall_edge_defaults = + {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f}; struct Config_t { Mode mode = MODE_INCREMENTAL; @@ -36,7 +36,7 @@ public: bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 uint8_t hall_polarity = 0; bool hall_polarity_calibrated = false; - std::array hall_edge_phase = hall_edge_phase_defaults; + std::array hall_edge_phcnt = hall_edge_defaults; uint16_t abs_spi_cs_gpio_pin = 1; uint16_t sincos_gpio_pin_sin = 3; uint16_t sincos_gpio_pin_cos = 4; @@ -76,6 +76,7 @@ public: void sample_now(); bool read_sampled_gpio(Stm32Gpio gpio); void decode_hall_samples(); + int32_t hall_model(float internal_pos); bool update(); TIM_HandleTypeDef* timer_; From ed75acb905e6a3d90a9f46d1e5a27e8c18c67842 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Mon, 9 Nov 2020 21:12:35 -0500 Subject: [PATCH 076/124] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 24 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..f29b8674 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps and configuration necessary to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Desktop (please complete the following information):** + - OS: [e.g. Windows 10] + - odrivetool Version (`odrivetool --version`) + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..bbcbbe7d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From ff3a3b2078eccd2348115c8f1524166f4a89f9f0 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 5 Oct 2020 13:58:52 +0200 Subject: [PATCH 077/124] use pip3 in nightly install tests --- .github/workflows/nightly.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 4b3e3294..8769d92d 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -16,8 +16,8 @@ jobs: steps: - name: Install odrivetool run: | - pip install monotonic # TODO: this is dishonest. Must be removed as soon as v0.5.0 is published! - pip install odrive + pip3 install monotonic # TODO: this is dishonest. Must be removed as soon as v0.5.0 is published! + pip3 install odrive # This one currently fails because Github Actions runs pip as non-root #- name: Check if udev rules were set up properly From 7afa99447c852bc188cb3b644b95f77dfdba4086 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 12 Nov 2020 15:21:25 -0800 Subject: [PATCH 078/124] make hall calib 30s by default --- Firmware/MotorControl/encoder.cpp | 3 ++- Firmware/MotorControl/encoder.hpp | 1 + Firmware/odrive-interface.yaml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 56e4c842..d8be9295 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -259,7 +259,7 @@ bool Encoder::run_hall_polarity_calibration() { bool Encoder::run_hall_phase_calibration() { Axis::LockinConfig_t lockin_config = axis_->config_.calibration_lockin; - lockin_config.finish_distance = lockin_config.vel * 10.0f; // run for 10 seconds + lockin_config.finish_distance = lockin_config.vel * 30.0f; // run for 30 seconds lockin_config.finish_on_distance = true; lockin_config.finish_on_enc_idx = false; lockin_config.finish_on_vel = false; @@ -760,6 +760,7 @@ bool Encoder::update() { float delta_pos_counts = (float)(shadow_count_ - encoder_model(pos_estimate_counts_)); float delta_pos_cpr_counts = (float)(count_in_cpr_ - encoder_model(pos_cpr_counts_)); delta_pos_cpr_counts = wrap_pm(delta_pos_cpr_counts, (float)(config_.cpr)); + delta_pos_cpr_counts_ += 0.1f * (delta_pos_cpr_counts - delta_pos_cpr_counts_); // for debug // pll feedback pos_estimate_counts_ += current_meas_period * pll_kp_ * delta_pos_counts; pos_cpr_counts_ += current_meas_period * pll_kp_ * delta_pos_cpr_counts; diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 2f384d7b..eef286fe 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -99,6 +99,7 @@ public: OutputPort phase_vel_ = 0.0f; // [rad/s] float pos_estimate_counts_ = 0.0f; // [count] float pos_cpr_counts_ = 0.0f; // [count] + float delta_pos_cpr_counts_ = 0.0f; // [count] phase detector result for debug float vel_estimate_counts_ = 0.0f; // [count/s] float pll_kp_ = 0.0f; // [count/s / count] float pll_ki_ = 0.0f; // [(count/s^2) / count] diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 68ed0def..35376cc1 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -974,6 +974,7 @@ interfaces: pos_estimate: {type: readonly float32, c_getter: pos_estimate_.get_any().value_or(0.0f)} pos_estimate_counts: readonly float32 pos_cpr_counts: readonly float32 + delta_pos_cpr_counts: readonly float32 pos_circular: {type: readonly float32, c_getter: pos_circular_.get_any().value_or(0.0f)} hall_state: readonly uint8 vel_estimate: {type: readonly float32, c_getter: vel_estimate_.get_any().value_or(0.0f)} From 6d22b35f990d654139bfcb7dc04ef3b16438b8c8 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 4 Nov 2020 10:37:22 +0100 Subject: [PATCH 079/124] add `config.enable_brake_resistor` --- CHANGELOG.md | 1 + Firmware/MotorControl/low_level.cpp | 48 ++++++++++++++++---------- Firmware/MotorControl/odrive_main.h | 1 + Firmware/odrive-interface.yaml | 18 +++++++++- tools/odrive/enums.py | 1 + tools/odrive/tests/calibration_test.py | 2 ++ tools/odrive/tests/closed_loop_test.py | 6 +++- tools/test-rig-rpi.yaml | 2 +- 8 files changed, 57 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04cc36d6..22034ba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ Please add a note of your changes below this heading if you make a Pull Request. * Several properties were changed to readonly. * `.encoder.config.offset` was renamed to ``.encoder.config.phase_offset` * `.encoder.config.offset_float` was renamed to ``.encoder.config.phase_offset_float` +* `.config.brake_resistance == 0.0` is no longer a valid way to disable the brake resistor. Use `.config.enable_brake_resistor` instead. # Releases ## [0.5.1] - 2020-09-27 diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 066cd533..4c9cf23e 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -310,31 +310,41 @@ void update_brake_current() { Ibus_sum += axes[i].motor_.I_bus_; } } + + float brake_duty; + + if (odrv.config_.enable_brake_resistor) { + if (!(odrv.config_.brake_resistance > 0.0f)) { + odrv.disarm_with_error(ODrive::ERROR_INVALID_BRAKE_RESISTANCE); + return; + } - // Don't start braking until -Ibus > regen_current_allowed - float brake_current = -Ibus_sum - odrv.config_.max_regen_current; - float brake_duty = brake_current * odrv.config_.brake_resistance / vbus_voltage; - - if (odrv.config_.enable_dc_bus_overvoltage_ramp && (odrv.config_.brake_resistance > 0.0f) && (odrv.config_.dc_bus_overvoltage_ramp_start < odrv.config_.dc_bus_overvoltage_ramp_end)) { - brake_duty += std::max((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); - } + // Don't start braking until -Ibus > regen_current_allowed + float brake_current = -Ibus_sum - odrv.config_.max_regen_current; + brake_duty = brake_current * odrv.config_.brake_resistance / vbus_voltage; + + if (odrv.config_.enable_dc_bus_overvoltage_ramp && (odrv.config_.brake_resistance > 0.0f) && (odrv.config_.dc_bus_overvoltage_ramp_start < odrv.config_.dc_bus_overvoltage_ramp_end)) { + brake_duty += std::max((vbus_voltage - odrv.config_.dc_bus_overvoltage_ramp_start) / (odrv.config_.dc_bus_overvoltage_ramp_end - odrv.config_.dc_bus_overvoltage_ramp_start), 0.0f); + } - if (is_nan(brake_duty)) { - // Shuts off all motors AND brake resistor, sets error code on all motors. - odrv.disarm_with_error(ODrive::ERROR_BRAKE_DUTY_CYCLE_NAN); - return; - } + if (is_nan(brake_duty)) { + // Shuts off all motors AND brake resistor, sets error code on all motors. + odrv.disarm_with_error(ODrive::ERROR_BRAKE_DUTY_CYCLE_NAN); + return; + } - if (brake_duty >= 0.95f) { - brake_resistor_saturated = true; - } + if (brake_duty >= 0.95f) { + brake_resistor_saturated = true; + } - // Duty limit at 95% to allow bootstrap caps to charge - brake_duty = std::clamp(brake_duty, 0.0f, 0.95f); + // Duty limit at 95% to allow bootstrap caps to charge + brake_duty = std::clamp(brake_duty, 0.0f, 0.95f); - // Special handling to avoid the case 0.0/0.0 == NaN, or divide by 0. - if (odrv.config_.brake_resistance > 0.0f) { + // This cannot result in NaN (safe for race conditions) because we check + // brake_resistance != 0 further up. Ibus_sum += brake_duty * vbus_voltage / odrv.config_.brake_resistance; + } else { + brake_duty = 0; } ibus_ += odrv.ibus_report_filter_k_ * (Ibus_sum - ibus_); diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 886fba1b..88549e74 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -74,6 +74,7 @@ struct BoardConfig_t { bool enable_ascii_protocol_on_usb = true; float max_regen_current = 0.0f; float brake_resistance = DEFAULT_BRAKE_RESISTANCE; + bool enable_brake_resistor = false; float dc_bus_undervoltage_trip_level = 8.0f; // Date: Thu, 10 Sep 2020 20:04:41 +0200 Subject: [PATCH 080/124] minor tweaks to v3 code --- Firmware/Board/v3/board.cpp | 21 +++++---------------- Firmware/Drivers/DRV8301/drv8301.hpp | 2 +- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index 17f0e9f0..dada4c12 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -297,7 +297,7 @@ bool board_init() { HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); - HAL_NVIC_SetPriority(ControlLoop_IRQn, 5, 0); // must be on the same level as ADC interrupt + HAL_NVIC_SetPriority(ControlLoop_IRQn, 5, 0); HAL_NVIC_EnableIRQ(ControlLoop_IRQn); HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0); @@ -364,19 +364,10 @@ void start_timers() { hadc3.Instance->CR2 &= ~(ADC_CR2_EXTEN | ADC_CR2_JEXTEN); /* - * Initial intention of the synchronization: * Synchronize TIM1, TIM8 and TIM13 such that: * 1. The triangle waveform of TIM1 leads the triangle waveform of TIM8 by a * 90° phase shift. - * 2. The timer update events of TIM1 and TIM8 are symmetrically interleaved. - * 3. Each TIM13 reload coincides with a TIM1 lower update event. - * - * However right now this synchronization only ensures point (1) and (3) but because - * TIM1 and TIM3 only trigger an update on every third reload, this does not - * allow for (2). - * - * TODO: revisit the timing topic in general. - * + * 2. Each TIM13 reload coincides with a TIM1 lower update event. */ Stm32Timer::start_synchronously<3>( {&htim1, &htim8, &htim13}, @@ -396,12 +387,8 @@ void start_timers() { __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_OVR); __HAL_ADC_CLEAR_FLAG(&hadc2, ADC_FLAG_OVR); __HAL_ADC_CLEAR_FLAG(&hadc3, ADC_FLAG_OVR); + __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); - - // it's sufficient to enable interrupts for one ADC only because they all trigger simultaneously - //__HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_JEOC); - //__HAL_ADC_ENABLE_IT(&hadc3, ADC_IT_EOC); - __HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE); } } @@ -466,6 +453,8 @@ volatile uint32_t timestamp_ = 0; volatile bool counting_down_ = false; void TIM8_UP_TIM13_IRQHandler(void) { + COUNT_IRQ(TIM8_UP_TIM13_IRQn); + // Entry into this function happens at 21-23 clock cycles after the timer // update event. __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); diff --git a/Firmware/Drivers/DRV8301/drv8301.hpp b/Firmware/Drivers/DRV8301/drv8301.hpp index 45f693bd..29a04465 100644 --- a/Firmware/Drivers/DRV8301/drv8301.hpp +++ b/Firmware/Drivers/DRV8301/drv8301.hpp @@ -42,7 +42,7 @@ public: * If the gate driver was in ready state and the new configuration is * different from the old one then the gate driver will exit ready state. * - * In any case cnahges to the configuration only take effect with a call to + * In any case changes to the configuration only take effect with a call to * init(). */ bool config(float requested_gain, float* actual_gain); From d653abfac2ecc401f103b8011b24bdc4fe2eda2c Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Sat, 10 Oct 2020 12:29:57 +0200 Subject: [PATCH 081/124] update dev guide --- docs/developer-guide.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 54daa887..6fac13a8 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -143,11 +143,7 @@ If the flashing worked, you can connect to the board using the [odrivetool](gett

## Testing -The script `tools/run_tests.py` runs a sequence of automated tests for several firmware features as well as high power burn-in tests. Some tests only need one ODrive and one motor/encoder pair while other tests need a back-to-back test rig such as [this one](https://cad.onshape.com/documents/026bda35ad5dff4d73c1d37f/w/ae302174f402737e1fdb3783/e/5ca143a6e5e24daf1fe8e434). In any case, to run the tests you need to provide a YAML file that lists the parameters of your test setup. An example can be found at [`tools/test-rig-parallel.yaml`](tools/test-rig-parallel.yaml`). The programmer serial number can be found by running `Firmware/find_programmer.sh` (make sure it has the latest firmware from STM). - -
The test script commands the ODrive to high currents and high motor speeds so if your ODrive is connected to anything other than a stirdy test-rig (or free spinning motors), it will probably break your machine.
- -Example usage: `./run_tests.py --test-rig-yaml ../tools/test-rig-parallel.yaml` +_Main article: [Testing](testing.md)_

## Debugging From a6587f8d8ae1441a924dd3769b823379465f9b37 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 11 Nov 2020 21:02:25 +0100 Subject: [PATCH 082/124] fix various issues - overcurrent error during motor calibration (this is caused by overshoot. For now we just ignore the current limit during motor calibration as we did before) - factor 2 error at encoder calibration - set error flag if Motor::arm() is called while the brake resistor is enabled but disarmed. - only arm brake resistor if enabled - auto-arm brake resistor on clear_errors() --- Firmware/MotorControl/encoder.cpp | 8 ++++---- Firmware/MotorControl/low_level.cpp | 7 ++++++- Firmware/MotorControl/main.cpp | 3 +++ Firmware/MotorControl/motor.cpp | 11 ++++++++--- Firmware/MotorControl/open_loop_controller.cpp | 2 +- Firmware/MotorControl/open_loop_controller.hpp | 1 + 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 7c40a185..7132100e 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -228,7 +228,7 @@ bool Encoder::run_offset_calibration() { axis_->open_loop_controller_.target_voltage_ = axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL ? 0.0f : axis_->motor_.config_.calibration_current; axis_->open_loop_controller_.target_vel_ = 0.0f; axis_->open_loop_controller_.total_distance_ = 0.0f; - axis_->open_loop_controller_.phase_ = wrap_pm_pi(0 - config_.calib_scan_distance / 2.0f); + axis_->open_loop_controller_.phase_ = axis_->open_loop_controller_.initial_phase_ = wrap_pm_pi(0 - config_.calib_scan_distance / 2.0f); axis_->motor_.current_control_.enable_current_control_src_ = (axis_->motor_.config_.motor_type != Motor::MOTOR_TYPE_GIMBAL); axis_->motor_.current_control_.Idq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Idq_setpoint_); @@ -324,9 +324,9 @@ bool Encoder::run_offset_calibration() { axis_->motor_.disarm(); - config_.phase_offset = encvaluesum / (num_steps * 2); - int32_t residual = encvaluesum - ((int64_t)config_.phase_offset * (int64_t)(num_steps * 2)); - config_.phase_offset_float = (float)residual / (float)(num_steps * 2) + 0.5f; // add 0.5 to center-align state to phase + config_.phase_offset = encvaluesum / num_steps; + int32_t residual = encvaluesum - ((int64_t)config_.phase_offset * (int64_t)num_steps); + config_.phase_offset_float = (float)residual / (float)num_steps + 0.5f; // add 0.5 to center-align state to phase is_ready_ = true; return true; diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 4c9cf23e..f0a2bb64 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -76,6 +76,9 @@ bool brake_resistor_saturated = false; // @brief Arms the brake resistor void safety_critical_arm_brake_resistor() { CRITICAL_SECTION() { + for (size_t i = 0; i < AXIS_COUNT; ++i) { + axes[i].motor_.I_bus_ = 0.0f; + } brake_resistor_armed = true; htim2.Instance->CCR3 = 0; htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; @@ -164,7 +167,9 @@ void start_adc_pwm() { HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4); - safety_critical_arm_brake_resistor(); + if (odrv.config_.enable_brake_resistor) { + safety_critical_arm_brake_resistor(); + } } // @brief ADC1 measurements are written to this buffer by DMA diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index ae24e3d4..49a91efd 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -161,6 +161,9 @@ void ODrive::clear_errors() { axis.error_ = Axis::ERROR_NONE; } error_ = ERROR_NONE; + if (odrv.config_.enable_brake_resistor) { + safety_critical_arm_brake_resistor(); + } } extern "C" { diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 557cde9c..a81cdc2a 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -198,8 +198,10 @@ bool Motor::arm(PhaseControlLaw<3>* control_law) { control_law_->reset(); } - if (brake_resistor_armed) { + if (!odrv.config_.enable_brake_resistor || brake_resistor_armed) { is_armed_ = true; + } else { + error_ |= Motor::ERROR_BRAKE_RESISTOR_DISARMED; } } @@ -217,7 +219,7 @@ bool Motor::arm(PhaseControlLaw<3>* control_law) { */ void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) { CRITICAL_SECTION() { - if (!brake_resistor_armed) { + if (odrv.config_.enable_brake_resistor && !brake_resistor_armed) { disarm_with_error(ERROR_BRAKE_RESISTOR_DISARMED); } @@ -609,7 +611,10 @@ void Motor::current_meas_cb(uint32_t timestamp, std::optional current float Inorm_sq = 2.0f / 3.0f * (SQ(current_meas_->phA) + SQ(current_meas_->phB) + SQ(current_meas_->phC)); - if (Inorm_sq > SQ(Itrip)) { + + // Hack: we disable the current check during motor calibration because + // it tends to briefly overshoot when the motor moves to align flux with I_alpha + if (Inorm_sq > SQ(Itrip) && (axis_->current_state_ != Axis::AXIS_STATE_MOTOR_CALIBRATION)) { disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION); } } else if (is_armed_) { diff --git a/Firmware/MotorControl/open_loop_controller.cpp b/Firmware/MotorControl/open_loop_controller.cpp index 3fe9dc24..16d2d5db 100644 --- a/Firmware/MotorControl/open_loop_controller.cpp +++ b/Firmware/MotorControl/open_loop_controller.cpp @@ -5,7 +5,7 @@ void OpenLoopController::update(uint32_t timestamp) { auto [prev_Id, prev_Iq] = Idq_setpoint_.get_previous().value_or(float2D{0.0f, 0.0f}); auto [prev_Vd, prev_Vq] = Vdq_setpoint_.get_previous().value_or(float2D{0.0f, 0.0f}); - float phase = phase_.get_previous().value_or(0.0f); + float phase = phase_.get_previous().value_or(initial_phase_); float phase_vel = phase_vel_.get_previous().value_or(0.0f); (void)prev_Iq; // unused diff --git a/Firmware/MotorControl/open_loop_controller.hpp b/Firmware/MotorControl/open_loop_controller.hpp index 82356a23..54371bd3 100644 --- a/Firmware/MotorControl/open_loop_controller.hpp +++ b/Firmware/MotorControl/open_loop_controller.hpp @@ -18,6 +18,7 @@ public: float target_vel_ = 0.0f; float target_current_ = 0.0f; float target_voltage_ = 0.0f; + float initial_phase_ = 0.0f; // State/Outputs uint32_t timestamp_ = 0; From 69897ece9c05521bd7627fed47753c0167813b43 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 11 Nov 2020 21:12:30 +0100 Subject: [PATCH 083/124] improve debug instrumentation - add ERROR output on GPIO - add exported get_gpio_states() function - add task timer for DC calib ADC wait time - fix oscilloscope --- Firmware/Board/v3/Inc/board.h | 2 ++ Firmware/Board/v3/board.cpp | 4 +++- Firmware/MotorControl/main.cpp | 27 ++++++++++++++++++++++++++ Firmware/MotorControl/motor.cpp | 2 +- Firmware/MotorControl/odrive_main.h | 10 ++++++++-- Firmware/MotorControl/oscilloscope.cpp | 22 ++++++++++----------- Firmware/MotorControl/oscilloscope.hpp | 6 ++++-- Firmware/odrive-interface.yaml | 15 ++++++++++---- tools/odrive/enums.py | 1 + 9 files changed, 67 insertions(+), 22 deletions(-) diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h index 3443b90c..4820b93b 100644 --- a/Firmware/Board/v3/Inc/board.h +++ b/Firmware/Board/v3/Inc/board.h @@ -41,6 +41,8 @@ #define DEFAULT_BRAKE_RESISTANCE (0.47f) // [ohm] #endif +#define DEFAULT_ERROR_PIN 0 + #define DEFAULT_GPIO_MODES \ ODriveIntf::GPIO_MODE_DIGITAL, \ ODriveIntf::GPIO_MODE_UART_A, \ diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index dada4c12..bfc56bea 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -513,7 +513,9 @@ void ControlLoop_IRQHandler(void) { // By this time the ADCs for both M0 and M1 should have fired again. But // let's wait for them just to be sure. - while (!(ADC2->SR & ADC_SR_EOC)); + MEASURE_TIME(odrv.task_times_.dc_calib_wait) { + while (!(ADC2->SR & ADC_SR_EOC)); + } if (!fetch_and_reset_adcs(¤t0, ¤t1)) { motors[0].disarm_with_error(Motor::ERROR_BAD_TIMING); diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 49a91efd..c2342ebe 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -151,6 +151,17 @@ void ODrive::enter_dfu_mode() { } } +bool ODrive::any_error() { + return error_ != ODrive::ERROR_NONE + || std::any_of(axes.begin(), axes.end(), [](Axis& axis){ + return axis.error_ != Axis::ERROR_NONE + || axis.motor_.error_ != Motor::ERROR_NONE + || axis.sensorless_estimator_.error_ != SensorlessEstimator::ERROR_NONE + || axis.encoder_.error_ != Encoder::ERROR_NONE + || axis.controller_.error_ != Controller::ERROR_NONE; + }); +} + void ODrive::clear_errors() { for (auto& axis: axes) { axis.motor_.error_ = Motor::ERROR_NONE; @@ -366,6 +377,8 @@ void ODrive::control_loop_cb(uint32_t timestamp) { MEASURE_TIME(axis.task_times_.current_controller_update) axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or async_estimator_ } + + get_gpio(odrv.config_.error_gpio_pin).write(odrv.any_error()); } @@ -407,6 +420,14 @@ uint32_t ODrive::get_dma_status(uint8_t stream_num) { return (is_reset ? 0 : 0x80000000) | ((channel & 0x7) << 2) | (priority & 0x3); } +uint32_t ODrive::get_gpio_states() { + // TODO: get values that were sampled synchronously with the control loop + uint32_t val = 0; + for (size_t i = 0; i < GPIO_COUNT; ++i) { + val |= ((gpios[i].read() ? 1UL : 0UL) << i); + } + return val; +} /** * @brief Main thread started from main(). @@ -580,6 +601,7 @@ extern "C" int main(void) { mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_UP || mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN || mode == ODriveIntf::GPIO_MODE_MECH_BRAKE || + mode == ODriveIntf::GPIO_MODE_STATUS || mode == ODriveIntf::GPIO_MODE_ANALOG_IN) { GPIO_InitStruct.Alternate = 0; } else { @@ -681,6 +703,11 @@ extern "C" int main(void) { GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; } break; + case ODriveIntf::GPIO_MODE_STATUS: { + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + } break; default: { odrv.misconfigured_ = true; continue; diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index a81cdc2a..be41bd3b 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -256,7 +256,7 @@ void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) { * arm() is called. */ bool Motor::disarm(bool* p_was_armed) { - bool was_armed; + bool was_armed = false; CRITICAL_SECTION() { was_armed = is_armed_; diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 88549e74..c88618bf 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -104,6 +104,7 @@ struct BoardConfig_t { float dc_max_positive_current = INFINITY; // Max current [A] the power supply can source float dc_max_negative_current = -0.000001f; // Max current [A] the power supply can sink. You most likely want a non-positive value here. Set to -INFINITY to disable. + uint32_t error_gpio_pin = DEFAULT_ERROR_PIN; PWMMapping_t pwm_mappings[4]; PWMMapping_t analog_mappings[GPIO_COUNT]; }; @@ -112,6 +113,7 @@ struct TaskTimes { TaskTimer sampling; TaskTimer control_loop_misc; TaskTimer control_loop_checks; + TaskTimer dc_calib_wait; }; @@ -169,6 +171,7 @@ public: void erase_configuration() override; void reboot() override { NVIC_SystemReset(); } void enter_dfu_mode() override; + bool any_error(); void clear_errors() override; float get_adc_voltage(uint32_t gpio) override { @@ -189,6 +192,7 @@ public: uint32_t get_interrupt_status(int32_t irqn); uint32_t get_dma_status(uint8_t stream_num); + uint32_t get_gpio_states(); void disarm_with_error(Error error); Error error_ = ERROR_NONE; @@ -230,10 +234,12 @@ public: bool& brake_resistor_saturated_ = ::brake_resistor_saturated; // TODO: make this the actual variable SystemStats_t system_stats_; + + // Edit these to suit your capture needs Oscilloscope oscilloscope_{ - &axes[0].motor_.current_control_.v_current_control_integral_d_, // trigger_src + nullptr, // trigger_src 0.5f, // trigger_threshold - nullptr // &axes[0].motor_.current_control_.Ialpha_measured_ // data_src TODO: change data type + nullptr // data_src TODO: change data type }; BoardConfig_t config_; diff --git a/Firmware/MotorControl/oscilloscope.cpp b/Firmware/MotorControl/oscilloscope.cpp index 05d2038f..21320fd6 100644 --- a/Firmware/MotorControl/oscilloscope.cpp +++ b/Firmware/MotorControl/oscilloscope.cpp @@ -5,25 +5,23 @@ #define OSCILLOSCOPE_SIZE 4096 void Oscilloscope::update() { - // Edit these to suit your capture needs float trigger_data = trigger_src_ ? *trigger_src_ : 0.0f; float trigger_threshold = trigger_threshold_; - float sample_data = data_src_ ? *data_src_ : 0.0f; + float sample_data = data_src_ ? **data_src_ : 0.0f; - static bool ready = false; - static bool capturing = false; if (trigger_data < trigger_threshold) { - ready = true; + ready_ = true; } - if (ready && trigger_data >= trigger_threshold) { - capturing = true; - ready = false; + if (ready_ && trigger_data >= trigger_threshold) { + capturing_ = true; + ready_ = false; } - if (capturing) { - data_[pos_] = sample_data; - if (++pos_ >= OSCILLOSCOPE_SIZE) { + if (capturing_) { + if (pos_ < OSCILLOSCOPE_SIZE) { + data_[pos_++] = sample_data; + } else { pos_ = 0; - capturing = false; + capturing_ = false; } } } diff --git a/Firmware/MotorControl/oscilloscope.hpp b/Firmware/MotorControl/oscilloscope.hpp index b1a5e016..df6dcd2a 100644 --- a/Firmware/MotorControl/oscilloscope.hpp +++ b/Firmware/MotorControl/oscilloscope.hpp @@ -8,7 +8,7 @@ class Oscilloscope : public ODriveIntf::OscilloscopeIntf { public: - Oscilloscope(float* trigger_src, float trigger_threshold, float* data_src) + Oscilloscope(float* trigger_src, float trigger_threshold, float** data_src) : trigger_src_(trigger_src), trigger_threshold_(trigger_threshold), data_src_(data_src) {} float get_val(uint32_t index) override { @@ -20,10 +20,12 @@ public: const uint32_t size_ = OSCILLOSCOPE_SIZE; const float* trigger_src_; const float trigger_threshold_; - const float* data_src_; + float* const * data_src_; float data_[OSCILLOSCOPE_SIZE] = {0}; size_t pos_ = 0; + bool ready_ = false; + bool capturing_ = false; }; #endif // __OSCILLOSCOPE_HPP \ No newline at end of file diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index f4ba60b5..bc982ceb 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -122,6 +122,7 @@ interfaces: sampling: TaskTimer control_loop_misc: TaskTimer control_loop_checks: TaskTimer + dc_calib_wait: TaskTimer system_stats: c_is_class: False attributes: @@ -311,10 +312,12 @@ interfaces: brief: Max current the power supply can sink. doc: You most likely want a non-positive value here. Set to -INFINITY to disable. - gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} - gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} - gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} - gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + error_gpio_pin: {type: uint32} + + gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} + gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} + gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} + gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} user_config_loaded: readonly uint32 @@ -374,6 +377,9 @@ interfaces: bits 1:0: priority (3 is highest priority) 0xffffffff if the specified number is not a valid DMA stream number. doc: Returns information about the specified DMA stream. + get_gpio_states: + out: {status: {type: uint32}} + doc: Returns the logic states of all GPIOs. Bit i represents the state of GPIOi. clear_errors: doc: Clear all the errors of this device including all contained submodules. @@ -1122,6 +1128,7 @@ valuetypes: Enc1: {doc: The pin is used by quadrature encoder 1.} Enc2: {doc: This mode is not supported on ODrive v3.x.} MechBrake: {doc: This is to support external mechanical brakes.} + Status: {doc: The pin is used for status output (see `config.error_gpio_pin`)} ODrive.Can.Protocol: values: {Simple: } diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 92620a0a..79649de8 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -19,6 +19,7 @@ GPIO_MODE_ENC0 = 11 GPIO_MODE_ENC1 = 12 GPIO_MODE_ENC2 = 13 GPIO_MODE_MECH_BRAKE = 14 +GPIO_MODE_STATUS = 15 # ODrive.Can.Protocol PROTOCOL_SIMPLE = 0 From 6f99ce478eb6f8f25550d55b096ac906f5540469 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 13 Nov 2020 10:25:50 +0100 Subject: [PATCH 084/124] use rtos signals in wait_for_control_iteration --- Firmware/MotorControl/axis.cpp | 10 ++++++---- Firmware/MotorControl/axis.hpp | 2 +- Firmware/MotorControl/main.cpp | 7 +++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 8971e415..2a1d5e57 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -104,10 +104,12 @@ void Axis::start_thread() { * @brief Blocks until at least one complete control loop has been executed. */ bool Axis::wait_for_control_iteration() { - uint16_t control_iteration_num = odrv.n_evt_control_loop_; - while (odrv.n_evt_control_loop_ == control_iteration_num) { - osDelay(1); - } + osSignalWait(0x0001, osWaitForever); // this might return instantly + osSignalWait(0x0001, osWaitForever); // this might be triggered at the + // end of a control loop iteration + // which was started before we entered + // this function + osSignalWait(0x0001, osWaitForever); return true; } diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index e9477e0b..50d506d3 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -172,7 +172,7 @@ public: MechanicalBrake& mechanical_brake_; TaskTimes task_times_; - osThreadId thread_id_; + osThreadId thread_id_ = 0; const uint32_t stack_size_ = 2048; // Bytes volatile bool thread_id_valid_ = false; diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index c2342ebe..3daae2d5 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -378,6 +378,13 @@ void ODrive::control_loop_cb(uint32_t timestamp) { axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or async_estimator_ } + // Tell the axis threads that the control loop has finished + for (auto& axis: axes) { + if (axis.thread_id_) { + osSignalSet(axis.thread_id_, 0x0001); + } + } + get_gpio(odrv.config_.error_gpio_pin).write(odrv.any_error()); } From 8133d6d8aa8cf2517cc0e132575351ec8034fd5b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 13 Nov 2020 10:27:44 +0100 Subject: [PATCH 085/124] [odrivetool] make matplotlib optional --- tools/odrive/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 0a6ae81e..ca64c758 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -7,7 +7,6 @@ import platform import subprocess import os import numpy as np -import matplotlib.pyplot as plt from fibre.utils import Event import odrive.enums from odrive.enums import * @@ -49,6 +48,7 @@ def calculate_thermistor_coeffs(degree, Rload, R_25, Beta, Tmin, Tmax, plot = Fa fit_temps = p1(V) if plot: + import matplotlib.pyplot as plt print(fit) plt.plot(V, temps, label='actual') plt.plot(V, fit_temps, label='fit') @@ -547,6 +547,8 @@ def dump_dma(odrv): "*" if (status & 0x80000000) else " ")) def dump_timing(odrv, n_samples=100, path='/tmp/timings.png'): + import matplotlib.pyplot as plt + timings = [] for attr in dir(odrv.task_times): @@ -574,7 +576,7 @@ def dump_timing(odrv, n_samples=100, path='/tmp/timings.png'): plt.rcParams['figure.figsize'] = 21, 9 plt.figure() - plt.grid('both') + plt.grid(True) plt.barh( [-i for i in range(len(timings))], # y positions [np.mean(lengths) for name, obj, start_times, lengths in timings], # lengths From 9d37b7516ddb0732809763f69c0dcb6a50c29b4a Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 13 Nov 2020 18:41:58 +0100 Subject: [PATCH 086/124] add DEFAULT_MIN_DC_VOLTAGE define --- Firmware/Board/v3/Inc/board.h | 1 + Firmware/MotorControl/odrive_main.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h index 4820b93b..a6fbab36 100644 --- a/Firmware/Board/v3/Inc/board.h +++ b/Firmware/Board/v3/Inc/board.h @@ -42,6 +42,7 @@ #endif #define DEFAULT_ERROR_PIN 0 +#define DEFAULT_MIN_DC_VOLTAGE 8.0f #define DEFAULT_GPIO_MODES \ ODriveIntf::GPIO_MODE_DIGITAL, \ diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index c88618bf..20074a93 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -75,7 +75,7 @@ struct BoardConfig_t { float max_regen_current = 0.0f; float brake_resistance = DEFAULT_BRAKE_RESISTANCE; bool enable_brake_resistor = false; - float dc_bus_undervoltage_trip_level = 8.0f; // Date: Mon, 16 Nov 2020 16:30:58 +0100 Subject: [PATCH 087/124] abort save_configuration when armed --- Firmware/MotorControl/main.cpp | 30 +++++++++++++++++------------ Firmware/MotorControl/odrive_main.h | 2 +- Firmware/odrive-interface.yaml | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 3daae2d5..d8bc9849 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -107,19 +107,25 @@ static bool config_apply_all() { return success; } -void ODrive::save_configuration(void) { - size_t config_size = 0; - bool success = config_manager.prepare_store() - && config_write_all() - && config_manager.start_store(&config_size) - && config_write_all() - && config_manager.finish_store(); - if (success) { - user_config_loaded_ = config_size; - } else { - printf("saving configuration failed\r\n"); - osDelay(5); +bool ODrive::save_configuration(void) { + bool success = false; + + CRITICAL_SECTION() { + bool any_armed = std::any_of(axes.begin(), axes.end(), + [](auto& axis){ return axis.motor_.is_armed_; }); + if (any_armed) { + return false; + } + + size_t config_size = 0; + success = config_manager.prepare_store() + && config_write_all() + && config_manager.start_store(&config_size) + && config_write_all() + && config_manager.finish_store(); } + + return success; } void ODrive::erase_configuration(void) { diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 20074a93..17b5aa01 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -167,7 +167,7 @@ static Stm32Gpio get_gpio(size_t gpio_num) { // general system functions defined in main.cpp class ODrive : public ODriveIntf { public: - void save_configuration() override; + bool save_configuration() override; void erase_configuration() override; void reboot() override { NVIC_SystemReset(); } void enter_dfu_mode() override; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index bc982ceb..372be420 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -351,7 +351,7 @@ interfaces: functions: test_function: {in: {delta: int32}, out: {cnt: int32}} get_adc_voltage: {in: {gpio: uint32}, out: {voltage: float32}, doc: Reads the ADC voltage of the specified GPIO. The GPIO should be in `GPIO_MODE_ANALOG_IN`.} - save_configuration: + save_configuration: {out: {success: bool}} erase_configuration: reboot: enter_dfu_mode: From 60b433700fddfdc38f830b6529fb3bbed5905543 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Nov 2020 17:15:00 +0100 Subject: [PATCH 088/124] fix uninitialized warning after CRITICAL_SECTION --- Firmware/Drivers/STM32/stm32_system.h | 5 +++++ Firmware/MotorControl/main.cpp | 2 +- Firmware/MotorControl/motor.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Firmware/Drivers/STM32/stm32_system.h b/Firmware/Drivers/STM32/stm32_system.h index 5a95e590..e065cb24 100644 --- a/Firmware/Drivers/STM32/stm32_system.h +++ b/Firmware/Drivers/STM32/stm32_system.h @@ -50,13 +50,18 @@ struct CriticalSectionContext { CriticalSectionContext(const CriticalSectionContext&&) = delete; void operator=(const CriticalSectionContext&) = delete; void operator=(const CriticalSectionContext&&) = delete; + operator bool() { return true; }; CriticalSectionContext() : mask_(cpu_enter_critical()) {} ~CriticalSectionContext() { cpu_exit_critical(mask_); } uint32_t mask_; bool exit_ = false; }; +#ifdef __clang__ #define CRITICAL_SECTION() for (CriticalSectionContext __critical_section_context; !__critical_section_context.exit_; __critical_section_context.exit_ = true) +#else +#define CRITICAL_SECTION() if (CriticalSectionContext __critical_section_context{}) +#endif #endif diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index d8bc9849..67e2c46d 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -108,7 +108,7 @@ static bool config_apply_all() { } bool ODrive::save_configuration(void) { - bool success = false; + bool success; CRITICAL_SECTION() { bool any_armed = std::any_of(axes.begin(), axes.end(), diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index be41bd3b..a81cdc2a 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -256,7 +256,7 @@ void Motor::apply_pwm_timings(uint16_t timings[3], bool tentative) { * arm() is called. */ bool Motor::disarm(bool* p_was_armed) { - bool was_armed = false; + bool was_armed; CRITICAL_SECTION() { was_armed = is_armed_; From b265a3388f668a0d53e36d4953382ace86fab932 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Nov 2020 16:20:52 +0100 Subject: [PATCH 089/124] ignore current readings when MOE==0 --- Firmware/Board/v3/board.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index bfc56bea..d9f768a8 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -506,6 +506,18 @@ void ControlLoop_IRQHandler(void) { motors[1].disarm_with_error(Motor::ERROR_BAD_TIMING); } + // If the motor FETs are not switching then we can't measure the current + // because for this we need the low side FET to conduct. + // So for now we guess the current to be 0 (this is not correct shortly after + // disarming and when the motor spins fast in idle). Passing an invalid + // current reading would create problems with starting FOC. + if (!(TIM1->BDTR & TIM_BDTR_MOE_Msk)) { + current0 = {0.0f, 0.0f}; + } + if (!(TIM8->BDTR & TIM_BDTR_MOE_Msk)) { + current1 = {0.0f, 0.0f}; + } + motors[0].current_meas_cb(timestamp - TIM1_INIT_COUNT, current0); motors[1].current_meas_cb(timestamp, current1); From b32a455dda4fde2936e0534fb70132d869733e1c Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Nov 2020 18:14:16 +0100 Subject: [PATCH 090/124] fix HWIL tests --- Firmware/odrive-interface.yaml | 1 + tools/odrive/tests/closed_loop_test.py | 3 +++ tools/odrive/tests/integration_test.py | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 372be420..fee4f9eb 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -258,6 +258,7 @@ interfaces: Setting this to False even though a brake resistor is connected is harmless. Setting this to True even though no brake resistor is connected can break the power supply. + Changes to this value require a reboot to take effect. dc_bus_undervoltage_trip_level: type: float32 diff --git a/tools/odrive/tests/closed_loop_test.py b/tools/odrive/tests/closed_loop_test.py index 26663fd0..9b64d1bf 100644 --- a/tools/odrive/tests/closed_loop_test.py +++ b/tools/odrive/tests/closed_loop_test.py @@ -40,6 +40,9 @@ class TestClosedLoopControlBase(): # Set brake resistor settings axis_ctx.parent.handle.config.brake_resistance = float(axis_ctx.parent.yaml['brake-resistance']) + # The docs say this requires a reboot but here's a small secret: + # Since the brake resistor is also started in clear_errors() this + # circumvents the need for a reboot. axis_ctx.parent.handle.config.enable_brake_resistor = True # Set calibration settings diff --git a/tools/odrive/tests/integration_test.py b/tools/odrive/tests/integration_test.py index a142e15a..e3d76007 100644 --- a/tools/odrive/tests/integration_test.py +++ b/tools/odrive/tests/integration_test.py @@ -106,6 +106,8 @@ class TestSimpleCANClosedLoop(): # Make sure there are no funny configurations active logger.debug('Setting up clean configuration...') axis_ctx.parent.erase_config_and_reboot() + axis_ctx.parent.handle.config.enable_brake_resistor = True + axis_ctx.parent.save_config_and_reboot() # run calibration axis_ctx.handle.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE @@ -162,7 +164,7 @@ class TestSimpleCANClosedLoop(): test_assert_eq(axis_ctx.handle.config.can.node_id, node_id+20) # Reset node ID to default value - asyncio.run(command(canbus.handle, node_id+20, extended_id, 'set_node_id', node_id=node_id)) + command(canbus.handle, node_id+20, extended_id, 'set_node_id', node_id=node_id) fence() test_assert_eq(axis_ctx.handle.config.can.node_id, node_id) From 18a391c092a2dfcd190fe25077f8b3c119427ed2 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Nov 2020 18:41:28 +0100 Subject: [PATCH 091/124] Reduce kI in motor calibration This prevents current overshoot if the motor snaps to align with I_alpha. Consequently we can reenable current limit checks during motor calibration. --- Firmware/MotorControl/motor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index a81cdc2a..ca9753c3 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -63,7 +63,7 @@ struct ResistanceMeasurementControlLaw : AlphaBetaFrameController { return test_voltage_ / target_current_; } - const float kI = 10.0f; // [(V/s)/A] + const float kI = 1.0f; // [(V/s)/A] float max_voltage_ = 0.0f; float actual_current_ = 0.0f; float target_current_ = 0.0f; @@ -614,7 +614,7 @@ void Motor::current_meas_cb(uint32_t timestamp, std::optional current // Hack: we disable the current check during motor calibration because // it tends to briefly overshoot when the motor moves to align flux with I_alpha - if (Inorm_sq > SQ(Itrip) && (axis_->current_state_ != Axis::AXIS_STATE_MOTOR_CALIBRATION)) { + if (Inorm_sq > SQ(Itrip)) { disarm_with_error(ERROR_CURRENT_LIMIT_VIOLATION); } } else if (is_armed_) { From c32cbec58cca0aa92dc3f7d09402d1f162cb36a6 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Nov 2020 20:58:26 +0100 Subject: [PATCH 092/124] rename some of the new functions and variables --- ...async_estimator.cpp => acim_estimator.cpp} | 10 +++---- ...async_estimator.hpp => acim_estimator.hpp} | 8 +++--- Firmware/MotorControl/axis.cpp | 14 +++++----- Firmware/MotorControl/axis.hpp | 6 ++--- Firmware/MotorControl/component.hpp | 24 ++++++++--------- Firmware/MotorControl/controller.cpp | 18 ++++++------- Firmware/MotorControl/encoder.cpp | 12 ++++----- Firmware/MotorControl/foc.cpp | 8 +++--- Firmware/MotorControl/main.cpp | 10 +++---- Firmware/MotorControl/motor.cpp | 16 ++++++------ .../MotorControl/open_loop_controller.cpp | 10 +++---- .../MotorControl/sensorless_estimator.cpp | 2 +- Firmware/Tupfile.lua | 2 +- Firmware/communication/ascii_protocol.cpp | 4 +-- Firmware/communication/can_simple.cpp | 6 ++--- Firmware/odrive-interface.yaml | 26 +++++++++---------- 16 files changed, 88 insertions(+), 88 deletions(-) rename Firmware/MotorControl/{async_estimator.cpp => acim_estimator.cpp} (86%) rename Firmware/MotorControl/{async_estimator.hpp => acim_estimator.hpp} (85%) diff --git a/Firmware/MotorControl/async_estimator.cpp b/Firmware/MotorControl/acim_estimator.cpp similarity index 86% rename from Firmware/MotorControl/async_estimator.cpp rename to Firmware/MotorControl/acim_estimator.cpp index e2edefa4..55cc1be8 100644 --- a/Firmware/MotorControl/async_estimator.cpp +++ b/Firmware/MotorControl/acim_estimator.cpp @@ -1,11 +1,11 @@ -#include "async_estimator.hpp" +#include "acim_estimator.hpp" #include -void AsyncEstimator::update(uint32_t timestamp) { - std::optional rotor_phase = rotor_phase_src_.get_current(); - std::optional rotor_phase_vel = rotor_phase_vel_src_.get_current(); - std::optional idq = idq_src_.get_current(); +void AcimEstimator::update(uint32_t timestamp) { + std::optional rotor_phase = rotor_phase_src_.present(); + std::optional rotor_phase_vel = rotor_phase_vel_src_.present(); + std::optional idq = idq_src_.present(); if (!rotor_phase.has_value() || !rotor_phase_vel.has_value() || !idq.has_value()) { active_ = false; diff --git a/Firmware/MotorControl/async_estimator.hpp b/Firmware/MotorControl/acim_estimator.hpp similarity index 85% rename from Firmware/MotorControl/async_estimator.hpp rename to Firmware/MotorControl/acim_estimator.hpp index 3505ce53..242b5d86 100644 --- a/Firmware/MotorControl/async_estimator.hpp +++ b/Firmware/MotorControl/acim_estimator.hpp @@ -1,11 +1,11 @@ -#ifndef __ASYNC_ESTIMATOR_HPP -#define __ASYNC_ESTIMATOR_HPP +#ifndef __ACIM_ESTIMATOR_HPP +#define __ACIM_ESTIMATOR_HPP #include #include #include -class AsyncEstimator : public ComponentBase { +class AcimEstimator : public ComponentBase { public: struct Config_t { float slip_velocity = 14.706f; // [rad/s electrical] = 1/rotor_tau @@ -33,4 +33,4 @@ public: OutputPort stator_phase_ = 0.0f; // [rad] rotor flux phase angle estimate }; -#endif // __ASYNC_ESTIMATOR_HPP \ No newline at end of file +#endif // __ACIM_ESTIMATOR_HPP \ No newline at end of file diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 2a1d5e57..6c8e6663 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -204,11 +204,11 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme motor_.current_control_.Vdq_setpoint_src_.connect_to(&open_loop_controller_.Vdq_setpoint_); motor_.current_control_.phase_src_.connect_to(&open_loop_controller_.phase_); - async_estimator_.rotor_phase_src_.connect_to(&open_loop_controller_.phase_); + acim_estimator_.rotor_phase_src_.connect_to(&open_loop_controller_.phase_); motor_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); motor_.current_control_.phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); - async_estimator_.rotor_phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); + acim_estimator_.rotor_phase_vel_src_.connect_to(&open_loop_controller_.phase_vel_); } wait_for_control_iteration(); @@ -219,8 +219,8 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config, bool remain_arme float dir = lockin_config.vel >= 0.0f ? 1.0f : -1.0f; while ((requested_state_ == AXIS_STATE_UNDEFINED) && motor_.is_armed_) { - bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_.get_any().value_or(0.0f) - lockin_config.vel) <= std::numeric_limits::epsilon(); - bool reached_target_dist = open_loop_controller_.total_distance_.get_any().value_or(0.0f) * dir >= lockin_config.finish_distance * dir; + bool reached_target_vel = std::abs(open_loop_controller_.phase_vel_.any().value_or(0.0f) - lockin_config.vel) <= std::numeric_limits::epsilon(); + bool reached_target_dist = open_loop_controller_.total_distance_.any().value_or(0.0f) * dir >= lockin_config.finish_distance * dir; // Check if terminal condition is reached bool terminal_condition = (reached_target_vel && lockin_config.finish_on_vel) @@ -286,7 +286,7 @@ bool Axis::start_closed_loop_control() { if (controller_.config_.control_mode >= Controller::CONTROL_MODE_POSITION_CONTROL) { std::optional pos_init = (controller_.config_.circular_setpoints ? controller_.pos_estimate_circular_src_ : - controller_.pos_estimate_linear_src_).get_any(); + controller_.pos_estimate_linear_src_).any(); if (!pos_init.has_value()) { return false; } else { @@ -308,12 +308,12 @@ bool Axis::start_closed_loop_control() { OutputPort* phase_src = sensorless_mode ? &sensorless_estimator_.phase_ : &encoder_.phase_; motor_.current_control_.phase_src_.connect_to(phase_src); - async_estimator_.rotor_phase_src_.connect_to(phase_src); + acim_estimator_.rotor_phase_src_.connect_to(phase_src); OutputPort* phase_vel_src = sensorless_mode ? &sensorless_estimator_.phase_vel_ : &encoder_.phase_vel_; motor_.phase_vel_src_.connect_to(phase_vel_src); motor_.current_control_.phase_vel_src_.connect_to(phase_vel_src); - async_estimator_.rotor_phase_vel_src_.connect_to(phase_vel_src); + acim_estimator_.rotor_phase_vel_src_.connect_to(phase_vel_src); if (sensorless_mode) { // Make the final velocity of the loĉk-in spin the setpoint of the diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 50d506d3..c79c91dd 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -4,7 +4,7 @@ class Axis; #include "encoder.hpp" -#include "async_estimator.hpp" +#include "acim_estimator.hpp" #include "sensorless_estimator.hpp" #include "controller.hpp" #include "open_loop_controller.hpp" @@ -39,7 +39,7 @@ public: TaskTimer can_heartbeat; TaskTimer controller_update; TaskTimer open_loop_controller_update; - TaskTimer async_estimator_update; + TaskTimer acim_estimator_update; TaskTimer motor_update; TaskTimer current_controller_update; TaskTimer dc_calib; @@ -161,7 +161,7 @@ public: Config_t config_; Encoder& encoder_; - AsyncEstimator async_estimator_; + AcimEstimator acim_estimator_; SensorlessEstimator& sensorless_estimator_; Controller& controller_; OpenLoopController open_loop_controller_; diff --git a/Firmware/MotorControl/component.hpp b/Firmware/MotorControl/component.hpp index aa156e1c..4569de99 100644 --- a/Firmware/MotorControl/component.hpp +++ b/Firmware/MotorControl/component.hpp @@ -40,8 +40,8 @@ public: /** * @brief Initializes the output port with the specified value. * - * An initialization value is required for get_any() to work properly. - * get_current() and get_previous() cannot be used to fetch the + * An initialization value is required for any() to work properly. + * present() and previous() cannot be used to fetch the * initialization value. */ OutputPort(T val) : content_(val) {} @@ -60,7 +60,7 @@ public: * of this class. */ void reset() { - // This will eventually overflow to 0 so get_current() could + // This will eventually overflow to 0 so present() could // theoretically return a very old value however it is very likely that // the motor will be long disarmed by then. age_++; @@ -70,7 +70,7 @@ public: * @brief Returns the value from this control loop iteration or std::nullopt * if the value was not yet set during this control loop iteration. */ - std::optional get_current() { + std::optional present() { if (age_ == 0) { return content_; } else { @@ -85,7 +85,7 @@ public: * overwritten during this control loop iteration then this function returns * std::nullopt. */ - std::optional get_previous() { + std::optional previous() { if (age_ == 1) { return content_; } else { @@ -99,7 +99,7 @@ public: * * This function is thread-safe if load/store operations of T are atomic. */ - std::optional get_any() { + std::optional any() { return content_; } @@ -134,10 +134,10 @@ public: content_ = (OutputPort*)nullptr; } - std::optional get_current() { + std::optional present() { if (content_.index() == 2) { OutputPort* ptr = std::get<2>(content_); - return ptr ? ptr->get_current() : std::nullopt; + return ptr ? ptr->present() : std::nullopt; } else if (content_.index() == 1) { T* ptr = std::get<1>(content_); return ptr ? std::make_optional(*ptr) : std::nullopt; @@ -150,10 +150,10 @@ public: // ok for this input port to fetch the value from the last iteration. // This would provide a general way to resolve same-iteration data path cycles. - //std::optional get_previous() { + //std::optional previous() { // if (content_.index() == 2) { // OutputPort* ptr = std::get<2>(content_); - // return ptr ? ptr->get_previous() : std::nullopt; + // return ptr ? ptr->previous() : std::nullopt; // } else if (content_.index() == 1) { // T* ptr = std::get<1>(content_); // return ptr ? std::make_optional(*ptr) : std::nullopt; @@ -162,10 +162,10 @@ public: // } //} - std::optional get_any() { + std::optional any() { if (content_.index() == 2) { OutputPort* ptr = std::get<2>(content_); - return ptr ? ptr->get_any() : std::nullopt; + return ptr ? ptr->any() : std::nullopt; } else if (content_.index() == 1) { T* ptr = std::get<1>(content_); return ptr ? std::make_optional(*ptr) : std::nullopt; diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 51c64983..9c4e97ca 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -97,13 +97,13 @@ static float limitVel(const float vel_limit, const float vel_estimate, const flo } bool Controller::update() { - std::optional pos_estimate_linear = pos_estimate_linear_src_.get_current(); - std::optional pos_estimate_circular = pos_estimate_circular_src_.get_current(); - std::optional pos_wrap = pos_wrap_src_.get_current(); - std::optional vel_estimate = vel_estimate_src_.get_current(); + std::optional pos_estimate_linear = pos_estimate_linear_src_.present(); + std::optional pos_estimate_circular = pos_estimate_circular_src_.present(); + std::optional pos_wrap = pos_wrap_src_.present(); + std::optional vel_estimate = vel_estimate_src_.present(); - std::optional anticogging_pos_estimate = axis_->encoder_.pos_estimate_.get_current(); - std::optional anticogging_vel_estimate = axis_->encoder_.vel_estimate_.get_current(); + std::optional anticogging_pos_estimate = axis_->encoder_.pos_estimate_.present(); + std::optional anticogging_vel_estimate = axis_->encoder_.vel_estimate_.present(); if (config_.anticogging.calib_anticogging) { if (!anticogging_pos_estimate.has_value() || !anticogging_vel_estimate.has_value()) { @@ -156,8 +156,8 @@ bool Controller::update() { } break; case INPUT_MODE_MIRROR: { if (config_.axis_to_mirror < AXIS_COUNT) { - std::optional other_pos = axes[config_.axis_to_mirror].encoder_.pos_estimate_.get_current(); - std::optional other_vel = axes[config_.axis_to_mirror].encoder_.vel_estimate_.get_current(); + std::optional other_pos = axes[config_.axis_to_mirror].encoder_.pos_estimate_.present(); + std::optional other_vel = axes[config_.axis_to_mirror].encoder_.vel_estimate_.present(); if (!other_pos.has_value() || !other_vel.has_value()) { set_error(ERROR_INVALID_ESTIMATE); @@ -262,7 +262,7 @@ bool Controller::update() { float vel_gain = config_.vel_gain; float vel_integrator_gain = config_.vel_integrator_gain; if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - float effective_flux = axis_->async_estimator_.rotor_flux_; + float effective_flux = axis_->acim_estimator_.rotor_flux_; float minflux = axis_->motor_.config_.acim_gain_min_flux; if (std::abs(effective_flux) < minflux) effective_flux = std::copysignf(minflux, effective_flux); diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 7132100e..358dca72 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -235,11 +235,11 @@ bool Encoder::run_offset_calibration() { axis_->motor_.current_control_.Vdq_setpoint_src_.connect_to(&axis_->open_loop_controller_.Vdq_setpoint_); axis_->motor_.current_control_.phase_src_.connect_to(&axis_->open_loop_controller_.phase_); - axis_->async_estimator_.rotor_phase_src_.connect_to(&axis_->open_loop_controller_.phase_); + axis_->acim_estimator_.rotor_phase_src_.connect_to(&axis_->open_loop_controller_.phase_); axis_->motor_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); axis_->motor_.current_control_.phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); - axis_->async_estimator_.rotor_phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); + axis_->acim_estimator_.rotor_phase_vel_src_.connect_to(&axis_->open_loop_controller_.phase_vel_); } axis_->wait_for_control_iteration(); @@ -269,7 +269,7 @@ bool Encoder::run_offset_calibration() { // scan forward while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { - bool reached_target_dist = axis_->open_loop_controller_.total_distance_.get_any().value_or(-INFINITY) >= config_.calib_scan_distance; + bool reached_target_dist = axis_->open_loop_controller_.total_distance_.any().value_or(-INFINITY) >= config_.calib_scan_distance; if (reached_target_dist) { break; } @@ -308,7 +308,7 @@ bool Encoder::run_offset_calibration() { // scan backwards while ((axis_->requested_state_ == Axis::AXIS_STATE_UNDEFINED) && axis_->motor_.is_armed_) { - bool reached_target_dist = axis_->open_loop_controller_.total_distance_.get_any().value_or(INFINITY) <= 0.0f; + bool reached_target_dist = axis_->open_loop_controller_.total_distance_.any().value_or(INFINITY) <= 0.0f; if (reached_target_dist) { break; } @@ -590,7 +590,7 @@ bool Encoder::update() { // TODO: we should strictly require that this value is from the previous iteration // to avoid spinout scenarios. However that requires a proper way to reset // the encoder from error states. - float pos_circular = pos_circular_.get_any().value_or(0.0f); + float pos_circular = pos_circular_.any().value_or(0.0f); pos_circular += wrap_pm((pos_cpr_counts_ - pos_cpr_counts_last) / (float)config_.cpr, 1.0f); pos_circular = fmodf_pos(pos_circular, axis_->controller_.config_.circular_setpoint_range); pos_circular_ = pos_circular; @@ -622,7 +622,7 @@ bool Encoder::update() { if (is_ready_) { phase_ = wrap_pm_pi(ph) * config_.direction; - phase_vel_ = (2*M_PI) * *vel_estimate_.get_current() * axis_->motor_.config_.pole_pairs * config_.direction; + phase_vel_ = (2*M_PI) * *vel_estimate_.present() * axis_->motor_.config_.pole_pairs * config_.direction; } return true; diff --git a/Firmware/MotorControl/foc.cpp b/Firmware/MotorControl/foc.cpp index ae4a05af..219f4076 100644 --- a/Firmware/MotorControl/foc.cpp +++ b/Firmware/MotorControl/foc.cpp @@ -183,9 +183,9 @@ void FieldOrientedController::update(uint32_t timestamp) { CRITICAL_SECTION() { ctrl_timestamp_ = timestamp; enable_current_control_ = enable_current_control_src_; - Idq_setpoint_ = Idq_setpoint_src_.get_current(); - Vdq_setpoint_ = Vdq_setpoint_src_.get_current(); - phase_ = phase_src_.get_current(); - phase_vel_ = phase_vel_src_.get_current(); + Idq_setpoint_ = Idq_setpoint_src_.present(); + Vdq_setpoint_ = Vdq_setpoint_src_.present(); + phase_ = phase_src_.present(); + phase_vel_ = phase_vel_src_.present(); } } diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 67e2c46d..834ce743 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -306,9 +306,9 @@ void ODrive::control_loop_cb(uint32_t timestamp) { // TODO: maybe we should add a check to output ports that prevents // double-setting the value. for (auto& axis: axes) { - axis.async_estimator_.slip_vel_.reset(); - axis.async_estimator_.stator_phase_vel_.reset(); - axis.async_estimator_.stator_phase_.reset(); + axis.acim_estimator_.slip_vel_.reset(); + axis.acim_estimator_.stator_phase_vel_.reset(); + axis.acim_estimator_.stator_phase_.reset(); axis.controller_.torque_output_.reset(); axis.encoder_.phase_.reset(); axis.encoder_.phase_vel_.reset(); @@ -381,7 +381,7 @@ void ODrive::control_loop_cb(uint32_t timestamp) { axis.motor_.update(timestamp); // uses torque from controller and phase_vel from encoder MEASURE_TIME(axis.task_times_.current_controller_update) - axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or async_estimator_ + axis.motor_.current_control_.update(timestamp); // uses the output of controller_ or open_loop_contoller_ and encoder_ or sensorless_estimator_ or acim_estimator_ } // Tell the axis threads that the control loop has finished @@ -480,7 +480,7 @@ static void rtos_main(void*) { } for(auto& axis: axes){ - axis.async_estimator_.idq_src_.connect_to(&axis.motor_.Idq_setpoint_); + axis.acim_estimator_.idq_src_.connect_to(&axis.motor_.Idq_setpoint_); } // Start PWM and enable adc interrupts/callbacks diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index ca9753c3..6585cccc 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -193,7 +193,7 @@ bool Motor::arm(PhaseControlLaw<3>* control_law) { // Reset controller states, integrators, setpoints, etc. axis_->controller_.reset(); - axis_->async_estimator_.rotor_flux_ = 0.0f; + axis_->acim_estimator_.rotor_flux_ = 0.0f; if (control_law_) { control_law_->reset(); } @@ -373,7 +373,7 @@ float Motor::effective_current_lim() { //Note - for ACIM motors, available torque is allowed to be 0. float Motor::max_available_torque() { if (config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - float max_torque = effective_current_lim_ * config_.torque_constant * axis_->async_estimator_.rotor_flux_; + float max_torque = effective_current_lim_ * config_.torque_constant * axis_->acim_estimator_.rotor_flux_; max_torque = std::clamp(max_torque, 0.0f, config_.torque_lim); return max_torque; } else { @@ -494,19 +494,19 @@ bool Motor::run_calibration() { } void Motor::update(uint32_t timestamp) { - std::optional torque = torque_setpoint_src_.get_current(); + std::optional torque = torque_setpoint_src_.present(); if (!torque.has_value()) { error_ |= ERROR_UNKNOWN_TORQUE; return; } - auto [id, iq] = Idq_setpoint_.get_previous() + auto [id, iq] = Idq_setpoint_.previous() .value_or(float2D{0.0f, 0.0f}); // Id doubles as a state variable // Convert torque to current if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_ACIM) { - iq = *torque / (axis_->motor_.config_.torque_constant * std::max(axis_->async_estimator_.rotor_flux_, config_.acim_gain_min_flux)); + iq = *torque / (axis_->motor_.config_.torque_constant * std::max(axis_->acim_estimator_.rotor_flux_, config_.acim_gain_min_flux)); } else { iq = *torque / axis_->motor_.config_.torque_constant; } @@ -534,13 +534,13 @@ void Motor::update(uint32_t timestamp) { // in this function. // A cleaner fix would be to take the feedforward calculation out of here // and turn it into a separate component. - MEASURE_TIME(axis_->task_times_.async_estimator_update) - axis_->async_estimator_.update(timestamp); + MEASURE_TIME(axis_->task_times_.acim_estimator_update) + axis_->acim_estimator_.update(timestamp); float vd = 0.0f; float vq = 0.0f; - std::optional phase_vel = phase_vel_src_.get_current(); + std::optional phase_vel = phase_vel_src_.present(); if (config_.R_wL_FF_enable) { if (!phase_vel.has_value()) { diff --git a/Firmware/MotorControl/open_loop_controller.cpp b/Firmware/MotorControl/open_loop_controller.cpp index 16d2d5db..7d21df98 100644 --- a/Firmware/MotorControl/open_loop_controller.cpp +++ b/Firmware/MotorControl/open_loop_controller.cpp @@ -3,10 +3,10 @@ #include void OpenLoopController::update(uint32_t timestamp) { - auto [prev_Id, prev_Iq] = Idq_setpoint_.get_previous().value_or(float2D{0.0f, 0.0f}); - auto [prev_Vd, prev_Vq] = Vdq_setpoint_.get_previous().value_or(float2D{0.0f, 0.0f}); - float phase = phase_.get_previous().value_or(initial_phase_); - float phase_vel = phase_vel_.get_previous().value_or(0.0f); + auto [prev_Id, prev_Iq] = Idq_setpoint_.previous().value_or(float2D{0.0f, 0.0f}); + auto [prev_Vd, prev_Vq] = Vdq_setpoint_.previous().value_or(float2D{0.0f, 0.0f}); + float phase = phase_.previous().value_or(initial_phase_); + float phase_vel = phase_vel_.previous().value_or(0.0f); (void)prev_Iq; // unused (void)prev_Vq; // unused @@ -25,6 +25,6 @@ void OpenLoopController::update(uint32_t timestamp) { phase_vel = std::clamp(target_vel_, phase_vel - max_phase_vel_ramp_ * dt, phase_vel + max_phase_vel_ramp_ * dt); phase_vel_ = phase_vel; phase_ = wrap_pm_pi(phase + phase_vel * dt); - total_distance_ = total_distance_.get_previous().value_or(0.0f) + phase_vel * dt; + total_distance_ = total_distance_.previous().value_or(0.0f) + phase_vel * dt; timestamp_ = timestamp; } diff --git a/Firmware/MotorControl/sensorless_estimator.cpp b/Firmware/MotorControl/sensorless_estimator.cpp index c3951056..84f12f55 100644 --- a/Firmware/MotorControl/sensorless_estimator.cpp +++ b/Firmware/MotorControl/sensorless_estimator.cpp @@ -81,7 +81,7 @@ bool SensorlessEstimator::update() { V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha_; V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta_; - float phase_vel = phase_vel_.get_previous().value_or(0.0f); + float phase_vel = phase_vel_.previous().value_or(0.0f); // predict PLL phase with velocity pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * phase_vel); diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 611e174c..90eecd2c 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -200,7 +200,7 @@ sources = { 'MotorControl/thermistor.cpp', 'MotorControl/encoder.cpp', 'MotorControl/endstop.cpp', - 'MotorControl/async_estimator.cpp', + 'MotorControl/acim_estimator.cpp', 'MotorControl/mechanical_brake.cpp', 'MotorControl/controller.cpp', 'MotorControl/foc.cpp', diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 6e62e8a6..578c3f7b 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -284,8 +284,8 @@ void cmd_get_feedback(char * pStr, StreamSink& response_channel, bool use_checks } else { Axis& axis = axes[motor_number]; respond(response_channel, use_checksum, "%f %f", - (double)axis.encoder_.pos_estimate_.get_any().value_or(0.0f), - (double)axis.encoder_.vel_estimate_.get_any().value_or(0.0f)); + (double)axis.encoder_.pos_estimate_.any().value_or(0.0f), + (double)axis.encoder_.vel_estimate_.any().value_or(0.0f)); } } diff --git a/Firmware/communication/can_simple.cpp b/Firmware/communication/can_simple.cpp index 650dd6e1..af897495 100644 --- a/Firmware/communication/can_simple.cpp +++ b/Firmware/communication/can_simple.cpp @@ -173,8 +173,8 @@ int32_t CANSimple::get_encoder_estimates_callback(const Axis& axis) { txmsg.isExt = axis.config_.can.is_extended; txmsg.len = 8; - can_setSignal(txmsg, axis.encoder_.pos_estimate_.get_any().value_or(0.0f), 0, 32, true); - can_setSignal(txmsg, axis.encoder_.vel_estimate_.get_any().value_or(0.0f), 32, 32, true); + can_setSignal(txmsg, axis.encoder_.pos_estimate_.any().value_or(0.0f), 0, 32, true); + can_setSignal(txmsg, axis.encoder_.vel_estimate_.any().value_or(0.0f), 32, 32, true); return odCAN->write(txmsg); } @@ -189,7 +189,7 @@ int32_t CANSimple::get_sensorless_estimates_callback(const Axis& axis) { static_assert(sizeof(float) == sizeof(axis.sensorless_estimator_.pll_pos_)); can_setSignal(txmsg, axis.sensorless_estimator_.pll_pos_, 0, 32, true); - can_setSignal(txmsg, axis.sensorless_estimator_.vel_estimate_.get_any().value_or(0.0f), 32, 32, true); + can_setSignal(txmsg, axis.sensorless_estimator_.vel_estimate_.any().value_or(0.0f), 32, 32, true); return odCAN->write(txmsg); } diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index fee4f9eb..83f294b4 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -506,7 +506,7 @@ interfaces: motor: Motor controller: Controller encoder: Encoder - async_estimator: AsyncEstimator + acim_estimator: AcimEstimator sensorless_estimator: SensorlessEstimator trap_traj: TrapezoidalTrajectory min_endstop: Endstop @@ -522,7 +522,7 @@ interfaces: can_heartbeat: TaskTimer controller_update: TaskTimer open_loop_controller_update: TaskTimer - async_estimator_update: TaskTimer + acim_estimator_update: TaskTimer motor_update: TaskTimer current_controller_update: TaskTimer dc_calib: TaskTimer @@ -811,7 +811,7 @@ interfaces: functions: get_val: {in: {index: uint32}, out: {val: float32}} - ODrive.AsyncEstimator: + ODrive.AcimEstimator: c_is_class: True attributes: rotor_flux: {type: readonly float32, unit: A, doc: estimated magnitude of the rotor flux} @@ -819,7 +819,7 @@ interfaces: type: readonly float32 unit: rad/s doc: estimated slip between physical and electrical angular velocity} - c_getter: slip_vel_.get_any().value_or(0.0f) + c_getter: slip_vel_.any().value_or(0.0f) phase_offset: type: readonly float32 unit: rad @@ -828,12 +828,12 @@ interfaces: type: readonly float32 unit: rad/s doc: calculated setpoint for the electrical velocity} - c_getter: stator_phase_vel_.get_any().value_or(0.0f) + c_getter: stator_phase_vel_.any().value_or(0.0f) stator_phase: type: readonly float32 unit: rad doc: calculated setpoint for the electrical phase} - c_getter: stator_phase_.get_any().value_or(0.0f) + c_getter: stator_phase_.any().value_or(0.0f) config: c_is_class: False attributes: @@ -992,13 +992,13 @@ interfaces: shadow_count: readonly int32 count_in_cpr: readonly int32 interpolation: readonly float32 - phase: {type: readonly float32, c_getter: phase_.get_any().value_or(0.0f)} - pos_estimate: {type: readonly float32, c_getter: pos_estimate_.get_any().value_or(0.0f)} + phase: {type: readonly float32, c_getter: phase_.any().value_or(0.0f)} + pos_estimate: {type: readonly float32, c_getter: pos_estimate_.any().value_or(0.0f)} pos_estimate_counts: readonly float32 pos_cpr_counts: readonly float32 - pos_circular: {type: readonly float32, c_getter: pos_circular_.get_any().value_or(0.0f)} + pos_circular: {type: readonly float32, c_getter: pos_circular_.any().value_or(0.0f)} hall_state: readonly uint8 - vel_estimate: {type: readonly float32, c_getter: vel_estimate_.get_any().value_or(0.0f)} + vel_estimate: {type: readonly float32, c_getter: vel_estimate_.any().value_or(0.0f)} vel_estimate_counts: readonly float32 calib_scan_response: readonly float32 pos_abs: int32 @@ -1040,10 +1040,10 @@ interfaces: flags: UnstableGain: UnknownCurrentMeasurement: - phase: {type: readonly float32, unit: rad, c_getter: phase_.get_any().value_or(0.0f)} + phase: {type: readonly float32, unit: rad, c_getter: phase_.any().value_or(0.0f)} pll_pos: {type: readonly float32, unit: rad} - phase_vel: {type: readonly float32, unit: rad/s, c_getter: phase_vel_.get_any().value_or(0.0f)} - vel_estimate: {type: readonly float32, unit: turns/s, c_getter: vel_estimate_.get_any().value_or(0.0f)} + phase_vel: {type: readonly float32, unit: rad/s, c_getter: phase_vel_.any().value_or(0.0f)} + vel_estimate: {type: readonly float32, unit: turns/s, c_getter: vel_estimate_.any().value_or(0.0f)} # pll_kp: float32 # pll_ki: float32 config: From 5a19799613e0ff6b9eefdfdd48332d2024fb16a2 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Nov 2020 21:03:41 +0100 Subject: [PATCH 093/124] prevent motor arming if an error is set --- Firmware/MotorControl/axis.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 6c8e6663..9b12a3c4 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -481,10 +481,18 @@ void Axis::run_state_machine_loop() { bool status; switch (current_state_) { case AXIS_STATE_MOTOR_CALIBRATION: { + // These error checks are a hacky way to force legacy behavior + // when an error is raised. TODO: remove this when we overhaul + // the error architecture + // (https://github.com/madcowswe/ODrive/issues/526). + if (odrv.any_error()) + goto invalid_state_label; status = motor_.run_calibration(); } break; case AXIS_STATE_ENCODER_INDEX_SEARCH: { + if (odrv.any_error()) + goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; @@ -492,6 +500,8 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_ENCODER_DIR_FIND: { + if (odrv.any_error()) + goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; @@ -499,22 +509,30 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_HOMING: { + if (odrv.any_error()) + goto invalid_state_label; status = run_homing(); } break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: { + if (odrv.any_error()) + goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; status = encoder_.run_offset_calibration(); } break; case AXIS_STATE_LOCKIN_SPIN: { + if (odrv.any_error()) + goto invalid_state_label; if (!motor_.is_calibrated_ || encoder_.config_.direction==0) goto invalid_state_label; status = run_lockin_spin(config_.general_lockin, false); } break; case AXIS_STATE_CLOSED_LOOP_CONTROL: { + if (odrv.any_error()) + goto invalid_state_label; if (!motor_.is_calibrated_ || (encoder_.config_.direction==0 && !config_.enable_sensorless_mode)) goto invalid_state_label; watchdog_feed(); From 138966aaee5d0606c3c5ce603863c993360e5539 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Nov 2020 12:35:48 +0100 Subject: [PATCH 094/124] make odrivetool less hardware dependent --- tools/odrive/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index ca64c758..f9313fa3 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -548,18 +548,18 @@ def dump_dma(odrv): def dump_timing(odrv, n_samples=100, path='/tmp/timings.png'): import matplotlib.pyplot as plt + import re timings = [] for attr in dir(odrv.task_times): if not attr.startswith('_'): timings.append((attr, getattr(odrv.task_times, attr), [], [])) # (name, obj, start_times, lengths) - for attr in dir(odrv.axis0.task_times): - if not attr.startswith('_'): - timings.append(('axis0.' + attr, getattr(odrv.axis0.task_times, attr), [], [])) # (name, obj, start_times, lengths) - for attr in dir(odrv.axis1.task_times): - if not attr.startswith('_'): - timings.append(('axis1.' + attr, getattr(odrv.axis1.task_times, attr), [], [])) # (name, obj, start_times, lengths) + for k in dir(odrv): + if re.match(r'axis[0-9]+', k): + for attr in dir(getattr(odrv, k).task_times): + if not attr.startswith('_'): + timings.append((k + '.' + attr, getattr(getattr(odrv, k).task_times, attr), [], [])) # (name, obj, start_times, lengths) # Take a couple of samples print("sampling...") From 1184c7778793288800112617bdeb401bab8f81e5 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Nov 2020 18:12:55 +0100 Subject: [PATCH 095/124] fix Interrupt and DMA priority for UART B --- Firmware/Board/v3/Src/dma.c | 6 +++--- Firmware/Board/v3/Src/usart.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Firmware/Board/v3/Src/dma.c b/Firmware/Board/v3/Src/dma.c index 1a3d8f22..56d09af9 100644 --- a/Firmware/Board/v3/Src/dma.c +++ b/Firmware/Board/v3/Src/dma.c @@ -82,14 +82,14 @@ void MX_DMA_Init(void) HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); /* DMA1_Stream5_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 10, 0); // SPI TX - must have higher priority than SPI RX - // and higher priority than the control loop handler + HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); /* DMA1_Stream6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); /* DMA1_Stream7_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 3, 0); + HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 3, 0); // SPI TX - must have higher priority than SPI RX + // and higher priority than the control loop handler HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn); /* DMA2_Stream0_IRQn interrupt configuration */ // Dear STM, no we _don't_ want to fire an interrupt for this DMA diff --git a/Firmware/Board/v3/Src/usart.c b/Firmware/Board/v3/Src/usart.c index e0f1912b..cddfd4e0 100644 --- a/Firmware/Board/v3/Src/usart.c +++ b/Firmware/Board/v3/Src/usart.c @@ -203,7 +203,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart2_tx); /* USART2 interrupt Init */ - HAL_NVIC_SetPriority(USART2_IRQn, 5, 0); + HAL_NVIC_SetPriority(USART2_IRQn, 10, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); /* USER CODE BEGIN USART2_MspInit 1 */ @@ -240,7 +240,7 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) /* Peripheral clock disable */ __HAL_RCC_USART2_CLK_DISABLE(); - /* UART4 DMA DeInit */ + /* USART2 DMA DeInit */ HAL_DMA_DeInit(uartHandle->hdmarx); HAL_DMA_DeInit(uartHandle->hdmatx); From 5d1e59f23a47f9b687bd32bab4e4236e38ca719e Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Nov 2020 21:48:48 +0100 Subject: [PATCH 096/124] disable motor PWMs on hard fault --- Firmware/Board/v3/Src/stm32f4xx_it.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index 175a4380..59ab950b 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -80,6 +80,9 @@ void NMI_Handler(void) __attribute__((used)) void get_regs(void** stack_ptr) { + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM + void* volatile r0 __attribute__((unused)) = stack_ptr[0]; void* volatile r1 __attribute__((unused)) = stack_ptr[1]; void* volatile r2 __attribute__((unused)) = stack_ptr[2]; @@ -127,6 +130,8 @@ void MemManage_Handler(void) while (1) { /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */ + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM /* USER CODE END W1_MemoryManagement_IRQn 0 */ } /* USER CODE BEGIN MemoryManagement_IRQn 1 */ @@ -145,6 +150,8 @@ void BusFault_Handler(void) while (1) { /* USER CODE BEGIN W1_BusFault_IRQn 0 */ + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM /* USER CODE END W1_BusFault_IRQn 0 */ } /* USER CODE BEGIN BusFault_IRQn 1 */ @@ -163,6 +170,8 @@ void UsageFault_Handler(void) while (1) { /* USER CODE BEGIN W1_UsageFault_IRQn 0 */ + TIM1->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M0 PWM + TIM8->BDTR &= ~(TIM_BDTR_AOE_Msk | TIM_BDTR_MOE_Msk); // disable M1 PWM /* USER CODE END W1_UsageFault_IRQn 0 */ } /* USER CODE BEGIN UsageFault_IRQn 1 */ From 2cfd63ae1f3db99c3b8d94a987723597682c4948 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Tue, 17 Nov 2020 22:03:47 -0500 Subject: [PATCH 097/124] Added test rig configuration for Patrick --- tools/test-rig-pj.yaml | 96 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tools/test-rig-pj.yaml diff --git a/tools/test-rig-pj.yaml b/tools/test-rig-pj.yaml new file mode 100644 index 00000000..640dda40 --- /dev/null +++ b/tools/test-rig-pj.yaml @@ -0,0 +1,96 @@ + +components: + - type: generalpurpose + name: homenet + net: homenet + + - type: generalpurpose + name: rpi + ssh: odrv + net: homenet + components: + - type: uart + name: uart0 + port: /dev/ttyS0 + connected-to: main_uart + - type: can + name: can0 + interface: can0 + connected-to: odrive.can + # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 16} + - {type: gpio, num: 19} + - {type: gpio, num: 20} + - {type: gpio, num: 26} + +# - type: programmer +# name: The Blue STLink/v2 +# id: '\x53\x3f\x75\x06\x49\x3f\x49\x51\x44\x54\x19\x3f' + + - type: odrive + name: odrive + board-version: v3.6-58V + serial-number: "2061398A4D4D" + brake-resistance: 0.47 + usb: auto + can: main_canbus + vbus-voltage: 24 # [V] + max-brake-power: 150 # [W] + encoder0: virtual_encoder0 + encoder1: virtual_encoder1 + motor0: D5065-270KV_0 + motor1: floating + + - type: motor + name: D5065-270KV_0 + phase-resistance: 0.039 + phase-inductance: 1.57e-05 + pole-pairs: 7 + direction: 1 + kv: 270 + max-current: 70 + max-voltage: 40 + + - type: encoder + name: real_encoder + cpr: 8192 + max-rpm: 7000 + + - type: teensy + name: teensy + + - {type: lpf, name: lpf0} + - {type: lpf, name: lpf1} + +connections: + - ['odrive.can', 'rpi.can0'] + - ['teensy.program', 'rpi.gpio26'] + - ['teensy.gpio12', 'rpi.uart0.tx'] + - ['teensy.gpio13', 'rpi.uart0.rx'] + - ['teensy.gpio11', 'odrive.gpio1'] + - ['teensy.gpio10', 'odrive.gpio2'] + - ['teensy.gpio9', 'odrive.gpio3'] + - ['teensy.gpio8', 'odrive.gpio4'] + - ['teensy.gpio14', 'odrive.gpio5'] + - ['teensy.gpio15', 'odrive.gpio6'] + - ['teensy.gpio16', 'odrive.gpio7'] + - ['teensy.gpio17', 'odrive.gpio8'] + - ['teensy.gpio6', 'rpi.gpio20'] + - ['teensy.gpio7', 'rpi.gpio19'] + - ['teensy.gpio23', 'odrive.encoder0.z'] + - ['teensy.gpio22', 'odrive.encoder0.b'] + - ['teensy.gpio21', 'odrive.encoder0.a'] + - ['teensy.gpio20', 'odrive.encoder1.z'] + - ['teensy.gpio19', 'odrive.encoder1.b'] + - ['teensy.gpio18', 'odrive.encoder1.a'] + - ['teensy.gpio0', 'real_encoder.z'] + - ['teensy.gpio1', 'real_encoder.a'] + - ['teensy.gpio2', 'real_encoder.b'] + - ['teensy.gpio3', 'odrive.spi.mosi'] + - ['teensy.gpio4', 'odrive.spi.miso'] + - ['teensy.gpio5', 'odrive.spi.sck'] + - ['odrive.axis0', 'D5065-270KV_0'] + - ['D5065-270KV_0', 'real_encoder'] + - ['odrive.gpio3', 'lpf0'] + - ['odrive.gpio4', 'lpf1'] + - ['lpf0.en', 'lpf1.en', 'rpi.gpio16'] From 92926c4d214cbefee2369ee7a4ae63e7ea62ffc2 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Wed, 18 Nov 2020 01:00:36 -0500 Subject: [PATCH 098/124] [tests] added high resistance brake test to TestRegenProtection --- tools/odrive/tests/closed_loop_test.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tools/odrive/tests/closed_loop_test.py b/tools/odrive/tests/closed_loop_test.py index 9b64d1bf..241ca442 100644 --- a/tools/odrive/tests/closed_loop_test.py +++ b/tools/odrive/tests/closed_loop_test.py @@ -210,6 +210,8 @@ class TestRegenProtection(TestClosedLoopControlBase): time.sleep(1.0) test_assert_no_error(axis_ctx) + + logger.debug(f'Brake control test with brake resistor disabled') # once more, but this time without brake resistor axis_ctx.parent.handle.config.enable_brake_resistor = False # accelerate... @@ -225,6 +227,29 @@ class TestRegenProtection(TestClosedLoopControlBase): test_assert_eq(axis_ctx.handle.motor.error & MOTOR_ERROR_SYSTEM_LEVEL, MOTOR_ERROR_SYSTEM_LEVEL) test_assert_eq(axis_ctx.handle.error, 0) + # Do test again with wrong brake resistance setting + logger.debug(f'Brake control test with brake resistor = 100') + axis_ctx.parent.handle.clear_errors() + time.sleep(1.0) + axis_ctx.parent.handle.config.brake_resistance = 100 + axis_ctx.parent.handle.config.dc_max_negative_current = -0.5 + request_state(axis_ctx, AXIS_STATE_CLOSED_LOOP_CONTROL) + + # accelerate... + axis_ctx.handle.controller.input_vel = nominal_rps + time.sleep(1.0) + test_assert_no_error(axis_ctx) + + # ... and brake + axis_ctx.handle.controller.input_vel = 0 + time.sleep(1.0) # expect DC_BUS_OVER_REGEN_CURRENT + time.sleep(0.1) + test_assert_eq(axis_ctx.parent.handle.error, ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT) + test_assert_eq(axis_ctx.handle.motor.error & MOTOR_ERROR_SYSTEM_LEVEL, MOTOR_ERROR_SYSTEM_LEVEL) + test_assert_eq(axis_ctx.handle.error, 0) + + + class TestVelLimitInTorqueControl(TestClosedLoopControlBase): """ From f66d83d2f6b8d4428792daf5520ed506b1d67a70 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 15 Oct 2020 19:44:45 +0200 Subject: [PATCH 099/124] add "implements" functionality to intf generator --- Firmware/Tupfile.lua | 14 +- Firmware/fibre/cpp/interfaces_template.j2 | 2 +- Firmware/fibre/cpp/type_info_template.j2 | 4 +- Firmware/fibre/tools/interface_generator.py | 289 ++++++++++------- Firmware/interface_generator_stub.py | 3 +- Firmware/odrive-interface.yaml | 343 ++++++++++---------- 6 files changed, 355 insertions(+), 300 deletions(-) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 90eecd2c..30c8ee2c 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -16,15 +16,10 @@ end python_command = find_python3() print('Using python command "'..python_command..'"') -tup.frule{inputs={'fibre/cpp/interfaces_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/interfaces.hpp'} -tup.frule{inputs={'fibre/cpp/function_stubs_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/function_stubs.hpp'} -tup.frule{inputs={'fibre/cpp/endpoints_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --generate-endpoints ODrive --template %f --output %o', outputs='autogen/endpoints.hpp'} -tup.frule{inputs={'fibre/cpp/type_info_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/type_info.hpp'} - +-- TODO: use CI to verify that on PRs the enums.py file is consistent with the YAML. -- Note: we currently check this file into source control for two reasons: -- - Don't require tup to run in order to use odrivetool from the repo -- - On Windows, tup is unhappy with writing outside of the tup directory --- TODO: use CI to verify that on PRs the enums.py file is consistent with the YAML. --tup.frule{command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template enums_template.j2 --output ../tools/odrive/enums.py'} tup.frule{ @@ -34,6 +29,7 @@ tup.frule{ board_v3 = { dir = 'Board/v3', + root_interface = 'ODrive3', sources = {'Drivers/DRV8301/drv8301.cpp', 'Board/v3/board.cpp'}, flags = {'-DSTM32F405xx', '-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-DFPU_FPV4'}, ldflags = {'-TBoard/v3/STM32F405RGTx_FLASH.ld', '-LBoard/v3/Drivers/CMSIS/Lib', '-larm_cortexM4lf_math', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'} @@ -177,6 +173,12 @@ for src in string.gmatch(vars['C_INCLUDES'] or '', "%S+") do stm_includes += board.dir..'/'..string.sub(src, 3, -1) -- remove "-I" from each include path end +-- Autogen files from YAML interface definitions +tup.frule{inputs={'fibre/cpp/interfaces_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/interfaces.hpp'} +tup.frule{inputs={'fibre/cpp/function_stubs_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/function_stubs.hpp'} +tup.frule{inputs={'fibre/cpp/endpoints_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --generate-endpoints '..board.root_interface..' --template %f --output %o', outputs='autogen/endpoints.hpp'} +tup.frule{inputs={'fibre/cpp/type_info_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/type_info.hpp'} + -- TODO: cleaner separation of the platform code and the rest stm_includes += '.' --stm_includes += 'Drivers/DRV8301' diff --git a/Firmware/fibre/cpp/interfaces_template.j2 b/Firmware/fibre/cpp/interfaces_template.j2 index d901327e..91af9494 100644 --- a/Firmware/fibre/cpp/interfaces_template.j2 +++ b/Firmware/fibre/cpp/interfaces_template.j2 @@ -30,7 +30,7 @@ void [%- endmacro %] [%- macro render_interface(intf) %] -class [[intf.name | to_pascal_case]]Intf { +class [[intf.name | to_pascal_case]]Intf[% if intf.implements %] :[%- for base_intf in intf.implements %] public [[base_intf.c_name]][% endfor %][% endif %] { public: [%- for intf in intf.interfaces -%] [[render_interface(intf) | indent(4)]] diff --git a/Firmware/fibre/cpp/type_info_template.j2 b/Firmware/fibre/cpp/type_info_template.j2 index 7e6cda57..7efff78f 100644 --- a/Firmware/fibre/cpp/type_info_template.j2 +++ b/Firmware/fibre/cpp/type_info_template.j2 @@ -26,7 +26,7 @@ struct [[intf.fullname | to_pascal_case]]TypeInfo : TypeInfo { T* ptr = *(T**)&obj; introspectable_storage_t res; switch (idx) { -[%- for property in intf.attributes.values() %] +[%- for property in intf.get_all_attributes().values() %] case [[loop.index0]]: *(decltype([[intf.c_name]]::get_[[property.name]](std::declval()))*)(&res) = [[intf.c_name]]::get_[[property.name]](ptr); break; [%- endfor %] } @@ -38,7 +38,7 @@ struct [[intf.fullname | to_pascal_case]]TypeInfo : TypeInfo { [% for intf in interfaces.values() %][% if not intf.builtin %] template const PropertyInfo [[intf.fullname | to_pascal_case]]TypeInfo::property_table[] = { -[%- for property in intf.attributes.values() %] +[%- for property in intf.get_all_attributes().values() %] {"[[property.name]]", &[[(property.type.purename or property.type.fullname) | to_pascal_case]]TypeInfo()))>>::singleton}, [%- endfor %] }; diff --git a/Firmware/fibre/tools/interface_generator.py b/Firmware/fibre/tools/interface_generator.py index f4774b2a..ebdd72ef 100644 --- a/Firmware/fibre/tools/interface_generator.py +++ b/Firmware/fibre/tools/interface_generator.py @@ -19,6 +19,10 @@ definitions: c_name: {type: string} brief: {type: string} doc: {type: string} + implements: + anyOf: + - {"$ref": "#/definitions/intf_type_ref"} + - {type: array, items: {"$ref": "#/definitions/intf_type_ref"}} functions: type: object additionalProperties: {"$ref": "#/definitions/function"} @@ -42,6 +46,11 @@ definitions: __column__: {type: object} additionalProperties: false + intf_type_ref: + anyOf: + - {"type": "string"} + - {"$ref": "#/definitions/interface"} + intf_or_val_type: anyOf: - {"$ref": "#/definitions/interface"} @@ -166,53 +175,8 @@ enums = OrderedDict() interfaces = OrderedDict() userdata = OrderedDict() # Arbitrary data passed from the definition file to the template -def make_property_type(typeargs): - value_type = resolve_valuetype('', typeargs['fibre.Property.type']) - mode = typeargs.get('fibre.Property.mode', 'readwrite') - name = 'Property<' + value_type['fullname'] + ', ' + mode + '>' - fullname = join_name('fibre', name) - if fullname in interfaces: - return interfaces[fullname] - - c_name = 'Property<' + ('const ' if mode == 'readonly' else '') + value_type['c_name'] + '>' - prop_type = { - 'name': name, - 'fullname': fullname, - 'purename': 'fibre.Property', - 'c_name': c_name, - 'value_type': value_type, # TODO: should be a metaarg - 'mode': mode, # TODO: should be a metaarg - 'builtin': True, - 'attributes': OrderedDict(), - 'functions': OrderedDict() - } - if mode != 'readonly': - prop_type['functions']['exchange'] = { - 'name': 'exchange', - 'fullname': join_name(fullname, 'exchange'), - 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': c_name}}), ('value', {'name': 'value', 'type': value_type, 'optional': True})]), - 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), - #'implementation': 'fibre_property_exchange<' + value_type['c_name'] + '>' - } - else: - prop_type['functions']['read'] = { - 'name': 'read', - 'fullname': join_name(fullname, 'read'), - 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': c_name}})]), - 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), - #'implementation': 'fibre_property_read<' + value_type['c_name'] + '>' - } - - interfaces[fullname] = prop_type - return prop_type - -generics = { - 'fibre.Property': make_property_type # TODO: improve generic support -} - - def make_ref_type(interface): - name = 'Ref<' + interface['fullname'] + '>' + name = 'Ref<' + interface.fullname + '>' fullname = join_name('fibre', name) if fullname in interfaces: return interfaces[fullname] @@ -221,7 +185,7 @@ def make_ref_type(interface): 'builtin': True, 'name': name, 'fullname': fullname, - 'c_name': interface['fullname'].replace('.', 'Intf::') + 'Intf*' + 'c_name': interface.fullname.replace('.', 'Intf::') + 'Intf*' } value_types[fullname] = ref_type @@ -260,13 +224,14 @@ def regularize_attribute(parent, name, elem, c_is_class): elem['type'] = {} if 'attributes' in elem: elem['type']['attributes'] = elem.pop('attributes') if 'functions' in elem: elem['type']['functions'] = elem.pop('functions') + if 'implements' in elem: elem['type']['implements'] = elem.pop('implements') if 'c_is_class' in elem: elem['type']['c_is_class'] = elem.pop('c_is_class') if 'values' in elem: elem['type']['values'] = elem.pop('values') if 'flags' in elem: elem['type']['flags'] = elem.pop('flags') if 'nullflag' in elem: elem['type']['nullflag'] = elem.pop('nullflag') elem['name'] = name - elem['fullname'] = join_name(parent['fullname'], name) + elem['fullname'] = join_name(parent.fullname, name) elem['parent'] = parent elem['typeargs'] = elem.get('typeargs', {}) elem['c_name'] = elem.get('c_name', None) or (elem['name'] + ('_' if c_is_class else '')) @@ -277,40 +242,145 @@ def regularize_attribute(parent, name, elem, c_is_class): if isinstance(elem['type'], str) and elem['type'].startswith('readonly '): elem['typeargs']['fibre.Property.mode'] = 'readonly' elem['typeargs']['fibre.Property.type'] = elem['type'][len('readonly '):] - elem['type'] = 'fibre.Property' + elem['type'] = InterfaceRefElement(parent.fullname, None, 'fibre.Property', elem['typeargs']) if elem['typeargs']['fibre.Property.mode'] == 'readonly' and 'c_setter' in elem: elem.pop('c_setter') elif ('flags' in elem['type']) or ('values' in elem['type']): elem['typeargs']['fibre.Property.mode'] = elem['typeargs'].get('fibre.Property.mode', None) or 'readwrite' - elem['typeargs']['fibre.Property.type'] = regularize_valuetype(parent['fullname'], to_pascal_case(name), elem['type']) - elem['type'] = 'fibre.Property' + elem['typeargs']['fibre.Property.type'] = regularize_valuetype(parent.fullname, to_pascal_case(name), elem['type']) + elem['type'] = InterfaceRefElement(parent.fullname, None, 'fibre.Property', elem['typeargs']) if elem['typeargs']['fibre.Property.mode'] == 'readonly' and 'c_setter' in elem: elem.pop('c_setter') else: - elem['type'] = regularize_interface(parent['fullname'], to_pascal_case(name), elem['type']) + elem['type'] = InterfaceRefElement(parent.fullname, to_pascal_case(name), elem['type'], elem['typeargs']) return elem +class InterfaceRefElement(): + def __init__(self, scope, name, elem, typeargs): + if isinstance(elem, str): + self._intf = None + self._scope = scope + self._name = elem + else: + self._intf = InterfaceElement(scope, name, elem) + self._scope = None + self._name = None + self._typeargs = typeargs -def regularize_interface(path, name, elem): - if elem is None: - elem = {} - if isinstance(elem, str): - return elem # will be resolved during type resolution - #if path is None: - # max_anonymous_type = max([int((re.findall('^' + join_name(path, 'AnonymousType') + '([1-9]+)$', x) + ['0'])[0]) for x in interfaces.keys()]) - # path = 'AnonymousType' + str(max_anonymous_type + 1) - elem['name'] = split_name(name)[-1] - elem['fullname'] = path = join_name(path, name) - elem['c_name'] = elem.get('c_name', elem['fullname'].replace('.', 'Intf::')) + 'Intf' - interfaces[path] = elem - elem['functions'] = OrderedDict((name, regularize_func(path, name, func, {'obj': {'type': make_ref_type(elem)}})) - for name, func in get_dict(elem, 'functions').items()) - if not 'c_is_class' in elem: - raise Exception(elem) - treat_as_class = elem['c_is_class'] # TODO: add command line arg to make this selectively optional - elem['attributes'] = OrderedDict((name, regularize_attribute(elem, name, prop, treat_as_class)) - for name, prop in get_dict(elem, 'attributes').items()) - elem['interfaces'] = [] - elem['enums'] = [] - return elem + def resolve(self): + """ + Resolves this interface reference to an actual InterfaceElement instance. + The innermost scope is searched first. + At every scope level, if no matching interface is found, it is checked if a + matching value type exists. If so, the interface type fibre.Property + is returned. + """ + if not self._intf is None: + return self._intf + + typeargs = self._typeargs + if 'fibre.Property.type' in typeargs: + typeargs['fibre.Property.type'] = resolve_valuetype(self._scope, typeargs['fibre.Property.type']) + + scope = self._scope.split('.') + for probe_scope in [join_name(*scope[:(len(scope)-i)]) for i in range(len(scope)+1)]: + probe_name = join_name(probe_scope, self._name) + #print('probing ' + probe_name) + if probe_name in interfaces: + return interfaces[probe_name] + elif probe_name in value_types: + typeargs['fibre.Property.type'] = value_types[probe_name] + return make_property_type(typeargs) + elif probe_name in generics: + return generics[probe_name](typeargs) + + raise Exception('could not resolve type {} in {}. Known interfaces are: {}. Known value types are: {}'.format(self._name, self._scope, list(interfaces.keys()), list(value_types.keys()))) + +class InterfaceElement(): + def __init__(self, path, name, elem): + if elem is None: + elem = {} + assert(isinstance(elem, dict)) + + path = join_name(path, name) + interfaces[path] = self + + self.name = split_name(name)[-1] + self.fullname = path + self.c_name = elem.get('c_name', self.fullname.replace('.', 'Intf::')) + 'Intf' + + if not 'implements' in elem: + elem['implements'] = [] + elif isinstance(elem['implements'], str): + elem['implements'] = [elem['implements']] + self.implements = [InterfaceRefElement(path, None, elem, {}) for elem in elem['implements']] + self.functions = OrderedDict((name, regularize_func(path, name, func, {'obj': {'type': make_ref_type(self)}})) + for name, func in get_dict(elem, 'functions').items()) + if not 'c_is_class' in elem: + raise Exception(elem) + treat_as_class = elem['c_is_class'] # TODO: add command line arg to make this selectively optional + self.attributes = OrderedDict((name, regularize_attribute(self, name, prop, treat_as_class)) + for name, prop in get_dict(elem, 'attributes').items()) + self.interfaces = [] + self.enums = [] + + def get_all_attributes(self, stack=[]): + result = OrderedDict() + for intf in self.implements: + assert(not self in stack) + result.update(intf.get_all_attributes(stack + [self])) + result.update(self.attributes) + return result + + def get_all_functions(self, stack=[]): + result = OrderedDict() + for intf in self.implements: + assert(not self in stack) + result.update(intf.get_all_functions(stack + [self])) + result.update(self.functions) + return result + +class PropertyInterfaceElement(InterfaceElement): + def __init__(self, name, fullname, mode, value_type): + self.name = name + self.fullname = fullname + self.purename = 'fibre.Property' + self.c_name = 'Property<' + ('const ' if mode == 'readonly' else '') + value_type['c_name'] + '>' + self.value_type = value_type # TODO: should be a metaarg + self.mode = mode # TODO: should be a metaarg + self.builtin = True + self.attributes = OrderedDict() + self.functions = OrderedDict() + if mode != 'readonly': + self.functions['exchange'] = { + 'name': 'exchange', + 'fullname': join_name(fullname, 'exchange'), + 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': self.c_name}}), ('value', {'name': 'value', 'type': value_type, 'optional': True})]), + 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), + #'implementation': 'fibre_property_exchange<' + value_type['c_name'] + '>' + } + else: + self.functions['read'] = { + 'name': 'read', + 'fullname': join_name(fullname, 'read'), + 'in': OrderedDict([('obj', {'name': 'obj', 'type': {'c_name': self.c_name}})]), + 'out': OrderedDict([('value', {'name': 'value', 'type': value_type})]), + #'implementation': 'fibre_property_read<' + value_type['c_name'] + '>' + } + + interfaces[fullname] = self # TODO: not good to write to a global here + +def make_property_type(typeargs): + value_type = resolve_valuetype('', typeargs['fibre.Property.type']) + mode = typeargs.get('fibre.Property.mode', 'readwrite') + name = 'Property<' + value_type['fullname'] + ', ' + mode + '>' + fullname = join_name('fibre', name) + if fullname in interfaces: + return interfaces[fullname] + else: + return PropertyInterfaceElement(name, fullname, mode, value_type) + +generics = { + 'fibre.Property': make_property_type # TODO: improve generic support +} def regularize_valuetype(path, name, elem): if elem is None: @@ -351,34 +421,6 @@ def regularize_valuetype(path, name, elem): return elem -def resolve_interface(scope, name, typeargs): - """ - Resolves a type name (i.e. interface name or value type name) given as a - string to an interface object. The innermost scope is searched first. - At every scope level, if no matching interface is found, it is checked if a - matching value type exists. If so, the interface type fibre.Property - is returned. - """ - if not isinstance(name, str): - return name - - if 'fibre.Property.type' in typeargs: - typeargs['fibre.Property.type'] = resolve_valuetype(scope, typeargs['fibre.Property.type']) - - scope = scope.split('.') - for probe_scope in [join_name(*scope[:(len(scope)-i)]) for i in range(len(scope)+1)]: - probe_name = join_name(probe_scope, name) - #print('probing ' + probe_name) - if probe_name in interfaces: - return interfaces[probe_name] - elif probe_name in value_types: - typeargs['fibre.Property.type'] = value_types[probe_name] - return make_property_type(typeargs) - elif probe_name in generics: - return generics[probe_name](typeargs) - - raise Exception('could not resolve type {} in {}. Known interfaces are: {}. Known value types are: {}'.format(name, join_name(*scope), list(interfaces.keys()), list(value_types.keys()))) - def resolve_valuetype(scope, name): """ Resolves a type name given as a string to the type object. @@ -404,19 +446,19 @@ def map_to_fibre01_type(t): return t['fullname'] def generate_endpoint_for_property(prop, attr_bindto, idx): - prop_intf = interfaces[prop['type']['fullname']] + prop_intf = interfaces[prop['type'].fullname] endpoint = { 'id': idx, - 'function': prop_intf['functions']['read' if prop['type']['mode'] == 'readonly' else 'exchange'], + 'function': prop_intf.functions['read' if prop['type'].mode == 'readonly' else 'exchange'], 'in_bindings': OrderedDict([('obj', attr_bindto)]), 'out_bindings': OrderedDict() } endpoint_definition = { 'name': prop['name'], 'id': idx, - 'type': map_to_fibre01_type(prop['type']['value_type']), - 'access': 'r' if prop['type']['mode'] == 'readonly' else 'rw', + 'type': map_to_fibre01_type(prop['type'].value_type), + 'access': 'r' if prop['type'].mode == 'readonly' else 'rw', } return endpoint, endpoint_definition @@ -430,10 +472,10 @@ def generate_endpoint_table(intf, bindto, idx): endpoint_definitions = [] cnt = 0 - for k, prop in intf['attributes'].items(): - property_value_type = re.findall('^fibre\.Property<([^>]*), (readonly|readwrite)>$', prop['type']['fullname']) + for k, prop in intf.get_all_attributes().items(): + property_value_type = re.findall('^fibre\.Property<([^>]*), (readonly|readwrite)>$', prop['type'].fullname) #attr_bindto = join_name(bindto, bindings_map.get(join_name(intf['fullname'], k), k + ('_' if len(intf['functions']) or (intf['fullname'] in treat_as_classes) else ''))) - attr_bindto = intf['c_name'] + '::get_' + prop['name'] + '(' + bindto + ')' + attr_bindto = intf.c_name + '::get_' + prop['name'] + '(' + bindto + ')' if len(property_value_type): # Special handling for Property<...> attributes: they resolve to one single endpoint endpoint, endpoint_definition = generate_endpoint_for_property(prop, attr_bindto, idx + cnt) @@ -450,7 +492,7 @@ def generate_endpoint_table(intf, bindto, idx): }) cnt += inner_cnt - for k, func in intf['functions'].items(): + for k, func in intf.get_all_functions().items(): endpoints.append({ 'id': idx + cnt, 'function': func, @@ -463,14 +505,14 @@ def generate_endpoint_table(intf, bindto, idx): endpoint, endpoint_definition = generate_endpoint_for_property({ 'name': arg['name'], 'type': make_property_type({'fibre.Property.type': arg['type'], 'fibre.Property.mode': 'readwrite'}) - }, intf['c_name'] + '::get_' + func['name'] + '_in_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + 1 + i) + }, intf.c_name + '::get_' + func['name'] + '_in_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + 1 + i) endpoints.append(endpoint) in_def.append(endpoint_definition) for i, (k_arg, arg) in enumerate(func['out'].items()): endpoint, endpoint_definition = generate_endpoint_for_property({ 'name': arg['name'], 'type': make_property_type({'fibre.Property.type': arg['type'], 'fibre.Property.mode': 'readonly'}) - }, intf['c_name'] + '::get_' + func['name'] + '_out_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + len(func['in']) + i) + }, intf.c_name + '::get_' + func['name'] + '_out_' + k_arg + '_' + '(' + bindto + ')', idx + cnt + len(func['in']) + i) endpoints.append(endpoint) out_def.append(endpoint_definition) @@ -541,7 +583,7 @@ for definition_file in definition_files: # Regularize everything into a wellknown form for k, item in list(interfaces.items()): - regularize_interface('', k, item) + InterfaceElement('', k, item) for k, item in list(value_types.items()): regularize_valuetype('', k, item) @@ -554,15 +596,16 @@ if len(clashing_names): print("**Error**: Found both an interface and a value type with the name {}. This is not allowed, interfaces and value types (such as enums) share the same namespace.".format(clashing_names[0]), file=sys.stderr) sys.exit(1) -# Resolve all types into references +# Resolve all types to references for _, item in list(interfaces.items()): - for _, prop in item['attributes'].items(): - prop['type'] = resolve_interface(item['fullname'], prop['type'], prop['typeargs']) - for _, func in item['functions'].items(): + item.implements = [ref.resolve() for ref in item.implements] + for _, prop in item.attributes.items(): + prop['type'] = prop['type'].resolve() + for _, func in item.functions.items(): for _, arg in func['in'].items(): - arg['type'] = resolve_valuetype(item['fullname'], arg['type']) + arg['type'] = resolve_valuetype(item.fullname, arg['type']) for _, arg in func['out'].items(): - arg['type'] = resolve_valuetype(item['fullname'], arg['type']) + arg['type'] = resolve_valuetype(item.fullname, arg['type']) # Attach interfaces to their parents toplevel_interfaces = [] @@ -573,8 +616,8 @@ for k, item in list(interfaces.items()): else: if k[:-1] != ['fibre']: # TODO: remove special handling parent = interfaces[join_name(*k[:-1])] - parent['interfaces'].append(item) - item['parent'] = parent + parent.interfaces.append(item) + item.parent = parent toplevel_enums = [] for k, item in list(enums.items()): k = split_name(k) @@ -583,7 +626,7 @@ for k, item in list(enums.items()): else: if k[:-1] != ['fibre']: # TODO: remove special handling parent = interfaces[join_name(*k[:-1])] - parent['enums'].append(item) + parent.enums.append(item) item['parent'] = parent @@ -643,7 +686,7 @@ def tokenize(text, interface, interface_transform, value_type_transform, attribu if not attr is None: return attribute_transform(token, attr) - print('Warning: cannot resolve "{}" in {}'.format(token, interface['fullname'])) + print('Warning: cannot resolve "{}" in {}'.format(token, interface.fullname)) return "`" + token + "`" return re.sub(r'`([A-Za-z\._]+)`', token_transform, text) diff --git a/Firmware/interface_generator_stub.py b/Firmware/interface_generator_stub.py index d5b22093..b55a89b8 100644 --- a/Firmware/interface_generator_stub.py +++ b/Firmware/interface_generator_stub.py @@ -4,7 +4,8 @@ import sys import os try: - exec(open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'fibre', 'tools', 'interface_generator.py')).read()) + path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'fibre', 'tools', 'interface_generator.py') + exec(compile(open(path).read(), path, 'exec')) except ImportError as ex: print(str(ex), file=sys.stderr) print("Note that there are new compile-time dependencies since around v0.5.1.", file=sys.stderr) diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 83f294b4..d88af102 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -156,171 +156,6 @@ interfaces: addr_match_cnt: readonly uint32 rx_cnt: readonly uint32 error_cnt: readonly uint32 - - config: - c_is_class: False - attributes: - # TODO: add support for arrays - gpio1_mode: {type: GpioMode, doc: Mode of GPIO1 (changes take effect after reboot), c_name: 'gpio_modes[1]'} - gpio2_mode: {type: GpioMode, doc: Mode of GPIO2 (changes take effect after reboot), c_name: 'gpio_modes[2]'} - gpio3_mode: {type: GpioMode, doc: Mode of GPIO3 (changes take effect after reboot), c_name: 'gpio_modes[3]'} - gpio4_mode: {type: GpioMode, doc: Mode of GPIO4 (changes take effect after reboot), c_name: 'gpio_modes[4]'} - gpio5_mode: {type: GpioMode, doc: Mode of GPIO5 (changes take effect after reboot), c_name: 'gpio_modes[5]'} - gpio6_mode: {type: GpioMode, doc: Mode of GPIO6 (changes take effect after reboot), c_name: 'gpio_modes[6]'} - gpio7_mode: {type: GpioMode, doc: Mode of GPIO7 (changes take effect after reboot), c_name: 'gpio_modes[7]'} - gpio8_mode: {type: GpioMode, doc: Mode of GPIO8 (changes take effect after reboot), c_name: 'gpio_modes[8]'} - gpio9_mode: {type: GpioMode, doc: Mode of GPIO9 (changes take effect after reboot), c_name: 'gpio_modes[9]'} - gpio10_mode: {type: GpioMode, doc: Mode of GPIO10 (changes take effect after reboot), c_name: 'gpio_modes[10]'} - gpio11_mode: {type: GpioMode, doc: Mode of GPIO11 (changes take effect after reboot), c_name: 'gpio_modes[11]'} - gpio12_mode: {type: GpioMode, doc: Mode of GPIO12 (changes take effect after reboot), c_name: 'gpio_modes[12]'} - gpio13_mode: {type: GpioMode, doc: Mode of GPIO13 (changes take effect after reboot), c_name: 'gpio_modes[13]'} - gpio14_mode: {type: GpioMode, doc: Mode of GPIO14 (changes take effect after reboot), c_name: 'gpio_modes[14]'} - gpio15_mode: {type: GpioMode, doc: Mode of GPIO15 (changes take effect after reboot), c_name: 'gpio_modes[15]'} - gpio16_mode: {type: GpioMode, doc: Mode of GPIO16 (changes take effect after reboot), c_name: 'gpio_modes[16]'} - - enable_uart_a: - type: bool - brief: Enables/disables UART_A. - doc: | - You also need to set the corresponding GPIOs to GPIO_MODE_UART_A. - Refer to [interfaces](interfaces.md) to see which pins support UART_A. - Changing this requires a reboot. - enable_uart_b: - type: bool - brief: Enables/disables UART_B. - doc: | - You also need to set the corresponding GPIOs to GPIO_MODE_UART_B. - Refer to [interfaces](interfaces.md) to see which pins support UART_B. - Changing this requires a reboot. - enable_uart_c: {type: bool, doc: Not supported on ODrive v3.x.} - uart_a_baudrate: - type: uint32 - unit: baud/s - brief: Defines the baudrate used on the UART interface. - doc: | - Some baudrates will have a small timing error due to hardware limitations. - - Here's an (incomplete) list of baudrates for ODrive v3.x: - - Configured | Actual | Error [%] - -------------|---------------|----------- - 1.2 KBps | 1.2 KBps | 0 - 2.4 KBps | 2.4 KBps | 0 - 9.6 KBps | 9.6 KBps | 0 - 19.2 KBps | 19.195 KBps | 0.02 - 38.4 KBps | 38.391 KBps | 0.02 - 57.6 KBps | 57.613 KBps | 0.02 - 115.2 KBps | 115.068 KBps | 0.11 - 230.4 KBps | 230.769 KBps | 0.16 - 460.8 KBps | 461.538 KBps | 0.16 - 921.6 KBps | 913.043 KBps | 0.93 - 1.792 MBps | 1.826 MBps | 1.9 - 1.8432 MBps | 1.826 MBps | 0.93 - - For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the - [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). - uart_b_baudrate: - type: uint32 - unit: baud/s - brief: Defines the baudrate used on the UART interface. - doc: See `uart_a_baudrate` for details. - uart_c_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} - enable_can_a: - type: bool - doc: | - Enables CAN. Changing this setting requires a reboot. - enable_i2c_a: - type: bool - doc: | - Enables I2C. The I2C pins on ODrive v3.x are in conflict with CAN. - This setting has no effect if `enable_can_a` is also true. - This setting has no effect on ODrive v3.2 or earlier. - Changing this setting requires a reboot. - enable_ascii_protocol_on_usb: bool - max_regen_current: float32 - brake_resistance: - type: float32 - unit: Ohm - brief: Value of the brake resistor connected to the ODrive. - doc: | - If you set this to a lower value than the true brake resistance - then the ODrive will not meed the `max_regen_current` constraint - during braking, that is it will sink more than `max_regen_current` - into the power supply. Some power supplies don't like this. - - If you set this to a higher value than the true brake resistance - then the ODrive will unnecessarily burn more power than required - during braking. - enable_brake_resistor: - type: bool - brief: Enable/disable the use of a brake resistor. - doc: | - Setting this to False even though a brake resistor is connected is - harmless. Setting this to True even though no brake resistor is - connected can break the power supply. - Changes to this value require a reboot to take effect. - - dc_bus_undervoltage_trip_level: - type: float32 - unit: V - brief: Minimum voltage below which the motor stops operating. - dc_bus_overvoltage_trip_level: - type: float32 - unit: V - brief: Maximum voltage above which the motor stops operating. - doc: | - This protects against cases in which the power supply fails to dissipate - the brake power if the brake resistor is disabled. - The default is 26V for the 24V board version and 52V for the 48V board version. - - enable_dc_bus_overvoltage_ramp: - type: bool - status: experimental - brief: Enables the DC bus overvoltage ramp feature. - doc: | - If enabled, if the measured DC voltage exceeds `dc_bus_overvoltage_ramp_start`, - the ODrive will sink more power than usual into the the brake resistor - in an attempt to bring the voltage down again. - - The brake duty cycle is increased by the following amount: - - * `vbus_voltage` == `dc_bus_overvoltage_ramp_start` => brake_duty_cycle += 0% - * `vbus_voltage` == `dc_bus_overvoltage_ramp_end` => brake_duty_cycle += 100% - - Remarks: - - This feature is active even when all motors are disarmed. - - This feature is disabled if `brake_resistance` is non-positive. - dc_bus_overvoltage_ramp_start: - type: float32 - status: experimental - brief: See `enable_dc_bus_overvoltage_ramp`. - doc: Do not set this lower than your usual `vbus_voltage`, - unless you like fried brake resistors. - dc_bus_overvoltage_ramp_end: - type: float32 - status: experimental - brief: See `enable_dc_bus_overvoltage_ramp`. - doc: Must be larger than `dc_bus_overvoltage_ramp_start`, - otherwise the ramp feature is disabled. - - dc_max_positive_current: - type: float32 - unit: A - brief: Max current the power supply can source. - dc_max_negative_current: - type: float32 - unit: A - brief: Max current the power supply can sink. - doc: You most likely want a non-positive value here. Set to -INFINITY to disable. - - error_gpio_pin: {type: uint32} - - gpio1_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio2_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio3_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio4_pwm_mapping: {type: Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM0`.} - gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} - gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} user_config_loaded: readonly uint32 misconfigured: # TODO: make this a system error @@ -343,8 +178,6 @@ interfaces: capability were both used as interrupt input. Example: `step_gpio_pin` of both axes were set to the same GPIO. - axis0: {type: Axis, c_name: get_axis(0)} - axis1: {type: Axis, c_name: get_axis(1)} oscilloscope: {type: Oscilloscope} can: {type: Can, c_name: get_can()} test_property: uint32 @@ -384,6 +217,149 @@ interfaces: clear_errors: doc: Clear all the errors of this device including all contained submodules. + ODrive.Config: + c_is_class: False + attributes: + enable_uart_a: + type: bool + brief: Enables/disables UART_A. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART_A. + Refer to [interfaces](interfaces.md) to see which pins support UART_A. + Changing this requires a reboot. + enable_uart_b: + type: bool + brief: Enables/disables UART_B. + doc: | + You also need to set the corresponding GPIOs to GPIO_MODE_UART_B. + Refer to [interfaces](interfaces.md) to see which pins support UART_B. + Changing this requires a reboot. + enable_uart_c: {type: bool, doc: Not supported on ODrive v3.x.} + uart_a_baudrate: + type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. + doc: | + Some baudrates will have a small timing error due to hardware limitations. + + Here's an (incomplete) list of baudrates for ODrive v3.x: + + Configured | Actual | Error [%] + -------------|---------------|----------- + 1.2 KBps | 1.2 KBps | 0 + 2.4 KBps | 2.4 KBps | 0 + 9.6 KBps | 9.6 KBps | 0 + 19.2 KBps | 19.195 KBps | 0.02 + 38.4 KBps | 38.391 KBps | 0.02 + 57.6 KBps | 57.613 KBps | 0.02 + 115.2 KBps | 115.068 KBps | 0.11 + 230.4 KBps | 230.769 KBps | 0.16 + 460.8 KBps | 461.538 KBps | 0.16 + 921.6 KBps | 913.043 KBps | 0.93 + 1.792 MBps | 1.826 MBps | 1.9 + 1.8432 MBps | 1.826 MBps | 0.93 + + For more information refer to Section 30.3.4 and Table 142 (the column with f_PCLK = 42 MHz) in the + [STM datasheet](https://www.st.com/content/ccc/resource/technical/document/reference_manual/3d/6d/5a/66/b4/99/40/d4/DM00031020.pdf/files/DM00031020.pdf/jcr:content/translations/en.DM00031020.pdf). + uart_b_baudrate: + type: uint32 + unit: baud/s + brief: Defines the baudrate used on the UART interface. + doc: See `uart_a_baudrate` for details. + uart_c_baudrate: {type: uint32, doc: Not supported on ODrive v3.x.} + enable_can_a: + type: bool + doc: | + Enables CAN. Changing this setting requires a reboot. + enable_i2c_a: + type: bool + doc: | + Enables I2C. The I2C pins on ODrive v3.x are in conflict with CAN. + This setting has no effect if `enable_can_a` is also true. + This setting has no effect on ODrive v3.2 or earlier. + Changing this setting requires a reboot. + enable_ascii_protocol_on_usb: bool + max_regen_current: float32 + brake_resistance: + type: float32 + unit: Ohm + brief: Value of the brake resistor connected to the ODrive. + doc: | + If you set this to a lower value than the true brake resistance + then the ODrive will not meed the `max_regen_current` constraint + during braking, that is it will sink more than `max_regen_current` + into the power supply. Some power supplies don't like this. + + If you set this to a higher value than the true brake resistance + then the ODrive will unnecessarily burn more power than required + during braking. + enable_brake_resistor: + type: bool + brief: Enable/disable the use of a brake resistor. + doc: | + Setting this to False even though a brake resistor is connected is + harmless. Setting this to True even though no brake resistor is + connected can break the power supply. + Changes to this value require a reboot to take effect. + + dc_bus_undervoltage_trip_level: + type: float32 + unit: V + brief: Minimum voltage below which the motor stops operating. + dc_bus_overvoltage_trip_level: + type: float32 + unit: V + brief: Maximum voltage above which the motor stops operating. + doc: | + This protects against cases in which the power supply fails to dissipate + the brake power if the brake resistor is disabled. + The default is 26V for the 24V board version and 52V for the 48V board version. + + enable_dc_bus_overvoltage_ramp: + type: bool + status: experimental + brief: Enables the DC bus overvoltage ramp feature. + doc: | + If enabled, if the measured DC voltage exceeds `dc_bus_overvoltage_ramp_start`, + the ODrive will sink more power than usual into the the brake resistor + in an attempt to bring the voltage down again. + + The brake duty cycle is increased by the following amount: + + * `vbus_voltage` == `dc_bus_overvoltage_ramp_start` => brake_duty_cycle += 0% + * `vbus_voltage` == `dc_bus_overvoltage_ramp_end` => brake_duty_cycle += 100% + + Remarks: + - This feature is active even when all motors are disarmed. + - This feature is disabled if `brake_resistance` is non-positive. + dc_bus_overvoltage_ramp_start: + type: float32 + status: experimental + brief: See `enable_dc_bus_overvoltage_ramp`. + doc: Do not set this lower than your usual `vbus_voltage`, + unless you like fried brake resistors. + dc_bus_overvoltage_ramp_end: + type: float32 + status: experimental + brief: See `enable_dc_bus_overvoltage_ramp`. + doc: Must be larger than `dc_bus_overvoltage_ramp_start`, + otherwise the ramp feature is disabled. + + dc_max_positive_current: + type: float32 + unit: A + brief: Max current the power supply can source. + dc_max_negative_current: + type: float32 + unit: A + brief: Max current the power supply can sink. + doc: You most likely want a non-positive value here. Set to -INFINITY to disable. + + error_gpio_pin: {type: uint32} + + gpio3_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} + gpio4_analog_mapping: {type: Endpoint, c_name: 'analog_mappings[4]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_ANALOG_IN`.} + ODrive.Can: c_is_class: True attributes: @@ -1101,6 +1077,39 @@ interfaces: length: readonly uint32 max_length: uint32 + ODrive3: + c_is_class: True + implements: ODrive + attributes: + config: + c_is_class: False + implements: ODrive.Config + attributes: + # TODO: add support for arrays + gpio1_mode: {type: ODrive.GpioMode, doc: Mode of GPIO1 (changes take effect after reboot), c_name: 'gpio_modes[1]'} + gpio2_mode: {type: ODrive.GpioMode, doc: Mode of GPIO2 (changes take effect after reboot), c_name: 'gpio_modes[2]'} + gpio3_mode: {type: ODrive.GpioMode, doc: Mode of GPIO3 (changes take effect after reboot), c_name: 'gpio_modes[3]'} + gpio4_mode: {type: ODrive.GpioMode, doc: Mode of GPIO4 (changes take effect after reboot), c_name: 'gpio_modes[4]'} + gpio5_mode: {type: ODrive.GpioMode, doc: Mode of GPIO5 (changes take effect after reboot), c_name: 'gpio_modes[5]'} + gpio6_mode: {type: ODrive.GpioMode, doc: Mode of GPIO6 (changes take effect after reboot), c_name: 'gpio_modes[6]'} + gpio7_mode: {type: ODrive.GpioMode, doc: Mode of GPIO7 (changes take effect after reboot), c_name: 'gpio_modes[7]'} + gpio8_mode: {type: ODrive.GpioMode, doc: Mode of GPIO8 (changes take effect after reboot), c_name: 'gpio_modes[8]'} + gpio9_mode: {type: ODrive.GpioMode, doc: Mode of GPIO9 (changes take effect after reboot), c_name: 'gpio_modes[9]'} + gpio10_mode: {type: ODrive.GpioMode, doc: Mode of GPIO10 (changes take effect after reboot), c_name: 'gpio_modes[10]'} + gpio11_mode: {type: ODrive.GpioMode, doc: Mode of GPIO11 (changes take effect after reboot), c_name: 'gpio_modes[11]'} + gpio12_mode: {type: ODrive.GpioMode, doc: Mode of GPIO12 (changes take effect after reboot), c_name: 'gpio_modes[12]'} + gpio13_mode: {type: ODrive.GpioMode, doc: Mode of GPIO13 (changes take effect after reboot), c_name: 'gpio_modes[13]'} + gpio14_mode: {type: ODrive.GpioMode, doc: Mode of GPIO14 (changes take effect after reboot), c_name: 'gpio_modes[14]'} + gpio15_mode: {type: ODrive.GpioMode, doc: Mode of GPIO15 (changes take effect after reboot), c_name: 'gpio_modes[15]'} + gpio16_mode: {type: ODrive.GpioMode, doc: Mode of GPIO16 (changes take effect after reboot), c_name: 'gpio_modes[16]'} + + gpio1_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio2_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio3_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio4_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + axis0: {type: ODrive.Axis, c_name: get_axis(0)} + axis1: {type: ODrive.Axis, c_name: get_axis(1)} + valuetypes: ODrive.GpioMode: values: From df83782bd4d7d08142442a8d1d410f4e000a61b8 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 18 Nov 2020 18:51:19 +0100 Subject: [PATCH 100/124] re-add missing objects in ASCII protocol --- Firmware/communication/ascii_protocol.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 578c3f7b..811efac6 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -28,7 +28,9 @@ /* Private variables ---------------------------------------------------------*/ -static Introspectable root_obj = ODriveTypeInfo::make_introspectable(odrv); +#if HW_VERSION_MAJOR == 3 +static Introspectable root_obj = ODrive3TypeInfo::make_introspectable(odrv); +#endif /* Private function prototypes -----------------------------------------------*/ From bf605a2eefde4f11e849a07b83f8e05d8c2f689b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 14 Oct 2020 19:26:06 +0200 Subject: [PATCH 101/124] introduce script for in-factory programming --- Firmware/Board/v3/STM32F405RGTx_FLASH.ld | 1 + Firmware/Board/v3/board.cpp | 24 +++++ Firmware/Makefile | 114 ++++++++--------------- Firmware/MotorControl/odrive_main.h | 26 +----- Firmware/Tupfile.lua | 4 + Firmware/find_programmer.sh | 5 - Firmware/openocd.gdbinit | 2 - 7 files changed, 72 insertions(+), 104 deletions(-) delete mode 100755 Firmware/find_programmer.sh delete mode 100644 Firmware/openocd.gdbinit diff --git a/Firmware/Board/v3/STM32F405RGTx_FLASH.ld b/Firmware/Board/v3/STM32F405RGTx_FLASH.ld index f1259a71..79e37441 100644 --- a/Firmware/Board/v3/STM32F405RGTx_FLASH.ld +++ b/Firmware/Board/v3/STM32F405RGTx_FLASH.ld @@ -121,6 +121,7 @@ SECTIONS { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ + *(.testdata) *(.data) /* .data sections */ *(.data*) /* .data* sections */ diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index d9f768a8..68bc9912 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -23,6 +23,12 @@ extern "C" void SystemClock_Config(void); // defined in main.c generated by Cube #define ControlLoop_IRQHandler OTG_HS_IRQHandler #define ControlLoop_IRQn OTG_HS_IRQn +// This array is placed at the very start of the ram (0x20000000) and will be +// used during manufacturing to test the struct that will go to the OTP before +// _actually_ putting anything into OTP. This avoids bulk-destroying STM32's if +// we introduce unintended breakage in our manufacturing scripts. +uint8_t __attribute__((section(".testdata"))) fake_otp[FLASH_OTP_END + 1 - FLASH_OTP_BASE]; + Stm32SpiArbiter spi3_arbiter{&hspi3}; Stm32SpiArbiter& ext_spi_arbiter = spi3_arbiter; @@ -257,12 +263,30 @@ PwmInput pwm0_input{&htim5, {1, 2, 3, 4}}; extern USBD_HandleTypeDef hUsbDeviceFS; USBD_HandleTypeDef& usb_dev_handle = hUsbDeviceFS; +bool check_board_version(const uint8_t* otp_ptr) { + return (otp_ptr[3] == HW_VERSION_MAJOR) && + (otp_ptr[4] == HW_VERSION_MINOR) && + (otp_ptr[5] == HW_VERSION_VOLTAGE); +} + void system_init() { // Reset of all peripherals, Initializes the Flash interface and the Systick. HAL_Init(); // Configure the system clock SystemClock_Config(); + + // If the OTP is pristine, use the fake-otp in RAM instead + const uint8_t* otp_ptr = (const uint8_t*)FLASH_OTP_BASE; + if (*otp_ptr == 0xff) { + otp_ptr = fake_otp; + } + + // Ensure that the board version for which this firmware is compiled matches + // the board we're running on. + if (!check_board_version(otp_ptr)) { + for (;;); + } } bool board_init() { diff --git a/Firmware/Makefile b/Firmware/Makefile index d12efb32..82cf9250 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -5,23 +5,45 @@ BUILD_DIR = build FIRMWARE = $(BUILD_DIR)/ODriveFirmware.elf FIRMWARE_HEX = $(BUILD_DIR)/ODriveFirmware.hex -OPENOCD := openocd -f interface/stlink-v2.cfg \ - $(if $(value PROGRAMMER),-c 'hla_serial $(PROGRAMMER)',) \ - -f target/stm32f4x.cfg +PROGRAMMER_CMD=$(if $(value PROGRAMMER),-c 'hla_serial $(PROGRAMMER)',) +include tup.config # source build configuration to get CONFIG_BOARD_VERSION + +ifneq (,$(findstring v3.,$(CONFIG_BOARD_VERSION))) + OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f4x.cfg -c init + GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x.cfg" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt' +else + $(error unknown board version) +endif + +$(info board version: $(CONFIG_BOARD_VERSION)) all: @tup --quiet --no-environ-check @python interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py -flash: all - $(OPENOCD) -c init \ +clean: + -rm -fR .dep $(BUILD_DIR) + +flash-stlink2: all + $(OPENOCD) \ -c 'reset halt' \ -c 'flash write_image erase $(FIRMWARE)' \ -c 'reset run' \ -c exit -flashbmp: all +gdb-stlink2: + $(GDB) $(FIRMWARE) + +# Erase entire STM32 +erase-stlink2: + $(OPENOCD) -c 'reset halt' -c 'flash erase_sector 0 0 last' -c exit + +# Sometimes the STM32 will get it's protection bits set for unknown reasons. Unlock it with this command +unlock-stlink2: + $(OPENOCD) -c 'reset halt' -c 'stm32f2x unlock 0' + +flash-bmp: all arm-none-eabi-gdb --ex 'target extended-remote $(BMP_PORT)' \ --ex 'monitor swdp_scan' \ --ex 'attach 1' \ @@ -30,80 +52,20 @@ flashbmp: all --ex 'quit' \ $(FIRMWARE) -gdb: all - arm-none-eabi-gdb $(FIRMWARE) -x openocd.gdbinit - -dfu: all - python ../tools/odrivetool $(if $(value SERIAL_NUMBER),--serial-number $(SERIAL_NUMBER),) dfu $(FIRMWARE_HEX) - -bmp: all +gdb-bmp: all arm-none-eabi-gdb --ex 'target extended-remote /dev/stlink' \ --ex 'monitor swdp_scan' \ --ex 'attach 1' \ --ex 'load' $(FIRMWARE) -# Erase entire STM32 -erase: - $(OPENOCD) -c init -c reset\ halt -c flash\ erase_address\ 0x8000000\ 0x100000 -c reset\ run -c exit +dfu: all + python ../tools/odrivetool $(if $(value SERIAL_NUMBER),--serial-number $(SERIAL_NUMBER),) dfu $(FIRMWARE_HEX) -# Erase all configuration from the ODrive -erase_config: - $(OPENOCD) -c init -c reset\ halt -c flash\ erase_address\ 0x80C0000\ 0x40000 -c reset\ init -c reset\ run -c exit - -# Sometimes the STM32 will get it's protection bits set for unknown reasons. Unlock it with this command -unlock: - $(OPENOCD) -c init -c reset\ halt -c stm32f2x\ unlock\ 0 - -# The one-time programmable memory stores the board version -# has the following format: -# - OTP format version (0xFE: version 1) -# - vendor ID (01: ODrive Robotics - do not use this on custom incompatible hardware!) -# - product ID (01: ODrive) -# - hardware major version -# - hardware minor version -# - hardware variant (equal to the board nominal voltage) -# Bits in the OTP can only ever be set to 0 but never back to 1. -# Therefore do not try to run this command on the same board -# twice with different data. -# -# This OpenOCD command is intended for a STM32F405 and does the following: -# FLASH_KEYR = 0x45670123; // unlock FLASH_CR -# FLASH_KEYR = 0xCDEF89AB; // unlock FLASH_CR -# FLASH_CR = (1 << FLASH_CR_PG); // unlock flash memory -# [write OTP] -write_otp: -ifeq ($(OTP_CONFIRM),TRUE) - # Data: - $(OPENOCD) \ - -c init \ - -c 'reset halt' \ - -c 'mww 0x40023C04 0x45670123' \ - -c 'mww 0x40023C04 0xCDEF89AB' \ - -c 'mww 0x40023C10 0x00000001' -c 'sleep 10' \ - -c 'mwb 0x1fff7800 0xFE' -c 'sleep 10' \ - -c 'mwb 0x1fff7801 0x01' -c 'sleep 10' \ - -c 'mwb 0x1fff7802 0x01' -c 'sleep 10' \ - -c 'mwb 0x1fff7803 3' -c 'sleep 10' \ - -c 'mwb 0x1fff7804 6' -c 'sleep 10' \ - -c 'mwb 0x1fff7805 56' -c 'sleep 10' \ - -c 'reset run' \ - -c exit - @echo "OK" -else - @echo "The one-time programmable memory can only be" - @echo "written ONCE on every board (what a surprise)." - @echo "If you're on an ODrive v3.5 or later we already did this for you." - @echo "Otherwise, if you're mentally ready for this irreversible action," - @echo "take the following steps:" - @echo " 1. open the Makefile and look at the write_otp target" - @echo " 2. understand the structure of the OTP" - @echo " 3. edit the bytes that are written to match your board version" - @echo "Run this command again, this time with OTP_CONFIRM=TRUE appended" - @echo "to the command in the terminal" -endif - -clean: - -rm -fR .dep $(BUILD_DIR) - -.PHONY: all flash gdb dfu bmp clean erase_config +flash: flash-stlink2 +gdb: gdb-stlink2 +erase: erase-stlink2 +unlock: unlock-stlink2 +.PHONY: stlink2-config flash-stlink2 gdb-stlink2 erase-stlink2 unlock-stlink2 +.PHONY: flash-bmp gdb-bmp +.PHONY: all clean flash gdb erase unlock dfu diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 17b5aa01..16689464 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -202,27 +202,11 @@ public: const uint64_t& serial_number_ = ::serial_number; -#if defined(STM32F405xx) - // Determine start address of the OTP struct: - // The OTP is organized into 16-byte blocks. - // If the first block starts with "0xfe" we use the first block. - // If the first block starts with "0x00" and the second block starts with "0xfe", - // we use the second block. This gives the user the chance to screw up once. - // If none of the above is the case, we consider the OTP invalid (otp_ptr will be NULL). - const uint8_t* otp_ptr = - (*(uint8_t*)FLASH_OTP_BASE == 0xfe) ? (uint8_t*)FLASH_OTP_BASE : - (*(uint8_t*)FLASH_OTP_BASE != 0x00) ? NULL : - (*(uint8_t*)(FLASH_OTP_BASE + 0x10) != 0xfe) ? NULL : - (uint8_t*)(FLASH_OTP_BASE + 0x10); - - // Read hardware version from OTP if available, otherwise fall back - // to software defined version. - const uint8_t hw_version_major_ = otp_ptr ? otp_ptr[3] : HW_VERSION_MAJOR; - const uint8_t hw_version_minor_ = otp_ptr ? otp_ptr[4] : HW_VERSION_MINOR; - const uint8_t hw_version_variant_ = otp_ptr ? otp_ptr[5] : HW_VERSION_VOLTAGE; -#else -#error "not implemented" -#endif + // Hardware version is compared with OTP on startup to ensure that we're + // running on the right board version. + const uint8_t hw_version_major_ = HW_VERSION_MAJOR; + const uint8_t hw_version_minor_ = HW_VERSION_MINOR; + const uint8_t hw_version_variant_ = HW_VERSION_VOLTAGE; // the corresponding macros are defined in the autogenerated version.h const uint8_t fw_version_major_ = ::fw_version_major_; diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 30c8ee2c..f4f240b9 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -120,6 +120,10 @@ if tup.getconfig("STRICT") == "true" then FLAGS += '-Werror' end +if tup.getconfig("NO_DRM") == "true" then + FLAGS += '-DNO_DRM' +end + -- C-specific flags FLAGS += board.flags FLAGS += '-D__weak="__attribute__((weak))"' diff --git a/Firmware/find_programmer.sh b/Firmware/find_programmer.sh deleted file mode 100755 index cea57038..00000000 --- a/Firmware/find_programmer.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -openocd -d3 -f board/stm32f4discovery.cfg -c "hla_serial wrong_serial" 2>&1 | \ - xxd -p | \ - tr -d '\n' | \ - sed -n 's/^.*6e756d6265722027\([0-9a-f]*\)2720646f65736e27.*$/\1/p' | sed -e 's/.\{2\}/\\x&/g'; echo diff --git a/Firmware/openocd.gdbinit b/Firmware/openocd.gdbinit deleted file mode 100644 index 08166929..00000000 --- a/Firmware/openocd.gdbinit +++ /dev/null @@ -1,2 +0,0 @@ -target remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x_stlink.cfg" -c "gdb_port pipe; log_output openocd.log" -monitor reset halt From cad2f5fd535602041c571bedd36ed5e4e0933552 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 18 Nov 2020 14:55:25 +0100 Subject: [PATCH 102/124] add Private submodule --- .gitmodules | 4 ++++ Firmware/Private | 1 + 2 files changed, 5 insertions(+) create mode 160000 Firmware/Private diff --git a/.gitmodules b/.gitmodules index e69de29b..ce3f8c38 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "Firmware/Private"] + path = Firmware/Private + url = git@github.com:madcowswe/ODrivePrivate.git + branch = submodule diff --git a/Firmware/Private b/Firmware/Private new file mode 160000 index 00000000..af06bd07 --- /dev/null +++ b/Firmware/Private @@ -0,0 +1 @@ +Subproject commit af06bd07f852d7121cae6608af13b140028e61a0 From 207426aa0f0f38902aed097a717ebd3830e5492c Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 4 Nov 2020 10:42:51 +0100 Subject: [PATCH 103/124] initial ODrive v4.0 support --- Firmware/.vscode/launch.json | 46 ++++- Firmware/Board/v3/Inc/board.h | 2 + Firmware/Drivers/DRV8353/drv8353.cpp | 195 ++++++++++++++++++++++ Firmware/Drivers/DRV8353/drv8353.hpp | 161 ++++++++++++++++++ Firmware/Drivers/STM32/stm32_nvm.c | 14 ++ Firmware/Drivers/STM32/stm32_system.h | 2 + Firmware/Drivers/status_led.cpp | 15 ++ Firmware/Drivers/status_led.hpp | 57 +++++++ Firmware/Drivers/ws2812.hpp | 86 ++++++++++ Firmware/Makefile | 3 + Firmware/MotorControl/low_level.cpp | 8 + Firmware/MotorControl/main.cpp | 51 ++++++ Firmware/MotorControl/utils.hpp | 3 +- Firmware/Tupfile.lua | 12 ++ Firmware/communication/ascii_protocol.cpp | 2 + Firmware/communication/interface_can.cpp | 8 +- Firmware/communication/interface_usb.cpp | 6 + Firmware/odrive-interface.yaml | 40 ++++- analysis/thermistors.py | 2 +- docs/developer-guide.md | 28 ++++ docs/pinout.md | 9 +- docs/resources.md | 56 +++++++ tools/odrive/tests/can_test.py | 9 +- tools/odrive/tests/test_runner.py | 28 +++- tools/odrive/utils.py | 26 +++ 25 files changed, 845 insertions(+), 24 deletions(-) create mode 100644 Firmware/Drivers/DRV8353/drv8353.cpp create mode 100644 Firmware/Drivers/DRV8353/drv8353.hpp create mode 100644 Firmware/Drivers/status_led.cpp create mode 100644 Firmware/Drivers/status_led.hpp create mode 100644 Firmware/Drivers/ws2812.hpp diff --git a/Firmware/.vscode/launch.json b/Firmware/.vscode/launch.json index cc8662d3..d277ce4c 100644 --- a/Firmware/.vscode/launch.json +++ b/Firmware/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cortex-debug", "servertype": "openocd", "request": "launch", - "name": "Debug ODrive - ST-Link", + "name": "Debug ODrive v3.x - ST-Link", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "configFiles": [ "interface/stlink-v2.cfg", @@ -23,7 +23,24 @@ "type": "cortex-debug", "servertype": "openocd", "request": "launch", - "name": "Debug ODrive - ST-Link - FreeRTOS", + "name": "Debug ODrive v4.x - ST-Link", + "executable": "${workspaceRoot}/build/ODriveFirmware.elf", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f7x.cfg", + ], + "openOCDLaunchCommands": [ + "reset_config none separate" + ], + "svdFile": "${workspaceRoot}/Board/v4/STM32F7x.svd", + "cwd": "${workspaceRoot}" + }, + { + // For the Cortex-Debug extension + "type": "cortex-debug", + "servertype": "openocd", + "request": "launch", + "name": "Debug ODrive v3.x - ST-Link - FreeRTOS", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "rtos": "FreeRTOS", "configFiles": [ @@ -35,7 +52,7 @@ }, { // For the Cortex-Debug extension - // ssh -t odrv -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\"" + // ssh -t odrv3 -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink-v2.cfg' '-f' 'target/stm32f4x_stlink.cfg'\"" "type": "cortex-debug", "servertype": "external", "gdbTarget": "localhost:3333", @@ -43,7 +60,7 @@ "load" ], "request": "launch", - "name": "Debug ODrive via external server", + "name": "Debug ODrive v3.x - Remote", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "configFiles": [ "interface/stlink-v2.cfg", @@ -52,12 +69,31 @@ "svdFile": "${workspaceRoot}/Board/v3/STM32F40x.svd", "cwd": "${workspaceRoot}" }, + { + // For the Cortex-Debug extension + // ssh -t odrv4 -L3333:localhost:3333 bash -c "\"openocd '-f' 'interface/stlink.cfg' '-f' 'target/stm32f7x.cfg' -c 'reset_config none separate'\"" + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "localhost:3333", + "preLaunchCommands": [ + "load" + ], + "request": "launch", + "name": "Debug ODrive v4.x - Remote", + "executable": "${workspaceRoot}/build/ODriveFirmware.elf", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f7x.cfg", + ], + "svdFile": "${workspaceRoot}/Board/v4/STM32F722.svd", + "cwd": "${workspaceRoot}" + }, { // For the Cortex-Debug extensions "type": "cortex-debug", "servertype": "bmp", "request": "launch", - "name": "Debug ODrive - Black Magic Probe", + "name": "Debug ODrive v3.x - Black Magic Probe", "executable": "${workspaceRoot}/build/ODriveFirmware.elf", "device": "STM32F4xx", "BMPGDBSerialPort": "${env:BMP_PORT}", diff --git a/Firmware/Board/v3/Inc/board.h b/Firmware/Board/v3/Inc/board.h index a6fbab36..db6adcf7 100644 --- a/Firmware/Board/v3/Inc/board.h +++ b/Firmware/Board/v3/Inc/board.h @@ -35,6 +35,8 @@ // consistent we just leave a gap in the counting scheme. #define GPIO_COUNT (17) +#define CAN_FREQ (2000000UL) + #if HW_VERSION_MINOR >= 5 && HW_VERSION_VOLTAGE >= 48 #define DEFAULT_BRAKE_RESISTANCE (2.0f) // [ohm] #else diff --git a/Firmware/Drivers/DRV8353/drv8353.cpp b/Firmware/Drivers/DRV8353/drv8353.cpp new file mode 100644 index 00000000..a1a06075 --- /dev/null +++ b/Firmware/Drivers/DRV8353/drv8353.cpp @@ -0,0 +1,195 @@ + +#include "drv8353.hpp" +#include "utils.hpp" +#include "cmsis_os.h" +#include "board.h" + +const SPI_InitTypeDef Drv8353::spi_config_ = { + .Mode = SPI_MODE_MASTER, + .Direction = SPI_DIRECTION_2LINES, + .DataSize = SPI_DATASIZE_16BIT, + .CLKPolarity = SPI_POLARITY_LOW, + .CLKPhase = SPI_PHASE_2EDGE, + .NSS = SPI_NSS_SOFT, + .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16, + .FirstBit = SPI_FIRSTBIT_MSB, + .TIMode = SPI_TIMODE_DISABLE, + .CRCCalculation = SPI_CRCCALCULATION_DISABLE, + .CRCPolynomial = 10, +}; + +bool Drv8353::config(float requested_gain, float* actual_gain) { + // Calculate gain setting: Snap down to have equal or larger range as + // requested or largest possible range otherwise + + uint16_t gain_setting = 3; + float gain_choices[] = {5.0f, 10.0f, 20.0f, 40.0f}; + while (gain_setting && (gain_choices[gain_setting] > requested_gain)) { + gain_setting--; + } + + if (actual_gain) { + *actual_gain = gain_choices[gain_setting]; + } + + // For reference: + // Rds(on) of NTMFS5C628NL is ~3mOhm at 160A, 100°C and we have two in parallel + // Rshunt of ODrive v4 is 1mOhm + + RegisterFile new_config; + + new_config.driver_control = + (0b1 << 10) // overcurrent protection of any half bridge shuts down all half bridges + | (0b0 << 9) // enable Vcp and Vgls undervoltage lockout fault + | (0b0 << 8) // enable gate drive fault + | (0b1 << 7) // report overtemperature warning on nFAULT + | (0b00 << 5) // 6x PWM mode + | (0b0 << 4) // [applies to 1x PWM mode only] + | (0b0 << 3) // [applies to 1x PWM mode only] + | (0b0 << 2) // don't coast + | (0b0 << 1) // don't brake + | (0b0 << 0); // don't clear faults + + new_config.gate_drive_hs = + (0b011 << 8) // don't lock registers + | (0b1111 << 4) // 1A source current on high side FET drivers + | (0b1111 << 0); // 2A sink current on high side FET drivers + + new_config.gate_drive_ls = + (0b1 << 10) // clear overcurrent faults at next PWM input or t_retry (whichever comes first) - this has no effect since we use latched overcurrent fault mode + | (0b01 << 8) // 1000 ns peak gate current drive time + | (0b1111 << 4) // 1A source current on low side FET drivers + | (0b1111 << 0); // 2A sink current on low side FET drivers + + new_config.ocp_control = + (0b0 << 10) // retry time for Vds and shunt overcurrent protection: 8ms + | (0b01 << 8) // 100ns deadtime (we configure the STM timer to do 120ns deadtime as well) + | (0b00 << 6) // overcurrent causes a latching fault (no retry) + | (0b10 << 4) // overcurrent deglitch of 4us + | (0b0101 << 0); // Vds trip level 0.25 V (approx. ~133A per MOSFET at 100°C) + + new_config.csa_control = + (0b0 << 10) // measure current across SPx to SNx + | (0b1 << 9) // use Vref/2 as sense amplifier reference voltage + | (0b0 << 8) // measure Vds across SHx to SPx + | (gain_setting << 6) // select gain + | (0b0 << 5) // sense overcurrent fault enabled + | (0b000 << 2) // normal current sense operation on all three phases + | (0b00 << 0); // sense overcurrent protection at 0.25V sense input (corresponds to ~250A) + + bool regs_equal = (regs_.driver_control == new_config.driver_control) + && (regs_.gate_drive_hs == new_config.gate_drive_hs) + && (regs_.gate_drive_ls == new_config.gate_drive_ls) + && (regs_.ocp_control == new_config.ocp_control) + && (regs_.csa_control == new_config.csa_control); + + if (!regs_equal) { + regs_ = new_config; + state_ = kStateUninitialized; + enable_gpio_.write(false); + } + + return true; +} + +bool Drv8353::init() { + uint16_t val; + + if (state_ == kStateReady) { + return true; + } + + // Reset DRV chip. The enable pin also controls the SPI interface, not only + // the driver stages. + enable_gpio_.write(false); + delay_us(100); // t_rst, max = 40us + state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up + enable_gpio_.write(true); + osDelay(2); // t_wake, max = 1ms + + // Write current configuration + bool did_write_regs = write_reg(kRegNameDriverControl, regs_.driver_control) + && write_reg(kRegNameGateDriveHs, regs_.gate_drive_hs) + && write_reg(kRegNameGateDriveLs, regs_.gate_drive_ls) + && write_reg(kRegNameOcpControl, regs_.ocp_control) + && write_reg(kRegNameCsaControl, regs_.csa_control); + if (!did_write_regs) { + return false; + } + + // Wait for configuration to be applied + delay_us(100); + state_ = kStateStartupChecks; + + bool did_read_regs = read_reg(kRegNameDriverControl, &val) && (val == regs_.driver_control) + && read_reg(kRegNameGateDriveHs, &val) && (val == regs_.gate_drive_hs) + && read_reg(kRegNameGateDriveLs, &val) && (val == regs_.gate_drive_ls) + && read_reg(kRegNameOcpControl, &val) && (val == regs_.ocp_control) + && read_reg(kRegNameCsaControl, &val) && (val == regs_.csa_control); + if (!did_read_regs) { + return false; + } + + + if (get_error() != FaultType_NoFault) { + return false; + } + + // There could have been an nFAULT edge meanwhile. In this case we shouldn't + // consider the driver ready. + CRITICAL_SECTION() { + if (state_ == kStateStartupChecks) { + state_ = kStateReady; + } + } + + return state_ == kStateReady; +} + +void Drv8353::do_checks() { + if (state_ != kStateUninitialized && !nfault_gpio_.read()) { + state_ = kStateUninitialized; + } +} + +bool Drv8353::is_ready() { + return state_ == kStateReady; +} + +Drv8353::FaultType_e Drv8353::get_error() { + uint16_t fault1, fault2; + + if (!read_reg(kRegNameFaultStatus1, &fault1) || + !read_reg(kRegNameFaultStatus2, &fault2)) { + return (FaultType_e)0xffffffff; + } + + return (FaultType_e)((uint32_t)fault1 | ((uint32_t)fault2 << 16)); +} + +bool Drv8353::read_reg(const RegName_e regName, uint16_t* data) { + tx_buf_ = build_ctrl_word(DRV8353_CtrlMode_Read, regName, 0); + rx_buf_ = 0xffff; + if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) { + return false; + } + + delay_us(1); + + if (data) { + *data = rx_buf_ & 0x07FF; + } + + return true; +} + +bool Drv8353::write_reg(const RegName_e regName, const uint16_t data) { + // Do blocking write + tx_buf_ = build_ctrl_word(DRV8353_CtrlMode_Write, regName, data); + if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { + return false; + } + delay_us(1); + + return true; +} diff --git a/Firmware/Drivers/DRV8353/drv8353.hpp b/Firmware/Drivers/DRV8353/drv8353.hpp new file mode 100644 index 00000000..b2ad72e6 --- /dev/null +++ b/Firmware/Drivers/DRV8353/drv8353.hpp @@ -0,0 +1,161 @@ +#ifndef __DRV8353_HPP +#define __DRV8353_HPP + +#include "stdbool.h" +#include "stdint.h" + +#include +#include +#include + + +class Drv8353 : public GateDriverBase, public OpAmpBase { +public: + typedef enum { + FaultType_NoFault = (0 << 0), + + // Fault Status Register 1 + FaultType_FAULT = (1 << 10), + FaultType_VDS_OCP = (1 << 9), + FaultType_GDF = (1 << 8), + FaultType_UVLO = (1 << 7), + FaultType_OTSD = (1 << 6), + FaultType_VDS_HA = (1 << 5), + FaultType_VDS_LA = (1 << 4), + FaultType_VDS_HB = (1 << 3), + FaultType_VDS_LB = (1 << 2), + FaultType_VDS_HC = (1 << 1), + FaultType_VDS_LC = (1 << 0), + + // Fault Status Register 2 + FaultType_SA_OC = (1 << 26), + FaultType_SB_OC = (1 << 25), + FaultType_SC_OC = (1 << 24), + FaultType_OTW = (1 << 23), + FaultType_GDUV = (1 << 22), + FaultType_VGS_HA = (1 << 21), + FaultType_VGS_LA = (1 << 20), + FaultType_VGS_HB = (1 << 19), + FaultType_VGS_LB = (1 << 18), + FaultType_VGS_HC = (1 << 17), + FaultType_VGS_LC = (1 << 16), + } FaultType_e; + + Drv8353(Stm32SpiArbiter* spi_arbiter, Stm32Gpio ncs_gpio, + Stm32Gpio enable_gpio, Stm32Gpio nfault_gpio) + : spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio), + enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {} + + /** + * @brief Prepares the gate driver's configuration. + * + * If the gate driver was in ready state and the new configuration is + * different from the old one then the gate driver will exit ready state. + * + * In any case changes to the configuration only take effect with a call to + * init(). + */ + bool config(float requested_gain, float* actual_gain); + + /** + * @brief Initializes the gate driver to the configuration prepared with + * config(). + * + * Returns true on success or false otherwise (e.g. if the gate driver is + * not connected or not powered or if config() was not yet called). + */ + bool init(); + + /** + * @brief Monitors the nFAULT pin. + * + * This must be run at an interval of <8ms from the moment the init() + * functions starts to run, otherwise it's possible that a temporary power + * loss is missed, leading to unwanted register values. + * In case of power loss the nFAULT pin can be low for as little as 8ms. + */ + void do_checks(); + + /** + * @brief Returns true if and only if the DRV8353 chip is in an initialized + * state and ready to do switching and current sensor opamp operation. + */ + bool is_ready() final; + + /** + * @brief This has no effect on this driver chip because the drive stages are + * always enabled while the chip is initialized + */ + bool set_enabled(bool enabled) final { return true; } + + FaultType_e get_error(); + + float get_midpoint() final { + return 0.5f; // [V] + } + + float get_max_output_swing() final { + return 1.35f / 1.65f; // +-1.35V, normalized from a scale of +-1.65V to +-0.5 + } + +private: + enum CtrlMode_e { + DRV8353_CtrlMode_Read = 1 << 15, //!< Read Mode + DRV8353_CtrlMode_Write = 0 << 15 //!< Write Mode + }; + + enum RegName_e { + kRegNameFaultStatus1 = (0 << 11), + kRegNameFaultStatus2 = (1 << 11), + kRegNameDriverControl = (2 << 11), + kRegNameGateDriveHs = (3 << 11), + kRegNameGateDriveLs = (4 << 11), + kRegNameOcpControl = (5 << 11), + kRegNameCsaControl = (6 << 11) + }; + + struct RegisterFile { + uint16_t driver_control; + uint16_t gate_drive_hs; + uint16_t gate_drive_ls; + uint16_t ocp_control; + uint16_t csa_control; + }; + + static inline uint16_t build_ctrl_word(const CtrlMode_e ctrlMode, + const RegName_e regName, + const uint16_t data) { + return ctrlMode | regName | (data & 0x07FF); + } + + /** @brief Reads data from a DRV8353 register */ + bool read_reg(const RegName_e regName, uint16_t* data); + + /** @brief Writes data to a DRV8353 register. There is no check if the write succeeded. */ + bool write_reg(const RegName_e regName, const uint16_t data); + + static const SPI_InitTypeDef spi_config_; + + // Configuration + Stm32SpiArbiter* spi_arbiter_; + Stm32Gpio ncs_gpio_; + Stm32Gpio enable_gpio_; + Stm32Gpio nfault_gpio_; + + RegisterFile regs_; //!< Current configuration. If is_ready_ is + //!< true then this can be considered consistent + //!< with the actual file on the DRV8353 chip. + + // We don't put these buffers on the stack because we place the stack in + // a RAM section which cannot be used by DMA. + uint16_t tx_buf_, rx_buf_; + + enum { + kStateUninitialized, + kStateStartupChecks, + kStateReady, + } state_ = kStateUninitialized; +}; + + +#endif // __DRV8353_HPP diff --git a/Firmware/Drivers/STM32/stm32_nvm.c b/Firmware/Drivers/STM32/stm32_nvm.c index 8d59f991..3d31ce18 100644 --- a/Firmware/Drivers/STM32/stm32_nvm.c +++ b/Firmware/Drivers/STM32/stm32_nvm.c @@ -48,6 +48,20 @@ #define FLASH_SECTOR_B_BASE (const volatile uint8_t*)0x80E0000UL #define FLASH_SECTOR_B_SIZE 0x20000UL +#elif defined(STM32F722xx) + +#include +#include + +// refer to page 68 of datasheet: +// https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf +#define FLASH_SECTOR_A FLASH_SECTOR_1 +#define FLASH_SECTOR_A_BASE (const volatile uint8_t*)0x8004000UL +#define FLASH_SECTOR_A_SIZE 0x4000UL +#define FLASH_SECTOR_B FLASH_SECTOR_2 +#define FLASH_SECTOR_B_BASE (const volatile uint8_t*)0x8008000UL +#define FLASH_SECTOR_B_SIZE 0x4000UL + #else #error "unknown flash sector size" #endif diff --git a/Firmware/Drivers/STM32/stm32_system.h b/Firmware/Drivers/STM32/stm32_system.h index e065cb24..42727d53 100644 --- a/Firmware/Drivers/STM32/stm32_system.h +++ b/Firmware/Drivers/STM32/stm32_system.h @@ -3,6 +3,8 @@ #if defined(STM32F405xx) #include +#elif defined(STM32F722xx) +#include #else #error "unknown STM32 microcontroller" #endif diff --git a/Firmware/Drivers/status_led.cpp b/Firmware/Drivers/status_led.cpp new file mode 100644 index 00000000..e3942a5b --- /dev/null +++ b/Firmware/Drivers/status_led.cpp @@ -0,0 +1,15 @@ + +#include "status_led.hpp" +#include + +void I2sRgbLed::init() { + uint16_t init_buf[1] = {0}; + HAL_I2S_Transmit_DMA(&hi2s1, init_buf, 1); + while (hi2s1.State != HAL_I2S_STATE_READY); +} + +void I2sRgbLed::set_color(rgb_t color) { + rgb_t stripe[1] = {color}; + I2sWs2812Encoder::encode(stripe, 1, 0, i2s_buf_, kI2sBufLen); + HAL_I2S_Transmit_DMA(&hi2s1, i2s_buf_, kI2sBufLen); +} diff --git a/Firmware/Drivers/status_led.hpp b/Firmware/Drivers/status_led.hpp new file mode 100644 index 00000000..ea229957 --- /dev/null +++ b/Firmware/Drivers/status_led.hpp @@ -0,0 +1,57 @@ +#ifndef __STATUS_LED_HPP +#define __STATUS_LED_HPP + +#include +#include + +struct rgb_t { + rgb_t() { + val = 0; + } + rgb_t(uint8_t r, uint8_t g, uint8_t b) { + val = (r << 16) | (g << 8) | (b << 0); + } + rgb_t(uint32_t val) : val(val) {} + + uint8_t get_r() { return (val >> 16) & 0xff; } + uint8_t get_g() { return (val >> 8) & 0xff; } + uint8_t get_b() { return (val >> 0) & 0xff; } + + template + static rgb_t mix(rgb_t color0, rgb_t color1, uint32_t ratio) { + uint32_t ratio1 = (ratio >= max_val) ? (max_val - 1) : ratio; + uint32_t ratio0 = max_val - ratio1; + return rgb_t{ + (uint8_t)(((uint32_t)color0.get_r() * ratio0 + (uint32_t)color1.get_r() * ratio1) / max_val), + (uint8_t)(((uint32_t)color0.get_g() * ratio0 + (uint32_t)color1.get_g() * ratio1) / max_val), + (uint8_t)(((uint32_t)color0.get_b() * ratio0 + (uint32_t)color1.get_b() * ratio1) / max_val), + }; + } + + uint32_t val; +}; + +struct Ws2812EncoderTraits { + static constexpr uint32_t kBaudrate = 3310345ULL; + static constexpr uint16_t kSymbolHigh = 0b1110; // 906ns on, 302ns off + static constexpr uint16_t kSymbolLow = 0b1000; // 302ns on, 906ns off + static constexpr size_t kNumBitsPerSymbol = 4; + static constexpr size_t kBitsPerLed = 24; + static constexpr size_t kNumLeds = 1; + using TColor = rgb_t; + using TEncoded = uint16_t; + static uint32_t get_bits(TColor color) { return color.val; } +}; + +using I2sWs2812Encoder = Ws2812Encoder; +constexpr size_t kI2sBufLen = ((I2sWs2812Encoder::get_total_encoded_words(1) + 1) >> 1) << 1; + +class I2sRgbLed { +public: + void init(); + void set_color(rgb_t color); +private: + uint16_t i2s_buf_[kI2sBufLen]; +}; + +#endif // __STATUS_LED_HPP \ No newline at end of file diff --git a/Firmware/Drivers/ws2812.hpp b/Firmware/Drivers/ws2812.hpp new file mode 100644 index 00000000..2a9b392c --- /dev/null +++ b/Firmware/Drivers/ws2812.hpp @@ -0,0 +1,86 @@ +#ifndef __WS2812_HPP +#define __WS2812_HPP + +#include +#include + +/** + * @tparam TTraits::TColor: The type representing a single LED's color + * @tparam TTraits::TEncoded: The data type of the encoded bitstream. + * Typically uint8_t, but can also have a different word size. + * @tparam TTraits::convert: A function that converts an instance of TTraits::TColor + * into the bit representation that should be sent out. + * kBitsPerLed bits are sent out. + * If the returned type has a larger size, it should be left-padded (MSBs + * ignored) + * The MSB (after padding) is sent out first (after padding). + */ +template +struct Ws2812Encoder { + using TEncoded = typename TTraits::TEncoded; + using TColor = typename TTraits::TColor; + + static constexpr size_t kResetTimeUs = 55; // officially 50us, but that doesn't always work + static constexpr size_t kResetBits = (kResetTimeUs * TTraits::kBaudrate) / 1000000ULL; + static constexpr size_t kEncodedWordSize = CHAR_BIT * sizeof(TEncoded); + + static constexpr size_t get_total_encoded_bits(size_t num_leds) { + return num_leds * TTraits::kBitsPerLed * TTraits::kNumBitsPerSymbol + kResetBits; + } + static constexpr size_t get_total_encoded_words(size_t num_leds) { + return (get_total_encoded_bits(num_leds) + kEncodedWordSize - 1) / kEncodedWordSize; + } + + template + static void encode(TColor* colors, size_t num_colors, size_t encoded_offset, TEncoded* encoded_buffer, size_t encoded_buffer_length); +}; + + + +/** + * @brief Encodes an array of colors into a bitstream that can be sent over a + * real-time bit generator like I2S or SPI in order to control a WS2812-type LED chain. + * + * The bits must be sent out MSB-first to generate the correct wave form. + * + * @tparam WrapAround: if true, the encoder wraps around to the first LED when + * the end of the stream is reached useful for continuous data streams. + * If false, the encoded buffer is padded with zeros. + * @param encoded_offset: The position in the encoded stream, indicated in number of encoded words. + * @param encoded_buffer: Buffer where the encoded bit stream will be written. + */ +template +template +void Ws2812Encoder::encode(TColor* colors, size_t num_colors, size_t encoded_offset, TEncoded* encoded_buffer, size_t encoded_buffer_length) { + size_t total_encoded_bits = get_total_encoded_bits(num_colors); + + for (size_t i2s_word_id = 0; i2s_word_id < encoded_buffer_length; ++i2s_word_id) { + uint16_t i2s_word = 0; + + for (size_t i2s_bit_id = 0; i2s_bit_id < kEncodedWordSize; ++i2s_bit_id) { + size_t bitpos = (i2s_word_id + encoded_offset) * kEncodedWordSize + i2s_bit_id; + + if (WrapAround) { + bitpos = bitpos % total_encoded_bits; + } + + size_t symbol_bit_id = TTraits::kNumBitsPerSymbol - (bitpos % TTraits::kNumBitsPerSymbol) - 1; + size_t led_bit_id = TTraits::kBitsPerLed - ((bitpos / TTraits::kNumBitsPerSymbol) % TTraits::kBitsPerLed) - 1; + size_t led_id = (bitpos / TTraits::kNumBitsPerSymbol) / TTraits::kBitsPerLed; + + if (led_id < num_colors) { + auto color = TTraits::get_bits(colors[led_id]); + uint16_t symbol = ((color >> led_bit_id) & 1) ? TTraits::kSymbolHigh : TTraits::kSymbolLow; + + if ((symbol >> symbol_bit_id) & 1) { + i2s_word |= (1 << (kEncodedWordSize - i2s_bit_id - 1)); + } + } + } + + encoded_buffer[i2s_word_id] = i2s_word; + } +} + + +#endif // __WS2812_HPP \ No newline at end of file diff --git a/Firmware/Makefile b/Firmware/Makefile index 82cf9250..426d983f 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -12,6 +12,9 @@ include tup.config # source build configuration to get CONFIG_BOARD_VERSION ifneq (,$(findstring v3.,$(CONFIG_BOARD_VERSION))) OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f4x.cfg -c init GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f4x.cfg" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt' +else ifneq (,$(findstring v4.,$(CONFIG_BOARD_VERSION))) + OPENOCD := openocd -f interface/stlink.cfg $(PROGRAMMER_CMD) -f target/stm32f7x.cfg -c 'reset_config none separate' -c init + GDB := arm-none-eabi-gdb --ex 'target extended-remote | openocd -f "interface/stlink-v2.cfg" -f "target/stm32f7x.cfg" -c "reset_config none separate" -c "gdb_port pipe; log_output openocd.log"' --ex 'monitor reset halt' else $(error unknown board version) endif diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index f0a2bb64..aee91b8f 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -80,8 +80,10 @@ void safety_critical_arm_brake_resistor() { axes[i].motor_.I_bus_ = 0.0f; } brake_resistor_armed = true; +#if HW_VERSION_MAJOR == 3 htim2.Instance->CCR3 = 0; htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; +#endif } } @@ -94,8 +96,10 @@ void safety_critical_disarm_brake_resistor() { CRITICAL_SECTION() { brake_resistor_armed = false; +#if HW_VERSION_MAJOR == 3 htim2.Instance->CCR3 = 0; htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; +#endif } // Check necessary to prevent infinite recursion @@ -115,6 +119,7 @@ void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t hig CRITICAL_SECTION() { if (brake_resistor_armed) { +#if HW_VERSION_MAJOR == 3 // Safe update of low and high side timings // To avoid race condition, first reset timings to safe state // ch3 is low side, ch4 is high side @@ -122,6 +127,7 @@ void safety_critical_apply_brake_resistor_timings(uint32_t low_off, uint32_t hig htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; htim2.Instance->CCR3 = low_off; htim2.Instance->CCR4 = high_on; +#endif } } } @@ -162,10 +168,12 @@ void start_adc_pwm() { // Start brake resistor PWM in floating output configuration +#if HW_VERSION_MAJOR == 3 htim2.Instance->CCR3 = 0; htim2.Instance->CCR4 = TIM_APB1_PERIOD_CLOCKS + 1; HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4); +#endif if (odrv.config_.enable_brake_resistor) { safety_critical_arm_brake_resistor(); diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 834ce743..d04ea747 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -34,6 +34,55 @@ ODrive odrv{}; ConfigManager config_manager; +class StatusLedController { +public: + void update(); +}; + +StatusLedController status_led_controller; + +void StatusLedController::update() { +#if HW_VERSION_MAJOR == 4 + uint32_t t = HAL_GetTick(); + + bool is_booting = std::any_of(axes.begin(), axes.end(), [](Axis& axis){ + return axis.current_state_ == Axis::AXIS_STATE_UNDEFINED; + }); + + if (is_booting) { + return; + } + + bool is_armed = std::any_of(axes.begin(), axes.end(), [](Axis& axis){ + return axis.motor_.is_armed_; + }); + bool any_error = odrv.any_error(); + + if (is_armed) { + // Fast blue pulsating + const uint32_t period_ms = 256; + const uint8_t min_brightness = 0; + const uint8_t max_brightness = 255; + const uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + status_led.set_color(rgb_t{(uint8_t)(any_error ? brightness / 2 : 0), 0, (uint8_t)brightness}); + } else if (any_error) { + // Red pulsating + const uint32_t period_ms = 1024; + const uint8_t min_brightness = 0; + const uint8_t max_brightness = 255; + const uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + status_led.set_color(rgb_t{(uint8_t)brightness, 0, 0}); + } else { + // Slow green pulsating + const uint32_t period_ms = 2048; + const uint8_t min_brightness = 16; + const uint8_t max_brightness = 128; + const uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + status_led.set_color(rgb_t{0, (uint8_t)brightness, 0}); + } +#endif +} + static bool config_read_all() { bool success = board_read_config() && config_manager.read(&odrv.config_) && @@ -217,6 +266,8 @@ void vApplicationIdleHook(void) { odrv.system_stats_.prio_uart = osThreadGetPriority(uart_thread); odrv.system_stats_.prio_startup = osThreadGetPriority(defaultTaskHandle); odrv.system_stats_.prio_can = osThreadGetPriority(odCAN->thread_id_); + + status_led_controller.update(); } } diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 8eb044f9..67c1fcea 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -5,6 +5,7 @@ #include #include #include +#include /** * @brief Flash size register address @@ -123,7 +124,7 @@ inline float wrap_pm(float x, float y) { #ifdef FPU_FPV4 float intval = (float)round_int(x / y); #else - float intval = nearbyint(x / y); + float intval = nearbyintf(x / y); #endif return x - intval * y; } diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index f4f240b9..d6a8aec3 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -35,6 +35,14 @@ board_v3 = { ldflags = {'-TBoard/v3/STM32F405RGTx_FLASH.ld', '-LBoard/v3/Drivers/CMSIS/Lib', '-larm_cortexM4lf_math', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'} } +board_v4 = { + dir = 'Board/v4', + root_interface = 'ODrive4', + sources = {'Drivers/DRV8353/drv8353.cpp', 'Drivers/status_led.cpp', 'Board/v4/board.cpp', 'lockdown/rsa_embedded/rsa.c', 'lockdown/sha-2/sha-256.c',}, + flags = {'-DSTM32F722xx', '-DARM_MATH_CM7', '-mcpu=cortex-m7', '-mfpu=fpv5-sp-d16'}, + ldflags = {'-TBoard/v4/STM32F722RETx_FLASH.ld', '-LBoard/v4/Drivers/CMSIS/Lib/GCC', '-larm_cortexM7lfsp_math', '-mcpu=cortex-m7', '-mfpu=fpv5-sp-d16'} +} + -- Switch between board versions boardversion = tup.getconfig("BOARD_VERSION") if boardversion == "v3.1" then @@ -73,6 +81,10 @@ elseif boardversion == "v3.6-56V" then board = board_v3 board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6" board.flags += "-DHW_VERSION_VOLTAGE=56" +elseif boardversion == "v4.0-56V" then + board = board_v4 + board.flags += "-DHW_VERSION_MAJOR=4 -DHW_VERSION_MINOR=0" + board.flags += "-DHW_VERSION_VOLTAGE=56" elseif boardversion == "" then error("board version not specified - take a look at tup.config.default") else diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 811efac6..eea1efb2 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -30,6 +30,8 @@ #if HW_VERSION_MAJOR == 3 static Introspectable root_obj = ODrive3TypeInfo::make_introspectable(odrv); +#elif HW_VERSION_MAJOR == 4 +static Introspectable root_obj = ODrive4TypeInfo::make_introspectable(odrv); #endif /* Private function prototypes -----------------------------------------------*/ diff --git a/Firmware/communication/interface_can.cpp b/Firmware/communication/interface_can.cpp index d1c748c1..8a557878 100644 --- a/Firmware/communication/interface_can.cpp +++ b/Firmware/communication/interface_can.cpp @@ -135,25 +135,25 @@ bool ODriveCAN::read(can_Message_t &rxmsg) { void ODriveCAN::set_baud_rate(uint32_t baudRate) { switch (baudRate) { case CAN_BAUD_125K: - handle_->Init.Prescaler = 16; // 21 TQ's + handle_->Init.Prescaler = CAN_FREQ / 125000UL; config_.baud_rate = baudRate; reinit_can(); break; case CAN_BAUD_250K: - handle_->Init.Prescaler = 8; // 21 TQ's + handle_->Init.Prescaler = CAN_FREQ / 250000UL; config_.baud_rate = baudRate; reinit_can(); break; case CAN_BAUD_500K: - handle_->Init.Prescaler = 4; // 21 TQ's + handle_->Init.Prescaler = CAN_FREQ / 500000UL; config_.baud_rate = baudRate; reinit_can(); break; case CAN_BAUD_1000K: - handle_->Init.Prescaler = 2; // 21 TQ's + handle_->Init.Prescaler = CAN_FREQ / 1000000UL; config_.baud_rate = baudRate; reinit_can(); break; diff --git a/Firmware/communication/interface_usb.cpp b/Firmware/communication/interface_usb.cpp index 28171eda..233fb9a9 100644 --- a/Firmware/communication/interface_usb.cpp +++ b/Firmware/communication/interface_usb.cpp @@ -35,7 +35,13 @@ public: usb_stats_.tx_overrun_cnt++; } // transmit packet +#if HW_VERSION_MAJOR == 3 // TODO: remove preprocessor switch uint8_t status = CDC_Transmit_FS( +#elif HW_VERSION_MAJOR == 4 + uint8_t status = CDC_Transmit_HS( +#else +#error "not supported" +#endif const_cast(buffer) /* casting this const away is safe because... well... it's not actually. Stupid STM. */, length, endpoint_pair_); if (status != USBD_OK) { diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index d88af102..5bfc1ff5 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -777,7 +777,7 @@ interfaces: value, the motor gets disarmed immediately. Note that this feature is only works on devices with three current - sensors. + sensors (e.g. ODrive v4). dc_calib_tau: float32 ODrive.Oscilloscope: @@ -1110,6 +1110,44 @@ interfaces: axis0: {type: ODrive.Axis, c_name: get_axis(0)} axis1: {type: ODrive.Axis, c_name: get_axis(1)} + ODrive4: + c_is_class: True + implements: ODrive + attributes: + config: + c_is_class: False + implements: ODrive.Config + attributes: + # TODO: add support for arrays + gpio1_mode: {type: ODrive.GpioMode, doc: Mode of GPIO1 (changes take effect after reboot), c_name: 'gpio_modes[1]'} + gpio2_mode: {type: ODrive.GpioMode, doc: Mode of GPIO2 (changes take effect after reboot), c_name: 'gpio_modes[2]'} + gpio3_mode: {type: ODrive.GpioMode, doc: Mode of GPIO3 (changes take effect after reboot), c_name: 'gpio_modes[3]'} + gpio4_mode: {type: ODrive.GpioMode, doc: Mode of GPIO4 (changes take effect after reboot), c_name: 'gpio_modes[4]'} + gpio5_mode: {type: ODrive.GpioMode, doc: Mode of GPIO5 (changes take effect after reboot), c_name: 'gpio_modes[5]'} + gpio6_mode: {type: ODrive.GpioMode, doc: Mode of GPIO6 (changes take effect after reboot), c_name: 'gpio_modes[6]'} + gpio7_mode: {type: ODrive.GpioMode, doc: Mode of GPIO7 (changes take effect after reboot), c_name: 'gpio_modes[7]'} + gpio8_mode: {type: ODrive.GpioMode, doc: Mode of GPIO8 (changes take effect after reboot), c_name: 'gpio_modes[8]'} + gpio9_mode: {type: ODrive.GpioMode, doc: Mode of GPIO9 (changes take effect after reboot), c_name: 'gpio_modes[9]'} + gpio10_mode: {type: ODrive.GpioMode, doc: Mode of GPIO10 (changes take effect after reboot), c_name: 'gpio_modes[10]'} + gpio11_mode: {type: ODrive.GpioMode, doc: Mode of GPIO11 (changes take effect after reboot), c_name: 'gpio_modes[11]'} + gpio12_mode: {type: ODrive.GpioMode, doc: Mode of GPIO12 (changes take effect after reboot), c_name: 'gpio_modes[12]'} + gpio13_mode: {type: ODrive.GpioMode, doc: Mode of GPIO13 (changes take effect after reboot), c_name: 'gpio_modes[13]'} + gpio14_mode: {type: ODrive.GpioMode, doc: Mode of GPIO14 (changes take effect after reboot), c_name: 'gpio_modes[14]'} + gpio15_mode: {type: ODrive.GpioMode, doc: Mode of GPIO15 (changes take effect after reboot), c_name: 'gpio_modes[15]'} + gpio16_mode: {type: ODrive.GpioMode, doc: Mode of GPIO16 (changes take effect after reboot), c_name: 'gpio_modes[16]'} + gpio17_mode: {type: ODrive.GpioMode, doc: Mode of GPIO17 (changes take effect after reboot), c_name: 'gpio_modes[17]'} + gpio18_mode: {type: ODrive.GpioMode, doc: Mode of GPIO18 (changes take effect after reboot), c_name: 'gpio_modes[18]'} + gpio19_mode: {type: ODrive.GpioMode, doc: Mode of GPIO19 (changes take effect after reboot), c_name: 'gpio_modes[19]'} + gpio20_mode: {type: ODrive.GpioMode, doc: Mode of GPIO20 (changes take effect after reboot), c_name: 'gpio_modes[20]'} + gpio21_mode: {type: ODrive.GpioMode, doc: Mode of GPIO21 (changes take effect after reboot), c_name: 'gpio_modes[21]'} + gpio22_mode: {type: ODrive.GpioMode, doc: Mode of GPIO22 (changes take effect after reboot), c_name: 'gpio_modes[22]'} + + gpio14_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[3]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio19_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[2]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio20_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[0]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + gpio21_pwm_mapping: {type: ODrive.Endpoint, c_name: 'pwm_mappings[1]', doc: Make sure the corresponding GPIO is in `GPIO_MODE_PWM`.} + axis0: {type: ODrive.Axis, c_name: get_axis(0)} + valuetypes: ODrive.GpioMode: values: diff --git a/analysis/thermistors.py b/analysis/thermistors.py index 5d6a30e2..e0fc72be 100644 --- a/analysis/thermistors.py +++ b/analysis/thermistors.py @@ -1,7 +1,7 @@ #%% from odrive.utils import calculate_thermistor_coeffs -Rload = 3300 +Rload = 3300 # 2000 for ODrive v4 R_25 = 10000 Beta = 3434 Tmin = 0 diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 6fac13a8..e7643581 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -241,6 +241,34 @@ This happens from time to time. 4. Power on the ODrive 5. Run `make flash` again +### `Warn : Cannot identify target as a STM32 family.` when flashing using openocd + +**Problem:** When I try to flash ODrive v4.1 with `make flash` then I get: +``` +[...] +** Programming Started ** +auto erase enabled +Info : device id = 0x10006452 +Warn : Cannot identify target as a STM32 family. +Error: auto_probe failed +embedded:startup.tcl:487: Error: ** Programming Failed ** +in procedure 'program' +in procedure 'program_error' called at file "embedded:startup.tcl", line 543 +at file "embedded:startup.tcl", line 487 +``` + +**Solution:** +Compile and install a recent version of openocd from source. The latest official release (0.10.0 as of Nov 2020) doesn't support the STM32F722 yet. +``` +sudo apt-get install libtool libusb-1.0 +git clone https://git.code.sf.net/p/openocd/code openocd +cd openocd/ +./bootstrap +./configure --enable-stlink +make +sudo make install +``` + ## Documentation All *.md files in the `docs/` directory of the master branch are served up by GitHub Pages on [this domain](https://docs.odriverobotics.com). diff --git a/docs/pinout.md b/docs/pinout.md index bc30bbdc..047874b2 100644 --- a/docs/pinout.md +++ b/docs/pinout.md @@ -1,5 +1,11 @@ # Pinout +## ODrive v4.1 + +**TODO** + +## ODrive v3.x + | # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART_A` | `GPIO_MODE_UART_B` | `GPIO_MODE_PWM` | `GPIO_MODE_CAN_A` | `GPIO_MODE_I2C_A` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` | |----|---------------|------------------------|-----------------------|--------------------|--------------------|-----------------|------------------|-------------------|------------------|------------------|------------------------| | 0 | _not a pin_ | | | | | | | | | | | @@ -24,7 +30,8 @@ (*) ODrive v3.5 and later
(+) On ODrive v3.5 and later these pins have noise suppression filters. This is useful for step/dir input.
-Notes: +## Notes + * Changes to the pin configuration only take effect after `odrv0.save_configuration()` and `odrv0.reboot()` * Bold font marks the default configuration. * If a GPIO is set to an unsupported mode it will be left uninitialized. diff --git a/docs/resources.md b/docs/resources.md index 4278424a..5212dfb3 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -74,3 +74,59 @@ Take this info with a grain of salt as we might forget to update it from time to | uart | 4096 | 0 | | usb | 4096 | 0 | + +# ODrive v4.0 + +## Interrupt Vectors + + - lowest priority: 15 + - highest priority: 0 + +| # | Name | Prio | +|-----|-------------------------|------| +| -12 | MemoryManagement_IRQn | 0 | +| -11 | BusFault_IRQn | 0 | +| -10 | UsageFault_IRQn | 0 | +| -5 | SVCall_IRQn | 0 | +| -4 | DebugMonitor_IRQn | 0 | +| -2 | PendSV_IRQn | 15 | +| -1 | SysTick_IRQn | 15 | +| 11 | DMA1_Stream0_IRQn | 5 | +| 14 | DMA1_Stream3_IRQn | 5 | +| 15 | DMA1_Stream4_IRQn | 5 | +| 16 | DMA1_Stream5_IRQn | 5 | +| 17 | DMA1_Stream6_IRQn | 5 | +| 18 | ADC_IRQn | 1 | +| 19 | CAN1_TX_IRQn | 6 | +| 20 | CAN1_RX0_IRQn | 6 | +| 21 | CAN1_RX1_IRQn | 6 | +| 22 | CAN1_SCE_IRQn | 6 | +| 26 | TIM1_TRG_COM_TIM11_IRQn | 2 | +| 35 | SPI1_IRQn | 5 | +| 36 | SPI2_IRQn | 5 | +| 38 | USART2_IRQn | 5 | +| 45 | TIM8_TRG_COM_TIM14_IRQn | 0 | +| 47 | DMA1_Stream7_IRQn | 0 | +| 51 | SPI3_IRQn | 5 | +| 59 | DMA2_Stream3_IRQn | 5 | +| 77 | OTG_HS_IRQn | 5 | + +## DMA Streams + + - lowest priority: 0 + - highest priority: 3 + +| Name | Prio | Channel | High Level Func | +|--------------|------|----------------------------------|-----------------| +| DMA1_Stream0 | 1 | 0 (SPI3_RX) | Onboard SPI | +| DMA1_Stream3 | 0 | 0 (SPI2_RX) | Offboard SPI | +| DMA1_Stream4 | 0 | 0 (SPI2_TX) | Offboard SPI | +| DMA1_Stream5 | 0 | 4 (USART2_RX) | UART1 | +| DMA1_Stream6 | 0 | 4 (USART2_TX) | UART1 | +| DMA1_Stream7 | 1 | 0 (SPI3_TX) | Onboard SPI | +| DMA2_Stream0 | 0 | 0 (ADC1) | freerunning ADC | +| DMA2_Stream3 | 0 | 3 (SPI1_TX) | Status LED | + +## Threads + +**TODO** diff --git a/tools/odrive/tests/can_test.py b/tools/odrive/tests/can_test.py index d2a00f9e..10ec2087 100644 --- a/tools/odrive/tests/can_test.py +++ b/tools/odrive/tests/can_test.py @@ -109,8 +109,13 @@ class TestSimpleCAN(): def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, node_id: int, extended_id: bool, logger: Logger): odrive.disable_mappings() - odrive.handle.config.gpio15_mode = GPIO_MODE_CAN_A - odrive.handle.config.gpio16_mode = GPIO_MODE_CAN_A + if yaml['board-version'].startswith("v3."): + odrive.handle.config.gpio15_mode = GPIO_MODE_CAN_A + odrive.handle.config.gpio16_mode = GPIO_MODE_CAN_A + elif yaml['board-version'].startswith("v4.0-"): + pass # CAN pin configuration is hardcoded + else: + raise Exception("unknown board version {}".format(yaml['board-version'])) odrive.handle.config.enable_can_a = True odrive.save_config_and_reboot() diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index f305858d..ae1f147c 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -207,9 +207,16 @@ class ODriveComponent(Component): def __init__(self, yaml: dict): self.handle = None self.yaml = yaml - #self.axes = [ODriveAxisComponent(None), ODriveAxisComponent(None)] - self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0']), ODriveEncoderComponent(self, 1, yaml['encoder1'])] - self.axes = [ODriveAxisComponent(self, 0, yaml['motor0']), ODriveAxisComponent(self, 1, yaml['motor1'])] + + if yaml['board-version'].startswith("v3."): + self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0']), ODriveEncoderComponent(self, 1, yaml['encoder1'])] + self.axes = [ODriveAxisComponent(self, 0, yaml['motor0']), ODriveAxisComponent(self, 1, yaml['motor1'])] + elif yaml['board-version'].startswith("v4.0-"): + self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0'])] + self.axes = [ODriveAxisComponent(self, 0, yaml['motor0'])] + else: + raise Exception("unknown board version {}".format(yaml['board-version'])) + for i in range(1,9): self.__setattr__('gpio' + str(i), Component(self)) self.can = Component(self) @@ -249,12 +256,15 @@ class ODriveComponent(Component): axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] def disable_mappings(self): - self.handle.config.gpio1_pwm_mapping.endpoint = None # here - self.handle.config.gpio2_pwm_mapping.endpoint = None - self.handle.config.gpio3_pwm_mapping.endpoint = None - self.handle.config.gpio4_pwm_mapping.endpoint = None - self.handle.config.gpio3_analog_mapping.endpoint = None - self.handle.config.gpio4_analog_mapping.endpoint = None + if yaml['board-version'].startswith("v3."): + self.handle.config.gpio1_pwm_mapping.endpoint = None + self.handle.config.gpio2_pwm_mapping.endpoint = None + self.handle.config.gpio3_pwm_mapping.endpoint = None + self.handle.config.gpio4_pwm_mapping.endpoint = None + self.handle.config.gpio3_analog_mapping.endpoint = None + self.handle.config.gpio4_analog_mapping.endpoint = None + else: + raise Exception("unknown board version {}".format(yaml['board-version'])) def save_config_and_reboot(self): self.handle.save_configuration() diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index f9313fa3..f7fa7a5e 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -530,6 +530,32 @@ def dump_dma(odrv): ["TIM1_TRIG", "TIM1_CH1", "TIM1_CH2", "TIM1_CH1", "TIM1_CH4/TIM1_TRIG/TIM1_COM", "TIM1_UP", "TIM1_CH3", "-"], ["-", "TIM8_UP", "TIM8_CH1", "TIM8_CH2", "TIM8_CH3", "SPI5_RX", "SPI5_TX", "TIM8_CH4/TIM8_TRIG/TIM8_COM"], ]] + elif odrv.hw_version_major == 4: + dma_functions = [[ + # https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf Table 26 + ["SPI3_RX", "-", "SPI3_RX", "SPI2_RX", "SPI2_TX", "SPI3_TX", "-", "SPI3_TX"], + ["I2C1_RX", "I2C3_RX", "TIM7_UP", "-", "TIM7_UP", "I2C1_RX", "I2C1_TX", "I2C1_TX"], + ["TIM4_CH1", "-", "-", "TIM4_CH2", "-", "-", "TIM4_UP", "TIM4_CH3"], + ["-", "TIM2_UP/TIM2_CH3", "I2C3_RX", "-", "I2C3_TX", "TIM2_CH1", "TIM2_CH2/TIM2_CH4", "TIM2_UP/TIM2_CH4"], + ["UART5_RX", "USART3_RX", "UART4_RX", "USART3_TX", "UART4_TX", "USART2_RX", "USART2_TX", "UART5_TX"], + ["UART8_TX", "UART7_TX", "TIM3_CH4/TIM3_UP", "UART7_RX", "TIM3_CH1/TIM3_TRIG", "TIM3_CH2", "UART8_RX", "TIM3_CH3"], + ["TIM5_CH3/TIM5_UP", "TIM5_CH4/TIM5_TRIG", "TIM5_CH1", "TIM5_CH4/TIM5_TRIG", "TIM5_CH2", "-", "TIM5_UP", "-"], + ["-", "TIM6_UP", "I2C2_RX", "I2C2_RX", "USART3_TX", "DAC1", "DAC2", "I2C2_TX"], + ], [ + # https://www.st.com/resource/en/reference_manual/dm00305990-stm32f72xxx-and-stm32f73xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf Table 27 + ["ADC1", "SAI1_A", "TIM8_CH1/TIM8_CH2/TIM8_CH3", "SAI1_A", "ADC1", "SAI1_B", "TIM1_CH1/TIM1_CH2/TIM1_CH3", "SAI2_B"], + ["-", "-", "ADC2", "ADC2", "SAI1_B", "-", "-", "-"], + ["ADC3", "ADC3", "-", "SPI5_RX", "SPI5_TX", "AES_OUT", "AES_IN", "-"], + ["SPI1_RX", "-", "SPI1_RX", "SPI1_TX", "SAI2_A", "SPI1_TX", "SAI2_B", "QUADSPI"], + ["SPI4_RX", "SPI4_TX", "USART1_RX", "SDMMC1", "-", "USART1_RX", "SDMMC1", "USART1_TX"], + ["-", "USART6_RX", "USART6_RX", "SPI4_RX", "SPI4_TX", "-", "USART6_TX", "USART6_TX"], + ["TIM1_TRIG", "TIM1_CH1", "TIM1_CH2", "TIM1_CH1", "TIM1_CH4/TIM1_TRIG/TIM1_COM", "TIM1_UP", "TIM1_CH3", "-"], + ["-", "TIM8_UP", "TIM8_CH1", "TIM8_CH2", "TIM8_CH3", "SPI5_RX", "SPI5_TX", "TIM8_CH4/TIM8_TRIG/TIM8_COM"], + None, + None, + None, + ["SDMMC2", "-", "-", "-", "-", "SDMMC2", "-", "-"], + ]] print("| Name | Prio | Channel | Configured |") print("|--------------|------|----------------------------------|------------|") From 1d94f11570a17278148d6a485653c966bfc55793 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 4 Nov 2020 10:43:44 +0100 Subject: [PATCH 104/124] initial ODrive v4.1 support --- Firmware/.vscode/c_cpp_properties.json | 18 +++++++++--------- Firmware/Tupfile.lua | 4 ++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 82c77a74..33d6b37a 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -10,9 +10,9 @@ "STM32F405xx", "FPU_FPV4", "USE_HAL_DRIVER", - "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=6", - "HW_VERSION_VOLTAGE=56", + "HW_VERSION_MAJOR=4", + "HW_VERSION_MINOR=1", + "HW_VERSION_VOLTAGE=58", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" @@ -42,9 +42,9 @@ "STM32F405xx", "FPU_FPV4", "USE_HAL_DRIVER", - "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=6", - "HW_VERSION_VOLTAGE=56", + "HW_VERSION_MAJOR=4", + "HW_VERSION_MINOR=1", + "HW_VERSION_VOLTAGE=58", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" @@ -64,9 +64,9 @@ "STM32F405xx", "FPU_FPV4", "USE_HAL_DRIVER", - "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=4", - "HW_VERSION_VOLTAGE=56", + "HW_VERSION_MAJOR=4", + "HW_VERSION_MINOR=1", + "HW_VERSION_VOLTAGE=58", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", "__GNUC__" diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index d6a8aec3..a4612835 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -85,6 +85,10 @@ elseif boardversion == "v4.0-56V" then board = board_v4 board.flags += "-DHW_VERSION_MAJOR=4 -DHW_VERSION_MINOR=0" board.flags += "-DHW_VERSION_VOLTAGE=56" +elseif boardversion == "v4.1-58V" then + board = board_v4 + board.flags += "-DHW_VERSION_MAJOR=4 -DHW_VERSION_MINOR=1" + board.flags += "-DHW_VERSION_VOLTAGE=58" elseif boardversion == "" then error("board version not specified - take a look at tup.config.default") else From d868e7fb53c3bb0a0fe0271c1cc6456b84ff34ae Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 14 Oct 2020 20:22:41 +0200 Subject: [PATCH 105/124] make HWIL tests less hardware-dependent --- docs/testing.md | 29 +++++--- tools/odrive/tests/calibration_test.py | 9 ++- tools/odrive/tests/can_test.py | 6 +- tools/odrive/tests/closed_loop_test.py | 2 +- tools/odrive/tests/fibre_test.py | 2 +- tools/odrive/tests/not_a_test.py | 19 +++-- tools/odrive/tests/pwm_input_test.py | 29 ++++---- tools/odrive/tests/step_dir_test.py | 6 +- tools/odrive/tests/test_runner.py | 40 ++++++----- tools/odrive/tests/uart_ascii_test.py | 50 +++++++------ tools/test-rig-rpi.yaml | 9 +-- tools/test-rig-rpi4.yaml | 98 ++++++++++++++++++++++++++ 12 files changed, 210 insertions(+), 89 deletions(-) create mode 100644 tools/test-rig-rpi4.yaml diff --git a/docs/testing.md b/docs/testing.md index 5ba1160b..720fee2e 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -44,7 +44,7 @@ If your test rig differs, you may be able to run some but not all of the tests. ## How to set up a Raspberry Pi as testing host - 1. Install Raspbian Lite on a Raspberry Pi 4.0. I used the NOOBS installer for this. + 1. Install Raspbian Lite on a Raspberry Pi 4.0. This is easiest if you have a keyboard, mouse and screen (micro-HDMI!). I used the [NOOBS Lite installer](https://www.raspberrypi.org/downloads/noobs/) for this. Paste the ZIP-file's contents onto a FAT32 formatted SD card (fs type `0b` in `fdisk`) and boot it. Then follow the on-screen instructions. 2. Prepare the installation: sudo systemctl enable ssh @@ -52,6 +52,7 @@ If your test rig differs, you may be able to run some but not all of the tests. # Transfer your public key for passwordless SSH. All subsequent steps can be done via SSH. sudo apt-get update sudo apt-get upgrade + # Change /etc/hostname to something meaningful 3. Add the following lines to `/boot/config.txt`: - `enable_uart=1` @@ -66,7 +67,7 @@ If your test rig differs, you may be able to run some but not all of the tests. 6. Install the prerequisites: - sudo apt-get install ipython3 python3-appdirs python3-yaml python3-usb python3-serial python3-can python3-scipy git openocd + sudo apt-get install ipython3 python3-appdirs python3-yaml python3-usb python3-serial python3-can python3-scipy python3-matplotlib python3-ipdb git openocd # Optionally, to be able to compile the firmware: sudo apt-get install gcc-arm-none-eabi @@ -74,30 +75,36 @@ If your test rig differs, you may be able to run some but not all of the tests. sudo apt-get install libfontconfig libxft2 libusb-dev - wget https://downloads.arduino.cc/arduino-1.8.12-linuxarm.tar.xz - tar -xf arduino-1.8.12-linuxarm.tar.xz - wget https://www.pjrc.com/teensy/td_151/TeensyduinoInstall.linuxarm + wget https://downloads.arduino.cc/arduino-1.8.13-linuxarm.tar.xz + tar -xf arduino-1.8.13-linuxarm.tar.xz + wget https://www.pjrc.com/teensy/td_153/TeensyduinoInstall.linuxarm chmod +x TeensyduinoInstall.linuxarm - ./TeensyduinoInstall.linuxarm --dir=arduino-1.8.12 - sudo cp -R arduino-1.8.12 /usr/share/arduino + ./TeensyduinoInstall.linuxarm --dir=arduino-1.8.13 + sudo cp -R arduino-1.8.13 /usr/share/arduino sudo ln -s /usr/share/arduino/arduino /usr/bin/arduino git clone https://github.com/PaulStoffregen/teensy_loader_cli pushd teensy_loader_cli + make sudo cp teensy_loader_cli /usr/bin/ sudo ln -s /usr/bin/teensy_loader_cli /usr/bin/teensy-loader-cli popd + curl https://www.pjrc.com/teensy/49-teensy.rules | sudo tee /etc/udev/rules.d/49-teensy.rules - 8. Add the following lines to `/etc/udev/rules.d/49-stlinkv2`: + 8. Add the following lines to `/etc/udev/rules.d/49-stlinkv2.rules`: SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE:="0666" - 9. `sudo ../../odrivetool udev-setup` + 9. `sudo mkdir /opt/odrivetest && sudo chown $USER /opt/odrivetest` - 10. `sudo udevadm trigger` + 10. At this point you need the ODrive repository. See next section to sync it from your main PC. We assume now that you navigated to `tools/odrive/tests/`. - 11. Run once after every reboot: `sudo ipython3 --pdb test_runner.py -- --setup-host --test-rig-yaml ../../test-rig-rpi.yaml` + 11. `sudo ../../odrivetool udev-setup` + + 12. `sudo udevadm trigger` + + 13. Run once after every reboot: `sudo ipython3 --pdb test_runner.py -- --setup-host --test-rig-yaml ../../test-rig-rpi.yaml` ## SSH testing flow diff --git a/tools/odrive/tests/calibration_test.py b/tools/odrive/tests/calibration_test.py index 3593fd14..476df3ef 100644 --- a/tools/odrive/tests/calibration_test.py +++ b/tools/odrive/tests/calibration_test.py @@ -34,8 +34,7 @@ class TestMotorCalibration(): axis_ctx.handle.motor.config.phase_inductance = 0.0 axis_ctx.handle.motor.config.pre_calibrated = False axis_ctx.handle.config.enable_watchdog = False - axis_ctx.parent.handle.config.brake_resistance = float(axis_ctx.parent.yaml['brake-resistance']) - axis_ctx.parent.handle.config.enable_brake_resistor = True + axis_ctx.parent.handle.config.dc_max_negative_current = -1.0 axis_ctx.parent.handle.clear_errors() @@ -87,7 +86,7 @@ class TestEncoderDirFind(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - for num in range(2): + for num in range(len(odrive.axes)): encoders = testrig.get_connected_components({ 'a': (odrive.encoders[num].a, False), 'b': (odrive.encoders[num].b, False) @@ -131,7 +130,7 @@ class TestEncoderOffsetCalibration(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - for num in range(2): + for num in range(len(odrive.axes)): encoders = testrig.get_connected_components({ 'a': (odrive.encoders[num].a, False), 'b': (odrive.encoders[num].b, False) @@ -180,7 +179,7 @@ class TestEncoderIndexSearch(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - for num in range(2): + for num in range(len(odrive.axes)): encoders = testrig.get_connected_components({ 'a': (odrive.encoders[num].a, False), 'b': (odrive.encoders[num].b, False) diff --git a/tools/odrive/tests/can_test.py b/tools/odrive/tests/can_test.py index 10ec2087..be1ce798 100644 --- a/tools/odrive/tests/can_test.py +++ b/tools/odrive/tests/can_test.py @@ -109,13 +109,13 @@ class TestSimpleCAN(): def run_test(self, odrive: ODriveComponent, canbus: CanInterfaceComponent, node_id: int, extended_id: bool, logger: Logger): odrive.disable_mappings() - if yaml['board-version'].startswith("v3."): + if odrive.yaml['board-version'].startswith("v3."): odrive.handle.config.gpio15_mode = GPIO_MODE_CAN_A odrive.handle.config.gpio16_mode = GPIO_MODE_CAN_A - elif yaml['board-version'].startswith("v4.0-"): + elif odrive.yaml['board-version'].startswith("v4."): pass # CAN pin configuration is hardcoded else: - raise Exception("unknown board version {}".format(yaml['board-version'])) + raise Exception("unknown board version {}".format(odrive.yaml['board-version'])) odrive.handle.config.enable_can_a = True odrive.save_config_and_reboot() diff --git a/tools/odrive/tests/closed_loop_test.py b/tools/odrive/tests/closed_loop_test.py index 241ca442..b21755a8 100644 --- a/tools/odrive/tests/closed_loop_test.py +++ b/tools/odrive/tests/closed_loop_test.py @@ -17,7 +17,7 @@ class TestClosedLoopControlBase(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - for num in range(2): + for num in range(len(odrive.axes)): encoders = testrig.get_connected_components({ 'a': (odrive.encoders[num].a, False), 'b': (odrive.encoders[num].b, False) diff --git a/tools/odrive/tests/fibre_test.py b/tools/odrive/tests/fibre_test.py index 68d08a3c..fc4e4f31 100644 --- a/tools/odrive/tests/fibre_test.py +++ b/tools/odrive/tests/fibre_test.py @@ -23,7 +23,7 @@ class FibreFunctionalTest(): test_assert_eq(odrive.handle.test_property, 0xffffffff) # Test function call - val = odrive.handle.get_adc_voltage(1) + val = odrive.handle.get_adc_voltage(2) # ADC pin on both ODrive 3 and 4 test_assert_within(val, 0.01, 3.29) # Test custom setter (aka property write hook) diff --git a/tools/odrive/tests/not_a_test.py b/tools/odrive/tests/not_a_test.py index 7be3e995..f8430aa8 100644 --- a/tools/odrive/tests/not_a_test.py +++ b/tools/odrive/tests/not_a_test.py @@ -11,17 +11,16 @@ class EncoderPassthrough(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - for num in range(1): - encoders = testrig.get_connected_components({ - 'a': (odrive.encoders[num].a, False), - 'b': (odrive.encoders[num].b, False), - 'z': (odrive.encoders[num].z, False) - }, EncoderComponent) - motors = testrig.get_connected_components(odrive.axes[num], MotorComponent) + encoders = testrig.get_connected_components({ + 'a': (odrive.encoders[0].a, False), + 'b': (odrive.encoders[0].b, False), + 'z': (odrive.encoders[0].z, False) + }, EncoderComponent) + motors = testrig.get_connected_components(odrive.axes[0], MotorComponent) - for motor, encoder in itertools.product(motors, encoders): - if encoder.impl in testrig.get_connected_components(motor): - yield (odrive.axes[num], motor, encoder) + for motor, encoder in itertools.product(motors, encoders): + if encoder.impl in testrig.get_connected_components(motor): + yield (odrive.axes[0], motor, encoder) def run_test(self, axis_ctx: ODriveAxisComponent, motor_ctx: MotorComponent, enc_ctx: EncoderComponent, logger: Logger): logger.debug(f'Encoder {axis_ctx.num} was passed through') diff --git a/tools/odrive/tests/pwm_input_test.py b/tools/odrive/tests/pwm_input_test.py index a79d8aac..c1951c02 100644 --- a/tools/odrive/tests/pwm_input_test.py +++ b/tools/odrive/tests/pwm_input_test.py @@ -49,11 +49,20 @@ class TestPwmInput(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - # Run a separate test for each PWM-capable GPIO. Use different min/max settings for each test. - yield (odrive, 1, -50, 200, list(testrig.get_connected_components(odrive.gpio1, TeensyGpio))) - yield (odrive, 2, 20, 400, list(testrig.get_connected_components(odrive.gpio2, TeensyGpio))) - yield (odrive, 3, -1000, 0, list(testrig.get_connected_components(odrive.gpio3, TeensyGpio))) - yield (odrive, 4, -20000, 20000, list(testrig.get_connected_components(odrive.gpio4, TeensyGpio))) + if odrive.yaml['board-version'].startswith('v3.'): + # Run a separate test for each PWM-capable GPIO. Use different min/max settings for each test. + yield (odrive, 1, -50, 200, list(testrig.get_connected_components(odrive.gpio1, TeensyGpio))) + yield (odrive, 2, 20, 400, list(testrig.get_connected_components(odrive.gpio2, TeensyGpio))) + yield (odrive, 3, -1000, 0, list(testrig.get_connected_components(odrive.gpio3, TeensyGpio))) + yield (odrive, 4, -20000, 20000, list(testrig.get_connected_components(odrive.gpio4, TeensyGpio))) + elif odrive.yaml['board-version'].startswith('v4.'): + # Run a separate test for each PWM-capable GPIO. Use different min/max settings for each test. + yield (odrive, 14, -50, 200, list(testrig.get_connected_components(odrive.gpio14, TeensyGpio))) + yield (odrive, 19, 20, 400, list(testrig.get_connected_components(odrive.gpio19, TeensyGpio))) + yield (odrive, 20, -20000, 20000, list(testrig.get_connected_components(odrive.gpio20, TeensyGpio))) + yield (odrive, 21, -1000, 0, list(testrig.get_connected_components(odrive.gpio21, TeensyGpio))) + else: + raise Exception(f"unknown board version {odrive.yaml['board-version']}") def run_test(self, odrive: ODriveComponent, odrive_gpio_num: int, min_val: float, max_val: float, teensy_gpio: Component, logger: Logger): teensy = teensy_gpio.parent @@ -63,14 +72,8 @@ class TestPwmInput(): logger.debug("Set up PWM input...") odrive.disable_mappings() - pwm_mapping = [ - odrive.handle.config.gpio1_pwm_mapping, - odrive.handle.config.gpio2_pwm_mapping, - odrive.handle.config.gpio3_pwm_mapping, - odrive.handle.config.gpio4_pwm_mapping - ][odrive_gpio_num - 1] - - setattr(odrive.handle.config, 'gpio' + str(odrive_gpio_num) + '_mode', GPIO_MODE_PWM) + setattr(odrive.handle.config, f'gpio{odrive_gpio_num}_mode', GPIO_MODE_PWM) + pwm_mapping = getattr(odrive.handle.config, f'gpio{odrive_gpio_num}_pwm_mapping') pwm_mapping.endpoint = odrive.handle.axis0.controller._remote_attributes['input_pos'] pwm_mapping.min = min_val pwm_mapping.max = max_val diff --git a/tools/odrive/tests/step_dir_test.py b/tools/odrive/tests/step_dir_test.py index c5d6fc27..153472e7 100644 --- a/tools/odrive/tests/step_dir_test.py +++ b/tools/odrive/tests/step_dir_test.py @@ -32,10 +32,12 @@ class TestStepDir(): yield (odrive.axes[0], 1, gpio_conns[0], 2, gpio_conns[1]) yield (odrive.axes[0], 5, gpio_conns[2], 6, gpio_conns[3]) - yield (odrive.axes[0], 7, gpio_conns[4], 8, gpio_conns[5]) # broken + yield (odrive.axes[0], 7, gpio_conns[4], 8, gpio_conns[5]) # yield (odrive.axes[0], 7, gpio_conns[6], 8, gpio_conns[7]) # broken - yield (odrive.axes[1], 7, gpio_conns[4], 8, gpio_conns[5]) + # test other axes + for i in range(1, len(odrive.axes)): + yield (odrive.axes[i], 7, gpio_conns[4], 8, gpio_conns[5]) def run_test(self, axis: ODriveAxisComponent, step_gpio_num: int, step_gpio: LinuxGpioComponent, dir_gpio_num: int, dir_gpio: LinuxGpioComponent, logger: Logger): step_gpio.config(output=True) diff --git a/tools/odrive/tests/test_runner.py b/tools/odrive/tests/test_runner.py index ae1f147c..28066800 100644 --- a/tools/odrive/tests/test_runner.py +++ b/tools/odrive/tests/test_runner.py @@ -17,6 +17,7 @@ import itertools import time import tempfile import io +import re from typing import Union, Tuple # needed for curve fitting @@ -211,13 +212,15 @@ class ODriveComponent(Component): if yaml['board-version'].startswith("v3."): self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0']), ODriveEncoderComponent(self, 1, yaml['encoder1'])] self.axes = [ODriveAxisComponent(self, 0, yaml['motor0']), ODriveAxisComponent(self, 1, yaml['motor1'])] - elif yaml['board-version'].startswith("v4.0-"): + gpio_nums = range(1,9) + elif yaml['board-version'].startswith("v4."): self.encoders = [ODriveEncoderComponent(self, 0, yaml['encoder0'])] self.axes = [ODriveAxisComponent(self, 0, yaml['motor0'])] + gpio_nums = range(23) else: raise Exception("unknown board version {}".format(yaml['board-version'])) - for i in range(1,9): + for i in gpio_nums: self.__setattr__('gpio' + str(i), Component(self)) self.can = Component(self) self.sck = Component(self) @@ -229,8 +232,9 @@ class ODriveComponent(Component): yield 'encoder' + str(enc_ctx.num), enc_ctx for axis_ctx in self.axes: yield 'axis' + str(axis_ctx.num), axis_ctx - for i in range(1,9): - yield ('gpio' + str(i)), getattr(self, 'gpio' + str(i)) + for k in dir(self): + if k.startswith('gpio'): + yield k, getattr(self, k) yield 'can', self.can yield 'spi.sck', self.sck yield 'spi.miso', self.miso @@ -256,15 +260,12 @@ class ODriveComponent(Component): axis_ctx.handle = self.handle.__dict__['axis{}'.format(axis_idx)] def disable_mappings(self): - if yaml['board-version'].startswith("v3."): - self.handle.config.gpio1_pwm_mapping.endpoint = None - self.handle.config.gpio2_pwm_mapping.endpoint = None - self.handle.config.gpio3_pwm_mapping.endpoint = None - self.handle.config.gpio4_pwm_mapping.endpoint = None - self.handle.config.gpio3_analog_mapping.endpoint = None - self.handle.config.gpio4_analog_mapping.endpoint = None - else: - raise Exception("unknown board version {}".format(yaml['board-version'])) + for k in dir(self.handle.config): + if re.match(r'gpio[0-9]+_pwm_mapping', k): + getattr(self.handle.config, k).endpoint = None + for k in dir(self.handle.config): + if re.match(r'gpio[0-9]+_analog_mapping', k): + getattr(self.handle.config, k).endpoint = None def save_config_and_reboot(self): self.handle.save_configuration() @@ -392,7 +393,12 @@ class TeensyComponent(Component): def __init__(self, testrig, yaml: dict): self.testrig = testrig self.yaml = yaml - self.gpios = [TeensyGpio(self, i) for i in range(24)] + if self.yaml['board-version'] == 'teensy:avr:teensy40': + self.gpios = [TeensyGpio(self, i) for i in range(24)] + elif self.yaml['board-version'] == 'teensy:avr:teensy41': + self.gpios = [TeensyGpio(self, i) for i in range(42)] + else: + raise Exception(f"unknown Arduino board {self.yaml['board-version']}") self.routes = [] self.previous_routes = object() @@ -435,7 +441,7 @@ class TeensyComponent(Component): env = os.environ.copy() env['ARDUINO_COMPILE_DESTINATION'] = hexfile run_shell( - ['arduino', '--board', 'teensy:avr:teensy40', '--verify', sketchfile], + ['arduino', '--board', self.yaml['board-version'], '--verify', sketchfile], logger, env = env, timeout = 120) def program(self, hex_file_path: str, logger: Logger): @@ -454,7 +460,7 @@ class TeensyComponent(Component): time.sleep(0.1) program_gpio.write(True) - run_shell(["teensy-loader-cli", "-mmcu=imxrt1062", "-w", hex_file_path], logger, timeout = 5) + run_shell(["teensy-loader-cli", "-mmcu=" + self.yaml['board-version'].rpartition(':')[2].upper(), "-w", hex_file_path], logger, timeout = 5) time.sleep(0.5) # give it some time to boot def compile_and_program(self, code: str): @@ -518,7 +524,7 @@ class TestRig(): add_component(component_yaml['name'], ODriveComponent(component_yaml)) elif component_yaml['type'] == 'generalpurpose': add_component(component_yaml['name'], GeneralPurposeComponent(component_yaml)) - elif component_yaml['type'] == 'teensy': + elif component_yaml['type'] == 'arduino': add_component(component_yaml['name'], TeensyComponent(self, component_yaml)) elif component_yaml['type'] == 'motor': add_component(component_yaml['name'], MotorComponent(component_yaml)) diff --git a/tools/odrive/tests/uart_ascii_test.py b/tools/odrive/tests/uart_ascii_test.py index 52a1c64e..c11e6a23 100644 --- a/tools/odrive/tests/uart_ascii_test.py +++ b/tools/odrive/tests/uart_ascii_test.py @@ -37,19 +37,28 @@ class TestUartAscii(): def get_test_cases(self, testrig: TestRig): for odrive in testrig.get_components(ODriveComponent): - ports = list(testrig.get_connected_components({ - 'rx': (odrive.gpio1, True), - 'tx': (odrive.gpio2, False) - }, SerialPortComponent)) - yield (odrive, 0, ports) + if odrive.yaml['board-version'].startswith('v3.'): + ports = list(testrig.get_connected_components({ + 'rx': (odrive.gpio1, True), + 'tx': (odrive.gpio2, False) + }, SerialPortComponent)) + yield (odrive, 0, 1, 2, ports) - # Enable the line below to manually test UART_B. For this you need - # to manually move to the wires go to GPIO1/2 to GPIO3/4. The ones - # that normally go to GPIO3/4 have a low pass filter. - #yield (odrive, 1, ports) + # Enable the line below to manually test UART_B. For this you need + # to manually move to the wires go to GPIO1/2 to GPIO3/4. The ones + # that normally go to GPIO3/4 have a low pass filter. + #yield (odrive, 1, 3, 4, ports) + elif odrive.yaml['board-version'].startswith('v4.'): + ports = list(testrig.get_connected_components({ + 'rx': (odrive.gpio15, True), + 'tx': (odrive.gpio14, False) + }, SerialPortComponent)) + yield (odrive, 0, 15, 14, ports) + else: + raise TestFailed("unknown board version") - def run_test(self, odrive: ODriveComponent, uart_num: int, port: SerialPortComponent, logger: Logger): - logger.debug('Enabling UART {}...'.format(uart_num)) + def run_test(self, odrive: ODriveComponent, uart_num: int, tx_gpio: list, rx_gpio: list, port: SerialPortComponent, logger: Logger): + logger.debug('Enabling UART {}...'.format(chr(ord('A') + uart_num))) # GPIOs might be in use by something other than UART and some components # might be configured so that they would fail in the later test. @@ -63,16 +72,17 @@ class TestUartAscii(): if uart_num == 0: odrive.handle.config.enable_uart_a = True - odrive.handle.config.gpio1_mode = GPIO_MODE_UART_A - odrive.handle.config.gpio2_mode = GPIO_MODE_UART_A - odrive.handle.config.gpio3_mode = GPIO_MODE_ANALOG_IN - odrive.handle.config.gpio4_mode = GPIO_MODE_ANALOG_IN - else: + mode = GPIO_MODE_UART_A + elif uart_num == 1: odrive.handle.config.enable_uart_b = True - odrive.handle.config.gpio1_mode = GPIO_MODE_ANALOG_IN - odrive.handle.config.gpio2_mode = GPIO_MODE_ANALOG_IN - odrive.handle.config.gpio3_mode = GPIO_MODE_UART_B - odrive.handle.config.gpio4_mode = GPIO_MODE_UART_B + mode = GPIO_MODE_UART_B + elif uart_num == 2: + odrive.handle.config.enable_uart_c = True + mode = GPIO_MODE_UART_C + else: + raise TestFailed(f"unknown UART: {uart_num}") + setattr(odrive.handle.config, f'gpio{tx_gpio}_mode', mode) + setattr(odrive.handle.config, f'gpio{rx_gpio}_mode', mode) odrive.save_config_and_reboot() diff --git a/tools/test-rig-rpi.yaml b/tools/test-rig-rpi.yaml index 45022206..0026ca30 100644 --- a/tools/test-rig-rpi.yaml +++ b/tools/test-rig-rpi.yaml @@ -23,13 +23,9 @@ components: - {type: gpio, num: 20} - {type: gpio, num: 26} -# - type: programmer -# name: The Blue STLink/v2 -# id: '\x53\x3f\x75\x06\x49\x3f\x49\x51\x44\x54\x19\x3f' - - type: odrive name: odrive - board-version: v3.6-58V + board-version: v3.6-56V serial-number: "20703595524B" brake-resistance: 2.0 usb: auto @@ -56,8 +52,9 @@ components: cpr: 8192 max-rpm: 7000 - - type: teensy + - type: arduino name: teensy + board-version: teensy:avr:teensy40 - {type: lpf, name: lpf0} - {type: lpf, name: lpf1} diff --git a/tools/test-rig-rpi4.yaml b/tools/test-rig-rpi4.yaml new file mode 100644 index 00000000..44002dca --- /dev/null +++ b/tools/test-rig-rpi4.yaml @@ -0,0 +1,98 @@ + +components: + - type: generalpurpose + name: homenet + net: homenet + + - type: generalpurpose + name: rpi + ssh: odrv + net: homenet + components: + - type: uart + name: uart0 + port: /dev/ttyS0 + connected-to: main_uart + # need to specify GPIOs explicitly for the generalpurpose type + - {type: gpio, num: 4} + - {type: gpio, num: 18} + - {type: gpio, num: 23} + + - type: odrive + name: odrive + board-version: v4.1-58V + serial-number: "206730814E53" + brake-resistance: 0.0 + usb: auto + can: main_canbus + vbus-voltage: 24 # [V] + max-brake-power: 0 # [W] + encoder0: virtual_encoder0 + motor0: D5065-270KV_0 + + - type: motor + name: D5065-270KV_0 + phase-resistance: 0.039 + phase-inductance: 1.57e-05 + pole-pairs: 7 + direction: 1 + kv: 270 + max-current: 70 + max-voltage: 40 + + - type: encoder + name: real_encoder + cpr: 8192 + max-rpm: 7000 + + - type: arduino + name: teensy + board-version: teensy:avr:teensy41 + + - {type: lpf, name: lpf0} + - {type: lpf, name: lpf1} + +connections: + - ['teensy.program', 'rpi.gpio4'] + - ['teensy.gpio14', 'rpi.uart0.tx'] + - ['teensy.gpio15', 'rpi.uart0.rx'] + - ['teensy.gpio16', 'rpi.gpio18'] + - ['teensy.gpio17', 'rpi.gpio23'] + + # J8 + - ['teensy.gpio0', 'odrive.gpio6'] + - ['teensy.gpio1', 'odrive.gpio5'] + - ['teensy.gpio2', 'odrive.gpio4'] + - ['teensy.gpio3', 'odrive.gpio3'] + - ['teensy.gpio4', 'odrive.gpio0'] + - ['teensy.gpio5', 'odrive.gpio1'] + - ['teensy.gpio6', 'odrive.gpio2'] + + - ['teensy.gpio8', 'odrive.gpio16'] + - ['teensy.gpio9', 'odrive.gpio17'] + - ['teensy.gpio10', 'odrive.gpio18'] + - ['teensy.gpio11', 'odrive.gpio19'] + - ['teensy.gpio12', 'odrive.gpio20'] + - ['teensy.gpio24', 'odrive.gpio21'] + - ['teensy.gpio25', 'odrive.gpio22'] + + - ['teensy.gpio26', 'odrive.gpio10'] + - ['teensy.gpio27', 'odrive.gpio11'] + - ['teensy.gpio28', 'odrive.gpio15'] + - ['teensy.gpio29', 'odrive.gpio14'] + - ['teensy.gpio30', 'odrive.gpio13'] + - ['teensy.gpio31', 'odrive.gpio12'] + + - ['teensy.gpio23', 'real_encoder.b'] + - ['teensy.gpio22', 'real_encoder.a'] + - ['teensy.gpio21', 'real_encoder.z'] + - ['odrive.axis0', 'D5065-270KV_0'] + - ['D5065-270KV_0', 'real_encoder'] + + - ['odrive.encoder0.a', 'odrive.gpio0'] + - ['odrive.encoder0.b', 'odrive.gpio5'] + - ['odrive.encoder0.z', 'odrive.gpio6'] # TODO + +# - ['odrive.encoder1.a', 'odrive.gpio0'] +# - ['odrive.encoder1.b', 'odrive.gpio5'] +# - ['odrive.encoder1.z', 'odrive.gpio6'] # TODO From 8519315e0a60eea2233000848bb8d14befd35b9c Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 12 Nov 2020 13:32:38 +0100 Subject: [PATCH 106/124] hacky support for MA732 encoder --- Firmware/MotorControl/encoder.cpp | 21 +++++++++++++++++++-- Firmware/odrive-interface.yaml | 3 +++ tools/odrive/enums.py | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 358dca72..7a174bac 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -41,7 +41,7 @@ void Encoder::setup() { .Mode = SPI_MODE_MASTER, .Direction = SPI_DIRECTION_2LINES, .DataSize = SPI_DATASIZE_16BIT, - .CLKPolarity = mode_ == MODE_SPI_ABS_AEAT ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW, + .CLKPolarity = (mode_ == MODE_SPI_ABS_AEAT || mode_ == MODE_SPI_ABS_MA732) ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW, .CLKPhase = SPI_PHASE_2EDGE, .NSS = SPI_NSS_SOFT, .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32, @@ -51,6 +51,10 @@ void Encoder::setup() { .CRCPolynomial = 10, }; + if (mode_ == MODE_SPI_ABS_MA732) { + abs_spi_dma_tx_[0] = 0x0000; + } + if(mode_ & MODE_FLAG_ABS){ abs_spi_cs_pin_init(); @@ -363,6 +367,7 @@ void Encoder::sample_now() { case MODE_SPI_ABS_CUI: case MODE_SPI_ABS_AEAT: case MODE_SPI_ABS_RLS: + case MODE_SPI_ABS_MA732: { abs_spi_start_transaction(); // Do nothing @@ -459,6 +464,11 @@ void Encoder::abs_spi_cb(bool success) { pos = (rawVal >> 2) & 0x3fff; } break; + case MODE_SPI_ABS_MA732: { + uint16_t rawVal = abs_spi_dma_rx_[0]; + pos = (rawVal >> 2) & 0x3fff; + } break; + default: { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); goto done; @@ -477,7 +487,13 @@ done: void Encoder::abs_spi_cs_pin_init(){ // Decode and init cs pin +#if HW_VERSION_MAJOR == 4 + if (mode_ == MODE_SPI_ABS_MA732) + abs_spi_cs_gpio_ = {GPIOA, GPIO_PIN_15}; + else +#else abs_spi_cs_gpio_ = get_gpio(config_.abs_spi_cs_gpio_pin); +#endif abs_spi_cs_gpio_.config(GPIO_MODE_OUTPUT_PP, GPIO_PULLUP); // Write pin high @@ -527,7 +543,8 @@ bool Encoder::update() { case MODE_SPI_ABS_RLS: case MODE_SPI_ABS_AMS: case MODE_SPI_ABS_CUI: - case MODE_SPI_ABS_AEAT: { + case MODE_SPI_ABS_AEAT: + case MODE_SPI_ABS_MA732: { if (abs_spi_pos_updated_ == false) { // Low pass filter the error spi_error_rate_ += current_meas_period * (1.0f - spi_error_rate_); diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 5bfc1ff5..f469eb88 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -1248,6 +1248,9 @@ valuetypes: SpiAbsRls: value: 0x103 doc: RLS Encoders + SpiAbsMa732: + value: 0x104 + doc: MagAlpha MA732 magnetic encoder ODrive.Controller.ControlMode: values: diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 79649de8..e503d786 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -45,6 +45,7 @@ ENCODER_MODE_SPI_ABS_CUI = 256 ENCODER_MODE_SPI_ABS_AMS = 257 ENCODER_MODE_SPI_ABS_AEAT = 258 ENCODER_MODE_SPI_ABS_RLS = 259 +ENCODER_MODE_SPI_ABS_MA732 = 260 # ODrive.Controller.ControlMode CONTROL_MODE_VOLTAGE_CONTROL = 0 From 75577940f8437de8cf6d8f2162c371d8e9af9c89 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 18 Nov 2020 17:50:41 +0100 Subject: [PATCH 107/124] reorganize build system --- .../Drivers/CMSIS/Include/arm_const_structs.h | 79 - .../v3/Drivers/CMSIS/Include/cmsis_gcc.h | 1373 -- .../v3/Drivers/CMSIS/Include/core_cmFunc.h | 87 - .../v3/Drivers/CMSIS/Include/core_cmInstr.h | 87 - .../v3/Drivers/CMSIS/Include/core_cmSimd.h | 96 - Firmware/Board/v3/Inc/usbd_conf.h | 2 +- .../Class/CDC/Inc/usbd_cdc.h | 188 - .../Core/Inc/usbd_core.h | 167 - .../Core/Inc/usbd_ctlreq.h | 113 - .../Core/Inc/usbd_def.h | 332 - .../Core/Inc/usbd_ioreq.h | 128 - .../Core/Src/usbd_core.c | 565 - .../Core/Src/usbd_ctlreq.c | 782 - .../FreeRTOS/Source/include/atomic.h | 414 - .../FreeRTOS/Source/include/stdint.readme | 27 - .../Source/portable/MemMang/ReadMe.url | 5 - .../Third_Party/FreeRTOS/Source/readme.txt | 17 - Firmware/Board/v3/Src/usbd_conf.c | 4 +- Firmware/Drivers/DRV8353/drv8353.cpp | 195 - Firmware/Drivers/DRV8353/drv8353.hpp | 161 - Firmware/Drivers/status_led.cpp | 15 - Firmware/Drivers/status_led.hpp | 57 - Firmware/Drivers/ws2812.hpp | 86 - Firmware/Private | 2 +- .../Device/ST/STM32F4xx/Include/stm32f405xx.h | 0 .../Device/ST/STM32F4xx/Include/stm32f4xx.h | 0 .../ST/STM32F4xx/Include/system_stm32f4xx.h | 0 .../Device/ST/STM32F7xx/Include/stm32f722xx.h | 15462 ++++++++++++++++ .../Device/ST/STM32F7xx/Include/stm32f7xx.h | 220 + .../ST/STM32F7xx/Include/system_stm32f7xx.h | 123 + .../CMSIS/Include/arm_common_tables.h | 123 +- .../CMSIS/Include/arm_const_structs.h | 66 + .../CMSIS/Include/arm_math.h | 537 +- .../CMSIS/Include/cmsis_armcc.h | 243 +- .../CMSIS/Include/cmsis_armclang.h} | 937 +- .../ThirdParty/CMSIS/Include/cmsis_compiler.h | 266 + Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h | 2085 +++ .../ThirdParty/CMSIS/Include/cmsis_iccarm.h | 935 + .../ThirdParty/CMSIS/Include/cmsis_version.h | 39 + .../ThirdParty/CMSIS/Include/core_armv8mbl.h | 1918 ++ .../ThirdParty/CMSIS/Include/core_armv8mml.h | 2927 +++ .../CMSIS/Include/core_cm0.h | 399 +- .../CMSIS/Include/core_cm0plus.h | 425 +- Firmware/ThirdParty/CMSIS/Include/core_cm1.h | 976 + Firmware/ThirdParty/CMSIS/Include/core_cm23.h | 1993 ++ .../CMSIS/Include/core_cm3.h | 482 +- Firmware/ThirdParty/CMSIS/Include/core_cm33.h | 3002 +++ .../CMSIS/Include/core_cm4.h | 524 +- .../CMSIS/Include/core_cm7.h | 577 +- .../CMSIS/Include/core_sc000.h | 348 +- .../CMSIS/Include/core_sc300.h | 466 +- Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h | 270 + Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h | 333 + .../ThirdParty/CMSIS/Include/tz_context.h | 70 + .../CMSIS/Lib/GCC}/libarm_cortexM4lf_math.a | Bin .../CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a | Bin 0 -> 3082154 bytes .../FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c | 58 +- .../FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h | 45 - .../FreeRTOS/Source/croutine.c | 4 +- .../FreeRTOS/Source/event_groups.c | 4 +- .../FreeRTOS/Source/include/FreeRTOS.h | 21 +- .../FreeRTOS/Source/include/StackMacros.h | 4 +- .../FreeRTOS/Source/include/croutine.h | 6 +- .../Source/include/deprecated_definitions.h | 4 +- .../FreeRTOS/Source/include/event_groups.h | 4 +- .../FreeRTOS/Source/include/list.h | 8 +- .../FreeRTOS/Source/include/message_buffer.h | 16 +- .../FreeRTOS/Source/include/mpu_prototypes.h | 9 +- .../FreeRTOS/Source/include/mpu_wrappers.h | 9 +- .../FreeRTOS/Source/include/portable.h | 24 +- .../FreeRTOS/Source/include/projdefs.h | 4 +- .../FreeRTOS/Source/include/queue.h | 6 +- .../FreeRTOS/Source/include/semphr.h | 4 +- .../FreeRTOS/Source/include/stack_macros.h | 4 +- .../FreeRTOS/Source/include/stream_buffer.h | 16 +- .../FreeRTOS/Source/include/task.h | 184 +- .../FreeRTOS/Source/include/timers.h | 22 +- .../FreeRTOS/Source/list.c | 4 +- .../Source/portable/GCC/ARM_CM4F/port.c | 0 .../Source/portable/GCC/ARM_CM4F/portmacro.h | 0 .../Source/portable/GCC/ARM_CM7/r0p1/port.c | 765 + .../portable/GCC/ARM_CM7/r0p1/portmacro.h | 247 + .../FreeRTOS/Source/portable/MemMang/heap_4.c | 64 +- .../FreeRTOS/Source/queue.c | 46 +- .../FreeRTOS/Source/stream_buffer.c | 4 +- .../FreeRTOS/Source/tasks.c | 185 +- .../FreeRTOS/Source/timers.c | 45 +- .../Inc/Legacy/stm32_hal_legacy.h | 0 .../STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h | 0 .../Inc/stm32f4xx_hal_adc.h | 0 .../Inc/stm32f4xx_hal_adc_ex.h | 0 .../Inc/stm32f4xx_hal_can.h | 0 .../Inc/stm32f4xx_hal_cortex.h | 0 .../Inc/stm32f4xx_hal_def.h | 0 .../Inc/stm32f4xx_hal_dma.h | 0 .../Inc/stm32f4xx_hal_dma_ex.h | 0 .../Inc/stm32f4xx_hal_flash.h | 0 .../Inc/stm32f4xx_hal_flash_ex.h | 0 .../Inc/stm32f4xx_hal_flash_ramfunc.h | 0 .../Inc/stm32f4xx_hal_gpio.h | 0 .../Inc/stm32f4xx_hal_gpio_ex.h | 0 .../Inc/stm32f4xx_hal_i2c.h | 0 .../Inc/stm32f4xx_hal_i2c_ex.h | 0 .../Inc/stm32f4xx_hal_pcd.h | 0 .../Inc/stm32f4xx_hal_pcd_ex.h | 0 .../Inc/stm32f4xx_hal_pwr.h | 0 .../Inc/stm32f4xx_hal_pwr_ex.h | 0 .../Inc/stm32f4xx_hal_rcc.h | 0 .../Inc/stm32f4xx_hal_rcc_ex.h | 0 .../Inc/stm32f4xx_hal_spi.h | 0 .../Inc/stm32f4xx_hal_tim.h | 0 .../Inc/stm32f4xx_hal_tim_ex.h | 0 .../Inc/stm32f4xx_hal_uart.h | 0 .../Inc/stm32f4xx_ll_usb.h | 0 .../STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c | 0 .../Src/stm32f4xx_hal_adc.c | 0 .../Src/stm32f4xx_hal_adc_ex.c | 0 .../Src/stm32f4xx_hal_can.c | 0 .../Src/stm32f4xx_hal_cortex.c | 0 .../Src/stm32f4xx_hal_dma.c | 0 .../Src/stm32f4xx_hal_dma_ex.c | 0 .../Src/stm32f4xx_hal_flash.c | 0 .../Src/stm32f4xx_hal_flash_ex.c | 0 .../Src/stm32f4xx_hal_flash_ramfunc.c | 0 .../Src/stm32f4xx_hal_gpio.c | 0 .../Src/stm32f4xx_hal_i2c.c | 0 .../Src/stm32f4xx_hal_i2c_ex.c | 0 .../Src/stm32f4xx_hal_pcd.c | 0 .../Src/stm32f4xx_hal_pcd_ex.c | 0 .../Src/stm32f4xx_hal_pwr.c | 0 .../Src/stm32f4xx_hal_pwr_ex.c | 0 .../Src/stm32f4xx_hal_rcc.c | 0 .../Src/stm32f4xx_hal_rcc_ex.c | 0 .../Src/stm32f4xx_hal_spi.c | 0 .../Src/stm32f4xx_hal_tim.c | 0 .../Src/stm32f4xx_hal_tim_ex.c | 0 .../Src/stm32f4xx_hal_uart.c | 0 .../Src/stm32f4xx_ll_usb.c | 0 .../Class/CDC/Inc/usbd_cdc.h | 183 + .../Class/CDC/Src/usbd_cdc.c | 548 +- .../Core/Inc/usbd_core.h | 158 + .../Core/Inc/usbd_ctlreq.h | 103 + .../Core/Inc/usbd_def.h | 395 + .../Core/Inc/usbd_ioreq.h | 114 + .../Core/Src/usbd_core.c | 669 + .../Core/Src/usbd_ctlreq.c | 944 + .../Core/Src/usbd_ioreq.c | 136 +- Firmware/Tupfile.lua | 647 +- Firmware/build.lua | 184 - 149 files changed, 38385 insertions(+), 8240 deletions(-) delete mode 100644 Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h delete mode 100644 Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h delete mode 100644 Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h delete mode 100644 Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h delete mode 100644 Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c delete mode 100644 Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c delete mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h delete mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme delete mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url delete mode 100644 Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt delete mode 100644 Firmware/Drivers/DRV8353/drv8353.cpp delete mode 100644 Firmware/Drivers/DRV8353/drv8353.hpp delete mode 100644 Firmware/Drivers/status_led.cpp delete mode 100644 Firmware/Drivers/status_led.hpp delete mode 100644 Firmware/Drivers/ws2812.hpp rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h (100%) create mode 100644 Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h create mode 100644 Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f7xx.h create mode 100644 Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/arm_common_tables.h (53%) create mode 100644 Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/arm_math.h (95%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/cmsis_armcc.h (75%) rename Firmware/{Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h => ThirdParty/CMSIS/Include/cmsis_armclang.h} (57%) create mode 100644 Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/cmsis_version.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_cm0.h (71%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_cm0plus.h (74%) create mode 100644 Firmware/ThirdParty/CMSIS/Include/core_cm1.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/core_cm23.h rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_cm3.h (84%) create mode 100644 Firmware/ThirdParty/CMSIS/Include/core_cm33.h rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_cm4.h (84%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_cm7.h (85%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_sc000.h (81%) rename Firmware/{Board/v3/Drivers => ThirdParty}/CMSIS/Include/core_sc300.h (84%) create mode 100644 Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h create mode 100644 Firmware/ThirdParty/CMSIS/Include/tz_context.h rename Firmware/{Board/v3/Drivers/CMSIS/Lib => ThirdParty/CMSIS/Lib/GCC}/libarm_cortexM4lf_math.a (100%) create mode 100644 Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c (94%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h (95%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/croutine.c (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/event_groups.c (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/FreeRTOS.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/StackMacros.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/croutine.h (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/deprecated_definitions.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/event_groups.h (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/list.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/message_buffer.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/mpu_prototypes.h (96%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/mpu_wrappers.h (96%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/portable.h (80%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/projdefs.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/queue.h (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/semphr.h (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/stack_macros.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/stream_buffer.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/task.h (93%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/include/timers.h (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/list.c (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c (100%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h (100%) create mode 100644 Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c create mode 100644 Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/portable/MemMang/heap_4.c (87%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/queue.c (98%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/stream_buffer.c (99%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/tasks.c (96%) rename Firmware/{Board/v3/Middlewares/Third_Party => ThirdParty}/FreeRTOS/Source/timers.c (97%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c (100%) rename Firmware/{Board/v3/Drivers => ThirdParty}/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c (100%) create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h rename Firmware/{Board/v3/Middlewares/ST => ThirdParty}/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c (75%) create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c create mode 100644 Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c rename Firmware/{Board/v3/Middlewares/ST => ThirdParty}/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c (51%) delete mode 100644 Firmware/build.lua diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h b/Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h deleted file mode 100644 index 726d06eb..00000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_const_structs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2014 ARM Limited. All rights reserved. -* -* $Date: 19. March 2015 -* $Revision: V.1.4.5 -* -* Project: CMSIS DSP Library -* Title: arm_const_structs.h -* -* Description: This file has constant structs that are initialized for -* user convenience. For example, some can be given as -* arguments to the arm_cfft_f32() function. -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -------------------------------------------------------------------- */ - -#ifndef _ARM_CONST_STRUCTS_H -#define _ARM_CONST_STRUCTS_H - -#include "arm_math.h" -#include "arm_common_tables.h" - - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048; - extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096; - - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048; - extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096; - - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048; - extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096; - -#endif diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h b/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h deleted file mode 100644 index 3e522777..00000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h +++ /dev/null @@ -1,1373 +0,0 @@ -/**************************************************************************//** - * @file cmsis_gcc.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#ifndef __CMSIS_GCC_H -#define __CMSIS_GCC_H - -/* ignore some GCC warnings */ -#if defined ( __GNUC__ ) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ - */ - -/** - \brief Enable IRQ Interrupts - \details Enables IRQ interrupts by clearing the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) -{ - __ASM volatile ("cpsie i" : : : "memory"); -} - - -/** - \brief Disable IRQ Interrupts - \details Disables IRQ interrupts by setting the I-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) -{ - __ASM volatile ("cpsid i" : : : "memory"); -} - - -/** - \brief Get Control Register - \details Returns the content of the Control Register. - \return Control Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, control" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Control Register - \details Writes the given value to the Control Register. - \param [in] control Control Register value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) -{ - __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); -} - - -/** - \brief Get IPSR Register - \details Returns the content of the IPSR Register. - \return IPSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get APSR Register - \details Returns the content of the APSR Register. - \return APSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get xPSR Register - \details Returns the content of the xPSR Register. - - \return xPSR Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); - return(result); -} - - -/** - \brief Get Process Stack Pointer - \details Returns the current value of the Process Stack Pointer (PSP). - \return PSP Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Process Stack Pointer - \details Assigns the given value to the Process Stack Pointer (PSP). - \param [in] topOfProcStack Process Stack Pointer value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) -{ - __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : ); -} - - -/** - \brief Get Main Stack Pointer - \details Returns the current value of the Main Stack Pointer (MSP). - \return MSP Register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) -{ - register uint32_t result; - - __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Main Stack Pointer - \details Assigns the given value to the Main Stack Pointer (MSP). - - \param [in] topOfMainStack Main Stack Pointer value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) -{ - __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : ); -} - - -/** - \brief Get Priority Mask - \details Returns the current state of the priority mask bit from the Priority Mask Register. - \return Priority Mask value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, primask" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Priority Mask - \details Assigns the given value to the Priority Mask Register. - \param [in] priMask Priority Mask - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) -{ - __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); -} - - -#if (__CORTEX_M >= 0x03U) - -/** - \brief Enable FIQ - \details Enables FIQ interrupts by clearing the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) -{ - __ASM volatile ("cpsie f" : : : "memory"); -} - - -/** - \brief Disable FIQ - \details Disables FIQ interrupts by setting the F-bit in the CPSR. - Can only be executed in Privileged modes. - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) -{ - __ASM volatile ("cpsid f" : : : "memory"); -} - - -/** - \brief Get Base Priority - \details Returns the current value of the Base Priority register. - \return Base Priority register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, basepri" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Base Priority - \details Assigns the given value to the Base Priority register. - \param [in] basePri Base Priority value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) -{ - __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); -} - - -/** - \brief Set Base Priority with condition - \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) -{ - __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); -} - - -/** - \brief Get Fault Mask - \details Returns the current value of the Fault Mask register. - \return Fault Mask register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); - return(result); -} - - -/** - \brief Set Fault Mask - \details Assigns the given value to the Fault Mask register. - \param [in] faultMask Fault Mask value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) -{ - __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); -} - -#endif /* (__CORTEX_M >= 0x03U) */ - - -#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) - -/** - \brief Get FPSCR - \details Returns the current value of the Floating Point Status/Control register. - \return Floating Point Status/Control register value - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - /* Empty asm statement works as a scheduling barrier */ - __ASM volatile (""); - __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} - - -/** - \brief Set FPSCR - \details Assigns the given value to the Floating Point Status/Control register. - \param [in] fpscr Floating Point Status/Control value to set - */ -__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - /* Empty asm statement works as a scheduling barrier */ - __ASM volatile (""); - __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} - -#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ - - - -/*@} end of CMSIS_Core_RegAccFunctions */ - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/* Define macros for porting to both thumb1 and thumb2. - * For thumb1, use low register (r0-r7), specified by constraint "l" - * Otherwise, use general registers, specified by constraint "r" */ -#if defined (__thumb__) && !defined (__thumb2__) -#define __CMSIS_GCC_OUT_REG(r) "=l" (r) -#define __CMSIS_GCC_USE_REG(r) "l" (r) -#else -#define __CMSIS_GCC_OUT_REG(r) "=r" (r) -#define __CMSIS_GCC_USE_REG(r) "r" (r) -#endif - -/** - \brief No Operation - \details No Operation does nothing. This instruction can be used for code alignment purposes. - */ -__attribute__((always_inline)) __STATIC_INLINE void __NOP(void) -{ - __ASM volatile ("nop"); -} - - -/** - \brief Wait For Interrupt - \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. - */ -__attribute__((always_inline)) __STATIC_INLINE void __WFI(void) -{ - __ASM volatile ("wfi"); -} - - -/** - \brief Wait For Event - \details Wait For Event is a hint instruction that permits the processor to enter - a low-power state until one of a number of events occurs. - */ -__attribute__((always_inline)) __STATIC_INLINE void __WFE(void) -{ - __ASM volatile ("wfe"); -} - - -/** - \brief Send Event - \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. - */ -__attribute__((always_inline)) __STATIC_INLINE void __SEV(void) -{ - __ASM volatile ("sev"); -} - - -/** - \brief Instruction Synchronization Barrier - \details Instruction Synchronization Barrier flushes the pipeline in the processor, - so that all instructions following the ISB are fetched from cache or memory, - after the instruction has been completed. - */ -__attribute__((always_inline)) __STATIC_INLINE void __ISB(void) -{ - __ASM volatile ("isb 0xF":::"memory"); -} - - -/** - \brief Data Synchronization Barrier - \details Acts as a special kind of Data Memory Barrier. - It completes when all explicit memory accesses before this instruction complete. - */ -__attribute__((always_inline)) __STATIC_INLINE void __DSB(void) -{ - __ASM volatile ("dsb 0xF":::"memory"); -} - - -/** - \brief Data Memory Barrier - \details Ensures the apparent order of the explicit memory operations before - and after the instruction, without ensuring their completion. - */ -__attribute__((always_inline)) __STATIC_INLINE void __DMB(void) -{ - __ASM volatile ("dmb 0xF":::"memory"); -} - - -/** - \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV(uint32_t value) -{ -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) - return __builtin_bswap32(value); -#else - uint32_t result; - - __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -#endif -} - - -/** - \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) -{ -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - return (short)__builtin_bswap16(value); -#else - int32_t result; - - __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -#endif -} - - -/** - \brief Rotate Right in unsigned value (32 bit) - \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] value Value to rotate - \param [in] value Number of Bits to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) -{ - return (op1 >> op2) | (op1 << (32U - op2)); -} - - -/** - \brief Breakpoint - \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. - */ -#define __BKPT(value) __ASM volatile ("bkpt "#value) - - -/** - \brief Reverse bit order of value - \details Reverses the bit order of the given value. - \param [in] value Value to reverse - \return Reversed value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); -#else - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ -#endif - return(result); -} - - -/** - \brief Count leading zeros - \details Counts the number of leading zeros of a data value. - \param [in] value Value to count the leading zeros - \return number of leading zeros in value - */ -#define __CLZ __builtin_clz - - -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) - -/** - \brief LDR Exclusive (8 bit) - \details Executes a exclusive LDR instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDR Exclusive (16 bit) - \details Executes a exclusive LDR instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDR Exclusive (32 bit) - \details Executes a exclusive LDR instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); - return(result); -} - - -/** - \brief STR Exclusive (8 bit) - \details Executes a exclusive STR instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) -{ - uint32_t result; - - __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); - return(result); -} - - -/** - \brief STR Exclusive (16 bit) - \details Executes a exclusive STR instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) -{ - uint32_t result; - - __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); - return(result); -} - - -/** - \brief STR Exclusive (32 bit) - \details Executes a exclusive STR instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - \return 0 Function succeeded - \return 1 Function failed - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); - return(result); -} - - -/** - \brief Remove the exclusive lock - \details Removes the exclusive lock which is created by LDREX. - */ -__attribute__((always_inline)) __STATIC_INLINE void __CLREX(void) -{ - __ASM volatile ("clrex" ::: "memory"); -} - - -/** - \brief Signed Saturate - \details Saturates a signed value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (1..32) - \return Saturated value - */ -#define __SSAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Unsigned Saturate - \details Saturates an unsigned value. - \param [in] value Value to be saturated - \param [in] sat Bit position to saturate to (0..31) - \return Saturated value - */ -#define __USAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - - -/** - \brief Rotate Right with Extend (32 bit) - \details Moves each bit of a bitstring right by one bit. - The carry input is shifted in at the left end of the bitstring. - \param [in] value Value to rotate - \return Rotated value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} - - -/** - \brief LDRT Unprivileged (8 bit) - \details Executes a Unprivileged LDRT instruction for 8 bit value. - \param [in] ptr Pointer to data - \return value of type uint8_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint8_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (16 bit) - \details Executes a Unprivileged LDRT instruction for 16 bit values. - \param [in] ptr Pointer to data - \return value of type uint16_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *addr) -{ - uint32_t result; - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*addr) ); -#else - /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not - accepted by assembler. So has to use following less efficient pattern. - */ - __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); -#endif - return ((uint16_t) result); /* Add explicit type cast here */ -} - - -/** - \brief LDRT Unprivileged (32 bit) - \details Executes a Unprivileged LDRT instruction for 32 bit values. - \param [in] ptr Pointer to data - \return value of type uint32_t at (*ptr) - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *addr) -{ - uint32_t result; - - __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*addr) ); - return(result); -} - - -/** - \brief STRT Unprivileged (8 bit) - \details Executes a Unprivileged STRT instruction for 8 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *addr) -{ - __ASM volatile ("strbt %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (16 bit) - \details Executes a Unprivileged STRT instruction for 16 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *addr) -{ - __ASM volatile ("strht %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); -} - - -/** - \brief STRT Unprivileged (32 bit) - \details Executes a Unprivileged STRT instruction for 32 bit values. - \param [in] value Value to store - \param [in] ptr Pointer to location - */ -__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *addr) -{ - __ASM volatile ("strt %1, %0" : "=Q" (*addr) : "r" (value) ); -} - -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#define __SSAT16(ARG1,ARG2) \ -({ \ - int32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -#define __USAT16(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) -{ - uint32_t result; - - __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) -{ - uint32_t result; - - __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) -{ - union llreg_u{ - uint32_t w32[2]; - uint64_t w64; - } llr; - llr.w64 = acc; - -#ifndef __ARMEB__ /* Little endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); -#else /* Big endian */ - __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); -#endif - - return(llr.w64); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) -{ - uint32_t result; - - __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) -{ - int32_t result; - - __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); - return(result); -} - -#define __PKHBT(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -#define __PKHTB(ARG1,ARG2,ARG3) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ - if (ARG3 == 0) \ - __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ - else \ - __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ - __RES; \ - }) - -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) -{ - int32_t result; - - __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); - return(result); -} - -#endif /* (__CORTEX_M >= 0x04) */ -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#if defined ( __GNUC__ ) -#pragma GCC diagnostic pop -#endif - -#endif /* __CMSIS_GCC_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h b/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h deleted file mode 100644 index 652a48af..00000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmFunc.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************//** - * @file core_cmFunc.h - * @brief CMSIS Cortex-M Core Function Access Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMFUNC_H -#define __CORE_CMFUNC_H - - -/* ########################### Core Function Access ########################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@} end of CMSIS_Core_RegAccFunctions */ - -#endif /* __CORE_CMFUNC_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h b/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h deleted file mode 100644 index f474b0e6..00000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmInstr.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************//** - * @file core_cmInstr.h - * @brief CMSIS Cortex-M Core Instruction Access Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMINSTR_H -#define __CORE_CMINSTR_H - - -/* ########################## Core Instruction Access ######################### */ -/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface - Access to dedicated instructions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ - -#endif /* __CORE_CMINSTR_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h b/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h deleted file mode 100644 index 66bf5c2a..00000000 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cmSimd.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************//** - * @file core_cmSimd.h - * @brief CMSIS Cortex-M SIMD Header File - * @version V4.30 - * @date 20. October 2015 - ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - - -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #pragma clang system_header /* treat file as system include file */ -#endif - -#ifndef __CORE_CMSIMD_H -#define __CORE_CMSIMD_H - -#ifdef __cplusplus - extern "C" { -#endif - - -/* ################### Compiler specific Intrinsics ########################### */ -/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics - Access to dedicated SIMD instructions - @{ -*/ - -/*------------------ RealView Compiler -----------------*/ -#if defined ( __CC_ARM ) - #include "cmsis_armcc.h" - -/*------------------ ARM Compiler V6 -------------------*/ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #include "cmsis_armcc_V6.h" - -/*------------------ GNU Compiler ----------------------*/ -#elif defined ( __GNUC__ ) - #include "cmsis_gcc.h" - -/*------------------ ICC Compiler ----------------------*/ -#elif defined ( __ICCARM__ ) - #include - -/*------------------ TI CCS Compiler -------------------*/ -#elif defined ( __TMS470__ ) - #include - -/*------------------ TASKING Compiler ------------------*/ -#elif defined ( __TASKING__ ) - /* - * The CMSIS functions have been implemented as intrinsics in the compiler. - * Please use "carm -?i" to get an up to date list of all intrinsics, - * Including the CMSIS ones. - */ - -/*------------------ COSMIC Compiler -------------------*/ -#elif defined ( __CSMC__ ) - #include - -#endif - -/*@} end of group CMSIS_SIMD_intrinsics */ - - -#ifdef __cplusplus -} -#endif - -#endif /* __CORE_CMSIMD_H */ diff --git a/Firmware/Board/v3/Inc/usbd_conf.h b/Firmware/Board/v3/Inc/usbd_conf.h index dbf800b3..aecb8d1a 100644 --- a/Firmware/Board/v3/Inc/usbd_conf.h +++ b/Firmware/Board/v3/Inc/usbd_conf.h @@ -98,7 +98,7 @@ /*---------- -----------*/ #define USBD_MAX_STR_DESC_SIZ 512 /*---------- -----------*/ -#define USBD_SUPPORT_USER_STRING 1 +#define USBD_SUPPORT_USER_STRING_DESC 1 /*---------- -----------*/ #define USBD_DEBUG_LEVEL 0 /*---------- -----------*/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h deleted file mode 100644 index c029e22a..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h +++ /dev/null @@ -1,188 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_cdc.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief header file for the usbd_cdc.c file. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USB_CDC_H -#define __USB_CDC_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ioreq.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup usbd_cdc - * @brief This file is the Header file for usbd_cdc.c - * @{ - */ - - -/** @defgroup usbd_cdc_Exported_Defines - * @{ - */ -#define CDC_IN_EP 0x81 /* EP1 for data IN */ -#define CDC_OUT_EP 0x01 /* EP1 for data OUT */ -#define CDC_CMD_EP 0x82 /* EP2 for CDC commands */ -#define ODRIVE_IN_EP 0x83 /* EP3 IN: ODrive device TX endpoint */ -#define ODRIVE_OUT_EP 0x03 /* EP3 OUT: ODrive device RX endpoint */ - -/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */ -#define CDC_DATA_HS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */ -#define CDC_DATA_FS_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */ -#define CDC_CMD_PACKET_SIZE 8 /* Control Endpoint Packet size */ - -#define USB_CDC_CONFIG_DESC_SIZ (67 + 39) -#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE -#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE - -#define CDC_DATA_FS_IN_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE -#define CDC_DATA_FS_OUT_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE - -/*---------------------------------------------------------------------*/ -/* CDC definitions */ -/*---------------------------------------------------------------------*/ -#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 -#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 -#define CDC_SET_COMM_FEATURE 0x02 -#define CDC_GET_COMM_FEATURE 0x03 -#define CDC_CLEAR_COMM_FEATURE 0x04 -#define CDC_SET_LINE_CODING 0x20 -#define CDC_GET_LINE_CODING 0x21 -#define CDC_SET_CONTROL_LINE_STATE 0x22 -#define CDC_SEND_BREAK 0x23 - -/** - * @} - */ - - -/** @defgroup USBD_CORE_Exported_TypesDefinitions - * @{ - */ - -/** - * @} - */ -typedef struct -{ - uint32_t bitrate; - uint8_t format; - uint8_t paritytype; - uint8_t datatype; -}USBD_CDC_LineCodingTypeDef; - -typedef struct _USBD_CDC_Itf -{ - int8_t (* Init) (void); - int8_t (* DeInit) (void); - int8_t (* Control) (uint8_t, uint8_t * , uint16_t); - int8_t (* Receive) (uint8_t *, uint32_t *, uint8_t); - -}USBD_CDC_ItfTypeDef; - -typedef struct -{ - uint8_t* Buffer; - uint32_t Length; - volatile uint8_t State; -} -USBD_CDC_EP_HandleTypeDef; - -typedef struct -{ - uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE/4]; /* Force 32bits alignment */ - uint8_t CmdOpCode; - uint8_t CmdLength; - - USBD_CDC_EP_HandleTypeDef CDC_Tx; - USBD_CDC_EP_HandleTypeDef CDC_Rx; - - USBD_CDC_EP_HandleTypeDef ODRIVE_Tx; - USBD_CDC_EP_HandleTypeDef ODRIVE_Rx; -} -USBD_CDC_HandleTypeDef; - - - -/** @defgroup USBD_CORE_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_Variables - * @{ - */ - -extern USBD_ClassTypeDef USBD_CDC; -#define USBD_CDC_CLASS &USBD_CDC -/** - * @} - */ - -/** @defgroup USB_CORE_Exported_Functions - * @{ - */ -uint8_t USBD_CDC_RegisterInterface (USBD_HandleTypeDef *pdev, - USBD_CDC_ItfTypeDef *fops); - -uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, - uint16_t length, - uint8_t endpoint_pair); - -uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, uint8_t endpoint_pair); - -uint8_t USBD_CDC_ReceivePacket (USBD_HandleTypeDef *pdev, uint8_t endpoint_pair); - -uint8_t USBD_CDC_TransmitPacket (USBD_HandleTypeDef *pdev, uint8_t endpoint_pair); -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_CDC_H */ -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h deleted file mode 100644 index 013a5c14..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_core.h +++ /dev/null @@ -1,167 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_core.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief Header file for usbd_core.c file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_CORE_H -#define __USBD_CORE_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_conf.h" -#include "usbd_def.h" -#include "usbd_ioreq.h" -#include "usbd_ctlreq.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_CORE - * @brief This file is the Header file for usbd_core.c file - * @{ - */ - - -/** @defgroup USBD_CORE_Exported_Defines - * @{ - */ - -/** - * @} - */ - - -/** @defgroup USBD_CORE_Exported_TypesDefinitions - * @{ - */ - - -/** - * @} - */ - - - -/** @defgroup USBD_CORE_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_Variables - * @{ - */ -#define USBD_SOF USBD_LL_SOF -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_FunctionsPrototype - * @{ - */ -USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id); -USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass); - -USBD_StatusTypeDef USBD_RunTestMode (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); -USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); - -USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup); -USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata); -USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata); - -USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed); -USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev); - -USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); -USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); - -USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev); - -/* USBD Low Level Driver */ -USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_DeInit (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_Stop (USBD_HandleTypeDef *pdev); -USBD_StatusTypeDef USBD_LL_OpenEP (USBD_HandleTypeDef *pdev, - uint8_t ep_addr, - uint8_t ep_type, - uint16_t ep_mps); - -USBD_StatusTypeDef USBD_LL_CloseEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_FlushEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_StallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_ClearStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -uint8_t USBD_LL_IsStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -USBD_StatusTypeDef USBD_LL_SetUSBAddress (USBD_HandleTypeDef *pdev, uint8_t dev_addr); -USBD_StatusTypeDef USBD_LL_Transmit (USBD_HandleTypeDef *pdev, - uint8_t ep_addr, - uint8_t *pbuf, - uint16_t size); - -USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, - uint8_t ep_addr, - uint8_t *pbuf, - uint16_t size); - -uint32_t USBD_LL_GetRxDataSize (USBD_HandleTypeDef *pdev, uint8_t ep_addr); -void USBD_LL_Delay (uint32_t Delay); - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_CORE_H */ - -/** - * @} - */ - -/** -* @} -*/ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ - - - diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h deleted file mode 100644 index bf882522..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_req.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief Header file for the usbd_req.c file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USB_REQUEST_H -#define __USB_REQUEST_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_def.h" - - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_REQ - * @brief header file for the usbd_req.c file - * @{ - */ - -/** @defgroup USBD_REQ_Exported_Defines - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Exported_Types - * @{ - */ -/** - * @} - */ - - - -/** @defgroup USBD_REQ_Exported_Macros - * @{ - */ -/** - * @} - */ - -/** @defgroup USBD_REQ_Exported_Variables - * @{ - */ -/** - * @} - */ - -/** @defgroup USBD_REQ_Exported_FunctionsPrototype - * @{ - */ - -USBD_StatusTypeDef USBD_StdDevReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -USBD_StatusTypeDef USBD_StdItfReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -USBD_StatusTypeDef USBD_StdEPReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); - - -void USBD_CtlError (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); - -void USBD_ParseSetupRequest (USBD_SetupReqTypedef *req, uint8_t *pdata); - -void USBD_GetString (uint8_t *desc, uint8_t *unicode, uint16_t *len); -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_REQUEST_H */ - -/** - * @} - */ - -/** -* @} -*/ - - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h deleted file mode 100644 index f259b51d..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_def.h +++ /dev/null @@ -1,332 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_def.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief General defines for the usb device library - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_DEF_H -#define __USBD_DEF_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_conf.h" - -/** @addtogroup STM32_USBD_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USB_DEF - * @brief general defines for the usb device library file - * @{ - */ - -/** @defgroup USB_DEF_Exported_Defines - * @{ - */ - -#ifndef NULL -#define NULL 0 -#endif - - -#define USB_LEN_DEV_QUALIFIER_DESC 0x0A -#define USB_LEN_DEV_DESC 0x12 -#define USB_LEN_CFG_DESC 0x09 -#define USB_LEN_IF_DESC 0x09 -#define USB_LEN_EP_DESC 0x07 -#define USB_LEN_OTG_DESC 0x03 -#define USB_LEN_LANGID_STR_DESC 0x04 -#define USB_LEN_OTHER_SPEED_DESC_SIZ 0x09 - -#define USBD_IDX_LANGID_STR 0x00 -#define USBD_IDX_MFC_STR 0x01 -#define USBD_IDX_PRODUCT_STR 0x02 -#define USBD_IDX_SERIAL_STR 0x03 -#define USBD_IDX_CONFIG_STR 0x04 -#define USBD_IDX_INTERFACE_STR 0x05 -#define USBD_IDX_ODRIVE_INTF_STR 0x06 -#define USBD_IDX_MICROSOFT_DESC_STR 0xEE - -#define USB_REQ_TYPE_STANDARD 0x00 -#define USB_REQ_TYPE_CLASS 0x20 -#define USB_REQ_TYPE_VENDOR 0x40 -#define USB_REQ_TYPE_MASK 0x60 - -#define USB_REQ_RECIPIENT_DEVICE 0x00 -#define USB_REQ_RECIPIENT_INTERFACE 0x01 -#define USB_REQ_RECIPIENT_ENDPOINT 0x02 -#define USB_REQ_RECIPIENT_MASK 0x03 - -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -#define USB_REQ_SET_FEATURE 0x03 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_DESC_TYPE_DEVICE 1 -#define USB_DESC_TYPE_CONFIGURATION 2 -#define USB_DESC_TYPE_STRING 3 -#define USB_DESC_TYPE_INTERFACE 4 -#define USB_DESC_TYPE_ENDPOINT 5 -#define USB_DESC_TYPE_DEVICE_QUALIFIER 6 -#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7 -#define USB_DESC_TYPE_BOS 0x0F - -#define USB_CONFIG_REMOTE_WAKEUP 2 -#define USB_CONFIG_SELF_POWERED 1 - -#define USB_FEATURE_EP_HALT 0 -#define USB_FEATURE_REMOTE_WAKEUP 1 -#define USB_FEATURE_TEST_MODE 2 - -#define USB_DEVICE_CAPABITY_TYPE 0x10 - -#define USB_HS_MAX_PACKET_SIZE 512 -#define USB_FS_MAX_PACKET_SIZE 64 -#define USB_MAX_EP0_SIZE 64 - -/* Device Status */ -#define USBD_STATE_DEFAULT 1 -#define USBD_STATE_ADDRESSED 2 -#define USBD_STATE_CONFIGURED 3 -#define USBD_STATE_SUSPENDED 4 - - -/* EP0 State */ -#define USBD_EP0_IDLE 0 -#define USBD_EP0_SETUP 1 -#define USBD_EP0_DATA_IN 2 -#define USBD_EP0_DATA_OUT 3 -#define USBD_EP0_STATUS_IN 4 -#define USBD_EP0_STATUS_OUT 5 -#define USBD_EP0_STALL 6 - -#define USBD_EP_TYPE_CTRL 0 -#define USBD_EP_TYPE_ISOC 1 -#define USBD_EP_TYPE_BULK 2 -#define USBD_EP_TYPE_INTR 3 - - -/** - * @} - */ - - -/** @defgroup USBD_DEF_Exported_TypesDefinitions - * @{ - */ - -typedef struct usb_setup_req -{ - - uint8_t bmRequest; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; -}USBD_SetupReqTypedef; - -struct _USBD_HandleTypeDef; - -typedef struct _Device_cb -{ - uint8_t (*Init) (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx); - uint8_t (*DeInit) (struct _USBD_HandleTypeDef *pdev , uint8_t cfgidx); - /* Control Endpoints*/ - uint8_t (*Setup) (struct _USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req); - uint8_t (*EP0_TxSent) (struct _USBD_HandleTypeDef *pdev ); - uint8_t (*EP0_RxReady) (struct _USBD_HandleTypeDef *pdev ); - /* Class Specific Endpoints*/ - uint8_t (*DataIn) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - uint8_t (*DataOut) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - uint8_t (*SOF) (struct _USBD_HandleTypeDef *pdev); - uint8_t (*IsoINIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - uint8_t (*IsoOUTIncomplete) (struct _USBD_HandleTypeDef *pdev , uint8_t epnum); - - uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); - uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); - uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); - uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); -#if (USBD_SUPPORT_USER_STRING == 1) - uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev ,uint8_t index, uint16_t *length); -#endif - -} USBD_ClassTypeDef; - -/* Following USB Device Speed */ -typedef enum -{ - USBD_SPEED_HIGH = 0, - USBD_SPEED_FULL = 1, - USBD_SPEED_LOW = 2, -}USBD_SpeedTypeDef; - -/* Following USB Device status */ -typedef enum { - USBD_OK = 0, - USBD_BUSY, - USBD_FAIL, -}USBD_StatusTypeDef; - -/* USB Device descriptors structure */ -typedef struct -{ - uint8_t *(*GetDeviceDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetLangIDStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetManufacturerStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetProductStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetSerialStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetConfigurationStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); - uint8_t *(*GetInterfaceStrDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); -#if (USBD_LPM_ENABLED == 1) - uint8_t *(*GetBOSDescriptor)( USBD_SpeedTypeDef speed , uint16_t *length); -#endif -} USBD_DescriptorsTypeDef; - -/* USB Device handle structure */ -typedef struct -{ - uint32_t status; - uint32_t total_length; - uint32_t rem_length; - uint32_t maxpacket; -} USBD_EndpointTypeDef; - -/* USB Device handle structure */ -typedef struct _USBD_HandleTypeDef -{ - uint8_t id; - uint32_t dev_config; - uint32_t dev_default_config; - uint32_t dev_config_status; - USBD_SpeedTypeDef dev_speed; - USBD_EndpointTypeDef ep_in[15]; - USBD_EndpointTypeDef ep_out[15]; - uint32_t ep0_state; - uint32_t ep0_data_len; - uint8_t dev_state; - uint8_t dev_old_state; - uint8_t dev_address; - uint8_t dev_connection_status; - uint8_t dev_test_mode; - uint32_t dev_remote_wakeup; - - USBD_SetupReqTypedef request; - USBD_DescriptorsTypeDef *pDesc; - USBD_ClassTypeDef *pClass; - void *pClassData; - void *pUserData; - void *pData; -} USBD_HandleTypeDef; - -/** - * @} - */ - - - -/** @defgroup USBD_DEF_Exported_Macros - * @{ - */ -#define SWAPBYTE(addr) (((uint16_t)(*((uint8_t *)(addr)))) + \ - (((uint16_t)(*(((uint8_t *)(addr)) + 1))) << 8)) - -#define LOBYTE(x) ((uint8_t)(x & 0x00FF)) -#define HIBYTE(x) ((uint8_t)((x & 0xFF00) >>8)) -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - - -#if defined ( __GNUC__ ) - #ifndef __weak - #define __weak __attribute__((weak)) - #endif /* __weak */ - #ifndef __packed - #define __packed __attribute__((__packed__)) - #endif /* __packed */ -#endif /* __GNUC__ */ - - -/* In HS mode and when the DMA is used, all variables and data structures dealing - with the DMA during the transaction process should be 4-bytes aligned */ - -#if defined (__GNUC__) /* GNU Compiler */ - #define __ALIGN_END __attribute__ ((aligned (4))) - #define __ALIGN_BEGIN -#else - #define __ALIGN_END - #if defined (__CC_ARM) /* ARM Compiler */ - #define __ALIGN_BEGIN __align(4) - #elif defined (__ICCARM__) /* IAR Compiler */ - #define __ALIGN_BEGIN - #elif defined (__TASKING__) /* TASKING Compiler */ - #define __ALIGN_BEGIN __align(4) - #endif /* __CC_ARM */ -#endif /* __GNUC__ */ - - -/** - * @} - */ - -/** @defgroup USBD_DEF_Exported_Variables - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_DEF_Exported_FunctionsPrototype - * @{ - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_DEF_H */ - -/** - * @} - */ - -/** -* @} -*/ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h deleted file mode 100644 index b476307c..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_ioreq.h - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief Header file for the usbd_ioreq.c file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_IOREQ_H -#define __USBD_IOREQ_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_def.h" -#include "usbd_core.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_IOREQ - * @brief header file for the usbd_ioreq.c file - * @{ - */ - -/** @defgroup USBD_IOREQ_Exported_Defines - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_IOREQ_Exported_Types - * @{ - */ - - -/** - * @} - */ - - - -/** @defgroup USBD_IOREQ_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_IOREQ_Exported_Variables - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_IOREQ_Exported_FunctionsPrototype - * @{ - */ - -USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, - uint8_t *buf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlContinueRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len); - -USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev); - -USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev); - -uint16_t USBD_GetRxCount (USBD_HandleTypeDef *pdev , - uint8_t epnum); - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_IOREQ_H */ - -/** - * @} - */ - -/** -* @} -*/ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c deleted file mode 100644 index 0158829c..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_core.c +++ /dev/null @@ -1,565 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_core.c - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief This file provides all the USBD core functions. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_core.h" - -/** @addtogroup STM32_USBD_DEVICE_LIBRARY -* @{ -*/ - - -/** @defgroup USBD_CORE -* @brief usbd core module -* @{ -*/ - -/** @defgroup USBD_CORE_Private_TypesDefinitions -* @{ -*/ -/** -* @} -*/ - - -/** @defgroup USBD_CORE_Private_Defines -* @{ -*/ - -/** -* @} -*/ - - -/** @defgroup USBD_CORE_Private_Macros -* @{ -*/ -/** -* @} -*/ - - - - -/** @defgroup USBD_CORE_Private_FunctionPrototypes -* @{ -*/ - -/** -* @} -*/ - -/** @defgroup USBD_CORE_Private_Variables -* @{ -*/ - -/** -* @} -*/ - -/** @defgroup USBD_CORE_Private_Functions -* @{ -*/ - -/** -* @brief USBD_Init -* Initializes the device stack and load the class driver -* @param pdev: device instance -* @param pdesc: Descriptor structure address -* @param id: Low level core index -* @retval None -*/ -USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id) -{ - /* Check whether the USB Host handle is valid */ - if(pdev == NULL) - { - USBD_ErrLog("Invalid Device handle"); - return USBD_FAIL; - } - - /* Unlink previous class*/ - if(pdev->pClass != NULL) - { - pdev->pClass = NULL; - } - - /* Assign USBD Descriptors */ - if(pdesc != NULL) - { - pdev->pDesc = pdesc; - } - - /* Set Device initial State */ - pdev->dev_state = USBD_STATE_DEFAULT; - pdev->id = id; - /* Initialize low level driver */ - USBD_LL_Init(pdev); - - return USBD_OK; -} - -/** -* @brief USBD_DeInit -* Re-Initialize th device library -* @param pdev: device instance -* @retval status: status -*/ -USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev) -{ - /* Set Default State */ - pdev->dev_state = USBD_STATE_DEFAULT; - - /* Free Class Resources */ - pdev->pClass->DeInit(pdev, pdev->dev_config); - - /* Stop the low level driver */ - USBD_LL_Stop(pdev); - - /* Initialize low level driver */ - USBD_LL_DeInit(pdev); - - return USBD_OK; -} - - -/** - * @brief USBD_RegisterClass - * Link class driver to Device Core. - * @param pDevice : Device Handle - * @param pclass: Class handle - * @retval USBD Status - */ -USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass) -{ - USBD_StatusTypeDef status = USBD_OK; - if(pclass != 0) - { - /* link the class to the USB Device handle */ - pdev->pClass = pclass; - status = USBD_OK; - } - else - { - USBD_ErrLog("Invalid Class handle"); - status = USBD_FAIL; - } - - return status; -} - -/** - * @brief USBD_Start - * Start the USB Device Core. - * @param pdev: Device Handle - * @retval USBD Status - */ -USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev) -{ - - /* Start the low level driver */ - USBD_LL_Start(pdev); - - return USBD_OK; -} - -/** - * @brief USBD_Stop - * Stop the USB Device Core. - * @param pdev: Device Handle - * @retval USBD Status - */ -USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev) -{ - /* Free Class Resources */ - pdev->pClass->DeInit(pdev, pdev->dev_config); - - /* Stop the low level driver */ - USBD_LL_Stop(pdev); - - return USBD_OK; -} - -/** -* @brief USBD_RunTestMode -* Launch test mode process -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_RunTestMode (USBD_HandleTypeDef *pdev) -{ - return USBD_OK; -} - - -/** -* @brief USBD_SetClassConfig -* Configure device and start the interface -* @param pdev: device instance -* @param cfgidx: configuration index -* @retval status -*/ - -USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) -{ - USBD_StatusTypeDef ret = USBD_FAIL; - - if(pdev->pClass != NULL) - { - /* Set configuration and Start the Class*/ - if(pdev->pClass->Init(pdev, cfgidx) == 0) - { - ret = USBD_OK; - } - } - return ret; -} - -/** -* @brief USBD_ClrClassConfig -* Clear current configuration -* @param pdev: device instance -* @param cfgidx: configuration index -* @retval status: USBD_StatusTypeDef -*/ -USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) -{ - /* Clear configuration and De-initialize the Class process*/ - pdev->pClass->DeInit(pdev, cfgidx); - return USBD_OK; -} - - -/** -* @brief USBD_SetupStage -* Handle the setup stage -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup) -{ - - USBD_ParseSetupRequest(&pdev->request, psetup); - - pdev->ep0_state = USBD_EP0_SETUP; - pdev->ep0_data_len = pdev->request.wLength; - - switch (pdev->request.bmRequest & 0x1F) - { - case USB_REQ_RECIPIENT_DEVICE: - USBD_StdDevReq (pdev, &pdev->request); - break; - - case USB_REQ_RECIPIENT_INTERFACE: - USBD_StdItfReq(pdev, &pdev->request); - break; - - case USB_REQ_RECIPIENT_ENDPOINT: - USBD_StdEPReq(pdev, &pdev->request); - break; - - default: - USBD_LL_StallEP(pdev , pdev->request.bmRequest & 0x80); - break; - } - return USBD_OK; -} - -/** -* @brief USBD_DataOutStage -* Handle data OUT stage -* @param pdev: device instance -* @param epnum: endpoint index -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata) -{ - USBD_EndpointTypeDef *pep; - - if(epnum == 0) - { - pep = &pdev->ep_out[0]; - - if ( pdev->ep0_state == USBD_EP0_DATA_OUT) - { - if(pep->rem_length > pep->maxpacket) - { - pep->rem_length -= pep->maxpacket; - - USBD_CtlContinueRx (pdev, - pdata, - MIN(pep->rem_length ,pep->maxpacket)); - } - else - { - if((pdev->pClass->EP0_RxReady != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->EP0_RxReady(pdev); - } - USBD_CtlSendStatus(pdev); - } - } - } - else if((pdev->pClass->DataOut != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->DataOut(pdev, epnum); - } - return USBD_OK; -} - -/** -* @brief USBD_DataInStage -* Handle data in stage -* @param pdev: device instance -* @param epnum: endpoint index -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev ,uint8_t epnum, uint8_t *pdata) -{ - USBD_EndpointTypeDef *pep; - - if(epnum == 0) - { - pep = &pdev->ep_in[0]; - - if ( pdev->ep0_state == USBD_EP0_DATA_IN) - { - if(pep->rem_length > pep->maxpacket) - { - pep->rem_length -= pep->maxpacket; - - USBD_CtlContinueSendData (pdev, - pdata, - pep->rem_length); - - /* Prepare endpoint for premature end of transfer */ - USBD_LL_PrepareReceive (pdev, - 0, - NULL, - 0); - } - else - { /* last packet is MPS multiple, so send ZLP packet */ - if((pep->total_length % pep->maxpacket == 0) && - (pep->total_length >= pep->maxpacket) && - (pep->total_length < pdev->ep0_data_len )) - { - - USBD_CtlContinueSendData(pdev , NULL, 0); - pdev->ep0_data_len = 0; - - /* Prepare endpoint for premature end of transfer */ - USBD_LL_PrepareReceive (pdev, - 0, - NULL, - 0); - } - else - { - if((pdev->pClass->EP0_TxSent != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->EP0_TxSent(pdev); - } - USBD_CtlReceiveStatus(pdev); - } - } - } - if (pdev->dev_test_mode == 1) - { - USBD_RunTestMode(pdev); - pdev->dev_test_mode = 0; - } - } - else if((pdev->pClass->DataIn != NULL)&& - (pdev->dev_state == USBD_STATE_CONFIGURED)) - { - pdev->pClass->DataIn(pdev, epnum); - } - return USBD_OK; -} - -/** -* @brief USBD_LL_Reset -* Handle Reset event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev) -{ - /* Open EP0 OUT */ - USBD_LL_OpenEP(pdev, - 0x00, - USBD_EP_TYPE_CTRL, - USB_MAX_EP0_SIZE); - - pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE; - - /* Open EP0 IN */ - USBD_LL_OpenEP(pdev, - 0x80, - USBD_EP_TYPE_CTRL, - USB_MAX_EP0_SIZE); - - pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE; - /* Upon Reset call user call back */ - pdev->dev_state = USBD_STATE_DEFAULT; - - if (pdev->pClassData) - pdev->pClass->DeInit(pdev, pdev->dev_config); - - - return USBD_OK; -} - - - - -/** -* @brief USBD_LL_Reset -* Handle Reset event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed) -{ - pdev->dev_speed = speed; - return USBD_OK; -} - -/** -* @brief USBD_Suspend -* Handle Suspend event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev) -{ - pdev->dev_old_state = pdev->dev_state; - pdev->dev_state = USBD_STATE_SUSPENDED; - return USBD_OK; -} - -/** -* @brief USBD_Resume -* Handle Resume event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev) -{ - pdev->dev_state = pdev->dev_old_state; - return USBD_OK; -} - -/** -* @brief USBD_SOF -* Handle SOF event -* @param pdev: device instance -* @retval status -*/ - -USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev) -{ - if(pdev->dev_state == USBD_STATE_CONFIGURED) - { - if(pdev->pClass->SOF != NULL) - { - pdev->pClass->SOF(pdev); - } - } - return USBD_OK; -} - -/** -* @brief USBD_IsoINIncomplete -* Handle iso in incomplete event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) -{ - return USBD_OK; -} - -/** -* @brief USBD_IsoOUTIncomplete -* Handle iso out incomplete event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) -{ - return USBD_OK; -} - -/** -* @brief USBD_DevConnected -* Handle device connection event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev) -{ - return USBD_OK; -} - -/** -* @brief USBD_DevDisconnected -* Handle device disconnection event -* @param pdev: device instance -* @retval status -*/ -USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev) -{ - /* Free Class Resources */ - pdev->dev_state = USBD_STATE_DEFAULT; - pdev->pClass->DeInit(pdev, pdev->dev_config); - - return USBD_OK; -} -/** -* @} -*/ - - -/** -* @} -*/ - - -/** -* @} -*/ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ - diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c b/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c deleted file mode 100644 index 49330c66..00000000 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c +++ /dev/null @@ -1,782 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_req.c - * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief This file provides the standard USB requests following chapter 9. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2015 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ctlreq.h" -#include "usbd_ioreq.h" - - -/** @addtogroup STM32_USBD_STATE_DEVICE_LIBRARY - * @{ - */ - - -/** @defgroup USBD_REQ - * @brief USB standard requests module - * @{ - */ - -/** @defgroup USBD_REQ_Private_TypesDefinitions - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Defines - * @{ - */ - -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Macros - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Variables - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_FunctionPrototypes - * @{ - */ -static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_SetAddress(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_SetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_GetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_GetStatus(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_SetFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req); - -static uint8_t USBD_GetLen(uint8_t *buf); - -/** - * @} - */ - - -/** @defgroup USBD_REQ_Private_Functions - * @{ - */ - - -/** -* @brief USBD_StdDevReq -* Handle standard usb device requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -USBD_StatusTypeDef USBD_StdDevReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) -{ - USBD_StatusTypeDef ret = USBD_OK; - - switch (req->bRequest) - { - case USB_REQ_GET_DESCRIPTOR: - - USBD_GetDescriptor (pdev, req) ; - break; - - case USB_REQ_SET_ADDRESS: - USBD_SetAddress(pdev, req); - break; - - case USB_REQ_SET_CONFIGURATION: - USBD_SetConfig (pdev , req); - break; - - case USB_REQ_GET_CONFIGURATION: - USBD_GetConfig (pdev , req); - break; - - case USB_REQ_GET_STATUS: - USBD_GetStatus (pdev , req); - break; - - - case USB_REQ_SET_FEATURE: - USBD_SetFeature (pdev , req); - break; - - case USB_REQ_CLEAR_FEATURE: - USBD_ClrFeature (pdev , req); - break; - - default: - USBD_CtlError(pdev , req); - break; - } - - return ret; -} - -/** -* @brief USBD_StdItfReq -* Handle standard usb interface requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -USBD_StatusTypeDef USBD_StdItfReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) -{ - USBD_StatusTypeDef ret = USBD_OK; - - switch (pdev->dev_state) - { - case USBD_STATE_CONFIGURED: - - if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) - { - pdev->pClass->Setup (pdev, req); - - if((req->wLength == 0)&& (ret == USBD_OK)) - { - USBD_CtlSendStatus(pdev); - } - } - else - { - USBD_CtlError(pdev , req); - } - break; - - default: - USBD_CtlError(pdev , req); - break; - } - return USBD_OK; -} - -/** -* @brief USBD_StdEPReq -* Handle standard usb endpoint requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -USBD_StatusTypeDef USBD_StdEPReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) -{ - - uint8_t ep_addr; - USBD_StatusTypeDef ret = USBD_OK; - USBD_EndpointTypeDef *pep; - ep_addr = LOBYTE(req->wIndex); - - /* Check if it is a class request */ - if ((req->bmRequest & 0x60) == 0x20) - { - pdev->pClass->Setup (pdev, req); - - return USBD_OK; - } - - switch (req->bRequest) - { - - case USB_REQ_SET_FEATURE : - - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if ((ep_addr != 0x00) && (ep_addr != 0x80)) - { - USBD_LL_StallEP(pdev , ep_addr); - } - break; - - case USBD_STATE_CONFIGURED: - if (req->wValue == USB_FEATURE_EP_HALT) - { - if ((ep_addr != 0x00) && (ep_addr != 0x80)) - { - USBD_LL_StallEP(pdev , ep_addr); - - } - } - pdev->pClass->Setup (pdev, req); - USBD_CtlSendStatus(pdev); - - break; - - default: - USBD_CtlError(pdev , req); - break; - } - break; - - case USB_REQ_CLEAR_FEATURE : - - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if ((ep_addr != 0x00) && (ep_addr != 0x80)) - { - USBD_LL_StallEP(pdev , ep_addr); - } - break; - - case USBD_STATE_CONFIGURED: - if (req->wValue == USB_FEATURE_EP_HALT) - { - if ((ep_addr & 0x7F) != 0x00) - { - USBD_LL_ClearStallEP(pdev , ep_addr); - pdev->pClass->Setup (pdev, req); - } - USBD_CtlSendStatus(pdev); - } - break; - - default: - USBD_CtlError(pdev , req); - break; - } - break; - - case USB_REQ_GET_STATUS: - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if ((ep_addr & 0x7F) != 0x00) - { - USBD_LL_StallEP(pdev , ep_addr); - } - break; - - case USBD_STATE_CONFIGURED: - pep = ((ep_addr & 0x80) == 0x80) ? &pdev->ep_in[ep_addr & 0x7F]:\ - &pdev->ep_out[ep_addr & 0x7F]; - if(USBD_LL_IsStallEP(pdev, ep_addr)) - { - pep->status = 0x0001; - } - else - { - pep->status = 0x0000; - } - - USBD_CtlSendData (pdev, - (uint8_t *)&pep->status, - 2); - break; - - default: - USBD_CtlError(pdev , req); - break; - } - break; - - default: - break; - } - return ret; -} -/** -* @brief USBD_GetDescriptor -* Handle Get Descriptor requests -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - uint16_t len; - uint8_t *pbuf; - - - switch (req->wValue >> 8) - { -#if (USBD_LPM_ENABLED == 1) - case USB_DESC_TYPE_BOS: - pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len); - break; -#endif - case USB_DESC_TYPE_DEVICE: - pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len); - break; - - case USB_DESC_TYPE_CONFIGURATION: - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - pbuf = (uint8_t *)pdev->pClass->GetHSConfigDescriptor(&len); - pbuf[1] = USB_DESC_TYPE_CONFIGURATION; - } - else - { - pbuf = (uint8_t *)pdev->pClass->GetFSConfigDescriptor(&len); - pbuf[1] = USB_DESC_TYPE_CONFIGURATION; - } - break; - - case USB_DESC_TYPE_STRING: - switch ((uint8_t)(req->wValue)) - { - case USBD_IDX_LANGID_STR: - pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_MFC_STR: - pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_PRODUCT_STR: - pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_SERIAL_STR: - pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_CONFIG_STR: - pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len); - break; - - case USBD_IDX_INTERFACE_STR: - pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len); - break; - - default: -#if (USBD_SUPPORT_USER_STRING == 1) - pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue) , &len); - break; -#else - USBD_CtlError(pdev , req); - return; -#endif - } - break; - case USB_DESC_TYPE_DEVICE_QUALIFIER: - - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - pbuf = (uint8_t *)pdev->pClass->GetDeviceQualifierDescriptor(&len); - break; - } - else - { - USBD_CtlError(pdev , req); - return; - } - - case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - pbuf = (uint8_t *)pdev->pClass->GetOtherSpeedConfigDescriptor(&len); - pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; - break; - } - else - { - USBD_CtlError(pdev , req); - return; - } - - default: - USBD_CtlError(pdev , req); - return; - } - - if((len != 0)&& (req->wLength != 0)) - { - - len = MIN(len , req->wLength); - - USBD_CtlSendData (pdev, - pbuf, - len); - } - -} - -/** -* @brief USBD_SetAddress -* Set device address -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_SetAddress(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - uint8_t dev_addr; - - if ((req->wIndex == 0) && (req->wLength == 0)) - { - dev_addr = (uint8_t)(req->wValue) & 0x7F; - - if (pdev->dev_state == USBD_STATE_CONFIGURED) - { - USBD_CtlError(pdev , req); - } - else - { - pdev->dev_address = dev_addr; - USBD_LL_SetUSBAddress(pdev, dev_addr); - USBD_CtlSendStatus(pdev); - - if (dev_addr != 0) - { - pdev->dev_state = USBD_STATE_ADDRESSED; - } - else - { - pdev->dev_state = USBD_STATE_DEFAULT; - } - } - } - else - { - USBD_CtlError(pdev , req); - } -} - -/** -* @brief USBD_SetConfig -* Handle Set device configuration request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_SetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - static uint8_t cfgidx; - - cfgidx = (uint8_t)(req->wValue); - - if (cfgidx > USBD_MAX_NUM_CONFIGURATION ) - { - USBD_CtlError(pdev , req); - } - else - { - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - if (cfgidx) - { - pdev->dev_config = cfgidx; - pdev->dev_state = USBD_STATE_CONFIGURED; - if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL) - { - USBD_CtlError(pdev , req); - return; - } - USBD_CtlSendStatus(pdev); - } - else - { - USBD_CtlSendStatus(pdev); - } - break; - - case USBD_STATE_CONFIGURED: - if (cfgidx == 0) - { - pdev->dev_state = USBD_STATE_ADDRESSED; - pdev->dev_config = cfgidx; - USBD_ClrClassConfig(pdev , cfgidx); - USBD_CtlSendStatus(pdev); - - } - else if (cfgidx != pdev->dev_config) - { - /* Clear old configuration */ - USBD_ClrClassConfig(pdev , pdev->dev_config); - - /* set new configuration */ - pdev->dev_config = cfgidx; - if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL) - { - USBD_CtlError(pdev , req); - return; - } - USBD_CtlSendStatus(pdev); - } - else - { - USBD_CtlSendStatus(pdev); - } - break; - - default: - USBD_CtlError(pdev , req); - break; - } - } -} - -/** -* @brief USBD_GetConfig -* Handle Get device configuration request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_GetConfig(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - if (req->wLength != 1) - { - USBD_CtlError(pdev , req); - } - else - { - switch (pdev->dev_state ) - { - case USBD_STATE_ADDRESSED: - pdev->dev_default_config = 0; - USBD_CtlSendData (pdev, - (uint8_t *)&pdev->dev_default_config, - 1); - break; - - case USBD_STATE_CONFIGURED: - - USBD_CtlSendData (pdev, - (uint8_t *)&pdev->dev_config, - 1); - break; - - default: - USBD_CtlError(pdev , req); - break; - } - } -} - -/** -* @brief USBD_GetStatus -* Handle Get Status request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_GetStatus(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - case USBD_STATE_CONFIGURED: - -#if ( USBD_SELF_POWERED == 1) - pdev->dev_config_status = USB_CONFIG_SELF_POWERED; -#else - pdev->dev_config_status = 0; -#endif - - if (pdev->dev_remote_wakeup) - { - pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; - } - - USBD_CtlSendData (pdev, - (uint8_t *)& pdev->dev_config_status, - 2); - break; - - default : - USBD_CtlError(pdev , req); - break; - } -} - - -/** -* @brief USBD_SetFeature -* Handle Set device feature request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_SetFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - - if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) - { - pdev->dev_remote_wakeup = 1; - pdev->pClass->Setup (pdev, req); - USBD_CtlSendStatus(pdev); - } - -} - - -/** -* @brief USBD_ClrFeature -* Handle clear device feature request -* @param pdev: device instance -* @param req: usb request -* @retval status -*/ -static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - switch (pdev->dev_state) - { - case USBD_STATE_ADDRESSED: - case USBD_STATE_CONFIGURED: - if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) - { - pdev->dev_remote_wakeup = 0; - pdev->pClass->Setup (pdev, req); - USBD_CtlSendStatus(pdev); - } - break; - - default : - USBD_CtlError(pdev , req); - break; - } -} - -/** -* @brief USBD_ParseSetupRequest -* Copy buffer into setup structure -* @param pdev: device instance -* @param req: usb request -* @retval None -*/ - -void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata) -{ - req->bmRequest = *(uint8_t *) (pdata); - req->bRequest = *(uint8_t *) (pdata + 1); - req->wValue = SWAPBYTE (pdata + 2); - req->wIndex = SWAPBYTE (pdata + 4); - req->wLength = SWAPBYTE (pdata + 6); - -} - -/** -* @brief USBD_CtlError -* Handle USB low level Error -* @param pdev: device instance -* @param req: usb request -* @retval None -*/ - -void USBD_CtlError( USBD_HandleTypeDef *pdev , - USBD_SetupReqTypedef *req) -{ - USBD_LL_StallEP(pdev , 0x80); - USBD_LL_StallEP(pdev , 0); -} - - -/** - * @brief USBD_GetString - * Convert Ascii string into unicode one - * @param desc : descriptor buffer - * @param unicode : Formatted string buffer (unicode) - * @param len : descriptor length - * @retval None - */ -void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len) -{ - uint8_t idx = 0; - - if (desc != NULL) - { - *len = USBD_GetLen(desc) * 2 + 2; - unicode[idx++] = *len; - unicode[idx++] = USB_DESC_TYPE_STRING; - - while (*desc != '\0') - { - unicode[idx++] = *desc++; - unicode[idx++] = 0x00; - } - } -} - -/** - * @brief USBD_GetLen - * return the string length - * @param buf : pointer to the ascii string buffer - * @retval string length - */ -static uint8_t USBD_GetLen(uint8_t *buf) -{ - uint8_t len = 0; - - while (*buf != '\0') - { - len++; - buf++; - } - - return len; -} -/** - * @} - */ - - -/** - * @} - */ - - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h deleted file mode 100644 index ceca6960..00000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h +++ /dev/null @@ -1,414 +0,0 @@ -/* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - -/** - * @file atomic.h - * @brief FreeRTOS atomic operation support. - * - * This file implements atomic functions by disabling interrupts globally. - * Implementations with architecture specific atomic instructions can be - * provided under each compiler directory. - */ - -#ifndef ATOMIC_H -#define ATOMIC_H - -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include atomic.h" -#endif - -/* Standard includes. */ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Port specific definitions -- entering/exiting critical section. - * Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h - * - * Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with - * ATOMIC_ENTER_CRITICAL(). - * - */ -#if defined( portSET_INTERRUPT_MASK_FROM_ISR ) - - /* Nested interrupt scheme is supported in this port. */ - #define ATOMIC_ENTER_CRITICAL() \ - UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR() - - #define ATOMIC_EXIT_CRITICAL() \ - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType ) - -#else - - /* Nested interrupt scheme is NOT supported in this port. */ - #define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL() - #define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL() - -#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */ - -/* - * Port specific definition -- "always inline". - * Inline is compiler specific, and may not always get inlined depending on your - * optimization level. Also, inline is considered as performance optimization - * for atomic. Thus, if portFORCE_INLINE is not provided by portmacro.h, - * instead of resulting error, simply define it away. - */ -#ifndef portFORCE_INLINE - #define portFORCE_INLINE -#endif - -#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */ -#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */ - -/*----------------------------- Swap && CAS ------------------------------*/ - -/** - * Atomic compare-and-swap - * - * @brief Performs an atomic compare-and-swap operation on the specified values. - * - * @param[in, out] pulDestination Pointer to memory location from where value is - * to be loaded and checked. - * @param[in] ulExchange If condition meets, write this value to memory. - * @param[in] ulComparand Swap condition. - * - * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped. - * - * @note This function only swaps *pulDestination with ulExchange, if previous - * *pulDestination value equals ulComparand. - */ -static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination, - uint32_t ulExchange, - uint32_t ulComparand ) -{ -uint32_t ulReturnValue; - - ATOMIC_ENTER_CRITICAL(); - { - if( *pulDestination == ulComparand ) - { - *pulDestination = ulExchange; - ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; - } - else - { - ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; - } - } - ATOMIC_EXIT_CRITICAL(); - - return ulReturnValue; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic swap (pointers) - * - * @brief Atomically sets the address pointed to by *ppvDestination to the value - * of *pvExchange. - * - * @param[in, out] ppvDestination Pointer to memory location from where a pointer - * value is to be loaded and written back to. - * @param[in] pvExchange Pointer value to be written to *ppvDestination. - * - * @return The initial value of *ppvDestination. - */ -static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination, - void * pvExchange ) -{ -void * pReturnValue; - - ATOMIC_ENTER_CRITICAL(); - { - pReturnValue = *ppvDestination; - *ppvDestination = pvExchange; - } - ATOMIC_EXIT_CRITICAL(); - - return pReturnValue; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic compare-and-swap (pointers) - * - * @brief Performs an atomic compare-and-swap operation on the specified pointer - * values. - * - * @param[in, out] ppvDestination Pointer to memory location from where a pointer - * value is to be loaded and checked. - * @param[in] pvExchange If condition meets, write this value to memory. - * @param[in] pvComparand Swap condition. - * - * @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped. - * - * @note This function only swaps *ppvDestination with pvExchange, if previous - * *ppvDestination value equals pvComparand. - */ -static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination, - void * pvExchange, - void * pvComparand ) -{ -uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; - - ATOMIC_ENTER_CRITICAL(); - { - if( *ppvDestination == pvComparand ) - { - *ppvDestination = pvExchange; - ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; - } - } - ATOMIC_EXIT_CRITICAL(); - - return ulReturnValue; -} - - -/*----------------------------- Arithmetic ------------------------------*/ - -/** - * Atomic add - * - * @brief Atomically adds count to the value of the specified pointer points to. - * - * @param[in,out] pulAddend Pointer to memory location from where value is to be - * loaded and written back to. - * @param[in] ulCount Value to be added to *pulAddend. - * - * @return previous *pulAddend value. - */ -static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend, - uint32_t ulCount ) -{ - uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend += ulCount; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic subtract - * - * @brief Atomically subtracts count from the value of the specified pointer - * pointers to. - * - * @param[in,out] pulAddend Pointer to memory location from where value is to be - * loaded and written back to. - * @param[in] ulCount Value to be subtract from *pulAddend. - * - * @return previous *pulAddend value. - */ -static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend, - uint32_t ulCount ) -{ - uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend -= ulCount; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic increment - * - * @brief Atomically increments the value of the specified pointer points to. - * - * @param[in,out] pulAddend Pointer to memory location from where value is to be - * loaded and written back to. - * - * @return *pulAddend value before increment. - */ -static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend ) -{ -uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend += 1; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic decrement - * - * @brief Atomically decrements the value of the specified pointer points to - * - * @param[in,out] pulAddend Pointer to memory location from where value is to be - * loaded and written back to. - * - * @return *pulAddend value before decrement. - */ -static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend ) -{ -uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend -= 1; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} - -/*----------------------------- Bitwise Logical ------------------------------*/ - -/** - * Atomic OR - * - * @brief Performs an atomic OR operation on the specified values. - * - * @param [in, out] pulDestination Pointer to memory location from where value is - * to be loaded and written back to. - * @param [in] ulValue Value to be ORed with *pulDestination. - * - * @return The original value of *pulDestination. - */ -static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) -{ -uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination |= ulValue; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic AND - * - * @brief Performs an atomic AND operation on the specified values. - * - * @param [in, out] pulDestination Pointer to memory location from where value is - * to be loaded and written back to. - * @param [in] ulValue Value to be ANDed with *pulDestination. - * - * @return The original value of *pulDestination. - */ -static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) -{ -uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination &= ulValue; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic NAND - * - * @brief Performs an atomic NAND operation on the specified values. - * - * @param [in, out] pulDestination Pointer to memory location from where value is - * to be loaded and written back to. - * @param [in] ulValue Value to be NANDed with *pulDestination. - * - * @return The original value of *pulDestination. - */ -static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) -{ -uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination = ~( ulCurrent & ulValue ); - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} -/*-----------------------------------------------------------*/ - -/** - * Atomic XOR - * - * @brief Performs an atomic XOR operation on the specified values. - * - * @param [in, out] pulDestination Pointer to memory location from where value is - * to be loaded and written back to. - * @param [in] ulValue Value to be XORed with *pulDestination. - * - * @return The original value of *pulDestination. - */ -static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) -{ -uint32_t ulCurrent; - - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination ^= ulValue; - } - ATOMIC_EXIT_CRITICAL(); - - return ulCurrent; -} - -#ifdef __cplusplus -} -#endif - -#endif /* ATOMIC_H */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme deleted file mode 100644 index 4414c29e..00000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stdint.readme +++ /dev/null @@ -1,27 +0,0 @@ - -#ifndef FREERTOS_STDINT -#define FREERTOS_STDINT - -/******************************************************************************* - * THIS IS NOT A FULL stdint.h IMPLEMENTATION - It only contains the definitions - * necessary to build the FreeRTOS code. It is provided to allow FreeRTOS to be - * built using compilers that do not provide their own stdint.h definition. - * - * To use this file: - * - * 1) Copy this file into the directory that contains your FreeRTOSConfig.h - * header file, as that directory will already be in the compilers include - * path. - * - * 2) Rename the copied file stdint.h. - * - */ - -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef long int32_t; -typedef unsigned long uint32_t; - -#endif /* FREERTOS_STDINT */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url deleted file mode 100644 index 6c23737d..00000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/ReadMe.url +++ /dev/null @@ -1,5 +0,0 @@ -[{000214A0-0000-0000-C000-000000000046}] -Prop3=19,2 -[InternetShortcut] -URL=http://www.freertos.org/a00111.html -IDList= diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt b/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt deleted file mode 100644 index 58480c56..00000000 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/readme.txt +++ /dev/null @@ -1,17 +0,0 @@ -Each real time kernel port consists of three files that contain the core kernel -components and are common to every port, and one or more files that are -specific to a particular microcontroller and or compiler. - -+ The FreeRTOS/Source directory contains the three files that are common to -every port - list.c, queue.c and tasks.c. The kernel is contained within these -three files. croutine.c implements the optional co-routine functionality - which -is normally only used on very memory limited systems. - -+ The FreeRTOS/Source/Portable directory contains the files that are specific to -a particular microcontroller and or compiler. - -+ The FreeRTOS/Source/include directory contains the real time kernel header -files. - -See the readme file in the FreeRTOS/Source/Portable directory for more -information. \ No newline at end of file diff --git a/Firmware/Board/v3/Src/usbd_conf.c b/Firmware/Board/v3/Src/usbd_conf.c index ff1a4f17..3d8f6b9a 100644 --- a/Firmware/Board/v3/Src/usbd_conf.c +++ b/Firmware/Board/v3/Src/usbd_conf.c @@ -677,7 +677,7 @@ USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_a * @param size: Data size * @retval USBD status */ -USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint16_t size) +USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size) { HAL_StatusTypeDef hal_status = HAL_OK; USBD_StatusTypeDef usb_status = USBD_OK; @@ -712,7 +712,7 @@ USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, u * @param size: Data size * @retval USBD status */ -USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint16_t size) +USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size) { HAL_StatusTypeDef hal_status = HAL_OK; USBD_StatusTypeDef usb_status = USBD_OK; diff --git a/Firmware/Drivers/DRV8353/drv8353.cpp b/Firmware/Drivers/DRV8353/drv8353.cpp deleted file mode 100644 index a1a06075..00000000 --- a/Firmware/Drivers/DRV8353/drv8353.cpp +++ /dev/null @@ -1,195 +0,0 @@ - -#include "drv8353.hpp" -#include "utils.hpp" -#include "cmsis_os.h" -#include "board.h" - -const SPI_InitTypeDef Drv8353::spi_config_ = { - .Mode = SPI_MODE_MASTER, - .Direction = SPI_DIRECTION_2LINES, - .DataSize = SPI_DATASIZE_16BIT, - .CLKPolarity = SPI_POLARITY_LOW, - .CLKPhase = SPI_PHASE_2EDGE, - .NSS = SPI_NSS_SOFT, - .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16, - .FirstBit = SPI_FIRSTBIT_MSB, - .TIMode = SPI_TIMODE_DISABLE, - .CRCCalculation = SPI_CRCCALCULATION_DISABLE, - .CRCPolynomial = 10, -}; - -bool Drv8353::config(float requested_gain, float* actual_gain) { - // Calculate gain setting: Snap down to have equal or larger range as - // requested or largest possible range otherwise - - uint16_t gain_setting = 3; - float gain_choices[] = {5.0f, 10.0f, 20.0f, 40.0f}; - while (gain_setting && (gain_choices[gain_setting] > requested_gain)) { - gain_setting--; - } - - if (actual_gain) { - *actual_gain = gain_choices[gain_setting]; - } - - // For reference: - // Rds(on) of NTMFS5C628NL is ~3mOhm at 160A, 100°C and we have two in parallel - // Rshunt of ODrive v4 is 1mOhm - - RegisterFile new_config; - - new_config.driver_control = - (0b1 << 10) // overcurrent protection of any half bridge shuts down all half bridges - | (0b0 << 9) // enable Vcp and Vgls undervoltage lockout fault - | (0b0 << 8) // enable gate drive fault - | (0b1 << 7) // report overtemperature warning on nFAULT - | (0b00 << 5) // 6x PWM mode - | (0b0 << 4) // [applies to 1x PWM mode only] - | (0b0 << 3) // [applies to 1x PWM mode only] - | (0b0 << 2) // don't coast - | (0b0 << 1) // don't brake - | (0b0 << 0); // don't clear faults - - new_config.gate_drive_hs = - (0b011 << 8) // don't lock registers - | (0b1111 << 4) // 1A source current on high side FET drivers - | (0b1111 << 0); // 2A sink current on high side FET drivers - - new_config.gate_drive_ls = - (0b1 << 10) // clear overcurrent faults at next PWM input or t_retry (whichever comes first) - this has no effect since we use latched overcurrent fault mode - | (0b01 << 8) // 1000 ns peak gate current drive time - | (0b1111 << 4) // 1A source current on low side FET drivers - | (0b1111 << 0); // 2A sink current on low side FET drivers - - new_config.ocp_control = - (0b0 << 10) // retry time for Vds and shunt overcurrent protection: 8ms - | (0b01 << 8) // 100ns deadtime (we configure the STM timer to do 120ns deadtime as well) - | (0b00 << 6) // overcurrent causes a latching fault (no retry) - | (0b10 << 4) // overcurrent deglitch of 4us - | (0b0101 << 0); // Vds trip level 0.25 V (approx. ~133A per MOSFET at 100°C) - - new_config.csa_control = - (0b0 << 10) // measure current across SPx to SNx - | (0b1 << 9) // use Vref/2 as sense amplifier reference voltage - | (0b0 << 8) // measure Vds across SHx to SPx - | (gain_setting << 6) // select gain - | (0b0 << 5) // sense overcurrent fault enabled - | (0b000 << 2) // normal current sense operation on all three phases - | (0b00 << 0); // sense overcurrent protection at 0.25V sense input (corresponds to ~250A) - - bool regs_equal = (regs_.driver_control == new_config.driver_control) - && (regs_.gate_drive_hs == new_config.gate_drive_hs) - && (regs_.gate_drive_ls == new_config.gate_drive_ls) - && (regs_.ocp_control == new_config.ocp_control) - && (regs_.csa_control == new_config.csa_control); - - if (!regs_equal) { - regs_ = new_config; - state_ = kStateUninitialized; - enable_gpio_.write(false); - } - - return true; -} - -bool Drv8353::init() { - uint16_t val; - - if (state_ == kStateReady) { - return true; - } - - // Reset DRV chip. The enable pin also controls the SPI interface, not only - // the driver stages. - enable_gpio_.write(false); - delay_us(100); // t_rst, max = 40us - state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up - enable_gpio_.write(true); - osDelay(2); // t_wake, max = 1ms - - // Write current configuration - bool did_write_regs = write_reg(kRegNameDriverControl, regs_.driver_control) - && write_reg(kRegNameGateDriveHs, regs_.gate_drive_hs) - && write_reg(kRegNameGateDriveLs, regs_.gate_drive_ls) - && write_reg(kRegNameOcpControl, regs_.ocp_control) - && write_reg(kRegNameCsaControl, regs_.csa_control); - if (!did_write_regs) { - return false; - } - - // Wait for configuration to be applied - delay_us(100); - state_ = kStateStartupChecks; - - bool did_read_regs = read_reg(kRegNameDriverControl, &val) && (val == regs_.driver_control) - && read_reg(kRegNameGateDriveHs, &val) && (val == regs_.gate_drive_hs) - && read_reg(kRegNameGateDriveLs, &val) && (val == regs_.gate_drive_ls) - && read_reg(kRegNameOcpControl, &val) && (val == regs_.ocp_control) - && read_reg(kRegNameCsaControl, &val) && (val == regs_.csa_control); - if (!did_read_regs) { - return false; - } - - - if (get_error() != FaultType_NoFault) { - return false; - } - - // There could have been an nFAULT edge meanwhile. In this case we shouldn't - // consider the driver ready. - CRITICAL_SECTION() { - if (state_ == kStateStartupChecks) { - state_ = kStateReady; - } - } - - return state_ == kStateReady; -} - -void Drv8353::do_checks() { - if (state_ != kStateUninitialized && !nfault_gpio_.read()) { - state_ = kStateUninitialized; - } -} - -bool Drv8353::is_ready() { - return state_ == kStateReady; -} - -Drv8353::FaultType_e Drv8353::get_error() { - uint16_t fault1, fault2; - - if (!read_reg(kRegNameFaultStatus1, &fault1) || - !read_reg(kRegNameFaultStatus2, &fault2)) { - return (FaultType_e)0xffffffff; - } - - return (FaultType_e)((uint32_t)fault1 | ((uint32_t)fault2 << 16)); -} - -bool Drv8353::read_reg(const RegName_e regName, uint16_t* data) { - tx_buf_ = build_ctrl_word(DRV8353_CtrlMode_Read, regName, 0); - rx_buf_ = 0xffff; - if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) { - return false; - } - - delay_us(1); - - if (data) { - *data = rx_buf_ & 0x07FF; - } - - return true; -} - -bool Drv8353::write_reg(const RegName_e regName, const uint16_t data) { - // Do blocking write - tx_buf_ = build_ctrl_word(DRV8353_CtrlMode_Write, regName, data); - if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) { - return false; - } - delay_us(1); - - return true; -} diff --git a/Firmware/Drivers/DRV8353/drv8353.hpp b/Firmware/Drivers/DRV8353/drv8353.hpp deleted file mode 100644 index b2ad72e6..00000000 --- a/Firmware/Drivers/DRV8353/drv8353.hpp +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef __DRV8353_HPP -#define __DRV8353_HPP - -#include "stdbool.h" -#include "stdint.h" - -#include -#include -#include - - -class Drv8353 : public GateDriverBase, public OpAmpBase { -public: - typedef enum { - FaultType_NoFault = (0 << 0), - - // Fault Status Register 1 - FaultType_FAULT = (1 << 10), - FaultType_VDS_OCP = (1 << 9), - FaultType_GDF = (1 << 8), - FaultType_UVLO = (1 << 7), - FaultType_OTSD = (1 << 6), - FaultType_VDS_HA = (1 << 5), - FaultType_VDS_LA = (1 << 4), - FaultType_VDS_HB = (1 << 3), - FaultType_VDS_LB = (1 << 2), - FaultType_VDS_HC = (1 << 1), - FaultType_VDS_LC = (1 << 0), - - // Fault Status Register 2 - FaultType_SA_OC = (1 << 26), - FaultType_SB_OC = (1 << 25), - FaultType_SC_OC = (1 << 24), - FaultType_OTW = (1 << 23), - FaultType_GDUV = (1 << 22), - FaultType_VGS_HA = (1 << 21), - FaultType_VGS_LA = (1 << 20), - FaultType_VGS_HB = (1 << 19), - FaultType_VGS_LB = (1 << 18), - FaultType_VGS_HC = (1 << 17), - FaultType_VGS_LC = (1 << 16), - } FaultType_e; - - Drv8353(Stm32SpiArbiter* spi_arbiter, Stm32Gpio ncs_gpio, - Stm32Gpio enable_gpio, Stm32Gpio nfault_gpio) - : spi_arbiter_(spi_arbiter), ncs_gpio_(ncs_gpio), - enable_gpio_(enable_gpio), nfault_gpio_(nfault_gpio) {} - - /** - * @brief Prepares the gate driver's configuration. - * - * If the gate driver was in ready state and the new configuration is - * different from the old one then the gate driver will exit ready state. - * - * In any case changes to the configuration only take effect with a call to - * init(). - */ - bool config(float requested_gain, float* actual_gain); - - /** - * @brief Initializes the gate driver to the configuration prepared with - * config(). - * - * Returns true on success or false otherwise (e.g. if the gate driver is - * not connected or not powered or if config() was not yet called). - */ - bool init(); - - /** - * @brief Monitors the nFAULT pin. - * - * This must be run at an interval of <8ms from the moment the init() - * functions starts to run, otherwise it's possible that a temporary power - * loss is missed, leading to unwanted register values. - * In case of power loss the nFAULT pin can be low for as little as 8ms. - */ - void do_checks(); - - /** - * @brief Returns true if and only if the DRV8353 chip is in an initialized - * state and ready to do switching and current sensor opamp operation. - */ - bool is_ready() final; - - /** - * @brief This has no effect on this driver chip because the drive stages are - * always enabled while the chip is initialized - */ - bool set_enabled(bool enabled) final { return true; } - - FaultType_e get_error(); - - float get_midpoint() final { - return 0.5f; // [V] - } - - float get_max_output_swing() final { - return 1.35f / 1.65f; // +-1.35V, normalized from a scale of +-1.65V to +-0.5 - } - -private: - enum CtrlMode_e { - DRV8353_CtrlMode_Read = 1 << 15, //!< Read Mode - DRV8353_CtrlMode_Write = 0 << 15 //!< Write Mode - }; - - enum RegName_e { - kRegNameFaultStatus1 = (0 << 11), - kRegNameFaultStatus2 = (1 << 11), - kRegNameDriverControl = (2 << 11), - kRegNameGateDriveHs = (3 << 11), - kRegNameGateDriveLs = (4 << 11), - kRegNameOcpControl = (5 << 11), - kRegNameCsaControl = (6 << 11) - }; - - struct RegisterFile { - uint16_t driver_control; - uint16_t gate_drive_hs; - uint16_t gate_drive_ls; - uint16_t ocp_control; - uint16_t csa_control; - }; - - static inline uint16_t build_ctrl_word(const CtrlMode_e ctrlMode, - const RegName_e regName, - const uint16_t data) { - return ctrlMode | regName | (data & 0x07FF); - } - - /** @brief Reads data from a DRV8353 register */ - bool read_reg(const RegName_e regName, uint16_t* data); - - /** @brief Writes data to a DRV8353 register. There is no check if the write succeeded. */ - bool write_reg(const RegName_e regName, const uint16_t data); - - static const SPI_InitTypeDef spi_config_; - - // Configuration - Stm32SpiArbiter* spi_arbiter_; - Stm32Gpio ncs_gpio_; - Stm32Gpio enable_gpio_; - Stm32Gpio nfault_gpio_; - - RegisterFile regs_; //!< Current configuration. If is_ready_ is - //!< true then this can be considered consistent - //!< with the actual file on the DRV8353 chip. - - // We don't put these buffers on the stack because we place the stack in - // a RAM section which cannot be used by DMA. - uint16_t tx_buf_, rx_buf_; - - enum { - kStateUninitialized, - kStateStartupChecks, - kStateReady, - } state_ = kStateUninitialized; -}; - - -#endif // __DRV8353_HPP diff --git a/Firmware/Drivers/status_led.cpp b/Firmware/Drivers/status_led.cpp deleted file mode 100644 index e3942a5b..00000000 --- a/Firmware/Drivers/status_led.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -#include "status_led.hpp" -#include - -void I2sRgbLed::init() { - uint16_t init_buf[1] = {0}; - HAL_I2S_Transmit_DMA(&hi2s1, init_buf, 1); - while (hi2s1.State != HAL_I2S_STATE_READY); -} - -void I2sRgbLed::set_color(rgb_t color) { - rgb_t stripe[1] = {color}; - I2sWs2812Encoder::encode(stripe, 1, 0, i2s_buf_, kI2sBufLen); - HAL_I2S_Transmit_DMA(&hi2s1, i2s_buf_, kI2sBufLen); -} diff --git a/Firmware/Drivers/status_led.hpp b/Firmware/Drivers/status_led.hpp deleted file mode 100644 index ea229957..00000000 --- a/Firmware/Drivers/status_led.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __STATUS_LED_HPP -#define __STATUS_LED_HPP - -#include -#include - -struct rgb_t { - rgb_t() { - val = 0; - } - rgb_t(uint8_t r, uint8_t g, uint8_t b) { - val = (r << 16) | (g << 8) | (b << 0); - } - rgb_t(uint32_t val) : val(val) {} - - uint8_t get_r() { return (val >> 16) & 0xff; } - uint8_t get_g() { return (val >> 8) & 0xff; } - uint8_t get_b() { return (val >> 0) & 0xff; } - - template - static rgb_t mix(rgb_t color0, rgb_t color1, uint32_t ratio) { - uint32_t ratio1 = (ratio >= max_val) ? (max_val - 1) : ratio; - uint32_t ratio0 = max_val - ratio1; - return rgb_t{ - (uint8_t)(((uint32_t)color0.get_r() * ratio0 + (uint32_t)color1.get_r() * ratio1) / max_val), - (uint8_t)(((uint32_t)color0.get_g() * ratio0 + (uint32_t)color1.get_g() * ratio1) / max_val), - (uint8_t)(((uint32_t)color0.get_b() * ratio0 + (uint32_t)color1.get_b() * ratio1) / max_val), - }; - } - - uint32_t val; -}; - -struct Ws2812EncoderTraits { - static constexpr uint32_t kBaudrate = 3310345ULL; - static constexpr uint16_t kSymbolHigh = 0b1110; // 906ns on, 302ns off - static constexpr uint16_t kSymbolLow = 0b1000; // 302ns on, 906ns off - static constexpr size_t kNumBitsPerSymbol = 4; - static constexpr size_t kBitsPerLed = 24; - static constexpr size_t kNumLeds = 1; - using TColor = rgb_t; - using TEncoded = uint16_t; - static uint32_t get_bits(TColor color) { return color.val; } -}; - -using I2sWs2812Encoder = Ws2812Encoder; -constexpr size_t kI2sBufLen = ((I2sWs2812Encoder::get_total_encoded_words(1) + 1) >> 1) << 1; - -class I2sRgbLed { -public: - void init(); - void set_color(rgb_t color); -private: - uint16_t i2s_buf_[kI2sBufLen]; -}; - -#endif // __STATUS_LED_HPP \ No newline at end of file diff --git a/Firmware/Drivers/ws2812.hpp b/Firmware/Drivers/ws2812.hpp deleted file mode 100644 index 2a9b392c..00000000 --- a/Firmware/Drivers/ws2812.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef __WS2812_HPP -#define __WS2812_HPP - -#include -#include - -/** - * @tparam TTraits::TColor: The type representing a single LED's color - * @tparam TTraits::TEncoded: The data type of the encoded bitstream. - * Typically uint8_t, but can also have a different word size. - * @tparam TTraits::convert: A function that converts an instance of TTraits::TColor - * into the bit representation that should be sent out. - * kBitsPerLed bits are sent out. - * If the returned type has a larger size, it should be left-padded (MSBs - * ignored) - * The MSB (after padding) is sent out first (after padding). - */ -template -struct Ws2812Encoder { - using TEncoded = typename TTraits::TEncoded; - using TColor = typename TTraits::TColor; - - static constexpr size_t kResetTimeUs = 55; // officially 50us, but that doesn't always work - static constexpr size_t kResetBits = (kResetTimeUs * TTraits::kBaudrate) / 1000000ULL; - static constexpr size_t kEncodedWordSize = CHAR_BIT * sizeof(TEncoded); - - static constexpr size_t get_total_encoded_bits(size_t num_leds) { - return num_leds * TTraits::kBitsPerLed * TTraits::kNumBitsPerSymbol + kResetBits; - } - static constexpr size_t get_total_encoded_words(size_t num_leds) { - return (get_total_encoded_bits(num_leds) + kEncodedWordSize - 1) / kEncodedWordSize; - } - - template - static void encode(TColor* colors, size_t num_colors, size_t encoded_offset, TEncoded* encoded_buffer, size_t encoded_buffer_length); -}; - - - -/** - * @brief Encodes an array of colors into a bitstream that can be sent over a - * real-time bit generator like I2S or SPI in order to control a WS2812-type LED chain. - * - * The bits must be sent out MSB-first to generate the correct wave form. - * - * @tparam WrapAround: if true, the encoder wraps around to the first LED when - * the end of the stream is reached useful for continuous data streams. - * If false, the encoded buffer is padded with zeros. - * @param encoded_offset: The position in the encoded stream, indicated in number of encoded words. - * @param encoded_buffer: Buffer where the encoded bit stream will be written. - */ -template -template -void Ws2812Encoder::encode(TColor* colors, size_t num_colors, size_t encoded_offset, TEncoded* encoded_buffer, size_t encoded_buffer_length) { - size_t total_encoded_bits = get_total_encoded_bits(num_colors); - - for (size_t i2s_word_id = 0; i2s_word_id < encoded_buffer_length; ++i2s_word_id) { - uint16_t i2s_word = 0; - - for (size_t i2s_bit_id = 0; i2s_bit_id < kEncodedWordSize; ++i2s_bit_id) { - size_t bitpos = (i2s_word_id + encoded_offset) * kEncodedWordSize + i2s_bit_id; - - if (WrapAround) { - bitpos = bitpos % total_encoded_bits; - } - - size_t symbol_bit_id = TTraits::kNumBitsPerSymbol - (bitpos % TTraits::kNumBitsPerSymbol) - 1; - size_t led_bit_id = TTraits::kBitsPerLed - ((bitpos / TTraits::kNumBitsPerSymbol) % TTraits::kBitsPerLed) - 1; - size_t led_id = (bitpos / TTraits::kNumBitsPerSymbol) / TTraits::kBitsPerLed; - - if (led_id < num_colors) { - auto color = TTraits::get_bits(colors[led_id]); - uint16_t symbol = ((color >> led_bit_id) & 1) ? TTraits::kSymbolHigh : TTraits::kSymbolLow; - - if ((symbol >> symbol_bit_id) & 1) { - i2s_word |= (1 << (kEncodedWordSize - i2s_bit_id - 1)); - } - } - } - - encoded_buffer[i2s_word_id] = i2s_word; - } -} - - -#endif // __WS2812_HPP \ No newline at end of file diff --git a/Firmware/Private b/Firmware/Private index af06bd07..aeae8a9c 160000 --- a/Firmware/Private +++ b/Firmware/Private @@ -1 +1 @@ -Subproject commit af06bd07f852d7121cae6608af13b140028e61a0 +Subproject commit aeae8a9ceaacdfc65915ccf1157650b157ee5e89 diff --git a/Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h rename to Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f405xx.h diff --git a/Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h rename to Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h diff --git a/Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h rename to Firmware/ThirdParty/CMSIS/Device/ST/STM32F4xx/Include/system_stm32f4xx.h diff --git a/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h new file mode 100644 index 00000000..095ec93c --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/stm32f722xx.h @@ -0,0 +1,15462 @@ +/** + ****************************************************************************** + * @file stm32f722xx.h + * @author MCD Application Team + * @brief CMSIS Cortex-M7 Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - Peripheral's registers declarations and bits definition + * - Macros to access peripheral’s registers hardware + * + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2016 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f722xx + * @{ + */ + +#ifndef __STM32F722xx_H +#define __STM32F722xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief STM32F7xx Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M7 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M7 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M7 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M7 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M7 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M7 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M7 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M7 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FMC_IRQn = 48, /*!< FMC global Interrupt */ + SDMMC1_IRQn = 49, /*!< SDMMC1 global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + ETH_IRQn = 61, /*!< Ethernet global Interrupt */ + ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */ + OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */ + OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */ + OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */ + RNG_IRQn = 80, /*!< RNG global interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + SAI2_IRQn = 91, /*!< SAI2 global Interrupt */ + QUADSPI_IRQn = 92, /*!< Quad SPI global interrupt */ + LPTIM1_IRQn = 93, /*!< LP TIM1 interrupt */ + SDMMC2_IRQn = 103, /*!< SDMMC2 global Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +/** + * @brief Configuration of the Cortex-M7 Processor and Core Peripherals + */ +#define __CM7_REV 0x0100U /*!< Cortex-M7 revision r1p0 */ +#define __MPU_PRESENT 1 /*!< CM7 provides an MPU */ +#define __NVIC_PRIO_BITS 4 /*!< CM7 uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1 /*!< FPU present */ +#define __ICACHE_PRESENT 1 /*!< CM7 instruction cache present */ +#define __DCACHE_PRESENT 1 /*!< CM7 data cache present */ +#include "core_cm7.h" /*!< Cortex-M7 processor and core peripherals */ + + +#include "system_stm32f7xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register , Address offset: 0x14 */ + __IO uint32_t OPTCR1; /*!< FLASH option control register 1 , Address offset: 0x18 */ + __IO uint32_t OPTCR2; /*!< FLASH option control register 2 , Address offset: 0x1C */ +} FLASH_TypeDef; + + + +/** + * @brief Flexible Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FMC_Bank1_TypeDef; + +/** + * @brief Flexible Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FMC_Bank1E_TypeDef; + +/** + * @brief Flexible Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR; /*!< NAND Flash control register, Address offset: 0x80 */ + __IO uint32_t SR; /*!< NAND Flash FIFO status and interrupt register, Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< NAND Flash Common memory space timing register, Address offset: 0x88 */ + __IO uint32_t PATT; /*!< NAND Flash Attribute memory space timing register, Address offset: 0x8C */ + uint32_t RESERVED0; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR; /*!< NAND Flash ECC result registers, Address offset: 0x94 */ +} FMC_Bank3_TypeDef; + +/** + * @brief Flexible Memory Controller Bank5_6 + */ + +typedef struct +{ + __IO uint32_t SDCR[2]; /*!< SDRAM Control registers , Address offset: 0x140-0x144 */ + __IO uint32_t SDTR[2]; /*!< SDRAM Timing registers , Address offset: 0x148-0x14C */ + __IO uint32_t SDCMR; /*!< SDRAM Command Mode register, Address offset: 0x150 */ + __IO uint32_t SDRTR; /*!< SDRAM Refresh Timer register, Address offset: 0x154 */ + __IO uint32_t SDSR; /*!< SDRAM Status register, Address offset: 0x158 */ +} FMC_Bank5_6_TypeDef; + + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED[2]; /*!< Reserved, 0x18-0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ + __IO uint32_t WINR; /*!< IWDG Window register, Address offset: 0x10 */ +} IWDG_TypeDef; + + + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< PWR power control register 1, Address offset: 0x00 */ + __IO uint32_t CSR1; /*!< PWR power control/status register 2, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< PWR power control register 2, Address offset: 0x08 */ + __IO uint32_t CSR2; /*!< PWR power control/status register 2, Address offset: 0x0C */ +} PWR_TypeDef; + + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + __IO uint32_t PLLSAICFGR; /*!< RCC PLLSAI configuration register, Address offset: 0x88 */ + __IO uint32_t DCKCFGR1; /*!< RCC Dedicated Clocks configuration register1, Address offset: 0x8C */ + __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x90 */ + +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + uint32_t reserved; /*!< Reserved */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAMPCR; /*!< RTC tamper configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR; /*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR; /*!< RTC alarm B sub second register, Address offset: 0x48 */ + __IO uint32_t OR; /*!< RTC option register, Address offset: 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 0, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ + __IO uint32_t BKP20R; /*!< RTC backup register 20, Address offset: 0xA0 */ + __IO uint32_t BKP21R; /*!< RTC backup register 21, Address offset: 0xA4 */ + __IO uint32_t BKP22R; /*!< RTC backup register 22, Address offset: 0xA8 */ + __IO uint32_t BKP23R; /*!< RTC backup register 23, Address offset: 0xAC */ + __IO uint32_t BKP24R; /*!< RTC backup register 24, Address offset: 0xB0 */ + __IO uint32_t BKP25R; /*!< RTC backup register 25, Address offset: 0xB4 */ + __IO uint32_t BKP26R; /*!< RTC backup register 26, Address offset: 0xB8 */ + __IO uint32_t BKP27R; /*!< RTC backup register 27, Address offset: 0xBC */ + __IO uint32_t BKP28R; /*!< RTC backup register 28, Address offset: 0xC0 */ + __IO uint32_t BKP29R; /*!< RTC backup register 29, Address offset: 0xC4 */ + __IO uint32_t BKP30R; /*!< RTC backup register 30, Address offset: 0xC8 */ + __IO uint32_t BKP31R; /*!< RTC backup register 31, Address offset: 0xCC */ +} RTC_TypeDef; + + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDMMC power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDMMClock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDMMC argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDMMC command register, Address offset: 0x0C */ + __I uint32_t RESPCMD; /*!< SDMMC command response register, Address offset: 0x10 */ + __I uint32_t RESP1; /*!< SDMMC response 1 register, Address offset: 0x14 */ + __I uint32_t RESP2; /*!< SDMMC response 2 register, Address offset: 0x18 */ + __I uint32_t RESP3; /*!< SDMMC response 3 register, Address offset: 0x1C */ + __I uint32_t RESP4; /*!< SDMMC response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDMMC data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDMMC data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDMMC data control register, Address offset: 0x2C */ + __I uint32_t DCOUNT; /*!< SDMMC data counter register, Address offset: 0x30 */ + __I uint32_t STA; /*!< SDMMC status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDMMC interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDMMC mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __I uint32_t FIFOCNT; /*!< SDMMC FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDMMC data FIFO register, Address offset: 0x80 */ +} SDMMC_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ + __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x54 */ + __IO uint32_t CCR5; /*!< TIM capture/compare mode register5, Address offset: 0x58 */ + __IO uint32_t CCR6; /*!< TIM capture/compare mode register6, Address offset: 0x5C */ + +} TIM_TypeDef; + +/** + * @brief LPTIMIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ +} LPTIM_TypeDef; + + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ + __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ + __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ +} USART_TypeDef; + + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @} + */ + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + uint32_t Reserved5[3]; /*!< Reserved 040h-048h */ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + uint32_t Reserved7; /*!< Reserved 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + uint32_t Reserved43[40]; /*!< Reserved 60h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO 104h-13Ch */ +} USB_OTG_GlobalTypeDef; + + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; +/** + * @} + */ + + + + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define RAMITCM_BASE 0x00000000UL /*!< Base address of : 16KB RAM reserved for CPU execution/instruction accessible over ITCM */ +#define FLASHITCM_BASE 0x00200000UL /*!< Base address of : (up to 512 KB) embedded FLASH memory accessible over ITCM */ +#define FLASHAXI_BASE 0x08000000UL /*!< Base address of : (up to 512 KB) embedded FLASH memory accessible over AXI */ +#define RAMDTCM_BASE 0x20000000UL /*!< Base address of : 64KB system data RAM accessible over DTCM */ +#define PERIPH_BASE 0x40000000UL /*!< Base address of : AHB/ABP Peripherals */ +#define BKPSRAM_BASE 0x40024000UL /*!< Base address of : Backup SRAM(4 KB) */ +#define QSPI_BASE 0x90000000UL /*!< Base address of : QSPI memories accessible over AXI */ +#define FMC_R_BASE 0xA0000000UL /*!< Base address of : FMC Control registers */ +#define QSPI_R_BASE 0xA0001000UL /*!< Base address of : QSPI Control registers */ +#define SRAM1_BASE 0x20010000UL /*!< Base address of : 176KB RAM1 accessible over AXI/AHB */ +#define SRAM2_BASE 0x2003C000UL /*!< Base address of : 16KB RAM2 accessible over AXI/AHB */ +#define FLASH_END 0x0807FFFFUL /*!< FLASH end address */ +#define FLASH_OTP_BASE 0x1FF07800UL /*!< Base address of : (up to 528 Bytes) embedded FLASH OTP Area */ +#define FLASH_OTP_END 0x1FF07A0FUL /*!< End address of : (up to 528 Bytes) embedded FLASH OTP Area */ + +/* Legacy define */ +#define FLASH_BASE FLASHAXI_BASE + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00UL) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400UL) +#define UART7_BASE (APB1PERIPH_BASE + 0x7800UL) +#define UART8_BASE (APB1PERIPH_BASE + 0x7C00UL) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000UL) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400UL) +#define SDMMC2_BASE (APB2PERIPH_BASE + 0x1C00UL) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000UL) +#define ADC2_BASE (APB2PERIPH_BASE + 0x2100UL) +#define ADC3_BASE (APB2PERIPH_BASE + 0x2200UL) +#define ADC_BASE (APB2PERIPH_BASE + 0x2300UL) +#define SDMMC1_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3400UL) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800UL) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00UL) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800UL) +#define SPI5_BASE (APB2PERIPH_BASE + 0x5000UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5800UL) +#define SAI2_BASE (APB2PERIPH_BASE + 0x5C00UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024UL) +#define SAI2_Block_A_BASE (SAI2_BASE + 0x004UL) +#define SAI2_Block_B_BASE (SAI2_BASE + 0x024UL) +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00UL) +#define GPIOI_BASE (AHB1PERIPH_BASE + 0x2000UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00UL) +#define UID_BASE 0x1FF07A10UL /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE 0x1FF07A22UL /*!< FLASH Size register base address */ +#define PACKAGE_BASE 0x1FF07BF0UL /*!< Package size register base address */ +/* Legacy define */ +#define PACKAGESIZE_BASE PACKAGE_BASE + +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000UL) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010UL) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028UL) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040UL) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058UL) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070UL) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088UL) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0UL) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8UL) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400UL) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010UL) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028UL) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040UL) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058UL) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070UL) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088UL) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0UL) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8UL) +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800UL) +/*!< FMC Bankx registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) +#define FMC_Bank5_6_R_BASE (FMC_R_BASE + 0x0140UL) + +/* Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000UL + +/*!< USB registers base address */ +#define USB_OTG_HS_PERIPH_BASE 0x40040000UL +#define USB_OTG_FS_PERIPH_BASE 0x50000000UL + +#define USB_OTG_GLOBAL_BASE 0x0000UL +#define USB_OTG_DEVICE_BASE 0x0800UL +#define USB_OTG_IN_ENDPOINT_BASE 0x0900UL +#define USB_OTG_OUT_ENDPOINT_BASE 0x0B00UL +#define USB_OTG_EP_REG_SIZE 0x0020UL +#define USB_OTG_HOST_BASE 0x0400UL +#define USB_OTG_HOST_PORT_BASE 0x0440UL +#define USB_OTG_HOST_CHANNEL_BASE 0x0500UL +#define USB_OTG_HOST_CHANNEL_SIZE 0x0020UL +#define USB_OTG_PCGCCTL_BASE 0x0E00UL +#define USB_OTG_FIFO_BASE 0x1000UL +#define USB_OTG_FIFO_SIZE 0x1000UL + +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) /* Kept for legacy purpose */ +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define ADC ((ADC_Common_TypeDef *) ADC_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define ADC123_COMMON ((ADC_Common_TypeDef *) ADC_BASE) +#define SDMMC1 ((SDMMC_TypeDef *) SDMMC1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI2 ((SAI_TypeDef *) SAI2_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define SAI2_Block_A ((SAI_Block_TypeDef *)SAI2_Block_A_BASE) +#define SAI2_Block_B ((SAI_Block_TypeDef *)SAI2_Block_B_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FMC_Bank1 ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#define FMC_Bank1E ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#define FMC_Bank3 ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) +#define FMC_Bank5_6 ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) +#define USB_OTG_HS ((USB_OTG_GlobalTypeDef *) USB_OTG_HS_PERIPH_BASE) +#define SDMMC2 ((SDMMC_TypeDef *) SDMMC2_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +#define VREFINT_CAL_ADDR_CMSIS ((uint16_t*) (0x1FF07A2A)) /*!
© Copyright (c) 2016 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f7xx + * @{ + */ + +#ifndef __STM32F7xx_H +#define __STM32F7xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Library_configuration_section + * @{ + */ + +/** + * @brief STM32 Family + */ +#if !defined (STM32F7) +#define STM32F7 +#endif /* STM32F7 */ + +/* Uncomment the line below according to the target STM32 device used in your + application + */ +#if !defined (STM32F756xx) && !defined (STM32F746xx) && !defined (STM32F745xx) && !defined (STM32F765xx) && \ + !defined (STM32F767xx) && !defined (STM32F769xx) && !defined (STM32F777xx) && !defined (STM32F779xx) && \ + !defined (STM32F722xx) && !defined (STM32F723xx) && !defined (STM32F732xx) && !defined (STM32F733xx) && \ + !defined (STM32F730xx) && !defined (STM32F750xx) + + /* #define STM32F756xx */ /*!< STM32F756VG, STM32F756ZG, STM32F756ZG, STM32F756IG, STM32F756BG, + STM32F756NG Devices */ + /* #define STM32F746xx */ /*!< STM32F746VE, STM32F746VG, STM32F746ZE, STM32F746ZG, STM32F746IE, STM32F746IG, + STM32F746BE, STM32F746BG, STM32F746NE, STM32F746NG Devices */ + /* #define STM32F745xx */ /*!< STM32F745VE, STM32F745VG, STM32F745ZG, STM32F745ZE, STM32F745IE, STM32F745IG Devices */ + /* #define STM32F765xx */ /*!< STM32F765BI, STM32F765BG, STM32F765NI, STM32F765NG, STM32F765II, STM32F765IG, + STM32F765ZI, STM32F765ZG, STM32F765VI, STM32F765VG Devices */ + /* #define STM32F767xx */ /*!< STM32F767BG, STM32F767BI, STM32F767IG, STM32F767II, STM32F767NG, STM32F767NI, + STM32F767VG, STM32F767VI, STM32F767ZG, STM32F767ZI Devices */ + /* #define STM32F769xx */ /*!< STM32F769AG, STM32F769AI, STM32F769BG, STM32F769BI, STM32F769IG, STM32F769II, + STM32F769NG, STM32F769NI, STM32F768AI Devices */ + /* #define STM32F777xx */ /*!< STM32F777VI, STM32F777ZI, STM32F777II, STM32F777BI, STM32F777NI Devices */ + /* #define STM32F779xx */ /*!< STM32F779II, STM32F779BI, STM32F779NI, STM32F779AI, STM32F778AI Devices */ + /* #define STM32F722xx */ /*!< STM32F722IE, STM32F722ZE, STM32F722VE, STM32F722RE, STM32F722IC, STM32F722ZC, + STM32F722VC, STM32F722RC Devices */ + /* #define STM32F723xx */ /*!< STM32F723IE, STM32F723ZE, STM32F723VE, STM32F723IC, STM32F723ZC, STM32F723VC Devices */ + /* #define STM32F732xx */ /*!< STM32F732IE, STM32F732ZE, STM32F732VE, STM32F732RE Devices */ + /* #define STM32F733xx */ /*!< STM32F733IE, STM32F733ZE, STM32F733VE Devices */ + /* #define STM32F730xx */ /*!< STM32F730R, STM32F730V, STM32F730Z, STM32F730I Devices */ + /* #define STM32F750xx */ /*!< STM32F750V, STM32F750Z, STM32F750N Devices */ +#endif + +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + */ + +#if !defined (USE_HAL_DRIVER) +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_HAL_DRIVER */ +#endif /* USE_HAL_DRIVER */ + +/** + * @brief CMSIS Device version number V1.2.5 + */ +#define __STM32F7_CMSIS_VERSION_MAIN (0x01) /*!< [31:24] main version */ +#define __STM32F7_CMSIS_VERSION_SUB1 (0x02) /*!< [23:16] sub1 version */ +#define __STM32F7_CMSIS_VERSION_SUB2 (0x05) /*!< [15:8] sub2 version */ +#define __STM32F7_CMSIS_VERSION_RC (0x00) /*!< [7:0] release candidate */ +#define __STM32F7_CMSIS_VERSION ((__STM32F7_CMSIS_VERSION_MAIN << 24)\ + |(__STM32F7_CMSIS_VERSION_SUB1 << 16)\ + |(__STM32F7_CMSIS_VERSION_SUB2 << 8 )\ + |(__STM32F7_CMSIS_VERSION_RC)) +/** + * @} + */ + +/** @addtogroup Device_Included + * @{ + */ +#if defined(STM32F722xx) + #include "stm32f722xx.h" +#elif defined(STM32F723xx) + #include "stm32f723xx.h" +#elif defined(STM32F732xx) + #include "stm32f732xx.h" +#elif defined(STM32F733xx) + #include "stm32f733xx.h" +#elif defined(STM32F756xx) + #include "stm32f756xx.h" +#elif defined(STM32F746xx) + #include "stm32f746xx.h" +#elif defined(STM32F745xx) + #include "stm32f745xx.h" +#elif defined(STM32F765xx) + #include "stm32f765xx.h" +#elif defined(STM32F767xx) + #include "stm32f767xx.h" +#elif defined(STM32F769xx) + #include "stm32f769xx.h" +#elif defined(STM32F777xx) + #include "stm32f777xx.h" +#elif defined(STM32F779xx) + #include "stm32f779xx.h" +#elif defined(STM32F730xx) + #include "stm32f730xx.h" +#elif defined(STM32F750xx) + #include "stm32f750xx.h" +#else + #error "Please select first the target STM32F7xx device used in your application (in stm32f7xx.h file)" +#endif + +/** + * @} + */ + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + RESET = 0U, + SET = !RESET +} FlagStatus, ITStatus; + +typedef enum +{ + DISABLE = 0U, + ENABLE = !DISABLE +} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum +{ + SUCCESS = 0U, + ERROR = !SUCCESS +} ErrorStatus; + +/** + * @} + */ + +/** @addtogroup Exported_macro + * @{ + */ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) + +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + +#define READ_BIT(REG, BIT) ((REG) & (BIT)) + +#define CLEAR_REG(REG) ((REG) = (0x0)) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) + +#define READ_REG(REG) ((REG)) + +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL))) + +/** + * @} + */ + +#ifdef USE_HAL_DRIVER + #include "stm32f7xx_hal.h" +#endif /* USE_HAL_DRIVER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __STM32F7xx_H */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h new file mode 100644 index 00000000..140be1b6 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Device/ST/STM32F7xx/Include/system_stm32f7xx.h @@ -0,0 +1,123 @@ +/** + ****************************************************************************** + * @file system_stm32f7xx.h + * @author MCD Application Team + * @brief CMSIS Cortex-M7 Device System Source File for STM32F7xx devices. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f7xx_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef __SYSTEM_STM32F7XX_H +#define __SYSTEM_STM32F7XX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32F7xx_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32F7xx_System_Exported_Variables + * @{ + */ + /* The SystemCoreClock variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetSysClockFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. + */ +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + +extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */ +extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */ + + +/** + * @} + */ + +/** @addtogroup STM32F7xx_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F7xx_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F7xx_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTEM_STM32F7XX_H */ + +/** + * @} + */ + +/** + * @} + */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_common_tables.h b/Firmware/ThirdParty/CMSIS/Include/arm_common_tables.h similarity index 53% rename from Firmware/Board/v3/Drivers/CMSIS/Include/arm_common_tables.h rename to Firmware/ThirdParty/CMSIS/Include/arm_common_tables.h index 8742a569..dfea7460 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_common_tables.h +++ b/Firmware/ThirdParty/CMSIS/Include/arm_common_tables.h @@ -1,42 +1,30 @@ /* ---------------------------------------------------------------------- -* Copyright (C) 2010-2014 ARM Limited. All rights reserved. -* -* $Date: 19. October 2015 -* $Revision: V.1.4.5 a -* -* Project: CMSIS DSP Library -* Title: arm_common_tables.h -* -* Description: This file has extern declaration for common tables like Bitreverse, reciprocal etc which are used across different functions -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -------------------------------------------------------------------- */ + * Project: CMSIS DSP Library + * Title: arm_common_tables.h + * Description: Extern declaration for common tables + * + * $Date: 27. January 2017 + * $Revision: V.1.5.1 + * + * Target Processor: Cortex-M cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef _ARM_COMMON_TABLES_H #define _ARM_COMMON_TABLES_H @@ -46,8 +34,6 @@ extern const uint16_t armBitRevTable[1024]; extern const q15_t armRecipTableQ15[64]; extern const q31_t armRecipTableQ31[64]; -/* extern const q31_t realCoefAQ31[1024]; */ -/* extern const q31_t realCoefBQ31[1024]; */ extern const float32_t twiddleCoef_16[32]; extern const float32_t twiddleCoef_32[64]; extern const float32_t twiddleCoef_64[128]; @@ -85,45 +71,44 @@ extern const float32_t twiddleCoef_rfft_1024[1024]; extern const float32_t twiddleCoef_rfft_2048[2048]; extern const float32_t twiddleCoef_rfft_4096[4096]; - /* floating-point bit reversal tables */ -#define ARMBITREVINDEXTABLE__16_TABLE_LENGTH ((uint16_t)20 ) -#define ARMBITREVINDEXTABLE__32_TABLE_LENGTH ((uint16_t)48 ) -#define ARMBITREVINDEXTABLE__64_TABLE_LENGTH ((uint16_t)56 ) -#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208 ) -#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440 ) -#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448 ) -#define ARMBITREVINDEXTABLE1024_TABLE_LENGTH ((uint16_t)1800) -#define ARMBITREVINDEXTABLE2048_TABLE_LENGTH ((uint16_t)3808) -#define ARMBITREVINDEXTABLE4096_TABLE_LENGTH ((uint16_t)4032) +#define ARMBITREVINDEXTABLE_16_TABLE_LENGTH ((uint16_t)20) +#define ARMBITREVINDEXTABLE_32_TABLE_LENGTH ((uint16_t)48) +#define ARMBITREVINDEXTABLE_64_TABLE_LENGTH ((uint16_t)56) +#define ARMBITREVINDEXTABLE_128_TABLE_LENGTH ((uint16_t)208) +#define ARMBITREVINDEXTABLE_256_TABLE_LENGTH ((uint16_t)440) +#define ARMBITREVINDEXTABLE_512_TABLE_LENGTH ((uint16_t)448) +#define ARMBITREVINDEXTABLE_1024_TABLE_LENGTH ((uint16_t)1800) +#define ARMBITREVINDEXTABLE_2048_TABLE_LENGTH ((uint16_t)3808) +#define ARMBITREVINDEXTABLE_4096_TABLE_LENGTH ((uint16_t)4032) -extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE__16_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE__32_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE__64_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable16[ARMBITREVINDEXTABLE_16_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable32[ARMBITREVINDEXTABLE_32_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable64[ARMBITREVINDEXTABLE_64_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable128[ARMBITREVINDEXTABLE_128_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable256[ARMBITREVINDEXTABLE_256_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable512[ARMBITREVINDEXTABLE_512_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE1024_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE2048_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE4096_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE_1024_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE_2048_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE_4096_TABLE_LENGTH]; /* fixed-point bit reversal tables */ -#define ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH ((uint16_t)12 ) -#define ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH ((uint16_t)24 ) -#define ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH ((uint16_t)56 ) -#define ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH ((uint16_t)112 ) -#define ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH ((uint16_t)240 ) -#define ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH ((uint16_t)480 ) -#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992 ) +#define ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH ((uint16_t)12) +#define ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH ((uint16_t)24) +#define ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH ((uint16_t)56) +#define ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH ((uint16_t)112) +#define ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH ((uint16_t)240) +#define ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH ((uint16_t)480) +#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992) #define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984) #define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032) -extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH]; -extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED_16_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED_32_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED_64_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED_128_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED_256_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED_512_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH]; diff --git a/Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h b/Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h new file mode 100644 index 00000000..80a3e8bb --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/arm_const_structs.h @@ -0,0 +1,66 @@ +/* ---------------------------------------------------------------------- + * Project: CMSIS DSP Library + * Title: arm_const_structs.h + * Description: Constant structs that are initialized for user convenience. + * For example, some can be given as arguments to the arm_cfft_f32() function. + * + * $Date: 27. January 2017 + * $Revision: V.1.5.1 + * + * Target Processor: Cortex-M cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2017 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ARM_CONST_STRUCTS_H +#define _ARM_CONST_STRUCTS_H + +#include "arm_math.h" +#include "arm_common_tables.h" + + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len16; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len32; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len64; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len128; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len256; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len512; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len1024; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len2048; + extern const arm_cfft_instance_f32 arm_cfft_sR_f32_len4096; + + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len16; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len32; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len64; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len128; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len256; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len512; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len1024; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len2048; + extern const arm_cfft_instance_q31 arm_cfft_sR_q31_len4096; + + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len16; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len32; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len64; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len128; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len256; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len512; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len1024; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len2048; + extern const arm_cfft_instance_q15 arm_cfft_sR_q15_len4096; + +#endif diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_math.h b/Firmware/ThirdParty/CMSIS/Include/arm_math.h similarity index 95% rename from Firmware/Board/v3/Drivers/CMSIS/Include/arm_math.h rename to Firmware/ThirdParty/CMSIS/Include/arm_math.h index d33f8a9b..ea9dd26a 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/arm_math.h +++ b/Firmware/ThirdParty/CMSIS/Include/arm_math.h @@ -1,42 +1,26 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010-2015 ARM Limited. All rights reserved. -* -* $Date: 20. October 2015 -* $Revision: V1.4.5 b -* -* Project: CMSIS DSP Library -* Title: arm_math.h -* -* Description: Public header file for CMSIS DSP Library -* -* Target Processor: Cortex-M7/Cortex-M4/Cortex-M3/Cortex-M0 -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* - Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* - Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in -* the documentation and/or other materials provided with the -* distribution. -* - Neither the name of ARM LIMITED nor the names of its contributors -* may be used to endorse or promote products derived from this -* software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. - * -------------------------------------------------------------------- */ +/****************************************************************************** + * @file arm_math.h + * @brief Public header file for CMSIS DSP LibraryU + * @version V1.5.3 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2010-2018 Arm Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** \mainpage CMSIS DSP Software Library @@ -66,26 +50,34 @@ * ------------ * * The library installer contains prebuilt versions of the libraries in the Lib folder. - * - arm_cortexM7lfdp_math.lib (Little endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfdp_math.lib (Big endian and Double Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7lfsp_math.lib (Little endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7bfsp_math.lib (Big endian and Single Precision Floating Point Unit on Cortex-M7) - * - arm_cortexM7l_math.lib (Little endian on Cortex-M7) - * - arm_cortexM7b_math.lib (Big endian on Cortex-M7) - * - arm_cortexM4lf_math.lib (Little endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4bf_math.lib (Big endian and Floating Point Unit on Cortex-M4) - * - arm_cortexM4l_math.lib (Little endian on Cortex-M4) - * - arm_cortexM4b_math.lib (Big endian on Cortex-M4) - * - arm_cortexM3l_math.lib (Little endian on Cortex-M3) - * - arm_cortexM3b_math.lib (Big endian on Cortex-M3) - * - arm_cortexM0l_math.lib (Little endian on Cortex-M0 / CortexM0+) - * - arm_cortexM0b_math.lib (Big endian on Cortex-M0 / CortexM0+) + * - arm_cortexM7lfdp_math.lib (Cortex-M7, Little endian, Double Precision Floating Point Unit) + * - arm_cortexM7bfdp_math.lib (Cortex-M7, Big endian, Double Precision Floating Point Unit) + * - arm_cortexM7lfsp_math.lib (Cortex-M7, Little endian, Single Precision Floating Point Unit) + * - arm_cortexM7bfsp_math.lib (Cortex-M7, Big endian and Single Precision Floating Point Unit on) + * - arm_cortexM7l_math.lib (Cortex-M7, Little endian) + * - arm_cortexM7b_math.lib (Cortex-M7, Big endian) + * - arm_cortexM4lf_math.lib (Cortex-M4, Little endian, Floating Point Unit) + * - arm_cortexM4bf_math.lib (Cortex-M4, Big endian, Floating Point Unit) + * - arm_cortexM4l_math.lib (Cortex-M4, Little endian) + * - arm_cortexM4b_math.lib (Cortex-M4, Big endian) + * - arm_cortexM3l_math.lib (Cortex-M3, Little endian) + * - arm_cortexM3b_math.lib (Cortex-M3, Big endian) + * - arm_cortexM0l_math.lib (Cortex-M0 / Cortex-M0+, Little endian) + * - arm_cortexM0b_math.lib (Cortex-M0 / Cortex-M0+, Big endian) + * - arm_ARMv8MBLl_math.lib (Armv8-M Baseline, Little endian) + * - arm_ARMv8MMLl_math.lib (Armv8-M Mainline, Little endian) + * - arm_ARMv8MMLlfsp_math.lib (Armv8-M Mainline, Little endian, Single Precision Floating Point Unit) + * - arm_ARMv8MMLld_math.lib (Armv8-M Mainline, Little endian, DSP instructions) + * - arm_ARMv8MMLldfsp_math.lib (Armv8-M Mainline, Little endian, DSP instructions, Single Precision Floating Point Unit) * * The library functions are declared in the public file arm_math.h which is placed in the Include folder. * Simply include this file and link the appropriate library in the application and begin calling the library functions. The Library supports single - * public header file arm_math.h for Cortex-M7/M4/M3/M0/M0+ with little endian and big endian. Same header file will be used for floating point unit(FPU) variants. - * Define the appropriate pre processor MACRO ARM_MATH_CM7 or ARM_MATH_CM4 or ARM_MATH_CM3 or + * public header file arm_math.h for Cortex-M cores with little endian and big endian. Same header file will be used for floating point unit(FPU) variants. + * Define the appropriate preprocessor macro ARM_MATH_CM7 or ARM_MATH_CM4 or ARM_MATH_CM3 or * ARM_MATH_CM0 or ARM_MATH_CM0PLUS depending on the target processor in the application. + * For Armv8-M cores define preprocessor macro ARM_MATH_ARMV8MBL or ARM_MATH_ARMV8MML. + * Set preprocessor macro __DSP_PRESENT if Armv8-M Mainline core supports DSP instructions. + * * * Examples * -------- @@ -95,22 +87,22 @@ * Toolchain Support * ------------ * - * The library has been developed and tested with MDK-ARM version 5.14.0.0 + * The library has been developed and tested with MDK version 5.14.0.0 * The library is being tested in GCC and IAR toolchains and updates on this activity will be made available shortly. * * Building the Library * ------------ * - * The library installer contains a project file to re build libraries on MDK-ARM Tool chain in the CMSIS\\DSP_Lib\\Source\\ARM folder. + * The library installer contains a project file to rebuild libraries on MDK toolchain in the CMSIS\\DSP_Lib\\Source\\ARM folder. * - arm_cortexM_math.uvprojx * * - * The libraries can be built by opening the arm_cortexM_math.uvprojx project in MDK-ARM, selecting a specific target, and defining the optional pre processor MACROs detailed above. + * The libraries can be built by opening the arm_cortexM_math.uvprojx project in MDK-ARM, selecting a specific target, and defining the optional preprocessor macros detailed above. * - * Pre-processor Macros + * Preprocessor Macros * ------------ * - * Each library project have differant pre-processor macros. + * Each library project have different preprocessor macros. * * - UNALIGNED_SUPPORT_DISABLE: * @@ -134,9 +126,18 @@ * and ARM_MATH_CM0 for building library on Cortex-M0 target, ARM_MATH_CM0PLUS for building library on Cortex-M0+ target, and * ARM_MATH_CM7 for building the library on cortex-M7. * + * - ARM_MATH_ARMV8MxL: + * + * Define macro ARM_MATH_ARMV8MBL for building the library on Armv8-M Baseline target, ARM_MATH_ARMV8MML for building library + * on Armv8-M Mainline target. + * * - __FPU_PRESENT: * - * Initialize macro __FPU_PRESENT = 1 when building on FPU supported Targets. Enable this macro for M4bf and M4lf libraries + * Initialize macro __FPU_PRESENT = 1 when building on FPU supported Targets. Enable this macro for floating point libraries. + * + * - __DSP_PRESENT: + * + * Initialize macro __DSP_PRESENT = 1 when Armv8-M Mainline core supports DSP instructions. * *
* CMSIS-DSP in ARM::CMSIS Pack @@ -158,7 +159,7 @@ * Copyright Notice * ------------ * - * Copyright (C) 2010-2015 ARM Limited. All rights reserved. + * Copyright (C) 2010-2015 Arm Limited. All rights reserved. */ @@ -238,9 +239,9 @@ * * \par Size Checking * By default all of the matrix functions perform size checking on the input and - * output matrices. For example, the matrix addition function verifies that the + * output matrices. For example, the matrix addition function verifies that the * two input matrices and the output matrix all have the same number of rows and - * columns. If the size check fails the functions return: + * columns. If the size check fails the functions return: *
  *     ARM_MATH_SIZE_MISMATCH
  * 
@@ -254,9 +255,9 @@ * ARM_MATH_MATRIX_CHECK * * within the library project settings. By default this macro is defined - * and size checking is enabled. By changing the project settings and + * and size checking is enabled. By changing the project settings and * undefining this macro size checking is eliminated and the functions - * run a bit faster. With size checking disabled the functions always + * run a bit faster. With size checking disabled the functions always * return ARM_MATH_SUCCESS. */ @@ -288,20 +289,38 @@ #ifndef _ARM_MATH_H #define _ARM_MATH_H -/* ignore some GCC warnings */ -#if defined ( __GNUC__ ) +/* Compiler specific diagnostic adjustment */ +#if defined ( __CC_ARM ) + +#elif defined ( __ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) + +#elif defined ( __GNUC__ ) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-conversion" #pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wunused-parameter" + +#elif defined ( __ICCARM__ ) + +#elif defined ( __TI_ARM__ ) + +#elif defined ( __CSMC__ ) + +#elif defined ( __TASKING__ ) + +#else + #error Unknown compiler #endif + #define __CMSIS_GENERIC /* disable NVIC and Systick functions */ #if defined(ARM_MATH_CM7) #include "core_cm7.h" + #define ARM_MATH_DSP #elif defined (ARM_MATH_CM4) #include "core_cm4.h" + #define ARM_MATH_DSP #elif defined (ARM_MATH_CM3) #include "core_cm3.h" #elif defined (ARM_MATH_CM0) @@ -310,8 +329,16 @@ #elif defined (ARM_MATH_CM0PLUS) #include "core_cm0plus.h" #define ARM_MATH_CM0_FAMILY +#elif defined (ARM_MATH_ARMV8MBL) + #include "core_armv8mbl.h" + #define ARM_MATH_CM0_FAMILY +#elif defined (ARM_MATH_ARMV8MML) + #include "core_armv8mml.h" + #if (defined (__DSP_PRESENT) && (__DSP_PRESENT == 1)) + #define ARM_MATH_DSP + #endif #else - #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0" + #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS, ARM_MATH_CM0, ARM_MATH_ARMV8MBL, ARM_MATH_ARMV8MML" #endif #undef __CMSIS_GENERIC /* enable NVIC and Systick functions */ @@ -331,7 +358,7 @@ extern "C" #define DELTA_Q15 0x5 #define INDEX_MASK 0x0000003F #ifndef PI -#define PI 3.14159265358979f + #define PI 3.14159265358979f #endif /** @@ -342,7 +369,6 @@ extern "C" #define FAST_MATH_Q31_SHIFT (32 - 10) #define FAST_MATH_Q15_SHIFT (16 - 10) #define CONTROLLER_Q31_SHIFT (32 - 9) -#define TABLE_SIZE 256 #define TABLE_SPACING_Q31 0x400000 #define TABLE_SPACING_Q15 0x80 @@ -414,29 +440,40 @@ extern "C" /** * @brief definition to read/write two 16 bit values. */ -#if defined __CC_ARM +#if defined ( __CC_ARM ) #define __SIMD32_TYPE int32_t __packed #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE __attribute__((always_inline)) -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined ( __ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) #define __SIMD32_TYPE int32_t #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE __attribute__((always_inline)) -#elif defined __GNUC__ +#elif defined ( __GNUC__ ) #define __SIMD32_TYPE int32_t #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE __attribute__((always_inline)) -#elif defined __ICCARM__ +#elif defined ( __ICCARM__ ) #define __SIMD32_TYPE int32_t __packed #define CMSIS_UNUSED + #define CMSIS_INLINE -#elif defined __CSMC__ +#elif defined ( __TI_ARM__ ) + #define __SIMD32_TYPE int32_t + #define CMSIS_UNUSED __attribute__((unused)) + #define CMSIS_INLINE + +#elif defined ( __CSMC__ ) #define __SIMD32_TYPE int32_t #define CMSIS_UNUSED + #define CMSIS_INLINE -#elif defined __TASKING__ +#elif defined ( __TASKING__ ) #define __SIMD32_TYPE __unaligned int32_t #define CMSIS_UNUSED + #define CMSIS_INLINE #else #error Unknown compiler @@ -447,17 +484,16 @@ extern "C" #define _SIMD32_OFFSET(addr) (*(__SIMD32_TYPE *) (addr)) #define __SIMD64(addr) (*(int64_t **) & (addr)) -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) +#if !defined (ARM_MATH_DSP) /** * @brief definition to pack two 16 bit values. */ -#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | \ - (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) ) -#define __PKHTB(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0xFFFF0000) | \ - (((int32_t)(ARG2) >> ARG3) & (int32_t)0x0000FFFF) ) - -#endif +#define __PKHBT(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0x0000FFFF) | \ + (((int32_t)(ARG2) << ARG3) & (int32_t)0xFFFF0000) ) +#define __PKHTB(ARG1, ARG2, ARG3) ( (((int32_t)(ARG1) << 0) & (int32_t)0xFFFF0000) | \ + (((int32_t)(ARG2) >> ARG3) & (int32_t)0x0000FFFF) ) +#endif /* !defined (ARM_MATH_DSP) */ /** * @brief definition to pack four 8 bit values. @@ -481,7 +517,7 @@ extern "C" /** * @brief Clips Q63 to Q31 values. */ - static __INLINE q31_t clip_q63_to_q31( + CMSIS_INLINE __STATIC_INLINE q31_t clip_q63_to_q31( q63_t x) { return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? @@ -491,7 +527,7 @@ extern "C" /** * @brief Clips Q63 to Q15 values. */ - static __INLINE q15_t clip_q63_to_q15( + CMSIS_INLINE __STATIC_INLINE q15_t clip_q63_to_q15( q63_t x) { return ((q31_t) (x >> 32) != ((q31_t) x >> 31)) ? @@ -501,7 +537,7 @@ extern "C" /** * @brief Clips Q31 to Q7 values. */ - static __INLINE q7_t clip_q31_to_q7( + CMSIS_INLINE __STATIC_INLINE q7_t clip_q31_to_q7( q31_t x) { return ((q31_t) (x >> 24) != ((q31_t) x >> 23)) ? @@ -511,7 +547,7 @@ extern "C" /** * @brief Clips Q31 to Q15 values. */ - static __INLINE q15_t clip_q31_to_q15( + CMSIS_INLINE __STATIC_INLINE q15_t clip_q31_to_q15( q31_t x) { return ((q31_t) (x >> 16) != ((q31_t) x >> 15)) ? @@ -522,7 +558,7 @@ extern "C" * @brief Multiplies 32 X 64 and returns 32 bit result in 2.30 format. */ - static __INLINE q63_t mult32x64( + CMSIS_INLINE __STATIC_INLINE q63_t mult32x64( q63_t x, q31_t y) { @@ -530,37 +566,11 @@ extern "C" (((q63_t) (x >> 32) * y))); } -/* - #if defined (ARM_MATH_CM0_FAMILY) && defined ( __CC_ARM ) - #define __CLZ __clz - #endif - */ -/* note: function can be removed when all toolchain support __CLZ for Cortex-M0 */ -#if defined (ARM_MATH_CM0_FAMILY) && ((defined (__ICCARM__)) ) - static __INLINE uint32_t __CLZ( - q31_t data); - - static __INLINE uint32_t __CLZ( - q31_t data) - { - uint32_t count = 0; - uint32_t mask = 0x80000000; - - while((data & mask) == 0) - { - count += 1u; - mask = mask >> 1u; - } - - return (count); - } -#endif - /** * @brief Function to Calculates 1/in (reciprocal) value of Q31 Data type. */ - static __INLINE uint32_t arm_recip_q31( + CMSIS_INLINE __STATIC_INLINE uint32_t arm_recip_q31( q31_t in, q31_t * dst, q31_t * pRecipTable) @@ -570,7 +580,7 @@ extern "C" uint32_t index, i; uint32_t signBits; - if(in > 0) + if (in > 0) { signBits = ((uint32_t) (__CLZ( in) - 1)); } @@ -591,7 +601,7 @@ extern "C" /* calculation of reciprocal value */ /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) + for (i = 0U; i < 2U; i++) { tempVal = (uint32_t) (((q63_t) in * out) >> 31); tempVal = 0x7FFFFFFFu - tempVal; @@ -604,14 +614,14 @@ extern "C" *dst = out; /* return num of signbits of out = 1/in value */ - return (signBits + 1u); + return (signBits + 1U); } /** * @brief Function to Calculates 1/in (reciprocal) value of Q15 Data type. */ - static __INLINE uint32_t arm_recip_q15( + CMSIS_INLINE __STATIC_INLINE uint32_t arm_recip_q15( q15_t in, q15_t * dst, q15_t * pRecipTable) @@ -621,7 +631,7 @@ extern "C" uint32_t index = 0, i = 0; uint32_t signBits = 0; - if(in > 0) + if (in > 0) { signBits = ((uint32_t)(__CLZ( in) - 17)); } @@ -642,7 +652,7 @@ extern "C" /* calculation of reciprocal value */ /* running approximation for two iterations */ - for (i = 0u; i < 2u; i++) + for (i = 0U; i < 2U; i++) { tempVal = (uint32_t) (((q31_t) in * out) >> 15); tempVal = 0x7FFFu - tempVal; @@ -659,55 +669,15 @@ extern "C" } - /* - * @brief C custom defined intrinisic function for only M0 processors - */ -#if defined(ARM_MATH_CM0_FAMILY) - static __INLINE q31_t __SSAT( - q31_t x, - uint32_t y) - { - int32_t posMax, negMin; - uint32_t i; - - posMax = 1; - for (i = 0; i < (y - 1); i++) - { - posMax = posMax * 2; - } - - if(x > 0) - { - posMax = (posMax - 1); - - if(x > posMax) - { - x = posMax; - } - } - else - { - negMin = -posMax; - - if(x < negMin) - { - x = negMin; - } - } - return (x); - } -#endif /* end of ARM_MATH_CM0_FAMILY */ - - - /* - * @brief C custom defined intrinsic function for M3 and M0 processors - */ -#if defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) +/* + * @brief C custom defined intrinsic function for M3 and M0 processors + */ +#if !defined (ARM_MATH_DSP) /* * @brief C custom defined QADD8 for M3 and M0 processors */ - static __INLINE uint32_t __QADD8( + CMSIS_INLINE __STATIC_INLINE uint32_t __QADD8( uint32_t x, uint32_t y) { @@ -725,7 +695,7 @@ extern "C" /* * @brief C custom defined QSUB8 for M3 and M0 processors */ - static __INLINE uint32_t __QSUB8( + CMSIS_INLINE __STATIC_INLINE uint32_t __QSUB8( uint32_t x, uint32_t y) { @@ -743,7 +713,7 @@ extern "C" /* * @brief C custom defined QADD16 for M3 and M0 processors */ - static __INLINE uint32_t __QADD16( + CMSIS_INLINE __STATIC_INLINE uint32_t __QADD16( uint32_t x, uint32_t y) { @@ -760,7 +730,7 @@ extern "C" /* * @brief C custom defined SHADD16 for M3 and M0 processors */ - static __INLINE uint32_t __SHADD16( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHADD16( uint32_t x, uint32_t y) { @@ -776,7 +746,7 @@ extern "C" /* * @brief C custom defined QSUB16 for M3 and M0 processors */ - static __INLINE uint32_t __QSUB16( + CMSIS_INLINE __STATIC_INLINE uint32_t __QSUB16( uint32_t x, uint32_t y) { @@ -792,7 +762,7 @@ extern "C" /* * @brief C custom defined SHSUB16 for M3 and M0 processors */ - static __INLINE uint32_t __SHSUB16( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHSUB16( uint32_t x, uint32_t y) { @@ -808,7 +778,7 @@ extern "C" /* * @brief C custom defined QASX for M3 and M0 processors */ - static __INLINE uint32_t __QASX( + CMSIS_INLINE __STATIC_INLINE uint32_t __QASX( uint32_t x, uint32_t y) { @@ -824,7 +794,7 @@ extern "C" /* * @brief C custom defined SHASX for M3 and M0 processors */ - static __INLINE uint32_t __SHASX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHASX( uint32_t x, uint32_t y) { @@ -840,7 +810,7 @@ extern "C" /* * @brief C custom defined QSAX for M3 and M0 processors */ - static __INLINE uint32_t __QSAX( + CMSIS_INLINE __STATIC_INLINE uint32_t __QSAX( uint32_t x, uint32_t y) { @@ -856,7 +826,7 @@ extern "C" /* * @brief C custom defined SHSAX for M3 and M0 processors */ - static __INLINE uint32_t __SHSAX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SHSAX( uint32_t x, uint32_t y) { @@ -872,7 +842,7 @@ extern "C" /* * @brief C custom defined SMUSDX for M3 and M0 processors */ - static __INLINE uint32_t __SMUSDX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUSDX( uint32_t x, uint32_t y) { @@ -883,7 +853,7 @@ extern "C" /* * @brief C custom defined SMUADX for M3 and M0 processors */ - static __INLINE uint32_t __SMUADX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUADX( uint32_t x, uint32_t y) { @@ -895,7 +865,7 @@ extern "C" /* * @brief C custom defined QADD for M3 and M0 processors */ - static __INLINE int32_t __QADD( + CMSIS_INLINE __STATIC_INLINE int32_t __QADD( int32_t x, int32_t y) { @@ -906,7 +876,7 @@ extern "C" /* * @brief C custom defined QSUB for M3 and M0 processors */ - static __INLINE int32_t __QSUB( + CMSIS_INLINE __STATIC_INLINE int32_t __QSUB( int32_t x, int32_t y) { @@ -917,7 +887,7 @@ extern "C" /* * @brief C custom defined SMLAD for M3 and M0 processors */ - static __INLINE uint32_t __SMLAD( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMLAD( uint32_t x, uint32_t y, uint32_t sum) @@ -931,7 +901,7 @@ extern "C" /* * @brief C custom defined SMLADX for M3 and M0 processors */ - static __INLINE uint32_t __SMLADX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMLADX( uint32_t x, uint32_t y, uint32_t sum) @@ -945,7 +915,7 @@ extern "C" /* * @brief C custom defined SMLSDX for M3 and M0 processors */ - static __INLINE uint32_t __SMLSDX( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMLSDX( uint32_t x, uint32_t y, uint32_t sum) @@ -959,7 +929,7 @@ extern "C" /* * @brief C custom defined SMLALD for M3 and M0 processors */ - static __INLINE uint64_t __SMLALD( + CMSIS_INLINE __STATIC_INLINE uint64_t __SMLALD( uint32_t x, uint32_t y, uint64_t sum) @@ -974,7 +944,7 @@ extern "C" /* * @brief C custom defined SMLALDX for M3 and M0 processors */ - static __INLINE uint64_t __SMLALDX( + CMSIS_INLINE __STATIC_INLINE uint64_t __SMLALDX( uint32_t x, uint32_t y, uint64_t sum) @@ -989,7 +959,7 @@ extern "C" /* * @brief C custom defined SMUAD for M3 and M0 processors */ - static __INLINE uint32_t __SMUAD( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUAD( uint32_t x, uint32_t y) { @@ -1001,7 +971,7 @@ extern "C" /* * @brief C custom defined SMUSD for M3 and M0 processors */ - static __INLINE uint32_t __SMUSD( + CMSIS_INLINE __STATIC_INLINE uint32_t __SMUSD( uint32_t x, uint32_t y) { @@ -1013,14 +983,25 @@ extern "C" /* * @brief C custom defined SXTB16 for M3 and M0 processors */ - static __INLINE uint32_t __SXTB16( + CMSIS_INLINE __STATIC_INLINE uint32_t __SXTB16( uint32_t x) { return ((uint32_t)(((((q31_t)x << 24) >> 24) & (q31_t)0x0000FFFF) | ((((q31_t)x << 8) >> 8) & (q31_t)0xFFFF0000) )); } -#endif /* defined (ARM_MATH_CM3) || defined (ARM_MATH_CM0_FAMILY) */ + /* + * @brief C custom defined SMMLA for M3 and M0 processors + */ + CMSIS_INLINE __STATIC_INLINE int32_t __SMMLA( + int32_t x, + int32_t y, + int32_t sum) + { + return (sum + (int32_t) (((int64_t) x * y) >> 32)); + } + +#endif /* !defined (ARM_MATH_DSP) */ /** @@ -1737,7 +1718,7 @@ extern "C" typedef struct { q15_t A0; /**< The derived gain, A0 = Kp + Ki + Kd . */ -#ifdef ARM_MATH_CM0_FAMILY +#if !defined (ARM_MATH_DSP) q15_t A1; q15_t A2; #else @@ -4792,7 +4773,7 @@ void arm_rfft_fast_f32( * @param[in] in input sample to process * @return out processed output sample. */ - static __INLINE float32_t arm_pid_f32( + CMSIS_INLINE __STATIC_INLINE float32_t arm_pid_f32( arm_pid_instance_f32 * S, float32_t in) { @@ -4826,7 +4807,7 @@ void arm_rfft_fast_f32( * In order to avoid overflows completely the input signal must be scaled down by 2 bits as there are four additions. * After all multiply-accumulates are performed, the 2.62 accumulator is truncated to 1.32 format and then saturated to 1.31 format. */ - static __INLINE q31_t arm_pid_q31( + CMSIS_INLINE __STATIC_INLINE q31_t arm_pid_q31( arm_pid_instance_q31 * S, q31_t in) { @@ -4843,7 +4824,7 @@ void arm_rfft_fast_f32( acc += (q63_t) S->A2 * S->state[1]; /* convert output to 1.31 format to add y[n-1] */ - out = (q31_t) (acc >> 31u); + out = (q31_t) (acc >> 31U); /* out += y[n-1] */ out += S->state[2]; @@ -4873,14 +4854,14 @@ void arm_rfft_fast_f32( * After all additions have been performed, the accumulator is truncated to 34.15 format by discarding low 15 bits. * Lastly, the accumulator is saturated to yield a result in 1.15 format. */ - static __INLINE q15_t arm_pid_q15( + CMSIS_INLINE __STATIC_INLINE q15_t arm_pid_q15( arm_pid_instance_q15 * S, q15_t in) { q63_t acc; q15_t out; -#ifndef ARM_MATH_CM0_FAMILY +#if defined (ARM_MATH_DSP) __SIMD32_TYPE *vstate; /* Implementation of PID controller */ @@ -4984,7 +4965,7 @@ void arm_rfft_fast_f32( * @param[out] pIalpha points to output two-phase orthogonal vector axis alpha * @param[out] pIbeta points to output two-phase orthogonal vector axis beta */ - static __INLINE void arm_clarke_f32( + CMSIS_INLINE __STATIC_INLINE void arm_clarke_f32( float32_t Ia, float32_t Ib, float32_t * pIalpha, @@ -5011,7 +4992,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the addition, hence there is no risk of overflow. */ - static __INLINE void arm_clarke_q31( + CMSIS_INLINE __STATIC_INLINE void arm_clarke_q31( q31_t Ia, q31_t Ib, q31_t * pIalpha, @@ -5081,7 +5062,7 @@ void arm_rfft_fast_f32( * @param[out] pIa points to output three-phase coordinate a * @param[out] pIb points to output three-phase coordinate b */ - static __INLINE void arm_inv_clarke_f32( + CMSIS_INLINE __STATIC_INLINE void arm_inv_clarke_f32( float32_t Ialpha, float32_t Ibeta, float32_t * pIa, @@ -5108,7 +5089,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the subtraction, hence there is no risk of overflow. */ - static __INLINE void arm_inv_clarke_q31( + CMSIS_INLINE __STATIC_INLINE void arm_inv_clarke_q31( q31_t Ialpha, q31_t Ibeta, q31_t * pIa, @@ -5191,7 +5172,7 @@ void arm_rfft_fast_f32( * The function implements the forward Park transform. * */ - static __INLINE void arm_park_f32( + CMSIS_INLINE __STATIC_INLINE void arm_park_f32( float32_t Ialpha, float32_t Ibeta, float32_t * pId, @@ -5222,7 +5203,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the addition and subtraction, hence there is no risk of overflow. */ - static __INLINE void arm_park_q31( + CMSIS_INLINE __STATIC_INLINE void arm_park_q31( q31_t Ialpha, q31_t Ibeta, q31_t * pId, @@ -5304,7 +5285,7 @@ void arm_rfft_fast_f32( * @param[in] sinVal sine value of rotation angle theta * @param[in] cosVal cosine value of rotation angle theta */ - static __INLINE void arm_inv_park_f32( + CMSIS_INLINE __STATIC_INLINE void arm_inv_park_f32( float32_t Id, float32_t Iq, float32_t * pIalpha, @@ -5335,7 +5316,7 @@ void arm_rfft_fast_f32( * The accumulator maintains 1.31 format by truncating lower 31 bits of the intermediate multiplication in 2.62 format. * There is saturation on the addition, hence there is no risk of overflow. */ - static __INLINE void arm_inv_park_q31( + CMSIS_INLINE __STATIC_INLINE void arm_inv_park_q31( q31_t Id, q31_t Iq, q31_t * pIalpha, @@ -5430,7 +5411,7 @@ void arm_rfft_fast_f32( * @return y processed output sample. * */ - static __INLINE float32_t arm_linear_interp_f32( + CMSIS_INLINE __STATIC_INLINE float32_t arm_linear_interp_f32( arm_linear_interp_instance_f32 * S, float32_t x) { @@ -5444,12 +5425,12 @@ void arm_rfft_fast_f32( /* Calculation of index */ i = (int32_t) ((x - S->x1) / xSpacing); - if(i < 0) + if (i < 0) { /* Iniatilize output for below specified range as least output value of table */ y = pYData[0]; } - else if((uint32_t)i >= S->nValues) + else if ((uint32_t)i >= S->nValues) { /* Iniatilize output for above specified range as last output value of table */ y = pYData[S->nValues - 1]; @@ -5487,7 +5468,7 @@ void arm_rfft_fast_f32( * This function can support maximum of table size 2^12. * */ - static __INLINE q31_t arm_linear_interp_q31( + CMSIS_INLINE __STATIC_INLINE q31_t arm_linear_interp_q31( q31_t * pYData, q31_t x, uint32_t nValues) @@ -5502,11 +5483,11 @@ void arm_rfft_fast_f32( /* Index value calculation */ index = ((x & (q31_t)0xFFF00000) >> 20); - if(index >= (int32_t)(nValues - 1)) + if (index >= (int32_t)(nValues - 1)) { return (pYData[nValues - 1]); } - else if(index < 0) + else if (index < 0) { return (pYData[0]); } @@ -5527,7 +5508,7 @@ void arm_rfft_fast_f32( y += ((q31_t) (((q63_t) y1 * fract) >> 32)); /* Convert y to 1.31 format */ - return (y << 1u); + return (y << 1U); } } @@ -5545,7 +5526,7 @@ void arm_rfft_fast_f32( * This function can support maximum of table size 2^12. * */ - static __INLINE q15_t arm_linear_interp_q15( + CMSIS_INLINE __STATIC_INLINE q15_t arm_linear_interp_q15( q15_t * pYData, q31_t x, uint32_t nValues) @@ -5560,11 +5541,11 @@ void arm_rfft_fast_f32( /* Index value calculation */ index = ((x & (int32_t)0xFFF00000) >> 20); - if(index >= (int32_t)(nValues - 1)) + if (index >= (int32_t)(nValues - 1)) { return (pYData[nValues - 1]); } - else if(index < 0) + else if (index < 0) { return (pYData[0]); } @@ -5602,7 +5583,7 @@ void arm_rfft_fast_f32( * Input sample x is in 12.20 format which contains 12 bits for table index and 20 bits for fractional part. * This function can support maximum of table size 2^12. */ - static __INLINE q7_t arm_linear_interp_q7( + CMSIS_INLINE __STATIC_INLINE q7_t arm_linear_interp_q7( q7_t * pYData, q31_t x, uint32_t nValues) @@ -5621,7 +5602,7 @@ void arm_rfft_fast_f32( } index = (x >> 20) & 0xfff; - if(index >= (nValues - 1)) + if (index >= (nValues - 1)) { return (pYData[nValues - 1]); } @@ -5742,11 +5723,11 @@ void arm_rfft_fast_f32( * @return The function returns ARM_MATH_SUCCESS if input value is positive value or ARM_MATH_ARGUMENT_ERROR if * in is negative value and returns zero output for negative values. */ - static __INLINE arm_status arm_sqrt_f32( + CMSIS_INLINE __STATIC_INLINE arm_status arm_sqrt_f32( float32_t in, float32_t * pOut) { - if(in >= 0.0f) + if (in >= 0.0f) { #if (__FPU_USED == 1) && defined ( __CC_ARM ) @@ -5802,7 +5783,7 @@ void arm_rfft_fast_f32( /** * @brief floating-point Circular write function. */ - static __INLINE void arm_circularWrite_f32( + CMSIS_INLINE __STATIC_INLINE void arm_circularWrite_f32( int32_t * circBuffer, int32_t L, uint16_t * writeOffset, @@ -5811,7 +5792,7 @@ void arm_rfft_fast_f32( int32_t srcInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t wOffset; /* Copy the value of Index pointer that points @@ -5821,7 +5802,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the input sample to the circular buffer */ circBuffer[wOffset] = *src; @@ -5831,7 +5812,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ wOffset += bufferInc; - if(wOffset >= L) + if (wOffset >= L) wOffset -= L; /* Decrement the loop counter */ @@ -5847,7 +5828,7 @@ void arm_rfft_fast_f32( /** * @brief floating-point Circular Read function. */ - static __INLINE void arm_circularRead_f32( + CMSIS_INLINE __STATIC_INLINE void arm_circularRead_f32( int32_t * circBuffer, int32_t L, int32_t * readOffset, @@ -5858,7 +5839,7 @@ void arm_rfft_fast_f32( int32_t dstInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t rOffset, dst_end; /* Copy the value of Index pointer that points @@ -5869,7 +5850,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the sample from the circular buffer to the destination buffer */ *dst = circBuffer[rOffset]; @@ -5877,7 +5858,7 @@ void arm_rfft_fast_f32( /* Update the input pointer */ dst += dstInc; - if(dst == (int32_t *) dst_end) + if (dst == (int32_t *) dst_end) { dst = dst_base; } @@ -5885,7 +5866,7 @@ void arm_rfft_fast_f32( /* Circularly update rOffset. Watch out for positive and negative value */ rOffset += bufferInc; - if(rOffset >= L) + if (rOffset >= L) { rOffset -= L; } @@ -5902,7 +5883,7 @@ void arm_rfft_fast_f32( /** * @brief Q15 Circular write function. */ - static __INLINE void arm_circularWrite_q15( + CMSIS_INLINE __STATIC_INLINE void arm_circularWrite_q15( q15_t * circBuffer, int32_t L, uint16_t * writeOffset, @@ -5911,7 +5892,7 @@ void arm_rfft_fast_f32( int32_t srcInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t wOffset; /* Copy the value of Index pointer that points @@ -5921,7 +5902,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the input sample to the circular buffer */ circBuffer[wOffset] = *src; @@ -5931,7 +5912,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ wOffset += bufferInc; - if(wOffset >= L) + if (wOffset >= L) wOffset -= L; /* Decrement the loop counter */ @@ -5946,7 +5927,7 @@ void arm_rfft_fast_f32( /** * @brief Q15 Circular Read function. */ - static __INLINE void arm_circularRead_q15( + CMSIS_INLINE __STATIC_INLINE void arm_circularRead_q15( q15_t * circBuffer, int32_t L, int32_t * readOffset, @@ -5969,7 +5950,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the sample from the circular buffer to the destination buffer */ *dst = circBuffer[rOffset]; @@ -5977,7 +5958,7 @@ void arm_rfft_fast_f32( /* Update the input pointer */ dst += dstInc; - if(dst == (q15_t *) dst_end) + if (dst == (q15_t *) dst_end) { dst = dst_base; } @@ -5985,7 +5966,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ rOffset += bufferInc; - if(rOffset >= L) + if (rOffset >= L) { rOffset -= L; } @@ -6002,7 +5983,7 @@ void arm_rfft_fast_f32( /** * @brief Q7 Circular write function. */ - static __INLINE void arm_circularWrite_q7( + CMSIS_INLINE __STATIC_INLINE void arm_circularWrite_q7( q7_t * circBuffer, int32_t L, uint16_t * writeOffset, @@ -6011,7 +5992,7 @@ void arm_rfft_fast_f32( int32_t srcInc, uint32_t blockSize) { - uint32_t i = 0u; + uint32_t i = 0U; int32_t wOffset; /* Copy the value of Index pointer that points @@ -6021,7 +6002,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the input sample to the circular buffer */ circBuffer[wOffset] = *src; @@ -6031,7 +6012,7 @@ void arm_rfft_fast_f32( /* Circularly update wOffset. Watch out for positive and negative value */ wOffset += bufferInc; - if(wOffset >= L) + if (wOffset >= L) wOffset -= L; /* Decrement the loop counter */ @@ -6046,7 +6027,7 @@ void arm_rfft_fast_f32( /** * @brief Q7 Circular Read function. */ - static __INLINE void arm_circularRead_q7( + CMSIS_INLINE __STATIC_INLINE void arm_circularRead_q7( q7_t * circBuffer, int32_t L, int32_t * readOffset, @@ -6069,7 +6050,7 @@ void arm_rfft_fast_f32( /* Loop over the blockSize */ i = blockSize; - while(i > 0u) + while (i > 0U) { /* copy the sample from the circular buffer to the destination buffer */ *dst = circBuffer[rOffset]; @@ -6077,7 +6058,7 @@ void arm_rfft_fast_f32( /* Update the input pointer */ dst += dstInc; - if(dst == (q7_t *) dst_end) + if (dst == (q7_t *) dst_end) { dst = dst_base; } @@ -6085,7 +6066,7 @@ void arm_rfft_fast_f32( /* Circularly update rOffset. Watch out for positive and negative value */ rOffset += bufferInc; - if(rOffset >= L) + if (rOffset >= L) { rOffset -= L; } @@ -6749,7 +6730,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate. * @return out interpolated value. */ - static __INLINE float32_t arm_bilinear_interp_f32( + CMSIS_INLINE __STATIC_INLINE float32_t arm_bilinear_interp_f32( const arm_bilinear_interp_instance_f32 * S, float32_t X, float32_t Y) @@ -6766,7 +6747,7 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(xIndex < 0 || xIndex > (S->numRows - 1) || yIndex < 0 || yIndex > (S->numCols - 1)) + if (xIndex < 0 || xIndex > (S->numRows - 1) || yIndex < 0 || yIndex > (S->numCols - 1)) { return (0); } @@ -6815,7 +6796,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate in 12.20 format. * @return out interpolated value. */ - static __INLINE q31_t arm_bilinear_interp_q31( + CMSIS_INLINE __STATIC_INLINE q31_t arm_bilinear_interp_q31( arm_bilinear_interp_instance_q31 * S, q31_t X, q31_t Y) @@ -6840,14 +6821,14 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) + if (rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) { return (0); } /* 20 bits for the fractional part */ /* shift left xfract by 11 to keep 1.31 format */ - xfract = (X & 0x000FFFFF) << 11u; + xfract = (X & 0x000FFFFF) << 11U; /* Read two nearest output values from the index */ x1 = pYData[(rI) + (int32_t)nCols * (cI) ]; @@ -6855,7 +6836,7 @@ void arm_rfft_fast_f32( /* 20 bits for the fractional part */ /* shift left yfract by 11 to keep 1.31 format */ - yfract = (Y & 0x000FFFFF) << 11u; + yfract = (Y & 0x000FFFFF) << 11U; /* Read two nearest output values from the index */ y1 = pYData[(rI) + (int32_t)nCols * (cI + 1) ]; @@ -6889,7 +6870,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate in 12.20 format. * @return out interpolated value. */ - static __INLINE q15_t arm_bilinear_interp_q15( + CMSIS_INLINE __STATIC_INLINE q15_t arm_bilinear_interp_q15( arm_bilinear_interp_instance_q15 * S, q31_t X, q31_t Y) @@ -6914,7 +6895,7 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) + if (rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) { return (0); } @@ -6939,19 +6920,19 @@ void arm_rfft_fast_f32( /* x1 is in 1.15(q15), xfract in 12.20 format and out is in 13.35 format */ /* convert 13.35 to 13.31 by right shifting and out is in 1.31 */ - out = (q31_t) (((q63_t) x1 * (0xFFFFF - xfract)) >> 4u); + out = (q31_t) (((q63_t) x1 * (0xFFFFF - xfract)) >> 4U); acc = ((q63_t) out * (0xFFFFF - yfract)); /* x2 * (xfract) * (1-yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) x2 * (0xFFFFF - yfract)) >> 4u); + out = (q31_t) (((q63_t) x2 * (0xFFFFF - yfract)) >> 4U); acc += ((q63_t) out * (xfract)); /* y1 * (1 - xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y1 * (0xFFFFF - xfract)) >> 4u); + out = (q31_t) (((q63_t) y1 * (0xFFFFF - xfract)) >> 4U); acc += ((q63_t) out * (yfract)); /* y2 * (xfract) * (yfract) in 1.51 and adding to acc */ - out = (q31_t) (((q63_t) y2 * (xfract)) >> 4u); + out = (q31_t) (((q63_t) y2 * (xfract)) >> 4U); acc += ((q63_t) out * (yfract)); /* acc is in 13.51 format and down shift acc by 36 times */ @@ -6967,7 +6948,7 @@ void arm_rfft_fast_f32( * @param[in] Y interpolation coordinate in 12.20 format. * @return out interpolated value. */ - static __INLINE q7_t arm_bilinear_interp_q7( + CMSIS_INLINE __STATIC_INLINE q7_t arm_bilinear_interp_q7( arm_bilinear_interp_instance_q7 * S, q31_t X, q31_t Y) @@ -6992,7 +6973,7 @@ void arm_rfft_fast_f32( /* Care taken for table outside boundary */ /* Returns zero output when values are outside table boundary */ - if(rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) + if (rI < 0 || rI > (S->numRows - 1) || cI < 0 || cI > (S->numCols - 1)) { return (0); } @@ -7063,7 +7044,7 @@ void arm_rfft_fast_f32( a = (q31_t) (((q63_t) x * y ) >> 32) -#if defined ( __CC_ARM ) +#if defined ( __CC_ARM ) /* Enter low optimization region - place directly above function definition */ #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) #define LOW_OPTIMIZATION_ENTER \ @@ -7074,7 +7055,7 @@ void arm_rfft_fast_f32( #endif /* Exit low optimization region - place directly after end of function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) + #if defined ( ARM_MATH_CM4 ) || defined ( ARM_MATH_CM7 ) #define LOW_OPTIMIZATION_EXIT \ _Pragma ("pop") #else @@ -7087,21 +7068,22 @@ void arm_rfft_fast_f32( /* Exit low optimization region - place directly after end of function definition */ #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) #define LOW_OPTIMIZATION_ENTER #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__GNUC__) - #define LOW_OPTIMIZATION_ENTER __attribute__(( optimize("-O1") )) +#elif defined ( __GNUC__ ) + #define LOW_OPTIMIZATION_ENTER \ + __attribute__(( optimize("-O1") )) #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__ICCARM__) +#elif defined ( __ICCARM__ ) /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) + #if defined ( ARM_MATH_CM4 ) || defined ( ARM_MATH_CM7 ) #define LOW_OPTIMIZATION_ENTER \ _Pragma ("optimize=low") #else @@ -7112,7 +7094,7 @@ void arm_rfft_fast_f32( #define LOW_OPTIMIZATION_EXIT /* Enter low optimization region - place directly above function definition */ - #if defined( ARM_MATH_CM4 ) || defined( ARM_MATH_CM7) + #if defined ( ARM_MATH_CM4 ) || defined ( ARM_MATH_CM7 ) #define IAR_ONLY_LOW_OPTIMIZATION_ENTER \ _Pragma ("optimize=low") #else @@ -7122,13 +7104,19 @@ void arm_rfft_fast_f32( /* Exit low optimization region - place directly after end of function definition */ #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__CSMC__) +#elif defined ( __TI_ARM__ ) #define LOW_OPTIMIZATION_ENTER #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER #define IAR_ONLY_LOW_OPTIMIZATION_EXIT -#elif defined(__TASKING__) +#elif defined ( __CSMC__ ) + #define LOW_OPTIMIZATION_ENTER + #define LOW_OPTIMIZATION_EXIT + #define IAR_ONLY_LOW_OPTIMIZATION_ENTER + #define IAR_ONLY_LOW_OPTIMIZATION_EXIT + +#elif defined ( __TASKING__ ) #define LOW_OPTIMIZATION_ENTER #define LOW_OPTIMIZATION_EXIT #define IAR_ONLY_LOW_OPTIMIZATION_ENTER @@ -7141,9 +7129,24 @@ void arm_rfft_fast_f32( } #endif +/* Compiler specific diagnostic adjustment */ +#if defined ( __CC_ARM ) -#if defined ( __GNUC__ ) +#elif defined ( __ARMCC_VERSION ) && ( __ARMCC_VERSION >= 6010050 ) + +#elif defined ( __GNUC__ ) #pragma GCC diagnostic pop + +#elif defined ( __ICCARM__ ) + +#elif defined ( __TI_ARM__ ) + +#elif defined ( __CSMC__ ) + +#elif defined ( __TASKING__ ) + +#else + #error Unknown compiler #endif #endif /* _ARM_MATH_H */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_armcc.h similarity index 75% rename from Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc.h rename to Firmware/ThirdParty/CMSIS/Include/cmsis_armcc.h index 74c49c67..4d9d0645 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc.h +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_armcc.h @@ -1,43 +1,104 @@ /**************************************************************************//** * @file cmsis_armcc.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 + * @brief CMSIS compiler ARMCC (Arm Compiler 5) header file + * @version V5.0.4 + * @date 10. January 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #ifndef __CMSIS_ARMCC_H #define __CMSIS_ARMCC_H #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677) - #error "Please use ARM Compiler Toolchain V4.0.677 or later!" + #error "Please use Arm Compiler Toolchain V4.0.677 or later!" +#endif + +/* CMSIS compiler control architecture macros */ +#if ((defined (__TARGET_ARCH_6_M ) && (__TARGET_ARCH_6_M == 1)) || \ + (defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M == 1)) ) + #define __ARM_ARCH_6M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M == 1)) + #define __ARM_ARCH_7M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1)) + #define __ARM_ARCH_7EM__ 1 +#endif + + /* __ARM_ARCH_8M_BASE__ not applicable */ + /* __ARM_ARCH_8M_MAIN__ not applicable */ + + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE static __forceinline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __declspec(noreturn) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed)) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT __packed struct +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION __packed union +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #define __UNALIGNED_UINT32(x) (*((__packed uint32_t *)(x))) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #define __UNALIGNED_UINT16_WRITE(addr, val) ((*((__packed uint16_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #define __UNALIGNED_UINT16_READ(addr) (*((const __packed uint16_t *)(addr))) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #define __UNALIGNED_UINT32_WRITE(addr, val) ((*((__packed uint32_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #define __UNALIGNED_UINT32_READ(addr) (*((const __packed uint32_t *)(addr))) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict #endif /* ########################### Core Function Access ########################### */ @@ -46,7 +107,19 @@ @{ */ +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ /* intrinsic void __enable_irq(); */ + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ /* intrinsic void __disable_irq(); */ /** @@ -181,7 +254,8 @@ __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) } -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) /** \brief Enable FIQ @@ -256,14 +330,13 @@ __STATIC_INLINE uint32_t __get_FAULTMASK(void) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) { register uint32_t __regFaultMask __ASM("faultmask"); - __regFaultMask = (faultMask & (uint32_t)1); + __regFaultMask = (faultMask & (uint32_t)1U); } -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ -#if (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) - /** \brief Get FPSCR \details Returns the current value of the Floating Point Status/Control register. @@ -271,7 +344,8 @@ __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) */ __STATIC_INLINE uint32_t __get_FPSCR(void) { -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) register uint32_t __regfpscr __ASM("fpscr"); return(__regfpscr); #else @@ -287,15 +361,15 @@ __STATIC_INLINE uint32_t __get_FPSCR(void) */ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) { -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) register uint32_t __regfpscr __ASM("fpscr"); __regfpscr = (fpscr); +#else + (void)fpscr; #endif } -#endif /* (__CORTEX_M == 0x04U) || (__CORTEX_M == 0x07U) */ - - /*@} end of CMSIS_Core_RegAccFunctions */ @@ -369,9 +443,10 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) __schedule_barrier();\ } while (0U) + /** \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. \param [in] value Value to reverse \return Reversed value */ @@ -380,7 +455,7 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) /** \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. \param [in] value Value to reverse \return Reversed value */ @@ -392,14 +467,15 @@ __attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(u } #endif + /** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. \param [in] value Value to reverse \return Reversed value */ #ifndef __NO_EMBEDDED_ASM -__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value) +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value) { revsh r0, r0 bx lr @@ -410,8 +486,8 @@ __attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(in /** \brief Rotate Right in unsigned value (32 bit) \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. - \param [in] value Value to rotate - \param [in] value Number of Bits to rotate + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate \return Rotated value */ #define __ROR __ror @@ -433,23 +509,24 @@ __attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(in \param [in] value Value to reverse \return Reversed value */ -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) #define __RBIT __rbit #else __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) { uint32_t result; - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) + for (value >>= 1U; value != 0U; value >>= 1U) { result <<= 1U; result |= value & 1U; s--; } result <<= s; /* shift when v's highest bits are zero */ - return(result); + return result; } #endif @@ -463,7 +540,8 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) #define __CLZ __clz -#if (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) /** \brief LDR Exclusive (8 bit) @@ -645,7 +723,60 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3 */ #define __STRT(value, ptr) __strt(value, ptr) -#endif /* (__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U) */ +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ /*@}*/ /* end of group CMSIS_Core_InstructionInterface */ @@ -656,7 +787,7 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3 @{ */ -#if (__CORTEX_M >= 0x04U) /* only for Cortex-M4 and above */ +#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) #define __SADD8 __sadd8 #define __QADD8 __qadd8 @@ -727,7 +858,7 @@ __attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint3 #define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \ ((int64_t)(ARG3) << 32U) ) >> 32U)) -#endif /* (__CORTEX_M >= 0x04) */ +#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ /*@} end of group CMSIS_SIMD_intrinsics */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_armclang.h similarity index 57% rename from Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h rename to Firmware/ThirdParty/CMSIS/Include/cmsis_armclang.h index cd13240c..162a400e 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_armcc_V6.h +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_armclang.h @@ -1,39 +1,115 @@ /**************************************************************************//** - * @file cmsis_armcc_V6.h - * @brief CMSIS Cortex-M Core Function/Instruction Header File - * @version V4.30 - * @date 20. October 2015 + * @file cmsis_armclang.h + * @brief CMSIS compiler armclang (Arm Compiler 6) header file + * @version V5.0.4 + * @date 10. January 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ +#ifndef __CMSIS_ARMCLANG_H +#define __CMSIS_ARMCLANG_H -#ifndef __CMSIS_ARMCC_V6_H -#define __CMSIS_ARMCC_V6_H +#pragma clang system_header /* treat file as system include file */ + +#ifndef __ARM_COMPAT_H +#include /* Compatibility header for Arm Compiler 5 intrinsics */ +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif /* ########################### Core Function Access ########################### */ @@ -47,10 +123,7 @@ \details Enables IRQ interrupts by clearing the I-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __enable_irq(void) -{ - __ASM volatile ("cpsie i" : : : "memory"); -} +/* intrinsic void __enable_irq(); see arm_compat.h */ /** @@ -58,10 +131,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __enable_irq(void) \details Disables IRQ interrupts by setting the I-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __disable_irq(void) -{ - __ASM volatile ("cpsid i" : : : "memory"); -} +/* intrinsic void __disable_irq(); see arm_compat.h */ /** @@ -69,7 +139,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __disable_irq(void) \details Returns the content of the Control Register. \return Control Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_CONTROL(void) +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) { uint32_t result; @@ -78,13 +148,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_CONTROL(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Control Register (non-secure) \details Returns the content of the non-secure Control Register when in secure mode. \return non-secure Control Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_CONTROL_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) { uint32_t result; @@ -99,19 +169,19 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_CONTROL_NS(void \details Writes the given value to the Control Register. \param [in] control Control Register value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_CONTROL(uint32_t control) +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) { __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Control Register (non-secure) \details Writes the given value to the non-secure Control Register when in secure state. \param [in] control Control Register value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_CONTROL_NS(uint32_t control) +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) { __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); } @@ -123,7 +193,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_CONTROL_NS(uint32_t \details Returns the content of the IPSR Register. \return IPSR Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_IPSR(void) +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) { uint32_t result; @@ -132,28 +202,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_IPSR(void) } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get IPSR Register (non-secure) - \details Returns the content of the non-secure IPSR Register when in secure state. - \return IPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_IPSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, ipsr_ns" : "=r" (result) ); - return(result); -} -#endif - - /** \brief Get APSR Register \details Returns the content of the APSR Register. \return APSR Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_APSR(void) +__STATIC_FORCEINLINE uint32_t __get_APSR(void) { uint32_t result; @@ -162,28 +216,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_APSR(void) } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get APSR Register (non-secure) - \details Returns the content of the non-secure APSR Register when in secure state. - \return APSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_APSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, apsr_ns" : "=r" (result) ); - return(result); -} -#endif - - /** \brief Get xPSR Register \details Returns the content of the xPSR Register. \return xPSR Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_xPSR(void) +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) { uint32_t result; @@ -192,45 +230,29 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_xPSR(void) } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get xPSR Register (non-secure) - \details Returns the content of the non-secure xPSR Register when in secure state. - \return xPSR Register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_xPSR_NS(void) -{ - uint32_t result; - - __ASM volatile ("MRS %0, xpsr_ns" : "=r" (result) ); - return(result); -} -#endif - - /** \brief Get Process Stack Pointer \details Returns the current value of the Process Stack Pointer (PSP). \return PSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PSP(void) +__STATIC_FORCEINLINE uint32_t __get_PSP(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, psp" : "=r" (result) ); return(result); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Process Stack Pointer (non-secure) \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. \return PSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSP_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); return(result); @@ -243,21 +265,21 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSP_NS(void) \details Assigns the given value to the Process Stack Pointer (PSP). \param [in] topOfProcStack Process Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) { - __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : "sp"); + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Process Stack Pointer (non-secure) \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. \param [in] topOfProcStack Process Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) { - __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : "sp"); + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); } #endif @@ -267,24 +289,24 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSP_NS(uint32_t top \details Returns the current value of the Main Stack Pointer (MSP). \return MSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_MSP(void) +__STATIC_FORCEINLINE uint32_t __get_MSP(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, msp" : "=r" (result) ); return(result); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Main Stack Pointer (non-secure) \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. \return MSP Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSP_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) { - register uint32_t result; + uint32_t result; __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); return(result); @@ -297,21 +319,48 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSP_NS(void) \details Assigns the given value to the Main Stack Pointer (MSP). \param [in] topOfMainStack Main Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) { - __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : "sp"); + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Main Stack Pointer (non-secure) \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. \param [in] topOfMainStack Main Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) { - __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : "sp"); + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); } #endif @@ -321,7 +370,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSP_NS(uint32_t top \details Returns the current state of the priority mask bit from the Priority Mask Register. \return Priority Mask value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PRIMASK(void) +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) { uint32_t result; @@ -330,13 +379,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PRIMASK(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Priority Mask (non-secure) \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. \return Priority Mask value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PRIMASK_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) { uint32_t result; @@ -351,36 +400,34 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PRIMASK_NS(void \details Assigns the given value to the Priority Mask Register. \param [in] priMask Priority Mask */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) { __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Priority Mask (non-secure) \details Assigns the given value to the non-secure Priority Mask Register when in secure state. \param [in] priMask Priority Mask */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) { __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); } #endif -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) /** \brief Enable FIQ \details Enables FIQ interrupts by clearing the F-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __enable_fault_irq(void) -{ - __ASM volatile ("cpsie f" : : : "memory"); -} +#define __enable_fault_irq __enable_fiq /* see arm_compat.h */ /** @@ -388,10 +435,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __enable_fault_irq(void) \details Disables FIQ interrupts by setting the F-bit in the CPSR. Can only be executed in Privileged modes. */ -__attribute__((always_inline)) __STATIC_INLINE void __disable_fault_irq(void) -{ - __ASM volatile ("cpsid f" : : : "memory"); -} +#define __disable_fault_irq __disable_fiq /* see arm_compat.h */ /** @@ -399,7 +443,7 @@ __attribute__((always_inline)) __STATIC_INLINE void __disable_fault_irq(void) \details Returns the current value of the Base Priority register. \return Base Priority register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_BASEPRI(void) +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) { uint32_t result; @@ -408,13 +452,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_BASEPRI(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Base Priority (non-secure) \details Returns the current value of the non-secure Base Priority register when in secure state. \return Base Priority register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_BASEPRI_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) { uint32_t result; @@ -429,21 +473,21 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_BASEPRI_NS(void \details Assigns the given value to the Base Priority register. \param [in] basePri Base Priority value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_BASEPRI(uint32_t value) +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) { - __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Base Priority (non-secure) \details Assigns the given value to the non-secure Base Priority register when in secure state. \param [in] basePri Base Priority value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_NS(uint32_t value) +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) { - __ASM volatile ("MSR basepri_ns, %0" : : "r" (value) : "memory"); + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); } #endif @@ -454,32 +498,18 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_NS(uint32_t or the new value increases the BASEPRI priority level. \param [in] basePri Base Priority value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_BASEPRI_MAX(uint32_t value) +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) { - __ASM volatile ("MSR basepri_max, %0" : : "r" (value) : "memory"); + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set Base Priority with condition (non_secure) - \details Assigns the given value to the non-secure Base Priority register when in secure state only if BASEPRI masking is disabled, - or the new value increases the BASEPRI priority level. - \param [in] basePri Base Priority value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_BASEPRI_MAX_NS(uint32_t value) -{ - __ASM volatile ("MSR basepri_max_ns, %0" : : "r" (value) : "memory"); -} -#endif - - /** \brief Get Fault Mask \details Returns the current value of the Fault Mask register. \return Fault Mask register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FAULTMASK(void) +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) { uint32_t result; @@ -488,13 +518,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FAULTMASK(void) } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Fault Mask (non-secure) \details Returns the current value of the non-secure Fault Mask register when in secure state. \return Fault Mask register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FAULTMASK_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) { uint32_t result; @@ -509,222 +539,232 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FAULTMASK_NS(vo \details Assigns the given value to the Fault Mask register. \param [in] faultMask Fault Mask value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) { __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); } -#if (__ARM_FEATURE_CMSE == 3U) +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Fault Mask (non-secure) \details Assigns the given value to the non-secure Fault Mask register when in secure state. \param [in] faultMask Fault Mask value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) { __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); } #endif - -#endif /* ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ -#if (__ARM_ARCH_8M__ == 1U) +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /** \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). \return PSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_PSPLIM(void) +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, psplim" : "=r" (result) ); - return(result); + return result; +#endif } - -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. \return PSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_PSPLIM_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); - return(result); + return result; +#endif } #endif /** \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif } -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif } #endif /** \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). \return MSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_MSPLIM(void) +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, msplim" : "=r" (result) ); - - return(result); + return result; +#endif } -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. \return MSPLIM Register value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_MSPLIM_NS(void) +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) { - register uint32_t result; - +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); - return(result); + return result; +#endif } #endif /** \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif } -#if (__ARM_FEATURE_CMSE == 3U) && (__ARM_ARCH_PROFILE == 'M') /* ToDo: ARMCC_V6: check predefined macro for mainline */ +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) /** \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. \param [in] MainStackPtrLimit Main Stack Pointer value to set */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) { +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif } #endif -#endif /* (__ARM_ARCH_8M__ == 1U) */ - - -#if ((__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=4 */ +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ /** \brief Get FPSCR - \details eturns the current value of the Floating Point Status/Control register. + \details Returns the current value of the Floating Point Status/Control register. \return Floating Point Status/Control register value */ -#define __get_FPSCR __builtin_arm_get_fpscr -#if 0 -__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FPSCR(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); - __ASM volatile (""); - return(result); +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr #else - return(0); +#define __get_FPSCR() ((uint32_t)0U) #endif -} -#endif - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Get FPSCR (non-secure) - \details Returns the current value of the non-secure Floating Point Status/Control register when in secure state. - \return Floating Point Status/Control register value - */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __TZ_get_FPSCR_NS(void) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - uint32_t result; - - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMRS %0, fpscr_ns" : "=r" (result) ); - __ASM volatile (""); - return(result); -#else - return(0); -#endif -} -#endif - /** \brief Set FPSCR \details Assigns the given value to the Floating Point Status/Control register. \param [in] fpscr Floating Point Status/Control value to set */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) #define __set_FPSCR __builtin_arm_set_fpscr -#if 0 -__attribute__((always_inline)) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); +#else +#define __set_FPSCR(x) ((void)(x)) #endif -} -#endif - -#if (__ARM_FEATURE_CMSE == 3U) -/** - \brief Set FPSCR (non-secure) - \details Assigns the given value to the non-secure Floating Point Status/Control register when in secure state. - \param [in] fpscr Floating Point Status/Control value to set - */ -__attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FPSCR_NS(uint32_t fpscr) -{ -#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U) - __ASM volatile (""); /* Empty asm statement works as a scheduling barrier */ - __ASM volatile ("VMSR fpscr_ns, %0" : : "r" (fpscr) : "vfpcc"); - __ASM volatile (""); -#endif -} -#endif - -#endif /* ((__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ - /*@} end of CMSIS_Core_RegAccFunctions */ @@ -801,45 +841,29 @@ __attribute__((always_inline)) __STATIC_INLINE void __TZ_set_FPSCR_NS(uint32_t f /** \brief Reverse byte order (32 bit) - \details Reverses the byte order in integer value. + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. \param [in] value Value to reverse \return Reversed value */ -#define __REV __builtin_bswap32 +#define __REV(value) __builtin_bswap32(value) /** \brief Reverse byte order (16 bit) - \details Reverses the byte order in two unsigned short values. + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. \param [in] value Value to reverse \return Reversed value */ -#define __REV16 __builtin_bswap16 /* ToDo: ARMCC_V6: check if __builtin_bswap16 could be used */ -#if 0 -__attribute__((always_inline)) __STATIC_INLINE uint32_t __REV16(uint32_t value) -{ - uint32_t result; - - __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} -#endif +#define __REV16(value) __ROR(__REV(value), 16) /** - \brief Reverse byte order in signed short value - \details Reverses the byte order in a signed short value with sign extension to integer. + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. \param [in] value Value to reverse \return Reversed value */ - /* ToDo: ARMCC_V6: check if __builtin_bswap16 could be used */ -__attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) -{ - int32_t result; - - __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); - return(result); -} +#define __REVSH(value) (int16_t)__builtin_bswap16(value) /** @@ -849,8 +873,13 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __REVSH(int32_t value) \param [in] op2 Number of Bits to rotate \return Rotated value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) { + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } return (op1 >> op2) | (op1 << (32U - op2)); } @@ -858,11 +887,11 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint /** \brief Breakpoint \details Causes the processor to enter Debug state. - Debug tools can use this to investigate system state when the instruction at a particular address is reached. - \param [in] value is ignored by the processor. - If required, a debugger can use it to store additional information about the breakpoint. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. */ -#define __BKPT(value) __ASM volatile ("bkpt "#value) +#define __BKPT(value) __ASM volatile ("bkpt "#value) /** @@ -871,28 +900,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint \param [in] value Value to reverse \return Reversed value */ - /* ToDo: ARMCC_V6: check if __builtin_arm_rbit is supported */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) -{ - uint32_t result; - -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); -#else - int32_t s = 4 /*sizeof(v)*/ * 8 - 1; /* extra shift needed at end */ - - result = value; /* r will be reversed bits of v; first get LSB of v */ - for (value >>= 1U; value; value >>= 1U) - { - result <<= 1U; - result |= value & 1U; - s--; - } - result <<= s; /* shift when v's highest bits are zero */ -#endif - return(result); -} - +#define __RBIT __builtin_arm_rbit /** \brief Count leading zeros @@ -900,11 +908,13 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \param [in] value Value to count the leading zeros \return number of leading zeros in value */ -#define __CLZ __builtin_clz +#define __CLZ (uint8_t)__builtin_clz -#if ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) /* ToDo: ARMCC_V6: check if this is ok for cortex >=3 */ - +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /** \brief LDR Exclusive (8 bit) \details Executes a exclusive LDR instruction for 8 bit value. @@ -971,6 +981,15 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) */ #define __CLREX __builtin_arm_clrex +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) /** \brief Signed Saturate @@ -979,13 +998,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \param [in] sat Bit position to saturate to (1..32) \return Saturated value */ -/*#define __SSAT __builtin_arm_ssat*/ -#define __SSAT(ARG1,ARG2) \ -({ \ - int32_t __RES, __ARG1 = (ARG1); \ - __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) +#define __SSAT __builtin_arm_ssat /** @@ -996,14 +1009,6 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \return Saturated value */ #define __USAT __builtin_arm_usat -#if 0 -#define __USAT(ARG1,ARG2) \ -({ \ - uint32_t __RES, __ARG1 = (ARG1); \ - __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ - __RES; \ - }) -#endif /** @@ -1013,7 +1018,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) \param [in] value Value to rotate \return Rotated value */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) { uint32_t result; @@ -1028,12 +1033,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __RRX(uint32_t value) \param [in] ptr Pointer to data \return value of type uint8_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *ptr) +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint8_t) result); /* Add explicit type cast here */ + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ } @@ -1043,12 +1048,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t \param [in] ptr Pointer to data \return value of type uint16_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *ptr) +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint16_t) result); /* Add explicit type cast here */ + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ } @@ -1058,12 +1063,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_ \param [in] ptr Pointer to data \return value of type uint32_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *ptr) +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); - return(result); + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); } @@ -1073,9 +1078,9 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) { - __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1085,9 +1090,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STRBT(uint8_t value, volat \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) { - __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1097,28 +1102,83 @@ __attribute__((always_inline)) __STATIC_INLINE void __STRHT(uint16_t value, vola \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) { - __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); } -#endif /* ((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M__ == 1U)) */ +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ -#if (__ARM_ARCH_8M__ == 1U) - +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) /** \brief Load-Acquire (8 bit) \details Executes a LDAB instruction for 8 bit value. \param [in] ptr Pointer to data \return value of type uint8_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint8_t __LDAB(volatile uint8_t *ptr) +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint8_t) result); + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); } @@ -1128,12 +1188,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint8_t __LDAB(volatile uint8_t * \param [in] ptr Pointer to data \return value of type uint16_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint16_t __LDAH(volatile uint16_t *ptr) +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); - return ((uint16_t) result); + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); } @@ -1143,12 +1203,12 @@ __attribute__((always_inline)) __STATIC_INLINE uint16_t __LDAH(volatile uint16_t \param [in] ptr Pointer to data \return value of type uint32_t at (*ptr) */ -__attribute__((always_inline)) __STATIC_INLINE uint32_t __LDA(volatile uint32_t *ptr) +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) { - uint32_t result; + uint32_t result; - __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); - return(result); + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); } @@ -1158,9 +1218,9 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __LDA(volatile uint32_t \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) { - __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1170,9 +1230,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STLB(uint8_t value, volati \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) { - __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1182,9 +1242,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STLH(uint16_t value, volat \param [in] value Value to store \param [in] ptr Pointer to location */ -__attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volatile uint32_t *ptr) +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) { - __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); } @@ -1247,7 +1307,8 @@ __attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volati */ #define __STLEX (uint32_t)__builtin_arm_stlex -#endif /* (__ARM_ARCH_8M__ == 1U) */ +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ /*@}*/ /* end of group CMSIS_Core_InstructionInterface */ @@ -1258,9 +1319,9 @@ __attribute__((always_inline)) __STATIC_INLINE void __STL(uint32_t value, volati @{ */ -#if (__ARM_FEATURE_DSP == 1U) /* ToDo: ARMCC_V6: This should be ARCH >= ARMv7-M + SIMD */ +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1268,7 +1329,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1276,7 +1337,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1284,7 +1345,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1292,7 +1353,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1300,7 +1361,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1309,7 +1370,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, u } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1317,7 +1378,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1325,7 +1386,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1333,7 +1394,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1341,7 +1402,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1349,7 +1410,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1358,7 +1419,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, u } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1366,7 +1427,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1374,7 +1435,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1382,7 +1443,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1390,7 +1451,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1398,7 +1459,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1406,7 +1467,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1414,7 +1475,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1422,7 +1483,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1430,7 +1491,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1438,7 +1499,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1446,7 +1507,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1454,7 +1515,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1462,7 +1523,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1470,7 +1531,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1478,7 +1539,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1486,7 +1547,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1494,7 +1555,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1502,7 +1563,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1510,7 +1571,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1518,7 +1579,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1526,7 +1587,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1534,7 +1595,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uin return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1542,7 +1603,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1550,7 +1611,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1558,7 +1619,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1568,7 +1629,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, u #define __SSAT16(ARG1,ARG2) \ ({ \ - uint32_t __RES, __ARG1 = (ARG1); \ + int32_t __RES, __ARG1 = (ARG1); \ __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ __RES; \ }) @@ -1580,7 +1641,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, u __RES; \ }) -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) { uint32_t result; @@ -1588,7 +1649,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1596,7 +1657,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) { uint32_t result; @@ -1604,7 +1665,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) { uint32_t result; @@ -1612,7 +1673,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1620,7 +1681,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1628,7 +1689,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1636,7 +1697,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1644,7 +1705,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1661,7 +1722,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1678,7 +1739,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1686,7 +1747,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1694,7 +1755,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1702,7 +1763,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, u return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) { uint32_t result; @@ -1710,7 +1771,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, return(result); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1727,7 +1788,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) { union llreg_u{ uint32_t w32[2]; @@ -1744,7 +1805,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, return(llr.w64); } -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) { uint32_t result; @@ -1752,7 +1813,7 @@ __attribute__((always_inline)) __STATIC_INLINE uint32_t __SEL (uint32_t op1, ui return(result); } -__attribute__((always_inline)) __STATIC_INLINE int32_t __QADD( int32_t op1, int32_t op2) +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) { int32_t result; @@ -1760,7 +1821,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __QADD( int32_t op1, in return(result); } -__attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, int32_t op2) +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) { int32_t result; @@ -1768,6 +1829,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, in return(result); } +#if 0 #define __PKHBT(ARG1,ARG2,ARG3) \ ({ \ uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ @@ -1784,17 +1846,24 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __QSUB( int32_t op1, in __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ __RES; \ }) +#endif -__attribute__((always_inline)) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) { - int32_t result; + int32_t result; - __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); - return(result); + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); } -#endif /* (__ARM_FEATURE_DSP == 1U) */ +#endif /* (__ARM_FEATURE_DSP == 1) */ /*@} end of group CMSIS_SIMD_intrinsics */ -#endif /* __CMSIS_ARMCC_V6_H */ +#endif /* __CMSIS_ARMCLANG_H */ diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h new file mode 100644 index 00000000..94212eb8 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_compiler.h @@ -0,0 +1,266 @@ +/**************************************************************************//** + * @file cmsis_compiler.h + * @brief CMSIS compiler generic header file + * @version V5.0.4 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_COMPILER_H +#define __CMSIS_COMPILER_H + +#include + +/* + * Arm Compiler 4/5 + */ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + + +/* + * Arm Compiler 6 (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #include "cmsis_armclang.h" + + +/* + * GNU Compiler + */ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + + +/* + * IAR Compiler + */ +#elif defined ( __ICCARM__ ) + #include + + +/* + * TI Arm Compiler + */ +#elif defined ( __TI_ARM__ ) + #include + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __attribute__((packed)) + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed)) + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed)) + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + + +/* + * TASKING Compiler + */ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __packed__ + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __packed__ + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __packed__ + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __packed__ T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __align(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + + +/* + * COSMIC Compiler + */ +#elif defined ( __CSMC__ ) + #include + + #ifndef __ASM + #define __ASM _asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + // NO RETURN is automatically detected hence no warning here + #define __NO_RETURN + #endif + #ifndef __USED + #warning No compiler specific solution for __USED. __USED is ignored. + #define __USED + #endif + #ifndef __WEAK + #define __WEAK __weak + #endif + #ifndef __PACKED + #define __PACKED @packed + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT @packed struct + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION @packed union + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + @packed struct T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored. + #define __ALIGNED(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + + +#else + #error Unknown compiler. +#endif + + +#endif /* __CMSIS_COMPILER_H */ + diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h new file mode 100644 index 00000000..2d9db15a --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_gcc.h @@ -0,0 +1,2085 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.0.4 + * @date 09. April 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) :: "memory"); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) :: "memory"); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ (uint8_t)__builtin_clz + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ + __extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#if 0 +#define __PKHBT(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) +#endif + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h new file mode 100644 index 00000000..11c4af0e --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_iccarm.h @@ -0,0 +1,935 @@ +/**************************************************************************//** + * @file cmsis_iccarm.h + * @brief CMSIS compiler ICCARM (IAR Compiler for Arm) header file + * @version V5.0.7 + * @date 19. June 2018 + ******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Copyright (c) 2017-2018 IAR Systems +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//------------------------------------------------------------------------------ + + +#ifndef __CMSIS_ICCARM_H__ +#define __CMSIS_ICCARM_H__ + +#ifndef __ICCARM__ + #error This file should only be compiled by ICCARM +#endif + +#pragma system_include + +#define __IAR_FT _Pragma("inline=forced") __intrinsic + +#if (__VER__ >= 8000000) + #define __ICCARM_V8 1 +#else + #define __ICCARM_V8 0 +#endif + +#ifndef __ALIGNED + #if __ICCARM_V8 + #define __ALIGNED(x) __attribute__((aligned(x))) + #elif (__VER__ >= 7080000) + /* Needs IAR language extensions */ + #define __ALIGNED(x) __attribute__((aligned(x))) + #else + #warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored. + #define __ALIGNED(x) + #endif +#endif + + +/* Define compiler macros for CPU architecture, used in CMSIS 5. + */ +#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ || __ARM_ARCH_8M_BASE__ || __ARM_ARCH_8M_MAIN__ +/* Macros already defined */ +#else + #if defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' + #if __ARM_ARCH == 6 + #define __ARM_ARCH_6M__ 1 + #elif __ARM_ARCH == 7 + #if __ARM_FEATURE_DSP + #define __ARM_ARCH_7EM__ 1 + #else + #define __ARM_ARCH_7M__ 1 + #endif + #endif /* __ARM_ARCH */ + #endif /* __ARM_ARCH_PROFILE == 'M' */ +#endif + +/* Alternativ core deduction for older ICCARM's */ +#if !defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7EM__) && \ + !defined(__ARM_ARCH_8M_BASE__) && !defined(__ARM_ARCH_8M_MAIN__) + #if defined(__ARM6M__) && (__CORE__ == __ARM6M__) + #define __ARM_ARCH_6M__ 1 + #elif defined(__ARM7M__) && (__CORE__ == __ARM7M__) + #define __ARM_ARCH_7M__ 1 + #elif defined(__ARM7EM__) && (__CORE__ == __ARM7EM__) + #define __ARM_ARCH_7EM__ 1 + #elif defined(__ARM8M_BASELINE__) && (__CORE == __ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM8M_MAINLINE__) && (__CORE == __ARM8M_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8EM_MAINLINE__) && (__CORE == __ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #else + #error "Unknown target." + #endif +#endif + + + +#if defined(__ARM_ARCH_6M__) && __ARM_ARCH_6M__==1 + #define __IAR_M0_FAMILY 1 +#elif defined(__ARM_ARCH_8M_BASE__) && __ARM_ARCH_8M_BASE__==1 + #define __IAR_M0_FAMILY 1 +#else + #define __IAR_M0_FAMILY 0 +#endif + + +#ifndef __ASM + #define __ASM __asm +#endif + +#ifndef __INLINE + #define __INLINE inline +#endif + +#ifndef __NO_RETURN + #if __ICCARM_V8 + #define __NO_RETURN __attribute__((__noreturn__)) + #else + #define __NO_RETURN _Pragma("object_attribute=__noreturn") + #endif +#endif + +#ifndef __PACKED + #if __ICCARM_V8 + #define __PACKED __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED __packed + #endif +#endif + +#ifndef __PACKED_STRUCT + #if __ICCARM_V8 + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_STRUCT __packed struct + #endif +#endif + +#ifndef __PACKED_UNION + #if __ICCARM_V8 + #define __PACKED_UNION union __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_UNION __packed union + #endif +#endif + +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif + +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif + +#ifndef __FORCEINLINE + #define __FORCEINLINE _Pragma("inline=forced") +#endif + +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __FORCEINLINE __STATIC_INLINE +#endif + +#ifndef __UNALIGNED_UINT16_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint16_t __iar_uint16_read(void const *ptr) +{ + return *(__packed uint16_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT16_READ(PTR) __iar_uint16_read(PTR) +#endif + + +#ifndef __UNALIGNED_UINT16_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint16_write(void const *ptr, uint16_t val) +{ + *(__packed uint16_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT16_WRITE(PTR,VAL) __iar_uint16_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint32_t __iar_uint32_read(void const *ptr) +{ + return *(__packed uint32_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT32_READ(PTR) __iar_uint32_read(PTR) +#endif + +#ifndef __UNALIGNED_UINT32_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint32_write(void const *ptr, uint32_t val) +{ + *(__packed uint32_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT32_WRITE(PTR,VAL) __iar_uint32_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32 /* deprecated */ +#pragma language=save +#pragma language=extended +__packed struct __iar_u32 { uint32_t v; }; +#pragma language=restore +#define __UNALIGNED_UINT32(PTR) (((struct __iar_u32 *)(PTR))->v) +#endif + +#ifndef __USED + #if __ICCARM_V8 + #define __USED __attribute__((used)) + #else + #define __USED _Pragma("__root") + #endif +#endif + +#ifndef __WEAK + #if __ICCARM_V8 + #define __WEAK __attribute__((weak)) + #else + #define __WEAK _Pragma("__weak") + #endif +#endif + + +#ifndef __ICCARM_INTRINSICS_VERSION__ + #define __ICCARM_INTRINSICS_VERSION__ 0 +#endif + +#if __ICCARM_INTRINSICS_VERSION__ == 2 + + #if defined(__CLZ) + #undef __CLZ + #endif + #if defined(__REVSH) + #undef __REVSH + #endif + #if defined(__RBIT) + #undef __RBIT + #endif + #if defined(__SSAT) + #undef __SSAT + #endif + #if defined(__USAT) + #undef __USAT + #endif + + #include "iccarm_builtin.h" + + #define __disable_fault_irq __iar_builtin_disable_fiq + #define __disable_irq __iar_builtin_disable_interrupt + #define __enable_fault_irq __iar_builtin_enable_fiq + #define __enable_irq __iar_builtin_enable_interrupt + #define __arm_rsr __iar_builtin_rsr + #define __arm_wsr __iar_builtin_wsr + + + #define __get_APSR() (__arm_rsr("APSR")) + #define __get_BASEPRI() (__arm_rsr("BASEPRI")) + #define __get_CONTROL() (__arm_rsr("CONTROL")) + #define __get_FAULTMASK() (__arm_rsr("FAULTMASK")) + + #if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + #define __get_FPSCR() (__arm_rsr("FPSCR")) + #define __set_FPSCR(VALUE) (__arm_wsr("FPSCR", (VALUE))) + #else + #define __get_FPSCR() ( 0 ) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #define __get_IPSR() (__arm_rsr("IPSR")) + #define __get_MSP() (__arm_rsr("MSP")) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __get_MSPLIM() (0U) + #else + #define __get_MSPLIM() (__arm_rsr("MSPLIM")) + #endif + #define __get_PRIMASK() (__arm_rsr("PRIMASK")) + #define __get_PSP() (__arm_rsr("PSP")) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __get_PSPLIM() (0U) + #else + #define __get_PSPLIM() (__arm_rsr("PSPLIM")) + #endif + + #define __get_xPSR() (__arm_rsr("xPSR")) + + #define __set_BASEPRI(VALUE) (__arm_wsr("BASEPRI", (VALUE))) + #define __set_BASEPRI_MAX(VALUE) (__arm_wsr("BASEPRI_MAX", (VALUE))) + #define __set_CONTROL(VALUE) (__arm_wsr("CONTROL", (VALUE))) + #define __set_FAULTMASK(VALUE) (__arm_wsr("FAULTMASK", (VALUE))) + #define __set_MSP(VALUE) (__arm_wsr("MSP", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __set_MSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_MSPLIM(VALUE) (__arm_wsr("MSPLIM", (VALUE))) + #endif + #define __set_PRIMASK(VALUE) (__arm_wsr("PRIMASK", (VALUE))) + #define __set_PSP(VALUE) (__arm_wsr("PSP", (VALUE))) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __set_PSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_PSPLIM(VALUE) (__arm_wsr("PSPLIM", (VALUE))) + #endif + + #define __TZ_get_CONTROL_NS() (__arm_rsr("CONTROL_NS")) + #define __TZ_set_CONTROL_NS(VALUE) (__arm_wsr("CONTROL_NS", (VALUE))) + #define __TZ_get_PSP_NS() (__arm_rsr("PSP_NS")) + #define __TZ_set_PSP_NS(VALUE) (__arm_wsr("PSP_NS", (VALUE))) + #define __TZ_get_MSP_NS() (__arm_rsr("MSP_NS")) + #define __TZ_set_MSP_NS(VALUE) (__arm_wsr("MSP_NS", (VALUE))) + #define __TZ_get_SP_NS() (__arm_rsr("SP_NS")) + #define __TZ_set_SP_NS(VALUE) (__arm_wsr("SP_NS", (VALUE))) + #define __TZ_get_PRIMASK_NS() (__arm_rsr("PRIMASK_NS")) + #define __TZ_set_PRIMASK_NS(VALUE) (__arm_wsr("PRIMASK_NS", (VALUE))) + #define __TZ_get_BASEPRI_NS() (__arm_rsr("BASEPRI_NS")) + #define __TZ_set_BASEPRI_NS(VALUE) (__arm_wsr("BASEPRI_NS", (VALUE))) + #define __TZ_get_FAULTMASK_NS() (__arm_rsr("FAULTMASK_NS")) + #define __TZ_set_FAULTMASK_NS(VALUE)(__arm_wsr("FAULTMASK_NS", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __TZ_get_PSPLIM_NS() (0U) + #define __TZ_set_PSPLIM_NS(VALUE) ((void)(VALUE)) + #else + #define __TZ_get_PSPLIM_NS() (__arm_rsr("PSPLIM_NS")) + #define __TZ_set_PSPLIM_NS(VALUE) (__arm_wsr("PSPLIM_NS", (VALUE))) + #endif + + #define __TZ_get_MSPLIM_NS() (__arm_rsr("MSPLIM_NS")) + #define __TZ_set_MSPLIM_NS(VALUE) (__arm_wsr("MSPLIM_NS", (VALUE))) + + #define __NOP __iar_builtin_no_operation + + #define __CLZ __iar_builtin_CLZ + #define __CLREX __iar_builtin_CLREX + + #define __DMB __iar_builtin_DMB + #define __DSB __iar_builtin_DSB + #define __ISB __iar_builtin_ISB + + #define __LDREXB __iar_builtin_LDREXB + #define __LDREXH __iar_builtin_LDREXH + #define __LDREXW __iar_builtin_LDREX + + #define __RBIT __iar_builtin_RBIT + #define __REV __iar_builtin_REV + #define __REV16 __iar_builtin_REV16 + + __IAR_FT int16_t __REVSH(int16_t val) + { + return (int16_t) __iar_builtin_REVSH(val); + } + + #define __ROR __iar_builtin_ROR + #define __RRX __iar_builtin_RRX + + #define __SEV __iar_builtin_SEV + + #if !__IAR_M0_FAMILY + #define __SSAT __iar_builtin_SSAT + #endif + + #define __STREXB __iar_builtin_STREXB + #define __STREXH __iar_builtin_STREXH + #define __STREXW __iar_builtin_STREX + + #if !__IAR_M0_FAMILY + #define __USAT __iar_builtin_USAT + #endif + + #define __WFE __iar_builtin_WFE + #define __WFI __iar_builtin_WFI + + #if __ARM_MEDIA__ + #define __SADD8 __iar_builtin_SADD8 + #define __QADD8 __iar_builtin_QADD8 + #define __SHADD8 __iar_builtin_SHADD8 + #define __UADD8 __iar_builtin_UADD8 + #define __UQADD8 __iar_builtin_UQADD8 + #define __UHADD8 __iar_builtin_UHADD8 + #define __SSUB8 __iar_builtin_SSUB8 + #define __QSUB8 __iar_builtin_QSUB8 + #define __SHSUB8 __iar_builtin_SHSUB8 + #define __USUB8 __iar_builtin_USUB8 + #define __UQSUB8 __iar_builtin_UQSUB8 + #define __UHSUB8 __iar_builtin_UHSUB8 + #define __SADD16 __iar_builtin_SADD16 + #define __QADD16 __iar_builtin_QADD16 + #define __SHADD16 __iar_builtin_SHADD16 + #define __UADD16 __iar_builtin_UADD16 + #define __UQADD16 __iar_builtin_UQADD16 + #define __UHADD16 __iar_builtin_UHADD16 + #define __SSUB16 __iar_builtin_SSUB16 + #define __QSUB16 __iar_builtin_QSUB16 + #define __SHSUB16 __iar_builtin_SHSUB16 + #define __USUB16 __iar_builtin_USUB16 + #define __UQSUB16 __iar_builtin_UQSUB16 + #define __UHSUB16 __iar_builtin_UHSUB16 + #define __SASX __iar_builtin_SASX + #define __QASX __iar_builtin_QASX + #define __SHASX __iar_builtin_SHASX + #define __UASX __iar_builtin_UASX + #define __UQASX __iar_builtin_UQASX + #define __UHASX __iar_builtin_UHASX + #define __SSAX __iar_builtin_SSAX + #define __QSAX __iar_builtin_QSAX + #define __SHSAX __iar_builtin_SHSAX + #define __USAX __iar_builtin_USAX + #define __UQSAX __iar_builtin_UQSAX + #define __UHSAX __iar_builtin_UHSAX + #define __USAD8 __iar_builtin_USAD8 + #define __USADA8 __iar_builtin_USADA8 + #define __SSAT16 __iar_builtin_SSAT16 + #define __USAT16 __iar_builtin_USAT16 + #define __UXTB16 __iar_builtin_UXTB16 + #define __UXTAB16 __iar_builtin_UXTAB16 + #define __SXTB16 __iar_builtin_SXTB16 + #define __SXTAB16 __iar_builtin_SXTAB16 + #define __SMUAD __iar_builtin_SMUAD + #define __SMUADX __iar_builtin_SMUADX + #define __SMMLA __iar_builtin_SMMLA + #define __SMLAD __iar_builtin_SMLAD + #define __SMLADX __iar_builtin_SMLADX + #define __SMLALD __iar_builtin_SMLALD + #define __SMLALDX __iar_builtin_SMLALDX + #define __SMUSD __iar_builtin_SMUSD + #define __SMUSDX __iar_builtin_SMUSDX + #define __SMLSD __iar_builtin_SMLSD + #define __SMLSDX __iar_builtin_SMLSDX + #define __SMLSLD __iar_builtin_SMLSLD + #define __SMLSLDX __iar_builtin_SMLSLDX + #define __SEL __iar_builtin_SEL + #define __QADD __iar_builtin_QADD + #define __QSUB __iar_builtin_QSUB + #define __PKHBT __iar_builtin_PKHBT + #define __PKHTB __iar_builtin_PKHTB + #endif + +#else /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #define __CLZ __cmsis_iar_clz_not_active + #define __SSAT __cmsis_iar_ssat_not_active + #define __USAT __cmsis_iar_usat_not_active + #define __RBIT __cmsis_iar_rbit_not_active + #define __get_APSR __cmsis_iar_get_APSR_not_active + #endif + + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #define __get_FPSCR __cmsis_iar_get_FPSR_not_active + #define __set_FPSCR __cmsis_iar_set_FPSR_not_active + #endif + + #ifdef __INTRINSICS_INCLUDED + #error intrinsics.h is already included previously! + #endif + + #include + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #undef __CLZ + #undef __SSAT + #undef __USAT + #undef __RBIT + #undef __get_APSR + + __STATIC_INLINE uint8_t __CLZ(uint32_t data) + { + if (data == 0U) { return 32U; } + + uint32_t count = 0U; + uint32_t mask = 0x80000000U; + + while ((data & mask) == 0U) + { + count += 1U; + mask = mask >> 1U; + } + return count; + } + + __STATIC_INLINE uint32_t __RBIT(uint32_t v) + { + uint8_t sc = 31U; + uint32_t r = v; + for (v >>= 1U; v; v >>= 1U) + { + r <<= 1U; + r |= v & 1U; + sc--; + } + return (r << sc); + } + + __STATIC_INLINE uint32_t __get_APSR(void) + { + uint32_t res; + __asm("MRS %0,APSR" : "=r" (res)); + return res; + } + + #endif + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #undef __get_FPSCR + #undef __set_FPSCR + #define __get_FPSCR() (0) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #pragma diag_suppress=Pe940 + #pragma diag_suppress=Pe177 + + #define __enable_irq __enable_interrupt + #define __disable_irq __disable_interrupt + #define __NOP __no_operation + + #define __get_xPSR __get_PSR + + #if (!defined(__ARM_ARCH_6M__) || __ARM_ARCH_6M__==0) + + __IAR_FT uint32_t __LDREXW(uint32_t volatile *ptr) + { + return __LDREX((unsigned long *)ptr); + } + + __IAR_FT uint32_t __STREXW(uint32_t value, uint32_t volatile *ptr) + { + return __STREX(value, (unsigned long *)ptr); + } + #endif + + + /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + #if (__CORTEX_M >= 0x03) + + __IAR_FT uint32_t __RRX(uint32_t value) + { + uint32_t result; + __ASM("RRX %0, %1" : "=r"(result) : "r" (value) : "cc"); + return(result); + } + + __IAR_FT void __set_BASEPRI_MAX(uint32_t value) + { + __asm volatile("MSR BASEPRI_MAX,%0"::"r" (value)); + } + + + #define __enable_fault_irq __enable_fiq + #define __disable_fault_irq __disable_fiq + + + #endif /* (__CORTEX_M >= 0x03) */ + + __IAR_FT uint32_t __ROR(uint32_t op1, uint32_t op2) + { + return (op1 >> op2) | (op1 << ((sizeof(op1)*8)-op2)); + } + + #if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + __IAR_FT uint32_t __get_MSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,MSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_MSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR MSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __get_PSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_PSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_CONTROL_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,CONTROL_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_CONTROL_NS(uint32_t value) + { + __asm volatile("MSR CONTROL_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PSP_NS(uint32_t value) + { + __asm volatile("MSR PSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_MSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSP_NS(uint32_t value) + { + __asm volatile("MSR MSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_SP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,SP_NS" : "=r" (res)); + return res; + } + __IAR_FT void __TZ_set_SP_NS(uint32_t value) + { + __asm volatile("MSR SP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PRIMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PRIMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PRIMASK_NS(uint32_t value) + { + __asm volatile("MSR PRIMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_BASEPRI_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,BASEPRI_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_BASEPRI_NS(uint32_t value) + { + __asm volatile("MSR BASEPRI_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_FAULTMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,FAULTMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_FAULTMASK_NS(uint32_t value) + { + __asm volatile("MSR FAULTMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PSPLIM_NS(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM_NS" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __TZ_set_PSPLIM_NS(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM_NS,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_MSPLIM_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSPLIM_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSPLIM_NS(uint32_t value) + { + __asm volatile("MSR MSPLIM_NS,%0" :: "r" (value)); + } + + #endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#endif /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + +#define __BKPT(value) __asm volatile ("BKPT %0" : : "i"(value)) + +#if __IAR_M0_FAMILY + __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) + { + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; + } + + __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) + { + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; + } +#endif + +#if (__CORTEX_M >= 0x03) /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + + __IAR_FT uint8_t __LDRBT(volatile uint8_t *addr) + { + uint32_t res; + __ASM("LDRBT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDRHT(volatile uint16_t *addr) + { + uint32_t res; + __ASM("LDRHT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDRT(volatile uint32_t *addr) + { + uint32_t res; + __ASM("LDRT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return res; + } + + __IAR_FT void __STRBT(uint8_t value, volatile uint8_t *addr) + { + __ASM("STRBT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRHT(uint16_t value, volatile uint16_t *addr) + { + __ASM("STRHT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRT(uint32_t value, volatile uint32_t *addr) + { + __ASM("STRT %1, [%0]" : : "r" (addr), "r" (value) : "memory"); + } + +#endif /* (__CORTEX_M >= 0x03) */ + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + + __IAR_FT uint8_t __LDAB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDA(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDA %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT void __STLB(uint8_t value, volatile uint8_t *ptr) + { + __ASM volatile ("STLB %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STLH(uint16_t value, volatile uint16_t *ptr) + { + __ASM volatile ("STLH %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STL(uint32_t value, volatile uint32_t *ptr) + { + __ASM volatile ("STL %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT uint8_t __LDAEXB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAEXH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDAEX(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEX %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXB %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXH %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEX %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + +#endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#undef __IAR_FT +#undef __IAR_M0_FAMILY +#undef __ICCARM_V8 + +#pragma diag_default=Pe940 +#pragma diag_default=Pe177 + +#endif /* __CMSIS_ICCARM_H__ */ diff --git a/Firmware/ThirdParty/CMSIS/Include/cmsis_version.h b/Firmware/ThirdParty/CMSIS/Include/cmsis_version.h new file mode 100644 index 00000000..660f612a --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/cmsis_version.h @@ -0,0 +1,39 @@ +/**************************************************************************//** + * @file cmsis_version.h + * @brief CMSIS Core(M) Version definitions + * @version V5.0.2 + * @date 19. April 2017 + ******************************************************************************/ +/* + * Copyright (c) 2009-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CMSIS_VERSION_H +#define __CMSIS_VERSION_H + +/* CMSIS Version definitions */ +#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ +#define __CM_CMSIS_VERSION_SUB ( 1U) /*!< [15:0] CMSIS Core(M) sub version */ +#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ + __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ +#endif diff --git a/Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h b/Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h new file mode 100644 index 00000000..251e4ede --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_armv8mbl.h @@ -0,0 +1,1918 @@ +/**************************************************************************//** + * @file core_armv8mbl.h + * @brief CMSIS Armv8-M Baseline Core Peripheral Access Layer Header File + * @version V5.0.7 + * @date 22. June 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_ARMV8MBL_H_GENERIC +#define __CORE_ARMV8MBL_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_ARMv8MBL + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS definitions */ +#define __ARMv8MBL_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __ARMv8MBL_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __ARMv8MBL_CMSIS_VERSION ((__ARMv8MBL_CMSIS_VERSION_MAIN << 16U) | \ + __ARMv8MBL_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M ( 2U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MBL_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_ARMV8MBL_H_DEPENDANT +#define __CORE_ARMV8MBL_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __ARMv8MBL_REV + #define __ARMv8MBL_REV 0x0000U + #warning "__ARMv8MBL_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __VTOR_PRESENT + #define __VTOR_PRESENT 0U + #warning "__VTOR_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif + + #ifndef __ETM_PRESENT + #define __ETM_PRESENT 0U + #warning "__ETM_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MTB_PRESENT + #define __MTB_PRESENT 0U + #warning "__MTB_PRESENT not defined in device header file; using default!" + #endif + +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group ARMv8MBL */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint32_t IPR[124U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ +#else + uint32_t RESERVED0; +#endif + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IOM uint32_t SHPR[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + uint32_t RESERVED0[6U]; + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x3UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Sizes Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Sizes Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[809U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) Software Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) Software Lock Status Register */ + uint32_t RESERVED4[4U]; + __IM uint32_t TYPE; /*!< Offset: 0xFC8 (R/ ) Device Identifier Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_SWOSCALER_Pos 0U /*!< TPI ACPR: SWOSCALER Position */ +#define TPI_ACPR_SWOSCALER_Msk (0xFFFFUL /*<< TPI_ACPR_SWOSCALER_Pos*/) /*!< TPI ACPR: SWOSCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI Periodic Synchronization Control Register Definitions */ +#define TPI_PSCR_PSCount_Pos 0U /*!< TPI PSCR: PSCount Position */ +#define TPI_PSCR_PSCount_Msk (0x1FUL /*<< TPI_PSCR_PSCount_Pos*/) /*!< TPI PSCR: TPSCount Mask */ + +/* TPI Software Lock Status Register Definitions */ +#define TPI_LSR_nTT_Pos 1U /*!< TPI LSR: Not thirty-two bit. Position */ +#define TPI_LSR_nTT_Msk (0x1UL << TPI_LSR_nTT_Pos) /*!< TPI LSR: Not thirty-two bit. Mask */ + +#define TPI_LSR_SLK_Pos 1U /*!< TPI LSR: Software Lock status Position */ +#define TPI_LSR_SLK_Msk (0x1UL << TPI_LSR_SLK_Pos) /*!< TPI LSR: Software Lock status Mask */ + +#define TPI_LSR_SLI_Pos 0U /*!< TPI LSR: Software Lock implemented Position */ +#define TPI_LSR_SLI_Msk (0x1UL /*<< TPI_LSR_SLI_Pos*/) /*!< TPI LSR: Software Lock implemented Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFO depth Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFO depth Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + uint32_t RESERVED0[7U]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 1U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: EN Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: EN Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#endif +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_DWTENA_Pos 24U /*!< CoreDebug DEMCR: DWTENA Position */ +#define CoreDebug_DEMCR_DWTENA_Msk (1UL << CoreDebug_DEMCR_DWTENA_Pos) /*!< CoreDebug DEMCR: DWTENA Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) +#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) +#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) + +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + If VTOR is not present address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC_NS->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB_NS->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB_NS->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB_NS->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MBL_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h b/Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h new file mode 100644 index 00000000..3a3148ea --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_armv8mml.h @@ -0,0 +1,2927 @@ +/**************************************************************************//** + * @file core_armv8mml.h + * @brief CMSIS Armv8-M Mainline Core Peripheral Access Layer Header File + * @version V5.0.7 + * @date 06. July 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_ARMV8MML_H_GENERIC +#define __CORE_ARMV8MML_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_ARMv8MML + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS Armv8MML definitions */ +#define __ARMv8MML_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __ARMv8MML_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __ARMv8MML_CMSIS_VERSION ((__ARMv8MML_CMSIS_VERSION_MAIN << 16U) | \ + __ARMv8MML_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (81U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined(__ARM_FEATURE_DSP) + #if defined(__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MML_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_ARMV8MML_H_DEPENDANT +#define __CORE_ARMV8MML_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __ARMv8MML_REV + #define __ARMv8MML_REV 0x0000U + #warning "__ARMv8MML_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __DSP_PRESENT + #define __DSP_PRESENT 0U + #warning "__DSP_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group ARMv8MML */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ +#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t FPCA:1; /*!< bit: 2 Floating-point context active */ + uint32_t SFPA:1; /*!< bit: 3 Secure floating-point active */ + uint32_t _reserved1:28; /*!< bit: 4..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SFPA_Pos 3U /*!< CONTROL: SFPA Position */ +#define CONTROL_SFPA_Msk (1UL << CONTROL_SFPA_Pos) /*!< CONTROL: SFPA Mask */ + +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint8_t IPR[496U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED6[580U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHPR[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t ID_PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t ID_DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ID_ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t ID_MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ID_ISAR[6U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + __IM uint32_t CLIDR; /*!< Offset: 0x078 (R/ ) Cache Level ID register */ + __IM uint32_t CTR; /*!< Offset: 0x07C (R/ ) Cache Type register */ + __IM uint32_t CCSIDR; /*!< Offset: 0x080 (R/ ) Cache Size ID Register */ + __IOM uint32_t CSSELR; /*!< Offset: 0x084 (R/W) Cache Size Selection Register */ + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ + __IOM uint32_t NSACR; /*!< Offset: 0x08C (R/W) Non-Secure Access Control Register */ + uint32_t RESERVED3[92U]; + __OM uint32_t STIR; /*!< Offset: 0x200 ( /W) Software Triggered Interrupt Register */ + uint32_t RESERVED4[15U]; + __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ + uint32_t RESERVED5[1U]; + __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ + uint32_t RESERVED6[1U]; + __OM uint32_t ICIMVAU; /*!< Offset: 0x258 ( /W) I-Cache Invalidate by MVA to PoU */ + __OM uint32_t DCIMVAC; /*!< Offset: 0x25C ( /W) D-Cache Invalidate by MVA to PoC */ + __OM uint32_t DCISW; /*!< Offset: 0x260 ( /W) D-Cache Invalidate by Set-way */ + __OM uint32_t DCCMVAU; /*!< Offset: 0x264 ( /W) D-Cache Clean by MVA to PoU */ + __OM uint32_t DCCMVAC; /*!< Offset: 0x268 ( /W) D-Cache Clean by MVA to PoC */ + __OM uint32_t DCCSW; /*!< Offset: 0x26C ( /W) D-Cache Clean by Set-way */ + __OM uint32_t DCCIMVAC; /*!< Offset: 0x270 ( /W) D-Cache Clean and Invalidate by MVA to PoC */ + __OM uint32_t DCCISW; /*!< Offset: 0x274 ( /W) D-Cache Clean and Invalidate by Set-way */ + uint32_t RESERVED7[6U]; + __IOM uint32_t ITCMCR; /*!< Offset: 0x290 (R/W) Instruction Tightly-Coupled Memory Control Register */ + __IOM uint32_t DTCMCR; /*!< Offset: 0x294 (R/W) Data Tightly-Coupled Memory Control Registers */ + __IOM uint32_t AHBPCR; /*!< Offset: 0x298 (R/W) AHBP Control Register */ + __IOM uint32_t CACR; /*!< Offset: 0x29C (R/W) L1 Cache Control Register */ + __IOM uint32_t AHBSCR; /*!< Offset: 0x2A0 (R/W) AHB Slave Control Register */ + uint32_t RESERVED8[1U]; + __IOM uint32_t ABFSR; /*!< Offset: 0x2A8 (R/W) Auxiliary Bus Fault Status Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTPENDED_Pos 20U /*!< SCB SHCSR: SECUREFAULTPENDED Position */ +#define SCB_SHCSR_SECUREFAULTPENDED_Msk (1UL << SCB_SHCSR_SECUREFAULTPENDED_Pos) /*!< SCB SHCSR: SECUREFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTENA_Pos 19U /*!< SCB SHCSR: SECUREFAULTENA Position */ +#define SCB_SHCSR_SECUREFAULTENA_Msk (1UL << SCB_SHCSR_SECUREFAULTENA_Pos) /*!< SCB SHCSR: SECUREFAULTENA Mask */ + +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_SECUREFAULTACT_Pos 4U /*!< SCB SHCSR: SECUREFAULTACT Position */ +#define SCB_SHCSR_SECUREFAULTACT_Msk (1UL << SCB_SHCSR_SECUREFAULTACT_Pos) /*!< SCB SHCSR: SECUREFAULTACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_STKOF_Pos (SCB_CFSR_USGFAULTSR_Pos + 4U) /*!< SCB CFSR (UFSR): STKOF Position */ +#define SCB_CFSR_STKOF_Msk (1UL << SCB_CFSR_STKOF_Pos) /*!< SCB CFSR (UFSR): STKOF Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/* SCB Non-Secure Access Control Register Definitions */ +#define SCB_NSACR_CP11_Pos 11U /*!< SCB NSACR: CP11 Position */ +#define SCB_NSACR_CP11_Msk (1UL << SCB_NSACR_CP11_Pos) /*!< SCB NSACR: CP11 Mask */ + +#define SCB_NSACR_CP10_Pos 10U /*!< SCB NSACR: CP10 Position */ +#define SCB_NSACR_CP10_Msk (1UL << SCB_NSACR_CP10_Pos) /*!< SCB NSACR: CP10 Mask */ + +#define SCB_NSACR_CPn_Pos 0U /*!< SCB NSACR: CPn Position */ +#define SCB_NSACR_CPn_Msk (1UL /*<< SCB_NSACR_CPn_Pos*/) /*!< SCB NSACR: CPn Mask */ + +/* SCB Cache Level ID Register Definitions */ +#define SCB_CLIDR_LOUU_Pos 27U /*!< SCB CLIDR: LoUU Position */ +#define SCB_CLIDR_LOUU_Msk (7UL << SCB_CLIDR_LOUU_Pos) /*!< SCB CLIDR: LoUU Mask */ + +#define SCB_CLIDR_LOC_Pos 24U /*!< SCB CLIDR: LoC Position */ +#define SCB_CLIDR_LOC_Msk (7UL << SCB_CLIDR_LOC_Pos) /*!< SCB CLIDR: LoC Mask */ + +/* SCB Cache Type Register Definitions */ +#define SCB_CTR_FORMAT_Pos 29U /*!< SCB CTR: Format Position */ +#define SCB_CTR_FORMAT_Msk (7UL << SCB_CTR_FORMAT_Pos) /*!< SCB CTR: Format Mask */ + +#define SCB_CTR_CWG_Pos 24U /*!< SCB CTR: CWG Position */ +#define SCB_CTR_CWG_Msk (0xFUL << SCB_CTR_CWG_Pos) /*!< SCB CTR: CWG Mask */ + +#define SCB_CTR_ERG_Pos 20U /*!< SCB CTR: ERG Position */ +#define SCB_CTR_ERG_Msk (0xFUL << SCB_CTR_ERG_Pos) /*!< SCB CTR: ERG Mask */ + +#define SCB_CTR_DMINLINE_Pos 16U /*!< SCB CTR: DminLine Position */ +#define SCB_CTR_DMINLINE_Msk (0xFUL << SCB_CTR_DMINLINE_Pos) /*!< SCB CTR: DminLine Mask */ + +#define SCB_CTR_IMINLINE_Pos 0U /*!< SCB CTR: ImInLine Position */ +#define SCB_CTR_IMINLINE_Msk (0xFUL /*<< SCB_CTR_IMINLINE_Pos*/) /*!< SCB CTR: ImInLine Mask */ + +/* SCB Cache Size ID Register Definitions */ +#define SCB_CCSIDR_WT_Pos 31U /*!< SCB CCSIDR: WT Position */ +#define SCB_CCSIDR_WT_Msk (1UL << SCB_CCSIDR_WT_Pos) /*!< SCB CCSIDR: WT Mask */ + +#define SCB_CCSIDR_WB_Pos 30U /*!< SCB CCSIDR: WB Position */ +#define SCB_CCSIDR_WB_Msk (1UL << SCB_CCSIDR_WB_Pos) /*!< SCB CCSIDR: WB Mask */ + +#define SCB_CCSIDR_RA_Pos 29U /*!< SCB CCSIDR: RA Position */ +#define SCB_CCSIDR_RA_Msk (1UL << SCB_CCSIDR_RA_Pos) /*!< SCB CCSIDR: RA Mask */ + +#define SCB_CCSIDR_WA_Pos 28U /*!< SCB CCSIDR: WA Position */ +#define SCB_CCSIDR_WA_Msk (1UL << SCB_CCSIDR_WA_Pos) /*!< SCB CCSIDR: WA Mask */ + +#define SCB_CCSIDR_NUMSETS_Pos 13U /*!< SCB CCSIDR: NumSets Position */ +#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos) /*!< SCB CCSIDR: NumSets Mask */ + +#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U /*!< SCB CCSIDR: Associativity Position */ +#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos) /*!< SCB CCSIDR: Associativity Mask */ + +#define SCB_CCSIDR_LINESIZE_Pos 0U /*!< SCB CCSIDR: LineSize Position */ +#define SCB_CCSIDR_LINESIZE_Msk (7UL /*<< SCB_CCSIDR_LINESIZE_Pos*/) /*!< SCB CCSIDR: LineSize Mask */ + +/* SCB Cache Size Selection Register Definitions */ +#define SCB_CSSELR_LEVEL_Pos 1U /*!< SCB CSSELR: Level Position */ +#define SCB_CSSELR_LEVEL_Msk (7UL << SCB_CSSELR_LEVEL_Pos) /*!< SCB CSSELR: Level Mask */ + +#define SCB_CSSELR_IND_Pos 0U /*!< SCB CSSELR: InD Position */ +#define SCB_CSSELR_IND_Msk (1UL /*<< SCB_CSSELR_IND_Pos*/) /*!< SCB CSSELR: InD Mask */ + +/* SCB Software Triggered Interrupt Register Definitions */ +#define SCB_STIR_INTID_Pos 0U /*!< SCB STIR: INTID Position */ +#define SCB_STIR_INTID_Msk (0x1FFUL /*<< SCB_STIR_INTID_Pos*/) /*!< SCB STIR: INTID Mask */ + +/* SCB D-Cache Invalidate by Set-way Register Definitions */ +#define SCB_DCISW_WAY_Pos 30U /*!< SCB DCISW: Way Position */ +#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos) /*!< SCB DCISW: Way Mask */ + +#define SCB_DCISW_SET_Pos 5U /*!< SCB DCISW: Set Position */ +#define SCB_DCISW_SET_Msk (0x1FFUL << SCB_DCISW_SET_Pos) /*!< SCB DCISW: Set Mask */ + +/* SCB D-Cache Clean by Set-way Register Definitions */ +#define SCB_DCCSW_WAY_Pos 30U /*!< SCB DCCSW: Way Position */ +#define SCB_DCCSW_WAY_Msk (3UL << SCB_DCCSW_WAY_Pos) /*!< SCB DCCSW: Way Mask */ + +#define SCB_DCCSW_SET_Pos 5U /*!< SCB DCCSW: Set Position */ +#define SCB_DCCSW_SET_Msk (0x1FFUL << SCB_DCCSW_SET_Pos) /*!< SCB DCCSW: Set Mask */ + +/* SCB D-Cache Clean and Invalidate by Set-way Register Definitions */ +#define SCB_DCCISW_WAY_Pos 30U /*!< SCB DCCISW: Way Position */ +#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos) /*!< SCB DCCISW: Way Mask */ + +#define SCB_DCCISW_SET_Pos 5U /*!< SCB DCCISW: Set Position */ +#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */ + +/* Instruction Tightly-Coupled Memory Control Register Definitions */ +#define SCB_ITCMCR_SZ_Pos 3U /*!< SCB ITCMCR: SZ Position */ +#define SCB_ITCMCR_SZ_Msk (0xFUL << SCB_ITCMCR_SZ_Pos) /*!< SCB ITCMCR: SZ Mask */ + +#define SCB_ITCMCR_RETEN_Pos 2U /*!< SCB ITCMCR: RETEN Position */ +#define SCB_ITCMCR_RETEN_Msk (1UL << SCB_ITCMCR_RETEN_Pos) /*!< SCB ITCMCR: RETEN Mask */ + +#define SCB_ITCMCR_RMW_Pos 1U /*!< SCB ITCMCR: RMW Position */ +#define SCB_ITCMCR_RMW_Msk (1UL << SCB_ITCMCR_RMW_Pos) /*!< SCB ITCMCR: RMW Mask */ + +#define SCB_ITCMCR_EN_Pos 0U /*!< SCB ITCMCR: EN Position */ +#define SCB_ITCMCR_EN_Msk (1UL /*<< SCB_ITCMCR_EN_Pos*/) /*!< SCB ITCMCR: EN Mask */ + +/* Data Tightly-Coupled Memory Control Register Definitions */ +#define SCB_DTCMCR_SZ_Pos 3U /*!< SCB DTCMCR: SZ Position */ +#define SCB_DTCMCR_SZ_Msk (0xFUL << SCB_DTCMCR_SZ_Pos) /*!< SCB DTCMCR: SZ Mask */ + +#define SCB_DTCMCR_RETEN_Pos 2U /*!< SCB DTCMCR: RETEN Position */ +#define SCB_DTCMCR_RETEN_Msk (1UL << SCB_DTCMCR_RETEN_Pos) /*!< SCB DTCMCR: RETEN Mask */ + +#define SCB_DTCMCR_RMW_Pos 1U /*!< SCB DTCMCR: RMW Position */ +#define SCB_DTCMCR_RMW_Msk (1UL << SCB_DTCMCR_RMW_Pos) /*!< SCB DTCMCR: RMW Mask */ + +#define SCB_DTCMCR_EN_Pos 0U /*!< SCB DTCMCR: EN Position */ +#define SCB_DTCMCR_EN_Msk (1UL /*<< SCB_DTCMCR_EN_Pos*/) /*!< SCB DTCMCR: EN Mask */ + +/* AHBP Control Register Definitions */ +#define SCB_AHBPCR_SZ_Pos 1U /*!< SCB AHBPCR: SZ Position */ +#define SCB_AHBPCR_SZ_Msk (7UL << SCB_AHBPCR_SZ_Pos) /*!< SCB AHBPCR: SZ Mask */ + +#define SCB_AHBPCR_EN_Pos 0U /*!< SCB AHBPCR: EN Position */ +#define SCB_AHBPCR_EN_Msk (1UL /*<< SCB_AHBPCR_EN_Pos*/) /*!< SCB AHBPCR: EN Mask */ + +/* L1 Cache Control Register Definitions */ +#define SCB_CACR_FORCEWT_Pos 2U /*!< SCB CACR: FORCEWT Position */ +#define SCB_CACR_FORCEWT_Msk (1UL << SCB_CACR_FORCEWT_Pos) /*!< SCB CACR: FORCEWT Mask */ + +#define SCB_CACR_ECCEN_Pos 1U /*!< SCB CACR: ECCEN Position */ +#define SCB_CACR_ECCEN_Msk (1UL << SCB_CACR_ECCEN_Pos) /*!< SCB CACR: ECCEN Mask */ + +#define SCB_CACR_SIWT_Pos 0U /*!< SCB CACR: SIWT Position */ +#define SCB_CACR_SIWT_Msk (1UL /*<< SCB_CACR_SIWT_Pos*/) /*!< SCB CACR: SIWT Mask */ + +/* AHBS Control Register Definitions */ +#define SCB_AHBSCR_INITCOUNT_Pos 11U /*!< SCB AHBSCR: INITCOUNT Position */ +#define SCB_AHBSCR_INITCOUNT_Msk (0x1FUL << SCB_AHBPCR_INITCOUNT_Pos) /*!< SCB AHBSCR: INITCOUNT Mask */ + +#define SCB_AHBSCR_TPRI_Pos 2U /*!< SCB AHBSCR: TPRI Position */ +#define SCB_AHBSCR_TPRI_Msk (0x1FFUL << SCB_AHBPCR_TPRI_Pos) /*!< SCB AHBSCR: TPRI Mask */ + +#define SCB_AHBSCR_CTL_Pos 0U /*!< SCB AHBSCR: CTL Position*/ +#define SCB_AHBSCR_CTL_Msk (3UL /*<< SCB_AHBPCR_CTL_Pos*/) /*!< SCB AHBSCR: CTL Mask */ + +/* Auxiliary Bus Fault Status Register Definitions */ +#define SCB_ABFSR_AXIMTYPE_Pos 8U /*!< SCB ABFSR: AXIMTYPE Position*/ +#define SCB_ABFSR_AXIMTYPE_Msk (3UL << SCB_ABFSR_AXIMTYPE_Pos) /*!< SCB ABFSR: AXIMTYPE Mask */ + +#define SCB_ABFSR_EPPB_Pos 4U /*!< SCB ABFSR: EPPB Position*/ +#define SCB_ABFSR_EPPB_Msk (1UL << SCB_ABFSR_EPPB_Pos) /*!< SCB ABFSR: EPPB Mask */ + +#define SCB_ABFSR_AXIM_Pos 3U /*!< SCB ABFSR: AXIM Position*/ +#define SCB_ABFSR_AXIM_Msk (1UL << SCB_ABFSR_AXIM_Pos) /*!< SCB ABFSR: AXIM Mask */ + +#define SCB_ABFSR_AHBP_Pos 2U /*!< SCB ABFSR: AHBP Position*/ +#define SCB_ABFSR_AHBP_Msk (1UL << SCB_ABFSR_AHBP_Pos) /*!< SCB ABFSR: AHBP Mask */ + +#define SCB_ABFSR_DTCM_Pos 1U /*!< SCB ABFSR: DTCM Position*/ +#define SCB_ABFSR_DTCM_Msk (1UL << SCB_ABFSR_DTCM_Pos) /*!< SCB ABFSR: DTCM Mask */ + +#define SCB_ABFSR_ITCM_Pos 0U /*!< SCB ABFSR: ITCM Position*/ +#define SCB_ABFSR_ITCM_Msk (1UL /*<< SCB_ABFSR_ITCM_Pos*/) /*!< SCB ABFSR: ITCM Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ + __IOM uint32_t CPPWR; /*!< Offset: 0x00C (R/W) Coprocessor Power Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29U]; + __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) ITM Device Architecture Register */ + uint32_t RESERVED6[4U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Stimulus Port Register Definitions */ +#define ITM_STIM_DISABLED_Pos 1U /*!< ITM STIM: DISABLED Position */ +#define ITM_STIM_DISABLED_Msk (0x1UL << ITM_STIM_DISABLED_Pos) /*!< ITM STIM: DISABLED Mask */ + +#define ITM_STIM_FIFOREADY_Pos 0U /*!< ITM STIM: FIFOREADY Position */ +#define ITM_STIM_FIFOREADY_Msk (0x1UL /*<< ITM_STIM_FIFOREADY_Pos*/) /*!< ITM STIM: FIFOREADY Mask */ + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TRACEBUSID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TRACEBUSID_Msk (0x7FUL << ITM_TCR_TRACEBUSID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPRESCALE_Pos 8U /*!< ITM TCR: TSPRESCALE Position */ +#define ITM_TCR_TSPRESCALE_Msk (3UL << ITM_TCR_TSPRESCALE_Pos) /*!< ITM TCR: TSPRESCALE Mask */ + +#define ITM_TCR_STALLENA_Pos 5U /*!< ITM TCR: STALLENA Position */ +#define ITM_TCR_STALLENA_Msk (1UL << ITM_TCR_STALLENA_Pos) /*!< ITM TCR: STALLENA Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ + uint32_t RESERVED32[934U]; + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R ) Lock Status Register */ + uint32_t RESERVED33[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) Device Architecture Register */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCDISS_Pos 23U /*!< DWT CTRL: CYCDISS Position */ +#define DWT_CTRL_CYCDISS_Msk (0x1UL << DWT_CTRL_CYCDISS_Pos) /*!< DWT CTRL: CYCDISS Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x1UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Sizes Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Sizes Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[809U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) Software Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) Software Lock Status Register */ + uint32_t RESERVED4[4U]; + __IM uint32_t TYPE; /*!< Offset: 0xFC8 (R/ ) Device Identifier Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_SWOSCALER_Pos 0U /*!< TPI ACPR: SWOSCALER Position */ +#define TPI_ACPR_SWOSCALER_Msk (0xFFFFUL /*<< TPI_ACPR_SWOSCALER_Pos*/) /*!< TPI ACPR: SWOSCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI Periodic Synchronization Control Register Definitions */ +#define TPI_PSCR_PSCount_Pos 0U /*!< TPI PSCR: PSCount Position */ +#define TPI_PSCR_PSCount_Msk (0x1FUL /*<< TPI_PSCR_PSCount_Pos*/) /*!< TPI PSCR: TPSCount Mask */ + +/* TPI Software Lock Status Register Definitions */ +#define TPI_LSR_nTT_Pos 1U /*!< TPI LSR: Not thirty-two bit. Position */ +#define TPI_LSR_nTT_Msk (0x1UL << TPI_LSR_nTT_Pos) /*!< TPI LSR: Not thirty-two bit. Mask */ + +#define TPI_LSR_SLK_Pos 1U /*!< TPI LSR: Software Lock status Position */ +#define TPI_LSR_SLK_Msk (0x1UL << TPI_LSR_SLK_Pos) /*!< TPI LSR: Software Lock status Mask */ + +#define TPI_LSR_SLI_Pos 0U /*!< TPI LSR: Software Lock implemented Position */ +#define TPI_LSR_SLI_Msk (0x1UL /*<< TPI_LSR_SLI_Pos*/) /*!< TPI LSR: Software Lock implemented Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFO depth Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFO depth Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Region Base Address Register Alias 1 */ + __IOM uint32_t RLAR_A1; /*!< Offset: 0x018 (R/W) MPU Region Limit Address Register Alias 1 */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Region Base Address Register Alias 2 */ + __IOM uint32_t RLAR_A2; /*!< Offset: 0x020 (R/W) MPU Region Limit Address Register Alias 2 */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Region Base Address Register Alias 3 */ + __IOM uint32_t RLAR_A3; /*!< Offset: 0x028 (R/W) MPU Region Limit Address Register Alias 3 */ + uint32_t RESERVED0[1]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: Region enable bit Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: Region enable bit Disable Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#else + uint32_t RESERVED0[3]; +#endif + __IOM uint32_t SFSR; /*!< Offset: 0x014 (R/W) Secure Fault Status Register */ + __IOM uint32_t SFAR; /*!< Offset: 0x018 (R/W) Secure Fault Address Register */ +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/* Secure Fault Status Register Definitions */ +#define SAU_SFSR_LSERR_Pos 7U /*!< SAU SFSR: LSERR Position */ +#define SAU_SFSR_LSERR_Msk (1UL << SAU_SFSR_LSERR_Pos) /*!< SAU SFSR: LSERR Mask */ + +#define SAU_SFSR_SFARVALID_Pos 6U /*!< SAU SFSR: SFARVALID Position */ +#define SAU_SFSR_SFARVALID_Msk (1UL << SAU_SFSR_SFARVALID_Pos) /*!< SAU SFSR: SFARVALID Mask */ + +#define SAU_SFSR_LSPERR_Pos 5U /*!< SAU SFSR: LSPERR Position */ +#define SAU_SFSR_LSPERR_Msk (1UL << SAU_SFSR_LSPERR_Pos) /*!< SAU SFSR: LSPERR Mask */ + +#define SAU_SFSR_INVTRAN_Pos 4U /*!< SAU SFSR: INVTRAN Position */ +#define SAU_SFSR_INVTRAN_Msk (1UL << SAU_SFSR_INVTRAN_Pos) /*!< SAU SFSR: INVTRAN Mask */ + +#define SAU_SFSR_AUVIOL_Pos 3U /*!< SAU SFSR: AUVIOL Position */ +#define SAU_SFSR_AUVIOL_Msk (1UL << SAU_SFSR_AUVIOL_Pos) /*!< SAU SFSR: AUVIOL Mask */ + +#define SAU_SFSR_INVER_Pos 2U /*!< SAU SFSR: INVER Position */ +#define SAU_SFSR_INVER_Msk (1UL << SAU_SFSR_INVER_Pos) /*!< SAU SFSR: INVER Mask */ + +#define SAU_SFSR_INVIS_Pos 1U /*!< SAU SFSR: INVIS Position */ +#define SAU_SFSR_INVIS_Msk (1UL << SAU_SFSR_INVIS_Pos) /*!< SAU SFSR: INVIS Mask */ + +#define SAU_SFSR_INVEP_Pos 0U /*!< SAU SFSR: INVEP Position */ +#define SAU_SFSR_INVEP_Msk (1UL /*<< SAU_SFSR_INVEP_Pos*/) /*!< SAU SFSR: INVEP Mask */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_LSPENS_Pos 29U /*!< FPCCR: LSPENS Position */ +#define FPU_FPCCR_LSPENS_Msk (1UL << FPU_FPCCR_LSPENS_Pos) /*!< FPCCR: LSPENS bit Mask */ + +#define FPU_FPCCR_CLRONRET_Pos 28U /*!< FPCCR: CLRONRET Position */ +#define FPU_FPCCR_CLRONRET_Msk (1UL << FPU_FPCCR_CLRONRET_Pos) /*!< FPCCR: CLRONRET bit Mask */ + +#define FPU_FPCCR_CLRONRETS_Pos 27U /*!< FPCCR: CLRONRETS Position */ +#define FPU_FPCCR_CLRONRETS_Msk (1UL << FPU_FPCCR_CLRONRETS_Pos) /*!< FPCCR: CLRONRETS bit Mask */ + +#define FPU_FPCCR_TS_Pos 26U /*!< FPCCR: TS Position */ +#define FPU_FPCCR_TS_Msk (1UL << FPU_FPCCR_TS_Pos) /*!< FPCCR: TS bit Mask */ + +#define FPU_FPCCR_UFRDY_Pos 10U /*!< FPCCR: UFRDY Position */ +#define FPU_FPCCR_UFRDY_Msk (1UL << FPU_FPCCR_UFRDY_Pos) /*!< FPCCR: UFRDY bit Mask */ + +#define FPU_FPCCR_SPLIMVIOL_Pos 9U /*!< FPCCR: SPLIMVIOL Position */ +#define FPU_FPCCR_SPLIMVIOL_Msk (1UL << FPU_FPCCR_SPLIMVIOL_Pos) /*!< FPCCR: SPLIMVIOL bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_SFRDY_Pos 7U /*!< FPCCR: SFRDY Position */ +#define FPU_FPCCR_SFRDY_Msk (1UL << FPU_FPCCR_SFRDY_Pos) /*!< FPCCR: SFRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_S_Pos 2U /*!< FPCCR: Security status of the FP context bit Position */ +#define FPU_FPCCR_S_Msk (1UL << FPU_FPCCR_S_Pos) /*!< FPCCR: Security status of the FP context bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + #define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCnSCB_NS ((SCnSCB_Type *) SCS_BASE_NS ) /*!< System control Register not in SCB(non-secure address space) */ + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + + #define FPU_BASE_NS (SCS_BASE_NS + 0x0F30UL) /*!< Floating Point Unit (non-secure address space) */ + #define FPU_NS ((FPU_Type *) FPU_BASE_NS ) /*!< Floating Point Unit (non-secure address space) */ + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Set Priority Grouping (non-secure) + \details Sets the non-secure priority grouping field when in secure state using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void TZ_NVIC_SetPriorityGrouping_NS(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB_NS->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + SCB_NS->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping (non-secure) + \details Reads the priority grouping field from the non-secure NVIC when in secure state. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriorityGrouping_NS(void) +{ + return ((uint32_t)((SCB_NS->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC_NS->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) + { + return 2U; /* Double + Single precision FPU */ + } + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_ARMV8MML_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0.h b/Firmware/ThirdParty/CMSIS/Include/core_cm0.h similarity index 71% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm0.h index 711dad55..f929bba0 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm0.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm0.h * @brief CMSIS Cortex-M0 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.5 + * @date 28. May 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS CM0 definitions */ -#define __CM0_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM0_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM0_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM0_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM0_CMSIS_VERSION ((__CM0_CMSIS_VERSION_MAIN << 16U) | \ - __CM0_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __CM0_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_M (0x00U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (0U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -555,18 +507,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -578,7 +530,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M0 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ @@ -614,87 +566,177 @@ typedef struct @{ */ -/* Interrupt Priorities are WORD accessible only under ARMv6M */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M0 */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ /* The following MACROS handle generation of the register offset and byte masks */ #define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) #define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) #define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } } @@ -702,24 +744,108 @@ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) - { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } - else + if ((int32_t)(IRQn) >= 0) { return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } + else + { + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + Address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)0x0U; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)0x0U; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; } @@ -727,7 +853,7 @@ __STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -744,6 +870,31 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + /* ################################## SysTick function ############################################ */ /** @@ -753,7 +904,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0plus.h b/Firmware/ThirdParty/CMSIS/Include/core_cm0plus.h similarity index 74% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0plus.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm0plus.h index b04aa390..424011ac 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm0plus.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm0plus.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm0plus.h * @brief CMSIS Cortex-M0+ Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.6 + * @date 28. May 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS CM0+ definitions */ -#define __CM0PLUS_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM0PLUS_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM0PLUS_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM0PLUS_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM0PLUS_CMSIS_VERSION ((__CM0PLUS_CMSIS_VERSION_MAIN << 16U) | \ - __CM0PLUS_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __CM0PLUS_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_M (0x00U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (0U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -404,7 +356,7 @@ typedef struct { __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ -#if (__VTOR_PRESENT == 1U) +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ #else uint32_t RESERVED0; @@ -461,7 +413,7 @@ typedef struct #define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ #define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ -#if (__VTOR_PRESENT == 1U) +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) /* SCB Interrupt Control State Register Definitions */ #define SCB_VTOR_TBLOFF_Pos 8U /*!< SCB VTOR: TBLOFF Position */ #define SCB_VTOR_TBLOFF_Msk (0xFFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ @@ -558,7 +510,7 @@ typedef struct /*@} end of group CMSIS_SysTick */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -578,6 +530,8 @@ typedef struct __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 1U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -667,18 +621,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -690,7 +644,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M0+ Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ @@ -700,7 +654,7 @@ typedef struct #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -730,87 +684,177 @@ typedef struct @{ */ -/* Interrupt Priorities are WORD accessible only under ARMv6M */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M0+ */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ /* The following MACROS handle generation of the register offset and byte masks */ #define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) #define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) #define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } } @@ -818,24 +862,117 @@ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) - { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } - else + if ((int32_t)(IRQn) >= 0) { return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } + else + { + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + If VTOR is not present address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; + } @@ -843,7 +980,7 @@ __STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -859,6 +996,38 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + /* ################################## SysTick function ############################################ */ @@ -869,7 +1038,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration diff --git a/Firmware/ThirdParty/CMSIS/Include/core_cm1.h b/Firmware/ThirdParty/CMSIS/Include/core_cm1.h new file mode 100644 index 00000000..0ed678e3 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm1.h @@ -0,0 +1,976 @@ +/**************************************************************************//** + * @file core_cm1.h + * @brief CMSIS Cortex-M1 Core Peripheral Access Layer Header File + * @version V1.0.0 + * @date 23. July 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM1_H_GENERIC +#define __CORE_CM1_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M1 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM1 definitions */ +#define __CM1_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM1_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM1_CMSIS_VERSION ((__CM1_CMSIS_VERSION_MAIN << 16U) | \ + __CM1_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (1U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM1_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM1_H_DEPENDANT +#define __CORE_CM1_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM1_REV + #define __CM1_REV 0x0100U + #warning "__CM1_REV not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M1 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t _reserved0:1; /*!< bit: 0 Reserved */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[1U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[31U]; + __IOM uint32_t ICER[1U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[31U]; + __IOM uint32_t ISPR[1U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[31U]; + __IOM uint32_t ICPR[1U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[31U]; + uint32_t RESERVED4[64U]; + __IOM uint32_t IP[8U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + uint32_t RESERVED0; + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IOM uint32_t SHP[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_ITCMUAEN_Pos 4U /*!< ACTLR: Instruction TCM Upper Alias Enable Position */ +#define SCnSCB_ACTLR_ITCMUAEN_Msk (1UL << SCnSCB_ACTLR_ITCMUAEN_Pos) /*!< ACTLR: Instruction TCM Upper Alias Enable Mask */ + +#define SCnSCB_ACTLR_ITCMLAEN_Pos 3U /*!< ACTLR: Instruction TCM Lower Alias Enable Position */ +#define SCnSCB_ACTLR_ITCMLAEN_Msk (1UL << SCnSCB_ACTLR_ITCMLAEN_Pos) /*!< ACTLR: Instruction TCM Lower Alias Enable Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Cortex-M1 Core Debug Registers (DCB registers, SHCSR, and DFSR) are only accessible over DAP and not via processor. + Therefore they are not covered by the Cortex-M1 header file. + @{ + */ +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for Cortex-M1 */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) +#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) +#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) + +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + Address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)0x0U; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)0x0U; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM1_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/ThirdParty/CMSIS/Include/core_cm23.h b/Firmware/ThirdParty/CMSIS/Include/core_cm23.h new file mode 100644 index 00000000..acbc5dfe --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm23.h @@ -0,0 +1,1993 @@ +/**************************************************************************//** + * @file core_cm23.h + * @brief CMSIS Cortex-M23 Core Peripheral Access Layer Header File + * @version V5.0.7 + * @date 22. June 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM23_H_GENERIC +#define __CORE_CM23_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M23 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS definitions */ +#define __CM23_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM23_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM23_CMSIS_VERSION ((__CM23_CMSIS_VERSION_MAIN << 16U) | \ + __CM23_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (23U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_PCS_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM23_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM23_H_DEPENDANT +#define __CORE_CM23_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM23_REV + #define __CM23_REV 0x0000U + #warning "__CM23_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __VTOR_PRESENT + #define __VTOR_PRESENT 0U + #warning "__VTOR_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 2U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif + + #ifndef __ETM_PRESENT + #define __ETM_PRESENT 0U + #warning "__ETM_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MTB_PRESENT + #define __MTB_PRESENT 0U + #warning "__MTB_PRESENT not defined in device header file; using default!" + #endif + +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M23 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:28; /*!< bit: 0..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t _reserved1:3; /*!< bit: 25..27 Reserved */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint32_t IPR[124U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register */ +} NVIC_Type; + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ +#else + uint32_t RESERVED0; +#endif + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + uint32_t RESERVED1; + __IOM uint32_t SHPR[2U]; /*!< Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + uint32_t RESERVED0[6U]; + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x3UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t ITFTTD0; /*!< Offset: 0xEEC (R/ ) Integration Test FIFO Test Data 0 Register */ + __IOM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/W) Integration Test ATB Control Register 2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) Integration Test ATB Control Register 0 */ + __IM uint32_t ITFTTD1; /*!< Offset: 0xEFC (R/ ) Integration Test FIFO Test Data 1 Register */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) Device Configuration Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Identifier Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration Test FIFO Test Data 0 Register Definitions */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD0: ATB Interface 2 ATVALIDPosition */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD0: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD0_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD0: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD0_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data2_Pos 16U /*!< TPI ITFTTD0: ATB Interface 1 data2 Position */ +#define TPI_ITFTTD0_ATB_IF1_data2_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data2 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data1_Pos 8U /*!< TPI ITFTTD0: ATB Interface 1 data1 Position */ +#define TPI_ITFTTD0_ATB_IF1_data1_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data1 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data0_Pos 0U /*!< TPI ITFTTD0: ATB Interface 1 data0 Position */ +#define TPI_ITFTTD0_ATB_IF1_data0_Msk (0xFFUL /*<< TPI_ITFTTD0_ATB_IF1_data0_Pos*/) /*!< TPI ITFTTD0: ATB Interface 1 data0 Mask */ + +/* TPI Integration Test ATB Control Register 2 Register Definitions */ +#define TPI_ITATBCTR2_AFVALID2S_Pos 1U /*!< TPI ITATBCTR2: AFVALID2S Position */ +#define TPI_ITATBCTR2_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID2S_Pos) /*!< TPI ITATBCTR2: AFVALID2SS Mask */ + +#define TPI_ITATBCTR2_AFVALID1S_Pos 1U /*!< TPI ITATBCTR2: AFVALID1S Position */ +#define TPI_ITATBCTR2_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID1S_Pos) /*!< TPI ITATBCTR2: AFVALID1SS Mask */ + +#define TPI_ITATBCTR2_ATREADY2S_Pos 0U /*!< TPI ITATBCTR2: ATREADY2S Position */ +#define TPI_ITATBCTR2_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2S_Pos*/) /*!< TPI ITATBCTR2: ATREADY2S Mask */ + +#define TPI_ITATBCTR2_ATREADY1S_Pos 0U /*!< TPI ITATBCTR2: ATREADY1S Position */ +#define TPI_ITATBCTR2_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1S_Pos*/) /*!< TPI ITATBCTR2: ATREADY1S Mask */ + +/* TPI Integration Test FIFO Test Data 1 Register Definitions */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD1: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD1_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD1: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD1_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data2_Pos 16U /*!< TPI ITFTTD1: ATB Interface 2 data2 Position */ +#define TPI_ITFTTD1_ATB_IF2_data2_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data2 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data1_Pos 8U /*!< TPI ITFTTD1: ATB Interface 2 data1 Position */ +#define TPI_ITFTTD1_ATB_IF2_data1_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data1 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data0_Pos 0U /*!< TPI ITFTTD1: ATB Interface 2 data0 Position */ +#define TPI_ITFTTD1_ATB_IF2_data0_Msk (0xFFUL /*<< TPI_ITFTTD1_ATB_IF2_data0_Pos*/) /*!< TPI ITFTTD1: ATB Interface 2 data0 Mask */ + +/* TPI Integration Test ATB Control Register 0 Definitions */ +#define TPI_ITATBCTR0_AFVALID2S_Pos 1U /*!< TPI ITATBCTR0: AFVALID2S Position */ +#define TPI_ITATBCTR0_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID2S_Pos) /*!< TPI ITATBCTR0: AFVALID2SS Mask */ + +#define TPI_ITATBCTR0_AFVALID1S_Pos 1U /*!< TPI ITATBCTR0: AFVALID1S Position */ +#define TPI_ITATBCTR0_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID1S_Pos) /*!< TPI ITATBCTR0: AFVALID1SS Mask */ + +#define TPI_ITATBCTR0_ATREADY2S_Pos 0U /*!< TPI ITATBCTR0: ATREADY2S Position */ +#define TPI_ITATBCTR0_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2S_Pos*/) /*!< TPI ITATBCTR0: ATREADY2S Mask */ + +#define TPI_ITATBCTR0_ATREADY1S_Pos 0U /*!< TPI ITATBCTR0: ATREADY1S Position */ +#define TPI_ITATBCTR0_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1S_Pos*/) /*!< TPI ITATBCTR0: ATREADY1S Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFOSZ Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFOSZ Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x3FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + uint32_t RESERVED0[7U]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 1U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: EN Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: EN Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#endif +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_DWTENA_Pos 24U /*!< CoreDebug DEMCR: DWTENA Position */ +#define CoreDebug_DEMCR_DWTENA_Msk (1UL << CoreDebug_DEMCR_DWTENA_Pos) /*!< CoreDebug DEMCR: DWTENA Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else +/*#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping not available for Cortex-M23 */ +/*#define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping not available for Cortex-M23 */ + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ +/* The following MACROS handle generation of the register offset and byte masks */ +#define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) +#define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) +#define _IP_IDX(IRQn) ( (((uint32_t)(int32_t)(IRQn)) >> 2UL) ) + +#define __NVIC_SetPriorityGrouping(X) (void)(X) +#define __NVIC_GetPriorityGrouping() (0U) + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + If VTOR is not present address 0 must be mapped to SRAM. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ +#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) + uint32_t *vectors = (uint32_t *)SCB->VTOR; +#else + uint32_t *vectors = (uint32_t *)0x0U; +#endif + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + SCB_AIRCR_SYSRESETREQ_Msk); + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[_IP_IDX(IRQn)] = ((uint32_t)(NVIC_NS->IPR[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } + else + { + SCB_NS->SHPR[_SHP_IDX(IRQn)] = ((uint32_t)(SCB_NS->SHPR[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IPR[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return((uint32_t)(((SCB_NS->SHPR[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM23_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm3.h b/Firmware/ThirdParty/CMSIS/Include/core_cm3.h similarity index 84% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm3.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm3.h index b4ac4c7b..74bff64b 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm3.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm3.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm3.h * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.8 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS CM3 definitions */ -#define __CM3_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM3_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16U) | \ - __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __CM3_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_M (0x03U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (3U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -191,7 +143,7 @@ #endif #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U + #define __NVIC_PRIO_BITS 3U #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" #endif @@ -308,9 +260,11 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t _reserved1:8; /*!< bit: 16..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -336,12 +290,15 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -487,7 +444,7 @@ typedef struct #define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ /* SCB Vector Table Offset Register Definitions */ -#if (__CM3_REV < 0x0201U) /* core r2p1 */ +#if defined (__CM3_REV) && (__CM3_REV < 0x0201U) /* core r2p1 */ #define SCB_VTOR_TBLBASE_Pos 29U /*!< SCB VTOR: TBLBASE Position */ #define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ @@ -602,6 +559,60 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -645,7 +656,7 @@ typedef struct { uint32_t RESERVED0[1U]; __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ -#if ((defined __CM3_REV) && (__CM3_REV >= 0x200U)) +#if defined (__CM3_REV) && (__CM3_REV >= 0x200U) __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ #else uint32_t RESERVED1[1U]; @@ -770,7 +781,7 @@ typedef struct /* ITM Trace Privilege Register Definitions */ #define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ /* ITM Trace Control Register Definitions */ #define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ @@ -984,7 +995,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -995,7 +1006,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1065,8 +1076,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1091,12 +1105,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1118,16 +1135,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1153,6 +1170,8 @@ typedef struct __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 4U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -1337,18 +1356,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1360,7 +1379,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M3 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1379,7 +1398,7 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -1410,6 +1429,45 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1419,7 +1477,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1428,7 +1486,7 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ SCB->AIRCR = reg_value; } @@ -1438,121 +1496,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1609,11 +1724,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -1630,6 +1776,38 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + /* ################################## SysTick function ############################################ */ @@ -1640,7 +1818,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -1683,8 +1861,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/ThirdParty/CMSIS/Include/core_cm33.h b/Firmware/ThirdParty/CMSIS/Include/core_cm33.h new file mode 100644 index 00000000..6cd2db77 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm33.h @@ -0,0 +1,3002 @@ +/**************************************************************************//** + * @file core_cm33.h + * @brief CMSIS Cortex-M33 Core Peripheral Access Layer Header File + * @version V5.0.9 + * @date 06. July 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM33_H_GENERIC +#define __CORE_CM33_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M33 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM33 definitions */ +#define __CM33_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM33_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM33_CMSIS_VERSION ((__CM33_CMSIS_VERSION_MAIN << 16U) | \ + __CM33_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (33U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined (__TARGET_FPU_VFP) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined (__ARM_PCS_VFP) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined (__ARMVFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + + #if defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1U) + #if defined (__DSP_PRESENT) && (__DSP_PRESENT == 1U) + #define __DSP_USED 1U + #else + #error "Compiler generates DSP (SIMD) instructions for a devices without DSP extensions (check __DSP_PRESENT)" + #define __DSP_USED 0U + #endif + #else + #define __DSP_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined (__TI_VFP_SUPPORT__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined (__FPU_VFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM33_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM33_H_DEPENDANT +#define __CORE_CM33_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM33_REV + #define __CM33_REV 0x0000U + #warning "__CM33_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __SAUREGION_PRESENT + #define __SAUREGION_PRESENT 0U + #warning "__SAUREGION_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __DSP_PRESENT + #define __DSP_PRESENT 0U + #warning "__DSP_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M33 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core SAU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ +#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack-pointer select */ + uint32_t FPCA:1; /*!< bit: 2 Floating-point context active */ + uint32_t SFPA:1; /*!< bit: 3 Secure floating-point active */ + uint32_t _reserved1:28; /*!< bit: 4..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SFPA_Pos 3U /*!< CONTROL: SFPA Position */ +#define CONTROL_SFPA_Msk (1UL << CONTROL_SFPA_Pos) /*!< CONTROL: SFPA Mask */ + +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[16U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[16U]; + __IOM uint32_t ICER[16U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[16U]; + __IOM uint32_t ISPR[16U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[16U]; + __IOM uint32_t ICPR[16U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[16U]; + __IOM uint32_t IABR[16U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[16U]; + __IOM uint32_t ITNS[16U]; /*!< Offset: 0x280 (R/W) Interrupt Non-Secure State Register */ + uint32_t RESERVED5[16U]; + __IOM uint8_t IPR[496U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED6[580U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHPR[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t ID_PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t ID_DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ID_ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t ID_MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ID_ISAR[6U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + __IM uint32_t CLIDR; /*!< Offset: 0x078 (R/ ) Cache Level ID register */ + __IM uint32_t CTR; /*!< Offset: 0x07C (R/ ) Cache Type register */ + __IM uint32_t CCSIDR; /*!< Offset: 0x080 (R/ ) Cache Size ID Register */ + __IOM uint32_t CSSELR; /*!< Offset: 0x084 (R/W) Cache Size Selection Register */ + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ + __IOM uint32_t NSACR; /*!< Offset: 0x08C (R/W) Non-Secure Access Control Register */ + uint32_t RESERVED3[92U]; + __OM uint32_t STIR; /*!< Offset: 0x200 ( /W) Software Triggered Interrupt Register */ + uint32_t RESERVED4[15U]; + __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ + uint32_t RESERVED5[1U]; + __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ + uint32_t RESERVED6[1U]; + __OM uint32_t ICIMVAU; /*!< Offset: 0x258 ( /W) I-Cache Invalidate by MVA to PoU */ + __OM uint32_t DCIMVAC; /*!< Offset: 0x25C ( /W) D-Cache Invalidate by MVA to PoC */ + __OM uint32_t DCISW; /*!< Offset: 0x260 ( /W) D-Cache Invalidate by Set-way */ + __OM uint32_t DCCMVAU; /*!< Offset: 0x264 ( /W) D-Cache Clean by MVA to PoU */ + __OM uint32_t DCCMVAC; /*!< Offset: 0x268 ( /W) D-Cache Clean by MVA to PoC */ + __OM uint32_t DCCSW; /*!< Offset: 0x26C ( /W) D-Cache Clean by Set-way */ + __OM uint32_t DCCIMVAC; /*!< Offset: 0x270 ( /W) D-Cache Clean and Invalidate by MVA to PoC */ + __OM uint32_t DCCISW; /*!< Offset: 0x274 ( /W) D-Cache Clean and Invalidate by Set-way */ + uint32_t RESERVED7[6U]; + __IOM uint32_t ITCMCR; /*!< Offset: 0x290 (R/W) Instruction Tightly-Coupled Memory Control Register */ + __IOM uint32_t DTCMCR; /*!< Offset: 0x294 (R/W) Data Tightly-Coupled Memory Control Registers */ + __IOM uint32_t AHBPCR; /*!< Offset: 0x298 (R/W) AHBP Control Register */ + __IOM uint32_t CACR; /*!< Offset: 0x29C (R/W) L1 Cache Control Register */ + __IOM uint32_t AHBSCR; /*!< Offset: 0x2A0 (R/W) AHB Slave Control Register */ + uint32_t RESERVED8[1U]; + __IOM uint32_t ABFSR; /*!< Offset: 0x2A8 (R/W) Auxiliary Bus Fault Status Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_PENDNMISET_Pos 31U /*!< SCB ICSR: PENDNMISET Position */ +#define SCB_ICSR_PENDNMISET_Msk (1UL << SCB_ICSR_PENDNMISET_Pos) /*!< SCB ICSR: PENDNMISET Mask */ + +#define SCB_ICSR_NMIPENDSET_Pos SCB_ICSR_PENDNMISET_Pos /*!< SCB ICSR: NMIPENDSET Position, backward compatibility */ +#define SCB_ICSR_NMIPENDSET_Msk SCB_ICSR_PENDNMISET_Msk /*!< SCB ICSR: NMIPENDSET Mask, backward compatibility */ + +#define SCB_ICSR_PENDNMICLR_Pos 30U /*!< SCB ICSR: PENDNMICLR Position */ +#define SCB_ICSR_PENDNMICLR_Msk (1UL << SCB_ICSR_PENDNMICLR_Pos) /*!< SCB ICSR: PENDNMICLR Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_STTNS_Pos 24U /*!< SCB ICSR: STTNS Position (Security Extension) */ +#define SCB_ICSR_STTNS_Msk (1UL << SCB_ICSR_STTNS_Pos) /*!< SCB ICSR: STTNS Mask (Security Extension) */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIS_Pos 14U /*!< SCB AIRCR: PRIS Position */ +#define SCB_AIRCR_PRIS_Msk (1UL << SCB_AIRCR_PRIS_Pos) /*!< SCB AIRCR: PRIS Mask */ + +#define SCB_AIRCR_BFHFNMINS_Pos 13U /*!< SCB AIRCR: BFHFNMINS Position */ +#define SCB_AIRCR_BFHFNMINS_Msk (1UL << SCB_AIRCR_BFHFNMINS_Pos) /*!< SCB AIRCR: BFHFNMINS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQS_Pos 3U /*!< SCB AIRCR: SYSRESETREQS Position */ +#define SCB_AIRCR_SYSRESETREQS_Msk (1UL << SCB_AIRCR_SYSRESETREQS_Pos) /*!< SCB AIRCR: SYSRESETREQS Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEPS_Pos 3U /*!< SCB SCR: SLEEPDEEPS Position */ +#define SCB_SCR_SLEEPDEEPS_Msk (1UL << SCB_SCR_SLEEPDEEPS_Pos) /*!< SCB SCR: SLEEPDEEPS Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: BP Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: BP Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: IC Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: IC Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: DC Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: DC Mask */ + +#define SCB_CCR_STKOFHFNMIGN_Pos 10U /*!< SCB CCR: STKOFHFNMIGN Position */ +#define SCB_CCR_STKOFHFNMIGN_Msk (1UL << SCB_CCR_STKOFHFNMIGN_Pos) /*!< SCB CCR: STKOFHFNMIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_HARDFAULTPENDED_Pos 21U /*!< SCB SHCSR: HARDFAULTPENDED Position */ +#define SCB_SHCSR_HARDFAULTPENDED_Msk (1UL << SCB_SHCSR_HARDFAULTPENDED_Pos) /*!< SCB SHCSR: HARDFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTPENDED_Pos 20U /*!< SCB SHCSR: SECUREFAULTPENDED Position */ +#define SCB_SHCSR_SECUREFAULTPENDED_Msk (1UL << SCB_SHCSR_SECUREFAULTPENDED_Pos) /*!< SCB SHCSR: SECUREFAULTPENDED Mask */ + +#define SCB_SHCSR_SECUREFAULTENA_Pos 19U /*!< SCB SHCSR: SECUREFAULTENA Position */ +#define SCB_SHCSR_SECUREFAULTENA_Msk (1UL << SCB_SHCSR_SECUREFAULTENA_Pos) /*!< SCB SHCSR: SECUREFAULTENA Mask */ + +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_NMIACT_Pos 5U /*!< SCB SHCSR: NMIACT Position */ +#define SCB_SHCSR_NMIACT_Msk (1UL << SCB_SHCSR_NMIACT_Pos) /*!< SCB SHCSR: NMIACT Mask */ + +#define SCB_SHCSR_SECUREFAULTACT_Pos 4U /*!< SCB SHCSR: SECUREFAULTACT Position */ +#define SCB_SHCSR_SECUREFAULTACT_Msk (1UL << SCB_SHCSR_SECUREFAULTACT_Pos) /*!< SCB SHCSR: SECUREFAULTACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_HARDFAULTACT_Pos 2U /*!< SCB SHCSR: HARDFAULTACT Position */ +#define SCB_SHCSR_HARDFAULTACT_Msk (1UL << SCB_SHCSR_HARDFAULTACT_Pos) /*!< SCB SHCSR: HARDFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_STKOF_Pos (SCB_CFSR_USGFAULTSR_Pos + 4U) /*!< SCB CFSR (UFSR): STKOF Position */ +#define SCB_CFSR_STKOF_Msk (1UL << SCB_CFSR_STKOF_Pos) /*!< SCB CFSR (UFSR): STKOF Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/* SCB Non-Secure Access Control Register Definitions */ +#define SCB_NSACR_CP11_Pos 11U /*!< SCB NSACR: CP11 Position */ +#define SCB_NSACR_CP11_Msk (1UL << SCB_NSACR_CP11_Pos) /*!< SCB NSACR: CP11 Mask */ + +#define SCB_NSACR_CP10_Pos 10U /*!< SCB NSACR: CP10 Position */ +#define SCB_NSACR_CP10_Msk (1UL << SCB_NSACR_CP10_Pos) /*!< SCB NSACR: CP10 Mask */ + +#define SCB_NSACR_CPn_Pos 0U /*!< SCB NSACR: CPn Position */ +#define SCB_NSACR_CPn_Msk (1UL /*<< SCB_NSACR_CPn_Pos*/) /*!< SCB NSACR: CPn Mask */ + +/* SCB Cache Level ID Register Definitions */ +#define SCB_CLIDR_LOUU_Pos 27U /*!< SCB CLIDR: LoUU Position */ +#define SCB_CLIDR_LOUU_Msk (7UL << SCB_CLIDR_LOUU_Pos) /*!< SCB CLIDR: LoUU Mask */ + +#define SCB_CLIDR_LOC_Pos 24U /*!< SCB CLIDR: LoC Position */ +#define SCB_CLIDR_LOC_Msk (7UL << SCB_CLIDR_LOC_Pos) /*!< SCB CLIDR: LoC Mask */ + +/* SCB Cache Type Register Definitions */ +#define SCB_CTR_FORMAT_Pos 29U /*!< SCB CTR: Format Position */ +#define SCB_CTR_FORMAT_Msk (7UL << SCB_CTR_FORMAT_Pos) /*!< SCB CTR: Format Mask */ + +#define SCB_CTR_CWG_Pos 24U /*!< SCB CTR: CWG Position */ +#define SCB_CTR_CWG_Msk (0xFUL << SCB_CTR_CWG_Pos) /*!< SCB CTR: CWG Mask */ + +#define SCB_CTR_ERG_Pos 20U /*!< SCB CTR: ERG Position */ +#define SCB_CTR_ERG_Msk (0xFUL << SCB_CTR_ERG_Pos) /*!< SCB CTR: ERG Mask */ + +#define SCB_CTR_DMINLINE_Pos 16U /*!< SCB CTR: DminLine Position */ +#define SCB_CTR_DMINLINE_Msk (0xFUL << SCB_CTR_DMINLINE_Pos) /*!< SCB CTR: DminLine Mask */ + +#define SCB_CTR_IMINLINE_Pos 0U /*!< SCB CTR: ImInLine Position */ +#define SCB_CTR_IMINLINE_Msk (0xFUL /*<< SCB_CTR_IMINLINE_Pos*/) /*!< SCB CTR: ImInLine Mask */ + +/* SCB Cache Size ID Register Definitions */ +#define SCB_CCSIDR_WT_Pos 31U /*!< SCB CCSIDR: WT Position */ +#define SCB_CCSIDR_WT_Msk (1UL << SCB_CCSIDR_WT_Pos) /*!< SCB CCSIDR: WT Mask */ + +#define SCB_CCSIDR_WB_Pos 30U /*!< SCB CCSIDR: WB Position */ +#define SCB_CCSIDR_WB_Msk (1UL << SCB_CCSIDR_WB_Pos) /*!< SCB CCSIDR: WB Mask */ + +#define SCB_CCSIDR_RA_Pos 29U /*!< SCB CCSIDR: RA Position */ +#define SCB_CCSIDR_RA_Msk (1UL << SCB_CCSIDR_RA_Pos) /*!< SCB CCSIDR: RA Mask */ + +#define SCB_CCSIDR_WA_Pos 28U /*!< SCB CCSIDR: WA Position */ +#define SCB_CCSIDR_WA_Msk (1UL << SCB_CCSIDR_WA_Pos) /*!< SCB CCSIDR: WA Mask */ + +#define SCB_CCSIDR_NUMSETS_Pos 13U /*!< SCB CCSIDR: NumSets Position */ +#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos) /*!< SCB CCSIDR: NumSets Mask */ + +#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U /*!< SCB CCSIDR: Associativity Position */ +#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos) /*!< SCB CCSIDR: Associativity Mask */ + +#define SCB_CCSIDR_LINESIZE_Pos 0U /*!< SCB CCSIDR: LineSize Position */ +#define SCB_CCSIDR_LINESIZE_Msk (7UL /*<< SCB_CCSIDR_LINESIZE_Pos*/) /*!< SCB CCSIDR: LineSize Mask */ + +/* SCB Cache Size Selection Register Definitions */ +#define SCB_CSSELR_LEVEL_Pos 1U /*!< SCB CSSELR: Level Position */ +#define SCB_CSSELR_LEVEL_Msk (7UL << SCB_CSSELR_LEVEL_Pos) /*!< SCB CSSELR: Level Mask */ + +#define SCB_CSSELR_IND_Pos 0U /*!< SCB CSSELR: InD Position */ +#define SCB_CSSELR_IND_Msk (1UL /*<< SCB_CSSELR_IND_Pos*/) /*!< SCB CSSELR: InD Mask */ + +/* SCB Software Triggered Interrupt Register Definitions */ +#define SCB_STIR_INTID_Pos 0U /*!< SCB STIR: INTID Position */ +#define SCB_STIR_INTID_Msk (0x1FFUL /*<< SCB_STIR_INTID_Pos*/) /*!< SCB STIR: INTID Mask */ + +/* SCB D-Cache Invalidate by Set-way Register Definitions */ +#define SCB_DCISW_WAY_Pos 30U /*!< SCB DCISW: Way Position */ +#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos) /*!< SCB DCISW: Way Mask */ + +#define SCB_DCISW_SET_Pos 5U /*!< SCB DCISW: Set Position */ +#define SCB_DCISW_SET_Msk (0x1FFUL << SCB_DCISW_SET_Pos) /*!< SCB DCISW: Set Mask */ + +/* SCB D-Cache Clean by Set-way Register Definitions */ +#define SCB_DCCSW_WAY_Pos 30U /*!< SCB DCCSW: Way Position */ +#define SCB_DCCSW_WAY_Msk (3UL << SCB_DCCSW_WAY_Pos) /*!< SCB DCCSW: Way Mask */ + +#define SCB_DCCSW_SET_Pos 5U /*!< SCB DCCSW: Set Position */ +#define SCB_DCCSW_SET_Msk (0x1FFUL << SCB_DCCSW_SET_Pos) /*!< SCB DCCSW: Set Mask */ + +/* SCB D-Cache Clean and Invalidate by Set-way Register Definitions */ +#define SCB_DCCISW_WAY_Pos 30U /*!< SCB DCCISW: Way Position */ +#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos) /*!< SCB DCCISW: Way Mask */ + +#define SCB_DCCISW_SET_Pos 5U /*!< SCB DCCISW: Set Position */ +#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */ + +/* Instruction Tightly-Coupled Memory Control Register Definitions */ +#define SCB_ITCMCR_SZ_Pos 3U /*!< SCB ITCMCR: SZ Position */ +#define SCB_ITCMCR_SZ_Msk (0xFUL << SCB_ITCMCR_SZ_Pos) /*!< SCB ITCMCR: SZ Mask */ + +#define SCB_ITCMCR_RETEN_Pos 2U /*!< SCB ITCMCR: RETEN Position */ +#define SCB_ITCMCR_RETEN_Msk (1UL << SCB_ITCMCR_RETEN_Pos) /*!< SCB ITCMCR: RETEN Mask */ + +#define SCB_ITCMCR_RMW_Pos 1U /*!< SCB ITCMCR: RMW Position */ +#define SCB_ITCMCR_RMW_Msk (1UL << SCB_ITCMCR_RMW_Pos) /*!< SCB ITCMCR: RMW Mask */ + +#define SCB_ITCMCR_EN_Pos 0U /*!< SCB ITCMCR: EN Position */ +#define SCB_ITCMCR_EN_Msk (1UL /*<< SCB_ITCMCR_EN_Pos*/) /*!< SCB ITCMCR: EN Mask */ + +/* Data Tightly-Coupled Memory Control Register Definitions */ +#define SCB_DTCMCR_SZ_Pos 3U /*!< SCB DTCMCR: SZ Position */ +#define SCB_DTCMCR_SZ_Msk (0xFUL << SCB_DTCMCR_SZ_Pos) /*!< SCB DTCMCR: SZ Mask */ + +#define SCB_DTCMCR_RETEN_Pos 2U /*!< SCB DTCMCR: RETEN Position */ +#define SCB_DTCMCR_RETEN_Msk (1UL << SCB_DTCMCR_RETEN_Pos) /*!< SCB DTCMCR: RETEN Mask */ + +#define SCB_DTCMCR_RMW_Pos 1U /*!< SCB DTCMCR: RMW Position */ +#define SCB_DTCMCR_RMW_Msk (1UL << SCB_DTCMCR_RMW_Pos) /*!< SCB DTCMCR: RMW Mask */ + +#define SCB_DTCMCR_EN_Pos 0U /*!< SCB DTCMCR: EN Position */ +#define SCB_DTCMCR_EN_Msk (1UL /*<< SCB_DTCMCR_EN_Pos*/) /*!< SCB DTCMCR: EN Mask */ + +/* AHBP Control Register Definitions */ +#define SCB_AHBPCR_SZ_Pos 1U /*!< SCB AHBPCR: SZ Position */ +#define SCB_AHBPCR_SZ_Msk (7UL << SCB_AHBPCR_SZ_Pos) /*!< SCB AHBPCR: SZ Mask */ + +#define SCB_AHBPCR_EN_Pos 0U /*!< SCB AHBPCR: EN Position */ +#define SCB_AHBPCR_EN_Msk (1UL /*<< SCB_AHBPCR_EN_Pos*/) /*!< SCB AHBPCR: EN Mask */ + +/* L1 Cache Control Register Definitions */ +#define SCB_CACR_FORCEWT_Pos 2U /*!< SCB CACR: FORCEWT Position */ +#define SCB_CACR_FORCEWT_Msk (1UL << SCB_CACR_FORCEWT_Pos) /*!< SCB CACR: FORCEWT Mask */ + +#define SCB_CACR_ECCEN_Pos 1U /*!< SCB CACR: ECCEN Position */ +#define SCB_CACR_ECCEN_Msk (1UL << SCB_CACR_ECCEN_Pos) /*!< SCB CACR: ECCEN Mask */ + +#define SCB_CACR_SIWT_Pos 0U /*!< SCB CACR: SIWT Position */ +#define SCB_CACR_SIWT_Msk (1UL /*<< SCB_CACR_SIWT_Pos*/) /*!< SCB CACR: SIWT Mask */ + +/* AHBS Control Register Definitions */ +#define SCB_AHBSCR_INITCOUNT_Pos 11U /*!< SCB AHBSCR: INITCOUNT Position */ +#define SCB_AHBSCR_INITCOUNT_Msk (0x1FUL << SCB_AHBPCR_INITCOUNT_Pos) /*!< SCB AHBSCR: INITCOUNT Mask */ + +#define SCB_AHBSCR_TPRI_Pos 2U /*!< SCB AHBSCR: TPRI Position */ +#define SCB_AHBSCR_TPRI_Msk (0x1FFUL << SCB_AHBPCR_TPRI_Pos) /*!< SCB AHBSCR: TPRI Mask */ + +#define SCB_AHBSCR_CTL_Pos 0U /*!< SCB AHBSCR: CTL Position*/ +#define SCB_AHBSCR_CTL_Msk (3UL /*<< SCB_AHBPCR_CTL_Pos*/) /*!< SCB AHBSCR: CTL Mask */ + +/* Auxiliary Bus Fault Status Register Definitions */ +#define SCB_ABFSR_AXIMTYPE_Pos 8U /*!< SCB ABFSR: AXIMTYPE Position*/ +#define SCB_ABFSR_AXIMTYPE_Msk (3UL << SCB_ABFSR_AXIMTYPE_Pos) /*!< SCB ABFSR: AXIMTYPE Mask */ + +#define SCB_ABFSR_EPPB_Pos 4U /*!< SCB ABFSR: EPPB Position*/ +#define SCB_ABFSR_EPPB_Msk (1UL << SCB_ABFSR_EPPB_Pos) /*!< SCB ABFSR: EPPB Mask */ + +#define SCB_ABFSR_AXIM_Pos 3U /*!< SCB ABFSR: AXIM Position*/ +#define SCB_ABFSR_AXIM_Msk (1UL << SCB_ABFSR_AXIM_Pos) /*!< SCB ABFSR: AXIM Mask */ + +#define SCB_ABFSR_AHBP_Pos 2U /*!< SCB ABFSR: AHBP Position*/ +#define SCB_ABFSR_AHBP_Msk (1UL << SCB_ABFSR_AHBP_Pos) /*!< SCB ABFSR: AHBP Mask */ + +#define SCB_ABFSR_DTCM_Pos 1U /*!< SCB ABFSR: DTCM Position*/ +#define SCB_ABFSR_DTCM_Msk (1UL << SCB_ABFSR_DTCM_Pos) /*!< SCB ABFSR: DTCM Mask */ + +#define SCB_ABFSR_ITCM_Pos 0U /*!< SCB ABFSR: ITCM Position*/ +#define SCB_ABFSR_ITCM_Msk (1UL /*<< SCB_ABFSR_ITCM_Pos*/) /*!< SCB ABFSR: ITCM Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ + __IOM uint32_t CPPWR; /*!< Offset: 0x00C (R/W) Coprocessor Power Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29U]; + __OM uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __IM uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IOM uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) ITM Device Architecture Register */ + uint32_t RESERVED6[4U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Stimulus Port Register Definitions */ +#define ITM_STIM_DISABLED_Pos 1U /*!< ITM STIM: DISABLED Position */ +#define ITM_STIM_DISABLED_Msk (0x1UL << ITM_STIM_DISABLED_Pos) /*!< ITM STIM: DISABLED Mask */ + +#define ITM_STIM_FIFOREADY_Pos 0U /*!< ITM STIM: FIFOREADY Position */ +#define ITM_STIM_FIFOREADY_Msk (0x1UL /*<< ITM_STIM_FIFOREADY_Pos*/) /*!< ITM STIM: FIFOREADY Mask */ + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TRACEBUSID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TRACEBUSID_Msk (0x7FUL << ITM_TCR_TRACEBUSID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPRESCALE_Pos 8U /*!< ITM TCR: TSPRESCALE Position */ +#define ITM_TCR_TSPRESCALE_Msk (3UL << ITM_TCR_TSPRESCALE_Pos) /*!< ITM TCR: TSPRESCALE Mask */ + +#define ITM_TCR_STALLENA_Pos 5U /*!< ITM TCR: STALLENA Position */ +#define ITM_TCR_STALLENA_Msk (1UL << ITM_TCR_STALLENA_Pos) /*!< ITM TCR: STALLENA Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0U /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL /*<< ITM_IWR_ATVALIDM_Pos*/) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0U /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL /*<< ITM_IRR_ATREADYM_Pos*/) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0U /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL /*<< ITM_IMCR_INTEGRATION_Pos*/) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + uint32_t RESERVED3[1U]; + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED4[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + uint32_t RESERVED5[1U]; + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED6[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + uint32_t RESERVED7[1U]; + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED8[1U]; + __IOM uint32_t COMP4; /*!< Offset: 0x060 (R/W) Comparator Register 4 */ + uint32_t RESERVED9[1U]; + __IOM uint32_t FUNCTION4; /*!< Offset: 0x068 (R/W) Function Register 4 */ + uint32_t RESERVED10[1U]; + __IOM uint32_t COMP5; /*!< Offset: 0x070 (R/W) Comparator Register 5 */ + uint32_t RESERVED11[1U]; + __IOM uint32_t FUNCTION5; /*!< Offset: 0x078 (R/W) Function Register 5 */ + uint32_t RESERVED12[1U]; + __IOM uint32_t COMP6; /*!< Offset: 0x080 (R/W) Comparator Register 6 */ + uint32_t RESERVED13[1U]; + __IOM uint32_t FUNCTION6; /*!< Offset: 0x088 (R/W) Function Register 6 */ + uint32_t RESERVED14[1U]; + __IOM uint32_t COMP7; /*!< Offset: 0x090 (R/W) Comparator Register 7 */ + uint32_t RESERVED15[1U]; + __IOM uint32_t FUNCTION7; /*!< Offset: 0x098 (R/W) Function Register 7 */ + uint32_t RESERVED16[1U]; + __IOM uint32_t COMP8; /*!< Offset: 0x0A0 (R/W) Comparator Register 8 */ + uint32_t RESERVED17[1U]; + __IOM uint32_t FUNCTION8; /*!< Offset: 0x0A8 (R/W) Function Register 8 */ + uint32_t RESERVED18[1U]; + __IOM uint32_t COMP9; /*!< Offset: 0x0B0 (R/W) Comparator Register 9 */ + uint32_t RESERVED19[1U]; + __IOM uint32_t FUNCTION9; /*!< Offset: 0x0B8 (R/W) Function Register 9 */ + uint32_t RESERVED20[1U]; + __IOM uint32_t COMP10; /*!< Offset: 0x0C0 (R/W) Comparator Register 10 */ + uint32_t RESERVED21[1U]; + __IOM uint32_t FUNCTION10; /*!< Offset: 0x0C8 (R/W) Function Register 10 */ + uint32_t RESERVED22[1U]; + __IOM uint32_t COMP11; /*!< Offset: 0x0D0 (R/W) Comparator Register 11 */ + uint32_t RESERVED23[1U]; + __IOM uint32_t FUNCTION11; /*!< Offset: 0x0D8 (R/W) Function Register 11 */ + uint32_t RESERVED24[1U]; + __IOM uint32_t COMP12; /*!< Offset: 0x0E0 (R/W) Comparator Register 12 */ + uint32_t RESERVED25[1U]; + __IOM uint32_t FUNCTION12; /*!< Offset: 0x0E8 (R/W) Function Register 12 */ + uint32_t RESERVED26[1U]; + __IOM uint32_t COMP13; /*!< Offset: 0x0F0 (R/W) Comparator Register 13 */ + uint32_t RESERVED27[1U]; + __IOM uint32_t FUNCTION13; /*!< Offset: 0x0F8 (R/W) Function Register 13 */ + uint32_t RESERVED28[1U]; + __IOM uint32_t COMP14; /*!< Offset: 0x100 (R/W) Comparator Register 14 */ + uint32_t RESERVED29[1U]; + __IOM uint32_t FUNCTION14; /*!< Offset: 0x108 (R/W) Function Register 14 */ + uint32_t RESERVED30[1U]; + __IOM uint32_t COMP15; /*!< Offset: 0x110 (R/W) Comparator Register 15 */ + uint32_t RESERVED31[1U]; + __IOM uint32_t FUNCTION15; /*!< Offset: 0x118 (R/W) Function Register 15 */ + uint32_t RESERVED32[934U]; + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R ) Lock Status Register */ + uint32_t RESERVED33[1U]; + __IM uint32_t DEVARCH; /*!< Offset: 0xFBC (R/ ) Device Architecture Register */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCDISS_Pos 23U /*!< DWT CTRL: CYCDISS Position */ +#define DWT_CTRL_CYCDISS_Msk (0x1UL << DWT_CTRL_CYCDISS_Pos) /*!< DWT CTRL: CYCDISS Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_ID_Pos 27U /*!< DWT FUNCTION: ID Position */ +#define DWT_FUNCTION_ID_Msk (0x1FUL << DWT_FUNCTION_ID_Pos) /*!< DWT FUNCTION: ID Mask */ + +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_ACTION_Pos 4U /*!< DWT FUNCTION: ACTION Position */ +#define DWT_FUNCTION_ACTION_Msk (0x1UL << DWT_FUNCTION_ACTION_Pos) /*!< DWT FUNCTION: ACTION Mask */ + +#define DWT_FUNCTION_MATCH_Pos 0U /*!< DWT FUNCTION: MATCH Position */ +#define DWT_FUNCTION_MATCH_Msk (0xFUL /*<< DWT_FUNCTION_MATCH_Pos*/) /*!< DWT FUNCTION: MATCH Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IOM uint32_t PSCR; /*!< Offset: 0x308 (R/W) Periodic Synchronization Control Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t ITFTTD0; /*!< Offset: 0xEEC (R/ ) Integration Test FIFO Test Data 0 Register */ + __IOM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/W) Integration Test ATB Control Register 2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) Integration Test ATB Control Register 0 */ + __IM uint32_t ITFTTD1; /*!< Offset: 0xEFC (R/ ) Integration Test FIFO Test Data 1 Register */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) Device Configuration Register */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) Device Type Identifier Register */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_FOnMan_Pos 6U /*!< TPI FFCR: FOnMan Position */ +#define TPI_FFCR_FOnMan_Msk (0x1UL << TPI_FFCR_FOnMan_Pos) /*!< TPI FFCR: FOnMan Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration Test FIFO Test Data 0 Register Definitions */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD0: ATB Interface 2 ATVALIDPosition */ +#define TPI_ITFTTD0_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD0: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD0_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD0_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD0: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD0_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD0: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD0_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD0_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD0: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data2_Pos 16U /*!< TPI ITFTTD0: ATB Interface 1 data2 Position */ +#define TPI_ITFTTD0_ATB_IF1_data2_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data2 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data1_Pos 8U /*!< TPI ITFTTD0: ATB Interface 1 data1 Position */ +#define TPI_ITFTTD0_ATB_IF1_data1_Msk (0xFFUL << TPI_ITFTTD0_ATB_IF1_data1_Pos) /*!< TPI ITFTTD0: ATB Interface 1 data1 Mask */ + +#define TPI_ITFTTD0_ATB_IF1_data0_Pos 0U /*!< TPI ITFTTD0: ATB Interface 1 data0 Position */ +#define TPI_ITFTTD0_ATB_IF1_data0_Msk (0xFFUL /*<< TPI_ITFTTD0_ATB_IF1_data0_Pos*/) /*!< TPI ITFTTD0: ATB Interface 1 data0 Mask */ + +/* TPI Integration Test ATB Control Register 2 Register Definitions */ +#define TPI_ITATBCTR2_AFVALID2S_Pos 1U /*!< TPI ITATBCTR2: AFVALID2S Position */ +#define TPI_ITATBCTR2_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID2S_Pos) /*!< TPI ITATBCTR2: AFVALID2SS Mask */ + +#define TPI_ITATBCTR2_AFVALID1S_Pos 1U /*!< TPI ITATBCTR2: AFVALID1S Position */ +#define TPI_ITATBCTR2_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR2_AFVALID1S_Pos) /*!< TPI ITATBCTR2: AFVALID1SS Mask */ + +#define TPI_ITATBCTR2_ATREADY2S_Pos 0U /*!< TPI ITATBCTR2: ATREADY2S Position */ +#define TPI_ITATBCTR2_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2S_Pos*/) /*!< TPI ITATBCTR2: ATREADY2S Mask */ + +#define TPI_ITATBCTR2_ATREADY1S_Pos 0U /*!< TPI ITATBCTR2: ATREADY1S Position */ +#define TPI_ITATBCTR2_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1S_Pos*/) /*!< TPI ITATBCTR2: ATREADY1S Mask */ + +/* TPI Integration Test FIFO Test Data 1 Register Definitions */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Pos 29U /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF2_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 2 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF2_bytecount_Pos 27U /*!< TPI ITFTTD1: ATB Interface 2 byte count Position */ +#define TPI_ITFTTD1_ATB_IF2_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF2_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 2 byte count Mask */ + +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Pos 26U /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Position */ +#define TPI_ITFTTD1_ATB_IF1_ATVALID_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_ATVALID_Pos) /*!< TPI ITFTTD1: ATB Interface 1 ATVALID Mask */ + +#define TPI_ITFTTD1_ATB_IF1_bytecount_Pos 24U /*!< TPI ITFTTD1: ATB Interface 1 byte count Position */ +#define TPI_ITFTTD1_ATB_IF1_bytecount_Msk (0x3UL << TPI_ITFTTD1_ATB_IF1_bytecount_Pos) /*!< TPI ITFTTD1: ATB Interface 1 byte countt Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data2_Pos 16U /*!< TPI ITFTTD1: ATB Interface 2 data2 Position */ +#define TPI_ITFTTD1_ATB_IF2_data2_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data2 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data1_Pos 8U /*!< TPI ITFTTD1: ATB Interface 2 data1 Position */ +#define TPI_ITFTTD1_ATB_IF2_data1_Msk (0xFFUL << TPI_ITFTTD1_ATB_IF2_data1_Pos) /*!< TPI ITFTTD1: ATB Interface 2 data1 Mask */ + +#define TPI_ITFTTD1_ATB_IF2_data0_Pos 0U /*!< TPI ITFTTD1: ATB Interface 2 data0 Position */ +#define TPI_ITFTTD1_ATB_IF2_data0_Msk (0xFFUL /*<< TPI_ITFTTD1_ATB_IF2_data0_Pos*/) /*!< TPI ITFTTD1: ATB Interface 2 data0 Mask */ + +/* TPI Integration Test ATB Control Register 0 Definitions */ +#define TPI_ITATBCTR0_AFVALID2S_Pos 1U /*!< TPI ITATBCTR0: AFVALID2S Position */ +#define TPI_ITATBCTR0_AFVALID2S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID2S_Pos) /*!< TPI ITATBCTR0: AFVALID2SS Mask */ + +#define TPI_ITATBCTR0_AFVALID1S_Pos 1U /*!< TPI ITATBCTR0: AFVALID1S Position */ +#define TPI_ITATBCTR0_AFVALID1S_Msk (0x1UL << TPI_ITATBCTR0_AFVALID1S_Pos) /*!< TPI ITATBCTR0: AFVALID1SS Mask */ + +#define TPI_ITATBCTR0_ATREADY2S_Pos 0U /*!< TPI ITATBCTR0: ATREADY2S Position */ +#define TPI_ITATBCTR0_ATREADY2S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2S_Pos*/) /*!< TPI ITATBCTR0: ATREADY2S Mask */ + +#define TPI_ITATBCTR0_ATREADY1S_Pos 0U /*!< TPI ITATBCTR0: ATREADY1S Position */ +#define TPI_ITATBCTR0_ATREADY1S_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1S_Pos*/) /*!< TPI ITATBCTR0: ATREADY1S Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_FIFOSZ_Pos 6U /*!< TPI DEVID: FIFOSZ Position */ +#define TPI_DEVID_FIFOSZ_Msk (0x7UL << TPI_DEVID_FIFOSZ_Pos) /*!< TPI DEVID: FIFOSZ Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x3FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) MPU Region Limit Address Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Region Base Address Register Alias 1 */ + __IOM uint32_t RLAR_A1; /*!< Offset: 0x018 (R/W) MPU Region Limit Address Register Alias 1 */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Region Base Address Register Alias 2 */ + __IOM uint32_t RLAR_A2; /*!< Offset: 0x020 (R/W) MPU Region Limit Address Register Alias 2 */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Region Base Address Register Alias 3 */ + __IOM uint32_t RLAR_A3; /*!< Offset: 0x028 (R/W) MPU Region Limit Address Register Alias 3 */ + uint32_t RESERVED0[1]; + union { + __IOM uint32_t MAIR[2]; + struct { + __IOM uint32_t MAIR0; /*!< Offset: 0x030 (R/W) MPU Memory Attribute Indirection Register 0 */ + __IOM uint32_t MAIR1; /*!< Offset: 0x034 (R/W) MPU Memory Attribute Indirection Register 1 */ + }; + }; +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_BASE_Pos 5U /*!< MPU RBAR: BASE Position */ +#define MPU_RBAR_BASE_Msk (0x7FFFFFFUL << MPU_RBAR_BASE_Pos) /*!< MPU RBAR: BASE Mask */ + +#define MPU_RBAR_SH_Pos 3U /*!< MPU RBAR: SH Position */ +#define MPU_RBAR_SH_Msk (0x3UL << MPU_RBAR_SH_Pos) /*!< MPU RBAR: SH Mask */ + +#define MPU_RBAR_AP_Pos 1U /*!< MPU RBAR: AP Position */ +#define MPU_RBAR_AP_Msk (0x3UL << MPU_RBAR_AP_Pos) /*!< MPU RBAR: AP Mask */ + +#define MPU_RBAR_XN_Pos 0U /*!< MPU RBAR: XN Position */ +#define MPU_RBAR_XN_Msk (01UL /*<< MPU_RBAR_XN_Pos*/) /*!< MPU RBAR: XN Mask */ + +/* MPU Region Limit Address Register Definitions */ +#define MPU_RLAR_LIMIT_Pos 5U /*!< MPU RLAR: LIMIT Position */ +#define MPU_RLAR_LIMIT_Msk (0x7FFFFFFUL << MPU_RLAR_LIMIT_Pos) /*!< MPU RLAR: LIMIT Mask */ + +#define MPU_RLAR_AttrIndx_Pos 1U /*!< MPU RLAR: AttrIndx Position */ +#define MPU_RLAR_AttrIndx_Msk (0x7UL << MPU_RLAR_AttrIndx_Pos) /*!< MPU RLAR: AttrIndx Mask */ + +#define MPU_RLAR_EN_Pos 0U /*!< MPU RLAR: Region enable bit Position */ +#define MPU_RLAR_EN_Msk (1UL /*<< MPU_RLAR_EN_Pos*/) /*!< MPU RLAR: Region enable bit Disable Mask */ + +/* MPU Memory Attribute Indirection Register 0 Definitions */ +#define MPU_MAIR0_Attr3_Pos 24U /*!< MPU MAIR0: Attr3 Position */ +#define MPU_MAIR0_Attr3_Msk (0xFFUL << MPU_MAIR0_Attr3_Pos) /*!< MPU MAIR0: Attr3 Mask */ + +#define MPU_MAIR0_Attr2_Pos 16U /*!< MPU MAIR0: Attr2 Position */ +#define MPU_MAIR0_Attr2_Msk (0xFFUL << MPU_MAIR0_Attr2_Pos) /*!< MPU MAIR0: Attr2 Mask */ + +#define MPU_MAIR0_Attr1_Pos 8U /*!< MPU MAIR0: Attr1 Position */ +#define MPU_MAIR0_Attr1_Msk (0xFFUL << MPU_MAIR0_Attr1_Pos) /*!< MPU MAIR0: Attr1 Mask */ + +#define MPU_MAIR0_Attr0_Pos 0U /*!< MPU MAIR0: Attr0 Position */ +#define MPU_MAIR0_Attr0_Msk (0xFFUL /*<< MPU_MAIR0_Attr0_Pos*/) /*!< MPU MAIR0: Attr0 Mask */ + +/* MPU Memory Attribute Indirection Register 1 Definitions */ +#define MPU_MAIR1_Attr7_Pos 24U /*!< MPU MAIR1: Attr7 Position */ +#define MPU_MAIR1_Attr7_Msk (0xFFUL << MPU_MAIR1_Attr7_Pos) /*!< MPU MAIR1: Attr7 Mask */ + +#define MPU_MAIR1_Attr6_Pos 16U /*!< MPU MAIR1: Attr6 Position */ +#define MPU_MAIR1_Attr6_Msk (0xFFUL << MPU_MAIR1_Attr6_Pos) /*!< MPU MAIR1: Attr6 Mask */ + +#define MPU_MAIR1_Attr5_Pos 8U /*!< MPU MAIR1: Attr5 Position */ +#define MPU_MAIR1_Attr5_Msk (0xFFUL << MPU_MAIR1_Attr5_Pos) /*!< MPU MAIR1: Attr5 Mask */ + +#define MPU_MAIR1_Attr4_Pos 0U /*!< MPU MAIR1: Attr4 Position */ +#define MPU_MAIR1_Attr4_Msk (0xFFUL /*<< MPU_MAIR1_Attr4_Pos*/) /*!< MPU MAIR1: Attr4 Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SAU Security Attribution Unit (SAU) + \brief Type definitions for the Security Attribution Unit (SAU) + @{ + */ + +/** + \brief Structure type to access the Security Attribution Unit (SAU). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SAU Control Register */ + __IM uint32_t TYPE; /*!< Offset: 0x004 (R/ ) SAU Type Register */ +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) SAU Region Number Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) SAU Region Base Address Register */ + __IOM uint32_t RLAR; /*!< Offset: 0x010 (R/W) SAU Region Limit Address Register */ +#else + uint32_t RESERVED0[3]; +#endif + __IOM uint32_t SFSR; /*!< Offset: 0x014 (R/W) Secure Fault Status Register */ + __IOM uint32_t SFAR; /*!< Offset: 0x018 (R/W) Secure Fault Address Register */ +} SAU_Type; + +/* SAU Control Register Definitions */ +#define SAU_CTRL_ALLNS_Pos 1U /*!< SAU CTRL: ALLNS Position */ +#define SAU_CTRL_ALLNS_Msk (1UL << SAU_CTRL_ALLNS_Pos) /*!< SAU CTRL: ALLNS Mask */ + +#define SAU_CTRL_ENABLE_Pos 0U /*!< SAU CTRL: ENABLE Position */ +#define SAU_CTRL_ENABLE_Msk (1UL /*<< SAU_CTRL_ENABLE_Pos*/) /*!< SAU CTRL: ENABLE Mask */ + +/* SAU Type Register Definitions */ +#define SAU_TYPE_SREGION_Pos 0U /*!< SAU TYPE: SREGION Position */ +#define SAU_TYPE_SREGION_Msk (0xFFUL /*<< SAU_TYPE_SREGION_Pos*/) /*!< SAU TYPE: SREGION Mask */ + +#if defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) +/* SAU Region Number Register Definitions */ +#define SAU_RNR_REGION_Pos 0U /*!< SAU RNR: REGION Position */ +#define SAU_RNR_REGION_Msk (0xFFUL /*<< SAU_RNR_REGION_Pos*/) /*!< SAU RNR: REGION Mask */ + +/* SAU Region Base Address Register Definitions */ +#define SAU_RBAR_BADDR_Pos 5U /*!< SAU RBAR: BADDR Position */ +#define SAU_RBAR_BADDR_Msk (0x7FFFFFFUL << SAU_RBAR_BADDR_Pos) /*!< SAU RBAR: BADDR Mask */ + +/* SAU Region Limit Address Register Definitions */ +#define SAU_RLAR_LADDR_Pos 5U /*!< SAU RLAR: LADDR Position */ +#define SAU_RLAR_LADDR_Msk (0x7FFFFFFUL << SAU_RLAR_LADDR_Pos) /*!< SAU RLAR: LADDR Mask */ + +#define SAU_RLAR_NSC_Pos 1U /*!< SAU RLAR: NSC Position */ +#define SAU_RLAR_NSC_Msk (1UL << SAU_RLAR_NSC_Pos) /*!< SAU RLAR: NSC Mask */ + +#define SAU_RLAR_ENABLE_Pos 0U /*!< SAU RLAR: ENABLE Position */ +#define SAU_RLAR_ENABLE_Msk (1UL /*<< SAU_RLAR_ENABLE_Pos*/) /*!< SAU RLAR: ENABLE Mask */ + +#endif /* defined (__SAUREGION_PRESENT) && (__SAUREGION_PRESENT == 1U) */ + +/* Secure Fault Status Register Definitions */ +#define SAU_SFSR_LSERR_Pos 7U /*!< SAU SFSR: LSERR Position */ +#define SAU_SFSR_LSERR_Msk (1UL << SAU_SFSR_LSERR_Pos) /*!< SAU SFSR: LSERR Mask */ + +#define SAU_SFSR_SFARVALID_Pos 6U /*!< SAU SFSR: SFARVALID Position */ +#define SAU_SFSR_SFARVALID_Msk (1UL << SAU_SFSR_SFARVALID_Pos) /*!< SAU SFSR: SFARVALID Mask */ + +#define SAU_SFSR_LSPERR_Pos 5U /*!< SAU SFSR: LSPERR Position */ +#define SAU_SFSR_LSPERR_Msk (1UL << SAU_SFSR_LSPERR_Pos) /*!< SAU SFSR: LSPERR Mask */ + +#define SAU_SFSR_INVTRAN_Pos 4U /*!< SAU SFSR: INVTRAN Position */ +#define SAU_SFSR_INVTRAN_Msk (1UL << SAU_SFSR_INVTRAN_Pos) /*!< SAU SFSR: INVTRAN Mask */ + +#define SAU_SFSR_AUVIOL_Pos 3U /*!< SAU SFSR: AUVIOL Position */ +#define SAU_SFSR_AUVIOL_Msk (1UL << SAU_SFSR_AUVIOL_Pos) /*!< SAU SFSR: AUVIOL Mask */ + +#define SAU_SFSR_INVER_Pos 2U /*!< SAU SFSR: INVER Position */ +#define SAU_SFSR_INVER_Msk (1UL << SAU_SFSR_INVER_Pos) /*!< SAU SFSR: INVER Mask */ + +#define SAU_SFSR_INVIS_Pos 1U /*!< SAU SFSR: INVIS Position */ +#define SAU_SFSR_INVIS_Msk (1UL << SAU_SFSR_INVIS_Pos) /*!< SAU SFSR: INVIS Mask */ + +#define SAU_SFSR_INVEP_Pos 0U /*!< SAU SFSR: INVEP Position */ +#define SAU_SFSR_INVEP_Msk (1UL /*<< SAU_SFSR_INVEP_Pos*/) /*!< SAU SFSR: INVEP Mask */ + +/*@} end of group CMSIS_SAU */ +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_LSPENS_Pos 29U /*!< FPCCR: LSPENS Position */ +#define FPU_FPCCR_LSPENS_Msk (1UL << FPU_FPCCR_LSPENS_Pos) /*!< FPCCR: LSPENS bit Mask */ + +#define FPU_FPCCR_CLRONRET_Pos 28U /*!< FPCCR: CLRONRET Position */ +#define FPU_FPCCR_CLRONRET_Msk (1UL << FPU_FPCCR_CLRONRET_Pos) /*!< FPCCR: CLRONRET bit Mask */ + +#define FPU_FPCCR_CLRONRETS_Pos 27U /*!< FPCCR: CLRONRETS Position */ +#define FPU_FPCCR_CLRONRETS_Msk (1UL << FPU_FPCCR_CLRONRETS_Pos) /*!< FPCCR: CLRONRETS bit Mask */ + +#define FPU_FPCCR_TS_Pos 26U /*!< FPCCR: TS Position */ +#define FPU_FPCCR_TS_Msk (1UL << FPU_FPCCR_TS_Pos) /*!< FPCCR: TS bit Mask */ + +#define FPU_FPCCR_UFRDY_Pos 10U /*!< FPCCR: UFRDY Position */ +#define FPU_FPCCR_UFRDY_Msk (1UL << FPU_FPCCR_UFRDY_Pos) /*!< FPCCR: UFRDY bit Mask */ + +#define FPU_FPCCR_SPLIMVIOL_Pos 9U /*!< FPCCR: SPLIMVIOL Position */ +#define FPU_FPCCR_SPLIMVIOL_Msk (1UL << FPU_FPCCR_SPLIMVIOL_Pos) /*!< FPCCR: SPLIMVIOL bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_SFRDY_Pos 7U /*!< FPCCR: SFRDY Position */ +#define FPU_FPCCR_SFRDY_Msk (1UL << FPU_FPCCR_SFRDY_Pos) /*!< FPCCR: SFRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_S_Pos 2U /*!< FPCCR: Security status of the FP context bit Position */ +#define FPU_FPCCR_S_Msk (1UL << FPU_FPCCR_S_Pos) /*!< FPCCR: Security status of the FP context bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ + uint32_t RESERVED4[1U]; + __IOM uint32_t DAUTHCTRL; /*!< Offset: 0x014 (R/W) Debug Authentication Control Register */ + __IOM uint32_t DSCSR; /*!< Offset: 0x018 (R/W) Debug Security Control and Status Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESTART_ST_Pos 26U /*!< CoreDebug DHCSR: S_RESTART_ST Position */ +#define CoreDebug_DHCSR_S_RESTART_ST_Msk (1UL << CoreDebug_DHCSR_S_RESTART_ST_Pos) /*!< CoreDebug DHCSR: S_RESTART_ST Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/* Debug Authentication Control Register Definitions */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos 3U /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Position */ +#define CoreDebug_DAUTHCTRL_INTSPNIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPNIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPNIDEN, Mask */ + +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos 2U /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPNIDENSEL_Msk (1UL << CoreDebug_DAUTHCTRL_SPNIDENSEL_Pos) /*!< CoreDebug DAUTHCTRL: SPNIDENSEL Mask */ + +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Pos 1U /*!< CoreDebug DAUTHCTRL: INTSPIDEN Position */ +#define CoreDebug_DAUTHCTRL_INTSPIDEN_Msk (1UL << CoreDebug_DAUTHCTRL_INTSPIDEN_Pos) /*!< CoreDebug DAUTHCTRL: INTSPIDEN Mask */ + +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Pos 0U /*!< CoreDebug DAUTHCTRL: SPIDENSEL Position */ +#define CoreDebug_DAUTHCTRL_SPIDENSEL_Msk (1UL /*<< CoreDebug_DAUTHCTRL_SPIDENSEL_Pos*/) /*!< CoreDebug DAUTHCTRL: SPIDENSEL Mask */ + +/* Debug Security Control and Status Register Definitions */ +#define CoreDebug_DSCSR_CDS_Pos 16U /*!< CoreDebug DSCSR: CDS Position */ +#define CoreDebug_DSCSR_CDS_Msk (1UL << CoreDebug_DSCSR_CDS_Pos) /*!< CoreDebug DSCSR: CDS Mask */ + +#define CoreDebug_DSCSR_SBRSEL_Pos 1U /*!< CoreDebug DSCSR: SBRSEL Position */ +#define CoreDebug_DSCSR_SBRSEL_Msk (1UL << CoreDebug_DSCSR_SBRSEL_Pos) /*!< CoreDebug DSCSR: SBRSEL Mask */ + +#define CoreDebug_DSCSR_SBRSELEN_Pos 0U /*!< CoreDebug DSCSR: SBRSELEN Position */ +#define CoreDebug_DSCSR_SBRSELEN_Msk (1UL /*<< CoreDebug_DSCSR_SBRSELEN_Pos*/) /*!< CoreDebug DSCSR: SBRSELEN Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ + #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ + #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ + #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ + #define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ + #define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ + #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ + #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ + #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + + #define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ + #define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ + #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ + #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ + #define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ + #define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ + #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ + #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE ) /*!< Core Debug configuration struct */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ + #endif + + #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SAU_BASE (SCS_BASE + 0x0DD0UL) /*!< Security Attribution Unit */ + #define SAU ((SAU_Type *) SAU_BASE ) /*!< Security Attribution Unit */ + #endif + + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + #define SCS_BASE_NS (0xE002E000UL) /*!< System Control Space Base Address (non-secure address space) */ + #define CoreDebug_BASE_NS (0xE002EDF0UL) /*!< Core Debug Base Address (non-secure address space) */ + #define SysTick_BASE_NS (SCS_BASE_NS + 0x0010UL) /*!< SysTick Base Address (non-secure address space) */ + #define NVIC_BASE_NS (SCS_BASE_NS + 0x0100UL) /*!< NVIC Base Address (non-secure address space) */ + #define SCB_BASE_NS (SCS_BASE_NS + 0x0D00UL) /*!< System Control Block Base Address (non-secure address space) */ + + #define SCnSCB_NS ((SCnSCB_Type *) SCS_BASE_NS ) /*!< System control Register not in SCB(non-secure address space) */ + #define SCB_NS ((SCB_Type *) SCB_BASE_NS ) /*!< SCB configuration struct (non-secure address space) */ + #define SysTick_NS ((SysTick_Type *) SysTick_BASE_NS ) /*!< SysTick configuration struct (non-secure address space) */ + #define NVIC_NS ((NVIC_Type *) NVIC_BASE_NS ) /*!< NVIC configuration struct (non-secure address space) */ + #define CoreDebug_NS ((CoreDebug_Type *) CoreDebug_BASE_NS) /*!< Core Debug configuration struct (non-secure address space) */ + + #if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE_NS (SCS_BASE_NS + 0x0D90UL) /*!< Memory Protection Unit (non-secure address space) */ + #define MPU_NS ((MPU_Type *) MPU_BASE_NS ) /*!< Memory Protection Unit (non-secure address space) */ + #endif + + #define FPU_BASE_NS (SCS_BASE_NS + 0x0F30UL) /*!< Floating Point Unit (non-secure address space) */ + #define FPU_NS ((FPU_Type *) FPU_BASE_NS ) /*!< Floating Point Unit (non-secure address space) */ + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* Special LR values for Secure/Non-Secure call handling and exception handling */ + +/* Function Return Payload (from ARMv8-M Architecture Reference Manual) LR value on entry from Secure BLXNS */ +#define FNC_RETURN (0xFEFFFFFFUL) /* bit [0] ignored when processing a branch */ + +/* The following EXC_RETURN mask values are used to evaluate the LR on exception entry */ +#define EXC_RETURN_PREFIX (0xFF000000UL) /* bits [31:24] set to indicate an EXC_RETURN value */ +#define EXC_RETURN_S (0x00000040UL) /* bit [6] stack used to push registers: 0=Non-secure 1=Secure */ +#define EXC_RETURN_DCRS (0x00000020UL) /* bit [5] stacking rules for called registers: 0=skipped 1=saved */ +#define EXC_RETURN_FTYPE (0x00000010UL) /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ +#define EXC_RETURN_MODE (0x00000008UL) /* bit [3] processor mode for return: 0=Handler mode 1=Thread mode */ +#define EXC_RETURN_SPSEL (0x00000002UL) /* bit [1] stack pointer used to restore context: 0=MSP 1=PSP */ +#define EXC_RETURN_ES (0x00000001UL) /* bit [0] security state exception was taken to: 0=Non-secure 1=Secure */ + +/* Integrity Signature (from ARMv8-M Architecture Reference Manual) for exception context stacking */ +#if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) /* Value for processors with floating-point extension: */ +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125AUL) /* bit [0] SFTC must match LR bit[4] EXC_RETURN_FTYPE */ +#else +#define EXC_INTEGRITY_SIGNATURE (0xFEFA125BUL) /* Value for processors without floating-point extension */ +#endif + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8U) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Get Interrupt Target State + \details Reads the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + \return 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_GetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Target State + \details Sets the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_SetTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] |= ((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Clear Interrupt Target State + \details Clears the interrupt target field in the NVIC and returns the interrupt target bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 if interrupt is assigned to Secure + 1 if interrupt is assigned to Non Secure + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t NVIC_ClearTargetState(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] &= ~((uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))); + return((uint32_t)(((NVIC->ITNS[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief Set Priority Grouping (non-secure) + \details Sets the non-secure priority grouping field when in secure state using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void TZ_NVIC_SetPriorityGrouping_NS(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB_NS->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB_NS->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping (non-secure) + \details Reads the priority grouping field from the non-secure NVIC when in secure state. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriorityGrouping_NS(void) +{ + return ((uint32_t)((SCB_NS->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt (non-secure) + \details Enables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_EnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Interrupt Enable status (non-secure) + \details Returns a device specific interrupt enable status from the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetEnableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt (non-secure) + \details Disables a device specific interrupt in the non-secure NVIC interrupt controller when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_DisableIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Pending Interrupt (non-secure) + \details Reads the NVIC pending register in the non-secure NVIC when in secure state and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt (non-secure) + \details Sets the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_SetPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt (non-secure) + \details Clears the pending bit of a device specific interrupt in the non-secure NVIC pending register when in secure state. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void TZ_NVIC_ClearPendingIRQ_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt (non-secure) + \details Reads the active register in non-secure NVIC when in secure state and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetActive_NS(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC_NS->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority (non-secure) + \details Sets the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every non-secure processor exception. + */ +__STATIC_INLINE void TZ_NVIC_SetPriority_NS(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC_NS->IPR[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority (non-secure) + \details Reads the priority of a non-secure device specific interrupt or a non-secure processor exception when in secure state. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t TZ_NVIC_GetPriority_NS(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC_NS->IPR[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB_NS->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} +#endif /* defined (__ARM_FEATURE_CMSE) &&(__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv8.h" + +#endif + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) + { + return 2U; /* Double + Single precision FPU */ + } + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## SAU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SAUFunctions SAU Functions + \brief Functions that configure the SAU. + @{ + */ + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +/** + \brief Enable SAU + \details Enables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Enable(void) +{ + SAU->CTRL |= (SAU_CTRL_ENABLE_Msk); +} + + + +/** + \brief Disable SAU + \details Disables the Security Attribution Unit (SAU). + */ +__STATIC_INLINE void TZ_SAU_Disable(void) +{ + SAU->CTRL &= ~(SAU_CTRL_ENABLE_Msk); +} + +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +/*@} end of CMSIS_Core_SAUFunctions */ + + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +/** + \brief System Tick Configuration (non-secure) + \details Initializes the non-secure System Timer and its interrupt when in secure state, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function TZ_SysTick_Config_NS is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t TZ_SysTick_Config_NS(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick_NS->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + TZ_NVIC_SetPriority_NS (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick_NS->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick_NS->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} +#endif /* defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) */ + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM33_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm4.h b/Firmware/ThirdParty/CMSIS/Include/core_cm4.h similarity index 84% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm4.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm4.h index dc840ebf..7d568735 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm4.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm4.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm4.h * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.8 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,60 +60,22 @@ @{ */ -/* CMSIS CM4 definitions */ -#define __CM4_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM4_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#include "cmsis_version.h" + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ - __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __CM4_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_M (0x04U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (4U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. */ #if defined ( __CC_ARM ) #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -133,9 +85,9 @@ #define __FPU_USED 0U #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP - #if (__FPU_PRESENT == 1) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -147,7 +99,7 @@ #elif defined ( __GNUC__ ) #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -159,7 +111,7 @@ #elif defined ( __ICCARM__ ) #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -169,9 +121,9 @@ #define __FPU_USED 0U #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -183,7 +135,7 @@ #elif defined ( __TASKING__ ) #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -195,7 +147,7 @@ #elif defined ( __CSMC__ ) #if ( __CSMC__ & 0x400U) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -207,9 +159,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ -#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -244,7 +195,7 @@ #endif #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U + #define __NVIC_PRIO_BITS 3U #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" #endif @@ -367,11 +318,12 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -397,8 +349,8 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ @@ -406,6 +358,9 @@ typedef union #define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ #define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -662,6 +617,66 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -831,7 +846,7 @@ typedef struct /* ITM Trace Privilege Register Definitions */ #define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ /* ITM Trace Control Register Definitions */ #define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ @@ -1045,7 +1060,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -1056,7 +1071,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1126,8 +1141,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1152,12 +1170,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1179,16 +1200,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1214,6 +1235,8 @@ typedef struct __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 4U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -1280,10 +1303,9 @@ typedef struct #define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ /*@} end of group CMSIS_MPU */ -#endif +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ -#if (__FPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_FPU Floating Point Unit (FPU) @@ -1388,7 +1410,6 @@ typedef struct #define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ /*@} end of group CMSIS_FPU */ -#endif /** @@ -1506,18 +1527,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1529,7 +1550,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M4 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1548,15 +1569,13 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif -#if (__FPU_PRESENT == 1U) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ /*@} */ @@ -1584,6 +1603,48 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1593,7 +1654,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1602,7 +1663,7 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ SCB->AIRCR = reg_value; } @@ -1612,121 +1673,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1783,11 +1901,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -1804,6 +1953,49 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + /* ################################## SysTick function ############################################ */ @@ -1814,7 +2006,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -1857,8 +2049,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm7.h b/Firmware/ThirdParty/CMSIS/Include/core_cm7.h similarity index 85% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_cm7.h rename to Firmware/ThirdParty/CMSIS/Include/core_cm7.h index 3b7530ad..a14dc623 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_cm7.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_cm7.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_cm7.h * @brief CMSIS Cortex-M7 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.8 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,60 +60,22 @@ @{ */ -/* CMSIS CM7 definitions */ -#define __CM7_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __CM7_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#include "cmsis_version.h" + +/* CMSIS CM7 definitions */ +#define __CM7_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM7_CMSIS_VERSION_SUB ( __CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __CM7_CMSIS_VERSION ((__CM7_CMSIS_VERSION_MAIN << 16U) | \ - __CM7_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __CM7_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_M (0x07U) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_M (7U) /*!< Cortex-M Core */ /** __FPU_USED indicates whether an FPU is used or not. For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. */ #if defined ( __CC_ARM ) #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -133,9 +85,9 @@ #define __FPU_USED 0U #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP - #if (__FPU_PRESENT == 1) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -147,7 +99,7 @@ #elif defined ( __GNUC__ ) #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -159,7 +111,7 @@ #elif defined ( __ICCARM__ ) #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -169,9 +121,9 @@ #define __FPU_USED 0U #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -183,7 +135,7 @@ #elif defined ( __TASKING__ ) #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -195,7 +147,7 @@ #elif defined ( __CSMC__ ) #if ( __CSMC__ & 0x400U) - #if (__FPU_PRESENT == 1U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) #define __FPU_USED 1U #else #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" @@ -207,9 +159,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ -#include "core_cmSimd.h" /* Compiler specific SIMD Intrinsics */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -382,11 +333,12 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -412,8 +364,8 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ @@ -421,6 +373,9 @@ typedef union #define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ #define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -529,7 +484,7 @@ typedef struct uint32_t RESERVED4[15U]; __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ - __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ uint32_t RESERVED5[1U]; __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ uint32_t RESERVED6[1U]; @@ -715,6 +670,66 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -1033,7 +1048,7 @@ typedef struct /* ITM Trace Privilege Register Definitions */ #define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ /* ITM Trace Control Register Definitions */ #define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ @@ -1250,7 +1265,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -1261,7 +1276,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1331,8 +1346,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1357,12 +1375,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1384,16 +1405,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1419,6 +1440,8 @@ typedef struct __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ } MPU_Type; +#define MPU_TYPE_RALIASES 4U + /* MPU Type Register Definitions */ #define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ #define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ @@ -1485,10 +1508,9 @@ typedef struct #define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ /*@} end of group CMSIS_MPU */ -#endif +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ -#if (__FPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_FPU Floating Point Unit (FPU) @@ -1596,7 +1618,6 @@ typedef struct /* Media and FP Feature Register 2 Definitions */ /*@} end of group CMSIS_FPU */ -#endif /** @@ -1714,18 +1735,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1737,7 +1758,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M4 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1756,15 +1777,13 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif -#if (__FPU_PRESENT == 1U) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ /*@} */ @@ -1792,6 +1811,48 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1801,7 +1862,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1810,7 +1871,7 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ reg_value = (reg_value | ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8U) ); /* Insert write key and priorty group */ + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ SCB->AIRCR = reg_value; } @@ -1820,121 +1881,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHPR[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHPR[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1991,11 +2109,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -2012,6 +2161,13 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif /* ########################## FPU functions #################################### */ /** @@ -2034,17 +2190,17 @@ __STATIC_INLINE uint32_t SCB_GetFPUType(void) uint32_t mvfr0; mvfr0 = SCB->MVFR0; - if ((mvfr0 & 0x00000FF0UL) == 0x220UL) + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) { - return 2UL; /* Double + Single precision FPU */ + return 2U; /* Double + Single precision FPU */ } - else if ((mvfr0 & 0x00000FF0UL) == 0x020UL) + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) { - return 1UL; /* Single precision FPU */ + return 1U; /* Single precision FPU */ } else { - return 0UL; /* No FPU */ + return 0U; /* No FPU */ } } @@ -2072,10 +2228,12 @@ __STATIC_INLINE uint32_t SCB_GetFPUType(void) */ __STATIC_INLINE void SCB_EnableICache (void) { - #if (__ICACHE_PRESENT == 1U) + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) __DSB(); __ISB(); SCB->ICIALLU = 0UL; /* invalidate I-Cache */ + __DSB(); + __ISB(); SCB->CCR |= (uint32_t)SCB_CCR_IC_Msk; /* enable I-Cache */ __DSB(); __ISB(); @@ -2089,7 +2247,7 @@ __STATIC_INLINE void SCB_EnableICache (void) */ __STATIC_INLINE void SCB_DisableICache (void) { - #if (__ICACHE_PRESENT == 1U) + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) __DSB(); __ISB(); SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk; /* disable I-Cache */ @@ -2106,7 +2264,7 @@ __STATIC_INLINE void SCB_DisableICache (void) */ __STATIC_INLINE void SCB_InvalidateICache (void) { - #if (__ICACHE_PRESENT == 1U) + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) __DSB(); __ISB(); SCB->ICIALLU = 0UL; @@ -2122,12 +2280,12 @@ __STATIC_INLINE void SCB_InvalidateICache (void) */ __STATIC_INLINE void SCB_EnableDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); ccsidr = SCB->CCSIDR; @@ -2142,8 +2300,8 @@ __STATIC_INLINE void SCB_EnableDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); SCB->CCR |= (uint32_t)SCB_CCR_DC_Msk; /* enable D-Cache */ @@ -2160,18 +2318,19 @@ __STATIC_INLINE void SCB_EnableDCache (void) */ __STATIC_INLINE void SCB_DisableDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ + __DSB(); + + SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */ __DSB(); ccsidr = SCB->CCSIDR; - SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */ - /* clean & invalidate D-Cache */ sets = (uint32_t)(CCSIDR_SETS(ccsidr)); do { @@ -2182,8 +2341,8 @@ __STATIC_INLINE void SCB_DisableDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2197,12 +2356,12 @@ __STATIC_INLINE void SCB_DisableDCache (void) */ __STATIC_INLINE void SCB_InvalidateDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); ccsidr = SCB->CCSIDR; @@ -2217,8 +2376,8 @@ __STATIC_INLINE void SCB_InvalidateDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2232,13 +2391,13 @@ __STATIC_INLINE void SCB_InvalidateDCache (void) */ __STATIC_INLINE void SCB_CleanDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ - __DSB(); + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ + __DSB(); ccsidr = SCB->CCSIDR; @@ -2252,8 +2411,8 @@ __STATIC_INLINE void SCB_CleanDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2267,12 +2426,12 @@ __STATIC_INLINE void SCB_CleanDCache (void) */ __STATIC_INLINE void SCB_CleanInvalidateDCache (void) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) uint32_t ccsidr; uint32_t sets; uint32_t ways; - SCB->CSSELR = (0U << 1U) | 0U; /* Level 1 data cache */ + SCB->CSSELR = 0U; /*(0U << 1U) | 0U;*/ /* Level 1 data cache */ __DSB(); ccsidr = SCB->CCSIDR; @@ -2287,8 +2446,8 @@ __STATIC_INLINE void SCB_CleanInvalidateDCache (void) #if defined ( __CC_ARM ) __schedule_barrier(); #endif - } while (ways--); - } while(sets--); + } while (ways-- != 0U); + } while(sets-- != 0U); __DSB(); __ISB(); @@ -2304,17 +2463,17 @@ __STATIC_INLINE void SCB_CleanInvalidateDCache (void) */ __STATIC_INLINE void SCB_InvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) int32_t op_size = dsize; uint32_t op_addr = (uint32_t)addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ + int32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ __DSB(); while (op_size > 0) { SCB->DCIMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; + op_addr += (uint32_t)linesize; + op_size -= linesize; } __DSB(); @@ -2331,17 +2490,17 @@ __STATIC_INLINE void SCB_InvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize */ __STATIC_INLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize) { - #if (__DCACHE_PRESENT == 1) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) int32_t op_size = dsize; uint32_t op_addr = (uint32_t) addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ + int32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ __DSB(); while (op_size > 0) { SCB->DCCMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; + op_addr += (uint32_t)linesize; + op_size -= linesize; } __DSB(); @@ -2358,17 +2517,17 @@ __STATIC_INLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize) */ __STATIC_INLINE void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) { - #if (__DCACHE_PRESENT == 1U) + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) int32_t op_size = dsize; uint32_t op_addr = (uint32_t) addr; - int32_t linesize = 32U; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ + int32_t linesize = 32; /* in Cortex-M7 size of cache line is fixed to 8 words (32 bytes) */ __DSB(); while (op_size > 0) { SCB->DCCIMVAC = op_addr; - op_addr += linesize; - op_size -= linesize; + op_addr += (uint32_t)linesize; + op_size -= linesize; } __DSB(); @@ -2389,7 +2548,7 @@ __STATIC_INLINE void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -2432,8 +2591,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc000.h b/Firmware/ThirdParty/CMSIS/Include/core_sc000.h similarity index 81% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_sc000.h rename to Firmware/ThirdParty/CMSIS/Include/core_sc000.h index 514dbd81..9b67c92f 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc000.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_sc000.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_sc000.h * @brief CMSIS SC000 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.5 + * @date 28. May 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS SC000 definitions */ -#define __SC000_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __SC000_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __SC000_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __SC000_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __SC000_CMSIS_VERSION ((__SC000_CMSIS_VERSION_MAIN << 16U) | \ - __SC000_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __SC000_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_SC (000U) /*!< Cortex secure core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_SC (000U) /*!< Cortex secure core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -569,7 +521,7 @@ typedef struct /*@} end of group CMSIS_SysTick */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -678,18 +630,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -701,7 +653,7 @@ typedef struct @{ */ -/* Memory mapping of SC000 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ @@ -712,7 +664,7 @@ typedef struct #define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ #define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -742,7 +694,46 @@ typedef struct @{ */ -/* Interrupt Priorities are WORD accessible only under ARMv6M */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else +/*#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping not available for SC000 */ +/*#define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping not available for SC000 */ + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ +/*#define NVIC_GetActive __NVIC_GetActive not available for SC000 */ + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/* Interrupt Priorities are WORD accessible only under Armv6-M */ /* The following MACROS handle generation of the register offset and byte masks */ #define _BIT_SHIFT(IRQn) ( ((((uint32_t)(int32_t)(IRQn)) ) & 0x03UL) * 8UL) #define _SHP_IDX(IRQn) ( (((((uint32_t)(int32_t)(IRQn)) & 0x0FUL)-8UL) >> 2UL) ) @@ -750,79 +741,128 @@ typedef struct /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } else { - NVIC->IP[_IP_IDX(IRQn)] = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | + SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) | (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn))); } } @@ -830,24 +870,55 @@ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) - { - return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); - } - else + if ((int32_t)(IRQn) >= 0) { return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); } + else + { + return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; } @@ -855,7 +926,7 @@ __STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -872,6 +943,31 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + /* ################################## SysTick function ############################################ */ /** @@ -881,7 +977,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc300.h b/Firmware/ThirdParty/CMSIS/Include/core_sc300.h similarity index 84% rename from Firmware/Board/v3/Drivers/CMSIS/Include/core_sc300.h rename to Firmware/ThirdParty/CMSIS/Include/core_sc300.h index 8bd18aa3..3e8a4710 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/core_sc300.h +++ b/Firmware/ThirdParty/CMSIS/Include/core_sc300.h @@ -1,40 +1,30 @@ /**************************************************************************//** * @file core_sc300.h * @brief CMSIS SC300 Core Peripheral Access Layer Header File - * @version V4.30 - * @date 20. October 2015 + * @version V5.0.6 + * @date 04. June 2018 ******************************************************************************/ -/* Copyright (c) 2009 - 2015 ARM LIMITED - - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - Neither the name of ARM nor the names of its contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - * - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------------------------*/ - +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) #pragma clang system_header /* treat file as system include file */ #endif @@ -70,53 +60,15 @@ @{ */ +#include "cmsis_version.h" + /* CMSIS SC300 definitions */ -#define __SC300_CMSIS_VERSION_MAIN (0x04U) /*!< [31:16] CMSIS HAL main version */ -#define __SC300_CMSIS_VERSION_SUB (0x1EU) /*!< [15:0] CMSIS HAL sub version */ +#define __SC300_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __SC300_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ #define __SC300_CMSIS_VERSION ((__SC300_CMSIS_VERSION_MAIN << 16U) | \ - __SC300_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + __SC300_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ -#define __CORTEX_SC (300U) /*!< Cortex secure core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __CSMC__ ) - #define __packed - #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ - #define __INLINE inline /*!< inline keyword for COSMIC Compiler. Use -pc99 on compile line */ - #define __STATIC_INLINE static inline - -#else - #error Unknown compiler -#endif +#define __CORTEX_SC (300U) /*!< Cortex secure core */ /** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all @@ -128,7 +80,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) #if defined __ARM_PCS_VFP #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -143,7 +95,7 @@ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif -#elif defined ( __TMS470__ ) +#elif defined ( __TI_ARM__ ) #if defined __TI_VFP_SUPPORT__ #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" #endif @@ -160,8 +112,8 @@ #endif -#include "core_cmInstr.h" /* Core Instruction Access */ -#include "core_cmFunc.h" /* Core Function Access */ +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + #ifdef __cplusplus } @@ -191,7 +143,7 @@ #endif #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4U + #define __NVIC_PRIO_BITS 3U #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" #endif @@ -308,9 +260,11 @@ typedef union struct { uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t _reserved1:8; /*!< bit: 16..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ uint32_t C:1; /*!< bit: 29 Carry condition code flag */ @@ -336,12 +290,15 @@ typedef union #define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ #define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ -#define xPSR_IT_Pos 25U /*!< xPSR: IT Position */ -#define xPSR_IT_Msk (3UL << xPSR_IT_Pos) /*!< xPSR: IT Mask */ +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ #define xPSR_T_Pos 24U /*!< xPSR: T Position */ #define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + #define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ #define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ @@ -599,6 +556,60 @@ typedef struct #define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ #define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + /* SCB Hard Fault Status Register Definitions */ #define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ #define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ @@ -966,7 +977,7 @@ typedef struct */ typedef struct { - __IOM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ uint32_t RESERVED0[2U]; __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ @@ -977,7 +988,7 @@ typedef struct __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ uint32_t RESERVED3[759U]; - __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ uint32_t RESERVED4[1U]; @@ -1047,8 +1058,11 @@ typedef struct #define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ /* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0U /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY_Pos*/) /*!< TPI ITATBCTR2: ATREADY Mask */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ /* TPI Integration ITM Data Register Definitions (FIFO1) */ #define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ @@ -1073,12 +1087,15 @@ typedef struct #define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ /* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0U /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY_Pos*/) /*!< TPI ITATBCTR0: ATREADY Mask */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ /* TPI Integration Mode Control Register Definitions */ #define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ /* TPI DEVID Register Definitions */ #define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ @@ -1100,16 +1117,16 @@ typedef struct #define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ /* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_MajorType_Pos 4U /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -#define TPI_DEVTYPE_SubType_Pos 0U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ #define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + /*@}*/ /* end of group CMSIS_TPI */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) /** \ingroup CMSIS_core_register \defgroup CMSIS_MPU Memory Protection Unit (MPU) @@ -1319,18 +1336,18 @@ typedef struct /** \brief Mask and shift a bit field value for use in a register bit range. \param[in] field Name of the register bit field. - \param[in] value Value of the bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. \return Masked and shifted value. */ -#define _VAL2FLD(field, value) ((value << field ## _Pos) & field ## _Msk) +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) /** \brief Mask and shift a register value to extract a bit filed value. \param[in] field Name of the register bit field. - \param[in] value Value of register. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. \return Masked and shifted bit field value. */ -#define _FLD2VAL(field, value) ((value & field ## _Msk) >> field ## _Pos) +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) /*@} end of group CMSIS_core_bitfield */ @@ -1342,7 +1359,7 @@ typedef struct @{ */ -/* Memory mapping of Cortex-M3 Hardware */ +/* Memory mapping of Core Hardware */ #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ #define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ @@ -1361,7 +1378,7 @@ typedef struct #define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ -#if (__MPU_PRESENT == 1U) +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif @@ -1392,6 +1409,46 @@ typedef struct @{ */ +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + + /** \brief Set Priority Grouping \details Sets the priority grouping field using the required unlock sequence. @@ -1401,7 +1458,7 @@ typedef struct priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. \param [in] PriorityGroup Priority grouping field. */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ @@ -1420,121 +1477,178 @@ __STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) \details Reads the priority grouping field from the NVIC Interrupt Controller. \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) { return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); } /** - \brief Enable External Interrupt - \details Enables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) { - NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** - \brief Disable External Interrupt - \details Disables a device-specific interrupt in the NVIC interrupt controller. - \param [in] IRQn External interrupt number. Value cannot be negative. + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) { - NVIC->ICER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } } /** \brief Get Pending Interrupt - \details Reads the pending register in the NVIC and returns the pending bit for the specified interrupt. - \param [in] IRQn Interrupt number. + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not pending. \return 1 Interrupt status is pending. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Pending Interrupt - \details Sets the pending bit of an external interrupt. - \param [in] IRQn Interrupt number. Value cannot be negative. + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) { - NVIC->ISPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Clear Pending Interrupt - \details Clears the pending bit of an external interrupt. - \param [in] IRQn External interrupt number. Value cannot be negative. + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) { - NVIC->ICPR[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL)); + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } } /** \brief Get Active Interrupt - \details Reads the active register in NVIC and returns the active bit. - \param [in] IRQn Interrupt number. + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. \return 0 Interrupt status is not active. \return 1 Interrupt status is active. + \note IRQn must not be negative. */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) { - return((uint32_t)(((NVIC->IABR[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } } /** \brief Set Interrupt Priority - \details Sets the priority of an interrupt. - \note The priority cannot be set for every core interrupt. + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { - NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } } /** \brief Get Interrupt Priority - \details Reads the priority of an interrupt. - The interrupt number can be positive to specify an external (device specific) interrupt, - or negative to specify an internal (core) interrupt. + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. \param [in] IRQn Interrupt number. \return Interrupt Priority. Value is aligned automatically to the implemented priority bits of the microcontroller. */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) { - if ((int32_t)(IRQn) < 0) + if ((int32_t)(IRQn) >= 0) { - return(((uint32_t)SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); } else { - return(((uint32_t)NVIC->IP[((uint32_t)(int32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); } } @@ -1591,11 +1705,42 @@ __STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGr } +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + /** \brief System Reset \details Initiates a system reset request to reset the MCU. */ -__STATIC_INLINE void NVIC_SystemReset(void) +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) { __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ @@ -1613,6 +1758,31 @@ __STATIC_INLINE void NVIC_SystemReset(void) /*@} end of CMSIS_Core_NVICFunctions */ +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + /* ################################## SysTick function ############################################ */ /** @@ -1622,7 +1792,7 @@ __STATIC_INLINE void NVIC_SystemReset(void) @{ */ -#if (__Vendor_SysTickConfig == 0U) +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) /** \brief System Tick Configuration @@ -1665,8 +1835,8 @@ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) @{ */ -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5U /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ /** diff --git a/Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h b/Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h new file mode 100644 index 00000000..01422033 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/mpu_armv7.h @@ -0,0 +1,270 @@ +/****************************************************************************** + * @file mpu_armv7.h + * @brief CMSIS MPU API for Armv7-M MPU + * @version V5.0.4 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2017-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV7_H +#define ARM_MPU_ARMV7_H + +#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes +#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes +#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes +#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes +#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes +#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte +#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes +#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes +#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes +#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes +#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes +#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes +#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes +#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes +#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes +#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte +#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes +#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes +#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes +#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes +#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes +#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes +#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes +#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes +#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes +#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte +#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes +#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes + +#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access +#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only +#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only +#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access +#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only +#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access + +/** MPU Region Base Address Register Value +* +* \param Region The region to be configured, number 0 to 15. +* \param BaseAddress The base address for the region. +*/ +#define ARM_MPU_RBAR(Region, BaseAddress) \ + (((BaseAddress) & MPU_RBAR_ADDR_Msk) | \ + ((Region) & MPU_RBAR_REGION_Msk) | \ + (MPU_RBAR_VALID_Msk)) + +/** +* MPU Memory Access Attributes +* +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +*/ +#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \ + ((((TypeExtField ) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \ + (((IsShareable ) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \ + (((IsCacheable ) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \ + (((IsBufferable ) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \ + ((((DisableExec ) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \ + (((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \ + (((AccessAttributes) ) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \ + ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size) + +/** +* MPU Memory Access Attribute for strongly ordered memory. +* - TEX: 000b +* - Shareable +* - Non-cacheable +* - Non-bufferable +*/ +#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U) + +/** +* MPU Memory Access Attribute for device memory. +* - TEX: 000b (if non-shareable) or 010b (if shareable) +* - Shareable or non-shareable +* - Non-cacheable +* - Bufferable (if shareable) or non-bufferable (if non-shareable) +* +* \param IsShareable Configures the device memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U)) + +/** +* MPU Memory Access Attribute for normal memory. +* - TEX: 1BBb (reflecting outer cacheability rules) +* - Shareable or non-shareable +* - Cacheable or non-cacheable (reflecting inner cacheability rules) +* - Bufferable or non-bufferable (reflecting inner cacheability rules) +* +* \param OuterCp Configures the outer cache policy. +* \param InnerCp Configures the inner cache policy. +* \param IsShareable Configures the memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) & 2U), ((InnerCp) & 1U)) + +/** +* MPU Memory Access Attribute non-cacheable policy. +*/ +#define ARM_MPU_CACHEP_NOCACHE 0U + +/** +* MPU Memory Access Attribute write-back, write and read allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_WRA 1U + +/** +* MPU Memory Access Attribute write-through, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WT_NWA 2U + +/** +* MPU Memory Access Attribute write-back, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_NWA 3U + + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; //!< The region base address register value (RBAR) + uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + __DSB(); + __ISB(); + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DSB(); + __ISB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + MPU->RNR = rnr; + MPU->RASR = 0U; +} + +/** Configure an MPU region. +* \param rbar Value for RBAR register. +* \param rsar Value for RSAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr) +{ + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rsar Value for RSAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr) +{ + MPU->RNR = rnr; + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Memcopy with strictly ordered memory access, e.g. for register targets. +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void orderedCpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + while (cnt > MPU_TYPE_RALIASES) { + orderedCpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize); + table += MPU_TYPE_RALIASES; + cnt -= MPU_TYPE_RALIASES; + } + orderedCpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize); +} + +#endif diff --git a/Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h b/Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h new file mode 100644 index 00000000..62571da5 --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/mpu_armv8.h @@ -0,0 +1,333 @@ +/****************************************************************************** + * @file mpu_armv8.h + * @brief CMSIS MPU API for Armv8-M MPU + * @version V5.0.4 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2017-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV8_H +#define ARM_MPU_ARMV8_H + +/** \brief Attribute for device memory (outer only) */ +#define ARM_MPU_ATTR_DEVICE ( 0U ) + +/** \brief Attribute for non-cacheable, normal memory */ +#define ARM_MPU_ATTR_NON_CACHEABLE ( 4U ) + +/** \brief Attribute for normal memory (outer and inner) +* \param NT Non-Transient: Set to 1 for non-transient data. +* \param WB Write-Back: Set to 1 to use write-back update policy. +* \param RA Read Allocation: Set to 1 to use cache allocation on read miss. +* \param WA Write Allocation: Set to 1 to use cache allocation on write miss. +*/ +#define ARM_MPU_ATTR_MEMORY_(NT, WB, RA, WA) \ + (((NT & 1U) << 3U) | ((WB & 1U) << 2U) | ((RA & 1U) << 1U) | (WA & 1U)) + +/** \brief Device memory type non Gathering, non Re-ordering, non Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGnRnE (0U) + +/** \brief Device memory type non Gathering, non Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGnRE (1U) + +/** \brief Device memory type non Gathering, Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGRE (2U) + +/** \brief Device memory type Gathering, Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_GRE (3U) + +/** \brief Memory Attribute +* \param O Outer memory attributes +* \param I O == ARM_MPU_ATTR_DEVICE: Device memory attributes, else: Inner memory attributes +*/ +#define ARM_MPU_ATTR(O, I) (((O & 0xFU) << 4U) | (((O & 0xFU) != 0U) ? (I & 0xFU) : ((I & 0x3U) << 2U))) + +/** \brief Normal memory non-shareable */ +#define ARM_MPU_SH_NON (0U) + +/** \brief Normal memory outer shareable */ +#define ARM_MPU_SH_OUTER (2U) + +/** \brief Normal memory inner shareable */ +#define ARM_MPU_SH_INNER (3U) + +/** \brief Memory access permissions +* \param RO Read-Only: Set to 1 for read-only memory. +* \param NP Non-Privileged: Set to 1 for non-privileged memory. +*/ +#define ARM_MPU_AP_(RO, NP) (((RO & 1U) << 1U) | (NP & 1U)) + +/** \brief Region Base Address Register value +* \param BASE The base address bits [31:5] of a memory region. The value is zero extended. Effective address gets 32 byte aligned. +* \param SH Defines the Shareability domain for this memory region. +* \param RO Read-Only: Set to 1 for a read-only memory region. +* \param NP Non-Privileged: Set to 1 for a non-privileged memory region. +* \oaram XN eXecute Never: Set to 1 for a non-executable memory region. +*/ +#define ARM_MPU_RBAR(BASE, SH, RO, NP, XN) \ + ((BASE & MPU_RBAR_BASE_Msk) | \ + ((SH << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk) | \ + ((ARM_MPU_AP_(RO, NP) << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk) | \ + ((XN << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk)) + +/** \brief Region Limit Address Register value +* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended. +* \param IDX The attribute index to be associated with this memory region. +*/ +#define ARM_MPU_RLAR(LIMIT, IDX) \ + ((LIMIT & MPU_RLAR_LIMIT_Msk) | \ + ((IDX << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | \ + (MPU_RLAR_EN_Msk)) + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; /*!< Region Base Address Register value */ + uint32_t RLAR; /*!< Region Limit Address Register value */ +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + __DSB(); + __ISB(); + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DSB(); + __ISB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} + +#ifdef MPU_NS +/** Enable the Non-secure MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable_NS(uint32_t MPU_Control) +{ + __DSB(); + __ISB(); + MPU_NS->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB_NS->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif +} + +/** Disable the Non-secure MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable_NS(void) +{ + __DSB(); + __ISB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB_NS->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU_NS->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} +#endif + +/** Set the memory attribute encoding to the given MPU. +* \param mpu Pointer to the MPU to be configured. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttrEx(MPU_Type* mpu, uint8_t idx, uint8_t attr) +{ + const uint8_t reg = idx / 4U; + const uint32_t pos = ((idx % 4U) * 8U); + const uint32_t mask = 0xFFU << pos; + + if (reg >= (sizeof(mpu->MAIR) / sizeof(mpu->MAIR[0]))) { + return; // invalid index + } + + mpu->MAIR[reg] = ((mpu->MAIR[reg] & ~mask) | ((attr << pos) & mask)); +} + +/** Set the memory attribute encoding. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttr(uint8_t idx, uint8_t attr) +{ + ARM_MPU_SetMemAttrEx(MPU, idx, attr); +} + +#ifdef MPU_NS +/** Set the memory attribute encoding to the Non-secure MPU. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttr_NS(uint8_t idx, uint8_t attr) +{ + ARM_MPU_SetMemAttrEx(MPU_NS, idx, attr); +} +#endif + +/** Clear and disable the given MPU region of the given MPU. +* \param mpu Pointer to MPU to be used. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegionEx(MPU_Type* mpu, uint32_t rnr) +{ + mpu->RNR = rnr; + mpu->RLAR = 0U; +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + ARM_MPU_ClrRegionEx(MPU, rnr); +} + +#ifdef MPU_NS +/** Clear and disable the given Non-secure MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion_NS(uint32_t rnr) +{ + ARM_MPU_ClrRegionEx(MPU_NS, rnr); +} +#endif + +/** Configure the given MPU region of the given MPU. +* \param mpu Pointer to MPU to be used. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(MPU_Type* mpu, uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + mpu->RNR = rnr; + mpu->RBAR = rbar; + mpu->RLAR = rlar; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + ARM_MPU_SetRegionEx(MPU, rnr, rbar, rlar); +} + +#ifdef MPU_NS +/** Configure the given Non-secure MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion_NS(uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + ARM_MPU_SetRegionEx(MPU_NS, rnr, rbar, rlar); +} +#endif + +/** Memcopy with strictly ordered memory access, e.g. for register targets. +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void orderedCpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table to the given MPU. +* \param mpu Pointer to the MPU registers to be used. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_LoadEx(MPU_Type* mpu, uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + if (cnt == 1U) { + mpu->RNR = rnr; + orderedCpy(&(mpu->RBAR), &(table->RBAR), rowWordSize); + } else { + uint32_t rnrBase = rnr & ~(MPU_TYPE_RALIASES-1U); + uint32_t rnrOffset = rnr % MPU_TYPE_RALIASES; + + mpu->RNR = rnrBase; + while ((rnrOffset + cnt) > MPU_TYPE_RALIASES) { + uint32_t c = MPU_TYPE_RALIASES - rnrOffset; + orderedCpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), c*rowWordSize); + table += c; + cnt -= c; + rnrOffset = 0U; + rnrBase += MPU_TYPE_RALIASES; + mpu->RNR = rnrBase; + } + + orderedCpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), cnt*rowWordSize); + } +} + +/** Load the given number of MPU regions from a table. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + ARM_MPU_LoadEx(MPU, rnr, table, cnt); +} + +#ifdef MPU_NS +/** Load the given number of MPU regions from a table to the Non-secure MPU. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load_NS(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + ARM_MPU_LoadEx(MPU_NS, rnr, table, cnt); +} +#endif + +#endif + diff --git a/Firmware/ThirdParty/CMSIS/Include/tz_context.h b/Firmware/ThirdParty/CMSIS/Include/tz_context.h new file mode 100644 index 00000000..0d09749f --- /dev/null +++ b/Firmware/ThirdParty/CMSIS/Include/tz_context.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * @file tz_context.h + * @brief Context Management for Armv8-M TrustZone + * @version V1.0.1 + * @date 10. January 2018 + ******************************************************************************/ +/* + * Copyright (c) 2017-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef TZ_CONTEXT_H +#define TZ_CONTEXT_H + +#include + +#ifndef TZ_MODULEID_T +#define TZ_MODULEID_T +/// \details Data type that identifies secure software modules called by a process. +typedef uint32_t TZ_ModuleId_t; +#endif + +/// \details TZ Memory ID identifies an allocated memory slot. +typedef uint32_t TZ_MemoryId_t; + +/// Initialize secure context memory system +/// \return execution status (1: success, 0: error) +uint32_t TZ_InitContextSystem_S (void); + +/// Allocate context memory for calling secure software modules in TrustZone +/// \param[in] module identifies software modules called from non-secure mode +/// \return value != 0 id TrustZone memory slot identifier +/// \return value 0 no memory available or internal error +TZ_MemoryId_t TZ_AllocModuleContext_S (TZ_ModuleId_t module); + +/// Free context memory that was previously allocated with \ref TZ_AllocModuleContext_S +/// \param[in] id TrustZone memory slot identifier +/// \return execution status (1: success, 0: error) +uint32_t TZ_FreeModuleContext_S (TZ_MemoryId_t id); + +/// Load secure context (called on RTOS thread context switch) +/// \param[in] id TrustZone memory slot identifier +/// \return execution status (1: success, 0: error) +uint32_t TZ_LoadContext_S (TZ_MemoryId_t id); + +/// Store secure context (called on RTOS thread context switch) +/// \param[in] id TrustZone memory slot identifier +/// \return execution status (1: success, 0: error) +uint32_t TZ_StoreContext_S (TZ_MemoryId_t id); + +#endif // TZ_CONTEXT_H diff --git a/Firmware/Board/v3/Drivers/CMSIS/Lib/libarm_cortexM4lf_math.a b/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a similarity index 100% rename from Firmware/Board/v3/Drivers/CMSIS/Lib/libarm_cortexM4lf_math.a rename to Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a diff --git a/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a b/Firmware/ThirdParty/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a new file mode 100644 index 0000000000000000000000000000000000000000..36c7461ce1aaf016d7bea61cecf5affc49b10408 GIT binary patch literal 3082154 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

~TcuFYnj z>$BNi#%%VQIh#FU&1P5Z+3XW%Hha*W&EEB9v*-QUY&4k7z9v{!$jd)@!I+gemQ_WT z6=jxH6qc1$mgO{-)pVBKZm{fSlVy)uEW2p4>_dlT_qi;4%VXIyKFe+dEc=3(W4BUs z>_ujdJ)YQbL=yHj@@a@u~*GG_P902F57eLV`q*% z;LfpkygBxqKgaUH9Q%rxXSY!E>;-0?J;cqk_r!U2w>-~YSLWH1>O8xu&9hJSd3J{} z&t5U-*<;o`yJXL^kDPgSzdO&~_U75M{ye)G%(E|v1$G;?z+Pe&*dyEm`#@Y^_sR?G zO=W>StuCfjwa@uq*Zg`@~sb54sENU2lOs?=P^?V1a#2ELsA! zXvLUCOX3zSRa~@0dC^joMN3u}t^8cq()2}ZyRm3pHW#g<_M-LBS+w@Ki`Fe~(K_QV zS{uQl^@3Qkwo*&hMP|u5%q>~>#U*Qxyky-_maJ3ilC`ESSj<}EJrGx{ zz4D56Q(3W2t1H&JwqiZkSFByeignFgu};`4))QyNI_R!gcfA$syuV^agB9yF!Knhp zsWFCAC5}^7kyAyPQx%0%WsOrcom00PoO;>h)T1`1K6E&BpUbJYJWf61bLvLGsV|6C zbt|>1USw9)!`!NRUtCr9$gAoNWmP?;uBvO=s`^Y{Rd*Vz>Q!@9J#Me6kDXQZfV-;R z@mAGy{;J9ctLiIaP2EDRsTY_v^$@qF-V@i<-SV1xU0G94YHR9KeNEkAtf^PbHT9Ui zrap4k)cx+7dfQu5&-!cXX0WEdB-Yh!)Vg|!Syzv6>*@n>UEM3Mt2dQ(^|ZFGKG)aP zUBt>T6=d7pM(C#%%Z!x8bYehA+w+zM^dS zvbN!C`i8&V*zhl#8~#yy!++>(`1{-q|CYDmp9wbn7sRH&mD=<#GMoNkZqvUnZu)!V zP5*|n>7UXz{b%~7zth}>i6+)e+Ex9OkrH~swl-+x8${uYY&FEG4+ zh~xcxBJb~(dH=e?`zJNtf2#BT4ukivn7n_?=KV(w@9%ec|F*~bX9M1UNksi^RMfx3 zMExUN)PEpG{k?M3&%1~H(^}Mju1Eb{M%2G%M*S0Z)PLec{ey1Qzw1T)^Fh>qO$kJd z5eSJB2vrmaQ5FbA5eQim2u&A=?S?>HHU;9SEf5bKf!OB?#4S%C&IAJSg4jZArM3_k znJvU&ZVPc=+(PV;w-7g!EyO8p3-L_fLhLlQ5LeAD#BqBI@z~iy9B{V~cf2jcxnK+N zirPwCV73y6xUIxJaVxP~-b!3owh||`t;AD(E3w1aN?b9w636VV#3N@bvESWF-1fE- zXM?T8OKKZ&iP=UR;kFSE#BIc0c^h$4*+!h!wh_5l@_L#6fo( zao5{MoDa4Uuc_^NjM=VB+;&|Rx9g(3U00Otx~y&2HDkMe+1#!lwYTdJo$dNQce{Se z+peDpw(Bpb9r{IPhklsbq2CvG=zHWH`VD1=eoEV+Kht;UJB=OsRda`a+}@!-c6R6o z+#UKIZ-;&^*rC6ocIp?Ho%$her+!b|sqdC|>erQ>`bll4{?yp1Uom&;$LyW@BWI_+ z-`%O-_IB!LgPrmmwr>(rJvSz>CcT_`ZaTxe!||RKXG>H z2i;xzU2m6uKG>zdrbIEuh@!-aqAH7`qKKlbiK1qR;$>45kJ_U6&=JLbt|;E}MDa`@ ziZ7_$;zee+c$nKQ-WPX^d*t2X4Q01@O4}_yGj@wt&E4X0d$;)5*)1M$cZ+wt-Qu}m zxA=A4m!G7lzb-=m69B>YC2b_EI0q44M zz&WWMaGn|moGa!5=a_xKdE_2&ZhHruv%vx9C3Vob#2j>va0i_S@ z2c2u?LFa^h(0Sq>bnbcwo%6v#=QSlUF-~GsSz;7LVq{HXG(%!8n-X)>mY9dG#N6^E z=1d?lFQ`MzMdlE5m^;MWmk%*FltauZ?GW?KIK*5v4>8B>L(F6M5Oc>n#GDHbF|Vk@ z%mwZ+b5B0ZTvrY=C$+=OQ{ymm#XQU$vkxHj+xJlW9C)!n0eejW+BuQaf%wHIAEC?BnJm_qci6J8qs0j+-y36XqrEg!w=|Vct|u zn5VT9=5ynOdCfjyK5XMKtP3)Exx0(B?2K)vEFQupMG)OGD5_0+gXU9m4xkKBvY?cgHylDkAb zkS|d;wM*1<;}UhvzC=B7FHv`cOVn%bvZ2bC4Mn?bX!d2}p?lf56I>+&=Ey8PI^F5d~R%dfZ_ z@;&W_{M5c7KXPx#w}Tt<;HLbVyXC6dEmyN|xetR|?hEd= zdtbZlKC^GTkKNnuo#3|nio4_9)9$!W?K|$H;Ewx}yX!vC?z+$IyY7?VuKQY3In7qN zhk?qy;O=qvwR_w%`yTf=xW~QH?sHG=``n}8KKD|4z&*Dga8H5<+-vQjtpyM57uqBH znf=Iq96YjLX^-uv!DIWS_QZZ3Jh5K~n)X6_syz#yYOjK4+RNa%_BwbOyo!W>#UaB0 zId;f$0MijNEx@*fY!BpnA>#zBQ^uQIFVR{C{ZF}i5k)KyBVGQPR48`86iu| zh*V-l68XK1*^*p@D)Az6i61fY`xxC4rX*IvmL&6g7;`0RiCLnRcqMj;kJeCz`x1bm z2w94C$W$O+0%R?Kxd_<{;3Ys7V*@f7z-EMu2I3_^b^~|`kmU&XIRM)cG9HMR0ND>< zKtdJ-n2=FmL*{u2kQD)50%S*kmjGE3U`j%^1Q?T$H38-%>`MR!C1g>6NeS5$U{pd@ z1(=nPT>*wAWLY*L(*kTu$haV0LKN7SkbyzG1jxn!BeU>1L%amY(g0HvvNgcigscrP zHz9ko2^k#3OMq++FghWt6YlH4`#TUX0kS>7_=KzvFh3#t0}N2e0s$`pvOzp#gz{M- zV1`0=2pFP}CE_7dgm?*%H3H@+WRG~rAR%4?WRrkV3Rxv!mO^$37^aYA0;VZsn}Bf& zStnqgLiP!G36O;XUIJvJfRS2wz7Q_~vQ)rJfNT{oRv~Kz%vH!<0fQB?SUhC15HA6; zT0CU75HA6;T)=dNY!?q1FT_iL>=*x+Z|_S0CM;ybfDsE>F<{0*b_^J@kR=0N0_;lw zUIJvzc*vXqdloWih?fA_G+@+1R*i?u8sa5DmJOJ;kZt23;|8o-$h;w50%YNUmjKx~ z9x`(HTtU19$kG8*7qWH0*oCYe51Bi}OMomM@Dd=K2fPHx>H#kSvU|Yrg)AR1eIeTi zj9A%Teu*+}3e6wXt`OMomT zFqI)&35;dPS^{$!vX{VMhAbv9nIW4AjAqDc0<#&io4{~}EGICXA=?RzXUKX2^BJz)OIvFYpo|`wI+k$N~cs9J0Yt$Or=~95Ta* zmjGE}V2VSwI0_kKV2z`F2~o%(1B)Co$%vN#S!H0BLv|S$=8$Cura5GrfpHF5XJDQ~ z_8A!HkcEyyCK~Y)3il`CB|w%MnCel)ONat%9qmg120LW2fyoZpY+$rQRvVb@kll_# zh8tM!km*Le1ju>=^BuC^z)OHEIPel68xD+k$cjfHGmdx(kR=DEJY>s(F%MaDV9rDK zJPH|fV9`S+9q|$%s}9V1$gTs!9F4$OPVz5@e4ig*c-jR#&rp>6}z%GCt2Ea0aod&=*fE@=>#7hA70qj5k76R-pc+C(mA$%?2HATFH@U@259Ptvu_X6G%#7hX@D|pWkFCly{;XOsXgz&wF_Z-)W zh?fBS62i|2;w6Ni9keeY{7fNULikyO&m7_-vxt`vewN`gjd%&L zFCqNQBVI!IEWk5?cnRUN0?!QMCBVLf@R>rqgz#B|XAbca!ejmb zUP5?w!5M~l3E|lWXB^@sgl8X|fryt7o{exuB3?pxcETBocnPpCAv|N@tc5cd@e;zb z7|vwGO9;!~VB-KUA?zWbHb=Y!*q0FA5r~%n`x3%C1o0BWy9Mn_ z2=AOcD>1x-5HBITo8XQ@yoB)Xf;$ZH5`YC1-f@VR5Z--o2O?fVcsIfw33nyjnTVGV z-lcG-B3?px*TS8PcnRTM40kf(C4_f1+}Vhi5Z>i*rz2hhk9Y}%ehBdr!rB081jI`S zYX_(y5HBIDEuh9gyo9j!fEonx62jU9Y81pv2x}LpVGu7NtZksiLA->p_JJA*@e;z? z2x=t6O9*QxsG$%qA*`*S#zMS=u=au)4Dk}e+6-zm#7hWkH>lwdFCnb$pvFVIghC&P zcnM)`2sI+&C4{vj)R2gm5Z0DZVmgs@hJnjP^H!df0` zdc;eBeF?xzDD=OGmk{;_&?6vTLfAV%4}o|IVQ&FF2I3`zy$AFlh?f8?x3EV+yo9iK zfgT3&62jgFdK|<{2zwvsfemjJv3*q0FYV2GCh z`x3$)4e=7f-VJ&<#7hW!JLvHcFCm;UK<|xs37d$Q5cY~_UqaYJB3?q+TSAYCcnM+e z2|Xy{C4{{x+LsXatcaHo_Oj5^B3?q+>q5_qcnM)I3_UU8C4{{)^vsBt5cbk&UqaYp zBVGcq3d0^8@e;z`9C~!bO9*>+=;0AB0rn+?JwD(z&s1g zx4^s$%)h`q49v&Ca~{mkz&s7i*TB3D^m!`qIMC-U+?9y-C4~GA%=5r}56t_({140n z!F&+R3&H#l%oD+U5zHIG{1MC}!F&?TE5ZB{%rn7!6U;lo{1ePW!F&`v-@^P9%u~U9 z6?A?S_$!#lg83|%*MiQY0?!5WT`=zjdVk=*U>*$S!(d(v=Eq>34Cc#V-VEl?U>*(T z(_mf==GS1J4d&Zm-VNs8U>*+U*N=J#Np59a$| z-VeIZ3Opdp2g1A{%nyRzA9zCOzAW&DFns;3J)GR!A~-XC~n zm|uo@W~g2-@Xk;@pT;~i%tyoXYs^o>JhePq9r}d=Zw>R;FpmxM*)XpS^V=}b4fEYF z?+x?cFb@v%;V>@_^W!j24)f(OZw~Y4Fpm!N=`gPj^Xo9r4)g6W?+)|tFb@y&@$lRp z^YbuI5A*dfZx8eLFpm%O`7o~!^ZPK*5A*#{|61VxVICmn17cnv<_DtwxWE_0yg|$# z#5_XGC&au$%rC?|L(DhCyhF@C#5_dQ?-zK9XdaNpJVi7wNTc~dfxn1(jA-6a;5DLo zM1kjs<`o6rBbsNxz66*7q%j{7^CHnaq`;Fz^O7{?O=A8e=22okCFWJ4c}#(4iRLv0 z-X)sn6nL0u-jl{U4d!QJo+g?X!M+5T0TlR~Xuee7bE5fEf!~SdQw6>!nqL+8pJ={S z;De(1SAidjd7@}uR^W|d{wSKS75JoR{+7o4QZ%0{@J-SDuE0M<^SuHe73))&pNi&# z1->ep9~StlXug=nd{#7nEbv>=e6qlIMf1x7{}s(Q)0hv7=AQ+AESis|F<%z*W-)&j z%~uP2TFk3O^VkB<7R_r5yjwKSg?$My14v^&F4pBRKNs_K(Y(08+eP!_0*@EXn+v>N zG>=YWo-dkL!@dNV0i-ei7xRG8yc_luuZS4R3lfxnD-%$U!N^oIh!8R-)RzBAG<3jAlJ zZxr~@NdGACqme#R;7cR@q`;p>`bvRMjr14Tmk{!+kv>!4TO<9Zz`sWNPJxe&by&>L zM*2{JuZ{Gh0)HFnOKHsKM*344^Sd$68|hVPq+b>I-$>s|BfYD@3rBibxGNFuO8~vB zz#B(;T7gH7`Q%7{EAY#aK3CwIBmFLo`RAC2j``?FUoY^}kv>@9t0Voez+XrDVu8<& z^hem205gCz=DB0OJJK%;{CA{prjg#6M*1i0OMn?b8tJ12zC6-T(@0M(@aU1=THw_q zJ+{EJM|y36caQYk0uLYa@v-iX^xy(dAL+#f-agWk3p{?LHy3#QNRLhEDI@0eGJP(#s3`29Tbf#`_5HegdSw7xow6eFjLcFYG(O`wx)5U)Ya8 zL?R>^i4asILgoDiwLJ4M zp7$r{kw{Y_5~(&KkF^_w z#vl^uCraWZSrR9xk~l?|#LJnIcq>~Huj5MMJwi#mR4j=%OC|9dxg_2dD~VSsCGmE( zB;KHv#QXG;cvGSzUTu`bJCh~x3bQ2MW|hS2?UHzJrX*hGl*C)ICGlFfB;K7XiC1|g z@eaQv-WZg``-xJ4BufQ?DitWYR48Xkg;usysN+h79wa6kV1nXUa0IY+0s`E6enVWtnEFEK?(wWx8TznM$QB)2^0f z8nm)ZpI(+}N|a@)jj~KCTm9s=TsHhhLUy z49YV7L^(~82|f8ZqUl;tEJN$}dV^ERoCn{qkSs5d!${58|##-6R zSRGdx>k%ts%~EBoMy`x?DV4EywKCSARmS@C%2-pPGFEL=#yZW)SesQDtG6p-y-sDU zC0iM*bt_}txyo3TR~hT@D`Sm8Wvrj5%8_JMj-aY?6jPOJWvg;^Tve_|tjaY@Rk<3u zD%Yh{<=WM%T!U7X>(i@pO^K>pwNaJpG^=uLR#mRvuFCZ~Rk@aIRj$^p%5{5HxemW7 z*BDgg`U!%}vkpm$A;?yiAnQ1S>=6mFSt7_9nIO9qf^1g_vOy!rKAj+&5(HUo5M-xG zkZl%0*4qTx>kwp1mLO|gg6#GPvco6H#(*IEiRuJNRVOH>I?>8jC+fKBM2}dVXqKuI zHF9+#&lgCvtJR4Htvb=CS0|c`>O`kmooKVF6ZLj=qSvWTv}CIjwQhBy+pA7=_|=KV zpgPe{)JS=bp+qq?QY%{{)p0dak60r$%QaG$QX{piHBy6CBlYPuQj<|5b(%F&n^hy# z+ci?JQzNxxYouDYM(Xxzqz=DEY7Aswb?qZHrpfCW}D^OY?o4- zZC7iv4O(rsPp{238MWC?vo_mi)n@DM+H9{=n{9Dxv)x{8w!^Q@HU_oXexi=evk6&> zsbgEYI<`lwW1HnVwo9pF+toU@L91i?^g6c5sAD_LI=0QKW9#iYw%4g+TiiOf+pA+c z{5rNVsAKzydW)p$EsCkPTDf|wN36G+<$5ch>9^X|daFUJxBB#YtI4RhI?Z~k&91k4 zoqDUqt+%?pdaJ{)w;F?btDk64^PEAIVj9#|u0ib)8`NgGLG4l+)OM{w?b937CZj>^ zG#k`5yFu-B8q^lILGAV$)DFKvZ44UJexlJQsYah-8vRzT(eDu({bsq*?@}85cCFFx z(;NLJqtWj)8~rxB(eHH{{T8>;@Aew~j-b)+r$~ZgNTQV^i5`(8nq`vcQb?j*BZ)qp zB$^D8=rl>9%_fOnha_5DlIZqGq9Y)QeyT~Qm?piIYtnnfCcRm1((}1^yN z(`?e)>?Xa}Y0_KVCcWEh(mR4Cy`O3pDW+L$<(kDFxmoN|n#FdlS?n{K#ZI$XY_pri zUZ+`Xaht_%uUYH}n#F#q#i5uMr8Cn8itF%t+t%F4zJVh@Os@2uRG}Q`ZUAlq zJD{k3uG8p|JB==_)9AB1jb69Y=nguKey&T-^XB9}yG!nMyX5YmOYY~o-5#ym?X$bx z-k{s<*Lt`yLzg#iM`G_@`@cz0E)leSDw({0G+${9k@P z_Q(&`TmCO!U+KThZmj+=WGROlL4>`y@#+7+==0-N-?i}@|6cU_^_=>}jo`aQ-`{_K z_JEi=`;nrb@Zu?DQ~O}iSNN@ec)0nQcNP7Gx9s@l?N>#g;rsvdotw|^75#=cw!VAw ztxHAU;Xl3eeVgfHMgL*V=YMYVdpnCh#4j}@H{ZQd^dqu|e|hsK$BMqh$q)R-<~Q4m z{=`qc`SHzPs(4-SDSox{4>mvdZ$-c2yYs6B_kBHNdWTxopM3a_Hy6KH^e?{itDoO| z{mGC49%@@o@1Ji@|8mF%4>hl!I_qw3zPIRW{OpJSZu57Z7yXTo|K^*U`jw*3@k5>e zy!q(^MZe?BiT`i&+)BtK4}FhQ@Bfd@bNxmCxLDFZv{Z`dI`26F*k;OMc;lP5kE7qHpr! zA8+MP?k@T#H^11yyOTv9<;`z(@qbuf^iy)>eSF!!7JZc;>l@&I>q{ZaJ=7?FlN;vW z`0b+4^3dTie(t{({g&^(Il&WmLMD8ubzXSKG{1dc(SP};A7=S~7%%!TKl_OV{#%tr zKc@7BW&X%FioVQS-&p18bkU#L_Wce1$cKtP&E2HHfBRw4uUR#^jsK?uMc?NB^_{%P z6#bi&w3}}zc^+!Ipygbx`d%qg8`a}QcnQva;fBbifKG1i5?=t_l?+Drc zp=Lc;uJ9i^QS^n*w%p{?<3)eyuMgbe_xvE#h(e#}`)BX*>t6_W2ZWlqxABmF--nC7 z(O=&Egg<<%=pX(3v1j~~^`ejT_=T5zc}>wzI=CF?|KrV~uXN9+{tJKTV?}@Ipzz~- z%l)Fy^w&Q74t|3#`c1zkzLWn#^^=0{^wVGXN&d5cQS_fm(ogZjzfkm{zU#~H;@?uj z-3p;+;Lh=%;k)OHzSJMO@8*B^`{6Ez&|@%v`oHqO`y#L(K z^8X?geXHrOy^kO3DEe359s2*1s9;@h=y}SL__#fOZ`evV`f1Uq}iK2hDZS6Ptj(-YwaD?8H-Jkd^{!czy^wW|v z7XS6LMPKdBU-=#W8ddby%JlE@r~bU?v;9ls6TJP?MZfJolK+GM@$E(5?K||}=Rf(w za92p^f%*84Z2s&giay*sk3Y%ZzFG9+{*Sjl#sBj_(U<$v7vJE&@@CPW`_FHt_~~~S zeY#)Sm*Ky=rRdicKmBQb_}{~wCZTV4`_dorfBdUO|E^ulGshPx%krFZz4Q`+vrt8!GyIr@rxd{sUhw`hEZG$p6c4{CLs# z`|UsaB7bL7!LY99s7w&Kgy-C0J(f`SR;XOq^u-Novetx6q3%>mNFZil&g}Y!v zkJEp?^%Z{K2Z}!7mQ!!?Wm}4V;a~g{m!Ehm+$j@!rG9h%ulY}ZFx)j0dZ^}p{%`m{ z6N*0Knd-0dJHGw4*9tVnk3RT2o_$}qvnKRnz5A(;CkN?QGzPw!UA%A$k#}5o% z1}!r5jQ!T{evALepA>z`Z0A4oAGlHUCl9=Oi~nK$rGig6{`ddFziAcy%KLWyD?fT9 z-02f~)qdhveg50u3wQm59=7qi@A88meB09oS|)er-}vluxEm<+y#3CX@^kOM40i^F zUby$M|IYu^@SUG1`kg;^@cVr8;iB((@V9=z|CfIacNK*m zyz;vLpj)cr_DtlcJ=qFqnCcU=*Qmte`=ziTP^ysdv?}Ff8`6|E~?OD z`LXZRMoZ5ZecEq+urB&nez?0T^kn`nUmuf?$*+YwwnFb{=NFoy)a|0* zo4wZ@J^KA{Cs*h-{Z3m;l>Et}|6BTGOLR|5(FZP{YmJuuLeURiUfLF2pD6mmkAAEz z`n^wtyTC$^>n^c9IvFkc#J~3K_UJdAaCcbfiKX7%5$!o#^o{?o87lgfzYceeh5qpe zSt?3iD*DLZf3Gv2p$K=Ag`V4E&0SIAe$iKMf4nRDxggwS7J76)b+|iP{krHgKmXnC z=oiY1e)EsMwWz-SujoS`z1 z*dx(>{YAg~I}VLTy$=_C?-So1jn;M*{qM?;jYWU|gW(Rl(3>rEG10f0i+*_PcbMp} zzNhGmx5e?OUtRRapZMB%H1m$4PyRiR@;><=g}d`YPx$HD$>@)?qHjK(oQ&T2Zn$eN z^w0mn(p2=?&7zNf_n%Eg-}*+ln=ka7-#s@S{o(1NuYUZy(^2Eg>ji(k_7gMFPwy%E z>`zm((MO*ScmIW+_Ame1Z1ne6ioW~5+hC(V@tZ~e{fjTM(XS7#7JT@>d44YX`Tth* zAM9N!PVN75L2HJOO-`;4_8xFovHs_{`ya0elwW zGl_f~;b#>-vp8=6pJn(=BcDh3S%=R&&L6c?`Hq;Z8+9s_?FbI~VP<4DVvNlacQ#ysP2P#(536%i&JPxed7M;m*hT zW~c?ACcrrks1=}QKz^^VmVlZ9=Q^O)fSLpC!whQ?s7Y|n18NnhS&*MBtYx64!MP8p zb)e=!{~DK%iEFnhEXC3~MQA=K~IHq zG0~*2%#rYfPg`p=#zSyu= zhMpPcaiEumo*MaR!(JPDZk*47UL1OIFjK*~Aegnl%mwXJ4QDYhlfgM5nAO0{2Kk}GSq{u} zaBc`@Juve@{^@WQ1T!I=BZ65G%#6_f)o_*sGbNlWf>{&HoRB{|oJGM*3g?VqRs}OF z5R>CBjS*=bB*F2s20I zj}K>&Fq6bNCzw^j%o6$O!&xTGG;!_;W}PteME?75778;_oP&Z{Da=f99tviuFjK|3 zD44av%oX|j!&xlMWN}UkX0uD z2xrMKQ${(2aMlbnXPmEsSv1U~QEnleRm030=dECt4Kr<=yMkFa%)D{_3TELj6UR9$ zn3coK9OtoMmJTy@w8u7_wZqIE=d)lI4>Ng`y9j6XFtf*bEtuuQOdsdAVAc;af0W+{ zwE)lraE=SK0?-Ul-Xqi!KvTfEF3=i4b3pl!P>TRf0_VIys{qXc4 zG!K+N3AGT=L~srav=Y!va2^b_6wp*~E)295&|FZyCDdX-lfgML&}u-lL3x-^%K=RX z=f*(m0nG>HXF@FqG$EWL1FZ-&Bb2uZwItA#aIOrrCeWNvJ}17`7_YMKoi3`G|7{ z37RC%xq(&*nkC9pg<2+PnmG3cS|@0pXkT!sg@PuEb8w)Qf@X^HTA`K-nkve1g<30U zt~ehDS}bU?Xs>Xn)q-Y=^Kzi&f~JddVxiUxnlH}Jfffv!Ffe5xM+aIlXvR2C2U;>{ z$|#2xYR#ZI<9r=x(V$7A+*+trgJzBLY@wD7nl{eefz}P0H_qRI77m&?&f$Sp4w^a2 z%Y|AxXzDnZ2UN@Cd4GuWedzls>{$lwKmYyyAS5&K=gVS_CbU8pMO8zz&SzmeiimdqxZ9r8$|DSVc#@p|M|~D zVGlL>ycG6RgZ7{QJQZ?<=<`_ zo`*MZ4iU8f{PXe#&Lg7d=?(OJy#d;P{&{-?=MzEu&p(faJ>BSeeFNte(ewNU&Ml(n zy|C{aod<W7=Xna}CPDkp zpZA4*>*zj6;T$Ds|M~l(u%8{>Cn=n(1nob6-=uK965U5BoU=stRbl@-y3bNLcL~~m z{=Q4${3W^%3wz{2`_JE(h5hpAJ}vB>2kk$9-==Ur6Wzy!J@ugd=kMzj&TFFkys+0E zwEz5lpThY~R1c(ZjuX`jDV*m7?LS{nq;Rei)f*|C?*#2XUyl@Wo~T|);k+lRXHq!# ziRzsc&VQnMD1~#Ns9q}MK|%Y^*HbB+3q|!-3g<&X`_I>7DV!4p?LS|y74o8}o=f4} zC}{urdM}0Zqo^KC;T$Pw|M_|`h4ZASo=o9fDQN%sdNYOdrKlcF;hZU|S5r7|it5=E z&YgnxpRadQIDd-j;S|oHg7%-Ums2>8it6bU&ZUC(pRc!5IG>8@@f6Ofg7%-U*HbvJ zit70k&aI+)KZWzFs2@n-94l!5`F1)`_K1#DV(20{a^~`XhHkW_lqf=K)oG@tr`FTwtFO23nDV!Sy?LR;7N#Xo3ng^wDju_30QaDeH=1D1>D+cX9 zKW|Fmd@-6wrEty|wEz6PDuwgLXr7hAxnnf%O5yx5nuir~$e{h_=Vd80KTDx`S_;kA zQfS_m!ue!0k4xd4GMd+=a9$bB^HMmsjOKkQoL@%sz!c6gqj_Np=b1tK&(9N6IM1Z& z8qHhNI3JDXv1y!>2JJsTuTA5;G@9q8ac&y4|NOi+jq}rJ9-PKGYS8}k^WrqlQ=@ru z8t1A(`_Ipt(`f#jM)T-G&Kk7;{Jgr5w?^~qG|pXv_Me}3r*Zxo&BF^hY|#Gm^YS## zW21R`8t1Y>`_IqY3;Ap`k58ldd>XX>{Jg%9*GBXFG|p|Kd4C$`w~-!@#yM`J7o>5X z8?^tto{+}5ZqWYodP5=K4cdQRk4WR3H_|K8IPVSGe_qc>pWJJLA+4cdQR4@u)3 zIMPefI1i5Wlr+wTBfTY!^WmWV=k=I0&WR(vCXMsrp#A6doHWjjBfTe$^W#VlO5+?k z(u>kKPY&9DUQbHnTshL4(l}oZ+J9b;O5>b4(yP)qZ;te=G|rtPy(^9L=b-)P^{_O~ zp(DL4jq~WB{paTYD^ujdGvxD}Z*Avq?*N*hYG|sn!_Mg`y(>Ujj^vX2OyMy+h*E7>N_m1?=G|s<+ z_Mg{7(>Mo@^wKoW!-Mvp*HhCt7mxJTG|tC^_Mg{di#hqX_1ZMf%Y*ix*K^Z2H;?q* zG|tZ>JvfbX^hhsG<2*fR|9L&RkgG>}a~kLCLHp0^(dqv~)OEm9)&KvJkxJ3hpc0Xm z(2$n&l#-~2kcN>$nc3ssbMCcAb|@JQnNLVcL#HJTq$MI14H`6Mr2e1J_jk_yKd;w) zUKRJ=bH3ltcz@1$zu$+`*|Ybn8BTAH_x|C2HpA)e+56oLr@zO0|8PH?;dJ=y{c?uW zI3s^o7!}%Dnd?AMOGhq2d4CiaW@{L6P26*ouYLdw3 zfaNPOoZkVUB39>e)HuzWs|Zv)Hs zV>tf?mJi5qJ`OBjkm39sSU#c1*Ma35GMv8y%SROXJg|I4hVy%1`HT$b`@r%Y8P5NK zvi{3BRCsK`fx z<%=?$p9IS%75PfAd{c(=mtgs*4Cgb!@>LnmZ-V8sihL(nzAMA|Pq2JghV!9d`LZHE z3YJgHaK02Q-&W*L!SZn#&ZmOq>x%p;SU#`Fw}R#SihSi+J}|@iSg?FyhV!#v`NRz8 zYr*o3MgA5nADQ8NE?B;@$nS#XGc%m;1 zkHPY>MLroUUz_p2`T{us49n+cINuDG?=AArVEN!89}Si-&TxJjET3HDtHJWk8O~pW z<)e#yHdwwo!})Ene0GuV2FrJ6ME)E9J=Ey z?}ODdFr4oPt9M{H{|{CVA@TuX^%4x{2g2$pM7|)b-h$!$L0CP8$R~u=YcQN&2&?B1 z`G&B14~Fv(Vf7#)9}!kB!f<{fte!;VE5hnc7|vgW)uV`fMp(TH!}*P{dKQuI2&;Et zIR6n=4hT;53SUru%mxR^ZFq}ULtH%-fl(2dohVv_7^*kcq5?1fSaQ-E% z9*E(5Ojx}T!}*!8dLogp39C0^IDZpXk0kOrVf9K3=Xb*DnMA%Ptlo*?{7>-SKU@=K zI3EPYtABn6;_YNa6T)nUQOh;!s^)=&Ub~?yD^;q3af_``LM8h zIfnCNVfAz(Ulvwx$8i2EtR7F~)57ZY7|ySS)$=i&ZwssU6FonK)dPxrTv)vz!}+k#Ru9W?J~XUe_W%88{->v9IA0o8Z!7Yr!F&I3 zPmbYyYFNFl$ghUg^D>-o4XgKMIR6?}56o~rHmqJ)Y-wms0W;ov)R_`qGzhU*z4CjNx>ZL_~IINypP|4_1FyOlf&w@ zMSeM~o}1x(b6CAMqw&Az$1toOT;!v}>ctt(Plwf$Gn}sut2Y<<>#%xshV$8B_39$O z9ahiIaK1aN-kst6cUV2V$cKm3%QKuG58nHSd+H45%fssJMgBaj9-rZSdRV zha3-*<70BXP>!F<@nku^F2@_@_{$uRn&WeGymF4;&hhLyzCWis;GXxyJqLk%o&xt= z2JX2~+;bwh=S^_WtzhRT(Q`Do=W%e)_28Z}#XV<)dtM3m+!Gdmt>&J?!adK0#Ra10 z%W%)B;huNHJvWDYz7_WzAMSZT+;fH4{1!duh>dsAbC)>(2Z%$$#ekm^P6T*T6K(>$ zpKt`wqZnfVKA+q(hwmfzuHpO1J!Ci@axWK-kK9v*<0bbt0bh`NjBq~WULTwvx#tGw zOYVKa`ICE4upZ=I5UdZmCj;w6?v23uk$V)dp5$HutS`A{kDjF%1F-(&8aC_)xt0w3 zL$2wJ&Ycmi(C-W8srnfN|0X!vp~KP>;U;kdN|}G>7|gLq^H4Nk=_FN`@b4* zOp#g(`Aupb7;%Bh& zL_Qn8gN--x-FOUaJmTJCe3E0uV`k%*oC}^48{gzy@toQCCu@Og!sZ8AD_k=+f5=+m znzH#t)*9EG%|EgixF>9WlD)z`WApcadkK5W<~Qy=<~!~^jWNLWA+8^?&qWjA1Gb)s z@DhjH*!m;Fv21-3;d*Yq;N~2*zL8kR^JVLwXbxuUqv*N-TR%nDNx17Z+;t=F`W0J` zMc4D#dM&!X$<}kx^`lj*WuazAi6KX_7Bm08n(ZP?pv|_M|2;L?N1EbuSEA( z*?uOv-^}(q(fxN=i`AG993O%0mm+=!+fPM&6}I1s_&aPr7V(*|R%5=h{aeH{WBYq0 z+wZUZrG^aoOWhqZNF@&Kr*??-P%6W|P@ji&Qs>2AQX~N;gm5vgQ@L7BNZ+LJy3`Y zN>a26GM}a%G zH4E4H3@rTNU9Oqq?W(oSTUA@!yH)$FSIQ!5ug!}`dWkP7_q?>k+S6^Rl&6YL@xex& zZ3knP^?T^)W_or{JWT!D3PdlF5 zsO4yBSm`iexZWXi(}R7hjV$)PHhQsFuzAm3CF2fz#@N|@w@LS&0TYissaw9;X>awg ztK0h3*6Ck2TQSp5HtD90Hri(G*7wYITia}Vw)^L{ExV)4t9L0{tlCv#ac-x+rTWe% zmKi(jx6AJMzFlY~wE7NzvfrMu;|csk-}2nf68KF;i|Sob=3C(1&vx5v+YO)AZmn(R z2%rARM$FU=zU`}R9elI))^B#HTRh-sy7%lhafYMouvaqP14sK}?`t^n)kY8YWo}vz zXH)55X{ZHfciM5x#>sHDF>uy{P6`GtunH~vYhguH)>Rw`_(ug+DGpYv&pANf4p#4; zi_)5fu$n2bsspa_tF2*`ue(*iic75whShI#&sn|(b|VvZrQaiF**4gv;)5zWQm|{* zuzTg6;!8%tZd${xp7m;l9ah!e2D_aDyZ*y_03u+`!dQrgCZ8IJ47ml)5GCgzTKauk zAabV94S^`C_B*LA4N+wX(G>+zcE>+YRRW?;AEJ*3QFtDru{+=`M5gl0T@a;-5Uo`Z zwY`Cpr>jF0TR}8OLR4RX=xz@(m^ucceHlc(9YlW=WI+jJLjz<*Kd%ikWa@;~kS*4b zH56n|24vAC$fl=|RbTjpW27O=lpx!5AnP_m_Sr!edP6pbLsq6jcIHBsmP5AQfUK>9 z>}`ZBeht~&30eIGvbzVeydScC5VHP%*}vrPU&`grAa!c6pKAWyLyi6Yh1xjKNzwhU zsfxZvs=K$2QtiD#?fO+t9q!4cYJR3t;y=Tw6+gTwukUtL(YMXio3A>Q!dE5A?29yY zsQU~5cGpw>pU;>0OFw7uJw8$Vf{)hxHy>8>6+bBQEjs&oi5(5Rn)f9zAcl?{4Mfp#8X6V1OLE1m_X|><0Cn0{yPxSm89{2ke zJUZvw_0ZW@`=PS0UwxC$)d#UY;t$sN7}ovpPN>cCZmQYltyClK?R5XF*V%j4UITYW zdab)#?iqi_+OxS@%2Tbn_@L+QZ3mgEeh-D-2zEEPq3oV|{kmJn zHETEBYXhz^Op0sk)rGE_SMRxmR@%8dztZQdaV5?<^s7KoMr5*Oi zOPuY+OS<*Ztwgp zyW8@rcbVj^+Etl*Zs*)w^_}UbGj@zRExUu4Bed#1^?keDDf{gWC!bhuI;n4Y{X~g{ z&Iv_};^R@~vycDWc0Ai=n?m+Iv(zkYvvFDJrtz6#rlT{R{*BG3+bWZxy*2h&>K55! z113r7yG`WN8RKI|m5gT|6>QEs^4e(Ok<~^Q(=s>drwtg^rdk?qPd&HsUCNk^o+(Zn z#8Pe=Bpy~UP(JLvzC5{hok6n7y5^*Sf83Mm*GeYMTAPs=puae=Zq0)Pr8Q0oo_gX5 zRjae((Lub$0 z80w%R8+ux~Ddg?U6CuhoT|#V?bVIUcNQJaaZwQ_|Jtx?Fnon?&;)dV{3JSp^6}|`! zrrs6`r(_5#CwmKfCz%MfC#efvU6|NmBCL7u zEtGnnA=H0&TgZF+MOg7hA^1o8hTw(mKEcjyIl=j_8-m}xk_u6Nr5j@N(j_Fb^+ZU^ zi>8n%FJwcvw`>eeYZit+dtMnT|GYocqG@SZTBCPZ)3frhDbISsb~fmQXFd%KfAxe3 zSAHT9;qZ7Me)VstXQz-j;}Es%&Frs;)a%*t9oNJYbgwxj#4ryMTCXln)V!LJ7+NWr^!$o@lE#(hq|nO-$<3F_leI1> zAC9`1c(}bnEM-N7XG+S2cPU@aZ%^HHzBVBzxrIlrmE|3oRyOk}|Ln1& zt)=qmdZkI}$4g|7iI>D4b1at0s5ujxp?PL>X3Br@nS({+vh0gevuX+zvKJQ~&(12C zeSAbg@p12bofB<&*H4(_Z8}+*+i-GjuHC8h)7_^=o#y56az??Q(ofr+nhQUv%-wX- z1b(wEPv?Ypelfgz_VKKO!dZ)@v{xj;DkvY; zf)#1LWRM(sxfxc;9ac*+DYP;JR&Q})>(vLanoh8);t3tsvSF21#T&qi_udGC)mMzO zx^)$H!xnZ$BG&VE0qoM2L(SE_uxr_{dxkLscYnZcX2Y&-j#h#lZmKDO-L`^V9~mVM z5peZ^2SkHvq&7rG*TW=;l1&gT5)sM}Ij^1sLKNwQ?`-ISs49o(@`fl|8fMYd4^dYM z(I_ zG9U}1ARGN4D;*&_Eg?${AzN2M)@nlbszDY{gKVA%Sv>}_TN<)l0QP!fm28>yfs1IbmR>ZpHC%1WhdyFq2{Xp%a+r<^)E};B>geGZY_hUrFHE=`-LMFRMaj6mBlZ#P8#x>2qgm>-o7E zx6F;W*zbF3AZ^}D`E$Mly4&VA8$0{jjgVU~-&)ysnq$p^h>cA?cK?QJv8i6O za7FPNpUqVR3rEcR;l0bST=PL_j(4@0tJbEA?0RQFkXuFrH^T7Of@vm{AH=d?ue!K6QpIwAA6 z9n^Xevup!QO%Lbm={}m5>2aa!o38lqH6H5Ia+cq|-{$Uq$$Z5hzhL*L*QHh-kyUoj z(W+P(nsnW5-blw)rDE1@7dFbT9&0_|n)<0~^|AaESH*9hdK=ppx*oMuS~Fheo{Rmo zx;6c3b}sd60`vzK^*JxTK5MPQ;yC9|N9)(xsH-?{sSWt2er&~oE9NTe{Jt3;h+AB{ z&i-!8el3E7_(83dD>BX!nutF z7Fv!O3YLaf##B0_Iu95=t6uLAH!O40bispt-(Rjay1c?--=ZO}jUErY*n7)PusM<0 zvsYeH$yhbA!~RqUW6W%Kwr?N4+ayk_d(Y;*112@&J@)MWow}uH=r_CM8ttu4{T_C% zPwKX=8uHcFb*$6B2gbYE%7==XF4XyC(>*8Mw9eVlh9{L#m~Uc!q3Fd#_z!I!tccI z#$&)^!DGT>!(+r_#bd@}$8*4Q!E?fMBj*U`isy{yj%$Exfopauhiiyy ziED~$i))N)jcbl;k9&Z7fqQ~`gL{N~g?om3hkJ;7iF=BBi+hZFjeCxJk1>F;fH8rw zfiZ%yf-!@!gE54$gfWG&g)xS)hB1e+hcSq;h%t$=i7|??iZP3^i!qF`j4_R|jWLd~ zjxmq1k2!$3fH{G=fjNS?f;oe^gE@q`ggJ$|g*k?~hB=41hdGG3h&hS5i8+e7iaCq9 zi#d$Bj5&?DjX93FjyaFHk2L^m0oDYp4OkzZhcysuA=X5!jaVbGR$|S>+KDw3 zYbn-LtgTpMvDRYE#oCKC7;7=sWUS3tqp?)^x1xSmUwQW6j6fk39f; z0rmv!4cH^FS76V;-hn*?dkOXw>@C=1u-9PE!QO*C2zwFsBi^pSoO0^e{Z|~n-zwmg^yB+Sq z<6rx$<|m#{f2#OTJimLUD}Lbl9-rs+9narIrRW>3hr-G?UvYho`YC+H^?K51_666k z!un7*uBYah+g-T6b36Zh#`R9`{;2o?_ix~n zMJMj3l4@cH?yvljn)kThHw~oT-a;hby~6l=tE~JAUHJjVPKuPS(i`I`B&>mlav>s0NBn9mvi`PE~7 zXX#vhfcbt_SNs9y|A}(LI;;meg$cD-A66J=)^jT#fa~d8k@7)~ni?p0}}nEqKIKVLjU*CsT#>?b&Oq zTUhVn*5%*C`Zs4)?+vVnR<#BYP z`^n?lE3p1QT)CnG`$78TlndA&f@e=HB$A>u{I(9?4QEHnnLWSKIINyGp%UT!*x7~t-I*H0h@2)5HXff(S#pW@?)0epgIA4d#e z^zC>yVt|823fYJOG?P-Z5Cha#kIO;~ko++|6EVQ`zoRn|1EfgAW+2`%{Ueis7(n}D z>@mau?zOVV5CiC)PD)1%(BvbZju^m2@7Phq0Q1CW9z_iB&&|9ehyh;l7al~Kgtj2Iw&iSl8@ z0O2y_$%p|oJ{u$>2FPk?PC^Xu=AL^JVgT{`l1YdGBpNdk5d(buwm1rp zjYSM_0j@G21_*pya0oHL_Wmu05Cbe+(Hnyppy^0<3}OI{@rE&o0VIlkL?Z^!u*;4{ z3=p(*b2MUrOY;Vz5CfdkFNi`6urRm?!(13ar?!Vv>}3JeTK46xfwCmb=rWAmOc!~k8v zg04rR3Xv6?U zuB|j;fDw7sG-80_sb^`#0Pc4)Xv6??X%vkZK<0@bjTm69rX!6Q!04_ejTm6_F+&0WcpC17Ln42Ecqp41oEI z7y$DbF#zT_VgSr{!~mH8hyk!3AO^ttfEWPl1!4fKABX|4o*)Lm`hpk$>kVQ6tUrhW zupS`>!1{z30P7WE0IXk#0kEDS2Eh7;7y#=XVgRgvhyk!3A_ln&mctiOl>upT1@!1|0B0P8hk0Ic7L0kEDU2Eh7`7y#=%VgRiFhyk!4 zAO^txfEWP#1!4f~ABX|4pCAUn{(=|)`we0M>_3PBupc1?!2X070Q(hU0PJ6g0kEGT z2EhJ?7y$bnVgT%ahyk!4A_lVpNIjlpCSgp{)!j?`z>Ms?7xTsupc7^ z!2XOF0Q+@5U;ymjhyk#lBL=|!ju-&@Jz@at|M5FxaPR_xCgiwxF@(bxJS5GxM#R`xQDoxxTmf081oqWm;;y#m=l;Am?M}gm@}9=m_wLLm{XWrm}8i0m~)tWn1h&$n3I^B zn4_4hn6sF>n8TRMnA4csnB$o1nDdzXSOc&YU`@cc_!?BiQO~=}fH6Cj{ z)_kn}*aNT^U{Ao_fIR|x1@;W=9oR##mtar9-hw>_dkyv+>^<0nuooc)z}|#C3VRjy zEbLv_!?2fOPs84ZJq~*v_B`x;*aNW_Vo${0h&>W}CH740o!CRMmts%F-ikdIdoA`{ z?7i57u@_@c#@>uQ8hbVNZ0z0G!?BlRPsiSlJsx{K_I$hofE*m;!5|j}`5?&2KwboL z6ObK*_vrCHIo_+r`^9+A7Vn$ly-P$7cn=Tnqv5?Qyq|>kbnw0k-rE3pu^RCO$Mfa* zwk+?9{0x7G<-x7y_?R3o5`M<8??mGk2)mQOEF@U|LWTFR4VaCmr_rLpP?kB3n(36 zbL^KsNgb2UqUxm6sUg7d&>oRQ?Hv(ErH+WE?h+oBkeWY|Pg#%jrxJh(a&x3R^>gF_ zN^O(_wH??Y(W7=zm7^@F?op(gWj0WuGHa;|GApT0!rRiKHjkc9QKQwUQo`Yy zN=+Cufif5~hVlotN#U4b)Qd5L{L!*Kd_7>G9F*%4{yh0XI>02ZYn2R z^13G*^JYw0%i9Vp9N`oVUfC2C-kT|tc@u$|V=z^m=Qp)4D1T~K&~w5^YYbYUP#ff; za3d&_aMKEdhA3tSX(}cM*#WC3Ns$+HOVK0fr{cb#S-|u$pJp5sK21NUV%n0RPQqVP z3^JT97Zf;MBB+RP***r!%y=2NYR02LS6~Tc&A1p?Kcgsch*DOdCNPI=l|lj&m3#wl zDD4mYPWWynfo3!H14Cvm3Oq-+Z<7NjD31(Wr#u+o1FWOd%C7?&lphC3sZ<3l1tyY% zN?t&!%F%#omFR$9gdgV`U^&Y=AY#^*fC|Eu(+-%VsuHk4bwYq2u$b~xd;A+!-}_6c zHTo|FW|M>3MgLT_eE({-BmO;vPZ!{CK6}4^*la8Ra>A|C^`D?V$NwMo$^KrziaM#@ z=l58>!%u8Zqn{QqrEKS%_e+>_+V9$&!+za_f9L6EJlD>TH+QSwe}s#-z;EO{ML*qn zqx_tJWtBFs)A!E2XTHDYRr#s{^J?4t6TTty<9*BK^L^V0U(d>S^#TK5*9D7xGYEH2 z)^}jRA0Ks%k3N>b+6vdW?NhE%=JQ4)+h-gwxz;QU^l@M4=##n7%%_&{`xf}j(VXgI zsVU_X28^$A%~#&-nl;{XS{J?bfCc8NmEe6$i|2h$%gMW!Z~)hMn`zJY7HUuSF40Cl z;1{n^i<-T37gc*X0)s4NQKr|eMUh_L7I}G12UgkU#fDyiix+$4EuP`kKsbXvo|;Qu zd0H>I?-@h*gV~<%mqdAvU+V3t4~#Uor5inuE!FnCw^Y&d7vU9tJ-Ah;`5<4X>R=(^ z7^WSRTqZoIwd}w_Yhbg*EL(N(;xe^^@0Q6O90v?HJ>7N>XWbf)Bf8}tw+Rn1%wzg; zHxHxbmL7hSdYQw{qA#s9cRAczI*VBGWX&Y>F&*ht9Za&d!?zn&C2EO zF)NX`IKutS%5QG6tDd{90A`)zs(iPkRq<}jDqpvcgwMF%ZSCs$Zmz4xyB!4vUe)S0 z*RQMZx+>}wyKVqho~ItqHA`=g>peX~*B-)oly@~+Gw9;GrrqTP;XjtR^sPy8QPB@_ zF#$$jfc^%T9R2w&_4?yn`Ux+x)p^U>Th4)N^PO`DN7BoAVC{D2S^und-U4jEfPbW& zbN=Z%@Zg`v2l@$j^4J06b%Fza>-HTuNqCeR2Y#&^cR*=<@BU4|B=lZ?eSg;aoc(v# zNACYl_?27tuQyn{-`!yH{-cCz`P%8T!7ZnW8}gj=fQ9I^!ObaYgQ?S%4NIKf5f0{{ zqt3=xj0s%)Gum>~&SJt{mE75D*}g+z`?VcWl zzfbtK-mnwKup_fgje+6o2D=moyHx_aR!?}i`mmD<|7yanZUrW=2kdep?DkpM^+$xC zJ8G*OM1v+o#a4(84~UX@h?WwFng@ip`vfAX2qG#JA`3aZOCZw5m=r+N-6ee9G>AlZ zh{z4b>wtm02clF6(V7iW%MhOLc8Fw6h-m4}%YdnD0a5M^(VhfRUrPAD@egv764;dwZ%tDT^24tF~p)+J%B;gB3K_=QlM(S)FB;4V5 zkf~Q7W0N3rkw>ftnLKgBEy(KEgj0MJGTj|Ae!am~K(sDU1tOsu`2ZMZLRFRoe*1`6yIcSE#saSFZ;)xiwT_U#Q0MP?hru_jv_WYS~ra+}=RtMjmtw zRB{`r=-Mj}5KeS6RQh75_`y*5ksm#0MZfzXbcBb{8IUXO2c5zQI>z+nVT3n*8#>7m z=qS$6S&&0LPFD`PjT&^FRnUF5K^Hmz-AD*sDUEQe3!zi-p<`{;`AT@!zo3)dgN}9# zIvaAX^`X;^Um68nFPreMW1tgSLr2tH(nGk|4bUm`pkoF?=R{ui^u=D#O(UVJW)hCJ zBXn9_=(wX6eIb1966nN2=*VW;YY2C{7drJl=-9`gb0d#i4?4M=Rtt5k7buAdxaaBq4xIkQ=T#zY5UGGe9k!geUFc|dl4YrKtQ~P(-lFzp$;f`37}zpK*h#{ z3qNbxK0wPJfSP%Lo+AN8Cj*+!22@>0IP#f*v|RvkuTW?teEH{q#Pb1>`vEdX?)=25 zlL4)(0BYADJo>GGz{Q7E`1X5rku!qS2x%Og{F2S^L z6Q+h*!nfN zmT;I{kf*Oa-T|f=H<)UC31@#7OgeMN#lqBcnDF;gU=p&2iAZZ~8R7CjfJrF>CZ+>0 zIU%oqxLgZNQ|&NSbrO!h9!y%JWe52$VDdt~zduZ31~8FL7&Db{|4U&~qhMm&JX(wB z06Jlky8shiC`@*!2~d(Tg=xys*Dq>;-;fS3CwQUOK^$I>{`{e`;f1<~<$r!t((rmVI6yQI2M7Boy}@2;^x!X| zhsgi^gYy0Tom%(%8_`C*8t9_V41A`720jvTUm9s;s}AXeNyNUs4MFFQ^ZF z&xw8_w6B3O?R!iq_0xd z?kOO8i#hI5VqP;l!Gl_Ei8AmPo8BKH;kA4WL>>qr}^@l&vWQ_RXPBnf% zK%M^XK=c{<-*-{6-z}-uZ>B`65%6sTwc*=ZYT~z*M7MGFs}@CnolhBmRU;aX4_~HI z6<;P$VPD1&J;%&1!>F&_gM6mDhmYEh9o?OL_3n25udWuN^GNP`z_;tV!(Z5SooGI4 zKbP^-KNs?yKA$4`56RDm`AFg7pB^Uqo6;wqe!?eq ze*Zq%O0+mX9xw2#daUS|@^}={$o9g3VCq=YA3U#l%yJ~B^nc9m)_p_%q!FzEn&s(b2iD-ZdYu0%4YUX>J z)J!INpzrs;cvanR_Da29?ShAV< zX5W3~>3R3QC+deL-Ocv=cqhuU@{Tvr679LO(R1+~ZO`F%6p60rO!e1;g6igjX4O>( zQDfA1JMCcYZQ;S}+Xsl=X#MR~2PfZFJNT(ej%bgPs@gsFRn>Sbt12ftq*u4XJj!ml zd4%7xB$}iJx70nvZjJSLdb8gh^+`cD@4IigS>~>CGo5IaYHl2G&$?mi?s;Q5(Jf8A zF~Yt3`Zu?m*PpwghROMQzMJ0lc(?J_eTkmw%C+@w3D@Sk?Y%ahXq!e~YjbU5?z&!J ziiyt2hT*v`X7;#BGloR-RCZO~HR9@^%dV^KE~tN!yjtSYT$$ohQW-?FP*#;2Tr?`@ zyGT@yBf6;KE3M8US8h35UCAdJDe)^_&d)DzcP_rXn&_phE=xNvyxes_;_~AIsGTak zbnHO*CBcE6m-Z1I)$mIi2U;(VJ8DX89>{wrJLbO@@@(GUS<-Z)}mp^wvomTU?EQhjlLWihx_C&L_?3}uT?75K+9c7>Q zp?)j1taP7SS<*g(GH;^g>N&e?U)|X$`%a(zy%%*|=4Y9`3(jWml{_mX8m|kb>-NT# zs_u0tl_GktNu_t~zmyc%-z|wI+OL3;&Gx1x^X=!Bj3zp;7sU_uoG&if6IUEhG+}zh zTlY*U*4*>`j2zL2<(zqB$3Jt{&f-iW(Ta^Yv(>KsKTW&J|Kx~n%>BQ5Tf_fKY?c3u zBO0=2MaH(JMe}W=ie!kMY*o>Hn@L3lHs1;(iMA}a(7;AesA{vLP@L$@WD9Rte<;Yd zt}392<}9FKg|%716l;xwzTK!ldz)Xro5@eweKg;LXwkOh&)+>af8=h-{LWpdORLPw z*_D)T^cCChYXeb=|oocN5XBN##oJY(L$;gE@U| z2kO{-Py6pMJ#D^2v~}csvbvRX*Xnpqjuqaou*%tOwIpY`)wmpaqIIh|Rlhy| zRMGaZQxQb>w)&Lbc7;<4+xt%TTA~K-+{t3ggp+Uu;G_f5!>ON~W+{2H&*IIArxvJ< z%Q_Kb5p=@A!s>)R(aB9b(P#eS_!IL-$BWHTGZ%Y&uetMaJ#)k3Q;2>}@_7BWx7mf; zZf1uPEghY$yUjX#!nT#!-Daq(8^}6k)|ACJtH`n>8atmX8MAF!?@bqFT{lI&-M36{ z(?^-crlpy)iT2Jtv*llt%<_LVGUNV59bQ+4{=an@3jdzT__-A|c`g}wTQ_G2w$9J6 zBKo|pW23g#9ecaw%rRyQYW3WXxo5|v?`!%KlSk=ACTG(_iH6TBeTm7o z^f4w&(%&1Sp0D?4nsL)ncjHS(HxO+f?`Yp<>!S}h>mAMCj5@zzN4IZ&dqi{d?IY4e z^A~lb!pQkZypiz{N2325dqm#oM_Tu$hP2z8Pz#uz=DmrRX0*vBO@-(JXQe$d9G;eM z_#st5G=e3m8iq-!l7>F1FE*lHa7Aj=#_6fH8^uy}h<5O9%DW9kDOWbcrzCAa9ic^v z-iGBV6E{pt`D}oi!gq&{8r(bVZgA%CdIP*y<#YJ^daJ{C*RML9wH~#GQinIK|CFq> zzCQWaI@BE=Nj|xbpX|48PqH!5AkItf|3@zQ!9P7oIsc#@@mkWBe@-XO`X@SRU@dAB z&69H0E>8+vtCX~b=oCLC_Uk`Ntk=Jgn4^!H#o$B}eWyefeUrq#HK<=yNIbDdEYWvO zM}iU2GL|Rw=p9SArzcFv(nDS2mV^y@OA-|I6cfI#Mvddg_@k>I#k;P)7{8Y29V6mD zu5ycKR++~qtwQbNtoRkHWa4F4{fv9F5_OQb;$l`7#o4S(j?*TZNc*_v6-IHzD;C8C zuRwjIMBJPepJNA?H^e?%j#|mwSij}*u|~`NW2Y0{q+#rBUG3N-x(cz*L_^tiXq;~2 zp?Ax!AG){<^_0nnte5c)X)SX&BuTWDx`zsNR1WcVMjzTrbe3%~zn0d-+*^7+=Gaoy zTt>yf6@ZxWORZzxFG2m~qL`Q^(_^fcNXKXrE#}MUhQ;@y^A?v!2QEfkW_0xQ#XiyB z7THGMT7()+ooL5JD$%-&#zc=Idd+uHCEAaogxXi5%!qa~C8}3T7NwX& z>1k<4$!SfGYS%HZV_ekcSVHEM{THXgzEg+ z5x?e5jJPuob)w(Ho#wTL>&|-^K9Xog&xHRsHzS-kHzwSe=tuX5chA`#er?Xi@Ps+2 zB~=U8nj;@BHb*l2u{!EX--UUpKMngwy((-1(U|6hmCrsL7B)LH%$(>=_l5PSnTJ)Y ztq)68L+z7x4 z2TpGbHYEDjyTP5)DuXMgl?I1TLoMu);91jRgMTUtgKsIKF7`mMouYNHrlMK!5TcP? z8Jwx073`v*7QBM!W#xjOPn8VLpE@A)n~K`mcfyHNn}u(t)C^^fWXLTP z9FRj@ud-mcoPwZ1cB0^vENXnE1$we#g3+?S=@(;A@B4-JAM=4W81sgnK(xP&bm{0v zG&Q=G-b{4BH|S28EA$1K3v{RqYJ!VsC7E2hd(;WKaun)=)9CG^l4-S3arDoTs1**Q z6GjSX>ybQqKG6+((RW9<)2Snz>AgflY)=mvVMEtR@1&1Oqn>yhts}jamXtQ8pA1KB zu>tKid@a3lxE?){=!|vgT&bnBx6~q9pJZr@@v-z)aXETA(J0H%mxhg^BZiHn&52%lIQ>;jie|(l z=|f_uT^6Tjiw&cDh7O}|4MiREP}&+^^Fhx%08{|fG^fDJ9$s4TLWV#sC>_(`R+Y98QlWdgzgK{_jz=?{67; z9nnV1(XR%^(q{(7(?OuB+&D0amLHfxzwK9`%Rp&K_s^h>`)AS${j=x~puVi=Q>R1w z=Fz5o8nhD8QZJ$_dzaEty}Gm|(N*iwKYp#HZ~QW#<3YK(>z6S-_t#dsuV))w4QkHA zJv-?=JvOvvk3Ibt6rMFdo#~@L-D$_4Ui1>8!{*VCehBF7A7QjBC_z{Jh@(gRNTwUV zr_rZD9qRS{1g-x)mzMorM7M%swCLLfI^f$Cdc(IH^hBcDuBFd@eMHk=8);*r;eJDZ z`0|0S`0|Ag1GTBym*4cvFJgkP-O>UE6sU)~Ckl3SD+tuPl?A^*m3q5tp&+?yslcvl zm0%&!d>aXBKbr~CKkpDY5&idmf#hco!IMw^f)k)<_4pJaSp6wpAoD3r&;lygf{%Fu zzmFvXgO3%02}C1)M{xGTLjnDvNnk?s;vWT{I=>4pb^aDafx6@#q^)^xDNKKFCvg8#Nz2iLV72+jaiuXCF)czIiF@QAh}!OubIoBz5r*za{^u)*uQ!4rvoy)C%> zRabDxtNvh9qGcZ+^6lk}kn1n!ha`YX*yiQ>5RI3nA%9zKLh3*{oY@)};?f!sva)t>NI9~P)cV$-5BorbbI*fr~AWYiT*w&y!1&%xbVrD@PCOG|6%x#$8F(NkH3ef zfXdq8@x+Lwk7q{=f2;X#P;y^=5E2>pAT`qVL1Cm8(f2=$d|LM=GPkZb z(jOGxhIP}U6za62x@y-)F`xoZsCA9nQ!9*GT$>UlMLYnmMitgPisIG0i!uSVxKhoS z=Y(K7d=qhEol{OrB*=#YE&qAl*dj8-R}0n#ybcc;f> z-(3{rN&Ew>VfGT-HMMb zyOkRo4oY~dTMe-bZhejwyCo6#6x8wgHy6bP-872Za??Ieg?JSt$JN{@ip#ojE6(!< z`WF0*n|ebgzWe&D_?w`tPrYs)?|j`YUhjHD{CMJLa54VMwMX#@*FMJY1qHtDHN}L{ z*Onx-F%(BwAHIO4O+Qlqf-b5tNdOuPjdrxniDVMLZIslfK6&`1Psyzpr4FA1CxNJos}9>-v^u=xqR-(` z#9!ge;fodb4#!ozJG_^8Elf)pTd_Q)^MXYR11K^WGVx##ORYXX zJvHAY0J+2NRvG`_DBb~A6zRlK9XAIe8jCR>WBgHa=3k@=j_`fb!UeiJq<1i{IhyT z&CgmNU2v9nRFZf+TsnH8wCQMEY41@7;`gv5eNySR^e-h|>36|l;dsf}^nj8_>82%L z)8`T&h=s>q6q_77U+i`)4xAVqi_aX>E3P{>rMTa=gj5|{u!4H3*r@V zCS$~zx{UV!x-u%k#UbTCjZF9dOfn7sbI(*J9ulRQ&x#&pmKJ@>j3Ry#i?UV~ZOfWe z+BB&baoXuMPwD+%nm4cn{8Ggd0d0| zPZ%D5o9}#_$&WpL6dWbI^B*1GlKuiSGdw-E1&zLQe93a8pnuRg_qYeo9$uv5OL^G}(ct~sSa zJT1oMbmT0_xs_v;a~#-+JovvA{A~&R>p0?dp#i^X3h(fRccSkF1E17>TH%ybZXY;f zG{CoOew%Md{4+FQCCp$&0$^p(OQQ-_>O+AwtlCQAtFZ%CQUEKO3oDBr8{Z0jV70fw zsxKmb8&R+mrLZH-RK z{v6-WbU`%KK~$W9=!k_Vaf4_vfv8zXd^_esB$+})1wdq>hsRxrv@a#wAnKM7KMx0p z#5jn^3#FI9f#euODG#F68lqN@_Q4ekOHTJ|YZca_5ERkk!+Or^sH&^f<`)ixp>pk(vlqzz3><6;y>)#B1an zREpM%pP*{gg9}SJRFNZ4P54k%_7D#e38*v+q2gFULM6|Fie7UgnRue8K&9UT6+Z|nKl-CQy(tkV1|4AmbO!WF35QNm1|8$oEq~&h zvJ5)OKIkY(&{@z!EaKaJ{LOv)4GSiO1=sxpb*eNDxiW8;`Q?Bfi<87BR~zyi0?}-Ac=om=r)VRe)%IJbnmHLkyst zGk|t70QJNW{}@?7LaPB0xdJjmFPU0EN`p`30X0byUzun?QkMZyeQ2m69y5l3wEO{a z0WIRY|E1B7-5kXmo!BtUJ_#D^vvklY18bnlyLh$oE!AU$6|e7VmP zh(FD6K!Q4e2ps_#qF2owK#IN1^8htYBfd3}fFv&gqWsYE4xE*m0A=0>v{?zLvxNBB zi~=OO5)i2iAXD_VsRN|?x78F->w4mIlK@EeIw0C_FUJ$l8&g2KA%J+xU$udIQv;yj zyMTr(0Tq`LFP!JEj{sVZ1=K7A^y~vD`T(G5Ye3az#3RQRkai*rR;(z*%BK|r$FbO!pM34@X z0ebBWzF!B^!U~ugw21G{O_(HpcF4n2F&d@|31K8m8kb<=_|*9k+^X7O3Tc9A=4e|al zhDnWviS6vyTH*sV5hl3}Fwq6TWQU$Wt>21Z+RKHh?*#D&It`Ox)EenQnSiT3qug{jn-cnihDqlU6^({TTn{GlgD{z+chR#!ahTTsH?^aW zQ8rBSIL(W*(>+f4IPK%q&-oiE_57uNuGJb6+W41h54RsOFZVA6p6DXqBYo+=l!ocR zkh91BP$enzhuSOrp@u!TCSD1smrQ!~oBCsYYv`C+zp0cHJwxlZ4N$M&%qE@+sH>C@ z>Z9Bp4~ZT3=%r5OGh%M0zo-KrzKZ;l8k>JmDf`Wbc?!Q%o%s>NOecJ!N;@uze3f4R z?V>3A>EiJgpDCZ?Tg5}uKTb#)B~F;YMn-|$j>Q%2}4~E8##Q0_!TO*WaaP+l@*jh zlN<4*KwawZpQTiq>XYHgUS}xXO_I_ZDiS zPNzCP4Iv&EsB?|EnMD1WxpzeIsW>WUdFlw&gV9vMzjsA`QR5#8sWXA|M@HN8sjNfR zBdyx~sk)2=;+cWE*})ul>VCz~kxx1gP?0y)Mu|^xpd4zpkK+8LyjSm{_BK|II={%0 z@^0=PWj4{23U5&&9vrB{Ey&zJ#WjY?>{+*#@_BqgCa7^GwYsKLFFH@6Ymey|K7J5M$M9b zF=mCrApgIP(Xx|n_3$rO=@E|*)CrGk>f{^xoswO=zMX$%bA#;dtQLOCjN!yf1ogyA zCq3X>Up*ip~?o+9$c zn*8Z7zo5ErY^f{ok^<+Bt9lW}hi9h}4-?ce?-ls+M}%J+_r2VWZ=vyRoY*r5e&Q2l z;&p<0=hLkg{Qo9|k6%?~!lz0r#_vco;7>RFAoAIoBfgYh;IVna%VP`pVMC}1(`Ksj zJd7annCs{qs z;Hk-{Od3#5=A}DTi~POR!fD>HVRI&zr~B|aH}06+dCZyjE+&R}z@QG?Xs;ztQmT70 zf1WXK>Z%!2cE4K7Q}ftLykbxy&pV+R!{t?cp20tf^V~k z=MC!K&u=si8kG?~?VG-S(E0-v)5O)61l?HFDe@;fds#8a;iBPmQ%AWV+ADDS!=VyE z%$y>TZ<+hR$H15fnHkADfXBCR)r`!%M}dbQxe~7()Z6cuz8IK3{Qr1554fnVZi^3n z=)(XqREZiz6C-M3uyH3QQS2BScI>FA7%W&Bu#F{(9Xp8)qcPaAFn40bPE0gdgEd$Z zON_x1zjyxk&Efrid7lal_ug~P-fOS5?%pNQ^&_`W_FkiV?_T8k$%E_E*Lw2sUVha5 z8I$*Xy7;~PA0|v*v+u}zwFV5I+#&Yw%Km45i#txP2<%dAl5dO2zfYY}?e4gbCy%|o zr?M~FA>Yu+vsVXK|FF6DGkKIYCVJK8!&2o{-lsiD{6G~TQ#Y{xsx>#mdu;V|zG-^`c>PhdH z7WA96_G9b&EgvRMGMWo3`>rjl(Qs1Mm!_Jd-mft!RY|BhAuxDSTEh&jR}uXR0`|Y0 zxXH7$=H}EtCZ2fYQERk+>BKuHKh=5~(br&O#DR&=My{w8JbUZJi=UpXb@b-SiHqO7 ztnA-b)^*av`Dxu^x{Mn>@xXhtV*IjuPTX{MZ;b5gmOQY@#BcqBYrp%n&cyGwd{O(+ ztI&yGHXf_>Qlg*2(Jx%3`Mnz2@Cp!Hm$??V26`!cSDFREi` zzB%K4i@naxVaqZ~Z#=2&54U?>dPdUB4j-tN0U03|rhd?LV5f}tj6YQNjhpxMql{P6 zy+0hZIx^$Ht|lKAHuTBZe07-C>xq63rNZm_XK?X{-+%MlgvPc1{m|+9@d?X5sj2mZ zqAx_kXWva2*my?W=1DmdPR8!8d$jko2~qFeuIxW&6<*qTkCl(kziBmL@ySsi?b!Fp zgtXObEBn&5{l_q2Y27y;`AvK`{_LaL^(H)gG`{G&B&`<}{U};4KQVsSYw@0s@cC?B zeYf7lioEf0p9E?>tLR%1Yn(hjGiR{B?4|Aknjuk6oPZE#+CPoc-IJ3BYM&eNbzTUQ&GKB;XBt%n$WMB;K= zr_U%~^Xc@$hUu|&PJiloGBVvf@ZZXQex*B}eLenAr??pFwXYXkofc>Bc>L=pPq$U} z`MbU~_v^1++#3&Rmi_hY|21fwP&DG}Q_uTpz02rdQgv?p*N1Ee8b@5N^>v>W_eF!H zfUk{11GOG!^f}2)yfbFmqtqrfyPX}=J#S?bbDO=$_Rzgc5iS*tcpDH=WI zps8Kc^btv8Jn}NMo@w+=iCR*7OvsX-n;!Qujyd=4zou`-zD{e=zmC>hjs7aN0?wp$ z^qStR#+SR(t|tE2%szB=n&*ORm3;^AWQ<5_{J3HBF+)42ec+qieESzo)3(%@U)hha z=^3xIs-NC%zI^QS(Y_4~@jv)n9X+giGp(l^eO;nGw~p@kU`hOpkvXH!7OC;O?Gr|? zu|BHoUpT#C%h4vM&y^25*BxEBW0dk^|De$ezgkt<*RXrr+oMiTd8TC7JT)rXCDNSx z^v6+4G8433a`cl~(rN0beLdHkGs_2#>VDv~dGM$a7-&KW}n=YiiVsO_hBX zm3d=Rzx?#g=UYp9q*g!m{uh1Rl+4U578vZO9t2X70eDDJ|uEgW#7m7b#4v2-|Np-lkOZFHuGVX1k?QdVIHH*TCYI*1!Zg> zJ#0niB?&ERbRL$w_i)1bd5wk*@VQ^vC$f2+|56&|#bbX>mDMrj-Us!x9)?;NtPaZ0|0sQwegUG zI-c#19|{}NwsrmXTL-=!{H|9It=A&`mRikF2QTllwY~R$d4or_`?-C!A=!h!toN$2 z&t*T)F9%;QN$9ZP^O}PfE==oCW0c3>+AVS``(NfRJvHcc_8%QGbG{!`Gr+&&?^EUt zieA!4>+wjRr)ss@4@&uZW=G$9bq8&`u)X8VC4Pf8ME_dZFEjSYg@Ji%t0j(XQ7~}j zsn&^YcIOPN_aIH{8A;!$iXoi_R&`Mmf8APtVCsMO5(nN07-)XvqxF`gztpZO7Y7XZ zwr8iw5xWL#9G2C|CvfqAuJ7kp_T5}@vD1JD>XQDSW-t6|d`vgA?|{TDaAUfHMfN_vO>B|pVDqNHA_1ak3?=vp>NWV4Po^*+Nm)GxCCAe$LZ!-J+d7_EdbCbSXBQl%y+y2AM zuIabK`nB}k*7fH`ulj!9ucWd+=(oG~_AR^~)~)LGC4IYln7g^Z9Mkvv8iTbSp7imG zc~`e@;-^1$8}-<`@5&EK@naRqubFzv{l{$mQP8Mn!48L+Kx;rY-6n9zQfGY1gRU%A$Em z1^3$aK79Gd%D$w#-gEE0HLfCQXq`K~{JKW;cv{2WYtKNl){B&WWXqpS?6tGIwMUWK zt=E6ew)Z%n)v(vtppwcyrpK;5?pd-jr01UWQ$4?L)wJiv|2Fkp^+#W=w<-P2_%r{; z*Z3a3m!F~M8GX?BEIyOZ=6CSB_?@yJD%Zj_acx{9*UB}^KB?Rb_r$$%kK8NwEc>VO zEO;h78=eu*if1PKs`4y(raW7oG0&Q3F8i&r7OV+t!y2(xteNb?%389ftSxKITC?W# zW}`nGdx1T{-e8ZgSJ*SMZ!3F=J;mN)kFnR-bM$hf-yD09J;~l=kFrk$ix3B(3s1hImcA^Xk}ONc4N7GeyshL|J!(GrV@NyH{% z6tRk!CHvG8%ZO>jHewvHj+iI=*AfefiNr=?B(aj1Df`+IONpt(R$?r%mY6I1-4ct5 z$;4)2G_jhPE&JdS%Zcg4c49oSo|rHD5_}cN#rJS6uF9=CHw4>%gAZuHgX)fj+`g^?~)72iR4CdB)O8DDf{x0OUbF^R&p%4 zmYgg5^^%Lp$>e5oG`X6bE&KSA%gO2Fc5*zqo}4fH`+^0)1YiR&0$2geAp8D;CBPJ5 z3or&)1I!`&0fR-rBw!OT3RnfqLXSZDN`hs;G+-Mr4p;}wBl`z~g}_8$BQO$J3Ctw> z3WKGGcX!h4a_F{5QF8wbYMF$9#{{|C;JnF1;K=1Logy( z5zHw27K0_hlweCRCRh{9Df=0NMZu(CQ!px670fF89D`-Sv|w8>E?5`LEBhaVg~7yN zV=yvU8O$vEB7>#D)L?5cHdq_XE&C;d#lhrYb1*ts9n3EKD1+s}^k921K3E^j5B8_O zD764J0kr`&0<{7)gY3IZEkR8|Z9$DetwGHp`!Q3CP?J!bP@_<*P_xKB&D1i~G}JcK zIMh1SJhFc?wGcHCwGlNEwGuUx?CVS|MNLI*MU6$RMa?DqJyVNOlTn*dqfx6-v&lZt z)N<5x)OOT()Oyr>vOhGnAT=SiAvGekA~mDz8%-@qO-XG@jY+LZ%}MP^zgKEeYEo)b zYE)`fYF27j`odDnQqxk~QsYwVQuES#k{Xy=n3|Z{m>QW{nVMPlrKXmqrlz*0#-`S$ z=9c}csl}VN9f@6Yff^(Anyy2qYq~NCDsNkyLtYn{WxGXp=xGgv?xGp#^ z+5a0Z3{DJg42}%049-mU1&2$6Q-fQBV}omhbAx-MA2M7VoE+R7935O8oSp0=4wna~ z2e${u2iFJZC;N-T1;Po!4Z;z^6~Y<99nyyxE)h-DcmW2qTy2ERN+?PSm9dXTxGv= zxL7z@xLG(_xLP<{*#{ji7fu&$7mgRM7tWX7sPw0X3x*Sh8-^o>D~2`PdCUImaN%&`aN}^~aOH63 zvM)PaI-EM(IvhJ(JDfY*JN>xf;^E}s=Hck!>f!8VA9uKXIDNQ%IDWW(IDgsS9W4M& z0Brz`0IdMcK=yq{OF&aVTR>w#Yd~|5{ov6e&?L|%&?wL<&@9j{=qrwvfu@1BfyRN> zf#xCm$D@UyiJ*<3k)V~JnaIBKXenqaXe($eXf0?iXfO0LM~gv|L7PFNL90Qtk$vdV za?o_pcF=gxdeD4ie|oeaG$FJhG$OPjG$Yx!9xVw?32h0D39Sju3GIn~>zWoNG%2(x zG%BAreeu!K(A3b@(Ad!0(A;Fd ze6%<;IkY)6I7nhR@uBsh`N{tJXn|;gXoF~kXoYBoXovKnM@vLg zL|a5-L~BHIq!%y!>d_+6B+(|(DA6j>EM=d5v`jQjv`sWlv`#cn*}oqx6ipOu6pa+E z6wMUvls@@rsc5Qbt7xoft!S>Y-#=O`nk?EZ8ZBBanys7zfR>AvefEg4N2Z5fRjtr^W(&J#e3Mw3RHMx#cnMzf~JFmnOWveC5B zw$ZrJy3xGl`~kFZG;y?XG;*|ZG;=wZ04*I&9c>+r9jzVB9qpZY1ZeSS@@VsD^l0^H z_HvE^T0WXS+CCaTT0febM>a_&LNcGv&$c^!vc ze8=a%eLwFVzmI;2{C=mrrti3(VSPN`aeYzYzx~Jc$~g%OSA6{+_tUGJ`9JP&X|Uga z+^?LUFl^b@w>*z3-6p){`E&^R@-5GcK8-v-%j&W>JkL3Ocf8^GHmf$}4bNN7TR5^U zBftWU?d-LF|MIfo&-xWjAKv)iJG*R1cjUJqZf-t>pm*8lm;SL}xzweu_X z$GUIcd&Pc{a~qy~SX9A&`t04@3ij8*lB5duo1EtmKk>!C?8l^zNB(7h7S_!9m;Fj# zO7`#2D~}f;A*tiQmz;<3vGreaK0*Tj_Ydbq&V_KgvFabr zQ_o{5|8Ty#{?Ojmd|e6!NO zIp4VrH~-Cf|JE^IVpXjce-jVp)-e1{e8{MN^EvT?KAgmlv|5v%6Hn%RnDCtVVsBXG zIq^o$qu7+N>lyKAKsU=X;#2P-9i9=d}n8xM;+>pdarI@j zr^LgrLY_S#K7P?eeL}pHb1nAH?)8Lt`uVQfPl&I>fB*L{JOFauMc!xP9SaeU)$s%V zB0ewM5&IYMTF$|kUGvSy_}ydj zg`Au5PlocCe3D<{`-MQ>w;Ym z$!8P)XL(3|`zyWUL-L)RzfnHq@dNVVlzRsskRL5W=R6=^%DEgb#?^d4K23N2r=0wn zx%g-~`Bu*BNG?h)Cm(;2RIi-;{L9P#{vcn=IUZG%)qjxBOUsA-L4F^!Jnj$jy`1my z_U)zn;DIXpHr@vxgr=w82QSFEAFnD5_rVi)fBO9%_@Z>dj(gw@IS=ITkK5e?kJRxC zzXv{<_2|J}@QR!ha$(o(yWp8KYr5S9-+0Y?|1Nk(&JRf+d-M)?X#42Jcfd!jM)$u1 zUXpV~j!ypHZSd4C>-pQ@t7@y(-Ue^Uc_ZJQZh9L$_R|Be+u*ZlzE^L7*XVl-e*1es z#x3yNfu*f(f$y51sd5XvC+Cyow%l6=9?YCKqYQlb*`>~9;6?iAGPfjs!t>w3lg}^w z^gH-+c-=+6gE!?olR^L1`yD*`V&MO7YWVch`J3QX`tpKb+ZKszi;4-7an;VSjP zpK7bC)C+R%%Cv)jT%n#=V&8v-`eNbP*;lAHmyGc`sY`eR!FAtX159m#EJM*w0;}UXycRYTO-liF)p8rgDk;u2wa} zCF(u;GczA%%cPyZQV*8LX8%fkc=OM%ex+WNb7S16y!eHBvSs_@zffQHcz^jX)SGgi zjPc>8zfg~!xU3i5sDGO<{PdNJ)T?sN%$gIEFH+COU1)ca`u5h%$cxmwa{kQNK>N?s z!z1Gs{Y-tlaL|CCsh8zknh_UWf2N*p7<9RW`npA*EhW_3a$Ze)w>Blz<2?q6P9LH^ zKY9A`1?qMBRx`(@{D*}XsOP`0)BghX{i7WXE>Q2w`8N9Vm(IfnJWttj9)2Lhd*XTc z0{UPx_h$Uii1Y9XZ=?S@2fxtdwt5b}LC(YZZ}Px%@DVv(J~;yHuFmrxnx27ANxJWQ z27aZyX4z@@7W#R^zkFw(aT-46T4?vv@H0Ip*FFtjBj@lWbU%LzKIhoad8goaHgrip z1>Ym*^K24bNZ^Am`#&m%ADX$Rs2ILT&h42TGO!pvspQfp#qdk&8htT*lbq)>=ZhUD z;iJa9pLG&`%HZGSBz%>e^YhKyS0~`JChE?efZuB5n|A`fOV0oK;1yxdig3oN&!u%8b z=D-O-Kf!m>|DE|of4n zIq#^=reqa9HoxU3D*UYLb59k%R?b2CdB|=Xd~U~Bs||klo=YDae6O63G~txH4L&&Y z(6u7?;e-1Nir|ao+@yQwdltbb-+EB52!8oPf43s|W;svkXvU9+;iIF9W*pW$0Nyiyc z!0+F`<8uJMU(R>>qWytF^nldG3k%T)R^A?5h+ZJ)K80>H7NRFi3VyI3eW7BJx*xqk z&Vx$Xp1L1BVrZMs_oGj&{X29&dWD=5b$aN@edrnI8m!xgzVV*txP9mya(>jM->U6H z4{1^IYA^ao*smpf(My<9z+9>K-c8$!p3=8Qx4q~qQQhk9MQ@SwrdnLTwg)|CSM2UR z=ri`5xqHxSn0vq+s#EEW_n_xod}-K&zO!g%`EK+cIiITai50uigF0l4-iDp z=tXjFRmV#&ccCZk-FJQ$`jXSe&AZT>r*)0( zT!6mT`}YqD(A(s^tbPN;+e$)@8`@~!kLYtHRTln;UdP-B=4h3@HUEg7x5PQ@NAx{4 z=2&UdyNLKp*`6YWE%Jg>vpzl{sEJ&=ceTzVie6qG5Q^59p0@ z9#`?yv>(tT4~Dh*0ey1hvFbmdS2EXvIbE^wzimg)Y~#0cJNjn)qq*DBJLUYYlV{?$ zqlczkFl|R4ef|6MZRn-U$zZORd(4(?=&AGjWo<)W4cpjj8+t4AG|*oMbuny1kNv>@ z@O$)GW8>rBqt`NbgE?TGznb(tdhUVeUA{-(?V4Bbd-PuBbD;mWs(x=PdT@S)x)pu6 z+qG3&(TnBWupf&Px1uKx`Q?MH=*wA;+_$1P%Xwn2KQGEhj~+N9Hy?ev<;C=T^lIjU zFlTJZ@;dqG*?M31eDrOcYn&=f*x)kHFgX7_~*3~x1g6ZXN0+A-A;OJ zK~I1F{hiI|>uuMlo6+0lys{4Y6E>sAJ0I=58GU}zP}EeqV1$`6l%Iu7$@o zq3;hieY*+0U(Pq{<=k@<9)Py<8*Rb^(6mP2COiOg?pgh5XE)*jXkp&E5f8vH!>o;X z0OUM0|7*%dJOH_uA~xayXn*t720Q?APFlyXJsa=|EdTJ3Ih#-rBi_RldUmuqg1w zw|D@4a}sY+3J-vs!}iK^!MAt-ZdFhH77sw*c5S}J10d(KeRRtGTRZ^sBk$$m0a$7| znuiBK&TTvRwIvS^z%8eKd3XR$u5X@)2Y^{FcmV$Jc(Wc4K%I+M*5d*AY+K=aJOFae z+yCZ_Uylc%U}4wwcmP^&{CGVc06G6H;q~)%cmNJGy|@k!K+X9(*Wm$RCJY_`&z@=P z@BqyDt>Zd80AYjbt-}K#=f!1z|JPbP00Rb{UyBDIrS6WkcmU)axfhQ|uEhf|<4*gv zcmUczsJj*q0JCNA0IaF@*BU$kA9Opv1`mM8${lO)05Ed~55V4bqu1a8SX|U`4IY3C z&12W#0g&_PCZ#@GjR)X)*u~X&0B)4-T8#%l&Z&!@K6W)80L$pEtMLE~9nokt9soJN z?%JY?Rd@i#92Rf(3J*Zu%Y&=%0LZy^H&#wvg$Ljt=YFg302EF(ufhW$=iPN%joQnrQ&dpO6tj@&)(E7#HTs!~?!;^FI0LXcI&1b!ziwB_Thx%MR0H57{xB?G= zoU=D+TEPlD0DA{5S%C+@tJnAycmU-5y($^aR^S1+ysqjBJOD>7Ij_J2Am{ScAA4#! z9)O~2+n3`3nB6&NIUWFJ_22>cBfa}_JODEznl8r!kbWs@IUWEx$FF0?{bhInS`Ik9 z3=hD%-aD4z0g&_kva`o8!vheqqvtX_08tO)m*D}BbN`ks@mPijz}xBZQak`|nI%i{ z0LXcO%eJpviU*)U?`cc%01S#4xD*e7oD;b0{D({N0IanKEX4!R=IF~McmU-5!0%q$ zm*4^DR&VnXJOK8w^OoQNV5SfrfaCSMEx`kjvbE_FJODq}e{TsM0A>wo^9IK~UW^Bz zwBE(VcmT5B>|cxrK+Yi?dVKm~JOJB|4qJ=|;PSpZuGcmSII{pSKa0Jl;vFTevJ=Pu4)x@7?#fZP^23-AC`bDp>W4*;{6 z@BpZL+bqBXaAj-51$Y1~KSnIT10d%#WHNL?EP^**QH+TS;^@IoDW!&xgcmSrh zIy)Z^z{#Qe=Hmg7b0GKKm^&X2K>vp0=Hmglv8vyEJOFY&WYYSN=i>qB-K^?-JOJiL zp7Zel$hna}PWycx9)K1DPtU^x@KcYy^Y8%3d6EmK%$tV?;BNl-d3XRC{Lz0N9sp)c z;Q=_lGHxCo0E1tRd3XT&FZG{?2SCoBtoqyixp)AUEV(!r4?ySu+gv;VaxUf2Pek_( z;Q{!hN%mYk0M$B;nu`ZO&Z``_x8+m_z=c0&%)tY|%qu(qbv^pc!2{5)Ub{JX01gam zJO>YeoQF9lIA{(YfM>Iu=HLO?tn?OAm?hntKP(l2cYZMF;+YPwZyx{cmU+Q%~ogso{a}!^`P6c z@c@kT|9LhZ06B;A_?jKF@c@`J*UrWR&~EJf*?0ite9rfl51)+(;K`95v+)3QcWyl! z4}hH8>3X2nY&-xRVnb%*0cdv6eKsBdInOi5{r)UG0M5miX5j%?zvlQXJOFae=d-Ds zX5j(YHh0-9JOEX{pE(N;fSmu??7bng@Blo_=r#)vK(9M3XW;>mb3t>S)|!O}pvMx^ zEIa^zC3w!l10d&xS|2~0i3ecO)oU~H0Hj?zI};CpoFjT5_{W)e0Af1k&BOyRZuO#> zcmS9!h6iAO>e!ih01BQBo{0xw(VXrx@c_uVqe0J`%)|q5@QCP)COiONZH}6W2Y^{* zcmR5DcAkj`;FlvWX7GN}7tim{zyl!Xl!nheGXoF6%;!Zj@Blm;Sug_+fSg}?W$m&V zcmTS8Wu1Ws;8y758F&DgX@&~``DJOJl!cbYHpl0L)~=1MpeE{cJn{ z_a)s}2L0CJ9Nm4QpL@c^_LG$$Jm zKxsy1HXZ;u-_`Z{h-^Fno9Yb6#sjc#pGt;0q|*)lZ6Lh z)ii4s9soIqwn?>#S$F_CEE3x1Muw5MGGE) zdd<#R@BqkpxzlSMvETt%bMUYQ4?x%c`z?3?=EzY;{lNKf&cJ&-+~7qaA!3O9)ODB(H1-aa&GX0&Wl-)O-D5Ty&S-~o_xhPOZTwcr7mKh?*A2cT9BZwnp( zIe$23yWWBapzi_?3m$;->FyRh0CFyI{qJ2ZcmSSWb+O<9NUiE(!2=-Y6|cVIWWfW_ zexQ>D4?xjNodpkooMZe;lFot$U{Woe1rNYYPn`u10NwxifBYQ}0AI%g!1v<;kaLgu zJUjq=J{|ymA07ZX51H%11Hkp+0pNP^0LVGX+z%cA?hg+D_lpNW&QIof-~r(I-~r%y z;Q^3ym3f|c0C>K50C?Vb0OY)7)&ma!>w^b?^}+)n=Pv0QMIi0QMUm06EW@{fGyE{fP&F{fY;G{fh^H{fq~I{f!5J z{f-Af&VS}S-~r%#-~r&g-~o_xp*c@@061TG061@W0OY)A&LbWG&LHe2oV{&haLn;{hPQ;{hPw;{lNKy}<)`0Kf-$ z0Kf})0OZ_n@B|(J@C6>z-xE_f&w|D@+yLbTP{B!Uy9suw$9suw%9soHP9XyQ(0DO%H0KAO{0Q`*y z06dNd0DO)I0KAR|K+aJI&*K3A-{S!Q@8bag|KkCm9>4=YeSimmdI1lBoV!jvfd_#4 z0uKQ71|9%8kDYo14*>NE9sueUJOFY|JM|160O}h&0Mt8p0Ob63>LEM;)JJ##sF&~n z$hq#+Q+NQVukZj+Z{Y!u^WLe)@BmPs;Q^pt!vi4az*EoR0ieFa13Q6iX)T4L+s88_#P_Nv-~oUizyknZfCoU%y>B7-1kD2=_ys%w@C|qX;2-b+ zz(?Q#fS0Qe0&0Pr1n0N_9H0KkXf0e~OD0{~xw2LS#A4*+}$ z9su|iJOJ=5cmUvE@BqNa-~oW2!2iY z9su|*JOJ=tcmUwT@BqM%;Q@dz!vg?+h6eyX4G#eP8Xf@nHar0EZ+HOU0KiY;0f4W>0|0-C2LL`34*>io9su}GJOJ>YcmUu-@c_V&;sJm!#RC9; ziU$Bb6%PRXDjop%Ry+XkuXq69WAOmM&*A}quf+oZe~SkIJ{J!F{4O2<_+C5!@V|Hf z;Dhl1zz^dAfG@@a0Dp`J06rNH0Q@o@05`!m;{kww#sdH!jRydJ8V>+`^)2B6fWO8A z0H2Kq0Dc<}0DLzd0Qhe_0Px{>0N}^*0Kk{y0f0Zp0|1|n2LOH@4*+~S9su}vJOJ?V zcmUw%@c_Wr;{kxb#{&SLj|TvL9}fV0KOO-1e>?!_0eAq=2k-!(7vKRvKfnWko`44c zeE|;udIKH+^anfu=n;4T&?oQ!pjY4lK)=8PfS!Q|0DS`w0D1==0Q3($0O%oj0MJM9 z0HBxP0YE>&1Av}_2LOEq4*+@#9su+gJOJo1cmU96@BpCK-~m9t!2^Jvg9iY82M+*x z4;}#YA3Ol)L3jYrhwuQP7vTXwKf(imo`eSgeF+Z$dJ`T1^d~$3=uvn8(5LVKpjY7m zK)=ERfS!d10DTJ&0D2c50Q4_B0O()1Av}}2LOEy4*+@_9su+= zJOJo%cmUAn@BpCK;Q>Iu!vlbxhX(+C4-Wu(A07boKRf{Ffp`GW2k`))7vcdxKg0uo zo`?qkeGv};dLteH^hZ1Z=#h8;&?oT#pjYAnK)=KTfS!p50DTh=0D31L0Q65h0O+B3 z0MJMA0HBxR0YE>+1Av~22LOE)4*+^A9su-LJOJpicmU96@c^LL;sHRv#RGtziw6LG z7Y_h>FCGB&UpxTl!FT}Bhw%WQ7vljyKgI)qo{R?oeHjk`dNUpX^k+N(=+Srp(5LYL zpjYDoK)=QVfS!#90DT(|0D3nb0Q7G>0O;X(0MN(r0HBxS0YE>;1Av~62LOE?4*+^Q z9su-rJOJqNcmUAn@c^LL;{iaw#{+<#j|TvK9}fU}KOO+|fBxgo{2O26d-z^{hM(m# zw9gWs$!GIB_+9)?emB>^wQx;b8`sFSa?M;j_rSeyPuv^#$h~sU+&j;JXTdY!+3<{b zRy;GF9nX+w$us5I@{D=bJae8sYrtBtCaeu>#9Fattev)oVl7!y)|NG9tyy!{o;|=` zU{A0&*dy!}_6&Q6J;YvOPqDYyW9&8d9D9#F$X;YmvNzeI>{a$GdzU@TUS?0Tx7p+D zb@n`apEJN&;7o8fI3t`D&J1UVGsIcqOmVh2W1Kb49A}R+$XVn}ayB`moK?;&XO}a~ zS>{Z0wmIXRbHNl)1*3vh z!K`3cFf3RWObfOJ5^57_6lxV}7HSu27-|`68fqJA9BLhE9%>(IAZj6MB5EUQBx)sU zCTb^YC~7HcDrze{0MuI4T-09FVANvNWYlKVXw+)dY}9VlaMW_tbkug#c+`5-eAIr_ zfYgH2gw%%Ah}4SIjMR?QkkpdYl+>2gnADoooYbDwpwyz&q|~O=sMM;|tkka5u+*~D zwA8lLxYWATywtwbz|_Lj#MH*r$kfWz%+$`*(A3h@)YR70*wot8+|=IG;MC&O00#mW0w)4D0!IQ@0%yYe z6L2VSDR3%qD{w4uEpRSyFK{q$F>o?)GjKF;HE=e(zX68>mjkB*w*$um*8}In`yX&X za6xcFa6@oJa7A!Nygvem1eXM-1h)jo1lI)T#QP_3P;gOjQgBmnRB%;rR=mFghXt1f zrv4M1w$!K$Ae5K%+pbK(pZeMKlbw3^Wb24Kxn44m1zme?$X83qcb>8$lyM zD?u~i{Yf+wv=lTIv=uZKv=%fM-oHeHL5o3?L7PFNL90Qt;r&fC9JCxX9kd-Z9<&}b zAKw2&140YZG$El4p%I}Kp&6kap&_9qp(&v)p)sK~p*f*Fp+RX{l+dKmrqHO+s?e-> ze-#Z2EelNxZ3~SHtqaYI_g~S#(8AEf(8kcn(8|!vcz+fR4J{2#4Q&mL4Xq8$4ebpL z4lNE%4s8yN4y_K&j`w%b@X+$m^w9Ru_|W>${CNKt4G=95O%QDmjS#I6&5-wp(GbxR z(G<}Z(HPMh(HwdI7!49F5=|0q5{(kA63vqLm(eiMGSM{AHqkiII?+6N{}~MwEfh@@ zZ4`|ZtrX1^?Gz0aEfq}_Z553btrg9c_pi}l(PGhL(Pq(T(Q46bd4C%X7cCb}7i|}f z7p)h~m-oNXfYE}{gwcl4h|!ACjCp??4H+#NO&M(&jTx;O&6)Sl(V)?y(WKF)(Wud? z(X4rY9Ss{T8%-N+8;u*S8_k>d-_gL)!qLRh#?i>p%F)bue;y4TEgelAZ5@prtsTvs z_wUi*(c;nM(dNRaHD!P)73VCIk9_ACVBr>e(`sfG*M{ zVPCE8e7eiau2J1TPa4!CyXV?odwLi5`K|BWevgx%^?yF#$-oDLZVoOPQZ#f+%KTxY zhPNG2BlT74>5(f&^&4G1?M~XdFNEAz7W{^V&<(=LmjZ!-0+_^dipPE77I`HA>^_epandQF^} z@n%Bmge&7)jNd%YZCvu$ed+r2u3xVkb0@8OTEEdNMx7q{Dz!#x+YzIN&mXoWrD$l$ zkeh=a40;`(qPoBAdb-Q%&cnX?xYL`C_6}L? z&F$W_+0%M>LS(CRUrzp_=I7@>8*2_x3Y&Lt_IHzS8owWRuu+Fkes4Ic!Q0r`^#VRx z`C-)$HrB2clV3CD{VmmNR9hb%R&`0FPsH@FSEf-Rw}ZO|6&srbZ1a!soA2|+YlP<& zj|BI9t~Fd1JLz_ukoEr(2%7b~62h@A0ii6OKHv z&sQ7Rt{$FwsNumoh0FK1+vm2|zB_Z5d8b>!*&Qo>7`(mFHm~n*=ND~Rvw7O4fg9Ux zi2JVgw-I?k>;2dHtqoWcvbx%;*p;8>_E<4?`Ql}JmtJ4uv82)BVT-=axv?;E;eZ9( zzNwhsW`5qhH*tEDo;iJHz|0LZn#{O7eZusJ=_j(sXV=NT zot2k0Fsn}1OG~k3on@k>hb7)p%TmSSWpTFX#D8Am&$YzA^$=fMC%)&UrH=UiysX<< zb;Qq~$c_-7b$NP|85?En*j?V z7v5O-ZO*VojTU<>xxQrY(#6ZhF7L78^W4~#)mDY94p`&2)_+~l`iQ*R-^P8{Zo|Nh z(>ATyT(sqOzSsASwhi9C;)k<4+zQM)Gk4i{yX|eaZ~6W^g$)nRJaqMN1KWJ{fj!~K z)}y}1(@$JG+49t`)9;;Ke9ra4#F8gJ5B=rVue~o{`7P;6=~aQpZuI~Ce%Z*|FYZje z=kdql@~DT~9>x86;;*hx?>-y*x7$CB8eAVX7^|zzm;r-$NqD3DOdXdo2gq|q$ zRiQTv{axr0bN5KNAH8ZWh<-Pt4aG+K?tCdC1pR;LAyiX%5b0$^KP3E=^kt&AQ>O5S z_7(opS)xxBy{=}8UhVYUqHhJ0g(F?7+=%*GGeXtRpZhFJvEvHA_<_3k$ z3tNXM^xGR8b^bFv|MU)||KJ~wBU%j>??BK?F*o&1n-`*=FMT05m8%`-Ps#f&^vQf$ zXLaYc;$0nje!l)IvOB#;b4Bk|`k!_QSlg>CdQa~uYftu>E8Yj=owC@t$H|KBS%2P- z%W3drAn)n@^5(|i=&TDviu{X)X06+tl3HW_u#LGRhfjRoW<+xB8mVtTew7;JdwS&B zt;&DFduwlbz$cT|x%8QwbNl#Y`c>U*J7>zS^LM7u z`^nsI?$m`tE>C^9Byt+PDBgBmJMDz%Npx(HXXeZ9ahc5cphw7_Mq4tMcRiB1K=iw@ zpLvj({ocz=dbcnGhX3X7{5xOg`}lr-j-Tgq_&h$B&*%5>`}n>5ey)S-;kvj!u9NHK zy19PtgZtsWxIgYwyI*nN+&|BO=fQK~`S6^0UOYFRAJ38J$#doT@|=0zJa?Wy>%e-j zF02ph#CoxAtRL&hda|ypFYC;Dv+k@v`+)txzF>c_PuMT)8}<+Ti2cOAVt=vE*l+AR z_86uk2g)FZ-DN%)VxSv(MS@?0fb<=YaFTx!`=#bIv*MoO{kcI07uPLExDg0uv1o zxN5(^W^V+(`%Yj)aONd}Rl&Pp=8gh)gWanM{C`Z;5Y!>dMJ*F9>Yj|4{s&jnWn-vnoLNbo-O1$P7g0*CUx;6dOr;3MD^ zItpH(v#9MiiTeD8sIjS&sdbH_UZv)wZVVE&AN3nG7dZvk}NvJER4aNw3 z4@L)P4;NS&ybETXCUECwfgQ#2YyZpN`FFm~_woJw96!(J@OgYLpU>~%_wjrA{agpv z!*y|eTqoDdb#wjP2lvB$aev&WcE94jxqqGm&x7Z}^Wi!1ym)RrKc1t;lZxlc^W{17 zym{_Cf7XHZU|m=r)`|6E-B>@?k@aL@W5i`;C3a{$n4qAK91ePxdMMm3_{u7n~2y z3Fn1#!};MHah^C=oG;E9=Z$m6`QsdN9yynsPtGalm2=DaM1%JE4`@?Y)aozo{NPQhK`XGBd>Ry6JzzxL!rC9rpLe1}mJl_67x z#OE3(Dw(TN<0tRQP&)1VI=-TFhH~~oM!ezK1m)KKY4OMBO;GGpv*SZ*Oi<3xni=nL zaJ;f)fi-?b`|*m$vU%|*ZjMu`ty&PjV&pjG#@a>k-mk|h_4Ag-SDP_b3Hf$K{0;B1 z%B;Lq@yeofC1c&%_*Oyb%Dq*2@fAzIR*o;*5dT%+*NSoB=J@Ul$0)~UZH?D^j!}M{ zvORu?B~58E=EwN-e?}`$2JVWFO&P5`?y@(2UFj&L&KHI8ww9xmy!waYm+Ts;w28FE zM@EcPqC9?z?>jwJ34U@kKB;_!Qv1@0c%QZ-l+FiE#ZTTgT$#7}Z2T6_;mYaB7vdKU z9;SHqycpk1O;J8;bSXZ+N{TYVP#Rx4VyI#%zZ!qmHbj|dyAf~oAEIA&pqB>dD!z={B!sIibvHK z@$a=vR?d{Y6bxBErEcDGF{ zQkt)EQ4){!RA#(zRfa$Bp=7spSB6LRP#S&fp>$42Qda+?S8AqpSAIzFQtmD5ri@tS ztt>gvRVjJsqtw6FMY-D8PdWO!vod40ztY;&SvmKMK{?UzE2SXHs5EciNvV?(sQfr6 zQE6Ndq*R^MQMvLmSjqXOgW}OFL~&lUm2H$L@oOxMQsdKxf@~Yl@ip$+Em6b!QDgWI0LfN{ux}v-N zx$^$S8cOY(Efg=eno3&fXG*t5wUnzRX2oqtj5743q69Ckt%M(rSF-JOlvg{ND{UTq zpt$5UQ-%fARhlkrs@!b)k+N}06J=BXdP<9w#>%0Yu}YP%;*`kz`bzaCpDL${8z`yK zjg-TGG*td}{Y3kZKl5*Vjql-m`5Ate&)~E8Og@|6!SCXC^1HbPu7zvj+PFrpm22kO zxd-lrd*a@>NA8t-=H7V*JPV!)&xU8jv*MZY?0AMeOP(pumS@bf=9%;CSp(LBHDPU7 zBi4#FW9?W&){-@4ZCPX1nl)$b*#qna_5^!_J;GjL&#-scL+mB?l(x6T9%HYu=h%Df zLG~hhlD)|uWv{Yl*}Lpv_A+~#z0DqHue0ab`F_qX#j3w3*bBVpgU}7;bnb=H>CRP)(iQU9-VmUFL*iMWm))VuI z{p0|00Xc!(K#m|+kTb{~| z+(wQg*OBwcedIuLAvuxUNRA{|k~7JjjwRQcM9wAml7q>`m}CD z#`=l%w6UIIeQm6-SZ^EaE!N-0`iuQwV?T)fVPk)Y{bFOki2Y+@|A_r$V?T-gWn+Jd z{bpmoiT!6||B3x*V?T=hX=8ti{c2;siv4S2|BC%=V?T@iZDW6n{cdBwi~Vn7|BLfr z<2;D-VdH#=^J3$?i1TCP{D|{p<2;G;W#fE_^Je3`iSuXU{E72u<2;Je{EK*CBOZwOU?V<=cwr-6i1=Y6eu#KtBc6!( zVk5qYcw-~pi1=e8{)l*FBOZzPWFtO_cx5AAiTGtBeu;QyBc6%)W+T3dcxNNtiTGzD z{)u>KBOZ$QXd^y~cxfYEiuh?Geu{W%Bc6)*suEvCyj6*}BL1qxUlEU0;<1R&Di}b- zYn6B{;tn0C6uW7(m>c3I-7Os)7N;y{ljV z@hns@fOs}47(hHL6$~JroeBmJ&r$^gh-a&U0mQRb!2sgft6%`J7AhD(tc?l=5NoA^ z0mRy=U;wd}Di}bltqKMZYpsF-#M-N10I?TTFo4(_Di}cQ6%`C1_Kpe$5PM10Fu?O3 zDi}cQH5CjX_MQp`5PMMt1Bkt;f&s){Rlxva@2X$`v6odafY{qA7(ncG6$~Kuz6u5q zXF&x6h_j)B0mNBR!2sgys9*qbmQ*l+I9n7(m226$~I^p9%&L zu}}pAh}fuV^-=rrDi}b-P8AFwVyOxS5V2JS1Bh6wf&oPARlxuv7OP+Y5t~&ofQZ#9 z7(m2s6$~I^xe5jlv0ViNh*+kMD9_+03sKuU;vSuR4{1%wm#bg^k=s=;fXMYK7(nEH6$Ie;`yt2I$^Cpkxu2gW_w#v&9iLC`=l7BO z`TgX6u7}*u^^yCzUUEOzPwwY_$oPyQnJlh4Tg4{CdZJ>tNN+TLLgIq>BF z?=7*%9C%aPd+b369wqmKPqn?uo^{|?Z7;K@9r#w;>+E?4{?*O`XTpJxwX?#Rao}g| zEODkB_*y$_oH+;n*3Kel(t*#lv&xxu;CJmTbEX~mUOVfYc?bU2VgWJ1Q6Ffrf|%i` zAGBCPOmWl~TC5@FIO-2A77>#i^@$d%h*^&MMT=#`G)H}-#X4f1qyEuiAu-WWA8E0Y znCYmWv{*_^b<|f{tR?0;>Mt!86O$eFnHH;w*^c^6i{-?0M}4QodSbq#{?l>+Il)mM zYPo`(;iw8O{r+(`~~)YDpSCC57IZ7uhbgB|raxu5!6%hlv;NByqla&o%J{nYp5e(HU4 zzXc57-~%*lz}FpofrcHx01iGu!xnr#SOW~;;2$(B0tRsK6BIz`+-3*bxli;FC0L2?lWRO&azD133674V!`i9DJ3AUBLhj zK1;*4U;qc-rD0z%fP)XyurV0G!Ix>+84Tdy(===i25|6g8ukVQIQTdXn}Y!ye4U2f z!2k|EPs8?L00-ZvVSg}ygAded12BMtFVt!WFo1(k)M^VbfP-(;Y7Z~~wFvQu+5`;X z;48J-1q|TeGqu_V4B+59wb};^;NU~G+6WBb;7hgI2@K%iQ?=TP_)4t>25|7NS}g_! zaPYJLU#kfW;NWkyS`G~0;CHoJ4-DYof3;c=4B+60wOSDj;NXw7S`rN4;Fq;p6Aa+s zpS4;P4B+6WwOSPnKJO_;;-q2Lm|xd979l13375t(FG^IQV_7)&~PP_F_yVp03;_24{(y@B1HesyPv9!R0B{%J7q|>C0Ne)n2Cf4P0QUj@feQfxz>R>9 z;7Y&%;@K(SC%6U;wy2@IKrh7yvGidH`+^3; zZ@@i*0pKF3N8l#G0C1JmD{z-!0Ju!*8MsX_09+^a4%{ag04|hz2yRrXkN#gTrG8St z0C1_)Q*f(b0Jv7_Ex1=O09-8f7~Cuv0IrsL4ek~U0GCTW2e%6bfa|5+6R}?b1Hc7S z55f(D0pNCIe zuAX`w?j8&va=$`7FLJxa@r&HAasF`s)c=m>03XDDhOO500(oioMvJL(I5R*SXbYbu{1epbu9U=KdQ@%PGl)bagVZNg_bK98T(zDMFV zemS4vxE}4Z`5lhyt&DG63!lNY@fqUxY1gdR=?n&)&e^2X8KZSN*BG5HC|0L)kJIU@ zm~}dRf=*{j)aksEbh@x)oz5pkr;A9_>HIQux+w9B^;VtPkfSp@=jzPHJe}D!UuO;~ z(3#x}b>=E|ompS3Gn-0uX0KA6Ijl@)_9@qyBcAHaeib@%l+H<~*E?AZ1}BTN$;o1j zcCxs}I9Y;Xoh`r!lv6J0Y;$-(Kb+U(*IoW;6o$L`$o$P)U zPWC9Bv-thax&VW-&c)=c3ygNwxy3l^f@7U^9&ygP5VNz+Gr?IGn&_|5?^j(qBD_OEa@SJk0c?#jdxy*aLE0>@K-3_P{(ByIa1C zJ-EQd?osGs53#%0J&RrJp(QSM?@||gc$tgcx7@`Z`P9YkU*Te}s&myD^sah?!BrPv za@8B7#h+tb^+B<&diOY2eHF8-UZ3EqHzm62y^>t@Vacv~pA=VpM4GGKFT+(IWpUM+ ztgZ=$99Lavu4{rZPke8_YeGA;!%b6zgVnk8`tDF}qpy32s(XqMOw#$;}#; z>}K^zakECGxmo=(+^kU+H?zs=R&2;|Gl%B76&v&15={AS#X$vb#qNb}#Z~NX#rk5m zVpEA*u~(^EaafsKu}`^Mal})%V!sNv;wYWF#h`cB2N>Kf0Va2SV6?l{5aX^7j&;|2 z#JTH3%>E9>u{09>pGo9>pPck7CbakK)i0k7DmqkK*t$ zk7D0)kK)Lu9>x9@9>rDlV(ATfv)-gP7^C&NfEc|YC{}L?h|?RYBC z)*F0M^oEEuy}>U-Z-}z!bzW9|q9I4G^Ul>L8uRpeuY7%CP=Q|WU8qm2Vi!MGtWPwR z=o7t4^@(9+`b3{{ePYB@eWG85J~2w~nPV_`T0BjjImT#DU1*GFPEf3;B{a@6r;6EA z=bhl0V@mYQ@k;W{2}}0O@k#N_iAeLz@yqbciL!W_y{w)kh8$0`cdloNG0!u>E8nvu zsK7J9yU??wirq8OtJt%|RN`6URq9z1R_0maQ|?(3@zk@#ufnq=O7CR|FnJjQqrJ=l zFRsrS7-ARyUhI_^TH=-HUFww>UgnkPTke$@`P3`XzrriAs@^*%z~r41 z80~Eijq%P2j`g;O#(C$2BzWh9CVJ<1Cwb?DCwu4krg-N>rg`W1XL#pSwR)EX_c4SO`y`o4d<@~GK1pF^;xo#9 zk|Lh^B>7eNBt_|b%|^X%uEF4I4mA1Z8l!y^j4{5sL9xCGfpNaMRm{GL#suG7Q=)HT zV3KccSh8=DF~v7GBF#6~FT*!C%HnGdv-*}Aa(vCI2Q{Pg*3g6Nwou9?1_cH|;{49YcKT}||pVb)S zX9|w>vj)cbnL^BdImQG(Q)r@JPGFLsDLmOP*O=mGicIq}`Dgfiu&A4F2{&lYeetw12TN#y>YW*1tF~&ObNA>|bI`@XrlR^e+ia^3M%V_AfQ2 z_~%BZ`RDp)_~%x&_}jy*{-psq{`T-(|I)xb|KhNG|I*+B|KjjM|I!e>`@}fk>Gg<>o#+-meV{Sm1F)zSm$`2?DDhS9m z6$X@5u?H9eiv!9`B>{YI-|~{H|k9WquFFKMh8Y4 zqfIf!=-^mmvMJ6O9bz`>LKBS9p@~LwXp%8HJX!oX#TXr#W-K;l7^ACNjJhzZQ6H9L zG>7FHlLN(-hvgfSgA0twVTH!z5W7(qUTjPbEiszIOO46lW#Ti+jmeQu#h)vT$yIfM z29rL}WHJOMnoNOtfzg2prkKFI;Ml-oQ(RzPh&j*@nh=;5ni!ZEniQB9o*bAEmJ*m3 znHE?amJyg&)e>k3vj&>Nasm^>as$i6CnTBj1IvO70!vMWfn_1~Ktp(OU|DEMpxEny zW#MIkNnz!IWsy$Yj-=Qj#>DsJRpDrpq%Qr5`9T;3?$?Or1n zQ)Q!YQ&l4ub9JL|j~b4R9UUC~999r!F~`H|=x0iH3^k=W`k6BwL(SQa0j3(y7>SgsH@_(xuFC zgt^?Y((RsOu&L6q(p2RbY_4{!^iVmeOf{Tpm>it^OfF6Vrp8WH)TyyK+{xP<bGCDdDaX0el;>Px&UdbKE_N<6l{iU#7 zDxE9MRnFz+YUfH%7ne{MAC~|ZUzbo*u#1mdxQmY|%EiSk#>L0O>LNDmQo}9HMdhC9 z66%`mA~x*e?Uv`#*gfAR)LiW1;8r5zZs1aBb`!?3(8m>RQ7++%?Y><(ljs_oZY8e4Ze^}wudY>Y_gr1vD_yHhRj!%t)vi?@zHU*b zU^gH4a5o=wl$(otjGK>Vnp?43rdx?yp4&aQe78#Z$?9I>mh4{UR_tEx7Uh1=E!(}) zEy^rcbFX%b^7M8OF#Ecfm;>BH-Gklp+{4}T%u((o?lJCpo@wsI?wRuM9QR;zp8Gxb zeD`v5xqFp+rF)gR+P%uNhN-d1#pGl1HHDi3OrfS=QZ`*|drLp{>Wex8};P|s|0fJcrw+#}B%;F)g@ z_bfJ#@F+1yd6bz)c$S-^JnxxE?_9x)!i zp2;5B9+ClhW_o0M=6NK0=6mFL7R%2i^6xT_G|zI6JkNXbTBZEH$|KXW+9SrZhG%0> z7f&D00MAg*V9zMe7|(pqWY28RG|ytsOwW6sd7dTmn@aino@cpdl_$#BAJ2d5lQ=GM z+*_ZI=c`ozS#*~_o*%a(aa`eww?0&xg)4NI<95XD&|NNE!5J!*{MS>?GI5@VGe!L2 z-~5i(c#opD$(aLQ=j;RTk(aH!ho5;5uk%Og&rw6=r*csRs2t?~-l`F*#`1q(Rj|rO zeiy6?m%l^h?GnGK|JU*MI=GL$4z|6Q+L_OC1*I-l>SU#ER(L|C zuC`zQ|JU8zr-M9-i>lamkKS@;KB{E7*AjWfDnE^o`z@2d)8vke<(|vrt}|6Js&aW( zvHUh$l_S5)RNYhM$^Y|JmGVlJeEOdJt4bPZou(7JKuCb=Ep|QHLzOlBkFYnbN=APl6`kwxt_MXu}<)CxWI2at%4tfWz zgE38&rc2YL8Pe2g`ZR5tafE7wZiHrpVT5{weuQ>}u}oE_E7O!2%G720GHsdBN9CjQ z(fAmA)ING2t&cHJm8Z+oQQ^tIPG}+H#|>%2(&B@fB9XSMRI!HRh}Gb@`fnVJY(U z`PzJAj4DPKqlq!ZsAKdo+8ATCs#;gAsWw!rtM%2|YNJl2)9ExigHEl}>$Ey!k}64; zq)9R)sgv|c+9YE?RX<%nO+Q0Fbw7PSZ9n5_)oI;n&1u7F^=bWS?P;Tz%1h^^@iKU+ zz4TsMFXMLAcHMT(cEfh{cKvqkcH?B#WZh)VWW!|jWc_6AWa9(X1Kk771H%LL1N{T- z17jmqBV8j+BSRx~BYh)nBjY;NI^8S2kI{iBBI^!tSDBUQ{D8nfADE%nyDC0HN zHQhDMHN!RaHT^a1HDhO0XI*DaXG3RoXMJaFXX9bjVclWPVZ&kdVf|t4VdFg2Jl#Ca zJi|QoJpDZFJmX8%OWjM&OT$a`OZ`jjOJi+SZC!0mZ9{E!ZGCNRZR0A{D%~p0D#I%E zD*Y<$D&r8<5Zw^X5W^7l5d9GC5M!yTR9C7gHI%AL^`+WUV;fZ)T^mgsLmPD)eH(2X zW3DP!m#fJ&IcgDp8lHNi-y?6ZMJOM5DjTU+1s! zH~6dl_5NCa<4M&?-ATyVcIa`ebs&4ea(Htef53)eeHc?162cE15E=%19by^18oCi zx+-0lu1PnftJC%A+H~Wms!w&FYCbi5s{T~}srFOj71b5p70ngH74;SU73~#cM^#5% zM@>gVM|DSiM{P&rLDfOsLCrzKLG?lXLG3}~9Mv4%9L*fV9Q7Ri9PJ$AbJcU*bIo(Z zbMIqMiYi5yqDe8Ns8jSQ+7x4;Do_`w2{Z(%1NDL0 zK;s3~1>FVB1;YjP1^or>1!F5!D_tv1D?=-FD}5_%E8`y39^D?z9>X5>9{nEe9^-V? zblr5#bi;J@bp3SgbmL>yW8GuTW5Z+hWBp_8W22kOP3NX@Gq|bU^ln-=;|A3R-3HAD z!v^&R{RZs@<2cng-8juS!#MRg{W$G7<1N)K-7U>6!!7kK{VnY+V|P_|U3X1)Lw9v| zeRpkl<1y7S-7(EE!!h+S{W0w^<091}-6G8*!y@$}{UYrm;~Ui*-5bpt!yEM*{TuBY zqd{fR88iliL2b|*v^)mf3?K0y4)d1Z9%>csy^#J_;wYb#r}lZFA#J)lS_`%}&Ek^-ldx?M~wq)fC+n%@o5F z^%VUS?G)o9)g#>_%_GAj^&|Zw?IWX;%1P&>aWXimo%BvxC*yk6dfj@>dc%74di{Fs zdSj3(NEf6DG6boE^g-Gn<8{?_-F3}%!*%s_{dMhiV^>vIT~|$4LsxZIeOGN)<5AU7 z-BHa^!%_87{ZZ{v<9yY8-F(e_!+iC8{e10w<15uG-7C#2!z=YG{VVM&V_j8UU0qFG zLtS-UeO+x`W2!1um#RrMq^eW(soGTIFx4>KFwHQ-F!eC~FzqnoCDkR}CCw$nCG{o! zCG90+J5@VfJ54)7J9RsKJ8e7TKGi=^)^#uI{?F8dp)m`0P&0WJ?^aYQb#HxdZEs_ts!&&`DKr$S z3-yKCLSwutUKg*4H^i&s_3_$x<9pS6-FwY@!+Z66{d?_uqwT1&DqZ$19+16^tz~~> zJK6IXE&CvKvNw`F1?(eeFZ(LlFR)zp2(Tyfi0sp3?$#K~C$zD+Qhq7mseWdIy zWxr`h*@McyR9UO)&Hh#Pw6f1t*1mcVlKrscJM5cfFD?6P*>lTUH+ys0uge}@_Vu#Y zm;Jx2OS2Zu`ZH_J+Ha*W<2r-p0F$I3p>N!usiGzJH#HbOY9Rn#a^*n>=!%6p0R7}8#~9| zv3u+vKfoXG3;Y8=!C&wj{0Be6pYSXE3qQl(@H_kuKg1vLOZ*c*#b5DT{1-pQpYdz_ z8$ZY2@q3m0C;vY#KOdLh7s%@c^1cFje*x>Itf9(v3Rr8E>lU&mE8kPd+AZt3^8JOZ z`?40y`Y>z8tRu6w%zCrje!M+am za!wBQ8)7dJ`-|9f#6BeUCb3_M zJxuItVy~0o0?uYy0{m;2`a zc@Cb3=i>QzPM(+N=J^>1#)ENTd>ALji*aN87)QpFabMlFz+P15h52D0 zEAz#?iT~T?k@;j^nP29ay|>Ie^N$^157-6vft_G4*bVlB9br${750UlVQ<(S_JCsq z31LqNXG_>y!Wk9zsQmxE4PZC;X5V|$8>|Ko!Dw(2Yz9BUWN;NM25-S&{_t;p$7{TY z_wpG&%Qd(b*924f4!(=;1Vf1<+za=_y>XA+EBDO3^9(!-&jcp&j65sP%(F8F^0RH< z2V=(Tj3w`9jQKoc&-LW`w*58C72n4k^8L&$_rsiXf7k-|i;Zyq*bdKwP4Rr#8qbRj z^8DB)V}Q*v2G}xVfQ>T-*gj(**DhiV@D;`YA7TvfEye(!V+`;`#z5}f#wX#si~*Ry z7~t!S0T{pt*nj5PdA-2?ex9Ar^Xy!YXXpAnJKx8%^Zh(K_rtSue}(q@<=MG^o}K66 z*?B&mo#*A*ZGY_JVSA1D*vHHES*~FpPusQm4*Pi9zMFfn&xh^axJUbZ+3uZZu+OLM z*?30#eDmy9#=ve5wz1)LyS>=Pj?XiejDg+0Y-7zB*zM0Y7mR`3K5cWw7})LCHkXWn z-M(#e%@{EEi~+X57+@QW54OS>U^|Q-w!|17-0L%54Oq}$i4Sr z{;*}nK%T7+^K19_0+`k?%gWj*an&wyi@ZOCr@bE))N|%^)4=Cpoj!rH?exj>Fw?-9 z(}Q~6kAlDT{xEJ)wM$Ce%GzP_>HjSAUW8eUzoo?8xD~euUU&DY`%xA7GpCpCdmaYI zTPLg2C+T0A2AWp~^@Q(DTV#{P2xiiA#;YMJLXCk!9un|FcY%Kj&5NEp65BEa~ch zEw9vXET3trEk&AF7BB4!OR~1g@~8Gs%O|?uEk|_^EpGZ>Ef)RHmI{5jWuW1zCD(At z;$SSXL>r}>q4Bt-SB)cOPi+_(|GZ&H{OCr*v1!kPsNov=f!n$$%l&(>ivajpGg?zA??bZ_%!L1vp93;t-M|I2+BI8H+V1`QmhErOU*7)E{0Hq*=YQ-IK7X@V<69s1`N17t zMjz==8ePBRj_6q((T-ud9X^_^GDd)_%@-q0>x=H+y$ zHBZ;|-rSI`N9G>unliUhw~)D!-8#*^(9Jm4%lG!2c;EeV%6${(bo^-aoK+vSnDgkP zH?#e^m(KpW`{votyGPC*(&OXVJ9;#j{jLYTawuwl&m~dDUL&JIdo_#7@AW*=q4$Z% z@ZM>WMZH5Jo%^(noYm*;tTTPi&ocY1pEcKS(yR-9KC_y9{BGugk1x!;^zr(cUjAV- z7x}lHS?2#b;={hDBNq2v9Z}YIY{ZBCydoC$dphHCzx)|q{S#)y^beeIv46uEO$OYV z9z9^k^z#Go(=OAa2ELniYT)TFmdXT!2_ml9$ahcv%#0A^ck{tO4^VyQ+^%dHl@SRpT9^Ln*GI(LnnRF zWLWbrqJ~vYE*iFHa=qaZlP3&sJ2`jwvq^7<=S>=4J%6GB2q zyM^o>ePhDkqt{OG3mP#YC8*YfYeDBeH;-BJ`P4DJKhGQUdi?7#hsXN|&lxE_K|iSqaPaWCXIx$??nd539i#b?*#6D#GD{=tXk zDzC@q$rY!{mCa+$eSS^8DMh}~FKDfN`;7^Egy+`i; zN$4zmJlr#(PThFv5r8ok_54qe6gCR zGZu*jeb{fTSkr2;s?%a!uOrID(iVxudHJsw>$@OU_-YaGP4)KT~JtDD@8_2a zV$2fcsNXI^P$W&zVygUBnb-b6f`;~s8kvAjUZINmaPP> z0tL0E3wospitQFOD-l%tZDEceTtIU#K|6mzy%0gaI6=V;f`&&06|V|9K3mkkSuH`! z=7O63f}WoXiY^c|O&3(n6?8o#DEo_`ZS~^Cg1oMtEd_=B1&zlED$f>lULh!*Eol9% zp!P*U?+QWj*RccL>j|oR3A%qIC_hZlK1@)5zMy}KM1d@c26++{PDymQE>WUVqQ#rI z{!XUu|M9U0` znp-4#?vg0FPon7&iK@pXx}KCMTO!f+l0@CB5`D`h3jZw8_*aR_4<$PPE>ZeViPlvT zwO>f|ekD=7TB7+IiR%AKbblvN{=G!||BL!W|BbR1+*STudH>!r^Zh%^2k-y2Y=8I0 z;`grFa{J#`mIeR5ur&C$%Chh6pO!&yf44k*^Uz{_^Q*=5=4VU(>vGG;*H&`9DW2uV3x6eD!LVrNgT&meRj7Ea88zw&?y|ZpnTbZ|V6m+EV^v zhAsDTcoA&LeLmC@@Vu|(!C$@>>tAgxroWn4@~a#zqpCiz{PpZz{Fl$F;#)qeh?h=s z@nKI(;@|&yEPmskd*i$PnH7KSNoxG;Czkj+Pa@;@{1FmA@Q>l~4<7f4w?1wcZ+>i! zKlXd=_%Xk~iTk^eb|es*lc{S~n_@6V3i{p+~cfxr64KDyU3HtAmd z*k<>t7oYg$m&KvKoLT(e-Q2~S@1`&Ab$7wyJ3oKEc=6Bvi`{;1zWCUkT8qctdA6wf z_SHq1w~sFJy}e-(Ip2a?A&Z=E`7iqRrq`k|H}#9E%YR##SzfZxw|w`)n>SJxF1#^) zq1%nXh2LLqwQ&6P4;H@rsWN8sPv>I#{IoOX-nFEd#A{(O&93>yoVjX_nR@ljf*MzE zEZB49@PZ*%(iS}ZF=D}*9|tbz^kcIH*UR3|k14x3-@WY6{KCtr^Cw;opHIHI>yrEY zz)LTqpI$7DUUP9rbeD^9(Kky+N5_@6jc!t^i$3$i?RnFFI5@A)4=d&!ybv;P%!MxV z-kz^DFZ=wxx&6-{nfpgc%G|V)khxt-I?cU(&Nw&W-0e9n&h4Lb@oeIpxo1buaXs5Y z4vN2-J>^X4>^f&Q&pvcIa`w2>AJ5jDZZMl%cf_efQPn4xM2V7P)WDO?qMn_29+`3C zM5Nz|w8+ZhkjOQ~Z6muEznyiz==`izMeApEDVj9vZlTYtxiV|rz6@QUmbDl`>_#AzxRsp`TpsQ+sE=}EI*boqvNr_8F#;HI3wk| zJJY*oA&#+r>Cv|cGa}L-;SJCb<}m*=A-w*2Or%T{`yEn_^u-z z!-J0eGgWt_VCtd6iBl&Y9x&D6aILAuhb~WvJhXO-$DuJ(E**565`XaLFWMc<{^IU| zNnfl!(EJO(1C^7j^7c&Lniny7WM13Jy1ZwTj_%K!6uy7iXQVc+bq zivM*8TmQemCIG0X<9p}eUUv%BkW8AatFUIcPo;x;d`{c2v?WVEUzquB? z=9`tlgTL_)HhlAX%!zGzV`8^W9pk&rJm%%rYeD}<{Yv%fOU1RpstvS=@N}8NkN^|ZLnO4-hcUpMwI%y8Q z@2t-6mAg8$SL|wIuOX}V_oSx#Gxc4M!qgo-(o%=?n3(#!d#BW|yX#Z^x|e_X=%YPf zuKFnM%Z?w7__EyB>&tlG>Q!F8rK>J<%U%`PEqYa>Zh@x9cxxTa4%I=-trrhmxB_*-bo|NXD5>qa89G5byW2cn*9c!f=>F{7ha0lwi zj1`-GX0Q0zXUK{N?VGPy-d??;W&1nHrS0;QXSG|KT)$mp@}ahalSjApPX4EjJ~^|^ z{pH=;6fM8gI%|1c>&44GTaR5{+{$1Y4V5EvR^Dc=QU{QRIfHmjb08*cQ$*O z)W6xyq=!ull9HNkOKR3MIq77RnMo6yj7(BB>7KNuapR=kjg3inJfALE?0J2Ot7pNI ze2*PVf;>`}{B54UWUV=5Ne6SllFO!!OJpK#yt#{`RGae}*J zUP6AO?1a&c(h{CGv?ioBoSo3RVOT;*gHZ{S8}v_5H|Um-RliljNA*n!*B$C5%yG~p z)OC1e-COUmbzr@_){44Utd_dxtnPJ>TfePy&^oftF6*<})bQ)9Eo!G&Pt{7WhSZ9& zzO5N$&8Qh}?Nl?=dg%k{lKa6YR>KE@)@?QVT6@*#WxZ+aW{oy>usRsqSaS_6tOE^A ztrdC?t3~f-b<;apkLns&Khf2*{;92PP1b&3_0k%xMH;>JGmX~zO0BV`tJT)FYL)f8 z&^D88?2XF0@jsQd>wjwNmG>I!%=dD{<-Ojz{hiV3_wEDh?SE@q7yMh#+Th;?)_rfC ztb^XVSs%Xfuv*_VwVK|vu;#yRV;%XrgY{W;H)~3DFKhGazSfif1X@4;=M(GeSHae= zUWHmayb8CL{vBlv|2xL2`#Zs!{W8Vc^W{2gxiAp(UhJ|uyf|pheSX{;@cf+h!CzOb z*1zsrO@BSM=2yM4j;him{PnC}!k5oX2`!(sN;vzpTSC~={t55@9F?%~&#;7Uf6h+0 z_QaYn`$<|tohR7|d;Z8v82CqV!h^>@CRiWePcT1zk#OvHed3tkofH4AY?+u|*)y?y zU#~BDbnoesqAxgcrr&o`6YetwyB?2g0Iv3Eoj{q~@xnYX`K>U$f`CVT0ETLnv< zMJegqn{Sqmx#_a3y1d)6%<`aRzU9%&Zr=E6*}@w~mbu;dY1#MJUo9Jd-Ff-DpL~~Z z{%P#;K0hs9e(ze=^2BRJ%bQ)hzx>Qqee%?+-pMtt4o=>4B{F%)m9@!Ff6Px_^W&Z5 zPCu$wTrX?BBBpG}3iq6}v9gN(sEwDdp+KaVcvqCZ=?`xF_Xi z>6MhY(zhv1O1)N|`C;hFX+O+gS?7oBl?N|eSUKjx%aw1>d#=hpAGoUj`RG-Dlw_|; zD=A&orKEb*?Q>pVCY&4bWs7rhUtT=B=gYZg%fECL)wQBCol>WqnV2d)v{MhAE=(PF z`p;C&X}GE(t4ExQU0r=LclDN&cUBKPStsq;iQZ`$CnD4QPGqN57GFtQQ>Fo;)Yi}Lzu{P;=)Y|sPcdotl z{q40&zpuZ}=lg-{ZXb(ZxBS?VbsdjAS$FrlW?!XzH}0#h-@%hz_-b{&W_{269_xSq zcJBK1-{!9G`|YFkRYyHDHXj|EG5F}(jMqmlX6!mrBQxlTf2Qt8T;`#}-(^lb{4&$w zu+P`Uha$d?Je2db$DxN`Upm-yL;S&z4ebtY+Hm*4%?+y$IB)bjFnVKE-r9{@^R8?h zndh)cmp6RV(fz5L!uMa=3Ly&>6;_iWAHvgcv;s6F0WYwn5ITC{ur*67_Yx3=Ex zyX}5X?6%CD6WfO87`_p0*KZ1Tt@&o&uIt~l-euZeu`_J@#+|#jkKFled+nWFcbpb> zFi!sNx}&!IW~98bQQlFp)3m*{d}5w_vS8O>xr*VN;c~@Hx$^xS-)*hso1*0#i*`rI zw|j3LC3mt#?&$HJ5V<>3c7oh#>D~;v>(O%e4YwSSCwZ5fCQmh7p04iZivL}br%jco z9x6{?C(BJnB3wq~=>Fj{Iu4sg%1CXM5v$4@Eu-hWakY%(-2)5Q9^h!twm?-pJbtK1~kDH`BKUBy~;Yu1S6Ru*TA6-SCS_ZF+JlQvK+e2Z9m z^~qSV`XOTd=G7YUgmK~#hfb%7cT5y7=_KBwPjwZ~nJXT2@ob!U(+Ke@uP<+(s}?US z6>rNHuZtG%6ZH}C#JA^PidS9`@5~l2oiE-xRJ_(}WfSq>IPv70rF+Dy6UDp7iI;a0 zZ?BcI>(T@9{z5^43_*k0f(kFf+CTECW8f4yaiqK$!D(K z7qlr7)X5U`Su7|tR?x^-P|11uctNP|um2=ybwp6>D?zVlL9rk~vu=WFF3ZLU!hL)5 zwxC^spkB71U!tJk7lMX^1Qpu|Iyx*pcITy_nw5?!(-%A`rO zu}ajLEzu`TqR=RbM*Sr!b(84SN}`lWqE$VKTAGAW62bDT9!oU4D^cx=M7MJi<&I0V zJ19|amqfoTiGu4S8m358OpxdpBT+I+qGh;5%}|M+!4gG3k!TtyQMIo`*Ip83MN_J} zgGAjn5`9}p6mBZf*h8YSn?z?PiP8-uTGx}PU0b5J^wF0{zWtqEqPkY1yGEkCTB7~` zMSW$jIJz2WGxP~bi9^Eyos7>#>p|H=j_w885$Iby5{-#m(Xl{_LfKo+zxf@n@gCmG zXOunXT#IXRZN7u=;yY#i$hH?9?Js^t1CH0}Gtc{#J?dzKay|OnbA7ae`95XuI=aE! zgH1nH?xDN%>E|AlJ?-cUau4VOa1ZE=aS!tKvVx*g@iX_p>)ZqH=N^B<^NeKmQPkWh5ctqu6JH_aMOEB-ukv` zv`{=n7o*;X|60~>d!_zy&Kt{(=AUV{&aSp>-(93ZC#6WVGv+3~u>8I|S-W6emE}?M zKecIN{KGjV>7-~tsJ+jWE{(UX;uYOzSZ_#?G5Z#cvrnZ)>-IltC z*EF$wvAm-0(|kwE`_Bf}J0&_WXp8hTy^H@}WnG7m;Hvn6*>fDa45)}dKJ>Z+8Znnd zf5fd@Nxa{Rtok2yKNf$@N!_5$v%T@dzMk9w{g_#32+c~3f4#VM!=R5W@p-pX8@@Oa z89%1|^M+{4pkvbe%2f&51A$MlI``ir~cFPqxMFH>0@e;3`Eexh~KvRCc+C2swl zc4fYa(>Kg(#{HBP*TQ+d%@?r8 zBOB+({XXb3*E1<$anFA!aE)&>EY7`mV>h&HJ``OQw`8xl>wi6PbMdbi_pIYU_o{ub zV>?gU?S4RXZqQx{-+D22WZF!VSBr0B53Rav`YR+mcIbi*=AT6a2Yr_6$FpPmIQ?xN z-fLW}$Aut|=l=e&17_rVppS!w%gFHhvDSHuJ$;g^7rWiLD_p>Xy>5w z(!rd&xYj0BlNGhn7jHQFpg#lOTQH68v#^Tqo+Eul#V9tcFGO322U1&r;Ey*%|TM!n1!ETg=>2vhZcr)E17*cQ3rs?OY3Ve9)#D z6+M07cTJXg*N+Wc7&!Z(_xoA|ngf?BWAe{wT5a8XE=GM|Vk?JU zJ7Yey7Po4cloa!aXxtQ8!(yB-$F=tE`v>+8_hA_9qisFS+s%B!P)cWjRhaI z{HM(?&W9Jg?LN9~ui&%==X?&eMK=hooJ(&9E=WH&t6kfWW(z#ROWW05C>;79E!!uH zrVx5MhlF$6x$!~!9d%RZho1Y`XTCOk{^*}J+s@;Fk@Xw6&##ja+@WThm(iaG9_ir! zt~6TvOZ|>$5uwX-;Y?g~o=$F&F#@Hq-&Yz z8#NRSpv|Qzb6Zt4>ehTp$lL*&BfBlh?=;t^)rD?oAE6WEoqT&v>aBR+o%;QA*1j+I zozgRL&hq~{ev~A-NN5R}uD6);dB&rU0%WD{$F_dmn+KK79+UlbcQljG8wxLooPEq? zNDp0&k7u{^*wN!m?FO?izJJ$4i7Q_H4@KP?vA^g0m?cp^oiO&AJ#}PM>pG#mz80;e z9im&be%|xQIvpK)FI{^gvQ4e<-bpjlB1;R3dLIxSrf)^tsLjZ>k@@>(^=T0Dc2>rd zGkp@9pP$uOYxdhD8cpaQSt=*Z8uQ%+zd;o~vo-`b`PhBKyP0{H7krF<(`3;|8r5_C zOlvm`xzProDMl?JB zVLx=A&{_)X^HersUhY@cAb-a3Ag}%xze$*}_I6DF7}116kLmCC4QIqoZZcr<`8(5F zghvmk{A9=U@qXtADDm{WtS-~n&WsuuIPu-Ib|+5_TvTy-+S12P0cc507F{QQuaVO- zj~owZT;V$H>6m(hT28zd-tpqZLFi1O{WN%EM0i1aL*Sk<9mBIs!GWi*{xfy!?cIUL z>lIAR5PhhlwGyWey*y;_jdKI0uGVfI+_zt?sck=aHrP}2sa}YNR9gJnDF>FP4f($3 zm?@oAzYaO`z1x(*gE|aFy9%ADTJN&Im^I|bq3K1FzHl})8J60&`4_8K%gUVST5T6C zDofg)$<6E68(uUvVsb@|3B&6=Y&&`H<=o+`Me_>1s+shYh#D76SYmB_7<%ybg$-n=S}SY@W`h# z8bwZYUaA>6;B?!GNB$Z%5*@8)qK!2?YENi}&u=3yMTCdOX*!MiscrMnx!Lif(AWwT z{Vb37*&)~ZIF3G4XF|w*ztGWh@4JNzsIzzU3en#}W6Sf-+6iNfenFqBM@-n=IVEV; zlUftLZgVXNZLZg%!&*%r;h3RY46X2uH=pJ4SGHPpXaZ~{DN+mXmtf2oHf4B znw7z&HCv8fRrgx()K(Rr)g5gbJ5n^gGDOd-peX3GcRh2*Uhd-XS%bST#vYz~e%zjc zJ#6Rl$f5W*zvDID!+ZG*pXC}{i)(UizJu@LJNa(zfqUVexHs;Rd*zcGZxnjYzSMzrm!t+3|qtIusv)LTf`=@O>7if#b&Wx zY#3X{rm<~o99zfcv3-01U%)5u4SWP&!DsLtdO&3}3_N@I8DGU&JTzO?(tz z#b@zdd>CKGr}1rk9AC%hm3$d2029ClFaoRqGr$fo1S|nlz!oqDtO0Yt9xw!v%oGe3@iiFz&0=rtON7FJ}?k01QWqVFcPc;Gr>+U6f6Z(!B#L9tOawyUN9Ie z29v>NFdD1|v%zjK94rUZ!FDhntOxUzI)PX~OdvK8BZw8m3}Oc{gjhmMA+``>h&99< zVh=HhSVT-BHW8zURm3b}7cq=jMoc5N5#xw;#5`gjF_2hDOe8iEBZ-y7OkyW7lvqkk zCAJb{iM6(vE3uatOe`iQ6Ptem)w^em|U2gnB15gnOvEincSHinp~Qkn%tTkn_Qcm zo7|fmoLroooZOroom`!qo!p%qo?M=sp4^@spIo1upWL5%ms)_DfZBi>fm(r@f!cu@ zf?9%_g4%)_gIa@{gW7`{gj$4}gxZ7}g<6H0h1!K0hFXT2hT4W2hgyf4huVi4h+2r6 zh}wu6iCT%8iQ0)8idu@AirR`Ai&~4Ci`t7Cj9QGEjM|JEjarSGjoOVGj#`eIj@phI zk6MqKPvHTm1*r+C4XF{S6{#7i9jPIyC8;T?EvYf7HK{qNJ*h#dMX5=tO{r0-RjFC2 zU8!NIWvOYYZK-job*Xu&eW`(|g{g_Djj55Tm8qGjovERzrKzc@t*No8wW+zOy{W;e z#i_}u&8g9;)v4L3-KpWJ<*DhZ?Wysp^{M&c9^e4b!scf*vUwftY~GJ19-l{Ro9m&$ z&Gpgd=KIjifS3z~u0Pnc-UV^14IgC6@rn;v^ZvmX0H%N~0~;~x9$d$8p* z>=o@n?6+us*i-De_3!Wh#J&$s+4v{+jy@swZ%DfK1b-OrHTwzvkyBRZ3H~xnGz{?{ z>#%`;;7^MhKX{CPE&R{=7=J_O5dVvPc*w;RN# z&x60ePP{_9lK3^y_uWs#v-iU`|3rMdW$E)1@vf2RTN3{&ePj(y;$hl>X4i<11BRZt zO1$hZ8koe-v)5~g0_cqex-)yO5MO;S54l3TMJJQ^JJWZ~kHq6&Gdlf9eD2ifdKvLL zOSCkJ-(x4st{aKxTYL*I6W{l=ns}Lbukf4C!*^XGAJ}#z@DlmKzm1<>Bwz3qO-}NM zv{79yl27z9-z+7+SfGz9CEtjt)1;LABc{ulAIL{!SJMyVC&7>E{6M~fwkP?^ZPzgu z$Y)wSe0!e!=Jb!*=gD`_|0MspUhj_*@}XlB(@MyX%6{rnLcWAXDEZUu4-?LjPerI( zoFl(lsJeKTeCwj@*c7lBJ>dTB1cN{Z5c? zqH9Y2Iq;J;#pI*m4Z9bUpZ@ai{UY+!`=WhH{+ctOOA-0(7rX8jlHU&ZNiHPcMIV*? z*Yf@C0`lSZg-Z*_kAMBWeF6FM1<_Eo<rtY`O8&lN^cOFdCPr|Ky6#l?=Bk5X@xita1*hkx1YBh({d6}yg5 zpG@!yIzqjIHZ1kaL7zj1sb`}8op_k~=E^IF!_+(I$5Q_ci-|l$J@mtW9*3xpjvT#o zkb0?yXv|VSr5|c{kb0`}_1y#1S5Z-`4^VHRLreX2>uFUU^_c0Ot$EaEqkTr^QLh~o zty=20sa=omr=HuY4c||Fw^rk}pL$Q>@jqK1zmIxwP3sQ(s1JL;sQ4fC;u_JsrG7l- z7WhBv$;w6Va;Yx^>mJCZ-b5Fd`ZN5pOD^?j;=9tl)TgUL682KBqMb|q+Pe7h9_m?h z(Uv{bx8FSa>dMV6Z~z_FW^922$Xh*n3mgDC#&7`N^|-Pb z4q)9MX`A5yR@@lA84dugV>p1Y$(OR=0M_eMv)}-dDu!mk0VwM>t%ELYf&=LEN9rax zfb@dlo8SP@Oojt^?sjD(9Dv`>wHx68tdXNP!U5bAU1d0c*~ewIT{wW5ew#MH0sO5A z*#HO7MzojV0J`^k_%$3rzf(D1!vRDlM0^bgfIc%E!13>2X2Jn@`+k=R2QcVmTqYa< z8qRP4J=)aBgaa6zb1?%B;K<~)8E^pcqVo&~aDB691{}aIrbp}H019sBu7?9yCtA>O z0833h*24iD*{oR)2QVP)!dGwr%6gUazHwi{0c`Br>?=5cTIwh3-~iB+h6Ct0K7Jh> zK>I%ju7d-}Dz3i{4q%JuPQw8NwcoiG4&cFssI_na;k$dRg#$pF8V;bm{gre$0DXB@ zIvhamscGqO0Dp*nH5|Z$FEr_J07p%i*T4Z-oj0w413)_*4j}Gk*EMhee`f2}zyVy% zy^;n8fDSesz~c1CG&q1o5xvvk0H&?1lLiNXRyG_!YG&?gH~{tX*wt_Vw~q~34F{mC z)Ae}qXDS@P+kXmE;Q%uI(^BC8+KT2j9Kg-pol@ZdzLlLMZ~zZlmVXHcfG#&2z}KhZ zzJvo9`)0(KZ~)~CyuO43K)V|bAZ}pkDmZ|fUuUm^1DMz;dKDZ1`rdE=<9m9pf&ys<~%~$~kFz&C}E8qZzv>&np z4gf87IDo&FsaL=OT+P0d3D>| z4&dpMBg^0b_NIKb3=UwJ=%~X1JdO!k1_zKH+HDyez}U4e%isV$6|HqRfHkta2@ase zvx22?0N%s1m%;%k>#})&eX$e{V0`C6OW^>Hos`{x!U5b7&2~6|t0!M3!2#rTzL^9E zu=IIB5*z@!?r;EOK21)71DOAEW)d90rTZh3-~iBmhXa_gy>Sv8z~u$TBshTFjHgTB z0P2fAJRCrsf`TP*0C^{OEP(?sI;Jjx13*I_4xny!$Pze!+vNdE-~civbX)=lfX+M| zK)p{4OW*)13#t;~03I&5od^feP_*db01~bqNQ49E9-Wm42M{=UMIs!4vL3yoRahb% zKn>0CL^y!w&3h)o0ibCQ2e5jqb0Qo-@Kk*w9Kel}FB0GY(7lHPXgul11ULY7PH_Sp zz=`g832*=r?%4@&0JQ_t65s%KpS32y0SsF+I{^+ryC*CG4&bW8s028GvuFAzzyUle z>6TzyFLi5`00(g4J5vH2K=!VB32*?XDl`c;4q(u)+bU_z?wu@DZR-IsbR96;4AtrZSn#{!KN4q)gcwG|HF z_bina4q#CWl@$&^{!0q}ir0DM0j0QUn2!2Q7i zaKCT>+&>%u&jSa*^T7e|yl?h6BL9;Q+9AH~{P)4gi0E1HeDv0Pq($ z0Q?6I0Dpo5z`x)C@HaRB{0|NQe}n_TKj8rIS2zIt7Y+b_h6BLA;Q;V=H~?k833vbp z06xG0fERE8;0GK4cmf9izQ6&1H*f&p4;%n^1P1^p1=VRU*G_UH*f&NA2UUSAfCek5Z~bdi1%;+%Kjqq0XP8i12_Qk1vmim2RH!o2{-`q z3pfDs4LAVu4>$nw5jX(y6F30!6*vI$7dQa&88`s)8#n;+9XJ5;A2wl1EjR%3FE{}5F*pG7GdKY9H8=qBH#h+DIXD3FJ2(LHJvabm zUlsWv902(t902(u902(v902(w902(x902(y902(z902(!902(#902($902(%902(& z902((902()902(*902(+902(-902(;902(<902*Whj0Mo*Kh#j+i(En-*5or<8T1v z=Wqbz>u><%?{EO*^Kby<_izB@`)~ltelqF-H~{JcH~{JeH~{JgH~{JiH~{JkH~{Jm zH~{JoH~{JqH~{JsH~{JuH~{JwH~{JyH~{J!H~{J$H~{J&H~{J)H~{J+H~{J;H~{J= zH~{J?H~{J^H~{J`H~{J|H~{J~H~{K1H~{K3H~{K5H~{K7H~{K9H~{KBH~?iI9rYj_ z0QDgp0QDjq0QDmr0QDps0QDst0QDvu0QDyv0QD#w0QD&x0QD*y0QD;z0QD>!0QD^# z0QD{$0QD~%0QE2&0QE5(0QE8)0QEB*0QEE+0QEH-0QEK;0QEN<0QEQ=0QET>0QEW? z0QEZ@0QEc^0QEf_0QEi`fUXABq%#)Pq9j2J7%jIm=38B4~Lv1N=IYsQ?hXAYPP z=7hOnj+iUvjJabDnM>xBxn+)-Yvx?pH;FA^6W9hef~{aP*bX*?En!pG7B+^hVRP6X zHi#`^lh`IUimhU^*e*7VEo0NzHa3o}WAoTPK7cRa6Zi%`g0J8+_zpgVFX2=87Cwfr z;dA&NK8P>kllUe+im&3c_%1$-FXPkrHa?E8=S! zAa)Q#h$X}nVhb^bSVPPq_7H=JMZ_dx6ETWdMa&|05yOaO#57_XF^*VA%p>*@1Br#i zL}DW`l2}R1Bz6)*iKWC;VkB#NK@yPYa`ILRpWELP@7PrP^(b0P`gmWP|Hx$ zP}@-BQ0q|hQ2S5=Q43KMQ5#VsQ7ch1Q9DsXQA<%%QCm@CQER~gPQHxQNQJYbt zQL9n2QM*yYQOi-&QQJ}DQR`9jDf{551*r+C4XF{S6{#7i9jPIyC8;T?EvYf7HK{qN zJ*h#dMX5=tO{r0-RjFC2U8!NIWvOYYZK-job*Xu&eW`(|g{g_Djj55Tm8qGjovERz zrKzc@t*No8wW+zOy{W;e#i_}u&8g9;)v4L3-KpWJ<*DhZ?Wysp^{M&U0YHx>`Y6#W ziGE1*Jfg1=y^H8iL=PhR4AD!7enIpEqVEsAedymqj~@E)&})Z&I`qt;FAlwL=x;+0 z8~W7Hi{`m>T%o57ePieiL;n|gywJymUM=)vq2~&HRp^~Ue-wJ4(C5TidYRC#gq|ey z9ig`f{X^&xLLU%%ebCQ?o*ne%p!Wv-HRz#1pA33o(C>nt7WA#4HwFDC=rKVb33^4) z3yYo)^mU+j1N|B3!9bq{dMVH^fu0ETJ)pM%{R`+(Kpz5n4bV@3o&od);Osx=`#HnU zIekAli_dv`&eU^mp0n|sf9H%l=h!)`&UtjsoO7<6v*Vl(=L|UKygAFwd2P;QbMBh6 z)tsN^j5Oz~37@3*5q?RJBYcxyM))T^ zi)?+0q&E?MN)IA@m0m;mD?NqqS$YTIxAX|Ycj*O$|I+gZAEvhteoPM^e3@Q7_%l6u z@aZkmdk4R!#}2+tFCF}wo;mn9y>ak!df?#e^t!>{>1l({)4K-0r$-IGPcIt$f2Q=D z0R!kQ0}jwb1}vag40u3K7%+j}FW>?_Ucd%=xquJ!Yyl(a%>qu)g9WUh*9v$+PZcnO z-YMV)JyO69dZB};2Av`z%+U8C;|iNp)xlHFq=TuPJqK4gV-B`*mK=QL z%s3d!*>G@{GvHt?XT8B&&UAyhoZSX@Iin5sauyr><;*o0%-L#im^0L1F=wU0W6nf_ z$((%#mpS7MHglF4eCEuu?VPflO$MhqgA7)4))>6zOfi_v*ZR{3&wLc7M$k{ELhK3SMZ)QtzbT9 zSHXSGsDk~RMODr}D(4uL^N7m1Lgiela?VdVucw^5WA7|`Xq9tt%6T^BT$*yeOgSf} zocB`BZ7Jtlm2*_ec_`&vlX5;uIcKDt7gEmsDCc*Sb2!R*8s%J!a=t}5r=pxUQO=Dh z=Vz639PGv5J<7QX<$Q#4&Ote^pqx8U&JQT(0F?dw%D#MMf4#C#UfJ)?nl;a?>_1ob zkt_Sbm3`gH{%mERwX$DY+4rpMUsm=ZEBlF+eZk89US*%IvfozOH_O@=HlXZdRraGQ z`%0Dlp~^l_WxuAf?~?in8&LK^D*G9geTmBcLS>&I^)5C*-HZ(=`|p%}bjp4>WnY`J zKTX+ZrtB9}_I<(MU<1lNEM-5HvM);6-=yqQQuZ4u`-YVLKgvEHWj~IxuSVG)qwI5m ztHK79eJ9HP5oI5UvY$iQm!a&hQ1(eE`yG^h3(EcjWgmgEA3#~xSJvm1b#`UFTv_*4 z)~}UyXk|T_nh+aM)_0Y4T4lXeSvQsGmk6LzDeIWZdZewL<3owDwxte+|C zV9I)yvM!~pFDdIp%6gBoZlkQfDC;Q7dWf>Fp{!3R>kP_zfx`PM{Jp}%Ge6jX!iy{X zw!%{@e6zwEEBvp*<0^ct!mBF$sKRq9e5JxWD*U0s11fx;!pkZAn!=MQd>8i4Gb{X) z!XqhskizRI{EWi0D13>+dno*c!b2#0g2D?Z^}SN3EA_TgH_P<*VgBtJ@IBGe5N-IL zcFlNOk8yfSqpKkr^S#j45bgP1=xdM!o{2^U+8xrbzPI%6?-MB6754tqz0vVN z(*i9I>2Kc~JrC)1PoHx7RQE>LLwex%M%x2D4D>zF#<2IZ?%hOmKG4iS>jV7^^ghti zu=l;T>3*QGA-(_mptphk2ihC<{@6Ahkc*Ff>wvUkG4%01Pu?gLD2I+ z9|Ua=d%x{ID$xn~P&7Z#3PJw^y%4lO?0vayx*?(wVABpkF9iJ%MMLI=^zi22{EpYq z7va5VjnLDbYj7>D$+h_ozKid)_XD@x3-`pmagW?9S|;>>=NWhwo{4AU8F^N;Pwf50 zZDYZhFgA=4V}(|V^bEI+A!EsyGPaB{W6hXLFLB!(Fc-`TbHf}lSIil6#~d=3%qerr z95dIb|7qyp{Z@WH&d>7tabB1A7w~?2pLwoV!1d(%1zaCp z2ELD8?0mo6PXYIXb_4e(zF)}w(zBi3?eaW@JP%qAJfA#oA!2Rs;negxx*mIULAt_0&P<8SL3kKP3HA@ftn{Gda@eA)ZzGoOXbr_665^DDkz z$b8d#9t=Rw0(%hqD8xR*UJ9`nd!K&nsStZY3j_N?7Xy0}`?KjKpqGI?qMd<#qN9Pm z((@h+KwkrUMr#B6Mt1{yr#C(rApU?B2>yW{2>w!pzu0vI@F%oE@Gta1@HaF zBVYhpBw&DiR}mN>-)++bK(7Qml6ymM1m8z<1mCx52gtJ&fdOcpfC1>9fB|SDfC1>C zfQK?RMPL9rDPRB^1YiLADPRCvDqw(&brBd~*K5G{i@*T1SHJ+7t0FJ}O#?6heHJi4 z=C%k7khv}b17z-P8VF(wMZ^QKjUq4rofj~`t`mXp7l8q2!GHl`YeisyT~7kvF9HL^ zHjBUjbY#E)G#9`CvE?G-8Co-70J<|^fY^Q!7$Ck-1O}i@0|uZ|0|toi6oCQg*AP#| zw~D|3@wFl_Kzz@p`GDSyEgp;Z4H$q94j7>5WQZ>pfdOdcfC1>|fC1wBMfUj5&-gyC zAGg0BoduiLg#CK>KG(;d1$KJb0)1)U1)2j9n@@O|tH-^bqYeKa%hee4n6$36?~_KNRgzxcjQW5;ga z_&)ZI?_>Y?KK_93;~)4w{!(Q3AABEw!uRnnn?4X2VD~?KAAiL6@lSjoe?^xA|Hb!h z8bNmd#`p1eeBbuRjtBTY_+Wc4pRwbI?OI&ZjxV+8 zcxHPxp3#nXwr6Jy?0AUpgO9ecV$AILiSOGqlnP=G>0|He0|Z?D4^7E7*)Ze%NdYo3h6jeBWks_V{D7MQqX@pKP{@&D!Ia z&6crgdwjFmIyP^Qe>PvhC+zXj<}3J&J$~Z*#8aDZ;bZoAYx6yP&>oL%zKM_8=pTfqQ(zJ>4GFxZ}t*{~T5 zu;**cpAEz9`J4^g!2qzH`L^eOwpf5Y5F5Y%d%kFk9bkYxpR~mmFuN^o*&y{B^Y4OpKY-e46x_dwpjcBsCu*5 z$e}O2kJ_o7+9|0dv7}m9szt1wrP|m#duK1|X7B8sy|Z`r&apiJgV+cSPXay!4l@A^ zBFF;~dj>=R!-O#i_~0P)8(#u5iR}={I3Y0bfJmG;WJmW8qy?$e-KAQt$a7Df-}ikL z@44XpcnPh04TyV% zxMzra|F{Oky+mA3;@%>z0dcPp*PFQah-*OHi^Me`?oHzQ6!$7|4LE%L^1GLbYe3xF z#Pu!ib>bQj_dap`i+iEC21IM~yGM$9rMPE`d#AVt#JyBp1LEE)t^skc71vwvenN3C z7T4prH;ZdP+^fa)I_~}B8W8t#aXpWFySN5Kzw3Tn?+;)9{q6eiZknaSe!j)p);(d)K%IM8|SJ-p}IRHm(72 zuN&`o(Xro;Ye3u!$NOR28^<*u?v>;HGVYz@8W5dJzkBMqw~qJMxYv$rK-_!B`)}Nf z|K5+I-aOu)<6b?k0pNX-;$A+k0nxeLkN5Yu*NW+2Ij~hHx0~_K@J+2D}!7!Fkc2aWnj(> za>u~D8RUq8xiiQG1M_E)^K~Mc$;Pu;klO|3(IAHl%%wrD7MM?ioGdV>jyxeTZ3FGFuw*lQ(%q_a-+aJ8{|NNxi-jk`mYXiAVE$Om~(^NB{1&>IZ9ye9eW~U zUn7`*gPbEU2M4)DU>+WO5aZhfb8(O>1m@!)CkV{RLGBNjmxCN1FgFLeJYaqfa(2KR z9pvVKc{<3!0dsYbYXj!%Ag2b**+K3Mn74x*88CMTxiEj2zaQqjfH^$KZ2|Lmki!Dz z^0B8XKIXuD9^|BeIX%cd0rPs0V*=*(AeRKp??KK8nB&Kuq4=Bu^L&s40_OT4*8|M= zV~1C_m67;$hiO<0OVEx9sqJE02ctc5`YhY zoCv@PK<)$J1+fk)em;O3fLsQ^4?xZW;0Pc$0q_Kng8;Y!$Ta|b0pt__&H!=;0B-;} z0)RV!TmZlyfX^Rr2;kcXJOcRe0ha*2dcY@uPabdz;ClzW0{GYgw*bC$z%PK$9B>Ta z8wWfC_`m_z0KRU(H-JwYaE@3v9G|a%cK{zX;2yvi4fqG}IRg#?e9M4`03R~oBEVM+ z_z3U`15N^bzkrtjA1~l0z?Td73GmqhjskqMfTsW-EZ{1@*9!Ow@TmgM0(_@{w*Vh0 z;4Z)y3iu1~c>)dte4BvB03RmcGQd{}_zdt#0!{;bkAT+zA0yy4z?TU44e%KPjstvy zfak;-z_4)Ey#&I5dRfcF3&9pFB|7x(}5{}2RxZh!*;-x}aSz=sC7 z5b%`&J_LMXfD-}V7vM#}#|5|%@MQsh1bkM2BLUwO;7Py-1-KIMH37Z^d`f^b0pAhe zO~6M4xD)UN0saJhK7Zg)hrS)aqks27C_aqyeV`x@W-afQ}h(JD^Jj{0``h0mlQn zVZif%4j6Dfpz8&E59o9O=L5Q1!25uX7H~hHi-jQwK_Cc2A&9^tDG&}p^8^HKlMqy) zAZVC|phX6PQY-}3I0zc$A!u2Epo|DX4GDr^8G=v+f^ZdrNDYD}bO>5AAZXWwpnVI1 zrfdjGI1p5DA*ka)(2Ng3n*jusLI~q z%@ae`HaTQfs3B{Z9DN3s;9MQX8@+ z^dW1_7_xTFA#2|nvZm}IE8z@T1$W5mcth5VKV)qNLslsqvU>0^G=~gBNpu)0W5duC zJ`61o!_W>n3{|ONXoMbymY88E%??9#ZWtQlhoKc=7|M#nP*WO)#^qsXRT+kI>M+#O zhM`G)7+N=mp}aW^wXI=j+8%~BoMEWw4ntjU7@GBmp{-yT+6#xFK0HFrAtO{09ihtD z2sDk4Pz%HewL^|jReFS4Vn(PmJ3`gD5o(Mdp;m+uDl3jqO=*N0mq(~oWrWJ9BUDQp zp(gbaYTX#2^5zKDwnnIFdxY9>MyR4YLUp|nYStg2wt^9AFC3xz&?p7NqtYBQDkafT zsf>+UFg_|R5Tnu#IVx4DQEHqXm6n)MDb0>bb#7D|<42_xVN}YBqf%2EmB!^!X;m4O za_Xqm(nh68eN8z;vsQ#LD=Us!O=-*; zm&dGCWz5Q{V^&KWvnKU1Yuy;L^5&S;w#KY!d(7H!#;l?{W_7(WYt|pLwt_KhFC4S_ z5DXzO3?tDO4259=gCzuq;dugvw@DbTP%tz{!|);l!zmVqYaA@i@i4qBz;H%{;f56b ztPI1b0>iip!=who6FLm988EzS!tlNY!&5d4Cma|qxG>!DV0gxd;mrVsOCb#RpmAss z9;XpxoW{^`n!v`TMSPr|C&uY*a-6PEeUdae6Ztr%T~D z-Ge3|6rPX~WJ1Q!37NnqBov>J=ZOhc!!*VtJD-VM^C{^%oLnvr{Fp_ zWzF$Z@QN@6XT>SFDNWIcJO!^RQ*ch5f?L{@jp$SGx-kXk%_+ETO~KRl6ujX~!9{lp z?s`-3tUm>B1yk@|I0g5iX=)Lkrst4pI*CrxWo+77#HZ;6Vw&C|r|Bv+O)b&W^b#{o zr`c(`&P`iO{4~8HOw(C$nr=$d^rAdXuPW1YPMxM(+O)l>Pt)thG@Uo6>9!U9tUXO{ zIMZ~|ou<3qG(GE2(_6tby%$c?eQ1V4;Td@jnURy|j9kWMEEJ!S7l;{ohn$hC)C{#s z&&W&6jGSg?NE1XF(c>A8M$rE z(5v>0yy48qMR!K-dNcB@KO=7iGxAhbI7cnL}%?XHftsDS$lz)wRgx_ zyGqSc8}zKb#LU`hcGj+Qv(^SbYp)2ic2=CVo6;86f zd2`loTeI|rJ!@|`vv$#)wYy&Qy+3Pj1+(^EIBWMI1j3@TKmLQE2tr_pgy9G>Paw!P zi69jUfs!WYE=_ANyfFPw1L3+>}l!}hN6f(zP=o~{}b5aVQW9ErDW}BR2 zD%2d5rstSNW{yd*b4-n!lhXVgvnw&kc#|*vn(t)8F9gBNDFX5UT{!l!NJu9htwA2 zg1+Fa84J#?x!~+u3vkh1a1zdfQ*alYj<+Be{RL+;Sa3?=g42T*DGXj@Fk}%)qKilw zTeL8I(ZPsCWQSZts?;Ktq!*DTW)VrVi%6YYw37THvLY-ZS#c3*N{cimFCweTB9c=V zk(Rb-WBMYpZY&~sa}jATw7<=q@o`Z^=&kOUzcV#O#GjOdndNaCn)) zk!2-`E-Phh*~0N<2Pc-59dcQzQp;41US@L4vXW+(l{&X<<@jY~MOap{;72c+Y&gqG(Op)$-m;zZmzAwxS=kGhl|HmW z72p-7fUG!4bj2xSD^>wtaSFtWvqP>pRceJQ(ko1nS#i?ric{xStRlbStOzSkR$OtK z(h6OWSDaO4#mT8FPD@*{3;K$)Zmc+YbH!;}D|FFbaWu;T26 zD^4FmAp%Ab0zpv>MNtAnB?3nkfcT3U5m(uU zv_V(^uIwW0l=CSJ{1Q6|UQ>Y{FS(3+^i0@mA%!zsha~t86J; zWqZ&XM8azbiL9v@x~3A?nndDj3Q4S~+vJ*Bq1K=ly@s@yH8sVqsWom*YVm7IOITAg z;+oo!)?iXzLrG;##nm;H)YfEDk1mZhb=O=|_pLR!Wv{6TXH6}*Yih?^lUx3px*4pg zrEpE{LF-TlUPn5}x{IOfE`hB}9eiEs5bN$Xx$ah|b*M|PBVA_QO|k24ja!$x{JPQ= z*4>P_?lz=#xFfHl9cA6c)peKD*5!`Au6B%dch_8Z_pNofYp=TrXWcEh>u$$em%IMD zyBVyzrEuNtK?#b06AXbQ&?K5b%UHr9@PtDU33P`{pj9eCmFWaiW)f(cO`vryVU_uW zQx+0vR!pExDM1r*f+ds$no|>KOH0^IU1=Htd?d;ns`|Hg9gQZEJ(B+tJTC8*I_tV7uOi zUH3QGtzd)Q3pdz4v`LZhCPN~dY7*U4%h;wx;+qahY^potrdp*osTRG-w3tmb&2Fl7 zZqsVYm;u-n{3P3REzGW z+VwW=mcOZP1)J(#xT*G`Evf@=F&$*fO`=M25{6(HhGH0jV-ika*fxn_6$*>?eaylb3`?;XR^u=v z#bXvOU|2@Pu!f9Dgo0tXieaRN!Gw-Uq=8|(CWh@>ShVM37U5u6!NstShrwkZv&aC$ zN+E{zpk%b?C#58kL{eyyBd{cv!jn>tNOId`lB-b3XwOetNhZmq*d$lul1z$ES~(%f zWyB=ckdo1!pOnfqyGB)4xRqdh-qm7OG4aFbleOTu+OX|;kR zR|=C{5894u-nK*_+c1G{YXr895csx?6WiK0xvf>GZHl0`ErQwBQtY-?47RMhSgeCXH=v*WA|jt!<95w{61N)(Y;n*73H{vcGMU z!M0Wkx3wO$1C`+&sf_HvWpu|QupOj~@5nh~2Q8C3UWM9;&bb|{%v%h8 z-QTfW!H!o7cf1~)l2S+tOQ9*OjHjdmkwVI33ae77Xx&p*ib-K6q4psRzXN% zSuuq*<&;!YQdmw+VJ$616M9PO7%41orm(h^iq<@3)tnSobW>Q@OVMRNWp#oSwil+b zKAe`)NE%C{X|9Z?r6Q4L%4C|W(rGKrq`5Sk=IVUfDhg>XE2g=ooR;cJn#-wauBD~v znx2-rMw-i;X|8Rjt-6!uif)?gdTF}ur>$;~=Jvug*M~CEev*-DNCvK<8Lf)>t)%xpS8O|*4qoSULVGz_8pgS6vqi1SE4U6PU3il z!l43#_6;y9^AKd0jgY2f&-iR1egjudR%Asiep zxH#VNaJ1m#4jJHhDa7#}l#BY;99%$hNCD091eU`JcuvU?Ifo!~e1*zU1vN*zj^8zN{JxcA3UWZ;yBLY=N+i0g6WA_H;=4FW?8+p$t5>L9s6+2!9cEYR zu)BJV+l4#)F5VG#<&L6#Jly_A^*;Pn&S0}Yyl+<^l{X6TmApf$d7sqsEUD+!mXUW!Gw<(Pd8T9M-LjMS z3vS-;czL$t=iOG2_e){k@4*GEfE1(xTENSAA$r|VP|9QhuTq8RbwdFwG6ku~7VtV( zfQx*=DF_9*C>HRhTu^Fi0dHvqp3n&+dwQepA|GTJoM+SN5Eiy63mFJ+`Iq zsa<2wZJB$1+uCEg_MTgJ_MEP}=Xbq5w(IXj`*HLc?)iO`APABmDii^gX#y@somrVB zh#E&=WuCyx0)f^g5K7r4h>l0dWuNfKfRI}u zLG++wej51!cJ# zmW3WviO$uEL?RWKL@P3hRS*)dM7?$eCCQ3Wp(+$fS1gjL&?H+iYFvdO`HDjd6_yk$ zMnkG#q+HQRrGk@c#UQmx^!zJ2X;h+fwPNgB6^^tk9_dtg(ybUBuOg6s#V3P`+6pU1 z52{ATR#j>tRk(##&T6k4y5mmHBR@D|&je5PR)ncl2i>(GVuFACdYSimh*_K!h z8d4Q&$yKeTRPmNt4M?r3*7U00GOB9FtOom5m225mujN$vmRk)vURAC8RlgNf)oxe~ zdT1?L=Nj6gYSB8^qIIrC>s%x1TrFDXTC~nJv@X_&rd-o%YK>^=HLYXRY|^X|ZL1co zZOyAWwPl$^tW7dVX zUH9s4UFiCCuN&0uZde!kP$PQ%(U3Yw1MZ*=xq~&34&I1fe>Bhz*-$%FBdT8wtHU(t z4%@IhT!ZQG4W}bC*pApRnoF`r)iBytgX`E0uj4fM zj@vN0UPI{k4ZjmK+*a5y`cM<EcbLOEl3g*;Kn!GdkazR+nkg zUAAd=xhB))n^FJUWV>QBXi812D>t>S(!{%JGiYf|qN_LcuF-TmW;1A8O|EM{yS+&5Z! z$7)GkyCrv>R#dxMVkhbjy3x_q3R_|?Xz5*eABjG&eRQ9QzDoP}z7l<>?UVb)KD7_E z>3yWl>|*>*_wGX%LeYEZD<861}?0EZf+uv8) z!M@%O_st&EhI()t=^<^bhqj3x)|Milg3=?}T90fSJ*o}$={C}5+E|}$6Me2N_4&5a z7us51Y#V*44fo_W+EdziPi>Putu6QTw%Rk=de3Z|J*y4(?KawX+IZh>haImi_x-lo z588S^Y==Fv6CIbGXe~R@T6Utf>_ltXiPo|ctz{=#%Z}QOj=+xDF?v=fTC+~HW}Rrw zI%L=DL~GUwq7S_rcE~>2Rl8!>?M6pT*X$bo=*Z~8J*gLV#Xb^UqCL5XMVELldVbOO zWKZo;z3BP%us+k1`fN|@b3M4v_wc^Zllx*%@5?!E$WNA`oB+K2m6KkS)(B)UZVavzH>@xIb0qVLJR+Nb+W^uhJnz7T!2`h4GszVrHG zKZvfOMc2@y52No!f48H5_oK_9Ukg9V-|l`X5@(qRk``3+Y_QAirI&w9C<>2z)y!7YH zUtY{ye01R-od3&nwR3-T);Y6tMm+t`=_38NPyL&dmrj}|k|+Mo@qc&h<*|Qp^x)_} zJ96^KKcy%NI`oeo>QK9fde&Hz{o5ONsIQGR(*0N~eW>@1eM7NsIMyEj{yfw%$6DZi ze4RsGb^lPG{rk4!+l;+3@qHZX&VPGXn|azM?~a$Zw5#tTm7B`?qF2*4)RBA-eD1{+YwS|Kr2| zkMBFyrvJfT0(>Hew;$hke1HGs@cw^)AOBmdg^sUt_;~-|PdI!&@qPdH%p5-7Kj`-l z*W>qNiyuS$SmVcVxPI~N{nn`;uJ7;X8sFdH`u|>w`0>PR6F&x^s|Nb$!{6s0^vZ|7 z?|%&c|MdgW%MOSkAc!NQxAcw0?K^adUTo%A8-S^z;$LJ0Z<4L~qF z0HMeLgrfrxi48zBJ^-=A0K}66kVp+c6Z8PI#tcBa>;SaS4M1o30qCYM06iB6ps&&Z zG$ju}31t8(r~^<(8-UK~1JG?_0D5T-K;NwaXvQ9ZHk|>e(hg7*K1eadAjOe`lt2$s^UNT%%??r(Zjd_44^o$fLF$1xNWGH= zsbP7LT2uz9lsZV&v_a~)K1f|P2B}BpAoal-q(<#QYS|g2GVUPN@CK=q{vdTN7^I$r zgVZNzh=SoE3Ppw}937%aY>1-qA&Mo2D4ramL~4kdpoge6W{BElhp2sSh&sa$Q8$Gl z>bW>XeU*l&DS3!WC__|19ilqg5Oq!;qHY^Q)Jt=S`fd$TGxiX*=?qaNcZlkFL(~O- zh`JjLQLn=x>L)xbA;_?Vp~Dh^4NDY0EHT8e#F4|2Ko3jv%&@f04oekoSUSoNOP7UV z>47*by_1KfMP*n@sl!rD8^kyv6x;>i(7q(-C(dPG`dMx9#Q6j^fq{gfXddym5#;jd-%-ZM1tTX(W zbyFC#o{MAFS1HmJ$YWMQ8M6xNnAOq7taJL9b=w%TUYcXpcWca=vB#`UXUr+EB)!qB;(BwQ=aYJ`UY6#-Uf{IP}9Bhi2_@Xv-Og_S|u(?~Owj{c-4C zFb=&5$Dv>F1T}|DP)T%xDq|DW5qyHWL`+ck$qDK$JwYun6VwhnK~=d4>KH#kT@faz zhvEeFUY?+qlnE-WPEd7if;yp3PzS~Y_1K)CKH3x1iZelF-3hAcO;D%&3F>+S~kJd3}<)V@y)7%t`8pHA&6dlhl?oN$t6lRNtGVF8Y(yy5-}y+C#R&h^pv!~Oi4TJlvL%Wq+|S)bVZnw9*R@a zdwEJ)Ql_M|IwjS$Dd~hhB^?-3(qnT<`e;u{E6$XZb*H4JHzl3&r=;t_l=L*5l0L)J z(ke17<F)ckKr=>6Sw6xAlOL=x$YID=lS$5^|XRR;vthLU}T6uQXYIC#JS$@{KCCpkc#98Z`9C3YR)+(yAR#%&~ z&g-+*9b?vdWzJeZ>{)BenYH%ZS*!2OS{MCU>s~Nxy$NTnUkDQEb`Ut?_ArGba5OG} zqhS;*&j-?+K;TCv0)Ma( zc-cYVjElex4}nkm2z)I-;3pvhe}d;CQaA_W=p0O9b1;q1!7MQc^W+>X(sS?{GY9Xo zbMQVl2cO~R;G4o6{9K%azshrPLYadL>KxqB=HPSs9DLiDgI}6+@OOI--gM^Rk~;_Y zygB%SKL_6p=HS=i9Q+fRrz0j$M@*ik@Ohdc=4p%?k!aV&z zoTuN(^Yo%JPp8y*x~9$3$Mt#osxePLGUw?J_B_4p%+ncno^E*a^htl7z81{WPr`Zn z6TCp9$O4U{3p9x>&@{e4v%~_;lMA#+FVJhu0=>&F(EHp1eTHA4Zwd?ab8&(GDlgCp zWq~fJ3v@?YpwH0=ZwgEDb8$)jDlf?iWl1imOL9kBlF#W&@@->DerYbr-|Z!N z(^-;B?vmW|mgEcml6*HqicRAyHcPD7Jh@_v^oqU4tk}EkioMUR*k||^`=+pBKNnZ*ukwnW zP$DL-uGk%I#XhI6*td-p`=z;Jf45icO=ra}xhr4A+6uwWQ@LL9jcUTmzawvR^N8u|13O^K4_`QO{X%&U*8VaA#QTV_>;m0Nl ze{@hd>!NVeL*Y|C3SSRU_-Tm3pOIBKhpxgcY!yC@ufjKoRrndX3V)$j;dN#e&aLg^TJc+|^d$^ZF`$$5@45nXB**dllYtR^dH&74Cbh z@I`+Wz89>*Z^Bjh7qUht(KWh^tnl9~f)&V{?uE=&aFMca3g(YxF69jlLeN(NDuQ`ZKal=g@V! zg{{-4@pbwJu}(iD*Xb|JI-O_N={C1cpXJx-Tf#d1LR_c6$?NonvQ8J(b-Jsq)93Yd z`i`+qzcSb9AND%E<*d_t?mFG~*6EA>I(;u#r{9F@^e-eKM~qsI7`1!^Pso>ugnXY& z$Zwg1yu&8sDwmLt@d^2gkdPmW3HiN}kke{Hu4@VTgr1NOjD-ByOvoRdgq(E~a??x5 zr~HI`JxIt;!-V`9*^qPShTOt7m6L*8>Y@Uog zooBb~Hn(M;<+tox!j}C)+_JwZTXs?1vb)-reO}+P?-*P5D|5^K;cVG^?v~y6w(N`k zmVGbSvfqST_AeAe2n<6g97C9B)8R-A5f}{FW-+7^G2){8|Gc|3S zIj(OrSB-7vk-5!$aJHF@yUjGbZRVuE&0Gt%nJ3{k^9k8uaCC~m@544+nR3TfrJm{z_jX{Dg1m5!EH&gp68wvkp|nrY>`lU7P@TIqRd<$|A9 z?gnY)b(mIuq8W$4G7g1j9EQj^9GP(hHse&djB}LFIG2Tt^FYiv@6?P_(=yI+J>y(8 zGR`A2<9u*4PQ%MMC;g0bEyy@e!i@6?$vQZibx17h(0JBiiLAquSx02D&MupE_PMNc zhR-@Tg{<>j%sO9{tW!|4PDjf+=k%;|+sHaE&8+j?$vP!B>-4;=bHUF#cZ00+I?Osh zQ5-2_IC2EXkxK-Q+$VA5EsG;n4o8mhIC4e6k%uCVyjO9guHncD9Y+of9C>Ww$VV4P znjVgv@^R#PfFn;s9QlmqkQSChPUAV`29ZOakvZfGlSA@s4ry~azwLhujNt$eS>S{6cq`GPcVc!FQQU z#4dB6+-2UfyG)haWsdQ?%oSmmc_{8O@6}zVuI(}>^j+q_*kvA@yUa&-muY&t%qf4D zxgP8?Ps3g2Gn!{wSe`kJ=b0Nso_R**nJ;XfX>)nzET3m?33=v)m}kBzd8Vl5nXZ;+ z&g*&Rj*(|xnR(`ilV|qaJk$5`%tb%X+zax|n=sG(LL(N56_g`*LAgW}l>1~sdCL}* zDpydB@df3IP*5I<1?9b3Q0iJiIiVMn1EZikHVeu}x1cn=f^y0)DA$96@-!?cpV6Yy z!ivgiyr|qDipn#xsC;3IN}DSxXZfOXODHNY#G>*|jX0xLRL<)~<&IHQUYSMZhf`Gc z+@jL=ipoX5sN4&R%A2sL{6b4k#2B3;c*(g$l$`ry$$86`oGMpxj`1buicoSMiY4d0 zT5{@I$vL5yoCBleJT^jZk$Akar9fqrlawBZrx zNuNNk1qAveB+yS0^CPh`O5SPHMO3{hb@vcd{ng+0nw*vmqNeIQoY zcWQ;LX%+UkUSY2q750%?VL!MPw&7LSlYWK07F5_LVTJvKR#_6OvNT?0S)$7FWR(@! zD!b2B*)x2Vy(v`L=VF!ps#e*KR%OrWRra<~WnY?A_PbkUdtQ~j;8)qZL6v7%9C|fWb5iaS69#Qb@irDSD%Y@^{ZM}J6c^mr`OfnMqPbr z*46KBUF~^w^@3kl?*?`Cby!z_VhxwV8!kgMT#jtG0@rYl@(uU0&~P7!4fmbaaF6Q^ z_o~rwADIpJgV%6R`VIG5&~TrG4fhk;bV;n~(s7LV@?ro##zBHTecem;Gyrz4>Z@PDbru#ZuLmUhG$hf_SPMOkx6m6z3w=hm z&@U0sYI800EZ;(J2`%)6*h0Ul5#!QY=y|<`-Z5I}E3<|Ea9e2KYoQnY7J4sep>M(# z`U~4PaG(8*wb|2no4rA_*=J;%{lc}`vwWMqCA8TWVw?S@w%M-MX3y(w_KwkJUzu(8 zhudcRUYot>x7m9^n|%|u*hIqdpBg>SwI0p2oZC4Wg?)BfIJsuB)EqyXq~WtG*Ds>Nl;c zp4YqT9iywhGP~*zx2yKOu6oh$s`rAf`X=nEzp$Qr1n;?*h@N|&?745bo_mb%xmSdq z`%vt;@3o$LLhrc;M$dh0_S}zN&pqY$-0MNleH!-M&sg6*jrZLfMBjZz_T4XB-#yFs z-CIK6eIfSUZ(83yulLv!mb(=_Q<5L4<3b`^eOCGKw(cp3j2f|!D##l#u7&`o;-qy+!5>y ze+0WJ9KoK8N3gHj5$v3P1iNhGw|&gsXv+r}~OrFo3|?j7SU_{X@r!7=W2c#QjrAJ-V- zxWwJ)i|y_GLLH?yyMzQ|G0K7IIcYjk87W>6B>=5&{*Px z#*-&BkvpNC;ZJBcg%jFy@r3qOJE5J^PiVJ|6WUAjg!bJ#poj&?(AZ;>#ydoA5OD3qQkMBF=F4$urzr z{tS0TIKw>@&v5VcGu(l3hI?$D;Xe9jxa+|g?rC_2`;4FEZV+d=XXIJ#3xAfoC7k76 zh-bNP+F9?GkZLyHB3e-tybJ}P8ymo^)uRSBrYhU>D+AZO{_Ch?bebdit zcZ~DeEAzbe!#l5C^v`Seg7ey&@Vxd5zu;XWE_nCJ3*KA)f_Fu@;5`&Cc<=QK-hpw! zdu(3tKKd8D>%j%@X?Vf=j9>I_5Es2?4p;Gx%kJ!5@eW{!VA`s|JHVG8z1X&*0Yr27eMV_$T}l&Jve! zp1g#M{3ZORa0!1dUc$fXm+;%hCH$p%3IFb2!tVx`@Ymrb{3mgl=g7;vAYA4jh?n_y z`epvAahZQ)Ugkgem-%bKW&TNcng4`e;aTDe&y!bpk-x&<6t3{k#Vh<*{R)5ExWd0Q zukhdfEBxKy3jaF1!v7?$>Ku7h7lf<&1M#Z<&bX>SGOy|%{Hyx4;Hv&4ysCe~4|J9| z(0TGe7x@GIrf{G?7Z3EW`hkAiIM8332l{vaK))Lt=&!>A{U>qF=g4cmAYAhwh}Zmg z#x?(udCmV2T=SoV*ZfcTb)O}!`#gEw7y0Y{P2sx#T)ggo)vx=vjqCnP^Sb}tzwX}+ zuKTaU>;6xI#qX0W{#Ib|ha!u=H(30!$>JXa7JnMD_-En<{*1hVf8lT7w}cz`3-Jd2 zO}~NPF>c_m%p3R*{|0_9xPiY3Z{WX(oBVz9CjVBr$v+ft^6!nC{A2Sb|1r49KMim4 zpNU)iGx8SyMYzSk5O49{^jrKL;}-wQyv6_UZ}InnTl|~w7XOR5t=}ha>u-hI`a|)y z{@%E)KQ?dcAA{Ta)9|+bnYg1rBk$;6ggg2R@s9q@xTC)^@901LJNmugj{YXRqyHlA z`uEAZ{#)U$|4_W^zc=pskIlRO$KbC2G`#D7Chqyq$b0@5;hz6Oyyt&2?)k6Gd;X8$ zp8qDi=l>!(LJ&COfyfc>432nYa>R##Bc6mD@rk%k@Z^0$6z&tx#rwop<391yyia@& z?h~)W`@~Q3fgp$vgm=aR;gR`3_z*l0o`esCPsBrkCm#x;@KAUzJ`}zh4~3WJL*aYy zPJA3_*NkyfYseAA(25lkk!8iFj=Az7s!$@5E2!dr=hMi(k$6;`i{q_%r-qej-1bqWIDLYJN1ohab(K|6Bcx zKl47GKd5~;+y0Yx)PMT#g);Q%&ENaCfBouzM*haj{l8p(@zfZT=23^|bgO z|Hb3gfAzN>_5SU-hZlb)9^CKz755MScjw+8U;nea|65zT^PfNbySM+fD&M-P|MAU# z!O1t?|2>687ykZr^B)=4CNJ6t(tq%%A?z zUtavv|2}i^!oPlWVdKC22j~Ao=r7M9PVL+>^ham^579aE7dty={*(VzJe@WFIsN~9 zF4EVR|Msa3>EE3ESATlxv`@eDi{s4JSa0%M zk8+q_7-zf18Lx5HTby6mKg=(Tv*3DhCS05i8)w8F<`@1!*K(L&7-!4H8MATL-0!^U z!~DWHi!RQjjkD?EjN1NTe&HYVF>#h{oN0HMUl?cI_5NR~-UKeUs%-y%=I3E1E5*`8 zGey%RN7Tb!m}xi_PKla|T8cS@W9T^^C{58avCPOcv9f5CrZ-aA3$v2apV24{CT4Gu znenO>IpqKP-mJAf|307ec+PWxXFq$bb>G+by01;<9oe^J;D!7`vhm2sB`eSJvZK!? z^Wa*(H(9?Szu1uhNER@efFZw- ztU${!kG`LfUr4qf8N*}^k~vKFpyiK;{6ew`$tZRqztFrxcvwPyq5WCNFDAdx{w`l5 zclV51p zD&!a2H4OQM+G9t-(}c!ca84Ecq2Z*q@@{6exf?H=Y{=AI7uh2$5LUugF}Yarwo z+S*`^g#1EVJFKCQUr2tjt+9|_*d2UCWQmerXlpa%7n5ITYnL?~@(al?wlyB|3&}4w z4_M4IjQNH!?=a>c#ymu_%R_$gT3%wvPmFnrF<&wKyjlKY%wvrCj4`iKUMu#1EYC6K zJBI6(j$U$Xqmn1>nj zF=Jk4^m)ZR&6uwl^EPAtX3XOZ_ji`p8U12mJ!JWwVZCJepD_*nD|0Pd4n&S>9~ezq35rm`@w_`^?`L z^K8TUKz=cMK$d?S^KirYV%g|E@))+tN7yE>V0$iC>re(! zr*en7lr_|w>nZf*dP@Ddp2|S3r#6`DsUOPqG!Ex_n#XdZh4I{IX(BgTnaqvWrgEe8 z)49>cncQged~U9=kee$l=H@C(xw+bMZmxbMH`lnDn`^G+bH&ztuH2T-RonBqx{iFV zp);Rr>dNO@dhdZdMg8k-r8WHw|=P5+c;e4Z5}U-l_m;fmC3?bZK^OLbuRB^0fx;WM}QygnqD9)D`i}Tf`;(Xn5 zalT=tIN!8doNs9>wN~0ot+kF)Ykg;_wXv(z+T362s|=L-YJ;V|`k_)^<8Z03d7?C4 znJkUhrb^@W)1~ponbLUkVrikWR9dJlmlo<*N(+svrG@6Ua%;7{+*;RBZf)o+w>EW^ zTU+|eebs?-U)^B2uVJX%*EC%2YndpIS0~HkbyMZ>hUxNn(@c51WwE?aT`DisEteM> zR>})atL253_DY-bf72Dma^fymdCTdfaiTdfvMB_|l zqIs#ZSX-_v)~{3+8&@lf&F$5;x{hjFLua+EsjJ%7GEnWW8?5#>3|0G^hO7N8lhuj3 zsp>?-bakR>raIBGR9&oFt}ZsLR2Q38tBWliwf6eXT6<$xt-X1$Hc&rQ8)zJ^4Kz>H zChMnblZ`XA$>!zSQvFJ8sd2To)Z9_m-q2as-qcms-ZEG>&@fat&@@~(&@xpw*)Ux< z*)&r(*|J=>)UZ;w)U;Z+)Y4hs(b!er(L7W?*f?B2*gRc7)i_f>)x1)_+_+l5+}zpF z(bUz@(K6I9*fiWQ*fQNP)il#E)w0sC+_c)T+|t$9**x4h)I8HT-Mrek(%jY5*)rTT z)H2gF-Ll%W(xUZO3+t~@>yP{})?bD7*OSZTd-ZtLzFe-{pUX83h?+M9a|?R9;H_Huur zy=kD(UK}j6Hw+cptHXu%ma)QQZM-n4^*Gr$RnX^In5@hcCY$FAlXVM)$?{@hvT3O> zSzInmHmnpTtE+{{mRfP3)><4WwG{`H1vXIMSsbWz6$hGoivx9i#R09!fu_OYfY#$c zb+|atGFDuwjTe_n6UC**sp3-obaAONQ(TgNcd2fnxTH0?)U;e&(t2E~t`?VCYFdk0 zlgj$C^;ls&_Lf@f`bw?k{!(kxV5zlXsMJ~=F15CdX+3IPHik7>VNK4L#_JYJa%_i9aQZ8n8UXbs0p-yYFDXhyX>#|nqthH4-8#^kU^<9iTQ7romdRVYpUnnW&95PSwWhXKG{3^R=%;h<`JR6ti;qnYzo_ou)Zh5{f&$Q(^wmiF*=hgCzTAoYGvuJt# zEYF;+>N&GKTbAd^@(fv?8_Tm|c|I)9gylJ~Jo}aBz4DA#p6kl9TzP&g&ury6tvs8R z=dtn(R-U`cvsQV&D$i8CSkF=A*{M7)m1m^#R0N)d%JWZo<|)rP<=Lh@&vd<>Vajt$ zc~Z`57W^5gw~@ZXo8*YrO>zG>Z`pM6>9_}(GUuiNP{%XYUOsk*dV z_x1O^ta}W;>a^~*TV8%1RDJuk-S@xu<>y3wr1Nv#x9+yqd(ng4x8FV8w?6UmbEUr5 z{j2UDfBNO;OUG%bU@`p>dN-q5G%g?iV z;SU|j^v0KUl!>0bk~jbJ<>z1R^XUVU7fyXyR~dNfh-C90uJxMqoS)qHgyhQC*>j?- z7d>Oc8Ohtvds%Ne@_#N!)_;4gXQg+2?9UsM*8f|3b{sv}Lsxw$*<$;bb(rcWKAe2= zh?n)4M{d0#`Q*9wTq)~~x48Y2$#z$+JzI_*ZTK^{Chmr{XUx&7{qPf?OIB}Od)6F1 z+Yhe#QgYn&=ewxe?05d1$r+kC^ zx8LbkKT7(S*PcyBkN4qEKbWll%9;j1uUB`%&yyS8b5^GJoZY&ZOl|(M?sIASNb+F1 z_Ut-(z~@eXJZU)jJ(&*l-P~W33!hzkmK{Cer8hp6w0`ikZ2qdc&9lkVl~Z}v0?%A> zyH5Tm+3|`~GM(s~TfCH{|5|(29X;bW-coS)pZcy$H@foKlI#1y37LMh_w7~p-q+c4 zuuLoYT5rAEVdyy1PS8*mo@jI*|K72gu5{(qE$+VO*0dIS%<0!{=6nGUttJ+E`$TI|jA zsQ>J?t=svJ2b(5DZ~CQMU+@0;_*;3_g=t3h>a1>=03k+zf8y4dg{$?^P{>lJ?oyo?BUL7-#gQ__%**5 z^(|h@-wA!NeJ#Fb=#TAt@jXM|Y(EP>Q`EosS^1empKb4j_oQCf-Xrgo_Z)RGt_9a5 z>SJ6hu36N{xRzYgsF!iAx#r=yANE=BnMD1J&x+41>S%nHe5O%P7kGdN70{2AJ z*SJ@>XEL2l_mbUHYtIX@dyRW8=oWS_a!*G6jeC`QHtKNP%iPmZkKK2H9HG96QaJz zS;3hRbT~UpI8&nD$XUag6P}x5XAx&o)E_ykIJ2S-$yvsk7WGKZI?lYPOL7)+CPsad zvywA2=!kZfa;64-(au`V+@L$!ShaN*W-C@_Iwx4eto~}xi6gk`gyYF!EpBL=jAzWrh|s}lRZBsykDE8WyUmI zkJ+e?hU;a|c;q=W;d-V~FXiml_0DwD@OfmbPM&XU`uB>pjzT!+wxGuPE#n*>jA- zev(GLm$P5{O{V*X{V01L5@)~mtL!;RVL#(}cJ}-v&VKE8Y1D%7Ty=Wrfn&!Y^WYcp2+m>P;X?< z{R;I+rhkWeC3{X-sAn=gJk&ece0ZpbvgeJ3dMVS%Lp_x}pDff{nQk8HvFv$fpfLN! zKGeh6^WH+eoIM9F)YF+>AL{MwxpAQ$&-D9HuluOuhk8DH{#>Z{GhILQ1KIQHLcidn z&L8@TOz#io`(EkZw@@r%%}`^YguKcDf8(C_=$|3^PV z^f83*oB0-^Um^MwqCX+}5~3d=`VgZ3AaI||ZxH*{a9?G)Keq#yU0Dqp|Lc^<}IhW4##b!dU;sIxlkU zSht10pVeXE>t}UU`2Ja)6n?&}?uqqHf zEvq}a|ChQus~#VjQr=EuZZ#>HJG#9AhEJ^86zPjNce zQ=ZB7ROi;%N^xP0tyIN0^J1JuG0w6WXEm446>9lfp*1h|n6FjZ^P_q$OK+hw-&^X+ z_f~quKzs9Jg}(e)sXsqf84%kX%+D8w^7EzP{Cs6pe|Icj6Ys2vch2Y>Vr8^gtBmRI zjThU*O50$il_|Y$y4YWyDfUQcF_x?I+K)c;q@%T=+RN>^p5(o@mb*E3f7 zDz$2VWx6s@X{`=cbS*1wVmvFAv5E++c+XU2vN~NEtIkx0s&ke0>b(AYq0&)Z6z5se zpD$OYsw)+J?aE5Ex7t_js!r&i168pe{c}#wP+6!BS6B33OVt_ud#XBJUDUtl_20|- zcTH=mRV<`UYpEkQnd=k}>B=o>osEl!ENFdA=Z14Dxry9-ZYH;!tI2cFsx{gs*4eK0 z)}gi5sWsQ7^%mA=sZVQkZGDdB2esCQ#4v|*qt!93<8iI$39aR@Hp|mmpRCP!t>Fc& z<;6Ac0qe4?by;Lx*7EJ8)_i-REwB3`-(K#_w->we?bY7=WT`JdS?JH}p2$z;2lJEV zq5NcVI6qk(%MX;s^8q={@I8*4W&T9=WY7J^V zl~=T;SWmTLr`A!Y)=+1;tJtZvGp%(qT^TG+mxqee#o^*~b*wlfhB>5lGNd&!r1h~< zS}d+qmWwM|6D!p=-OnARTDhxKtM+OQ^y^+8)V)8fdw8PMt9xC0OR2ZISen;;t$n04 zU*%rz(*4-4`*yHARUX!TIH7xbM)&8U?p^LfF@N266P01zJ2RDj-9L-EPiAyKq^Ud!tKxWpA!M-=}+|Kc{P*8^{moJ{is}Y0quVPvpk6*Y@S7b9*T8PtbbzNlM8xRR_6p?UaV_kJrnDgSii)&CDtpkPKotNtV?1&66=syf5f^Y z)*G?Ti1kIRE27UU))BFOh;>7(7h;_d>w{Pq#Cjms0Wq&Fp8LAr3az&P^c324-*ss3 z2=`l|NB3f{_Le@~gZhma7%U^_q!XZ(&lup303Crfsj9 zFU;j?x({nQ?|XGG_7<0NW4a&5iYvK!-IMdhTE10heUI+VzCyds_Ky5GXT9#xg+gz> zwb-BUD-PtxwXcj9hw=-$XBUd2@?4DN+Y00IS4`yl3zMo3rt%YoX`S=Bp9^#PTJ!lf zo$-Ce#e9EpNv~PXPw0&A*FLjYtQFdI%J&r73Im0X!lcgjfkIbdNxzdT_R2@mSLiAB z7Y2$0=Bb!04(Zp!g{9({UOQeGEl$WcF=i}&Y~FO9fgw)}X%Z|L9mw;Zzn+x72zAGpaFExp?-rQdcQ{rvv>zBuBJ ze_-=<`@S&Z3V+`%(|7Ov&xp%)p0@7L)e(2tk?Yre^Cy|XK0V^LI^lEc z9$y)8XFYM}x-UL8;y!)ZJ(*rS^Vbpgg`0o1uKbq~w|?h8u6yI3M%=DuU@qrQd;E}KIe(?Sg_pgbaGM##7 zKjL24VejtG+%@8Ub?&9{5!EF>b{D=gC{TuiIzD z{j_<~7&g86)jR6?AN7UqL$(=lcU}0U?mull;@2i1WUteR4AMK_1j}5M?^t$AnM>n|j zH*Ax9{ihq;9k1Iq<1@2&Zg5}g+CI7VwhithKYT;-=Ib}OBahlK<2beRHn{7~-Z|Ot zqz$gL`o`p~hi`DFUb$<=d;WK)4erOcbS9UzZE(Nr*gg4cWrO>{xA!m)0ou^fkFR%q z5AK~D@w4^r$>X|`cE8@8^Z33QKRWv6_3qWrtxLXn^?H|lC`q2UXuUh6wI}0De|h_Q zSKZ;jhAjRS&852Qn%YH&(8SUq0bMyuWWl>^6STk z-8ZM+m+b%Gu)A@$^UX7XR`>B+hTZbIi;}CZ9Cjz%cX4vmIm7M?hrB=IdGFtU*gbIU zWy#OB8+M=n-TI_dKkVLl>gDFKK>Pdm!|!+7?LC@&HBhj`zDm@BTo>2VZ%C z{=DakL}T1;sBmSnE_uKE@(-@eIO2nK?{|;(UX`5qk4xNd>#t6J@XJfwy$@cU@y6|M zyTtYNU6Z`_%1hkpE3QqR9K6I``-^MM!+|#W+TAa4=e_eI$z_{g;^wxvF8S7T7rPf0 zugm!5x4wU|+x^7rll5P?*zK~_c(T>C7rWL+#?AAA*7^6oi`|VUe=OO0&x_q&+k8CP zaf^$cfAr%S5B>4)FLKund?Go`U*vw?epB+R+b?pt$8Iu@2-@i(XI$hqKXoEGtoI`K z-}X->JMMguduZuX8DBl`?-#lq2R@zr=AjGSo!i`!Y%_hKYk%~XjKl7G!G&(-q|YSH zM_=e3-Ria^-Qz;{)bDP~cjlWayX++-;lPkvzM{1@3N1i_4z2S&2Cb#|Ue78-*RPxd3^WA^vr_8g0 zR{Z?A=ezg4^~=d+hoA5Ag|8&P-1&U>e*cw>CvWkWA@}P2zLpezIONv-`)kRMK0D+N z`S#Z{?)=zEL$38r)5$*j54i`Pm`=`Y8**oV@f#VRKH=x@bN%h#N?v>C``jJB|5h^d z;rF>;Pkh@vF=*LWy!CzV&#nKHOt-(!-S^=CBzL`Vp1X4FyBY63>D%YIZ@x5>tX_Yf zd)?jNOU^y}Ja^tDcbSIk_b1PP>TI{+ZS%<|E;!r0 zKeslUg=hcd*PrcLFZfCFk>}s*zP|GV$tQmCUboBR516M17O?ILd3%ojf5~r7c&~e+ z;i2TH-SzML9?JXz4=`>R$)lyGMSR`40~N#Grfe$zLV=o;~P3^|@as#RCT2D^GYh^C|57}_yKf`_Y!apSEpLvG+QpcmoNB2L&UH8bNnLpy>$KT_gI{2}q^}FwJ?|uHUWb)eg zxF6j4$IM4@%$wiin%n-ATv~dM`|ksPO4dL8Zuh{*|7L!RS6%*Y_xzv#oP6~i?{+8M z{Fh|g?cVKfIbzv7Ot6{Te{{P0`nUd?e0Ab<_klB>NIvo2(_QD5YqM_5SiI15y4!Z+ zlgau=Pjjbt{ylkk`ZPEC$G>Mjj~$MdUGd<5B(K^2H23_!|B*cL?5XZUUs}ohAluz^ zs@tjY>EzloPIU*)KAkM=eX8p^?-}z*89Vy*fcwTzpG|)Hg#owM=s%NdE*Wqi?_Bdv z;i05@M4^DE={P3mZh>xA* zD*NT!W2cdAcOllkbI-sN`xRKe}} z{E63vQZIMJQ^bkSY9%Zcuq-jcg`^#pgrXVyGku(!JL6WnQs zmfhruC%9cE%Wm@>PjG+yXE~dPyJhxx*L-`$eQoS`ch$ct?v|sEcgu&ZdBR|MTl}@( zz3*RDcl)>c-Nz2AxrUAX?)J%A=J&aDvwk;nXq_AR{c-N?x7E4Rzi^zZd$!IzWU#^e z_dU*a-&*f_i^sX|{$IWO;)CyWTOHhx`G*RpywlzBRD=8Pj_-6oeoLeK&C0Rv;hWYx zXRyXyK6tGAR?_4?f9SF910QR0&u@0D>-uw(i~aB0K6i|J&vngi|Fe&Azguc{Eqff} z_TO{OqXxS?_WMV>ncud!`>#3L{c6`%*LLL5?)RgunZN1yM~`w}+~F1OkS`qN&c5^& zuI=2T+?Ef%!aQxT&|{uE(siD{nfw0Sk?!XEHgm04AL*WY^_tfWZTrwy9_jk-dZpXv zH+}Au##g!GZGEo)lNqKrx_7v@ z^=|IAD80ko_NmR?pXT509(`i-%%`>0QEzu`*KOgh+5GKpvp;O%e)ZcU+&|ycmif6_ zPd~z4xX|Wa+~EkfoqKNcL);GMu6g)i({I1I*Zpt(Htr`U_PX1S z-^T5@b+0>MYHik(8LumEdz;(;pmz76fw#Gjf4tqDx$WEB&mU_y&mXM&q0b)dDjT(@tS85ufR*gi9503jqZngCGNHFe4{(>>2BvI*Jg0hM$SCD+kJ1ZUEIu$-L7SH z7x(1j>)atf*(LKGzHs6?_r8;Nb-&qSojZPNS2yvC{oPJa@0$4&?>%&XH~gVDxt%Ke zyMI6QCU^0@`?(X_);y7T8NR#Ee(o1v-pzgW>3!XwpV`fQ=xh7BKdf7u`Go~Nw%xvN z!$Y0!J&$y`?Y4Ncd*G8@?rW#4c_{Hl9N5_9&R==6JN8HWxG#6@?hYN@$K5}=yNmwG zN1xf--PyW_8~NJa?y~+pTa>_HsX+ z-81uJ@@syNKjSsLmcJ8yn|v+4CSRNH!S~{O#(8Ca7JepvHhxBaR(|H_1LeK&o_KG( zN8T&%IdW011=oaY!!_bsam|7c#I7aRlxxd1=2~;jX zdp7!LxtF=8xwpB;x!1YpBPV7puqId=tP$1#X^xm$DbIC$KlLN3d70XGGsHdkK3AdkcFEdkuR|oOfq0 zVozdkVvl04V$X^`WcD)lH1;<3IQBaByvWVj3)vId8`&e-E7>!nubI7+J(azcJ(j(e zJvYwZvlp``vp2IxvsbfcN1rr%IeR*LJ9|8PJ$ruCjX4WA6F3_oVhWdfU}r0nX{QQnzNcSJNnEy%Q@3I+d1Po>pAnIeoiewO+alx zjXhZ9y$n4Ky$wAMy$(H3^xe=4(G$@d(Ie3-(KE%l4tgniDtaq=EP5?^ zu9*KpFGf#BZ$^(suSU-nd~4Rr(bLh}(c{tU(ep*FL@!8BNN-4wNUuoG80SvtCFv>Y zE$K1oHR(BH9tpiDJt@5@Ju1B_J!_m}p_iqnrMIQWrPrnBjXorLVe5(28`C4xE7LQ_ zxfptBdTM%WdTe@adhVEyLN88FPH#?+POnbS9_MW6<>~3^?dkF9_38N|UqcH(6F?h4 zBS0%aGYCFC(-P1W&=$}b&>GMjVqObc1eyfe1R4ce1)4>i147F{(?Hum<3Q^`^Vm7$ zzMzGmiJ*<3k)V~JnZ&swv=lTIv=uZKv=%g%m>+`{gC>JEgGPf^gJu(agQn%6>7ebP z@u2mf`9#i$7KA2*HiSlmR)l60=bq4#(3H@Y(3sGg(41nP4O$eM6xtLT64Lp_#?GEVQ(3q^Y5;p|PR0p}EC;9ke(!IkY)6 zIolSP|FqeZJlvkksY({j;t(RR^z(R$H*BX>p%MiWLGMk7WmMl+6ccWB9I z%4o}I%xKMM&M^-OEgDT4Z5oXlts2cb&hepTqiLgUqj95kqj^UkBU(6`INCTGIa)cI zdGL{%mX4;5wvNV*){f>L^O?}%(d5zQ(dg0Y(d^@#AzD6~KH5GSKUzPUf8_750GI%5 z07d{SfEmQOMOXq%0k!~RfHlAzetO>i#vou3FbUWMi~?2xvxsw$und?6Yy-vt>wtMg zUneXCCITCQk-$n|CULG3mI70Ot-x4dEijjuUj>VS$-rh{G_V?&O`Ow&<-l}cJ1`zt z56ma}OJPAUA=nU%2v!6$igTZ^B$yIx3C09#f;q)JEm#yx3N{6!f>pt+;v6X~3#J9z zf^osRU|!MZ3JZgY!Ny=@uriogoJ)nJ8B=@iFUHtlZ7{c(?*)s4$-(Aebg(*@U7T}; z<-zn|doVs&--wuB^kcvRVS=zh7$K|>W*FyYVTmwB*dmM()(CTqd1J6hm?UfxMhUCD zTFf%e;leUuny^h6C#)0Z8GXO7P%kD58-i-pO; zW?{6j+F>!68!-iqRuws~ToI8dkKPaXQTZS>inqkf{ zj|~o~^@%Z6#gwqe|`ZkTuULBqmf;;?ZTIjkIJ9_ONA=`eNJI*c9G z4s#z5`EaoKeP0M{UW^`A53`SR*06k-K5QSx59^2d=j?x@{P$h_xBmQY{#>uWo7d~_ z-_5L+zTVw@J+c3L@Brxh-NX0O_rHf3F8w_B@bl>ByN91oKkq&Ky!!d?VYW-}=N{gV z-rqgEKfT|3c)xo8HuI(HaSzu+*XJItkFM7}TrXX}dzb~&^_=B;>iW)deRaKOx!$_| zv&@L;^O)uH(C0JD=cCVSmd}fsF=of~dCu~A>hqoD^A-D_<@46(Z!=}OA7;59bbrip zf9QUh<$httj9D|?PqW-ly1!<*zjVLNa=+>Rvl%qqkF(s5x<6;RKXt#(+Wo4TG-lIu zKhJVM>;9hQ{?`3I%l)qV-)7dd9%fk&S|78l53QG3){EB9EVFD{PqVBit*=?um)6@X z>rLy=X56$MXIYP0pR=q_t=C!BtJd!DM$CUk1`(?_0sr@r$7Ek+W%6_W-HD!O* zew(u2YX7wvJ?+OS`?2=tl>J%zb;^Ft%pS9Q+Rsz=bM5ab`@8o0l>J`&zs>aNJV-eY zbUvh<4>~VW&I_F%DYJe$Pg2ekoi8cpi_V*r^G4^7%>e2=N;!{oKBb&bIO4(3Pj$YgoUb}> zQ_fqRzcyp2^El-^*7=-rKI^6_1Qs1cFNvU^K|CpCw^-xMZr1~hOK2p7uQZK1~N|}XJJ(W^VslH07uT*cP z)LW{*Y(`S`SV}#n`YfeBQ@xf_uc>}ZnVnQUmr~EEzDud^RPUwKd#e9zrc(7_N9i(tok^mK32V)QZK81PMPIYJ)KfdtG-UDuT^iS z)Z5HPJ%g5%ni2{fYXOlzxSoQf5ol z&!qG->Tgo|8?pbCenl9 zrT)uiRMn5A^keGJQu;IXYbpJj`nQzXRrPZz{ha!{l>ScrUP`~G{x8LAseUk}A5?#s z(jSWbr}T^JA5&&s)la7Mlj<*1`b+hjDgCDUPn&^NKbq2ysy|KXPt~uc^sDM$Q)Xk; z&!+UV>Tgr}TlKps{jU07o0(NVoYD`gKThe7)i0;?%j%y~W@*(=r}We6uT%PK_1h`^ zw)$_Iu~k2w(vPb@PwCIa{!{vO_3tUOx9aCp`g!&DDgC|r{gi%R{lCrRN)JfU1Ede6 z=mXLVQuG4p2Pw0<(i2kj1nCPY`hxU^6um+EgU#?tk4VuYq)(*i6VfYE^a|-0spWbc z{r^(*46*;zJOKASl%jV?|FD@~=^-h4i1d*ZeMEXmie4i9BxM#@dP<6(B7G%AUyVZzLTQwNbgC}d!+x^OtJK!6g^1# zP>Mbz_Mf5`Nk2-NHI|-~q9;jTO3|03H>K!J(w}SwS$b589wmJ$MW2#hm7-TMlgw0>GSnDnv~y-fO9$}F?=v=lu}`dW&pQY!e=y}rjQuICPeJOgM^go-4mL8a*2TC7I z(Fdg$rs##z4^w8Pr6;E7iP9HS^hN27DSD&yN1LIR9+{#?N}o*8C#6@W=#|nhQ)a8B zXQt?x(l=A|P3fH}dZ+YHo4J-AnxcnFA5GCm#r{+DQt78Qi!D7hMNgH!>d{xFw|ewe z>93v{ZRxQdJy!axN1v5m>(OhO*=BZIdag&$mA>oIccu4w^j_(|Hq$LV*rNwaANJ_O zV*eh!m>F+oy`?96^knJF9(`GQvqx{X*>BB&OON*G(bA_q`n1@;N3UikoY`>c*&aPx z`nE^kmfr2ryQP2I%((P$j~*_4+@p_6FZbx>($77!G2*tUi!R8pO;?m(d(t(duGq2=X>;g>H8jiUwXes@0b4X@c@ViczA&LfQJu=7kGGq z__bGBo9v# zU-IxJ@g@&%5`VH8eDNp`j}o8q@G0>s53drx^33LoXL)#*_?CxniFbK;m-v^>?2Cta zc$oN@hmVPud3c%lnP-+?Jk7(?#MeB0O}x#++r-~&#$Pw{r z;&~pPC%)(5d*Xc_-Y5QNnE>K}9v&z@=;4Fng&tlge(1>x5Kr{*MDaxrUlecj@J8`R z%McKc^zcaWNe`bCuk`RrG6l#M5YP1ROz}+*-xTlk@J{hho0Sv~_3%*fQ4b##FZJ+J z@l#J0fq1Hir;4w7_^NoThqsEqT1J6*tcS;n&wBW**uTdEKxP5i1>(6Lo-4lV;k)9! z9^NbdYncY(!5$tgKJ4Mc;>8|bOvV9O2ja;do-DrX;mhL99^NeeY#9jR(Ht~@O1HY z4__B=_waV{caH}^Jl@0O#pgYIUcBDJ>&5Rq*$d+N9-c40@8SF6{T|-0`4G!w(62on z0R5TA1EAM>JOKK;9uI)Nmd692ukG;w=zDoQ0Q%k@4}g9aj|V_Mo5us7pVi|5(9iDi z0O-AVJOFxc9uI)ttH%SNxme45(6#V*0Ca6U9spe{j|V{4&f@{lwehS>R-t~9@ zbT4~60J^t59su3z9uI)#zAdvtYr*3I(Aw~L0JK&-9ssQ!j|V_&$>Rag+VXe+wAMTx zfZQJsw2TX_MUMwSYt!Qa&|39)0JL^J9ssRnj|V_&+v5SyTK9MWG%sYC7}^Uw9sunP z9uI)_3XcasdxysZpuNQ70npy!@c?MA@pu4~pJf>u+KW6M0PRg44}kV6j|V_|m&XI3 zz0BhQ(B9_p0BEoCcmOmv?ePF;FZ6f-v^RP@0NN`(9suo~9uI)_QjZ5fd#lF-puN`P z0Z=}dWprpS_ILoaH+wt)+N(Vt0PWo#4}kV^j|V_|yT=2dz24&i(A<>A1E90O;{njw z;PC+HtnhdMbar?=06I%N9sr#!9uI)d8jlA+`Cpa+qO-{30npjx@c`(o@^}Drc6mGi zI?FsB0G(|f4}i`(j|V_=m8wVB5Qpjyr20Z{Gc@c^in^Y}Sc+j%?ys`We`0L{@^riyAoj|V`t zp~nNDTG8VHQ0?gP0H~JqcmPyedOQHCH9Z~x)t;8YqFU7B0Z?t~@c^h+^>_f3Bji=X zCaPsUepc1C9uI(OU5^JqIRhRKfNEin2SBy4#{-~R+2a9F?dpS>dOQHC zZ{YC&s8{fK0Mt8pJOJt?JRSh`79J0PdJT^UK>13RA){Wz;{i}_;_(2eSMhiN)Vp{* z0P1Bt9su<=9uI(e9ghbZLp$0QFWL4}f|t zj|V{cbe2)0Ud-bGP;ch(0H{~!W)fOAh@OS{ERd_rA(k?t6 z0BIQ>4}i1{j|V_nhsOh;Tn>*1Kw5~$10Ze0;{lLX;_(1TJMnk`q@{Q~0Mb@G9sp@A z9uEM|0>A?xEym*kkT#>b-Ppg!10e0j;{lMCw&U>tNbB)<0Hpm`rjWECj|V{7 zkjDcct;pj6kapzp07y&ncmSj=c{~8pnmir=<)c{!k+dj}2SD1C#{(d(%Hsi$cIEK^ zNXzofvq;G1$aoAh`9q*Z!60Maf! z9sp^X9uI)DO^*jaTBpYYpqv@YT#^>*@c>90^>_fJm3lk?(oQ`d0BNZn4}i2)j|V_n ztH%SN{5y{aKw7NF10ZeI;{lLX>+t|cyY+Ygq~&_%xuorSJOI*qJstqfU0bG;v|x`1 zK-#d!10b!~;{lL%?C}6dOZIpGq%C_q0MeR09suP(S_YJ~XpaX#+O)?5Ag$Wt0g!g> z@c>B6_ILoKZF@Wb(z-n!0OjgfW|XvWj|V{7xW@w^t=!`Qkaq6z07y&s%+pC*_jmxL zwR=1O%9pf^DQWQ@4}i3Jj|V_ny~hI}?cU=7ke2W907%>ScmSmJdprP|KetROu>g+; zKy1L{0T3(jcmTu>JRShC1dj(mY{BCJ5Nq&w0F+;88CGHu9uI)ngvSFQR^jmgh+TL* z0Ad*)4}jQ)#{(eN;qd?{N60d-#6mnC0I?B|2SBXE;{gym@pu5lQam02u@#R8K&-{% z0Z=}sWn_uPcsu}NGhU1a_V4ikh~0QR0Ae{F4}jQ?#{(eN>P0ElII zJOE-_9uI(6m&XGj_GOt}VqqQ+fY_L~`Ph9ndOQGPXC4oLSenNJ&~<|T{oG$*{~iy3 z@+&RlODxXg0T7$>cmTxeJRSh$TzWhJVtF19fY_eL12A&eMvn(T?9VcL!~#7Y0I@-j z2SBXQiy3x{9eO+fVu>CPfY_qP10dGu@c<}4)H1}xB0U}eu}O~y;OZSmJRShCOOFRY zEYsrw5Zm;40K_^y9suP&S>~8nsK)~!HtO*Jh?ROg0Ai;e4}e&z#{(d?>hS=GwR$`N zJWB-+fLN@@10Xi*@c@X`dOQGPw;m6GSgywdAhzrA05pAd#Nz=_E`w#7i3NM+!NrC> z9ssdoj|V{P*y8~ZOZIpG{`kO%#{(eN?C}7IJzEBvShU9jAU5ss0Ekt4JOE%;!}ez1SOKkT2M2lmg;2m9yeh5hsM!~S_cuz%hk?4S1w`{(_` z{<$8of36SgpX&wt=lb0ft|#oD>kIqmdc*#?{;+>O57pZgW| z&;1Mg=YEF$bAQABx!+;`-2bqD)&uOH^#S{5z08L71N&z^!Twoauz%JY?4R`q`)57E z{#l=}f7a`4Sii7;)-&v%^$q)Hy~F-l|FD1d1K2Z(pZyN@&;AGdXFr?``y=e1{Sx-i{t5eM zKZX6Xzrz06Z(;xJziHTyVgKyUuz&Vz*gyL>?4SJ{_Rszf`)9w0{j>kW{y7g||C|r7 zf6fcoKj#PRpYtRQ=L_tg^9J_M`2+jsJW9j)1pDW_g8g%T!TveVVE>$Nuz$`w*gxkV z?4R=x_RskU`{%rb{d0c8{y9%!|D3O|f6iOjKj$y(pYs^@&-o1d=e&mfbAH4AInQDL zobRxI&U@HD=RfS9dI0uMeE|EXUV!~mKfwN}Ct&~77qEZo4cI^R2kf7E1olsT0{f?4 zf&Ejz!2YRcVE@!Nuz%_u*gy3T?4NoF_D_8T`=?%l{Zl`|{;8*6{~ixOsJCGM)L*cF z>M__q^%?A+dJXnZ{RaD|o`d~U-@*Q=_hA3jf3Sb*LD)a_A?%-e5%y302>Yj=g#A-r z!v3i@VgJ;huz%`N*gy3t?4NoS_D}r^`=_3T{Zrq<{;79i|J1)}sE1+y)W@)Y>SfqJ z^)u|BdK&gmeGU7k-iG~Cf5ZN%$6^1}=dge3b=W`kJM5o&9`;Xt5BsOyhy7Fk!~W?9 zVE^<7uz&gm*gyRP?4N!D_D_ES`={T4{nLNI{^>_x|MVxYfBF^JKm7~rpMD1RPk#gZ zr{977)BnK!>4#wd^hdCN`X$&u{S)k;ehT(ae+B!e--7+qf5HCg$6){TXRv?zHP}D> z8|_y z|MaJ@fBIF}Km9B0pMDnhPk#&hr{7IO{|o!4A5KGm4Ev{FhW*n&!~W@~VgK~kuz&h( z*gyR@?4N!d_D_Ef`=?)r{nNk0{^{po|Md5;fBJpcKm9-KA3Xr}k3InVM=yZ=qaVQj z(Gy_*=nJra^aj{J`UC7AJp%TRK9L5!0``x70sBYKfc>Ly!2Z!YVE^bJuz&Or*gyIR z>>s@Z_K$u7`$tcK{iCnI{?S`t|L8BUfAkpGKl%*pAH4?lkA4IDN6&%%qwm1}(R*P3 z=s&Q3^dQ(j`Vj0Ny$JS?egykRPlElUFTwuNn_&OwPq2UVDA+&x6zm_p3igkF1^Y+O zg8id!!T!;^VE^b}uz&P0*gyIh>>s@h_K$uB`$tcM{iCnJ{?XfD|LAY9fAl!mKl&W( zAH5FtkA4UHN6&-(qwm4~(feTk=zp+(^g!4@`XKBdy%6?~ehB+VPlWxWFT(!O8)5(G zkFbCANZ3F6B>s@p_K$uF`$tbr zgTCs6-U|Cie}(;{$HM;6XJP;7wXlEmTi8E(F6>qs@_K)67{yX|J>>oWE_K!Xd`$w;a{i9#Q{?W5x|LEJWfAntHKl(T9 zA3YrQk3J6jM=yu{qo2e6(bHl7=IhyA1H!~W6t zVgKm;uz&P_*gre~_75L`{lg1j|L_CYKRf~U4_|=&!y91#@CP4w1neI^0sDtn!2aPE zuzz?4>>s`X`-gYH{^1|6e|QM&A3g&6hnK+q;U}9!2aPkuzz?C>>s`Z`-k_y{^38ce|Qk=A3g;8hZn*A;YYB4coOU%z6AS+H^Khl zPq2S@6zm^91^b6r!T#Y_uzz?K>>s`b`-gYI{^4J+e|Q+|A3g^AhnK>s`d`-k_z{^5VHe|RA5A3g~ChZn;B z;fJt)cp~f{z6kq=H^TnmkFbAuB>s`f`-gYJ{^6gne|RYD zA3h5EhnK?s;is^Fcq;54z6$$?x5ED6udsi3EbJdX3;T!H!v5j6uzz?i>>s`h`-k_! z{^7r{e|RwLA3hBGhZn>C;m5FlcrxrCz6|?^H^ctn&#-@ZH0&Qf4f}^z!~Wsduzz?q z>>s`j`}cSN0{@2n!^2_!@Nw8byd3rqKZpIp(_#Pcb=W_=9rh1@hyBCjVgK-X*gw1; z_7A^@{loKN|L}d-KfE9IZ~qT|0Q)mu6Z``9cljE@Phel0?-Be4_PzNTf*-+tHh#w7 zSFoR*_YnLH_TG4pyjR|H@ITnK;F<(~gk3AHS@2KTwd9%xe}!FZu6giZ*k{3K68stV zS@D?#|Au{*e5S$QVV^ahdGLSOy}&&Y{2_L)aL)w)$lAT6dn))#>|W!Z3;q+k7r7^c zKgI4=Eo0>>0s7WP1sFO7IuiUc;Uf{71GI zu_v)Nu}1~JlI>mWVZqO2dmDRP@H^Sw#~v8`P_{R+M+U!??VaqQ!B1s-D|;+^EqiY8 zU)f&Fo*evHwpX)f2mhAs@a&~rdh6X>Kovoa) z!Ea}0FK2M@5&?5xD zu=Nh~5W!Duy#+l+@Ecq2K@SrA$kvejo`!v;UQ^|ti5!S8OpFFi25 zu=T`i{&?$^>6wFn9`9;dVA=#4MVMDG?E(!W%rn6LP2)i8 zK=TOm52l5niG=wG(@M}x!u*73DQGHTzQVK?G?y@cVOk8DOqkCwtp?2|%x{>MgQgSa zJ51|A^9l1GrUju1h4~QEiqMS0{D^5uXi8ze#IzLY&sfPI~(^}D7(O%JD!#tL0 zvuLzoUdyywG~6)HW!f$pZ%-Fm?t#00AmRA zhQ=OX5MdtC*aVCs%qtqZfMJArMq?W=jxg_N>;nc8<{^!Zz(~Tpq_GniN|>jF{iiUN zFmGw>1qK6)fyu02o6j^>1G5S9o5pfrI$^%kSP#r6%zqjSf(eEBP-8_fqcA^eED5F* z=1XD!#+<_Zsj(=SRG3dSRt2*P^Q*?PU|M0m)mRtIE6l$d3xkP;`B-CRCySYd`B`IW z#?;p4YmK$R+`{~=u{fAqn9ns<2eS+FyTb7X}>W!Ho^Wh{L?N zv11rAEE%R8=F5#W!<@tXxv}Vd#H7P~I_#f$bz|2s>@d%6Y#YWM=G~2b!@$Elys>c@ zd6<_sb`C>_rNh+2e7&*ud&S(t{JpVwn0%PeH&zd`5A*xR@?rX6z906_yg%%J6c0ei z12As@9)OS+VBP^d03lDnyajjwLf(LR5AXnlJOcA394ZfheqQq|g!}^YGT;FS`3B~7 zzyl!mZypG|5O@GWK7x5A@BoDT1oKjGJ@HoH0SI{u=DolJ5b_wzoAF0^0Q7m8XCve{ zn3n?&K*)D6uLm9gv48V`;03`05b`0+D}o0gc$e@1ggg`THsJvXc_-$5!UGWUP|O>J2O#97n0E>f zK*&>Je>IO)$XhY*6&`?)$70?rJOFsL*st+!;QxKs) zK1U5N93Fs>k7HgrJOCj-$GmiS07AZwdF}83g!~=z;^6_no5%T#R}T*W-aXE5ynJ{7 zLcWiA{qO+9=eOVm!~+oWfy^t22O#7JQBRnsFysrF*ANdt$R9Exl;-KF0MxK**~y?~O^0}%4G%v*~G z0Ix0ew(4*5;D$Ud^XB3K2zg!R-NgeC^1P_;&Ep&LzRdfJ2O#8unKu{@K*$R-?=T*K zkSAu|VmtsLZ_K>McmP5k8U2ZQl<_Llui#zA1E7A!Jk9EF%;SvLnSMw8k9nZ+Lemf7 zjm86jSDJna?=&8Okf%m}WghF0w`SgJJOE<<=E=sJjRzp)wV8Jt4?xIsGjBH@fROiQ z-fuhrArH>H;dlV>iqkKue>4wy$dfZ~IUazJH%I?z9`uk$XWn!?03ol=yz6)XLY|#@ z+wlN|ygT#0;{m`6Pd}{wIKcyeR~`>Q$j>t`JsyCNuV-F+JOCko&%F3}0PyD1pX1fX z1Aup*{ypUR(chcLKji(H_a6^H$OE+506YL8FA)8}W(Y!_pv@NG0SI}6HhX{vAmkC+ zYyuvDkXML)VKWRN&(LNY%mc8NcWAQ@=pW2N-~kBvh&C&M2Y}fL^b=+&@BoB-Mf4W( z7H#$d{e@W!JOCk|(PlO90EGNTo8`a*Aog!F9?W{+0SNhzHVc9WAml^ZtOyB zYz+Dsvod%9LVl*r(%=CI`IM6a`%osi#YvpjeJnC(H|W7Y=` zKz!a8vp{$NLOv*Zq0J11{Lr;oB6$EpzNpO_;QamHA@9{@zwiKrJXo6z!vnyq7$K*+nb**81@ArBXQ+-BrLUarl~;Q^4IZZmZuUl+Zd zyj`2ULw^r>yy){bqsOcs9)OVFYqNZK07Aa6&HCX1h|hmx77!0W$OpDrK|BBHu4i-`w7?B8ZInbpJt!0aacCgeH8cWlNp?s}q@hAt63i;GFtBMC8eI%^u?c2zlf- zn~Vn_o$9h z2O#9J+iW%-0L%UtueI52c>qG5yUljv0SI~THv5eSAmqWrhiyhYtK2zmGLZ<~P^54V~4kdF^9x0(5n zpKr7DcmP7azRlX>0SNi~@OYcaXEq-`&#XQkfRNvBv;24fLcYJv`r`qJ&xa%n01p7! z0QdvQ3cv$Eb^!hXvIOt|=xbZX09gZg0LUJ|e?S%i9ssfl@F$Q}fCqr=0{jbP8Q=jB z`?rh(vJUV7#OGp@g@7M|Yy><2WF_F2AUgpMfUc#(PeHZ<9ssfy@LTBGJ3IhnG2qA0 zXXEeyi2XbK8e})%0U*l(KL^ClSvi?eh}Ro4iA9Xzr!y=c7%BVUb5#& zShfTn0I`3E-$eJG!vjDT1%4E=DewT0Re@iH>zym;*27Vf{HShqCwSnJ8YtP{UAd3S(4%r-d0JK&e9ssgC@b8f2 zfd_zW5Bxo3ec%Cz&kH3B1P=h&Aozo{S2#QXWQX7ZAWH;45!oVm0JPUQ{6^Y)93B9& zNVF%x{v93wvP$p(kX?d*i7XR50NUFe{wA_c@BnDkez~m ziYyg80A#D+uhL%Y@BnDjn=%e2zR>ICuca#=##)Rt_Ejot+N<99cSe0CcuG{B>mQ-~kZ( zclhsg7CSruWb@$9BdZ4w0NFkG_sH_W13>@k>WEtUSB-;oN09i-) z9aZ}{JOE@N;fEv}2@e2SN%$pIJ2^Z6s-+x$O0t#AUpc#$xun|5;Q=6v2|p&;On3mu zYQnEcb`u@|)p8C$C)rMT0LXg6?-`$?OBNJJ1$JV6wvS0FWJqf0!&WJOE^i;V&j@3=e?Vzr%k_78xD@ zvdQo#lU0TXfb25-%j#tu9su<=4u3OQXLta_{vG~jve57Vkd1~vnyfTD0A#1(pC(HU z4*=O}_^ZiU!vmn+%i+IPFXr$7kj;h%fUGt=0P5Ww{%x|{@BonQhQFJvH#`9Gxyoe0 z;Q=5U4u3dVad-g8j>A7rmK+`cv44lZoUA!K0O~y*{&TYE@Bolahd-UHIy?aCT^;^) zvh45xkZp&*ovb@N0P1}m{&%wQ@BpYccKGAT%EK>Db{-x8vh?r(sJFI^J+k)j+pG6> zcmT-a!;i1t+~ENrs}H|E*?o8b$nwL_PqrT(0J8q@`^V=?lLg2;0NH?e0LThtUO?J` z!vjE;ARYi|3y%2$vIg-0i2Xa}56B|K13)$*^9j-_93B9&3z=Ua%McF$*@k!kq;>EN z0OlVY9ssfsnTH@75f1=aiOfroornj3EJfxi$X3JyK-MDj7Mj0scmT*^WFAA>jKc#! zRwMHoWH;ggke1_^=OEh=4**$@%zKdihzEcyNajJvhQtFvRwVNxWJlrwAWM>Y60#-n z0FX7wyot0YhX;TxO6F0>ro;n4RweT)WLM$=ke20`XCd1X4}i2To&jLlmv{iAg;^#h z*_hJEVE;~<8S^s^4}i2Z%hV)W6Au7co6OrtdvkaI$l_!khipzf0AzJCuS0ex9ssgD zndgzV=kNfK^~t0MbSs9sp^jj(I7v zQ}FISZTbB7UY0VA~fV5|a2Y@VEJOI+B9UcI( zYViP&UCaC$S+;lp#Qq)gZDifz0TBCl%)gO^iwA&gT;}7*%Ebdfb}sXCWa;7oAX}ID zI#oM8*$7>l9h}Hfb3-EC&^OA10eQq8OvfV4iA9Xi(~#$ zEXLsh5Sy`#X0aN_ye8SrcmT+9W}Z`Q$Ke4G`*+NHiv2h|0Al}+c~G*U@c@YZJLW~n zj>ZEZmgJZxC0iN~09n&`0K}dwgIet0F^@_%H68%6s+m_6yK;B{$g*ahm27J~0AyV= z?<)4?@BoPYJLX}<#+=Q^?z5JeEq3Pc0Enfzn>A0{b;4T4Hd))u+iL#S;Q~cH+V*ieLX0pxk0EqoN=AFep z9UcI&f5$wu*r>w;AXe&_mnJ(M4}e&z!vi3;>X@$vfS|iknPTVw^%RF0I=+LF<{Gr#{(et@0br4D|UDQ#Qq)g<6{2~55OOl zEsqC4?B6kOF81v30EqoN=F!Eb9UcI&YRjw_`*+N*i)A}J0A$-U-!9h8GXO06p80pN zaEAv#?B6jTFIMjG0EqothQ8Rp!vk=rvi0!*i2Xa}?Zw_59ssd_XPNxU=Enmd_V1Y2 z7rVC%f3bhZJipk!W&D%%j|bqTmtGoS{{OnG7e_Yy=53Yh_dadp z{ATWyYcF3nv)cC5^E+)=|DP?lIk9~CWhXUuyy2BsUOD`?J-2z|x;qcJt5CLlXKtwx`z(<{KUEM{qvJsy=|Kh{Pc`x9(9L*HUIQ! zH$J$>k=JZ^_~f5|{-4L*eCY>I;Q#!Z-{a4C4X@?z@OSwdd@a5vUz_j2_u_lxdvD4F!LY&`}SQfdWJv0;UD|_o!8&@ zw(4K_`&U-}yu{c0>@yGlmaqS+2bO-x_xt94rJwTspM3P7AM^8Ef6$li~`qFE-22ZYEe>KZ&&1Eh&&bcp&&lnIhT8J?RRp8d}aHR{ybm%SuvL@?xCxd-)V2XwooX4ME@$jR(~V^ z#$NlK>UZ?g;!eEAoxtyu%4g_r6-w2w>q{1D<=^V77HZW_y{uF!pLO0v{Mn`arM-DU z`y2U+SMmL}@8*|V@VmRbgD+Jp{c!C|-Nb+8YcKMuT4|0~luBRM7cL*nU-?k}O8u++ zDt>)!{$_rCB|rI%`Fr*2$~^zLo`3w3e=O-A)&J8!UZJ;9`>KBNQZZLIsxMkP@b$d2 zvi`1Km(#DSPwH1)`b9mjbGe3F^-W8EN)FrPbsfC!U;5*LOOQI;sr2~0o4o9{__ANemyP5acHyUcb^foL zyzWQwb^nd8dnwoOGyN2$!Tc-q^7w9_im%&Izqxi*^0|gB`SG61@3+YdH`lN2ZS-X? zEan=%t^X)B7Ej;gg)YAE((HxRT*HgJlda1i+2n=oj|UZINqujMtsGZACvoeAAD|*S%K1X|LNY z*KnGCv$SE`H*NB|2L0N;-huITr{o&G!?pR+w(s2Jb$i6uogH82a}DQkEBs;Gjhnpg z#Q3@!^$WY$Bf8jc)9qc_W4kYI^1>_P3uogCUn=Ap9_1^}Z?~|?3%?p)_~-0}%*CnheAp&0+)=-_ zbuyq|#EX5seqGvr_X{_9U0;0NSbUu>_GfwBLA!r?lh+N$*G0;lipQd!d?)Po- zy2<#u#rQg1>>@wkZM*+%lh@71*X4CJvWu;oK?`~JSLyfSJ6?$|+&R9Gi+wR)@%TNu zH+kWf`nA2|qq7(4V&BGBeAAw1Z1Tdl#20SJUZ{(GB=6_wYg-w2+U8CV}eu66xTHfUM*(Ms^$2a`qLE6%?_sX`=umxY| ziG$iU`F*y5hG|}Z?!ld#{64F`q51z~?^^)tD60Nv@7|l-CTU380;LqZKnoO_SDU5} zN}Duk+9I^2mA=G*G?@;v~DjbLN~mGdnvwJ9~C!FDfY7F>AIH&x<_wVcO}M zv&4z#HJuxSvFQ1yta0My5@zT7QAA2(8ESGzu-yLit&ZM@KuaghtIS@RCo`Wx(|_RT z-D35AW%VYS+)~;roW9G^`;pa)WvjiPnB4cFqM{?F4@Z=31Fu-UVnj+~`5LawDXfsO z)2kfC!$3;`&$NmUK=B47k1m*guA^9M6??2=HoCz-QE~0`PdbY2R`Cl~@tJ&+`+b&i zNag*G;X075@%GDiN*kS?WDKV{klm&^xT&k%+Wc@ZGG$ z-^`fc=nVm#`*W83k26kj;**uzVAHpo+!Lv{d&Vk9Zvkkjvn#CL|Dx+*d0w0GSx0ZJ z)%z+4ISJ38ZlB>vIH39wNAX6h_;gYc_o5{%d0F-UI*LEEiZKj)#Nq=u3F}$J3#*4C z&Yu6jKrxqBX7u2iQYT)jk~i;KYXIY?T#loomz{o=qc{Py+$!2##T?Xd^zWx{a1_s> zB8L~(x{7Su(R0uElB0MTXyX-nqw|O6`uT_E7eFO4%H-xw1B+44LB`LYl6yk#oS{gn z@i>Z@#>W_+b+jR5d)}O(9*Fo6;7f3rgUXYsr{_g-aQa1(i&{_#(gza5@fEH>1e`J_ zl;JXunB%$k{*gb}cnA<<^BSkrb#%5b?W|v(XlZS0?n)fHW>UqNx}_^tCA!<&+qx3< zoy!x=%Nv@Tnwp#HCKQ(^#+8;;tQ=p5_@Wil<3@T30l-t!iqnTVCI|taU}R zd7I64UC?EUi%al7C&);-h3%_48=Fh2+m|nIUvXM}0}jBL#ZApE^{d*t7k6LK(cD$M zjK{gStGfv!TQH~}6n*jX`tD`m&|=9oih;%5VxhQEs;_%>Yg1EOb9H-j%i?hpDi*IS zn_yOsFI(Kr6f;`87c`$gXGK%<8p*b-B2#Qdxj`zVaBX$Av~(|qB!iX3Ev;*sn--Uq zjw?4^t-%2;ZW%w$bS-P|>`riK-qpIcna7o8k_n`I{o8~Qw-9CHCZ+Q@zVT&gQRStR zE7BsQ;1aud@hX(1tO7?iYwqcZ>awx~T1Ii1w2jiqWs?)fb~d*)*LO9KNqnGc!6_xS z0ToRsYUr$A(YP$p*?fL$S8Mx<#JKX(^2rq+OcX6|>{xYTBaX9qP0{j+;JcTtTHXL` zL3~Tc`4ft|I*OXgDj3t&Uf*4Wc6Z{k`p%|A(bA^X^_?w6;}S*l#wUteR9u>J1ku9rMGWM(0|S9FY_!dO>qzYlk$xQ=uePlBSB} zX?##vmZq&8H6lBTvpsy4qX1QNzuPt4~=tXW`-r#Wf4( zuV`LfCu|*Sk3}XFA$&C9ziI;4&6NipnM`M}?|MFJ-9eDuTA}9)}oU z1&H~k_EqQ^%&HY#txH!lHzgX;YBCj4FTZfb$>rF`x?ZFygQ;3gW)!Yo=|*sw^?#YN zp3;&hFEqCRxbngcgY5^+Am}L4EM&+`F5(RFw*RRxQblQ5N*9B&jHL@Hxjmw{>zp5j+A~YqB(;r zd`jY2_WP&F0HLM5Gl5PbF;fQ#3G@Yx&0SsXon2%0`L{A9sa^gdx!@L8%gZ<&&A>Ih@P;aDnc;rZqZ^FTii<;f9yKGE{WHwT5}2c;5&^3A~^`N1Li@Q{3X zNPb92en?0@A|&TCek%Cp(2)Glln|6}Muy}gL-NBy^20*%!$b1JL-O~9ri{P>WZuK`lQH)BKcu_+-a-xP)9MIm`{NY36p6?{_?l9!}}pnOvrlGCS@ z3ce`|$#Ls)f^Wu!NIo?rpBj>%7?ShdQ7ZUmT1Y-EB?RT;U5*=kQ{}Rd z&j|4~A%13vuMP3DjO{Ta@RsmyVXx3AZl#6jpKXf>_(ZBim6Q)Qr(25&^10?b%i~s5 zXeOA8Eid*;&DEBd{3^_sEDxVVp*g`kXn71p3UUAbnesirLUWjT!Sd(|3vvI?h8{x? zZjFT)Pw+hcRf|U1YGgtW@$*$Y zhOdq!01M3#<}S;l-V1Sm_>SeJ{)U@v%J(2qp&4SHv2wA8MwaQ(aa2?K6He-J$9?vp z6U}{?69<7jIQEI{^K=+^Bj}UsqZ@wY64kJ;BS;^5?32ilt4QJk=T6wasE>Yod2sLV z$Zm<;&60*;&5nv;xf3^)X9{VZC_(S znL{28o|--$&Y;X8O(}CIPRbmLlQM_m)aBt8kYx^O(R~(*k{0DA>(dKJ8*(++cwC;X z#^u>uTt0NMhdLy;p$85xPH@2sn{@_va^QOQi2?FSl02X2A{QBxP@Yc+jOR$4$Ppir zBP-(j73V8{P_apo;}`06D)uODQrx2W8O6^lens(K#cwG-toW$n&lR6j9D;tI`Mr;L zKgz58EaFyl_sX{udyV<9@>dhTY0MqUe~WmZF~3m$cf@aC){ydpaleUt-I!yE$ghI< zHDgXyelZb+U#tA(#CwdnN%=d8cN_D#@-kuStH%6A`3UZ}k-Ll;Nre65h<6%Oqx=Hm z9mcdNzm|wf`;_uG5pOr<+sZ#q{IW5BQ2rI-ZN?Pfen|U=6ES$4tURZsM81gnQofz| z1=N@F*AQ{C?o|E(;;qK~Qu#j+KZh-b4fYF&w-|H0@{@@-8*`fS4aA#_`H=D-A>L@r z=aj#j_+L04tN8!m;{&eC-jQf?phZ+sPDXxck~s3HnK^ z<@T4-16x7=$9>5!#}{azCybvu*{2e21$>S4}ipTUMI4a}5t zmfVvU9Eo!&WXOj@Z(9sEZQ7}I1@>8@7}HjYo0=O|EyX+CmUi68gsyLBz$OiJr9 zi)AS$8>vDY=R7HmJE1A&gra6lt|-Qv(c=1U%tmQg)y>IC0}Y2l5yMw`24I?W?=tSk zj6^sB8}%?mln~aVjNuq;7#8eRkIxQFW9`%4v)F9FP*K7H>|MQKWGY1@JFCZMiZs2d zz+Y)KtsaIqX?j=TA>~1<1hje>7N_Z*b}H<>FQsVpeiqXES%*fbs@2;S)>|3)KUFQ6 zZ$1vfJ&u}AXe6vws(fR^dN?rpV{NecE)3~KF}}=He=Ea!PeG3-QiHX}XCwS4&s+32 zVDhY1s(had>+OUN{EIeNd*2P~WqD?aRRUVQUx)OXpx2W@@0F0=^U%X9oMf=}hM?g= zl01&*E5I#DswML+3G3mZFzNqAkkybE_qJ9Gt=J+8g`R%;)2okltqK9cwcM+I+c` z$hCJj^m;Pr9fFR<)hqw+%=u0X>0JfAOzo6DbJyNg|C9NAG=}u9{Vh`2!aK)tVOZ}E zUdDdo>X6=|7ho?#yZ>rf4`;B41=e7T@Z*r)lO8HGLwkNPtXBp-#Mxl&WrJ|*<;imB z;kncXt9Mjbk1KyN=uHpnRYI@8T1!>snPELH8p_ab$T|@uhg8CobrE^bqipijd@^M3 zS~yjDtP&7n3AcsxhF=KF*0`f5>q)>{(eMw!UWWeuw;_AiUXK(R&f9BYJ+2qa&>wIG ziF-b-y#@JZIB%0edc$!+KbS%9oRHp=w?*9Mm#mjdLVCNPm#O{rhV(kP*e=8Q{Z?4- z+t9No!Kvq`!g>!y4ByMyV3WKW(t8#!-JE8hG)>=)TYm+QM9i}p&If%tuHLTiBVU^- z(B|73(p&Na90&ewuzHt<^&X3utr_y=vJ^L8aqjh4YpHy{9o9P;m6D-e=u2|4F=n1` z?u6gO1}l3CdW^1ALA1*^TTu8p;HP8baj-e=2-&-Hk#9C4-Ue&$wUFL~v;4GnW%c%i z^1W&`XOIQ?&H&ncb1_!2X_(h3us6Ypu;jt2XV3kHvoT}I8Rt-s+mRu?>*{^ePQx0k z-to{wwI)w}94}MpzqY~VTZjm!`pxw?juS%pG7a^e=3#1JZ(FW0Cn0_VHXd&QHl7RD z-mn$E*-4=Wn{P?^xP%+Z$|p^jTwaF1M50s#!M?1tw4$OSk(f|6X;PrSBxn81lWT(& z7lBdUk*!_EY-B+&wJ{jGAB*#Mpjo`*6M02{+*N&L?Wk>LcY*Kijz@oA`^=uYo^S8= z@)z!j*7=BCxM$?pS+(Q$9Ml%u5szo@h-8o7b9mkC9a&j3cVx$+e_VnEKJn;px0@I9 z3cME|_YT?RN8;NeljrY=p5RTHx8tblBlbk5|84uXUf6Ysd4MaA!h?I)WJP}_`)9Gw_GE-Q1&NJW!ySSbUkcOS34a2ez!vXjdV@>5>V9S|}omWZeU|1d0x}wZ@GoJ-}ZorOH_KJWVuk05C zwp`iAz~&rIQ?*RXQsx|$WH}6mD7E(J^sGW}f%leHAYqA`EK$lX_NM0rE0&JQEA)nV zm08!!z>pvwORhwUus6Z-qj<2aiEK`4of26Ite}YbkW>`w6!geI66_ZMJTsp_an$_T zo8KFVg8e}N&zwrA#O9msHN7NQwP4ouk~kTgXHFxW5yY?QC2@|#pGs(x_?6LK5-S3R zZ>PQwBc~+^ zj(m+HUzS0>j`FG4Jaalhvtq4Eu%-?$UENuQJw5A2baiA^jy|H-+J&G=1xrL=Q&xgF z1Dhv%iDgrf<}IDIL?y4xfJ=p9|$-A*Sj)~(^#`>cLL6{c3d;a0~)y82D-b1|u7|3uF}!1GrY7yAb!{J|}L zSFu01*dKI~zj~_dPx1#9`{%a!$1R$B+^AYy;-1&g*4}vDLM%uNmO#kDhty(-_Ep_w z2D@>(1}<2kB*jfFaglW<4Q=OD!&laK2}g$2EFCpn-A0#o1k2Fy+M+vUkV|g*S&6ex zYntr37Q56VxehOt8MFSkUJhs1O*w7OS((U{N^&lwMpzj_=(%#2UcKSfDC>jL8g<9Q z&c^+{>_i@c@% zAfB_oDW9Y`Q}JX)xS>VPn>F>&Vg)-CS1VqmxLNT^#p@MsRlHmAn~D!9KB~A)@#l)q zDgIs2L*;NjhYYNNgUI7F9YEx9Du1~0{6-GtMaoZ9zE+Xn(_y;Pm2Xn)Qrw_;rJ~Hy zf&NzIZy|!`H*=T|1Ix#jN0jpcWSbzLP#mRrykd!BmEufAIS)uTU-`2XWsVc#Ta-Un zai!uaML93f+o1f16){ah%5$~irxiKiXF2(3M7&q=KE>}Seov841l0Q<#os9YUh!qc z-HLJ^q5n_i`3S-Eg^I%!M=Bnrc#LAHV!7gU#cIWq6z3|QsrUiKa}`%8p0Bu8@d`zD zd@ProZy-OEME;A4w=3SOc%R~9ia%C-Lh&iZ=M-fw7wqx5hjw38J44ZR<&wB9*g{PVJST6S4HVsHqj|04l zga#{Pe{N}tUV+{WTN#dQF=#j6VYr7ZfgbZ^em37uCk;q55rs;S&|vdg0p7LOeL&Er zX^-8gwYLehO#{-L3482IZLqS75#id~=-6W)Z0%hG+NPn6rLecsiLfN5sioPw3if!M zEFZf2q`jL!GfgQrh9_}>wuS1C^4qbwdNn9)I}>Q2j2rKZL>umgp4$d_91LH><{t0$ z&hc`TVUPC*pwX5)01{q6qJj7yZ-c!jZ6cs86VW+5GYTiG5|IZZekeBP%k#ss*=q=- zX+k^YHDiOd=k_uai`z@~>VL$vn1T8quL>GUAOGX7dk&@VF?K+0OYMQLdM|m?4%{>N z2C{?ZCh2$D^l_ ziDvT_iZbvHgUBnHE*hN#h;7F%+7tK}r;*O&U%UhYZwhQ+A}MzWUYwVJ<6q<`B%^?_fVKX`=76>S#Z3Wg{foDN%{h#wYJZ0P_sAZZWPeAN z?J?2mtRRku?tQ<1m)jhQNLo&g`-VlRm^f_Utcfz8!33kWuclqZo|K@Rf?hhu|RjZlfr zH>*0t)p@eS&nE=gb*z)@XwdO6f|EWa2KVK-(Y>~N@k||!vp0rkjZlHjH|Kj|oF!ul zI2=g^hsJCq*kVOc7M6=iK`#*@c8Vmy(3TFZZbI_S?z9c`Ll7c^z)gmH*|UYdpIB`zCHd&x*ziw`JKgne!}nk zlYhjbxu;=)MIUeDBzXNYIUC_|98b4nIX^{1rMJ=Vn06D+;Dvm{vz+GGVr8wIxSt%C z;+1>PIxJJ)P8+y~@?YX|?BjD}|6>FDndFCbozTL;a$g8)ndvPL-TfajdX!;LX4@B`V0>!_GG5%=AL+Ln` zGcon1Db7^neV_5CDxRarn;_#m6jv)=q_|n}O2z9HZ&kcoQT&Ur^N{k7DsEF0|03ki zDKCC0@EQG!c$g9YBIM#<1TX$Y@Zw(tFaAaF;$H+W{zahp7ZET1MeyQZ1d4wVDE>vD z_!oiVZ-V|--gCio7|Z$)|04LimH)QNzpMO@l*i$u`Ge$q=`%C^KnazPQaoOmZ?|AO*&D&C{`J;g^Aw=4c!@pp=URNSrjiXxwU znC~FPLd63W`6xvB7)4I=BtJn>`i1PEPhva+kv$`kpRgyg--^JeFkIvJ%^x|@^)S90 z-I4eTRnQN!{us&wNbXU+5{|$gRftEMAcXZOV>kv|9NXUX#zKc_+s2)H9 z``!PxnSr)sC1`X@hRD!{d%))*_2Q8kcMlMuOgwr${N61nj(Hje>yH(&ZAJD|sWnV-#fC1{%lq~W~Q1PKjRwj8``&-EX={>Y8cvuS8z5$qL^&|qaf z;9Yz4A5xFyW1noxcQt662BcXAd*?V2miz={uDzRKkH<-Su0Qfd@Jv&Rjo}fT=#`=R zqx{R*T)l}f&c53Q%DC~p57CCtL(ke~9FK$HUTkjp=s&c_%TbCw-tU7(8|?r{coK;Q z;*Y!^_I_a#0d1Lx&f%G1I9Ys0bTHy=zAOjJX0Hv9rbfpqq#3Nel8Vy6y{P-W`6J8A zCyyJbKk{+3QG_&qWXIiC$4j<(yRily^ByPE_P%`B+J!Gi*Lo*%9%K~pdv^X|?b4l> zt*v=E4oUXf6rT+~%i*)Y#~ePk_Lti$yt10o8hR~rxAj7M*Gt}7Z;G|?9Jp7ucii7G z3*{=ULL~%s8TFn?Q~gSKWs+kiQe1yIDK#7`p%6C=lwi)8;l==vi`PoGagG$yCJ zhICbf#AM3=0#okD#j{%hsUK&VilqKLMEPT9a{q1aN`n+pesu}6A?Fdi_~#+Q6iK=5 zN25L9VsjvYhjJ6?ORp|q*93Dg}bOa>!$#5=flmQG!SB!k0PKsX;M_g&Q0<%d>^sJTzidry-O@`g@h{lmk2tBac2t8u zC=rR`Q7wIr+JL=Owp=pj&Y5hNHqk5Xo6D(;(xL+wob1IHm6|x1RFk=7;R$9zy1s53 zanI<)vVZFtcRDk2pJpy*m_+GL#!LB7)28bse{djqhB%`yn7x^FCGO{MLFb|OaOdQ) zld*xFNmYK{Mt@V}QZF*&k?%d6|BjxI$1XP&qB0J@TzZNFSG3aD4|pGYmX=emO_1Q1L)T@tHw5nmopaV>N!f;uOUy#ahLM zihORP-Dbr$Me)5L{$k}XRlHL12E|(xZ&UoL;(dw_DE?6KdBqorF=PH$@z2B@%wbZw z&)XgNI1%L-qWrjhDGx;2B&W0;))Oo=3)8AA;PB!ArdXOI1Em@dQPV z;+XDKMX5LNb;@&CM|ry<@B8G}DsEQXqIiwsXB2N%lrbsN-KqRNir-dzNKxt&dOuPA z*NVSUd|7d~;vU7<6s3NVKAZIhEKuaP3dp}t(fY2AQ(lH4kc*EJc!I`f(Jme)ZT*53 zJG@$H&+75oe%t1h1=q;Cd{&uxNBMEsPHnI~Eg$S~nv>x{P&1tfOI9PE&&ZY77z%J5 z(sy?dXf%Q3^E>+5X3zK+g=?V9%3wXj}j_cClY4M8 zFIK%Laeeik;hU@XjBKmkGpz1++w)$0%;dkAb2z;=gSOqiJL;9b8j*Faes$HJXt1vJ zvS3}Sycd#kM7`5+@OfnxTbzN&E4cT?A3s{b5_K6UqIPyjz z9z9ngqf22UR!_6Ku9f*_oOgCP1Um0*4M+wrPFJUQ!b_kN*y3Mg>+1)K!GO6QLN~JrS$w!LxOhS-qwI`_-Awu0qBpXC+0ig+7e@k0g z3p`Bt(6E=lsA6m}^r@Aj!~+&+oE6A}sv8F;4h>m@+Dk4_I>S|}0E4fZ(+tT#gQb(e zjQC^`helwAT%dHiXkf?V#HED5(;tL2mdL=Li@n!qw<>DIEVysc+ zKGEVYN%%)j^&1oLc9oQr9A8>g>Yr2WPgqpfcY*2vd|qxDQ$OUDa*w3ev!Khh>HHgUkD`CA;PTUvZSeztw`6T!bDD|oGUFX`n;Wkwg!zqP+j9FT8i z!k9WE-x>495>5xi&S*w{Vq<($_N7^u#V(J2*uR3a$Eqel@DBZ5HIAQC;oo(wYO+5_ zpG=mF*12RDx96cU-ilw0r8bH!EJLc)jASigzo1 zQ}H3iM-{gz{#@}n#lI_hyeZsr`I^2*`Ay1yT=}b%zd`w%l>f5wD6W)KMtmr*oEIP;b6Ea_;wVKvZ!x|^ahl=` z#W{)`B~y=s6XMy5a$dl5tj~BkKfrF~`7)C67b|k4PhQR!kk8QMuT%V@;_Zs}D1Jkc zqkHOGf7=tvKc)Cv#Xl(SQv8b|9|xG;`rGoA7r!RrdzV@+pM!Z!6llVn0%zk0q2ptGH9~ zPl{Qr2RuC4_5fD&ZF{lDHH`648m{uUIL9m1Q@qQ)gYTp{Uc6u0V2AWG5rI>j47Y*$ zsuN+!D#W9^HkH^ImgE|<1;T?squIzK0-vqf|JpPlWscWPBs5qVuYXHZ#NV0VXC`g1 zvhbQy`i>INW4_Ff$3g7GX48N)J_?l}p~1>lfOn6h;DDe_(;m9nq`gg`Z5oi~DA;4$ zx53ITMuclG;n-szYRgBzicLcs<6&=w6Jg0uLgv~lg*_f0%ZJ-R(jM!SX-cs%{1-0J z^F#GVIr~Fb?_d~jX95kBapV1sXv5{uE4K{LwwJGgcFQ-(IbM!!?D75pG)`#;K*Ie< zG}$shn^z%d=q1B!*n2X`rSz!p9G=+&dzFYh81Zl98=Qo8dQ1j;?qkb<)|{4&uNdeY zFIn@3%Oa@1v~S*3iC-}5W3@|b&)5^)>`gyoPyFPp-SMm;?|seG*}0^h;gje$Qa(hu`IQgf=Kpy#vR7p^cD)DF@9V) z@;GCv*uP*cDZO=AmU;#7;rPC!<4hwT4~eYVH; z;PHi4s(pHK{y>g5m9Nhdh>t&rop&Pd@>OK98E@MC*l)lVcnvG5%q* z8rDW2d90sCFBpfh>~|Me?QSp{?j5_wgF9u7zbo(j0m z2o(37FUH;hzAnOiAad}dgdn?)i+V}WpyT)hCq0?7;xsT`N8oXL=1c^&l=&OjRMlSc;jJT&w7;7U>*(eC3)(rNl7J9-HRE%H}mpYU@R`KyZk+~QzgR^ivT z_~W>=H2V#fmWIA)mv%qo8dLqg9o%AEXgWY=U|%m^h8bD|_gAvA`uQl{ep_JrAHU(U z`p&oXDZGD)!!Qp$On>v}*bCL<58s%zDRyb}GXL^-y04I9<5Y;s`l+^LM5?!C)c%-9 zHw^g{B=hG@DisDYkB(z?j&XP%QuFAXj?tM!XJeT|7PdF@UAyC`kB6m+isG9BU#lJTRyi1YuENJgB#qEkuDn6t5d&L(O|E4IuCZw0| zp8=2J9TU%s$0#08#IU`L2)WG70$-^-=h#p_Px-TyU#7TRVNYwvn;Zn@El>=3)rB(6w}s=LWu)y=D%;g#Q&@pMGZ>8X_3I^ zU_Q=S|4Vrgo!nc!v4|m^r4s6OC{3_;q8iXP4dZXd_6;Y(l9h;u`^FF%I{NV?vOlnC zsP{p%pQ}h{u(Ag5mZoSo^bW&jgO#0w2sd9d7-fJSeQT@-9tUwHHk$^d$w%8ukkDXd zbWphVDji=Ky3wS)ji7BBnka@nwtX9{tOpUUz4?wk_OZ5nSA(``K$>Z=*X~4E@)MA` z_LjgNkB`TT+eXsfji8yP6dS{raiZ6U>W}g-V{`TRTdVAgZJ>6aE-!kWTIl8dN`+d-8qa6SVk0a4Qe2Hsd?-w=^kY!^aI)`WCI9Zj5JQ(qBVB{5MJJYOg1!HK#?3rYa4Q7YX5dTgxaaElBwa5Wm-V7Yt{wks z{3Wgzjmvt`?44V7*!7)UGdlj&{Fl<>vJsaTipxS=PAD$6cKdek#jHZ};v!ziz6;69pNNl30CT>vgM39$@m{bLcOlMq>|nX55BSbnL%J2^gdebg<+WiA2zb&6Te0=^0}c~~ zQwc|6!$iF9UJ~O1rb%YB)dtM6UJ_>qOj|FFZomeIA>mqy$1h)y_;e7DeKM>Ag*kzQ zt=Q7%1R_Ens8CiG<#246dAL^ApawU4^N)v2kXbe%6nxfE%ToUmDv!uz7Fjrg@it)-8Ctv}?C;v~Ts7B_6hGQ!nZ5^)I-=AF&8)Q!mP}Ff}#nF2jmb z@i7H1w$$>`zP_~7WVN&jbHOSa*{Ayb zjIg89AYy;`(>UHt-8(trO@)E@(?&p@z4caXVSk$K+xocFg4Xn=eQINo2YpTB6(=dq zR6JRcFPx~iNU>S5O;LO{kYB7kZ=Tc_Uk&gE9TY=|l`3y9O73_kJz`Mt>1^zBHS@Hk4{wY45 z+cY%M4tvzI!OAW{glliBV=oUf)-%g_9X6W=q*)IEuTvW=`6)!W_U?c^rswhER*uhUr5hWd|PyKq^96i z?}S6Mw#?uf*ggDhSIl?Yf&&feE9%VvoyU{@q^$)9)91RKj5L^0#`##7;5Ld}B)bk= zL^^d@|B4B26CsG)%xGEvim7NPF-1I9j;t`j?K|L76Ws2m;*nGcCb;q4o&N4ZF>{dh zub9AANhX@jS9vJI`^d$kpJlpebPOQI8+25EccFxIhTmPFLp(Osm+5XkbBecjlriRW zNC0}ArSTJ42STh%WmiDT;N|ctN-dSm8OlgmDq97LU4$-`r3W@Q_zBpTK)!%2kG*8D zRF-ZqER}_G>siFz0uGXl?yG&6#wI`78apO$&d@}zhl27KTe(~{HV4e0ety<9C;ew# z-6gKL(dT9OSyx`uoaRPkJ%y*a&E>I~`2>ply0_Rd=rlL>V{mVE^pY4EFx|Z*Dg&me zmqbm#EKB}O0n)U|?;xBF32YJAE-^VS+Dl?tz{Gn=oNFb(WH436a|pbublO~y52nr0I18zs zE|NSz6rI3B%i>C2{DuQa2`nZ%YfMTbOu`mHi2)KuVvk0Rz%v*GmeLI_zaFhMVq{eL z-f(~g{vhnD+dG>}=B#LJTh)XCO8PzGt^C?*~c+=gcb~w_w`p+2jCD(-YXk3{1C$a znR%9WKKOGF|M{VV?gXmA^z}@pdDB{#u4rybh!0cdpDkXzU>-V|S=H536323V?`iGr zZC#0$_Ra);MJ_ROc|&tk6H?6YY;SDt>T2)o8k3k%TwYw37*|?WQ98M7a^hJ0{#}AKm--%-Ap4!2V~(yLcX& zJr3gj@RRXbJM}!mTS+Pm#81X?GY2KC_mrQ^xyT(SnT=%*))$Zmn%`#Q_?>v5;vqyl zqm3p)={Sv_s5nh=rsBzprz)PK*r?c{xLWZd#m$OWDqgR6tD=lKQLb+)|B&LNirW-_ zuK1kdtBRv}4@7$Td2w{zrR0Ge;Bwxb_+lWhQC@s8;1?>thzPzxd0CT+bh0KD@jV*< zF-2r6_GPSve58JXJjcv8p*TvB&oqqZFql}SI8#yT74h?xKTDApBK2DoIh-Khr6~0b zzDN0uiXT;!`iA^k<)z-i-=h2%6z@{JSCOM-=JP|vpDI3~_=4g~ihox8o8p`Gp&h|` zMSgM~f$};JXn&`G<8#XC>mcsUht{Hc@<9*icPVduX&aQcKD3W2f2AUy0GN-=8wB2= z{N0LjU1g`vHx66$ZF~5j>hZdN8-6b!c}c(99c+i9K2SAj^RU1sLon_&<_N@4&sRx# zQX?QJV>kvI$LM?08w;IqzU-@P;CW-H!DelfzZu(APJ|^Z5s&V{5E)*}fjgXAi}lR&q8pC z6Jg0uK<3)(hCLo1%Lg)P??%u}Q;LnD5f^A9>d*$txP4jC>OBHubhX%E_4qDSz)gtUYI}UN&JMzh7E0aa=;e-u-LKSRh;$!8M%x&YbwyM!~-p zk7d`MUwbC}Yw)IxE&2QA*Gm3=$)696c+=+X$UAo4j-fUFU)ER0;c<)pWqnQcj=aX% zJK(MQ<8Tv0+TW;$?Z@()rtxhN>tTy*m~q6Oh6(I9*DQodj( z{x#qJuIWWc1K*b1h5797+qI-;P!P%9h`0#bg>ToAN=A<}rfLoM(d2SuHm(qjCT1WaayrRiE$I?u7PV_hJDGDdn>QGg>{N2ewWJ)C$y(Bwt|fgxBXlk4 znWQtUC1sn6T$|H-umVopC^#N2c*BHf1eLwV>a z2d8@x^AGdNh8B85yhXWZiKFs>m?^`#D#*o=@oOQ4<-!kf^)T<0S`m(n7})b!FC@FY zuorSZ;^229Q2d5o$m5`ZUtC?&OX8$}S=USAw15ddeOCttzD5G?lkhc0dP$rcFwtHT zZB_zIhDD&@up`*Q?8Vo3CR<04`|5S_3B3lfF`p1*2nQnx8XT~L@n}&#gAf?UFTs*v z$<)C}l1GK|5_mje2O~RNmM>yfuOrq5^csMK!?8aO8(gM@3T%V0sJ5`@pO>r|k^I)H)2FGKvd;wH`PZfu-V*U{O&w6lJB zqNTO1xhrw(nn@L7>XxopmFO1#3~s3j>u9NiKSsPUE63L%zGy}Jisqu``i9oJ*1&sN zx4gb_S?h}Cw|i1z*9BcBD1Rn5DQ@-RCUyOusM0<&2{D3L+|}I#-xVCAsBMlGme+SL z3%N-j#7x41;tQh@|KYsisOLY_)#7(0{Jexew795bPD!w+cVJFanNOHDD#`eBvg%gd z&ue>w$1$lrodV~432?y9)y0b!E;?;SS%ulBd3v4AU8~x<>0bTMGT(h#=KC*kv+|k9 zotMZ?%?5T}|M9T~wc(40&DIR>?R@%61s_kN!@w3Kqk@MH%f1;OE9*V%W0h;YkB_yC zFW%oaA8R#i(FZwSkpp(}9M2G26;~);sJKD#!-{;gq5k!Xw<_MH_$|f96t^orsrZcI z?-gHE{F|bW5g_d!syJCu9u*MZsr&^*I72?9crg(Vq?f5&d~M*bR(`9>#rFm~Ur}DZ z_XfT1s@{(@oZ-qFfyO6U!Cpn;}12k&`RPpGkzB zI^~xrqB=w_BVFk6TBbhRATgoH;S%}d75}e%z!$0g4T>L8{J7$^iqbAno-ZqZr{dQY z?^ong7t8%q#a}A^O7VHc7Zi6ZzM}Y=BF1rIhmYH|H&~I6aZ*o;vL+nz66M45D62Hy z`he#uzd+IYTGF26}Ko#`$PUWEB_@$X@`in-yM8N83 zmVF-cWq$1Mh%2#K+aS$qG}r_Q4OX@sylZb!VbDj?UJf>EZzE`%2Bf(L_Sl!&U}Zgs zaP2K~?6D8F_O1qP(||O$!XEEAHdyi#h;Z$#fju4{%g0fMwRa~wFXTA*gVsp#a;~XzXIktRZA2WZ$cpyIJEwJ|sW~9N^3(+|| z^LdsJkq0B*=F9WLvbjEH{uaz=#Mxl&m6Vm~n&E_my{<8?m{<{L?%l_{ktYh5MY%v~ zjqya8zv&;wU!*;1?a)`Fvcfo;afR{FSF=MahqI9)D-@T7xL7DIwZiyPSz(;NEmFfZ z#+>QN6~-H8*pPx@&v@ce!jTA1_6!44eJ zj}oG>2N4-ba3^bw;U}I32Ht1v0om*-QWHH{XOSOM5_q4FCn>*E3zzbr#A5bLln1$e ziK5_lYB9TWDY)2NNTNN#8e@JoBjXz5JD}-JgVdO<+#z`JN4b~ZsZD^2%R52&o!Tn0 zJcYsU)G|}&cWNI8>-GnyM3+G^JJQ4?lF1vbyj9wGXu?;Gd1%uQ!{stI5-ET zyH9h{-KQ*;oLIU)c{;ZWpT5TUe8j;kNTB!)-Jc04$Qol3?0vvA^^&Lwm|))JqJU}Z zrO^%82Drv}t*9;_Y{k}hjWI8Fl$9_N8`cmn4IX&masJ<U28Lsl@M&ye$0&=> zGs2^^VXGw+UH%NTu}JVC^Y_NoN@o(KU4Dlu=;IJX9i|+D==&Xqpj52Rn+$&T_T1XK z;ovoSZKAmKPQebiSlLvqltJP<6A81O;qG?i~LN*lNI^ait)U65t|j;6uT5JR^-i+ zdRHpmpm>YoZHix2yif4~#UCm@ulOP{_MbJy{rSQ;)MWj9Me$xmsaM25uKYGdJ}*)K ze-x#j!M~vVZbj=0`=|1pgTQp}^fkqD9pL$v*CETxKxA(~v}>E`bB{oYeV^zRcU`c& zyS@QyiMu-kTxO~6<~7}>xT(Io-V`@<;k_gyio-Y7cf0rLoxg^-7UzonqYXa;L7%D( zmYjhI*YB_lehf~9VgJk~!L~p@$pUOP4M@}98sesq9@h|m+DT~1CD3#8-I|N}hMF() zv-z^mv}r(^Sr8;hXs|N+UR-+}=(pLfc^t0)iG8k31JW#oJ?hzDWg8IT+Us%bsm-cAptk7c=ts=1%{}+-#FOdng4jQInk(?z zy3xnrnObzk=`Tmu7L*rESi1A_wXf`aaP97$1%Ln919$9Pw)T#lbJnijdCb~_cW%jf z{crz#y?vLde(c~oUwn4`B|9Si@CSz1V!d(R>F_qawX`FVHtQ{>9geiIx0Lq2-ChpX zOXqFaK5xgML*~}Zs~Nt_j~~5_we!r&!>;%?YUi);l`Y-*`Lz0&J%IZ7`fe{iYfm)y z*W17KQuHHOUoF2eZ022owiC3csP}%*c^u6B(P=1_#5eugp@?t#or|bQBsPm&B%6aV z|0G6DpU-_Xb~XgQj1lZN{nk_G3zVMK&3!z^IgpWf_D{j3KJWKEs&NLPKXxWbna3Hp z-ZCvDDUIS`xCakmd22Yj=u ztFNYG?@g_)<}^v2H7SRHngrTOj_e9SCy1Bwe3=iG66N(G;t`z;>}KKmBCrwQXFdqj z?CS^&bWi1q81{Zxe%u3KV+NVDNK0aL0b!g-7ZI${)O^Ogr0)Eb(KA|-M$dp;LQ3(h z97q$`e6x0aFA1jf%-ICy62tUCtZ*h-DUwCBw+@?c)~xR(5tMk5HJ-^8$FjP?K<{tYUZ1tkQ=M*!y;6qX?46Zg8k?UNJ2f}9ASbqPP~^U>1z8KT=4YLn zpMWorx6XIy@A(dMj*T}Uj;}d4CJ4O6rGhUWGMC4o+zGyn##0e~u&N^&;q!y#PVmhE z4#&Giq2wP7rzzq{KNb8wJa4zD;A3U;h!iW&2b{mb!{K>L`U6AVQs59=yjV`r;+Ao3 zYvrAWt0m95{JzY`V2k*eVixR@Z%`E9$snB%h6gwf=;+Jq&xgnxHs#|Lrzln_@_C)| zg^Ij|l5bX&K`Qvw%3q|oS&_q5>R+#TtK!{?-&A}^@lnNXia%F;PVr^M7><{A`PClc zaYQ_ma!f$vV1Uc1=rf>vhVrwNKUH~o$p-ln<=YjxPK)U-QXbigT^V%2K8GjFhi!#8 zTv0|A;72KctRgQ#>hW48Rx8d@oU6D%u}N{6BA=m|u1j&PqSQCyIsTKk~i@>1{M zZ&F_BA3O)h)R*~~z=xIpf#OdT|67qGXzKr7QO*PS-OB%6k%MpQ$@u}=d3RiA#rWZh z?^BfXhjlB0E&BlRVTzO=Fzs@C*E<3aMZKVkl09ZQ zXgA2X3=v0Qqn`C)g!L$6U_XY_u~)rP=!EliN76ih4D5H%O_@q;3{hNA#{${^Q->S# zdp|Z?29UBY`D0FmC3zjW_Qt~AA_!@ZZOPg@6SPeO(k#G%e}{wyD{BRBX^Muzz;q|V zlFg90`EJB5rUOFe%Xpjb8qhWkNV5QKJwZZ)m34!6?fnIP5U&B+gDWy=?<1gX8jz+F z_NZrrm0gAi*IpF2JZld{P1@s(9-D?XHozX|PTOF~>k#4E8wPtkPb?qEq`fbIW*W8! zhSzZtdTgTuPZ_tn6|LTS7;i_M4OZ_vh#+mlF6hm-43PEA!2ZlF-$>_pheBr0$B#kd z)OP?RRN`JS*)l+z7w@0;+MswW?ERXA2CGMO4$rKEz4?gbeZl5i0GefU?LCf@%6o$i z)?P{3gg50ctSGN24?K%|pRd-#!2r7G;93vntL?h`n)r%sf8B{63xIzR9N|F`oG^bt<+|9pTyYEQvbdISfm zoe%A-0c+<$JJyf3H?413a;ElTjdpf{_u}_ujrQSkjJ+=(a4vkH=k9`+@0gvJrX5v_ z9F;dEYzynTPx$@zUH5metC=+iCp$RbQSTs}w7e6*u`!1Oeieg#9p=aJ$q(MWmH;cJAv#-EP>Mvt|8@6DEo%r{D z1U9FZ(ppL1WgvGlZ`Q>yb+3$cgxy!IygYAb& z-Dc`OlhhUVPojG@1fRq9x1{d5)Gfeu8rT)~C|X}64Mcq$8#*~rO*I5UcTuc#!o3)>1*l%hcVQ#S8 znqP{{vn#KqtT=9C=N8JK8^2#dlz|)O9J=9IFz_@IDDLZCkx@>?c6 z(_lJ!N$^yH>69-f@?3%G?j`YXz?|RvJP76hTcDER1yGofN3i*)JdxB2@}Ex_g$)z? zx_e1*G!CYzm&Ce&S=LM9iviOnS#r>e<0J4qV{+lTUJ^l3cv&Yy6qGZ$Fv$2!0$UV( zu(o00rKySoG3q+m==0*H8wp1!|Hw4mow*&7| zn)q-)&m}&FEtQHKZ_c#;cOd%_(!>JP0rMiV(@mu!$4?|s6N(bpRdi#+YVjZ#+ zi{d59tIY;BM>n|qd)>2bSE0YvS*G8mXFE~hui0=+(fdoT+**>{{MIf0@PxnY2ES-g zO%cA`S>mtR;^XJ0SKr`IO2|Cal>a&P^EN3Twe{t`o$JdNFRofJtE|5tg--KE|6e$% zaY(XI9Lt?&E;Pv*l&P#ZM&ef{tINvv`7@G?lB zr@uwv;^BY!H$4+hr^G%fIcN1UFLKrQANk(H`H15<`Ca6@rXMQ^ss5Mz{+PSUXYhg` zbL@vCDG~#jyE+1TEI!Xg248h(8G6cJ#>SL|bX=#LN5pyKb2E{Tn#4nh5RN7e4jv5{ zKSkwLinWToWmA5(BA<%Lb0|Vwsd$m1JRTu_v+|!*yjJlh#oHC%s^6LZcII!_5f_q= z;W&vox-K2^(aOsZ8N5uQfPAv1uU7d2#WOX&UimaXH0-ZoJn&-G`-tjoQJ!oq}zf}2eRnEm#Jl;Pk&qqP(zoy6+yp$h6ME=8+mxoO7 z;dRGFDwkgcfPAX*wVEEMN9^C5rN$1@L@uXZ-bwwq1T+`8yQvR(wcN+9%TeSoxnSKBf3;#osILPz<-v zDBBh6aA_m4KvCK$_>syd6!{|))ElQbMRA&CqtnS4BE8$PZJb7lZsUiga9%pQ%XC1^Gpa zbXbr-Uy&XQ@*h@|em|bNKG8fyZm(b4&cvaGi`ZVjZ|(P<((XC`c}K&_MnKb-Fwl22 z>@%5$=Z>KqTd6ey+Kyc4=yak@6PQ!bu57TfYDD1HZz@&5@1)L#-a+8eRFXb--qURw zs@{n<^m!5*tgIQlr71cK1-c2kHdt9bBHVoMNBg=1ddwG1GMR7S1hA=b@5(kNkz$j1 zbs)~Q*M>$5lVT6Is-(TkK-)5aG$XbBlV-58OF+B!);ad_AY(nVoV3O`U@}#|6K+|3+Hij=C(T76kjq*FNxq3&VP?_Gn@STcVzH6aZjyM}^ zd-(<;-11%J94|+s_IMuyjnl;Hmhd?2O|}fs=EX6jbK1?#u=i93J?cA$XW|%NRw9z) z;i1@=FV7>(=GvQ$N;@8LHrRYi$|k*xp?$i$YQ()YP!3*r!S|eV3 z6STcbPR*!+IL>D#uPPrbdtmk~#+&h7>>tJ!ys4?7^6$XrEWys3N$EU9$(x!v%<8My zkEfAl; zma;(ZDU}l0L%~9uYk}v2*CQM55kUpBVhe1=@&yn+4}7$|8Dpg$lwPSxxwX&(;&yC~QCru2jk3;J&oJuNb04F3 zLFYH=Ms4-?HOkY#s)tdx>iZb|J#^R%os!x!($^@@49^CPx@V)0QTCGjq>l4K%AO<` z4Ng)r5l7Up= zIq$FV>Ey^~^9!oxp!^PfkHyEFR0zJoQj_d{Q(ZE3_Q!jygOE=_vOq(U6p4Yn z$2tUhEI!XkhWA*!>{H)har+*m;Q;xqyvG{Pbos$tPU1}EPga!o4~RcU`9?*K5U9UO zaf9NA6}KpII6%Fd6mM6g&xP?1E6RHX@J}kwr%uW_7$Cl=_%}s)kAV1*s4L2kBF5k! zRy>}F2jnu9i$529rSkIL06ZT8nf@%*U#2MU2@tehJseoNlFBE7U<;1-o%rT7^|&P`=`=>{QwP4NLmX}5^quKdpxf2Bw-2-Cfy zDD4$|_Rl(tSkv2Nmf* zBHy8Ssp1uia^Hyu_dV({5Fr-iV-)#eqfLC*e{uVr_01#qF_q_zp%xoX zUoxx%(};F%gO$xd#2{>yDtPb!yoZGz$3tuqw1*}lz8{+gta%1)l>M>|R@Q_FOH+0k z4BT!D<;a$RcJm#MAy)!=%$NBw39$>CYi|M)B@kzWm9-nX0JPwAhV{^;5#5rD$hVAh_ z3L2-r10Z2I3O|tdXdSTkOPdI2%S3bz&zuH(m5AgktD)GKFV7FlhJ2IZDzwuvNiJni zrX04mwJpZi74Wl;iM@UEsC?Y`iiCu{uVLalLxi;VXr*_5Dqg^EZu?_vy{XlE3i7J= z zcs+?;oR^k=H`p!Dx$hdz*L=#G8!nUJ~p)Y?fVIAUU$x$ zhoA(f6WCkfhw2DHF5RBwLR3$Eg+O)!`hMc^4aX5FbfbRA(I=HDsFIjnJX4} z#3snqM|Gc|R?BU!A1y-Ot#h|B#$}6Ey?>a?Spp-Ot{rzwc4Ae zZ;Wj6KEO%+?_^&n@8GGxo;MZthwqc)tW+6zV@ZX9_&#~};XR*qp7MRBu42dWsgdgQ z!~mG*kaP136%SNAgot(Oqlr-BErojH6`2?LDn&l;kYA{{NKpnXh;LKAOYvewJ`XV6 zm5Mhg-lBM$;#U>#Q+z=2r-~fg@>%&;iq8=-y!nI5#pi^4{;Bce0|hU`E9ebD1+p9? zi83q-?6Eb_E~-F~=ZXAqMeDnh`awMJwUl$Z57Ev^uTg%s;;D*!OHI9V6dM%JQ{*!R zEqa?QhL(||OOfMJ`p z!OH3pVQI?vGxxbpge5-+nVausjPE(GnfWq5n=i|4(||PHsGtN14OZ3$-nDl>`aoXu zw8uvWYwr@!HVsH~73@*Z1}j^S2-hCJ{bB8~PqFqs1=^;ejhkWbbSJ`+>?d7&Ps1LM zljY+hfwlKvpqZu=8^fy*&<|__W!!F4w0f1ehS}%XVD;F4khb9t=&=piz$JmVf3__Y8qvP&PcU>Kyxx>uNgEu4Ymy0(e zURS**TIYlR8@w2g$PC)7ZQgP59)FvE-0U8+{qmj;@ny)n4lx_`*lzMRfY~h(={xu` zWX>$U*#UviHXRu=gZoHKULZuWc}@8v7&ZL^+(%@G87!7GbL+Wamkm0P$T|qmw&;KuJ|WNB(PyR z7vX4Z;=~}qYJxstoW!q;^pcnqFwtHT9Q*lZWfsRA$-ol^j|PDS>Fd$RLacf;NU}(n zKuM5OW=9^G{`!a`l8PgtgjR=OyRV?|5~VxavdEsomiiube(dW<6X5*FPmCHluC8RJKYZ%k z^Foe~)O@nA3*_zm6oT8U2OmTjs5?XsUtK62^^NB>H=Uubn&k6&nk(f0$U`DuQm_;3 z-m~9Q2&M&cf?%@prM?*>gr2A_=d-^1**TJj?!xtru&c3wT}oAc-bQ~@pkQj34h-}o-32zHozE?=aGH^jzftDDjq__SadWIO1xuIZv7*? zPm-Uhc(UTDisvXcDsn7C{nd&WDQ;H0Qt^7lTNUqC{HEeVijOL8QzbO8L zhzGRS6~!-tfs%Z~75eht3Yg%n5Ahrtv79pZ27I~lGKd3Tt-RbS|37=*0v}az{eSQ7 zCRsv&@DNccT?OSKgzyj}Mo9=P50OWRNRdi-gqHy#34(~Ch^X}iik4bxP^sE#D_XSv zTCDg+YyCmBt+m=>+rJMU`C-)>#n%7#d+s^AvkO8LwNjbS&i$S0CVkno4-yQw-+=pD2%FptK_}U&A?oWceiZzN0 z6faS{Opyaa%DY+dR>dDGa-_(3X>Z_D%0H{PMN!%t@vkVqRq-vwor?8}A1O-vBVQKV z5tysUJy_(W{ehj8=Q|F=@zo(a5 zlqfYhFfGw^7RCFR>)Om2*$)By+ZAcV6JWSLr8Rim9*OtwbUn<|NV8J&5KP`ghi3R$ zXo%q!>BOb$VRFznRv{hBrGA1>4I=wFC%g9}yo|>XX3pX&5#XN3B6J+=GboSady{u5 zXv+iAdj|5D&YZ<9Mu02tYDXT(guEL;TOP{zBjj<;)0~m)k6n4IA%y40`t3^((*+md z9J`s2mh$NDQDnbIcO@bj6yP^jf$K6^Sp0NSnJTIYQE&SmY#{tKsA1rXO8@*1Lt zSr2)?w@g557ou}|UKQ(yz{3%4hSc2$c@S*joWFlF7yr2$8kc&pX$9$1UA3z2le36R-T}&bAR9y^T&{Y@H zA0(Y$31QKeq(mPx095oEvZ9gUEr*)}tY~C@0jkkRvZ9gs6WKLn3%BCPCFj{3zlqSs zerd)#erc~G(YC&SJ%Z1K&+tfng4)L2qZ>|s^yxEh*QJTny_i;YzSy39 zJwdXZJ(WML5UR=_l0k7}2p!;|i6V4@hbGFeW!aRa%UMVU<(Cpz2ir)SJhM`SiffBw zQ1LRW_%~rXtf)iusjneb=8Ff2vS z#D3HqyPbrdX?(UQn)){`7A)oy;w!p0F&LKFStPqvhI|>{gKB|tI7ST|2QW$3knceq zhUDnwZcPu?K1fIYKP+GogPnVUTf-Jnk%v#^69!Lj>Q;$ zq9XfE@&grzDUMP+TXC`?M`lbvU-2Tvs}=d~!}!&TU$ibt*65%-*$;;I1X-g4o~8z< zlal?4;7?Fq_RWCjepbd0A%d5EVBp7+$9vcu4X;)_UlFGz@x1exPTC#FdxCr$MS1jt z=YNjj9KR6r6?-cVP#mntcW3aQ-jwqf#chgpitj0Ypvaq@=_87Ay}&nDUalATnDQMJ z<+>r<)&|KwQ}D9C6i7V-<#Ilo_$|eY6fvfj@GBLsQQV&nOzsQF<481w&c?3ir&^1Y z`W-3#;~w$JpRI75j@>SyiuWR6#W9z(36kDQ=Yj6 zAb8)HGm_6pSKcPbqu%Opg!4W!d3?RFJRrS)f_adHI*XeE-e^YMgTdMU*!cvoIM`aAYllm}H@LLU1L%LCH84Dy)HoW(6c zfGaQBG${|F6Y^Gpwmg(^BjinR0*qXVI9DF`E!lPB^WM&PEokQ9{Xq9F{-+nA4b3r* z{$52(Hy6U#2Ief?1_Y2c_jjZ#Fb2qS=^lc2>lbs*mm>hH-!q`0x2gh&Yl}=L83VMk z_>4xngv*D#KPI?jI;M9{&l?MQMF?zzaE?P*F0T*k=E_@z`?M3n%vrgKp~ci*JsY%c zsmDnT)|ai}gAngue6F$dHkb{|U4J$9U%`T5_?WogH@q{F9o`wv=Kibj&P>o5+3&#q zUnIBruCR~&S0}C6;b-sO@@;6)ss}%a@XnDIaw~5&TzU z9g;lEA`x!M%6Jpyz{1|Gh{=qw3Ryn)T>G5#N-mr9`5(bXD5>M}qDF0)q-Z_Dq|@Rk z%P(GtBga$Oe2eu<$kxIKTdf9b)?lyIkR*rARzs55f>hg#RQH?T<7w^7R4^S z&%bWkB6eU0wB71an7qnO?6*2f_gjUbBueU5s0s-5Ln+Fa1KP;sTFXkTPjQ!`NbxD< zB`cH?oM6O!k{l8OB@3&cTny7d4Mxzgs*n1w{_u*Z^_&lZC)$(k<{BL{<=lYk8r225 z&(Y*ZYrf<~MCcXR1-a{pI4&^9j-t3}WT`)%Urhncfz4M`r^7K!(j_}Th8;|6h}h<- zYlwosQ`#OY1b%wG4WA>_Q}j}l=Op-I>=EuDD(C{}iR&kp3g(_ov4&y$MIOpIMKA?_|~F zrSx_Oy%OUnv+AMl+6fW*V}msvPf{T*JX~Ssu@W$a=f- zCWk#QhA?v$SBU`kJeETt!T&tvL3BdirJyYjNbgd}V>)vdw-^DgyfuzI_9<3B>O(CL zW!wOHd)vFV0&%Xqb&$vNWBvGGHF>v!ro4Q3y0;K-UgzzNRB!eV>BIDOLf*ZG^mr=q5Hn-tR3F(CS2VPEWp}(U7$~ zT~XQgdy&UpX7Ze>^BZg%rdPwivCsj}A-ELXM7Otg6;If9ee|aI)$yaKqlnN>%)t2C zw{t^f^^vyzDG`KGSMkKNuoTm_q(0WQq`qw>EXOR`v1P=zXq%z|PYh^>H9lFJ?|D1o zkF1ZJEEZ(?eX!->H+Nl9h6W1SC*o6=ox=y>ZBmca1N0XsAR&|sF+@OrL0k5)A2SGC zumh{#NJd05Vu%QfuA_L8gk+sSh-Prz4pbReflI0~_y7yjcDCA&Ih?u8eoSYU5y_H; zqkdFu9!4YlD`IyQpBNcOQdX+{7>+1(6;C-xIzP%iTYk@HLC4^u(-|hD37dIkhv99wqrr26bvlL1mm1}W`G#5d> zQHMXFFZF7$4VNOIs52*e06qSUih11$9L(_B}9FPtC7H~ue=fOcgf41T8h1_ zaB;iz|JWC1_pkSGn%d6)(B`R6v3aU$^xVnVKVDMOH`a+7qp~@3Dyw5t=gf^2m!2LQ zIBQbHlqnTcVk76ynOsp_J!fup=L7zdFS>^ucvttqZpBp%FEk#!u3V-+hD zWpIOWpp2h@muUDhMUL~C{}#m`D&C{`GsRyhKBD+rMb6(*AMmQ;YebB7-_ZECmH(&m zcqB@E1`*{pQ(lH-;M*xbNDq!JE6!ECRPl1fYZb3oyjAgsiqejdvrhS+D{``p@}E%Lr1*j&r~MfJ zXT`rNZdWw@2nUKx7h-z>Gs$Nq+Xt+KTl;;!`jNzaeMmpThK9ps5Z4h$z6XQ@b`O1v zrR$1#(!6eT)XS8UP{->=R|0Q&$ghOwnk{oi_Co;veqNDAtOsLy3fvxv_x5!C$PNhL zJ!;P6O+;?w54^NTGASe9~F_Jp~%4 zSjD__9dHs2(T@y+yx#{|%SX4jMio%9xTk95+7Y3t@KZ!wV4RQ2Gr+Pj;_zC9033JmXE!Y1{4h7ey7aM zp_B4I2tgBabbSb#5cv}0Bed}nv_-_{b24Wd4i8&Pv1oPBg^2dUkMc1b=KYhfKeLHU zDCE!1>X~dF-(j$FA^EzGM46$xb25 z?#u6W2aZeN8$F23{t0AnhX?%?&~`{gaAvrK!SxLM`bz95922e&C}5?Pa24kT{VtnTEvQ3l)zAoz2EPtzDUYTSqIQi?SY(UQj?O9v9Glm z^4^?Wpz(EBbD_&B4rm3Ic(F$Uo{sYPxF+T(@|jJZVb0_mnmMODD8=Oj>(u_+7o!c@((ILtoW$nCdKCzrCpKFbVYyHaA{|R zr?>M*8jn$ll$%K&?>W{!U?tqz?~BlB9J1fSRt_94gSd`3M&ZK&`>Avq!!!?p*Nv_O z-ei-%7@qso%o&-k(8iQtW9RKHN5hty<= zxOANc_ZKuor!gE26fCJ{brZZ=8={k*< z9C%U zOV??vMmp-j%-Q|&00P|h+vS{Z3&h#^J_Q=5SY`P)LtaC48tjOFZ<&DBW<=-oyc{%2 zLv$L2sBHVa$V-mL8fd?z=ZSq9YQN=OUiiJ}G#YPi^36#60=3IdT7fqzQ&_*;-!e+}WU|=t09#X+(%fHEm zvYufI?imU67qHo~5%HlI55==^grSu}LZH70N3({5OWJG^oyhY{(5f?zA>JQqv*mb_ zX0wIcUm$8AYKb~r%BX0B4VVF z&}+DkcMen&gmyxf5qS0pB~W(|_No>qtly?7G6DgknCm#nHHy$la-Hkf0u$~SP|!IL zSe5;1H3HFR64(>MI0}J(dZ<|z%ZG=8I*%q$tSUH2vIZ!Oz}v>`q<|DB!z`h!qe|L#q@`{?S*+^{m z_|!^`;AME9+KPtiJ=hFRFCN&w;0v`VH$c#s%GXGofYZ-H<-6c)FsrBf`fif41T*#d8%GC@xauD3J0lS6rpITJd(pI~6&G zV|vqTY*PL?#ZS9N83DQ{}il~OXWZ$2RyC_W_zk9=U ztX~ei)o&$eS04Aw(9V%LBbOt|xug2!A zU5L)i2Gd1>RVxFOYAx9aM{5fF4*x>`Ihc-DVCKT8JiP3Tuu&lxOk7m zabiLHMEt*?@SMirs31spSl2NLVOU79mJrII0t1UVUZ#LR0}|dq+H7>pW_)4=$Hm~1 zD>!&ZhJVh3+30vz#AIzD_|f|jg|!+l5s(X2%6J+vX*N2(i)gjc!N(_^>N@)Ic8kGB zd2)e~4k`$uV{r(KbbKFFqn!{M80pvmHrUM~MmpG~1p8TprR6HH*wG>^4OfB1juv4j zf`y#D>PeEOXv40`GRp8SY{UARYXt(z z4+6tw=L(5-0kfc%#Bl+$DDmAMe4&^_#=${&RV@ihPgA1=!bdT7xWsbYlW@a8L772d z-aR!Z__Nx25R!qku>>wg!FmuvUwGN$!cr(p)g>f@LQ4r{@Uk9+L{QaIP~v#L%#H0g zK|pZ&K@ii5V-V7EJKV$F1L(yFS1p$U*CdCR&Sd)jH(71KRj z!)-A|wk;;PAcX3S6iv(ly__f2F7B#@1}{i)A0f$F9a?5GdCu&bxf3VXoHA$XR4fNd zD90QtAEp)Ai)v=y<~|yz3=3|^FIn9p<2_g3!XCsNdx}H*V1m{NOG{bHus7#8BI!QQ zeK2hIR9#DOZ=@+(5cuhJEnOj-I+$LHe1MTJR^$VP{OOA06elW9S3FPg0>vv7uT#89 z@dt`BaD&_*DbFzv%XyrLckrhbe@(>rcZb2ae=8s56Ak$fS2R1W zX1`VJb0VJ3)dl1z&-Vg`#}qp#9%mOqPfDEo*|$^WHWZf;}GTaL)q2xmL+ zJcv{ZSRRnx>u8`D33V1%3Eq{r1|19UY0ArnH+h$WwmcwV`!PsIn$F@DgLdVubL4?c z$h*Zu$ZN4gcWqesYbi=gGtK*o%Zx8DveyjL5;-l(wb8>r(f^NoZF{-}pBLAj-EjqLr zRo|v^<8H70!0lV-wc4JQJ!0E&o%~w{Zhzx~r*~(yKXQ9Cf{^1j?T)rTZTp+^?mXhD z-I+-H`hpE_`#FX2f$T3OrOl(eeWZGAo?r0ZmPT)X z+$T4FMBHoNGalKU@gBZr^l?`Fiul^y5kGTxqknkq{p|yNh87+fzIpm~KjUwWOSTOx zX<44J4z^IP^V%mUoDI)p(;HW#p@Zuc@%b#t>5Mk{*trl!!m!DkprH*C6YY^K8BzF| z{7@FOJ-mUSP%0r8%A!^$%-bx~uY$)&#u!9|Hf5hBBPOK7#E350c3Dc0IMm>Nf$z1_L`*XCw5mfY1vL%fD5WZ3Myjh+z`uR!jN|(?GFtKyz!J_fkL?5MR-7;$JnK__~G@+2F{_VPC>^00o^B zp$k0hvewAQq~ifIMGT+tq9JB_Es5y?Q(3zN#MSUHq(WFF;TJ8cC2?;M&f2ilG_6)l zf<_UT!uP(FOqinJI#Oaw2}}|4R+JIn41x*4IjtxInQ%OrZ0{B}kvQ^w;WNFm8zN`i z5aAgtaQKCwmpeR9V3WgJ6oG0XX0#6l&aeTRW%fMuK=hL1INuD;!C)^A#tGh_t7KFbJ*e2v?f;Rj}kO{Z9 zhVPHqOt18Y$mkm)Jhdqf&y$+%@T?sNLYAIYxy#}Abs>17A^70$B81d2p!VdiLx&Iw zhWh(nxm7cWeAV1J)8O77un&I7GjI}$q zOSc~RJ&x+0pWm%pkE6SV&&>-r%j5CLp~a_wUnu-BdC}VNU5g9DFL`<4wTlbW&G10q z*rzv^O(l0Sq1PWVb@aLOCeE#}vl=~T-rUI*-AC_vfx5dRmtb|4p7s8}WVNRnaxcoV zYN^PGsi#*=oE=nBs#{fBQ4KAut@d(sa4E{uq+T^8_<(e(=l1D^_CmV_=6p@jmn@1Z z-2ZB0#-adDE+0#%KcxwH_Cn}FJXi5N3l}kYZ~bRX`Rt*??Ctbh>}vZr?Xa}bWzl6> zmuFs)ab@H?;j3WLr+?Fe(Ddx49r`yekt5O-7vS&}wQ=0cIVZQ9X7IuC-6}hUToA{? z<0&GKnS`!0*}Vp;&IuFbYD}n{GUL2ibEe2ljCDrHdk=gzM(d9$W&%UeU=0Ixi_OUc zKWW`rSB>AFE|fY4${C}0mLea)44-gpm{bx=s(l8^QPw{dm2JZ4_m=0iHu1hIdlTSLFYf;f0Dtih~u0DUMex zSDdDJj$)1C0>w)dFH_`TkmX#j$d?=P9Dx$=P~>|N`JXBNT=7?ma{Um$MR~by;9pUG ztKu7q+Z8`l6x}t_r>|#g&UQt(=~~+=A5-Kz9OHW{if$YHVCCgHf*-5=6vgR^)Q2(u z0>!0@S17JgT&u{pLZ*LQk?)1%w=2pgQ^5aI`F|<;7+W%(*a-#ZDBn_%rbQXg7fWIn zMX_rNo_25{@jfznrJyYjNN+vv!IdP`S=9U0q@F-;`wgv0@Zv%-ZIdZhay%$9@Ck# zxFra1<+X9-p@|amR)MxWlyNKM-QWZm$+mLkb%H!zC!R0IXm-A9K{HQ2JY8=jV*g@} zarE~pTDo6l?_GbsLDK=m<)bn9JT%90=^lc2>zD7GFV)6Yzh^+>GF1V@Er7g+=+BEF z?~j%VXzfCDPS0Bpc|{0hpM)$@F0T*k=G;ae_gVHpm^qV|cnb~8Rqa=Q-ltd3{Fu1% z?8O6z#eLr-v5uV*^<+7l*kR&v+gqR$_9L|R{LWnM*gAgd<_X(Y#qe>~8BM|WpH@4XZnaPkF5Zfu!hss`tOsL-ZcJ5HcR=?{&=V(5Ce4J6 zj$vfz+013gkUbw$!l7ShGIRF)KE1urbCSqwTv<8S3;iau91&g)<=qad$=^ZvUQ>Sw zkqY}j5lHl#J%)4fPho!@1JAwz3r3lBHQ9H`_G9d3P`RR*FXrU>2Vk4CSN8jpZSg7- z3dzJRcc`a!^gO8nj=9$Ec*NQTZ zA;BSIUlKeE5{FsJ)@&HfT?nEG-kbIwW@m3Fanon5ER*hqLUl$T^<5&y$=B zEa!5|N#Za|r8$v&e=`fZ%oQDM#9ii^p-jdQ^n01jyvA6Vin}BaHm$KrHKSwBke0E= zK7u%@z*z)I*ywP?H*QRpaoJ6rI5X5P33GgZNJ}PZaf?5*1 zNU+IHD2ESw^Dn9;Q3a3iD*)FEIg-G_9SiQ@bYrw^ab{wyrX}znj1S!rf;?w{O1L9I zp)!OZ&lp0Gur!ehL<~OU<<}B}lx3EZ!3Y&M6-S1*!^e={KlT02!4b0z^Dx6UG79y?) zK0T?0J4weP8BZiVsf9a9+ag&A9)DXo^1zmGoy*^cyFKKM;_*{YIs~eG982cRt>|tl z{eyoh!tCQso>e`gdcw5HlXdxW`jTdhxROhn(cBze>5^u=cj6|M3lZ*zZRLKrCYFcY z<(QnfuY31MD7-5tFXD%6eIQ(;cKh&I)=}Wg`35ChlfO4@EZZg zhe9b|2f#QaIV~YJ^@4rmc(PVZn=sFru1F}IQy0$eYpO!pA!V9_Dr_mw8Ay786~74+ zMxQ>cc-R2%++IDP_fO0th!Wgg%A1Jc80z4f$&dy{wGYi`)Wmr8g6Sl=AyOwd4wg9$ z_Lk<@E0!EY-zSia6_4}9)o zFyv9^KSHrgaiZdM#aW6hpXnDXUah!7akb)|iuWr%thh<>1;wq3qU(m-I_2L}{78|n z=Pb90qO2nZ&u?8Ze4^qH74IS9S+!1aJrT+T+9qTACzKcaNZ?;k{w0n7tMYFvijErT zL^loO)3)_=O~}67*pink>_!|Vt2(p zihUJJ6bC7Cz{z|Z=Mv9SoS?|THp6+}6RQoMPwPsJL12Ma8X(e^>lS zkwbEpM~wl|=4hxTAa8Rt)De)ERg6ID0mzS1q!xhu3`M@}lV7CBcYX3J6{Y{n3Us&( zrz7&+fGBg3z*gk(Kxw1l2RPU9-(a5dD>c{QzOmVR9bYxkMR`T=bZeUi*O@wVrlH59 zZ;!c-=MaW(j3pdyQE~lobmgswymH(}l*jwXB6|rZihT` zmaZHDZn>?Rqd(Ge8E)mWZ?ilgy*qGU$4IENxH;fmdFxvy+XechguG>-Ee}OJrT-Js zbQZS+v@7okM;;z&33YA#W{c=E;Yry9$|} zPHR8L-wW@iE5$>IZD5XZ^cxf{{0XEp*$iVl(>(<5*6$VPd_gAa_Y7!U`YM1p&N-Z9 z43K5g;c_xv!tH>NKPI?jI;M9{&&x;S79o&*QVV$2FBhJ5bLCaxKJ9@pb5^dsqH>oX z%sT9UuA_Iuzs*r~n3R^7>u7`3Fn;HA|F~=YHPLfv*CCu2|I5x(7q*SJ+8J5spW13? zcqT16oLG1H!m^#2@EJ2ttjkFzh*6^?k@vognQZXbVehriH2c|KBq zuqCT5_sECp1`WNuZs|C$+i;QI-R}(f#|>?{GcwaZspZblLfVb!Q#Wwt=$%N+eu0b%EXXE;kgudd_JKW!-(Fzmi{EVJ-L zF1+gvw|+l+ z<1ah?p?`dXtrIL~B^B}c&ElN*aacz_&0$W0_dcdmWC?;F{RX*E22D9&4rKtjNJe+u z8{tbx7gN=T*^QorXoid>qFLVqmo!IlHMlUJs^R?6k}Se)HpuiYIY0U)Wk)0DfXkSV z1MEiNQ5o~lMB>4T;Bdh?Pe_6~Kutrnh8%-uVfU$mQHt%^q zPFWmbW$#R|&y(dS&>zH{BOyGSlQ{lh=4^!$8q1``5a#75G#{w1xErhKLSQxYrwyFHP_p9`GHT|EPK?_Gwk5f(CPGH)9*S>{}T2#gU zGR_q0_FQ6JKnu!95#b0*xL<*S>YeZ=JdIC`tR=<7hyayvRiL00C)B`)yye4*mT#1k zZxq7C@j5dB5)VaY+}FGf-W$>Ry}_N2swEfH3bE?=jJY|;F^qTze709z{!c8uAKJNX zE%~1YVN)!OJjdHeH{HU>Z-hr_rv*8m1;?9-6(_z3pY2sGvK(N=@w!Lhbsav**6{k_ zm9|2TAr!)gz4Lsr`!P(y%Lu{ael%f>B&qgm*$`|kNFgi~auneg@Yt8(*YYAfCE;a+ z-%5CmUt0&FUcyHa*o-hdLdX{K3_@_d${24hsI*M0bU+yL5C*`*yh&iJgx43F5ePv| z&s!v4zh!L^P6#SJlCT6G)>&(0-vO@!m??=10%m%xT?sIiazala8SW!Oa6(m!YDokq zG%|TYV~}ibcOB0n)4L@Ckju~sh$}dPSP;;|iM)Xr&I`cGl717&5-dMyz28QFOgN^u zG^BZM$UlV0%R^a2%40Z@jc$4D0x%9y;#ltv@FBD{k@ZFVaN>X6-~RY;b}Za@ zYIx|_a6w_$0@jbidV_Qb}@VsNX9@Bjgl6C*)wD6k6vGDxDaHm-K zTe0xTh2eM&-b~#uAXg2UT{W-93$``{(;JC7gik%Y;EW2rw9OGyM`Isf(lp7JxmRN9 zn2DA1Dv}?(`TA~>Xz=V*a5tw;J8(|ollD~nM|M%>t$Ok&pl_^eCzaGW6MZiDtqcdhP2>Mh`HvJc(PqrY_dKRMO!*^~ z7d$e_8R*igk+rPzWwOqO3W}4qQ*5f(Lh(q&c8XmT^AxGdVE+D!!xc*v$0?qzI8||`;#|e^6qhJ2Q>1Q$@^4nW zP4N!JpDNz3_?Y5Tiq9+lR`F%UKP%QL{zLIY#eXYO-NN!)C?2WUPEnp4(PV#xzp2(dte>ff5LXALpLCbkaOG7w^7xFm`mF?Qc_`y0$h+GKFmgHKTzMBm9?y^ULz5@u ztpUwE`S5g?BGVaYYjcdFzgyAL@tbCBD|43aAq0>%HyD*KG6rauW<6-Pepfi>%ki|G z?~|Z$Nvi9Zf== z$>Uo!jJr12E>oW#4WDaii*tZW%(bN2WtyI_%QXF$QVhQLTxjh4CZ4ANIiU#Z6;YP@YS)+bOLI5kw$GZai~k@ zbL<$s9(QkGpJ_V6_?O1i%=dA8mF%Nr#XeIjBzEjGjRBjzlzHP@k>)9~KW1WK{~y?- zeWoTz=GbQ%Mwagrfqf>f6l%=jxYQ@d9~Hi^3{Qt9zRxvE^pPjA|7m}_EDm(S1zRC( zvxQ@$iQiiLWD8D>uUz7zVmJ2B;U0&a?_lsA%3jeQ{dJQ(uU}4K&gDf-gWViBJv#P6 z-11<(5oodY^!p&ZfGBS$2q(&03epq*u4#yG26P9ayt#a-mY-M~mYOyZ6qBG)1lApv zpX?Mke>AE#nJqC7gk);1T((UFr@o@h@}YUkm}{GeDOaz;gJ<*7m1 z?c}Nd7jLCFw1pzrLb2}&47r2Oom<0!HD-sV&90adlPRySfQ2Z$GNjCH6ghnuyRc8R z4>gUJp`QQsv(Ib_W;_nrio!bw4@8mTbs)>b<%|~ zBf&WdbCh!|Jf1G{_~Cp<4~qm!IZpSL_$J}r_Frg!CEMSs?PlH6juZzN?eH#Vx-Z^8 zoD*rT$oi3&ITO5BvwoaA>8jzq6#FU`D-Kp1p*TkIEJZ%PDR-74AK&CfR}WmFyy)n` z->Lk1#a}8urudBFW<|bVQ{HQe?<(#lLYc!?G-8;Ka=cJCAtL?Z%C}YiIOUI5o@;8E zo;oDr5Dg!#ysUwToJq=8DT;0$;fs}DN(6tc^3+GMK6h#OJ&LHNgmYlRc)316-V5YS z2Ps1s@EkTVypv+SVsFJl#Ue$yPDnpY`7*_^igKM0U#WbxBA4f}oQoBgDqf=~*AMYG zD}RfkTt|dcXT$vWD?X_Bh~ndl&niBz$Tvymdrk38#T|<8D88>KdvB25_B8O-llj{y zep9i%qFiUhcUS&I#gi5LE5;T1?n-&%6lW^hUWbLs%Ul`KeMfn@?%;1${uad_EB;iG zFT<4otRmlr$-kw@_hItQ6>YAht@1Wk(ouPvE9t5HNs6Z^@`al6xU7y%%wXnTPkQJ!!3tk(;QvKI*a8_K_< z_`YHoiZZ6pQf#SsxMF+7j*7h$PgInCQT3vfLr1j#W6GQW^tSeY{8V!SDR!x><32Pm z5VUsiJS5!_J_?UZBaVHV>+qKl$3BlVuN@uJy(?63;I*Uc3Xd+_D}twc3H>pzAN8$F zL(lT~>S?k;%DgS-M9mr59|5ksU698)R?1^tOkOEy%LCGT1^4P>B-B~l6!1nf>K2^% z2s?Qvt{k*m?iIKXFGf0+%Wx}~eYY!bCn^;~m^q81?%b6ZZ5cdoC=Ww`guG>-Ee}Yq ziT+PW(^=dS(5}2TjyyC`Lf$GT4@mDDkhju=IB_dMyYf0g9Cck+%vL;Z%f~GkJ-jW<%{#74+y6 z6Ssf6R1dY5(o*IR%q~?)!Y)<+&0nTnsvhx;ExmV|d9Y1Y?A@6tyJ=qAeiJhu{f_3av6(FMd^UNFAsoKeWLQY0Zi(ilxJ5?OTw>X8Y*s0nL zHfg7d%TmCZovPtr)9h4nL5bLlnn+?Wa&s%SSehcSA3IggT5b`;++2Vnxk;qksgknC zqORjv-}9nqA1V9i%*5OrYDsPq>9(ra*Lq!%r^WY>)L(WV*uNrO^Vca&Sg+0Xq}lLC zU?%1rAJ4rbeSb6p=`*%8U?%3PZSRVcJYR~%tH%BrnJlbL$|TVd&Lq*1d!Ho26CT}F zU>ED^fNn$Nm0>vXHVp?P9D8R>$q-^fo=M;X@<8onvBcHzkWOIXpKLFSRmFA|t8p;* zLkWDH#6}@PAmI#933ntYwVoA77(?JIDe@2~BKQifH2{($Ea76H%&b-*qRdjJo7H0U zf7GlNU$KKm;6FLxVxZg!74nnd&0Lk8kDCV3;yU0s9NsZu#h!xMouIB@c}ti?km4-d zNm_`ABKY*A7VadqE-5{!g*!>FfW&Lzoyo(tU_RI4AA>1L_B0N4XJh}uNXQtWb3YBC( zuaih6jf)GA;LvwQFl>^yjYP|Rk#k49Ci|W{+K=rnQ9+`olYT(vj==0`$BU2N&vWi* zFywJgWP~DhE956CPFI|zxKPpNj5rcwdYO9wu2%j|#rqW>R@|icg5p-iHx%m>-&6cZ zQRWmN-{y>t;8+2?%@Ivho@?S5E^|518LuM`r0$S&N59heP0Ig9`9CQCit?{3|DK}k zmw?=iP$0h<5%Ofe1j5@XFS>v5-ISMo5lA;w`O!qw=S)S>?IV7U#>>(~@Mg#B5>2;4 z@kT|yU9x9->&$s;s-?3>tBkE@CF%zuB`5VqKp~AbJWXl_HRUXUBp7g62(D^93eBFZw^G6+XPNf zo?}gh&s3~doUeG1;u6KH6t7dfNs$A5%DGkXhl*5MF#I9K{mq@dr0HH!{F|cO?~wCP z?cD!)h0|=s3>!$;Hg|-_y|RrI|WZ=3d5%< z+MMWjmA5&v+myF`N+rqQt(A*ZU1s$4LTt^&v zJ#b;D>G-})n%9<&>DCGr9C&T%y27K2^oro=UexPHy&cogv%EOG$p$Ikisim9 za~9Vh0j|6qkVjQD<*_a%kA0!#0qGrwh;KUqMovjfcPZ}M_aM)lr7K5(Tkg^p2tfT< zF2k){_N|r&q_+Sk7bBt0;^u%)k#|_||DZfPViNL}fwnv#y;YFciG(_fTLRvdw-WMr zjVKR~qJ+Fvpe+wbZzJUW(g`qfCE{FpPe2}$?ssnMf^4jbNNYdJyceFxI?;_srPv1M z7)QTB(ZZiYIzIQzv7PB2f_LlpoYQ_#P$tgz8PK>)RRD3(rVX3hdIjFuw%#lX^Xc#BNx`n!i_fX5<6wIF9w@p$Z`3& z_h7GV`H?@b^Jdo9Ob%af^6YHK{_(0vpg>Tp8ewh8I z`|6&~USC&~O|Hq7B|E+B@VfkmxeLayY7o!ji8t2O4a)Yf=eJ$_o3~+(j3+PaQAofZ z6W6rl?842-fdDU<>oBi09X#foWJ!*nxdjO^^)#D-p^P7p3uS#@l4NWE7k&?PxVR$7 z#Crwi##TczX52Uev5%Dh3~3|z<0L-vB*jFsWJKXdFDDm`aN;Y&KanY_{jGpZK7qX~ z{vj4;;Sc3qlr;*+3$xBg$fB$(L0^!?m1hgR=r?hfg)Z>#0vFwmLnyFS)+n+AOg8^+ zp<7#v)Nm){hKVpQbX#ji=E&Ng+gsOz#yiBFDAVVRRq%mGq8~4jtEvCuOq&HDIf-) z;gS068*gB%4yMTLLnWD=@a^-q=&iGaKu;BQzkkxmzJHRv=l4$J3z+G(B*q3zW$jcDRq)78;42J10KTY}#7208vu6JMAqp3_(*<|Cb+pR!m_#sS2&!?6e+VIzoIzu`nSdI+}9tnra% z2rmNLAM~&);Kczw0w6AeV-CDpl_SZjVgij26S9m@4i5uuzFf_jV3-3RZXr1r9#brY z;Nrrv9PcNd03_ikj_tG>KCpdvIxOvx+X&CIaav-8bLNSfGWVJw?QnoNlbGwhH@q`` z?P$Ueg4>DYHAvIaV$Sv6&wpPd*COIBcodruMSdgvUT+#+8AMpMK<@4SF%cB&oXv2{ z<`&l$5~atJQ3x2gT>;3EvYvovUN@YPE`K-r(2&=c$4_ksEtrfr_`dd%m|Y6&`QY5C zbky&)pi%d*iiT%Q%I94QC(WBtS<`LC>{HpCxRvEfgrCGi6yJAgnirmmZ_3BQQ)4)u z)49`-o}K%2IX=Iq{JMv$5LsB*OoxYxTD!upMC3$3{H^a-aImkpjUY~b)gfPtl@BTDfd4yoFV*%LT*G&QMB zrJ16Xc_#fZM?%c#A>#&4IDN=y$SfJGo0RryTrCjn=MFw&`=`278%oGLLuNzV|ilXDd^K=R1SHa!~HX znt6CNA5>D(H`Zy;@H1j%bLLc5$EMDi8$*?217}UDm@)<1r$)}5Gr6L=dd}SH&ao5n zdgt|s^~~?lC;y}#C&fBpb#%qV>Wa>>vyi2Gg=Xn?LbpkCC(fQcJvO)EycyLq=FE=u z?495Hq(0x4XP`S@)jQ}J360Ngnp51k1c#>H6Hh+Rxh#o8 zy5a&H4wAVmz60-o4;)7VNq4Y(cLtrmJP$c{b#$`&@?M|zoJiVz<6M<=sM5!Je?`9R zRvfqCDGFr(2SK!b)?5+l1>uh%BJx-w-XOYacrV4iip7e96*+{VyfKQR^9Mg&`8kT` zDK1gGPVpwiA1HG8LU|7=Zcuzokz)_WKd<(J!DehGKSg|b{lli(R_8?{i-vl7a zz6HEzlxY0vMDV4`k5_)8^5-bOP;s$_^KFs&L`M&l7jcw(hlb0T2K)oci*6qL6UskB z1pk8auMknctwda(?Hc~B;s+X^iN`$iWh>??;`CE=+;aUPFIU6)9z^*vW&?ImzLR3U zBKr)cD^x5}l=}eT!;~*mn zSCsn&>GwD1LtO#u^_t?FiaQklsraFyukCAdKIwBj$7uX|r#d+;52gi}HU{ zq=JO`-dFrsktz*_H&(Q{pu?3vO0m6SN5$@ny%ec&p`1aAHa9dz`EM)QT+wvpD-~^@ zmCYS3)o^~|mU6FCyg||SS*=l?Djuezc8vI_qA19~|3-Py!h!#z@>CKrJ#|9Fw-u=r zBA=mnl%lA|5MH2sp(2$&>|urlIHckHcFXKzbd(9ESVdoW=D=fGck;E%OSClcx`ZV7l-9_NVoT%|k=wG#4Hfwnv# zy|IvYuM=S8O2oPHK7u?X6?tg#guJz&nTKW3QH4(%m*yBpf3KpY>w!wK4a`|Oj>Slu zTZ43ijRCTq=^lc2>xWNsC)%$i;_P}n0~*z-0*HGP@=h`aXk~G%>Qvin1$hmb6YGHS zkj;r%xw(+Vy1DXd&}jT`nX_{F#x{3Wb%X82ozS~aZpW~Js-r#OSOI>lmM{p(~A!em`yJJ?W8@W){2ykJ( zd4>CpWJDyR2od267*ae*Lb5m$6DA%0Rt-a<8QoZHH0v>N$xq`vz!dBhB3%cPJQ0E- z`Dc>EUF<~)X5ff9t04%-L>9XwR)<_PB7!3kj%hQFVY=`5%SpFps_XnaKxal?#4)Q4 zGwk$zGT~;3jxO**b^cbcUL(I6vHmFt4RpUmpNr43XEWkdju{K!St0D};hfxhvXx|u z7|YRQ@Tr{sTS0OG5GTI;5|+0BTuvVl{s3mZ3TzYE4mE_aei;~iHYZ@lA{#!MBMg`2 zfW=31gxw9c=|7O;Z1~yCxs-!B*}diEE?@xb+}yStwebBjP<{H)7h(J@Fv`|3XGqJK ze78R8PBAswb5==>!)2$=Dy4mKCnt4Qsfjb^gw}7$Nv7t03!jrrx(SIhOSviAgPJ?r zgIc7{Ibon9jy)46oQO369V4<88BXLZ0O%MX;m{$Wx03BaZG;>{;QfcWq^epH$4Ph@ zf$!1?FPCYhp%OlZ!2b+BZnLPC1dGM@U|8ZpAx9EeI6`quxS+sMgdmT6?S}b+nIlq* z;4#-k2qc^VD&bmzg4&-DNRWT#dioixFLOUT#2|?4%^`+?sARxn=$C(fVPuz7b&l&O0Y69(`o~y72q?kbalyeoheA33@jhLNimKQ!Z z7H(D;ZZb8DPs}$d?9!{t3Eji9^THhqi@F@!Iou?M$%Y-FsogOVal?gEkM0)!)#AKR zIA!Jm%K7A%=EZtf@F|u9wNW-IT?e;}y#l`C`WKMT&egBY&OZO^P=w-m3URMYC0Qzw!?&KB~A$@j1ocDgIHB zFL*5PllEP(2MlEeIu)j)Bij8X_c{1h42N#R?yt|TleLcLkWN+_8wXq>aUF5w6Ug*Q zy@TMy(bDl5P1>GbOm{K7IbJ)uuJGtuydrqIN^}%{ZLUoatWEokP+ho_r`|MB9q_GA3L@NT*t5Y9F*$2j^8iWWXT zJ6JbqJlmO$eY0D?rOx@bM4VlZXF%hURsqDVfxL$3WLH9-t14vO=!nkgd9Og8>0~iv zk#dn$>Sq5TuwGZ(r>BxoXYvx`?uOdnI-%kF6u7qpE=4C>|G-M+E(TA;i1`J?!(D(8Go@Huw1;bTU);eLvXJCGNEsJ#1rW zVT+EA2UfM}ivp`!Rq=DKX4b_^dZ;d;*8FwZ6CsPq! zfH3~&J-?8xyPoV*WKUr%M>l~jt$rN17Ql;5tN|R>aOe@(#2U!I&h7BcIXv^@l*EB% z3yx~rf{8J>bTUe9S%#oKBsiiKJ6eTGw6c_kEklUr?f`;gOTX+^%T0B@@CPVOSg*|# z(ri!HL>3=*^bInR^=Us=vTuF$;aD@M|(qB zcE=;B=_jhX8*Cygl!+^ruG?;DTPRvJ#`jTLpeYaQ?NQs^+N@$7o>Z2j??};sH^4*| zt_XVU3#HdSJD}STd1V+*WfJO?`d zk8|>%;=i<-8xQ^^^ha~^!p)}^gRc|*8cq=8h1=c|o{|?XE8N#4fLK}zCIKod=T=Oc zFb^9MOerpEcQCRLTqX{ZT3^SqRl`kHInx1q?ivU*&nbF;wOaLw(+drsQ;NFo(QGNXSG`;Yp9XG1%@erI z#Zxpqt~f%mOykEYrmvB{K-0oLV&D{j{K7ZtY> z@y7EO5%qtEhZ};Q zQjyvV^0p^|Ygx(vOws1eY%jv|8va{Fn-6(ac`jpP{%wk~UJ*R?-VEn(pBPb0-Y3NxV-%?FBNN)}Nu~rGt7S|uND=!9lRk+V6k99G5 z>ULEfH7zuS2 z$2pD^d99M|vaflbrf95ACJboumZZsB19`kgtREgliTbSqZFxX?eIV~HC&0*+h;!?= z8S;3YD33Z{JKwdSnTKW3{TZ3~JT=ES`g;{E-FNY4XB(KabQ=&r+FTTs=X1{-%cXk= z-mTwDPWwSen5f?~pmCY10OAHfUPI=2-h{k{%=26fd3=tr&%zK>%FP9`ZqDuNZEm{n zc^>YaZje2yo;@49_I^6f0WM{pC$K{`NLJlwP3D}e3I+UCtAjg1k{{)Q2T+z$8 z@31)jMslHy`^kl5xjh!>bDx_md=D3&$@oatJA`P)eUOD!`TYRN`JbNP!W)n!l0Qt6 zMV_a$NEZJpC_-kXqLJ4nFmfG|WcZz#Q0+-^zQM~n5{b>8)B*%tD0Zk8Wvv5!LDuhZ zggvQ$P=?x*lFgSZ{aX>!5PMQG?nu~^;uMr$gd#C;=K;>=h@|gQ4>micKg^(}+zd zjtN@cg$Pywb0oh1V?Yc(!y|RbCe(gxLZSUI&I_g|WB>)5P{fOa@HWIH0i8p(b-AYi7{k|+w8>4_!iNLE?Pca5>|$WE9fiC_nc#PT3~ z72wW*#4+K5*ii(g2qr9&X-6>&E4!y1s32#lo3pewnX}Y#rrUwy`I#Lj(gpCa-!qgr zIG{%mDVX6Dc?-PdAx+tg1Bm0~URQX8olXp7R+kftB)obGaX>&%C(8R78gDFU(G9?k z6LE^9saga`xCkh<<3uvJQfCm_!24bqft40JPLx~a#2KX0g_vm!gi}rRno*+(C*D5}!xIytT#Z8LO zE54}svf^usI~6}xY>UQZd0&*>Bhme4V@^TK`7ROs8X|N?vU(yXuw%gRhcw>S#6PR~ zME8q&zNkE^pP~ze2;p-TIozXs89)K~Unbv4F<-H_VxeM@;$X#LisKc_6{jhlqgbQ3 zK=BgA%M`Cwyk7D9inWS&DE?USXNo^p{FS0yZ=46;L3uuW8z#P@xK(kRVx6MQZ6VzU z%JZd_`M9T@C|loweD@~bL9vr!cg0?ce9L9JL5ial&s3bCI9ZX4LYaP!;(Wym6fal2 zN|Eo)Okb;br{dj;Hb?Mt<*DOf`o|T2t@yu+e^C6B;@=eCP<&r;w_+wn-IOQxvVdZr z8%Vn*47WLgnDU(!yD1hZ7Alq~njNE&%G=x=6#$ewO>ws3T*dPhFI41mTBg5B(dHCB zX|7JRI!OPZrvH_q%-tcJJ%1?L@VN&2pS9BaDgFOu|FfHQs^b9Cm6?2iCf9`}B*-={p*#pIQOwmcwZUX0Hxb4E@V za-V1h9W>IiT!ve@>2ncxqf#-%n=^UT!@KfYL4nTy1?BB)F5=gaS75?`CT|I7S6&Cm z8;LN=gStFXzg3_u4@fVZ70jWMrn9(}pj~-2kZ0Eor=F0vR`c+Fp!+cr{VT2gDDz%; zqNTeNC&@N2$2j^8iWc4m4`=s$+0Jwi!MpWaHy0(bZuTEy!LQ&V@V{k_=Ss&RXVskf6?4@RP>+i?EuZr zmw#^0FXGs89odCsi~oV+JMfL{l!>oJpo*zheHuA-dr(|< za-%{BtT=Wa)bA5{zMwl0cfp6f3ttlAU5y~Ju_OYRaGgLw6;H^A4|^BY)RL$Qm?^a+ zRtL;+5^t39rK=+2UFftT)05^+Jv&Xp0R7DUOXB z5{frg;F!jBE`KflBq1-4$4~w3fysUUKYQN-UqyATKXYbIa`G^Qhk_LG06|0`2@u0W zgd~K+Lmoj=KuCoE;bkC^goi0ADj-^0@mZ^0gI=`MTK`;&y|l$*eYDhS|6pxz`)_-- zS1XoC3O+%+_5Xcq)}Avb1PDT@RQK=9`PSNxHGB5V?Ai0po;79FORDBADPI(wyP&e7 zCVKvgf%!el<}F?ttrd6QxS&Lh-|jNF_ZH{gSN1PMe4oWti!1t6l+RjFwqWt>N=&;i zTU0)K{({97)=4&7b5)I%=y|Bgg-$&B9QM5dG-CXF1Gswf;;0JJGFMuGyS+W zzC^*1N67Iget$X`9F*pL8OKW859Fc`>mRdft*kkTv9ytdgP|!^?sKO1R~)QZs2Ee6q&QWvLUEzua>Z*DZ&AEWk(X<(=i7?B+EV|q;xmf8A7lJZ zMd2W!|5RqO>cJ`<8kKMY+a9@2+}J#lDIcDoT4I-x$>=Dt=kZGSS19scnCn}i zxKeSA;!TR{74K9OjvwV7RDH9ev_Imd{ei}B&d)Uc1;v*Yf2;VK;v0$_{IgykD*jzD z#CAk{s$z!XnToQ1kZ$IK%YK0_H$%We%{NYQq9W&u(r&rpBE>5euU5QHk@p|W|8I)& zOaQt}8Ut?D_$`Xt6rWbyq4=WWuN4jFYdGEpjeke+1I7PUv@v#(_E5}FJX`S`#Vp11 z75ge)sAxFf0@X(d}4RVdd=~Bk=BR()2H^&Q(Lj3Hal@xbGpyk{u0^) zO(Xp$K;}*pO+fQX=z@RdGu%y{7w!Kuje%Say5VEyWB=Q9y&oga1nxJ6Vl2is^;{?m z0ue?mKpML(BEx&=>&ZXy9ubGTe`{L;*_453eG41NKEni)HVY9(W*WcmKHW4%AdUT2 zaJ`(U5~65~0+k6|FT)S81nbu+u)Q3OnEL%3GInV-K*CJe z8)Ouq+1}QW%`wm9-mv#y6f~H8#K7iR_ro5KRgP!OdQnvB7PR-Gj!D#_G>2UH?`GYu{5fvD(hO_m zvp#h=o@&OY4lmcfPn|j{Jdlp!Q-|ZDILpYZw8L&hed_QkdQ_h}BVqmMK6PXuq1=$A<>oVq+e&kDULnXELtfNzCfN}7uVE1mYf1L=%6AlO!hT|vB3M7Jqo+Q@XMyQvDjI*P}1 z8!k0W9Zf_1ptj}d!EU1?qbJY7+0$+sqBtUHHy(c+dbBH2;Y^eBEN-rr?hJvgU{8I> zlzxqcSo#13mZor~$*HB=bKL_`atKS$ll7+41WR%#A|)xDX>w^vWIx#??kRMJBm8hv zS2zK>7@bz@M7p?5B3<0Jsb`#a#xu^D#xu@Yo@bo)!JKGi2DB#@Zyq@hH^>~1`GFlJ z=uQF;3h~QM;e3goOvv`)SJYGBzCnJoi$ED)Q_nq0+f69reiAj2z*`ZI8>ZytaC5+po2?2s znK9>_*>Gf(VB)!vCeEB;P4k-gpuZfm!tw^&xQ$bECM8IVkcfxQX%H#_SRO zHt5~5ou{rnDx=&ok1_aCM?RdZ>qkU|CvMhFOv2#FnpJtla5Gajfjn-8fcDdc(LADy zn1IWf<6)rx8UA97+nvv)FZ%iAC*(2a6cf|w=+J^bV%YbYz&@`qqg~uX40ZOGpV2we zyl;w!c;C&q(}{;>yEo;b1D;Q5*>rMN@!*NU$xavab6jf(FpeynKodW-Zk6oub}-dFW`iZaKP z>!l8E;RlL8BI1VqY0W2dlcCETQ{?}>rvJC5i{A;Pf26vL!2!#s5Rt#F>f(0-dRNtZ z5wTwLEzF@BFFYd3%iK|*%x^)y%QXK2#YKuMwcHxTZz$fO>EBfRw&HgcWsWH9Z>J92 zq4<)fn{QpBSrd3m_6e*vxdUQUkwZP|y%cj4<@iB7M|n&it~gSWx2B9QRh+KK+f>HS zSFBXzEg$2rRa~ohv!WbdNWVvQIliEOSM?t&%Iz`Yf2sQaRg~ik@eQgs<<&pYc!&EN z`FJBqG;^!vIS2Hv8qYgRrspdbDvnSbt4MB;`KBmNSDd3bU$IKDM)4ZO>lJTO{JP@Z ziuWo$sQ9p=;oG;W&YN7;W2fS4if<_L?v?2uEAlRtdZr@pQK=i=T>78n`0;|~lXUjL z%r{f*w4;t&`wkj^Tyut-^5F1n^#u=(hD1Q~O6YjxDoZwgb0=cqJB72BP&MHato}gGpNiJ!tP^3@F$K zay!6M`1V#qHf11MZ^It*nPAdZAtGom9rw`29{X5Rzq=uuG7zngVQ)hq!icvcEoiS3 z>~Z^8zYHwK-b0XCCKn6C{mAsaChf=c?_mk%8=clN9(*hE8QZka^)mbbOR#=ff$fFp z*YD?$vFob=65fKnmhj+tFwznpJPVCWvh7$rcqxw4?xqqzwgp3AfHipP2!i z3(h+l_2&T(>^D#%TEK(5&G6vxJfFVr$<901#^%MW^DaN&PM@&n!mON+R==C`(VF+V z9dc%89dh&SVf(>@-y%GC(oVZL^PqiE+e6OWbwvkvoi%gcQ*&eR7_z-R8rIv@u3e1{ zO1}ad#0U3H17FgzoG-nGG zLAh{+#3Xa~JCX1#tjvid{{@ zRzb3x9_celi(|tLDGnN)dfbnEumBqq0cO!%TrJ2$uPHmu%L-U+0{6M{z-)X>wO(0I zfu|(m34=u}Aq>R=Zro#gX}P7`xR;l=o_^rkLso7*k$V+R;myv))MC~l=EV}ct;ns1 zD1ntJ!V+*I&Ujw40)9ql8k36&vO`I7wsVRPk0_YND7+&NPcNnIxFgB;YSU9_8B# zw3;axkMhlTLRv`D&4t}-Ra1n>RyILyL##`279Rf;>VIAOdjQ;Tph;#gs##DoW8Uo9 z+4I?`hfTA_v$8v@-J)DTnsVIiAP$>qJwOL37>MznnS3j5%LUI=Je!CCGVkR=$aI0m zqp0NLScK(3rJf7>^rr;kS|R71f2`hW@(hZ)y2Y zR6he3DwbvwF z;_>;jidl-i6b=8)fgkg6 zs7aLL3^-Et@rvmCB>ghQuPDw{T&Tz!JleThakb()#oHC{R(x3TJBt6I_@v@C#itd? zztH|K6<=2Tt)d*4NZ+G6?;Dx_e-$GKdQ*vQtD4BHs!;42g-CgKY+Z!r2c^- zZ!f9K^B-Um+a)=Eyg-$7b3Dz^a_ty@viR=-r#<~`eF^P>rjhmo{8Q(Fk7WfkaKAC| zJZAk&SOwur=rc?(X~PhKZpA9l1oDbAag6XrUh|wi(CuGO)i5){p$WX+Ma5{eA`+ zyS^GA;W^kFWE3FR$$;i(K0ow^5%$q0n0&;*=E(&t7m@7|Z`R9tux>$nxj0U{Ab?^n2^a9WUeCc#2K7`a=%9_j0QW4$(WZL+B8vk}P-scFD`@ib(F!-wA>34XQvPzxo?LsB=q zACb;frWn84cT#~r1cBs70P#8C+<50L{u#g8GNy8q`FIPfcwGv)GO@vRpMn-{j}+JQ ziJjzbLQJy#3I6Q@*;};`8SKbM{4d;s|AAN{ytaE@t{;Mw@+{TiOtmjf6}or?ZT$*M zs`$93Ms782KmI%)*UMxL&9kO)qVyl*KO&I`rT>TsxKTrLvxvjLBF~ zoG=rMIFO*gRW)HeOC)L{ftB%Q1Dk3!@f5-87%$9}SqZok9z%?I@&ux{b?e6%Go}oZ zB(%f-1z1pa9MQ8=Gm|KHMOMumBDX1o4J2~2AXiQX%AFCsH4|q^eB8SORO!(IEGnyt zd9;MHL@Xt+qT;QYmP@cGbxM(E4z`^z6AR`C+x2E+jxi&bO&knCy#j~s=h&-*?-=Czn9boe%*Rp#F|y zva7eu1~IIFU1_7#&B)K$bcLnh?P-{DM`@8#BGH|fDnnbeWvzZ@Oj zw{H{45k95|suOl4Jh zZ6CN3xft_L=S2I=o3p%p$=p8uqJ1XxkM^0nxT;T0?UDtvk)?7$dCh{w^AI^#y+-$` zsqnln zp7GEUZjj?ujybp=ns9^Oncb8eE3Lg?#Mzg6*W#eY-WsQ4X4;bl;cgER683A{|I`i4dN zAD9mPH7&PC^}nf}#H$_BIgTZsO+>kFsv}zhA0h38^>JTuy&V)gDP}32ugC!<^9@lP zt~gSW7jmYTD)PRRdb#34#l?!t6{X#f?|RkODoXny{tne2QhY@5dy4;|_+!QGioa5P zS@FLW|3^{U5B3{X|3HyLXx5vvA&AnhK#q&48(!-?)%iXmAJu6#uW{uNCEdio}l( zG>g=SJqqh}k>=z19>PInj-L~m&&gByi}{>vX|E3?_P~POfKjm*@)QkVH%MTAMw$DK zf%$4h^8U<~pctwt!<0=}dIlnlSb+E-k5P+0C5l82=CS*I9)st-DFe~k1?Ar*J`+sZDntbBy%Mm;KGoFkZpfw#L~AV)76c-UcstU9_TGR!ZXfH1 z?%TKb5M-9g#lrA6?1TceiwR8Q@;yb9F9*h&!H`!7=nXxfDs*gPv8l_)?Y#|M1=$n|DoVcpER0nr+Z zo!Xs(24hd=R4iDmUR^P7)A)7m*Zh3WyaBnnE#y2!N za$!km{jCy__y;a*5|Zfr6bazMq=z z#c8qC(46=A=Q&`d10K%-YbfH%+cIwQ|FXOtAw7l2h13$guC75;lKT_m z|8Ybx*fmfioX%?Jy#PImg$IyjU$~zC6H{1GJD+Lxr75=&2C$4hDRm{4AvB$eLrlfA zs-?9mmLf7epD7p9);g;H$5&sc`T;FTY8E6^lo^jOCGq5vYn&R`)IICr^qTT%9=R6a z+R~fh+HiW~{JdKFDmE|VEN+bnJU0WJnr&THhZyc;h}`om1QGud*q3z10ym=7054-H zGjhZSO9ZbBTUtWA%ruta<<7BITw70pYqPDXgln*-~<6IPJlj_ln z=TxjX($qKfM_&2~F>jyyxo)$omXe|JxvCcNkMV=^aHcSU0$VU|amAddaEd;IsD)4Y z%s7r&gIrja>0^NGa*CVtNs9Qwf%yB3z60J0Rf~ssm&&+P!GZC*#Ot3IsKrA|I52Wn z93Zg$eGV*sbj2Ak|Df^i`%dKj@4;9Xd9gypnBpYGsfrbfm5Mcr*D0=5lVC zjhFdF(7&v@%+G-?^NWywvF3;Q1m1(^3+u)GON=V=yrJGpF-MUjLdHw`0t;0ip~%4u z)1`fZGgP0gxKMGi;!?${6gkdh`CAn4Q2eIi2E~nv-&N$$l;x!Tfj?3GdBtBS?o@n5 zkpo$l+pYLF#dj5De<1zus)yN*&{Gs!D;mF~90@c1d_~S#pq{5lA7RvG{{fR|H#y$^ zP$ivbIr9zD{OzctBj}*7&@>7B5L7?lo=8F${4<}qr_S@D{ePx0 zoCq(q*)t*krgtuaW^6Oz4J_n7O)%n6MBp^F3N&G3D$X4wk}tvT_xXtNkWCq8y&N0( z1O*KyZ6h+zw(D7E=bIRgQ!x1q~+c3g|(5 zx1i5qpF(@sB;VfkkWCqg)^)JQd?uK*YY-8%cYnYh`z%wxJ0Y7gv~e2}t_VaJaXr$4 z_8x;hZXfHHfyLO{0GVZSu`qm$oluRoHi2ne*zW}MRl*qC$^?^dD^kQlXU12d#q9J5zmTD)(Td49rwg z?!lyOk=%#D9t{A?lzT9}k}MM#s&Lte9outaWg|9iFN=|#>K@BIn0icZ>Sb_KFQmk| zsX+%rN8+Yd9gUmfbC`5ePP||>4RLO2IFdXzs%H^I&{Q&EJ(MihQ#UN!lw30nHzgO~ zNH`s)@o}IBP$Mrno0`Z>ac=5KUws`?Iw^>MMQ)1CdlYUe%FVVeWAC}n6YmGG zP=K4_SRyJ>rL_PrV<|H}B(Wur0=zP%kR-&*Ok=l}FJ46=5d#k2AbHoiyL82d`keJNMu_D4v1d@Y^oiCP@D9*LV`m-7WT zm1=rj!Ipd{rwwv)eCh>i@o+rc6x%8+XI-d_ysrRwa015P5lda!24`*Q(JIOm|zCHMTo#{ogdzU z)DmuL6T0V?a8t7|zGi=Ff?3y0L>QTA?La8Bgqz~a%PrxiDzJf38rERe)s&lBAK+Ga z#WCCp?^sM3nplH`__ZaT#`}vPSF<5tkJor(?@q`;d-VC&5^icU>_t%aSlkrJ&z5jg z)iBl)Zt6AU!>jOg8} zc`2o^*Ai}OHFjzjV+?5Q`P`Ivz-s|FH88)$+*Cm;Sz*FEfSfOdc~gTe%w<}C(5jxX z=iaojJHxrZ$E+zk!#XeXpj(!Kc}$rHow082Q{bkS#h$`!sEcEn2a{GlT=cu`TfkLi z9t;atl>x458)iv87TbndQcl>LDK!N1ta`*Yfz{el^vd?RvFz9n_uDDEc10S#7U{Up zNj+=l3k|mYOmT;UcG}h=7deN-wqi{gv1dlu`)#{NZ2zuy)tzPb)a9|i>>h&IQ!CJ5 zUc0+?3FLG(=Es2sHDZW+KV1V?i}KCr+Cuo+^JGXDGyRLv!{53+5=b$v!^&c`)CZ+Dx{|` zWhP;$cm}2Ok&>tKAWr|1uU-lD=(DFzz@b|+pSC{vv!~btaP}0D4cwHWVy}ofPlTw5 zQ&$0Q@x%uJECdP(|9-d`5?-St@WAojEu`e-CH*HM6aP^x;E9$2Jfl-+bd$uE#>@DO z5zDm062?5He%zMYWB z6>^Wm9KoyaG)R7kLLyrgukd7=VL^C1Gv%2hZh*(OAEJ<$JPWuA3+S$E{q-^daS}S= zAICU?EC2jsg@Pfddvuq0w>a+aew_YOvDCrYiLhPSy(X-2GP5}!tL^%E6Bj#|{gzX) z7JS`lGqpuivFNePW4l_;iLl<_iCENo0njJPgj|VV_{_U*Cut_v;V#Th1zGmFCCNTD z>vgJ`E?nNw!5oR}Vt>=@x1kaLS)PZL5$rSr{VAhQ#XPLmSP!pUUf|d(9$Lb6@iNDu z2HQW8>q_)eCR7(549eUTjET4}$yagx#4{DoCSu+|cOo)fpz$aw`GzWvP#mjxy6{{s z8jEwRWUddk`z-1}c?S`gt@?1yH%fJJ8;WvMR4>Kv!j9`6B&S1Vqpc(dX+6y^Lm%G|Djnvdt@Nt)Yr+I!HFyCSp)+m0d61OML} z%X;_sXg=}{5}M{?8pDaq>k5vG&FHxhHjQoCe;donK!g!5Mm)Lzs{jkbd#RQ+6NzU* z#-Sru58iH$!(z%nwB}<2pP-<@q;Vdlk(tyH8~#8b!idw67F=&BI+fRu$n|o4+z#Sm zET#-ZYbz3>6f~H$h0ufc@^BGk|3rJ(B;Ve3kWCqg*6(01eyxh9aneW7UU9%4`#)2^ zJ0J(`y@iD4xHI-mL3^dJ$L(YNAo})rLqvPISQsYZ0K38*OwgIeuhLlcc?sKdTJ0l&py=~WFZIXA?_M{k?J!|Ur&5iTwt}EKt zII^yB7rb)S37dA~_}J^anezVbnP^0BU%2u`g!CG0?O$b^6aO&ZYa%jY+SLU}z$CAp z)I!PRIznz(Xt0MU8^2t`s6@izgff!+3uy64UQa@E`onU0rB==ISO8c$Y6g3YUC@(YhSbldK3!}BM@RFOz_s>9Kyv+!&`^X z5Qef0c(dnA{zz!Q+|8pikUz=u)v*5~tHGTCc2;#mP4^n}~7ummVCie9x!X#ZzeK$;P}8kRuv zgIB{`q1U`ZlOI8%94OXgQ=TkV+`weNGz4Q{736hT@RB8gb%Il_mGwarh?C%zp~yz} z0_zqGL47t3uF&W8okY(y4O-ySGGRmUPGa^s6P9Df*W4Uue&{?KBv|dZ=bbiVa{TuZ z9m}@k9;&=4T>X^$3S4iRrL<09YFCSCHLn#u3okH!ZW*t*)2-8(`-%qo72X9IYo|IhpStj-owqKfK=~PE#yboUgb{@vDlwt}#FFbBMfN68}wcqvCfIw<$hL z#EqumUVcd(qqN^>`m3tHq56KszbmHk@`v>p&P$#Mp}cS^i06=y_J(P`(W;MEo%2MP z&cJ%`qDxFyhN;x5JCDgIIMb;aF^A1HpL7-GA@j`$P-irZXZ zYmM)q*h#VJ`-U77v%K+VCEO%*!*$92fo{02>6+j8bC=`(D4f;Fe;<)2+!ya7V)skH z?vQXIycPR76L{V+6k##8B?8KvK!g#=+4Hn5kW6@vje8IB+Gjz>VdL`^;~|?e%u4Sx zPf*Zc(q=+8GLy~%*}%`}nP9{+L zY{3$&U(hcYufV2$&qKy8tp-TQLZO!MR`d(TYmo`2PQ<|GSu^<;Sg||k|h^>$Hr}uYzNDLpp>oyVou`>Hk zJkaUw+s~eOpu>y98{VvG(~uFGc%WV7@P7n5bZ`a&@HdE{&Z{n+{$(dlC?4+F~ z2i!~AApVUSd(eShnX$8D7sMLcfAZnqKiP2r-}`uL-*sHWTkv`1eYB2sa&2CZ(|Mrt zLf8wm3CMO6ZE~p&6H){7JObUVV-X?=CWG;N5XQci6rlU7H6M+ z%xqmziNoAqIrL%W3|t_EjVCN|J84JG!>$J>^&O_UE@`ju6#Th2+v_RwqUYXf^O~IG zzJmW`yBPmAAMAM3c8Ip;5$oc&AxE+Bv;qUgEBjScAEY{rsSjaE5zq03uR;7RmQ;2e zb`dl4ab3!S#eLyAp6mL(r{Y_pqPnOmQv0i#<*9k97DI)dX1~sP5?x{4U4*`;sUxgJ z$MWFU*4G4fLZF=pokW~S7>fnY%&VI^iD%a>PU0!d!h)haG-w$j z{{4^%33Iv#%mHuc@r>NterN~Dwk{=j8K)AKd9eh~_*6)KV0BZi2V{3Ml6qGxN!F!z zxRdU1FMymK(DNblU^nT!^c4hj9$H+1N$2Lx4d|>9s}Wo$R0*sE*N;hQ1UX8rQbG(1 zNUCe)Q<`j2kXHn@lagr^xiYjA;c_f!_iO7Z%<lfE*LUTM-9Cntb^If)zgY9ye!6p8y6ut!bruV z9B)|TM>sNC_f_P_4OAdF76#mb;8?8!8tu0hKeX{Wv~7~!RBzKH{Y<&wnMm3>4-5Kq zxk$!&w|KAO5pOeQh}*Rpm2(y>TU0g2`&zJO!h8snPsOT|fG3lW<7Jrq)+9w<=&4sI zE>v8u$ooI0-=cV%;{A%>R@|b<@h|f~qxcKOor;Es`?KorDIOx?M*9;*hZjZM1DQE! zZB-W@2=+O|W_k}I^uDSWD2~wh398GSCgfkFI_Ivi{MQt*T3J7jKGuWh7m@pu7*)(t z?4@`lzHYdtk5nXYKs%QzPFI|vI9E~HANe?9XMWBbA+AK6#iDZhlaz;5!dBzcg70ZlBS3;vl8ypDvX`IyGg z0}F3-j+HMLd77?wCE`rrzGLW(1znLXU5wa-*(Yg%wn9l^0b^{`U_BICW z;S%E8`xazVhBg+!UUOXLT}TVs<2O6aeuL=Sdl)k9@pxd^iJj22{g}QPOE6y^j90OM z2BvX&N>QV|Ic`P)vRxQ{h$UFRZGr9OsK&J43y^UvR|6!xf`Z@USvdacm=!eG*4cTrc+z>lUk#tk(PR8s^U2gQ4m6kY2?f?YiU8{&(B1+V@mVZ2xX~)+DQO?X$5_>Fo5Ml!(MX z@RQw;gh^@_Bf&wlSs~;1><|iKO57YKv}3|B8A7<>43u!zGNy15|J`I6V?@I8D6^>x zG>+e#clhVIK;w1Y>0QGAFb(C-f(R;-pQ?l@c`Ro~KH$nD?zK#F-M)ZuFZ{t9+Iq?Z zDBo<~2Qd6(XUNJ=(l~=1$$?SZ=O?-Q!B0NI|A{Hf3BpdAuUZzqhNx;RX_qjqh#9#G z`N-wS*B~X!5%Ldb8^75c+jzL!{eY+aV|~?YrXs{X1k#ostaFirZ8ml>jFG?axE(-eT$0Vp;O)e*p5d&LEAcg4Q z9RR7vKq&g@M~Velz~>RXYSr4pmU`8?9MKZEz1$qIcLZ(_m^%DR;4N%OB-4R;+vTNt z|1@Yq84pV9bIYEcsEabPXVK-o&Ks4Jo8zp=b~+9j)~nk&PFmC%KPHsuGGCY~@zd2D zAqL+iL6>AFN{jsy$uMwSvXzI?VPDW-9{ETTP};(L1+u9N{No^4<@i12FKF_<{z$wT zJD0+YcB{i{+_lb)WX+(nd;5&~$7cr}5AjZuai?N_8pn|FI&fqc4=tIW=3QL8PQD3G z*3=1^@M82r#_Q9G@L}AYq9U(V)MqPJD{^?k_-hnzQRH}v@&9Z-tUE7; zs0SYjlh-O#{SwtLQ$%$Wcmtjf~ zFIQdK6MC)cydS3hRf@cqqy9BT4*00oD?Y0DxFUzqOy|80@oB~96kk%@rT9n1*A#av zHYmQQctDXuOxl&fG_dKrmvTHN$J-CZBtOqamOClDOcPG#U%^?}m(V_FS~;Hq33G9% zdjr8!^S&dGSrfS57=~kE{Y+R5fpY~-Fyb&o@J6OU6Lvx=KqC7#?0)|qdje!rhFM>S z^6wNhn6z@}MrP7lGz8CW6O737D!AUvH1wlL z+M9`vAro=5$NNxY?*_=G3`Fafu*ZBRm^9uh2JKY`?6J==_Q;`_GPLmr*qa%MFe3Ze zpuJTv!tG=IGO!qX4?<>{Tr3P1VJ9@lwLFHjV7_{s!&Oqk`(qjx_A5jaGO6uH_!s`>iPiG<71HbA`z(SwBRQBQxve{$bs) zUOzmBh;F_Xw-@JHJZE9Y=2|Xn@%xS&aeYEa;99`6r0;w3+?~m>xnNqR9B`-GLx+E~ z=DiW`CU?U!zFTokvD46f_I|J|w~%EChj)gHGY{IAY%1zzc$VsYPtA_)OW;|G(FW-p zps>Sl8u(%5xc@G3tcA%_h=czwPQ`-%uC>fj#6RI#;L2+d5<;68Q@Ds(-DF ztAZ=9=b^=2d2MCJvCQDP@*?l!@hl-y{~pf*2VO-~+~h7ywj*7rMcfBj&UGgN!r#ZA zyUOEPE~A{tv+y~K@+@2%gB_tqvLo><-(rr#d6ugXoyP6*c$ObQit{Y|s0&h>oz-=6 zU3B534iGE2xTXYD>^08nItI@oS7balBeF@F@+|BvK$Q@9f`E%4oR7t^manX*knP12 z28&oiAnAhnUf0%Bpyein3oVjaA$E}DNrbKmSF`9PS${&YqmSc}s^;G=U3OV4@TWi!Wv^MUO<+oO1d_=2aYte_{7B{50ZU z@c1hb;@u`APX+(NUYuh)FHnnzmhdlUB0I0_Z1*PoOB2UfUYAGfa&?X9{1}YqS|g6U z18ot{CgNVCJ2AcGzOg21`7ihvv>WbOve+KLUW$f);k8|O1;yctBNfLhUaH6;Bl8;` zX0GZB6*=x>`qheS6mL>ouXv|oQ$FSqjek^;14r8XiQ+F5Us8NU@pp=^D!#7xw&Fg; zLyDg$n)d@!IsCwS&Q#?1nmUK7MEPDMP>$E5@H2xnKZjPIdfrSEe&)1yMuoQcEb%k! zpG+{lY!M=`>-?|^Qgi$a$LhTIEx^L?29yF6JPR@o51%*Sc@wnvDwKamoCzjvCL)Z? zwCgcu=8Hfk7_kfy!S!aLPpn2F*Ng7cUoSZaQwE~-lw(;@3K~q>70`qB?nK|pzMuAZ zH_LXR-RrTKG7zml!Cw3t5l_1YX+e7%1NI>L_ITb0?d^p<&eb!)i0mhV_O`$X>%;nG zU@`UE0GVZSu`o=+PMCo){AJ{x=J?n?aWAQUjqq1F^vF8tS z`3^rEQXP|@8E|6$sB*V_J<^A7^f|p_ukJmouI*m8&K}Yh9;h6|4QRZvZqnW)Ea8O% z8k6dlH}=5tNL>Tins6rA8YjGSGkizc7cEE5gS(O&GdpgDzo^`8KX1Ho;WK-!h1DZp z-D}tVbvHM4BHG1kE7$G@Ii1{1eV`GHhL&$TcwqD}lo zjX{*n$*q=E$v*@;LdwV<#zSu)79M__C>)wevL295gK}3a^dm*= z=gH%VUai(oAkvH}1JMsO5@2(@ihh~EJ`uBJ2>Dnr)5n&I7C=$XhTPLFH!3}p2*Ed{7is^K;Bx@PS`%0Z<@X$o?bJ*ne>%`bk;~hC;W#* zGW~A+bntQ{>N4y5b328{bHjqO^wWBq`l-b^JS)i@r9?;P=Jd|Vah7H~=Wr%ZFU;h* zaOL!yi z`sMZHiM)|_egzy$kvKx6-iKoZl#W$}a6=%x5m0y|pzuaWl#v>Eq~4mZzak&Nuw0=c zuZq;iDNa)?S7iG#eVHOhdDPb_-l}-F;=d_wRQ!(OHpOR&xL5hP;xCC94DQnOKdAni z>U$N%XE*Z6du&+0yvGJz-d_XCduzY~Tn1=Q-d6)MaDBXv5~baM+;7x7D9RNVdY062EHTs`2JAZ*6n$*k^>zoAH%l-XT%=H7Y9GNXzTXwh-)|YNr~hCg-F@D zeL22T$gdG@-QGJ!zv;i;mC}%UcBg$#db^!3HrVNx9dOe&!1lb@hN5(Q?{Mq(XsmpM z8+&J0=FD^D%Y##5yLU(N-VQBJgOPA`I$_P~xJo`Sm@DguxWl+c zH#rN*STXrEFk;^lqb^4v;fb_$bHcX=%aF_NhZ4BR@ejeSff|{@g#Ir;k77w-Z(`@I z=l{eMo{si~OoIpYt%Q7*fd}>ZEOilw3pm1z=y_0=*iuBMS1@G=ZLOoq1KU$yr+N-e zNGdnv=wBW@xdhqC|MDPP4P2P?#WJ?CuFgV~A9!vi@M4$MA!fD2UUm%CMy+#LG-$nOZF3E}a6`26yQM9+_clCEcY{^h}r0-lJG- zYZ}2bT?WYytPbZ>%>{aerhEgUSDz`SJafbi@ObruF~G{PBKTO~%KBhAlT!xKqPQl? zKW0q?D`)~=2L61`ebOHMgxoge!$-Q!?*z8a?pe41p3)zycjmtXKj~Aa-{-U$lK8Q} zMdd3dmsj?~_xq}5UomMxT}5C*lgDJWjQ#N|{*RmZl)!VHithm)HU}zBu6;h{J$avt z{M4*gU`%3p$5~%ESr|TiaP)l687r--s;r65ty&T-ymVZ2#G+XhbLLdciIyy>nq5&- zQ?;a~XY|7CyzIWwez|?~a|iVu6g?jfG%Ct#DtbnzqexDL7U^?gpIJ-F7tfv_T~e`Z zLCu1y#nFCwxp{-~G1yK=pAzJ`vP>TXWS3K%k+nKyO=NBIjY;dmH@P=EU;FI8m)0t< z4e^nz7kMi9Enb&+{qq8~cxVZ~MNWzX1h#*i-|~Bq0KMhK(Rvb=6L~LfFw0{sUZ@yT zoTNBaks}=Dlc5|?cp2bz8oySN11aWvK=ENk-k&i3`-(qO^#)DBlhZM?0L*PLz2#z(VRMAJg~})p_sBe4M+%^f{^vufm%P+DAQj zVJ7l?A#(o`ql!FlsP|ILQOsAA_CLV06m|;0-U*HVYXDcpLT&%cM@hU}*GgREBrTwA*MD^ztf1$Wj@fAf5WLcgM%!z+fd{_5cIah@D+f2>CGoBeZ==TqqkcG}ZYpQ-s& zo4M08aKAAWVZpBU!yAyuWtw2bp@`sVS)d6UQ*rJfk^B%251$_z580Gq*2{74^#lbC zCT%8kBQq(}@#cY0W-w{IGYGDie$v+?k?ZBX5VwO^g~gPCXq6)&NyH6F*Fr6rPnDY&m2m9u`)%NkIw~&}O4)qSDj2;p)n}%14@Szs zOeN(WOxhO7eHiS~0I*EC2h%IbGJ&BAmyOuEJtxNhvKZN^?y=m1smJ7|UIsVyLQ0&Q z8gwvpByMWe(YPsI2ZWpYi)qOK#s2e43pn`#F&Dp94i z054-HGd?7-C65BUGNq6t#LG-$nH5-so1(zAftw;+k43pDik|5*+It)exG92Xx(t#Z zqL9cuEd+XnrrZvZ1@S_mDbE~n13Vc05QW5KR*n@BZYo#~aT0i7;Fv}*U2o!rlqsyB z37>Cn>O`F)1-YpSa@^dMxDfKFiI#IyQt_Z0C7(^2J->Vj=&vh=8{a#A3P~Q1 z{78sup#!B;#863Vd@Ai) zQ~ZLPLOX`zzLfIWF3`W=ro47I4Y{d*1y@X;DQ;>D&IuFDfVT({xV!ViTaa49O>IK= z+!AhT7RJ}XA+%Z7OsvPqTzNYX3VcV&1S9g!AjpOAGSCukssbArHO7F(UQ=#reSllx z6^HE-!NNNhQwE}Z6EJ>liKp@YB3Qo-0eeT|rtZRuTEb0jhCSBjSlkrJ^!~tT18D6z5A}-qc_VbD7p3w5n(9xi@X>&T#JUF>A`s zu+Gao=$2(*9#iH)XRMq16u7Bnv8ON(>f%`D!K9TB7j4?U1zc6;!LV>u8Q`k6A@#A? zHq4T8!rn}&A(&^?BeoT+)|R5jw$F`a$9}lqPT939((tuN$9+!fSvy~7u=u3I{#}@`KMH%>9;t&abpDv&lN+v%5XHOSH3q8!-!cW0N6Cdor*%Rm8!r9X+ z(BjUXo@U1LnZa}RME~B7%$1VG`D z!B=?@r+>*;uY`K^*;6OiDb&fIJ;fe?v!{q`;HC@}dqpr*gs6y9R{?JE#0LN@1PTfN zez+MDzFbG(?sKho?TPi2yu1@3Nyx;16bpEwWdP6U6dK(mv8C}cK4Zi(?XZL~kEvhx zdWumjwl$q5>aaN0RoB*2ScL`L8DTXRmcYa-ir$*0nKeaN!M>f4$Q5#r!yLh@?=(n$ zh(aP;6|eAQnqfgW@HXX{BW_?a68#W`#N=7PRaiiGUF)xx35b)>9NonV1w&Bx=q{Pf zZy(Ay18=(7JUX%1$0A>>*@MffmsHJLQoblUcR^)EP4xT~1M_>9&0D-QS}Suu&;duy zB#tu7^^ln!SN1PMe4oWti!1t6l+RjFwqWt>%B6EE$`+N+p1)vm#mP2Xb5)J{d>ZYo z2oJTEESiA{D>JS6}HrGI0u1&+Pqp(R`wFLUh7+5U-KR}=SC z(iI**&vgOplGhrbI8a za@FPSe56;aj^;?1>%((~{1x{xF{+rQ*h`V4T&CwMep#Xl&%s`#ej9>w<+4=VEDrCr%Cz^1;#&(ZWQ ziX3J$znOa_zTBY~X#7aUOB7|lA$_vymn+UvL`z*xc(2+YO)@vLVQwE~N*K(eq zpuwbZ9;K0))Dau>Kp?`1(~%ZjZz(#J*O18da(&zm;$keO3`A=y5~36|n6!n^gZA=p z5oG^Fd)Or3-gS^o8Hm>JU@v~Hil<$Tw4lA>fIaqqrhcRcg7)4*LUY_1`=+41QrP46 zv3?MJd%Pi{y<98|ldz+&Fb5NKrg3>x(d7FHj8!4d1e5PS5kc96TvTG9QGnbIhVNqu z){pLoP5bc*ZnpP1$k?UT015L^s3qK4CG5Ro3IR=>h=I-XzG;WZ_J}v@Wj$Cob8bMi zUcpX1mx2ak&kX+QkpOP8j?JB&pr0&oXCudT#+!!eJNH_3Gxpq-G8yk1?ufZr;LY~B z3peg}QcK`9YvcY<#+Jt23p4kI>Pq(7)8=)~%-!FZ`O%K;`x;|)JGM`bz1R3=9b-lz zW^UamaBS~2{@jlliI|~?`HRM!7h}GPI=+AC?swfCyV4s{I-R-Cu{!LGble8tTUeXq z-LX9>24>It-S*9m^XjfE+6O;ejk~T#ZG}y{aeVCc-As9Z_e?aRw=Y~f337U`CMUUR zY|QuSh$KwAx&Tpl7x5jI3?-B6z$C9Q(wrK~g=8`?k!u(s5j zJ)Xz-te>z7GIP@37PCI%$>WLMy4FvCD1qhJu3>8_`7ny!`h4C?CVQ<$JSzaEj=q%=UnXq#vEXRzmxjD}K z(0MjUu-b9YJ8j0~I2*4W;{3wO4mn^K4}Txg6<)U(rAx!!D*MBf2`WCV6erjp23fP0r1H1M#!`UA$I-`UksDMxP4ai5AN+)T4@7ioFzb6!R4aD;6q_P~?D- zcBU%MRGgz&saUPJLUESyY zAP4JrBtKctBOi8YwK0)}LM`E~=qHQUA`?uVh=I+sX2Kq?ImhCy*5f$sVvGTey_~*- z@}q%pEdTEL1M+jd?8kl|aTO0voEBdDoe!UzXIY%{wfd5qHc!~I^YR$9ize)`vL_wLdb)K387JGCu;K>}e%*d!hwBa{*btRbVnus~hyKw1rN3G?P1y6(tE_KL*z@#?j6=@5 z4<1SyVSj7%o|2TLL-v=qHn?u+P+I=uheF*JG=#(bi#K-oO5q!=8rm1{8Q~)T`;Q-T zyTQtL|0I^){_6k4+(Rj)-HQM8*Q60%-d?f2$UPjn!;+inJ6m-tF4!~N`hEM%hIFg2 zAiROeo_oE^IFl7_Uj zp))%+L{c{%vgSS1;5ZZZIWF?0S%<>Qmvy}1kUL^tgOmJDs|kCaTVdaKuzk^DIdgyp!8 z&nTX&S`KQP{x(-}F}LYnsI54n02%xEXGS&q$tDjkX);|6CGhE)lVZj0U1%1TS4kbIs+h-AYj z`rx#S!adSQw~eN8?jXM*$&W@#{?u%HSi0m$>yh5tZetfD;R^=LmKONbB(7TN_|zl~ z!FUMj8Hrz>%!sSJ#I-ud?%S5urlwsc>&y&WeWhIMU<%sBwnw+++S+zOb#ua)*B4*U zu=FA6Y%K&HyM}3%0~7Di63K}>*3~;iXaj{)7l}OLB!Q((xBvq9SOU{IC$WTg3Vx6y z)0#+NhLCmJC_fECS1h)5DM6FRnq*d#c2jXJy~Y}u)oEZ>J#`-3X<@sns0?RMMf=QqR77Sar6(pTo zPmu*s9aBzIq#*uSwiXN4LeR3sTJ{=Q|I|QPrXZgLcAM-uu%ijLi#VOY%}BDA!}ngN zddh5oB)khvo$@Ly_`(E%wjqcN1`A0%<5FnSZ>8RSoaF6>xTnA>{)H`zjH#ioyhWA zENzkPFzZ?ezcWg*O73tc-r>e8>**$+>6enM(mUKQ-(g0I%EmL7A0-EE0)BTE}mTrH(yOBZk|39PGyQ|&4R@?jS&sOpH=aJmC z^RwrpK@R8I(eclyshxxCKQf?gnSFNEqD57UXVjL@!US8d98L>qU1tDEcjwCJ`A$Z* zvoPv(h&oqg_vTF0Hql;AO14v%-RDAQdA5_5?JUdA8<>R%CpG(mJYZIC@18mRI*;nt zd&834Z;b8T_5R%c1BTw=hMaqyH(cBK+sayJ`Ss2j(VpFUb$4pAAzjd?e=N6a@1ecN zj>?^b5?y=0(7Ee$l(^A(qYH{0SXr9`7woo;4am>KtX9ueVEhBBweu@#%bT{0RXx0_ z1}+M%>PZV06BjI=Q?X)r;%8c$IW%OBYhnvG_uN3NP4eCqPMGN0%&b_csnpz?1uL&! z>!jSkyx0UYwH1r11K&$cNE<%5Y~+I4QA=l)!JA#5nyR_A%gdKk%osjy(&$MuF3c{T zRI<2Yd6}qXP|DyLZ(+sk+L|)>(6g@0?~f)#Q}*u%{9Haqs9aDzLziDYg0%i{lRe}%nGF9~F)+3Y;oqKr|FW2a_h6{UTa zpBrFQ76`6zIrMexh4{sSx+Bb^CW+3`Sv)wxMg(;bM1kN~AQS?@v04Q*+HWoE&-7yx zYv+g;Za)$E+r$+}4zAH>199z={le!6Jp(z!E<5PNE-3zNArPNMo4kdu# zJ2*V!w>4TT^7#dIu0Mpt?nG=Zj}^uXHx4XRokLE>Pf#pXELWVXSfj|hHRfNVc(dX+ z74KL4w&D|tPbt2jD4aL!{7&^(72i~BQ2bCag98EN7oUbe@l}Ko`~b!Sg~Lv@EV+3^ zy7`tzsg{%ZnMjvM^+;dDc<4(so%8ajU#I%lR9~<9y{d0g{DI;iCCYwWyihTw zI7V@z;+GY9SIlzcM2SljIjW|x0= zH!8|;0Xw|kr@bF5KBM?E#a)WOQT(IgYl?E*p!|ob|6MVp$Hy6ptrev|K>kjulSSbA z`zsDs9I7}%k@NubeOd7`#R|m*ihO67`Dzu(@K9f?NScEB1ByRXd`j`xivOkfk>cMK zNoBBnH^m&q{)%KYn0|?35|0aX_vZMAD(U9@kn%Q&4DsAi{|EAT4Nb9qgI?#?h6rw+S-KLfoyWHe(M9<%h8>w-%F6Oo2mg4Hp1Q@ zqX5nJwuT(o?bZg^`%^Rdm_M+2)@!g=f=FJs&3d_iSht|PTtwz0&IGgGoPK>T3_PMb z=G^N1L3smP`iyD_4<;}K-Z|_&qk463@xslGk!i2)9bPxH@w|maW9lKj-}v;x+%23b z{bb|QsiU^QH9+P3#)sCD=E#f`|o8j-!`ki)9K$80t{3r7-Vo%4v zJqG!`XAnf2!jfC=*jVVkPzj=3}p(wgAsPL}MGJ za)4=<+=71&fl~YU>P;w>#%C)NS(4ZNw7GbGBH9(y_-sYAE;wn^=9sh+XsJAO?Mqp3 zHPm!IY7x!leoEuB7E#-v*G_%f&-)rw>x`ZF`!CcbgAIZmDmR7UrEee#PffVx|FEYf zM|dj2hbQJSNUCg|uj1O37Jm%Fha`Mv!Y3_wX3`0n&oB==GR(t{R?R-_kQ)cMV<1Mb zjd-RJA!d0pn?gbz{{N1JQ^bi(Z?xVo==okx7bnDzJNx2AK@q7e-sgPVI;*XprA4_v z>4!)E7}e9LG(_a^~IVjer6wK9&7z2f|9q!WnS;JTk4wA zZq+|;v-)K%vATAXQ0~ZtstkHWL3hnC;{YaZ0ntU9u>*XW!hOdA)L-_Sw$7>|Xuw05a!- zzBznyS5Nvge-zOJRMc z-!me25)a<+nPP-^KL{aJweU$2)&BpX;inA2wDw2F*`fG~26&n=g z`6beAurka?c82(M#rqWN6}KvGQ#8+QCV;tNxhaa%70Ku@zDjYq;#U=KP`pv`R>eCM z$pNu^cf~%6eHDlNfA+ovz>4BpyZbIPmthzN1_9Z;Yyz^)u&Wq{ZPZ>Uv zzw~~Mjn^md*HHZCaQm72IPcf^Eb$K2)g0RszH6v74qNr&MTUP*!!SI^kHZm*ZhLvZ z920o53gP1V{sMiQ;K?~+)+Mhm|NYu<+`mu5-#f2wAq1YLUo$9pm5SuN-#qZ~bl0Op za6Q@_rc1d`_cYKx4#?oIXv4E4ggb#37|j6zWw?S_%QF+`VTnxeZO0y?!iUx*IM!oVCRQUNzNWIyLNQ&-5O-VU8+== z1zpT~|FFfW|ASXX9T5=kaaO|vuZ+w(BvCjQT#|2?630&FZz^5PU@5mLP$u1+31kYt z3cuXmKi7jxhVV)D8pTMNl)W5E71~yYVa9p@nbdmvrBdSoX@*N(6tZtu&?VK_UMdw1 zE5|#k!rK_Eb5J;}0xOHer3#1j3sOzR*~ZyHs?@!T>RennzBWiTlV|I~@r!~~b8+no z$IlB=W#Y0yBWkZeR*NoSGC}%cK&nOI-GIg+zf2eQgfFM;;C)sQvbR$_EeLy&2qyuL_ptUN#U+?0|tX?KR^2X3C+fK7Qc&ttPm zGA8Gw64O|;44GOsS2oMbGz-NUA=7{b75K zDdYPollP7-#4rvbXLJAbAnXG&JpzXCHEViL7#tKdDV)$KQIu((Y7rh1wo0}R4-MOd zZNqk9hXjYt2e`N_73_UC9sqkb#aTS}?XHSs$$AzqcmSc705h7H^Suiv`bX>^!|^t{ z401V&f@I9c@$6~k9PPf_g5_DVT%-MB^4oyiOieu>YT_e4fkz9k5$&~US43nv5Dz6n zc~>GH9K9s(FFZ;(OjsqHCOknnTewhY9S7(?L;NP;<-)6lUliUf{C~oig#B>x%%{zt z0ISHSF@6x*`4ze{rksw*D@(L^z!u~&Kf>!_;rgsReJNHh1$|c5t#G)|Q#8#C`plbM zH#_JvXC)STMYfvti}#>Mle1UMUbdhHifH&buV&7w1+xy6EaCSBSL6d-B84kM3Z{j;vFR z6HgbHGIIVd90Q5hP0vaki(&##j)Xiu78sDh{1Rlm#;gL=2T?E`hmQj?_*M|I z^|2S~^oiFRpI@80L%lV|jahp99S}FaEIao zHZ+F&y21g>Z9=%LXaduvyB$Y7e?w#E%k%d6<5({~hU47Q!HnUmq3;RA^^WI2M~t0b z@LT2&Lbicy51B60XWo!*T0}Q%joe+Be zozFkz{BvtZpN>4c^GJnz;9tBSf{QF*{U40q{>ZRlrV~7pC*dd2V>o{k=^gY)6p~A# z959qSk-w?*^Ylm+@?v8XhFu1QY+Q0fx%uFdkMSovn7>c+r`KfsVpR7V7yp0Ag_$3c z%cN$IOQqTYFt~v(_pR=uFk?Ju~WQ8dB}3LiI)R|1g}g*Khu zIb&nGq}dTBI+xg(E+wwi#&l_M&4WhN$5lH;H&RuQ9-e@vg2JDGYaEt?(U^`&a7?!v zVQoxD!p3xr{hu?Yv!cWac^BtD8PoB*s(oxh^hY1lwMe9*F?FJF;GhcG*8>71H*^I$20rP_0AR<2yWVr7pkK5W&e!ai+Peq_aw+3x6o z*34d6(<6H#qV)COzv?}(_Z)Oc=Pt~ys9C*eC3ZK+_8U+>;K)Irv3|pQjrG`ad@|N^ z?I^zo{r4K{v0NJ(>*aj~x?TlhF6?oR$n5KVl(8O%?Tj}{SS_3)JXu&HTq0yAf$`22 zZWL}2eo6Rc;T^(n2<;s_;@dlT;14DLi_pe=kWa&NnCbX;&9>$i9ez9SLf410u^r<< z&VC*7A2tU3FkcA%WDIEia9%4q_HW}o4?jj2139}}-VFtDR!rc@k&ws75+^~~KFB!M z!6lEz64OEZI3R=n2J>Ger`9PsgU`5jnxW|=O}hvz}e9|LmC z#&F(weXAgd>zfzT_kLqQu7^FCF<>3^F+a>7Zk;H9`D4JBG4SEOY2Gn^JZ==0%VQWW z;K7UmX$O#19D={T`D4H{W9Q5A@#S|P==d0L2lO4x81NG4dx8nd`En-4PA@1%dy$3k z5Xj$e4A={W*YFr{@SqR%9xvMa@q_nrEXFy&<&6O!I>o=ov%Lo_NGg05Sjc6DpJGJ&=hv&JYQ@lF1_tTEJwPSGUcal+3CKPy}z zWS@@V*9k8WUM94MEc|~Yz8#uJhVLOXn_xU=2apHa`Ml4#rH*s`;zWi}Z>zF>?-(DYkggy>+aFghBNyvFW-b?ZPb%#FYi}_=Ddwu!imRF&# z0*80h$Kys}xjfpVa1UnOatXp^6^Fod>GH=d<+1Z+`S|?hk6Z45zJnRJR6^eqmXiDN zNV5Pl{VmfD|n677|n677|n67W5n677|n677| zn677|n0_A{MGkz@vQg~73=SwX%gJONGF`m97Q?Oqg=^sf8iOuJH+hvI2bnx53A=&| z&2%y71ClP}o!S&5rG6yB$)#^l*q@5RDb0>yfiM_ccrT z!x;sB7jvvEal4q8HHH*dY5;gNEb%?fX?lv4CzQ8p9C8nI+=%f2Fd&M?rYLBu6WrRI zb%T|GyL}K@t0?!5k5fJ-uqXC`q8VP~>jb{3=tcgD$3*_RhCvju9fG^+>DzCY1+RsE z^zqs7a-zQhNZ*3B}dc9oL-Lu}Q&z9fsCn$KU61^w41j_%VSy>Z33WQ%NaNjBLk+j#kM965L3p2~a+kaJ0lJggT1 z5zD;Zd_}Bw)Ss^K9(i*WO=NcqWWSyJB_B#`EW2A=a6Tj{?=L(`I80b2oF+U$I9s?- zxLRm-x6rpq{N=){g=TjP|C`1CKjBM4v%7`=r}zW}E)>!ziag)Rac<&V;RYfGaJG*z z{BM{1>qMOA{qlcI_=M#7>x2I&|F?xQhuJNp$l=I#rvfdDR?-8v>0krD`ESH=eUHXp@vO7cmIAM)&k&xX{`kyIe z47Dmu-9i-)D&^4&_tS0gWXBhmKAL-A+u-9c=ue<) zY)s%u)=5t@++oNNN5I~Bzu6GP(|rb&a2;e!m-Uk8L9D~!SIqz2sKCW*% z+6mrg)EA#K;#`oA12T9V`nr>l^M2=pkL#NUeJp3{D}AkE-Bysc4_v?cbdPx{q5$J zSG?2W^~AcZ_`TwtGR#C>d)J7UQzJ&4(hRdvw6*@szSQ{I(bwUm-Fc@%?(b1dBl%lm zC*(=YMcIa_VTOAl;2W8_e?B z&tY8)Q9+CJ+Xdqhi&og_{RPi3inGE_2Zg(gb5_{tL?Oc^oE3IDS?GeA6?Qr$&aAN0 zX>qW^W+2WG6@81Ug7iw{EL~9eD!9gPQQckXkVTvP5*RmYbyu1KH*4kSCck0iyG@Q+ zE8drywHgAFF8n<7+N>1`%v!xpVSk<>X013ERREH8Bs`j9&*tp6@Lp?vItV_z%Y1+q zTOdKljgeT(Ew!Tu`ABR;4`HM-?-`H4$27YP;V{mgSt_g*mI{e7A3%{>G8IP1;s}RX z1UAVo1@EE2L8}yt@`H^Q{u#@R|4B1f@$c`&XRh3wl%KnDQ&lbN&rr2`&l##gC`8Ok z*@W&8N8{HyRs%ki8U?kbTN9OCDi7aA)~ZhZNh>Gf4Q|d?hu932zt}O6zoghc)kmZV zl?Bc>_(8y2#a*2L9O}R)lT)ka;ZDriZ*XGATk!wgCa1b#7HWS{AIwf|ST!z2b9QQf z5of9{UNtV+XlCbR`=JS}O4WMq{Mo4dTn2xy#Q@PKr7XpN&bk0iLSc%@()(mG3Y$A} zlCfe??!#!&ih<|?MKe@3bM<$crmDAmATnV><@#qY7C=kIe)oILU9s^RR+3sDp75`~ z|6hxEs~c;ODgJkwyJDF)Fn86!QUW*bXQknzFS4`2n1C~5obx%@xhtm0@S}v)!YM*q zM*#o1;%kMc3C|W@BHSXpPI#;EZsE6u4+?)E+#!5c_=1pcv6#P;gnn-83h~zqZzZPl z=CUxzd076>%KtgxOGNnZ71~>4#D7Qr{@YykZW;3Sc-k|(|29`o@{rSpgUI0qaiYT6 zcdZeg-=U%W6k+~1l+IE3{Jr#W`WBDf3&!WYO61s$*ji{E5%9glmkU4cTo*gEOvmOT zfmY87bLqp|{C)oZQFB`#X4%Fkb6dPB7;8ANIzgJ(j&1-B-tXS|uIp$Bd>r~M!Ert6 zrFY(M1O%wkL8UzAVa|YiS#q?6DiY0Uu$S!PFlaF*8l1rbxtw23jHdh;WQfM#-gzCf zA&93t6z#!g$e1qYe0UzjIvhR@$lz!2$dZurezoA^`ksJ3-dEJeaf8=)0calwWUwC| z4CkHqJ0F6$zMseR@xJx?^5?c-IUZc=l^~oaugcT+D)b%9+}16)(3|qE8+C4%?s6bq zA7u7m=C-a!IJPU^@jU4A=eGDgCSQIWQTg+I95gO{E#uHV0euHEx0S*u?O^7%GPqa= zGq*Ja^|YtUg8lk(?%ThA!*g2|*=)H@V!7Xn^7280u!5xjz=NOLTE~in+k*ETXGFO- zz~#M;yLY+O5dPa(c6bivv|dmgo~3a#56uFct6)s9|) z47g@%0m^|HHa9MF~FSw54vIp)3$62aWC+!8tM{{iIiTegDhd=iX=tm-) z++;U$?Cqt3l7Asvqr8s+`86lr_itXB4n);w}i=Q!i*WU{FBXG*oTSF z6Oi|gt;Z*JEv_`U?uPH#_|SANN0C36J>=RT99kc1l@FW?aCcY(b}Uva>dghgzb};B$x8V7rFUh_M$Fd*O&Vprp&~_I8B1>_iJqhDu zK$Jw}&^I#I5x=Z==&ze|{Z=sm`q6d6!Li5(h3kbdj1x~c-qY}7iH63Wv|IAdlkCvO z$5>mDiI*VbHRh%DV~+f>=dYpQVfcIJ^|7z(Y5LuQg2ZpVoiESZmmlW^ zQKxDF=D6nnV8)7fL*Ea5Bp}a$ju<<=U<~wSA!J*~c9H2aedf*AWst!_6kbDPMLvV) z&Y$nzSS|1Sf#2G#7*wuJ2R?W$&d8&fa&D~H9@dP#|M1%A37M8v!7IZG$L~m0-Cwo2 zsz=qN*Mj1i`%>2?7hd^JuQz_VFUVf;POsO0{7&`|5I+^;HI_3oel_U;hSJ*xUurCxa?O+H(7Q@j3eC(g{2 zm%m-GB-6kA?Ns$SLHXN>UaNz7LFau%XHE^W`wEt1_ob?{`x3o24FCPk@4WQd1=Pv- z{g5~6glshU?{a&Xci;V}=P(xM(Bog^9Ks`N|Eoz=s;Wa(xT(vIJKtJ&PT7vkPqRHL zc8q$ivPY90>w5IsaoK;@eg{6w3wCsV?#{P@<((OB)*Cy+&T|%axAG49?B3D6>c)jP zT=<=rYAabn)YM@|88oGKbc3eW(DeLHAO6|jL(e%siufnNQ-6u+f5r4a6x08m-7ij&}O!cE|BZmV|q!Mwp#cqMniFWLTRlVnCx zasuv+WRD4yB+`8V$teuebCyX8f5kAv80OehDM_XOOvxe!9Cp4*3i+jTSjt?5c$j10 zex12F2JW_*@uAsZ_re)`6O3STF%B6rZQ$fnkwXD1@jTF z!wbTBOfHq$2&J}*A?#=FL;!C;^BCv?Z21P`nbP!UGxI=X&SBW4nfs`xbC9`&>@xF8 zbWUa1>qsxpFqg2S$qb{iS_LkKuEp@c$K$AW6-lZ|Pvp<3nGPnV!bWTHQ?!+td%Yn1 z5xkS%horHs`Fg#80?=6VTe{ZC565j-qXkWzx|u+Fi=z z;XKio742tHw3lPi9POgL>Y_CXYf+RTuX67B9zKMgKncGh6kb3ZuwL*h;l&L%@r+qR zOZXlsypWgucCbyc>-EL-D}bQsGJHPz5^{W*(X_Ta2ruPz{ibpF(6n*vP&CDbavV*a zo7VOhH-y~uAiTU7f##CI9FsBUk(nyy1~8?c!cVxF0q-WeGh!bjI}f>VzP|+9w01-g zZegbP8aEBuzJh1?DV%H5+G$II@Jh0a!8T+4;7m4Mc+=r{&^71FtGnRWycvi`L6p(6 z1?{o%L}Y|6AXr@CEhqX2+-cS#>;ciz2k?hL=yu?oLa z6RYt%E$kPxTBbl}D$r>Rlzt?#ZA)KI~p_cKfMK@yD^uge?Z%8obLgwoE8+K-nF_{goW+`99Y3Dd@sA#S&D{+G40UJ zVm&wJXfUpx?`Ob?WUZbXJDco07KX8F7?bt?Ov!2gH8H@*!G01qn>& z*+%=ijQ#oBGcX^YC8%W0`GUD4h+J*|oW-)*ela|TBl!E)b_QCkK<`)E8SZ=wmtWiI zb8xktpLPo;;1av92n|3)yI0zwf=y&=zXXzYtfZ4z5%z;CK_Oud{}%ingAr5-zcLq2 zA^RlQrqw|>jdlGqjbQ{`1%kKU5jc3xytk)?6+!!%@CdJgw9@(D7Mv2aS`8_mDbu6i zL930B@i9fiZ>J*Yz{GTpk;>q(Fnk$VJB~~thMM999ulJ_=3t3w>TSDnGUTjy47bH-s|Wr@nOjUk#o~|lL;$uz^IgP z3yxHPW`7&0ubRhX0#!H<5Rv24JPf7~e9S2p^ET%}@G+-s0~0xy{M20X$HkMM8cTjE zlfM^77#v4%>l~&Myy~fu>S9Qp>KTNQIFLWWWE{!h)NmUglT0fSlv^rJ^^BbADPDB} z3IY$$dde3gCYFd3mKr&Qz?1i(NgupU=HzS~iD2c~#PyC|O{AtUI3B;|7<3JeL{OU` zUgYStL>KfpQ08JmQw(|u4n#YX=z?9G|pItS)Fg2qTUBq-DCqD#j-*6K-1HR`4;Kd!l{PGTEIH zwO_er=Kmt={9;R#~ zfptys{7l={9Uh;;0S|)D&os-=D(6A)`I)wj)CT84U@n;Kv~8rmh4Ucg@)K1$ z7ON|tDcwPi!hynO>MqSGxm2c5@0VSGoTe!dnCpe#gi$Fc!0*GNJTh_b*tO>Kq5}@* zz`OU7)8Fgy7}qxjW6(svsq*)?qneH{IjqD-A(mEY^H+R#tWRI}|DUrJ(Om!D%)Zs~ zcUtLnVHhUM`XrZTlZ~>;qCS{;Ocr2`mtpdTqM<1a5L3zI3#%kdZgYW>-RsO_CYFd9 z@oE&}ykb+6d_3@e53O{Pd07kE&cVF-$z!q$P15K~gkz$gUZgtn$Ec3=S@Y;inf)YB z?Ne2qEE|_x+Q-bq{0>WRzTc_c)5xFN6RZ8H-QCD#Gyl~7CkpXgV;7>zpV=SloS?>? zQd6P zr<29mB0N7`KiK)Pxz+Mh3YJhN3-|<}9k4TC=*k zX4!(eg|_<8>SMjNSIbw`^~0L5XzAB~^hQkXJZ&Xl?TFnz{j^2%<{?zh*pP;6AIrwjWrs=f9s7#e^A@ccFbf|`tgBfu zf63`wAzJ@qy9c(Jee1oPpX)LwJG!|z!TEXHh{tW~awk&HYBH(`2S}~lR5&v&%cx)2 zhqq?M%Gpb1VMSqsXR&A*1K`xp1IGtBrJL;$>q6K&J$G9EHZPVWb|&-$&#>&WG@e0 zHS5vQ=CXG#xaqB`=U))iW(^aE0fXtEi0|Y!$V3 z!z;$)SgAvM&fx}Tm&uxtz{TSgCLSV0Qb7X3uEge=?u37Z{5d9J_z}X%!WqIj!Ue)Q z;pswNFUG%AxK((g@K)hnLQWYl{P%@F7VZ-MUbsj2mhdk^+u#N9n{Ye;>@FNEtP;); zo*}d~t_Xjh_#X)05ORn^eMblf3ug znuzq8ifR}!K3OyRkbZxCK3{Jiis zBEsJ(yhrkTh1^4n`FTqC3nIe5D11foKMLO!CJ~46u;+?}Z!647-bGk0oFP0*c(w2m zBFg^>BAW9Z!e@xc$1mm2{msem7XN4Qe-U4R8=d~#ria*3*jIRraJ=x-!ui6L!gaz8 zMC9vo@ms}zN&HRXzbgLQ!pDWr34cw*dA&%)_5YLP?+R0RqfB`VBEq#6b|>OIDujb1 zKT0@GNJ9t4JDG^^3xrD~uNAHnt`}}4BK#MHH%fks@E+m4!pDgS|6?JoB$)2cg|7-< z6Vfo4;Ts7{h&Y-HI|xSzKPy}>{00%_e=iZ8p8JIl5~24Q;je|iCnEe_@!V65JdJJ{ zzKO6o5&C)xhY4p07YWZLBK`*9Wx^|j*9x}@k&@jHxIGN*d4syiz5?$PJemyK3E7vV zyj(a~c(ic1uu5pp9fap|jqy$t&Jvy~X}w7O!-Zpo6NJ-+Glev7WcX#mRl?JS>xAot z)~`VL%f(+U{DP1sk<@4X3Lq^b$v-Zn1tj@j3TX&Q{&gXZ9my96Y4b?_P$BIa$@db{ zypjB|LYgp=pDLU!tP#>$k^XChG+HEov5>ZaY`?!GyhX@8Jt@CSX#ETDkBYbc1^Auf zpB4UGxJ&r5aF6gU;oCy)U%+(mF#|(V}+B2w7;bM zMByyqBH>bDop6ot9O3!Gi-ea6Y5YjNHw$kUeqDHv@LnNJzZm{8;nTt&3!fKSe+c1z zC;ksYnpZM@$bJPdBP!r8(_!lgo* zbTXXvr+}NpUm~>r6y(>4r_m?F-!A-y@SDQ#3Lg^EER^AYBK)P$_qSdazeo62VW|E| zBViLEjY+95E42O>_+H{^UrPUDg}&c4RXi0cwHi7ELrg)}lHf4T5#;TMEA2)``6 zL%3b|P2snN4+%tUdlyfHpVpCz6u#M37)4Gc9A>?ai+7s3~WF9}~2(z=!DzAG#!a(t1n zneY%{J7HFMgs_j0=CRZ_OgKhZEj&(mf^fF5Mz~zKQn*%lws4d165*A?&k1i3epz^j zaJ%qc;r&9|?=rtTg+CSkT==5!W#L}oTf%pRN$8-SB4LTJwXmJAv#^`6LO4)3R9Gn- zC!8pxi80feEnFyEB3vmvO?bBOJmH1HONCp7*9g&E+Foe&#xJ;EvXmnX9s37(JR8Ex z(2sj;yaS<)cYHix7ls4+anAt78zT8oVI>jij1-=%e(;g(7b1Qc`$h0?LH|Q?<0}gf zehlRZS1129((|F#Wv{4N(x)U&S|A*1oWUObOye~EM=F%tkrM6#Ndfxux+Yxv8i*4GC3_4@od^KL?b$)bzVdvzP zzxYGY`HB4noty6sh9gYDh`m8FarH^TFaD6|JO=v0io0GZO3ZzwagrhEdt!hU`9ss! zUOasJ&JI-t+qz?gd_~n!RgJFWn)$+ONvB^qAIpl#XRb{R95J#()eOWb9WZ@gal6ui z)Awb1%?zePG8QKOqxQ8ZJ~0>xpOKJ{g!C4y!*9GDtL`sv2i+ZQt$E?K1z+eWz2-NG z>&>wiALV%I_H|X6Esd@yL^)oHa=fx?eO1s2<=E)?s-i0=R5jlG>3#i5aduTR_w_3e zx@{^wabLe(*OorNFI8H+H&a}+H&fKEb=x+Xfn)adt9)vIa=}R`BjmXF=F);I*Oi8! zJG@PHWYDD=k{}yw-MBv-kl0*s&69sf4S?1WL6=AOmkdBjE=X?u1!R9}v*49fVavTi z6-sfy=)FPV_x1+^T340s4Js$SJUSS;Hwee>!y?3f<8k(>p@r98IsB_T+gCOEQYFrp zyACwAvdmmdIun&;(Y2Yqsm$j`Vy^_1WoB<_rhaLTIiNIe81>@LCV8cqxwa8X(`hz8 zE6w1$|IYgY9LISHyVX+R?{H%m{|pS*x92G_vAzKrMfP2;dC)41os zl{}B2{cv?J0@Ku%S~d-*B)5z#Xo}U;r$NNU&?PS`6xX&QRAx(cLBB4mf=&&E?T3V@ zk*}}ekLB4;NhwP#qz^PSI*S^(qM9nsvgDFyjX|nR53e>}K4&(gv<5O)(hswczCqSX`Vp4&TVPqzkI`=sVzQ)-SxSZ_{e61zZLgKIaeP;6CH(`I z7c*-mZQN>buB45FQib4@QcywY-O-*K1d) znP1Dx`O9X}THamY8uQhYTg%(25bPafZ7uIzV2inymqj}qJ@Jw%5G==Ex)Z{}TVCUe zp!rOuTMeH#osX^MEn&x=cNAM3mJ(jdm28A;3zEQ+P%kUH1u~ssq%vp`+QzG8%+6t8 zhM=fnZtolo5o5wsEvCM116u<{Bo#ki>OFz00+2SUH zvvwF^<}vUfR(cN-_8t))MBejUI1+bp*_08PmHXKwupPK^n&7GBMR>A`xsE6Voup z$HrhSU{tQ4CH@EpmP5P@cwJ&@8-s&EATr1Jd^QvYXlqXmI&CAvxQ(y@3PhbM+C^cR z1kW!GFRZ}zr&m+BNBn22`CDlakP zXuA!LKycg!KMhE*xOsmNX(47FB*w{uIMLD2k#pTaqLLHdG;$`vsoOH0NW(LPAW%sd z%=EerybYW-TaY6aV~7JR-V}n%&z3P>2Qobd2}X|93fG9hm|<{YE)P?E9&i^_#}rii z*sM&<0;@RjCpe&tH#V;f#nd(tNBk}r4!;L**j=y!cL5p3ZG?qk4crA(t!hq^DpAeH zuv(MmY0BH5vG?|w#$8~+5!+mT#+f5z+-EEqtO%n_LEg|wOhlBm7Ht~Ao$r=P*0B;C zh(&Of-0ldHR0JP{H@6tSn{i-SKLfLSlMd{TAn5`}cf?Qx{DlQ(XVV<>doK>$s7~Of zMyLyS1feC4f?!R(I|6>n;uHqqO`|)4q)Ro@N4@bo!iE!{tPiwm&--zzRs2en60R$O z9USLDsul-42yFhG2dQ;9;6d1g!+DUp9tS)Kx8ZOer0&B35Bx^13#k7a>jTH2X3|X* zvM}QA{rrD|zF;Egg_s|Dz2Cp}w&`j9H%Ecz)Xax@kT8h6?_f-TSMW`fgAJ}{Dw zCAEM*mVm9-vji-T$&$-l?#i6XrFSG-k6X_e_wy&WCU6khq)WhPQ_+Fa3jP>XZ&hHj zd7tFsan;Ed^SKBvfkkkBY2YLAqI%jC*DIW;;4Dv8RQ(ysit4kZ>?GXsLoJA`S4tMI zyD45{^Z{jLr(jy_6m!LMJY(ewY8Bard>bshp$8V8UCNJ8c+L7GKa0XEo1a`-zwkcF z>cianFs;QJGyp5r+(IfXDa%V%t;qwGT8>MQ}mXc*Azu&%MPOzk#+@HEsbd4a|O{V!UHNvT(Mhv`oF$@kX>Hy z_=F!`(q(<44VjIF7ZhwtUzoZG`_Nt%4lkLUxU8t8%kak4_<8Dwx#fhmeUFdNp2K^@ zpPAUwaD4W!ykn2!KVf~LKObU4>j~@o|L3e9Wc_)+^@FS*%`sN0i4{O8=#4nwW!>)& zSU+ej@q4cy#0~qNt@8)m=nck;->^}9JhUv8ytdViuZvp2ejevS3z4ptioG zD-i|UOY#cgr-Y+~fyseW!_(#M) zF8^o5zaZQr{EN`Ol?i>V6Hf2p!k$Fv8z}xL@wV;(;ipP|im--=d~)wnBHtNOYK%w1-h&Nok-G|^g??ru_gd)xnE)p&k)(O`L*9q4PFBV=dyju7L z;T^*5!fy({EqqA$nD8m#kA!T`n7>~N?fwP-y7;$+e-);AzreqdkaJ<=TL|;lSCvc7 zc9P+S3M+-9g??StH1Wp^`R14LJ}X=!JX2`x1N=WH-r5E5w}`(>Xzc^!-xF``1Nfcd zpB3_LGSk~Fd`tMY5K|=PpAzP;pR)D?@!QFt@1v=&Txjc?!5=MtxUfn%ML0vqH`t87 zP?*1dieDd~|9L{b;wFEUkngz3-zN0!(YM7vDCD@6~iGCf`@5KK>_@;25(AH0( zJ+SptK(n6(wo-d>n6R_Z)=j~`Ts+@EvOO6p93`}MRFF>*KSRj(+6=!)$Sw5ATYCpQ zL;N|y3xu|g3jSA!w{=wDZJjpo2FdxNocg{gykGdB(Aq`#KPCQI;d8E`W@lO?=Ej&-iH~#eh zqR`fPgTF)kw}kfz9~1si_&>s*32l8h;=dt&ztGluLtcQo#`KDWWx`g%4#G}CzAb0? zLBeB%!-Qjn6NI)73*l#qpDSD-q}>Abtreat+#uX6+$yAD1H<1cq-6tnTWdUNpm#oKyw@I~k#G8`=q?0F~bD(oSw5DpaDx+**$hl#iKRp4!X z74RgyC!aoZCBz#90qRd!cX(6}NCtoJ)Bs^T$TWIUy5zf|Q0c|}NuuA^6E*|`J z@iT?9gmZ;Ug{KN_y%yrxdM)5a$uAOa5nd(SCcH^Vn-S*U*3ARIBmR5B9}1rq{zUk^ z@VCO3gntyiA^fW_%(!%H-4@a*7T;26>$V{8DBjj>f$uB6zmR4v%x{%&f^dp(rtl=; zT;T%YGT{p0>B6&wxXiXbqh5av=a8iw>C*A}g=WSO(d<65ejdVEJcMgOIgmzc3~$d_ z_}g>9`@&VSO#>cY_1mwup#GkDHz@;_U z4Zo}hcm$gpAoSqFl|w+9b&QVT{2Md!tW$Ixi?ga#;-K5r-qk-0>n#k!F}*1`;`-QU zo$M1+SU@lVM_eDb@XSI+eaws3w-$6<-xKJAalXwvpTE@*#PwyNuRCPa$9#ExTR_LM z6Kp~Ooq&76JMVWX1aW<1pl=PrQeP1cukVYXeH@U%uaOwX1KxQufBnbbpq%S9z4PJn z*MB_FF$f+&Sf-1iWt8sypnV+bxTv{Xmq41%``rsVeje+vo+Jwy^}XNvk9(ny;k@(u zo`fK-?|SHC9il!ANu&Jz2DFa@GI$>PxGvv2@AoSR;`(+#AKM@5LsJv!%U}O-H|CdS z<>{l&cW@AyC%UpW2ecsr4#`;slQL4A7r1 z%P5xZpfzr+F5WX1jv`d`$gP3C2O#7a9j9*TmI7IJaeYH@U)}x~g{ckgk2J%MrRfFZ850U#RI--3vaw@_dlmIln|Y zKVQ|Yx*Pnv<@uL)?NSZn^v+%K}{b{{Iz zVGnqvR))I(1^oVCPBcarm%-_s6 zQ+zjluTbD(sGUKD)SY<|nMqxl;jB;N)~Q!AU!-!52~)c>CxE7M-hQb)ie6gWg6tnN z`v8f~ErJY3s;Sp9^AWm9comp%pcMf8gM|>%g$d-h1h&W2rA#wC zG6P`~WWMwc{DepEg72R|AtEGhw5h)N&g zSj=5ONBZyyE(B+6EW{ii;+&w2+nuGmaS{ePIQMHI*833e24&wvV(IRjhCzrrAL3RJ zEtn5{zpIR6@N^IEWP>1A`yh`;L6Sk)@nF(N{ErLr-#*ChC`e&Y#+j6K&v#sqpZg%C zkh+AL24zhlP50s!Hc04QFlB=%8OF(ENLU7CY^Bq~hdBw)x$GU3TY3a<5QO15mT`ei zdL*Y_AmKTc_2wyzI?YLV3T4NDPFG#%B+PLcxBN+uzSc>Y!LqMW&zP?|3G>s8RhCmL zc8$t{GEO(AXYdy(-371^zx5&d24z`lJ&xPsA;dF2L;><+ItB-29eGB_mqCIMtQMw& z8`)Thr-QO@L71L7(1mCXun^-xSkZkG*Xa99=F>A>5KgvO5YBR2knbYM(_qpkoZ*7- zZnYp=e2_w1qvq&MrBCGEHpt34AB1z=rsY>9LD?Z-(kF4+8$oUYeDsfLrNYLrg{NUX zE!hjY!+4(C0u&m74pXM3GpDyNWHs_%+bVJv`i&nxbRfPqCbfu}{2#>v}AO?Dmxo|p3=HQ#vSgCau(Px5+WF$#pemu$bTJSraz}uIEiQp4 zi;qN=`jbvmOXpIa^-LFvz!PLBf1(Pt7~&N!6k#0>h9dG@6^i5)IFKUYdK?TzdIcEUC%PgD^GnAoXurK?Wh~w1g+}{9wFGsFs+>vxAs0Ph#R?CnlUN zF>$>U6TT!d@n$C`+#@mZJ|`wTB{7kg6-g3al9q@Nfu0Q*rnKHgCbdwKeVvTI0^2y|NDWe~Uo%@Ar}Ha?Q|V z`O>A!m(8lIG1)(x95FOG zWPbnT!l4~ICZ}eTeY5R5f;uFdoIJE|$K>K{vXztcwxEYncycz`*$F#llgk*fI-4Bm zBu%r)I!dat_$-B~!nZz-W2|}EWP2y+mQBuKn3>sR4<~7xO$@*_kL=!@t>o0Is_B83 z8H4j2ZXZ4vyKG*~8vFXepaBQ)687sdBu6e4K^d|TeAv{2dpUXH{ifxFzG>j zIHUfzM3yNAvlU&*2F#gtM_67OIA31BhQx#1DMjbPWOC&)_JmQhCXAhi`)|aUKz6DP zXtC=RF95sjytD}H?qs#8$mO!RHEdUNsZ1O;aqPsZdgWE`>I|DYdd7rN6Q|D_HFfGF zcl(?Edd+k&jX96!0c}T|b)OZQNI%qxp#v&sSlW^`C8oTa0H( z{2LxK`EZfrb9_WeW_@BqctX*`kCX(%8jmgFmpg_x9xuLHI7#T`9=-o~i_RY3?3&qg z7UAI!GL6q_VRx|J69xwbO$sM8N)%<9r&{0(-K~!&HDNS6gc8#JRo>dD_v&UDDo0=DdM9}XNdA;Oesvyy zNgjV~9=|=0|9&3-TprK4fA5%nLF|%|=SoKJ$oI_Shvo6{_fZT#FVBB%9)DpT&-pg* z82|1({^30S`8kQsiPrA@V*H-2Ij4>E7$o{(#SsL0|LKPBYonEdm?7lgZoWh@8y z^Wj9S5LOCL6xIsQ5nd)V`)|a%UA(vJyqA0$-*X@qVQNPBtio}NB+7p)-t51@zajpw z@;?mi5A!ulX#3ZL-yq)XD8c_w{2pO5Oer&7XW>BMXd(CBq5o%vX9+JCn%xQf9};hG zoWK|25z2Tygw?|3!YhRTB}6gTvwyMrg>qqA%JRW2VQBLa;Jb)NRW*6JaDZ^Auu?ci zX!Q;8r;BI*hx$(u&J!*X)(Y1M&lIi~T0KPk&EmHTuM=J`yiItA@NVI^gbxTG7Cs@| zA^fTE=fYQouL=Jw+%Nnfd*=?UH^{fuXJ8NUy@i8?M+=7wxd{g2O%__a0p99AaF*nY zgiD2WLT?9sp7@QzONCp6*9x}@ZxMb)c(?Fd!tVhmApEi7M+nCWCkkf@PZG`*E)bq7TqQhHxK4PH@G{|5 zLVJIX^luV>tI*b&K+bo2Jg@tN4+tL<{!sX=@HycwAzukHKHu*V{dckwDaZ4LAmvSj z&4h;v+Y7r0{dX?P#Sai3E%e{J7%iSJ3#s3K_u@G5Cky8Y7YWVo8sS%oKV7&^xL$aP zaI^4p!fS2 zpBnhY9+AZ!ps3A#9DBt0I|5!qx?VVFd{K#mZWB6>4*<~(wn=Y>LlubZ5kZ=}4jH2T zzIQ%<6CjA|8-srHOteST$NJ>;eFn6T12UKk&rD3<$>n+BJ_|eBCmTrjvj`VY_xg6I zx6sLSDfj8}jgHq1G8j@E1X&Vt-fu1VxW4V3+EW^v?TjgCMSN2lVkeF@H^Q`22kxG~<-xpj(Fi;1Rgj zy`vwGdxbvSHJI*T*?Z^1Jq7`3?=ragmv{!~FU>=s4KCOIvzn7rzw+)12_*iiT+oA6P2wFq#)8+MH-jHtOYEf|PS9s^s?c0Aye>30H z&j;H34H`TcW`A%WZ0|Su7)vd(_iKw>*518ycd+)JJ;CBFyTi5N-qQ4Gdv0A@uxap~ z!nGq7)pppvr|PK0-qKWQ|I_wdusD3>f$+3FkFCuddH9}Xk&^3SU&fV#w4%wYN;j}%6kDInTJR!9=lbXM$+v4QjOtSx;4dc2Z zy@7k?p0Ik)&5IA;lUjV*p7h$9J?jzv(Y1s2d>N^nzqZ?+)vlh07 z<(BV1?Fo+BxjS5(Mt*NuTe;}8Juj{ekLk9jGh*EoFJ3X$2%j@FF$ZKM6CUN+lZb$KxdUbo!%Iiqv^&#Z7i}HH2^7=bV zm8lO$UK5sA+C^e7=wa!L3aVOF6<00Iq}K&KPu|(4DzsgwpF#O;Mfvdxu;lyUObXYo zS`;3|k}n-txVN-`C7*ib!GI;7+FP2;4DqFZ0b)Of*qd4UM@_?-T#uX{0%Ym3^mm~2 z%T)R^a7G1tGX*StUY!q9`c*ii0y`t@etj3?1v$zq2;tq)sa=@ zC}-q&829A1?;Y7KkP10gD1H{qss7IMcOnqBk9=Oh^pZQ6CfQyl+7CBLW-cL-rUhK` zane0cG)bYotWTw1qz7#e6T|B0kt%FYCX;^5pl~W6XOP(!T$1CaWb45Qon+TL*<~`x zUyv+cL>-ybAHhJC%@~9kTM(H^VQMBwv$26~We?B=DYk9}W?@-)BS`G0d})TGx<+?` zgKZ^+3GAnQ7b9&pFW6Q#VtTNxe9?R}jWD$oz_!u_=6=e@Kt&;Sr#Zz0+seJ5GqkPT zK;@4x{O*hm7onV%bFi&+(M#Fz(YA6IfVP$C^U1%KnTSwDVLve8VPRN<3^j%=W!8w= zAndpcKE?lyK#pwk*&yuvRmhsuoi;THyD&E($BHcBb13Y(i_+R*L3p^Mh6iCcqt@V6 zaM;}<$B8TUs!y{>31-5JM;S#8r<4Nwle1r+PgRUs-JuIrfD3lh%?HWF>|L!-=QhvO%S)SFd+m_&jD)D0x6K>UovWDp!?G&=#r z2tF)}!eRIgSB2qU5rEfbwZAr}?ShZHHb(N=oSwTjYjfAeNOx_{U?jUXXF3Yk<}9P^ z+MJ!cHX?a#Jc(=LNV_)YP`I%R%xQ#IvX;rxch*mzR)!<_xu^K#!1Z(ik zBv^xAA;B8l9sOCW!E;Ek2H!w}HTX#qtif-SU=8ky{;1X9Q6yM{w~$~BevAZbaCJGCls3W!!ds9pK9A+am}B@(>y z_mkk2|0M}tdG>tm%8w$!D}N^mUim+e;FUk56a=sQKoY$2V@dGJuOz`Me*pi zh9Mgx9;vG`AKh{{ZkSu(4RZ@W>)}>Y|HjaYG&h4o0)Q} z#v!%gdib5Uc@~Gp_U~>J+oBsqUVm3^+(_n7K`~}H3x~JmJFA%QEO=@14Q;cK!H$w( zSI#U~!9>oUpurTv7#zu9RqZwsKJ*lh+#;6+g&ju;BXM9?+|)J_F6xSmMLijUVpyxb z5l(4`ZvzZzw~dnX>PsuzZKLEaNfD00hvqiB&cbIR4&!K1 zHbB=d23}$G=4#^CBnOzw;#cufv3PYWApZi6M6h{0QSs_Pnq#jfivxy^1V&8;D>Js& zw>$lnYog3Ca6cR{cqO>9LdlX~VW61TSgG9QnA(Gi$z+N!_UzA3E}#I(A7=_q&1@s# zw4GWSY0E;$3?#5)xX(xemv}y8YfZj+0@0o9s#=idviMcJR4iT{3-=-%D5P;j#fu6l z3)y?6x6#F$NObW|MJwf87SK?7%%`i?n@78l?jj5VgFH+kvP_F{PJc@Eu%AxbRzPyK zLJ*l+ad2v>4^h984Rs+W5~Zt-A^FqEDyee7;x@;Mdl?SoU;^<99GT#)>K<=R>d|2v zIi^~K$_N!E^SNjq&xLt+h^`g2vTUGfyo*KdMjS|IB9Z4&Ujy@4Uqdc0W0V)=z8aCu zF&$QLmkvXbQ>gJ4L_Z0jZ)m>a=nU}=$pMjT2@1C=f!+sN!n1gBI}m(;`fW=)3t;<> zcL!;03yC3)$!sIR=f8fbGg`!&`@zngbhDi^{ zfe#E27!r4AuC3uM%Zq^vQJuRewbAV|AAYJ3EiAt!E;Lnb!)bfaCxlDH5_>{%Mm6MtU_DCj|z}G3P%CjftBD% zQ)R~S$N;%ypE{}_Nd7WhRtpnnpPaf*XF-fwoB=k9P`dBq+J(p*ed6_@eS zxw~R4?h0~>8Qm3($vo!nQF2$~;5|xYPMXJaQd|EXrG||-paJa#(;=sHqI(n~n{wpc zmjb;Wod(dYiR`T9Kjzya4DLQcrIE)GeuTr_h@^JmKo|nmV263a1x8LLQ1OBHty4XP zKrLY~o$!*8QwSN;Fr7dP72KV*_N@o6uKr!#(jrbJICaMpQl?`n!R77vT;8U{^ENfQ zQxMB$N%1^xEoIF7v~AS4&h$+q_-sszVrCHFA5>th_k(bR!SMuE{bX=j${zJ|9Fy5b zVhxVQ!D&HV&<^(_x=0jUS5Js$i-ITW31b~3rsaxoIw*4#RpP+4C0NO_jfqa3nObN8 zrV^+d*DKf1(SIlo^brgzEx_s)1tfSj>^!UovI%EvNc_I!0CO=VDJrYPkq)4wbBd(Y z)LjDlZX8fEo+veSAk7WHFIAyY_8QSByOydF@&Z$t*+$xxE&5cT%`q|)LUkoL|GHe+ zqwBWD%G?#f%3M3zK+Fs}v;AR%*U2s&8#(kuGuueG=(V{X7qjZlur62!dMgf`;RK>P z!*{%>orcjI(TL@vjmR$QvUB|hQ6N%E5qAvb)vP!HRj6nc%*Wvdz z94r~)9XMQz$Q~3qrds5Bb=HIOxo95Gh4uGx{U2(*9!IWqCU+kWmK2faQC~y-)|nb| zd12p(oN`|c(i}tGjl-qGP~;RUdRCDCH4coI2>WnYpN0e*Ixv}SBwVX`TE<$Qj zcRC!u;xR&C`t`Uf*y?r_#L#g2MVFi z$q?pSyE{wWMdem~46A3wVf@|RqjO*Y9^bYRj4o4XqjyJ%c;bsx)Qy)~A( zZ)4bg|MTmAIm3TEpl+xSiZZtbFIw>B7mUT0X>s`z%;RH{dMl$E zSbV1CeT+2MR?tF&Ey#+kKKy`n!+qe|JgLwpPkD=A)|+>qE9CQf*i%jx3&d^`kU48$8oGOc-$>WRi z_{Mp>y#|bANwZXoSmeiEu0;Z_xoZtB>UZVkhHLKPE@?Y+<|Ij2!?}40Hyz70c}KvT zy%b*a<~V=anvv%<=C>)LeidYvZbL1bTRw*6u<>>}uh~8zp0&zR1IzQ`w;wny!*4&Z zWIxI`9r`mKij7r}I7(P8oFcR#H2iDCFBYCAJX^Ryc%|?f;Z4G?3BN6TQ1}Dk4&k%H z{lX%gC-Zlxu)A=CaHep9ko|s!-zc=b+QHu?{^!D4b|X=3xw90}Z1hphHj)QkF1$u~ zgV1d8;r|Ws_X!^nJ|(2Z2KDR`+Fs`1Ul+e$n8LeZhHD~hDLhQrUD%h1^DrBF;2QA> z+?w38*=)~&MdYE6J4_Kf3Xc%>7Y-GU5>6BzFFZwPw&c)b-&g>0>nZ9r+i~Ef;;$BN z6WSi$@aN`b3~x5#KyEt4^Y{S~h6uFDCGJ$X-wIzB{+WpIyhhYhNCeMauE^V--QYWk z?@5HdzCwPIn&HL>rwC~f!f@PslxTZ&Bis`CuM(auyg;~F_<7-t!rO&+3%?_LRQR;e z_T)x7FNl9x_!bfQcw5*AH#YO#T-a8aB_doGVQ5+sr@{8J(7Yo-4 z&lb|Cje0H=ULm|%c&pIXIwG94o4|V{&tI$cgyb|4qF!3F5q~FqRY*%W%HI~++DGtB z*bV}l3ETXC^4Q!55ORd*hi;9W^7KbXfYPr^m5_2j}P1Uyl-*2toK4%kYZSQ^G z_jB+6F4+05XARFw zy~Ms^>$z7P7s&iHrAS^N(tarA)^n>ke30QZqDXEMy*X8_=TdP56x01&IjeTSP;% zb`az?k|ScSI7~cCG`~woZ+@4+`3heot`h6TtHf)??~2>RJH)%h2gQfQCq-`!i6fHV z5u3z6iY?-oVu;s$)YJT4fgC$YxtrKaG;2p7e3<03#B;^*;`!o*;$pE%{HC~8G{0BS zdxPXg@fMLIH(4L^y9NG4vRM-X@-HPnA-*iWCjL(Rz4)>CCy~QHsXtvbYd*k#ie$6K z1N=iIb8slrj}tjYl=6il=P6NME^_E7<*P-rb_4uo?FR5Rg?~@v2veqiQhZr_P2^Bh z{!TZE-kJ=5lKeN(#UmQiaU3dnir7u;A@&sqh{MFQ#1b(oe%;y(U$+*+dg<99en)H+ zw~4olcZ>Il968JVc}zSYJ}9 zyUagUoGeZgE5tcsrMOhA6<3Q_h*yc%i{BPG7?|bVDc&poQ2d$rb8)|TKzv<%Q;dlp zi5z20{cS}KHKyD{>?;lsIq;bAqs20DlF0GMjK4^{L|iVe5ib|liyOoSaf`@d&D3{~ zxJTS8{!)BGd`|q8_?q}zu}S=+_?h^*=;GA|%S#bEh^L4gRL%JQ;$U%@SS*%`6U8Ya z$6_=8V)0UOg}7F{TD(zg5IKaK`M)pzK>U&T@8T~+j`U{w7sc1bH^o1Q?~5D|&h&o~ zLwF#doGP{xJBhu-zTzNph&V|cT;`hX#h(8lKSe@yg z6JHiz6aOH-FaBBli^wtV%->xcC=M3S63-Di6rSlX5?6>TMUIeX{Pp53;&zck?0P4MIvV$P~SMQT%0amAkGyp5tobK60a1$BQ}aV#9bmMHn6;(ioXya6Q2+S|;_t-Yi!R<_Fnx;HN$euxHt_%G+E8cf`S^Vj z_hQ5NT!`{JvYZZy_a#M=&HG!Vn=aYB4?+5W$(8~P`_<*QdM%xA0$aVDK5^}*gw+Lp zJp3PSZ~;wrDmFB`d8ZDTwU|iH;ZPXY9e_|Soj^|==xy8ZQ<9Z>yI?8%Y6u8 zp2H)haij3ppY8+5_uDA**GtENy8d)qA^3+_c@UTnz&J`uam3pSP)xdOUqs!SCgP*Wtb2YF~io-O?)EZrn5U#d+sB zFI@uy{N+BMhxhI{e_1ZF#LK-CelHKa&Oy9%ETBiui{tntzrOqN@-c!i>ch`oTp!;J zdU@b={sMgwdgQ#gA4B%*dmj2&F7@HE8P|6JelHKa&S|G%qL|m|d~y5X_v`x-`mzy5 zeQX}DonL{U`n-Mo>~#BH2|>m&j@$1}RM_e33m__<{D1kAOkKCbk{)L(Cd*PaYs_LJG<%}1oCwLN^JAYY;klpTCq=cjYu zQJM=|-UVL?95nPKfg1bd&kSp%{Cz~XvESJ71C~J<7Y|-T7~atzkKaQt{9n5Fs#&x& z-!7ZEbjh+s|NLFl{F?d8E?!jYF%IM&Wr00+5bDT9~W)jM|i)%>@}2&v3Ysmb@(l7 zD?M^v9DX)V(Dyv_MG!`PypQ+vO@QCa1Fw^hhTyUGoEOKj5uTrMW05XGj~wep$NMq= zetdv(*;iOD!@Y7Zg&$GXfN_=3ho7dn^U7-dd-x?XeJh?SQ75K{inwcla0C(_WldjZ zxvU5402Oh^_06ZprG&nLMTP&`?_pu#@B;IL`1QYs!+EfA`6@Cv1NSqX9N62Le0$W5 zZj9a#4GtdAoOQ#F=xuv;Hb1egOGD1)QyV8A`>i|qLnoXQZ3q8zZoBB@V~?jgjnkZ8 zX9pTP1iEaS&?TuUH94sHE5DPur5(z~h~>!$qIywDn+1(YlnT%y6*D zNo{c^A3Kl{9Fh_}r6GM&cGPWpD$suW!ItuUzt0Ze-0`-|P2HoxrcgN8_=hf&k7ah~ zu%mNhdY7!7p_|=CSe9~AZW$hJ+ZgDX+~{maFZvwE>|uASdusz2+=`=m3?cW1wuWv6 zVWZ0VO`BVTI}XBs7@M={CFDWRJGQww@a&r{kt3-)({4|{?RBKf*m1boc{cObw%gup z$=-J>v>a?ss&}>qj(i*(at`|Ah&Nyj%c*~*+1dE^nDd|wU#%`YPKW_M<-~=bRb4t72trJ^JpsIfG)?zzWv&^@Cx(inhTn zi@m#z=R#5J?mYBY^sl2Ev0vLan#jW}1*2FLD7TM)ZGZ%FJZ=y-R;>ezz% zy4Z$_qS%3rt7ART%Yu1>Vx{#3v3WVHFX~n`IQIUC%VKv{4Ubh~-yVnVUg-b3^Kj0f zpZ3AId2#)k*oOLLvCzi6*p=9>smPB7a+b%sZ>z@na7pZYIsIZ+rwxp`BVhk5ZFp?M z#+=fnv7Q?T#l}=DkKH)}eRkuSv3023eE0(hyDIG2Htv$20HDeux) z@2aJ-?^jgCdgKkj+1(Fz!Ye% zD($c}WwQ&m-|B7-f~RZ^Ztk>gQ)vkP(B_WY8cN%43m3NCoQc1go7>=To6Q;co3S|^ zf73Uo;cwdJRQyfd9LC@9=BypeF0;#3I@Cw zZEj$b`jx|rWmJv%;9#f#sa-x6CyiRdzrmEhGJiOPw+%oFZ8#+icyU~d%fT#e?ij?o z9Pi>5n9+~n;AIey-;7BKgv}6^aFFJ0LT4f-Znf)CWK0geioYrJhGAp`Kc2M844Z?x zr4OVoM7}`&vk04pE%g+N18;|SUOYH> z-oXhkmT>Yyha|j16W(F+rZ0i6BI;@~`k>B8X-l9@J}8q9y5xf@`JhR@;c?$WEU`+R z>C2#fD1AJj>C2$qNb@OGNw|YT4H6D#d;P{pNNA)9XqWUFRK-a57zx!eQW9=Z(rZv9 zBhlhhdJU>&B;LHF*Pv=PpOH{KBXQ}@^g5JxCd=b}S9%@FGg2v~TEvZD+!iAt&PaQW zgt{0hibI)Ri@Mm9MnYYT^kXBTE=J;AWqK{@Vk8c+ORq&;jPy1onjkxi<@LbhLwYsJ zGg5_-P@a+2842YX=`KnaEl1Lk%=nfSk=BSGSrLhin2cLcBu4Fwm}5m$&xrX}ME#7& zPxVNQ3L0@H#o8q(=o>7k%8Dq+h*ww<1sQR(6;Y58@3JBaGUCHlL_tP;!HOuzh`)!J zF?u+f;%uHfLH?wn*S8Dw%IuKtW}cck1CPd21GLJTl;m{QN zhjxwP28Ggn28D2*C2SVaoKTwIYEgPZU|dE*?8++%9l8fDV%9=Hd+X07`O?XCzV(jr zyn(I(;c$Q+4zn>a^q8?W&bM-rI_@IyVRk3MXxG_v<68LRE&?AWl@g3Uaq=hv9mdE) zkQ6kWpa4yU_-O=_+9~_=aYS>#ne&P&1}_4Q<1KnMHZGG$uEG{@X05_`%SR-5BRIi0 zVj0!lcLhE9*aFUMo-!Qy3h!^?dgHI$BG7Zl#9okyy#Rs6v5_O#w70B8z`1}ZG2W@f z0^^-RXm3Bb0)FGTLkpnZREnObOj%Qi7rbzKSU5`Y^6{#>4;$t~mXOa||9FxY=xpcm z%Q;Jv)3tl7=HF`L?Xfc#EiwTca*m;q^9|VGkCK(>Ebxzh6*Pm$oQ6^;GwvSzeFYoh zOUba+F`<7#-&pFcwBZwJ7@eztBmFdTkM);zgl;Y)jPNp}7=QL)+m8)l<4EoS=Qqcz zy^|%H;p`~gr+ET3&$HU5dB=w}L~`S(`4emi8#^2|8*6Jfj$DWhHi0++eT8Y9oz(o0 zt>;9W-#)CL1=_t%SrnD4WSB6ycFs?F((Hzk$YEjV5|<9{?(($-_7aOh&O9$#bR=uZN+iEnoeL%Sqh9DpR^*0nbOR~lrS?H3>(^gysxd}=M}ZlnkwR>Bx|4HtYQso*gjo%GQ><% zfd-Q2L|1=`#Ocws$EQ;bJ>u=7t67XRt=r91B=>1LiJY=#%sHBrX&)?xUqyg&W}8bQJ{u%QtqliUF$*b`51 zvX2z}8TLEB{21<|lAOWpJ^$*5qW{qg4VzhfHS-rPSu^}Uy~;4|aM7=D00srz%vo+t zzI#rYJ1XDpFmj-oO7CVQrBrnDP<%pVh$_;b$LZil2L zfguCk%OZUSx@#gs3+RIdgrV-Lh?^Q2=+?p_LXo?A7Fq*QLkjY2+jLxPcIkD;miWE3 zrt{bae?x)0ZJ=qn2~2LfhUrJ9GDfuRsy=_lZBifp}=bMbCm2D1NSJlM?*+XE9v}sParBzs{)vT205^j!(HUTB3 z0(G+t+S_c)%Z#+xO1800QROClnT@vX?Jw5)ZFOAj#fke=YgKyXoFLgaqhl1yF(**M zcTPdlpbkH*z+1;aAYtX;M3WCb^A-QUw*KI=@?mV0m@n=6al@zRxkxZb(4SN`iCR#Py_!9>up2nCB!5_26_g6wOFaTyeDzVIQ zxVi*qvaHAfdD9BpG@7$V+_ z**9yng<;P0ub!9Ix`nY{VQ+Cyyb5R>4XV%nm4tzhEQ#cpK$7DZIXAr*iC4V+6+T!T zA&wIHK7;xA9fjo66Uiqva;eAX8^`^BG#4~xGP-xS{=aroX7KOiyS z^drT8DLKFg9hA>+JB-hgZ03MNK3#HO#jh9N7R}sgq%(7-!E`(TP)`?;pUWr@5l4$t z#JS?7;y1q`6bCnU^34NULJ;gy{v1sNdBYuwLOGUnd zW4;^2ZQ{R)=5-lqX!;xS@rj%I=7)B$|GQbWcb=D848j z7T*w?#6OCE62A~Rc8&E(7TbvYT*+|LFTrk-dx@rhBAhSln65}PwpbvaE4j6e0)F#j z`bFYWv0CKVZ^nO9yh^-QY`wlLKN~XrUE%}cPsE4CN5!Yb=R|&JWd66rcg17kN8)E< zNA`21&lV$MuK0E9$MRDq^OuXW#MvUh2s57Jy~t{@PP|-PCvFgL6t{?5MSd)$p8LhU z;zJ_WykfkuRRg{%`8VR*;=5u@G`4Gyo}XWtKUvHa+lx73H?cr068XWF`NxXo;&gGg zI8Uq=>qK+CK>l@-H;6ZiTg0v6J)(Kdk9>P2o9hbXeUi=R1IVvPJ}e#)-w`=}kM;jh zb#vMHNa^k9MHBC%8)EAn+I)ARKw$rqgDJh4*bYj}p&iRM)) z0?G?wV?Dwmzy47!6Q_#C)&cx;Brg!F z#AV_ZajVFW?9^v$8Gw&Ueq4N3P|Og`7iWa`klb6$ z7YoJd;w;hFDnPzvl8tR0$X7|;DBdLQ5_gODi+jX}#7D#z#Fxak#dpOP@k=p?TT1R< zSma;>%Du%RakyA4mWmvX!1Qy(rDCZaK-t(P06DIKa-nE!5I{CI2tbZ(V0?whp$(Lc?E#Qu8z^5Ta&QCX z?}{AVK>1#g!y70+Dsp@S<(EVbaG-ot{G<4>h{Kc^SB`XaOpnvbu-N+@qKunn@BZaq z8fR{%^ZnhM^Z)Ua_f9sJo$cs3{Dc|T9e>fB4ldos6^R}gN7n}%4$s%7>yI?d!(&KC zeg01!Jce|Wv3a^FXCp%wFVYt`9)6C;F2+U|jM)1E4kcmNU>4SkUyuCy2I0Bt9)x+$ zi@Ov7o}Y2ABVCXM$a!%~Ap6T*+5xK0d*E`o06dmj_vbu+hDg0~4?{BGZfB6LO?e9zQqc2wj#uaz_=jPB&hrYMHOrTdM(zkog<(*{&c%Y1pRcLK3;Lms{-2gT|O}Kb#v$n3x=Qh&#r@w)pef57t@&L?6sUSQxvjY*Fkk_=9HU! z^_9iZq@KU{Aem!*&Dy=3)4^jC-0+<}7_nZJ7F`%ND^_p1(-~E{Fr3j4+Ke@xlY2$e zw_xSqVA6L}w!D&kZ@0kl?9h>HjBhW)i1M<#qKA<8>1>Xp-xXbjxeT{pygTPI1f$6} zhoUPn0z9oDuyq(#NM4N*;Fvjqk>S_mJ%Y6BM+9#{%hn%k$vDzJ*02rZs*!Vl)!C;P zw}iJ3!zk;waJ*N?c4Gwg6C>V0X+vW@ssarw*>YPuAjh!S==z~pHTs9ggDG6mcvbA` zD$II7Nl(^~g^m}Hr&FxqSZPbRsWTYf(m%@C4#C?dAmmB-W`t93c0Me)GPE^()_u|4 z$1{@9BSV4Xk<`HPg3LEt!n+2SW);ngj*4P_!alBW{qy62bo9lFwBzA4%p|DiYRW5d ztkDj69iee$?A?kP&2H5lScSPZc6S-Z=I3F4Nd?-riv2G2cvfm%><+U+c5UqFh|J^J znHU>C;!ez47=!+gmcqP~0OkW+n}b$GKj|@IC5}6eHTvXJRb173MeK3(-I9tiC}$`} zuD^m18^wjeeRZ5Sv!w%Gk3Nn1_;<(;Q5zK%KhB)@|&G87K>}p)cp;#+un>vcg13~3wu=&`T*J;ZFEt^DY1rUv8p-FSGOXH`eVLJ)tu&x`fknX^$TKsDsV1U z^@ye9Ex_D|`LUF$2X@w`>BBWakkP|vnNUwp2(Igs}R>h(mk zi*xJCAEw@#wDmdXP4tLB(9!*D>R+_< zFGkNO3vFE$d%LWA6f;4vw{KS=6?(_T6*xO`FgvJhWvnM=gj|g?;SOA7zK1K$#W+Jo zS6$T{sKBg@ij-}uV@U{kq8@cYKM7PUME}agxs!{T4GU2|)-bQA!(5UL%}&Ju^gqlo z!RAyY--fFXu0pw0xUyl}UBUSfn8i|YEozSW4V*p28Cg{sxA#Gci*al&j(s2Nm-oVc z97UNa_0MCUFxRQRrjl1B7jqJ7DuZU#_B5a0h6cB`#oPpQy_(Fm#shi$MX_tXZ;ggF zOgr?`PuHy-f2i~8;PRY96`-?xG(+cxUJUfCsq7n#n5*AI$DOwAUJMNEdjoTuu{+i?XI+@4`OuFmBhepSPdPB~y%$qr4HZ9&IrS}Q!>4imat6G0 z2vhzyG7a@eJ4Lfz{;e z1BKzi$D!}$lF`k<`cGfnaTK*)d-tLHk7f;c=+M7GTDSJQhwne?{PIfNE7{-wU|=48 zUNbn&;a>08-2C3!KbVhU-pd7&&qoS38GqtytBrzfBfdk~fx5Ic%OkhN^2miw)-FT@7-MA|#u9|IV|naA z?fIe{#@_=ULI@1zXBRxgpdxLNC9njlJEGMGoA#&uoTFR#7t$YV%e$fk9`8Sl!PsVPUUqYFdy(RNSLT7%}k^CxU zzHOEqz}m}H8VuCDoJ9X9a;J3D#*wDB3uHBNvzFXv=m%hO&74l;YMi^uJ5PJ}qi)xu( znnYg~9`#HwElN6q6pJ8c^JzCQBHVbXIqFJykIU8lAIok#~nf%PfPdi15US*=|kZ~De zRwI-~$8AO@hY!R&DJjQO;*4E{G$}l7v5xu00JlH{0>1)t3-tcKH20@NAU%CV`dMZL z@YFVrn@M3@hv7IM(oQ-rsMXTut!DpFY@>z7F$sxcrhhr{w!Y=Wvrbq}yj>tLt^<{~ z@9vE2Fc4>4hj{s|7dpq4)4$L;S2pKL>)y)dDqrF$39&1*W}mn!dKX__?6a0QAB1|G zdgA)#$Z8zAFzyBHv?lgCE__OE#UX>gfHc3@2q$@YW_rKFl@0F#$_XaHdYoQL&NwRP zr_Z?KyBEyAA#j2)8!-W!>#VBYNzZO8%-c!N{Z?3L)&^vML$b=9^c=9l(ztrQvcQ`g z=$u1rFjbiXpK%Pj!332PyRe-&p^6D?^=A>BI*Qua%vNzO1TIHcm|Kk{>nIgqOU4g6 zm-(fi`x3S?mJNhp95pBMt~nv^npSy@+$yhas9Dlx_OUkv%={u4n}l^wz35_iW@Agn zWS7QZV+Q>jjJ#?dW%_qxOLHhUOhj0iGFv(;9@a1#aixtxh91V2>DaVhIKw`{mZ5N) znjv3eOLiJFm>XW>JpGx@O4cFRFd5qztDVO&&1npKVU(#o%hbPNQX(}&kDM68T0##) z(H{Ohj6o?3#eKD{KXoabVVtyKN>&)SOI=0Y$Y$#zAG?!tEVw=zyWauGmnI1`WecJS!NG>;S8IF&9soxQjWI}!V)dS{f!_N^9RO~ zXBdCYyy4(%8$OXV;{lwSN+dUqJtj->r1C8s3IB8(K8aMmrOIadUJC?F_88aqQ!Wci zslUx(Cg~KzkJ!&IFkC(7+X_`c{GU$DP9jo&Pg^W`?K_xvR0+o5Ieaq z5_H>1sSX?W+g~^KFRO!fqm*q^O`?toHWoTqH%bd_>}01%V;psiwXxVg z)`ODjG2YX`>keM+Iw33xKDgITdPicz^^sVE9IkUkVm)GnnODEw&p{Y6{JCU?^^YX^ z2^sTRTa4!(2uL)Nyx;nBN!}Z^{+&1*nQ=)am;}>H9$M?Ag~Er{A|mcsTl`QE!;N<; z@h~>zyKyZ&JOd!?SW8cl*&W2t!xqK5rfW>ozF~!GGv$iqrBDGEg7*qfalT~(pvSn5 z_#457bK^9EAB|b3TIfU|U@#oueLO$NGp|uH~oY)oOuf{urU}r)oA#Hp7+V-5Fjk|^) zb|T5-n@zA^W65YjDz^GnY>lg6zB70)?3_n%lnmDD4)3IgIT4N !K*s#p&E?mq~u3Nq$A;PI^8u z$*)Mi>@z$rs;xRC3t2=1ikY_r!>I-;F( zv6-y&*h97^v6t+fzHaj#M3heGfCS9}`3wO#Ais%h(>9s#*oU>@QAUZtJAA5|& zl7lodp@P7n`}~Fd-cI_!c(Yzadv+2K`y6DryDU zeAA0V@Y#xUg)Zy!vEe;;_!MhlcOH~p+D z_6aiYTN=ZZar9Vsg_-*%+-Wg1h|S#3@Vh8fHVa+zPmi4)QA&X+v0=)3l=6ZtF|J`YbJ+OtOj~LFlN*rkm`yv$D~q zAKz^!Yi!TbPsjH~aJQY5%CMod@vN~u7kIZk9-lE2APfC1mi@fp4?uf-*@)*Q==L@?1fz z?(Qk6g-X3NAF;7G{;UxtweY#DFRwtzi{sBbb4oh?rL5fuTfUZ4 zrxeA8y_vw;@#=!SIR0!+O4T+t{$#*kJ>GDIoEitWqV#sfhTAY=r14%r@ZG430oU;x z5cVLv!9rQzGK^V&Hp4x~yAJ$_5@U^bCQ)HSi9I%y*lR-x-qYg7ilA0#z~8tlf)6() zgjG77m}Nr=t8_Z?V;f3XrPC8i<1cd%>EJymlujj>f-iHX?xgQ=8$rAqkD$-iY$|+l zXO&FzE171L>_ZZm&LKP{({?75OhdSFSK;rC*k~_@+>8zRhR|=GBkM zS-}I?uoJ{9*j%S(t+CN^*oJ!rGC4wwV~Ny;9ux1eUM+AV#!76(hP%JZchY04v7#>C zHs-#Ng+FVO)vnn|k1f8oI=+)886u49kH1OAhu$c+3w?}kkAm2|e)o^!=i_Til6{>0 z-)9}bE`g5Lo4at);`u9=*3GTEta?#xekGfDZf)H{%wL3n)0ZQwE}dUj3CT2a$Q%Rr z3A|Pxg!$Z9R^HB9cYlQstS%;c6P(-Zat5vIW^x9t>zWy_E@!;Du9=JOA}1wIAGUwS z&}UL`wk~p-cz1q6qig0myY3PjW9DpodhD!Y*R8a1T`g=Gu2QZxft;9ZYv^W}09z|F zd090yv*=yd%pP|+OWNg(XP?j1kkidwS_n`sGrixPzyLed-*s(?Zkj1)oQXLh$j;<< zPply4b$dakZl*bn&y;4GY)ZUbvp;qptnNh0e-!$IwfXLA&d7@KZo5bicS$7I9TstC=es@g2cGVhMcfgQZtl2zw?||ElU*8d z7w5ZOk(mA=Zhig$N{gW*;?B%>^CE6-WFY!X{?M-O+?|$~_134|*4KLweb4WO6U7f7nbTX5`928%Z zWZ3>^TPBfDZEPcLc3OCFr}B@9H0sq!bpXA?X%952$;9U1nC_v(#-FH**AMjGy!DT_rPGyc$S>RhKB!v{<5l8`1PL ze^Dmhb|;-oY5s=w8c64x?eFF&cr9pi*&ur`jDNhTtgVt!Z}!O+WTwu?&+l|wo=Q-g zoNzLoq&%;5CAXQa_LG)r+D>&c<(afbvCXy8cg65&aBcWk_Qq|*S@y9LzTF)XamPpI zqC*eqJ76Hsm(}@sh5g)9^W7B@7!Yu$A0#?Ph7Haki)-)&JxU7k1m~m@mFl+!z_M$9(#Nsubtw*;Icw9w;v|+r!HDGVbQXS z>nff9g{BV@Rv@ZNOjAsszhLPiXC;27hr^!0-<@z#J!To&U&aEgfrRSmur#m`nG>c9 zXtChGWI!Npbb;j!cKEl*XMn*TFjn-RHpO6XQ2sC2U^t2Ch3Y}`s|RDb2FpR#@&cRI z*lI9!IKc!%-D((VC`?RG=dx5LtSAxYb>AXi_r;PUi_8hY_9=8=&SELf4g9r!v>W_I zR@Gofh8~PJWFl0WZnS zUY~qT=-S|Q?gm(JD9tPgm1l&eq=%-ag{Gy3riVi_Qj(rJ!45-q$qNUbe(K5nnN&18 zuFc-)pVTfyN#@8FJ>vu>7#bosg6{vg8Eixg{?=_v|wX{atm7-yb8pFdBLFIUkC<4aC~E# zhGy6Sb_1_Ky(w=Lzbo#osMH*We z82;9aG8&-2!Uv1P#WCUpaf&!cTqLr5>Rl~fAzmXkihL`;ba#qB6n`#0E_lf^5K1w3rbK+s~4^kpC7ZeEkpCq4OA_Usik}2>h&WEXltlU!V!deQm?NBy z{SLclXs$1ic@1SaE9d>_mBQgN)vSIrFPOHPv4H_}{hz-q~L;x|Qp z7-za`#f_r5?jZaY$-Bhe;t#|hiF`>)J&%gN5?>a7Bfcdzi^s(l(R?h19*&}?K7QUO zyNIWW97@3O0`Y9IL~L#Aq*CEa#d`4yaie&XxLdqO9wbA3a4&Ie%rW#U!hwc>ZhZ6d#SG5ub#wXG5J zEd=rAdIx@~bhv3S`S{h9`OUXa&|Kd@j;?2TsW?`gEzT2b#8u*rBEJzb|6Ss};-li@ z;wz$O`{M)2ABxF1v8X3a>?ZaQ3q^jrW4Z}qxj0MYs0YTI>l(2^uJN9277^*ti)6JHQt5`QDUC4L}&D7J`Sik!(oy%<|;$Px0C^Ta}N zs8}kR>ni-5`@!_H#n!eyRw=w*+#ud4a<&Nb?-G9~dNx5Gmh9OCd0sNt7ht|4qGt=_ zkCHzYKNtTha_R_wA3BKUx(xYr$$i9r;$U%@SS*%`6U1_HmN;AFycCvKEpk{rqKK~1paSJ-Xz{38k-~V|3LDOL}Pmdeq(zC zd{W`hh@8s9@{A1<@DGyT7e5p~5x)@sCMM%ijromj60n2hQ$$V;VtjvbusBRSTQoLH zkZzphiQ-Jr*e*dlrxsD)a&eVdFJ3KPCw^DlCf+IDEz;%#_3agZAwDL)D*i_N5AhFT zO#DdXR3_>RiS5KrBBwSnexS&yO_WQ;iQ*KoLYyOV>J!t~ikFM)#OuXxi<}$9^mmB= zCjLO&D?TJXCO#=XFCG$K6MrkdBfcm8QT$l^Qp7+CQx8tgVtqP^TvLW}MC>E>6N^O7 zT4B0T;uvv)ST3F~UMMaWtHfGywYXl~Aab@C%lV#ok9fcM6Y*!_K5@VJocJs8Rq;3C z@5Q5HO#DdvLj0S^>1wP`M=@7CLmVjvc1BjP^sRq;1s zO#DdXvkvtoi|xhEVh^#mI8+=Va>5|P8Vm1i^VFjMqDMX7dMCv;ui4^@h*{5PFcT)#RKB= z;w$1|@m=wl_>uUT7{r6O*?%!h>?r1nXNW_^5n_oL6{m}{M9zt&{-t8Qc!hYaxKZ3H z?htp2_lOUQ4~vhBPl>OHhsAfrW8z2RXJQcVB+UMc*6c+d`0}N_&br4c=_Ju6Y)zCFSSiLXY?|@ zqj;)#y4YLH697^)5J_MTkI((;@jf8;vdD2 z#V!O}ik#^81cNTL*JjNND$v9oj^Ran9i0es&GSbuWxfzfDhPbUa=Lc>N3{5_y zgF2I*3!*Z;u>}h~&3sPu;!!v6{o_KWhg>N=`z0Tco`1=0eyTQ z$ok>&Ca&*hUmkd!KjU8WYEOwT?rxu-=`P1TM?ap3a$c4@A^XcM8ix1TNXK%aI$rLB z@OydSb?)wobo9u1aX*Ia*SB!I<3tcfeW*fQ-!t%gdEj+kgT4qoa$ek%kp24BLm$hf zKKy*f_5BupFAu!VpP+9M+S+qo+-nH%>)Q)`yzWpRE;(_1N8x84Zy#s(a~!@u_8jB5 zeI$D6UdTVWZO6YU9bjDXK)i31pYZzC7x4S*_q=a^PfQuvPTm%(3(-T0} zjgD>PtG4qc^l`ku=R7azo%4*F4SjnM$ZtXXUeEevVPoC=`ZCZ@{Z<`3eFKY%hDCht z>ufs?8a(vOLX&=8+S)M_q5%wU^<2O?BfdWjD;;EF;(q^^<`;W^y+~QPE z#ou80!IrFD2TIGcA8W$1TlW&CDSrBk%)kxNo4yDIqDVcnWpL!+`{BU+%Bd!HT-M`F zfy}g0r~3B^S>db=+~^d_NdC_9;yJtsoz|up}aam7&;f%R2y6%gVG2F|NmSESZEhDoIzMnMa!pfyN zZLmjAeGv>?A6=4F9(Ax+PeTeuJ05pRQQMGn>-qCWw~Q!T~X;XlE zUCh0q&CC+$%PalGi^o@+mU)WPd-AbKslleq(3>r#`=+%7GbWWzZ7IsyI|df+(jIGa z!lPS)_fKmna;8)Unw+%nH93LDTY|evT8h?Fz3x4aQH$9fNB|0~HZ8R;~t|7JYV9Oc14z?8RnbQ&+i53o@*HV~u zL00P4aanJ)IOT7&1dG>~y4VZ0{slQs_sPeql6hR8{K7@6PihG+T~j#+d5W_pADf@{ zEcVQ4N=v@-i{O3eOZOpH8MgGtz6jlSu%+Xkm052h^jq^DYD!Bhk1lCSPe-qt$)mCV z3wLJ!=y)8HwCy~4=R#-7@o-8MM{#G#q?W?+%;VwA`(8}n{w8Yc?|09wOKwcwKKIzP z@TG?`w;ycjzvs73210mFWo=JuoP6wn+osX!nc9e>y8R_sRDG!>xG!~EW69Xp+iXim zj;w}Tvs1TS&{9~;J~-Uzt^T(v>jm__?7+AlILc5j|N@6Goe^!sqbzc(ePqR)+~YD!6)6n!3gxL3igW3WG+k2}%i zXxVWmWpvB%YPWRayg+GqYeUH|4*w{chF+M8UKkjhwmE&<)-kD#Nw9~RzIhk=A$#G$ zmh5$jo_N*a{pg9agYC9gfElJIPRI;4WuPZU*0Cq9GCgsR*Ar9!Zcl8mJ#nPJC#K@u zKu;Xmswa+QPi#o^#15t>hHXzg?MC#(c8w1n-4XrK(dwUGy(SoK6AkoRP+uH2qJc@OLO-%I`A-x`PqqX5+WVaX)2Yx#Q@iF+sQg1@!)vp#_)Hi?>LQJKPK& z2nWr#;b6+mNQwHI5jNrAZ;=AF2xkGIMfelk7sau20x!XjZ?+JUgFk{lB`X*R z-vTlGOD1;13{2|jgqtY_!*>}mDa<*c!H`5Z%q4Sz$>D5-CiM)4@dN7wQ^MVh7!E(h z*sxS(gfC-ks>DnuL`!kOv~Vs`rv_a74OHNdoxe&NJ8Wqju@ks}B}5UH#_9!TKaIba zQ{*_Fz=e$6PLU-B=CJNBVoP7*1m^FArzhgmt0Zu_mGny_Tt)%rz)Ug@l1jf+!qXI% z!RFcmYB*{nm|QawO)91@nJ{;`J&bS(1vbIk%+zQkG=Y)cp~NOI(iJI? z*aW}lj@(X(O<*K0Q(&9Gh)Zw_XPdx?yC9k-cqiTj#)l?&jm^e^aTw-+?RBIKM6i>Y z)0{vlJ^W~9+hYv(cr3)WyrI35c@Uok8*3|(>-Irzgd4Xf1rAp|#Ywp|kWp8SMh?1I5X=lw{BrTAGZN$&^~UjFm~u zfT$XHcGqYI&6JmkoDvw<)^CI@%V&hGozDoHvAzb`X&2xdq7J=bhz+gs^>)xu0Isp$ z$|WbEQH`-1dd$EXM39wU1QNv^4=v`t5;pCovE)K*2qu^<$=NhEo&_F07h-xWq38+z zXsdLpPic8V=~SQ6a(}LFM(H%-RcxFdJ8dU@htM@pgJSBKZYnm7pdOpZr?D96jYCH; z{gx9^Uw{HLv=V;u7&grPErfyzM&C@Ydj4svN1!3i6rGsL%IfSlVVg=h_~}`rX)2*w zmU@-JoLVF{&MPz7se_%C9z30m<7u7y2wRuwC##FEY5a$YrEtIe2VpA0uD}MRd59`D`pbz@Y;ByH%abp62Is<@U2@l* zF$A?>qk+2>PS>6EcQXfg7SS7<7e&9VA%CNdV=+gtp{R0X{&j9BorG_A`#uO-7}dmVR6I6ds+$*ifda1O60ay9pbP zZWhYjYpOBR>%y=eRi2z^(;6hSE(+X>4YTSy#L;fNO*di_^E^~el}riy{VkE1x)o7aKZ&B$0(Ra zyo=3VTj(>xv2h8cj#b#ue+lZbiF_KzBE4}ut5^Z{<{sGBB5XK%dyOHb=@2lBz8-kQ zdOrk`p^(YR3~abYO$Rv25ngW({HF&SwKH-ujC>xODTJO^u=!3k?#a6d;+5b%@BUt)(VhrYLYnVQh*x=GB58yYsUMwmhz6gst23 zlhw`FB>p4ILcIe3uW`JhJcw>t#8_Vx{kAISTaEU1 zn@y9Dx-5I!9En`#c4$lm%v2S0cY}j{H(H^W5!dYq+$`&5lf*8=f$}7t{tgJwITr2?SiT6LHDRc@MB62x8^{ z%Z;Z>GeKqndUqx37T~T1tj&OcwRm82u+9P3)IZS2_%Abg<-dwSv>RYovwwg-8W{+j zgFi{`qB{5V#qNcX)7;)L{D3L%_OF@jo@aI~c`*{Y$!>ZbIfH+4`NteMV;I341k=#Q z96=sqO_=#((wQ0ft#X*KRyodQUbk{2O;ju#>8_cDXF7Z*kad%{0_<64YCisUxZNbK zGxhQeIM{A6jWOmpjxuRv8^N>nV2_nLmF;5INWn_e^-Q&|W=5agfz@nCSDnQ)jPaY! zm@q_Ox`=zJzfb(5zH&-r$=q;3uTuw_W@z-+)z;4JgYB)h^Lv&PO#g6QYaPWL57X>! zrrozSW=G61v34ic%%XB*Kf>sAeVyMJKrx}Ft|nJ4lbbehSHQHvV)xYh-8m7rN5q{U z=|@n==ilD{9ePQ~*Grys0zJAF^cdTxaQpzAC`m5jPMlPaIpjl&Fzr1t0X{xwv~X@> z-EvIa=G<^z5s?&`1{WgJ2{Zle-2K`m%Vy4B>L4BB<8#t4s+?crSPKc!`4`nKuldU4 zQY!xHJob}Jg_orOQwd+q%|v)jNcUM^h+A}sSA~`YPP&Wn8ho`Ho)Lrpk_7_0`|%m} zD~1k63wbRWw=Qw+q#2RXg9b&0hH9 zmo8YeaN(kb1BMJ(P=nd_m64i7tCrL*S-vb%SX5AS=Fkfx11`O&dgV7RT3&@Lj2Cf{V?yY6E1Oo%rFf zR!cY+MzQ_wKVy@CuLly(9i9B@P=0HUN_ z<0RoJ9skW-;C3QggmZ(D#7V+HoL(e$#5^M*e30VL7RQKXBHM%cXNyb3W#Tu*b>g?h zE#e*G-QthMUx)|9m&GIEQ86ZdDt;+)mJ`8COJNH(u{kiJRsr;6trOX_JWb`|@IL&Z{&?+TfI zp2#-?lrI;rBvJ1hBsWOjCfO`?hWJM)r#RMvFSuXvFDSjSn+m;eNkE^*{3Vm^sz&L13qp6d+H7Q2eQ#J=Jnaflcd&H01; zQzXw2Y14#y=Zlr%Qt@(eop_yigUBzv%+IeFf83;A1;uMw{o8^kT*4sn-gY=I#EPbBk8H}yX!z9haX^4mA#&G`(PceUVW3jeEU z&ToXL@;m|Ch#kdj@pQ4LXl#if{Xoh5y3YKg#Yv*M9w3|xD>D8P@fO{4GkhotwAR3zkNcXDb zH^u)DIk1D}d@3571CVj6X2LnrgYliju3|*=Y`FB3JXjnio-LM$94bP6GsO$V`J%A_ zgZSl=jSU#c#s&;{mBOzT8^kRlN1sstJ>nj5uV`$;ApSAQ2Sj7D0O2o7Hnw9R|4#Dn z#baVj{8aq2$N?`bzm3>Q>>~CMdyB@_4AP$|d6YOt{5snO97egxEAeHKLv^VCJ@I4lPvT!irs0++Ui#NUe?z(oC@iGL9Tc+_KfSj-S}#BO4qm@f_y&lIC#nb_Kv0Y^-+oJ++zagDfE z^lTd3D7iu85G&^2E#4mwjhh||RLL}T*+{soekh?k0W;u`TP@mkT?K!AUf zWSkEFN9TSO>;7mPf7fsv_VGG|viM}qxnAD=b*bdBB+}JMUPB`NzclwNKCSDd595hx zVPA>%Ka!th{+H78iT;21G%z#o1&7AGHvr74kfi5uduCjB{AD^EKI4i+4~(PhgAHB( zYt!+)L+f&R@9H`H%o{fzfBorpAPwKqc+N|=s#UsN^sfRh5$L6>fuG%|7#m$~ciZ>) zUW9#~=~#chxA67}US~S=o#`p@#Wle1*EbUShNF?Fk9s_PyWsch(HjgE*2o?#r+VnzudZ9Jgefh8p{RZtj{S?>tF8p2|c%7}#_d43zb6(ur2=MEB z1^Rf+pgumnd+q!Y{LJI+;|jbeEcZIC;u!yzR_Pu)?d0>e(vaAz1F|>;-Rv?M^wW7= zA{mMN_4~lLznzqh{oxd(Lv^Z|m+k=cooT)1uU8iL$yaTsD|U#_(w_6Yr0;Zaf~TXu zAdv4z@p{nIFAHSd{Q7oB@Jxs>&w2U=77Z(i_}tgc+bS$97;ZCs{k*O4R8!hl=51|^ zUbkmlIMm>74m1WaZ|nVaPScf{POmPQdGSPQN_17Ue>AD-%JlV7%>URmyCt_^PD^fN z^0AB3mW>_#@g#Thu?68kwB+MSN!x#%h8Gv%V}ZctA7_ttx2KK1;QT|Mw?E$Sd5@1K zAWzRP0w0b3qTTU%gpZ@s`A15^i&0MZ4 z*L91o439p9IWDOo%%piLTY5#Nwe;CDt)+LwOEnsPFJ14ly3(}b zJI2_weIgf?{u=cP9}k3sgU5^sG(P!a()RplFg^z>xH+lufx}C~p<5Ssae7WZ_GD(D z>6(-qqk*RN!EF4!Hu+L#c8eSNk6AB$-mYcy=k3s{ZltuuUHAE{GXP$A-_&(2Z`%+Hd zW_`Ks!!7?}O+48b+rAjvz8u@$U(L}|d-b}ZPewUMRdEkE0^9$_jj_vPrEjC&*aHHL zZ@X~rruZe_f6Mp6mQSJut#`5IL%V&x*Os4~*lR<^6<(`5D&kwu^zQE0a8sv>ku}B1 zp*6(?*y3Msi>pwd-X#;*>+&L`n--A;` zULXAXdw50)&rl%7CjAJ8!NL3&j!ohduwXLIZ9&_kXcNoewPL`@fP(`km?rBFv>8B~ z!%v_lp26o!!Ob-6eX41)%Amp6q(37a#wIO7iuBl|pE3R8h>5$gNj7RH*@wwFd2hUA zB!fxh5N|h)K|WTCm)S*YB7UVS;1t476k0iPdteohZ$_pVDuI8e`*#sMS!bN%KMlCC zNm&IM&u6hUiA}V9G3y~jaa2+kEnmvI!Ngc%1oF+;ATdbr1w_WnS7yI1wUiQl@{w5`BghE}in?v5mLVVCS4C9_AJYt^=oQ_0pw z+UCM8+1+YeE4FQml9ixnSII4rWG?KI{mCWE^Z7wh zP~c#wDNJ;ONf-=eQh;jY&%j_PllYz|f5sfojpgFrlRwh)EAx=`!xS)l$%K};fXW{( zVFQGQd;;%Jqv77T4s2)xpiJbnpbb}}To-#a!~*UclcC~4=da)d(2b@+^%~+DjYWV{ zDHnzYjpsvyL~Ifh%w(cdu45uTcIWh-jrcsC&%_EtgT{xrMBL~3vU5HYow13DIItlJ z+eUvvpc4~?294t`5eH#0f!&>)iNQ!-qxDc#Fwrj&IEINrgT{Q9s5bHP{%)-PwSAe0 zZQ|^o)+RnOD+~>yZDN9Zndth(OvE-_z(j1*^-L5RMB8*f5^#6$Z|3yW)+Rn%vo`&j zi9&;Do0wo76W#J&*rs=wh;7==M4>^nO(lqIgVld|Puj9gah$`fO?x6x7#c*|#02e_ z=v6*pv^E{cL~IkEu@!~}(KhiVa7QM3<76h{b{ft^+)kVnqA)awwuuS4GSS9+nTXqI zF%z*(=Q2@f5N#7*R}E&OKWz=$#3ymqCQd6_7#c*|#00~cXj_uD+)jKPXKi|ei9&;D zn?@tSSSI?E)1g|MwlKgp@nKwHXb^1^6O3n~&-lF1+Ejp3hP9~}fx^&0ZEC{f>3$@z z;ShWJ>zIlgsw-3RfZ|-Eg`tHS)r6;7rdY?K_VW3Run{M>pUPCL5IB*kLW{;XLxU$j zEbDos*uYdJK4(3(vMykNW!=nFp@qt7%CeZ^H%!&aw{q4o1JP(J$DVfXyv}b1WoO(X?P({^>+N@kPtf+Xv*!&Ke>-J6ecouJh&kJ5 zGn_wf^?5GLzcsUFc@}jd zr+RG0)9CHKhdM5zi1X+jejcQYDd9xwq~B4(nbb*>(7L_sRC=e`-b&fdrA~g0vYkww z+$>*PWIP%(8LR)S{|H)WTVxO-ZHr7mpfI!uw?0p&OfikAe(itGRNSZMGZl~1)0rx? z&_3OSKbSL-Vh&Ti`d%2c66V_vVJhWvGA3Z66l4gN%?dJVcq znCck>US_J$qVZ;E)UMkQSj$vf{Y#jNd-d;`ihJ6ZOch#0+r<>TB=tY`pJytzD+kXn ztX=ycP#9W7+ZCQN{p~(JsO8#K##C%qU#1EzqU{RLnf|AKccx;yMl%)LHG`=_i)g#( zE!GOF-z>)YORZg;?8n-5K2wDj(RMM#0Zg@b>^i1myRKy_ZZFPrSQuJF+tm&!%9v{3 z*l)vjJ;zjR*V{}LT14B$6rGuBzgPkGQES&HOvQEuxZW)cEu!t}j}(KLs#C0jskpuN zfU&i!EmMUS(RMM#P^Rhw&?4Hdkw`Iysd~ijW-9K7lbMR`TEJAH zMYLT^F^;JYj(r%m>nx^XyDn#{&?4HdJCNdTrkWVr7ssHr>kg*kez=~gLJPI4>2gFc z#l1{5Ic8@(PPJ%fybqWnj1A9tdHxl&?TdeIgv;Mx6?>dDHHq;4;#1P z-x;m*1~g9QhU3LK>f6SibF?^zhntZ1tz*m^)E;A=Uwe#s!`fra3--a#a1K!)lY5Mn zh}X{@8pm)6P?(DW^M&Y+#+{+TxhGUaJr_MO{ebeLI_jBXile-@0&JE8Ys3ZqfV+iUx3w#KH5#(24 z1dLKkM}#o6i}>7v;|P~*)kyMltgs-M14abGAhqBW43&*|cUg@kwqV83!~P`maAU*L zxs45Ta}lsgEiRYgglb`fGF>f(hv_)3F_i=Xmuf^_#dtX439JEzI8B-7CCDVH@7*>!LVGScA@>p;lEE!F%uvBB9wS`Tk37VSPQVhFTcQwGqU<@cm zCZcX(%d({?PPZ=`m4{VPhZbwX#$|d7tVLl9X|lj8;+Xxgu8~w>q3qNgtp9lbV71+D zz|d*duF*T|joSN4n0aKZJ|5h&EF#qx2>%6Z1~!RCzFuS{jM`ZV(H@KL5zMWA=N|*D zEtBz|7dUz7m5wFLyrPR@9a>or^P(clTNpQm+2S9~mtw_1#jU{15oA}FVvTuKwm*$V z7)g;K#_)5Oc>-$&rk8=py=S9|iztSh0J$J9TUf?StQZPRuy)NcP`S~H6({YjN@iw% zUXuMqtDJAX*}J7x_6``uP+8J$TD{=}+t3K=i2Vs(OWQSA-z@A1b?R8nO0l}ECpJjB zx(gY@n!BBT7_zoy8vZZD3gf#0*Ck`xz_6>!VlXh?1s?)j@-gX#rw#-*e6i&;f}Ok0 z1)Ctv3QImTJQ6WRcTnbs&@q#kUB1E=%Y3ZoV9h|N*ZymGh4)HY>kjOE?h z{Im!=TiBLUu%bQdfafhZf^A_#pk-T_4nf4(!kV)mu*sal?I})`+OQP0VMD95DrLp( z&2~hxPpd|R32C8Tudhm3ad)^IYouOjRjNcusX{d^U#oAOR@bs-Dm>l8QR-&mh+gfm0K3`= zOXAfI^Wj;>BEmAsQmi;j5}#Ud96@uO`bUPz>RjSPepX!iv0-}tUW6IAU#BpO=r0LM zP{B6y4=aY~K~#r}%h#rs*#@1vd|~K0XWT!MZ|a(biJ6d>1SHRQJG*9aEgiWBvTA`9 zM-|b_f+LBESi@;67@p^%t_E(f;Any;bj;ik)+BOm!UA#-%ZiZ8%yP^H0P~=lWLq#@ z2aIiD^IX+N6F)V}RYO;TOobt}wl=(o z>4n@UVvStHge9dDA&1nm>mA?}tMKTs#h4Z1YG_=8$A_8M-6xo>u3i}ACd|Nv0WMP* zWcOf7n4y`F>vR==9(6&(AbSiY60YN{9v4B(z;82>GwAAabXb?jc0dlP-Du}qGIgQD z-sg{xyU&?egcP%jb{re9LdxT7dn6 zq)02bo&}QF9>XlZdN~DE0e3(~Sm9k1-WTduBT9v9- z`XVD`l~$#!iqL)B5hB0tp(=VoL6ZY&n}w>V84nF^+enY4RjK;ZLOWjA%CstF)gM-F zJEyKF(yHNMLRt*95>r-bRmzHoDR;6+z0#^wiL}sB5N4lNrL69N6%Ud~UTIaTL|WWi z+bXR}S+SUytv@!FxbT0&L^xle@mw~FrA|!!|3~p{GubWd`+s^O95=!p|1lQ2vi*QL zQ)VrgTIo$Wc-*}CbB~`tX?Akjj9Hac$u^6-cWpcF_&E!b)i!_}<6@KH2>)@I2*9QR zIHA)x=ntARcTVL&9A!OjhRbf;>`7Cm&zSRX&$enwmFMao*4xJBPQm;O6GE5Z4qe>z zI8$a<&8V7i{FEuB)4AtNm|8h)(t=sl6RMZYtE_?!k69;FRZqoedkDDPF;X6iCl-_{>K4tU^PCsPT~_60DQV!+R5`dxpgNY`&6{G=i|k9qVE(J% zzh3<3Dznhp=7&W+S+^D+q-t@dYMidc!>Yx@%Uv#yd)5m{y7;XsK6jc@|1H}l0t!;8*QQcGqFH3p zsn5`aGDo=*JGtDC8WSuj4c2?pf(MslULDjjxPN&v!cvo;cP+`Zw3$J{1FFGG*)F%C z;f8wOrV0vdRvOIzBeTb{U5BL1K3Qn#ir5#y@jc-lRT}(x8RpO#h{wlxGphaV#sK?U zDK>Dp!~Km3zTf_KYr?fS)mi5)4y$){AYXGV1{f@ z>&1}81`ESo{@jEsCsluFLuJTel*Dtc_>|9@_68}n@GnM$Ki(JD7yYf1^ya(I9wGR8%L)^EX z#=i9{>|1G;n*5UOTfXgEe&#UtTc%}~*+#q18LCQZ_qlC}8MwWy8dLuC49>!AOE)j; z8H-1DpPfyvGrzJ5Cq6GV?F>duV3wB?I&{JR2@^*2?_FNrp({g2jT%v2f3kxPU3AVJ zF?YfIDV6O>OyG(PNs&~1kX@}e-N2z&iZ0?xUOfvt{^gz3qfC;-$ zo#~XKbJe8lQEQ%~)MPicwCsm(9|Xj5@I!4qX8URH@-ZHEAI|$UY{aNO*nM|0D^tDc zcwp@Ib(>2k)kWd#J7EbHQLA>TIh;b%I`byk#7|Z`n}etBMorJsgNEoh!!5@TXO13T zYVLH7o<8}#V%uzro?3JI@RIdg?ca0uyiyMrUfTXQ%+bR!9W`jXTX4}BS#~yO5A9z| z+1q7hD#d@(96sECqI39g|M-4$_}C8zo(z>twI8B-rv#pIc6qL-d;EooaMWT-GR$^1 zY1Rbl+#Ej5G>6Y#n!~3#AH;bcza5)LHs-S>M~)++3{#uCWPh?ybNE2tLGg!)eMR0H zGu2YSRa`0BTolOX7Rfd@4&q>b4kFiTJmD?Oxdjz zFP8pN@n-RM@gWlRd{TT)`WMA_#P>v-BLen+qYQo{eSo^K-i<|g)|lLigk5{Fv-I7? z{^B6Tk?z*a-{wRB^WY&&xw&|N*j=m;M~cUgC}*lq+WyVxMd+3z7XCz1btVtcWZ z*q229gGBnX(SDRzDb5g=kgz{RJX88}#mhy0LWKF;Ny7eq@nPxLi!Y0>i+>LNF>`s+@776<+#Jk03N!Yz0;?+#anb7x^%)x=Qv+Hm0ILVX5>EbMLfw)9mA)Y3lE1oZ2B3>rmAl@S0C2}Md>+!I- zUVKJ;UVK%wIXPkfuH^T{PsP88UyI?~eH_rja+-?!h%LnqVi&QGSRsxQj}fPfv&6;X zNutf&iSo{ue1mBBAJE?=`Iq8D;yRJz4Orjj#kzCv*?kM*|Dbpdy`lYfks~)LXNfjK z8S);I_Yqr)ZN>0@hx45hfy-&fpUY%7+D;r-Mhk`EW_-dAy8FY}uy+T3~w+uVBKQt6k8 zXN%{FSBo~M9>TXuzEceEuO5(WbLt^%_gf%G3$y%<;!f|g5cjp>ZO%M|8|l6zyuady z|CkSl0+ZcD4)>)zP~@mz%EyTu*-LqzXmjF0K2QVy135;P_HT+D2TOUIXmis+wz=s*4tZsK6Okid zDYq6mxRvt3;!)x-k;7OSKV7u@B*-@B9LNEyjJNwFkmFV<-!5`^DrLJr0y#>R@|&XF zA0hlHWvqV_zZNrfpOGUr7WWkQ6N^QTWMw{fKLs8xxvw}-94w9&$BK4;g>!9%F8!(Ex#IcarQ($$N6xZ7cZv6l4~ZN}%lHkV-G4!TQ*zz=ukF%*A!g`4C0lGH zHWgclNwKYXkl0!5E*>u0eH!X9Q1W1LtTCi`l#ng4{r~`#Z?{Ne=JpI!La2e>YV6 zks^mQGe3?;B^QWG#1-Oc;!i}o--G?nBwsFGBizd`+x-|MzF< z?LH9c{wDcrFdt3hWMsp3o$8n6Auyli-Sat zJ!kq#ah5nwTqK?-o+6$ho+qvnSBrMvh-YY&Ja!4xUpAFU1`q2edOjU*x!U$}L3>U8mex69-NIq;nFjUvaNQ+`0?5Oc~eiX3T9`FA1*lT-d&{-912c(n#d91l$VGc^iBCEBFBDHUL$gNH|4uU zj`F7bq{zYClsAeT(@lA+$kE$;Zt}I5*~rN`VxhQ)xUX0wa;!JgcNGs6dyDm} ztjNLPOg~$!78i@l#Z$#|#Gi^6i*V%5zRzb}Xvg|rgf&u( z*Buf34GWhrZY31a{Q4t5PX5aLm`|AB)d+`a5cKlkGr(tM;SvVb-pe2mk06Ci9bmUhC9rMFaL@K{OA{?ec(EAAa9Te^qk+}B|j^?)-`SF~={MbEV z{XRvQ`LQf6dtgWVS-3MoW*k>Or;Xa(-u}DIJuv{*g$ShjFwWuHK!203z zm8xIexo7WzQ4hq23+u#%ZHiRe+l>6a%iOb{Ais6UgnmctAJ(q`E9(}`Z*9kKn|roL zr(|T=)!ef^I(6^t%y&Ka?7Ac>flE4fPH2O^*~2Ti=dLeSEI(|}Df1qm=dUQAIBrAZ zP0-ApyrCE9&26;-(uB&F{i3b|yy8<%e(dCvCpN1%t|oruyd#or58061)T?doh81l` zzML#7t!HuIK(F}Zlh>boN-9mPsC7N7g0@{=9`gB$`7P_kpa#oZznmy)Q7>jiQL}ow zf}$q%bn&8sdb%N>eA9_7XjU=r2!(( z@nTBxcr{Ei{I>WXud>OpGCyPUviLq^c^2OuEX%?!@BKLIafDCGT7tYz%!(nr)Jx2P z5I@o9xhU~VW^s;x1CSkO+p^=ABb-x!e!3Q7?hYCR2`Xd7UTz=g;_<{v6JxpeP|T1R zTVlSK;4}DmPJ-jK@>pCRtJA>e zXK?&J)<`JLvx@ZjDuZ({_GKUr(}t|7U(TeNP&H&#{eG+|zvR`BRrM<yE@WyA>XyH^%b?FQcd*RKB0r8fe6Pj!l{D=`}8=Qqv`@|)+6%53Bh_X|)&tOReL4}%h$cn>$9PwoKxJjJEQ&s|2`&xMXN{cxV0 z%r-Dmvao&nxzMG zIz}hnX{bqikTs{}DX2YdP$S@Tzc{==}B|=Lwlo3R|pu;OZ3FbLFz--Ff_(R5aUx349#>A1RLy9 zFtp4?5NxrFU}&|AAg)S9Fm$(zAns2^F!Yp*AlPV^6+^#u5yWP!E`p(LSkVrG&2|wC zeeEI$w%*llLB z8Oq;dMOnl)th6E7Z)U>)dkr=SrIrH_!U$eMT^0+wEQ4KE9Al>qa#jR83z^5r@mOWW z;6$v*i{LIrt8mwn75AnEAk@L(LwEw4%pEPofWYREAt}L9UU;Z5-ai| z*uk_SufnPl8N3B6tO#{gb?$M+ch`^+#t# zsH2109UZWvYn&DFHCEQ1Fh$2&DKU22m332WjcsY!Mq z)WjuHlUF%4u~ur5T?A|538_i;3Dm?!smaY)p(g%>mGvgsYN&}CsY!Qek3h(l7Wf~t zP~0i-pC-{IDi^>w<_&?xKRrJAzh)%zj5#x^`E7bP9J${3WAspZ{4uWZIA}rRkI~0A zgkkRN*>mSisGc+#1F|tJ*}6W%HUo|WKWLc5K-Qw7U`evL^`j-h%w$lQg!nuL9p1Wp zV9=?$D0l>e57&9gmMsH}Cp~^(aGx8D2zjq$@WAqcCBejG(1NKRQ>tgY(z4)Yn7wYv za$R1RxuJ3Y>_^=rTh5J@ObrIJfn&7>G1ea}wv&-)8mO15 z@yLE7Moy^cJz&tN;r-D3)YvwSGfUa^9)85AqxuaVIicV1;X{U}M{uUkua`un){jVO{nHq&@drGRm zMvUs)x8H~n8l}iyNNw{}zgR$fKy(~s-O)_x(S$fS>PNbhjz!czPzE0{YEbXt|IG|?r%xtl+s=_T&%M`e&7Djtz`1fjv!5YXWwf| zRq^{+k{a+AeZvx-hV2)i-p>N|{OFH}RVLO1w{fTs-ytPLJ|m%AAT|;0Nf`9zlM5!L zx2Hdlxdr&W!#pzIJ(DF7WwJdtLnIzq+q<%M(svLK6Z?q+#iPY>;tX+~XgY;L>nuZgX>hv;2&i7XUU&SHeX@H2RP)cek5cYQ;2%BklaJ@ z<3u#C);mkvt|*`TD(lfqv`bycC6e2SWnyQsr`SvEFAfq%ietoy;#6^#Xxkm-EtY(e zc$#>YxKg}GwEY0-u9ke0c$;{Sc)z$#d_sI)+#qfg-x7Z>ekgt-elFT^hVtz=0~>Pt zfQ`j{#Fiq@6O3;shQ98$KSAGH`hKF_JU~A}@>p@aI9;42R*8Hp&T>u`Z9jzk6Ui&Z z%fze1>&2VJUx@dKkBEqIK;(6jF;$`B`#p}h}#n5;D z0m+YuPm0fqFN?2>?}+b-ABmyw{1=k{D#q}W%XVjq4a7#`USjA=Z)1jGe}MFS%FgtM zisfQ|k>9Xn{77+v82ZxBl+4!?w6n3p;8Mw_i)V{AmKgCDNxnk7TD(=fQ@mGvKzvNJ zF~`W~Wy$7a0lDt??mtxgpTsZ49U`WYas4OO^|`mP$;h{*;@gO!?|o;&G1LfW z;lhjk(NJtGwh|8zJBnS!x?`yaNPm<#QXC^r6m5((%KMSzx#A-6MDY~y4DqL8I5xn> zUL&6?rLQ|iV6F7`h~Zd)b&{VJ!?6ReO181t$hYnof-Q>QCWd1P{wmqea{ZVgLXFq5 z>FXVc*3Z~|jpOwD)P!iL^{QvgPb^ph{*IkC~f=!gVGwh5ouc^X? z+mTBh>UM17_%gf2|OEdGv2+J#FPcRm!+{MJVDi~3Am54$i8^Po?A zlb!VQ&+Fv34*79AvAw)|58Hbe!px8R1DBS#=Q$UB9WIRH`j8mf-PUO5K2Fcd4zQ&K zZuO^!0?2Z?Jc%{h-e)51<4M~L?GM1g^YBUw+{JD zgt8g*VY%EstXrfTyd?J13DAX0m|y!Y9XmLmCHH@~kJAs&$7x?|@VshlIo`z8UEt$1 z85`QRbuAw!=gJg*gP3kRa(Cix5Q~|+6MUR(kZj7w$=*~YvL?ei?c+3xX*qaWK2C2l z=)N}GiACGjhT-G11A6#4Wudz8aiW72l=5-m zDhCF?g#4_@%{luzT#=4T2KzD9<4W~BeVnd`*{0CPiFuXuEDDZF1|8_b)F~PK2rf;x z!{%kPX`Bp3M0}hMOa^@+j!Fh?X@1Ce^Kr5nqU-xOVVGgPsjBO{ZhWhc)A#dkvTcSf zhdJP#lT^y5=zqq?DLpxS%1P#j?(GD(9ovGb3fJ;_s_nkBw36Q$iTGyyU-x|4xuX(x z{rZkj^-Xa!%$ZVYb2iuYjH-X2?Dlcm?c;=>=O2)dQ=Li8>-#wA7ofI}6O{j3e4P5C zzv+uJR2(Hv7H5d_#6{u{)yIj?=-8gV;&5@2I8R(I{#4xUga{a*P!|adUK2EM5cKbNFe%S5fUC-k*l@4ieZE?qNbRMU&)I3fDHtb>Z zI1Sv;&E|1xZSy$w$2?AL1`YXS+Auel(`gg^N$J^~4%o04W^>BKY)+jqo6~-TFB z2m6Bahr4;4PMa7@*~f~SA!hopO&=7rZDI2_HHExz*vlVq)~3dowW$#DtYOc7u%f6D zLe;~b_@JPuAwqM8J^DesC?BDj!yfpc)@)3VTFdZ{W4G+t1o$-$JUj}hH6POy-mgGM zua?gcpT~*#XGV`ui-eJ%$vOL-XHqyHla1txWpLm^kc&jIa6YCtkqQnzat~K`9~2%nV2F9MvH^#wGTd1*QX%)Imnqy&dqyiLpVDg zgKc(vCBivP;$0F~K+N3+Dc((UD|xwHpo@1;oDOZQIF@@O#U2ua+zrg_;6y&OoOy|T zA3r3~#Kc4{T~Omal~rCY{TJegO3csYWX=7)toWG-HDt~GauboDU?TMX5zN&` z@1Pgh_(nJ3Uk5}NUIQH`Vk!nRiVlMcvn(ZnMz_OcWXfbeukmd(IcSnI=?mt~3CC1=ztgs@+Vx<*15i8UL zyOip%Yn7ONOLtFL5o|ZD$kUvfQ2S%#MNUny-LxjJa%w{DkC8VyHKF#$$h)1IQ2S%) z_OlhKr6WR^C57mT6~)=~I_dTY3{G}dgc=&uZfGzcQ9OMjFX9QT%!_2N?_`ddgUzID z4aqW)l9(wq$xNXR^XAMh%-n6p5eV7R0{>$ciaQ1V(iGX< z3KzOJG)e}GN{fqwMakkIJGs83D41Frd`ORjmI0<-xfE^*>%HotmVH~oU!gSE=wjg4 za69}Oih^l9;lfZ_)-t$mdC#DDei^7k+Z;zfyG#)8r%F;RR6V zT~qxOdI^i}_?O%jcIj26EZ`o6>3Vf;le}1}^-P5etF4d_ccH)$4 z#=kP*%J*`pac2o0H9r`S1YG*!XI;K(iUpJ4+GpTl^ zf7__~-!zX3x5v)rQ5lqKo5rElKQpI(r2fG?D&c86>SIv%obSFL-o#BI$2n6rpltFr zaijQ_xLN$Y_@VeGF_Xl=EbfPVpN=7DMr3LHSLaX}uY4-SSz?uVvUrAQciyPSRg$k4 zZxeqZ+SMTJo|pWJXzhpo9m&>i$Xg_H8?v2WiUF^oAlsN5u#@CIqJ7s9`iYX~ip#|F zL=N_0J{!jSe&Nyr@~j}p-w zE00GP%eCzZ7D#R;+BGKhC6e2SWnyR1uAyPqOLBj4kT_19DDsH}%l(nKKwKiO5Kj|- zDqbL7Dqbn_zMlEn_6BWxgS>2}{weV}@m28+(Y8PA-jlpV+$Me@{#EqZ->}aRIi`tn zfoO9PL2e05{;VjHo&$ia}z=MeF5(a!sc^!WjYf!T%Uk8#?L_^J8-HP--t?{>e3W5;7b zciZx>yocj&TexsLa%qQ^a-?>5%}e-DqxJ{c}y{rI3an%^M&!0?>GR}S2# zST1%e8`mACfoS93zhOX27A9!u4kNwK}VH2j>dl$k?!{2W%ucH#2$0%GF$MqpGwBvWl=Y~d#qusL#hf&YN zu6rne>}M`dVvV+UR;0a+5f^Tc-yn?Y%mZwR;~J(%D1a=J3$HsP)%H$8et)zW_b;>~ zBfF*73;9iilGit3x!gXiTQt9`(P_s*7cOCb?YngA=H9Eh|GRn**P(M+w~l7I>-TW$ zI$CL|_i)Wo%Y#1pWXRye>1$&vgA2S*E?Dt}w`Ya7J)R${@U~{;wb^ILmd6uQD+xP{eg+Hi$3^g5dee4nC z$8T#jHvi*b;IwV6W+pnf+SYnz4ED!wYrXWMeqFYmv2@Y4KQ8q?4wjZ9&zD|4){B+y z_qS7b4BO;iGGxo>L~PBFEx*oryC!elkS)(-5BbE)zv5$WVAj^`;4PHheV>CXsw*yF zS>>qH*jG{d&-=Uzxy!Z>mM++KBkDD6Y00*%vAqymvhB>JQ=w~9J{6@eM18t!+kfVw zZBH+K3@HvMKlNj8=HsZvf#t7n%-`YrVMa8j+{r-0Dj@W_z zg|SUvcz1q~{fpS;7#FlX5i9&SHZxZCD*hLAybw8@j5-7?f#uH(wig85(C$Usj$hj6 z$Gj&Om2_hu=s)>>rrBVcx?lKgJhZDy}^{s{iceHWk-SfA8#;XFdGkyk4+= z)O&OdPB`wo5chwLgFBBm2E19TyWkD$Oz801m($MiV<}#2pFXBZEFxkVn~_n#GgIt9 zo-!~m#abes@f9QXWkm04(`4CgY690+&?T~lA}aj`n6Bx;@%-1U8{z|A;sqr`8RWNg zg0dNwDsGo}@vO&~ub;RJQFs~L1#uaCyNWlsZ4u6l^A0~VehvO-@eS@A#3dewgg3ZU zVs?oHUsdA`?$1n&C2phGLt>D)2|42p?q-XPCyoR02A3yU%r235iLpJERi3@}#T#6g zbAE!iX?TOnI`9o{22ZzmgZn2S+rJk{{VEjZc`NKc#QUJj;Y5CZ^=9b#6Ozl?`U`I7 z|6w_Y!qQ*383Jz+b6C8;h{f?vFAuW=FQwsE6lO}`c|Pw)66o09V3vfuIUPLzL?(EV zHgh^k`W>YilO*khGV&Hzd;UpmM?XqSLg`dU`47U}Me=ezpFdhos1PcPULmx@AmPjD0k364IG;Hh&@;`aM}la~~z+%MAFx6>5Jk z{422DYvFH31ZSjc^f&_kKECU}0_(qhEtd1k6<7!E=U)S(>uJp`<|lpIVk>(UqC<-< zDL>p^h#qy)k(rRtrB0d&sWJN0Nrmjx5=(Ru&Dg6>ypbZi)rs#?WWVlj#pU7>sgND( z#0e0aSkF57Ny^r>PW}VU*|)7Mbq20-3fZ|%ywXIJ?8GkkT`!!6?seh|6xqM6EiFGX zUC0i0;_o10_bkFj@afNwyGE>iae3$SGP5aM3Mv?w3ys@XkJt z^oPdqJY$bC=(t}mO6!_2NB?4{2tM&}F4!{onfP()@)Y3`URIxkO@wfF zlIJk$X&)|01mJH#w6WlDqO)198cKG>3Jbzzu!?8;)Ur21cy~^?433UuFfyIN@JI$D zQ>hU+$1)f}ti(#kh7oHRDzbu)Jpf=fzISXow-Ja>Ei904p6>gqJU-B5B(!BU{@;R?Wsokn@fN3l*dOo){RFSW{%aU? zJu#ZCxCpE7jV9QVn73+BxlT7da7s* zC6zD;hNSg{DI8#=1<3gQMDNVTGTo-R--x5BbMRsdTy&m z&a@|8DOEiDs!pm^3U~4U)ESS*9{VFt=lG&8df_}m4UfTmYOPP4o;tq0DTwrzi=mb~ z6we;vj=%$87FO5u|J+%W_2RDs>NSMtX;%t0^RVW7)`clOb=;B#PH&y;Qc$aU)%(Rl zz5eMlbKU-G@h$!3^z1LzE!tl$hFaCD)|vYIp6w(3<$hbOUg`cqOWj^)dX`>o^@{9| z7DKJ-mHI{IcKladtvuDE^3epl4GTE8o}0YY8e zhgbW!Z~<8%J&;-o5W;DbNLp|t(aM5D2`0qFXPCaeyKWOR7)J2XH!>rV792&iv!Kn6 z=Q0~b^fT>Hg7sp)!&3dkBr&Y+cEAFu2Nx^R!(5mux~!0XXYps^r#9w12mBrR4dBdY zxAREh@4`{Z`>*+wo^!53lmE#Xrf@%Cn5%VG<*3CS=8Sc^Q(5N~eO-fh zapr7a*T>GCfFfUNoB@C2idmD6cax`?@2QKmAd}HbVL zk?Q{+Uv8W-w~Aev@)iC+^by9L?s?qf>QA~i9iIzKhMU0VdgHdRO`6*6EwGc%G|n3_ ze@eY?+_4z%h`xOfPPSo>kDNPqR#kG^-1$j3DkuBRo?JO~YUR}A(D`$xR902ZonO^9 z*`>5|X@_LTvJPF#dUWWKY=bXqR8Fd@Y@0k5N!nK`$w6HXnmiwKqD@a?ADdA%WA2<} z$IfM)dvrYx*Xgx=e%nMYGs-!bAoeYKlS_McaJ@gUlO(D zwaB;HK9*mSjO5Gfncn#Yr^QbX&OmMxUwrCGx)J}N{fK#`mtOchBfSK8j#xWBBOQql z{fcYHXV!^N)RA-Q$ao77S-#8n8YAW?zp=-My}a~wOVco54D2g~X;t9y5;wgBsQo?= zv5D>4lzzuuC3CwMqWwop=C&CQ;zk{_B<;V#j0urYXHzVBXmLjuNd5b!vN!SMrsM)nY8n0PVxYwS;=A$GkEeJ90t7yF5W z#9<UP8cjKEPbG)G-j_@LEHCukK40>M;^pER zv98bdqtZVv+W7|gg+AKvN^j>K=s%PErTC4=sU27k`h}2tiu;Rp9)iAuia?YxC}`kpYoowvZ-CEq3fN_<%SwfK@qZxyD0U)(N!A^u(T zdA@;NL$R?){}$?7i#^0cMLVA%eu(6w#bZVKS}^_b;sSArNRJuDpC?`}n(r6%H%h)$ z{FNAfPx490{GJ)}3BM!xrewO_@OS9X;+NtMF+du|XNe8P#v;9R7~fhf6+4PO#6v~< zekAPe`;p*K=|_t8-AL%COP(uMiA%+0qJ2LS_V)cq(7qoD+V>+t`USCmH;eY&NXYj{ zenfmsd{%rxd`;Xa(v^t$Z52NgzZAa~=>|f(Y_WmZL~JG|#S-yAvAx(`>?zWDiTMr@ zj~0&=>CVLXIpP9wiFmSjx=1f4+OHPr=0y2c@m}!(alQDY_`JA5q+b-%zb|eT|15qf z?hxrXMf-;0o?>&cSZpnJ5qpTe#eO0^ubAHy@kioZaglhUc(!<+c)7SnyivSWq$d~i ze^z{1d|jkx7vnz?w~JqhboF908F~ ze=goB{z80Ed{lfvd|BKizAgS){7lTkH5>EK6zy=Zb5@YsFuQ4~z7kWBD(O?~3n>Tg5+%e;0k+Dl=WS*g)J*EEcBWUc6bnPkc~(Tzp!jKOoEdgZP>FrTC2);MSjZjl`znK4MF; zz1T^lXCbfCj}V85BgA9H2_pRtXRxO!bJ!vvnJ>PM97^<#>qiX5Y zN!63Q(#cg-UMUr&JALNChavwUI{HY&hOwn`iauz!O!&>+sg;u#9FNE8)8?Xh3s0In z8IJ~|L*Qo3oe~u%;rZe5m8fGneSOWW8FMPb9MMRxbjsY>vn%n8I+AMI{7JK6gvXPm zld7xd&%on-Wc!_$=CH&izdg5MqR#vmS+KcmX^H=|!)2l^b?q3(r8QRW3%jyw2b;R( zt_U+h9QP|OM`DfI@qTCJPV5%evE%pS`!fSAVSe)=b2sWGeX^z7_xN0j`#kMf|BJ9j zit&De{H_f%iNvi$IGSH~Gj|XvO3}^}C9)=vv&+FiMN$8j#9&@Dfdkx_*4T9c> z$S=u&mN0GuOp?m) zQ-qmc*v1R-ptftcGb)bpf3IWrEFQ?tg)UrZ!!-y0$Z+YtubT-gl*6iK!YEq5>5=yG z*=M*P@OcBOGcQd41oG<<3Sd|k+Z3s`w;K6zJbJiEp9*0yBbP9pGH?&xdkBW z7R~SKBxW3hE?mO=+IKB0OGcJmjhXA#{kzXyxsH{>BUE?pXoEWLX=COFV9XqzJC*pW z{Ox{TaKSnIwycO34{AKA`+r@4CBR@%lB3cks5x^5Y#Vnzfz&P7UT9D{ntB zzBMo7#Tu{R1Mg$+l^XBr-_*dbaBEgRZd@?X<|%06-75A9u6zvomKA%Sbzphy%B=Ds zTOP~4txpVT58Q|+Mq9IUuIZD#J)VtJxf$Ez87G&o+w=~`!DWpG9~h>hLD`4Ax&G+->^n2%*f)?I&LSL3&KufW)b5z1G7$v@Yl8+hf@o7!SBU zzh{MiLF>&Jk(c>UpSJsMv8Dy2%yk7_^0yb{=at_vJ=Sf=mM8L<(~y45`Id>u zYa;UMwQah8*`FZgO>A*iWAwrkc{y9-iH9}?;rO{D1|+;zL$>tE$*ADAKyA^JCueo8 zXtepBJ`Fa#UXxfkw5BA<97|TdQIiL$P4aJ$+N?};Np#CAf4wHYBG#2Q3;WHa)ruBp zjVRAUDer8;7`-#?Ig9NEaepCpy1dEQdke$8&m!DE4B~nPQ5ZFKC=|il_!n#2hyP<4 z7a=0Jn&EKn&NCp`?j1XP8ujsvhiPL^6MHWx!3r|15GOwk={_Kg(VQ9OpC{dOLdgy~Xx3hOc3KDC_K~y2E&QXSZOdk<=h8FbhYr`-e#bYL0DV~U-bN`ye9(->X9)AFE2rxBq+ z?M}PG3`P(%h;$kc#Uxht3dx@Fy`wJ!oMo*R(4w~G$h2juD`~|BU~o>l+m2=QOw*35 z23+nuo1~U#|4fAz{g6yWA`s~ZM%V8LTuY(Q;Y4UqyC24y!3cr|k$z|e@i0>z(;pab z!7=!63-=x7%C?hzu%dO0clp$6M}5nYY0FeEwX^*24-E;I%?hw{xG?kRvRT5;;^*Q- z6Z6{R3>N!8<*6ERO(>m$t@6FSR1fmI1WS_e@@&zP&X%!YA3B7__OtNRq*EEbO+|OW zSPTB^#D8YioS~K}or!S0q#|fY|H4>syhDytHa5^%Fy@?EW6llL& z@~lcP`gK=6gNp2@_9@Mj)1moQrjyq5w?W>Xn}4;>l8(-6F_o6ya^F4$Ys6zxE_11T zFbaDY9+TEM=Y=;%pZ#QMbhPWx_1l~$`yM>*h#A%W7fc?92T})B&7D@gXwv-334MZ)<@(({nLl^1qu%3U(;smU7rS36JI&XAr_ z^}Be=Ms_Tm0MlRgO?}!G2G}brCQYfHJKx+}zne=X_LUJcmR8yp$AjM{qNWM*cKwM8E{uvUx?epRW#s60EABdrU*q+&nH~&f4ohJR+(qAZfwd89g^N|ko<>M^o^O$6N zj|Tnkr8mDx#B+(hm*Wvhna3E}Ol%>Rh;78~Vo$NJc!bCYOU&muk;fI~>EcpxnRvQr z`z7pFOSb(I^0ks}|Ac&>H712V?{LbUxI@*|S@WQ_4Ii<`u^#ovn`ihOuRd-L}L1MWXymY6RV ziniY)-u(T*cG8!L-NZx0K4OK)2XQXnucSPTaCb-^>bV ziHj9rw0?Yy&Fi6X3GF6A5w%+ff#>sZ3GH|;r(L*|VBh2M5-wpJ&;8t`dnw{8JT~Jv z0q&F7RqZ(!_wRGChG`J=nqz-{o&hak9DgIiFr(;`NPpUJ3FG*i5-s<%rtlGf9m~zZ z%672aTd{^|5cEossEh$EVcd<7qxm(#MP?E@=EvhQ%1UqtYLm{A$rM!tSKl2>IUGM~)&`RgF zPwwhncn27iCAsW+{@APASb?c`;o@KiY6;Jdremb<+1XDm4z4n8uR@%zVOI> zb1w8gxqL;w-}v$-mtqdroaAMBhvgmOZ!gH*qdc~~AkW_#(8S6Cc;O zVt7qXuVZTx?VwAHC@);|#EzKH_+zJ^UE#HSp{8KP(KR{qaKM$8EwXJF-U4*y{TQ+dankK5~r}B4y#*A7%Qd?uZxnYW~7U`NfmAW}400A9=+{ zJ({+|n`f_@vNba(-0t=1yB%+RKg#!m3*Y`IQCzt9l&u(|5B=L8#eBv+u_MdUKfc|I zF>T&QIbQDNdD|0tmrYC@vT0)Og*lh|hvZzi{PP8OHn}3Xy76Vj(_`fmr#HQ_`QD8x zlB>OnKo4IXn|@b$)`d51Pb3;u#40w{vbp!G7UZ$-b}x+vLn0 zesO+9a54I?-*tOeZVfVuDh{kb|Mh!%hu4pGc~|s<9n)uA?D{)in0w(9J2DF!SL9Uq z6?v;&kLUDb?7AI(zhuSWnu4C(wn*s@uE}fB0KMD*y}bO+M(Mum-LxXMB$$56TK|A} zMZ=0ATSjDVsA;mI;f5(odR1@^*y0)QP47@~-SqAi81i-zH8G|>xkJSb(}Nk0L4V$d z6FusgqHRUd5=%ARJ8;W}1NQS;4zGzH?squWYdo=d`ni3++~`t!kD6b;9a?d}3e4jT z++E=jZ4vGt{0mM+RPZ(8V&S(z`63(s1?^xG3}r;l+$ z`NWFzvTtWrk4P-Y<{fbSQ7a`@+yph@(50-U_?Lm-5ihXaW%xen<$=QHCyH7F+2ZMYvZbT z12`?5&hW3W<{!&k&tU)Xjx&D>bWY@rWKw*$;8XtbDUtUt=%mxB;Ju^!y2)~i zoPytV(r=mYe&}2$y~);XqG+A;7Gw9Lv31hh6njy$PI`wTM>V=$dRM*VI_7t5+yjVo z9b@7qiqz%F935Chf5s*H603!ur2?~4>!aTL+S_oo6}5C+oOv;A$JY=upB%*Pcr80E-?* zD0(D#Se?B%1t$b=#Mq#on(_`4s_valBweG-Px~J z4a<-V+DMdRg||*QIT33%!kJ6GqT=`(inFi=URB)YAHAe@JS1D#W@Xc#q#2slpV_2} z9S!ALtP#&2=ADE3MZ7`y`|TQfG(xs;lQY*$ZoJeKJ8=6*Fb20U8=Muyfb#AeS zijG*(3NLe^M-|0`BPwJ;ky++>t43y~(o(Y$>z--tszI48Rt?Q;u?+o@u!({DpuXQ? zP_TxP?DU8MQZfT(RGe3>KD8r*dN7 zbAQYBsyr0L22*5)Iq<>Cl#Vj{Ht?1u)-b?^LWoo7p zbkLRZ2W$P3CGe)Fzz0bnFZv~-!=IxLg*Sgt)W3hMZ3v;qBHWG>1)#W|yy3$iA^3kD zr^@GfH&qQ9c!6L?K(|*FED;e>M$rx(S_Ln943k0neufc zqr=AC7EAaYk15j-^beE6n17_j|GcVeBNA$~IboRGZATcz{0SE_f5N5AAJBT(!G!`K z@Zy~V2+r5`8BPLBz$;H8|o0fOL=Ik zYGoL_Cg3C%9icq9wE|kgD+DKqb##}uawkDg-q{+Fo&tjx93uWd{o8^UN(X(SbWqyL z$G;W67>TL!U(5ep%(=M=-PV7JHn&biB?_C>2^SF!%+}Y+(b`SuDDe?awG*1z3Fk!# z7y7gn8u!)>Azl zAqzFT;o_ixp~8MX!jK{M-uAZ9wm2PpZDYQmp;fUgI7|{wEf$*j2xt26ceE3l`t%ge zv?FR;yq(aetb+{skO(TErjKXO@s6Icl(TTh zIp2CZ!qw9gIgRz~B^>M{3>fl7KC%QV?X9g{giG!Gy#jH&p5F?mMeUv4=pVlg zAitZVmry^7zf{i6p>6nRKf8WBy?`&5sk7KFeq>F-33}OQYELUOO0f}MKY=`<9usFx zo62!n>pG|Jwa!n?uD;f33UB$=Px8O$7z< z0~Sr4fZyv?qPh|L?7zft>p!|fr%nUpDNxwptGRjn_J~T~uJV?6^qB4aKkswM)5%DW&97Uf+Mw8WuV2sE z6Q)j@I&m&`C7e79+u-q0KQryVpcqmL^WtaVmyQ+p{d^00j#jE!d|lCZ8QSr-wZreNd$Dm$TmZn0)<-kDG@=VpXC&f5-o6N*r%{QvN-0>IW4E z^>2U6b8Dx&T1A!9r9q3EA~KNr$$t`mit4^EIds&Qw%0XZueL$FQS_B?6F6~kx^{y! zbeq1-8;^3eC3nQ2^S?(|G zfl?FE`VA>x%lApGt&qSDH_?P)jvqE7lXPF&M{p4Hr#Jnd;nVrkbq4Y=Dq%+^Q~D&+ zP?c~5!>J7U`3C$Ie7e=l|7(W386IPJn&B0O3O-#blM5JDGi+k`jNu!G9~qJ@l=7v+ zkmu1srnx-%_hmSS;WCC~-=uJ!7YDq^atX9JV@-&}_Ln#u22IM3_B{UlIdQ+z{0$o#%uka=DY zWEU3h$HGIIO!f@o-Lz-eiG}xJ;lr3bn#og{Jd4STnY@C@{61f}AMGzd`Psw5`Tf2i z|H%BWGJn#-Q#=`yQ<$8`L&TS5F#B9 zhWLEG;7@5Ync6P8#+;!QLuZEW49WCE@u(dnjAPh^A?YH>pKmXK5+++@D@Y9{en!EFY+&DNTx56A2WQ*@FPPqeo?p{L$ZF6?8-2VVHCse49RFj@%a5t zfqeS~oX`Au9RcL;n7p6i4TiTFl6j5Nsblz@As{U@-T*cdj$VUOeV`DrN5lvdWM@A^7{}XoZp8KNXANvf1e@0 z?;vD;-$5W*FDbl&A(<~p{*~ch_Z<|WOemawXO@ucn}o!PBXnTMw|kJine5LnoS|ah z!8j&&VMqp2N@oN^vXGKIo8dx+iv0%HGFh?TAem4p{y~Pv7@lHCHdPA0#gJo|$WXDr zU>=hnF|1@rMp(LDvA>`Y13!}08JaM(VCc%wlVK#oScY8~_Fy=W;ZTPBzJcgR_<@3yZ^2GhAH}^yVSnp9{jiy`606#@tYO>dmNMbejD-PnM^8p{{8)@_|AWs z<9sGH|5aP?9r0f|9tVLq@`^(dZ~R~4Q9ry~5ecY>HyUnKFF{+J zblBkq*a0zB<=+9tqx{o+S?M+c8rRcau!=(wwh$i5>9s|AZb*dEqjVJMZG>BK4V;3r z2((i2P&jEksc=)gWF)=^^<8l&!gj+$`My(ZVQfV_x-V%Z6!$$1x8fQ&1tT!F_9F*7 z6k*39E2sC?MIf+)AEk%kduw{P;Z|G&r(he>vm*yP6k#_YE2n3Uib40K^gusuO)nE} z#WipW&Lh3$ib|&xmI}9WdR>qnwGEUWE@@4#1a3-C@r-W9SUaMqj4X`8>;4k24!ViI z<3#_;;sN+$0B%ZUq&Vn3)A0nSa{k6BJuhi>6y@;=Ze%AOz#mVLUbsR4D&7S>lTx+? zJCGjf4ityNNvQO?1a5dCN8o7*f2dvf`|1NJZ_4SVA_D136o(?c&ng}N;C~kWfnR(sf6QSW|lwFPk>1aF4E70vAbRy*G1gZjWuAIyGp`k+4s zmRO|b99JI4feGvOR#n^Wt*FPv7b4w@Z#DZ!!1BHLR?@aCk5U&Lxb{xqry6$+$^_yN z3mmlf4cAX!y>&JG_2KK+el^b`UfsH%R&P`AU^iS6P3bUI#Y4<9VF!qW-|FR>cbd*K zTJ1ZX2iMheYReQ%iFh8YeBZF#x7w@mgc`YT^|^IZjIYw}WW8I~X&u z*~Li?AxFA>-imq_U^lh64;pVrycG>Mo#OmKZIjani`Ns{cluxwr`}T~d|uq#IB|;B{C9RV17(ztm)KCa}Ag;)+JvnJ&CvqA}15OXEpkvN<2bS0AElukU0OvGH8k!w(UA?N67P%`2v^luK&=2bTr|G=|nB@>Y~ z@exTr!stGIDb7&^M;HAN#A6DMu4Fx)RD7I8mPlxRDn6m$=t>q*rcNq2x)S1TickH8 zqpPu$BGCk2d|J-YGDl>Syt<1Hq%*w>*j4ED36~R9jdQB4k6J z`4*)l{^0 ziBMTb6xdS`^^!?kPonBhfgY(NR5tK6YNtpajM%TpO_kh$G}bDcwDG@*#Fh9b7d7Bz zK_*~Q{FBp|XOeiSRips@_&9%o$~MXZX@?+!d&`R|cu|H_y7Wo* zpd0>l79Fn9FYuxXg%N3O2IDkVL@<%BZ!@HtMBn-ly6AQ@U33jz({LJ{!M_#cID~(s z&o)-XG~poOmQBciw;$7w_hU`csotcYMjX`xdl7BF2f2B%885h>i$ za2omGUpYA(@Q>6=#)?>2Yk|1X9jM6QP;tgjl$UeyR`YT^HHI~)8-3~x%pD_<*(G{i;TJX^751W^M%FS zo2gj+d4G&rtVp8r6$m(-;6;EzjdsV97}(_K2OnMjX?Oq9keIte@no1`s}+E7hz3xN)m!5`G|MvuemDwQp1JCez<6%JD67_EYXbT~%z z!wi3tV=^3uIISoF>!08X#A!_mkpARI!oLONAeF~aa#Y|S{SJfmWBf}Y#~b|nL=F{{ z6R9d}sCJG)f)jA6^j7Ig*`n%6`U<{!u7JZ>5rb7v8>*f&a78>_HC$yAT}AbC2VF(= z^Ef%Ee$ww77%QT(`e{SW4qZB*E+3$EasawwDP1*5 z1=JqYN4mlR4t+|)6b|}917k%rlL+|GVdsQ@`RM;mQLOeVeS~1X+RG{McGQqkD;6dX z0=3nzM=D)9=&K4r7F6hFe3^}-oKY$6CkK@) z{l)=b()8m6eA!zg9lkb1!@-yBNODlwZXySjEzQiFsBBgGIZ?@~^yka9n8K-C&2U4$ zTra}Gm#Yo(;(!z7NHQ}Ig zb%w)O5mmWd=~6$se2mH-x{As*jIN?`?M_!IVknm@T|wobDA!SR1(hr9+s~KlF1l0^ zO;N6Ba&93KonrhV!+-N`m&$7iBwwy2(2y@Tz>)%f+e@*lc2^@iEyY>_NrYCUn0D0E zW~%{sLFb>YKhRZLaDWP}rzGO3X-NU4$o9uHP+DPt$cEM`{ze8WhAKm}hVmIO5)1r| zXrY3!7T$*;nm3am_@L@5$3%T;%?@%e&$~IhF`r!vc3WPs{Bo5;8%nC5%0M;6O|7&b zp#IUF=-xI;_Y%`mnQ!0r;kT*pszY+uL57B0BNOm?J2 zB_L`x;Z3>QkMJ(@2ecma8Gx`#AP?S2J!p}~|J25S!AwB(0kL3#psn*^ zxcFlP{*S>4IxE3A-q{EL`NIzX+u}qH38vvx!}N}iCR73y%O)W6M;H937mT_f_fQ(S zecrK^>IN$UMXb+Wt-6cPUV{{|=py-xX^r*St2GwI7=sh#hyVQX*{d~QpS`}Qsv^W! ztXfe10>b3}VT4g~w-ez_=1<7U-BdKKhtiip(NXINhVafl1b5}W1RTzPTSt!f`zJw$-&{I6hD7!q-wImiF!;R z&qyBvx+j(e3?S@=6EO(rc~k}Q{K)t&@7_6pkmKDeT3_@b$aG8MlPhkG+`)%G=)P_^ z!D%N5=AC`%Uh?X(R4V+>u|~+_$nz{ui~jS+=U9J}JpX1X*)Llw zx`rAUWRze#PN87FO6wOj0Uq&ucwd4oIK^0@pw!mPhu12T<+1+e(fCYH;S(QBK=%>~7A-$a4*6BBpAkI?eHv=W-V!urJswOMVa1Yw*Ws zuYUjNMOo$#x`-Z8Df!P{t;s9iy!>@5UcORIq6kz9N^RL^uh#n`0&fnWHbVk82hitCi_iZB+J??DxD`?eMmx>ky#IOOT}Q%<*^Qrs`H zE|1$^Ic|TYxUFxC(xw~Jx+X;y$*p{_aFJ5DNI5(L0*gwfx0IzKI#wx ziXp}V9;J`Q4_*O$i~$58I7KLN+r#9d_EcvF^_&lx-1>YBUdFdVk|FQ2oMOO-Bznp6RA{h3k|j=|Ab$B_ za>$>#kK&nme?@*i-y9KJ4@v@sU`0qVvC2nr0eLIpQ5Qrn6${w@(QAOhi&|Rt+W*mO z&_8+&RCv)vemIqq|LoP8JjI~Gk{6Vs*pv#2B82eCw5I&otM&ervMNqWNO9a{4vrxe zpyFr?M;uP7aJz6SAOF^MJX=xD-TaU4$t_<5fd3t4&z+I z{N|J0q^UC|&a-n{92Vj}V)Cp7cJujPw}*YkPVqDKBk*hX{BPOM2^;}`?^&~FP4u2P zcHGnvQ{~Bxm^pU*6#9*+|K?=pEx`|4@rxJ~`Bqwy0=p5v-e~;Hc~j?&o;-fM4;ILP zP&Q%Wq_GQT%pW~}N&Lil@Dsvs-uwyR{6j!7k>zRT*!fc+VU~@rnJ+--{Z)ke*rwWt zRvTe85!U%w_z36O1$fvyVx13k?n0q{F?0juc%a=)q{6#0=lL+dwc51#v``p9TR#hh zy?j`VE%W6bedTHR@X(Ipc+fxEmRi`G{&l5)xK0>pci7R&-qx|ZtuR8qx<`>oS{Q^? zL5?=g`0wxKjMYi8v^Gdst>Pm*veHM`gf&zK^Rb!rVJ+lP_;fyUC#=DWq{TkMe2|?~ zgul#>685wcM%mfZ;-sFFgx|09;TILD@!40CFDs&~d|qHA7JydF-qSf%Y@eedoaG}l z#H}X%!>ywJ`K`daP*M5oLZ!JA=b^OtQc-3}V1qaLVkPVr4+<$QC5>FHV zyz&ES@@trs7rH2IUj4s%J=2#GQ7PGbZwjaQLM}v_m()ekFkI8~*w0`{JeY z+f27EYWbg8=Ok|y{yl4(_;#XoK@Yz==s&e~sdaT1H6UN`c2KWc)Bj=>6JKlF#SOti zC0fq(Hyf;fxsZucmUDiPq@u+BW>u47{SuJh=)83u6Wy>!yEs-{|6)B8RwAKz|6wiD z=l$CG)cRBQQ4{#G=RYWXkpg}h6T90GEI;GFPV{vD!@GOao(E(J{u6uls4myl*7;&> z67{HmyEdtVuAk~)T_@_$6$b~$KZzSQSX0-jz1GTqSo=ghdz`M-di4!z8^vFVHVHRl zMbiXbL1I#JN@`m6|El#&MtD}m2!%$#$_MT5t*`6>nc}yOwup47bo`&R-ie-BdA$?I z%VK_PsCXoL_6|77@E)E;eff?3T|2`oKr}Hvry;ARxtt?J zvKnOk#v1=U5TDV2WS|K{;xEy92WLV<`PiDm{aJVnLwXMK@57Krza)=iIGG{MJ;{Fw z!;K8TX1JT-F@~oZUSW8TVJgD{hSdz47(Qe8hT%ts{5mJxpSVht4>yLP3_CF#$dF&> zgm`3mr1<+qaV7?fFV&$NFKm&CBv-@r3|wfzGW!J zaFni#WY~q_T!yO|enp7SjOWz@d0ssTZTngHaYD#EpC0L5Ve-!`T*_oJZ_@p8nOwx= zI)?9*9>``)=GXB+c44w7A>?2tM=+V^&*S>8Oy<||K<4@KKz`j0 za4Ci3xmGg#nh^eb81j61g!6oP;7=?bnOEsL8H<;}u#h3on@2d$n+HB+@n0~cri<=_ zUo_(prW!syvcyul4h(soJls4#9`Pf{AJ~B*&x?n<2gx{x5#oJJVK|@RYKGew?qhg@ z;cbS=4D%WCymqA5!sOo=k}-zv!~gCZ(2Qg>H*FZ&5+a}e3_}PJuPu{1GPyUC2QYaQ zlczIW!f-7i(&u^XXg|JV{yc9T_c_hvOAK!@lrc;p#C1Gh9q~(wyn*n?qT7Cw+6|NHbvhyQ(g;1U*ZEkpkI>EX}+K0WXtyN>^zddTNkylV{k z->JuaWh@@?m?@t{gsAsqA19=PzEd;=2SdK!#Pz3`e3s#5hSwRUFw9_>&#;(b6T@E^zGV0t zLn290KKcF=NE-?9?|~t)rbzZ=7{D-uA<=9oyca{hpM^Y>$;7Lo@Hq@uFkHiM3&ZUU ziG@h<_cJ`pknf-2&#&76a?C%CVHU$eh9wM%xkc$cWk}R5lKFldXv}1DhOP`f84{g~ z;&)+4tS*v=GbH8~$+H*|>5Akv4Ec4*kashgxLOo`njulPNLH-(BWf1O#NQz#h8AHp z!@sWoBXSe9TWSo6IYlzR4jE|4WE+Mq3_TbIFr?qKrt4xDwrALn;UI=Q!4dKJ^?<-B z%%67dr0Z#aal%CmiGV}$T86~6B6%Of!wmUx4E)bB`69#r>iWRvES*;jKQg2Sfbv1? zE<#g=)(rV|%J8SP6%_8pFo)?#Cs$8CPSjXk(|Mh zA2&fRW->9VD7=OtKaPU@jLF2Bqi`b55$Z5BV91Z7;P1#}FNS^$Lm4X84Jy_N_F?{m z84hR2uNOpm{CYtk(eUWLYZ&fexSJsn?kJoeUjd1CM>4-I5Xi3s1ZFXRV%Sl5HA5oO zk^G7wKdyqTf|LA-Uq@)nkZ5)!yD}us8}+07xC+>w$(d>fO$Ww@6iu>~o;iwya39pqa~PGFeAkZ6Py{}IC)h7Am#FjS2D-ZA+TLw@{+ z>m_VFMQlV$uMIiLXzB@ zArT%)9>;J7!+3_ogQW0v42cd&@*alxZ2A2xz8IgP?%I(*;?P0gE1Gd0QEWW&<68Lt zbv$dw!krkp5h5Mlf(abO#z{gNZ{Yfkgzz_}cu*oK(re4)I6}nZ^*{(;Ofu!`zq~#c z+U37n1xw!ZCoZ1v^LJ{o|F=FCZ=OF;jQrt>|5PV1?BS0fh6+%`qw*&=)i*jQ9_c(4 z2h}e+y5dxh_bX%$r{Yk=>j)2gN`kg5;G=`Q?vVZwT`pf=DPJRSDyK)R{vq&F9E$Xo z!h_-|4hiaI4c&kpim-){mG5ipC=l48e$svEev}^JMx2Uk;1oQ@BG!*e9t!8azr<_6 z;?$E=zuBRPw;QtZeS0#R3`d%{OR$K$8;3d+_z`Ir)im+68D5p07=^^UBTpxQ6 z>y>FQ55+<0(OJh(@h%S7VvMYa#KI^Z)i*^RC|KK0AUH%0b|~u06UfT>n}PgM+Vs3= zrdpr(6Wqv7Jb*vW*!}y~#~wm@G-p;E3MZk`>k`!4$>*v>rJ?sj`P0Wqc~g1~f)n^Y zT4`?xQ>5n`;va6Obo{H|3}ZSVoa*dXvDgQI6IV9IJ{Ji%jh>HUEwu!k-rcO& zdqx7z)KeN;Vk6*e4_u3#<|W{~G|t4%j}mZEix0-W=_TO$G;NQ4HeSF@8MHQb^fCdr zA!$Ku=kEmEnV_k$r!Nb*+~Xr+SLO=1KMecCX1@?{L2En2?lw{3;$MWs7DTFW*T#6n zZl0jRspMM6-uOm^8x(60JK~-Scm1L$cJWgc&f4Z}jEfO!lN_VQ z^=ac9Q?O5sJH6E*X3!fo&fud-Ouqr@+}ATSViK>bbJi6fqc2)WIIicf(R!;T+~WIn z(ap^guA}e6=&%7Aoa>SF=s%?zoQ=W#=;#1V&TH-E=vOB+xxTMXMB7oQx8eOfW&GqcODthiFZ7%=j-00=%I$XTRr07qZbU3#|L!;kl>vHdOdPYz9R+npB z)i%1Dl^*x>MPT%q(|Vl#1lQ>0!TMaEV$0~1Bz^8&7v1PBLku{lJ1Wr$PYt*`-fyDj zZ!qL$A8C#{Xlul^H!6<`ziY$=ZOn=4HQbo%_)Zq}=%X>WXvU4GiX$dmYSoWXgFBmY zk^R4q>inxImy^6B>f%8&ZbQWSsBK-%xltDvMb*4B=Y~2=k2-(ef?K?2RMfMvmfUU4 zeo?0_+Hm%(Iz|;2wBgRb3X594!-^X@#VcxmZ)+~7!Zym&z=rehXA~7&WWxbPxf5=_l1!IvK_b)MyZibM;*C&o9{$UTj<1H z{qtg^YcFTcX5q2Op>8hRwI|<2s)}8?1>-kI+Sj{s;~y=LypiO_t>_ycDgDu%OH3Uf z*?xxy7v6SoWcU(KuI%S-k*6kjap!$wA~*N*=FXq?kF0I)!3|Imp3x*fQ27d#?*2gGp= z?luwJkdEB(Qw9;I)^y?yIEo`wE_CLyj=T-ed(?%Cw0RP4s@08aJXjT;6WyK5v?vJw zV{Q-b*S*Q%N6+@;`kLMjzggdl`*rt)@Q(IAJ@@vQ~1e- z{#?z@W#K}vf!tk#+2O_W2XUo4$Aw#SgSk+{LE$AjL%D~$x`wNd8pho;iVi<_X*gH0 z+b=vpJd*2Z>J;8<CCWfwJDsxCl~f^&QwnOr>kLmtEO>2fv3aH^q#@(|M@_ek8CE_ zGirO7OXzH_yL3(1u8Z;9u1*WW=D5w}oHD0`C7hVYsq`BWw%Kt3=U&n~Om<=+_x-32 zVYA#8b0ZrYZNuL^zr{d#V&OF`(5p&PmKpOQm!3%=s6hTjgIHg_{umT)0-q0v@u zV6UU0Z*FYk97=bGHjLQLwV$vlw2OELcm4UYQ0q%OxfRQ1hfWyzEw^8Ne5kd~cbw+# zL7`n9?BR0lx`o!y+s8e<5FPr;;{Z1%(m!-w{Xxzn%_(%^k?*`@3ng3@u$8(&$t5=9c9~rmou5C!q z*hDV0pHaw5zhrJevs#Fnb1GN3@_q1@HtF2YCeMOb8E0~z&esIj>1A`L*RCp3+@JA&@bdvbuR_ym=tquPah8+YhA)MwA~v#&h-)Z>!U5f`GMu!_ysG2 zue7h^qV?wns|={-R-c;~e1CE+=h=B^@GmRsx!AfM!5jB9a`#rX4c>9NnLB9}6s(cc z!u@{BHTcD|C*1YHZGt-+Jm)Ik=mu+ryx`{TRt=6B`HGt#_*+ob#y4DD{^Ou$SAOU2 z&#efWUh|HV807^GHvGtOR}+ISb^L>C8gMgc(K3N_@(YTcZXxv*I|aGiZzFX$Zx*z~$VR%gzgAHAbUW#^4}SzsN^+1s zIQ}y5PkU$SwjPavqBXA4oHwO`3v1n_TMuUi_V45+eb7}J`2Bew>5SLc0{ffzOLrYU z6S!b?pfsTSp+MDV!P1_;e-rp&WSF$}_`1L`xe?NreHI1!b&Qs-{9{_+jJvVYjps)N z8ill%Y7Orf=zJwks-xK{@VH-R>DRZy1Gir8D%~>GJFqIKhg99tKJfe7y`-uc#(~N0 z`$|_Vmjn*W>Mvd7^D$t^utCz7)z1T@kB3N~e_I!@d)aX5jLr`O^7KYYr+-KfSbS=X z^yyFc1GdDDm%f~EB|uy@QM%OfRKPEbr${&C?hlBtm@YN>>g#}4cVe1U$LFLVEn$(0~ndS4$`L z@(kGIwoZCk!#3bm^#&y6E@2{_ox&l%DwclD|&c57JSKPxx=ye^h!Ze4qcU z87HJGK5X^B)$X*k_TDQ085U=y#jEG}Z+vxLIy7#wzk2RZ(wUOs{=Z(lA~neB0Pi`}@+q4c7iEy`|Ee^9KIemWk5p`C|X|nkmwa z(eM0@d`y!*PbLTjdZ|;;D!-j?nxyT8^ZYbaf04e=pXAq| z|4iEB_%J_LhZoYY`MvxygI`Ozj_v)5yZUBDUmbTH*}JA{-;jR#GRcENU)c&H+4ViC zzL(FL%7nA;`U=Y}WG_2k^1Z2REn8!M!Z){ro$Shoy}sR-Im$K^Z}oM%?jn1Cd6nYD&!x9-6-6+|f)0chTjvOaT-hR?YH)*2m=)(Oz z>wKol+(&=yGvvSw*@!M{e6~yCWl;eOd>od{lciZr^)Y?2P$rU$^qDqxsqE>iK0a2Z zD`e}d<9vMjuaRYBh4~!LSuc~`@$#9~Ym;p9Sv#LYIa_7-_8a@S_y0zgxLM+3T)IoP zeCbE;kz@DB8mIs2Eq=0JCLC4ot-0iTS$>}q@0pULvH@|K-aZFT$hL%W-aUQJ$mV-r z^G-`VCo^(9<9%%8PqN7thrCPPUXjJ?@9>_n=Z4Hzvfg_{$Zgq4!D8~ce-_ioo**`iusZ_Sbd+3!`3-W6LO z%KR(MyfxdF$($>+ym$XzDJv}h(`)hNT3KwxE3ca~8)cI!o4opawaB_vmw9!4`c&3Z zo8z_r(hFIa#ssgpg>PizTYmQH7V}OP{rsHQkCIQa;@5}0#uo_^d_H{ZwdjN>p`+?1 zuZ9JZ1Z&OZUKhG*CtNp-_sX`_PtdiR;ML`=QG&b65HG6&vjjE&9$sB9wMjS`)z&NL z8`}g)*C4Npa~u=A2f2AQ40KH}n_%U&Aj&f#ajw4CC}-b<(6u73AM^tgX776I8Tv;^ z!i?ijJYAnfB=}yh_KdHNNw}R-=&4`SK0&K2)l)sQQ-b63yPkuRx+RE2mpwmny%H{1 zob-Hmzh8o_-+s@Y4+bR+@BFpr@6urjV@9v>e49Kf!FTZj&pz4X64G~0_5Ad3QiAXK zk)A5`(-Ov{^z|J6a#q5~x{jWj!g&d=&&kbz;IJf zo001h1}xL`T)gC~1gArPc)0G{nvihsrH99@ZxZ~g8$H&Rf1405Ec5VH-IuV+HOIr( z<50qw9tj@n2OLQ-ocptf*P0Uvd-k34aJzIS;pv0J9*b+vCzv&V>(R#Ka)PnJCJ%$| z*Ap5dmU|3acPnAb#CVV2?%hie@0{S#@LrlQ;NB1qmx$zqB`rNXZZAtum~YT zgif7l1`A@7?cw)tt~c z=b3wX*^`9hr)u4M1-wXzEH8G~-})xuf?jZ|d+zg5%n&?or286W#8d zc5f?IPt9@h8gaE8dxXJa2)Gi zy~aLq?YM#N`mdc67a!~D{>>DR#9npL?jx#v62;B|?u&*5CLWsX;$Bk_nrM2?(tUs5 zsKhzXb=`l+Yn%8}kkI{Q|Bi_{OMiFURnj%FK>CZ@>e0OtZ);S$-EHodxUpZMTipD? ziJ^y5-7Hi`BxW_HXWbAjF+Gkdxo#l47yN~B4 zhD=@SHf+P9#GqRX-QpaVCt7MwcUzLMI`Q%F(QfZ1Zb&?OvAJej_3W{n$-3so>p~stGIS3@%2y(H>-(96TPqKxScXPmDo>9)op3!*~AGG ze{ucB9!yY9LwO;Ew&aEo(#+rq$hu+pDikham z+GI2*cI-LYwdwHF#3k4IyZ*lLW#Un*F0Q@${GNDsLzL?i&yR_j&;49;HB^%FhB>=_ zcq~rJNVagDo~fC1Cs5bb{<2=u(W9!a*87Z;R_g!mGJ1_gQoq%|xHQhNNwR)X<#J`1 zV^YnyLYIVYZb^rVQeDiWy_5QOz2|b(KOpJPdzW2yx`ig43Owa<%|0rr%h>}iZLHfR zHQ0aSl5E*2Y59S5F4ru(C+Qn6c4@HclXQ6d43}Z{1Cu;7$GRB04NJPZaiEKi|LCMJ zQ8$-vQ4^Bxu8na??>aRpMm5l7+t67__t(0*?3g|;DOzOZQoL$$(%p^vE+hA>NQ%@H zySV(cHtEjxch3IlUnNBvKXYE*v@PlGfm&yY`mUt54iBA+-1a6Voy~Br>v1TlXAtM? zH}7at$^C22_Ya;*n%wP2=WWU7lHL`6@4WxjrKBAbb~!gX+(`0zwb6O*fICT9YnM4k zZ{m{X8^t?!zMqt2e{zEJj^EOgib97voBQV`eUs7CxngEfQr|J{oomiNN^0|4h_l19 zs-$PzJe`mFHzcJw**T9|)RJ`Jp0V?g2hWoZ4%Tq~M&nJ=?w6mOgoEBE?b-g)>FS9; zlTLUxIURW;O1_s?=9Jb&Gr4w3u9MFXddbEbNlrO$jFbDFz2$Viw`KD8JuW&Wp0iE< z?fEe$b4};uk-PUgotoy6ToAs^X?B)x^5E*#PV)kSlYjYYfz##v5y{{9O>=S=woUHv zXp~dgtd7a*8~Qt?ly^(6@ay9Av`3%h#EK}V9uEd4-{0!*R3A7zIW5A)>H66*$;~a6 zPWK%rC0icSb5c1xJ$Xzok<&7(Imx$G-Z}alTA1v1Ud=j}U`MVz19e+vPo4iT=tfSqK?~}hx_`z}O<73G;R)6d0 zz2Z#r%a~1$BBP7Ro&Q+wXn5{w^7Z?390zo~l^nHtl4DlQ{p70l!yT8dPE0aEOm%v_V<&F*`@cUvF0@|MJ+(@!_P#g>Pi^Br1PswJFs~kqusiv6TDs)(INRkq~CCz~wteY}_ z#C?Yt6XTRZ->VK!3oTLxiOx7=@3BpJTYAW$dYE&{y-PbC9PK?*PHfufu=lZF%1>jL zIYeCxNhyzvcQ9BJmEvPI(ZRG&`;_CahC1}H?~)Ro*URDZ>z*kef9l}SDZPJ6%{O5V zn#YEw{1Wf&@W-;zDOUX*9PEZnOqm~I<}fE@dde$HZHHe?=cMcxs5pH8ZehxlrZ@IW zDwn5>&S|k-v|coPQc_e|BVT%6E@_><>(B zPPx$NXn%Flvy|WJ&Fz2t>UBzdy^j631MgF;8dU9nzbHt3*Z8~L^(3`ajg}{N2P?Hx zyFaV3`|+J&s`OQ{U6rXt>a_P6cF`fWshw4&b_s)>Q-^5Xu!~>jnfimtIlB%={ZlRN zkJ!beg{EeBe`h!PRdni=$SroKY~xaMx~;M^?$s^TY507*(<}R=UYar0Zp`IDsqTh4;i{m)(P?&&U1y{2Jp zx2VtBR840?y8*j5r5Z8=#&P#3Ad(@;}?LXUg`1YpMr)wwJ_Iv#_b?Vijwo^vD zPIYbQW&1<&`&2jcINO&2DrwVugxgL!s-E_0jgRf`2D)kYZ#vqZS#O+{{j0g{!grQw z4j#I;^Ps7u`zHrPYZS%Oa7q9o)JTaS^_Cxg7 zHXdhYr|tP_trW-6*HT;6K|$vYUvLuWY0nqeTF*->O_M%+Xx(>3by|IBmUXAV zrnDjZW!8Oud75VP@n`G!ldsc4#$2#IIrc-^v7}?x&uvxH$9V0t?p-ZOpS$~O>x5%^ z=_Q}mT6dppn!anwV(TVv>-1xVGp+ai=9sS5e!TT?sYg0@X^3^ecK`HTyI$7TBg4`| zc6G3}@QF=-Eef}GSLu{4S>j{ey|_pE{AZ5VD=+j<51VLVow#vW`oIcZ>o()Yrl$`Q zS+9$olD7HcO z>)_7xvh3Sdu3zm-cj)LV8g14lCR4*V13j*=W_S z-JNuUPiTe1W$9mUo?~?}FfDzG*<`D?zPahgPK~hY?ej1_Dzu-~RiDcAkhIQLj{Xhl zJ4ZxY9SMGt-t$F(m0Q%S^l4wYS>26$pZ?g!#%g$Pm5j8XjjY5YBpEvWG^}n<)62N} z^p7@6mYHVU+w!_ihwauGp6)GeOpiNdd`zus^ZJ%&M!>Y9HjnZHGP1QZ+N3`X&&a;S zwMo!ymk~7RMw>+UE*UDH&b7(v);lBU3qh^pW3vsdv-PBm+1y=HhAC3=;N;5 z=GiP+hJEu#%VAg2GRB;FX_@|eZiddZX3OZfY3*NI{M2o8=6Sak3&X@6nQHpg7V)wBGE3hUS)|`RoatPb zVPO+>I`eV1)MD1Xif3~>U_C{va*$WmQlkR0296W9j(K9LY{f9lg6+I8WBd`k_~>g{i7RmPhYki$8ua&kFAAX;B_+m-S$Py~U+U*Q~UmW){oV z_-6GVqhk@_5}Gw~iqPVDW=vM|oOkB?7j(*cz5Ka(s9mqDwVNBv(=!KV?b=&r-e=j! zET_|X<{6$7v%+qsn1@%+$jVK>XMSMU{H$LpubMyWvOH_c>mSX7RMux*)H-55^Ul_+ zK~8(j_bvK1YhBDX^J~Eevx0}MHJ80PmNj$HVsq~2vss>dW|?1Fb|owR#su>n?QUm9 zl?*c*ra4=AR;KvOM>>n=jFMoaL5a zYhGXdYu3f*Cg#yM-ex_p);3@NwIF+FPgV1qlO)-zR=zbWZKt2@clnuFvyFN7jD~u% z+E2FGL2b&+(kflEH}%UiJ0$hZp0YK?Z1}0r?Bb+*X5z17v-4E0nH^ZrIeS>_Su=|< zy|d$29x+?rV{o?VgFR*?5u>xaseEl_>Nz=Er{g-aXzSV8W40_Y>t(PgyF=b=vo7kZ zvTs;SGV}OzWA^RwBg|gE-Jae1r+#LqU+>9Y@Ue?oyO%#?`*e>rlf5{Vz4&0TndhsE z*#nJj``u~rbfv_a{3yMGnKp;ljHo=5Yr0lsX4np^)lVrV@}TD1+Yl0 zSd!D}d8FyypVs7TnigQ%T(>!Aa+8~>ndPpW`iZut&ixMLd}=T;)!%k3=h|d#(~^|4 zIhwzynywIE&G|I@or$31otz!NJvSM(F(F5~ve9IJT1L(;NrlNzx`jEy-32B`2bSlU z*r%J!{GmQ4`6tfAY8ghW`W6z)xNnaK20}?EDz1yd}y4>$ndtg5z#|U z2JP#T`%P7E6PI^=ayPH*WRf*}Xs(BQv`IH^Y;JsRpvm_x({e{H@i0l=J~#JQM|+dR zw@Y(9bInZlPFR<#vr^Y2Hh*jGUT?9ijBYCazFRn%q-(iqLkcXw?yNZH95KG8g3i+{qte2*NMxVZ?wgme#W(tL-LCDxEePUkI4&-w=uptZ)#q*?k2`P4d>?Rx@j9$AS z()s7S-rW<72L1LRZ%pWIqry|Ec`nYEjVy=e=mjk}TkkpcP1 zEp|qCXN}BHeq&~25j!b=wW^*`n8ED)-}Tju%o`TvyW4*$Zj|Ir9A@W>+ zyc^;f3h*q3c%}k8n<1XD0MBZOXD-0A8{!=l;9VHvofP2R7~&lj;9VKwofY8S8R8ul z;9VNxofhET8sHrl;9VQwofqKU8=wpdP!4i=&=8lX-VqHY?XjuxV>8lcV=qV5`?4i};>8=y`XqHY_Yju)b? z8=%e?qV5~u8z{uLV1RF;5Z{IYzL7$FD+c&x3i0je;~Of(x1^76su16nKEAO+d~5pn z<_huc>EjzL#J8xAZ?X{Irar#WLVTY)uT zLR+kdHn|9GvmV;$BDB?dXtRsacI%-HFGgFghc>+!ZMz=Y_+qs6dT8^D(e~@14=6@o zpoc!87=42t`iNrm6?*71iqUuIp${oWU!sRTr5JsS9{QMK^fh|ubBfXT=%EiPMqi|d zKB*XelOFo0V)Rvd=(CE^cj=)ID@I?Yhd!+seVZQoxMK8mdg$|t(f8@04=hGssEa!Qzph`wJJV}OSk3+Q4@@DO7I zU5pVPVyvKxF~dWQ9dt2(v8oC&BJjB>T7h{lz7>np)O!5$8 z6J3l^9%8Jbi!sYXj9qjwhIxpwj4sAB4>7jU#Te%y#yYwf^E|}ZM;BwD5{!j(F(xX( z*hmLsq!NskbTDQr!PrR$W2h30rF1Z+D#6%F2V<-fjJ0$y<|@J1O9x}H5{$)kFeWR( z*h~jwv=WTfbTDQs!Prd)W4IEG<#aHnE5X=K2V=YvjP-Od<}1P2PX}Yb5{w0PFeWU) z*iZ*!#1f1ZbueZu!Prp;W5^PWC3P^SEWy}P2V=|k)jCFM|<}Jn8R|jL@QjCRlFeWa= z*jNW+X8*`XPn9FEmPV)$J8*R*S9$~JdjXBRF%zd;m2YQ6LkT&K- zk1#jV#vJJp=1SU_Gd;rGNgH#hN0>`#VNUf3b1N;(u^wTrrG+`yBh05$0-In6o{?+)WE}xJQ`FX<<%RhPj;<=6Ge8>uF)mSBANt7UqCumuO=nTZXx>7UsZZmgYWtb~#Va{BJxw97L&}EoQYhg}ZhPkyC=Gf(!YinW7U5>f87UtmPn2T#- zPF{|=xhCf5<(R8$V$NQUxw|Ij@a34xYhq4cj=8-i=J@59>uX}pUyixICUgMh&;@8h zCr}REfF^VV<7A9XhNq_0o{rwbSxFnwP-@;QUTqICUh_r(8Xv% zCsP64j3#t670}gaLT6I}-Hj%6I2F+4XhNq`0o{%UbUYQ%^=LrnQvuzN26R9b&;@Bg zCsYC5kOp)_70?xFKxb3|-H`@#NEOf}X+Wn`0o{@YbWD}dHEBTSR0-Xa26RxB&_!uL zCshgElm>KEmC#jbKxb76-IWG(Se4LaX+Wn{3Eh?kbX=9tb!kB7RSDgf26SMR(1mG0 zCsqmFmc$LuQX+Wn}3EiFsbbM9N^=UxoR|Val26TW`&;@Eh zCs+mDpayh=RnQe`KxbG5-Ju3_h*i)fN}yA$f^Ja)9b*-AjS}b_tDt+7KnGa`U8Dp$ z$tvh3CD2h;L02h(&aw)+O9^zCRnTQhpwp~^Zc_psXBBjv66idup!<|S2U-PPs02FE zD(FTf(2-U_S1N(dv}t-RRSGrHFT{K=v=FzdzC;3TMb>T1UlJj z=w>C*(N;rOD}m0o8oFBvbhy>f4+$kos#OQ2J(hHhB`9dk8w%@XLGtD$?AKnGn7U91ybv zCD2h2pwq5_Zd)BX?i%R2)uHpQf$m!!I`A6k!quS@uYqn{ z9Xj$F=*rchGp~W}Tpc>}8tBs1p;NDcZe1NZ_8REg)uD5*f$m)$I`|ss;?<#(uYqn} z9Xk3N=<3y>v#){fUL8988tC%Xq0_H{ZeJZb{u=1|)uHpRf$m=&Hh>z~0@Ps>sDW)j z9X5g**b3BPGpK>>Kpi%OTG$fQVN)GiTG&$5VNgP7OA^df4*RVAHFIZBGq0zIxdD)L`?ghwV=dHo$t=0@Yv>tcPt- z4K~7h*b3EPGpvX0Pz^T3de{=xU{kDzZBY$2#(LNq#jrWn!}chK4YD4#NHJ`Z4X{m$ zVWVt-tx^n|Wdm%NV%RVnV9ON4rr7}7rWiKP2G}~quz5DX_9=!9v;nqIF>Im@u#Jjg zBW-}KR1BMG18k>a*iai_OBKVW+5p?C7&g`h*jmM~xi-M|DuxZV0k&8%Y_bio&5B{8 zZGf#-jNxztY`0?Aa2sID6~m_60Nbt@Hr@u#jrUy!uBkN4Z0DwXfbTkjj&CNVWVz@ zty&D5bt7!oV%V@7Vapc7rrikJwiq_K7ZErwkHLKrfdlgxTo@rZF^|EG5rQN07+e`4I5UsI zoe_dV^B7zjAviUU!L1R3WAhkX8zDG1E#Te=!NF+(7e@$AP7Am>LU43iz||3gv(p0Z zju0H47I1ll;PkYB+amx!Hb$+1wWl#0&h zuIoe2B!?p8TuD?)$8DHxHiwznh7H@khRv7_vja0bI1RHIV-CZN4I|}PNcue=zyIy_ zyWRf3+xPq1sO$QC-iPP?`MRMjl7liyH5sqB=4GE_H}rE*ZF z>ZY<)4$4^FRMyHtnX8-1UO6a(byHa^2W7HuDx2k?jMhzMwH%b$x~c4zgECwU019nqcFb8GAZYmq*pp00BvSJR(j8!N*=AaB&g|cK0 z%9K?oTjrpQS%tD@4$7QWD0}9h3|fV>tW_wx=AaB)g|ci8 z%CuD|+vcE*TZOW24$8b$DEnqr2ChO`IIA*o70Sk0m65AZR?e!-T!pf8R%Pfal%=yO zQ&*vEomCmT3T5rA%G^~bduLS!@0PN7R%P;TDVt|iM(>ugdRArjZYjHGRfg}DvV2x$ z`fe%PXH~}Uma=|UW&Um{`)5@K@RqWGR%HTjDH~{2M(~!hf>vb)Zz(%yRfh1EvV>M; z3U4V}XjR7Wma>LcWe#sCduUY#@s_fPR%H@zDVu0jM)8)iidJP7Zz;QIRfh4FvW!+` z8Y`7;v?}9RsjQ<_na4_HAFawjRw@f=RVK1h*+{E0l9kFzT9uisRCdy;3}vOVlvZUb zE0wLZDq~rxtff_%%SvT0t;%3lDvN1VCbLr6Osg`QmC9;bmD#LRcGId1XQi^7R%JRX zmF=`D<5{V!r&XEHN@YK-%79iX3u;v+v{Knnt1_Zh%8FW*8Ld)w)T#_=m9nH(WlF1* zEww6RTBWS1RhiQ&WlycjpjIi1YE>q+O4(GaGOAU|s#=vRVKDd*;uPGvQ^5;T9uitQg+s=3~iONv{q$mtCX#^ zDq~xvtgTg<+uO?CT9v`Qtt_rpncUmT=311|y{)XSMVZ~(%I;c};k~UauSJ>O+sgJ@ zl<~c-tgl6x-`mRms%p3Bwz9w$WrA-j8*EWV__ngb7G;KSD?4mahWNI!#1>_WZ!24D zQO5YTvc?u=j&Cb_Y*7aJwz9|;Ws+|zn`}`=`L?pk7G;)iE4yq_hWU=N%ob&u?vd$J|p6@98Y*7aKjF7G`;M~O7G<{YD7$S@hWn1P+!kfJ?7&D!Xn`hW)Ox>=tF(?<(7F zQO5nQvhEgT-tQ{=ZczsQuCnkJW#aED8*foY{;sm}7G>t|Dm!mchW@Uy^cH36?U?|UuG9(XVNer2i2eKuGjL8F86GP_Yf$WJPgYrNY#gIvP zAe&;ys63EWF=SRA$gUVNEDvN^)LXgjfozK*kIAyZQ=TVu%BRLj~JGB?$-H--#OwJeSylT$65W60=K%jy_1 zJJqr~h73=&ERP}6Q!U$L$oN#t`WP}l)v`Z^3{bT!kRcOPEgNLW2vy4p88SoFvO|Up zQMD|QAyZT>TV%)>Rm&O~GDp?2M}`biwJee$lT;&{WXLGh$SN5!OEt1fh741UER!MA zR3qDD$T-!=IvFxgHL_0{FqTmx3uVYe)yPH}GEz0NQijY_jqH>mLscV7Wyn<3$W|FL zRyDF#hRjus?3E#dRU?aK$Yj;XW*IVCHL_ZU%vO!;ma*y8hc&WXhD_H(*)BuI>!GZd zA@lW6_REj~dngNL$b>zV4KrlK9?FUtGGh;A$27QX+(TJ1L#FJZY?&cr_E6T$kU4uO zduGU>J(NW=WYQkWrWrD74`tO1nYD+qYlaNlLs>RMrtP6@n<3-&P}a?md3z}PX2`%j zl!Y^7;vUMz88UK@WaSK*xks{dhHk7M$mvevUY~d-6PpMLk91WES@2g z_eeI+kkNZ2t7pjUJ(ArsWcVJ*@) z8Zv^9WCabG!AG)#h792&Swcgm@R4kxA!GPh*3ghSd@Ort$RIwJMKoj*AIl~hGK!C7 z6%Coi$Fhrt4C7;2Mnk6Yv23Fu$C(U5t3EcJ{khy#;duhmEK98zFQG-Nz$Wjzg<&sy0}Lk6@~7Sxalt(6TmWJGIaMGcwJTG>%UhO}0e)Q~By zl`S=7OlxIL4Vlwg*;7LXwN@6@kV&nTO*LdxYh_gpnblg^RYQigR+iO}X|0uQHDp|C zWnB%K*C(>Ch79ZzSy)3R_K9q)AtU=lR@RW2eIh$+$k0BKr8Q(~pUBo4GPX}-Z4H^* zC$hJO4DJ(ITtg=JiEOSRqx(cw*O1wLBD-tI@IH~{HDr39$o3jCzE5O*4Vm93vcHB5 z@Do{JLnio%Y_K6C{6tpRkQuI%9X4c$>tu-ync_OxVnfEbPS)6vIj)mEHe`_NWRVS- ztv}7nd&;(YD31lPS#q3BA?XBUK=vlPi3(Une3;s*@levQ(0|8 zX8Wn^wjsm)RF>N~6F1t**18UA|xm<^f!di`|`8UK1& ze?#WKUiSb0{~!PR(`8Nu|9P!f2ZR59&F8)b|Mz!#hZ+3yZk)6m{PX|L8f@_IJ6WA( z@bBNhAk*N_v#ouh!JqG)O>+$XywAlgHu&?8{&}Ur-zQ|;dV{~8_10E{zwgmG-x~b= zmpLCc_<1D#@07vMr#|?y!Ov^+50%=HSd78LYYl#$SN?8f;pba8tF?un_kF9Eg`a=! zTiq@EKF)Z@S@`|Le9_Os?`uh)6brw<6DKEH_ z`b>xe&xhTPHV2*;i{2XO!1E)zhj?PIPg5& z67$G`=i}sk4K+vH%cvaEssYc>)H9tM@H|~MC9na{*9{fX4S3$ZR@$cl&)*@&@CH1O zFE1M3fai0P>+}XZuU~z)v;oiW_k3S!!1Mg2&t7l9^F1N@-3C1GfBOF820Z_L2YlIp z_d)7!KQ`d~kUIL;2D~qPE}d(@`{O|NwFbOTBCGB-;QdluYBc11^J1M-L*73V=67hw z`>1iVt_^uVJ^w~nL*7^W+L;Y`fBpX6;D)@S-ftr}Wj5q}_fwC;hP?kAzL?XH z_hIAM#SM8sp5C*vA@9qXiR&Bk{`~FW)`q-K9}fDqA@A2~zaDDH`}X}2ryBD9b^qh< zhP;o*kEv|P`+4g5#|?R3hfi$e$ou=i->#0l&)qUT9eKY;Tn%#Mecy6gtRwILt^f6P zWIfPw#t28&2jLYH99b{4&70xK`eFC2GDp@E9{KYfSzq+2T;|An!!Q3mN7f&QZ*6j9 zJ<=`jD@WER11k19vR?6?amn;IxtU>U#QJB}-_0Ab z9@;U%tr6>^&;IP%i1pHx(cz6)KOH-1Hex+>e^^o@)>kKvj&8(yD|29-rRIHs4?qLpJB0$ zS&t4m*taq3Q{RLUjajdLvVB5h)~^@4&S=bf_PaG@jalCgZaKd(>)n^;FKf*DH~Znc zjad&@&)C$M^>N5wUp8jF>^fp^W7f}W_y5|M_4FT6e>P@){pH8k8nfO`aJ%1__4o8w zjV7$edp>b$!utH%+zw4xum5%4w+ZX_jY(lmSkJfq*4~8my&o_*$1{>?a>q^k~NZvS(6!GxnPuKIz|# z{pX8KQk$_Kot-_Y8T(Vm1G&xEuX=QOrWyO!pXR>YjQ#ABb8j?bfBQq!`_0(zy1co$ z8T;R=im#fnA0E?xUo-Z{!#+6HjQ#TQ$A30s|9mm!dNcOZC0p({V}HG}fyIgacCT^G zo!EaD@95yfemt~kS10!8C6mIO*su5c%I?JeeR;FNPVDFNC#5;DzyI}%3@7&cI~o@_ zvH$NpZnhKW0nS?&I&nVmqGg2>=LOHEeBi|S!GjH-I&q$G@4+r7&KHXM9dzQnp>*X5 zC(a)p{d>`g^N6PrH=Q`2c&R@z`t#8hG%-~B~n{z(%PS3s5$39kv}$c<~(ShLn~*_hYt4b?96$QeL;XT=SKmDqMSKTTGY6=Gv`a=1`KuP zylLHnbZ5?=#vaUe<~-`92F1>tPjyb5=gfIk!1GI-IltPt`yFS_v)-!N;LQ0}RoHfC z&bxNy?Q!P(>)yu0&YXv>{`-_O=VM#lE<1Bx7MfD&%=ww;tB;*IPkVN+V++pLdOd2@ zg7dbbV2>7@zqQB;Y{7Y4+ttx6IG=mv*WN8SubX5Y)`Ii9^^xORaGsYuZCVS?_a?6{ zZozrq`Jd;t;Qa5qCresz9_ZM8RSV7szZmyn3(gAx=Z8JN+0%ma#Lm|bx8Qtn zVw*o&aNgK3@p22!ADib_w%|PSnN^Qla6Z{@zhg_zD@!X|wdDM=QG1V;oM%4l8Q7BZ z&A~a*EjjP3Th_ZJ=bz3yhqmNA^!bbFEjb@eZZNGS=cUheD{jg8sbliImYkgX#gT{ypfQnSv5^K4hA ztuCBz&-U2u!g+UO*iSB;f17=Nci}vI)yThGI3Mpl^`;Bwvtu9+yabJ+$c4sT@4~Dw!Z^eDW zqE5fH;(not=lNFLHyrc2-irH&KfBy-#eGDOx8chD#E)H^T)D4U?bY6u`-`tUd|bKD zXxk~omHUmI9THr*?|84x09Wom_O(oP+TfcU_q)_a|2#yyVJ# z%HUhCyK=vB_sSYq?pscu-RR2w%i~{nxN;wpzVCZi?q?o;^|LGYHNS58}9!~SHIqd`@q`#HEp;b+>*Gl4fll$+U#h< z{o#^p-?iaB@td7T+Hk+9gZMctY^x#n&?kDShY}l6jO1Gsh zZMnZ3Kc-V#?lTW~cWcZ2X3DLIw%m6*e$}%r_n#N$B(>!}^h}@8ZMh$*Y0G`@@SmTw<^DJH<;Lx}5AJSn-H!X=e$^iB zxG#S8)4+DzA0N($ZpVFcc!%EYxL@A+OL9Bzo6}wx+m8F^77Paj!1 zrycjzNBb>m$Nlxy2g}=WpWSZ5+IHM;PaM9b9rxXP>b`Er{def5eeJjp-<@)-9rxp7 z>dv*}zTD}ftL?Zy|9!~acHF0bcE7G2_v`PjZqlCn_V>&-?YV#7cEz(j_wlD*32M*% zyi>QB_T1N}9O>Pj`}7(ehY(@&~~mo@-AYAu{%A zd-4f=kKSodUSZ;dy7uH3UaV@|fjq-!&$sSCzTt|qX9w~Q9`6NqApejV5Z!@1#MZA8 zJCKjCBqnztFERG$m=5G8c8<*IK%T<=VnGM;74x%ZcOY+Z`{sfUd=`(+368-3>d*nvF9-nye5$af^pIn#l>$G+Mt9ms$5d8VoZd5{AS z9(N!g(znpjjl9T#n=WqTNBU%SawAXj!v%jg@+I*jBizWF?D*MqBY)!GYp@%6lvQ7j zawDJ8sM{1b@+t*u^4!R;oOF7|jXX>EocV6#Tb5M3?nd6_ud!>~$iD<0`pAtuOku?5 zZscRO{%?;Pd6|Ek9Cjl=)2isC8+n@OKmT$gUz3t}(~Z1M*2mRu*blK-l zJ}I&IS9kJC(Fe}DlV57z=3jU6Ojk2*yOVEPyS>((yi>oYjvdKAof+7wBYCLQ<()c` zkJ^2~zax1mr>+ql$xrpqGdq%}nz3V0NAgwW4@Y(+ZVb(wV>aUI+17FT$|R3d|TtL8J) z{7&TKeD=TIiM-s2>#IAFpBwM=Q77_r`+98eM82-Uux~q&xAQ3YsT29THm{%PL>}*- zEf+eG&s%c%dMEOF&HugEiTqx6{nJk5`QB;Yv@`j>4?@~>Chzw`f6vb3|6;}mb|w#a zpfIX4`9S}ddvzu+IO*-A&g2L4K2GaQo^a^SDV@m|HvB2CGkL?MC(ApNKfHPArOxCL z-7A)MCZE`;dR1rginpI`=uCcbrQ^2F! zZ0dfkGx^A6?sq$rm;A@At~2?`MjaY^kf*F^{toAG&1T2Oi`_-^ktKL4I^x+SeZBNvnG9^&nq5pxZAV-$FW_|Y%``>ugS_e<^tIJ++_9V}mG`WK(`PL(*k0*K8W}SjP$-lOE6zfSI_T=w< zJjutVe>&8YyzKgy$9j^V-7+rAlRRxnSb-<`+NX}sd6Kv7ckX3R^0z~_Eb}Cf>pEwR zC;8mBlRok!uY1;QyC?bGGuL)|lILBqq{BKx`Yo6qRFP^&N zNj|vGnkSy*g|j9$@*+Rnvr8*4^2DdEbo3%$?7hLyi@b5Sj4&_q$Jae=UgVJ{Ug+;d zKKZqGMtG4|E*df3i~MqnX4AaLGf&w&%Zq&T^zu1gg`cOmb-#N~V!^6!UMU+qF3{*x|u zx{!~L+Em+xynJ>KM{n};_Rm{-lc)bS!rhyE{a;`DdXu-`7#ZSC{=W4Wao*(deM9?t zlg~f5WvDlK{cyjr-sJattj+W$&wsStOmFi2&6hsoP2RuCI^Uc8e^KdDZ#=*|*WdBR z2TT~V-WxBl|Inx2_<2+_u(JHLH~ygA-+z1K5uyiFc;gf9 zeOm2}R~XXD;)7r4UDC`4&v5FGHa_@<2GO2Ac!ytC1o+?|!tO=*;32|>nm+i5qnif$ z;3evkkPxHZ7ggO@a;4Kb~nd5`MsQF}p4<6&YCvW)RGu)Hj^}%bj zSpAU?eq+_uZ9aI8Z$fwb;5%kMcfbel@y)(tKKPH-P0#t@L7a#E$Dsx;{yisD&)UNm=w|NC!@knK_KHC+aGHqF_#aFF=`$<>4Rr{rme)ub=ms0NZtnj0xugI1_~YryKkeav_zI)%y_Q&_FEqTQs z@3*JNGJpKvpsTC=@qqo_UGI+%+&=76f4tzU4qyA@2Ty$QgFl{dVdfEkeBmdpPWs~w z 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

!;%sXR|+* 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 wbUL9zhU`)%>DFt=Wm}ZUBZ{j@FEK?@ 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

!QIYFIEj!|cjBh|^|P<0$RP#sD3Q-_ef z)c#~QwHMh*?Mk*+JCLo_)?{C zNbgk-Nv~CRNY7O_NRL&QNe@(`q&uq9q?@Yaq^qh!q)V#3q*2ul(izoe(kazC(lOO4 z(jnC{(mvH9(r%TCv|S}7ZB_|L8&qu4S{04-w+bOGQ}vUUsJciCRIMbHYCcJ>swIh4 zvq^l_OcF;`LSm>2Nw|tkqN=h<1FAGqk1CPWp^723s=`T)svy!ll`pAARdd6BAnd7-LXd4Z~{ zd48%3d0wisd2XtcdCsaMdG@OPdDg03c^0azc_ym$c?PQ0d3vhldD^PQd0Hw>-fyKm z@4Hfz_gTr!`=Dgxy-{L$FO&m$PnF$y50!0sca@EKx0Q8y*OhbfE-NeYE+|X$&MBwn zomNuvPAGHojwsXf4l0xK_9|oZb}A$Cwkd=2HYxq`)+s&m)+n9xRx0iCmMSgt7AsBi zhL!qxD&?d+nNllHr2LV~SANcAE8pePl`nH~<&#{h@_uf=@^)^I@>*_(@JC}xxvb|Tz_R_uD7x_*F!lw*F`xq*HJk=*H$?-*HTHzHB;u~8Y$Cq z^_7XaI?9;b3Ci%?amt|Fp97oDs#{oa2g{IY$&%att~u!nhn!@ERZfD!G$%%3kQ1rU$q7}A&k0og%=S}!$@Wsb&vsY5 z%63*f&2~`S&$dzA&bCxs%QjV9%r;V-%bu(_nXRKZl08AOKYN^FSN2c&*6eTc_1T}~ ztFzzBmuJ70FV220*JMAD%d#KHh1qxIoa|e2diHfWntfT`pM62zm3>a$ntfV6Kl_Bd zCi|$oD*K>(M)p2=N%k&zVfJ=8DSNX#D|>@HHG8c*A$yfPI(xZ1H2W`kK=wkpceY0E zmaULGWJ~4N*+RKlHcxJt&64Y8)8*r{G5N17s{CtKzx+d1kNj0ur~GMFtNcM$ll;G| zdik}iTKUDSIr4K^v*f3;X2_3bmCE;L70Y*J70S0}QREx4^5mpnWNFFcvwq8>vcAhgv%bjuvp&eYvfj#EvtG#@vYyGTvL4G!v+m0b zvhK)qvTn+>vaZQ~W?qtg$^2LLKJ%RHW#)+NN#+UJ{mdh>+nEPtS2Op@E@bYKoz2`X zJCV6rb~tl`Y+vSD+0M*Wvdx*xW$QBklC8>IBwLoLkuA(r%2b(BnKV-*<7e_^tW36y z$YjWRU9!2EZL-SDW?5NggRCgCPL`ipEz8ZUl4WF8$dWS4 zWU-kgvWUzgSx{zy%r}!P^T^DRIcH|dY%|kj7MV#h+4nO~w!Dii|JP#Tg%^nv8c+dB!WLDC4=5lkr$e&v+n3Gww?JGj2({GOkNoGpAKGR{k@GR{cLGfqmUWgL@E$v7k>W$c$`X6%usWbBZ}XKa;5W^9s%WUQ0=XRMKW zX8bL6$yg?}&sZY0%vdNj$xuu6GZa$o45@TnhEVz=ohSX2&X&GSr%PX?zmw7ZhKX}2Xe(ymJ`r(KbZrd^bbq@9-> zOFJVun089CJMFk+TiRjChO`5cHEDY#E7EpK7N>2KXwo)Iv(*npiR`O&}>x<4TItSdxM?nj|+3lVqe(CCO<6lGwB! zNkm$wBsi^2;+xhi@kncsIHlD|Y}2YG7HP93Mrjoiy|i-4gtY0B->JotZ>duxA5tlj z*Qt4uXQ|ne2dU|j|58&V*HRND7gOUT=Tf62CsV^EM^Zy1`%(iXJ5&86TT;Cw>r&k% zt5RJgOH&;s3sY?+s#Gh9B-LEPPc@OSQVk_|s-9#hb&{kfb%LZlb)2Lr^{2Ql^_zH3 z>SyuH)DPn6sc*$oQ(ub7sn5jOsgK2}sSm^nsdvRuskg+Tsn^8;saM2asTakrsiR{1 z)H7nM)Kg;9)Z=3P)Fa|asRza5Qum5~r0f!ZPT4Mgo3cgxB4wlaQOY{;-IUeh8!0Qr zms6IBM^l!FM^YAwkEN)^2T~N`-6>M>wiJP9enV6jUrr84?Rq z`o-*&9x*MYLrhI+75Akyi91pn#LX#n;`)?o@!XUuab-${xHP3)T$D0hOi3vg=cE*h z(^Dwo#FRX7OiH#mEG0u6n35{?PDv8GrNoIHQ=-MzDG_3`lu)rjN|0D5#b2zI;v@c< z>>>V~>?(ej>?D4fY%hMCY$LvxY$?8(Y$m>vY%Cs4))${n))gO1))pU39xvXL{71Ac z`KM@O@;A|%HUl(;IUlBDY zUli3RkBa6dpA}UmkBCZ>Pl$?=kBTVCheSEa`$XxqXwl zYejC!t3(dTD@4}Ge~CIEl-*)T9i~NQYXz2 zNt33F1WCmrR#KseNXi!tC6PqENjaj9q)bs$QkrO9QnF}HQi7-=DONN+DM~aoDO^NO z3K3-|1&UIW{6q;!UZSWZcTs4Pi^xC8QRJ0mCvr)$7TG6Rh%A##MJ7o`qRC16BJCty z(Vs+Z(f7phqK}Dxgl`gm3ZEr@6FyA*Ec`F=gYbIdTj9mTSHg3N&xI!wp9qg6J`nCp zyer(9cw4wR@rH0+;#J|_iI;>+6aN)1NIWN0CY~0G6Hf|xiN}PD#KS@?@qln3aj&pD zahI?)al3GS;uc{|;znUr;yPh@;u_(!#J`0FiOYq#iGK+*5*G=R5{HGci7H`uqFfl1 zC=vQ33We^8JfUMETWFKW5Sk?tLc>H} zcrRg|@Mc1d@JhlQ;b_7v;Yh+v;jx4=;emt_;qHVY;nsvH!VL)&;p&7u;qruR;i7~L zp*kT|C{0Kb3KHUltb`aLkq{{yN(dA7Bm@iF69R>e34X%51aIN&1P|fN1Xp26f|IZ? z!Cpv8un}e@SPD}T%!Kg?#=^)117UE2p3pC0lF%bzg3vi(oX|Gmm%t+7yTB;ni$E{o zqhLb9JHfB`*MhI{F9q-8p9x;YKNdWRe;~LYe^+oT{#>)icc(EWWULZ(`=L+KES%S!Tx*#MT z7x={^0?+tCfpdJnz&5@|U=iObFp6&z=*2e+CdAJd{EnL^_!?Iucpo=M@G5SW;7MGC z;C@`W;8xsp!PU58!M|~Zg41#Ng5z-{!NIs3!JfEG!M3<`!G^dL!J4>4!Sc8`!Q!}R zfjTZiAd3qX2;zbSthfLH5$7uyit`fm#<>gH<6H!dagKtzI6J}YIBUVoI7>lEoS9%s zoUwouXCTOo(-Wk`O%lY#O%O!HX$gYke)E0fe(*iwzVe;oKJjhi-t*1l-tZ0MUh#F~ zp7Y1YJ>ma~eaQb3dyoGv_CNm1*jxO^vDf)`W3TXU#$M!KjveKX#-8Pm#E$Tf#-8LK zh&{&N6?>S!HTD31ee7QTs@PrpWwAT>3uCwPRk54+lGycpUhG;vGj=r}i(Sbdj9td> zj$OiUja|r}AFJWl#H#qSV&(kuSP8#4R>&`if(Kzc(g>zau7-m`*F4>r7rgN? zPkFzhAMw6KKj6KKzRPM08@++Yi2jF%Mz7}eNB_<1ieAoZ ziT;aMAH9fI9ld~88Lj4(Mk{zl(J~$-TFlFi7Vy%dxx9pEHZLlg!3&Khc>d8C&oi3J zbBP|{*+uvAETX%4#?c)-z35ingy?48@2L5_uTk@O@1tsXucGGiouD$kW_qkteweBad^Hkw>_a$b(#79CXT*OU_T)<6;RCA*u72J?W8P`8j%=L^Ea-Ac2T)Rj% z*CLY1HHxHhbt7@^gh+(@D`JrQC8D4EKB9;FGNOz7IHH|1mGop!mIii7kK4Kns zB%+3UG-3{Se?%2`XG8^eOT-NBx`0>u33aR*C4`#s}o_w)r!#P{s`CQehQz&eH%W3`#fBW z`!M`B=fCiuoa^D=I2XgeaL$H*f6MovKZI*wQPT8?Y@Dvo{lN{(gtGLCWhU!2L| zi#QX*7jS-usX1T6l$;M?GR~_o3Fk?ekaI7L&$$`K;amx0aYn=FoRKhsb2JR)90;Rw zc7+XawuJR@)`fL*R)uwPmWH))7KF8M6k&}VQCI_q8&=1mht+V9usNK*uqsYxSOup! zYzAjuSQ%$dSP7>hte7)BtdLU}R=^>JkvW-Rd7PB6Y))KQCMO~+jT01>!tn`9@&gxJd&azN#&Z5u>998HzjwJLqn-}_n z%?SO*#zH@{2SPuxyF%ZyTSDKk>qB3$t3#i&D?^{MOG6*Ar-nXYlSA*ZvqS%5r-t5Q z$A?~LM}}Ty2Zvr}`-NU$dxVa%okGvDZ9-48%|cJI4MUH!bwZD_wL%ZEe}o)he+t>l zejBoz{XAqR`(em7_J1K;*w;cfvM+|LXP*uEhkYVsHTzJ=-|Rgh%h}sQma;d7EMcz+ zS;$@1R_ydf3?^UF_75c6NM7D?2KrnH>@`pY0b?&-MtZV>^XZvu#7>u+2lN*oGk$ zY@Lu9Y^{(|_K)Ba_UGVY_S@h>_VeHZ_QPNb`@i5k_O;*~_Ql{V_SxWc_KDzB_MzZp z_MYGb_O{?S_Qv2C_L|^G_VVCx_M+fWwkkM?EeQ@_^Mn1^%wTUe7VOC$2zF<81-r0Y zf}Pk6!4B-|U|V)&ur<3h*pfXp*o;jMHeqK68?jS^_1W>kdhEzx9d>Z=M7D46c(zCI zIJQ&pFP2U450+W*Hbpw+D8 zpp~q+pyjNHprx#!pd~DypoJ{ApkbCnkeX!`q+po_$yoY95|(z5i1jCs&-xa~Wqk-_ zvt9)sBDjx)MlbjRp>|Mgsd-M+19U`vbdJy8_!;TLN2I>jIlue+M?Q z{tB#T4F}e-O181^&1It*vU?FQpU;%4d zAemJVn8(Tu%weSmX0Z|jGg#4qsjSezWL7|6BFifjOSA=LWoIRtCIfmIl0LP7Qd;BnLcWW(7Q9rUX1<#sxfJMg-hr1_j(<`Uc!$x(D1~ zItE;0S_fQVng(2A>IYn4Y6qNW{_#J{{N{g}`N97b^R@p8=2QP;%=`X_nYa88GOzgW zXO8;sWsdmoW*+t5$=vV1ow?J03v;voCgwW-4a~p&*D?R{U&|c!U(J;J|IHNoFK4p- zmof?eCCnlJMa&-m1o4YSc-#jN$0Gpqci%yNG*v)EtA%=hOrbNso?G=DZT!Jo;D z@~1IF{BfqgKg#sJ8*~~M3mCWOQGnohd%9*?UN|{^zN|@{YikYkYrZSiMO<^wZ z%V#S6$V{`B)Xp*N}O~SD$&^SC4toSBH7lSDSglcLMW}uNHHU?;plC-(QRk zzCReNeZMi5`F>$6^!>z8`hH-Decv&-zHb-|-&YLO_XVTh_Zg$p_X(rf_aS4R?|sG` z-@A;NzW*^wd~Y$P_}*aT`CemW_+DWo`Cekg_+DUy`HnIIe9tkwe9tgkd`B4ez9$)$ zzQ-BHzDF5)zK0nTd=D~y`Rr$W@!89G=d+vf!e=Mrkpq(q7kxG|&ibrp zobdUFamZ&4V~@`&#x|doj14}^8LNGkGM4!)VJ!4n#8CMxU`Tv444#jQ!SGQqP#+ni z-$%me^bs+deFThoK0L-89}Z)t4~tRa!(dGDp)vA&a7Km?%1H8|GGcrN8DT#Ci~yfr zhL=w_!^NkQVdvA%u<&VR82L0abbXo_<9+5cetOq4K6}?O-g(zBUU<)CJo28+xZ^#G zaoxLuanXAQIMar$@9qx6rShv~0957M7{?x)}P+(*CVxrctma~FNoa|eCIa~u7r=N9^Y&rS55 zo*U_#J=fF!@%)Fr(sK=ciRUW1#&acI=DD0M@LWb`dHzMmJr~mlJr~luJ%{P7o@#o7 zr;1+fsi0SS%IKw@68cn65uNNQpl5mV=_#IEdYmVl9^uKP2YJ%zKAr^K%@e0Pc%pPG zPb%HmbC9m**-xL~*+>87(L?{@(M5mf(LsOV(MEsd(L%rD(M-SY(MZ4O(Lg`zF^_)2 zqn3WiqlUi6V=jH0$87ork6HB99u@Ru9y93+J<91yk5anWqlC`&D5ldris*<(A-&I| zfZpLjp*MMu=ye`>^w}Oc^cfym^l2U$^a77`dag$*J>4Ujp6HQCkM@YChkC@({XJsn zo*q$jXO9TFtw%WB+#{53=n+iU@d%`AdHB=6yZg~Uy8F;yyL-`}x_i*?ySvkGxx3P@ zxI5EF-5u#8?hf>$?soM3?l$zD?pE~8?w0g_+|B7L-A(CB+>PlPcO$yY-GDA|pG;@D z>(X&|9r~cVHoe<@0=?B;i{9Wqj$ZBln^x)mlUC~foi^3|8;#`tg_i05iI(jCkrwOz zo)+%@mKNy#n&$2PistJ6f@bgjjArTngl6pin5O6ckT${nKJAy=J=zzyJG6Ihw`nij zZqgpP-JsoZyGFb2c7=A)?K17G+eO+5w|{8|-Okf?yPczLbvr{_?>0hP<#vj;)a?Xq zf!i^f!tE$c-S*LX-S*Jh-FDF$-FDJy-L})J+_uun-L}w*-8Rwk z-8Rs&-PY4m-TtA)yRD%`x~--KyZue`bz4DmcUwktbX!WZc3VO-bz4N!cUwr)b{nSs zaaGg4xvFR%Totret}@yaS1IkDtC)7fRY<$!DxjTn<e@r=bM2yaxOUK*T-#}NuC27$uFbR= zu1&OQuJdUHt_`$Y*Lk#b*IHVlYYi>hbuKN`bvDi4wTkBHT1j(qt)SVs&Y+pOmeCAc zOKFo_OK9U1lnSkc$(TJmL_$Hrtw{(XiS#~8s-vC>vsvIb-D!8nq7iu z^IQUGb6ossGhKXXB`)5yDK1{LJQojIhKoBb$;FixPjF19pV z7aN+nixth##geAuVouX?F{S-*HlclTHln?8Hl#gs)~DTfo=m&ttV_G%Jc%~ytW6tn zo!$ zLv%U6CR&_d67|l{iMh_thzjQ?M2YibVv6%aBG35&k>PxgNOHbQ#5n&)ggM_P0-SFW zUd}fN7w2n)t@BmF-1#zL=zNjTalSxkIgb)QoX!)UoX!$&oX!x>oJNQTPN#_5PA7;f zPREH+r=!G((-GpR(;;HN(?MdV(|%&J(>~%Kr#-|?GOi5PI&Ky*2+Ct95TA?lsh5_6nZ6EmGw5hYG5i78Gih&-odM26E+BFSk95#zL& z2y?c z+;U_SR~(tds3U_IaikGP9SLH;BS!3SM2Ss~RAQ~;5V68>kXY>4PpBRH2&rQa!FTK? zn2wzU>exZ_JGK#>j;%zqV+%3Qv5A=D*htKDY#>S;>xn6jbwsXXEs^e6O(Z(bC88bY z5TTA$gumk~!qc&WaCV$Y*f^FGW{zcqfnzB#$+3hO=Qxe{?odR0beKxKc9=pubtoY2 zIph;J9mvFG2NH4KA(uGikV71C$R_qVWD+|ZGKh^1X~Y_bRARY9GO@@ZiBLHt5)y}a zg69xNFdSkC#37pKbBH2393qG&hj60KA(WWy5JJpw2qvaE1QPiU0Yr|2Kau9(OC&h> z5K#_ZM2LeY;pgB%csRHbjt;JbwSzNZ>fl7^J2(>B4)(+!dpqKry)E(H-kNx6Z$&(| zw;=A?n-e$e&4^3(Cd4^=W8$Q}5pl@gfY@WNPi(WFOl+{%C05((5X<-}5?e^n^cKdLW z-5xyCZa1D}w+oN4+kuDKZN~%bw&7lOTX7e=&A6T2CfwX^BW`H79@nv3hilpWga5Ey zi+{3RjlZ#7g+I0Z8^3S662E1;9KT|_3?H>!ijUYX!H?K3#`oDS!gts%#5dUv<7;g- z_zGJ!zSvfUt85jx#8!^;Y-KpZR*IvxV!Yp0gm>5q@g`dVUT4e0XWMe|8MYjJnk^g8 zw`JlvwhTPYmX0Ub((ou-91pR@a6elVceh1wM_VdxZ99mY+796Qw*9!aZ6E&IrU(CO z(~ZBk>B3*ybmEU~+VQ(KZTNMYR{Ww(3x3w789!muh##_@o<|dc%V%_?rlTCU2Mp>oec@Mu*t)XY;tg2n{0f%O&0#c zIurk7oq@lxPRE~Fr{WK+Q}El?$@mrPBz)955g)OR$B$aa;rp#)@g3GN_$KRUe64j9 zzQQ^JUu+$YtF6OuiFGK>vkt)-*1UvH&@ zudL=D<^#iN6`i@mteZ!_( zeZ>l`K4T=SPgthaM=Z(e0~TZT9t*R2hXq)@#k{QEV9r*rFJZFiXpWn6c#n zOwV#ZHo>rB_*h-7_*kX%ynA+kWOlq+f<6Er3m=>!s)M6FZZ}B(QX|WP(wpfA9 zvsjMJwpfPEuvm(LQg^JtVhNUOu^3CUScD~5EX1NL7GNP3!w%_Z1-b20Y9T!cL`7h-qJ1=w|SK6cTZhn+F!V#m!n*g&zM0-{y4eFLN3;Y))Wua~u38#Je4-R47Bi}@f{56m?m zz$(o9u@dt>Y>Ig=mS^6BWtewkN#c`IgP-h!E#H)96o zP1q#!M(mGSBlgW~KK8+^0efXuk3BJ)hut-+!)}<>VwcQnuybbB*h#aw*deny*dDXl z*fz5&Y=hY>Y!$H7tP)#bR)HzZW?~|<85rBF93#xiupzTjtjBCR)@D|MHJDAqs)0(g zVyx7x2rD$3ijm9;u}rfmSh86G7HgJ|g_%*X05dY?1-O`zFk7=c%-k#&Gc?P=bj-3b zEwe1_yJ;r&5qNEyfju=%$L^V?VK+@vvCF0@*m=`r?38H|cGxr#+Y4+rO~5vq#$&5Z zvDiY>7))szjfnw{X%t2?jl`&?5m>KjIM!wwhRrt(#cF_ArXg6FX)rd`GzcS` z24Y#J0a%KuKNbswoBCmaroNcBsSoCA>W$f%dSMoTk*O!9YwCe%nYv>?Ox&BQ@{}udu*SH9k#>77TaiIgRL>K#+Cz%Osp`Ki6tgB zvB0<{<`^A7Ow6!e6H~0+!~|0vpy<+?m z9R)^=f1pQ=zoYw%zo9#fzoMIfwZ>o2<;I`UMaG{{6(BMGh;ofTpmgK+C<62uze78W z-=dAiZ_rwx%J?-}Zu|-@GJc6tfNbLzXsYpZG|u=L8UX|uKSh0vpP;VBk5PNT()bZ- zZ2S<_HGY7O2YwpeM?V|gL*E+RMV|o=jP9Vfjs8Qg8r?=mff1uy=uxAa=zd^_(G7Hy z(RFmK(KU1hu*m2tsxrEQN&ueGWt47o2}O)9qJ2Pz(FL^8=wGxJs4^Nw%Z<*XMMmdP z3XpAd7ELuegT?_7MyJsrqY=~xa5FlE+8dojEsai~#(jQ@K#v0Z4fmrv4ELd%fVGBu(G`Y!&_#gCa5pM3+=cQ0 zy5UX~G2DUn0Ud_h(MH2xD_oo+=3PX6vNGEw&5l;6^Jw3h(;K0K!X4u!}X}E z;X2eFur&M!H8xy}>H^~p*PuTQ)}WsaR-a)=`z;b;Sx(HC}Gf^?X)n}k|eL6}7di7~& zJJ6_4pf$iOeH<+Viu5s*tdF8uK#D$s#sT5_R5TFq)*nJ$06YCb)Ixs%H3D?>`%x|6 z$K*cr6YzR+FZvX?Ke-3J1zeuojh+WiP3}UE0DC8QqT45Tpc{cTliSf{z{1IGs1guO zZbdl&ZE_1r1$rhoqiw+a$xUbtP&v5~Ed{1do{y4&%*hRCG7vkt9t{TqCeK5?0GG*i zs4ZYVxfV49bSBrJOTMb&}CXz#ZVaP7-<%IHQw@9tREr zyLA%KExBOMTz&xF3bPg~Bn5Gkj767?GnocB| z07L;HIuWQZ;I0#nIs#UJiB1?g8JGzCniPtD0p0=6CxxI7f&YMOlY-HIfz!aTNkQlV zU?;G7QXu*dumV^-DF9UiQh+zfA7ub2&_Br!?Esp9I-qKjFIo;11NoDD&}<+Th@a$* zMgl>A&m=F@4X_6+0pm%Ys2(sL_^IuIeg@tE&$Qjq2f%IM3NWhehK>M7fqmMp=nh~L zum)JJ?Sd`>RDc-ZYCEHJ00DZnozQlm5vT!XX*;52KoLL&va}u06d)D|2LiS2QE$Kn zumdc#?NCEN2hak3OteKm0;<+18zx$ztAS;}0zd(X zCR(B#fB=Soo{1J{8_)n$1C_w^iRNe_Kmsy>Bp_y@85#!o1D=30U^~$iH3JNQNx+{8 zrs%f`Cg=y?74R6i3*4Asj9voH0w;h&!0rh~=vH7ounPDK7zX4M3{e5V0&rju=$c@F zwgC0O9AG9;0!*2pkLCjDKq3$ggaCek$ArnKBVY}f0{Xy2;P-ev^egZVcmX^D?f}<- z3*&XsGr%$60I&<#0{jE41eT1~LDhg1-~&tm1^R(bplSRhv<{dJ%m9jkd>{u%1LDVP zqme)`-~+e;4uBP44Cnz9fS+0u(a*qJ;5qOBxD8wdMu8FFD6k*cp)~>B1gr&?1B(C^ zAOW}l9YBCypdDxgYPH6rvw$+72%rF2Knf5Cgad(qH{c4`0TzG}paWfHPnVm;naBB;XG+ZrmSa!Jj`!^q=3zMKJfid2FUVoJ$X9%ftEbaK<~F(++2? z!+GhCkjGbM8k>#jNOgK{K;1JoXA;heyfP#m zo7uQ==QP&ziJZpq;Jv3YTA zgZlT&2iJ_vUh^G%GByM4>ww4II|GNu=863?Fz$|IVDZ>&u8M)%V>7b62JCO^59}G6 zHw9-#ZSPl)&2ED8m*5Pgw0@f#9{oGV<`Kb}LU1n7>b}@(oW2`lGj{U&?63Ou?H!v} z^R)N(*xZOiFg80PwOjkVefO%dISg=?!ip}**i3_IT{p&N3ry^CJ9D%1_*hOq%-V-} z`7rYy=GMdPd6*v$GvHy4JIrE-dFn6|9p;+DY;u?{4l}}G&Ns~JhI!jCGaKe!!|ZC9 zKMgaaVGcCRa)x=#FjE=kBExKBm`@BdhG9-H%=(3Sy)bhZ=H|leTbN%9GiYIsEX;z1 zd9E;%73QkKY*d(U3NuP!&M3?Zg?XPavlHfS!t6|#e+e@zVGbqCl7xAXFw+s{GQw;{ zn2!iE4q;9q%o>DwfiUwA=JvttJ(!;dGw@)J9n7MGd2%ol4(7VSY&Mv$1~bxN&Kb-q zgLz{xGYsZ_!R#)WzXdb2U=9|{vVwV3FjETVLcwe&n9l?=mS9d2%sPU3MKE&+<_5v+ zADG_*Gk9Q*4$Q)Vc{VVU2Ik7ZY#5mD0yA1*&I-&*fq5q|vjpaj!0Zs1{{b^RU=9b& z(tvpwFw+9&Qow8pm=6In9$-!b%vykX2{7{j<`%&00hk{EGXP*8KkUVa{q(RW9`?1v z-gMYs4tvC5pEvB)hW*yCXBzfB!`@}se++wwVIMH;<%RvYu%{OG#lqfJ*q;h}OktlW z?Dd5Gny}{*_D#axN7%mzdk|qCA?yW&{d}+|5BAl;-ZT46Fu^_~*h>WafnZM$?8||@HLyPh_PD@471(P6`$b^S2khH`y%(^50`@?_ zJ_gu}0Q(7GPXMgzhqd{zz8==d!#a0Zs}AeUVa+(K`-ZjKu>Km>P{TTCSj!CSkzq|S ztP6&s4XRDXbfXwV$wl6V_nDI!agz3F{eQO(Lu-gtdXNz7N*u z!8$uwD+lY{V9gq=JA<`ju>K3yaKSn(SW5-#pt$fg z3#?m#wI{HC1lEASIu2Ni0qZGXO$4lKfVBy*z5vz;z^LU`8)@9W^58@xAzcVF=S3f@7%dn9-l1n+a; zoeaEJfp;VDegoc7z>+N8vXm{4RvwcJTWQeq+J!B>1fZzgOTl2mEe;|NY_r zclaM1{*Q+Lh2j5M_@5O1uY~^%;r~1M9}WJ`g8!A^{~h?B1^(}V{~h4|5BGey&%?bP z?&ok1{~J+!hI=*KpW&Vi_hGpAE~Li1f_p67SK(d?_s`fN>La*MvIm>)!~O8s!0bD4 z-+R+vehcnz+Wn>1;XbCIhPYcpJr;TjCrUAWf5^>r-c53Hkb?S$(kTqEJS z2-iZm{=qd5u5)m0gXkV9E;JO0W61aZAHDhd@0BZwW z58xUA-+TD3!}lA$)9^ip?=E~_;X4Z7OZYCr_Yb~v@I8a?7JQ%JI|Sbw_^!bB1HKdR zJ%GyWj`YGf6%5?PKcMV26o zkOhbaQ6UOMhDZ<*B0zWu2Vo%$gg`I^L57e4q!;N%I*~S{1!+PWkaK6B9Krd2=PaJ5KqJ%aX}mrJH#5XKui%ML?6*b zCL&tMZ|V=~SL!F~JL+rd3+hwqL+U;1f7Bb)tJF)>QR-RhDe7_R5$XZz9_mi&R_Z3| zI_etgO6pSTV(KteNtIDWR34Q@rBN~J5VeomMQx)tQR}HS)Y;SuY8iDJwUA1o=2A1M zsnkSjEH#1}LJgq$P(7&5RC}s5)r@LHolMoHYEgdK zKQuCQeCW{7-l3gCTZYyTtsYu2v}9;_NHHWH;ter}@FD6@-%#gJ%TU8m%}~`)`OvhX zDMO^8tfADQgrTUR(4l}K?;+PAhau}B(;@aEv97(@sA2RjFw2kQst4pt0KA1oXs4Q37|55^9L z4+ai;4Y~~44O$Eu4eAbR4gMJTH1KBN>A?MgTLYH|&JUa#I6Sa-VB5fkfmH)b2Zjga z1A+n805;G+&^gdFP&ZIDP&P1ifIN^nkTeiI5IW#D;6C6mU^!qkpffOT;9LLu{uliZ z`)~JO=|A6pvj0&3?*1+P|MV~KU)Znc7xpvz(f+>v_Wp+cx&1Tyi~A}4nf*!qQT@UF z-u*89HvOjkdi~@3zxTcGd(rox?`GenzB7GC`}X#2>s!~iqHkfJyid@_=%e;^_ciy` z_Eq+k^yT+u^(FO1^#%2L^*Qxf_8InR_x8-Q2sTcWJM> zSKQ0$MSFXCTY77ID|@H)QhGCb<9ow<{d!${ZF^06b$b8weC~PG^RVY;&&8gRo{-|&>*4m`J$*f`J#{^mJ=1!~J?TAhJ)u3`Jx)EAJqA4!dcJkP?S9gIyZci2 z>Fz__JG(b@ujpRTE$L==Bi-HIjoov*%etp@XLToZhj;sSJ9k@k8+4EF{?_%T>rvOu zu7A5ucJ1%l+O?)@NtdFF*M)cWb~SfZca?Wd>B{O#=nCud>2m6_=+f)@)A_0MMd!WF zE1hRL4|ne9T-Uj*Q`ITxq;>Xnwscl^mUk9(W_HGPhID#%+IO0CPU`&8@wVe}$IXt> zj^iDBIyQE!=os!0bX@A*%ul;iSNc(~I zt?jGZ7qv^bfvZrN3%hDD_3#VnMrLCo=rL=|IlH3yB z;@x7`V$`D5@~Qc0^R4Fd&4-(}H?MA9&@5^un!B4Dnk$;8G-otNH~Td^Hk&k0Z2r>p zyy4cHH_cx*UooFGzkhzy{8{r0 z=V#21n(s5;Zoc9CKMn619yDBTIN7kf;h%;@4Wb6Tp|hd3p|l~dA)z6t!KJ~hVPeDQ z`lt0b>QC42tKU%nSG}~JUf*3mufDvVRG(NMT<=?kw|20$skXAVpf;s8wAQWGymmtE$C^hqS87hw?5J5) zqpso9^w-Rf94^cg|fkS2dS4w|8#++_Jg3b7SZF&b6JZH}~6|=W}k%8JV+t&YC%z zIh;BDbL!`m&B>h;JI8m9?Hs*1-)29ZeQoy1**j;inysA8oZU0Kc6Q0^tl5#XJ!f0a zo;dqs)x)ZbRY$5eS1qj)S7BAHRaI35RmoLBRgP7LRX=CFoON^7$gJJ7R?kw+V$SNB zRXc0itc+O^v)pHy&C;6nw(?Hp`O5v3>nayiax42Q>nlquvn!)2Ju59LCse+#xK}Y+ zaiC&d#excMMSn$oMQKG=MP!9Xg?WWm#oL*8W}ch5cjnrenwhMbJu_=(7SBwd89LKt zrqRq_GhWQNG2`Tn9Wz$Ukj=nnw9S|`gEAvwhTja^89FmQl|L-MP=2s{efh$2Zh2pM zU3p1)MtNAdOSw_`&$8!b*UL_nZ7W+=CN4wDn#yLB<(5U4d6t=%X_dVxyZg@V%a|5A&1stc zv~R_ai!T-*EM8ZvDP|RS70)RyC{8H$Ew(D2Q2e&&cF{=Dj-us7;v#C%{G!sL%%ZR& z=OTlmuTvjQy)gB_)PJU`r_!gkPn|WDJT+#j=Tx()e+pj|UMoCSxT$baA-Axnu)1(c zVPc_Qp;h68!Z%ZHO*uJb+myei2&VK;shd(XC3#A~6q_m9Q{ENaE*L4;Ua+)4STImf zS5RD#QV>vJQ=nb&F8_A^sr>EvOY;T!{rR=|Q}dJZ{qn8zC*;4O+@zeKY@sZsa49{M zxs(D*JjI)0PWeNALB2vhOkPjckQwB5awR#J97%R18mF+p zd#m@^-iLZ`?Y*q`oZfl8NA-^F?ccj)Z`C@bac1qX3>V| z>rp48IO@%)m!hhoCPyVlMMiawa*29Cm+3g|rS9MQFYdz-n$n7zrN3R}Tdo=0sSJ364&w};@tqGbRR2VciC@#n^$Sueibglb|?$zDb zcVEzbdiU|&6T1g?Z`s|Z`%i&i1#;lVz!w8c1JeTg1O^4R4y+e=E8uj%zJN^uivr36 zG6MPr^ayAZU>ESS|7rjI{%`s(_AmF(@bBjz?BB-U&i`k>(|-H?-t=4KSMHbL*VnIy zUmL%Aez$y2`|k7IjRpM1XZ(LQhZyx>#fGu|iBC%~tLkCo5$ zZYR1`cU#x(`EEts#&wJ9=G(1#x4*mo*!6hVJzdvyeYWeguA{p4>e{udYuEc-e&}+v z%dRdfy3Fa4-DPMO)umIHhF$J@f8%}Fd#m>n?-|~cya#%Rczbv|c>m&c#%sUVCa*no#%Hh=sdb}Oy{nhT|3|J^h2kkopyCv(P?(4tWHBZ zMRe-W$*I$wj$d~?*zv87FLkWwn9;Fs$Dod_I$C$U-r+=ty&cwenBSqG!{`n%9lCaK z?eJ&&@7sUUenN18{HN1%thhtcC|+v9Ebv|ZKq z>9)CThqYC0JGE`l_O~|Y+Z=53R-46b%G;#1Noo_=#=VWP&5x~*x8B`)RqLl)=d>Qy zIU_y-CDS|xbObG`zP+(-Iu!0aG&Vj-#ys9wY#nR4Yw0+ zd);1ld)955+Xy#u>+IIh?YHLVn;&exx%r~zWzAEYCp7nO-n{w4W|x|M+H6;|<;`X_ zo6>Ahvyf(Oo7pwH+4N-7>Za?O&TpFEbY#=$rd~}OHND&9e3L^>-fFVANm-NBCW%e_ zn>26o(Djn*QP*9r%Ux%=PIevW+S9eIYdzPWT)uSK>+-tGvo6zIM!1kmCzl2;cN(8- ze4z2B#tR#lG)`$8*Vw0Vlg9U*zjyxFd7JZ6XOr^;=YGyX&Mlp-oUb-I-e`BDm5t^! z%4(F{D6ElZBl|`_H$2r)H(cNFxrPM|M>ULY=+&@M!@CX6H#pc}bAv?S@IAg7j2R!&zPk2~&mT#wapul}_9BkEKAPW2nqzvFPu;ef*?hZh~DJB)Ma?a0FT7s6dJgq& z*?whfwq0jC-!|WNq-~UKXWIt0cWlnt9I)AB^PYm`mpue)-PLE zSf^PhTKii!w|;1K$?B-pPODd~W>`(M>TlJDQ zIA+y49NAVz`^!jJcaVMWe)8AKb{LHH4kPxvrqtxvjl@}2Hnzo5bhl(hm&jimyUTLG z-qu|zw6}9X%Ercaa!#Qf3zSPOK~t>V{H4b2lJ@8&yCl0EhRCkB?D}U*b@nzNNj)}p zd6F{PZ@?jBHbNCvHa7Kf%+~0QLpBA*c4%F_1ROWmnQ+)-_YwY9+1*1Cs)al3l6B{zWN;e<4+_<7TtcezjcZE&Q=rZC_n;Uk1lgDYM4@(QVo|B4x07 z-IBC*yd#CzS_<1cW=X@>*+05#C&vr&Ox9bH&W@Yq=!TlBSha70Ms1Lmc;b)!9eDz8 z*!y4~Eg9wDg`y4m8*Iin)ZVYJTzIS{Y2_%d$!45`+>zXV&C^J6cywWVM`t-Y-jZ~3 zJRq%1t*O+iy`>##4!*d?qjqe@aR(cD$@a~p$>|Qek<-wafxpHA=bCPX8VxVWA8f{Y zDAg$0U@UZ&Jr86#U&h(SB4=rMUrBG1a>dSaktx`1)Zbv7?kqQRU5?7#7)xr>9dM$v z+>Wu-S#GB{evQj;nz6!J`g4Y)r9DQIvlFiNf~4gJjk9ag*O7LKLV7s9cUR*;t3v!8 zZ1i`<;1D496@vzs16NozwB=?^j<)TIA5G&AG4F)9){wkVF*B86*8C1a<# zob|W0q1kKFj`WOwiuI~3R`$49BSW*Vr1_WU|5I#oZLu|vi;=-?JZd#wwc7blxfg58 z$%k9hHj}~a9qbz))<64Cu|2iLe#XwD#u*K6tK=RUJ6hp6SZ?B*+H!8ldsNQO;I>Fw zH^i~aKjrMOTXVBfkIS_+xS8Z;M>q}sr<@;l-HpaCaX@}Ho9uBxW5FtlV5bPNZ1I6^l&&Kepao{T1lfpQFzmKc2I42@h#mDPTg-U;BwK72ekd*G(XHe)!S3TN40}dH zvKYCNwOq9BC6nviSMuYtup@1F&c*c zQ`CP|-J(%Qc%W(6RxP&q?`-Ya|8q&HsCJ9O&i{PI|9#!9P_J|&=^eG3Jf^x^-Sl%9 zIIIi-^4EC3VFQB^8_MEm^!4?_f62K;`RSQ{L-JE|3MS;{XU7%gq!&)k%_%UXhmFb0 z&z+c`n(Z-Ra#m)6hgV5Ru=kjWIYl0Yxw%;d9;x}+9+}x`nHd?G8Do0*26+Vf2Lw;+ zJ_h+dIk`E&yHe98kC~j4o>i2QIVL+bebVHd%>Uoj7L*nk9^FaeqYD`fxPD4TdSOt? zJZr(|SKNG-}LOerkQ%PjDnB+oIWpfCf@8xnZ^a$a_7;UuJL9c!#d5^y48r**3&)OxA&TBJ>J9G)1$4YwX4Sz>nvaEF&@?(k@U1K_OQmp zIK;!+N0RNVOCqdCcvyQ&!U_r|W7_1x!I{OG`30GAS*a5T8}edu3*u7K3v=@glP63l zluSbk8eSL>oKk4$pH>=ZzztwGdT_s#e$hh`Qj!z<#}DlrJ-DX11%;`FMFoa}NxAuj z9^!=#&zwAQQenX$oD&id8fYk(T$U;K`?&VOhDS~4o0&7Qa8gYr>9{a1iMn#qGv#?1 za?lY{vv8^*IVC18ZU~xGgxb3Yq9O79hkC>W1bE3xlZvv_ zU?(7dLSAtXpMpG}jDTRt$;wSF^g(xuoRpfM;o&neV|r@-1fM_;p8?%Hd?w`N`Ve0x{NG(jQo1T#8lb)MXn2$FQnLA;EAw9PsWkSvC zsCk4v0s<|!Uh_KYJluf55X*zKyuG|$HE(uEYFd^&wZZb#@LchjYo2lbe>~%wTQcMg znLarq1E*x=qR};f1-pK=VGN+HJ zNsd8c%)tEIDY(mmG4U}mHP`!B|9@1u)ceH!)pU_Z?=}Rlw$A;@oj+>+;LOx4L*b)V z4lK+!<|t&B-yjnG**0%gvZPVRB|Z zdgVVy|GDwUXGu#R_qYGZt$7vyd=5inVq%k%EuX-@3fDf{=$c!wd@6D$(wN6>dE$`- z`Uiy=YMPxFBR7h+)Lc+*vgSj{PtBNIB4bOP@1@Rzt2w{sp8nOAf7+WkDmJBGVluwv zm;^(eCun(Nb!z<|257(kYMgs~o&M4N6Z^+m`e^NV^Uo*}Jve@7zu5jmQep=W9x%A( zX-f}yeEN{s(TRPB4vu}Y*GS)gT(EC!|9Ff_kE*GC;=O8~xB;Cp%)?6tj3G4> z2IdzJoahm2nKV4mZPPOg3Uc!cy#Jra6PW>>@cSDZJh+O6P7Mrhl4;pwR*Y`w{FKdX z>p4~njD~1&h*D*W!c>S|+n7fqQNNysdJXLQIat-Vcd=<=Y-((7?Phd0wlKCdwlcP{ zlDOqRwz_#XhF|`!Tfkc8o;sVgW&Wy=>f!I!*{ltS6Y8Ylmm9j)rqNU*(seg$gZ&fb?SaBrHZ;YE<8?P{ zgToW$9SnnN3q(nIhuTz)wKmj$qI`WrW^IWXQXURerp{(6skWqI|cr-Nrdxn&;;E54brj94FYUY_bgXEKGIi=3w|Dw{GYyU?p|BI=| z5P^HE-_2l<@5QQ#!B8R=ub@IK`o|2htqnDOzzUD*8A+qdJSP^9{zb8PrSjESc|ICb ztU4iNlr*e-n_}I?b}#^sgvB3RvL-*kV&xl=axoS=&|-&LY?{SRw%8ntEw|WN7W=Hl zF0t4Z7Ax;m>fd3pW{dsAVn4Uo(-!-U#a_19dlvi9Vr|etq`vwV>uj;jEVh-!wzpV6 ziydgODHfY!v6U8E6M>-K<(BlD7W=-%erB;3ELN~cX~zSLZHI4E(mgFU&SHmJtRPu( zyue~-S?ofK70^$PAG6r=7JI{D?^|qrL;zCG-C}!KY%h!LFBYNxV2d3lww=K+*1s>*uqA!el0Icge{D%$v7~Q^MZ0`37R&WR z#iCyiv82a~MZYe$q@Nax@8ESydaGD`Cm&nVUx~%{@QWq=P%OR&cU(j2?<5xAPqZc7 zUo1Y3QI_;Li$%ApX%|kfu`)MHJ7v5P>tV4SELP?q$@jBZL9-ij(Y7Zwz0)Fwb%|8>t(V278_)-y)8DuVr#xj z)HlSE&al`?7Mo|Wg%(?Gu_lWZFjm^L*kYH7MZbDoEc#Q;cZ_~iLz|%g2*xbs1#T64 z&{AF)04e{aSd{--EXoVGE9Guk>|L?wcMrwN_$%Kf`dLG<=wD66qI?bW0_!1Zl=l*g z^8R8`K13|a*OTY!@lSt4vgZ8S{-`Y1MNK zrT;F!ZdG-P|8FjCX*2FKBQvdNBI2G2xp)LM`>APZi0U6bg-1DAx#@M2sR;ciW?G)p zqZ0p_lO?E-rA9nxgKv6nc6KI0z?!G~=&T7qp->3HgKugfBK@?YLR9;Iyotwz*ZtTN z4tnBDbeWpX6Mv-~hSZwv$#QZ`w$AvWEB}w>{88q~>%N4%+D-bRY~nkJR~&_(Y(Kaf z4GFMq@sl$0lj}*I)K}ZykFam>kUx*M+WN*L`LA*wmJAr#YRieQ@vm~8bq?0qedWn= z9gz`*pJl79PkfPo)t8Bk8J0pYwdKS|`B%A1$dDVfY_;XSL;7FkenLj=&11K=ocJ>T zDkp=iGk%t>wwxOZNx9nXBSdNwFqYJAwa1!Zr|$dchKwk53V9zGYHF@4&6P2^_8h6h z*Vbt0S0|%(H~5Ki<@Jn)_&WDdTP_egb+4P%+-R7Aa&lb^X^*cP`$T;Q?2QHw9IxGK z>x+J(z8mhwn#UsbNfaW_Mebw76ZL(M`szHs$M-noiTWPYsZadk+WMwEQQvLU_agFY zx7ytaPt@ns;!!jy?ZcGuxV~abeeylYHVaPbjoO!rwA3fRnNO631>Uk(v_kA=$`{cPu`(#Y1yEm$vdna zTO{29^=+;_h;;3)*gDCfE|{q2DHDDEnTd`bHc=Ng(Y1F>)Zi@>EnH)wl2=T0V4jJxt4vfr%|tiS zO~gY@bSKV4&-FCX)16K9Lo*Ytvo+D-U#qC^cU2U0tcpzUSCP+#DvDfKMcd1(=$Xta z+TXv5#`VOnZ517;hrFLEX~&65ih8e-{8v`eoLQ9=IiZq9CRWnvu9Z~XppvX_RnXg? zRgigW1qHlNLH0Qn)Vps5UGlD=tMw`<`C>VFa5*I}E2nFP<#eTgIVE_NQ$u4pg`Fv* zBiqVo|I=mEYFrte?_NeW4rR3Pd?`J(y_CM5T}sP_mlAuIQquhr3O`yxi&vD;;H(mw z9$rE=^-JjYQ`2eC`sw7FKb<1fbUNxVoeq9kOdVDg(~U{R)H1M`cK%sJI}a35t9eEA z)4(F?+`NcB{<@G(t|_F%35C?TOCja_R6uRF6;Nn?0qO1q^wynxvUw+;4j1Oro$mSc z-0f+!VB0jhpFNGf>@tm-U&*5nU(ciO#^%vPw>%p4MK0}`pG!~m&ZYMs&`BQ0Jd)O0GDB`IpG&pby zo%(h%9bGV)0wX4q-PK9d_vJ}s9X*M>Z%m~9uS}#*q9#)8wF%_Ecmgd5pFra-X42|; znKUFIlgdwJP?O3G@@$tuYd%b;m#3uDg9hnzYFiq0N=~C|cT%a{E2(rmIF)XHHJ+X= z9#5|{9Z&ANQpja+3eCJVjwU`gjy~`jM@tTlrSDS4(%L`A(D|3f&;s8vwCC_>8Z~}2 z72O*}4ljkvx)`T^&J=RU@d>Wdx;f7*2;nhtnHJhmlpvFgo+|Q0g{o zDBX7%O7UxlP^0ccXy}1tavPLPBfl9;ZrOur$e)AA`RPHF5^`};Y`_sv@{m41JA04^Ym+bTV(&2l3$idWyj@tAg*Lg|wb;Bg`UYtmOHcO=b z%Mz$_>jav;DxM~K#?!Ia<7jXDIPzH+OPxBz()zW%X>+^Y6tX6U`nHXsA6N7u#}>V4 z_bbtK!zG$FEr_C@9inL8Y@(KbEBd=cQO@m1nm9F*Zd{C@2csis>4|XKpA=3-`@?8= z&oG*^C6vBv7fMH#giwrA2qn$vN#Fb$Og0mPY0sG+biYpz`pg_e5TTOy>h84FxjP-6 z5lGX14xlY50aS6!pDslB(}_3zXtbLjmCW%a@7q3tx+{%#>`DuY zy3p`%ylGE@H*MSQMNuuhXwt0C)a6zc&%pK+^G-WD z<=&3!Re91E-+54YtOrGJY)hBwx1~^tEMz!ToW4jzAN2q>Pr68T*&=sV|t@g zWBPiAGwnIsh{A&!QRedvDdchk+Nm1QsaKq6{Y^(|73WBiuhl1qI}T(@a-cPmw|pg^tVcy{7~6A z{iWKyeP68~@TXev^F8(Ti@&SI{&&^(&+e#@oL`l<(`_|p)6c4J+$}ZZ`=69&)eYtE zeqFu2^Qv0W??-k2@?~|zbV)UI`#~Mt`n|dt_nmtF{5R_Lf(y#m;k*i1c}{H!Jge4z zcv`uRI;H-)aZ+WNzElZ~PpCIv|6I-Meq3Gq;F$U(`BT;H;t}PX|B0Gz_*kVaIIO;C z`Jwt~<3ZKC#|J85|9*9{?>=??lveZ8% zcB-jwY*)^{+f?GNw^h@yEowS%R=K_3R1ZJes5ZNgul=Jx)Rp96s)PvK{tDIra zsd*>nseuEZRr`-Vt-eW^tKK{~TeXXNN+s=`soDk4P{UFy)sdlPYD405^@_^Z^}KI-|YUDTk#oz<3*4r+ZHPZeU* zMoqoiLPZ{Hu6FEbqK>}OSj{hMsD2&msBEL{)q(c5%D0}CT6Fxc$ZjwF9_c&$m&j%A z*CUJ1{SbL@#f8Xc$DWS-sPz|-bI%`%{BXsg$md6M9Io%5%O`mP#UE&d1ES?vh>=DwWjqQ}!p(R&{m%;G2E`J2alZK5=1JM-4F zCh`laZ@zchM0bZbFpCd`cVx~wX`-T4uI42tP~Qj5%tX)>h>4C2YH$AfV-sZ*bux?Zg?DY`*KJHni}2maeaGK6aLt#SaYN8crW)8%!}VPQ9+x&X7T6H zugsO3O!R%mVDrTdCTjT1P&2JFQNZRAX7TmV_snNknkeO$ab~v_c)m^2%>7<9(UhK< zX7K~jKh3W$HqoHUspjqYR}Mo~=a}{LCTjY@G_&}K=)>l{Pn&3`eTjMVY!elBEjKTm z2|u%cm0A2p^mB7mxrub*EVF%yiRzzw%6z!c1b=IeS$s=;3+7HaCi-H*Gv>onaNJ{_ zIc1`WBDT*ri{FXw#hg1H_jzK0`RW)G?aFx3JaVLoj{mmMEIui|DRaxgCih<`bWVgbE}pnDz@2d79SYjxOuG$zPFBV zn@2P>(TsPunyu@b$TNPsS^Q&+1Lg=L{PIb=%pX6fqOcPqP5wdnkR3oqTPQTGY{Nc zMF*chZWdo1W0%>mriu=2Ibr^Ic@@1EdD8sMD^;}q)340p$76gmU!7k?89$vd&!1aG zJuA3X+@rXPD!YAa-j-KIzSZBEJ*QUDSA8y;#s9}RX}*+FMNU&M zn?pub(Xv0Um}d>CqPFv{nk6>CSZe+~p^6%8x?ye~gYqFa&2f=cbn5Uev&0P;Z_U{O zRa9~LwmH9R740j!W6thaMfYv)nkA;dm~4)7$Mb9dhq;4m6&39G)BL+r6+IpOz%20w z#%=Q~LlwQ0^0ztU&q|7&ZqS#0MVz6GT4E85?b`E7B`yEdN^k$RlB`~~*1=~hDRYdC zmN*6DzwYp9CAGY6tDpO@l5%#`)7Kg2m)dEGVK7JNc{?g8!QMe1-CRj?KXcI5>nn+0 zsjnrz!91deEU6^Vj!rt~#Y#%L;iRjct)%Ro4Yb5Qm}~T`@=974-AES}R?=>JXFV>v zk`8|6tR*hOe574dD(To*7ky!PC4JJtRj(dcN&Bz6YKfUJXKA}$m9(<7seUW0lAelc zrsKO;Qo3DpE%6lQH9fvVB{hE8O`mU7N#Blk*Mpi?(#Cczw8UDN`*f>%mDG7#OI`4= zf-V%d();dK(1OU;TH-Lwk2>N?1)cw-jn4X}g31@R)r(G5kn0E!EioGAP`&SP1^Hj{ z)E{#Ny}P-c{%Cgv^~i6pC4R#^t2ez-K^}j1(DPPR&~t}6>a-;lbbEd$EwLTuV*T6P z3VPGcOK+>Hpa&PcbZT)0C9L<>68B-g)*CV_=x9J!9X7UtZ0>c{2ZvNpuY?Lrlv7E)U@dVb{0BYsY&o@C)KmBSyqs7GB8(SWl_ zI`U!}rL5|sC02&-qQv9DMfgsYKiyZH|j|n zN~v&TnvPix3?M6AH^INu`>cP47Aye1rCwHAN@aU8b^n}F!jfnGM_MT*L`~F!6Ttt} zfqhHq*Ta+azNk`qfBt0MyGJRN4w#|^Lx7K}{o4Rn_-d+N>QYLkWm)=eJ+yONwibK= zeyo1^Rta^#l%v18SVB8C=IVy0OUNrLPYd<{UssRc2h8F2G+n-{ga*BvubNempDEC-)T8ZU zy`f_fRhkxS!JH6t>DTOw=svxq9sew(fYmSQ-0OvuW_Vc(9)-9}w>VZvJNLb;)3}hn z@OVY9-d0FAX1t;Wt3vFi4T}q@Ve}H+ZgwFxU$aDq6%~@Zaj6y@3-O&EFuaf&>|d(; zBoxv^k5{z{Eu^b6Ue$teAqLcTEemN&)G~eEv5@AjUZ&rCP(ULM%eCNNh!eHfg#xWygy zG{C%4H%u;|Z(6O=Q)6&`#VReB+RbyTv|Wb+$_`(x7Xd*ghX|&IUc$HJ@Bw*r4xNxYwO=vO`}VlH|gOoOrwKyH|e*l zrqOGcHfg~H5szycJB`BEzNyRlP9vk$X8l(9G~)4_wP1#b+4ZmP)5yEcTiV_MIAX9$f9=)}6i#8t0qln+P=&L({GY)-Q3)YC(UoT&nM~7Ut>IpOQ zC~VqR?UkEH8$a8s1&0JapcnSdqiN4=(>=rUfRAm{UwY?Jo5bx}FiKzu`gXlM+Ge{$ z7yOn>x6*g$>lbsW^MM^&@Jrwfn)l_>tg4;5<&ldYuV&! zYu19n0%OskhqBSX&3f^UY)Wda^$)AEX=Rz#g3khn(OG5L~q0RW`M;+pjx1WK++K{aSEc;5oX*#Vks0b3of2&!VB_2lTb- zEE;tBfELUbn2&ztl`INa^no7tR2DV8^?`1amqoYx9Mpmb0~gZxe-Y3twuf|w;4B)G zc1Ux(ENaPzv|z=+j`W%PQ)yPohkC@1Q_1_}hx*u;Q|Xg{!&-1;;7fY-mZ|jXwZr=U zt5c~W{v+LY?o|3~?MGTLW?)eI%j4J{ESI7AmJ+=I&~7gy7nvGe%K^B`|v9**gLR3eYWc);HRhb#%7af zs`F``ZJb2&r=He=%L5P8=f9drJ3P?o(&9VD`Wa^`My(Y5DVK z_35b-X~vId_0VAxX>80nEqFd~MIGFABK>vmoPM<_aQ@`4^{;Ba_zN_*M&l0Gv~Yf0{}DYrfOYyEAFZ{qOYGt21f& z(C@Wy3&2A4sKQLT=x|Xxj?1J!(=X~>@tI_=FKXc(fS2m)Ei$Qo{tvp;CX*f>`9VMY zDTA(dyrhMb0H&&M?#rNcXD;cHZ)Q+Y_se?k;tYy-{<0Rn0=TQroRC4=qps+;24v9K zWmoj|kPI^3xuS*305+@pJ7$pY8$arTJL$CBa8)ljpH4kTUDd*K0Ke6nx29982G{gk zOVerQgll^J%yc@>*R*gTz<70eaypgeU)Q4|(rNFJ>)NklI{n(=h8BJVI56U=G>SiU zLzjP(M(O@PX`heMsQB5Rv~VZDign4WX*4tZrgol{Mx`&^)T^hY(Zru_YT;FYC+l_L zY2>x)mTu7@jqd+`OV4#mqeBCK*21{}bJnrvQz^pg7rpdQDqS1(i~ep)DlORgixxfx zxU?Q!l}dXvZtHmyQYprKTW{~5N(Y+$s)efocC9b9Nu_lk{)%xtmF(Ky(RXi-r_||p zwD35`fB!=ZZv?zumxrcM@*98Za~>)5`@=tVcl#7tH0-_>P6?R2{^+Z5^rr0t zZL7yo=dllT*!pp_eAfdld=qecJ-=WaWv2h7H;fubd*A;{zZX4@>|7pd;i7=;>qCvk z(Np^#YJGPs?P&hD-g12{v5rGMZi} zuwmiazjr3r7bIg%3K7qIW#%@s!&mX;zC+?ic*`>*Y3^O5 zb4F5_ryUEo2QG%~Lr2o0VmqGFW+Vl#vExJ5BkAN3I~Lv#d<~C3HiE1@?Rm}45wxg} zJ)d1Vf?5^Zvv7jobhvx^2=X{$&&dfRX!&pUJjG`OIeR#;@P*)gc*gI;>1v?^o4y`S z{Z~0~(f;A|_Qwt^Tq3w3PMANO#o$`j$mW29zf5>pUmtUWSX9R!5&$Ss&m51wd zg7t9P{R@s?8Adl+I(TOZ47VZ*U6^EUK-RH!M-y2HDuQ~C>*M`!eCJk74P4HQqHDM^d zHmL!xOBzZu7d7B7eTPy?bpsa86Py>f_;T_(A5 zy#FAITIkBc_kuU%-ggJm=!>r0^UOd>acaVz)dOiY{UYBQdBxj#i8XvV@lgX?6qxjz-V zHs=?g?@x9S&G~F@e_EW;oQ0PLAIigm`qR>Pn)9>G`jg9r=DhP>KYF^J8w+O*&Xj+y z?nh&XyRp@(ezecz#tmlnqt+YUSom!4s$4&|A052s#(#C{M~yt)`A7SHG&Iqjh3f|Q z${UXMrB7dV=hCcLxy$7w3Te@bO#sE5dbQ$DHzv{9$*ov;cksnLXlfFrzt@VN?wdqyziY+s z`Xtdw`_?R+JUC^(e=CuEN3>>_&lBl$Q)_OwBaw37XwAacgLh`{!bDnfw>5hXO{8{h z+Hm9GM0z8(4GWhKZko^jow11d0u8%b$!! zxsh$TA|io)oza#>0|1VjKlv-3p8c#XXI_Y>BX`^KA3C1uweeul2Y?4>`x)^xCCdZP zA)Xd5_TWFG;_2P@Jy^5@;L7=^F`lm4dvcfWc#b zCl5)Fqx(lZd45bB-MH<^qB#KP&KIqrKZt6_)<4A3s}tLCtAnvLZ9zL0Jpy=n?m0V_ z8h+i5Lo;IOgjIX)-aD2SdADcLDuBD^#x}9^S8;p(?P71*^jdrV{Da;!;9z?e9Rv7% z&VQ;mJ>9qi_e}3iEkZi*Z@qfchH)KOG!D=J@F=U^^w#DMZ1_HgI)2fCSMQIZrT03p z=pUdH;E!g+(A3zDJZO9j9husZzn~cEu&5)8HUe4#)(?Bp!3!PP`$8|OZ`+BVGxwsT zuANwP6VMxQLw9AqkGZC*E;dBFqAvkiA7TZO#=7&Bbs8IJ9FOYXc`vWnV0U4 zrnE7gS@agrE%5Oo*o~d}yCKn(daN^F>mE%*?sR6+VnEx#SAL12PElTb_IMQ8PVnMG zTcha0d@mNA2J{a+H#>?dPI>W|zEKqYmlt>G8byZo-YgmpXe4;+k3^Gmym{hbqNXo- zbMp;E@4e^EqVIr?g45E8ZrOC<2hl_`x^&?eJc#NK?82h`fYyRnomb?z9O-IBv-Wl2 zh07G(zSM<97Xm#7PZ+L9`*-C-JruPZ)|Fc~QIuESl|?fG%?9s29!d5ecI7|bj-;gP zT{-CaNSfo^jYUraT?a2tjHD~0y78XQk<@5LH~z{tlKj?pW6_#G`@#43N6^??-Pn3{ z1o%H6wyuhx+)y7D9SZazd^0qHCO+lEXPQUQ$c;W+{aZNo{?vy>qXG>HXKxQDyXL+e z{z5oi2=`@Ub~wE~-j_wc0-Xt$dWBQ?o4)K_FPwfq=F2B8hS8Q^eK8M*i57*OSA~&v zq#v)V2&46>ejG3=jA*VOi|z$_74FeAjK+WF$8X&ZrOUtiaqHuulu%7hsM}ch9yeI9u5y+y?fewdv9uKCaLEYK3C78-abm!Q4!8Ej@ zJBxM)S{>dS6HIqM=+0Rl!Bl;vJ2(BS2bDVpvFLiB=i%gcd(fr9L40LV4|<_Eh>LQ2 zQ1@j)Y|;F1xmOR${4R)pwdq0lG4=_0EP5c6VU#7D!#I~v2cs@Roh(`*T!V2f!ZjJ! zCbWUk7DAg?bVO()qpgHCGulqL2gbb+?ukWXgnMM%E8(6Q_fB{QjAuc3CM^0RJR`=l zB0Mw3vm-o1#CUA7S(rLZ4yu9YP;s^d&-{V$njOk1_fhq0ce; z9-$91`XZrEvgoAHM;U#U&}SKam(Yh9eVNdwSu|AWVN7Jvk70~tjFp5jlQDJ@#!$vsN*GgFv}G7$8DlMB%w>$dgfW;g78Ax~#@H-+ zImTE`7_%8;H(?BCjOBzeokf#|F`hBj6UKbT*iV=P7;^z(PGHfiVUA$T6@)nhesw3p z9Kx7O2y+UHmJM?ZW3D00IgGi7Fb6T_BEp=+qI1I>#h9xIa~5OnBFtfoxr{KUv1s5h z$1&zQ!kovL`v`L&V=g4ji7fg!%#n<_k}zj7=1#&K%9u+Db1I8=4s$GHt|iR5jJcOE z2Q%hk!ko;atHT`4n5zkMHe>E4%;Ai=oG_=eXznn_Gv<22oX?p12|fVB7a;fqEP6cn z2n=6=;4?6M2Z9g5@FfU71;e)x?IOe1Aov^%--Fw+%!|;6wJ`lqfBKSls`ak$c3}1=hGckN8f)B;;r3gM1i#8BG z7Q@#f_*@L%i{OJXd@+Jg#_-KVZ^`i02tFIbcO&?43}24m)3Im@;o~uUJ%Z22@cjrr zAj20V_=GHaL->dcUyN79zw%Ec#8v zNQ_vC5Hm4iCqfLxh@}WI6^phLF%~1%qSE+*jM$41gE3+;LQKY@`$UY!h}8%&8zXij z#BhvQju6waXhIR=F=9PJ%*TlR2r(cd79_-kEP7GIh>Tc~5Hm7jM?wtAh$RUzC5x66 zF(xC{B*dJI*pm>0GGb9eOv<7&MU2XbRS7XGBX%Xku#8xi5Yw_~P!Z!YVqHSa%ZPmm zF)$+*Cd9-n`c%Zoj98fvGc#gmLJZA_r3o=Li*^+;HY3(1#N3S7n-GIDVsS!D&Z28Y zjLwMF2{Ah(b|=K}j98u!)3az^5#uvreL~F7i2Vr|00RpkU;-?9SYQMUtbl+SFt7sx zhQPoQ2$%wkRu&io18aa^J(Phx5HJV^7D2!ySah_&C>U4;0kdFW7X%E0fn^Xd4Hk_p zFb)RRLBKp1*aravVPGKyOoT;$3yg$;l@Kr!26jTgP#9PW0aIbo<^p43U@Zj9g@L^g zFc=0FL%?KMbi2T47+4MF;o%JIhJfKPup9!W!=mX0#>2pR2$&B8`ypUJ3@nI%39;yX zfe|sVA_8W_z>WwQ5(7&jU`i}nU|>uPtcid*F|a2B2F1Xl2$&R$P8b*!1FIrnRt)Tl zfMGGPECQy*q9F#x#lX44B4A()ER2AOvFM9|kuk6`0%pd*&IlM914|=dYAo7g zU~CMmjexl^ur~q*$H3wUm>i2P85kV{t0Q1`4D618;W4m00;b2JSq8?(!1@T79|QX% zV1NuPkbnuY=$U~LGO$7dX2`$}2^b;+OC(^5ELvw^j0~)ifH^X-M*;@Pz#<8lB#RCj z7$pO%Bw&^d?2>?CGO$bnrpcm_2FA(2ItiF31N$UkpbRXOfQho`r-6|&uu=kM%D_$u z7%BrxC19#7+G=2|46IcFb7f$!3K%Q{i&em6S#;OHXc<_o0%ps=ZWS9T0D zf$=i1UIomTf&D6Azzi%{0TX7?YXc)@V8sfUF#|hRz>pbOvMLWvVbO8}V`gB@3Yaqk zdse`p8CbLeCe5PrcHfo4z^WB6YX)|$fMGMRYz0i4MFS3un}KyJVBQSuTLA-SVBrdw zIEy|U7&!wgSHR2}*tr6R&cM1!5t`Y2n;Sk zfm2}7s)J)-a19Ea1A}`|;2;=WgaRkQqGJa~!Qd(sI12`Mp}=7W1AO;ttzzMPF?ZFW- zxFQA4h`}8xa7YX;Nr6*h(c*(+VsK3goD+k4QsAH%T$BPQ#iG*(N5$Z(6gVpeccs8# zF}N%RPK!mu4~~n$bt!OO4DL&T17mPu3Y-{&8&lxO7+jeGXU5>p6gV^nm!`m}v1tFn zu`#$d1W!((uH3Y;FxngMWp46aXs^J8#- z3LGGV3sm3)S=JMPBV=%e3Y;N>J5=Bh8C;?Qr^vF_030KOYgFJI8Qh}+2MNFOlmaKo zvJL?pC4;L};4B&3r3#%oiNR$maGES@6u@yZxK0Jmlfiu|aG(q>RDly^S-${|l);rM zaHb6IRDnZfaH$HMD$CjiaI6fjRe^J5aIXp+EQ550vs`eD^}o)8Qie~hs@xT6*y&6*y}Kcdfu-Gq`L8PMc*71~_g8*R8;LGq`UB z4xGV-D{$g0>odTSGq`dE&YZ!WD{$xxE?t3BXIZ-ej-A1^D{$@%?p=X{XK?WfoIK0A z4si4gu3mw&XK?ol96p1~SK#zn)_j2DXK?)roIiv6SL2?_VrT&rGyyE@L7)*}Xay8B z0}Snef`)*hB~Z{5u&fn<#(<$UP|zGOvrbGOU}z;2G!qQ%go1{Gp`}pJRIsc~ zfyRQNwNTJpFtis68VrUOLqU_lvTg+$4Te@jL9@ZoZYXFt7+MYmO$W=G7HB*eS`P)y z2SfXzpaEfMK@>D0EbCpM5n*UW6f`3Y?TCVg1oO?i3Yrp@wJ^|_FtjELniGchL_veX z(4r`4Qdri>K%>IYswiky7}^yD4GTldqM&JESwjPj3q$Lopm||vUlcSj3@r@C@jRCG zHPFZ~v@!~s8Q|^P6f`spEscVvhGp#yG&T&aje_Qep}kSi;4ri}3Yr|2bve-JFtj=f znjMCAM?u5G(DEo~dRW%%K;y&E`Y33A7}_5N4G=>Mq@W36SU#$t&xJ}h@m}F&>%6iND7)HmUTeTC^5833YsN`c1c0Q#LzM+Xqs5o z2tnh-&^jq-o*3FE1q~EK3#FimVp%@~jTA#GrJ$K&Xr~l3R17VZf~JaPZ4oq946T)d z=8B=cQqW*Av{(w7ES7ag&}cEVS_+yihIUIq!^O~YDQLP_)+9mW#n5^wXucTQF9i)4 zLkp&$31eu(6f|NCt(byljG-M<(2y~-WD1%x-h;JF(3ml_W(t}!hW1QBgT~OJDQMDI z);YD^hVxA;6f|oL?V5sy4gGt$f~JjS4HPtP46U1j=8d6!Q_#RMv~UWVIF|KM(8w{g zatfL`hIURtL&wn4DQN0g)=oiV$I#j-Xzm!=I|U6MLyM=N$zxep1&tmhL%x5)5x;M3mQj; z)=@$8$k0A2XdoF{NCizKLmR1}kz{Bk6*Q9!?WBT+lA)zk&{VRl4THv#p|w=dTr#wm z3K~p?7E?i!$+B(?8cl{)Q$e%I&~7SdI2l?_1x+W*nlflS8Cp*T%_l?qsh|O6Xh9V; zp)Bjopb=$gMHMuo4DG0bhLoWtRnU~OtVM&yl%X|M(3~=~rwSTWh89&plghGA4H{L3 zR#ic>%FwPVXjmCqRs~Hf%NjOlTp3zd1LRUWoTg)G_fq}+n|wUXk`^NvkdL5 z+O4NEw6to>5uA^;Z_wEC(oH4>%`HQFtDwPUXmJ%Zxh(7ApwVS$brm$b4DGIhhL@q` zRnYXZteJzxm!b7l(EKvAzX}>)h89>s6U?%n4jN&GR#-tZ%+L-iXowkFVg*ex%UU~V zj2T*E1AW@wQWG|4RM@Sssf@Yhc-B!?WGql_anr@agfzWs}wB8DuZ-(|;K?Bavf-7jkS=I|e zBhHzP#wlpV8QO6L4LL(guAnJrSxX3wIYVo%pgCt~&lNQ23@y5XCY@!SAvEd?t-69{ zouOS<(6BSK>~1r0qz zORu1*XIZ-ljXgtaub{bSXzvv?_zW$+f+nA3T_ZI546VL`W}l(mSJ3b?wEPO1e*RxP zop+oQ)f=xVf=CqvqzForB27>PA$e4grW8>T1OyaC1Vlud0!tATq&GnXX+P;o*}m+~ zHn#WPd+&YIbKiLHxqo~=NoMAhnK_xvyzlopaWIeK@z=!buNTk1CfGzUj1 zjDjX!26rK@;{tFARhxEQDT|2+hG?3L~Kj zE1?%=LKAjEFARkyEQMZ}3eCZ03S*%OYoQnBLKF5vFARnzEQVf~49&r93ZtP3tDzTW zLlbsGFARq!EQem04$Z-I3ge*(>!BCsLlgExFARt#EQns15Y54R3L~NkE20->L=$#I zFARw$EQwy263xMa3S*)PYoZtCL=*NzFARz%EQ(&36wSej3ZtS4tD+ZXMH6;KFAR$& zEQ?;47R|ws3ge;)>!KItMHBW#FAR((ER0^57){t1y)ZJGurhjKW;9`E^uo|+!qVu4 zsnHzlsW3L0ur~5MwL=s3MlTGGCM=F#m>kW)r3#~?39F+QW=9itM=uPICM=I$m>$i+ ztP10!3G1U5=0_9uM=uPJCM=L%m>|u;vkD`m2`i)*W=IouNG}YLCM=O&m?F)=x(Z{Y z32USm=13FvNG}YMCM=R(m?X`?!3v|K39Fny^-SVXib` zuk^xTX~JUZg~`$!+^sNLny^}WVYW13xAekrX{VRlb;5LM4klL^FHKl4oiJaTuwS~t z&+XEL1=9%=ra5?BVZ=0H#dN}qX~K@_gdx*}CDRF0ra4&N&)(Rj32UYk=1ddzOeYMQ zCM=pxm^977`3j?^39F_PW=#`zO*iq=U7E0LI$_#02LmjOns1pWJ6Bbb?OrqxCn1xZ) zgjLiDv#1HXs1t@!6P8gorKOYRV4Q_<)P!}^3G=84`=}EJQWF+ZCrqT~;Gcz&)P$AP zz0lW56LwN345cP4rB0Yi&A~?hGSo>E)>0?Tr6%m9P8dv0SWKNTnVN%}7DiJm-g!y4 zd9srx?50i_PEA-&oiLpeS39p0##0m4Qzy))ChVt97*I`EP+i{zQciel?PDiRSW%rY zqnfazy3(ainy{ogVM;Xzi!F?)CakGWm{U#IQ=KrVny{!kVNx{*r!9=CCakJXm{m>K zRb9JnPMWZ+I$>Hh2g5Clt0t_g?!+D^P1skRFtD1iusW|_oHPgDEsU%ttgO!Gh?6Gl ztnR^aCrwydoiMeUgZ2Q%L3u9GILukO@8ay-p5b;1B^!UF4r3Dz7uxiG@o8z;~SGpq?a zth?kX{hmEZCrq*CV9iHpPMWaBI$@4AVUKmfAZx-R>x4y&XD4y2|KM5 zhFTMrTDLUANprC6!dPpTSm~rWc=;PuPMWadQ-m4UgdLwE z47nyO`4nNwH3v&CjJYPP`ILrwCr#M%DMby^&Yt5_gh|&NoV_sWny~6qgjv^wU7s@B z;G_x5KBa@vNuF~)pW-L4!n#lS)+GDWdDfJ!W+zQp_$k7~YYsktl10wT(m_*vflS!MQqEQwQ(q zU@jfpqk}zk@P7^l&%wbtSTqL@=3ufMT$F=Na_~V8M#sU)I9L@2FXCV}_=_DLUx&}u;dOQRO&y+3hwswieRTK_9Uef356|I+ zbNJC5o-l_m%i)c3_>&wSA%{=L;gxatRUDoXhi~Kmc_$qH4Tp!s;bU=lX&inYho{Kl zYjSw29R4nc$IRh#b9n6>em{rj(BXS@crP9PPlpH9;e&N}aUFhOy(2E{h|N0UqmCG* zBTndu)j8s2j+m7rZsdsFIN~Rc7=|N`;CPmIJWo5GX&uj%j%Pc^^OfTn$MKxuxYu{w zZ#(XJ9rvA%dq2nhm*XDDaUbHi7jXO@JAM-#ze|qa2FLZ;agB6brySP`$M@CoopFp? z$JkNxKgbyQZ=MIoc#!2@GUoo9w?W3{fAcfQ82)b_2FF-&%(tNCRgmw_fAc4(c@oro z2=bl%Z{7p>ZvQvGfm{Rro5#R0#vJn%$Tj7^c?s0~18SZD$M}sS1~;`ANk*1_Y;Z^nPzz2fT?N3VE!#lNuP#j7eVRq>~aGgUmPJoEm?jVeA=aiEI#R9vUxHx;L;cud7zD!x*2l!}*> zmyJR6L;K{uJM*I6lSeDK1a(cZ#!9Je}g^ z6d$KJIK{guu1)c4ic@o3R~;?5LbrZ_UiizzNlV!!|KUyAcmJeT6O6rZIyEX7+X zu1fJ!ijz`2l;WNg-y}Hz92}G4l@ynx_#?#`DV|7iLy8Yl9FXFD6xXBp9mVM=9!GIE zimy=|jpAh#7o+$W#knY+MR6;NPf;9-;!PA+qWBTXG5*JiC>}&{AByi#9EajH6qlj+ z3&mL|o*?h z%6F~&)yhY${L;!7t^Ci*=dAq9%D1fi$;yYU{Km>xto*~uC#?Ly%J(bYw*UOS%Eznx zy5gz(&zGzGx5{U${Itq9tNgLb2dn(9%Gav=tIDUU{HV%zs{EzON2>gy$``8qpUUT{ z{G7_Ssr;GBhpGIQ%2%oUlgcNl{E*7`sQit}$Ef^@%9p78hstND{DjIksQiJ-2dMmh z%Gam-d&;M${CLWDr~GxwN2mO9$`_~nZ_4MU{A|j%ru=Ejho<~y%2%fRW6CF{{9wxW zrTksW$EEyQ%9o}5SITFl{8Y*}rTkIK2c`T@%Gad)OUkFD{7A}or2Iw7N2L5h$`_>k zKg#E${5;CHqx?C_hok&9%2%WOGs-8U{4mP*qWmq&$D;fy%9o=2C(37{{3OaZqWmGs z2crBQ%GaU%8_K7l{20o2q5KufN1^-@$`_&h56b7D{0z#sp!^BShoJlh%2%NL1Ij0$ z`~WKNuk!mUkFWCeDlf0{?<&u(^64sXuJYq553cgvDzB~b*D6n~^3f{qtn$k$kF4^= zDle?^zbenG^0_K+tMaod53BO6DzB>Yrz%gX@}Vm4sq&jDkE!yNDle(>k1Efo@`);M zsPcm<52*5eDzB&VcPdY(@^LEfrt)hlkEZfvDlew;UnwcxrSelM52f-=DzBvS zM=DRG@R};`u6Wuj2D64zJ?vDz2{L=PFLF;^8Xpt>W7%j;-R=DlVjv*tm4Be4y@w6Dz2;Iw<=Dn;;}03s^Y6Ej;i9NDlV$xpDNC&;+ZOLsp69=4yode z|39wy|Kf)#PN?F6D(ODc|};zcShBvU`WWw+b0i7XFUeOz(ib^||MIVXUiaaXpd?V^BiYT|UL-dL$t<26ZqSr-vrP7vY zlBl3W-is!Ra*OR86ul}+DYD}v8X^iSw6j6=5OaU(M>-&AEKkac3OzU6+@nnoexF+-qLr`<;T)5(NA7>N<8I!iZl;9 zBSfd&Wq;k|7>c4@<-Ch7xk&q>Z=7Y>BWXu;?x7sd2Qp4XzV~H6MEm}=lmCyMsiLd* zq`d-G>Mmt?ay6@ziY_N0kTREQV<-4>_>JYuQ zR`&ZFxt^_&eg9g<`f5A1U&(h@H1SJWw@Sv%7jh0imvg*Qj^PTq)-0Frk4X2K?8`E_ zmMoQHy~NJ_Po?}~ITwqhFQPu5$oFTVT<1QP>(obfe*93bMGIsfK9D}lm+SaE>Bn5T z*3YriUi8BIvhVN7e$AHa?=0DmnReFCknh8EIT!EBF`j1U+EhFLz9ZM{x9vQ6OU46o zJoGX~b#mUP*r}Ur$M~l7agto;MeQfbci;`V9!#*)^L6)ytFk=&c&;;A7kZp zjC>v~%SXxjk+R(@cDjv_b2D7Z50kMwRO%Zd*NMS$YzEmW9Vq8^fLwQ8lI#78axLyJ z$GV@4j~8Uz_O)}SkDXt8%l`I~-+<@kd)Cv=oE~z#y4xA~oE(>Ka!$L-x$YwU?ksh5 zlJXtxT<;*`w7ne9XJpK@voq^y8Lw?+?6;BcRBJg_tz^u#ly+Lkd9>TuY_qY*YU53d zjTg)|tVSD|1{>bZHvVj~@nfT`Z?N%ty^U^lHmYiDgjUeVWHe7RT{F-fJNtTU~nbJj4z}@bkc|#cWSamRr~GYv z;%B3euZ;{J8<)IoGFOC+7rks`d&p;Z8%y0}y{nBBXB(#<*_i*(M#l#>g8#L#=O5|E zJsS;oY}~yq`*6!f-0w0+ga-&brbzAWYbvf*>l#&>_(7;r(hJ1=#d zljHHbw0FkFk<+pdr)(6TkiH$a!7&?UN2J}uHuS&A@`E;x{c7WlUuQjlnvG(Z?JLUTN@MC z+lXIlT21iuWTf&lH>e^jn<#r_+y2QvCC~dS!UzYrE*R`wXt`xjUJ0^ z+*oL1(#JNLdrHpL0vqM?Wem)db2Hb*@9#^S@7Z`TOOC}%83)sCeDtpLeX5PG-?35q zmTZgkRd3_q6dTV@wsCfnjH`(@u1v5o@F1XXBr-(uXlp*C-p~M%s8dLXO*T z8~2CW82z#w|G_qf53&hsCCBz98H@dG^z3KjcwgyDAL(x|oA7ZqzVBh9cyRBq*9sg>$tSyp7ly}-%~`Bt2At>k4}nU-ah zIo_3F|ysY%_l;!SLyj`t4>tf~eM^+v^u=3P>>Bm1-uHUs%f7{C2x2zny zVI|{#R)$}8E3%F25uteiMzCGm)r z-iNI$J7neZud+`EtPI|7<;#7tUwf<+I?3l<(*6!v|D%F=Z;-&*-`ot1~*SSkD3%D~lD7JX^u=oeN3S6VSFw=(K8+3%%RPJL?S$s#Mw3*~%# zY-RC>a*RKaV>{1E@f_Lr_pQ7$Th7l+Id;>n1W&V4_l_L*x8+qag$Gd;7>DOHg4{uwzd(*<-e_QzLx`p4bS~zyu!huV&4}V(N^oND7 z&skV@*1`v;Exdcu!W+jej5uoHg~Jv)9kO8k#X{wN3mN+?gzvH7;bh^)P79}hvaox* zg|%BPEZl72?M)V5*=V8r1}U@7Lg6(N5h z8?xW8%YKcwu=Q07bH`X1Jj#M;goWr~a&BI>ux+q~_Xb+%|B}?z&w^iH3nzP9`22Yb z<9b+V`J9EAXDwXpEc?+>_PxD@*6pNx8w-E5wy>(Dh0%61%@#A7$;>YXGjp5F^k^`X zR%_;RwV5v~%?vL$Q&D2(-y$>L7nm8JXQm@l<3$;{B5QuaqPo3@$hwZ)A0CbRs`n0ac0w6)I6tZ&Seua^FOX=daXW+GNd z-##<*%u-pu*vz}1n1xq2vulCu%Y5nk963(!nJJlRX3umpeWsat__ox8nVczRw!A5Q zpD4%gbvZWU&4i3Kvt+cH%2&)d4L8$usF^E+rSAjH_`PUmUOzKgeWcA^W(+-Kzn(Mm zY*#aXb~f{JM>F@IG4tlrX8fNrGrN@>OS_51RukDK6JHuk@(gQYL%oUm8WUTpOc={e z{8VbfQeyu@>MA<%0%Ed_eNU0}W>I;#2gG|H)n3(Nn!r#Zl zn~zQ0_b~CYn~6W2O+5S1#QuLx819-_d)p+>946-d&xGGqlgwFb;>tx6T`!n$I%lHl zjENm}H(&>FWYnKhH#)IVPFs%!JPjDgUmC z&F`4xe%Qn?oryhfnn-)Y#JJZ@92_U@k1;V}l!@O)$orwvzab`m9cUu`MX9@=^uLdZ znCDFl=wV`GHxs^HOmyjFVp)3=cb=C0f6BzPRwjtGNtJDrjF{t%=wglRiZbF8Zj^as<#Ujcp8{mP zuaRbN+1^X)b~kdz#YoXZBQM=I^65Pzr*0dGxM7qzg^WzTYUKOBjNJUwNY;5H-F`PR z)IHzGUvrEE&Nfmq!$`+>jf{E6$OjnN zG{wm2Nk*I}NIT<=)Q^?+M;jUYijld)jI14EXU+yEs8<`x=QmS6h}6$8&*GSK~k=$yPiBg;-2=z7dR=fegW+XmVlF!0nq19m6r z-wp!}+YMB1F;KM0K=yYAlGYoD`o=)uY6G6D4E(#&!1d1zoLgex@FD|F9~;=Xz`z&t z41DyyfoZc0yf)py;HlEqTLxO{3^M14f%FLmg2x+pG}geSQ3eiL3cyk_FFoB21RnM0|~tV(J|AK%RLG0oIOG!qiq%wIvxYzb)QecxsV zdP^CPX2M-%`J-kw-EZdId$RrQW=j8V#`BugbGeyMFE%s$583u?Gfz%6bMm;fd!(6H z4#~0u&G_$Y=J4)j7Vd18`3RaR-XeY2B<*}BeOup5>u;I~{i<2+#hO{LqM4q{nn_*U z%%2OJ`Rqd}GryUf_nWyqOX{88O#i9PWYWxqDa|aJ)J*pY&BTn8HpVnFV`MWehfCjv zH1os2W=6i)OhI4SwwKi1qnUQyn(^-3%=aCddAVIPX-_qCq-8U2*_x;|HF2%Ei4Pl^ zXkFXHqsk^eFKgo2;wF6Zn^>FEM6b*y0#lpVkkmw%rB%f4x1;_4=%zi8si6-}6zHgRHc z6GIj@;jy5JIrEyxd9R7@XEyQFyG@*VyNQ?eO}M_<#Jg`a$#=O)=00trW@HmPhd0sw zN|pEtfqmfl@0t++930YH83-;flFBp zG^8~!E4hJ-@eR~OH!v-tfwLhElms>~$*+M!-VLOAG%&)of$a|)2>z#m9(Nk}{6+)+ zTx-DeR|C`kls3*akbb&>AtxI6_DBP+2OF^MZ{Xd%4IJLpK-`ZF^xE3MvP}(K+0a1Y z+6IPw-M}}i8o0ZnfvTkqj9J{khL5G*4;rYP)4<5tvVM94H{NNWK;OVilN(s_hV*ZI z1F>ToXg^Z+X;=e01~=gRQUm2LG%&Dt1B-ezaI|X!0i7B!Jk!7{Z5vqLO8RK47e8q| zjZO8uTwl+^>Us{8*W*%LPeFb?-E!(7qh8`J^_)wnCora->WF%Jht@Ooi7fZ4=Ztqf z-X8T7y42J0VLjvisb|scdUpO@&y}n7Jh@a)=^wK0_j*R3s%QSOdN%%6&xv2^d9bgZ z7$>Ror+PYVtLK#;>Y4dnJ*(E$v-|6MF087@b%kuRRO(n%PpgmX>GOfKF{hqcv+7y# zZatgduID#hJ(nldZ&)7coyw$UwdEM$+*14Xw z?d#drww|9`)pOid$DgJ;ZZy^Lpso(jsyYJ7>WC<+BO$Mj^sGAa(&{Kls-rTlj=HEi zn#1Za1=V5mucL)lM=P&7o^lgCs-w-n@_MI^);DDNHCg{x9o9c&`?Ga4os{xNrJh4| z^1n=V+txtj{D!#@xQO?xbS%$$3ClL-=}qKU0BDO1$8W) zTgRN)b?B$p$vr_GFY4>){HC<=dL8Aj){!!*jwi$GcsQhvO9SdS*sqRFz3cd_M;$Y| z)-kqI9lf5BezlQ4w5XFjQZ3GgTFy7rva6<+&ns$~UQ)~Of?7J{)KZyVOLTHA|HjvH zEV`Br;kC>St`(naE$y{hial!ybgkv`!&;d$yH@5ot>x9frR>#O3NF_2_RNvJLh4yw%g`mYG%c*<$%0x= z&8=nS>{>=oua!B5Y6;TSa$-^~OJA?$F~}M=XVfq*rG}dL8tz8d z@O5|%gM(|x_OIc*PYoY>*3i+lh9?hd*mQzff|nNtw9bjHQQ^rvbly2HrCK$eGUJ9UBj|fHMCz*gZq*iR((>#vme&rHMfSZ zX3P8OH4=la;R{_29VXTA=(QR?9a}@oku}^HTEqK;YN+jBBeCEbCiSc#qiYRLooX2H zObr1~Nx2p^k`t_!-_dHO)>o5TRgF_wHT{aJ@yM-aVP-Y8Db*ZKsAgzPwfx^*HJ=7o zQ}18RZ$8xw@~pLH;&5U=d ziPBZ`@uX_fU#n*2SZVW>YSs>w`Uh6?egA42dsnlyM>WQ0tNF2GHRh+Q`Kfg^CR-KT zO;t2ERxawL( zuZL9}xmQKYTT;jMDoX#7G8d|dJX-}PtGIupikA*nas1~hTI`nkf2tyLOBJ)fuflD8 z6@%AQacor;=H*pHH<-<9%QQ_0bbmE@kQ zWc=w$em+)7;-N~09H?aTo=O6DRPx-mN>*;FA98}4U{*`FGE9L)>DrGLxO7?fC zB&cmAZCh0`-CDtZLj{5L6|}0ZfU*j96jtD#Q$a&|1!Iyc_$ID`>yZ^?hE&iqutH|l zs^EZU1>P>Q&HV~S+^t~6jS9|OtswMb1=e#Fj6Ge!=f^5IeW-$<{S`Ftu3+en3O?B? zWxub$Wqk$tUsv$#stR<=rHxOe{Kpme&99*Ry$X8FkiNWA!PmM9j!%^SjISVjbOmik zR4{T#1s}Xr!Ir)iT&RKzbj|-+H&+?l{06h)W5WxwVy~E3#9Cva!$@H z=h9T^kG>q|N#*#wCT)%>Cw4?RX+xy#m!!VF<<#^nr>Sc>mX77LY*$X(*5$OfmC?yq z#-QIo{K1>Yj7Ez{mba!UB=VyWwd%!hV@<y z==-$vrBxYamQrGxOL46$<_~lY=UMS_~v!yIQQOcW#OX>cLtlLvc@Qzac+FHui?@O7t zzLb~0F2(YBDRIk6xv{8}9Uqo5e{Ly*W|h)3wUkhODSu2VW!-C~Oc_&3rxB%O3@+u) zi>3V7r<55zO6l=zDS7Qn`KL`O+gp_KuDOIRO(il1SqWFmOITY}!fUxDnA1z}Pb%SH zYzgxsO6VRe+xV4m>2V34yOl8HVF|@|OSpYQ%3LjBqbjohL=z>s6_HI(vRLHwCrBOqs}F)dZvUPPnGb*R?KE&F@qb5iK{ARcWE)B3yS5Q zt61U-#f(oVCM&9#Uqg!-7g#L$x?-6Jv6xZL#U%by%#K^d47y%S=%r#doG+&P>0;cE z7PItVG4_4MT-{a7wC%+bBar&OEoRu7VnV(sX7y*qv|U`x|2{0{ow>#GA6in^)M9$+ zi*cS<%)If%l#VK9->_m{7*vc)zhdU}DyFcTY}cun=h_u>t5q?2YZ39yMXatX!dy|r z(c&To?(RFtb=YN#?HWzVoLlL9DDG~;#h)K(f@L62MJ0BGhG*{}FRYdsI zBIf9eh@M!){P9J^jFQ)3@_C>v?^i^`^Ritx*}kKcds@o3ERtMqAdY zEadAWg(Uw{$n?F1xa=ro(AGkZZ!E;RwvcbWlJYAGd3#AAcRwzq=lnwX-+@9(rWHy| zs}PS#g}n5d)HS-0%3+0kIH(Z!euebwRmh%hg%oxyWaiU_+-_M&M@s?gn+k}kDUjGy z0Y?i9sLU?ly|e;uClv5>bOEbE3-Apn;02!onLoRLxQ7LdyjQ@18wF%vDd3Gi3pjSR zfa2o?l4C94^!@_MoC@H_0#5x!w&ZvBP43l~W=CkvKeD3$mC;wSl)*+wiZKN)H9yg77q}1o}bXA_@qViakm&eJB zJhY@d%470)K0HsJ1M~P+%j29!9{!JH`Mo^4-^@dIRkr^#kKfMb@$k6Rb0`n%&v^`V z%45d%Jl1Z?WGmn|<@-StgI|UGg~fOdj`I=MiMhC964?rrKOORpc_bD3>>La+#l&%a;kc{1}zX z(U4rO_~+vMI9Kvsxuo9DrR;Vt=IgnV`^=^9`CLYv%4On_TxR@|%SU^1S@}~g-)_!j z`-WWhugT^37rFegESKw_KxETvV7NIiDQrQ*)?|&!H+Zhw|VYiv4oP^U5L1C5M!M zbBMc@L&UWl0x#y^{d*2BCvv#^TMpOu=WyOBhhy7w_<2(f+t%l>esvBjR_5^Gk{sUs zIEOdpeQ}W_HM?Uz==dZCN}qWO1%8i?tP5=!&xF zoRdX%Y8Ln7v)CP(#k}Aw`uk;3;hBY}a~8k;lf_3jvlwtymR-og?MxPXk7e=x!7O_2 z%OYz>7B{wJvEjQcCVZ2H^@}V5mSu79lPuPfwvl!AlOMWY|_+Q5?zG$1pz!uUUVD|77yP%}m-|&BXhHtUHs*&|{gT|C-6My_vkTBa_C>ncUcr$)Yux zJo7~+9!oP>voKTs(=?OdS($8^n#mwtCNUE-**P|o;Uh9h9F)oKewmDTK2y99ne1vW z^*)tJv^9gR%^CEs&EQE{25Sp4=$e&*YjOrlVl%LZWpFJpgXumQl(=PZ=s^Y}?_>~p zJ%ja^GU#+JgS#g(c<;9i%6`t^mt7eQ-j;#i#tfFN&A_-SgVUd7FlJE(p&w-M`Fk0d zr^zyX2Cq!aAmG&uK7A#Fn!y3sZqI@!n5S#v0zx_#;V zxHFwrThlr8T{>OAN$2-3(&@J>oofrF?EG}@&Pr$ORH)vt2q9TczV_PUE%4G#*r?F{&huTe)crPEX^ngf#j@rEw}ajZS`P?DtH= z`Y4Uf_tL2NJB=?cr;&O-jk%}N2soU^!~<#EbxPyK?P;9+K8-f((%AZC8YQ2lv3OCM z9J@3oy_d$#X=yyKm-^mFqi$>(D@LRdF-XeyOXGUaG`c*S#`b5@$ZwU#97`&$jj8mn zPUS#JDwVmZe3YJwXF@73MWwPoIF&NrROWi7^6*hAJ?^El?eA35{z_%a`BW~ROvUwXI7ZlzFkHHFvyOkvNN6e5qN(C1evvnPf7Kc-;bl)}5~Q#krn3dze; zcxiD8UoS}E-}h2znwEl2pTgcZQY3erBKO!Sd@v}5GcTl&(j$ewT~b)qE``5ZrjTPw zW5+@3h@Xkz<*klsjVv?8=D$D(o zIO>&zt8)@r_mXIHBT4>~Dv1T>lh}4LiHpA_;s0|IMLUytW~;3GE{S<-lGylp5~r3V z;rvk&33HNYn4U!Uw~`n?DTxopC9&a^B#sPD;#R*Tf_f&A*Ch#SyCizGN@AQjk@p)C z#nX_;uA)TFW+(DbN+M5U6Uhimq%I(lcHW8fb4_I2zllu0naHPC5?TL8B2K3gCC8E| zzhj9w?~*cG6G{9ok%Bde)PJ5xtEGuN`>~Xro5+Y6iM;VvB2yTkxdcAzn81f^5+oiFkFGhMS8L)KTpG{wdGS1x z9xr+7cq$^|$$Sz|q)$BFZt>i`AJ2tb@f^Gw&(;g^tU4Xf2S=p*fp~^H#nXLTJofM6 zDP0pU|Jxmp*OGXye-zK*Ir023J)R}R^Y+AehP@guV?3U^f$=2wmF>F6bE#82d)i8! z_Bf^+;uu;RN4v5(O7r3fO^@SdLL9$D#_?599MiNo2DryzyB|l|tvFn-#&PmO9P3ZV zG2@7=I}nFyR~+$M9Q)S9vE=hO`A=V|^P@Pj-jCzayK(%gk7MZ@af}@sN9*Bn zBn^;u`o!_$b8)=iF^(77#0mEh%j2e44p+zWX-TZi(Gg2iYAgY9u^bPNWock6!@Ogu zcZro}kXU}b5zB(hv2x!QOTmd)?jDMj{Cg~ucf`_ib1Wh2V>$AbY_mL;=NHA2K0lVf zX2tUPJFyIz982-|SZ9>W_CVyM0q!;PykEWZ##&(kqP9FAe{{un0iijf?3jO2J? zSon1e&#a8$@!}XZEQn#idojdKjbY!E7+!llhJsNsoO(HicV3L4_W2n8dNzjlpNYZP zGKRlR(JZKs##$cD&4Oq?%#`+L~$-6iZM^32=b0%nQIjF z|3t|-iV}ZO6c5iuG5tgosRyH2w>Jv=k5MwUKoo=4MaeU56w{VPk??U8tL93%=~3(< ziY^nQxHvY75yPW!8xY0RKGI&dC>D2!qM&sY-&!IuG(@teGLmPCA~}&2N$=!HE=NZ) zG$fLHzLAXch{XLtBvWoj;(IlcX%`|1J{8IA!;wV%9Le0BkwkBfWZt)t#C#>M%Oi>U zB$7GvBMF}w$*i{{37Qni)Nzq$BO;kRC=%Dck-XYHk~b;s^jZYxFGSGsv}}7gLUJJytk@YL|JM=0 zTi-@-|Emc4FOLupP6Xz85qv!}g2cBXcxO@s_g;;l_lO979vDGYp9mH`7lD6=2wr(A zf>V}o%njjuQ5i05N;qS)!o?pLjwLFbmBHZzY2l1;59i4JaB6Ob^Wl|nT+WB{{K;^( z9SSF9Z#a{F3g_IWaGKYKvv5^7&P&7T_Hj5H=7baRZaBmA;ru)yTw|UhoIevLp64(o9|+^OU16kc31j$%Ft)7@BWQUT-4}(ia(oDG@nJ^EybG)}d(T5Sr>k7*!F%7X=}l&j=wZA%xbE zA-wSo+KGZ%t!I~7dIZ^0P$1=DLsFcW_W=96{7 zY*`h|>7~Iu{3w{{_k$^)7EC)`FoRwXhEc&R9umwC{en5tBbXbVg7Ix5^;mFxd!pzpCF$69Ype_AWF{$VLlo}$6tcz=M==K ztwHEE1o8grAeJl-V$CN(Y@HXx{ux1>K+3-n#O*ObxC{-#_r)N>pARCjOAuL42T^Q) zLQV4%4AoC)QSwCQ;(0>nlqYnLc|z}yC%owUgu(7l740RPDWTp2IxUkPB@pa3TK4WQR^0W`D^ zAf}bHVe;pfI)Cx(_%kKXpPp&{RLA;@ugPEj56GWw9{$XG;Lppq{IOp4C;ps2H;?W5**urrq(Q<264L{_x|% zNk3tzq}*OVD!2RbaHAhv*Z47cr62Z1e)!M#WB*J)X1wLcGZXy?ALGa2p?2uSXq-DQ?@wq{ z{Hk$gw`{XbqiBQ1(bXCgmTP1#)Yvyy>YT0-t<(77b&cMmG`t6Etb9S^sqPv#I%v#p ztx<0B;Ygj2{5Pf#(YZcsNcEv}tPl4>e8j)zLz$Zozy9mP%Qt)oxa7m~vp$%Q_;7Z= z53la@A>s!gzFOx)i&Z|HU*f~K4}A!m?IZVWJ{TwY$Ti=Gp~HRfe#wUgy?iL{;=_)o zeR$UH&Gja4VL!d)x6hlUnch?-c(XggTX+d?u6TL#>LYLbZhJHTsyEr^y;*<48}qN; z{JPtl9@}J_4c?4e?TyE0-b`EQP1GE37QO3D-V|@vzUEECE8grF?n{MsB zIp5No7mbhkU(I7ilsx92?8l5xevE6>W8MsU%wzAz;PM#1yN`M2`eOnvJZ9Rd#{?dd z*S+$2`(xhT_!!@>AERIK7_UzrGjZNyoToo#to|`~Uw_QdQIEMg_%ScM@R+mDJ*G>C z$NbvrF)d79Y^(L6ro@ZYIbLKYd+}kk7okC3=zY9+%t+8j^*p5{s0DW0qz?@90oPlgWkCD2Rta- z>A~BZJ@|c{2bEuV@Xiu>zrcg?Ssw7VY%|e=qA?y!94ck|dyvt?gVCKl*!7eL(PnpE ztas&ZjZ%+zfW7(Z`*4T-`Z(&z-dE?!5e`I~z~A<8{cLwtL-~x80rd z-?@{!+MN;0-T8i@J05e~Y4NT*Gp4w6WV}1^Bi!jV(4A$y-MR9tJB3fXGu-aRx+XXN zt#qTdz>RSkZu}7MhD(?mjec&t?jfJ=yWxDpjk-&2j6Lfn9y2%Y{Om@_Pi_p^PqH*S32!(;u8U&)ow z#jZ5ZcV);-R~F#Pt_iN(9_32XU{_kc;3{0AD+}98y)9+A!G-v07tBR249;@ly+ju_ zMY!-sfD68!F4Cq89d5aZ|J_C6cP?x{>cYkSF8J+mpZP&Um>~j}pFLq(=d>0PS zaN(}rg^1T(C?DxU`@t>@@9V;hZZ3TJj0<~PxbRoAGhS8BBo{hUm*GsO1ZReYJM)ge zGfO?3*?ixbqc@znamg9&j5A4xovGaC%u_!()8~6<#;$Q@`U+?He`jYl%ynkpyUzSR z#hKgVo$(&wO!NR}a(g*b-$lwi?M!#uBZf3S;rn- zBYwa4h-=p$@$ZF4c%FR3lY@_l+5L#Lt&b@9_7Ro;hoh^2%A)DQpaLQyB}iBZ2#Sb^ zBJu8hKt#$yP(e{jqy+^9>F)0C?y!SbY{c&F{0vNN{pb9gbIA|xnJ-Bnb2bV7O;N6B!58%4sa_8* zYxRJy+Jknb9+XJ;AVIVT+XQ;B@mn`6-giUyc{irq>xRhHZhRc<#+{?xIKHnNZJpi7 zYv@L3c{g10Sl!fa%#G=0n3it**xHTz?%g=%*o{WpZlqXs!`Gx6EA_jfqs{8f?8ev0 z-MAssjlJW#Q7G7ro!`3{MzRYFUUaeFpDwmn(}jy?yU;$+h2;HRaPR7ZNn;l#S9IZ9 zUKg&Uva*;iB!+as$-fKwTe={%u?w$lyI4%jE)<(|!C$`%X4+j)n#ry=xeKRcx==Qb zqO?&PB@?Kgw8-GM(yv!)y_`THFP4dyc3J_Iw6(XiHFgh z=m_pa*w#*1xOYO;9g)a<~r866m>(19}&9Y_%Fz~Vpc`1iRThyH8F&d2T0 zxz&!h7uwNyvK`Kc+o9Okj;k&0NUv_klA?C}&S*zpd^^0u+M&F?9XCDOk>S*i#cSK~ zZCN|oOj$d_cF5?o-^9NBg}5Nk))sCJnAXv4?hHq^do!|MBO5WLog?z3%h z8fb&~{x;_K)&{SJHprE?;aF}P{8QSX7~O_bJKM0$uMJ9@+4*{wZ_@?^3s#5P;JvU7 z(sSBya7G(k6xuLWvJG9rZCLZCmHAJ!qUyg^EP33D*Ed^{aiJCSPPXFep;qkdZH0VG zD-Kq*!m+Rwf6`k~8P^I5ZN-CtRz!HTV)CX|9I|VLgLNyunY1Ebzm@4_D=w(D!h3Qn zgk@S$Biag{Kr3#Jv|!u27KlA-LH(T;=JV2m>!(}b*WUu6JuN72Yk_WU3(gj|z&Wc0 zUlUr88s36wfi2kW)dCCW7TjIig00J2Ah@Ijd4?@e(`mszbyhaD1-B=*z)P%!twXgS z<$E*ahnvy(yqSGhHnX+4W~>@)#(jP>y!SR^q`eujbfH=Wmu6g9*NpWmn(<^wGrWwN@j<5wh7THsbT-M!3l|;;LvP77H|@XQTns-ZdcOX#;-UZh+^d23$YY0FxsP=;&*JLTdx! zs~a#}*Z_x&1{{xTfL3S&3IiJOZ%YF_Ha6g*Z37lrHK5YC0YZx!;H%YuD=I8cu>s|h z4Gk-ynk9&>v;8oP4Jg*+VQ|hrH zx*i92)H$)xb9Go`K#)YwX_~@c=fQ*tw-Igdi*^MpEF4XcCt_Bw=l*5SgY zIu^II4nfv+IAu}?dHp(g&#l8D)jEhN)xlA!j`@bw;n$y9SbeEQ$$zzY`KT5PZ`LC2 zd@U{=uf>#uweae$#h%7m2vpQ!Szav)Q)=-zsur3%YZ2sIi+-n^og^ zd^Hrqs|#(|C1cyC(`WvgneF|J0kUNsKQsm8k*)tIKh>Pb{1R;U`izpC)` zV-;jxRblaiDr~=2h3d0axOlV*fA&^Ev!e?3byY|xsX|Xy75lcUf@pXZ7H+SCvu71j zH&wB{p(;GEs=`>4D(Eb#f`e8SVpXcps!)Z?5>@yvRE253D`EDj627l0*_v!6_Fk*R z-LsVt7^sBmzDk&PRKlmO5}75H=+3HS+$5Fw5>^Sh?Uh*MS&4O@%*! z4ZTWyol^-Jl}hL+RAQwByS7jzQhrsS;bR2`URL1#{R(`)S^=4}6_|Up0v3BK;M!h+ zu-Xdb7gw-v)e4-9XZ1oW@HL%vatdNwiQ@uSpj!cAar2`vNS7Dr(A)3lPcKw zRN%qr3Vi%gjxis~A@`yj8u!X!aHSm9XUegmzZ|~1%MspM&gLBDC@m~UOL{r>$Cl$% zNI9-96wi;L)g3=(p)*F=$2#Fta9j1D+ecA4s)?`*a((m{kJlfI6+bGQV;r%Ukja0%Y`mf&%739eO^;8cDI_NSJh zHM#_4J4=x6TY?DJ68PDdVB?AsEL&0nBf}ERo>v0-nd};qOYlXCwHGPDg+IkO^tl*K zuZxlUuo#ioi{Uj?47-71(7s~!ekg`iT`_(Y7vpJWFC?IJwCP=wPbiqL$p z2r1n~@Ms=N4gXauHre6ya205$e5)5bIO~r!__3SrqsGl`U-Kcxe$Gog-FdWgmY>k z^r8zPwzCkAd|+6P zUlzdiegWoREr8%)0WS3yplWvk{8|fOR9%3vg$1~tR)E@=0{92BJih`6yA|MyeE}*~ z6u@H%Yin45-`WK@saAkYr2?#zD!_D+0=)X2kM2+T2!53hdXNvHYxx)&%tsDCAM5ty zV{%(Q9#`k1p)em_>G{x#&Bw>!eC+mPZQSx<;E<1BEAnwdqe}UR zmSSm>&f^Ps&a4{uuY&{UlVx57M3P0z#am^|bK z=V7^D9z@*oFkqjD&=q+sUO203kcT?$JZw9?pSTZw@{* zvGa-?wqBHjcgd_yWDbmja`4J42iZo@V3Mt!z|Y$cD*@Y&<)Vjl`~OXf$NwY-u)pva>NhF`Hqpv$1Sj zHr{T@M!I7*G*`27^K6EH&xWvWHtIE4`>ELs-Jy#+p>^U!}5xP9~NGX5y}ACcHOg;=64o5-l?!gG^K}V0C6^;^4GQES1f~m2sKaD3FO) zUo#N+CIf#SWgzKB2Bgkqpll$6y{|IR-kyPlwHXX^mjSbk44jM2fNe+y?)YWE*)4-{ zP-Vb(MFu{aW?-j527b=XK$L0*Mk{6@Q6huIR>(lc&vZz9NJq}|bjaLIN5Q3Z$el_@ z(V=w6_oSn^ksZrfI)~*avGVYAOx&K1T#s}}ZcInU>U4-%q$7z-#~9soL~Equ_mp%5 z%cSGe*mU^)O~b3tX>fg=hIu;$Dc$><2no?nV zGZiP!rDEZ+R5a{Mg2`MYBpOWE4^nIUyCVN2kK>M+*Air$GH#3UY3z;OB)DxSdF0d#5ST?Mh+mm?;=l zngXw^6kLi=fnI0|%KcfMdkQ?)r{Mg`6zG_xpwN)D)lR`CwG<2}ra)CP1xZ3F`0r;j zmVHP@$Ma-J+)YNnrDR+_nGBsn$;jzW#>a+a*pwxsJDZg!Cc`f*8AAcdn6)JtNgI;! zY*jK$mL{XZC>cNIC9^$;Wb`N{W4u%{JcN@m@GA+EKPF-4izHmUn*@!^Nr*X>ggb|l zFux}Wsf|f^QkDdRoFrr?CgDX`61Z(i$lH>HR~wQ*Htc+95?&f5fv3aj%uK>lr6eqp zVr_+!aQ{~#!)hn8IP;0PdN&cOmlN3@S|UyzN`zcbB0L%sv9~M{!a0dplbDG5utazbkk3wlQ$hk7Llf}MKLHEe6A-#S0sNH-5Hd@Em0<$1 zv=eYumDN>Dzy^r~RE;jSN#TCI3IoDq-7^6{`2k4OHfcwGDz2eEf? zSo$Om(Kq98@LU|e48&pXzBqWa$Dz6=4z~;AAe9~mtC%>%?2N+!pE!JQj)VHzIBc|z zL!NOQ1{cQR-|RRnm>vf&**H{;i^G+Fu@L?m3xoe+8UH~n!}P`C%3v&n__0{DI~JZT zu_&#K#ZX=>ekaFbPGl?`17nfy8H>XkWASEnEEFwbVZp^Bcz!Gz)LHwJWpUD`GHXNenCvVh}Jl28AjyI4mE7C*m;}J30nh-=nemT{Lz+iALGYXz z6Afw6XgK|jLfMNb+_)44$wN`FYKTHyRum5JiozGSC}^&Tf~$TMif2UO{P-x0`W6Yj z$C2byFnsQHsQ|`H}cAITBMvBC+ga1a{qxK*PxhT<(g% z?~(}2jE}&ott{U@0=0`HaCS}vKFUQvUN8dmCLC_p!;#DnM@LIIF6D*eb3{1gJj0>C zIvftXaD>hbN2z2u4*U$mt!H5vxe$hl2g0CJ7l!2-VekqLL%d5Es;t9sU||@pObf#s z(J+ku5(=dUp_qRr6c)Xq*i;^hz{F4_2ZW-0eJHw?gyPuTP+XG_#q&|27Z-nh?LsJ<})G4eIA1Hiy^2#5Q6r)5cFk) z;9zhFj=F^4lywM(7KY&Bv=FwI5P}<@gK_JAFz%cV#=RaUWlR#7_y^;zLojZe2IHny zFs@As#$~}^oPV(d=@J--8M_w9gX%?|L=c3|#~9hl~{1Co|IAfUSg!;^R5f$$ET`w)bKcY@G#A_)1N zL5MC6f=_G^*7^p)cx@178wWvVb`XBa1mWqQKn%SMM9<|w_Wlb*L_;9+(F?@VU4fYG z76|d>f%u>oh%3_r(Kjv-`Cqmp=)rc_o!JiE-tCYo-;Uvg?Ktnh9W4&qkzl$VE?V2c zo3tHrg4^-ozik-2whguY+Ys8k4XbmvK_h${{<&|%^_APuYOoEFDlA`o8)kkBz>h}( z7&;rkICuiEwK4#d9DoVi18{#s0NTw05UL#j3xxp4jt;=%xBlq3;g4NM{bAbb5Al3| z+>G!?wTC}EZTvCM$RFQSS-htS{)qp+6;@BSLgL(3T-~!3MO9m|K83}B3fzi^jx25o zi(TR}Z!2afZpD+)TT%1Q4^B7zFlE3GcU%2XobLy_NIyt=`mtC-en>R(gRz<)eoXK~ z?{{DLJn?0mtiHIj#~0aEzF3;#i{IOQ(c|a~_ocp2nCFXg3ciRM?F*fEK6rA&2YE+* zu%y)opYwcB7vTdN4d_mJLHYt-QIAl^oCHfH#)X? zW6cI{{8{47_|m*#DesM8L2u;0@q)p1FWm3}{p72`iiP3XBQ7-F=h5tNo{*?#Z zuXy11ArIs>ct9h|1N|W$uyysoOKT4{XZOH_X&wxF2*Xb2H=I+syiJGsBQ=hGoQN3~kxWuwt8W!C*61sIcRBmL74# z&_g#^4Z7iUuN#(>yD{HJH-=$x!+v`==q`3cho&3UW!+Hy*A)t{T#<9x6=M&%BC6gM zBN?vn4R*z2XIJ*S>&krTTtQP@8UEK5Dj!@>c-sXc$6XN8?t<3^F0hYs!5L2%EL!b? z1|t{9sJS3|f(zb#bB4oXXPh|e4DH>{Y%bx9F-gww3vk8_2WOC}GaBbOLtM@oLI0d^ z=amzTuQ;LMkP}23oZy$~gv-HBSm5G>LMtbH*L8x!6ek=Mc7o!EO$fcc3Ac}Jf`0oZ z6c%j4=g3X4@!Z7xVm3k4XcIQ8ZUSF?6BNH~MBt;1I6t@%YJD3KS-uh16E*c8D7W{b6z?k>XIX_9B_nMog;RpJL1d^N60%l!pp)D`*j>KR>=`- z$2g+?-3ENRu>m-`0ckB8a5HxURKho4tNRA*U$FtB^f$m-c?0stZD3Ru>!EsoJ$z2B zM{n19d@o)Pid~O5pY=Fnw;tnZJyxo(N3P_0T>s&KNlzVMf6f6VdmM1P(g6y|4sh7! zfTHydxMAu587&9c$T=Y6pFPgLvWL(Wdl(TK*)U&K8%#K91LaN|=oZ;v zNwf{tc-dgfY8&>gYJ&t-8x)A!pl)OpdLFJ~yoRfAu4ff)m9E0GxK;Szy9&S7tb!0$ zK|*5{6r@&R#?O_Q{d6VfpIZsTJu6{Sxsv≫F(xm9TYK3H!w>;i$P1&J$N+^Pd&) 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

7q!u1bO1k=lJ9sx@OsnpZ z`W*6eE1o^!%$Zi*_R|ZBUjCT*bs2eXbkB*Bj$IrzT) zacIuayzQq`SFh-~(VI_rbHWwRprqDs%V7Hc2v)ZBHoslnc6YXArd z!tGn~CRK+&jr18>G^sj$<^F1??B)|rn_+o-Pb3ZA*8CNeZ1;(zdkYVG2@encc>nwL zJ~?gBCxg9R2j5>E+PCPiGiZFym{0z^B=Pp#Pez0WR!91e`Q%{9LkHipp}A9Y5;rB? zo>Lv}5{kN6bt@u0FDOdfaKkPfA2(@3!usUfBhhuatB>}0p#J8~(S}=IjE-%YzB!Z^ z*>wG`glL^j<-6*yZn8c*te@QcTb337`@e4s#-s@ z#;)m>VfR8Xl@F`hVx({Y1vl8Ew!}b#S!g;DO6ZAD?`*{Oe0$Uj5#areKLgAom2g5i zf?-d(jebQ?>P;y{Dww94C6^;fe1a7>O&!Q+oIHoEY3fQkyYOt|(S$PGcdkhsHqQaV zN&P9gkxLOP6bTnVJ>G%NL_}+)8Y*#lfk_;RXim}*#9E!S1d&!H@%hyiNq@xe$|OE3 zy*#N7{9&cai*4v~_jQ14JUO8o+zPtQME=L_cDU<=`Lr=P655fl0FIOk@DuuZ!aBxo zz<3tv45O+<|oRQFXl^QOu=y6UDV~xPw^LXCbD)OihDXgyR(Xkj{-# zyaRhW=j`qfiunjg9|eaylp>E={o>`YsipKL1NnT$9nPxeB2_vc%eW&bE}#fH?nsK9 zho@i7d`7W0?@=7b$VOa(OF{jD9x$)k&nzMX*qvV2Vz0t^}?eTpvcRCZv)AUUEu&17d^0tdt2&FJ^@% zvP*IOwuYCn@Yp}c?w;Thkd1DH(`n3rYPztw-&jv{8@5psb{RMs>ehGLr8P|P(oRpC zm{{K(=cXgSP+KP@RP|1A#8(qC|a-uOkin6J2_>F$+J;{Lz~#f z^dIL=Or$^B%-X`5=HyKT&56iV>uyW(g$YbntBr%!Lx|LtS(7`dp03?WQQMh2h zN-RULHDINS&*Bh3Fr5vEC4+-E>@Y+k_-fU z;|42CuG|L4F6^!|jd;}fFI!$o$E#K-j(u2`1@PNL1wbxl!%e1X5E_8`EfR8;g*U;U zg+!BVSxv_Q>}XaZ0{uLgWEbp-xGB8MX&orw`kPj>q%0)T_+>S^SlYeDe=N)S7whbX z2Jc}TozTJ95<3_{i(M8u*sQ!v;9wDxkruh^Sj{dZB|D4~R{sZ@fyXoCl$yVqm~V7W zAgBhmcll(Qar5b(-FQUVQpwKxp^4dT*XJj;uB6Ob!7^>@>cqYol@y>s9=-5gQwV%w zjY1P`v7^viV}*{TL^`uI!f$)*u&^VTD@rsnUZVA^yxADF7j`sQh_rE+h2n7+BRjh> zZgcGD)`Z$LM8>Sbf%VvFDMTmfP0qFZ)N89hk(rd`kVZ0 z_&>^zb>$!qVKkFDNDFrkS%e*9fY@ri^AW>ryYX9v9sPh{Y^2JA(`>u(`z?0YxrpFM zLk~95E`~c`Qp%VL4pYl)6Fe5aW@`d*xbeD(D8r6?hLXIkfg)n7^`87|(2mOUeFcnZ z$MQwb#zHUhLz}~kFHqtd*q#i*Qwcq|!?Dp7`=gVmY+nerN_9h=472 zjxidH7k;YIP<$B{Mx=Bb(GRK-kAwW5`NVE5D=?Xe4O=*K{?b{6|AQ<-V|F31Et$W3 zI?P5&r!UUVg6#I79yBtPcSX(J%>;1;=`LT09_7q zOV~orHNa#AwxEe#@o=yHaPN}I=a#JZx@8&h?-`gt$x_M1Ay{VaAdxUjkREQHZy>{Ka3o`S)`ZU4p<2A_i=5*`rHRuL- z$=$km&5fq?Zr!Q&G6Y?g*}GjQFQr@eKHeqJdZqV^)!n?`I+=ao=3Z#+0#oGvFDr5J?7dtY~Pcat}>b|VF%J-%nwB+m9gSKWjXr2p%=4r zHI|e?gU7fPX)F#4SyOof ziS~$HnCwOHq;{ft_!wU|YbH7rU-ui>7m5Cx+J^ELIljd&&-CNB^@5v+oBiPSecMpZ zBjcNACdEM+&k+Kfhlez@;zuHH#hLD?__fH!qkp1US8OJ>6LZ8d z;zi;@afP@^G`5N8KKD=t?_e4JbWqH39Q*ggVnSW(P8rvo0XKWEcW4i=@|C=uZ84v9`Nt{Z`6r27W$t1d} zA;%cyrlPqehultbM=?u0mCYg_@S1!@KBK2xAYLN!-5vdxi7Q0Yzu~`5@@*oYYcu{` z;&$-?@geb9@p_v)5Hd%8F%n+A(@YX7_WoaUG%>N#fK3L z&k^%QGY;WDP4dO!9PtvdR9r5u5w8*X-h%qqi`&E<;!g2l(by0p{?n3c+bHtI1NHf~ ziJwXKZHaNwG~r><|E5!8$r)m%$dyE>udB#68I+^qDAE6>(-g@UiVMWW;$`9$qHmjc zy=1-%q5eC>ZQ>4*e;2}V+G>zbi_eKKioR_kU#2kLhvJvw*J2`0L<~b#*i+HzqkNAN2koc_lyja`j@PPb35D$uDo?IGWOv3$PmVI#>+#WTbwN9&ySZ;ed_LCWx0L~;}b|!WnlOKxL1#xQU z%X>; zZ)xCo_+h+H4eATGw)LbwB#G&}7jC9uUfiPf9p^{2jvK=t!5)nF8U`xwo&Akr+@BMD z|2#}g^LznhJ-9uMJy^a}l#loREFWjretq}Bjp7soW?Kt={d@srp4?FESYM{hHHHt+ zq0Ns+2Aa#+1AXjQ=!UU;Sq_#h&ZA%2O-6gTtK1k2v)@?Q?HWJ(BLF9(2>( zX!h~+X*V@Z&#qgxy=nIGmeWR+{SwSr`Qvff$D6O5QB?5s4T^sT-zF3PRs zU$!MLa^$NCE+{`jLv_rU(TnUlhE{tGA84$eNF`soWNu5}Ynq<-1+cFtAG-|tpLHn)s!$ZL}4-WsmB`$2c}-1B(&l2=Y#<$5QQzIf%twS(Oa&!4!?{h)c_!S|QGmy&WMobp^s?k9tD2YoVd zQ0R+#A@7TNUgzpam(JCR{X-QMgV$jN(z>@#ZJAt=wkajA&g~m^xp@`4Qa03CpIq*A zT77i+1L@KF#`Y~7tsDKdv3s+!`P(AXEyM0oFm*ge6CZ};7|-|*7ErteL9k-Gk8l&O zH;C{pvUf3tR;Xh>V#6SAIN*(gicsen#wUpv1n)e?8Bj`}a6%I%4kz&`gm(trQ+Q&9 zLELYUPL^;U=Qv*GV5)nJ&bH^%X$QFnagKzrG3T76DX!}zmkzBQtV3cI*OAh{tA0N8o!-SX)98t zb!5`Xtj%ylOXGRfokEcol)Mr-?o^8RQ`~`6?gbQ&L#$`4&!$mqkLy7_Sf3eT90g-= zX2h)!)A@YIhNf}q>t1M%8g3}lE~n0mqWI;UChaCDw&FI3^|<0=0S`d0L`VgWJA(r6 z#_P?H@Gu3u$}}PTTbp{bB=AF|yzn^ZKxu=`%;;)!eEAnI4MwsYR=yIl(=Zc1Hr6#J z7XQwrk%?tx_Jvu{)R_IC4!Q^8|MxO?8<1)Y-U16b%AD;a3pvsXNJ9E0X; z^;2N7M$00i$su=*asm@qupxf<@+#xoZz3zmE+wmEI)SA z@?$v)vH?4mAS3vI+;d8nSJLs46^bjX;rP}FD?*iYv^LGNB3#KPW<8MHZwOyAGBFzq z$|j>Z8My&FEa-^M*pb_kN;>#10m6(*I_|Q63I(Ay2$gJb`xE1DA(y`kL z^DF82trZql@=@!j*pVgix%FRFNykwutf-ufI?cllqmiK`n*s-8%W6KjXJuiJLa>g; z->+=YMy?FnY~}KayRf5E5G*q~7O~qnClRk0=R|^qL|mddvPOR-*h$cukMcOt&62@R z?a9@NFleJ-55wgvE9vN9%69pxN;=H-*13E|>;Pf$zLlIsm@Nap-LRuf1Z$6)=fx`( zU-gbr^Rd&mjU0y^jY05akH&a7XfTMZ?Z$NMtEdVw!#F1pD~xk8!3=$)x)pFEGEpXP z#4uZ+45lnh>S6iOvGD>gR#dB=%^zXOHimk(i}e8cWH+zT4pc@nBr+8>a-AuB%5D$C z6v8%757S9h;+%NSaigYF7L%$YM)l;l>d91nk*#Uo43I+!E$fe{ng=9azj?8!v9?09 zAqTVhoi}Kq$IiSPyKlv}7w!WA`2)C*2IMBlp(S=dK06mn<;#8vgL(W={=YW*^UK3y z%frlnUqF5x?zaMRygW8O%adX9w=&B^!>>fg%KxZ5x1wO%vE%$p{2V)bGWopmKA*57 zaZ-FFPB;wTFv=7-tKSOoapQG9!CHizl46prM~gv#g@Y5x%|;`?JtlM-!80lnEg`Fn z_oPH5u!(rzYPP!+6UKdvXtkdoKNU^#BW_3NBUTMzwJ$N+-!R_Oh)=PHoF&VsfGWwaWKJan28yZo7g|mTR0r+ zBzp6%^x8%4%h@=hd-q=5TXk>W{aozbz3JV&)|uW#nO^tF*PPp`V|%Z6CYMUwz>uCWB1=ij`d@Je!0d%1yWMP9bGmy7;C zd%4)f*R#)Y0`mVQdpUE3v&$Qv%w7(C5cf5>2Z-1SGVP-HxQ4`Ie?zqoz<&p2JV?7w+)3ggA8(9V4&Lih{x`|SZVobU zM;ZQ}!v7}uh~zIN^R|)cc~8Rl#%>OBBgveeFuaSHC4XZ#2mc(&!xcV8vay>(xS4lB zZ;`?;6|WY{#Z4sQ-7VfP|6P)wl>CfjvsM`N|4IHIipKs1{+PC!a@Q5jnqaVtJDoD} zX)k~C-6P0-By$Z*+RqugI{1&2zp=Z4%q{rdo##2q=^`IUQ#NkzbEo1m*@n_=w;=|&j;?v@D;!EN_@z3I4 zL_WTz{x8I@MKfOD&*#|;Zy+`kTZnwX&G0O-pLnh~SR5w)NA2jYlOFy}H1pjkZW1fS zyG1^6XS^rG=foGpSHxGvH^q0vk3=&ygq~xPkBcGpTljNwN}BNpHk8ciIQ=t4KJKU7 zRpcy=GG}Y#Kyk3hc_sbFi5G~|#UHYxTdjCki8qPs#I2%lPj|262gFCj$3-)KQJ&vO zeo5RX{!#Sp>)w-W#y8R(l5Ff|A^%;n%W(=hO*G>jvKjB7v7-e$D7=fF?Xyy&(A& z@m2B9;$OtSiXV%|#lMRQn1NDn9kGGfMC6MghIbNcuMNm|LkyoJUL?*C=ZXu&+G_=t z$^UB6Uo&u>CCB z4IK5SioX3_hGf3BV|ZV2fH+7TA&wCziBrV_u~3{Z7K?lnNc~rdH;6Zjw~Cv^pNKyd z`SOtIefzwpCHwYyFG~KM_y_UNV(qp4j>^AU{7&@nE|K~0l_c3j^w;!jCAp*6RqQ49 z6?4QqafCQVyg-~T)?V9>Z#tR(wPNkH{7~F~$xhI}Z)v3KcBa@?>>zd#dx*Wn95K(A zbzL+1{?qLYG2{4uc7|waJ}b7{nE<9=yxlb$k4td#&HllS@yc-R^f#RT%r*+Y zej0kv&XDWe`kNm%3?97A&QZXu(;TxNd=tp%o(}b~TvM?7Y2bAD4!Lo_!*|xUGvu0> z4`TN>Ki(X81oNHH9Ci@Mi1{);x06don6K|g zaQkVfA{l)q&j6 zQcPcMJHrbx5r3f8aih*hu#+6e+)^=7Uu2!;mtowW6OAW+Rw7=WFMzBEH_jJ=^<9bb zrNfWqYk=La?>@Lu95#j7c0=DkV`q31^c_$df@R}I2Aa$H2Ksuy6WuVDFU!HQA>Y_W zd)TvLQe0p6erILJj11xM?F>)(jcnK%_V_3542#;D48Lb**f`qtQ0T_owb$=VSsR8G zVFD}+Lu)#%y$kk)-Yf#^B3*XX)x#S$E3ER)PCt}#c2<>x*mZ80QT5YK>4%b&3#-~q zeeIBQL;I>$Q_Byzof!Mk+=8mB&Tf-;Y1NEQORMgg+NP@g*~L|Bh9k#$MOAB8WL9;U z*P$x3qIXqzcxF|oQ%Tjf<~^&PJ9}BxPv@Og^^@d2@XxLqvSLY9`xTq^hbo3gTjU*o z(AyM(m0>8a;i1sHNcQ+c-qhct7MpW_k21E)YhTp{rT^*Kg;m?9GM`>m3B!9-wLxtP zS6o`PeZ~9x(~h`ly{eK{6jz~>zcRH*-+V_DvJD>VAbR)kDoy*o-*4;z zX`tE>K>@j=qvPgdjrgQyc_=?B{U~={zhik&CMTXM<$&6P<%%B}-@8vN0st5-o zGF(39+>=ek5Hk*$h;9f%Z3ZxQ3Zn}VYqJ_%5hTAyk`FI; zB+0*#1j4Z$&qseeR{eQ+hP)JH)?_%O8^uH}Im()1sUlKSEj5`GUDp{&epF;lhC{m1 z%(utnvs8b)sI#V8>U>huEXB@uW(lg~j$y{_BwyxX8nQoL+y2zTv8?(xNn&cDA-0f! zV>?5{U#$m&x7Xn9ueRp$= zaYg|W2+X&?AG#Wovw}c*{AZLv*}zN>Bx-71Wd@?9*f4*DU`wlPV2q>BNFdnKD%pcd z1Xhp}@Q8KTgadHpD&S+SS$VzOO znnAeS@G)tRkoFoC&r$>Gz>W?K?scK@uLN$~Hw z`H1;}*=Rh#1t*NdhO-c28EV?(5{hioN^>@tGs9-a0}nZCh7)NyiaGBL z@Al4^Zbvte@}ClG#@LH*b0VV&&1qy)g~meLWDzg6Z5HtnXCj>q7x5uP)u52b30LwbJ31yji2V+1$agAH`4ZQP z>3{UoM-q({!>KiLJOb99$h!>f(;&-;ptpb<4LMBMg8kdrAU>KXHDMN4Y_hHyO_Ull zC75cWzM5nU=GpA*XBbJGV&c`qi8FkDA(5GJ{Aa8J>dcNZHNlQLjP=D?Gn6PDqlkXK zHRbG|-G+_lh3MXZ`EK&_4GYS4@Oh^al`p&w@w}3jjwk!k!=!X z9I!p|M6n!9%6v&-PZ4_p&?KU0h|Ux~bPG2^r_dG9A@_E~k5rP{5~i7AeJ zI#E0@+mTuWkEHiaJ=d0epi1D)Y)=bkw=y#yQ3rTZg^@(@!229glwbm%LP$hNJYda3 z%doWLL(B&M;rzv(8#tqHpS+JXH}FS}TZO5C^qx02PJel3k54Y+n{VDo`p!G|!UbM0 zc;;)j=C$HM#rg1phnao-@B`&MZwB4!^(o;0jzP`I^A64PrsR1!gFXBNd0L*=E6+c!IdR;ml)r%7mk16rqUUo*Op1tT-?;bZV6op^C zgikX~BuqGKEB>(gOumyiGX+XhtmH4vpWYUn6xMXESu+z^aJBxs|MlC*CDv(EF`3dlsjar&aJz z%cmyd$X_%NyU&R)y(3Xoz7_qDe2bF)Jo(n+H9L@RJ^wB`;9NnrUw^J3#eK_qr1-3I4O{Jz2yghj$qp_wZPeP@cEESxFiz#q#qbESY6ivGIrdf~T( zw+Q)FzkDSTG=YvFUk7lm&KW9XGJ|DnQTgad>_gky*pgsT*uM#LCTmD2hB zM0%d+^F=Qc&94BaUn%+YTol#4i$# z5c2bf`cD&@_6mB6=o!L=!o|W1gcl10@hcUdU+&a5R(QJ5w14oYiJl`g zb7#RfU!_2E9{?{_`enkah1UyH=lGfX0r|eG^v8rx2%i<2`vP*$iGD%&iqQJZGxrDN zxSWi~vrlO56VM!{CEtwD0&_$kBJ3hORya^NSU5yz?jPhIBihdKs}em;SR-60Tq?Xk zNHYcNLvs@GTS9Yxf!-wgUg7@;zb|}D_=NCTp|K7jKkXjW`?_$C@ExJK50P%?@MU0} zpYq1$1?(u=`pl!{gz0^SLxjVG<-&2ovxIi;9(_76|6CzWFQhLLUM{>!c)joj;kSji z3Tdlh{)dG>68=Q^bK!R3?}dL9(zwI?yM^xx_X#~bn3x_Frq0>xEdJ3#T8AhtPdE2Mde`mPXuL%3dev+#D|cZK&0zb|}L_*3E2LOWOQccNbrzAoG& zd`D<~>cJ_3sh6FbXMOA)C4N_7FJXa@CNUn*P~m7{g^)%vrcW2n5zZH$FI+0LKK3pX zy;gXg(9Yf4B>DlNoy)gX^y5N1mv5Ws)H!{7#D7QlFQJ>}*E`j>-XY>2COk^mRY+5x zX)nSe;ZWgdq5ZG#1kq;+rweBZX)L6^rNWDamkHMjuM^%Xyi<6e@O#1^2!AA`y^;EV zBYaW#itr8L9^re!e+lh>dRxK%NIf~iLxibw`e@KJ=SMhHI6_z<951AgG|EpF&JxZM zE)bqCyg+!dkQP(s|CaC;;T=L6QJKC~_@wX|;je|i7t+p3`M(L@7B&k1E%ai(91XFQ z?;uS5KW{hjdkOmq2MP;?CBjpMWkQ;Bseh{QT;W_HE|XbPWcyzrjXZ9DgN1sZwkHiC z+dun6d>Qgf<;yvSJmnY<_QSCNTpE0<`k|lDf|cX<8sofv43rCw4DM7gv8guv%#jN&M};fiY}!B1uJ(B z=wP{9@nO0K43Z1+9=An+vtftq)rlrm?tcE@wAM1xIB=n`uF+3R`@bqY9 z1NCuxP?)cT+i|1LQ=DMseyq4n>WG1WiIxD`_QLO$;PEaB9B*66vA)}|p*qz76Y8LE zkR^cjcscG6sJ62T`gSxUM|m{2M7S6F3c=(Y!*i-rMM`x%A$VtA?x^Ikal~eT~l68;4HZUbgc_F9ZMR z9fp7QwrcNP2=Z=&o7T(JdX@D1U`j;a$a%v5lQFR+W!!?3hv1*w(qT z41*bGy6l^pY_A5{A~Fdf$m(iSCMp$2Nw)a&xg;m0j}Q_H#-&&yI8J z_6&V$yAzKZe|htEha5lVJQ~Nv@vl$V<+oi1KWN(6t8C7Pzr$oW4;KhC)!4i+8Scjr z^d4Z`{v-Ax5FV$fh3r3K-=y|%)O<9@B4%tQ7X2qAlJnwTXQn?>m;Yl}&ZzUuk6|%I z?p;6sB7z&MCB(vASwc8G65#(07Bl6jGi@`{o#=M#mg8;AnR0!xUmE2%aeZ_?;!C2} zV~?3~{0zcOxgUaxorYcba(4}&RX7A$%#^z$aw;O}o0xQ0Bn%GwJ!2C4`Ok2q%K4%jc;ZAQ6fYgK3yRd4JXyW54$k2+X(t zbg!JV;T-WP#q1$7M(ZAHqvglt71g#;6r zkrJFvKztk<<`6sypfqO|0A9dG34jSU7u<7M$u#@`m2(=bIFhV1>*^~jwiw)_V`$p*+~+6Gqtm9f>6W57yM~ zp2*VFZtI)l=T^=;uU0?KOBjKT$#u%?uQmN4p8bzri*44{7W$z^>O}4S1B>*>vIQp! zmce>1enYol&fHoDR$pUjHWuvWOuqboT~o{UfAvoSO>0P4&Rb-}58gD1^K9oVB!9kW zXM7~YTV#a~&LH8mh=i>@V21ycE$aNXJCIKuK1B~;PcIBjZB6_65V+L&Oxw+A2Tq1? zGK$!zVJGj1ae&Hj+XN12LnqC#^!SliTye!-)+%0@p=<)3jIb%Nr z_7XikI`<3e^1DgP_s?}X-hga4A~UBbT$ z8-?!+BRr3gZzaqY@?(^I>nr9c(W&<20pgz^+R(9eg&9;e)dqp-(mOpGh0$}x@M zSZpzD7?d&LKyp2i$~?hxcbFXhj@OQ1I5w-BH1EM&Td6==3F3Uz6=Gv}8PC5bA=?3Q zG!gUcW4*><3+fw)it_%q!KTds!^WAGj|yGVOu18#7A$uY+Sd-qv0RqN;~*};7S#73 z4m=K?4K|G-*w2kZAh;CK;D)bDA;QJrdl3B#eUC1dlCL&@_t6VRR)VxW0AE1@qR z%tOJqA$sX=g%VUHN!i*CzkCT5n92&2*uWKF6mV0Y{1C8hq3sQ^xaNm zjkhnd`*|mXb>+`Bh6~;G;a$VS7?WRzwdwfJkCPeaKXhha+%eWSZAD~PaT@-mQ-3M` z9+XiZ$A5$F#eaiZ&#oQX75@y{Y5o-yTHmQ;0#?ay+7n{_NZ6J@^M@g|GI=-`bf)o0LOl^Jf=b3hv^^L(Kavb>)sQIl>y# zzn2-^*extS7Vc+shbIA=j>+GNbSKJD_2p5vqh--z?3YHDfKwkmAMqv84cKE$o^HnM zm^{bE@K2hH0f{mB4gQ#X8pSq7)*y*v^6f}|TVyDmmIVBJCNU;|4a&j4XZ$gFPHqg0 z$^RZI82mB$OehMB$$y0j$uapnl8G_-%)6o1ACu>pd)9QO4q%ns34EepNS?&=NXs@q zh#5#S#|*riD7zgw7?d|eF^J3)nQPELv8S~ohyQ#j;c2@b)PL2Oe9AvpEQuEd$K*9) zk28=YI9}h9G5M!(rp%Z;BOFxa&^z(hHh~dd_0%!>p5S4~l|Xra=$)}&`UwQ)+kZ@+ z?+^?*6DW^GYy>M?kr)vM)1Qlt5pouD8a9oPGddR={`oYgBI906W3Tzm8C$KhY=rC|lo^f+4IHwUdVnYT3Yk~E11fC&Q0K@-?nUIM+Kj$#| zPT*PM7=6>T6|$LA#^_mF45>c`pae(T7h$wLDV-Q?XEFY<(*5KNF`X&jiaffd|Q(o6X7wo|a$;(pTYW@I@Zj z|G57O=;Idj@D9VceH^3rcoUlc1I(P^b&qZ6)2Wa5lv6jlxZs$AVsAYb=Dmg=>#YM` z?Vd49L;M+eWd*t3kKw=n2UvE;A^yZbbYkT60Au~e2f4r8u0z zZ+=H4XD3A3CSr_ylrer@i{{4on{k<+Y^TVB?jXK7G$xF1(Hzo1+CGSf5%Eplm6)NP z1o(ZGK2SJRI8u1J@N6M__0%_8_(dDzzm^>i9RGpF`Zp{6QQ^;s_{jTzb(%%^mY&)Y>fYS$=R{xA)cn3`F5cEkg4g{Qs~aeEwYsf2M2*d=J`S zKLv+_fo7ZtTo>9B8v=dU@qX!moM>9+(-N=B{cH&N80~6IXit_k1H53l$Du+`qrKT+ z)24vI@w`GMybT*dA#}1_mdE2DrrHqRLZgj?Z-dpBYD4JP&hKMTABKh!`c@%s^H4`S zwSUGLY}!i1gY_#3=tGwvp)b{j&;|Ogwn_r}u20c74*Ga}tRF`d?D5`)IJ4(tWB49~ z_@5LTn8q#DhVVT4EAy<7WGLs4P9zgXq4Fh3(v(Xw)rK%JaJ=lH+4?<=II2_4JPd1~ zZ;&4~dzQ(-K2@OF&TQywi4EZy=xd1$;eGT6dYO{^oh=tOgnk7d#fH$gpG*!1w;_bt zLBws*&wxL_A9MS+a%TUOp_8}wdVO~pX7}&7=-v%O&)S}ex&383H+Vxazdz%M^3E=1 z_+xhe#tSw(SFhjf9P1X8?OYx5_Jmr|bMOV3SOsvHw=aDkX7_h=*SFqvseAwZ5zOe1 z>}r)3D;*2pwktbk_ucx2vv_02v2R2dKaSW+#L^a9US#q8#pJ%WJ+dq2O(=2JExOO! zm5KTP;q<$UV^uk@9k|Cox_QgcXJIw)%v^r=_#;cn`N{ToUhPr(it&0|J&ZH_{dC`h zB@a04-i4E#@f}15+Ous=gujC=;SIi5+?eV9h9b9v^8Sp(#Qgq?NrWR?DHe|IBo&L? z2wgBB90f=k5IEfK@wMeo>W6{gXMQHrC2(Wr4Hyfv0bnM7B_Lrn*a0HV40=c3M|63V zt#?_J!{bY%3!t|?T8sFS=sN6SHQ=zl^?AD*g}~?Sw~>0GC7sb0LFgg?bHpgM-o)|^;?<3~Vn3?>ikoqI2>@yH?3t2&TViu>{xJ4uipetuKxx+|uCclZC3%);- z-;ng*fGO?}8@>bk*Fn;90%+SGx<2>bUO)a!etxlH+&^KNrVW5kZ_`_MmgW<;`ZBr1 zZGJqT_**}2@Q>mXclhyKKq7QU3`4^NX7x=5jQ!eAAW)y5z}Rzs0$~TX{f!Lfx;hmE ztFoejF^Z>5{Aalwi(8MHXeHh($411T7F zC-5PH?DY+dJmd2TtP@Hg@Gw~qd$_8HJzgf+18&9;!9(S80HQq#m9tzA2gcT-QPPp7e#}RHc(Q<-c^9saFh+%&;HXJWOCpFacq=syD0=5Wd zWQA!vAa2Hnr4tVT9`mD50Q}NUxA}vH2CD$(9AseXiHe2TR4hxiikeN`t|j`pjB&=W{2k1c(7@Irp*y%L_KJaC=Wbm`HUT&nY*&p#j#7Gm!_?XTo%6E zyCQUzJ0vqdG}hQ7hG#mD|Ky3EKKZk!o_-eG=0tP9T$7-4`hRJ!;I(STUg1BGnp=~= zriqY-?c>-hPGEj~FP|bDB^)a}Q#e`3ZV=_!@gXh|t`dG-c)jpj!drw73bzQ^aiN|k zh0hAv!yupC3*w8yH-s^~FUdbtc#Lp>(2PGLeGF*~R#gg5BVzETN^)lW8T35S=PTWe zH-mqr_-33L^sP$g_b$u-AK{P0e?s&hM876{L;QC|<1kFU*h6CdGKAT}jzZIyLVB*~ zBPBgy+S>8 zj|$npB>j}oTwlS-rDR7e-(hJ;RV8rg&ba?{2JjmgzJUIha}Pu^xxViB=?l?x5DRy9JHW*GY$oO zTXduF-$LVu0_oP&<_KJRR>(d)qo z>dS^c9w+tTlqdAvhB))^-N(=dnGQ=ie@suce;k8KwZ#6h4sz3sW`D=yV0Z*uuzno_ z$IB69d%UUkkN2UkCH9ZwpznDWq+rhrG0-fXf#`3v#Qrf8C#{!N2DJKM{}|BcBiKKV z@7H&LFMe?Q$0$yWNwI&>=HboEn0a$?<@Wa2mu>e}m8^oDV{3Syx9_x>SVOU6($4j< zzS}bQWV;!AvO{UGX$&o`Xw1&2Y|O5|bnnK!6^*eQD;hIEXOP}q*%+%I(wMzz2zZz7 zz4P6Q#xT;tqgEo#J!yzZ4QIH}7lJ+y`hEv*O0kYllssNiT5<&b)7Wa;*^S{7CpLzA zT)6kv%ZUXZM|{aS>Hu$wH~`JUeO%p3P=A>GrQ z8zYY{Si|{zV89N-=MGf;2^Bzfeou&nef{C9A>!LXBDF3JzG;6&x)be!sP$;H1dOFo z-l6r;v!QE=ajpbANFIpD68G0gkKIB=m%Hl$@a6;A*0IPPk;#ap52DyziPaNDB=1hF zo@h;SQzRFX#-q`15Q*Fq=?=*F5t)OdMyq!Vh&J=83&>$Hi1)Ga%ubX%+3=k#_uHV{ z|?c^O0tBhZD%H;IU`u! za6aHhKq%)Sri>st5oEhMk|TN5-17Ew00Y4&A+z>bq#wfNG*2?Z-sc|?>0kv8<#!9- zaLhCu5w=3No}C<%3C-Ql6b7e*6$oakLWXI~w3;geO{R{Fu%sT$WCenmn4y}PzCOoh zI@xC8&lo#c0cGmQ>fMM8w=mOYQ_2jR;Sy%BiOPVcN9VqWvDvi>bN?B0BxM{vMUKrF z(Jr3u;&lsGxMulDWCVyzIfODqR-SuGhVLJW;yG?xw=nG+#Tm?%ja+sOOB$Mm@}-Yx zXO^ubNwkq9LD%fCye_;T(C4mz$uNu1IH_^0AKFm+`M z8ySZE3A|~1+Y)0B`UwQ)+u!JvX9Ow;HfM$T{~yIuj81(FtCC=4D+98X;3opBLhBE4 zF*eVcU)^vuB5XhKr$pdm+H;oGH!wnt;1gI|=C!qM#&X2^(sD#(5fp*thy`rK23rZi zFQ7hHz(f|{pThZJL=&M8V*H^V(Q3q>@~Dwnu;zo{=d4NOq$F=v)TNq8aV$`=H%Y)` zM9DLZWKA`ZHy%tzl-!6dKsi&M@ z89r!C=#0I8C|?!FILkT?L&7C)Qdavn|r_7o-%U3sX&g7{x zXU+Y@)z+S0tKSUSdM6eo)GnBkbmKL3PVKDPN#{(Rnm3c@bU*tWUNQKi&onGhfB;Un zOawDQ@%ztA{zTI|!Np&^MtQ9UdsFkgBl!cb&R(~?aL9YU%?6m^9r;SDZ6Kg{n} zi>zG9qn|amxBxz`iu*W8hhekk7WHxFEvPHtu8;E(40k~r3H)MBs&ksIDM~fsHS<%e zW)>Owi<&;v8s{e(M_o13YZuI}a}rjd6i;F}xPoM@5^GM32d_kB$^KaG#MDA2=|m@M zBllvHCRL0bU;L$WvFct^=gq~!i>Y-d&6_d9Y3f`oaS0D<$fI4)T>Jz6sOtbfwt2y6E$d?h%wTV zHANiS;3o#pe-j&B@^RYKLTB%O$fMhiuW)}{jP@eVeI;_bD3Q89Y(3Ell1sJEaW)b2 zn+cV`siIA<1N3>KmkC!3*9zIQVE)^M4+y_6d`$Q=;Wpt*!qW< z+lW3)XzVfITRWYx&w-wz^tr-y!rO!#o!~h9_k_l7g2A~TEB)t0(7zOI>?WXJ6m9G! zpm&SjEBSwmZiRjT^K(3y_3tQ}4wOkBE4ruX0isV7Jyi5a(c?s$^&H4=)^PymDgAt< zFB82=^p&DF5OKUW3C(&9)Q_`DSRsh5!V zFX_0D&qdNbguR7>geM70gzVo_{!F2{Z$M8MJxf?CH1`kEFA;sY(A+=ZUnjajc(ah7 zA1voV;SYt62{}g0^j`>nD|}A)7vZZybALhpAEM3u1^NTg9G;@SLxe{Pj}rD2a@?45 z#|y3fszmf~VVQ8OkmFs`gkKYy z`x*R$**UM0{MEv53GMo z*}!YZP>Rj!CcPWmeYO&Tv|_~ZsCNpnF$_c}h-<{yFJqISKGv%eTTtJ(k-+DX4K_M0 zMQ%5ge6yKys~{IFcP1LyHkHfrY`J{qTiu8{mmyP}5e1tz4|Gu9z>a>ulE={+o7Km6 zhRuVhb0;$K{bhqqyAX_^zTp9VFdZiJtwY@Ap^k@j4ZT)_RII2?(FkvI~@x5+?Eep*k(UzH34Sj#GLO?4=wD$^={|J38v7=_7fNsp7 zVD-U{IdwQX4yM0e6W59c%6Hk6G)Z=1f^-)?Amqh;v z4y>oN?ZJAw6O?Z~y;#=MJjlX&x*>8NBI(A+x-oJa#JGxV0CRm?WGMXuloz=vasnic z_4IK>A~#100U0Bi<)w(r`_Ov?9N&8SQiKfOdg{kB&FsaO4IkFiGa%{?aCtloJTVL| z*Ry5QX|a1eqjAKt+5dD0l4K7$dnn04Jb!%HiCNhD{J zWEaCdnRUCLq_NAMLXvYQ+wz0eEo7bC@^%*i27pma<`njs+Os2h5+m0kQh~@JR-gm> zl(2*`Qz;_mbu#s*Hq&&Q>3n9g0?KqKGc86Y9*#TTl)_IiQ_A<5!6qsL%#NHg){&R4 zRhauPwguAsPdaEyr2VqJH98(*jgB3BqvN4LNu0$47#-OnVRXb}JsEhlVRR($edpU2 z8C&Nk5SVX&qod!gsUX;#6%CA0JjLk9SEK7x60B@xK$cA_5m*%(4vB159*jN>j7-D^ zB?P|9!1tHInI`U>MqqREoQ2DbV>^}viywhUL7jj^V8v;DB(f}7AF296Y%pUIe2vS3 z8qZE@Y-)Yv;lV@|CR&ZKK2jr_J8WEptFU=+E^UtAuUOI)**PK0!!&`cW3WZAv<&7w zMyVX;J%YI_o$-Wm*gR*!@&-m&D3aa00EPagfh_KX-unL>8^f7X+&&S>bDlf(s za%va(oaD0u2btiNB9Wy-uHMQec}aU651l8pm9uQwwV}e?!ebij`30+9EUSUiyr8Xc zF_mPmFIcJ4ogf)9S(8`zMnfleLHfIRzJ$Is;x)0ZSeJdDkP&Z?>$QOquk~PWR-Sig zo_8*ccwOQ!$*oGK5%2vv8u3=9(}>qtH>2xPFN=)#TAu;Nr3+@{cK15Ry`sV1<1*E? z+#Y8RBw_Q|e*v9uGb$M1Tj~Pt{_roiBAPFX12()C8SSjqBW9#P+ zh#xT3@@GEQKF13#D_*i>`xmND`42z-_f2rU9iQYj@QGcV*>Pps#gR+GmwKzP((X&U zoK8-2Yv(#3E!&RBXQDg2I*-`q@39moF# z`K>TMqx8RseoeGlzl8L6MVoa?pwrMzqP}b*=tD#s|7xJ=w~Faz{2sLNuLhc*h?Fl@ z`Y6%-Vx;^OBE}782p1^b*x`^b)t_4GdZfD~f4}e{>DeN*1v^A|DfwdUn;ypc(u^3`)d$=vyej{)b~F^j(w1RO86V0xnIEFA^K(E zF5%yWjl%bZp4uJV1yOHXVF#hHXM=x~=u|tHxqpyeq;zxNfF3WJqbbxoOUOYL(o2QL zehqr9=#9c#gd9_$ytQ9HEBcp0^L+mF?MD9v-UmNhb|b#eY_Q+D{Mtq{O@wWTrDIUg z2Aft41|G#up%PYM+!Rng_Mo`~znEb=%I`y^2X7ycyxXpv8a}4w)`$x&N z3y~JoH!z@&?+vSO9pXWK1E7!39UE+v11Uj$!=aDI$NGJk-RN~>qCMCK>f`oZVZIW| z@V@0au))f00mIgTX*bYy0qdAZ?QWt2mS=Pdamso(Q~BqnM)5SxOUhzg`apfD4b3K#(SMVZ>x$-Wv zUFVXolpI;oez)hf-qyYJpMP@~yC?n+_U9qzOF&wbcK?9(q}@Mo9-WH&(Z40b?g|`B z#xzh|fR`HJ+iwDRFe^jJm==J+Cu1TN?~&|)6$OWj+i0n|m3Wm9 zZx2X>K8V4JLfC}OH7+I5x9%i;)#RQPr>-5&xBT8exOsFRX81gz8~BA+YLHWDlX4bzH1GEh$8nCrb(o z@D-g-*o=)*fJEqt7%e75n-dn3)gbv6g#m+b2R0Zs!ii&L7Vu242ME<>@zxpOhMYBH zh}C|)%m`u^kgemnkZc)o8aC)4m_XS=Y$&UmSmnp7h%-%m%@}|Q0ol4jaC}`g`{~L+ z9se2bR>NJV21>FPmRn`G zz+Q#6#N#I9^yK~{t-t!OSZU?E zufnnKx3mhUFuAk}PjU_Gb7_@1uMu;Oy;EE-CvKKj&B*f(k9+69DsxQSJ9GoBG)Kn0 z#UsnH_3&Ed_2}be=Xvw;yv)4OJ^FcNdEQgbj4|DFr+Ddc?*{yBX&1(bT<1gAP$d^d zeKJd-5{8SQ@nqPT@$n%A1#v8n%PTO;v*NwxOs!sU($sl1b<-F1nsYqpx|s{+OaaaS ze@6Af{=I6edrd1CK+f!Wlk0k6>D@^)C)Z4i_c~|Vd6R2q^y(AuRn`~d{B!5^s;#S; zH5DSWXHBl1HTN72{x6t2wGIXLs-13-27zh#Yfq3)H4q$d6<1>Dz8RCz$F&cAI!n6} zM<657hq+yBvGLEpK}_>#kt4&P^^2~8QXCr|d_B~h$+*soCe4})lgiww( z<$W{ycqgq*babmw>sZ@xd-o9cPZnL_iC2zs_?j@>YQv2c~}O5r-;twQz!D1X23 z5#ck!UkZOOd_nlKaHnv$@Li#Y=0d$@+#1+R^kCsA;RN9{VnlWrpc$8Lg>?r?UqJ*- zC-2nzb03nqNHjmwn7&Q)bE5w&`gPH7iZaxN2CuEJwbSu(5?fj6@8KLQsFm+-x8+!HMI7S zA1nQ7;V*<7>tOx3<68CW>@KXxH^|{F?kjg=U=}XtT}_c%1lsg~tn1*8`m@ zewolbzdyuIW7|`Ew$H3jYJCr19$&8S!6`KXP1}THvB#@4(ZlHHvlDwO*ApDZdF>b| z_kfA{JKF={yVT}k%4^upK|8a-rWJ#MN3>I@gx_8W=Xtv>vDs*Ol5})2hG-mK#IoVGZP1F3aO_5c#gRc@T9jLwk)gqF~eJfez|h zhepfy3iW-Mo#sxpf2m89bzzFWI|BOn-m>*$U&GdcIv$3;wE>2WUW>F~{T_lo9v|z6 zQ=ZUw3*yX^kBwm@GI2$M4NT+qU7?lRfJ*Tk*kI+hfWf#8*?14zJmm2>7#_kFtl!qa z@wP>pJ>H)oj_OncOeloDL6!j8ve*|4RNL7GeSc_1j`C=3iBQk_fq5u6w%lyQS+}6R zO*m=Y61kG)!cKz^@?X6iO?Evz;UEH;^gO3NFwXV2U13@>lY z7zHninV0Ny&na(=4PLp=9r}1_bZ_YGvYoGVkLV+Z)N9yzqsS0 zC8d$Qp?Ax6zOracV|>)Aof#QFf8Dv;T@M|1yBoZ{q4$PC_U%LOhU__|C}n6m=(3%= z>ZcciDjCz*seTAFtlk+8kJuY(oHVrKqg_hx9h&jzn9{t`F^%m9d+WzEcAV{==v{Ny z_UQVo((pHra1Sd9uZ?{3u#(8ywDs+FdufMm3xDfRjqP`g$DgREb;Z7@>zZ<2r1ss^ zz6v$yQtF;O2EH11!d}$M{GW<8qxW8Q9^H(K?_dA0+ZvIKi*RyR+E!#vw7-L~Xc!s* zmiIgW28=~~D|&HACjLY9JQ6%Mijeiy*oQKvJ5)4>{BX1fGI)Pup%a#q6N{Kx7qRFZ zBqU8pRTMj%xqY{d+e!Ow8@a09%b&ususPnqI-@6iB1jZqUgmD>RTdEok2TEj*C3Mk zlNGyfY5zvL6J^hAd6f76vgkPMmqyuluaELBTM~T}d)Sw_Oc(Ygeuc!wU>CmJy^Zk~ zAR7L<%R9VPn0>(3vG5&{ix5fAV$xla^+=+vX%aQuEi#neh2*A4DTK^=q?ajuPlTT< z8SZ@KaR<8g7&)gGPH(sJPxg-aF7s78K z=Ku5RWEPratNzvY(uj! zxZ#$t8ricj#NkJ?`clYn=b{Qo>Fc<|CDc#RoPiWF+#Grx@S|D9i72~xIJ=xN)|M4( z73ThPY=Zjm|79r^pYlob6plP#oZ@?c#wq(gKs=Y%ojyT4XX%qBG7tn4h-^S60YZr| z05L5DqKt2;VvL{8NFXrd{w6HGdts>}3^viz34ims1ZE33cmv0n7qFJ+Xhi!U6>E8x zH!xz0u57Rsg%mT`;1@QQU~4nB!6x!$5XT1pZUnzJr!^v)2HhcNt!Y+#m%*}>EMP1I zOkjRL)A&H9QEb(Irn8fo#w(9=93tJZp-=*k0wtB(Wa{K$;t*Zmlwyjnv`3o;r-zkY z+{E!3Ih)z)j-b;8OAPh<^&9&fqAy`fbJmum6?AG~{55Q-1%cH^ElP+VV1rnB8Z0v= zZYppZkBXnGSTD7_ff3dSMn6cKU@M4gZ&}Q89JVY}r_Py=E=Mo7VNq2Zt~|>|%MzfPV0i zS37gojDW+s=2rgLl{!hUe2Dd>f4+W>7Df2}_7c{Ku_MkLI%)KXv7?K|4H@qE|J6!Z z4eXhecf_&t7Sv3g-g~sM36#p*Q`>t#@w}-?&v`*pR-n)^!^V#)s>C5-SUsMwdvU=| zLa2%5$6VA>I)LL^kIH%H;Rc;oH`bhyk82NVQv3ndHGM2Ee~Cj%t`{<1=!{8d{(!!? zwmOGRY%RD_PO>^YH~-bPK+{r;cS*ms&ssda?)>WM$p-NWt{>8}H+qa@d-{+yM4z&$ zitqiPovLTx@y@4vv;O>wF^lSY^M9_-wB5>|a%c}I`4%JR)3950L^=Gf_XEH5lc6Q+ zhmL?aU%0#uP3>0pIf(~fisj0tu%t9>A7>5G3CxeNic^H6gcZWGg;Ryq!Ue($g_j9A z`HlK+6y7e}Ed2jkLv$j0E~wXO!s)^~AwSib&M_}jec*aV>W713`C-;&Y5`cTnFi9SX&$22M5U-T)W&lJt6s!U%l z`f}lQ!Um<|a+u@e9mC_~GlIyrOk}$znjRvstLSdR0%3n4zoRKXQfS%_X!iKYH?~ip zX-B|w#h)u&AzUfELilyz^}-v3-xl5~G=nM7`;h2Ig+CTPBittBrvU3^Y^1<9MDG!@ z|IGCFg_*owpxX)$6CNqd6|z%LId<%c{GKG5_6Iyg^cZ2KaDwnGp=qCxpC!6lXxc0I z{CuZgyXNOA(N_y^65b}Xwk*5mhckki|A#`m=I2?_#lLD4BNrv*pFeJV7Y}>3HY`*FkEr~6L=3jXe$Md%~FCm9-U4hHip09 zI&+>pMg#ui|6J#Bw8mzSV-?~y52DUU=u7rxl4&cE7SvY~(8u?N)yHRZ zP~Vx*$LER-Hp*vtP~SM{gXn%Xq}#w_o_uT!xv2col=H{*`>+M&zK!>2b2g+ON)BK` zc4qQglfmO)cm!LpeiH-7%O0LR-lq{qb*cd-ltW)jY)G@Ak7N8c*z-aRG)reS^zl7% zFdNbqw9{Tz8PMv34XIy#(u8!-wLZrW>NBvVHl%^P>6@`3MP?40dChCt?ybeA!$aI- zd$L0JHimX&?(;sFv~xqNf%FfT7TXhxJX5-5kDJlq^^Oj_#ie!JoV9o5zOtQNMq-l0 z?(}^wEIhd-g|NK1`&#dH-pX=dg?VemX78;m&&ArI^_xfT4ef;$>Fw5UWwj2!l@%`B z*}8DZ&hU^37KBBh{})*I^RrfEB~O-g-tA>(Zo6YoCrbAhZsIeKL?{^UZ|pBya_u;sjCz^ocd+H%Ul^w`hze1CEU%AAA&6xgxy6+8WlZ!z zBQqRK{$0(NsZYw7DA&6>?Pp>)88N@SC6wX8`nnAH0p$)^gPkvPBV{__iR#N3vbT8< zw+gR@Y=_UkwJNZF3NJ7&me7lq6S7*`9yW#Z%e1&0^S3Lf8MeNM*Ol*N6duI*x!H+ zD{p*8vU3q}PN{J^HQ4H-!doH2{7SJIsN-nEUS=2b_*I-h=z|Ua*&_HAtanWXS!x6x z*kCeZ^bR9-Ho>p&GPeP76Bc35ZjfspOommTV2v|0b_1{#VvE6aRCvsMr?Xl9Gzy$_ z%YjX&fedy+GMt6gX-1A*a##$CAh4!Q1;8+2F!qJmGNFVn=H{EdU)FH&bDHw0n{1Yv z!)he6*vhc1cBss?#$}!KF%f(dY>_co#F7VAjZNrl`0xZN)Y#jWf_o2WFNbn>dOO~Ez;n+;53Uh+7!6fA@1&4!JL@R;ww z#?uiD!Fpeax7HWg&}khTehforTdahOA0-=S>$+RYkA|_bcYd#4uu0~3?$yUz9MA8W zU(nl|I>YOK##!C^PVdfTa`)m_W51FA!j>5)jhnm!{y}U%TV=v<*3?RAt(-xBgf01v zSYrKAGiJw0li)?LAZcv+WUP-VrctZ4jr33-lS>rOIL;m zbYdn-A<}ZI|GGme<;0_$Ls;fjGOq8hjSlb1*0!2%Z@! zOENq&q8Fqyk_2MS`8@0hes)5N9rR@QC^k!;|K@C#&3Qev?UyB`Vf#2X%Yn>~Z~LLb zkwSg~kk3v6ah7ndaEb6j;bp>a2yYbLF5E2qZ?#z(n-9*@ui4qbfaf2GppDH3w6XO7 z&H5$qL+I%--K?VnKS%TtM9^{3W*r@9vu+9UrAn_7R!eTNXltvyO!QSsrxP0LHS3h1 z=XTMXl+H1Hrhi}bPeeaOOn01LiT@|@UlMJmO(UOKw*# z@w*HA5s}U@E2eX(h51H{Z${lfPZZ5D7RqsMFy%O6#rzxsBfUa2np+c_6Ypf&pm?tm z<3c_IN%s);7IN&F{F8(w!r{U*h5Rn2+;rhAVXg2y;U&V$g=>Y^3HdokJvR%@{Q~+y z(dNDZ{Ugy&37-}IPROxv>Ul}{SK;4;NdIo+pC?=*yi&*kU8b)S-YDd_CHapC zIYvqP&q9txk`Cb&Oyn>mF)rjdB!bEf@`G3C-pwT4Euuu%hHfi%9>RgM0_`J5kMrVLw<4k)9O6J*01=6M<9xS&G zo$7m4F3V#U;sR_zeaE(ToH%$k*tGdz1oh<}=HJ&m4rEE_TZOpIgQznMi4QTNVAEED z4(jVieMqFf3~aW394NMV5Ow|yeU}6nHhMkMg8E7hPY!EmBF*Z%4RPk-vxDJc6h?cd z4NT)kvrAC!uIYGCy!^tj^?Mp|RHqtX!d3{e-(myn z#E^q6P;F--^s%2{gOwxVwUY?%Ltjg5<~>oVUWr`EYXF=1fP#-;Gw(mB|A3a-%vZKE z1vXiCS^Yq0=FpjIU+d`HP<+C67v3>po!)a*tm`(ctqg_X@iM=(?Hk^??!d}7LdFAT z+c(_B@i)`Xt%QfnsyB~0H~wbSc*;EI%@xKQ=8!ki-~ls2kC=;J+&;kVU(&B62c9ou z+m0@M$9TS^B)wj~3%hsqmg-?#!?+h~80Yc*ZK@b{*WkovL~#Xr+A|$ES@3(w=NHW1 z=GUKY%=9Qjkt>ngdypyC@8!iL!VxZ>^Il=RXqMqbe?*8y%s6W-%AQ=(&>aKi(b>8D zUb0`3@Ox=GCE@7#kby~i4w7Kfehtvn?`1tWPINbR*6-zD#Fs`npRPX2p6-$;-(Rq0 z)0ZS{**^po<8-nE_`UoN(gS`k-=<{J@8w340l$}=ZxBF9*rsPKHpM zzhmbPWt8_-=2#|_ny&7eLf`G>u-Qng0pGV+kI2L^Yyg=(fwCh>=DbfW#uMd>VCL`@ z?|Y(rn`D2I#uH@~gnc7*1D?E~{aH|Ykbps=3 z`b1R&-_)!wRs|+ij0eL6zUNk83uq;)Ben<@x?x=&qkbW$5xQf;Q4#zi;31JkoQsV) zU{qz4V>z&25!h|@9C%1%WCgZ>U5n&O!z#z#1Qs$9TL?!&9An~Z%7~RdzY1VNK(>yu zN}r}=Q*Y^76iA=hOnQAFop+@PR)1^M1->cYDeTtSoLtt$_e@C+DV_p&p)51musb2i z8)bRLN#1D7D^2pK#)MAThfFN&GqArr5%VKK+B{>wxU2nquVHvr{ijB-pE|CW9rxzL z3Wl|NTCr@e+BPnz+o2W5+BIODs?OgqG6T^>ZJ(Y_6%{ivDw2V7N-ulxx+JE7zK^KhcGB zwRVkIvam14GPxExfBZz8JqG`}b&yqaAHh22->+b-{IZ@xZf{&()&L zjFsNSp-bFQVe2P;@_43Gl>ViiO6TbR(oV&5-;ABgZ!gWQt>dE!Ar0Hdu~YF~&GG#r zA>YZQ*~KTapF^A^WcPqHzfOpYh3wFgzEa5V7Sgv0Hwo_-J|cWZ_)FpMg)azS7VZ@8 z7QQR=@a|(dorS%G^lCvq9jy?}_&Lyyo6jU4<2JL27!+72xfPWU>uzu*2DODgx=nw&DtZ-CyO3J1kFzu%2$e> zC>p2592a{}JiZJe`_rV^qa~Vt7LdI>(#Hu03i%~U{t)3XVTF*LXr@mTRtal_3x!LC z7YMHqeqG3KMC!Roc&G3lq4`orx@j-KAB+D};j=>1evtm6=vRdNw50wE*EkNG5OX`^L5b;!kdNG zPV-&Sj|zV*H0=+1ej)nL!oLW23I8tqw~%WHSbjU9S=$NPv`1h!@%aT!xs!yZ9fGb9 z&2MO?PZyf!(}&m}{;$U0`FQ-idp`UujlZwP{bPguG8+yCnolBZMXVOr-3FUh0tOyc zPN5RE;rW#fIeu)TsbCPmA159Mw0RJ9XoKT@YlBUj0fvn;jeZXoG*j*rqy@{}k(K-} zRt#yj+|=>+jcBxSq}yQ2N*#Z%ZkrrGZVh3pkMBa8hdQ=GAD_QA*tC^k1najVppVaO ztM3NHZ64~_27OBd3>&>3X+eE!ppVDN^PGvz>bnhb=E=v#Fd9O%bK1Z(ZmHw%Ytmab z{?3<9KdTJLavAsx57uv8;CK;D)Gu}Xy&v?oWc>XOC~3*~do}d6Wc+<2+G#IVT*01a zjK2@;^AX112lnkN@q_=L>KVS6aa;K3FX{I%v-IotMpi}F_FYx9D*d`EiVJt&wIXNL zoxs~x%wN@}G`zOLPz@`-8T)Nx_};Re7soP6BWu0gp1a=3DotBkG_$I6edjjUoIdlS z%{OgEhhweVx0~B~%^D|kL&i5kyS%nsa~50UUES)MjP+%^oy^uZc)Koa?XAZ@t)g&f z*W=u6q4iGggwlsg!ND(34=?Rv{3^P;Vi|2_X6IKl#tO^f>+mQ0(z;YMcC5t21kc zdb_SlziWGTY5EOqZd_kl(Yf^vp_0(L%Qi-`YPG8%#r1dqS>msFHOT6oxZmYM) zO0$fAM5haWkNPdtrQeoUX=f1k(IaqA_>Yyadk!v21}6t_z$rDr$YA<$Zp^$!Ly_Yl z;h8Q$j}eIC=UU}ZAFTr;)ro8pFXY zi*kO`(&&BI*GCUW;*uz5P{0Dw69g;}zXrt2!{ly9M30c2M7BG@ZP{f1QSf;q5P>wKxU7m8Y7xHl~p~BXXH5wPR@7S(|Nud z4F#`^hB_24)U3&{l^E)HL%~#HsF9?m*nDRh%I2#!l+AaMp=`eELE+-I!-3@R!nO)? z|2cNd82DA#hc0ys7(#H8@hsTi7vn^n3AkL`VH~|zf#5-GQUbF#TLWzxjwnI$W5v$!C3+h zDb^nqu&0*wf-VzS@8G$e2pZ)P#vm6i-yO@X3PZH;T=1g8W)vp!T)-(Ofw}3jGFg@6 z@70ze{b*CuGYKr!a~9VJj^Ip4m|)LpYi9#`ihj5e@Bp?nr}CQc*lWT(kvwgdW}KIp z_Y9F&*QPPf`-W*mzwEUe6Nerw2zgCti+wIOv?GFl#n+BX9@^SbMh-iQ`soWpM9Sc7 z-kZ2Ha7pkm^#g_ZqQhk%=<0-;w)_xPuq~oZhMKm?gd(0$o+(pOPaRe(7JF@I8CG;6 z$%JI>@*8aJ=)p93B>4$ht25-}8yjd_sKuJm!vPczEFY!T!08!|R45%a3Rq<%D+qin zkgcjXXC{&pj~hIBZ0c=@JcJDo5&|zNj=u_E0+V^)gI7mX`ZSY>9UlSYVHv7rCGRn| zR1>IWD>htLLu1FAl#+oz*V*y|&ooNBY@`wyJAw4CM;I|b@Q{NcNIz~)M=$QbSpS@x zvtV}J$M+Qa6U@2r&X0SC#=V+&XP6Pu$#l~Or+M>7df9`$*=O8+>WGuPqc?f2;&2w~ z9X8`f!}|5>g`d!k?3RC$Hz&{Q81Icg)p=d&7M*%TZg($Za6#`*U6_7E*CYCR+nl^3 zz3(iKW2s%dQ|~TE_R2l-D@Tx^~?8#ks#a}zzuCey^bthFER|7SR@K1{9*O=w728=2rYS)U2- zAVn}xqeA9_C4`#T@bhUB(yah)lc)Kg(}m&EC_KYy$g$c-f*C z$(}InSUJK&gqVVC)**EzLa3+seT63oPZ5q1^2?q2&K6D=o+~_0$Q~8tRtwh(*`Fo< zcHslU?+YIj{!F+{_>%B7;hRGCd8u!&5StowM0g1i z-{qGJuOedTW1Z4(5^dJ^K)7a`rOvjhEt(v95+^c$l8A^G=3 zw?==9<(l<9z|N#0f1LQeM4RHtiPJK{P)_$nPRNR+umBCp<}5Bs3o=$j1*9<{vMdAeqdqS=Yft^F=$*nn!gqw` z{(v5SkMQ`S!gQgzU%=-mFVlMnjolyg0MRE1IXp?ZF~ZY?#-}d$sdm@7O6O#8=362( z>sUcwD*6f`$2%!^lkhI#cZA;)J}fjt*^vK06J@tJJo!@~AL zj@wdC>i_o4dRFi`5uWLV!V+P*aGcPrYlU2u=vl%!LXO!|51qsj*9f@|g|uCtw^1|) zb;*A~xJ9^C_%q?pg&g&z{ENcZgntvhEo>Beco0!OD$EkL6LQ#?>Gb1CwCnwj7j4)3 zRf;}Sc(!nc@LVAWo2h4+@DkzW!nMNdgdB~g{2jvY3hx(wU-+o-3E@*hj#)GR3&K}~ zuM0Vf&Gh$$9^#~WBPu8CbnVHcqEu`6!w(EEnz}G zp#`Qf9E%NI$b-uDgiLC=i>wl)p{bcL4Eq4v*#v^?>20uSL%?9yq0lC@!+Hi(p7wP# z6Qhsy8jH>5LDYE~1-;IQf=!za+Qymm0F>NGMG7`;GU#BrtI%1_M}uU!ERV-QtixvW zAnKe2dwrY{1)Ek4I;igT?Cf@>m>}TRj>wk>(U}cKeaBn)Vi0nYSnITb%9Dj ztp@0)-}9V#&b>p#inU)iJ-^KTpZDx@X33m$-g&1-ZqJ;rqQ}suXHIWl?cvMy7i==y@)e_qil7K(LUbpv!7-dizr)#qU3E$!;ufZ*=PFHqpE$ zE5bVqBYAr>=3ic#w>N!$`-bK-e%K~&Z|c;a7ItiCUc93|yz#0cZ)2OH`}R0ZQ)#Cc zC8UM(_J)qWrqErHS+uu)dr>-U1*dF0df)25&K~^p`om)zqe)jC5ewD2c&~ALLwH+g z&u4vqhjt$x^J*hVcj><2?VR>*zf?RDt=p2kcH@>*w7&%VF?z?-sZ*TP+Q*!5?Fc8S zc8n9LP2MTQCfbvG_00Z@-JW|h9>BiUJhA^W7kf6p=ic-OZZG^fq@H_I5qh!vW_>I+wrD)| z^YptS8*XUVvZyC|D73ui-q4h5kn%$Jllr8M?TQ{t8?6fSOJ8mbY52JpU! z7d)FH1crt=GW6QeKZ%t?;o?r@lct6DEMv0!H?xDE*;?um#BSGmVPNUDL~Nj?tI%ag0{yDS;XCeXIXfWIc0 zb)J*F7zv?m!CI~DV%{>svRzz<42{1-Dci+YBWJ*NF%?Dvwu?Dm60}`B9CgNR7k`SZ z?jX}hBs1UnAl)uV)Pyq#ZV|nu2y4Ned&PTfc@{eIkBLlsY<~DP=%xMh^M~1bao#7T=po_tN zqRItHTQtzcc-eS(o-%?Aqhn13J-APr2zIb_yaQq3lbC9}(+KlU0lu|J@)GeiG3a9% zfyGy(H4~3mL+1RWOMJ`g9Tt>AvU~3yR?p;N?)j~qi2OLRM*kN79-i2bvMJa85^<_ zTm0})9X*`?M51sVJ)G=;5UR5~6IgNPKJZ{t#e`gg&2=Uc-^XU!L(f&dP*X?G^}bMD zN6)RkaAw`3@NCD1?8FOxc({(9-}^$S?qsxu$AyQ?9v`fBb6?nQc!~Rsx0Kk14R1Uz ztE1-$KRmG?iM)Hs4Wf(*n5 z##{U;z?uSU{B04`*Tjw`xL??(p|(ANE@nz~afs}Xq|lytV7X0;wucaI91~|_!(~bo z8*eG$U(gAqhCs2V_!^|~pzTx=C>BA!V4ePz_W^SnLk<9%%p$F$0rW?I0PCuA4RS(wND`~ zz!q|{jdEK{+9(JAc243MF6HA~Rk_$9G`OCuFm-ywkJ1R@vT+EPNTZo!2T0&(v*+jjJt-*M7OIXz(9xvMuf+iRcIwWl{N+v`4j-Vmsur1oDr@Td+) zGuQ$4df@Il-I)LZ5)EX;NA)6>`fGFnAr z|E+XZ_#fh+Ix7P+CWbySIxDenbyn>EP(G~j;XC0x3??|onHdji9{17nRdiN(EDoZx zau9RS_TJ4Q5B{G=zrN1OK$e3!>X^uN0?HG`GVzOaR?b&EpI4dh8u1p9&ua`fHf=%9 zKU1z3Ulz@pKf>RZ+#r4`hWY%0_*5}dY%R7G&6+>b^^n|8950rM)gqt$SngW!8WIyE zo5UMQOsxM%@%Ku8Q1Z_uKP#CJa!ha5=20$ZktlN>fZVHe=A|siJoc==ISJXk;|#gI zWV7~;a88{u-N_0cB>9Wpxi@R?NPoK0FBQ!iJIdokBJ10za&8tkEBqnxF@--T`InM^ zEBSTFJ0)}WknP+j`7;vz>0#1{G`cQ`=Zib6->j`e?jRY5+oa>OEz@)QgXDEawh=i} zq1;LABIb%Gh$oAMVyQS@Y^(!QuJHLH#|x}yiMUd{NaTbg znfR=@UHrB98}Uu?9dVEN7cs)~3FTT{7;{}fZmIAi#iPZ};_+fnv6nbl941Z@za!EP zAnRKoo-Lj$ULt;1yh;3_xLMpPa*CbhJuGtML-}d(m!i3@5N@s~@I8g^7R~jA@c)(U z>bx_$UXmx5lM)wG`&RPuwOxEsyQI|&d^wtM+E@u?EW@GK%m*q zRFropIzwwvUJeT3c-q#>_53P2L%I3A`t&kNLtm~lbSnoExGZ?h z(IQzJrFb_$qU)WGP1il@cL_LGO!L+`sr$VT zqw7ZRNNt`{o3t*naay?teV_ZG^W^OQ@rvl4%*cdTe$44Gt|7B+Ryp=2Z(Kuqx3LXr zPU(IpXKcff*fKKb6pd?$b}QZQg~m06`;Xc0c5ny4PJHCe`lJnOic&VTE(&cZy=!0$ z7GBcw%ZvAi^W47Eiax22B6f=NRD+ZAN|B5Gc55H3&)oQG@~JO2yUOj?VuKqiEvMc2 z(9ZKhEn{fzag+VMNxv?J{fW|g4fhB81v547x^x`(P%ZYRMN)0*{?LbE9Kq1lld(^s zoo>csJ5!P(8>f`}`*+gzj6>SH^vt5av3K7u3S;lw4SnyLh9jS2ddT0mDbBElaKAL{ zRa)&&>z%AtMlY%b&gy2dPv0IBd+VJsu_~h><;UPG^_ti6h%W!A^^ebi-TUiskN5u# zNoR_KKu&}-cF>ZVgM?5=GQ*`%WHeG3)hN%}qT&35*BMv4H-HOoQjPLfGU31lCM5Z7 zmJy9Sgc_iQRRJksi)Ib;|A0ZhEt;Rv+pU<{Ipq<~nZ+`qVe`&HIC&-|H@Xc`2yBXa zp&hja1^c#WB2~uBOVSxg<|OAJdU-NOBFmB+kmKBBe&MPnxhEpeN#=+K+EGOiBCo%?Mnu5hGvLp&eluwqnr_lKO~oQyj&1w?nq()RQ`eQU=1C8;z^cZ6N*# zVzV3;Y!>@Z#(%(O11XlQK`GSxZ-(aq=vYytT7Pq+CuMZ3-k=CQDI+YzqeycYsWAfe z;hP(!DI@*}Vzbj&j1kX8lbhYgO2($639C>+GtOqYec?x^tVu=Du~G&3&HDz24@&g}H6YU~cBz&fGujjYDJVzTf74&gOoNxot}2K8*Xl z9^19p+?MWHHv5M*dlIy;&5j=dPb-@=n4MW~WA;|=7G!V3elvZRZLW_=ae-eT6y?y1OXy6YC^ z;(@;3W`Ca9ZPH+N>YUc_R2%EwY_spO+1+$?S1Wj0*`&ej%({x%$GNW`(C{ucYXP&` zWWlWKkd3oa?swd_xRet6Jl1ACm04}FU{+@1%UkX=cftW#&#_rAXI7g`Sr6ywaSyWb z?(EKWO;=sd9PFyQZPurl)h1I`s3cyEijLr|HYLp8e>00v9+e6s$EFW!k(K7+v9}4n zh!JJbFbEm>jvr5;4nG2IAevGe&*i8A-e34p;EK_2OqugAR zePY@qQ)kPFlWX)BTUouv*7*p*^LHR+g!N!@hT(??!_m8kp!Ue&B(xhF%(#*_P^vwW zAJ_LHUcQFy3(l->-$v)-}uNVZMMJ z7evdnPU#Ww`nks2TvT*RWE#)JbeY)L23VB|lT4oEpe;!q!d&or02>r~i0#-s=d7AK zdNObVB0L809tK@Ig10TtS-hl@y}tUBthCt}0pPbkw!(ob1p zlHrFbyJ17|#7LyT-QlX`iH?N8Gn)BK3{n}#KKm;+1g=?5PY64oOd%<^Z5@TE-x~xk zc9W1tkd>f5;#q76-@2Th?buMjR#N~4|DcWuv}Mh59#po+k48eO8_Ga~RdiG&MA*)Rx6#O$k*aw@<@!+nRTy`H( z6Ix{4_4ws!oXR7)K9u^|XGfURdOO09jQAxsC?hYgqvyAN_)1jAb35J7*0Ynz$Pn{b z=S(1;z=nJH@;Z8WufaW>;LTVW^NuhX8S=JCP)hLnK#i`sBFux2Gl96#><9{^$G`nd zxCh=xv7xa9uNITlo(aJnv$xv=Ta7(3?j>YpE}U<>0soRIwy_Vu^9DBj59Dt0e-3V2 z750A*{7dXVANb38Ew94HG~}(me+kK!GtK9V9?PP6%MM}kmfVI7cL|oZ+T^M>~J-B%sxzM4Sy%@1^6b)frd0^K(s#TmB^zkVxqzPvq>3Y0lxxF2m800i=8-5?hhJPwX#?Llveh}{@#%;r|A2iMn@I@W@&j-cN zV`lPG#WPaW!M7O`LGcw$1 zerRk^aMyW{-F9GUHYm9347(p&$XQlHG7J3iU2ls{LJ(Zo{PKtYa)T}?Pcr!@umiu6 z#=Wn;jo(3wD$9BdTR40E;yGo`?7mYgDi@tvIdehQ+M;i83Q-OFaqnm2WxU)JP%KG4wC+?iDY>yEP*RL!fJe(LPm+2!on&wVHkor{lA&4CUu5}?zi+FT9^vllp% z%;E5!hHm~K@9eBj$9T=MymEX6u0^)DFsuF1Uh^!YnxCCC$x9vH@pP|QwEy$%k8afZr{k=apD;Im;gXdFccwvvDPwHL} zT@T}rNd98_8YI8B>JH|8BE3I^+pAW3hp&YAfDs?Bns`jRK__{oSzfQ<1GYpjI_c;h zUdlwoUy;5N$s7E<7n#I6ZQj48u^AUsBk!?ZX@9TJ@J`;QWy8JO@ZLc|w)gt7Z10Lt zwzqfLT&&}}Ze>{&3}sri?SZl^d4TS>?dz{*Z*uzdF{4I}9QZlAlW{d_tJoj-_}n)| z$KUGjANmUx_84T(cK$m}RpQz-1}6_t$d7;J?hvh2CY1ZJM$ps2Pac2r{|UPO@zeT% z*EWptN?*Spn*Kc#wp#y|djG%L7G-|m9})i#eSu#85^tVs6#GY*U+3T29_8VIeG2~H zW$-`49wq-Gok4hJzxX=7Pe61IPYp8o)r13i_I zBA=TmPZMX0d|qYvS>iHrwYXN~AdKm55x0sz6(1F!61R(Qi0_KK#E-;%B0G=eCy7nO z!^M1YkXS0t5*La$iX2k0ya!1jKo@;u{DYKS0tN9VaOcPGyZ)N@+Xr2Dme*H zB8*QX5#LI32eGGkqT;#Q$@DSF#gfNKro9Em8(Vpxv5^Pz`I+HYiq|MT*UcGj^n+3V z-IC4oEM(3rF#ai}|CMA;(6ha7NPb819?2ZyF&-zo$sZvho27D;*HW_4|AlPy_YrUO zej)c)zQH8=dzd&;;WNe4#6^m)63-Ve7QauTT=QtT*pA+bN*#goK=VhM?Slf~I$x#H)GjrDeoO*`Z>`nli@N_UI+ zKccm1x1BQ5y(0cz@mwWm`#umq7Sj;M^jQ2cJe6?%UYbLfAJBfWs zq%(T4;82B+6sL+a#Q7xBEf$xF=PUkV5m$(*7u{@_F7kf|+tWrgV@SxIBzF;W#S_HF z@2hZfhUJ_hP8Vm3r-=*2<>L7wCw7?sa`6iBYVmsUX7O(EK5?7)xcH3t3$gM0DmxTz z?(3-cW69>e4%yt#!BpNq!DeDBv7N}-X2y3ByNl+&kMQ1-2a5K+6;3=eU$NNu{gpWi zFBcozaII1J3h`o*OV2EKo%nt62jX=ir+ApoOooDYOTJHhRD42wPOKL>HN*Vo{}1?O z@4M_*`p-o3{|Nc)doNVSVR^@h$BWtG2_hAdn65x95=V%m#qWqS#YG~&vB>;O#bx4! z;w2&{Fqv+>$XOi9=KmPDRr0;!!=in^`dZj=1D z_?%cT+W)t2Oa8O?p~(3vmLC?)|6%x>N^boAOJf_d_C1(qRa>V#7@mR5|m@oDg2aChRVsWha9dV|3n#lJRSP!)Y$@9du;uYdG;wJHj z;+^7F@m}$1@p)3@wbV0iFb<+ijRm-iO-2Iim!;Ti+>c~7x#!%*kpYX zF3=q#wW{*iKmFA;w14q;vBJDq>3o>*NCtaYUEYo_rzLpgUGL# zF&}kG$t~jj;zQ!k#HU0moihFJ#J9wE#ogjZ;$KC+T)})%FjEMI`IbaHt|Oy^=g^_5%Ed!S@A{j74bFk zE%AMEkGN0#RHSY$%Wol4MVE4Ck(#=c`-s%Wr94ukQZD7`B6V{qSBliXrF@}CHC)P9 ziPXlWe7i`ETgnfKRJ^5JFH--O@>?QRY$<;tQqz`lsz`-f%I!p|(^Bp!QmdBoFp)a5 zlxf$Qr0OhLAyP4x@&zLGV<}%LQU#Xs?IJZ{DL*7qA(rwl#aG4Oi`0Q-{0AacVJSyM zYQ$1LT%^7$QKW7w<E0_Aac z*w5^jYq8lp@H%5r9>=cM*|-e|2$r`QwhPOo0Qva(>;-4Dq6@^>_IP$%5y-qBncyKHm8s%vBSH#h}R*mmy}3$4GZaOA4+ zI&V!9th@hgMeEoxv2ZMJ)$MpY^Pv@0t7^gfSDfmEc6-s$JJzK?*U)lP^3DsqQ}EvA zv!RTg$tnGc@TO!M-sHo3i5p^7u?O%b;!)da>3n7k*3C!0e7M^O?@NZ-u5fk^3B$tq z)ZdMPUGu3O8V)NC?@ab!*?eSH!(j#Q*1LF*F=J<{cP~naZZC}G#&Y+D(%imnR`i4I z_Gv{KdqNq{4}K1I-hYQyOeqiJ&C5uw({=WCZ$rwJXd&K|T$hrxAyhjBYLQ-T@;c~h z;!Vk?LTh44cuQ}{_G@FmYslOdt$Vd0JcG5-Qv6W1HJrRB>Wso$llid9?w*vhH`)#M z+Qa#KGuk?cN!uUF%h{XJEg$ySUE@!OKeeqNp4yFJuh++lhM2b|`^0+f4UKU7vh`uS zfB8whx8bm&-?VsWkDEbj>N?hh zBy+0DGxIzHc$dQ~hV_W{X45}#i3v$IW>TV&70ee+<|0nw6+^1^_y*XsPDE;NF6EBH z=)IiYZhUdk>q~FW0u~ny|1Zl4Cl^w3qh<~)8s-u$-Zrd8od33=wZ&eFWKQz2h+dvt ziQi?(oXt8nxe5F=$^6fDPV#un{SJKbYFP6HNBFt#`t;}Mb4f1#@9DcoJ-MI zU;hh4V~gD=&ex-LOF>=(Y2W$dw+vDdkp(bOor&ZW4Q;tfn| z#12UiSD|KK97XZFagi$wczG!W0h#9`^MA9I^)Gmni2s23!Vq~Yx&ZG5Z8x4fATWZw z$41a|zao$!?)Z2CooHgN@lGaQ#zynUAqq>i2$zEDv&7oG}iNJK0(;b$(Zqi4Mz zZW|QPxMh2|cTh47lhxP|O)SL*W%#9a^sMlOnmT$e^@Zv>de->DnRU0pvlSb%6ZaZ# z3BkuFyfC$_j-E&TaNB3l>SxR6n-)$W&NSWiv3oRx-z=lE}FokYKTI@c-Y$hD>#xY+uHuMz1yy!@C^7>Vrfe_y@t{Ph! zEVt7tx_JYG0X5j51Wm9XalGC5Dd8v9>l#}Td%sCsO86%SUf1{(0{bt;lx)hSxB?qq zr6VrF<~hrj*U{s5^Rz_QPD6ge* zB61Tpyl+>;w~**(OVEL~K}=?A3>1k%jO&cl-As;g#0+eBOVF*G4^O2DFC}>5n>SKy zzMB!iDguWRUK_gvo{O;IR3R?K26O9%JSaG+2%g7CfnVc}M{z?K}FbbFD8ny;l0fYa;hIpFcX4#`WR=EB9~2x=t`Ru z!8T=EctS>qU+(cIBAbS%u63`QntJm0D`|uOH|c$UT_fzz;la;alGWK;n(d`z9p$yj z_U2_l@%y-=y~DEbFzqHyOd8>pJ?R~Pe@E|8XRh}UG`N4}^r!jt;#_YN?5_XWnK(su z*P9K`^HL{znbF}(y=KF`W^24f+0YmtH8eN38~=KEGqb%u{RedIFa$=|y_=V3d9OOz zV76DW#`}v41?{V0b^Up0W?$|3me&)O)bnzuS3Cb7_SC`Ig1w0 znjf%<{tY#}o{n4pXK=0)s>w)i5}z~Ve+2$#IH-PhVMcLiyHU&@oYC=H-E8~c^I&$@ z`5!pbCNLJ`;sTynIIc)I&&+rR<(3YnGVyfaAB!*2&Hf_e>;6IsE=;&2Y+pw=yEpSg zXLhhST$~_I73YbZ3uF3o#FgSD;*}yFw3zM|af|q^4R-VkX2|yPSq%N-;W3VKl!V+| zvYAta++K1IF<`&veAu0x{D=$U$i#ZZ<2f)iF`&E z4(T6N_|Fy22`{$$CCRTz{-b201BdvJBpcl~$aGwL$oau3}&DWHBZV7fZx(A}2kWf1X$&R*UC|vp5FVaOFulUL0Z1GglyhDLjO5T=0HpJu@TzgZI;Ym=y45+g?~HI6 zhWY>yg5(`?B-Me|Mx@|`T13pp&WM4TwvcT46-E*BSxRpR-g zeYfOt$?L=oB8_me9^h~43JuWxxh2#6qjiPw2>h+C5IPnP-E7>~(G_~Y7J`y+85=he zzky(5daD)gdDhuDPQvr?u0Ro2;ec<(|B}{_ahjO&xRf>#o6Q5S`6k0}>5;Q>r$M%U zM!k+gsNZazjhl;rV7)^RgIN{m<+5JZ$NeB1>k3h4E(_t-+42@6AXr`-4Al5+%JNdN z+45GyZ}Y(G{0QYG#)FBtRfr3gcU+)6WQmt|E&Rdq9zx_F0|C~%p;39%iQ)dSeHqwn z`}|4ngs#vHh~)bk*0DTpcZ)fS7>J?kBKZlM?os(|)MlhBvI59@=^nxs+~0!0{vu1f zeZPPo&8Yy4`v{STqAOI4^8R2mfwo^roNjTK)8f!{g=XM9H3MnowB0B4%YuXiW($g0FHm#KIe$&Y=zIuyq~l5SMNKgc}30N z4=>H^S!?{AcU`=+^!?=15Hr~;>b&cs$<5zSI_<1o$DW4xrJZ(NbXw{Ak;%6edPR$O zbv~^VQlGi2^HRhweYt*W(d?MJVb)#V&XmxHe{xS-j~wBm+Cr9j7PgP--Cpnf$z2Nl zpqzWQY#!|GyeNDRw09!cj%^p(a8J?Jq8qmq7Ilhw>)Xa^w}fh)?wjh5izOSKohr09 zy!01FZ^sXD9%j#)XJgaFVVC2E^zY%PpFOfB!vBHpj+viuqg;c5!Vcf)@_1^7tWEIs z6v7enm>f=il~OcfUYLs}cSMm1)ttj1d3>eW>u3h0(eQ2v(7bsZ5zxFbZ<9sC!%Tr; z&ZERNZ(f6t#3y$r`7``1Pv(`fESYmK=O%NcRg=6D{&SLlf?sIf@Wp3n-rNc)`X~J0 zU|$EMgegGV=90(>$dq<8lU){Bioj3@Cyh!y;nfmDX|JQc@OL8(CN}Iu|4RAt$fuCg zT}}(Rh3;zr9T&ss+#euOlkW5lqPG;jCQnf;qPUvkaTEtrU1}SA89h*|JA}1FP)J5T z#i0~eP|SnJ9Y%2%MUDpD;S>vzzbQ?4kD#=kk}=^ulDo(k@-jY##~sC;OGB9%yy>{7 zP&|Pm=(wXP@@==K^A_ep7l(Q8_C?shHsb5Pm?x31lx5Cam@l!ZG5tNB#k5CEQ>f(_ zp$`R^{x(7x1*4;5#LFQzTf!YNVn#7rGT}A+{su8PNH?JM&3S-Q!uXc)cm3hRf-un^Nqof``Uw~e zW%*nX!-hp=!d6^bM;|9#7@-naacLb=#U0y9qrRlMDV6B)yV%e~g4c)VRMphc6E?MtVZ{a5ST4!7NBnY;DDJqkR2i{5jm7eC4Z>JA+Du$y zykmX|uwK+j>@wc5tg$7EMp_;n)(S7-ADe_S3KAO2p9k4d?sFTHbJ}t7LZ%(U9wZ?n zyB?o350mVBtjCAR8Q9Pif+ay4p_nwYUrYkO?UPcz7V10hJF?+#0>OPUc9au4$6K;- zOv?UAGS(7&|2>dH&YICAI}H9&B(pQz7Gv`u%DCqEWiLQK!8Q_J+eV!GBmH!^c;L}N z3k42Zh`E^+b`J^lKJGKVW41QfaIO;lvEe|3NapmM z4-@wJ@LxP&pU-qGXMl12{>*W`!}@!bS>9P$$9kDrUPTsu@m%I6WhZ%O4EHj#z0+5& z^*Tg{L#+deyfzchnb#*b-&@*0-#aVYOV93_>n+IkQla)CZ@d1ZMzn=uMn=|AJ-c^4 zGS7S7$vUb-+xEF_J9>9QL*o}tR(?(=Z_l#+M|pGmdw00mp(L2iiz{fzQ}_j@y()^T zs_|{_%Gu^K-vjNQO*db$o153uu5nl4>)(s35``SN5bmoc#Fxxxm(Q#WCOu`yxRC=( zor4)kn|JEMvN>4?W;oD78QNBeFS^G~t$luV-qdc4v&EGc;tTczONWdbHTaaV(+8K9 zjxIe=y}*?8`QirkojPP*_0Yw$rlOSYRg30UpEa|xZ2F*4V}^~HenNK9nBs+HXH7NY zR0vaxD;J$sHoLlN>X1Q$96ZDg!WZb{^-Cq-E9fDZldxhm%%fJ#TUu6qc14+Q9nLfq z$`j6iwGqMt&jf3N(LZyz^YPQ5qG)FIOuuIsZhA-h7`i$u=Pj8&Zy`?Hg|o}1^HgTI z&SRY@)~qByai7bdp7^TZph11Jj$Gd76Ciz9W6TCAYRCl5%qad`-=uQmzch{Z3lt;jY%{T9;`slmFeS_3&;A z=fIWmeZ>JHAJrH>Mw~3p6wT8c;?I<9^e7;&lKef9PfRT52cmg8f_#tU2gFCkC&lN* z7sOY^*Twh6DEf-!bJl|FEcOzOO*@32LK$SZN=|a_+KP{COM4%{>-02BK=WfH?f~M zPMjq!5|@eRlPHI8SCLmK9NlR8g;xvv$DBW4rer>6Gu%Awft@6G5x=e;!|5uAbG59; zoKN6V$*V>4gpKekC2th37jG8t6z>wZiI0oVh-PvO<-H`?oL`XNlKigtXYoUklRs={ zir7qSC1!~oMf1Fi^tqB7>oE*g_%QK+^Aq{Lqwtxc)n`~Bd4+g^xLRBzUL~6I6y@C@ z`4;g<;ug``*0Q!8&3TJ_Pb&Tw;;+P4MQdYgm*fw`ed4DgUoK?-H4$5iZN#HRj@6j1 zt7!CwAag#A;W6iE{5MBa!rVG zW1CvPQ24LJ--v$@8|x`p+ges%fpdT?x2edlBT_y>Y%g{aIZ4R)KH@-eusBLA5sl4A zq@ODJ%WPqBBg{G*SB3!VXIv)~LcJI3 zY}`x)1nYgP1=hxqmi6M4jMrNYzs&=$(+e7#P4KdP_4{wIFzHn##-Mb1*h_m~q6-av;)4qVT zbC>SMwiT%ulDR#!vFa{Aey&Og#yeQ6|I4m|(~>uyk1tg-2Uh0movuskGj>M3aP9GE z71zw&zCXtb`I}hx{-k~wEO?l(K8diOI?pwvuSmM;cMajqSHRrFI$Ngvj~Su{NMLCpKYcxPjqA~*N3qSb{j!t#fE((h15@*X$Y z!?)34rQ@|b_Jq^j+9<4agf@D6KFbT$F8^r!kGVhGlcVY*8-H_G%Epxa;g6=2{|dDu z#_yH-a9+}DSly3@B-gtq+*J6N`bE3Ljx1`(&PEp;ad%^8x!dPgIMVo<`|X49z4MRj zqdPOaGh#=?!nGq}i0xOe*nuV%z1V?AHo5jY0V^He#y5-F6kYKS&(z~^!T8r!*yS^I z`nd=^-Cil?T=svk=HHKL-RL3&V9norQEE5+cFlh?g>ZxhBfKUkA~bN82}#~hL?hc7 z7EK-tB$oe&K=M9D6a3|WFT&h8TrYwRj>A}EIQdCLqvD?u(Qu9_G&~Q8YXb1!OcI}c zon)?wEl=kCU|Di0e$P$56(KdrRVeG6WRAz62|#sOXaZ2{CQ4ObXaf8X{3+oO(i%;G z5%8qFjxcBfTnJDTU?g*`mKaL=j2XWh$v{eDccTNNFORf@obH~8Jg~dLhS5yKbw^XYmLgI}8NQ|*UAZNUWo1ntV_qXt-PkwMVMJqxgO7fG>}tloPSJea`%#FQ z>rSRfwOwOc!$^Esh6I%YBd#@~<4$2I4?<{qFUv9F zJp7+;O5GD*ycl8z6bDS)GmP7Y-Sb7NR%V z(uG}bemS@~ysqzUaVbJWscQ?7qNT5g&??&*klsK2;(`p1k0x|DfVh>M8-EO-@&O-T z_^4>CfY8$)8zaa98$pkGQ2-I-Y8ye%8f?r#@+vY#u|DgD!-I7~;v$oF?O5_sYzQWp z)z@6GgO1qH#{H&R>r}POia1!q(;Vyo-&h20%Ir zG2!&IH6@NAdSZ*9CHPh?cboN@WVZP%*S2432(}?6-RIkJM%XqwXwu?(B;1NkHa2*Z zLCanS-u1mip=~@pG1K_*#AM?g!wJhJ*i6OrtTN$Kh+BTQml%%C zWTaW3yOD`sVUuZt%EFvoaH6Xh4%^1H#q zL(IM*ORymmZwXV4cN{U@51j>YRuBoU2iP_kl;GZQ&J$jOy&X_)DUS!SOyihuhsirW z(W3D-b|qrigE%q-XVaiIL#)Gw&ji=h(Q~sOZga8B*G$l~M48iU?DdH8`)e9;he^xL#{H=w@4J-8nmG(HGkB2Xk+UPn(DMIxN&j1A}Wz02)YL8S_wr%#wPCB%~^ z%^Az<=;4Kr^Mq(;T9s2r`d7<1?hwyhoa_Ym$&_vz8#u#w3I`68f8UB3~aFB zvb>HSb_l{(0hb$ZDdC^ku-uY3Pbcv_{gI!9@Xu2uVQ+B86{Qe-Y0OxN;iboG4If1% z<{R%Aq8b~ty4<+h3(w%|&F)Mh_zVj56XI=b#(0W7RS|AG1YRUI?p^%yFo&GnI-8Py zMVl*yCEGrK+0-zB@N2-ggX!^)^#mRY9s-DE^(9k zo8KN#gz4u!12ZFObG^m=HbpvD1yZOkBC+fRE&vsEM=7PqivD*1$!eC!MdZMsrA-BY`S#unwYU zV;VniVL~tE0392v4Y8nXK~-7x-&VE>m}QCgLn9>{_E)a6VCIrR_!i{MQynU^ARw+D z6xY}I2kYL%+askL=t)x-$LP{j6wfk}murs7@p zrJ6QWyfHdf@s2Z|FHyM(8ib*iSwi84=fnXjIq@SzjkH9&hDmwsU#yqYJO1Cnf1*36 zUJh)Ucy0V|P8J&VoH^!qAT!V8SBV`r8{P=McU)bzr2D87ounqoqf$bt(dOZnZY%dN z$VFWgMT`obHT8%v*)v50uTQngU-``IveKU-(t{t_#b#!FaYuqkh!kv zyTt!U${wCLgU&NE?THZl(@hBQc`#iUbe;pHmVop8nV5J8ZI`hA%y$2=r^f;K7m^hjL| zOq(12qjY&Ds=P9BzE~xmCtf736|WU<67LXyBJv5H?R{4KrO1bMhW}oCOWYyu7C#X^ zbTZSo6O9f9WU4DOoR7QYBykRjIjC}RKA8$#62-5Oe4%8%M#Xr}*O5ji0_EK)nUA|H z{{hL5NPb4LnKwoJtCIhq`1d65lH4HKST08TWIpeqK8`wAerw4`O70~2c**&a`$&$7 z#R@k%KPYFqQJ^`WL0&5iZzG!LX2_i+cM*AgG2IE`$zq{6Ts%eOQ#;dh_(jeZPZgJl zHRAaq*B6=odt$A)K{V$*;yEs5I&$TeM8!E5%F2%SChj zARh;g%>P5tTt|?%O1@X*#5vPFCO#{g>j~k%mdtk@neGjdqczHZ7C#jCiJyuQorhM( zC{uDv(OhRJ=V-~tiQUA;HdD;?hV&fuvi=g$Tz8PCN}eH}DxNM@if4+Ab&Xaje6?t8 zrCcfbX7M(0i+GRtp!kURr1-38b&!57`S+r^j?w=2B)>0yBsPd#qTqg8og}M+)K1}@ z#4ciE8!3Yn&eau`bBb6ha?yw3hiW5*%N|U>Mx=>z${R(igLJdxAB)yT3fD=P{t59p z(dr_-EZKg??@h_?i1xdE`y?mf#>Vmv)kaEV-6X4%G)m=Iouo;UXNp!wX@O+Ej>dY= z7S9#Ae#7u9M5~i@on)(%bf;u4@i6@(;*;XD;;+PCi?4}qi95wViw)vmMQbZ1iaJ=2 z{Z5~?ozhm}S>mx`S20gKQ9M~J6o-kU#PQ-}k!x10cY#ewj^`W_U1U`K`pS`aQn^$~Qc3mj>60xz36@HtC?YH0g+br3B=kGzukBCo-&x%}2Wd1*h z?}$4^E-y0vGtqwMFAeLA47cC;Yc1J+=dXig`<=gR$tQ??M6Oq|oRQ*KaiTa=EECZT ztdA$2cOaVlxI7F`(*G?y^8fw1dUz`&8w0-T?1KzG{_|D#{$)CQIXEUbJn>O!KK#MC zTpmNZZrC_7Ex<-mg6Rh1qNZYwb&TWo zxM;(-BHc(UfUK8p8@6Ek9uMpfuLoDgfVMlAw?EgRGN|a|nTEJ1?NT*G-K7 zW7Cm=?sEF0ykZ1$tY+)w@nPG7<*mkf+8bfk+46ei=H;GXboKn7__wNhAN*TYc_-xc z@>71fZr==yZQ8E3`e-S4LzuVOcjk#Ee3j2cSB0(QQgW+C$KDIM#XE6 zL|(UIlOsID&u<2FZnQI{X!ttDg~RQENO%0;+e2%Br0_%dO^$|djU=OtC`bF@+pG>$ zl!K1&?N$d0W^*9k5qTP*4pgKAvV`x9Y=@lUvcFu84*hdIbgtu0hm<w9@{QVj@t$i(*$S|B!Nll$JSl_@tws6zZV3{N0 zLWnkrC+UrLr)0YciyqC~=59gim4)d{mLU;gky54xai z2Fu^X*VN}NoL4>ljJ({hw<6cqZP}qiD1?=*>3BibpVd!1nfNt0{`fAzc7>4EzRZzuoG>oOc~ zBwXUQ$guy9_oVqu#b*ZJC47{WA;aEOGX?R&-2+^;pX2O>N9h{Ajc9Rj01Qyn4XU0 z*+BMHex56w%jff-Nta!9_JZn}v+zDyb*0a8>*Fxg$(}>o3E8u%s+`8-QXP{+evfP> z>>8X}hH^N@ol`bz@u|~4A2NU5!ZK89V$l#Md-kFQ3(9cg2;`buIdcILVc|D>W_5Mt zyjhE@QS2c*^W=ln>@&K2AMsy2ff?5xzv#C3b8+KzjHBZ-7SCn-{QO1fx*-ko1nUhx zs@N-ch|&SZ^~djATN(V;I|%;Zn4$=UwLv70F;4-O$MVNuvw7e(^ZPf@BWL5vAX`79 z2BRag=#jH=Ga(1-oeF{H9qVO%w%#T1+dS|(WhgIrx3F>5hzpiCKTsYY`D}TY!f*4y z>nuij91B=yy}UMq<<&r7yVyQlD)I6-C}5r(Y;+Id0P;P1>lnxFI?<-ZtDo!2!kC5{2cf_VoW3-hO zjs<&aEchKJ$z$Od3!0NZ-w35?EZIHbY5smY7A)&AY9{`vEQu{%IJ+A5cdDHK_|MJx z4_06IOLHg0$AVvBlx8Y5W5?WH7!n>hc7&mHf9&Xg6fQV|j1S`uee4(?nEeX|g&5id z_wT?VAa*l8Mof%IIU4%!x6k$MSn!{!qsM2QgtJc^e#n2|Snwd98ngE@jd}2F!e=^; znfMIzRmOsx-)8#3;&74wr5XP1Sg`6VjRn8TXD_}T3*uP|=NTTsjmyU`$0*j>EBD*6 zU}9{Uiq5fPqf6m854-BHZ^wduNAMmP9J~D!$ATvubS(Hq>w@`*{#}Z0Jn$Ge@fv|e z1z7w@tP2|5VG|y9+v9{tzYjv52{`ZvV?m=Y=te(61l9#Jk=lEYe!DIh#o!nNK(2&g zG4O0gQPqh^=K6AC0Eh`R$D;|e_%{{!?lm$QRbDq5ViD1>Sq}_{IR_Y_Qlq|ykQBZZ zAy@&tB(evd=p^J#d=DXdEXDZu5TevpgcXFXNNH98&5GdV5l$$kxR>GA<=kFk)W;bS zt^oF<2P=SltIh4dlnG1HDu|PeP@2Y)-IH0c8TGL(->v}uw~hK>%=YW80G=El^&L2L zi4V{?jyc%M7tQGYV;0&>#RsboOOlv`W<`m%9gxl+Q6Bnm)$gsq_%U9Z8HOgtqTK1k z&_?T7|J9?p#EW1DS+;Gol4n-%;^vplu~8TK%ftSlKRzb5Yk&XZ_qI5Wu#V4S->&)j zy}|EH|NpM}`Ip^6*8F&B`)gZYW1PpSQI6*tulaFZgyECLnPRzkrg)CHO8lN^l!}o5 z2a<0Te<(gK{z`m9{6PFnwC{(c;vY8G5V7WYu++Mt!R<>-~Cm)}rWTHg}CqwBp@MXTx?!rQ|8z5V0M z`E|kRJ2po1_+>yBUkVH*?aA~8$4VM9(@PsNYcAfmVqZx^^qP`}bjazHcb7IqYX&uB z-Z2Pa7w@~|lahuo;=&^@K%9HhAQKAw0{#u+0-ij~B&?j@<2qQ?^fQ z2=|-X5bnBi-}xW9{bP{+QtxCv`0noSE!rJ&Nax`@ac=p2A9nYn1Jh~BrrP#opmQ-6 zT>%j~s_TeQgw~?H-@@xB$NxY`#N8Bk!XIj8JJ=JTXG$ zy~J~oCG4xKKFL_4uIgnnG-sB{96kmDiZZW+y3v=>4vbq*G3AFjO!O@PMylL&6p-{b z;+^C?c$edRp#{LH_&qn7Zv@sPPeWPfB;SG%SOCn15IM(v0aA1|ej=B;8-bKCH55{# zky|4(;Yp)i)yQpj#WRiOt|PbG70wUIEGsV3ic#|-ih~&2d|2GuY#8esax(&kAyKnUj2TLi?=iL*M{yYU(k*Ve0N`x5 zDX8*!#J9Q(L3|OR)pB@#3r}mC;4pp*0e4bn8Vb)?Z0D0@dV$S!vdzR73|rd-%G8F<dm_OpgYXc|)_$}12c34|C@WxvYI-2SR%p+ww8Hqs}J%mL$;Up z@JLp{BqbfftY8y1*O^NA71RVPn92(LF60ZM3D*Im~<6iv*_85MUfrPQ->fD#$t5U`>lDH(?7oH8mug4F3|qIA)uQ4HZ<+ z0MGROHGpwN`1Ru}1M!tLOy@U-HyY#G;n&}lDGG5IbR4#bbB@!qQysmm5FZvKDvfs> z;UB`qt~rF?M*wr=9brCId80`-mf(HTbE=ou(ZgMVIX3(n#}e6Ec7Pw^SLTPE5f5WG zG858O+jNPrGvZ-05IF}MKi?=f1N4I?*&vE68l}biED?4_JnR~z*<{lB$KgibzXULj z@eB(b(Y6qA*M~i+$^f_g7XD*&!J)Iw4t3nw5#Uwuehwj$QXgQ)H>~9)1 zz^T{A^;%?)gk3RjPPW%J+bhd%=XJ>rhrAb>TpM=1ZIyH3-_|4>{wFHu=JtSekCE=E zTfw!D>HW-^TY&J(O!%J4;a+-{SDo#_WZ6~A`+M&@+2*~CxOt?{TOgaaurL>I zF%D{(tB(1u8@NndX}yneo8p zaUVU0^6kIFk&Z(Q_WuFzxFqf}2Tpg}TP@P#i5sJE|G4toeq7ThIfp~CY`)vliNwM0 zrtmzmuW07Gk$$9PJ|Z)}(Q5){OEym(kk695Ok6Fl75QYr{2bwvTg9J>kBU!;+r>A; zcg0=eN8&z_9maCad^p%d^5J5>I7l?_z95}>HyB)~@Tc8L(*OL9NO^F0g3 z8@oh^A1j%90W5Eh$afPcpRM?1l2=LouH?58e7Kwb8XZ&Q*TsM%*B+nD8M05Qh{vyej zist%3_!W}PTL+MDkj$YD>)|Mpd`Ns$d`A3*_>%al_@-#)1CifcU*HD{H`f>BPbG7D ziuJS-j}qI9-NYR6MA5t#k9lsopWN3vc1kZc}Kae$T>X9Jw(paQ7#m%J+SeTIW5Qd)5ONtH)rb@ zZ?139{QtxJvb}zB+M0NKeNWTzYRT}XxZf)N@Bb#A{pWkg%bfEU&Hi^>^@PnB^aZ-- zfb~a+afd@~TzmX79lGAQe9?k&bjM=DqsW)0<1JPM%Db`MWg8KQ zD}*1ne5U{#U2i-Ds*3NS@g)+xJ8hI<@LtN%JZ80k&4Zh$Nw2N54_F~k%{T7vvDgC z5G-$KpuBX%+48P}-{xT%_aPIXbF8!8^#}--HxcD=|Jc3^Y__}`;b)#4Y;+gl`Lm)? z|1ti@*n;UkMj`A2>lnw4=LTtAIj-a0RsdNq-M!d??VEx2+5Y2GxozK5@S`~ufN`5p z9{;afXY0ag6hD_4SAp_gv57#NjVSRYjAg-gn)^fl1 z(S-6?`O*6>`0Ml?*F-yPPu>%CllDYI8GE8wNyTbu`k033$Tj;~?j8s~R!SjsS+# zd>@4q_%mQGL&A|JI65$ujh}%_Oi1#tiD=|?gkjl~4>5^lQ$FH&KVgvnX6Ii}L%8EG z7Fdm+a5qj!d;A9(&RJl}3AcuT71WnmoEtU&W252Gj0=ZX0`V18s`VtjiUOVF4)EF) zRKAsYZt@KXsY&LF;yKCGC&CIU*Y~i3x&uXrxt{1sH+ zpK%SdZ#CgqLFKp-E2!Kbx=rR6L3>eNW+zDQiS%Z{m-!Qly(#i3S}UmRKi=?~@k!k6 z$Hr};$eWAXpW+eNn(;Z#J&EFcihRs)Pv(B|05$tQ6AxfhpMcnWJjDXG$t`YiKF}8d zF$T`$b5qOf5OfkfSHMHdudQr?)_ktSY8W#OhsTsO>*xQ+-j@JYRov^}bMH-Z2_XcC z2oY&y2@sJrBE~I*aDlKG!fNy>`&PsV(NaV}1Vls|mQn;Ih}2TF7Oh%~R;kbWcdJF8 zwbp8Dt+k@u`@p9*RNMZ)Z|0kGPr@R#eO+EV$=q+gncw_o&N*}D%$e=DNL;mUB7K>W zoPZ*o%t(A+UJdKbGpX=3%tUyE5nP}mVCeBr@NUCP*Cfi{f9+974m`FB4kvQtGuI6~ zp5z7|Tib!hlY^2ti^mvv+=jD?fk#yBDZ{Icfk(n7SUbK**KIa{unqRefk#s+L!$^T z=BPTlDBft`kyS7T9tlpiG9b&=if@<|4jvL&tnlfdMRjycgoP3UD;@YRsH0;UEJpVT zEN+p|)r;%sU`p^O@F=Jg;2UO~BalRW`l*Wc5kUTsLQ$>kR zBSs*pk;NV3T!eM7kO;seRT4OFsesjPnH?&qhpix$FDI|9)}Hw<};QtDZhEXUpYdU zI^-5vsZ@0l9JAx0^Mp1DEnd7KT+*qeppKN;fY%~&7ha8}+d(pH z;-#+eHp5dtLC%Wu-vfDW49*T`xmECgy@9+e4CG~=8=2iB(z-|Fix|i|IX}{rV|hQz zl{G%$qc*4Uf3S}e+RyKU7>s<~=@YFugb(5gX~jTkz5#DFmahxnVB1y|xXT>S9) z2I`JGM0euja@t0~|FVof?$T(8&d)LzXdGiv$X~Q5<=CTh$6fz#M(z9s52=hXoByYm zjhH1fc0jmyzd+#|J8#;U!fS+VATaz!;n#$>3ElV{->E3a zS**log)a(U75-BAj<8<%M`4t$2*fw*Q-C?*t%YWfI{0@K?3;Tp(O7yjS?B zuyKF>kLlk8{WJ>ywfMW@ABg`>+(Q$I^65n8SNtULFNk*%H~sjK?<>yNM8+=_A1-db z7Xp2g$V=;QBPw?p!j{6;LNl(7c&CXs9uG7355fm1+}t+1vzFwFF#Co0jGGTdMsCOl- zivwq<5T2BC#)U0|M^Il$Kp&qQPTwZDT^#BthCbFe&N$}=cm(xTKp*qN{2g!n=nx`t zT-X`vqy4s!omDjB@x0|ZaK_0!3J;eDhCK~A7l+)HmxtjF=5IpacyVd`<9!BhWTzTn zOg;3S$oSDr=sS_|qavJ)6B$36igMb`Wdi7qv1jk1-hETUNBN&QespH9{wF$qbPtXJ zros5p!3XW=QH2>j8ev9{E}R+pB>Lf4j2&I}-iGYH`>^uP%fO1cG_07bj*m)27JB_# zB%%wsimqiMV>VaRRVHGyxuWi@44PxKKNy)=1s8^vZZQzsObDm_1hFHgb#Qux@yRev<)~es zXPUPIW-%a|Vdl`p(##__mcb`}Y7l7{Smc+~)oBo_#sI^X*kkp3hBLKz(CapOc+!sFVEd*HJu<8ipJ$#@UH7$@SitLQb}V_-2( z@xm@MEJ018VaR53mlpN2+yXMu<_*_=L|%vINyoJmp&6=GeaMZ z>^9H(ky9BY=^3^@yC3ADND!na=roMZ=qDVy#48=?zS=ecEq-lQS_9Z>APh1Z?hdj?8b~th|m02T|T2W=zQJwJkhCSF5 zUtx9R(kg%8!*sZ7tR^@T#BE&s?2mS$qb&fn`^|jz)ttf17nw ze(G94^{+hyliI$^KQDiUrBYZV*kG$`7P7#g%W;NgQcI}*#=Cy;$R_OW9v`&iW{fKcGTPq&9PKu=r2CZ7WJlX5Xe*PBZK)%ir+Y&KP zm9GZJTPHO@@^K-b*Nk5!yi7PpIA6F>xJr1laFg)sLbd{^?>oXh!smrA311cdLio0j z?|F=WQ20Axgbf4un|^EH8RF*(hYK$fP9>(H9VImV(oL}bK;cV?;48$xBECUFPKU?8^^JDlBahx`D{Jcwe9#{_$bAHPLgSZ<0G76OFew4Ctf0KTps7k|4YKf!ezpn zgr;0VPvdcsyA}Rzp&K9JoL8o6$|LZ{;y)GsT=;9@Z-wkvV7yO+5tbuxH!jj#yp@n0 z4-D@j>?t(kitsn(5ja@>7YYAE=+*Sr<=xwErUC#Z)ZRspIc5J+ZrwooT2YQAIFcKan23!2XGVGQJD9zLS;}t;}YlBv!?sG1)7q3ExRe0;6NIpEWcZ`-pU$Xe^ZW z2lDaR`v6V6k%;fHWr9T}{DCnihg;?k;#tavPg5q~#i{>%Qfl-DQ$ODYnle3a2-I5e2Q$L(*8&ze&+ZQiW8Gyac!EMW4iE3cX~^|C3G=1-Y4bsG9sr_Gyo#br~c z_Zj1Fu-{_}&hh8&ihVld<(Hk>F|shf;DRteCf;xYbc1fvBTsHm_9&fL9{pZWP8j}+ z=6MIYF*y~vKfYaOGTk7B)L}I6ws9H7_S066#?+bi}m0f*c)s0TaQDgP@6o@S+$n5%dc708c%g@Nf&52)-p0G)M03bW*4{ z1W@^z#}E4`FA793dQb5FLmisx%RFucppTlU2(lfTiEQ#|`g!_0nSb`5u_;3wBpfar zEo5_z;VA0{=LqKuuM@5mt`%+-eqDHv@H@gi!pDWL3HJ-@g)u&Ok^V`-cEq%h|LyZm zxeok6u`;F$e)*2=-W!XoxBu(pdclWoURN5*vFYlJt1-L71I0X4qJTDN zKvO^8RVhPDJ{i{OhBH(F%RW+apfd|uFfGT}A{FpBNUAkX^+r@!1Gzup&pAH!pb`y9|Usv3~Iq;)C zJ}sQS?Qpv|aE7jgKFT@c!dRaL_4NIVE!rs$BV~~ zpTFP0jYF&k7;`T4ok-uyMCkj-MFP6>LJS;UXaV$x| z`2ARuahS>48Z%B**reUP6S$yJ5L$qtaY3H(OL#0t+&c7V*Pyy?k8#an9P zO@q#Ph&v-0DqN7<4joGi3zC_cy^>8fcS&x`JUbcM+&Q^=^@8MS$ZP)UD~*nAnFYyi znbpazI2YGtE=Vq2-8uP9@D7Dtl3wOKqvN`~f@BlLyLol*N&GM>~ zcRqZ17l*qxcuX(m4(H!pVJtc`|yQU|-;`xX_BYAi6JY3gM zB5POY0B-6_0CzltOB)sy1k<1-rhp^zz&jxID006HIXexxjucN% zcFLQdyaTDPfO~skXXJ=E#TBSxs%IuURZ+Kh?vG#0!2LBi5y=~j9A}^e=l07vvqk)| zLtgfh5BuHw{R3Gy+3&Si5mZ2^={lhx&qSZA6>&p=pVfpu>$?0+=nyRKY9am z_#jlZLt zVWLmS`zph3h0SIUueXtu&G~F*&HEaunexr7d7DVtV9DmrgWj!=fj&*z-luJG?9C_l z3VE9u7_Pmr`Q+X{8QFYtAD@h9J}DJ4y!oV5$gpfqfbh0Z!))lwzS5@~L7PqOJt^ef z#?U7W?c?Zdw5<_3S)t?b_-!_|kE4t5bjs#Q@^0rD7>O20_V1y<(q~AyaMZhlnm;7v zII5+&cv7;{Q?!VbtNm=~7}63yw1(7t1jeY^Ksv||-AVdICbXRiJxa=rSuK5?6o(=D zDe1#Z$OyMXD<}J>e%y0ONBVIuAuadgR+G+Q=$%YxIq5Z2W#}eSu5z&Shooov!apND zgQ|>hf|R54mU=i9*<3tfX%ABF9qH|0LZzhFQP|LNq+A(j>1@(-eBs5UeVLFEUPsDh zl9t{@%H1z5{V!1T-*-e|&*5;xKSR%bV6uNEiX&$C^V2cMp8M}YAsn3hk3&Y4c&*>$ zgS!)(s(WxIBcW?wHV)Pc41m9HI>3bve*%M#ocnJDMg;{L_W3`=ixsLQP(xrj05ia9 z23~}BV6mA`NgW;BlN6&1gzm7$pAK$=26;k%tG>7*kt_|)bRsx zCz6#)rows{dH(D;JZvs3AW|AL{Hsk;_ZSUh3HGSRL&6vywW7s{LtwGx)0{dwcA047 z2#jX@;cd)Z{N5X8dbMT5_7FhYCB#A_T)lM35IB~afZ8F%4aQx)fXH)!2!1<%BNR@y z3I`LVuq>c(`H~7mT4N&C4kb$AqJY90n?Jh&`7#jXa13#zQC?HRYqH4dU`1xkWc==e zh3sgeWXnn*XERzGH-h*%7nVoQBj?k1rF9P>vdYK`eKfq=M`hjGe>4OO2gl^955 zabk{$Epgjh>2Z)A18Mr0h2oGPa`ICrqmL59tlAOXYD%zXp?wFeP^cMVTpj8tX{Jbp zYAcCVuvYgN#H@sF`b>sJ>Xk%}Lv=8^DFNL~0=lh6=>DhzX>_Qg+&oyDk7LNzR45;m zG~=tG+JVIFur?o+P(Kj)pwGRq$j3n99$2fJ637RWKt8NS9^(Y*rsQKb<^bY*uvRy6 z`q2RBrq2hGApQ}yy*WloKsS?sZmW^m=}K=$FapwRM}V%3gvSnvofnQ%5WvI z1J0h+#NxwpMjw_&mNQo>t)>PAtd6C0wjY}pooA0eEJMt-D@!(ZgJMy~vUR^7o0pZ> zpFS)lh&{lS78|?4g|B1j`Mn>T7o-ZY>7#2~MJ$E2$Hpti^G_dMCFqY6Sx;E~l)zcC zm$I%B{#90P0`Y!WtBF@~0&@xb0#cejwbelwbElDJsQ&VIbRaQk+8Nr zQvyYZYH{dgrWCV04_A4nWTlNcintio>K=_KqoA8UrlUMmJBqjw*6OANbTbL)wi>B> zjC50SGc58wi1-w&)jbMP20=G{UXldy7qC`0C7_#0K)2OM-Iqu=CEv0!ONrSy`&M@e zqLe~6eOkjJ^-^L1tkq2k=w=eoZ8cK&ROzN2N_?IiGU(TlZ+ zKI!%>j3%;l*s|W>g4fYM4r^ocg7M1Ihu0rzjdJC{#%^#)>sT&s@niFn@Ep*GR~WGe zxhrjBH@IkZyz;O3v3b&Yb?C!uh}fm>TH4qRE=wJ+ZF_r0aooIe7a=x%bcHS=F7zdM zg?P^C!z%;*LstXWS@(G2$FQmMI3CjU`8{kl&u(=M%fUzVrys8vvDiqqr-|`3JGJTaXWF&kb*RQN<2YHP!ElV)w4sPLIC z{2@3hZMd)V7w}^Zj6CzUF=ifqRacH8O3!cwo0i}>n~xHr!pFGqTi{TgRZ3L&B`*9f zI8^UkL{#`x7tZ@sC37fI;d5O0vv9CHBF}yUpU-HP1^D@;<|+8G9Kdg!i<^h^uC{T< z5ZjyZ>Iz~ZEEc8KE^)$>AuI)z&Tm)B&%baVr2Garxnd}P%_cOEsD!Em3GMb1x(fnI zDDD(|42S&uIaueX5*qE~R;K($IlmWDekIPYbt;Wg=QkIAyl-)a1{0OZnm{HW@H3f& zKx=zChY}^cC?Nc(FU%n~$iwh0*qX->DQJTHzmemx*(~PYv&A%9%r4l%dHK9+cGr1c zB(pry0xNNsVITJV$Rmq;^z7NQ_XYSV`dZK4J%{73SMNSPF7LU#XRkA_zrOD={}B#( zBj*sYM;M>pBO=c)o{oejM1HimN918Izp7{NVIz9>xqiir$ed-7ydFJ!^&HVP@&~Ux z9L2P?33H1kh43@^v*vJdk-?-XQ~FIZzmr4r&4=R<6+RQ@_BTK9^(Pzovy3KVXQ(fZ z#+;+T=B1pC6qt^dn&I?+dLq|^xxIOsdQT47FCI>r($}2LzMpyWXUz>vpqnuFOsY8( zXFITGsGXOGD|D98dDi5>>Gr4G{l#+#&BQWlNzFA&O%3#uF_Zd&bI>NtMOQO^CWrpQ ziIMhp#8INu@^^B`pF8NM&g`LB>I5|KMii7({^I0uPF=`k^(gb|AcKp=-^oyh>x7@l zZn`17aS8Z~OQ|W46XxQ?EqMKq>oLnOdp+Ikv=M$ovQ!H>x~P7Ps6~2lHPxd^~T+hPL7|4mcEOQBj@h5 zXcV4n=0D*1WUisV-uO6k@$4;hLeutG=YQ@wdK{bJ+_(Nh_J5MI1xsYxiOBZ|;$=d% zFv;f&7YbJiZx(J6eqDHv@H@gi!pDWL3HJ-@h5uV~^s3pI!pPZGL>$i&@#W%t?PU0R z@!Q3>i{B%DpZFu<9$IwN*OZ9(IpTTZ-Gm%WW%yv>I3YKGVtlh_CBkQm*C_nU;#{o4 z_^ZVE!b$!$@jJxXY^J{5;v5oyEHeInYbF^MqwWzM0bB+^;~sag$FI&JuFGoc??ZA+8ki#hRRB z=EU2DJA^xh_X!^qJ}TTRd`9>qA?JEg->br334bGG2PDHk5FQqC4Hf;+F@QhcLuvv9l6)KieVSNwkA z6T+v2rk;Y_OX9Bye<3_5F*9kWY_X?jBJ}=xSd`tL_Fe&^%*p7LI%@=kRb{C#6EESduM+(OYtAw2ELVa_E zR|^*kmkC!2HwkYO?hx)2?iD^Md|tRu_?GY;VN&>kFf-leCtH{=>?G_i>?Ir`94@?6 zI9WJbI9Iq*xLSCdaGP+a@E+k~!Y72!3SSVuBYaQzf$(Et43md=ezSzR!ZyM}VGm(H zVX?4O7#DIf67^LHCkv+w=L+WumkU=3YlRzxw+Xii?-xEKd`$R+@PP23uwMA7Fgs@R z*HV}#Y%d%l94;IqoFJSnoGZLqxKOx5xKp@W_<-;!;j_Y*gs%uW5t`>OD$Ek*2s;Q1 zggu0PgvG-1g%!dv!b^k`g)@b7gq%Ib^p^@(3D*cW2sa6L3GWp?B-|r>N%)HJUEu-Y zhr)VcPNq#iS6Cn{6!sDJ6OI&C3a1KZ3af=R!ZpHL;dbF&!h41H3!fA|E!-!3S@^o} zP2mT^kA<0-V8`>BEo?2!6ZR1H5f%&27jpU@<5vp*LpVt|Q#eO>mGH~LrNZUHuL#!* zw+golzbX8d@FC$IA?FV=KR*(_EPPe?E8%a1oM1@#dLc%@437$Pg>8iS!cM{>A=f}q zPl>QhI7T=@I8iuNSR-5@Tq;~H+#uW}zbSlIctFUR zjSS~9L1IjpCFE2``sWL~3cCwA_mSb;XM#9HI9$ldko2D@1clCKhSJ|y{V z!X3h$!rj6Lgq$!*`DcWjF-iW0kW(hf4+%MEk~~e=OxQx$R(PtAlPW3SN61-~@R+IQEFgS*Qe- zW=KBzTOivSZWJw}kNKJg8`O6yJl}zzGcN2)@NjO1MW`HQ$r%^MKDc1I^&L@f;l5)Q`Sef#x4(LIIuyo2%Awf#AJ z3Vjg~v!24yvf^lLuvlgoPC~?V*tk7~Y9Wk0g-jePks{)MmEgrpUr#LhI1`QH3kf*Pt<-g^w;S>O zH8YpkJ%!jq6TLKh1+#!PGhA5^z0BWJXcg(i)Sg1EC_PDg3b8Zzv-cEY0)ah+s4=jo zkXgxaqI(KWfhyX6hLs8b;(H2RhRYl_%Ub`XXNd$h6Y5v=zq*-FgCXp{_7*_@r)ON4 zIS36-pYb`;J2i>nsQVlFD$H~NBhs%3L;T0sy+}Lne2%#ejSE$3X3SAGt&p&r8q%-m zvo;6{PRVea0r?Ylj-3GaZlUsj?;C{YlW!t^E6RFB8nlq`2xki{U?OOpbqa7iZ5yz^ z#lAs&f64JvZRNY@IFWsW3L%Hu-`_WA7$eFUy`NQxj<;b@gR<{7tKu|pyc-~^=Diq= zaklF@-pRHr@g!keV$&n{Mn{v@$ek}7BpfarEi~;l=;J#S_0AFU9fVb#*9x}^ zzb?E-_#NTjdmh5S+Fn5;*gQ%5U)(FGVLAP$-+1}&)ko3`_Xq0|-q$qjbK#r>XI$;r z9Uj4V-h&`~CqEgMGPL9u!&09!6>xCu+!^QOZ1tc%juUW+#~4A zjpV+8x0qdwAmemg1s+Vd0(I4H_%U7jyL4BVs-u-xj!o z`i`|n(AOXr)Rz;`hw9GP_pL_yxJMA{7-yXGoA3zg%ZEO9-PqOSj`w?TGY-oG&D#jr z$~Qq7hS5GLbaKlwj^3B@qT~R^df z=-sUh&%CB^OX&2_!HVUbHir)_ZPJy$vF7W?9yoc}Mf>w-v`w_XyKSP~mOk-w;&1E^ zdE6tqGJdhyD|*0z_D$O+@@}$WVH4INe!2;Z#+$`2+TZ4G8x}QTKiZ$=U9`V7bml(N zHqr9A(FfX}{>J|FBjSxdkOzO8Ryf`?o*(aKbpB|6rq?!+^Yfb%ZQi$WVGA>_v&>0?fbE5Sy9^29L(+2WKo}&Q=+BUUuTt4yw z`Cy!*=VSB%hPm|Hc{V>sUK?Kfqo>=Z>CK6}EjFG@yPco5jd%34ZJ14aS^OqkL+0hk zw1Z_Zcx`#iHz(TNWaHViT^Z$d50=khyi}M?J3Zb!e(`~}cehRE6!IvK%)#gbryyrX zKkGl*-~MiUB(b|I;uo0`%al_klW~uJ2CUvxE_Nq}?m3v8wdYt@Vf2A^$IQpgiTp>X z???NOl@G=}Rz4V(%Ez(OKlP~T^XlE4=+K}PG&~24cdWD-mP*^6%cETb)*45<2JM>a z{F_>#VY-ZStaKTcO4pv-qosTFT4}(6Q<~a1w&XQTmvN4jZmK3?$+KnPXz7087{{B3 zbXlg3mM-HQD_w>KkC*v4GTqw~IiHL^aICc3B5lU|%hJ9*(dL?1BLAA(@$8Bv+5@wI z?XEdixw}2l@_k$LAMN_LX?lAi5ApL9|7dwTb~(5m>Bf*Bt3P#qZb!aiiPp$lJ}?*9 z0od}IA^UUgK2}Y2dm{JeqYoVW9*HHgp9|Jj4a>~2?~zy{>k+FrmA_ab^X^z8wk4KG zzv)=_%s`@@5*UYj32p_^~<&S}1$ z?EqJ*+|jXiu>SVS@OkNK$0lfd#MtscYh=lXPuvYngF0i8=VqyWtr&PAbpLq06n) zL0Ruy8`3bpJjzh_y)Sm#T#EC?vKvNQs`D1M$_loyRn~P&r}bTM9=fbw@?oF5d+z@J zfgH1QToxLgwoMxKzK8~2)*iGjC%G1;X#?6H?9^uFy?W`K<{L3<5!}9UC&LFrVQd>? z-#9bfHkwusC6RIT8Ze8$(Twj=ES5GJ0oXT=vx8GZVH^R9{Dwbv-#E?*_VUBd%<1w&F@WU9`8b-nxSc1u=#=w3`8W;oP++u$Wj3aB> zH|}LN#$miDPL$aGA_jEf9+6-9{ZH4!( zW_+=9rIQEcd7 zQqK9dbTMg1pFT*+l`1y$Q&Oa(ZQ2H5C>S3(cXScyb3D&xi?A9r*TAHQPNvnOjn5Qj0$|7J!j{m-cup3LWJiLHs;xa9=D*54x7%{-EbZ=9qi;5 zWqK6K2(7JOoLim{UtLEBb@`%*YK+S_^u?%eCTuLU_QS>Lv99gR z*VuUkQ260uI(P)w8jrv^$;RI*!aiAW+z*=`s#qT#wLVHWcjbtMYA;zIt^JSnQ8KC9 zyV#x4^w7BV(edk9M2+c>-*aIRZ6u;yxIRk8GRYE5)Jb{sS|g9264)}>R1`8^0~{|8 zCBGesl79DEcO>D^yoo#%DSX#gI0VweVN(jps3YAd91_%bFsN^4K;J=MUj<^0X`qiR zrLQEYuRf@+I-sxK*T-?1sSWgzrS$c07dXTP2r_0Rez^kFopBa$mNgt@CC>Ed)HRq0 zsV9tN)%xhg>!Wnrm}AyQ$F7gkT@TwF1?}38W8FTw1bgg6yLB+T3(dw=P7hf@y4S$A zl;GM`iy>}h-=SPMB};cZSYNg_CCi0RivzNBZ-DhP!0pDEf%o9~`+y+bIe6Cig4}zI zg45x7HLS}i-RpvaP9xV2eHsv?J1-j0=mcxvxg6GMquYpJ&BY#ExZ#Q^-Q!S9(?hl}PJnw^ zfG>xecd!e`$u(vsewV?fZep^%ur3R)z|F30S7Xur9xSr7lx5Ep>kvy7JXv|6X!-$b zP4VndM}Ib5Pg2<0y{6c@UFb!pJO7}lwBcLbB6e-hSLW$%`?yQ$G`oM>+tTfx$u z4eP7Y4MSD*=S||PvSDkVwyGGk64pObUQWiNC+``jlN&g$-De}wpSOu0iI+ALk?6^( zxPAb&r6Yizj|KyHjTt~sF6MQ~tbNs-G!w=ftiQc)?8$c8=vfvFV5z1ydM<;-K46)4 zTTS}%{(H#xbxP>-N7y6s%Da&3;QRoWSKf78u;vHwu44kc%ckSL@B>_acvrm@4B%bG z7;nS#Ls(a|>0W7XmWGGQgxDWiKcBp4n9>}LUi-?G=aE}jNt=u z87!X9YnG%Jb*>|M%x34d#dJIYiv}Noxk2pP{1%8Gi@C~!wH_Db)sfg1)&v664s4;u zSD`krXqFOoBNTc+T};Q5uu${q;`H9_>PVPpWB_u;ti~^&W;i8ywAJDvsDRKda1`U# zIPx*N3n5%$ix8ZsTC|B=oTuQZM@Zv3ix1Wslz(?Qojhx%C}KksT1&No=F(dTDMCR+ z$OMF3luCm;jO|6aK?o|&T0F*`Hda|&iii-k%v=bMkc%!6(zZ!NbWmqOcRDQ#CH zg!5bxM-4VIU9@eBo$CAH$&L0QHXI5@tdEjOY^N{A`j5-4Ayy8tVe6x065H*IvF>YO zPwfzh4P754li0()SZl;BfOUnDZr%wE3ga$#va`nt(Jf82z9wGbQdlQMH1HjOQbTr+Ej}t(WK#5|uZUuCSf_~YZMHP%=*blAT|e5x5PJ>B=Q8Qa0t@W9uudJ_ zjEsCWD#Xmq0|808CE4gkV6p8E>vYmB$woz)S>=jfCrP&?8?}RZZOTmk?PZ|50>uaU zY;;4vj*yoT()dW{!0&+Zw+&)<1~|(DMF9qw2s>?Idl*E4o6UBhDQ_`iHqY;A1q4fy$% zejbTEb3^PI{>aaSx}R^w)-E40a#-wM6B#b5gHog0y3YGPHm3U^a=*7V^1|YCBj5FU zT;KEikM>;g=xLF8dm=4+l-w3++9UEkZvy6Uc^)>)>dzgs&c@bSy?gPeH-Gx@r!Rks z_;V(I`nfIX%wAi^-H7gAZ|ga;;hde?oSFgs7w-mltl6HNE&O>WKidLsGRgdd4jA#Co=!Lb4uLk>C=c`0M2v9Yk#c0=mL*1y9wQ8mzk?|+0@IgnuGgYWYW~B z$Gi_&znCrPGtu?E73pz)IJ;cQ_lUnB&i)Jfe<1%bGDx0HL_9W&$$N;OFJ3NwsW>|* zD7RAn8^yU_8}+c|%h!ni*t<9z{?+pweE`EvlO!1}S_Xr=5|I^|xi@z(*eR!A-UPNrVL|zwhe#4XLmC2v` z?2u0tUm(6zyjJ`+@!jI=Bx1Z5gm;myKL!mkVO5q?L=dY0)t zE__Y6Usx}Uq28mM*-sDn*Zv0fJnF}}{F2gP2MjsuX{KvdXoG)M-0YhNzC-+8$v-N5 zR%rIegWNmf9||L=H>sa}F2pYgI}wpy5AnX@=ZTxoDnkBZakFF`-0ZgrH2Z7i1!gcSDYU?}RSLgM-0Y``a6Ts}Z}!s!zgFDrrwPvI2E#WJ!MBK; z{WQUMi9ew5?}`7XIG-zw|C0Dm#m#=2i2siGL4|)LZuZkeI1Segq1rcS?niL8m+0R{ zXnHTeJBxP{_7V0IvMt1T1BK>(1RpPcsc^EeMz}z@M0mY$jj&d@U3i!9Tf+Ngl7xS6UK#XXHjmH zaIA2;aJI1VKACKfG2SxaO+vR{CfjEW-z>aC_;sPV?-9NWKT@ICR5g})ba_7vsZewrL7CvPw0lsa;DClkwrZhy@&;uD0I3$GMj zE4)s4tMGQ=H-+C4{!sXg@F&8b2^;UT$$>%U%h{9VK1J9?c)GAe$aXK|R0!GrC7&g{ zT=-?-wZe76jl%81yM&Jj9~V9+{IT$DA=|}F??WNm$K=h>up+h)o+>;|*iTq2j0?+! zmkFl`uMo}`t`f4{%yigpCc6DK?-PGe_yge&g)a(!BK(!`H$qOjq`p50o1)!I-duQ! z@C(Azg*}CAcT;|daH4RkaEb7G;a7y~g?9_TEqqq^f{4{~&CRXFlax2{|K( zyt{CKaFDP{c&Tu{@Jqrq!dl^N!fnC_h2InYQ231S7s9uMN#O^=j7*!qroxkjrwBU< zy9$eh{e^L1xp1^_yl{$ehOqHImrLcpT*z6#JYTm8-F}z5#J?l_uJAG86T;_&KNdFL z_c9^>L&AFDr$SCUX8O&AjrYOqDE}_PB4K|a=P5J(Fd-)@lTQ7kk$;u3O`&n|fGWoBCoUTm%2O;MxlbLryPNV$g`D0^zDc-4xKr48U&$Bb|B{dsz#0F5 zuwMA7kTb#=-dfl}SRmvSafX)&hYKr&oHNew*+NblCtoJSW9qp3R&uce?dsr`miRJNkWQL6Y3__^^MhVT z`plU%cbd~Uzh+*j$CN8BzkC{om;xI?OrJODatIBm9MNM^P0hSnldr0Q+CRhmBkjo$ z9EnF0(U|V|#bYT{Vi@`?3$Q030=f49WoYr34UL1vW%o@Cb$59!qD zeJoc_-{e3XI74kvwx6a$#-+t~Y3F9pn~>Xxa_Wo=tA*02B z;0#^e9{r7U$ha`Rvj_DJ$MkLXA5b5veP7>3xLq7LLmQ!wa?ZH0b?^x4yCk3w+4S}8 zgxkfTj=P}mlYobF?m$>j-yG=Ubz=T_dff3o05{|Gf~C15-}VF1O~Wu+_WcFrUg&uA zJsF;t9Ke{8PME^%2$1R0@LC4*R}(niRtR(X`x)GL?c(J#=4I$Rkv$ofL*ITE3CO(D z5b+x8o1D&gpMfX)e9*;f(&hDG-jJ?uCZgPP-w|h=KJ3ZRzgK?BeCD1EeT&ZQXT(2y zPlkQ=qWJq;w!u7C?8z|rj703F+WOF&7rxi3&B~d%{RYpBops@RtJ;R&Z<0AKk+!gP zBD!!~A~L&mB0PIs!mD~TK0XmHJ{hiu58>h;y!<-vP)Rg=s3bHfJ|>YNgH_@c9Alb3JD4A9@Be`?e zRms9#V-m5v&dEEg&P;ZKU)S=hlAR!*Q9M1lbMu%)dSPxN4SJ&G1(02curA3PcNHa9 zBVMF@T5{Lw1<7x&o{?;_nK&j9-cyjgadU3M+cQ0xx%tb!d2 z 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

RW@5$YLL&WwQ2#eQcD$g+{N0~&sxP51zJI@ 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
uint32_t ulTaskGetIdleRunTimeCounter( void );
+*
TickType_t xTaskGetIdleRunTimeCounter( void );
* * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS * must both be defined as 1 for this function to be available. The application @@ -1758,7 +1753,7 @@ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e9 * of the accumulated time value depends on the frequency of the timer * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. * While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total -* execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() +* execution time of each task into a buffer, xTaskGetIdleRunTimeCounter() * returns the total execution time of just the idle task. * * @return The total run time of the idle task. This is the amount of time the @@ -1766,10 +1761,10 @@ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e9 * frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and * portGET_RUN_TIME_COUNTER_VALUE() macros. * -* \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter +* \defgroup xTaskGetIdleRunTimeCounter xTaskGetIdleRunTimeCounter * \ingroup TaskUtils */ -uint32_t ulTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; +TickType_t xTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; /** * task. h @@ -2206,121 +2201,6 @@ uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait */ BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); -/** -* task. h -*
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
-* -* Clears the bits specified by the ulBitsToClear bit mask in the notification -* value of the task referenced by xTask. -* -* Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit architectures) to clear -* the notification value to 0. Set ulBitsToClear to 0 to query the task's -* notification value without clearing any bits. -* -* @return The value of the target task's notification value before the bits -* specified by ulBitsToClear were cleared. -* \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear -* \ingroup TaskNotifications -*/ -uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; - -/** - * task.h - *
void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut )
- * - * Capture the current time for future use with xTaskCheckForTimeOut(). - * - * @param pxTimeOut Pointer to a timeout object into which the current time - * is to be captured. The captured time includes the tick count and the number - * of times the tick count has overflowed since the system first booted. - * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState - * \ingroup TaskCtrl - */ -void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; - -/** - * task.h - *
BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );
- * - * Determines if pxTicksToWait ticks has passed since a time was captured - * using a call to vTaskSetTimeOutState(). The captured time includes the tick - * count and the number of times the tick count has overflowed. - * - * @param pxTimeOut The time status as captured previously using - * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated - * to reflect the current time status. - * @param pxTicksToWait The number of ticks to check for timeout i.e. if - * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by - * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. - * If the timeout has not occurred, pxTIcksToWait is updated to reflect the - * number of remaining ticks. - * - * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is - * returned and pxTicksToWait is updated to reflect the number of remaining - * ticks. - * - * @see https://www.freertos.org/xTaskCheckForTimeOut.html - * - * Example Usage: - *
-	// Driver library function used to receive uxWantedBytes from an Rx buffer
-	// that is filled by a UART interrupt. If there are not enough bytes in the
-	// Rx buffer then the task enters the Blocked state until it is notified that
-	// more data has been placed into the buffer. If there is still not enough
-	// data then the task re-enters the Blocked state, and xTaskCheckForTimeOut()
-	// is used to re-calculate the Block time to ensure the total amount of time
-	// spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This
-	// continues until either the buffer contains at least uxWantedBytes bytes,
-	// or the total amount of time spent in the Blocked state reaches
-	// MAX_TIME_TO_WAIT – at which point the task reads however many bytes are
-	// available up to a maximum of uxWantedBytes.
-
-	size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )
-	{
-	size_t uxReceived = 0;
-	TickType_t xTicksToWait = MAX_TIME_TO_WAIT;
-	TimeOut_t xTimeOut;
-
-		// Initialize xTimeOut.  This records the time at which this function
-		// was entered.
-		vTaskSetTimeOutState( &xTimeOut );
-
-		// Loop until the buffer contains the wanted number of bytes, or a
-		// timeout occurs.
-		while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
-		{
-			// The buffer didn't contain enough data so this task is going to
-			// enter the Blocked state. Adjusting xTicksToWait to account for
-			// any time that has been spent in the Blocked state within this
-			// function so far to ensure the total amount of time spent in the
-			// Blocked state does not exceed MAX_TIME_TO_WAIT.
-			if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
-			{
-				//Timed out before the wanted number of bytes were available,
-				// exit the loop.
-				break;
-			}
-
-			// Wait for a maximum of xTicksToWait ticks to be notified that the
-			// receive interrupt has placed more data into the buffer.
-			ulTaskNotifyTake( pdTRUE, xTicksToWait );
-		}
-
-		// Attempt to read uxWantedBytes from the receive buffer into pucBuffer.
-		// The actual number of bytes read (which might be less than
-		// uxWantedBytes) is returned.
-		uxReceived = UART_read_from_receive_buffer( pxUARTInstance,
-													pucBuffer,
-													uxWantedBytes );
-
-		return uxReceived;
-	}
- 
- * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut - * \ingroup TaskCtrl - */ -BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; - /*----------------------------------------------------------- * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES *----------------------------------------------------------*/ @@ -2437,6 +2317,17 @@ TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; */ TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; +/* + * Capture the current time status for future reference. + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/* + * Compare the time status now with that previously captured to see if the + * timeout has expired. + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; + /* * Shortcut used by the queue implementation to prevent unnecessary call to * taskYIELD(); @@ -2492,19 +2383,6 @@ void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) PRIVIL */ void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; -/* Correct the tick count value after the application code has held -interrupts disabled for an extended period. xTicksToCatchUp is the number -of tick interrupts that have been missed due to interrupts being disabled. -Its value is not computed automatically, so must be computed by the -application writer. - -This function is similar to vTaskStepTick(), however, unlike -vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a -time at which a task should be removed from the blocked state. That means -tasks may have to be removed from the blocked state as the tick count is -moved. */ -BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; - /* * Only available when configUSE_TICKLESS_IDLE is set to 1. * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h b/Firmware/ThirdParty/FreeRTOS/Source/include/timers.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/timers.h index 1b6d7f97..cb721797 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/timers.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -1234,8 +1234,8 @@ const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint /** * void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ); * - * Updates a timer to be either an auto-reload timer, in which case the timer - * automatically resets itself each time it expires, or a one-shot timer, in + * Updates a timer to be either an autoreload timer, in which case the timer + * automatically resets itself each time it expires, or a one shot timer, in * which case the timer will only expire once unless it is manually restarted. * * @param xTimer The handle of the timer being updated. @@ -1248,20 +1248,6 @@ const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint */ void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) PRIVILEGED_FUNCTION; -/** -* UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); -* -* Queries a timer to determine if it is an auto-reload timer, in which case the timer -* automatically resets itself each time it expires, or a one-shot timer, in -* which case the timer will only expire once unless it is manually restarted. -* -* @param xTimer The handle of the timer being queried. -* -* @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise -* pdFALSE is returned. -*/ -UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; - /** * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); * diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c b/Firmware/ThirdParty/FreeRTOS/Source/list.c similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c rename to Firmware/ThirdParty/FreeRTOS/Source/list.c index 7618ee8b..21dabdec 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/list.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/list.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c similarity index 100% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c rename to Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h similarity index 100% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h rename to Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/portmacro.h diff --git a/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c new file mode 100644 index 00000000..ce867ee6 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c @@ -0,0 +1,765 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM4F port. + *----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __VFP_FP__ + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0xFFUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXC_RETURN ( 0xfffffffd ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* For strict compliance with the Cortex-M spec the task start address should +have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ +#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* Let the user override the pre-loading of the initial LR with the address of +prvTaskExitError() in case it messes up unwinding of the stack in the +debugger. */ +#ifdef configTASK_RETURN_ADDRESS + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS +#else + #define portTASK_RETURN_ADDRESS prvTaskExitError +#endif + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__ (( naked )); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ) __attribute__ (( naked )); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvPortStartFirstTask( void ) __attribute__ (( naked )); + +/* + * Function to enable the VFP. + */ +static void vPortEnableVFP( void ) __attribute__ (( naked )); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * The number of SysTick increments that make up one tick period. + */ +#if( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ +volatile uint32_t ulDummy = 0; + + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + while( ulDummy == 0 ) + { + /* This file calls prvTaskExitError() after the scheduler has been + started to remove a compiler warning about the function being defined + but never called. ulDummy is used purely to quieten other warnings + about code appearing after this function is called - making ulDummy + volatile makes the compiler think the function could return and + therefore not output an 'unreachable code' warning for code that appears + after it. */ + } +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + __asm volatile ( + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " isb \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +static void prvPortStartFirstTask( void ) +{ + /* Start the first task. This also clears the bit that indicates the FPU is + in use in case the FPU was used before the scheduler was started - which + would otherwise result in the unnecessary leaving of space in the SVC stack + for lazy saving of FPU registers. */ + __asm volatile( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */ + " msr control, r0 \n" + " cpsie i \n" /* Globally enable interrupts. */ + " cpsie f \n" + " dsb \n" + " isb \n" + " svc 0 \n" /* System call to start first task. */ + " nop \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. + See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); + + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + #ifdef __NVIC_PRIO_BITS + { + /* Check the CMSIS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS ); + } + #endif + + #ifdef configPRIO_BITS + { + /* Check the FreeRTOS configuration that defines the number of + priority bits matches the number of priority bits actually queried + from the hardware. */ + configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS ); + } + #endif + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + vPortEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + prvPortStartFirstTask(); + + /* Should never get here as the tasks will now be executing! Call the task + exit error function to prevent compiler warnings about a static function + not being called in the case that the application writer overrides this + functionality by defining configTASK_RETURN_ADDRESS. Call + vTaskSwitchContext() so link time optimisation does not remove the + symbol. */ + vTaskSwitchContext(); + prvTaskExitError(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " isb \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */ + " it eq \n" + " vstmdbeq r0!, {s16-s31} \n" + " \n" + " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r0, r3} \n" + " mov r0, %0 \n" + " cpsid i \n" /* Errata workaround. */ + " msr basepri, r0 \n" + " dsb \n" + " isb \n" + " cpsie i \n" /* Errata workaround. */ + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r0, r3} \n" + " \n" + " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldr r0, [r1] \n" + " \n" + " ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */ + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */ + " it eq \n" + " vldmiaeq r0!, {s16-s31} \n" + " \n" + " msr psp, r0 \n" + " isb \n" + " \n" + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */ + #if WORKAROUND_PMU_CM001 == 1 + " push { r14 } \n" + " pop { pc } \n" + #endif + #endif + " \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + portDISABLE_INTERRUPTS(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portENABLE_INTERRUPTS(); +} +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE == 1 ) + + __attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __asm volatile( "cpsid i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + above. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( &xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ::: "memory" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + configPOST_SLEEP_PROCESSING( &xExpectedIdleTime ); + + /* Re-enable interrupts to allow the interrupt that brought the MCU + out of sleep mode to execute immediately. see comments above + __disable_interrupt() call above. */ + __asm volatile( "cpsie i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* Disable interrupts again because the clock is about to be stopped + and interrupts that execute while the clock is stopped will increase + any slippage between the time maintained by the RTOS and calendar + time. */ + __asm volatile( "cpsid i" ::: "memory" ); + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* Disable the SysTick clock without reading the + portNVIC_SYSTICK_CTRL_REG register to ensure the + portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, + the time the SysTick is stopped for is accounted for as best it can + be, but using the tickless mode will inevitably result in some tiny + drift of the time maintained by the kernel with respect to calendar + time*/ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT ); + + /* Determine if the SysTick clock has already counted to zero and + been set back to the current reload value (the reload back being + correct for the entire expected idle time) or if the SysTick is yet + to count to zero (in which case an interrupt other than the SysTick + must have brought the system out of sleep mode). */ + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt is already pending, and the SysTick count + reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* As the pending tick will be processed as soon as this + function exits, the tick value maintained by the tick is stepped + forward by one less than the time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Exit with interrpts enabled. */ + __asm volatile( "cpsie i" ::: "memory" ); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__attribute__(( weak )) void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if( configUSE_TICKLESS_IDLE == 1 ) + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Stop and clear the SysTick. */ + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +/* This is a naked function. */ +static void vPortEnableVFP( void ) +{ + __asm volatile + ( + " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */ + " ldr r1, [r0] \n" + " \n" + " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ + " str r1, [r0] \n" + " bx r14 " + ); +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredictable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + + diff --git a/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h new file mode 100644 index 00000000..62543ac7 --- /dev/null +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/portmacro.h @@ -0,0 +1,247 @@ +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL + + /* 32-bit tick type on a 32-bit architecture, so reads of the tick count do + not need to be guarded with a critical section. */ + #define portTICK_TYPE_IS_ATOMIC 1 +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +#define portYIELD() \ +{ \ + /* Set a PendSV to request a context switch. */ \ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \ + \ + /* Barriers are normally not required but do ensure the code is completely \ + within the specified behaviour for the architecture. */ \ + __asm volatile( "dsb" ::: "memory" ); \ + __asm volatile( "isb" ); \ +} + +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD() +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) +#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() +#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Generic helper function. */ + __attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap ) + { + uint8_t ucReturn; + + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" ); + return ucReturn; + } + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) ucPortCountLeadingZeros( ( uxReadyPriorities ) ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +#define portINLINE __inline + +#ifndef portFORCE_INLINE + #define portFORCE_INLINE inline __attribute__(( always_inline)) +#endif + +portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void ) +{ +uint32_t ulCurrentInterrupt; +BaseType_t xReturn; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" ); + + if( ulCurrentInterrupt == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} + +/*-----------------------------------------------------------*/ + +portFORCE_INLINE static void vPortRaiseBASEPRI( void ) +{ +uint32_t ulNewBASEPRI; + + __asm volatile + ( + " mov %0, %1 \n" \ + " cpsid i \n" \ + " msr basepri, %0 \n" \ + " isb \n" \ + " dsb \n" \ + " cpsie i \n" \ + :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); +} + +/*-----------------------------------------------------------*/ + +portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void ) +{ +uint32_t ulOriginalBASEPRI, ulNewBASEPRI; + + __asm volatile + ( + " mrs %0, basepri \n" \ + " mov %1, %2 \n" \ + " cpsid i \n" \ + " msr basepri, %1 \n" \ + " isb \n" \ + " dsb \n" \ + " cpsie i \n" \ + :"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); + + /* This return will not be reached but is necessary to prevent compiler + warnings. */ + return ulOriginalBASEPRI; +} +/*-----------------------------------------------------------*/ + +portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) +{ + __asm volatile + ( + " msr basepri, %0 " :: "r" ( ulNewMaskValue ) : "memory" + ); +} +/*-----------------------------------------------------------*/ + +#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" ) + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c b/Firmware/ThirdParty/FreeRTOS/Source/portable/MemMang/heap_4.c similarity index 87% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c rename to Firmware/ThirdParty/FreeRTOS/Source/portable/MemMang/heap_4.c index eaf443f4..d7cd8a5b 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/portable/MemMang/heap_4.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -97,12 +97,10 @@ static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( p /* Create a couple of list links to mark the start and end of the list. */ static BlockLink_t xStart, *pxEnd = NULL; -/* Keeps track of the number of calls to allocate and free memory as well as the -number of free bytes remaining, but says nothing about fragmentation. */ +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ static size_t xFreeBytesRemaining = 0U; static size_t xMinimumEverFreeBytesRemaining = 0U; -static size_t xNumberOfSuccessfulAllocations = 0; -static size_t xNumberOfSuccessfulFrees = 0; /* Gets set to the top bit of an size_t type. When this bit in the xBlockSize member of an BlockLink_t structure is set then the block belongs to the @@ -223,7 +221,6 @@ void *pvReturn = NULL; by the application and has no "next" block. */ pxBlock->xBlockSize |= xBlockAllocatedBit; pxBlock->pxNextFreeBlock = NULL; - xNumberOfSuccessfulAllocations++; } else { @@ -295,7 +292,6 @@ BlockLink_t *pxLink; xFreeBytesRemaining += pxLink->xBlockSize; traceFREE( pv, pxLink->xBlockSize ); prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); - xNumberOfSuccessfulFrees++; } ( void ) xTaskResumeAll(); } @@ -437,56 +433,4 @@ uint8_t *puc; mtCOVERAGE_TEST_MARKER(); } } -/*-----------------------------------------------------------*/ - -void vPortGetHeapStats( HeapStats_t *pxHeapStats ) -{ -BlockLink_t *pxBlock; -size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */ - - vTaskSuspendAll(); - { - pxBlock = xStart.pxNextFreeBlock; - - /* pxBlock will be NULL if the heap has not been initialised. The heap - is initialised automatically when the first allocation is made. */ - if( pxBlock != NULL ) - { - do - { - /* Increment the number of blocks and record the largest block seen - so far. */ - xBlocks++; - - if( pxBlock->xBlockSize > xMaxSize ) - { - xMaxSize = pxBlock->xBlockSize; - } - - if( pxBlock->xBlockSize < xMinSize ) - { - xMinSize = pxBlock->xBlockSize; - } - - /* Move to the next block in the chain until the last block is - reached. */ - pxBlock = pxBlock->pxNextFreeBlock; - } while( pxBlock != pxEnd ); - } - } - xTaskResumeAll(); - - pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize; - pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize; - pxHeapStats->xNumberOfFreeBlocks = xBlocks; - - taskENTER_CRITICAL(); - { - pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining; - pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations; - pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees; - pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining; - } - taskEXIT_CRITICAL(); -} diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c b/Firmware/ThirdParty/FreeRTOS/Source/queue.c similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c rename to Firmware/ThirdParty/FreeRTOS/Source/queue.c index b3203b80..d882bf67 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/queue.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/queue.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -203,7 +203,7 @@ static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer * Checks to see if a queue is a member of a queue set, and if so, notifies * the queue set that the queue contains data. */ - static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; #endif /* @@ -373,10 +373,17 @@ Queue_t * const pxQueue = xQueue; configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); - /* Allocate enough space to hold the maximum number of items that - can be in the queue at any time. It is valid for uxItemSize to be - zero in the case the queue is used as a semaphore. */ - xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + if( uxItemSize == ( UBaseType_t ) 0 ) + { + /* There is not going to be a queue storage area. */ + xQueueSizeInBytes = ( size_t ) 0; + } + else + { + /* Allocate enough space to hold the maximum number of items that + can be in the queue at any time. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } /* Allocate the queue and storage area. Justification for MISRA deviation as follows: pvPortMalloc() always ensures returned memory @@ -770,7 +777,7 @@ Queue_t * const pxQueue = xQueue; #if ( configUSE_QUEUE_SETS == 1 ) { - const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); @@ -783,7 +790,7 @@ Queue_t * const pxQueue = xQueue; in the queue has not changed. */ mtCOVERAGE_TEST_MARKER(); } - else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + else if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to @@ -983,7 +990,6 @@ Queue_t * const pxQueue = xQueue; if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) { const int8_t cTxLock = pxQueue->cTxLock; - const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; traceQUEUE_SEND_FROM_ISR( pxQueue ); @@ -1002,14 +1008,7 @@ Queue_t * const pxQueue = xQueue; { if( pxQueue->pxQueueSetContainer != NULL ) { - if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) - { - /* Do not notify the queue set as an existing item - was overwritten in the queue so the number of items - in the queue has not changed. */ - mtCOVERAGE_TEST_MARKER(); - } - else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to @@ -1082,9 +1081,6 @@ Queue_t * const pxQueue = xQueue; { mtCOVERAGE_TEST_MARKER(); } - - /* Not used in this path. */ - ( void ) uxPreviousMessagesWaiting; } #endif /* configUSE_QUEUE_SETS */ } @@ -1177,7 +1173,7 @@ Queue_t * const pxQueue = xQueue; { if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) != pdFALSE ) { /* The semaphore is a member of a queue set, and posting to the queue set caused a higher priority @@ -2189,7 +2185,7 @@ static void prvUnlockQueue( Queue_t * const pxQueue ) { if( pxQueue->pxQueueSetContainer != NULL ) { - if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) != pdFALSE ) { /* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to unblock. @@ -2879,7 +2875,7 @@ Queue_t * const pxQueue = xQueue; #if ( configUSE_QUEUE_SETS == 1 ) - static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) { Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer; BaseType_t xReturn = pdFALSE; @@ -2896,7 +2892,7 @@ Queue_t * const pxQueue = xQueue; traceQUEUE_SEND( pxQueueSetContainer ); /* The data copied is the handle of the queue that contains data. */ - xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK ); + xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, xCopyPosition ); if( cTxLock == queueUNLOCKED ) { diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c b/Firmware/ThirdParty/FreeRTOS/Source/stream_buffer.c similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c rename to Firmware/ThirdParty/FreeRTOS/Source/stream_buffer.c index 7ad5d54a..85519707 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/stream_buffer.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c b/Firmware/ThirdParty/FreeRTOS/Source/tasks.c similarity index 96% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c rename to Firmware/ThirdParty/FreeRTOS/Source/tasks.c index d9d4feb4..db0516d7 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/tasks.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/tasks.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -300,10 +300,7 @@ typedef struct tskTaskControlBlock /* The old naming convention is used to pr responsible for resulting newlib operation. User must be familiar with newlib and must provide system-wide implementations of the necessary stubs. Be warned that (at the time of writing) the current newlib design - implements a system-wide malloc() that must be provided with locks. - - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ + implements a system-wide malloc() that must be provided with locks. */ struct _reent xNewLib_reent; #endif @@ -340,23 +337,23 @@ PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but doing so breaks some kernel aware debuggers and debuggers that rely on removing the static qualifier. */ -PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ -PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ -PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ -PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ] = { 0 };/*< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1 = { 0 }; /*< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2 = { 0 }; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList = NULL; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList = NULL; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList = { 0 }; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ #if( INCLUDE_vTaskDelete == 1 ) - PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ +PRIVILEGED_DATA static List_t xTasksWaitingTermination = { 0 }; /*< Tasks that have been deleted - but their memory not yet freed. */ PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; #endif #if ( INCLUDE_vTaskSuspend == 1 ) - PRIVILEGED_DATA static List_t xSuspendedTaskList; /*< Tasks that are currently suspended. */ + PRIVILEGED_DATA static List_t xSuspendedTaskList = { 0 }; /*< Tasks that are currently suspended. */ #endif @@ -371,7 +368,7 @@ PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseTyp PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; -PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; @@ -996,9 +993,7 @@ UBaseType_t x; #if ( configUSE_NEWLIB_REENTRANT == 1 ) { - /* Initialise this task's Newlib reent structure. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ + /* Initialise this task's Newlib reent structure. */ _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); } #endif @@ -1169,7 +1164,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) being deleted. */ pxTCB = prvGetTCBFromHandle( xTaskToDelete ); - /* Remove task from the ready/delayed list. */ + /* Remove task from the ready list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -1209,10 +1204,6 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) check the xTasksWaitingTermination list. */ ++uxDeletedTasksWaitingCleanUp; - /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as - portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ - traceTASK_DELETE( pxTCB ); - /* The pre-delete hook is primarily for the Windows simulator, in which Windows specific clean up operations are performed, after which it is not possible to yield away from this task - @@ -1223,13 +1214,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) else { --uxCurrentNumberOfTasks; - traceTASK_DELETE( pxTCB ); prvDeleteTCB( pxTCB ); /* Reset the next expected unblock time in case it referred to the task that has just been deleted. */ prvResetNextTaskUnblockTime(); } + + traceTASK_DELETE( pxTCB ); } taskEXIT_CRITICAL(); @@ -2049,9 +2041,7 @@ BaseType_t xReturn; #if ( configUSE_NEWLIB_REENTRANT == 1 ) { /* Switch Newlib's _impure_ptr variable to point to the _reent - structure specific to the task that will run first. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ + structure specific to the task that will run first. */ _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ @@ -2113,17 +2103,7 @@ void vTaskSuspendAll( void ) BaseType_t. Please read Richard Barry's reply in the following link to a post in the FreeRTOS support forum before reporting this as a bug! - http://goo.gl/wu4acr */ - - /* portSOFRWARE_BARRIER() is only implemented for emulated/simulated ports that - do not otherwise exhibit real time behaviour. */ - portSOFTWARE_BARRIER(); - - /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment - is used to allow calls to vTaskSuspendAll() to nest. */ ++uxSchedulerSuspended; - - /* Enforces ordering for ports and optimised compilers that may otherwise place - the above increment elsewhere. */ portMEMORY_BARRIER(); } /*----------------------------------------------------------*/ @@ -2250,9 +2230,9 @@ BaseType_t xAlreadyYielded = pdFALSE; not slip, and that any delayed tasks are resumed at the correct time. */ { - TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ + UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */ - if( xPendedCounts > ( TickType_t ) 0U ) + if( uxPendedCounts > ( UBaseType_t ) 0U ) { do { @@ -2264,10 +2244,10 @@ BaseType_t xAlreadyYielded = pdFALSE; { mtCOVERAGE_TEST_MARKER(); } - --xPendedCounts; - } while( xPendedCounts > ( TickType_t ) 0U ); + --uxPendedCounts; + } while( uxPendedCounts > ( UBaseType_t ) 0U ); - xPendedTicks = 0; + uxPendedTicks = 0; } else { @@ -2606,24 +2586,6 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than #endif /* configUSE_TICKLESS_IDLE */ /*----------------------------------------------------------*/ -BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) -{ -BaseType_t xYieldRequired = pdFALSE; - - /* Must not be called with the scheduler suspended as the implementation - relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ - configASSERT( uxSchedulerSuspended == 0 ); - - /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when - the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ - vTaskSuspendAll(); - xPendedTicks += xTicksToCatchUp; - xYieldRequired = xTaskResumeAll(); - - return xYieldRequired; -} -/*----------------------------------------------------------*/ - #if ( INCLUDE_xTaskAbortDelay == 1 ) BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) @@ -2655,10 +2617,6 @@ BaseType_t xYieldRequired = pdFALSE; if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) { ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); - - /* This lets the task know it was forcibly removed from the - blocked state so it should not re-evaluate its block time and - then block again. */ pxTCB->ucDelayAborted = pdTRUE; } else @@ -2835,7 +2793,7 @@ BaseType_t xSwitchRequired = pdFALSE; { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ - if( xPendedTicks == ( TickType_t ) 0 ) + if( uxPendedTicks == ( UBaseType_t ) 0U ) { vApplicationTickHook(); } @@ -2845,23 +2803,10 @@ BaseType_t xSwitchRequired = pdFALSE; } } #endif /* configUSE_TICK_HOOK */ - - #if ( configUSE_PREEMPTION == 1 ) - { - if( xYieldPending != pdFALSE ) - { - xSwitchRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_PREEMPTION */ } else { - ++xPendedTicks; + ++uxPendedTicks; /* The tick hook gets called at regular intervals, even if the scheduler is locked. */ @@ -2872,6 +2817,19 @@ BaseType_t xSwitchRequired = pdFALSE; #endif } + #if ( configUSE_PREEMPTION == 1 ) + { + if( xYieldPending != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + return xSwitchRequired; } /*-----------------------------------------------------------*/ @@ -2986,7 +2944,6 @@ BaseType_t xSwitchRequired = pdFALSE; #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ -__attribute__((used)) void vTaskSwitchContext( void ) { if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) @@ -3052,9 +3009,7 @@ void vTaskSwitchContext( void ) #if ( configUSE_NEWLIB_REENTRANT == 1 ) { /* Switch Newlib's _impure_ptr variable to point to the _reent - structure specific to this task. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ + structure specific to this task. */ _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); } #endif /* configUSE_NEWLIB_REENTRANT */ @@ -3221,20 +3176,6 @@ TCB_t *pxUnblockedTCB; configASSERT( pxUnblockedTCB ); ( void ) uxListRemove( pxEventListItem ); - #if( configUSE_TICKLESS_IDLE != 0 ) - { - /* If a task is blocked on a kernel object then xNextTaskUnblockTime - might be set to the blocked task's time out time. If the task is - unblocked for a reason other than a timeout xNextTaskUnblockTime is - normally left unchanged, because it is automatically reset to a new - value when the tick count equals xNextTaskUnblockTime. However if - tickless idling is used it might be more important to enter sleep mode - at the earliest possible time - so reset xNextTaskUnblockTime here to - ensure it is updated at the earliest possible time. */ - prvResetNextTaskUnblockTime(); - } - #endif - /* Remove the task from the delayed list and add it to the ready list. The scheduler is suspended so interrupts will not be accessing the ready lists. */ @@ -3515,8 +3456,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) const UBaseType_t uxNonApplicationTasks = 1; eSleepModeStatus eReturn = eStandardSleep; - /* This function must be called from a critical section. */ - if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) { /* A task was made ready while the scheduler was suspended. */ @@ -3558,7 +3497,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { pxTCB = prvGetTCBFromHandle( xTaskToSet ); - configASSERT( pxTCB != NULL ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; } } @@ -3893,9 +3831,7 @@ static void prvCheckTasksWaitingTermination( void ) portCLEAN_UP_TCB( pxTCB ); /* Free up the memory allocated by the scheduler for the task. It is up - to the task to free any memory allocated at the application level. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ + to the task to free any memory allocated at the application level. */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) { _reclaim_reent( &( pxTCB->xNewLib_reent ) ); @@ -4045,10 +3981,7 @@ TCB_t *pxTCB; { if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { - /* It is known that the task is in its ready list so - there is no need to check again and the port level - reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); + taskRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority ); } else { @@ -4128,7 +4061,7 @@ TCB_t *pxTCB; the mutex. If the mutex is held by a task then it cannot be given from an interrupt, and if a mutex is given by the holding task then it must be the running state task. Remove - the holding task from the ready/delayed list. */ + the holding task from the ready list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); @@ -4249,10 +4182,7 @@ TCB_t *pxTCB; { if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) { - /* It is known that the task is in its ready list so - there is no need to check again and the port level - reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); } else { @@ -5106,6 +5036,7 @@ TickType_t uxReturn; } #endif /* configUSE_TASK_NOTIFICATIONS */ + /*-----------------------------------------------------------*/ #if( configUSE_TASK_NOTIFICATIONS == 1 ) @@ -5139,39 +5070,11 @@ TickType_t uxReturn; #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) - { - TCB_t *pxTCB; - uint32_t ulReturn; - - /* If null is passed in here then it is the calling task that is having - its notification state cleared. */ - pxTCB = prvGetTCBFromHandle( xTask ); - - taskENTER_CRITICAL(); - { - /* Return the notification as it was before the bits were cleared, - then clear the bit mask. */ - ulReturn = pxCurrentTCB->ulNotifiedValue; - pxTCB->ulNotifiedValue &= ~ulBitsToClear; - } - taskEXIT_CRITICAL(); - - return ulReturn; - } - -#endif /* configUSE_TASK_NOTIFICATIONS */ -/*-----------------------------------------------------------*/ - #if( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) - - uint32_t ulTaskGetIdleRunTimeCounter( void ) + TickType_t xTaskGetIdleRunTimeCounter( void ) { return xIdleTaskHandle->ulRunTimeCounter; } - #endif /*-----------------------------------------------------------*/ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c b/Firmware/ThirdParty/FreeRTOS/Source/timers.c similarity index 97% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c rename to Firmware/ThirdParty/FreeRTOS/Source/timers.c index 00200b8f..59b3840d 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/timers.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/timers.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -132,10 +132,10 @@ timer service task is allowed to access these lists. xActiveTimerList1 and xActiveTimerList2 could be at function scope but that breaks some kernel aware debuggers, and debuggers that reply on removing the static qualifier. */ -PRIVILEGED_DATA static List_t xActiveTimerList1; -PRIVILEGED_DATA static List_t xActiveTimerList2; -PRIVILEGED_DATA static List_t *pxCurrentTimerList; -PRIVILEGED_DATA static List_t *pxOverflowTimerList; +PRIVILEGED_DATA static List_t xActiveTimerList1 = { 0 }; +PRIVILEGED_DATA static List_t xActiveTimerList2 = { 0 }; +PRIVILEGED_DATA static List_t *pxCurrentTimerList = NULL; +PRIVILEGED_DATA static List_t *pxOverflowTimerList = NULL; /* A queue that is used to send commands to the timer service task. */ PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; @@ -182,7 +182,7 @@ static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const Tic /* * An active timer has reached its expire time. Reload the timer if it is an - * auto-reload timer, then call its callback. + * auto reload timer, then call its callback. */ static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; @@ -292,7 +292,7 @@ BaseType_t xReturn = pdFAIL; if( pxNewTimer != NULL ) { /* Status is thus far zero as the timer is not created statically - and has not been started. The auto-reload bit may get set in + and has not been started. The autoreload bit may get set in prvInitialiseNewTimer. */ pxNewTimer->ucStatus = 0x00; prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); @@ -334,7 +334,7 @@ BaseType_t xReturn = pdFAIL; { /* Timers can be created statically or dynamically so note this timer was created statically in case it is later deleted. The - auto-reload bit may get set in prvInitialiseNewTimer(). */ + autoreload bit may get set in prvInitialiseNewTimer(). */ pxNewTimer->ucStatus = tmrSTATUS_IS_STATICALLY_ALLOCATED; prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); @@ -459,31 +459,6 @@ Timer_t * pxTimer = xTimer; } /*-----------------------------------------------------------*/ -UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) -{ -Timer_t * pxTimer = xTimer; -UBaseType_t uxReturn; - - configASSERT( xTimer ); - taskENTER_CRITICAL(); - { - if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) == 0 ) - { - /* Not an auto-reload timer. */ - uxReturn = ( UBaseType_t ) pdFALSE; - } - else - { - /* Is an auto-reload timer. */ - uxReturn = ( UBaseType_t ) pdTRUE; - } - } - taskEXIT_CRITICAL(); - - return uxReturn; -} -/*-----------------------------------------------------------*/ - TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) { Timer_t * pxTimer = xTimer; @@ -514,7 +489,7 @@ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTi ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); traceTIMER_EXPIRED( pxTimer ); - /* If the timer is an auto-reload timer then calculate the next + /* If the timer is an auto reload timer then calculate the next expiry time and re-insert the timer in the list of active timers. */ if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) { diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_can.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_cortex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_def.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_dma_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_flash_ramfunc.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pcd_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_pwr_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_rcc_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usb.h diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_adc_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_can.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c diff --git a/Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c b/Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c similarity index 100% rename from Firmware/Board/v3/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c rename to Firmware/ThirdParty/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_usb.c diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h new file mode 100644 index 00000000..2d838d14 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h @@ -0,0 +1,183 @@ +/** + ****************************************************************************** + * @file usbd_cdc.h + * @author MCD Application Team + * @brief header file for the usbd_cdc.c file. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USB_CDC_H +#define __USB_CDC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ioreq.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup usbd_cdc + * @brief This file is the Header file for usbd_cdc.c + * @{ + */ + + +/** @defgroup usbd_cdc_Exported_Defines + * @{ + */ +#define CDC_IN_EP 0x81U /* EP1 for data IN */ +#define CDC_OUT_EP 0x01U /* EP1 for data OUT */ +#define CDC_CMD_EP 0x82U /* EP2 for CDC commands */ +#define ODRIVE_IN_EP 0x83 /* EP3 IN: ODrive device TX endpoint */ +#define ODRIVE_OUT_EP 0x03 /* EP3 OUT: ODrive device RX endpoint */ + +#ifndef CDC_HS_BINTERVAL +#define CDC_HS_BINTERVAL 0x10U +#endif /* CDC_HS_BINTERVAL */ + +#ifndef CDC_FS_BINTERVAL +#define CDC_FS_BINTERVAL 0x10U +#endif /* CDC_FS_BINTERVAL */ + +/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */ +#define CDC_DATA_HS_MAX_PACKET_SIZE 64U /* Endpoint IN & OUT Packet size */ +#define CDC_DATA_FS_MAX_PACKET_SIZE 64U /* Endpoint IN & OUT Packet size */ +#define CDC_CMD_PACKET_SIZE 8U /* Control Endpoint Packet size */ + +#define USB_CDC_CONFIG_DESC_SIZ (67 + 39) +#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE +#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE + +#define CDC_DATA_FS_IN_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE +#define CDC_DATA_FS_OUT_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE + +/*---------------------------------------------------------------------*/ +/* CDC definitions */ +/*---------------------------------------------------------------------*/ +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00U +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01U +#define CDC_SET_COMM_FEATURE 0x02U +#define CDC_GET_COMM_FEATURE 0x03U +#define CDC_CLEAR_COMM_FEATURE 0x04U +#define CDC_SET_LINE_CODING 0x20U +#define CDC_GET_LINE_CODING 0x21U +#define CDC_SET_CONTROL_LINE_STATE 0x22U +#define CDC_SEND_BREAK 0x23U + +/** + * @} + */ + + +/** @defgroup USBD_CORE_Exported_TypesDefinitions + * @{ + */ + +/** + * @} + */ +typedef struct +{ + uint32_t bitrate; + uint8_t format; + uint8_t paritytype; + uint8_t datatype; +} USBD_CDC_LineCodingTypeDef; + +typedef struct _USBD_CDC_Itf +{ + int8_t (* Init)(void); + int8_t (* DeInit)(void); + int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length); + int8_t (* Receive)(uint8_t *Buf, uint32_t *Len, uint8_t endpoint_pair); + int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum); +} USBD_CDC_ItfTypeDef; + +typedef struct +{ + uint8_t* Buffer; + uint32_t Length; + volatile uint8_t State; +} +USBD_CDC_EP_HandleTypeDef; + +typedef struct +{ + uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE / 4U]; /* Force 32bits alignment */ + uint8_t CmdOpCode; + uint8_t CmdLength; + + USBD_CDC_EP_HandleTypeDef CDC_Tx; + USBD_CDC_EP_HandleTypeDef CDC_Rx; + + USBD_CDC_EP_HandleTypeDef ODRIVE_Tx; + USBD_CDC_EP_HandleTypeDef ODRIVE_Rx; + +} USBD_CDC_HandleTypeDef; + + + +/** @defgroup USBD_CORE_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_CORE_Exported_Variables + * @{ + */ + +extern USBD_ClassTypeDef USBD_CDC; +#define USBD_CDC_CLASS &USBD_CDC +/** + * @} + */ + +/** @defgroup USB_CORE_Exported_Functions + * @{ + */ +uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, + USBD_CDC_ItfTypeDef *fops); + +uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, + uint32_t length, uint8_t endpoint_pair); + +uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, uint8_t endpoint_pair); +uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair); +uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_CDC_H */ +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c similarity index 75% rename from Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c rename to Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c index 7fabf6ee..4326cf73 100644 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c @@ -2,23 +2,21 @@ ****************************************************************************** * @file usbd_cdc.c * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 - * @brief This file provides the high layer firmware functions to manage the + * @brief This file provides the high layer firmware functions to manage the * following functionalities of the USB CDC Class: * - Initialization and Configuration of high and low layer * - Enumeration as CDC Device (and enumeration for each implemented memory interface) * - OUT/IN data transfer * - Command IN transfer (class requests management) * - Error management - * + * * @verbatim - * - * =================================================================== + * + * =================================================================== * CDC Class Driver Description - * =================================================================== + * =================================================================== * This driver manages the "Universal Serial Bus Class Definitions for Communications Devices - * Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus + * Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus * Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007" * This driver implements the following aspects of the specification: * - Device descriptor management @@ -28,35 +26,34 @@ * - Abstract Control Model compliant * - Union Functional collection (using 1 IN endpoint for control) * - Data interface class - * + * * These aspects may be enriched or modified for a specific user application. - * - * This driver doesn't implement the following aspects of the specification + * + * This driver doesn't implement the following aspects of the specification * (but it is possible to manage these features with some modifications on this driver): * - Any class-specific aspect relative to communication classes should be managed by user application. * - All communication classes other than PSTN are not managed - * + * * @endverbatim - * + * ****************************************************************************** * @attention * - *

© COPYRIGHT 2015 STMicroelectronics

+ *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

* - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** - */ + */ + +/* BSPDependencies +- "stm32xxxxx_{eval}{discovery}{nucleo_144}.c" +- "stm32xxxxx_{eval}{discovery}_io.c" +EndBSPDependencies */ /* Includes ------------------------------------------------------------------*/ #include "usbd_cdc.h" @@ -65,75 +62,59 @@ #include #include + /** @addtogroup STM32_USB_DEVICE_LIBRARY * @{ */ -/** @defgroup USBD_CDC +/** @defgroup USBD_CDC * @brief usbd core module * @{ - */ + */ /** @defgroup USBD_CDC_Private_TypesDefinitions * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Defines * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Macros * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_FunctionPrototypes * @{ */ +static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev); -static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, - uint8_t cfgidx); - -static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, - uint8_t cfgidx); - -static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, - USBD_SetupReqTypedef *req); - -static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, - uint8_t epnum); - -static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, - uint8_t epnum); - -static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev); - -static uint8_t *USBD_CDC_GetFSCfgDesc (uint16_t *length); - -static uint8_t *USBD_CDC_GetHSCfgDesc (uint16_t *length); - -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length); - -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length); - -uint8_t *USBD_CDC_GetDeviceQualifierDescriptor (uint16_t *length); - +static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length); +static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length); +static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length); +static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length); +uint8_t *USBD_CDC_GetDeviceQualifierDescriptor(uint16_t *length); static uint8_t USBD_WinUSBComm_SetupVendor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -//static uint8_t * USBD_GetUsrStrDescriptor(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); /* USB Standard Device Descriptor */ __ALIGN_BEGIN static uint8_t USBD_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = @@ -152,15 +133,15 @@ __ALIGN_BEGIN static uint8_t USBD_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_ /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Variables * @{ - */ + */ /* CDC interface class callbacks structure */ -USBD_ClassTypeDef USBD_CDC = +USBD_ClassTypeDef USBD_CDC = { USBD_CDC_Init, USBD_CDC_DeInit, @@ -171,10 +152,10 @@ USBD_ClassTypeDef USBD_CDC = USBD_CDC_DataOut, NULL, NULL, - NULL, - USBD_CDC_GetHSCfgDesc, - USBD_CDC_GetFSCfgDesc, - USBD_CDC_GetOtherSpeedCfgDesc, + NULL, + USBD_CDC_GetHSCfgDesc, + USBD_CDC_GetFSCfgDesc, + USBD_CDC_GetOtherSpeedCfgDesc, USBD_CDC_GetDeviceQualifierDescriptor, USBD_UsrStrDescriptor }; @@ -253,7 +234,7 @@ __ALIGN_BEGIN uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = 0x03, /* bmAttributes: Interrupt */ LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */ HIBYTE(CDC_CMD_PACKET_SIZE), - 0x10, /* bInterval: */ + CDC_HS_BINTERVAL, /* bInterval: */ /*---------------------------------------------------------------------------*/ /*Data class interface descriptor*/ @@ -329,14 +310,13 @@ __ALIGN_BEGIN uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = 0x00, /* bInterval: ignore for Bulk transfer */ }; - /** * @} - */ + */ /** @defgroup USBD_CDC_Private_Functions * @{ - */ + */ /** * @brief USBD_CDC_Init @@ -345,40 +325,54 @@ __ALIGN_BEGIN uint8_t USBD_CDC_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = * @param cfgidx: Configuration index * @retval status */ -static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, - uint8_t cfgidx) +static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { - uint8_t ret = 0; - USBD_CDC_HandleTypeDef *hcdc; - - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { + UNUSED(cfgidx); + USBD_CDC_HandleTypeDef *hcdc; + + hcdc = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef)); + + if (hcdc == NULL) + { + pdev->pClassData = NULL; + return (uint8_t)USBD_EMEM; + } + + pdev->pClassData = (void *)hcdc; + + if (pdev->dev_speed == USBD_SPEED_HIGH) + { /* Open EP IN */ - USBD_LL_OpenEP(pdev, - CDC_IN_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_HS_IN_PACKET_SIZE); - - /* Open EP OUT */ - USBD_LL_OpenEP(pdev, - CDC_OUT_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_HS_OUT_PACKET_SIZE); - + (void)USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, + CDC_DATA_HS_IN_PACKET_SIZE); + + pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U; + + /* Open EP OUT */ + (void)USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, + CDC_DATA_HS_OUT_PACKET_SIZE); + + pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U; + + /* Set bInterval for CDC CMD Endpoint */ + pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = CDC_HS_BINTERVAL; } else { /* Open EP IN */ - USBD_LL_OpenEP(pdev, - CDC_IN_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_FS_IN_PACKET_SIZE); - - /* Open EP OUT */ - USBD_LL_OpenEP(pdev, - CDC_OUT_EP, - USBD_EP_TYPE_BULK, - CDC_DATA_FS_OUT_PACKET_SIZE); + (void)USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, + CDC_DATA_FS_IN_PACKET_SIZE); + + pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U; + + /* Open EP OUT */ + (void)USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, + CDC_DATA_FS_OUT_PACKET_SIZE); + + pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U; + + /* Set bInterval for CMD Endpoint */ + pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = CDC_FS_BINTERVAL; } /* Open ODrive IN endpoint */ @@ -387,62 +381,49 @@ static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, USBD_EP_TYPE_BULK, pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_IN_PACKET_SIZE : CDC_DATA_FS_IN_PACKET_SIZE); + pdev->ep_in[ODRIVE_IN_EP & 0xFU].is_used = 1U; + /* Open ODrive OUT endpoint */ USBD_LL_OpenEP(pdev, ODRIVE_OUT_EP, USBD_EP_TYPE_BULK, pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_OUT_PACKET_SIZE : CDC_DATA_FS_OUT_PACKET_SIZE); + pdev->ep_out[ODRIVE_OUT_EP & 0xFU].is_used = 1U; + /* Open Command IN EP */ - USBD_LL_OpenEP(pdev, - CDC_CMD_EP, - USBD_EP_TYPE_INTR, - CDC_CMD_PACKET_SIZE); - - - pdev->pClassData = USBD_malloc(sizeof (USBD_CDC_HandleTypeDef)); - - if(pdev->pClassData == NULL) + (void)USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE); + pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U; + + /* Init physical Interface components */ + ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); + + /* Init Xfer states */ + hcdc->CDC_Tx.State = 0; + hcdc->CDC_Rx.State = 0; + hcdc->ODRIVE_Tx.State = 0; + hcdc->ODRIVE_Rx.State = 0; + + if (pdev->dev_speed == USBD_SPEED_HIGH) { - ret = 1; + /* Prepare Out endpoint to receive next packet */ + (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->CDC_Rx.Buffer, + CDC_DATA_HS_OUT_PACKET_SIZE); } else { - hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - /* Init physical Interface components */ - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); - - /* Init Xfer states */ - hcdc->CDC_Tx.State = 0; - hcdc->CDC_Rx.State = 0; - hcdc->ODRIVE_Tx.State = 0; - hcdc->ODRIVE_Rx.State = 0; - - if(pdev->dev_speed == USBD_SPEED_HIGH ) - { - /* Prepare Out endpoint to receive next packet */ - USBD_LL_PrepareReceive(pdev, - CDC_OUT_EP, - hcdc->CDC_Rx.Buffer, - CDC_DATA_HS_OUT_PACKET_SIZE); - } - else - { - /* Prepare Out endpoint to receive next packet */ - USBD_LL_PrepareReceive(pdev, - CDC_OUT_EP, - hcdc->CDC_Rx.Buffer, - CDC_DATA_FS_OUT_PACKET_SIZE); - } - - /* Prepare ODrive Out endpoint to receive next packet */ - USBD_LL_PrepareReceive(pdev, - ODRIVE_OUT_EP, - hcdc->ODRIVE_Rx.Buffer, - pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_OUT_PACKET_SIZE : CDC_DATA_FS_OUT_PACKET_SIZE); + /* Prepare Out endpoint to receive next packet */ + (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->CDC_Rx.Buffer, + CDC_DATA_FS_OUT_PACKET_SIZE); } - return ret; + + /* Prepare ODrive Out endpoint to receive next packet */ + USBD_LL_PrepareReceive(pdev, + ODRIVE_OUT_EP, + hcdc->ODRIVE_Rx.Buffer, + pdev->dev_speed == USBD_SPEED_HIGH ? CDC_DATA_HS_OUT_PACKET_SIZE : CDC_DATA_FS_OUT_PACKET_SIZE); + + return (uint8_t)USBD_OK; } /** @@ -452,40 +433,40 @@ static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, * @param cfgidx: Configuration index * @retval status */ -static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, - uint8_t cfgidx) +static uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { - uint8_t ret = 0; - + UNUSED(cfgidx); + uint8_t ret = 0U; + /* Close EP IN */ - USBD_LL_CloseEP(pdev, - CDC_IN_EP); - + (void)USBD_LL_CloseEP(pdev, CDC_IN_EP); + pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 0U; + /* Close EP OUT */ - USBD_LL_CloseEP(pdev, - CDC_OUT_EP); - + (void)USBD_LL_CloseEP(pdev, CDC_OUT_EP); + pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 0U; + /* Close Command IN EP */ - USBD_LL_CloseEP(pdev, - CDC_CMD_EP); - + (void)USBD_LL_CloseEP(pdev, CDC_CMD_EP); + pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 0U; + pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = 0U; + /* Close EP IN */ - USBD_LL_CloseEP(pdev, - ODRIVE_IN_EP); - + (void)USBD_LL_CloseEP(pdev, ODRIVE_IN_EP); + pdev->ep_in[ODRIVE_IN_EP & 0xFU].is_used = 0U; + /* Close EP OUT */ - USBD_LL_CloseEP(pdev, - ODRIVE_OUT_EP); - - + (void)USBD_LL_CloseEP(pdev, ODRIVE_OUT_EP); + pdev->ep_out[ODRIVE_OUT_EP & 0xFU].is_used = 0U; + /* DeInit physical Interface components */ - if(pdev->pClassData != NULL) + if (pdev->pClassData != NULL) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->DeInit(); - USBD_free(pdev->pClassData); + (void)USBD_free(pdev->pClassData); pdev->pClassData = NULL; } - + return ret; } @@ -496,65 +477,97 @@ static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, * @param req: usb requests * @retval status */ -static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, - USBD_SetupReqTypedef *req) +static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) { - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - static uint8_t ifalt = 0; - + USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; + uint8_t ifalt = 0U; + uint16_t status_info = 0U; + USBD_StatusTypeDef ret = USBD_OK; + switch (req->bmRequest & USB_REQ_TYPE_MASK) { - case USB_REQ_TYPE_CLASS : - if (req->wLength) + case USB_REQ_TYPE_CLASS: + if (req->wLength != 0U) { - if (req->bmRequest & 0x80) + if ((req->bmRequest & 0x80U) != 0U) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, (uint8_t *)hcdc->data, req->wLength); - USBD_CtlSendData (pdev, - (uint8_t *)hcdc->data, - req->wLength); + + (void)USBD_CtlSendData(pdev, (uint8_t *)hcdc->data, req->wLength); } else { hcdc->CmdOpCode = req->bRequest; - hcdc->CmdLength = req->wLength; - - USBD_CtlPrepareRx (pdev, - (uint8_t *)hcdc->data, - req->wLength); + hcdc->CmdLength = (uint8_t)req->wLength; + + (void)USBD_CtlPrepareRx(pdev, (uint8_t *)hcdc->data, req->wLength); } - } else { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, - (uint8_t*)req, - 0); + (uint8_t *)req, 0U); } break; case USB_REQ_TYPE_STANDARD: switch (req->bRequest) - { - case USB_REQ_GET_INTERFACE : - USBD_CtlSendData (pdev, - &ifalt, - 1); + { + case USB_REQ_GET_STATUS: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U); + } + else + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } break; - - case USB_REQ_SET_INTERFACE : + + case USB_REQ_GET_INTERFACE: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + (void)USBD_CtlSendData(pdev, &ifalt, 1U); + } + else + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_SET_INTERFACE: + if (pdev->dev_state != USBD_STATE_CONFIGURED) + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_CLEAR_FEATURE: + break; + + case USB_REQ_TYPE_VENDOR: + return USBD_WinUSBComm_SetupVendor(pdev, req); + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; break; } + break; - case USB_REQ_TYPE_VENDOR: - return USBD_WinUSBComm_SetupVendor(pdev, req); - - default: + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; break; } - return USBD_OK; + + return (uint8_t)ret; } /** @@ -564,11 +577,28 @@ static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, * @param epnum: endpoint number * @retval status */ -static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) +static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) { - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - if(pdev->pClassData != NULL) + USBD_CDC_HandleTypeDef *hcdc; + PCD_HandleTypeDef *hpcd = pdev->pData; + + if (pdev->pClassData == NULL) + { + return (uint8_t)USBD_FAIL; + } + + hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; + + if ((pdev->ep_in[epnum].total_length > 0U) && + ((pdev->ep_in[epnum].total_length % hpcd->IN_ep[epnum].maxpacket) == 0U)) + { + /* Update the packet total length */ + pdev->ep_in[epnum].total_length = 0U; + + /* Send ZLP */ + (void)USBD_LL_Transmit(pdev, epnum, NULL, 0U); + } + else { // NOTE: We would logically expect xx_IN_EP here, but we actually get the xx_OUT_EP if (epnum == CDC_OUT_EP) @@ -577,12 +607,10 @@ static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) hcdc->ODRIVE_Tx.State = 0; //Note: We could use independent semaphores for simoultainous USB transmission. osSemaphoreRelease(sem_usb_tx); - return USBD_OK; - } - else - { - return USBD_FAIL; + //((USBD_CDC_ItfTypeDef *)pdev->pUserData)->TransmitCplt(hcdc->TxBuffer, &hcdc->TxLength, epnum); } + + return (uint8_t)USBD_OK; } /** @@ -592,8 +620,8 @@ static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) * @param epnum: endpoint number * @retval status */ -static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum) -{ +static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; USBD_CDC_EP_HandleTypeDef* hEP_Rx; @@ -622,78 +650,80 @@ static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum) } } - - /** - * @brief USBD_CDC_DataOut - * Data received on non-control Out endpoint + * @brief USBD_CDC_EP0_RxReady + * Handle EP0 Rx Ready event * @param pdev: device instance - * @param epnum: endpoint number * @retval status */ -static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; - - if((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFF)) +static uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev) +{ + USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; + + if ((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFFU)) { ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(hcdc->CmdOpCode, (uint8_t *)hcdc->data, - hcdc->CmdLength); - hcdc->CmdOpCode = 0xFF; - + (uint16_t)hcdc->CmdLength); + hcdc->CmdOpCode = 0xFFU; + } - return USBD_OK; + + return (uint8_t)USBD_OK; } /** - * @brief USBD_CDC_GetFSCfgDesc + * @brief USBD_CDC_GetFSCfgDesc * Return configuration descriptor * @param speed : current device speed * @param length : pointer data length * @retval pointer to descriptor buffer */ -static uint8_t *USBD_CDC_GetFSCfgDesc (uint16_t *length) +static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length) { - *length = sizeof (USBD_CDC_CfgDesc); + *length = (uint16_t)sizeof(USBD_CDC_CfgDesc); + return USBD_CDC_CfgDesc; } /** - * @brief USBD_CDC_GetHSCfgDesc + * @brief USBD_CDC_GetHSCfgDesc * Return configuration descriptor * @param speed : current device speed * @param length : pointer data length * @retval pointer to descriptor buffer */ -static uint8_t *USBD_CDC_GetHSCfgDesc (uint16_t *length) +static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length) { - *length = sizeof (USBD_CDC_CfgDesc); + *length = (uint16_t)sizeof(USBD_CDC_CfgDesc); + return USBD_CDC_CfgDesc; } /** - * @brief USBD_CDC_GetCfgDesc + * @brief USBD_CDC_GetCfgDesc * Return configuration descriptor * @param speed : current device speed * @param length : pointer data length * @retval pointer to descriptor buffer */ -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length) +static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length) { - *length = sizeof (USBD_CDC_CfgDesc); + *length = (uint16_t)sizeof(USBD_CDC_CfgDesc); + return USBD_CDC_CfgDesc; } /** -* @brief DeviceQualifierDescriptor +* @brief DeviceQualifierDescriptor * return Device Qualifier descriptor * @param length : pointer data length * @retval pointer to descriptor buffer */ -uint8_t *USBD_CDC_GetDeviceQualifierDescriptor (uint16_t *length) +uint8_t *USBD_CDC_GetDeviceQualifierDescriptor(uint16_t *length) { - *length = sizeof (USBD_CDC_DeviceQualifierDesc); + *length = (uint16_t)sizeof(USBD_CDC_DeviceQualifierDesc); + return USBD_CDC_DeviceQualifierDesc; } @@ -703,18 +733,17 @@ uint8_t *USBD_CDC_GetDeviceQualifierDescriptor (uint16_t *length) * @param fops: CD Interface callback * @retval status */ -uint8_t USBD_CDC_RegisterInterface (USBD_HandleTypeDef *pdev, - USBD_CDC_ItfTypeDef *fops) +uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, + USBD_CDC_ItfTypeDef *fops) { - uint8_t ret = USBD_FAIL; - - if(fops != NULL) + if (fops == NULL) { - pdev->pUserData= fops; - ret = USBD_OK; + return (uint8_t)USBD_FAIL; } - - return ret; + + pdev->pUserData = fops; + + return (uint8_t)USBD_OK; } /** @@ -723,10 +752,9 @@ uint8_t USBD_CDC_RegisterInterface (USBD_HandleTypeDef *pdev, * @param pbuff: Tx Buffer * @retval status */ -uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, - uint16_t length, - uint8_t endpoint_pair) +uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev, + uint8_t *pbuff, uint32_t length, + uint8_t endpoint_pair) { USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; @@ -752,8 +780,7 @@ uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev, * @param pbuff: Rx Buffer * @retval status */ -uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev, - uint8_t *pbuff, uint8_t endpoint_pair) +uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, uint8_t endpoint_pair) { USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; @@ -772,14 +799,13 @@ uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev, } /** - * @brief USBD_CDC_DataOut - * Data received on non-control Out endpoint + * @brief USBD_CDC_TransmitPacket + * Transmit packet on IN endpoint * @param pdev: device instance - * @param epnum: endpoint number * @retval status */ -uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair) -{ +uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair) +{ USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; if(pdev->pClassData != NULL) @@ -828,8 +854,8 @@ uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair * @param pdev: device instance * @retval status */ -uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair) -{ +uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t endpoint_pair) +{ USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData; /* Suspend or Resume USB Out process */ @@ -1041,14 +1067,14 @@ static uint8_t USBD_WinUSBComm_SetupVendor(USBD_HandleTypeDef *pdev, USBD_Setup /** * @} - */ + */ /** * @} - */ + */ /** * @} - */ + */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h new file mode 100644 index 00000000..c7d2ba39 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_core.h @@ -0,0 +1,158 @@ +/** + ****************************************************************************** + * @file usbd_core.h + * @author MCD Application Team + * @brief Header file for usbd_core.c file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_CORE_H +#define __USBD_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_conf.h" +#include "usbd_def.h" +#include "usbd_ioreq.h" +#include "usbd_ctlreq.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_CORE + * @brief This file is the Header file for usbd_core.c file + * @{ + */ + + +/** @defgroup USBD_CORE_Exported_Defines + * @{ + */ +#ifndef USBD_DEBUG_LEVEL +#define USBD_DEBUG_LEVEL 0U +#endif /* USBD_DEBUG_LEVEL */ +/** + * @} + */ + + +/** @defgroup USBD_CORE_Exported_TypesDefinitions + * @{ + */ + + +/** + * @} + */ + + + +/** @defgroup USBD_CORE_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_CORE_Exported_Variables + * @{ + */ +#define USBD_SOF USBD_LL_SOF +/** + * @} + */ + +/** @defgroup USBD_CORE_Exported_FunctionsPrototype + * @{ + */ +USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id); +USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_Start(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_Stop(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass); + +USBD_StatusTypeDef USBD_RunTestMode(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx); + +USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup); +USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata); +USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata); + +USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed); +USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev); + +USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); +USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); + +USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev); + +/* USBD Low Level Driver */ +USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_DeInit(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_LL_Stop(USBD_HandleTypeDef *pdev); + +USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr, + uint8_t ep_type, uint16_t ep_mps); + +USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_FlushEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_StallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_ClearStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_addr); + +USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr, + uint8_t *pbuf, uint32_t size); + +USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, + uint8_t *pbuf, uint32_t size); + +uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr); +uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *pdev, uint8_t ep_addr); + +void USBD_LL_Delay(uint32_t Delay); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_CORE_H */ + +/** + * @} + */ + +/** +* @} +*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + + + diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h new file mode 100644 index 00000000..f973a8b1 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ctlreq.h @@ -0,0 +1,103 @@ +/** + ****************************************************************************** + * @file usbd_req.h + * @author MCD Application Team + * @brief Header file for the usbd_req.c file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USB_REQUEST_H +#define __USB_REQUEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_def.h" + + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_REQ + * @brief header file for the usbd_req.c file + * @{ + */ + +/** @defgroup USBD_REQ_Exported_Defines + * @{ + */ +/** + * @} + */ + + +/** @defgroup USBD_REQ_Exported_Types + * @{ + */ +/** + * @} + */ + + + +/** @defgroup USBD_REQ_Exported_Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_REQ_Exported_Variables + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_REQ_Exported_FunctionsPrototype + * @{ + */ + +USBD_StatusTypeDef USBD_StdDevReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +USBD_StatusTypeDef USBD_StdItfReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +USBD_StatusTypeDef USBD_StdEPReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); + +void USBD_CtlError(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata); +void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_REQUEST_H */ + +/** + * @} + */ + +/** +* @} +*/ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h new file mode 100644 index 00000000..7441ee65 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_def.h @@ -0,0 +1,395 @@ +/** + ****************************************************************************** + * @file usbd_def.h + * @author MCD Application Team + * @brief General defines for the usb device library + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_DEF_H +#define __USBD_DEF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_conf.h" + +/** @addtogroup STM32_USBD_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USB_DEF + * @brief general defines for the usb device library file + * @{ + */ + +/** @defgroup USB_DEF_Exported_Defines + * @{ + */ + +#ifndef NULL +#define NULL 0U +#endif /* NULL */ + +#ifndef USBD_MAX_NUM_INTERFACES +#define USBD_MAX_NUM_INTERFACES 1U +#endif /* USBD_MAX_NUM_CONFIGURATION */ + +#ifndef USBD_MAX_NUM_CONFIGURATION +#define USBD_MAX_NUM_CONFIGURATION 1U +#endif /* USBD_MAX_NUM_CONFIGURATION */ + +#ifndef USBD_LPM_ENABLED +#define USBD_LPM_ENABLED 0U +#endif /* USBD_LPM_ENABLED */ + +#ifndef USBD_SELF_POWERED +#define USBD_SELF_POWERED 1U +#endif /*USBD_SELF_POWERED */ + +#ifndef USBD_SUPPORT_USER_STRING_DESC +#define USBD_SUPPORT_USER_STRING_DESC 0U +#endif /* USBD_SUPPORT_USER_STRING_DESC */ + +#ifndef USBD_CLASS_USER_STRING_DESC +#define USBD_CLASS_USER_STRING_DESC 0U +#endif /* USBD_CLASS_USER_STRING_DESC */ + +#define USB_LEN_DEV_QUALIFIER_DESC 0x0AU +#define USB_LEN_DEV_DESC 0x12U +#define USB_LEN_CFG_DESC 0x09U +#define USB_LEN_IF_DESC 0x09U +#define USB_LEN_EP_DESC 0x07U +#define USB_LEN_OTG_DESC 0x03U +#define USB_LEN_LANGID_STR_DESC 0x04U +#define USB_LEN_OTHER_SPEED_DESC_SIZ 0x09U + +#define USBD_IDX_LANGID_STR 0x00U +#define USBD_IDX_MFC_STR 0x01U +#define USBD_IDX_PRODUCT_STR 0x02U +#define USBD_IDX_SERIAL_STR 0x03U +#define USBD_IDX_CONFIG_STR 0x04U +#define USBD_IDX_INTERFACE_STR 0x05U +#define USBD_IDX_ODRIVE_INTF_STR 0x06 +#define USBD_IDX_MICROSOFT_DESC_STR 0xEE + +#define USB_REQ_TYPE_STANDARD 0x00U +#define USB_REQ_TYPE_CLASS 0x20U +#define USB_REQ_TYPE_VENDOR 0x40U +#define USB_REQ_TYPE_MASK 0x60U + +#define USB_REQ_RECIPIENT_DEVICE 0x00U +#define USB_REQ_RECIPIENT_INTERFACE 0x01U +#define USB_REQ_RECIPIENT_ENDPOINT 0x02U +#define USB_REQ_RECIPIENT_MASK 0x03U + +#define USB_REQ_GET_STATUS 0x00U +#define USB_REQ_CLEAR_FEATURE 0x01U +#define USB_REQ_SET_FEATURE 0x03U +#define USB_REQ_SET_ADDRESS 0x05U +#define USB_REQ_GET_DESCRIPTOR 0x06U +#define USB_REQ_SET_DESCRIPTOR 0x07U +#define USB_REQ_GET_CONFIGURATION 0x08U +#define USB_REQ_SET_CONFIGURATION 0x09U +#define USB_REQ_GET_INTERFACE 0x0AU +#define USB_REQ_SET_INTERFACE 0x0BU +#define USB_REQ_SYNCH_FRAME 0x0CU + +#define USB_DESC_TYPE_DEVICE 0x01U +#define USB_DESC_TYPE_CONFIGURATION 0x02U +#define USB_DESC_TYPE_STRING 0x03U +#define USB_DESC_TYPE_INTERFACE 0x04U +#define USB_DESC_TYPE_ENDPOINT 0x05U +#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06U +#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07U +#define USB_DESC_TYPE_BOS 0x0FU + +#define USB_CONFIG_REMOTE_WAKEUP 0x02U +#define USB_CONFIG_SELF_POWERED 0x01U + +#define USB_FEATURE_EP_HALT 0x00U +#define USB_FEATURE_REMOTE_WAKEUP 0x01U +#define USB_FEATURE_TEST_MODE 0x02U + +#define USB_DEVICE_CAPABITY_TYPE 0x10U + +#define USB_HS_MAX_PACKET_SIZE 512U +#define USB_FS_MAX_PACKET_SIZE 64U +#define USB_MAX_EP0_SIZE 64U + +/* Device Status */ +#define USBD_STATE_DEFAULT 0x01U +#define USBD_STATE_ADDRESSED 0x02U +#define USBD_STATE_CONFIGURED 0x03U +#define USBD_STATE_SUSPENDED 0x04U + + +/* EP0 State */ +#define USBD_EP0_IDLE 0x00U +#define USBD_EP0_SETUP 0x01U +#define USBD_EP0_DATA_IN 0x02U +#define USBD_EP0_DATA_OUT 0x03U +#define USBD_EP0_STATUS_IN 0x04U +#define USBD_EP0_STATUS_OUT 0x05U +#define USBD_EP0_STALL 0x06U + +#define USBD_EP_TYPE_CTRL 0x00U +#define USBD_EP_TYPE_ISOC 0x01U +#define USBD_EP_TYPE_BULK 0x02U +#define USBD_EP_TYPE_INTR 0x03U + + +/** + * @} + */ + + +/** @defgroup USBD_DEF_Exported_TypesDefinitions + * @{ + */ + +typedef struct usb_setup_req +{ + uint8_t bmRequest; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} USBD_SetupReqTypedef; + +typedef struct +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t wDescriptorLengthLow; + uint8_t wDescriptorLengthHigh; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} USBD_ConfigDescTypedef; + +typedef struct +{ + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} USBD_BosDescTypedef; + + +struct _USBD_HandleTypeDef; + +typedef struct _Device_cb +{ + uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); + uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); + /* Control Endpoints*/ + uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); + uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev); + uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev); + /* Class Specific Endpoints*/ + uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev); + uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum); + + uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); + uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); + uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); + uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); +#if (USBD_SUPPORT_USER_STRING_DESC == 1U) + uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); +#endif + +} USBD_ClassTypeDef; + +/* Following USB Device Speed */ +typedef enum +{ + USBD_SPEED_HIGH = 0U, + USBD_SPEED_FULL = 1U, + USBD_SPEED_LOW = 2U, +} USBD_SpeedTypeDef; + +/* Following USB Device status */ +typedef enum +{ + USBD_OK = 0U, + USBD_BUSY, + USBD_EMEM, + USBD_FAIL, +} USBD_StatusTypeDef; + +/* USB Device descriptors structure */ +typedef struct +{ + uint8_t *(*GetDeviceDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetLangIDStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetManufacturerStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetProductStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetSerialStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetConfigurationStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); + uint8_t *(*GetInterfaceStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); +#if (USBD_CLASS_USER_STRING_DESC == 1) + uint8_t *(*GetUserStrDescriptor)(USBD_SpeedTypeDef speed, uint8_t idx, uint16_t *length); +#endif +#if ((USBD_LPM_ENABLED == 1U) || (USBD_CLASS_BOS_ENABLED == 1)) + uint8_t *(*GetBOSDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); +#endif +} USBD_DescriptorsTypeDef; + +/* USB Device handle structure */ +typedef struct +{ + uint32_t status; + uint32_t total_length; + uint32_t rem_length; + uint32_t maxpacket; + uint16_t is_used; + uint16_t bInterval; +} USBD_EndpointTypeDef; + +/* USB Device handle structure */ +typedef struct _USBD_HandleTypeDef +{ + uint8_t id; + uint32_t dev_config; + uint32_t dev_default_config; + uint32_t dev_config_status; + USBD_SpeedTypeDef dev_speed; + USBD_EndpointTypeDef ep_in[16]; + USBD_EndpointTypeDef ep_out[16]; + uint32_t ep0_state; + uint32_t ep0_data_len; + uint8_t dev_state; + uint8_t dev_old_state; + uint8_t dev_address; + uint8_t dev_connection_status; + uint8_t dev_test_mode; + uint32_t dev_remote_wakeup; + uint8_t ConfIdx; + + USBD_SetupReqTypedef request; + USBD_DescriptorsTypeDef *pDesc; + USBD_ClassTypeDef *pClass; + void *pClassData; + void *pUserData; + void *pData; + void *pBosDesc; + void *pConfDesc; +} USBD_HandleTypeDef; + +/** + * @} + */ + + + +/** @defgroup USBD_DEF_Exported_Macros + * @{ + */ +__STATIC_INLINE uint16_t SWAPBYTE(uint8_t *addr) +{ + uint16_t _SwapVal, _Byte1, _Byte2; + uint8_t *_pbuff = addr; + + _Byte1 = *(uint8_t *)_pbuff; + _pbuff++; + _Byte2 = *(uint8_t *)_pbuff; + + _SwapVal = (_Byte2 << 8) | _Byte1; + + return _SwapVal; +} + +#define LOBYTE(x) ((uint8_t)((x) & 0x00FFU)) +#define HIBYTE(x) ((uint8_t)(((x) & 0xFF00U) >> 8U)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + +#if defined ( __GNUC__ ) +#ifndef __weak +#define __weak __attribute__((weak)) +#endif /* __weak */ +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif /* __packed */ +#endif /* __GNUC__ */ + + +/* In HS mode and when the DMA is used, all variables and data structures dealing + with the DMA during the transaction process should be 4-bytes aligned */ + +#if defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */ +#ifndef __ALIGN_END +#define __ALIGN_END __attribute__ ((aligned (4U))) +#endif /* __ALIGN_END */ +#ifndef __ALIGN_BEGIN +#define __ALIGN_BEGIN +#endif /* __ALIGN_BEGIN */ +#else +#ifndef __ALIGN_END +#define __ALIGN_END +#endif /* __ALIGN_END */ +#ifndef __ALIGN_BEGIN +#if defined (__CC_ARM) /* ARM Compiler */ +#define __ALIGN_BEGIN __align(4U) +#elif defined (__ICCARM__) /* IAR Compiler */ +#define __ALIGN_BEGIN +#endif /* __CC_ARM */ +#endif /* __ALIGN_BEGIN */ +#endif /* __GNUC__ */ + + +/** + * @} + */ + +/** @defgroup USBD_DEF_Exported_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_DEF_Exported_FunctionsPrototype + * @{ + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_DEF_H */ + +/** + * @} + */ + +/** +* @} +*/ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h new file mode 100644 index 00000000..b7159d53 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Inc/usbd_ioreq.h @@ -0,0 +1,114 @@ +/** + ****************************************************************************** + * @file usbd_ioreq.h + * @author MCD Application Team + * @brief Header file for the usbd_ioreq.c file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_IOREQ_H +#define __USBD_IOREQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_def.h" +#include "usbd_core.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_IOREQ + * @brief header file for the usbd_ioreq.c file + * @{ + */ + +/** @defgroup USBD_IOREQ_Exported_Defines + * @{ + */ +/** + * @} + */ + + +/** @defgroup USBD_IOREQ_Exported_Types + * @{ + */ + + +/** + * @} + */ + + + +/** @defgroup USBD_IOREQ_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_IOREQ_Exported_Variables + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_IOREQ_Exported_FunctionsPrototype + * @{ + */ + +USBD_StatusTypeDef USBD_CtlSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlContinueSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlPrepareRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlContinueRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len); + +USBD_StatusTypeDef USBD_CtlSendStatus(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_CtlReceiveStatus(USBD_HandleTypeDef *pdev); + +uint32_t USBD_GetRxCount(USBD_HandleTypeDef *pdev, uint8_t ep_addr); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USBD_IOREQ_H */ + +/** + * @} + */ + +/** +* @} +*/ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c new file mode 100644 index 00000000..3faed352 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_core.c @@ -0,0 +1,669 @@ +/** + ****************************************************************************** + * @file usbd_core.c + * @author MCD Application Team + * @brief This file provides all the USBD core functions. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_core.h" + +/** @addtogroup STM32_USBD_DEVICE_LIBRARY +* @{ +*/ + + +/** @defgroup USBD_CORE +* @brief usbd core module +* @{ +*/ + +/** @defgroup USBD_CORE_Private_TypesDefinitions +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_Defines +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_Macros +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_FunctionPrototypes +* @{ +*/ + +/** +* @} +*/ + +/** @defgroup USBD_CORE_Private_Variables +* @{ +*/ + +/** +* @} +*/ + + +/** @defgroup USBD_CORE_Private_Functions +* @{ +*/ + +/** +* @brief USBD_Init +* Initializes the device stack and load the class driver +* @param pdev: device instance +* @param pdesc: Descriptor structure address +* @param id: Low level core index +* @retval None +*/ +USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, + USBD_DescriptorsTypeDef *pdesc, uint8_t id) +{ + USBD_StatusTypeDef ret; + + /* Check whether the USB Host handle is valid */ + if (pdev == NULL) + { +#if (USBD_DEBUG_LEVEL > 1U) + USBD_ErrLog("Invalid Device handle"); +#endif + return USBD_FAIL; + } + + /* Unlink previous class */ + if (pdev->pClass != NULL) + { + pdev->pClass = NULL; + } + + if (pdev->pConfDesc != NULL) + { + pdev->pConfDesc = NULL; + } + + /* Assign USBD Descriptors */ + if (pdesc != NULL) + { + pdev->pDesc = pdesc; + } + + /* Set Device initial State */ + pdev->dev_state = USBD_STATE_DEFAULT; + pdev->id = id; + + /* Initialize low level driver */ + ret = USBD_LL_Init(pdev); + + return ret; +} + +/** +* @brief USBD_DeInit +* Re-Initialize th device library +* @param pdev: device instance +* @retval status: status +*/ +USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev) +{ + USBD_StatusTypeDef ret; + + /* Set Default State */ + pdev->dev_state = USBD_STATE_DEFAULT; + + /* Free Class Resources */ + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + if (pdev->pConfDesc != NULL) + { + pdev->pConfDesc = NULL; + } + + /* Stop the low level driver */ + ret = USBD_LL_Stop(pdev); + + if (ret != USBD_OK) + { + return ret; + } + + /* Initialize low level driver */ + ret = USBD_LL_DeInit(pdev); + + return ret; +} + +/** + * @brief USBD_RegisterClass + * Link class driver to Device Core. + * @param pDevice : Device Handle + * @param pclass: Class handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass) +{ + uint16_t len = 0U; + + if (pclass == NULL) + { +#if (USBD_DEBUG_LEVEL > 1U) + USBD_ErrLog("Invalid Class handle"); +#endif + return USBD_FAIL; + } + + /* link the class to the USB Device handle */ + pdev->pClass = pclass; + + /* Get Device Configuration Descriptor */ +#ifdef USE_USB_FS + pdev->pConfDesc = (void *)pdev->pClass->GetFSConfigDescriptor(&len); +#else /* USE_USB_HS */ + pdev->pConfDesc = (void *)pdev->pClass->GetHSConfigDescriptor(&len); +#endif /* USE_USB_FS */ + + + return USBD_OK; +} + +/** + * @brief USBD_Start + * Start the USB Device Core. + * @param pdev: Device Handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_Start(USBD_HandleTypeDef *pdev) +{ + /* Start the low level driver */ + return USBD_LL_Start(pdev); +} + +/** + * @brief USBD_Stop + * Stop the USB Device Core. + * @param pdev: Device Handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_Stop(USBD_HandleTypeDef *pdev) +{ + USBD_StatusTypeDef ret; + + /* Free Class Resources */ + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + if (pdev->pConfDesc != NULL) + { + pdev->pConfDesc = NULL; + } + + /* Stop the low level driver */ + ret = USBD_LL_Stop(pdev); + + return ret; +} + +/** +* @brief USBD_RunTestMode +* Launch test mode process +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_RunTestMode(USBD_HandleTypeDef *pdev) +{ + /* Prevent unused argument compilation warning */ + UNUSED(pdev); + + return USBD_OK; +} + +/** +* @brief USBD_SetClassConfig +* Configure device and start the interface +* @param pdev: device instance +* @param cfgidx: configuration index +* @retval status +*/ + +USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + USBD_StatusTypeDef ret = USBD_FAIL; + + if (pdev->pClass != NULL) + { + /* Set configuration and Start the Class */ + ret = (USBD_StatusTypeDef)pdev->pClass->Init(pdev, cfgidx); + } + + return ret; +} + +/** +* @brief USBD_ClrClassConfig +* Clear current configuration +* @param pdev: device instance +* @param cfgidx: configuration index +* @retval status: USBD_StatusTypeDef +*/ +USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + /* Clear configuration and De-initialize the Class process */ + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, cfgidx); + } + + return USBD_OK; +} + + +/** +* @brief USBD_SetupStage +* Handle the setup stage +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup) +{ + USBD_StatusTypeDef ret; + + USBD_ParseSetupRequest(&pdev->request, psetup); + + pdev->ep0_state = USBD_EP0_SETUP; + + pdev->ep0_data_len = pdev->request.wLength; + + switch (pdev->request.bmRequest & 0x1FU) + { + case USB_REQ_RECIPIENT_DEVICE: + ret = USBD_StdDevReq(pdev, &pdev->request); + break; + + case USB_REQ_RECIPIENT_INTERFACE: + ret = USBD_StdItfReq(pdev, &pdev->request); + break; + + case USB_REQ_RECIPIENT_ENDPOINT: + ret = USBD_StdEPReq(pdev, &pdev->request); + break; + + default: + ret = USBD_LL_StallEP(pdev, (pdev->request.bmRequest & 0x80U)); + break; + } + + return ret; +} + +/** +* @brief USBD_DataOutStage +* Handle data OUT stage +* @param pdev: device instance +* @param epnum: endpoint index +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, + uint8_t epnum, uint8_t *pdata) +{ + USBD_EndpointTypeDef *pep; + USBD_StatusTypeDef ret; + + if (epnum == 0U) + { + pep = &pdev->ep_out[0]; + + if (pdev->ep0_state == USBD_EP0_DATA_OUT) + { + if (pep->rem_length > pep->maxpacket) + { + pep->rem_length -= pep->maxpacket; + + (void)USBD_CtlContinueRx(pdev, pdata, MIN(pep->rem_length, pep->maxpacket)); + } + else + { + if ((pdev->pClass->EP0_RxReady != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + pdev->pClass->EP0_RxReady(pdev); + } + (void)USBD_CtlSendStatus(pdev); + } + } + else + { +#if 0 + if (pdev->ep0_state == USBD_EP0_STATUS_OUT) + { + /* + * STATUS PHASE completed, update ep0_state to idle + */ + pdev->ep0_state = USBD_EP0_IDLE; + (void)USBD_LL_StallEP(pdev, 0U); + } +#endif + } + } + else if ((pdev->pClass->DataOut != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + ret = (USBD_StatusTypeDef)pdev->pClass->DataOut(pdev, epnum); + + if (ret != USBD_OK) + { + return ret; + } + } + else + { + /* should never be in this condition */ + return USBD_FAIL; + } + + return USBD_OK; +} + +/** +* @brief USBD_DataInStage +* Handle data in stage +* @param pdev: device instance +* @param epnum: endpoint index +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev, + uint8_t epnum, uint8_t *pdata) +{ + USBD_EndpointTypeDef *pep; + USBD_StatusTypeDef ret; + + if (epnum == 0U) + { + pep = &pdev->ep_in[0]; + + if (pdev->ep0_state == USBD_EP0_DATA_IN) + { + if (pep->rem_length > pep->maxpacket) + { + pep->rem_length -= pep->maxpacket; + + (void)USBD_CtlContinueSendData(pdev, pdata, pep->rem_length); + + /* Prepare endpoint for premature end of transfer */ + (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U); + } + else + { + /* last packet is MPS multiple, so send ZLP packet */ + if ((pep->maxpacket == pep->rem_length) && + (pep->total_length >= pep->maxpacket) && + (pep->total_length < pdev->ep0_data_len)) + { + (void)USBD_CtlContinueSendData(pdev, NULL, 0U); + pdev->ep0_data_len = 0U; + + /* Prepare endpoint for premature end of transfer */ + (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U); + } + else + { + if ((pdev->pClass->EP0_TxSent != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + pdev->pClass->EP0_TxSent(pdev); + } + (void)USBD_LL_StallEP(pdev, 0x80U); + (void)USBD_CtlReceiveStatus(pdev); + } + } + } + else + { +#if 0 + if ((pdev->ep0_state == USBD_EP0_STATUS_IN) || + (pdev->ep0_state == USBD_EP0_IDLE)) + { + (void)USBD_LL_StallEP(pdev, 0x80U); + } +#endif + } + + if (pdev->dev_test_mode == 1U) + { + (void)USBD_RunTestMode(pdev); + pdev->dev_test_mode = 0U; + } + } + else if ((pdev->pClass->DataIn != NULL) && + (pdev->dev_state == USBD_STATE_CONFIGURED)) + { + ret = (USBD_StatusTypeDef)pdev->pClass->DataIn(pdev, epnum); + + if (ret != USBD_OK) + { + return ret; + } + } + else + { + /* should never be in this condition */ + return USBD_FAIL; + } + + return USBD_OK; +} + +/** +* @brief USBD_LL_Reset +* Handle Reset event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev) +{ + /* Upon Reset call user call back */ + pdev->dev_state = USBD_STATE_DEFAULT; + pdev->ep0_state = USBD_EP0_IDLE; + pdev->dev_config = 0U; + pdev->dev_remote_wakeup = 0U; + + if (pdev->pClassData != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + /* Open EP0 OUT */ + (void)USBD_LL_OpenEP(pdev, 0x00U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE); + pdev->ep_out[0x00U & 0xFU].is_used = 1U; + + pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE; + + /* Open EP0 IN */ + (void)USBD_LL_OpenEP(pdev, 0x80U, USBD_EP_TYPE_CTRL, USB_MAX_EP0_SIZE); + pdev->ep_in[0x80U & 0xFU].is_used = 1U; + + pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE; + + return USBD_OK; +} + +/** +* @brief USBD_LL_Reset +* Handle Reset event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, + USBD_SpeedTypeDef speed) +{ + pdev->dev_speed = speed; + + return USBD_OK; +} + +/** +* @brief USBD_Suspend +* Handle Suspend event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev) +{ + pdev->dev_old_state = pdev->dev_state; + pdev->dev_state = USBD_STATE_SUSPENDED; + + return USBD_OK; +} + +/** +* @brief USBD_Resume +* Handle Resume event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev) +{ + if (pdev->dev_state == USBD_STATE_SUSPENDED) + { + pdev->dev_state = pdev->dev_old_state; + } + + return USBD_OK; +} + +/** +* @brief USBD_SOF +* Handle SOF event +* @param pdev: device instance +* @retval status +*/ + +USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev) +{ + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + if (pdev->pClass->SOF != NULL) + { + pdev->pClass->SOF(pdev); + } + } + + return USBD_OK; +} + +/** +* @brief USBD_IsoINIncomplete +* Handle iso in incomplete event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, + uint8_t epnum) +{ + /* Prevent unused arguments compilation warning */ + UNUSED(pdev); + UNUSED(epnum); + + return USBD_OK; +} + +/** +* @brief USBD_IsoOUTIncomplete +* Handle iso out incomplete event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, + uint8_t epnum) +{ + /* Prevent unused arguments compilation warning */ + UNUSED(pdev); + UNUSED(epnum); + + return USBD_OK; +} + +/** +* @brief USBD_DevConnected +* Handle device connection event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev) +{ + /* Prevent unused argument compilation warning */ + UNUSED(pdev); + + return USBD_OK; +} + +/** +* @brief USBD_DevDisconnected +* Handle device disconnection event +* @param pdev: device instance +* @retval status +*/ +USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev) +{ + /* Free Class Resources */ + pdev->dev_state = USBD_STATE_DEFAULT; + + if (pdev->pClass != NULL) + { + pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config); + } + + return USBD_OK; +} +/** +* @} +*/ + + +/** +* @} +*/ + + +/** +* @} +*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c new file mode 100644 index 00000000..c31d40e0 --- /dev/null +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c @@ -0,0 +1,944 @@ +/** + ****************************************************************************** + * @file usbd_req.c + * @author MCD Application Team + * @brief This file provides the standard USB requests following chapter 9. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ctlreq.h" +#include "usbd_ioreq.h" + + +/** @addtogroup STM32_USBD_STATE_DEVICE_LIBRARY + * @{ + */ + + +/** @defgroup USBD_REQ + * @brief USB standard requests module + * @{ + */ + +/** @defgroup USBD_REQ_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Defines + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Macros + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Variables + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_FunctionPrototypes + * @{ + */ +static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static void USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static uint8_t USBD_GetLen(uint8_t *buf); + +/** + * @} + */ + + +/** @defgroup USBD_REQ_Private_Functions + * @{ + */ + + +/** +* @brief USBD_StdDevReq +* Handle standard usb device requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +USBD_StatusTypeDef USBD_StdDevReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_StatusTypeDef ret = USBD_OK; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + case USB_REQ_TYPE_VENDOR: + ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + break; + + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) + { + case USB_REQ_GET_DESCRIPTOR: + USBD_GetDescriptor(pdev, req); + break; + + case USB_REQ_SET_ADDRESS: + USBD_SetAddress(pdev, req); + break; + + case USB_REQ_SET_CONFIGURATION: + ret = USBD_SetConfig(pdev, req); + break; + + case USB_REQ_GET_CONFIGURATION: + USBD_GetConfig(pdev, req); + break; + + case USB_REQ_GET_STATUS: + USBD_GetStatus(pdev, req); + break; + + case USB_REQ_SET_FEATURE: + USBD_SetFeature(pdev, req); + break; + + case USB_REQ_CLEAR_FEATURE: + USBD_ClrFeature(pdev, req); + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + + return ret; +} + +/** +* @brief USBD_StdItfReq +* Handle standard usb interface requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +USBD_StatusTypeDef USBD_StdItfReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_StatusTypeDef ret = USBD_OK; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + case USB_REQ_TYPE_VENDOR: + case USB_REQ_TYPE_STANDARD: + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + case USBD_STATE_CONFIGURED: + + if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) + { + ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + + if ((req->wLength == 0U) && (ret == USBD_OK)) + { + (void)USBD_CtlSendStatus(pdev); + } + } + else + { + USBD_CtlError(pdev, req); + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + + return ret; +} + +/** +* @brief USBD_StdEPReq +* Handle standard usb endpoint requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +USBD_StatusTypeDef USBD_StdEPReq(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_EndpointTypeDef *pep; + uint8_t ep_addr; + USBD_StatusTypeDef ret = USBD_OK; + ep_addr = LOBYTE(req->wIndex); + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + case USB_REQ_TYPE_VENDOR: + ret = (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + break; + + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) + { + case USB_REQ_SET_FEATURE: + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) + { + (void)USBD_LL_StallEP(pdev, ep_addr); + (void)USBD_LL_StallEP(pdev, 0x80U); + } + else + { + USBD_CtlError(pdev, req); + } + break; + + case USBD_STATE_CONFIGURED: + if (req->wValue == USB_FEATURE_EP_HALT) + { + if ((ep_addr != 0x00U) && (ep_addr != 0x80U) && (req->wLength == 0x00U)) + { + (void)USBD_LL_StallEP(pdev, ep_addr); + } + } + (void)USBD_CtlSendStatus(pdev); + + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + case USB_REQ_CLEAR_FEATURE: + + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) + { + (void)USBD_LL_StallEP(pdev, ep_addr); + (void)USBD_LL_StallEP(pdev, 0x80U); + } + else + { + USBD_CtlError(pdev, req); + } + break; + + case USBD_STATE_CONFIGURED: + if (req->wValue == USB_FEATURE_EP_HALT) + { + if ((ep_addr & 0x7FU) != 0x00U) + { + (void)USBD_LL_ClearStallEP(pdev, ep_addr); + } + (void)USBD_CtlSendStatus(pdev); + (USBD_StatusTypeDef)pdev->pClass->Setup(pdev, req); + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + case USB_REQ_GET_STATUS: + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) + { + USBD_CtlError(pdev, req); + break; + } + pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU] : \ + &pdev->ep_out[ep_addr & 0x7FU]; + + pep->status = 0x0000U; + + (void)USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2U); + break; + + case USBD_STATE_CONFIGURED: + if ((ep_addr & 0x80U) == 0x80U) + { + if (pdev->ep_in[ep_addr & 0xFU].is_used == 0U) + { + USBD_CtlError(pdev, req); + break; + } + } + else + { + if (pdev->ep_out[ep_addr & 0xFU].is_used == 0U) + { + USBD_CtlError(pdev, req); + break; + } + } + + pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU] : \ + &pdev->ep_out[ep_addr & 0x7FU]; + + if ((ep_addr == 0x00U) || (ep_addr == 0x80U)) + { + pep->status = 0x0000U; + } + else if (USBD_LL_IsStallEP(pdev, ep_addr) != 0U) + { + pep->status = 0x0001U; + } + else + { + pep->status = 0x0000U; + } + + (void)USBD_CtlSendData(pdev, (uint8_t *)&pep->status, 2U); + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } + + return ret; +} + + +/** +* @brief USBD_GetDescriptor +* Handle Get Descriptor requests +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + uint16_t len = 0U; + uint8_t *pbuf = NULL; + uint8_t err = 0U; + + switch (req->wValue >> 8) + { +#if ((USBD_LPM_ENABLED == 1U) || (USBD_CLASS_BOS_ENABLED == 1U)) + case USB_DESC_TYPE_BOS: + if (pdev->pDesc->GetBOSDescriptor != NULL) + { + pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; +#endif + case USB_DESC_TYPE_DEVICE: + pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len); + break; + + case USB_DESC_TYPE_CONFIGURATION: + if (pdev->dev_speed == USBD_SPEED_HIGH) + { + pbuf = pdev->pClass->GetHSConfigDescriptor(&len); + pbuf[1] = USB_DESC_TYPE_CONFIGURATION; + } + else + { + pbuf = pdev->pClass->GetFSConfigDescriptor(&len); + pbuf[1] = USB_DESC_TYPE_CONFIGURATION; + } + break; + + case USB_DESC_TYPE_STRING: + switch ((uint8_t)(req->wValue)) + { + case USBD_IDX_LANGID_STR: + if (pdev->pDesc->GetLangIDStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_MFC_STR: + if (pdev->pDesc->GetManufacturerStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_PRODUCT_STR: + if (pdev->pDesc->GetProductStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_SERIAL_STR: + if (pdev->pDesc->GetSerialStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_CONFIG_STR: + if (pdev->pDesc->GetConfigurationStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USBD_IDX_INTERFACE_STR: + if (pdev->pDesc->GetInterfaceStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + default: +#if (USBD_SUPPORT_USER_STRING_DESC == 1U) + if (pdev->pClass->GetUsrStrDescriptor != NULL) + { + pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue), &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } +#elif (USBD_CLASS_USER_STRING_DESC == 1U) + if (pdev->pDesc->GetUserStrDescriptor != NULL) + { + pbuf = pdev->pDesc->GetUserStrDescriptor(pdev->dev_speed, (req->wValue), &len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } +#else + USBD_CtlError(pdev, req); + err++; +#endif + break; + } + break; + + case USB_DESC_TYPE_DEVICE_QUALIFIER: + if (pdev->dev_speed == USBD_SPEED_HIGH) + { + pbuf = pdev->pClass->GetDeviceQualifierDescriptor(&len); + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: + if (pdev->dev_speed == USBD_SPEED_HIGH) + { + pbuf = pdev->pClass->GetOtherSpeedConfigDescriptor(&len); + pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; + } + else + { + USBD_CtlError(pdev, req); + err++; + } + break; + + default: + USBD_CtlError(pdev, req); + err++; + break; + } + + if (err != 0U) + { + return; + } + else + { + if (req->wLength != 0U) + { + if (len != 0U) + { + len = MIN(len, req->wLength); + (void)USBD_CtlSendData(pdev, pbuf, len); + } + else + { + USBD_CtlError(pdev, req); + } + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + } +} + +/** +* @brief USBD_SetAddress +* Set device address +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_SetAddress(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + uint8_t dev_addr; + + if ((req->wIndex == 0U) && (req->wLength == 0U) && (req->wValue < 128U)) + { + dev_addr = (uint8_t)(req->wValue) & 0x7FU; + + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + USBD_CtlError(pdev, req); + } + else + { + pdev->dev_address = dev_addr; + (void)USBD_LL_SetUSBAddress(pdev, dev_addr); + (void)USBD_CtlSendStatus(pdev); + + if (dev_addr != 0U) + { + pdev->dev_state = USBD_STATE_ADDRESSED; + } + else + { + pdev->dev_state = USBD_STATE_DEFAULT; + } + } + } + else + { + USBD_CtlError(pdev, req); + } +} + +/** +* @brief USBD_SetConfig +* Handle Set device configuration request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static USBD_StatusTypeDef USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_StatusTypeDef ret = USBD_OK; + static uint8_t cfgidx; + + cfgidx = (uint8_t)(req->wValue); + + if (cfgidx > USBD_MAX_NUM_CONFIGURATION) + { + USBD_CtlError(pdev, req); + return USBD_FAIL; + } + + switch (pdev->dev_state) + { + case USBD_STATE_ADDRESSED: + if (cfgidx != 0U) + { + pdev->dev_config = cfgidx; + + ret = USBD_SetClassConfig(pdev, cfgidx); + + if (ret != USBD_OK) + { + USBD_CtlError(pdev, req); + } + else + { + (void)USBD_CtlSendStatus(pdev); + pdev->dev_state = USBD_STATE_CONFIGURED; + } + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + break; + + case USBD_STATE_CONFIGURED: + if (cfgidx == 0U) + { + pdev->dev_state = USBD_STATE_ADDRESSED; + pdev->dev_config = cfgidx; + (void)USBD_ClrClassConfig(pdev, cfgidx); + (void)USBD_CtlSendStatus(pdev); + } + else if (cfgidx != pdev->dev_config) + { + /* Clear old configuration */ + (void)USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); + + /* set new configuration */ + pdev->dev_config = cfgidx; + + ret = USBD_SetClassConfig(pdev, cfgidx); + + if (ret != USBD_OK) + { + USBD_CtlError(pdev, req); + (void)USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); + pdev->dev_state = USBD_STATE_ADDRESSED; + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + } + else + { + (void)USBD_CtlSendStatus(pdev); + } + break; + + default: + USBD_CtlError(pdev, req); + (void)USBD_ClrClassConfig(pdev, cfgidx); + ret = USBD_FAIL; + break; + } + + return ret; +} + +/** +* @brief USBD_GetConfig +* Handle Get device configuration request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + if (req->wLength != 1U) + { + USBD_CtlError(pdev, req); + } + else + { + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + pdev->dev_default_config = 0U; + (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_default_config, 1U); + break; + + case USBD_STATE_CONFIGURED: + (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config, 1U); + break; + + default: + USBD_CtlError(pdev, req); + break; + } + } +} + +/** +* @brief USBD_GetStatus +* Handle Get Status request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + case USBD_STATE_CONFIGURED: + if (req->wLength != 0x2U) + { + USBD_CtlError(pdev, req); + break; + } + +#if (USBD_SELF_POWERED == 1U) + pdev->dev_config_status = USB_CONFIG_SELF_POWERED; +#else + pdev->dev_config_status = 0U; +#endif + + if (pdev->dev_remote_wakeup != 0U) + { + pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; + } + + (void)USBD_CtlSendData(pdev, (uint8_t *)&pdev->dev_config_status, 2U); + break; + + default: + USBD_CtlError(pdev, req); + break; + } +} + + +/** +* @brief USBD_SetFeature +* Handle Set device feature request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_SetFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) + { + pdev->dev_remote_wakeup = 1U; + (void)USBD_CtlSendStatus(pdev); + } +} + + +/** +* @brief USBD_ClrFeature +* Handle clear device feature request +* @param pdev: device instance +* @param req: usb request +* @retval status +*/ +static void USBD_ClrFeature(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + switch (pdev->dev_state) + { + case USBD_STATE_DEFAULT: + case USBD_STATE_ADDRESSED: + case USBD_STATE_CONFIGURED: + if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) + { + pdev->dev_remote_wakeup = 0U; + (void)USBD_CtlSendStatus(pdev); + } + break; + + default: + USBD_CtlError(pdev, req); + break; + } +} + +/** +* @brief USBD_ParseSetupRequest +* Copy buffer into setup structure +* @param pdev: device instance +* @param req: usb request +* @retval None +*/ + +void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata) +{ + uint8_t *pbuff = pdata; + + req->bmRequest = *(uint8_t *)(pbuff); + + pbuff++; + req->bRequest = *(uint8_t *)(pbuff); + + pbuff++; + req->wValue = SWAPBYTE(pbuff); + + pbuff++; + pbuff++; + req->wIndex = SWAPBYTE(pbuff); + + pbuff++; + pbuff++; + req->wLength = SWAPBYTE(pbuff); +} + +/** +* @brief USBD_CtlError +* Handle USB low level Error +* @param pdev: device instance +* @param req: usb request +* @retval None +*/ + +void USBD_CtlError(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + UNUSED(req); + + (void)USBD_LL_StallEP(pdev, 0x80U); + (void)USBD_LL_StallEP(pdev, 0U); +} + + +/** + * @brief USBD_GetString + * Convert Ascii string into unicode one + * @param desc : descriptor buffer + * @param unicode : Formatted string buffer (unicode) + * @param len : descriptor length + * @retval None + */ +void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len) +{ + uint8_t idx = 0U; + uint8_t *pdesc; + + if (desc == NULL) + { + return; + } + + pdesc = desc; + *len = ((uint16_t)USBD_GetLen(pdesc) * 2U) + 2U; + + unicode[idx] = *(uint8_t *)len; + idx++; + unicode[idx] = USB_DESC_TYPE_STRING; + idx++; + + while (*pdesc != (uint8_t)'\0') + { + unicode[idx] = *pdesc; + pdesc++; + idx++; + + unicode[idx] = 0U; + idx++; + } +} + +/** + * @brief USBD_GetLen + * return the string length + * @param buf : pointer to the ascii string buffer + * @retval string length + */ +static uint8_t USBD_GetLen(uint8_t *buf) +{ + uint8_t len = 0U; + uint8_t *pbuff = buf; + + while (*pbuff != (uint8_t)'\0') + { + len++; + pbuff++; + } + + return len; +} +/** + * @} + */ + + +/** + * @} + */ + + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c similarity index 51% rename from Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c rename to Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c index 093afad8..8ac5491f 100644 --- a/Firmware/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c +++ b/Firmware/ThirdParty/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c @@ -2,28 +2,20 @@ ****************************************************************************** * @file usbd_ioreq.c * @author MCD Application Team - * @version V2.4.2 - * @date 11-December-2015 * @brief This file provides the IO requests APIs for control endpoints. ****************************************************************************** * @attention * - *

© COPYRIGHT 2015 STMicroelectronics

+ *

© Copyright (c) 2015 STMicroelectronics. + * All rights reserved.

* - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 * ****************************************************************************** - */ + */ /* Includes ------------------------------------------------------------------*/ #include "usbd_ioreq.h" @@ -33,56 +25,56 @@ */ -/** @defgroup USBD_IOREQ +/** @defgroup USBD_IOREQ * @brief control I/O requests module * @{ - */ + */ /** @defgroup USBD_IOREQ_Private_TypesDefinitions * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Defines * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Macros * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Variables * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_FunctionPrototypes * @{ - */ + */ /** * @} - */ + */ /** @defgroup USBD_IOREQ_Private_Functions * @{ - */ + */ /** * @brief USBD_CtlSendData @@ -92,17 +84,17 @@ * @param len: length of data to be sent * @retval status */ -USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { /* Set EP0 State */ - pdev->ep0_state = USBD_EP0_DATA_IN; + pdev->ep0_state = USBD_EP0_DATA_IN; pdev->ep_in[0].total_length = len; - pdev->ep_in[0].rem_length = len; - /* Start the transfer */ - USBD_LL_Transmit (pdev, 0x00, pbuf, len); - + pdev->ep_in[0].rem_length = len; + + /* Start the transfer */ + (void)USBD_LL_Transmit(pdev, 0x00U, pbuf, len); + return USBD_OK; } @@ -114,13 +106,12 @@ USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, * @param len: length of data to be sent * @retval status */ -USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlContinueSendData(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { - /* Start the next transfer */ - USBD_LL_Transmit (pdev, 0x00, pbuf, len); - + /* Start the next transfer */ + (void)USBD_LL_Transmit(pdev, 0x00U, pbuf, len); + return USBD_OK; } @@ -132,20 +123,17 @@ USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, * @param len: length of data to be received * @retval status */ -USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlPrepareRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { /* Set EP0 State */ - pdev->ep0_state = USBD_EP0_DATA_OUT; + pdev->ep0_state = USBD_EP0_DATA_OUT; pdev->ep_out[0].total_length = len; - pdev->ep_out[0].rem_length = len; + pdev->ep_out[0].rem_length = len; + /* Start the transfer */ - USBD_LL_PrepareReceive (pdev, - 0, - pbuf, - len); - + (void)USBD_LL_PrepareReceive(pdev, 0U, pbuf, len); + return USBD_OK; } @@ -157,32 +145,28 @@ USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, * @param len: length of data to be received * @retval status */ -USBD_StatusTypeDef USBD_CtlContinueRx (USBD_HandleTypeDef *pdev, - uint8_t *pbuf, - uint16_t len) +USBD_StatusTypeDef USBD_CtlContinueRx(USBD_HandleTypeDef *pdev, + uint8_t *pbuf, uint32_t len) { + (void)USBD_LL_PrepareReceive(pdev, 0U, pbuf, len); - USBD_LL_PrepareReceive (pdev, - 0, - pbuf, - len); return USBD_OK; } + /** * @brief USBD_CtlSendStatus * send zero lzngth packet on the ctl pipe * @param pdev: device instance * @retval status */ -USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev) +USBD_StatusTypeDef USBD_CtlSendStatus(USBD_HandleTypeDef *pdev) { - /* Set EP0 State */ pdev->ep0_state = USBD_EP0_STATUS_IN; - - /* Start the transfer */ - USBD_LL_Transmit (pdev, 0x00, NULL, 0); - + + /* Start the transfer */ + (void)USBD_LL_Transmit(pdev, 0x00U, NULL, 0U); + return USBD_OK; } @@ -192,21 +176,17 @@ USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev) * @param pdev: device instance * @retval status */ -USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev) +USBD_StatusTypeDef USBD_CtlReceiveStatus(USBD_HandleTypeDef *pdev) { /* Set EP0 State */ - pdev->ep0_state = USBD_EP0_STATUS_OUT; - - /* Start the transfer */ - USBD_LL_PrepareReceive ( pdev, - 0, - NULL, - 0); + pdev->ep0_state = USBD_EP0_STATUS_OUT; + + /* Start the transfer */ + (void)USBD_LL_PrepareReceive(pdev, 0U, NULL, 0U); return USBD_OK; } - /** * @brief USBD_GetRxCount * returns the received data length @@ -214,23 +194,23 @@ USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev) * @param ep_addr: endpoint address * @retval Rx Data blength */ -uint16_t USBD_GetRxCount (USBD_HandleTypeDef *pdev , uint8_t ep_addr) +uint32_t USBD_GetRxCount(USBD_HandleTypeDef *pdev, uint8_t ep_addr) { return USBD_LL_GetRxDataSize(pdev, ep_addr); } /** * @} - */ + */ /** * @} - */ + */ /** * @} - */ + */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index a4612835..b61024b5 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -1,5 +1,13 @@ -tup.include('build.lua') +-- Utility functions ----------------------------------------------------------- + +function run_now(command) + local handle + handle = io.popen(command) + local output = handle:read("*a") + local rc = {handle:close()} + return rc[1], output +end -- If we simply invoke python or python3 on a pristine Windows 10, it will try -- to open the Microsoft Store which will not work and hang tup instead. The @@ -13,6 +21,395 @@ function find_python3() error("Python 3 not found.") end +function add_pkg(pkg) + if pkg.is_included == true then + return + end + pkg.is_included = true + for _, file in pairs(pkg.code_files or {}) do + code_files += (pkg.root or '.')..'/'..file + end + for _, dir in pairs(pkg.include_dirs or {}) do + CFLAGS += '-I'..(pkg.root or '.')..'/'..dir + end + tup.append_table(CFLAGS, pkg.cflags or {}) + tup.append_table(LDFLAGS, pkg.ldflags or {}) + for _, pkg in pairs(pkg.include or {}) do + add_pkg(pkg) + end +end + +function compile(src_file, obj_file) + compiler = (tup.ext(src_file) == 'c') and CC or CXX + tup.frule{ + inputs={src_file}, + extra_inputs = {'autogen/interfaces.hpp', 'autogen/function_stubs.hpp', 'autogen/endpoints.hpp', 'autogen/type_info.hpp'}, + command='^co^ '..compiler..' -c %f '..tostring(CFLAGS)..' -o %o', + outputs={obj_file} + } +end + +-- Packages -------------------------------------------------------------------- + +odrive_firmware_pkg = { + root = '.', + include_dirs = { + '.', + 'MotorControl', + 'fibre/cpp/include', + }, + code_files = { + 'syscalls.c', + 'MotorControl/utils.cpp', + 'MotorControl/arm_sin_f32.c', + 'MotorControl/arm_cos_f32.c', + 'MotorControl/low_level.cpp', + 'MotorControl/axis.cpp', + 'MotorControl/motor.cpp', + 'MotorControl/thermistor.cpp', + 'MotorControl/encoder.cpp', + 'MotorControl/endstop.cpp', + 'MotorControl/acim_estimator.cpp', + 'MotorControl/mechanical_brake.cpp', + 'MotorControl/controller.cpp', + 'MotorControl/foc.cpp', + 'MotorControl/open_loop_controller.cpp', + 'MotorControl/oscilloscope.cpp', + 'MotorControl/sensorless_estimator.cpp', + 'MotorControl/trapTraj.cpp', + 'MotorControl/pwm_input.cpp', + 'MotorControl/main.cpp', + 'Drivers/STM32/stm32_system.cpp', + 'Drivers/STM32/stm32_gpio.cpp', + 'Drivers/STM32/stm32_nvm.c', + 'Drivers/STM32/stm32_spi_arbiter.cpp', + 'communication/can_simple.cpp', + 'communication/communication.cpp', + 'communication/ascii_protocol.cpp', + 'communication/interface_uart.cpp', + 'communication/interface_usb.cpp', + 'communication/interface_can.cpp', + 'communication/interface_i2c.cpp', + 'fibre/cpp/protocol.cpp', + 'FreeRTOS-openocd.c', + 'autogen/version.c' + } +} + +stm32f4xx_hal_pkg = { + root = 'ThirdParty/STM32F4xx_HAL_Driver', + include_dirs = { + 'Inc', + }, + code_files = { + 'Src/stm32f4xx_hal.c', + 'Src/stm32f4xx_hal_adc.c', + 'Src/stm32f4xx_hal_adc_ex.c', + 'Src/stm32f4xx_hal_can.c', + 'Src/stm32f4xx_hal_cortex.c', + 'Src/stm32f4xx_hal_dma.c', + 'Src/stm32f4xx_hal_dma_ex.c', + 'Src/stm32f4xx_hal_flash.c', + 'Src/stm32f4xx_hal_flash_ex.c', + 'Src/stm32f4xx_hal_flash_ramfunc.c', + 'Src/stm32f4xx_hal_gpio.c', + 'Src/stm32f4xx_hal_i2c.c', + 'Src/stm32f4xx_hal_i2c_ex.c', + 'Src/stm32f4xx_hal_pcd.c', + 'Src/stm32f4xx_hal_pcd_ex.c', + 'Src/stm32f4xx_hal_pwr.c', + 'Src/stm32f4xx_hal_pwr_ex.c', + 'Src/stm32f4xx_hal_rcc.c', + 'Src/stm32f4xx_hal_rcc_ex.c', + 'Src/stm32f4xx_hal_spi.c', + 'Src/stm32f4xx_hal_tim.c', + 'Src/stm32f4xx_hal_tim_ex.c', + 'Src/stm32f4xx_hal_uart.c', + 'Src/stm32f4xx_ll_usb.c', + }, + cflags = {'-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-DFPU_FPV4'} +} + +stm32f7xx_hal_pkg = { + root = 'Private/ThirdParty/STM32F7xx_HAL_Driver', + include_dirs = { + 'Inc', + }, + code_files = { + 'Src/stm32f7xx_hal.c', + 'Src/stm32f7xx_hal_adc.c', + 'Src/stm32f7xx_hal_adc_ex.c', + 'Src/stm32f7xx_hal_can.c', + 'Src/stm32f7xx_hal_cortex.c', + 'Src/stm32f7xx_hal_dma.c', + 'Src/stm32f7xx_hal_dma_ex.c', + 'Src/stm32f7xx_hal_exti.c', + 'Src/stm32f7xx_hal_flash.c', + 'Src/stm32f7xx_hal_flash_ex.c', + 'Src/stm32f7xx_hal_gpio.c', + 'Src/stm32f7xx_hal_i2c.c', + 'Src/stm32f7xx_hal_i2c_ex.c', + 'Src/stm32f7xx_hal_i2s.c', + 'Src/stm32f7xx_hal_pcd.c', + 'Src/stm32f7xx_hal_pcd_ex.c', + 'Src/stm32f7xx_hal_pwr.c', + 'Src/stm32f7xx_hal_pwr_ex.c', + 'Src/stm32f7xx_hal_rcc.c', + 'Src/stm32f7xx_hal_rcc_ex.c', + 'Src/stm32f7xx_hal_spi.c', + 'Src/stm32f7xx_hal_spi_ex.c', + 'Src/stm32f7xx_hal_tim.c', + 'Src/stm32f7xx_hal_tim_ex.c', + 'Src/stm32f7xx_hal_uart.c', + 'Src/stm32f7xx_hal_uart_ex.c', + 'Src/stm32f7xx_ll_usb.c', + }, + cflags = {'-DARM_MATH_CM7', '-mcpu=cortex-m7', '-mfpu=fpv5-sp-d16'} +} + +freertos_pkg = { + root = 'ThirdParty/FreeRTOS', + include_dirs = { + 'Source/include', + 'Source/CMSIS_RTOS', + }, + code_files = { + 'Source/croutine.c', + 'Source/event_groups.c', + 'Source/list.c', + 'Source/queue.c', + 'Source/stream_buffer.c', + 'Source/tasks.c', + 'Source/timers.c', + 'Source/CMSIS_RTOS/cmsis_os.c', + 'Source/portable/MemMang/heap_4.c', + } +} + +cmsis_pkg = { + root = 'ThirdParty/CMSIS', + include_dirs = { + 'Include', + 'Device/ST/STM32F7xx/Include', + 'Device/ST/STM32F4xx/Include' + }, + ldflags = {'-LThirdParty/CMSIS/Lib/GCC'}, +} + +stm32_usb_device_library_pkg = { + root = 'ThirdParty/STM32_USB_Device_Library', + include_dirs = { + 'Core/Inc', + 'Class/CDC/Inc', + }, + code_files = { + 'Core/Src/usbd_core.c', + 'Core/Src/usbd_ctlreq.c', + 'Core/Src/usbd_ioreq.c', + 'Class/CDC/Src/usbd_cdc.c', + } +} + +crypto_pkg = { + root = 'Private', + include_dirs = { + 'ThirdParty/sha-2', + 'ThirdParty/rsa_embedded', + }, + code_files = { + 'ThirdParty/sha-2/sha-256.c', + 'ThirdParty/rsa_embedded/rsa.c', + } +} + +board_v3 = { + root = 'Board/v3', + root_interface = 'ODrive3', + include = {stm32f4xx_hal_pkg}, + include_dirs = { + 'Inc', + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F', + }, + code_files = { + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c', + '../../Drivers/DRV8301/drv8301.cpp', + 'board.cpp', + 'startup_stm32f405xx.s', + 'Src/stm32f4xx_hal_timebase_TIM.c', + 'Src/tim.c', + 'Src/dma.c', + 'Src/freertos.c', + 'Src/main.c', + 'Src/usbd_conf.c', + 'Src/spi.c', + 'Src/usart.c', + 'Src/usbd_cdc_if.c', + 'Src/adc.c', + 'Src/stm32f4xx_hal_msp.c', + 'Src/usbd_desc.c', + 'Src/stm32f4xx_it.c', + 'Src/usb_device.c', + 'Src/can.c', + 'Src/system_stm32f4xx.c', + 'Src/gpio.c', + 'Src/i2c.c', + }, + cflags = {'-DSTM32F405xx', '-DHW_VERSION_MAJOR=3'}, + ldflags = { + '-TBoard/v3/STM32F405RGTx_FLASH.ld', + '-larm_cortexM4lf_math', + } +} + +board_v4 = { + root = 'Private/v4', + root_interface = 'ODrive4', + include = {stm32f7xx_hal_pkg}, + include_dirs = { + '..', + 'Inc', + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1', + }, + code_files = { + '../../ThirdParty/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1/port.c', + '../Drivers/DRV8353/drv8353.cpp', + '../Drivers/status_led.cpp', + 'startup_stm32f722xx.s', + 'board.cpp', + 'Src/main.c', + 'Src/gpio.c', + 'Src/adc.c', + 'Src/can.c', + 'Src/dma.c', + 'Src/freertos.c', + 'Src/spi.c', + 'Src/tim.c', + 'Src/stm32f7xx_it.c', + 'Src/stm32f7xx_hal_msp.c', + 'Src/stm32f7xx_hal_timebase_tim.c', + 'Src/system_stm32f7xx.c', + 'Src/i2s.c', + 'Src/usart.c', + 'Src/usb_device.c', + 'Src/usbd_conf.c', + 'Src/usbd_desc.c', + 'Src/usbd_cdc_if.c', + 'Src/i2c.c', + }, + cflags = {'-DSTM32F722xx', '-DHW_VERSION_MAJOR=4'}, + ldflags = { + '-TPrivate/v4/STM32F722RETx_FLASH.ld', + '-larm_cortexM7lfsp_math', + } +} + +boards = { + ["v3.1"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=1 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.2"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=2 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.3"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=3 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.4-24V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=4 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.4-48V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=4 -DHW_VERSION_VOLTAGE=48"}}, + ["v3.5-24V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=5 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.5-48V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=5 -DHW_VERSION_VOLTAGE=48"}}, + ["v3.6-24V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=6 -DHW_VERSION_VOLTAGE=24"}}, + ["v3.6-56V"] = {include={board_v3}, cflags={"-DHW_VERSION_MINOR=6 -DHW_VERSION_VOLTAGE=56"}}, + ["v4.0-56V"] = {include={board_v4}, cflags={"-DHW_VERSION_MINOR=0 -DHW_VERSION_VOLTAGE=56"}}, + ["v4.1-58V"] = {include={board_v4}, cflags={"-DHW_VERSION_MINOR=1 -DHW_VERSION_VOLTAGE=58"}}, +} + + +-- Toolchain setup ------------------------------------------------------------- + +CC='arm-none-eabi-gcc -std=c99' +CXX='arm-none-eabi-g++ -std=c++17 -Wno-register' +LINKER='arm-none-eabi-g++' + +-- C-specific flags +CFLAGS += '-D__weak="__attribute__((weak))"' +CFLAGS += '-D__packed="__attribute__((__packed__))"' +CFLAGS += '-DUSE_HAL_DRIVER' + +CFLAGS += '-mthumb' +CFLAGS += '-mfloat-abi=hard' +CFLAGS += '-Wno-psabi' -- suppress unimportant note about ABI compatibility in GCC 10 +CFLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} +CFLAGS += '-g' + +-- linker flags +LDFLAGS += '-flto -lc -lm -lnosys' -- libs +LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' +LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' + + +-- Handle Configuration Options ------------------------------------------------ + +-- Switch between board versions +boardversion = tup.getconfig("BOARD_VERSION") +if boardversion == "" then + error("board version not specified - take a look at tup.config.default") +elseif boards[boardversion] == nil then + error("unknown board version "..boardversion) +end +board = boards[boardversion] + +-- USB I/O settings +if tup.getconfig("USB_PROTOCOL") == "native" or tup.getconfig("USB_PROTOCOL") == "" then + CFLAGS += "-DUSB_PROTOCOL_NATIVE" +elseif tup.getconfig("USB_PROTOCOL") == "native-stream" then + CFLAGS += "-DUSB_PROTOCOL_NATIVE_STREAM_BASED" +elseif tup.getconfig("USB_PROTOCOL") == "stdout" then + CFLAGS += "-DUSB_PROTOCOL_STDOUT" +elseif tup.getconfig("USB_PROTOCOL") == "none" then + CFLAGS += "-DUSB_PROTOCOL_NONE" +else + error("unknown USB protocol") +end + +-- UART I/O settings +if tup.getconfig("UART_PROTOCOL") == "native" then + CFLAGS += "-DUART_PROTOCOL_NATIVE" +elseif tup.getconfig("UART_PROTOCOL") == "ascii" or tup.getconfig("UART_PROTOCOL") == "" then + CFLAGS += "-DUART_PROTOCOL_ASCII" +elseif tup.getconfig("UART_PROTOCOL") == "stdout" then + CFLAGS += "-DUART_PROTOCOL_STDOUT" +elseif tup.getconfig("UART_PROTOCOL") == "none" then + CFLAGS += "-DUART_PROTOCOL_NONE" +else + error("unknown UART protocol "..tup.getconfig("UART_PROTOCOL")) +end + +-- GPIO settings +if tup.getconfig("STEP_DIR") == "y" then + if tup.getconfig("UART_PROTOCOL") == "none" then + CFLAGS += "-DUSE_GPIO_MODE_STEP_DIR" + else + error("Step/dir mode conflicts with UART. Set CONFIG_UART_PROTOCOL to none.") + end +end + +-- Compiler settings +if tup.getconfig("STRICT") == "true" then + CFLAGS += '-Werror' +end + +if tup.getconfig("NO_DRM") == "true" then + CFLAGS += '-DNO_DRM' +end + +-- debug build +if tup.getconfig("DEBUG") == "true" then + CFLAGS += '-gdwarf-2 -Og' +else + CFLAGS += '-O2' +end + +if tup.getconfig("USE_LTO") == "true" then + CFLAGS += '-flto' +end + + +-- Generate Tup Rules ---------------------------------------------------------- + python_command = find_python3() print('Using python command "'..python_command..'"') @@ -27,241 +424,33 @@ tup.frule{ outputs={'autogen/version.c'} } -board_v3 = { - dir = 'Board/v3', - root_interface = 'ODrive3', - sources = {'Drivers/DRV8301/drv8301.cpp', 'Board/v3/board.cpp'}, - flags = {'-DSTM32F405xx', '-DARM_MATH_CM4', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16', '-DFPU_FPV4'}, - ldflags = {'-TBoard/v3/STM32F405RGTx_FLASH.ld', '-LBoard/v3/Drivers/CMSIS/Lib', '-larm_cortexM4lf_math', '-mcpu=cortex-m4', '-mfpu=fpv4-sp-d16'} -} - -board_v4 = { - dir = 'Board/v4', - root_interface = 'ODrive4', - sources = {'Drivers/DRV8353/drv8353.cpp', 'Drivers/status_led.cpp', 'Board/v4/board.cpp', 'lockdown/rsa_embedded/rsa.c', 'lockdown/sha-2/sha-256.c',}, - flags = {'-DSTM32F722xx', '-DARM_MATH_CM7', '-mcpu=cortex-m7', '-mfpu=fpv5-sp-d16'}, - ldflags = {'-TBoard/v4/STM32F722RETx_FLASH.ld', '-LBoard/v4/Drivers/CMSIS/Lib/GCC', '-larm_cortexM7lfsp_math', '-mcpu=cortex-m7', '-mfpu=fpv5-sp-d16'} -} - --- Switch between board versions -boardversion = tup.getconfig("BOARD_VERSION") -if boardversion == "v3.1" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=1" - board.flags += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.2" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=2" - board.flags += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.3" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=3" - board.flags += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.4-24V" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4" - board.flags += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.4-48V" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=4" - board.flags += "-DHW_VERSION_VOLTAGE=48" -elseif boardversion == "v3.5-24V" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5" - board.flags += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.5-48V" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=5" - board.flags += "-DHW_VERSION_VOLTAGE=48" -elseif boardversion == "v3.6-24V" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6" - board.flags += "-DHW_VERSION_VOLTAGE=24" -elseif boardversion == "v3.6-56V" then - board = board_v3 - board.flags += "-DHW_VERSION_MAJOR=3 -DHW_VERSION_MINOR=6" - board.flags += "-DHW_VERSION_VOLTAGE=56" -elseif boardversion == "v4.0-56V" then - board = board_v4 - board.flags += "-DHW_VERSION_MAJOR=4 -DHW_VERSION_MINOR=0" - board.flags += "-DHW_VERSION_VOLTAGE=56" -elseif boardversion == "v4.1-58V" then - board = board_v4 - board.flags += "-DHW_VERSION_MAJOR=4 -DHW_VERSION_MINOR=1" - board.flags += "-DHW_VERSION_VOLTAGE=58" -elseif boardversion == "" then - error("board version not specified - take a look at tup.config.default") -else - error("unknown board version "..boardversion) -end -buildsuffix = boardversion - --- USB I/O settings -if tup.getconfig("USB_PROTOCOL") == "native" or tup.getconfig("USB_PROTOCOL") == "" then - FLAGS += "-DUSB_PROTOCOL_NATIVE" -elseif tup.getconfig("USB_PROTOCOL") == "native-stream" then - FLAGS += "-DUSB_PROTOCOL_NATIVE_STREAM_BASED" -elseif tup.getconfig("USB_PROTOCOL") == "stdout" then - FLAGS += "-DUSB_PROTOCOL_STDOUT" -elseif tup.getconfig("USB_PROTOCOL") == "none" then - FLAGS += "-DUSB_PROTOCOL_NONE" -else - error("unknown USB protocol") -end - --- UART I/O settings -if tup.getconfig("UART_PROTOCOL") == "native" then - FLAGS += "-DUART_PROTOCOL_NATIVE" -elseif tup.getconfig("UART_PROTOCOL") == "ascii" or tup.getconfig("UART_PROTOCOL") == "" then - FLAGS += "-DUART_PROTOCOL_ASCII" -elseif tup.getconfig("UART_PROTOCOL") == "stdout" then - FLAGS += "-DUART_PROTOCOL_STDOUT" -elseif tup.getconfig("UART_PROTOCOL") == "none" then - FLAGS += "-DUART_PROTOCOL_NONE" -else - error("unknown UART protocol "..tup.getconfig("UART_PROTOCOL")) -end - --- GPIO settings -if tup.getconfig("STEP_DIR") == "y" then - if tup.getconfig("UART_PROTOCOL") == "none" then - FLAGS += "-DUSE_GPIO_MODE_STEP_DIR" - else - error("Step/dir mode conflicts with UART. Set CONFIG_UART_PROTOCOL to none.") - end -end - --- Compiler settings -if tup.getconfig("STRICT") == "true" then - FLAGS += '-Werror' -end - -if tup.getconfig("NO_DRM") == "true" then - FLAGS += '-DNO_DRM' -end - --- C-specific flags -FLAGS += board.flags -FLAGS += '-D__weak="__attribute__((weak))"' -FLAGS += '-D__packed="__attribute__((__packed__))"' -FLAGS += '-DUSE_HAL_DRIVER' - -FLAGS += '-mthumb' -FLAGS += '-mfloat-abi=hard' -FLAGS += '-Wno-psabi' -- suppress unimportant note about ABI compatibility in GCC 10 -FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} -FLAGS += '-g' - --- linker flags -LDFLAGS += board.ldflags -LDFLAGS += '-flto -lc -lm -lnosys' -- libs -LDFLAGS += '-mthumb -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' -LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' - --- debug build -if tup.getconfig("DEBUG") == "true" then - FLAGS += '-gdwarf-2' - OPT += '-Og' -else - FLAGS += '-g' - OPT += '-O2' -end - -if tup.getconfig("USE_LTO") == "true" then - OPT += '-flto' - LDFLAGS += '-flto' -end - --- common flags for ASM, C and C++ -OPT += '-ffast-math' -tup.append_table(FLAGS, OPT) -tup.append_table(LDFLAGS, OPT) - -toolchain = GCCToolchain('arm-none-eabi-', 'build', FLAGS, LDFLAGS) - - --- Load list of source files Makefile that was autogenerated by CubeMX -vars = parse_makefile_vars(board.dir..'/Makefile') - --- ASM sources must precede C sources due to LTO removing weak symbols which appear after strong symbols --- in the call to the linker: https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966 -all_stm_sources = (vars['ASM_SOURCES'] or '')..' '..(vars['CPP_SOURCES'] or '')..' '..(vars['C_SOURCES'] or '') -for src in string.gmatch(all_stm_sources, "%S+") do - stm_sources += board.dir..'/'..src -end -for src in string.gmatch(vars['C_INCLUDES'] or '', "%S+") do - stm_includes += board.dir..'/'..string.sub(src, 3, -1) -- remove "-I" from each include path -end - -- Autogen files from YAML interface definitions +root_interface = board.include[1].root_interface tup.frule{inputs={'fibre/cpp/interfaces_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/interfaces.hpp'} tup.frule{inputs={'fibre/cpp/function_stubs_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/function_stubs.hpp'} -tup.frule{inputs={'fibre/cpp/endpoints_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --generate-endpoints '..board.root_interface..' --template %f --output %o', outputs='autogen/endpoints.hpp'} +tup.frule{inputs={'fibre/cpp/endpoints_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --generate-endpoints '..root_interface..' --template %f --output %o', outputs='autogen/endpoints.hpp'} tup.frule{inputs={'fibre/cpp/type_info_template.j2'}, command=python_command..' interface_generator_stub.py --definitions odrive-interface.yaml --template %f --output %o', outputs='autogen/type_info.hpp'} --- TODO: cleaner separation of the platform code and the rest -stm_includes += '.' ---stm_includes += 'Drivers/DRV8301' -build{ - name='stm_platform', - type='objects', - toolchains={toolchain}, - packages={}, - sources=stm_sources, - includes=stm_includes -} -sources = { - 'syscalls.c', - 'MotorControl/utils.cpp', - 'MotorControl/arm_sin_f32.c', - 'MotorControl/arm_cos_f32.c', - 'MotorControl/low_level.cpp', - 'MotorControl/axis.cpp', - 'MotorControl/motor.cpp', - 'MotorControl/thermistor.cpp', - 'MotorControl/encoder.cpp', - 'MotorControl/endstop.cpp', - 'MotorControl/acim_estimator.cpp', - 'MotorControl/mechanical_brake.cpp', - 'MotorControl/controller.cpp', - 'MotorControl/foc.cpp', - 'MotorControl/open_loop_controller.cpp', - 'MotorControl/oscilloscope.cpp', - 'MotorControl/sensorless_estimator.cpp', - 'MotorControl/trapTraj.cpp', - 'MotorControl/pwm_input.cpp', - 'MotorControl/main.cpp', - 'Drivers/STM32/stm32_system.cpp', - 'Drivers/STM32/stm32_gpio.cpp', - 'Drivers/STM32/stm32_nvm.c', - 'Drivers/STM32/stm32_spi_arbiter.cpp', - 'communication/can_simple.cpp', - 'communication/communication.cpp', - 'communication/ascii_protocol.cpp', - 'communication/interface_uart.cpp', - 'communication/interface_usb.cpp', - 'communication/interface_can.cpp', - 'communication/interface_i2c.cpp', - 'fibre/cpp/protocol.cpp', - 'FreeRTOS-openocd.c', - 'autogen/version.c' -} -tup.append_table(sources, board.sources) +add_pkg(freertos_pkg) +add_pkg(cmsis_pkg) +add_pkg(stm32_usb_device_library_pkg) +add_pkg(crypto_pkg) +add_pkg(board) +add_pkg(odrive_firmware_pkg) -build{ - name='ODriveFirmware', - toolchains={toolchain}, - --toolchains={LLVMToolchain('x86_64', {'-Ofast'}, {'-flto'})}, - packages={'stm_platform'}, - sources=sources, - includes={ - 'Drivers/DRV8301', - 'MotorControl', - 'fibre/cpp/include', - '.', - "doctest" - } + +for _, src_file in pairs(code_files) do + obj_file = "build/"..src_file:gsub("/","_")..".o" + object_files += obj_file + compile(src_file, obj_file) +end + +tup.frule{ + inputs=object_files, + command='^c^ '..LINKER..' %f '..tostring(CFLAGS)..' '..tostring(LDFLAGS).. + ' -Wl,-Map=%O.map -o %o', + outputs={'build/ODriveFirmware.elf', extra_outputs={'build/ODriveFirmware.map'}} } if tup.getconfig('DOCTEST') == 'true' then diff --git a/Firmware/build.lua b/Firmware/build.lua deleted file mode 100644 index e6d93e41..00000000 --- a/Firmware/build.lua +++ /dev/null @@ -1,184 +0,0 @@ - --- This file contains support functions for Tupfile.lua - -function trim(s) - return (s:gsub("^%s*(.-)%s*$", "%1")) -end - -function string:split(sep) - local sep, fields = sep or ":", {} - local pattern = string.format("([^%s]+)", sep) - self:gsub(pattern, function(c) fields[#fields+1] = c end) - return fields -end - -function run_now(command) - local handle - handle = io.popen(command) - local output = handle:read("*a") - local rc = {handle:close()} - return rc[1], output -end - --- Very basic parser to retrieve variables from a Makefile -function parse_makefile_vars(makefile) - vars = {} - current_var = nil - for line in io.lines(tup.getcwd()..'/'..makefile) do - if current_var == nil then - i,j = string.find(line, "+=") - if not i then - i,j = string.find(line, "=") - end - if i then - current_var = trim(string.sub(line, 1, i-1)) - vars[current_var] = vars[current_var] or '' - line = string.sub(line, j+1, -1) - --print("varname: "..varname.." the rest: "..line) - end - end - - if current_var != nil then - --print("append chunk "..trim(line).." to "..current_var) - vars[current_var] = vars[current_var]..' '..trim(line) - if string.sub(vars[current_var], -1) == '\\' then - vars[current_var] = string.sub(vars[current_var], 1, -2) - else - current_var = nil - end - end - end - return vars -end - - - - -function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) - - -- add some default compiler flags - -- -fstack-usage gives a warning for some functions containing inline assembly (prvPortStartFirstTask in particular) - -- so for now we just disable it - calculate_stack_usage = false - if calculate_stack_usage then - compiler_flags += '-fstack-usage' - end - - local gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) - -- convert include list to flags - inc_flags = {} - for _,inc in pairs(includes) do - inc_flags += "-I"..inc - end - -- todo: vary build directory - obj_file = builddir.."/obj/"..src:gsub("/","_")..".o" - outputs.object_files += obj_file - if gen_su_file then - su_file = builddir.."/"..src:gsub("/","_")..".su" - extra_outputs = { su_file } - outputs.su_files += su_file - else - extra_outputs = {} - end - extra_inputs = {'autogen/interfaces.hpp', 'autogen/function_stubs.hpp', 'autogen/endpoints.hpp', 'autogen/type_info.hpp'} -- TODO: fix hack - tup.frule{ - inputs= { src, extra_inputs=extra_inputs }, - command=compiler..' -c %f '.. - tostring(compiler_flags)..' '.. -- CFLAGS for this compiler - tostring(inc_flags)..' '.. -- CFLAGS for this translation unit - tostring(flags).. -- CFLAGS for this translation unit - ' -o %o', - outputs={obj_file,extra_outputs=extra_outputs} - } - end - return { - compile_c = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'gcc -std=c99', compiler_flags, calculate_stack_usage, src, flags, includes, outputs) end, - compile_cpp = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'g++ -std=c++17 -Wno-register', compiler_flags, calculate_stack_usage, src, flags, includes, outputs) end, - compile_asm = function(src, flags, includes, outputs) gcc_generic_compiler(prefix..'gcc -x assembler-with-cpp', compiler_flags, false, src, flags, includes, outputs) end, - link = function(objects, output_name) - output_name = builddir..'/'..output_name - tup.frule{ - inputs=objects, - command=prefix..'g++ %f '.. - tostring(linker_flags)..' '.. - '-Wl,-Map=%O.map'.. - ' -o %o', - outputs={output_name..'.elf', extra_outputs={output_name..'.map'}} - } - -- display the size - tup.frule{inputs={output_name..'.elf'}, command=prefix..'size %f'} - -- generate disassembly - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objdump %f -dSC > %o', outputs={output_name..'.asm'}} - -- create *.hex and *.bin output formats - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O ihex %f %o', outputs={output_name..'.hex'}} - tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O binary -S %f %o', outputs={output_name..'.bin'}} - end - } -end - -all_packages = {} - --- toolchains: Each element of this list is a collection of functions, such as compile_c, link, ... --- You can create a new toolchain object for each platform you want to build for. -function build(args) - if args.toolchain == nil then args.toolchain = {} end - if args.sources == nil then args.sources = {} end - if args.includes == nil then args.includes = {} end - if args.packages == nil then args.packages = {} end - if args.c_flags == nil then args.c_flags = {} end - if args.cpp_flags == nil then args.cpp_flags = {} end - if args.asm_flags == nil then args.asm_flags = {} end - if args.ld_flags == nil then args.ld_flags = {} end - if args.linker_objects == nil then args.linker_objects = {} end - - -- add includes of other packages - for _,pkg_name in pairs(args.packages) do - --print('depend on package '..pkg_name) - pkg = all_packages[pkg_name] - if pkg == nil then - error("unknown package "..pkg_name) - end - -- add path of each include - for _,inc in pairs(pkg.includes or {}) do - args.includes += tostring(inc) - end - tup.append_table(args.linker_objects, pkg.object_files) - end - - -- run everything once for every toolchain - for _,toolchain in pairs(args.toolchains) do - -- compile - outputs = {} - for _,src in pairs(args.sources) do - --print("compile "..src) - if tup.ext(src) == 'c' then - toolchain.compile_c(src, args.c_flags, args.includes, outputs) - elseif tup.ext(src) == 'cpp' then - toolchain.compile_cpp(src, args.cpp_flags, args.includes, outputs) - elseif tup.ext(src) == 's' or tup.ext(src) == 'asm' then - toolchain.compile_asm(src, args.asm_flags, args.includes, outputs) - else - error('unrecognized file ending') - end - end - - -- link - if outputs.object_files != nil and args.type != 'objects' then - tup.append_table(args.linker_objects, outputs.object_files) - toolchain.link(args.linker_objects, args.name) - end - - outputs.includes = {} - for _,inc in pairs(args.includes) do - table.insert(outputs.includes, inc) - end - if args.name != nil then - all_packages[args.name] = outputs - end - end - - --for k,v in pairs(all_packages) do - -- print('have package '..k) - --end -end - From fc1c89c8ab90b5b8cfc3af8c4f62f11d3a9b03dc Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 18 Nov 2020 19:30:03 +0100 Subject: [PATCH 108/124] fix compilation for v3.x --- Firmware/Tupfile.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index b61024b5..32d4a041 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -264,7 +264,7 @@ board_v3 = { board_v4 = { root = 'Private/v4', root_interface = 'ODrive4', - include = {stm32f7xx_hal_pkg}, + include = {stm32f7xx_hal_pkg, crypto_pkg}, include_dirs = { '..', 'Inc', @@ -435,7 +435,6 @@ tup.frule{inputs={'fibre/cpp/type_info_template.j2'}, command=python_command..' add_pkg(freertos_pkg) add_pkg(cmsis_pkg) add_pkg(stm32_usb_device_library_pkg) -add_pkg(crypto_pkg) add_pkg(board) add_pkg(odrive_firmware_pkg) From 287dd47b8f71f6faf6950a3ab844f06ddff5d429 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 19 Nov 2020 12:19:02 +0100 Subject: [PATCH 109/124] move startup delay to fix race condition --- Firmware/MotorControl/axis.cpp | 14 -------------- Firmware/MotorControl/main.cpp | 21 +++++++++++++++++++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 9b12a3c4..07fd263f 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -428,20 +428,6 @@ bool Axis::run_idle_loop() { // Infinite loop that does calibration and enters main control loop as appropriate void Axis::run_state_machine_loop() { - - // Wait for up to 2s for motor to become ready to allow for error-free - // startup. This delay gives the current sensor calibration time to - // converge. If the DRV chip is unpowered, the motor will not become ready - // but we still enter idle state. - for (size_t i = 0; i < 2000; ++i) { - if (motor_.current_meas_.has_value()) { - break; - } - osDelay(1); - } - - sensorless_estimator_.error_ &= ~SensorlessEstimator::ERROR_UNKNOWN_CURRENT_MEASUREMENT; - for (;;) { // Load the task chain if a specific request is pending if (requested_state_ != AXIS_STATE_UNDEFINED) { diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 834ce743..469f71a0 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -485,6 +485,25 @@ static void rtos_main(void*) { // Start PWM and enable adc interrupts/callbacks start_adc_pwm(); + start_analog_thread(); + + // Wait for up to 2s for motor to become ready to allow for error-free + // startup. This delay gives the current sensor calibration time to + // converge. If the DRV chip is unpowered, the motor will not become ready + // but we still enter idle state. + for (size_t i = 0; i < 2000; ++i) { + bool motors_ready = std::all_of(axes.begin(), axes.end(), [](auto& axis) { + return axis.motor_.current_meas_.has_value(); + }); + if (motors_ready) { + break; + } + osDelay(1); + } + + for (auto& axis: axes) { + axis.sensorless_estimator_.error_ &= ~SensorlessEstimator::ERROR_UNKNOWN_CURRENT_MEASUREMENT; + } // Start state machine threads. Each thread will go through various calibration // procedures and then run the actual controller loops. @@ -493,8 +512,6 @@ static void rtos_main(void*) { axes[i].start_thread(); } - start_analog_thread(); - odrv.system_stats_.fully_booted = true; // Main thread finished starting everything and can delete itself now (yes this is legal). From f150114d7707dab3f2182d1deee29b5495c6210b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 19 Nov 2020 15:32:50 +0100 Subject: [PATCH 110/124] fix CI --- .github/workflows/compile.yaml | 2 +- .github/workflows/nightly.yaml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index f8468433..380730df 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -76,7 +76,7 @@ jobs: run: | Invoke-WebRequest -Uri "http://gittup.org/tup/win32/tup-latest.zip" -OutFile ".\tup-latest.zip" Expand-Archive ".\tup-latest.zip" -DestinationPath ".\tup-latest" -Force - echo "::add-path::$(Resolve-Path .)\tup-latest" + echo "$(Resolve-Path .)\tup-latest" >> $GITHUB_PATH choco install gcc-arm-embedded # downloads https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-win32.zip diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 8769d92d..42951d0f 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -16,7 +16,11 @@ jobs: steps: - name: Install odrivetool run: | - pip3 install monotonic # TODO: this is dishonest. Must be removed as soon as v0.5.0 is published! + # TODO: this is a workaround for https://github.com/pypa/setuptools/issues/2353 and we + # can remove it as soon as python3-setuptools on GitHub's ubuntu-latest + # moves to version 50.1+. + pip3 list | grep setuptools # show version + export SETUPTOOLS_USE_DISTUTILS=stdlib pip3 install odrive # This one currently fails because Github Actions runs pip as non-root @@ -27,7 +31,7 @@ jobs: # This step is mentioned in the user guide - name: Add ~/.local/bin to path if: matrix.os == 'ubuntu-latest' - run: echo "::add-path::~/.local/bin" + run: echo "~/.local/bin" >> $GITHUB_PATH - name: Launch odrivetool # This returns a non-zero exit code if the odrivetool throws an exception From da6d33e75d5729c3d52cab87e95888f1061c83a0 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 19 Nov 2020 21:44:36 -0800 Subject: [PATCH 111/124] add extra build outputs --- Firmware/Tupfile.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 32d4a041..9fb17f2e 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -451,6 +451,11 @@ tup.frule{ ' -Wl,-Map=%O.map -o %o', outputs={'build/ODriveFirmware.elf', extra_outputs={'build/ODriveFirmware.map'}} } +-- display the size +tup.frule{inputs={'build/ODriveFirmware.elf'}, command='arm-none-eabi-size %f'} +-- create *.hex and *.bin output formats +tup.frule{inputs={'build/ODriveFirmware.elf'}, command='arm-none-eabi-objcopy -O ihex %f %o', outputs={'build/ODriveFirmware.hex'}} +tup.frule{inputs={'build/ODriveFirmware.elf'}, command='arm-none-eabi-objcopy -O binary -S %f %o', outputs={'build/ODriveFirmware.bin'}} if tup.getconfig('DOCTEST') == 'true' then TEST_INCLUDES = '-I. -I./MotorControl -I./fibre/cpp/include -I./Drivers/DRV8301 -I./doctest' From 88c1a3399af05706759ddd1aa98d73a76826c025 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 24 Nov 2020 16:49:00 +0100 Subject: [PATCH 112/124] enable GPIO clocks on demand --- Firmware/.vscode/launch.json | 4 ++-- Firmware/Drivers/STM32/stm32_gpio.cpp | 19 ++++++++++++++++++- Firmware/Private | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Firmware/.vscode/launch.json b/Firmware/.vscode/launch.json index d277ce4c..48ee3ff1 100644 --- a/Firmware/.vscode/launch.json +++ b/Firmware/.vscode/launch.json @@ -32,7 +32,7 @@ "openOCDLaunchCommands": [ "reset_config none separate" ], - "svdFile": "${workspaceRoot}/Board/v4/STM32F7x.svd", + "svdFile": "${workspaceRoot}/Private/v4/STM32F722.svd", "cwd": "${workspaceRoot}" }, { @@ -85,7 +85,7 @@ "interface/stlink.cfg", "target/stm32f7x.cfg", ], - "svdFile": "${workspaceRoot}/Board/v4/STM32F722.svd", + "svdFile": "${workspaceRoot}/Private/v4/STM32F722.svd", "cwd": "${workspaceRoot}" }, { diff --git a/Firmware/Drivers/STM32/stm32_gpio.cpp b/Firmware/Drivers/STM32/stm32_gpio.cpp index 36cb4c0b..eca0e653 100644 --- a/Firmware/Drivers/STM32/stm32_gpio.cpp +++ b/Firmware/Drivers/STM32/stm32_gpio.cpp @@ -43,8 +43,25 @@ IRQn_Type get_irq_number(uint16_t pin_number) { bool Stm32Gpio::config(uint32_t mode, uint32_t pull, uint32_t speed) { - if (!port_) + if (port_ == GPIOA) { + __HAL_RCC_GPIOA_CLK_ENABLE(); + } else if (port_ == GPIOB) { + __HAL_RCC_GPIOB_CLK_ENABLE(); + } else if (port_ == GPIOC) { + __HAL_RCC_GPIOC_CLK_ENABLE(); + } else if (port_ == GPIOD) { + __HAL_RCC_GPIOD_CLK_ENABLE(); + } else if (port_ == GPIOE) { + __HAL_RCC_GPIOE_CLK_ENABLE(); + } else if (port_ == GPIOF) { + __HAL_RCC_GPIOF_CLK_ENABLE(); + } else if (port_ == GPIOG) { + __HAL_RCC_GPIOG_CLK_ENABLE(); + } else if (port_ == GPIOH) { + __HAL_RCC_GPIOH_CLK_ENABLE(); + } else { return false; + } size_t position = get_pin_number(); diff --git a/Firmware/Private b/Firmware/Private index aeae8a9c..3ad39794 160000 --- a/Firmware/Private +++ b/Firmware/Private @@ -1 +1 @@ -Subproject commit aeae8a9ceaacdfc65915ccf1157650b157ee5e89 +Subproject commit 3ad39794bb0d7c44e9eed7e910d4df35a01513ba From a3b7497150b3a3da96c5a2d394112ccc30bb7bf7 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 24 Nov 2020 23:58:44 -0800 Subject: [PATCH 113/124] change LED color and add eye curve --- Firmware/MotorControl/main.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index d04ea747..bcf595d6 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -59,26 +59,29 @@ void StatusLedController::update() { bool any_error = odrv.any_error(); if (is_armed) { - // Fast blue pulsating + // Fast green pulsating const uint32_t period_ms = 256; const uint8_t min_brightness = 0; const uint8_t max_brightness = 255; - const uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; - status_led.set_color(rgb_t{(uint8_t)(any_error ? brightness / 2 : 0), 0, (uint8_t)brightness}); + uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt + status_led.set_color(rgb_t{(uint8_t)(any_error ? brightness / 2 : 0), (uint8_t)brightness, 0}); } else if (any_error) { // Red pulsating const uint32_t period_ms = 1024; const uint8_t min_brightness = 0; const uint8_t max_brightness = 255; - const uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt status_led.set_color(rgb_t{(uint8_t)brightness, 0, 0}); } else { - // Slow green pulsating - const uint32_t period_ms = 2048; - const uint8_t min_brightness = 16; - const uint8_t max_brightness = 128; - const uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; - status_led.set_color(rgb_t{0, (uint8_t)brightness, 0}); + // Slow blue pulsating + const uint32_t period_ms = 4096; + const uint8_t min_brightness = 64; + const uint8_t max_brightness = 180; + uint32_t brightness = std::abs((int32_t)(t % period_ms) - (int32_t)(period_ms / 2)) * (max_brightness - min_brightness) / (period_ms / 2) + min_brightness; + brightness = (brightness * brightness) >> 8; // eye response very roughly sqrt + status_led.set_color(rgb_t{0, 0, (uint8_t)brightness}); } #endif } From ac6c55315618b97a1962f07133bff19a90811196 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 25 Nov 2020 19:30:04 +0100 Subject: [PATCH 114/124] add get_drv_fault function --- Firmware/MotorControl/main.cpp | 10 ++++++++++ Firmware/MotorControl/odrive_main.h | 1 + Firmware/odrive-interface.yaml | 24 +----------------------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index bcf595d6..959ff40a 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -220,6 +220,16 @@ bool ODrive::any_error() { }); } +uint64_t ODrive::get_drv_fault() { +#if AXIS_COUNT == 1 + return motors[0].gate_driver_.get_error(); +#elif AXIS_COUNT == 2 + return (uint64_t)motors[0].gate_driver_.get_error() | ((uint64_t)motors[1].gate_driver_.get_error() << 32ULL); +#else + #error "not supported" +#endif +} + void ODrive::clear_errors() { for (auto& axis: axes) { axis.motor_.error_ = Motor::ERROR_NONE; diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 16689464..96bedf79 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -193,6 +193,7 @@ public: uint32_t get_interrupt_status(int32_t irqn); uint32_t get_dma_status(uint8_t stream_num); uint32_t get_gpio_states(); + uint64_t get_drv_fault(); void disarm_with_error(Error error); Error error_ = ERROR_NONE; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index f469eb88..859fd328 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -214,6 +214,7 @@ interfaces: get_gpio_states: out: {status: {type: uint32}} doc: Returns the logic states of all GPIOs. Bit i represents the state of GPIOi. + get_drv_fault: {out: {drv_fault: uint64}} clear_errors: doc: Clear all the errors of this device including all contained submodules. @@ -456,29 +457,6 @@ interfaces: sensorless_ramp: LockinConfig general_lockin: LockinConfig can: CanConfig - gate_driver: - c_name: gate_driver_exported_ - c_is_class: False - attributes: - drv_fault: - typeargs: {fibre.Property.mode: readonly} - nullflag: NoFault - flags: - FetLowCOvercurrent: {bit: 0, doc: FET Low side, Phase C Over Current fault} - FetHighCOvercurrent: {bit: 1, doc: FET High side, Phase C Over Current fault} - FetLowBOvercurrent: {bit: 2, doc: FET Low side, Phase B Over Current fault} - FetHighBOvercurrent: {bit: 3, doc: FET High side, Phase B Over Current fault} - FetLowAOvercurrent: {bit: 4, doc: FET Low side, Phase A Over Current fault} - FetHighAOvercurrent: {bit: 5, doc: FET High side, Phase A Over Current fault} - OvertemperatureWarning: {bit: 6, doc: Over Temperature Warning fault} - OvertemperatureShutdown: {bit: 7, doc: Over Temperature Shut Down fault} - PVddUndervoltage: {bit: 8, doc: Power supply Vdd Under Voltage fault} - GVddUndervoltage: {bit: 9, doc: DRV8301 Vdd Under Voltage fault} - GVddOvervoltage: {bit: 10, doc: DRV8301 Vdd Over Voltage fault} - # status_reg_1: readonly uint32 - # status_reg_2: readonly uint32 - # ctrl_reg_1: readonly uint32 - # ctrl_reg_2: readonly uint32 motor: Motor controller: Controller encoder: Encoder From 876565442990656ecef17bdef45d3322db1f2157 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 25 Nov 2020 22:22:17 +0100 Subject: [PATCH 115/124] fix overcurrent on arming The first current samples happen exactly during low side FET switches and are thus not faithful. --- Firmware/MotorControl/axis.cpp | 28 ++++++++++++++-------------- Firmware/MotorControl/axis.hpp | 2 -- Firmware/MotorControl/motor.cpp | 8 ++++++-- Firmware/MotorControl/motor.hpp | 1 + Firmware/Private | 2 +- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 9b12a3c4..90cd80b5 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -485,14 +485,14 @@ void Axis::run_state_machine_loop() { // when an error is raised. TODO: remove this when we overhaul // the error architecture // (https://github.com/madcowswe/ODrive/issues/526). - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; status = motor_.run_calibration(); } break; case AXIS_STATE_ENCODER_INDEX_SEARCH: { - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; @@ -500,8 +500,8 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_ENCODER_DIR_FIND: { - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; @@ -509,30 +509,30 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_HOMING: { - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; status = run_homing(); } break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: { - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_) goto invalid_state_label; status = encoder_.run_offset_calibration(); } break; case AXIS_STATE_LOCKIN_SPIN: { - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_ || encoder_.config_.direction==0) goto invalid_state_label; status = run_lockin_spin(config_.general_lockin, false); } break; case AXIS_STATE_CLOSED_LOOP_CONTROL: { - if (odrv.any_error()) - goto invalid_state_label; + //if (odrv.any_error()) + // goto invalid_state_label; if (!motor_.is_calibrated_ || (encoder_.config_.direction==0 && !config_.enable_sensorless_mode)) goto invalid_state_label; watchdog_feed(); diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index c79c91dd..0682ed54 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -128,8 +128,6 @@ public: void set_step_dir_active(bool enable); void decode_step_dir_pins(); - bool check_DRV_fault(); - bool check_PSU_brownout(); bool do_checks(uint32_t timestamp); void watchdog_feed(); diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 6585cccc..31de84fa 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -199,6 +199,7 @@ bool Motor::arm(PhaseControlLaw<3>* control_law) { } if (!odrv.config_.enable_brake_resistor || brake_resistor_armed) { + armed_state_ = 1; is_armed_ = true; } else { error_ |= Motor::ERROR_BRAKE_RESISTOR_DISARMED; @@ -264,6 +265,7 @@ bool Motor::disarm(bool* p_was_armed) { gate_driver_.set_enabled(false); } is_armed_ = false; + armed_state_ = 0; TIM_HandleTypeDef* timer = timer_; timer->Instance->BDTR &= ~TIM_BDTR_AOE; // prevent the PWMs from automatically enabling at the next update __HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(timer); @@ -587,7 +589,10 @@ void Motor::current_meas_cb(uint32_t timestamp, std::optional current && (abs(DC_calib_.phB) < max_dc_calib_) && (abs(DC_calib_.phC) < max_dc_calib_); - if (current.has_value() && dc_calib_valid) { + if (armed_state_ == 1 || armed_state_ == 2) { + current_meas_ = {0.0f, 0.0f, 0.0f}; + armed_state_ += 1; + } else if (current.has_value() && dc_calib_valid) { current_meas_ = { current->phA - DC_calib_.phA, current->phB - DC_calib_.phB, @@ -678,7 +683,6 @@ void Motor::pwm_update_cb(uint32_t output_timestamp) { (uint16_t)(pwm_timings[1] * (float)TIM_1_8_PERIOD_CLOCKS), (uint16_t)(pwm_timings[2] * (float)TIM_1_8_PERIOD_CLOCKS) }; - apply_pwm_timings(next_timings, false); } else if (is_armed_) { if (!(timer_->Instance->BDTR & TIM_BDTR_MOE) && (control_law_status == ERROR_CONTROLLER_INITIALIZING)) { diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index ef048537..d22a1069 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -112,6 +112,7 @@ public: // Do not write to this variable directly! // It is for exclusive use by the safety_critical_... functions. bool is_armed_ = false; + uint8_t armed_state_ = 0; bool is_calibrated_ = false; // Set in apply_config() std::optional current_meas_; Iph_ABC_t DC_calib_ = {0.0f, 0.0f, 0.0f}; diff --git a/Firmware/Private b/Firmware/Private index 3ad39794..d9b3887e 160000 --- a/Firmware/Private +++ b/Firmware/Private @@ -1 +1 @@ -Subproject commit 3ad39794bb0d7c44e9eed7e910d4df35a01513ba +Subproject commit d9b3887e5f15a21672cbe059898a3715fa483482 From e7ef78bccafb41efa53907df2d3825ad9c342d8e Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 25 Nov 2020 22:52:38 -0800 Subject: [PATCH 116/124] Sample DRV fault when going to idle. Update Vds setting on DRV --- Firmware/.vscode/c_cpp_properties.json | 9 +++++---- Firmware/MotorControl/axis.cpp | 1 + Firmware/MotorControl/axis.hpp | 1 + Firmware/Private | 2 +- Firmware/odrive-interface.yaml | 1 + tools/.vscode/launch.json | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 33d6b37a..9d93f425 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -3,12 +3,13 @@ { "name": "Win32", "includePath": [ + "${workspaceFolder}/Private/**", "${workspaceFolder}/**" ], "defines": [ "__arm__", - "STM32F405xx", - "FPU_FPV4", + "STM32F722xx", + "FPU_FPV5", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=4", "HW_VERSION_MINOR=1", @@ -21,8 +22,8 @@ "compilerPath": "arm-none-eabi-g++.exe", "compilerArgs": [ "-mthumb", - "-mcpu=cortex-m4", - "-mfpu=fpv4-sp-d16", + "-mcpu=cortex-m7", + "-mfpu=fpv5-sp-d16", "-mfloat-abi=hard", "-specs=nosys.specs", "-specs=nano.specs", diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 90cd80b5..25af856d 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -417,6 +417,7 @@ bool Axis::run_homing() { } bool Axis::run_idle_loop() { + last_drv_fault_ = motor_.gate_driver_.get_error(); mechanical_brake_.engage(); set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on); while (requested_state_ == AXIS_STATE_UNDEFINED) { diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 0682ed54..be42a7c6 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -177,6 +177,7 @@ public: // variables exposed on protocol Error error_ = ERROR_NONE; bool step_dir_active_ = false; // auto enabled after calibration, based on config.enable_step_dir + uint32_t last_drv_fault_ = 0; // updated from config in constructor, and on protocol hook Stm32Gpio step_gpio_; diff --git a/Firmware/Private b/Firmware/Private index d9b3887e..11c72c40 160000 --- a/Firmware/Private +++ b/Firmware/Private @@ -1 +1 @@ -Subproject commit d9b3887e5f15a21672cbe059898a3715fa483482 +Subproject commit 11c72c408f49cdace46a3b78da1231c439f2eda9 diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 859fd328..e5980558 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -406,6 +406,7 @@ interfaces: # unused doc: Check `motor.error` for more details. step_dir_active: readonly bool + last_drv_fault: readonly uint32 current_state: readonly AxisState requested_state: AxisState loop_counter: readonly uint32 diff --git a/tools/.vscode/launch.json b/tools/.vscode/launch.json index 9a36a076..5247bf4a 100644 --- a/tools/.vscode/launch.json +++ b/tools/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "python", "request": "launch", "stopOnEntry": true, - "pythonPath": "${command:python.pythonPath}", + "python": "${command:python.pythonPath}", "program": "${file}", "cwd": "${workspaceRoot}", "env": {}, From 84c8ead0829fa41f9c2cdc5b7ff0c75b48d63a4f Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Fri, 27 Nov 2020 00:38:38 -0500 Subject: [PATCH 117/124] Add index offset Make index_offset in turns instead of counts Get rid of *-1 in the index offset Fix --- Firmware/MotorControl/encoder.cpp | 4 ++-- Firmware/MotorControl/encoder.hpp | 26 ++++++++++++++------------ Firmware/odrive-interface.yaml | 3 ++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 358dca72..05318b49 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -80,8 +80,8 @@ bool Encoder::do_checks(){ void Encoder::enc_index_cb() { if (config_.use_index) { set_circular_count(0, false); - if (config_.zero_count_on_find_idx) - set_linear_count(0); // Avoid position control transient after search + if (config_.use_index_offset) + set_linear_count((int32_t)(config_.index_offset * config_.cpr)); if (config_.pre_calibrated) { is_ready_ = true; if(axis_->controller_.config_.anticogging.pre_calibrated){ diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 841f7a4f..9676b396 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -14,27 +14,29 @@ public: struct Config_t { Mode mode = MODE_INCREMENTAL; + float calib_range = 0.02f; // Accuracy required to pass encoder cpr check + float calib_scan_distance = 16.0f * M_PI; // rad electrical + float calib_scan_omega = 4.0f * M_PI; // rad/s electrical + float bandwidth = 1000.0f; + int32_t phase_offset = 0; // Offset between encoder count and rotor electrical phase + float phase_offset_float = 0.0f; // Sub-count phase alignment offset + int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, + float index_offset = 0.0f; + uint16_t abs_spi_cs_gpio_pin = 1; + uint16_t sincos_gpio_pin_sin = 3; + uint16_t sincos_gpio_pin_cos = 4; bool use_index = false; bool pre_calibrated = false; // If true, this means the offset stored in // configuration is valid and does not need // be determined by run_offset_calibration. // In this case the encoder will enter ready // state as soon as the index is found. - bool zero_count_on_find_idx = true; - int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, - int32_t phase_offset = 0; // Offset between encoder count and rotor electrical phase - float phase_offset_float = 0.0f; // Sub-count phase alignment offset - int32_t direction = 0.0f; // direction with respect to motor + int32_t direction = 0; // direction with respect to motor + bool use_index_offset = true; bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state - float calib_range = 0.02f; // Accuracy required to pass encoder cpr check - float calib_scan_distance = 16.0f * M_PI; // rad electrical - float calib_scan_omega = 4.0f * M_PI; // rad/s electrical - float bandwidth = 1000.0f; bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 - uint16_t abs_spi_cs_gpio_pin = 1; - uint16_t sincos_gpio_pin_sin = 3; - uint16_t sincos_gpio_pin_cos = 4; + // custom setters Encoder* parent = nullptr; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index d88af102..c186b678 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -984,9 +984,10 @@ interfaces: attributes: mode: Mode use_index: {type: bool, c_setter: set_use_index} + index_offset: float32 + use_index_offset: bool find_idx_on_lockin_only: {type: bool, c_setter: set_find_idx_on_lockin_only} abs_spi_cs_gpio_pin: {type: uint16, c_setter: set_abs_spi_cs_gpio_pin, doc: Make sure that the GPIO is in `GPIO_MODE_DIGITAL`.} - zero_count_on_find_idx: bool cpr: int32 phase_offset: int32 phase_offset_float: float32 From ee06628e4cddf25eceba14e89c05b4d4c5e0277f Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Tue, 1 Dec 2020 19:48:09 -0500 Subject: [PATCH 118/124] Added comment to hall polarity detection function --- Firmware/MotorControl/encoder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index d8be9295..812377f2 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -233,6 +233,10 @@ bool Encoder::run_hall_polarity_calibration() { return false; } + // Hall effect sensors can be arranged at 60 or 120 electrical degrees. + // Out of 8 possible states, 120 and 60 deg arrangements each miss 2 states. + // ODrive assumes 120 deg separation - if a 60 deg setup is used, it can + // be converted to 120 deg states by flipping the polarity of one sensor. uint8_t states = state_seen.to_ulong(); uint8_t hall_polarity = 0; auto flip_detect = [](uint8_t states, unsigned int idx)->bool { From 39570ecbf4605105cf30e68f78965f282ff2ffd7 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Tue, 1 Dec 2020 21:44:07 -0500 Subject: [PATCH 119/124] Split hall calibration into polarity and offset calibration, full calibration sequence only includes hall polarity cal --- Firmware/MotorControl/axis.cpp | 18 +++++++++++++++--- Firmware/odrive-interface.yaml | 9 +++++++-- tools/.vscode/launch.json | 2 +- tools/odrive/enums.py | 3 ++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 27fc1cf9..de54d1af 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -465,6 +465,8 @@ void Axis::run_state_machine_loop() { task_chain_[pos++] = AXIS_STATE_IDLE; } else if (requested_state_ == AXIS_STATE_FULL_CALIBRATION_SEQUENCE) { task_chain_[pos++] = AXIS_STATE_MOTOR_CALIBRATION; + if (encoder_.config_.mode == ODriveIntf::EncoderIntf::MODE_HALL) + task_chain_[pos++] = AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION; if (encoder_.config_.use_index) task_chain_[pos++] = AXIS_STATE_ENCODER_INDEX_SEARCH; task_chain_[pos++] = AXIS_STATE_ENCODER_OFFSET_CALIBRATION; @@ -503,13 +505,23 @@ void Axis::run_state_machine_loop() { status = encoder_.run_direction_find(); } break; - case AXIS_STATE_ENCODER_HALL_CALIBRATION: { + case AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION: { if (!motor_.is_calibrated_) goto invalid_state_label; status = encoder_.run_hall_polarity_calibration(); - if (status) - status = encoder_.run_hall_phase_calibration(); + } break; + + case AXIS_STATE_ENCODER_HALL_OFFSET_CALIBRATION: { + if (!motor_.is_calibrated_) + goto invalid_state_label; + + if (!encoder_.config_.hall_polarity_calibrated) { + encoder_.set_error(ODriveIntf::EncoderIntf::ERROR_HALL_NOT_CALIBRATED_YET); + goto invalid_state_label; + } + + status = encoder_.run_hall_phase_calibration(); } break; case AXIS_STATE_HOMING: { diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 35376cc1..2482184b 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -1163,8 +1163,13 @@ valuetypes: brief: Run axis homing function. doc: Endstops must be enabled to use this feature. - EncoderHallCalibration: - brief: Rotate the motor in lockin and calibrate hall states + EncoderHallPolarityCalibration: + brief: Rotate the motor in lockin and calibrate hall polarity + doc: + ODrive assumes 120 degree electrical hall spacing. This routine determines if that + is the case and sets the polarity if the halls are on 60 degree electrical spacing + EncoderHallOffsetCalibration: + brief: Rotate the motor for 30s to calibrate hall sensor edge offsets doc: The phase offset is not calibrated at this time, so the map is only relative diff --git a/tools/.vscode/launch.json b/tools/.vscode/launch.json index 9a36a076..5247bf4a 100644 --- a/tools/.vscode/launch.json +++ b/tools/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "python", "request": "launch", "stopOnEntry": true, - "pythonPath": "${command:python.pythonPath}", + "python": "${command:python.pythonPath}", "program": "${file}", "cwd": "${workspaceRoot}", "env": {}, diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 9442723a..ab3054ee 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -35,7 +35,8 @@ AXIS_STATE_CLOSED_LOOP_CONTROL = 8 AXIS_STATE_LOCKIN_SPIN = 9 AXIS_STATE_ENCODER_DIR_FIND = 10 AXIS_STATE_HOMING = 11 -AXIS_STATE_ENCODER_HALL_CALIBRATION = 12 +AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION = 12 +AXIS_STATE_ENCODER_HALL_OFFSET_CALIBRATION = 13 # ODrive.Encoder.Mode ENCODER_MODE_INCREMENTAL = 0 From 3f206be2ec39697e081f88567a68d9be0b82e7df Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Tue, 1 Dec 2020 22:18:03 -0500 Subject: [PATCH 120/124] Better enum name for the hall phase calibration --- Firmware/MotorControl/axis.cpp | 2 +- Firmware/odrive-interface.yaml | 2 +- tools/odrive/enums.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index de54d1af..5d778eb0 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -512,7 +512,7 @@ void Axis::run_state_machine_loop() { status = encoder_.run_hall_polarity_calibration(); } break; - case AXIS_STATE_ENCODER_HALL_OFFSET_CALIBRATION: { + case AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION: { if (!motor_.is_calibrated_) goto invalid_state_label; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 2482184b..e393615d 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -1168,7 +1168,7 @@ valuetypes: doc: ODrive assumes 120 degree electrical hall spacing. This routine determines if that is the case and sets the polarity if the halls are on 60 degree electrical spacing - EncoderHallOffsetCalibration: + EncoderHallPhaseCalibration: brief: Rotate the motor for 30s to calibrate hall sensor edge offsets doc: The phase offset is not calibrated at this time, so the map is only relative diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index ab3054ee..e68500ae 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -36,7 +36,7 @@ AXIS_STATE_LOCKIN_SPIN = 9 AXIS_STATE_ENCODER_DIR_FIND = 10 AXIS_STATE_HOMING = 11 AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION = 12 -AXIS_STATE_ENCODER_HALL_OFFSET_CALIBRATION = 13 +AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION = 13 # ODrive.Encoder.Mode ENCODER_MODE_INCREMENTAL = 0 From dc1721a1dd85dc6e1201d14a050a6b85489c4da3 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Tue, 1 Dec 2020 22:48:47 -0500 Subject: [PATCH 121/124] merge fixes --- Firmware/MotorControl/encoder.cpp | 2 +- Firmware/MotorControl/encoder.hpp | 3 --- Firmware/odrive-interface.yaml | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 488a2a3a..17ac87a1 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -664,7 +664,7 @@ bool Encoder::update() { return false; } - auto maybe_phase = axis_->open_loop_controller_.phase_.get_any(); + auto maybe_phase = axis_->open_loop_controller_.phase_.any(); if (maybe_phase) { float phase = maybe_phase.value(); // Early increment to get the right divisor in recursive average diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 4ce863a5..f20436ab 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -24,9 +24,6 @@ public: float phase_offset_float = 0.0f; // Sub-count phase alignment offset int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, float index_offset = 0.0f; - uint16_t abs_spi_cs_gpio_pin = 1; - uint16_t sincos_gpio_pin_sin = 3; - uint16_t sincos_gpio_pin_cos = 4; bool use_index = false; bool pre_calibrated = false; // If true, this means the offset stored in // configuration is valid and does not need diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index d7d2e008..7a414ea6 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -974,7 +974,7 @@ interfaces: pos_estimate_counts: readonly float32 pos_cpr_counts: readonly float32 delta_pos_cpr_counts: readonly float32 - pos_circular: {type: readonly float32, c_getter: pos_circular_.get_any().value_or(0.0f)} + pos_circular: {type: readonly float32, c_getter: pos_circular_.any().value_or(0.0f)} hall_state: readonly uint8 vel_estimate: {type: readonly float32, c_getter: vel_estimate_.any().value_or(0.0f)} vel_estimate_counts: readonly float32 From 17e0318048c001e3c4825cbf91e84ab4c765b220 Mon Sep 17 00:00:00 2001 From: PAJohnson Date: Tue, 1 Dec 2020 23:10:15 -0500 Subject: [PATCH 122/124] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22034ba4..2f919929 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ # Unreleased Features Please add a note of your changes below this heading if you make a Pull Request. ### Added +* Added polarity and phase offset calibration for hall effect encoders * [Mechanical brake support](docs/mechanical-brakes.md) * Added periodic sending of encoder position on CAN * Support for UART1 on GPIO3 and GPIO4. UART0 (on GPIO1/2) and UART1 can currently not be enabled at the same time. ### Changed +* Full calibration sequence now includes hall polarity calibration if a hall effect encoder is used * Modified encoder offset calibration to work correctly when calib_scan_distance is not a multiple of 4pi * Moved thermistors from being a top level object to belonging to Motor objects. Also changed errors: thermistor errors rolled into motor errors * Use DMA for DRV8301 setup From da6c6216b5580b494f36b45b08bb681113f2fa61 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Wed, 2 Dec 2020 13:14:40 +0100 Subject: [PATCH 123/124] attempt to fix CI --- .github/workflows/compile.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index 380730df..21147d58 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -76,7 +76,7 @@ jobs: run: | Invoke-WebRequest -Uri "http://gittup.org/tup/win32/tup-latest.zip" -OutFile ".\tup-latest.zip" Expand-Archive ".\tup-latest.zip" -DestinationPath ".\tup-latest" -Force - echo "$(Resolve-Path .)\tup-latest" >> $GITHUB_PATH + echo "$(Resolve-Path .)\tup-latest" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append choco install gcc-arm-embedded # downloads https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-win32.zip From 35a86816119d969a5402b07712dda06f2baad8e0 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 3 Dec 2020 14:11:27 +0100 Subject: [PATCH 124/124] change enum naming convention in yaml file --- Firmware/fibre/cpp/interfaces_template.j2 | 2 +- Firmware/fibre/tools/interface_generator.py | 9 +- Firmware/odrive-interface.yaml | 248 ++++++++++---------- tools/enums_template.j2 | 2 +- 4 files changed, 132 insertions(+), 129 deletions(-) diff --git a/Firmware/fibre/cpp/interfaces_template.j2 b/Firmware/fibre/cpp/interfaces_template.j2 index 91af9494..07cc35c5 100644 --- a/Firmware/fibre/cpp/interfaces_template.j2 +++ b/Firmware/fibre/cpp/interfaces_template.j2 @@ -38,7 +38,7 @@ public: [%- for enum in intf.enums %] enum [[enum.name | to_pascal_case]] { [%- for k, value in enum['values'].items() %] - [[((enum.name + k) | to_macro_case).ljust(32)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %], + [[((enum.name | to_macro_case) + "_" + (k | to_macro_case)).ljust(32)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %], [%- endfor %] }; [%- endfor %] diff --git a/Firmware/fibre/tools/interface_generator.py b/Firmware/fibre/tools/interface_generator.py index ebdd72ef..713ddce5 100644 --- a/Firmware/fibre/tools/interface_generator.py +++ b/Firmware/fibre/tools/interface_generator.py @@ -131,10 +131,13 @@ dictionary = [] def get_words(string): """ - Splits a string in PascalCase into a list of lower case words + Splits a string in PascalCase or MACRO_CASE into a list of lower case words """ - regex = ''.join((re.escape(w) + '|') for w in dictionary) + '[a-z0-9]+|[A-Z][a-z0-9]*' - return [(w if w in dictionary else w.lower()) for w in re.findall(regex, string)] + if string.isupper(): + return [w.lower() for w in string.split('_')] + else: + regex = ''.join((re.escape(w) + '|') for w in dictionary) + '[a-z0-9]+|[A-Z][a-z0-9]*' + return [(w if w in dictionary else w.lower()) for w in re.findall(regex, string)] def join_name(*names, delimiter: str = '.'): """ diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 1af868a4..bc8b14cd 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -20,16 +20,16 @@ interfaces: toplevel interface. attributes: error: - nullflag: 'None' + nullflag: NONE flags: - ControlIterationMissed: + CONTROL_ITERATION_MISSED: brief: At least one control iteration was missed. doc: | The main control loop is supposed to runs at a fixed frequency. If the device is computationally overloaded (e.g. too many active components) it's possible that one or more control iterations are skipped. - DcBusUnderVoltage: + DC_BUS_UNDER_VOLTAGE: brief: The DC voltage fell below the limit configured in `config.dc_bus_undervoltage_trip_level`. doc: | Confirm that your power leads are connected securely. For initial @@ -47,7 +47,7 @@ interfaces: limit. To limit your PSU power draw you can limit your motor current and/or velocity limit `controller.config.vel_limit` and `motor.config.current_lim`. - DcBusOverVoltage: + DC_BUS_OVER_VOLTAGE: brief: The DC voltage exceeded the limit configured in `config.dc_bus_overvoltage_trip_level`. doc: | Confirm that you have a brake resistor of the correct value @@ -65,12 +65,12 @@ interfaces: connections you can also try increasing your brake resistance by ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake resistor value. - DcBusOverRegenCurrent: {doc: too much current pushed into the power supply} - DcBusOverCurrent: {doc: too much current pulled out of the power supply} - BrakeDeadtimeViolation: - BrakeDutyCycleNan: - InvalidBrakeResistance: {doc: '`config.brake_resistance` is non-positive or NaN.'} -# BrakeResistorDisarmed: + DC_BUS_OVER_REGEN_CURRENT: {doc: too much current pushed into the power supply} + DC_BUS_OVER_CURRENT: {doc: too much current pulled out of the power supply} + BRAKE_DEADTIME_VIOLATION: + BRAKE_DUTY_CYCLE_NAN: + INVALID_BRAKE_RESISTANCE: {doc: '`config.brake_resistance` is non-positive or NaN.'} +# BRAKE_RESISTOR_DISARMED: # doc: The brake resistor was unexpectedly disarmed. vbus_voltage: @@ -365,8 +365,8 @@ interfaces: c_is_class: True attributes: error: - nullflag: None - flags: {DuplicateCanIds: } + nullflag: NONE + flags: {DUPLICATE_CAN_IDS: } config: c_is_class: False attributes: @@ -386,23 +386,23 @@ interfaces: c_is_class: True attributes: error: - nullflag: 'None' + nullflag: NONE flags: - InvalidState: + INVALID_STATE: brief: An invalid state was requested. doc: | You tried to run a state before you are allowed to. Typically you tried to run encoder calibration or closed loop control before the motor was calibrated, or you tried to run closed loop control before the encoder was calibrated. - WatchdogTimerExpired: {bit: 11} - MinEndstopPressed: - MaxEndstopPressed: - EstopRequested: - HomingWithoutEndstop: + WATCHDOG_TIMER_EXPIRED: {bit: 11} + MIN_ENDSTOP_PRESSED: + MAX_ENDSTOP_PRESSED: + ESTOP_REQUESTED: + HOMING_WITHOUT_ENDSTOP: bit: 17 doc: the min endstop was not enabled during homing - OverTemp: + OVER_TEMP: # unused doc: Check `motor.error` for more details. step_dir_active: readonly bool @@ -566,9 +566,9 @@ interfaces: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - PhaseResistanceOutOfRange: + PHASE_RESISTANCE_OUT_OF_RANGE: brief: The measured motor phase resistance is outside of the plausible range. doc: | During calibration the motor resistance and @@ -599,11 +599,11 @@ interfaces: resistance_calib_max_voltage > calibration_current * phase_resistance resistance_calib_max_voltage < 0.5 * vbus_voltage ``` - PhaseInductanceOutOfRange: + PHASE_INDUCTANCE_OUT_OF_RANGE: brief: The measured motor phase inductance is outside of the plausible range. doc: | - See `PhaseResistanceOutOfRange` for details. - DrvFault: + See `PHASE_RESISTANCE_OUT_OF_RANGE` for details. + DRV_FAULT: bit: 3 brief: The gate driver chip reported an error. doc: | @@ -620,8 +620,8 @@ interfaces: test motor and 50A on another test motor. Refer to [this post](https://discourse.odriverobotics.com/t/drv-fault-on-odrive-v3-4/558) for instructions for a hardware fix. - ControlDeadlineMissed: - ModulationMagnitude: + CONTROL_DEADLINE_MISSED: + MODULATION_MAGNITUDE: bit: 7 doc: | The bus voltage was insufficent to push the requested current @@ -633,34 +633,34 @@ interfaces: For gimbal motors, it is recommended to set the `config.calibration_current` and `config.current_lim` to half your bus voltage, or less. - CurrentSenseSaturation: {bit: 10} - CurrentLimitViolation: {bit: 12} - ModulationIsNan: {bit: 16} - MotorThermistorOverTemp: {doc: The motor thermistor measured a temperature above motor.motor_thermistor.config.temp_limit_upper} - FetThermistorOverTemp: {doc: The inverter thermistor measured a temperature above motor.fet_thermistor.config.temp_limit_upper} - TimerUpdateMissed: {doc: A timer update event was missed. Perhaps the previous timer update took too much time. This is not expected in official release firmware.} - CurrentMeasurementUnavailable: {doc: The phase current measurement is not available. The ADC failed to sample the current sensor in time. This is not expected in official release firmware.} - ControllerFailed: {doc: The motor was disarmed because the underlying controller failed. Usually this is the FOC controller.} - IBusOutOfRange: + CURRENT_SENSE_SATURATION: {bit: 10} + CURRENT_LIMIT_VIOLATION: {bit: 12} + MODULATION_IS_NAN: {bit: 16} + MOTOR_THERMISTOR_OVER_TEMP: {doc: The motor thermistor measured a temperature above motor.motor_thermistor.config.temp_limit_upper} + FET_THERMISTOR_OVER_TEMP: {doc: The inverter thermistor measured a temperature above motor.fet_thermistor.config.temp_limit_upper} + TIMER_UPDATE_MISSED: {doc: A timer update event was missed. Perhaps the previous timer update took too much time. This is not expected in official release firmware.} + CURRENT_MEASUREMENT_UNAVAILABLE: {doc: The phase current measurement is not available. The ADC failed to sample the current sensor in time. This is not expected in official release firmware.} + CONTROLLER_FAILED: {doc: The motor was disarmed because the underlying controller failed. Usually this is the FOC controller.} + I_BUS_OUT_OF_RANGE: doc: | The DC current sourced/sunk by this motor exceeded the configured hard limits. More specifically `i_bus` fell outside of the range `config.i_bus_hard_min` ... `config.i_bus_hard_max`. - BrakeResistorDisarmed: {doc: An attempt was made to run the motor PWM while the brake resistor was enabled but disarmed.} - SystemLevel: + BRAKE_RESISTOR_DISARMED: {doc: An attempt was made to run the motor PWM while the brake resistor was enabled but disarmed.} + SYSTEM_LEVEL: doc: | The motor had to be disarmed because of a system level error. See `ODrive.Error` for more details. - BadTiming: {doc: The main control loop got out of sync with the motor control loop. This could indicate that the main control loop got stuck.} - UnknownPhaseEstimate: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} - UnknownPhaseVel: {doc: The motor controller did not get a valid phase velocity input.} - UnknownTorque: {doc: The motor controller did not get a valid torque input.} - UnknownCurrentCommand: {doc: The current controller did not get a valid current setpoint. Maybe you didn't configure the controller correctly.} - UnknownCurrentMeasurement: {doc: The current controller did not get a valid current measurement.} - UnknownVbusVoltage: {doc: The current controller did not get a valid `vbus_voltage` measurement.} - UnknownVoltageCommand: {doc: The current controller did not get a valid feedforward voltage setpoint.} - UnknownGains: {doc: The current controller gains were not configured. Run motor calibration or set `config.phase_resistance` and `config.phase_inductance` manually.} - ControllerInitializing: {doc: Internal value used while the controller is not yet ready to generate PWM timings.} + BAD_TIMING: {doc: The main control loop got out of sync with the motor control loop. This could indicate that the main control loop got stuck.} + UNKNOWN_PHASE_ESTIMATE: {doc: The current controller did not get a valid angle input. Maybe you didn't calibrate the encoder.} + UNKNOWN_PHASE_VEL: {doc: The motor controller did not get a valid phase velocity input.} + UNKNOWN_TORQUE: {doc: The motor controller did not get a valid torque input.} + UNKNOWN_CURRENT_COMMAND: {doc: The current controller did not get a valid current setpoint. Maybe you didn't configure the controller correctly.} + UNKNOWN_CURRENT_MEASUREMENT: {doc: The current controller did not get a valid current measurement.} + UNKNOWN_VBUS_VOLTAGE: {doc: The current controller did not get a valid `vbus_voltage` measurement.} + UNKNOWN_VOLTAGE_COMMAND: {doc: The current controller did not get a valid feedforward voltage setpoint.} + UNKNOWN_GAINS: {doc: The current controller gains were not configured. Run motor calibration or set `config.phase_resistance` and `config.phase_inductance` manually.} + CONTROLLER_INITIALIZING: {doc: Internal value used while the controller is not yet ready to generate PWM timings.} is_armed: readonly bool is_calibrated: readonly bool current_meas_phA: {type: readonly float32, c_getter: 'current_meas_.value_or(Iph_ABC_t{0.0f, 0.0f, 0.0f}).phA'} @@ -798,9 +798,9 @@ interfaces: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - Overspeed: + OVERSPEED: doc: | Try increasing `config.vel_limit`. The default of 2 turns per second gives a motor speed of only 120 RPM. Note: Even if @@ -812,11 +812,11 @@ interfaces: default value of 1.2 means it will only allow a 20% violation of the speed limit. You can set the `config.vel_limit_tolerance` to 0 to disable the check altogether. - InvalidInputMode: - UnstableGain: - InvalidMirrorAxis: - InvalidLoadEncoder: - InvalidEstimate: + INVALID_INPUT_MODE: + UNSTABLE_GAIN: + INVALID_MIRROR_AXIS: + INVALID_LOAD_ENCODER: + INVALID_ESTIMATE: input_pos: type: float32 unit: turn @@ -914,10 +914,10 @@ interfaces: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - UnstableGain: - CprPolepairsMismatch: + UNSTABLE_GAIN: + CPR_POLEPAIRS_MISMATCH: doc: | Confirm you have entered the correct count per rotation (CPR) for [your encoder](https://docs.odriverobotics.com/encoders). The @@ -928,21 +928,21 @@ interfaces: switches on the encoder PCB and so you may need to check that these are in the right positions. If your encoder lists its pulse per rotation (PPR) multiply that number by four to get CPR. - NoResponse: + NO_RESPONSE: doc: | Confirm that your encoder is plugged into the right pins on the ODrive board. - UnsupportedEncoderMode: - IllegalHallState: - IndexNotFoundYet: + UNSUPPORTED_ENCODER_MODE: + ILLEGAL_HALL_STATE: + INDEX_NOT_FOUND_YET: doc: | Check that your encoder is a model that has an index pulse. If your encoder does not have a wire connected to pin Z on your ODrive then it does not output an index pulse. - AbsSpiTimeout: - AbsSpiComFail: - AbsSpiNotReady: - HallNotCalibratedYet: + ABS_SPI_TIMEOUT: + ABS_SPI_COM_FAIL: + ABS_SPI_NOT_READY: + HALL_NOT_CALIBRATED_YET: is_ready: readonly bool index_found: readonly bool shadow_count: readonly int32 @@ -996,10 +996,10 @@ interfaces: c_is_class: True attributes: error: - nullflag: None + nullflag: NONE flags: - UnstableGain: - UnknownCurrentMeasurement: + UNSTABLE_GAIN: + UNKNOWN_CURRENT_MEASUREMENT: phase: {type: readonly float32, unit: rad, c_getter: phase_.any().value_or(0.0f)} pll_pos: {type: readonly float32, unit: rad} phase_vel: {type: readonly float32, unit: rad/s, c_getter: phase_vel_.any().value_or(0.0f)} @@ -1135,49 +1135,49 @@ interfaces: valuetypes: ODrive.GpioMode: values: - Digital: + DIGITAL: doc: | The pin can be used for one or more of these functions: Step, dir, enable, encoder index, hall effect encoder, SPI encoder nCS (this one is exclusive). - DigitalPullUp: - doc: Same as `Digital` but with the internal pull-up resistor enabled. - DigitalPullDown: - doc: Same as `Digital` but with the internal pull-down resistor enabled. - AnalogIn: + DIGITAL_PULL_UP: + doc: Same as `DIGITAL` but with the internal pull-up resistor enabled. + DIGITAL_PULL_DOWN: + doc: Same as `DIGITAL` but with the internal pull-down resistor enabled. + ANALOG_IN: doc: | The pin can be used for one or more of these functions: Sin/cos encoders, analog input, `get_adc_voltage`. - UartA: {doc: See `config.enable_uart_a`.} - UartB: {doc: This mode is not supported on ODrive v3.x.} - UartC: {doc: This mode is not supported on ODrive v3.x.} - CanA: {doc: See `config.enable_can_a`.} - I2cA: {doc: See `config.enable_i2c_a`.} - SpiA: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they + UART_A: {doc: See `config.enable_uart_a`.} + UART_B: {doc: This mode is not supported on ODrive v3.x.} + UART_C: {doc: This mode is not supported on ODrive v3.x.} + CAN_A: {doc: See `config.enable_can_a`.} + I2C_A: {doc: See `config.enable_i2c_a`.} + SPI_A: {doc: Note that the SPI pins on ODrive v3.x are hardwired so they cannot be configured through software. Consequently, even though SPI_A is exposed, this mode is of no use on ODrive v3.x.} - Pwm: {doc: See `config.gpio0_pwm_mapping`.} - Enc0: {doc: The pin is used by quadrature encoder 0.} - Enc1: {doc: The pin is used by quadrature encoder 1.} - Enc2: {doc: This mode is not supported on ODrive v3.x.} - MechBrake: {doc: This is to support external mechanical brakes.} - Status: {doc: The pin is used for status output (see `config.error_gpio_pin`)} + PWM: {doc: See `config.gpio0_pwm_mapping`.} + ENC0: {doc: The pin is used by quadrature encoder 0.} + ENC1: {doc: The pin is used by quadrature encoder 1.} + ENC2: {doc: This mode is not supported on ODrive v3.x.} + MECH_BRAKE: {doc: This is to support external mechanical brakes.} + STATUS: {doc: The pin is used for status output (see `config.error_gpio_pin`)} ODrive.Can.Protocol: - values: {Simple: } + values: {SIMPLE: } ODrive.Axis.AxisState: # TODO: remove redundant "Axis" in name values: - Undefined: + UNDEFINED: doc: will fall through to idle - Idle: + IDLE: brief: Disable motor PWM and do nothing. - StartupSequence: + STARTUP_SEQUENCE: brief: Run the startup procedure. doc: the actual sequence is defined by the `config`.startup... flags - FullCalibrationSequence: + FULL_CALIBRATION_SEQUENCE: doc: Run motor calibration and then encoder offset calibration (or encoder index search if `.encoder.config.use_index` is `True`). - MotorCalibration: + MOTOR_CALIBRATION: brief: Measure phase resistance and phase inductance of the motor. doc: | * To store the results set `motor.config.pre_calibrated` to `True` @@ -1185,63 +1185,63 @@ valuetypes: don't have to run the motor calibration on the next start up. * This modifies the variables `motor.config.phase_resistance` and `motor.config.phase_inductance`. - EncoderIndexSearch: + ENCODER_INDEX_SEARCH: brief: Turn the motor in one direction until the encoder index is traversed. doc: This state can only be entered if `encoder.config.use_index` is `True`. value: 6 - EncoderOffsetCalibration: + ENCODER_OFFSET_CALIBRATION: brief: Turn the motor in one direction for a few seconds and then back to measure the offset between the encoder position and the electrical phase. doc: | * Can only be entered if the motor is calibrated (`motor.is_calibrated`). * A successful encoder calibration will make the `encoder.is_ready` go to true. - ClosedLoopControl: + CLOSED_LOOP_CONTROL: brief: Run closed loop control. doc: | * The action depends on the `controller.config.control_mode`. * Can only be entered if the motor is calibrated (`motor.is_calibrated`) and the encoder is ready (`encoder.is_ready`). - LockinSpin: + LOCKIN_SPIN: brief: Run lockin spin. doc: | Can only be entered if the motor is calibrated (`motor.is_calibrated`) or the motor direction is unspecified (`motor.config.direction` == 1) - EncoderDirFind: + ENCODER_DIR_FIND: brief: Run encoder direction search. doc: | Can only be entered if the motor is calibrated (`motor.is_calibrated`). - Homing: + HOMING: brief: Run axis homing function. doc: Endstops must be enabled to use this feature. - EncoderHallPolarityCalibration: + ENCODER_HALL_POLARITY_CALIBRATION: brief: Rotate the motor in lockin and calibrate hall polarity doc: ODrive assumes 120 degree electrical hall spacing. This routine determines if that is the case and sets the polarity if the halls are on 60 degree electrical spacing - EncoderHallPhaseCalibration: + ENCODER_HALL_PHASE_CALIBRATION: brief: Rotate the motor for 30s to calibrate hall sensor edge offsets doc: The phase offset is not calibrated at this time, so the map is only relative ODrive.Encoder.Mode: values: - Incremental: - Hall: - Sincos: - SpiAbsCui: + INCREMENTAL: + HALL: + SINCOS: + SPI_ABS_CUI: value: 0x100 doc: compatible with CUI AMT23xx - SpiAbsAms: + SPI_ABS_AMS: value: 0x101 doc: compatible with AMS AS5047P, AS5048A/AS5048B (no daisy chain support) - SpiAbsAeat: + SPI_ABS_AEAT: value: 0x102 doc: not yet implemented - SpiAbsRls: + SPI_ABS_RLS: value: 0x103 doc: RLS Encoders - SpiAbsMa732: + SPI_ABS_MA732: value: 0x104 doc: MagAlpha MA732 magnetic encoder @@ -1249,17 +1249,17 @@ valuetypes: values: # Note: these should be sorted from lowest level of control to # highest level of control, to allow "<" style comparisons. - VoltageControl: + VOLTAGE_CONTROL: doc: this one is not normally used - TorqueControl: - VelocityControl: - PositionControl: + TORQUE_CONTROL: + VELOCITY_CONTROL: + POSITION_CONTROL: ODrive.Controller.InputMode: values: - Inactive: + INACTIVE: brief: Disable inputs. Setpoints retain their last value. - Passthrough: + PASSTHROUGH: brief: Pass `input_xxx` through to `xxx_setpoint` directly. doc: | ### Valid Inputs: @@ -1272,7 +1272,7 @@ valuetypes: * `CONTROL_MODE_TORQUE_CONTROL` * `CONTROL_MODE_VELOCITY_CONTROL` * `CONTROL_MODE_POSITION_CONTROL` - VelRamp: + VEL_RAMP: brief: Ramps a velocity command from the current value to the target value. doc: | ### Configuration Values: @@ -1284,7 +1284,7 @@ valuetypes: ### Valid Control Modes: * `CONTROL_MODE_VELOCITY_CONTROL` - PosFilter: + POS_FILTER: brief: Implements a 2nd order position tracking filter. doc: | Intended for use with step/dir interface, but can also be used with @@ -1302,9 +1302,9 @@ valuetypes: ### Valid Control modes: * `CONTROL_MODE_POSITION_CONTROL` - MixChannels: + MIX_CHANNELS: brief: Not Implemented. - TrapTraj: + TRAP_TRAJ: brief: Implementes an online trapezoidal trajectory planner. doc: | ![Trapezoidal Planner Response](../TrapTrajPosVel.PNG) @@ -1320,7 +1320,7 @@ valuetypes: ### Valid Control Modes: * `CONTROL_MODE_POSITION_CONTROL` - TorqueRamp: + TORQUE_RAMP: brief: Ramp a torque command from the current value to the target value. doc: | ### Configuration Values: @@ -1331,7 +1331,7 @@ valuetypes: ### Valid Control Modes: * `CONTROL_MODE_TORQUE_CONTROL` - Mirror: + MIRROR: brief: Implements "electronic mirroring". doc: | This is like electronic camming, but you can only mirror exactly the @@ -1351,7 +1351,7 @@ valuetypes: ODrive.Motor.MotorType: values: - HighCurrent: + HIGH_CURRENT: #LowCurrent: # not implemented - Gimbal: {value: 2} - Acim: + GIMBAL: {value: 2} + ACIM: diff --git a/tools/enums_template.j2 b/tools/enums_template.j2 index bb20ca37..58d5bfc3 100644 --- a/tools/enums_template.j2 +++ b/tools/enums_template.j2 @@ -8,7 +8,7 @@ # [[enum.fullname]] [%- for k, value in enum['values'].items() %] -[[(((enum.parent.name if enum.name in ['Error', 'Mode'] else '') + enum.name + k) | to_macro_case).ljust(40)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %] +[[((((enum.parent.name if enum.name in ['Error', 'Mode'] else '') + enum.name) | to_macro_case) + "_" + (k | to_macro_case)).ljust(40)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %] [%- endfor %] [%- endif %] [%- endfor %]

vOO8kocJROYwQ}f5lhC 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 literal 0 HcmV?d00001 diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c similarity index 94% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c rename to Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c index 7828403b..fa7f303e 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c @@ -26,7 +26,7 @@ * *---------------------------------------------------------------------------- * - * Portions Copyright © 2016 STMicroelectronics International N.V. All rights reserved. + * Portions Copyright � 2016 STMicroelectronics International N.V. All rights reserved. * Portions Copyright (c) 2013 ARM LIMITED * All rights reserved. * Redistribution and use in source and binary forms, with or without @@ -53,49 +53,6 @@ * POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*/ - /** - ****************************************************************************** - * @file cmsis_os.c - * @author MCD Application Team - * @date 13-July-2017 - * @brief CMSIS-RTOS API implementation for FreeRTOS V9.0.0 - ****************************************************************************** - * @attention - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted, provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of other - * contributors to this software may be used to endorse or promote products - * derived from this software without specific written permission. - * 4. This software, including modifications and/or derivative works of this - * software, must execute solely and exclusively on microcontroller or - * microprocessor devices manufactured by or for STMicroelectronics. - * 5. Redistribution and use of this software other than as permitted under - * this license is void and will automatically terminate your rights under - * this license. - * - * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY - * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT - * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - #include #include "cmsis_os.h" @@ -407,7 +364,7 @@ osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer, + (TimerCallbackFunction_t)timer_def->ptimer, (StaticTimer_t *)timer_def->controlblock); } else { @@ -415,21 +372,21 @@ osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer); + (TimerCallbackFunction_t)timer_def->ptimer); } #elif( configSUPPORT_STATIC_ALLOCATION == 1 ) return xTimerCreateStatic((const char *)"", 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer, + (TimerCallbackFunction_t)timer_def->ptimer, (StaticTimer_t *)timer_def->controlblock); #else return xTimerCreate((const char *)"", 1, // period should be filled when starting the Timer using osTimerStart (type == osTimerPeriodic) ? pdTRUE : pdFALSE, (void *) argument, - (TaskFunction_t)timer_def->ptimer); + (TimerCallbackFunction_t)timer_def->ptimer); #endif #else @@ -991,10 +948,7 @@ void *osPoolAlloc (osPoolId pool_id) } for (i = 0; i < pool_id->pool_sz; i++) { - index = pool_id->currentIndex + i; - if (index >= pool_id->pool_sz) { - index = 0; - } + index = (pool_id->currentIndex + i) % pool_id->pool_sz; if (pool_id->markers[index] == 0) { pool_id->markers[index] = 1; diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h similarity index 95% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h rename to Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h index 89a105dc..2f24df0f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h @@ -53,51 +53,6 @@ * POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*/ - /** - ****************************************************************************** - * @file cmsis_os.h - * @author MCD Application Team - * @date 13-July-2017 - * @brief Header of cmsis_os.c - * A new set of APIs are added in addition to existing ones, these APIs - * are specific to FreeRTOS. - ****************************************************************************** - * @attention - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted, provided that the following conditions are met: - * - * 1. Redistribution of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of other - * contributors to this software may be used to endorse or promote products - * derived from this software without specific written permission. - * 4. This software, including modifications and/or derivative works of this - * software, must execute solely and exclusively on microcontroller or - * microprocessor devices manufactured by or for STMicroelectronics. - * 5. Redistribution and use of this software other than as permitted under - * this license is void and will automatically terminate your rights under - * this license. - * - * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY - * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT - * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - #include "FreeRTOS.h" #include "task.h" #include "timers.h" diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c b/Firmware/ThirdParty/FreeRTOS/Source/croutine.c similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c rename to Firmware/ThirdParty/FreeRTOS/Source/croutine.c index 9ce50030..56c8ac29 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/croutine.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/croutine.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c b/Firmware/ThirdParty/FreeRTOS/Source/event_groups.c similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c rename to Firmware/ThirdParty/FreeRTOS/Source/event_groups.c index bf4ec246..65a5ff25 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c +++ b/Firmware/ThirdParty/FreeRTOS/Source/event_groups.c @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h b/Firmware/ThirdParty/FreeRTOS/Source/include/FreeRTOS.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/FreeRTOS.h index ceb469a7..9d09d91a 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/FreeRTOS.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -241,26 +241,10 @@ extern "C" { #define configASSERT_DEFINED 1 #endif -/* configPRECONDITION should be defined as configASSERT. -The CBMC proofs need a way to track assumptions and assertions. -A configPRECONDITION statement should express an implicit invariant or -assumption made. A configASSERT statement should express an invariant that must -hold explicit before calling the code. */ -#ifndef configPRECONDITION - #define configPRECONDITION( X ) configASSERT(X) - #define configPRECONDITION_DEFINED 0 -#else - #define configPRECONDITION_DEFINED 1 -#endif - #ifndef portMEMORY_BARRIER #define portMEMORY_BARRIER() #endif -#ifndef portSOFTWARE_BARRIER - #define portSOFTWARE_BARRIER() -#endif - /* The timers module relies on xTaskGetSchedulerState(). */ #if configUSE_TIMERS == 1 @@ -953,7 +937,6 @@ V8 if desired. */ #define pcTimerGetTimerName pcTimerGetName #define pcQueueGetQueueName pcQueueGetName #define vTaskGetTaskInfo vTaskGetInfo - #define xTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter /* Backward compatibility within the scheduler code only - these definitions are not really required but are included for completeness. */ diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h b/Firmware/ThirdParty/FreeRTOS/Source/include/StackMacros.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/StackMacros.h index 56439917..3ed8b22d 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/StackMacros.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h b/Firmware/ThirdParty/FreeRTOS/Source/include/croutine.h similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/croutine.h index 8d7069c0..8b3b41b9 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/croutine.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -157,7 +157,7 @@ BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPri } // Alternatively, if you do not require any other part of the idle task to - // execute, the idle task hook can call vCoRoutineSchedule() within an + // execute, the idle task hook can call vCoRoutineScheduler() within an // infinite loop. void vApplicationIdleHook( void ) { diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h b/Firmware/ThirdParty/FreeRTOS/Source/include/deprecated_definitions.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/deprecated_definitions.h index 21657b9d..9cece988 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/deprecated_definitions.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h b/Firmware/ThirdParty/FreeRTOS/Source/include/event_groups.h similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/event_groups.h index a87fdf37..1f38bdb7 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/event_groups.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h b/Firmware/ThirdParty/FreeRTOS/Source/include/list.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/list.h index a3e30249..2fb6775f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/list.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/list.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -183,7 +183,7 @@ typedef struct xLIST * Access macro to get the owner of a list item. The owner of a list item * is the object (usually a TCB) that contains the list item. * - * \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER * \ingroup LinkedList */ #define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner ) @@ -225,7 +225,7 @@ typedef struct xLIST #define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext ) /* - * Return the next list item. + * Return the list item at the head of the list. * * \page listGET_NEXT listGET_NEXT * \ingroup LinkedList diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h b/Firmware/ThirdParty/FreeRTOS/Source/include/message_buffer.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/message_buffer.h index 0c3edb9c..cfa08cb9 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/message_buffer.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -62,10 +62,6 @@ #ifndef FREERTOS_MESSAGE_BUFFER_H #define FREERTOS_MESSAGE_BUFFER_H -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include message_buffer.h" -#endif - /* Message buffers are built onto of stream buffers. */ #include "stream_buffer.h" @@ -399,10 +395,10 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE. // priority of the currently executing task was unblocked and a context // switch should be performed to ensure the ISR returns to the unblocked // task. In most FreeRTOS ports this is done by simply passing - // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the + // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the // variables value, and perform the context switch if necessary. Check the // documentation for the port in use for port specific instructions. - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + taskYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } * \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR @@ -588,10 +584,10 @@ BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE. // priority of the currently executing task was unblocked and a context // switch should be performed to ensure the ISR returns to the unblocked // task. In most FreeRTOS ports this is done by simply passing - // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the + // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the // variables value, and perform the context switch if necessary. Check the // documentation for the port in use for port specific instructions. - portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + taskYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } * \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_prototypes.h similarity index 96% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/mpu_prototypes.h index a21b7a66..5d749071 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_prototypes.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -69,21 +69,19 @@ void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseTy BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) FREERTOS_SYSTEM_CALL; TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL; UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL; -uint32_t MPU_ulTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL; void MPU_vTaskList( char * pcWriteBuffer ) FREERTOS_SYSTEM_CALL; void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; uint32_t MPU_ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTaskNotifyStateClear( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; -uint32_t MPU_ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTaskIncrementTick( void ) FREERTOS_SYSTEM_CALL; TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL; void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL; void MPU_vTaskMissedYield( void ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL; -BaseType_t MPU_xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) FREERTOS_SYSTEM_CALL; /* MPU versions of queue.h API functions. */ BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL; @@ -124,7 +122,6 @@ TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; void MPU_vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) FREERTOS_SYSTEM_CALL; -UBaseType_t MPU_uxTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; BaseType_t MPU_xTimerCreateTimerTask( void ) FREERTOS_SYSTEM_CALL; diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_wrappers.h similarity index 96% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/mpu_wrappers.h index 5f63d4f2..711393f6 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/mpu_wrappers.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -77,13 +77,11 @@ only for ports that are using the MPU. */ #define uxTaskGetSystemState MPU_uxTaskGetSystemState #define vTaskList MPU_vTaskList #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats - #define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter + #define xTaskGetIdleRunTimeCounter MPU_xTaskGetIdleRunTimeCounter #define xTaskGenericNotify MPU_xTaskGenericNotify #define xTaskNotifyWait MPU_xTaskNotifyWait #define ulTaskNotifyTake MPU_ulTaskNotifyTake #define xTaskNotifyStateClear MPU_xTaskNotifyStateClear - #define ulTaskNotifyValueClear MPU_ulTaskNotifyValueClear - #define xTaskCatchUpTicks MPU_xTaskCatchUpTicks #define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle #define vTaskSetTimeOutState MPU_vTaskSetTimeOutState @@ -129,7 +127,6 @@ only for ports that are using the MPU. */ #define xTimerPendFunctionCall MPU_xTimerPendFunctionCall #define pcTimerGetName MPU_pcTimerGetName #define vTimerSetReloadMode MPU_vTimerSetReloadMode - #define uxTimerGetReloadMode MPU_uxTimerGetReloadMode #define xTimerGetPeriod MPU_xTimerGetPeriod #define xTimerGetExpiryTime MPU_xTimerGetExpiryTime #define xTimerGenericCommand MPU_xTimerGenericCommand diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h b/Firmware/ThirdParty/FreeRTOS/Source/include/portable.h similarity index 80% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/portable.h index a2099c33..59e81694 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/portable.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -118,26 +118,13 @@ extern "C" { #endif #endif -/* Used by heap_5.c to define the start address and size of each memory region -that together comprise the total FreeRTOS heap space. */ +/* Used by heap_5.c. */ typedef struct HeapRegion { uint8_t *pucStartAddress; size_t xSizeInBytes; } HeapRegion_t; -/* Used to pass information about the heap out of vPortGetHeapStats(). */ -typedef struct xHeapStats -{ - size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */ - size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ - size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ - size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */ - size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */ - size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */ - size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */ -} HeapStats_t; - /* * Used to define multiple heap regions for use by heap_5.c. This function * must be called before any calls to pvPortMalloc() - not creating a task, @@ -151,11 +138,6 @@ typedef struct xHeapStats */ void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION; -/* - * Returns a HeapStats_t structure filled with information about the current - * heap state. - */ -void vPortGetHeapStats( HeapStats_t *pxHeapStats ); /* * Map to the memory management routines required for the port. diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h b/Firmware/ThirdParty/FreeRTOS/Source/include/projdefs.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/projdefs.h index 0d95130b..e0458619 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/projdefs.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h b/Firmware/ThirdParty/FreeRTOS/Source/include/queue.h similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/queue.h index 52ccca55..3b9da937 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/queue.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -1284,7 +1284,7 @@ uint32_t ulVarToSend, ulValReceived; // name of the yield function required is port specific. if( xHigherPriorityTaskWokenByPost ) { - portYIELD_FROM_ISR(); + taskYIELD_YIELD_FROM_ISR(); } } diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h b/Firmware/ThirdParty/FreeRTOS/Source/include/semphr.h similarity index 99% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/semphr.h index 787c7912..2c106eac 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/semphr.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h b/Firmware/ThirdParty/FreeRTOS/Source/include/stack_macros.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/stack_macros.h index b5bac083..18406bbf 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/stack_macros.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h b/Firmware/ThirdParty/FreeRTOS/Source/include/stream_buffer.h similarity index 98% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/stream_buffer.h index a8b68ad6..0f00119e 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/stream_buffer.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -43,7 +43,7 @@ * (such as xStreamBufferSend()) inside a critical section and set the send * block time to 0. Likewise, if there are to be multiple different readers * then the application writer must place each call to a reading API function - * (such as xStreamBufferReceive()) inside a critical section section and set the + * (such as xStreamBufferRead()) inside a critical section section and set the * receive block time to 0. * */ @@ -51,10 +51,6 @@ #ifndef STREAM_BUFFER_H #define STREAM_BUFFER_H -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include stream_buffer.h" -#endif - #if defined( __cplusplus ) extern "C" { #endif @@ -241,7 +237,7 @@ size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, * (such as xStreamBufferSend()) inside a critical section and set the send * block time to 0. Likewise, if there are to be multiple different readers * then the application writer must place each call to a reading API function - * (such as xStreamBufferReceive()) inside a critical section and set the receive + * (such as xStreamBufferRead()) inside a critical section and set the receive * block time to 0. * * Use xStreamBufferSend() to write to a stream buffer from a task. Use @@ -339,7 +335,7 @@ size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, * (such as xStreamBufferSend()) inside a critical section and set the send * block time to 0. Likewise, if there are to be multiple different readers * then the application writer must place each call to a reading API function - * (such as xStreamBufferReceive()) inside a critical section and set the receive + * (such as xStreamBufferRead()) inside a critical section and set the receive * block time to 0. * * Use xStreamBufferSend() to write to a stream buffer from a task. Use @@ -439,7 +435,7 @@ size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, * (such as xStreamBufferSend()) inside a critical section and set the send * block time to 0. Likewise, if there are to be multiple different readers * then the application writer must place each call to a reading API function - * (such as xStreamBufferReceive()) inside a critical section and set the receive + * (such as xStreamBufferRead()) inside a critical section and set the receive * block time to 0. * * Use xStreamBufferReceive() to read from a stream buffer from a task. Use diff --git a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h b/Firmware/ThirdParty/FreeRTOS/Source/include/task.h similarity index 93% rename from Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h rename to Firmware/ThirdParty/FreeRTOS/Source/include/task.h index b0cc60b6..f3cf118f 100644 --- a/Firmware/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include/task.h +++ b/Firmware/ThirdParty/FreeRTOS/Source/include/task.h @@ -1,6 +1,6 @@ /* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -43,10 +43,10 @@ extern "C" { * MACROS AND DEFINITIONS *----------------------------------------------------------*/ -#define tskKERNEL_VERSION_NUMBER "V10.3.1" +#define tskKERNEL_VERSION_NUMBER "V10.2.0" #define tskKERNEL_VERSION_MAJOR 10 -#define tskKERNEL_VERSION_MINOR 3 -#define tskKERNEL_VERSION_BUILD 1 +#define tskKERNEL_VERSION_MINOR 2 +#define tskKERNEL_VERSION_BUILD 0 /* MPU region parameters passed in ulParameters * of MemoryRegion_t struct. */ @@ -314,13 +314,13 @@ is used in assert() statements. */ // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time // the new task attempts to access it. xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle ); - configASSERT( xHandle ); + configASSERT( xHandle ); // Use the handle to delete the task. - if( xHandle != NULL ) - { - vTaskDelete( xHandle ); - } + if( xHandle != NULL ) + { + vTaskDelete( xHandle ); + } } * \defgroup xTaskCreate xTaskCreate @@ -498,9 +498,9 @@ static const TaskParameters_t xCheckTaskParameters = // for full information. { // Base address Length Parameters - { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, - { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, - { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } } }; @@ -584,9 +584,9 @@ static const TaskParameters_t xCheckTaskParameters = // for full information. { // Base address Length Parameters - { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, - { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, - { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } } &xTaskBuffer; // Holds the task's data structure. @@ -831,11 +831,6 @@ void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xT * task will leave the Blocked state, and return from whichever function call * placed the task into the Blocked state. * - * There is no 'FromISR' version of this function as an interrupt would need to - * know which object a task was blocked on in order to know which actions to - * take. For example, if the task was blocked on a queue the interrupt handler - * would then need to know if the queue was locked. - * * @param xTask The handle of the task to remove from the Blocked state. * * @return If the task referenced by xTask was not in the Blocked state then @@ -1743,7 +1738,7 @@ void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e9 /** * task. h -*