Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9eadbf1be |
@@ -1,2 +0,0 @@
|
|||||||
* text=auto eol=lf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
@@ -3,6 +3,3 @@
|
|||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/ipch
|
.vscode/ipch
|
||||||
scripts
|
|
||||||
__pycache__/
|
|
||||||
*.typ
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# ESP_Project4_H26
|
# ESP_Project4_H26
|
||||||
|
|
||||||
A repository for project 4, done in the Embedded System Programming class in Reykjavík University in the Fall semester of 2026
|
A repository for project 3, done in the Embedded System Programming class in Reykjavík University in the Fall semester of 2026
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
@startuml P4-architecture
|
|
||||||
' Project 4, Part 2.2: Layers of the reduced CANopen node (Node ID 1).
|
|
||||||
' Arrows point from the user to the thing it uses.
|
|
||||||
|
|
||||||
' skinparam packageStyle rectangle
|
|
||||||
skinparam classAttributeIconSize 0
|
|
||||||
hide empty members
|
|
||||||
|
|
||||||
package "Application layer (uses OSI 7)" {
|
|
||||||
class MotorController {
|
|
||||||
+ to_operational()
|
|
||||||
+ to_pre_operational()
|
|
||||||
+ to_stopped()
|
|
||||||
}
|
|
||||||
abstract class State
|
|
||||||
class SetpointGenerator
|
|
||||||
class PI_controller
|
|
||||||
}
|
|
||||||
|
|
||||||
package "CANopen layer (OSI 5-7)" {
|
|
||||||
class CanopenNode {
|
|
||||||
+ on_frame(frame)
|
|
||||||
+ poll(now_ms)
|
|
||||||
}
|
|
||||||
class SdoServer
|
|
||||||
class ObjectDictionary
|
|
||||||
interface NodeApplication {
|
|
||||||
+ on_nmt_command(cmd)
|
|
||||||
+ set_target_velocity(rpm)
|
|
||||||
+ get_velocity() : rpm
|
|
||||||
+ get_duty() : 0.1 %
|
|
||||||
}
|
|
||||||
interface CanBus {
|
|
||||||
+ send(frame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Platform layer (Zephyr)" {
|
|
||||||
class ZephyrCanBus
|
|
||||||
class MotorDriver
|
|
||||||
class RotaryEncoderDriver
|
|
||||||
}
|
|
||||||
|
|
||||||
package "Hardware (OSI 1-2)" {
|
|
||||||
class "CAN controller + transceiver" as CAN
|
|
||||||
}
|
|
||||||
|
|
||||||
MotorController -> State
|
|
||||||
MotorController --> SetpointGenerator
|
|
||||||
MotorController --> PI_controller
|
|
||||||
MotorController ..|> NodeApplication
|
|
||||||
MotorController --> MotorDriver
|
|
||||||
MotorController --> RotaryEncoderDriver
|
|
||||||
|
|
||||||
CanopenNode --> SdoServer
|
|
||||||
SdoServer --> ObjectDictionary
|
|
||||||
CanopenNode --> ObjectDictionary
|
|
||||||
CanopenNode --> NodeApplication
|
|
||||||
CanopenNode --> CanBus
|
|
||||||
|
|
||||||
ZephyrCanBus ..|> CanBus
|
|
||||||
ZephyrCanBus --> CAN
|
|
||||||
|
|
||||||
note top of CanopenNode
|
|
||||||
NMT (0x000), boot-up (0x701),
|
|
||||||
RPDO1 (0x201), TPDO1 (0x181)
|
|
||||||
end note
|
|
||||||
|
|
||||||
note left of SdoServer
|
|
||||||
0x601 / 0x581, expedited only
|
|
||||||
abort codes 0x06020000, 0x08000022
|
|
||||||
end note
|
|
||||||
|
|
||||||
note right of ObjectDictionary
|
|
||||||
0x1000, 0x2000-0x2010, 0x606C,
|
|
||||||
0x6083, 0x6084, 0x60FF
|
|
||||||
access rules, little-endian packing
|
|
||||||
end note
|
|
||||||
|
|
||||||
@enduml
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
@startuml
|
|
||||||
' skinparam linetype ortho
|
|
||||||
' skinparam nodesep 100
|
|
||||||
skinparam ranksep 80
|
|
||||||
|
|
||||||
skinparam state {
|
|
||||||
BackgroundColor LightBlue
|
|
||||||
}
|
|
||||||
|
|
||||||
' hide empty members
|
|
||||||
|
|
||||||
Application <|-- CANopen
|
|
||||||
|
|
||||||
class Application {
|
|
||||||
{field} state machine
|
|
||||||
{field} controllers
|
|
||||||
{field} setpoint generator
|
|
||||||
{method} hello world
|
|
||||||
}
|
|
||||||
|
|
||||||
CANopen : Zephyr
|
|
||||||
|
|
||||||
Class01 <-- Class02
|
|
||||||
Class03 *-- Class04
|
|
||||||
Class05 o-- Class06
|
|
||||||
Class07 .. Class08
|
|
||||||
Class09 -- Class10
|
|
||||||
|
|
||||||
@enduml
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
@startuml
|
||||||
|
' skinparam linetype ortho
|
||||||
|
' skinparam nodesep 100
|
||||||
|
skinparam ranksep 80
|
||||||
|
|
||||||
|
skinparam state {
|
||||||
|
BackgroundColor LightBlue
|
||||||
|
}
|
||||||
|
|
||||||
|
[*] --> Initialization
|
||||||
|
|
||||||
|
state Initialization
|
||||||
|
Initialization : Entry / Stop motor
|
||||||
|
Initialization : Entry / Initialize internal parameters
|
||||||
|
Initialization : Entry / Reset speed measurement
|
||||||
|
Initialization : Do / Wait 2 seconds
|
||||||
|
Initialization : Exit / Send Boot-up message
|
||||||
|
|
||||||
|
state PreOperational
|
||||||
|
PreOperational : Entry / Stop motor
|
||||||
|
PreOperational : Do / Blink LED at 1 Hz
|
||||||
|
PreOperational : Do / Accept Kp and Ti configuration
|
||||||
|
PreOperational : Do / Select P or PI controller [c]
|
||||||
|
PreOperational : Exit / LED off
|
||||||
|
|
||||||
|
state Operational
|
||||||
|
Operational : Entry / LED on
|
||||||
|
Operational : Do / Run speed controller
|
||||||
|
Operational : Exit / Stop motor and LED off
|
||||||
|
|
||||||
|
state Stopped
|
||||||
|
Stopped : Entry / Stop motor
|
||||||
|
Stopped : Do / Blink LED at 2 Hz
|
||||||
|
Stopped : Exit / LED off
|
||||||
|
|
||||||
|
Initialization -r-> PreOperational : initialization\n complete
|
||||||
|
' Initialization -d-> Operational : initialization\n complete
|
||||||
|
|
||||||
|
PreOperational -d-> Operational : set operational [o]
|
||||||
|
PreOperational -l--> Initialization : reset [r]
|
||||||
|
|
||||||
|
Operational -u-> PreOperational : set pre-operational [p]
|
||||||
|
Operational -> Initialization : reset [r]
|
||||||
|
Operational --> Stopped : fault detected
|
||||||
|
|
||||||
|
Stopped -l-> Initialization : reset [r]
|
||||||
|
Stopped -> Operational : set operational [o]
|
||||||
|
Stopped -u-> PreOperational : set pre-operational [p]
|
||||||
|
|
||||||
|
@enduml
|
||||||
+4
-13
@@ -4,28 +4,19 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
|
||||||
class Digital_in {
|
struct Digital_in {
|
||||||
public:
|
|
||||||
enum : uint8_t {
|
enum : uint8_t {
|
||||||
ENCODER_C1 = 0,
|
ENCODER_C1 = 0,
|
||||||
ENCODER_C2 = 1
|
ENCODER_C2 = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const struct gpio_dt_spec *gpio;
|
||||||
|
|
||||||
explicit Digital_in(uint8_t pin_id);
|
explicit Digital_in(uint8_t pin_id);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
bool is_hi();
|
bool is_hi();
|
||||||
bool is_lo();
|
bool is_lo();
|
||||||
|
|
||||||
// Calls handler on every rising edge of the pin. The caller owns the
|
|
||||||
// gpio_callback struct so it can find its own object with CONTAINER_OF.
|
|
||||||
// Returns 0 on success, or a negative Zephyr error code.
|
|
||||||
int enable_rising_edge_interrupt(
|
|
||||||
struct gpio_callback *callback,
|
|
||||||
gpio_callback_handler_t handler);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const struct gpio_dt_spec *gpio;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -4,7 +4,10 @@
|
|||||||
#include<stdint.h>
|
#include<stdint.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
|
||||||
|
|
||||||
struct Digital_out {
|
struct Digital_out {
|
||||||
|
// uint8_t pin_mask;
|
||||||
|
|
||||||
enum : uint8_t {
|
enum : uint8_t {
|
||||||
STATUS_LED = 0
|
STATUS_LED = 0
|
||||||
};
|
};
|
||||||
@@ -18,6 +21,7 @@ struct Digital_out {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const struct gpio_dt_spec *gpio;
|
const struct gpio_dt_spec *gpio;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,9 +5,13 @@
|
|||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
|
|
||||||
class InitializationState : public State
|
class InitializationState : public State
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
// static const unsigned long WAIT_MS = 2000;
|
||||||
|
|
||||||
|
// unsigned long enter_time_ms = 0;
|
||||||
|
|
||||||
|
|
||||||
static constexpr int64_t WAIT_MS = 2000;
|
static constexpr int64_t WAIT_MS = 2000;
|
||||||
@@ -22,8 +26,6 @@ public:
|
|||||||
void command(char c) override;
|
void command(char c) override;
|
||||||
|
|
||||||
void exit() override;
|
void exit() override;
|
||||||
|
|
||||||
NodeState id() const override { return NodeState::INITIALIZATION; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+15
-40
@@ -7,16 +7,16 @@
|
|||||||
#include "motor_driver.h"
|
#include "motor_driver.h"
|
||||||
#include "p_controller.h"
|
#include "p_controller.h"
|
||||||
#include "digital_out.h"
|
#include "digital_out.h"
|
||||||
#include "node_types.h"
|
|
||||||
#include "node_application.h"
|
|
||||||
|
|
||||||
class State;
|
class State;
|
||||||
|
|
||||||
class MotorController : public NodeApplication
|
|
||||||
|
class MotorController
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static constexpr double DEFAULT_TARGET_RPM = 0.0;
|
static constexpr double DEFAULT_REFERENCE_RPM = -40.0;
|
||||||
static constexpr double PWM_PER_PERCENT = 255.0 / 100.0;
|
static constexpr double PWM_PER_PERCENT = 255.0 / 100.0;
|
||||||
|
//static const uint8_t LED_PIN = 5; // PB5 = D13 = LED_BUILTIN
|
||||||
static constexpr uint8_t LED_PIN = Digital_out::STATUS_LED;
|
static constexpr uint8_t LED_PIN = Digital_out::STATUS_LED;
|
||||||
|
|
||||||
State* current_state;
|
State* current_state;
|
||||||
@@ -28,32 +28,22 @@ private:
|
|||||||
|
|
||||||
RotaryEncoderDriver &encoder;
|
RotaryEncoderDriver &encoder;
|
||||||
MotorDriver &motor;
|
MotorDriver &motor;
|
||||||
|
P_controller *controller;
|
||||||
P_controller &p_controller;
|
|
||||||
P_controller &pi_controller;
|
|
||||||
P_controller *controller; // Bendir á p_controller eða pi_controller
|
|
||||||
|
|
||||||
Digital_out led;
|
Digital_out led;
|
||||||
|
|
||||||
double target_rpm; // Það sem beðið er um (RPDO1)
|
double reference_rpm;
|
||||||
double reference_rpm; // Það sem stýringin fær
|
|
||||||
|
|
||||||
uint32_t profile_acceleration; // RPM/s
|
|
||||||
uint32_t profile_deceleration;
|
|
||||||
|
|
||||||
// Síðasta sýni, notað fyrir CSV-útprentun
|
// Síðasta sýni, notað fyrir CSV-útprentun
|
||||||
double last_actual_rpm;
|
double last_actual_rpm;
|
||||||
double last_pwm;
|
double last_pwm;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MotorController(
|
MotorController(
|
||||||
RotaryEncoderDriver &encoder,
|
RotaryEncoderDriver &encoder,
|
||||||
MotorDriver &motor,
|
MotorDriver &motor,
|
||||||
P_controller &p_controller,
|
P_controller &controller,
|
||||||
P_controller &pi_controller,
|
|
||||||
State &initialization,
|
State &initialization,
|
||||||
State &pre_operational,
|
State &pre_operational,
|
||||||
State &operational,
|
State &operational,
|
||||||
@@ -70,15 +60,13 @@ public:
|
|||||||
void transition_to(State &new_state);
|
void transition_to(State &new_state);
|
||||||
|
|
||||||
|
|
||||||
void to_initialization() override;
|
void to_initialization();
|
||||||
|
|
||||||
void to_pre_operational() override;
|
void to_pre_operational();
|
||||||
|
|
||||||
void to_operational() override;
|
void to_operational();
|
||||||
|
|
||||||
void to_stopped() override;
|
void to_stopped();
|
||||||
|
|
||||||
NodeState get_state() const override;
|
|
||||||
|
|
||||||
bool fault_active();
|
bool fault_active();
|
||||||
|
|
||||||
@@ -86,34 +74,21 @@ public:
|
|||||||
// Skilar true þegar nýtt sýni var keyrt í gegnum stýringuna
|
// Skilar true þegar nýtt sýni var keyrt í gegnum stýringuna
|
||||||
bool run_speed_controller();
|
bool run_speed_controller();
|
||||||
|
|
||||||
void set_target_rpm(double rpm) override { target_rpm = rpm; }
|
|
||||||
double get_target_rpm() const override { return target_rpm; }
|
|
||||||
double get_reference_rpm() const { return reference_rpm; }
|
double get_reference_rpm() const { return reference_rpm; }
|
||||||
double get_actual_rpm() const override { return last_actual_rpm; }
|
double get_actual_rpm() const { return last_actual_rpm; }
|
||||||
|
|
||||||
double get_pwm() const { return last_pwm; }
|
double get_pwm() const { return last_pwm; }
|
||||||
double get_duty_percent() const override { return last_pwm / PWM_PER_PERCENT; }
|
|
||||||
|
|
||||||
void set_profile_acceleration(uint32_t value) override {profile_acceleration = value; }
|
|
||||||
uint32_t get_profile_acceleration() const override { return profile_acceleration; }
|
|
||||||
void set_profile_deceleration(uint32_t value) override {profile_deceleration = value; }
|
|
||||||
uint32_t get_profile_deceleration() const override { return profile_deceleration; }
|
|
||||||
|
|
||||||
void stop_motor();
|
void stop_motor();
|
||||||
|
|
||||||
void reset_speed_measurement();
|
void reset_speed_measurement();
|
||||||
|
|
||||||
void reset_controller();
|
void reset_controller();
|
||||||
void reset_parameters() override;
|
|
||||||
|
|
||||||
void set_Kp(double value) override;
|
void set_Kp(double value);
|
||||||
double get_Kp() const override { return controller->get_Kp(); }
|
|
||||||
|
|
||||||
bool set_Ti(double value) override;
|
void set_Ti(double value);
|
||||||
double get_Ti() const override { return controller->get_Ti(); }
|
|
||||||
|
|
||||||
void select_controller(ControllerType type) override;
|
void set_controller(P_controller &new_controller);
|
||||||
ControllerType get_controller_type() const override;
|
|
||||||
|
|
||||||
void led_on();
|
void led_on();
|
||||||
|
|
||||||
|
|||||||
@@ -22,4 +22,4 @@ private:
|
|||||||
bool initialized; // Flag that ignores motor commands if setup fails
|
bool initialized; // Flag that ignores motor commands if setup fails
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef NODE_APPLICATION_H
|
|
||||||
#define NODE_APPLICATION_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "node_types.h"
|
|
||||||
|
|
||||||
class NodeApplication
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~NodeApplication() {}
|
|
||||||
|
|
||||||
// NMT
|
|
||||||
virtual void to_initialization() = 0;
|
|
||||||
virtual void to_pre_operational() = 0;
|
|
||||||
virtual void to_operational() = 0;
|
|
||||||
virtual void to_stopped() = 0;
|
|
||||||
virtual void reset_parameters() = 0;
|
|
||||||
virtual NodeState get_state() const = 0;
|
|
||||||
|
|
||||||
// 0x2000
|
|
||||||
virtual void select_controller(ControllerType type) = 0;
|
|
||||||
virtual ControllerType get_controller_type() const = 0;
|
|
||||||
|
|
||||||
// 0x2001
|
|
||||||
virtual void set_Kp(double value) = 0;
|
|
||||||
virtual double get_Kp() const = 0;
|
|
||||||
|
|
||||||
// 0x2002
|
|
||||||
virtual bool set_Ti(double value) = 0;
|
|
||||||
virtual double get_Ti() const = 0;
|
|
||||||
|
|
||||||
// 0x2010
|
|
||||||
virtual double get_duty_percent() const = 0;
|
|
||||||
|
|
||||||
// 0x606C
|
|
||||||
virtual double get_actual_rpm() const = 0;
|
|
||||||
|
|
||||||
// 0x6083
|
|
||||||
virtual void set_profile_acceleration(uint32_t value) = 0;
|
|
||||||
virtual uint32_t get_profile_acceleration() const = 0;
|
|
||||||
// 0x6084
|
|
||||||
virtual void set_profile_deceleration(uint32_t value) = 0;
|
|
||||||
virtual uint32_t get_profile_deceleration() const = 0;
|
|
||||||
|
|
||||||
// 0x60FF (RPDO1)
|
|
||||||
virtual void set_target_rpm(double value) = 0;
|
|
||||||
virtual double get_target_rpm() const = 0;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef NODE_TYPES_H
|
|
||||||
#define NODE_TYPES_H
|
|
||||||
|
|
||||||
enum class ControllerType
|
|
||||||
{
|
|
||||||
P,
|
|
||||||
PI
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class NodeState
|
|
||||||
{
|
|
||||||
INITIALIZATION,
|
|
||||||
PRE_OPERATIONAL,
|
|
||||||
OPERATIONAL,
|
|
||||||
STOPPED
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -24,8 +24,6 @@ public:
|
|||||||
void command(char c) override;
|
void command(char c) override;
|
||||||
|
|
||||||
void exit() override;
|
void exit() override;
|
||||||
|
|
||||||
NodeState id() const override { return NodeState::OPERATIONAL; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5,27 +5,21 @@ class P_controller
|
|||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double Kp;
|
double Kp;
|
||||||
double default_Kp; // Gildið úr constructornum, notað af restore_defaults()
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
P_controller(double Kp);
|
P_controller(double Kp);
|
||||||
virtual ~P_controller() {}
|
virtual ~P_controller() {}
|
||||||
virtual double update(double ref, double actual);
|
virtual double update(double ref, double actual);
|
||||||
virtual void reset() {}
|
virtual void reset() {}
|
||||||
virtual void restore_defaults(); //
|
|
||||||
virtual double get_Kp() const { return Kp; }
|
|
||||||
virtual double get_Ti() const { return 0.0; }
|
|
||||||
|
|
||||||
virtual void set_Kp(double value)
|
virtual void set_Kp(double value)
|
||||||
{
|
{
|
||||||
Kp = value;
|
Kp = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool set_Ti(double value)
|
virtual void set_Ti(double value)
|
||||||
{
|
{
|
||||||
// P controller does not use Ti
|
// P controller does not use Ti
|
||||||
(void)value;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,14 @@ class PI_controller : public P_controller
|
|||||||
static constexpr double U_MIN = -100.0; // %
|
static constexpr double U_MIN = -100.0; // %
|
||||||
|
|
||||||
double Ti;
|
double Ti;
|
||||||
double default_Ti;
|
|
||||||
double T;
|
double T;
|
||||||
double integral;
|
double integral;
|
||||||
public:
|
public:
|
||||||
PI_controller(double Kp, double Ti, double T);
|
PI_controller(double Kp, double Ti, double T);
|
||||||
double update(double ref, double actual) override;
|
double update(double ref, double actual) override;
|
||||||
void reset() override;
|
void reset() override;
|
||||||
void restore_defaults() override;
|
|
||||||
|
|
||||||
bool set_Ti(double value) override;
|
void set_Ti(double value) override;
|
||||||
double get_Ti() const override { return Ti; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,25 +4,27 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "stdint.h"
|
#include "stdint.h"
|
||||||
|
|
||||||
// class P_controller;
|
class P_controller;
|
||||||
|
|
||||||
class PreOperationalState : public State
|
class PreOperationalState : public State
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// P_controller &p_controller;
|
P_controller &p_controller;
|
||||||
// P_controller &pi_controller;
|
P_controller &pi_controller;
|
||||||
// bool pi_selected;
|
bool pi_selected;
|
||||||
|
|
||||||
|
//unsigned long previous_time;
|
||||||
int64_t previous_time;
|
int64_t previous_time;
|
||||||
bool led_state;
|
bool led_state;
|
||||||
|
|
||||||
|
//bool waiting_for_kp;
|
||||||
|
//bool waiting_for_ti;
|
||||||
|
|
||||||
enum InputMode
|
enum InputMode
|
||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
ENTER_KP,
|
ENTER_KP,
|
||||||
ENTER_TI,
|
ENTER_TI
|
||||||
ENTER_RPM
|
|
||||||
};
|
};
|
||||||
|
|
||||||
InputMode input_mode;
|
InputMode input_mode;
|
||||||
@@ -31,15 +33,12 @@ private:
|
|||||||
uint8_t input_index;
|
uint8_t input_index;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// PreOperationalState(P_controller& p_controller, P_controller& pi_controller);
|
PreOperationalState(P_controller& p_controller, P_controller& pi_controller);
|
||||||
PreOperationalState();
|
|
||||||
|
|
||||||
void enter() override;
|
void enter() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void command(char c) override;
|
void command(char c) override;
|
||||||
void exit() override;
|
void exit() override;
|
||||||
|
|
||||||
NodeState id() const override { return NodeState::PRE_OPERATIONAL; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -3,9 +3,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
#include <zephyr/kernel.h>
|
|
||||||
|
|
||||||
#include "digital_in.h"
|
//#include "digital_in.h"
|
||||||
#include "timer_usec.h"
|
#include "timer_usec.h"
|
||||||
|
|
||||||
class RotaryEncoderDriver {
|
class RotaryEncoderDriver {
|
||||||
@@ -26,10 +25,6 @@ public:
|
|||||||
bool new_speed_available();
|
bool new_speed_available();
|
||||||
void reset_speed_measurement();
|
void reset_speed_measurement();
|
||||||
|
|
||||||
// Blocks until the next speed sample, or until the timeout expires.
|
|
||||||
// Lets the main loop run in step with the 4 ms sampling.
|
|
||||||
void wait_for_sample(k_timeout_t timeout);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint8_t HIST_LEN = 4; // 4 x Ts = 16 ms gluggi fyrir rpm-utreikning
|
static const uint8_t HIST_LEN = 4; // 4 x Ts = 16 ms gluggi fyrir rpm-utreikning
|
||||||
|
|
||||||
@@ -40,11 +35,12 @@ private:
|
|||||||
volatile int32_t history[HIST_LEN];
|
volatile int32_t history[HIST_LEN];
|
||||||
volatile uint8_t hist_idx;
|
volatile uint8_t hist_idx;
|
||||||
|
|
||||||
Digital_in eA;
|
|
||||||
Digital_in eB;
|
|
||||||
|
//Digital_in eA;
|
||||||
|
//Digital_in eB;
|
||||||
|
|
||||||
struct gpio_callback encoder_c1_callback;
|
struct gpio_callback encoder_c1_callback;
|
||||||
struct k_sem sample_sem;
|
|
||||||
Timer_usec timer;
|
Timer_usec timer;
|
||||||
|
|
||||||
static void gpio_edge_handler(
|
static void gpio_edge_handler(
|
||||||
@@ -53,6 +49,28 @@ private:
|
|||||||
uint32_t pins);
|
uint32_t pins);
|
||||||
|
|
||||||
static void timer_expired(void *context);
|
static void timer_expired(void *context);
|
||||||
};
|
|
||||||
|
|
||||||
|
// public:
|
||||||
|
// RotaryEncoderDriver();
|
||||||
|
|
||||||
|
// void begin();
|
||||||
|
|
||||||
|
// void update();
|
||||||
|
|
||||||
|
// int32_t position();
|
||||||
|
|
||||||
|
// void update_delta();
|
||||||
|
|
||||||
|
// float count_per_sec();
|
||||||
|
|
||||||
|
// float rpm();
|
||||||
|
|
||||||
|
// bool new_speed_available();
|
||||||
|
|
||||||
|
// void reset_speed_measurement();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+2
-4
@@ -1,8 +1,6 @@
|
|||||||
#ifndef STATE_H
|
#ifndef STATE_H
|
||||||
#define STATE_H
|
#define STATE_H
|
||||||
|
|
||||||
#include "node_types.h"
|
|
||||||
|
|
||||||
class MotorController;
|
class MotorController;
|
||||||
|
|
||||||
class State
|
class State
|
||||||
@@ -13,6 +11,7 @@ protected:
|
|||||||
public:
|
public:
|
||||||
virtual ~State()
|
virtual ~State()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_context(MotorController* context)
|
void set_context(MotorController* context)
|
||||||
@@ -28,7 +27,6 @@ public:
|
|||||||
|
|
||||||
virtual void exit() = 0; // Stop motor
|
virtual void exit() = 0; // Stop motor
|
||||||
|
|
||||||
virtual NodeState id() const = 0; //
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -8,8 +8,9 @@
|
|||||||
class StoppedState : public State
|
class StoppedState : public State
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
//unsigned long previous_time; // Used for blinking LED
|
||||||
int64_t previous_time;
|
int64_t previous_time;
|
||||||
bool led_state;
|
bool led_state; // Ditto
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StoppedState();
|
StoppedState();
|
||||||
@@ -22,7 +23,6 @@ public:
|
|||||||
|
|
||||||
void exit() override;
|
void exit() override;
|
||||||
|
|
||||||
NodeState id() const override { return NodeState::STOPPED; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
#ifndef TIMER_USEC_H
|
#ifndef TIMER_USEC_H
|
||||||
#define TIMER_USEC_H
|
#define TIMER_USEC_H
|
||||||
|
|
||||||
|
//#include <avr/io.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
// struct Timer_usec {
|
||||||
|
// Timer_usec();
|
||||||
|
// void init(uint16_t period_us);
|
||||||
|
//};
|
||||||
|
|
||||||
class Timer_usec {
|
class Timer_usec {
|
||||||
public:
|
public:
|
||||||
using callback_t = void (*)(void *context);
|
using callback_t = void (*)(void *context);
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Brings up can0 at the same bitrate as the Nucleo (125000 in src/CAN_FRAME_TEST.c and src/can_send_test.c).
|
|
||||||
# Usage: ./can_up.sh [bitrate] (default 125000)
|
|
||||||
BITRATE=${1:-125000}
|
|
||||||
|
|
||||||
sudo ip link set can0 down 2>/dev/null
|
|
||||||
sudo ip link set can0 up type can bitrate "$BITRATE"
|
|
||||||
ip -details -statistics link show can0
|
|
||||||
@@ -18,24 +18,3 @@ monitor_speed = 115200
|
|||||||
upload_flags =
|
upload_flags =
|
||||||
-c
|
-c
|
||||||
reset_config srst_only srst_nogate connect_assert_srst
|
reset_config srst_only srst_nogate connect_assert_srst
|
||||||
|
|
||||||
; CSV-útprentun í Operational fyrir step/load response (sjá capture_serial.py)
|
|
||||||
; build_flags = -D LOG_CSV=1
|
|
||||||
|
|
||||||
; Part 2.1: CAN bring-up test (src/can_send_test.c) instead of the motor app.
|
|
||||||
; Build/upload: pio run -e can_test -t upload
|
|
||||||
; [env:can_test]
|
|
||||||
; extends = env:nucleo_l432kc
|
|
||||||
; build_flags = -D CAN_SEND_TEST=1
|
|
||||||
|
|
||||||
; L4.4 example (src/CAN_FRAME_TEST.c) instead of the motor app.
|
|
||||||
; Build/upload: pio run -e can_frame_test -t upload
|
|
||||||
; [env:can_frame_test]
|
|
||||||
; extends = env:nucleo_l432kc
|
|
||||||
; build_flags = -D CAN_FRAME_TEST=1
|
|
||||||
|
|
||||||
; CAN reception with a filter (src/can_rx_test.c) instead of the motor app.
|
|
||||||
; Build/upload: pio run -e can_rx_test -t upload
|
|
||||||
; [env:can_rx_test]
|
|
||||||
; extends = env:nucleo_l432kc
|
|
||||||
; build_flags = -D CAN_RX_TEST=1
|
|
||||||
|
|||||||
+9
-11
@@ -7,43 +7,41 @@
|
|||||||
/* Devicetree node selected by zephyr,canbus in the overlay */
|
/* Devicetree node selected by zephyr,canbus in the overlay */
|
||||||
#define CANBUS_NODE DT_CHOSEN(zephyr_canbus)
|
#define CANBUS_NODE DT_CHOSEN(zephyr_canbus)
|
||||||
|
|
||||||
// L4.4 example. Built only with the can_frame_test environment
|
int main(void)
|
||||||
// (-D CAN_FRAME_TEST=1); main() in main.c calls this function.
|
|
||||||
void can_frame_test_main(void)
|
|
||||||
{
|
{
|
||||||
const struct device *dev = DEVICE_DT_GET(CANBUS_NODE);
|
const struct device *dev = DEVICE_DT_GET(CANBUS_NODE);
|
||||||
|
|
||||||
if (!device_is_ready(dev)) {
|
if (!device_is_ready(dev)) {
|
||||||
printk("CAN device not ready\n");
|
printk("CAN device not ready\n");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = can_set_bitrate(dev, 125000);
|
int err = can_set_bitrate(dev, 125000);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
printk("Error setting CAN bitrate (err %d)\n", err);
|
printk("Error setting CAN bitrate (err %d)\n", err);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = can_start(dev);
|
err = can_start(dev);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
printk("Error starting CAN controller (err %d)\n", err);
|
printk("Error starting CAN controller (err %d)\n", err);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct can_frame frame = {
|
struct can_frame frame = {
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.id = 0x456,
|
.id = 0x123,
|
||||||
.dlc = 4,
|
.dlc = 8,
|
||||||
.data = {1, 2, 3, 4, 5, 6, 7, 8},
|
.data = {1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
k_msleep(500);
|
k_msleep(10);
|
||||||
|
|
||||||
err = can_send(dev, &frame, K_MSEC(100), NULL, NULL);
|
err = can_send(dev, &frame, K_MSEC(100), NULL, NULL);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
printk("Sending failed (err %d)\n", err);
|
printk("Sending failed (err %d)\n", err);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// Part 2.1: CAN reception with a filter. Only frames with id RX_CAN_ID are
|
|
||||||
// accepted. The driver puts them in a message queue from interrupt context,
|
|
||||||
// and the main loop takes them out and prints them.
|
|
||||||
// Built only with the can_rx_test environment (-D CAN_RX_TEST=1).
|
|
||||||
//
|
|
||||||
// Test from the Pi:
|
|
||||||
// cansend can0 321#DEADBEEF -> printed by the Nucleo
|
|
||||||
// cansend can0 322#DEADBEEF -> rejected by the filter, nothing printed
|
|
||||||
|
|
||||||
#include <zephyr/device.h>
|
|
||||||
#include <zephyr/devicetree.h>
|
|
||||||
#include <zephyr/drivers/can.h>
|
|
||||||
#include <zephyr/kernel.h>
|
|
||||||
#include <zephyr/sys/printk.h>
|
|
||||||
|
|
||||||
#define CANBUS_NODE DT_CHOSEN(zephyr_canbus)
|
|
||||||
#define CAN_BITRATE 125000 // Must match the bitrate of can0 on the Pi
|
|
||||||
#define RX_CAN_ID 0x321 // The only id this node accepts
|
|
||||||
|
|
||||||
// Queue for up to 10 received frames. The CAN driver fills it in the
|
|
||||||
// receive interrupt; nothing else runs in interrupt context.
|
|
||||||
CAN_MSGQ_DEFINE(rx_msgq, 10);
|
|
||||||
|
|
||||||
void can_rx_test_main(void)
|
|
||||||
{
|
|
||||||
const struct device *dev = DEVICE_DT_GET(CANBUS_NODE);
|
|
||||||
|
|
||||||
if (!device_is_ready(dev)) {
|
|
||||||
printk("CAN device not ready\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int err = can_set_bitrate(dev, CAN_BITRATE);
|
|
||||||
if (err != 0) {
|
|
||||||
printk("Error setting CAN bitrate (err %d)\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept only RX_CAN_ID: all 11 bits of the id must match the mask.
|
|
||||||
const struct can_filter filter = {
|
|
||||||
.id = RX_CAN_ID,
|
|
||||||
.mask = CAN_STD_ID_MASK,
|
|
||||||
#ifdef CAN_FILTER_DATA
|
|
||||||
// Older Zephyr versions must be told to match data frames.
|
|
||||||
.flags = CAN_FILTER_DATA,
|
|
||||||
#else
|
|
||||||
.flags = 0,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns a filter id (>= 0) on success.
|
|
||||||
int filter_id = can_add_rx_filter_msgq(dev, &rx_msgq, &filter);
|
|
||||||
if (filter_id < 0) {
|
|
||||||
printk("Error adding RX filter (err %d)\n", filter_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = can_start(dev);
|
|
||||||
if (err != 0) {
|
|
||||||
printk("Error starting CAN controller (err %d)\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk("CAN RX test: %d bit/s, accepting id 0x%03X\n",
|
|
||||||
CAN_BITRATE, RX_CAN_ID);
|
|
||||||
|
|
||||||
struct can_frame frame;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
// Sleeps until a frame is in the queue, then processes it here in
|
|
||||||
// the main thread, where printing is allowed.
|
|
||||||
k_msgq_get(&rx_msgq, &frame, K_FOREVER);
|
|
||||||
|
|
||||||
printk("Received id 0x%03X [%d]:", frame.id, frame.dlc);
|
|
||||||
for (int i = 0; i < can_dlc_to_bytes(frame.dlc); i++) {
|
|
||||||
printk(" %02X", frame.data[i]);
|
|
||||||
}
|
|
||||||
printk("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
// Part 2.1: CAN bring-up. Sends one frame every 100 ms so it can be seen
|
|
||||||
// on the oscilloscope and with `candump can0` on the Raspberry Pi.
|
|
||||||
// Built only with the can_test environment (-D CAN_SEND_TEST=1).
|
|
||||||
|
|
||||||
#include <zephyr/device.h>
|
|
||||||
#include <zephyr/drivers/can.h>
|
|
||||||
#include <zephyr/kernel.h>
|
|
||||||
#include <zephyr/sys/printk.h>
|
|
||||||
|
|
||||||
// Change these to see the difference in candump.
|
|
||||||
#define TEST_CAN_ID 0x123 // 11-bit standard id (0x000 - 0x7FF)
|
|
||||||
#define TEST_CAN_DLC 8 // Number of data bytes (0 - 8)
|
|
||||||
#define CAN_BITRATE 125000 // Must match the bitrate of can0 on the Pi
|
|
||||||
|
|
||||||
static const struct device *const can_dev = DEVICE_DT_GET(DT_NODELABEL(can1));
|
|
||||||
|
|
||||||
void can_send_test_main(void)
|
|
||||||
{
|
|
||||||
if (!device_is_ready(can_dev)) {
|
|
||||||
printk("CAN device is not ready\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int err = can_set_bitrate(can_dev, CAN_BITRATE);
|
|
||||||
if (err != 0) {
|
|
||||||
printk("can_set_bitrate failed: %d\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = can_start(can_dev);
|
|
||||||
if (err != 0) {
|
|
||||||
printk("can_start failed: %d\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk("CAN started at %d bit/s, sending id 0x%03X\n",
|
|
||||||
CAN_BITRATE, TEST_CAN_ID);
|
|
||||||
|
|
||||||
struct can_frame frame = {
|
|
||||||
.id = TEST_CAN_ID,
|
|
||||||
.dlc = TEST_CAN_DLC,
|
|
||||||
.flags = 0, // Standard id, data frame
|
|
||||||
};
|
|
||||||
uint8_t counter = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// Byte 0 counts up so you can see new frames arriving.
|
|
||||||
frame.data[0] = counter++;
|
|
||||||
for (int i = 1; i < TEST_CAN_DLC; i++) {
|
|
||||||
frame.data[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks until the frame is sent (and acknowledged by another node).
|
|
||||||
err = can_send(can_dev, &frame, K_MSEC(100), NULL, NULL);
|
|
||||||
if (err != 0) {
|
|
||||||
printk("can_send failed: %d\n", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
k_sleep(K_MSEC(100));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+11
-21
@@ -1,8 +1,17 @@
|
|||||||
|
//#include <avr/io.h>
|
||||||
#include "digital_in.h"
|
#include "digital_in.h"
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <zephyr/devicetree.h>
|
#include <zephyr/devicetree.h>
|
||||||
#include <zephyr/sys/util.h>
|
|
||||||
|
// Digital_in::Digital_in(uint8_t pin) : pin_mask(1<<pin) {}
|
||||||
|
|
||||||
|
// void Digital_in::init() {
|
||||||
|
// DDRD &= ~pin_mask;
|
||||||
|
// PORTD |= pin_mask;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// bool Digital_in::is_hi() { return PIND & pin_mask; }
|
||||||
|
// bool Digital_in::is_lo() { return !is_hi(); }
|
||||||
|
|
||||||
static const struct gpio_dt_spec encoder_c1_gpio =
|
static const struct gpio_dt_spec encoder_c1_gpio =
|
||||||
GPIO_DT_SPEC_GET(DT_ALIAS(encoder_c1), gpios);
|
GPIO_DT_SPEC_GET(DT_ALIAS(encoder_c1), gpios);
|
||||||
@@ -53,22 +62,3 @@ bool Digital_in::is_lo()
|
|||||||
return !is_hi();
|
return !is_hi();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Digital_in::enable_rising_edge_interrupt(
|
|
||||||
struct gpio_callback *callback,
|
|
||||||
gpio_callback_handler_t handler)
|
|
||||||
{
|
|
||||||
if (gpio == nullptr || !gpio_is_ready_dt(gpio)) {
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpio_init_callback(callback, handler, BIT(gpio->pin));
|
|
||||||
|
|
||||||
const int ret = gpio_add_callback(gpio->port, callback);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Physical rising edge, independent of the active level in the overlay.
|
|
||||||
return gpio_pin_interrupt_configure(
|
|
||||||
gpio->port, gpio->pin, GPIO_INT_EDGE_RISING);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
#include "digital_out.h"
|
#include "digital_out.h"
|
||||||
|
|
||||||
|
//#include <avr/io.h>
|
||||||
|
|
||||||
#include <zephyr/devicetree.h>
|
#include <zephyr/devicetree.h>
|
||||||
|
|
||||||
|
// Digital_out::Digital_out(uint8_t pin) : pin_mask(1<<pin) {}
|
||||||
|
|
||||||
|
// void Digital_out::init() { DDRB |= pin_mask; }
|
||||||
|
// void Digital_out::toggle() { PORTB ^= pin_mask; }
|
||||||
|
// void Digital_out::set_hi() { PORTB |= pin_mask; }
|
||||||
|
// void Digital_out::set_lo() { PORTB &= ~pin_mask; }
|
||||||
|
|
||||||
static const struct gpio_dt_spec status_led_gpio =
|
static const struct gpio_dt_spec status_led_gpio =
|
||||||
GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
|
GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
// main.cpp er sleppt i test-build (pio test) svo setup()/loop() rekist ekki a prof
|
||||||
|
//#ifndef PIO_UNIT_TESTING
|
||||||
|
|
||||||
|
//#include <Arduino.h>
|
||||||
|
//#include <avr/interrupt.h>
|
||||||
|
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/devicetree.h>
|
#include <zephyr/devicetree.h>
|
||||||
@@ -12,11 +17,12 @@
|
|||||||
#include "motor_controller.h"
|
#include "motor_controller.h"
|
||||||
|
|
||||||
#include "initialization_state.h"
|
#include "initialization_state.h"
|
||||||
|
|
||||||
#include "pre_operational_state.h"
|
#include "pre_operational_state.h"
|
||||||
|
|
||||||
#include "operational_state.h"
|
#include "operational_state.h"
|
||||||
#include "stopped_state.h"
|
#include "stopped_state.h"
|
||||||
|
|
||||||
#include "node_application.h"
|
|
||||||
|
|
||||||
static RotaryEncoderDriver encoder;
|
static RotaryEncoderDriver encoder;
|
||||||
|
|
||||||
@@ -34,7 +40,7 @@ static PI_controller pi_controller(Kp, Ti, T);
|
|||||||
|
|
||||||
static InitializationState initialization_state;
|
static InitializationState initialization_state;
|
||||||
// Pre-operational velur á milli stýringanna með 'c'
|
// Pre-operational velur á milli stýringanna með 'c'
|
||||||
static PreOperationalState pre_operational_state;
|
static PreOperationalState pre_operational_state(p_controller, pi_controller);
|
||||||
static OperationalState operational_state;
|
static OperationalState operational_state;
|
||||||
static StoppedState stopped_state;
|
static StoppedState stopped_state;
|
||||||
|
|
||||||
@@ -42,7 +48,6 @@ static StoppedState stopped_state;
|
|||||||
static MotorController motor_controller(
|
static MotorController motor_controller(
|
||||||
encoder,
|
encoder,
|
||||||
motor,
|
motor,
|
||||||
p_controller,
|
|
||||||
pi_controller, // Sjálfgefin stýring
|
pi_controller, // Sjálfgefin stýring
|
||||||
initialization_state,
|
initialization_state,
|
||||||
pre_operational_state,
|
pre_operational_state,
|
||||||
@@ -51,34 +56,42 @@ static MotorController motor_controller(
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// int main()
|
||||||
|
// {
|
||||||
|
// init();
|
||||||
|
// Serial.begin(115200);
|
||||||
|
// cli();
|
||||||
|
|
||||||
|
// encoder.begin();
|
||||||
|
// motor.begin();
|
||||||
|
|
||||||
|
// sei();
|
||||||
|
|
||||||
|
// motor_controller.begin();
|
||||||
|
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// if (Serial.available() > 0)
|
||||||
|
// {
|
||||||
|
// char c = Serial.read();
|
||||||
|
|
||||||
|
// motor_controller.command(c);
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// motor_controller.update();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
static const struct device *const console_dev =
|
static const struct device *const console_dev =
|
||||||
DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
|
DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
|
||||||
|
|
||||||
// Received characters are queued by the UART ISR, like the RX buffer that
|
|
||||||
// Arduino's Serial had. The USART has a single receive register, so
|
|
||||||
// polling it from the main loop loses characters that arrive in a burst.
|
|
||||||
K_MSGQ_DEFINE(rx_queue, sizeof(char), 64, 1);
|
|
||||||
|
|
||||||
static void uart_rx_isr(const struct device *dev, void *user_data)
|
|
||||||
{
|
|
||||||
(void)user_data;
|
|
||||||
|
|
||||||
if (!uart_irq_update(dev)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (uart_irq_rx_ready(dev)) {
|
|
||||||
unsigned char c;
|
|
||||||
|
|
||||||
if (uart_fifo_read(dev, &c, 1) != 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop the character if the queue is full.
|
|
||||||
(void)k_msgq_put(&rx_queue, &c, K_NO_WAIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void motor_application_main(void)
|
extern "C" void motor_application_main(void)
|
||||||
{
|
{
|
||||||
if (!device_is_ready(console_dev)) {
|
if (!device_is_ready(console_dev)) {
|
||||||
@@ -86,9 +99,6 @@ extern "C" void motor_application_main(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uart_irq_callback_user_data_set(console_dev, uart_rx_isr, nullptr);
|
|
||||||
uart_irq_rx_enable(console_dev);
|
|
||||||
|
|
||||||
const int encoder_result = encoder.begin();
|
const int encoder_result = encoder.begin();
|
||||||
if (encoder_result < 0) {
|
if (encoder_result < 0) {
|
||||||
printk("Encoder setup failed: %d\n", encoder_result);
|
printk("Encoder setup failed: %d\n", encoder_result);
|
||||||
@@ -99,27 +109,17 @@ extern "C" void motor_application_main(void)
|
|||||||
motor_controller.begin();
|
motor_controller.begin();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
char received_char;
|
unsigned char received_char;
|
||||||
|
|
||||||
// Handle every character the ISR has queued since the last pass.
|
// Read a command if a character is waiting.
|
||||||
while (k_msgq_get(&rx_queue, &received_char, K_NO_WAIT) == 0) {
|
if (uart_poll_in(console_dev, &received_char) == 0) {
|
||||||
motor_controller.command(received_char);
|
motor_controller.command(
|
||||||
|
static_cast<char>(received_char));
|
||||||
}
|
}
|
||||||
|
|
||||||
motor_controller.update();
|
motor_controller.update();
|
||||||
|
|
||||||
static NodeState last_state = NodeState::INITIALIZATION;
|
// Give Zephyr's scheduler time to run other threads.
|
||||||
|
k_msleep(1);
|
||||||
const NodeState now = motor_controller.get_state();
|
|
||||||
if (now != last_state)
|
|
||||||
{
|
|
||||||
printk("get_state: %d -> %d\n", (int)last_state, (int)now);
|
|
||||||
last_state = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep until the encoder has a new speed sample (every 4 ms), so
|
|
||||||
// the controller runs right after sampling. The timeout only
|
|
||||||
// matters if the timer stops; the thread is idle while it waits.
|
|
||||||
encoder.wait_for_sample(K_MSEC(10));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,61 @@
|
|||||||
#include "initialization_state.h"
|
#include "initialization_state.h"
|
||||||
#include "motor_controller.h"
|
#include "motor_controller.h"
|
||||||
|
|
||||||
|
//#include <Arduino.h>
|
||||||
|
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/sys/printk.h>
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
|
|
||||||
void InitializationState::enter()
|
void InitializationState::enter()
|
||||||
{
|
{
|
||||||
|
// Serial.println();
|
||||||
|
// Serial.println("State: Initialization");
|
||||||
|
|
||||||
|
// context->led_off();
|
||||||
|
|
||||||
|
// context->stop_motor();
|
||||||
|
|
||||||
|
// context->reset_speed_measurement();
|
||||||
|
|
||||||
|
// enter_time_ms = millis();
|
||||||
|
|
||||||
printk("\nState: Initialization\n");
|
printk("\nState: Initialization\n");
|
||||||
|
|
||||||
context->led_off();
|
context->led_off();
|
||||||
context->stop_motor();
|
context->stop_motor();
|
||||||
context->reset_speed_measurement();
|
context->reset_speed_measurement();
|
||||||
|
|
||||||
enter_time_ms = k_uptime_get();
|
enter_time_ms = k_uptime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InitializationState::update()
|
void InitializationState::update()
|
||||||
{
|
{
|
||||||
|
// // Non-blocking bið svo aðallykkjan haldi áfram að keyra
|
||||||
|
// if (millis() - enter_time_ms >= WAIT_MS)
|
||||||
|
// {
|
||||||
|
// context->to_pre_operational();
|
||||||
|
// // context->to_operational();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
// Check the time without blocking the application.
|
// Check the time without blocking the application.
|
||||||
if ((k_uptime_get() - enter_time_ms) >= WAIT_MS) {
|
if ((k_uptime_get() - enter_time_ms) >= WAIT_MS) {
|
||||||
context->to_pre_operational();
|
context->to_pre_operational();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InitializationState::command(char c)
|
void InitializationState::command(char c)
|
||||||
{
|
{
|
||||||
(void)c;
|
(void)c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InitializationState::exit()
|
void InitializationState::exit()
|
||||||
{
|
{
|
||||||
|
//Serial.println("Boot-up");
|
||||||
printk("Boot-up\n");
|
printk("Boot-up\n");
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-18
@@ -1,20 +1,12 @@
|
|||||||
#include <zephyr/kernel.h>
|
// #include <zephyr/kernel.h>
|
||||||
|
|
||||||
void motor_application_main(void);
|
// #ifndef PIO_UNIT_TESTING
|
||||||
void can_send_test_main(void);
|
|
||||||
void can_frame_test_main(void);
|
|
||||||
void can_rx_test_main(void);
|
|
||||||
|
|
||||||
int main(void)
|
// void motor_application_main(void);
|
||||||
{
|
|
||||||
#if CAN_SEND_TEST
|
// void main(void)
|
||||||
can_send_test_main();
|
// {
|
||||||
#elif CAN_FRAME_TEST
|
// motor_application_main();
|
||||||
can_frame_test_main();
|
// }
|
||||||
#elif CAN_RX_TEST
|
|
||||||
can_rx_test_main();
|
// #endif
|
||||||
#else
|
|
||||||
motor_application_main();
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
+24
-58
@@ -1,11 +1,13 @@
|
|||||||
#include "motor_controller.h"
|
#include "motor_controller.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
|
//#include <Arduino.h>
|
||||||
|
|
||||||
|
|
||||||
MotorController::MotorController(
|
MotorController::MotorController(
|
||||||
RotaryEncoderDriver &encoder,
|
RotaryEncoderDriver &encoder,
|
||||||
MotorDriver &motor,
|
MotorDriver &motor,
|
||||||
P_controller &p_controller,
|
P_controller &controller,
|
||||||
P_controller &pi_controller,
|
|
||||||
State &initialization,
|
State &initialization,
|
||||||
State &pre_operational,
|
State &pre_operational,
|
||||||
State &operational,
|
State &operational,
|
||||||
@@ -18,14 +20,9 @@ MotorController::MotorController(
|
|||||||
stopped_state(stopped),
|
stopped_state(stopped),
|
||||||
encoder(encoder),
|
encoder(encoder),
|
||||||
motor(motor),
|
motor(motor),
|
||||||
p_controller(p_controller),
|
controller(&controller),
|
||||||
pi_controller(pi_controller),
|
|
||||||
controller(&pi_controller), // PI er sjálfgefin stýring
|
|
||||||
led(LED_PIN),
|
led(LED_PIN),
|
||||||
target_rpm(DEFAULT_TARGET_RPM),
|
reference_rpm(DEFAULT_REFERENCE_RPM),
|
||||||
reference_rpm(DEFAULT_TARGET_RPM),
|
|
||||||
profile_acceleration(0),
|
|
||||||
profile_deceleration(0),
|
|
||||||
last_actual_rpm(0.0),
|
last_actual_rpm(0.0),
|
||||||
last_pwm(0.0)
|
last_pwm(0.0)
|
||||||
{
|
{
|
||||||
@@ -36,6 +33,18 @@ MotorController::MotorController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// void MotorController::set_states(
|
||||||
|
// State* initialization,
|
||||||
|
// State* operational,
|
||||||
|
// State* stopped
|
||||||
|
// )
|
||||||
|
|
||||||
|
// initialization_state = initialization;
|
||||||
|
// operational_state = operational;
|
||||||
|
// stopped_state = stopped;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MotorController::begin()
|
void MotorController::begin()
|
||||||
{
|
{
|
||||||
led.init();
|
led.init();
|
||||||
@@ -64,6 +73,7 @@ void MotorController::update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MotorController::command(char c)
|
void MotorController::command(char c)
|
||||||
{
|
{
|
||||||
if (current_state != nullptr)
|
if (current_state != nullptr)
|
||||||
@@ -72,6 +82,7 @@ void MotorController::command(char c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MotorController::to_initialization()
|
void MotorController::to_initialization()
|
||||||
{
|
{
|
||||||
transition_to(initialization_state);
|
transition_to(initialization_state);
|
||||||
@@ -96,8 +107,6 @@ bool MotorController::run_speed_controller()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
reference_rpm = target_rpm;
|
|
||||||
|
|
||||||
const double actual = encoder.rpm();
|
const double actual = encoder.rpm();
|
||||||
|
|
||||||
const double pwm_percent =
|
const double pwm_percent =
|
||||||
@@ -141,71 +150,28 @@ void MotorController::reset_controller()
|
|||||||
controller->reset();
|
controller->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorController::reset_parameters()
|
|
||||||
{
|
|
||||||
p_controller.restore_defaults();
|
|
||||||
pi_controller.restore_defaults();
|
|
||||||
|
|
||||||
select_controller(ControllerType::PI);
|
|
||||||
set_target_rpm(DEFAULT_TARGET_RPM);
|
|
||||||
|
|
||||||
set_profile_acceleration(0);
|
|
||||||
set_profile_deceleration(0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MotorController::set_Kp(double value)
|
void MotorController::set_Kp(double value)
|
||||||
{
|
{
|
||||||
controller->set_Kp(value);
|
controller->set_Kp(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MotorController::set_Ti(double value)
|
void MotorController::set_Ti(double value)
|
||||||
{
|
{
|
||||||
return controller->set_Ti(value);
|
controller->set_Ti(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorController::select_controller(ControllerType type)
|
void MotorController::set_controller(P_controller& new_controller)
|
||||||
{
|
{
|
||||||
if (type == ControllerType::PI)
|
controller = &new_controller;
|
||||||
{
|
|
||||||
controller = &pi_controller;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
controller = &p_controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
controller->reset();
|
controller->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerType MotorController::get_controller_type() const
|
|
||||||
{
|
|
||||||
if (controller == &pi_controller)
|
|
||||||
{
|
|
||||||
return ControllerType::PI;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ControllerType::P;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MotorController::to_stopped()
|
void MotorController::to_stopped()
|
||||||
{
|
{
|
||||||
transition_to(stopped_state);
|
transition_to(stopped_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeState MotorController::get_state() const
|
|
||||||
{
|
|
||||||
if (current_state == nullptr)
|
|
||||||
{
|
|
||||||
return NodeState::INITIALIZATION;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return current_state->id();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MotorController::fault_active()
|
bool MotorController::fault_active()
|
||||||
{
|
{
|
||||||
return motor.fault_active();
|
return motor.fault_active();
|
||||||
|
|||||||
+114
-27
@@ -1,25 +1,58 @@
|
|||||||
#include "motor_driver.h"
|
#include "motor_driver.h"
|
||||||
|
|
||||||
|
//#include <avr/io.h>
|
||||||
|
|
||||||
#include <zephyr/devicetree.h>
|
#include <zephyr/devicetree.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
#include <zephyr/drivers/pwm.h>
|
#include <zephyr/drivers/pwm.h>
|
||||||
|
|
||||||
|
// Í Fast PWM gefur OCR = 0 samt einn stuttan púls í hverri lotu.
|
||||||
|
// Til að pinni sé alveg LOW er hann aftengdur frá timernum (COM-bitar)
|
||||||
|
// og keyrður LOW gegnum PORT-registerið.
|
||||||
|
|
||||||
|
// static void ain1_off() // D3 = PD3 = OC2B
|
||||||
|
// {
|
||||||
|
// TCCR2A &= ~(1 << COM2B1);
|
||||||
|
// PORTD &= ~(1 << PORTD3);
|
||||||
|
// OCR2B = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void ain2_off() // D11 = PB3 = OC2A
|
||||||
|
// {
|
||||||
|
// TCCR2A &= ~(1 << COM2A1);
|
||||||
|
// PORTB &= ~(1 << PORTB3);
|
||||||
|
// OCR2A = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void ain1_pwm(uint8_t duty)
|
||||||
|
// {
|
||||||
|
// OCR2B = duty;
|
||||||
|
// TCCR2A |= (1 << COM2B1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void ain2_pwm(uint8_t duty)
|
||||||
|
// {
|
||||||
|
// OCR2A = duty;
|
||||||
|
// TCCR2A |= (1 << COM2A1);
|
||||||
|
// }
|
||||||
|
|
||||||
static const struct pwm_dt_spec bin1_pwm =
|
static const struct pwm_dt_spec bin1_pwm =
|
||||||
PWM_DT_SPEC_GET(DT_ALIAS(motor_bin1_pwm));
|
PWM_DT_SPEC_GET(DT_ALIAS(motor_bin1_pwm));
|
||||||
|
|
||||||
static const struct pwm_dt_spec bin2_pwm =
|
static const struct gpio_dt_spec bin2 =
|
||||||
PWM_DT_SPEC_GET(DT_ALIAS(motor_bin2_pwm));
|
GPIO_DT_SPEC_GET(DT_ALIAS(motor_bin2), gpios);
|
||||||
|
|
||||||
static const struct gpio_dt_spec motor_flt =
|
static const struct gpio_dt_spec motor_flt =
|
||||||
GPIO_DT_SPEC_GET(DT_ALIAS(motor_flt), gpios);
|
GPIO_DT_SPEC_GET(DT_ALIAS(motor_flt), gpios);
|
||||||
|
|
||||||
static const struct gpio_dt_spec fault_test_button =
|
static const struct gpio_dt_spec fault_test_button =
|
||||||
GPIO_DT_SPEC_GET(DT_ALIAS(fault_test_button), gpios);
|
GPIO_DT_SPEC_GET(DT_ALIAS(fault_test_button), gpios);
|
||||||
|
|
||||||
static const struct gpio_dt_spec motor_slp =
|
static const struct gpio_dt_spec motor_slp =
|
||||||
GPIO_DT_SPEC_GET(DT_ALIAS(motor_slp), gpios);
|
GPIO_DT_SPEC_GET(DT_ALIAS(motor_slp), gpios);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MotorDriver::MotorDriver()
|
MotorDriver::MotorDriver()
|
||||||
: initialized(false)
|
: initialized(false)
|
||||||
{
|
{
|
||||||
@@ -28,17 +61,42 @@ MotorDriver::MotorDriver()
|
|||||||
|
|
||||||
void MotorDriver::begin()
|
void MotorDriver::begin()
|
||||||
{
|
{
|
||||||
|
// Arduino D3 = PD3 = AIN1
|
||||||
|
// Arduino D11 = PB3 = AIN2
|
||||||
|
// Arduino D4 = PD4 = FLT
|
||||||
|
|
||||||
|
// DDRD |= (1 << DDD3);
|
||||||
|
// DDRB |= (1 << DDB3);
|
||||||
|
|
||||||
|
// DDRD &= ~(1 << DDD4);
|
||||||
|
|
||||||
|
// PORTD |= (1 << PORTD4); // Enable pull-up resistor on D4 for part 2
|
||||||
|
|
||||||
|
|
||||||
|
// // Timer2 Fast PWM
|
||||||
|
// TCCR2A = 0;
|
||||||
|
// TCCR2B = 0;
|
||||||
|
|
||||||
|
// // Fast PWM mode
|
||||||
|
// TCCR2A |= (1 << WGM20);
|
||||||
|
// TCCR2A |= (1 << WGM21);
|
||||||
|
|
||||||
|
// // Timer2 prescaler = 32
|
||||||
|
// TCCR2B |= (1 << CS21);
|
||||||
|
// TCCR2B |= (1 << CS20);
|
||||||
|
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
|
||||||
if (!pwm_is_ready_dt(&bin1_pwm) ||
|
if (!pwm_is_ready_dt(&bin1_pwm) ||
|
||||||
!pwm_is_ready_dt(&bin2_pwm) ||
|
!gpio_is_ready_dt(&bin2) ||
|
||||||
!gpio_is_ready_dt(&motor_flt) ||
|
!gpio_is_ready_dt(&motor_flt) ||
|
||||||
!gpio_is_ready_dt(&fault_test_button) ||
|
!gpio_is_ready_dt(&fault_test_button) ||
|
||||||
!gpio_is_ready_dt(&motor_slp)) {
|
!gpio_is_ready_dt(&motor_slp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gpio_pin_configure_dt(&motor_flt, GPIO_INPUT) < 0 ||
|
if (gpio_pin_configure_dt(&bin2, GPIO_OUTPUT_INACTIVE) < 0 ||
|
||||||
|
gpio_pin_configure_dt(&motor_flt, GPIO_INPUT) < 0 ||
|
||||||
gpio_pin_configure_dt(&fault_test_button, GPIO_INPUT) < 0 ||
|
gpio_pin_configure_dt(&fault_test_button, GPIO_INPUT) < 0 ||
|
||||||
gpio_pin_configure_dt(&motor_slp, GPIO_OUTPUT_INACTIVE) < 0) {
|
gpio_pin_configure_dt(&motor_slp, GPIO_OUTPUT_INACTIVE) < 0) {
|
||||||
return;
|
return;
|
||||||
@@ -49,10 +107,10 @@ void MotorDriver::begin()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0) < 0 ||
|
if (pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0) < 0) {
|
||||||
pwm_set_dt(&bin2_pwm, bin2_pwm.period, 0) < 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// PWM-úttök eru tengd í set() eftir þörfum
|
||||||
initialized = true;
|
initialized = true;
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
@@ -72,17 +130,22 @@ void MotorDriver::reverse()
|
|||||||
|
|
||||||
void MotorDriver::stop()
|
void MotorDriver::stop()
|
||||||
{
|
{
|
||||||
|
// Both LOW
|
||||||
|
// ain1_off();
|
||||||
|
// ain2_off();
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// BIN1 low and BIN2 low puts the DRV8833 in coast mode.
|
// BIN1 low and BIN2 low puts the DRV8833 in coast mode.
|
||||||
pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0);
|
pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0);
|
||||||
pwm_set_dt(&bin2_pwm, bin2_pwm.period, 0);
|
gpio_pin_set_dt(&bin2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::set(double pwm)
|
void MotorDriver::set(double pwm)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,27 +164,50 @@ void MotorDriver::set(double pwm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const double magnitude = pwm > 0.0 ? pwm : -pwm;
|
// uint8_t duty = (uint8_t)(pwm > 0 ? pwm : -pwm);
|
||||||
|
|
||||||
// Pulse width in nanoseconds, kept as a double until the end so the
|
// if (duty == 0)
|
||||||
// 4000-count timer resolution is not lost to integer truncation.
|
// {
|
||||||
const uint32_t pulse = static_cast<uint32_t>(
|
// stop();
|
||||||
static_cast<double>(bin1_pwm.period) * magnitude / 255.0);
|
// }
|
||||||
|
// else if (pwm > 0)
|
||||||
|
// {
|
||||||
|
// // Forward
|
||||||
|
// ain2_off();
|
||||||
|
// ain1_pwm(duty);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // Reverse
|
||||||
|
// ain1_off();
|
||||||
|
// ain2_pwm(duty);
|
||||||
|
// }
|
||||||
|
|
||||||
|
const uint32_t magnitude =
|
||||||
|
static_cast<uint32_t>(pwm > 0.0 ? pwm : -pwm);
|
||||||
|
|
||||||
|
// PWM pulse width in nanoseconds. The overlay sets a 50 us period.
|
||||||
|
const uint32_t requested_pulse =
|
||||||
|
(bin1_pwm.period * magnitude) / 255U;
|
||||||
|
|
||||||
|
// First put BIN1 low so changing BIN2 does not switch direction
|
||||||
|
// while the previous PWM signal is still active.
|
||||||
|
pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0);
|
||||||
|
|
||||||
|
uint32_t bin1_pulse;
|
||||||
|
|
||||||
// Same PWM scheme as Project 3: one input PWM, the other input low,
|
|
||||||
// so both directions use fast decay (coast during the off time).
|
|
||||||
// The idle input is set low first; both channels share TIM1 with
|
|
||||||
// preloaded compare registers, so the change takes effect together
|
|
||||||
// at the next timer update.
|
|
||||||
if (pwm > 0.0) {
|
if (pwm > 0.0) {
|
||||||
// Forward: BIN1 PWM, BIN2 low.
|
// Forward: BIN2 low; BIN1 PWM duty equals the command magnitude.
|
||||||
pwm_set_dt(&bin2_pwm, bin2_pwm.period, 0);
|
gpio_pin_set_dt(&bin2, 0);
|
||||||
pwm_set_dt(&bin1_pwm, bin1_pwm.period, pulse);
|
bin1_pulse = requested_pulse;
|
||||||
} else {
|
} else {
|
||||||
// Reverse: BIN1 low, BIN2 PWM.
|
// Reverse: BIN2 high; BIN1 pulse is inverted for reverse PWM.
|
||||||
pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0);
|
gpio_pin_set_dt(&bin2, 1);
|
||||||
pwm_set_dt(&bin2_pwm, bin2_pwm.period, pulse);
|
bin1_pulse = bin1_pwm.period - requested_pulse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pwm_set_dt(&bin1_pwm, bin1_pwm.period, bin1_pulse);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -145,12 +231,13 @@ bool MotorDriver::fault_active()
|
|||||||
return true; // Fail safe if the driver did not initialize.
|
return true; // Fail safe if the driver did not initialize.
|
||||||
}
|
}
|
||||||
|
|
||||||
// The overlay marks FLT as active low. gpio_pin_get_dt() returns
|
|
||||||
// the logical active state, so 1 means a fault is active.
|
|
||||||
const int driver_fault = gpio_pin_get_dt(&motor_flt);
|
const int driver_fault = gpio_pin_get_dt(&motor_flt);
|
||||||
const int button_fault = gpio_pin_get_dt(&fault_test_button);
|
const int button_fault = gpio_pin_get_dt(&fault_test_button);
|
||||||
|
|
||||||
|
// Negative values indicate a GPIO read error, so treat them as a fault.
|
||||||
|
// With GPIO_ACTIVE_LOW, a pressed button reads as logical 1.
|
||||||
return driver_fault < 0 ||
|
return driver_fault < 0 ||
|
||||||
button_fault < 0 ||
|
button_fault < 0 ||
|
||||||
driver_fault != 0 ||
|
driver_fault != 0 ||
|
||||||
button_fault != 0;
|
button_fault != 0;
|
||||||
}
|
}
|
||||||
+26
-31
@@ -1,10 +1,14 @@
|
|||||||
#include "operational_state.h"
|
#include "operational_state.h"
|
||||||
#include "motor_controller.h"
|
#include "motor_controller.h"
|
||||||
|
|
||||||
|
//#include <Arduino.h>
|
||||||
|
|
||||||
#include <zephyr/sys/printk.h>
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
|
|
||||||
void OperationalState::enter()
|
void OperationalState::enter()
|
||||||
{
|
{
|
||||||
|
//Serial.println("State: Operational");
|
||||||
printk("State: Operational\n");
|
printk("State: Operational\n");
|
||||||
|
|
||||||
context->reset_controller();
|
context->reset_controller();
|
||||||
@@ -13,20 +17,26 @@ void OperationalState::enter()
|
|||||||
|
|
||||||
sample_count = 0;
|
sample_count = 0;
|
||||||
|
|
||||||
// Tímabundið til þess að prófa undirbúning CAN communication
|
|
||||||
printk("Kp=%.4f Ti=%.4f Target=%.0f RPM\n",
|
|
||||||
context->get_Kp(), context->get_Ti(), context->get_target_rpm());
|
|
||||||
|
|
||||||
#if LOG_CSV
|
#if LOG_CSV
|
||||||
|
//Serial.println("time_ms,ref_rpm,actual_rpm,pwm");
|
||||||
printk("time_ms,ref_rpm,actual_rpm,pwm\n");
|
printk("time_ms,ref_rpm,actual_rpm,pwm\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Part 1
|
||||||
|
|
||||||
|
//void OperationalState::update()
|
||||||
|
//{
|
||||||
|
// context->run_speed_controller();
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
|
||||||
void OperationalState::update()
|
void OperationalState::update()
|
||||||
{
|
{
|
||||||
if (context->fault_active())
|
if (context->fault_active())
|
||||||
{
|
{
|
||||||
|
//Serial.println("Fault detected");
|
||||||
printk("Fault detected\n");
|
printk("Fault detected\n");
|
||||||
|
|
||||||
context->to_stopped();
|
context->to_stopped();
|
||||||
@@ -40,6 +50,13 @@ void OperationalState::update()
|
|||||||
|
|
||||||
#if LOG_CSV
|
#if LOG_CSV
|
||||||
// 4 ms á milli sýna, tíminn byrjar á 0 við hverja innkomu í Operational
|
// 4 ms á milli sýna, tíminn byrjar á 0 við hverja innkomu í Operational
|
||||||
|
// Serial.print(sample_count * 4UL);
|
||||||
|
// Serial.print(',');
|
||||||
|
// Serial.print(context->get_reference_rpm(), 1);
|
||||||
|
// Serial.print(',');
|
||||||
|
// Serial.print(context->get_actual_rpm(), 2);
|
||||||
|
// Serial.print(',');
|
||||||
|
// Serial.println(context->get_pwm(), 1);
|
||||||
printk("%u,%.1f,%.2f,%.1f\n",
|
printk("%u,%.1f,%.2f,%.1f\n",
|
||||||
static_cast<unsigned int>(sample_count * 4U),
|
static_cast<unsigned int>(sample_count * 4U),
|
||||||
context->get_reference_rpm(),
|
context->get_reference_rpm(),
|
||||||
@@ -54,47 +71,25 @@ void OperationalState::command(char c)
|
|||||||
{
|
{
|
||||||
if (c == 'r' || c == 'R')
|
if (c == 'r' || c == 'R')
|
||||||
{
|
{
|
||||||
printk("Command: reset communication\n"); // Heldur Kp og Ti gildum
|
//Serial.println("Command: reset");
|
||||||
|
printk("Command: reset\n");
|
||||||
|
|
||||||
context->to_initialization();
|
context->to_initialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (c == 'p' || c == 'P')
|
else if (c == 'p' || c == 'P')
|
||||||
{
|
{
|
||||||
|
//Serial.println("Command: set pre-operational");
|
||||||
printk("Command: set pre-operational\n");
|
printk("Command: set pre-operational\n");
|
||||||
|
|
||||||
context->to_pre_operational();
|
context->to_pre_operational();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (c == '+') // Bætti þessu við til þess að prófa að rampa rpm í operational state
|
|
||||||
{
|
|
||||||
context->set_target_rpm(context->get_target_rpm() + 10);
|
|
||||||
printk("Target: %.0f RPM\n", context->get_target_rpm());
|
|
||||||
}
|
|
||||||
else if (c == '-')
|
|
||||||
{
|
|
||||||
context->set_target_rpm(context->get_target_rpm() - 10);
|
|
||||||
printk("Target: %.0f RPM\n", context->get_target_rpm());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tímabundið til þess að testa reset parameters
|
|
||||||
else if (c == 'x' || c == 'X')
|
|
||||||
{
|
|
||||||
printk("Command: reset node\n");
|
|
||||||
context->reset_parameters();
|
|
||||||
context->to_initialization();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (c == '?')
|
|
||||||
{
|
|
||||||
printk("Actual=%.0f RPM Duty=%.1f\n",
|
|
||||||
context->get_actual_rpm(), context->get_duty_percent());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OperationalState::exit()
|
void OperationalState::exit()
|
||||||
{
|
{
|
||||||
context->stop_motor();
|
context->stop_motor();
|
||||||
|
|
||||||
context->led_off();
|
context->led_off();
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
#include "p_controller.h"
|
#include "p_controller.h"
|
||||||
|
|
||||||
P_controller::P_controller(double Kp)
|
P_controller::P_controller(double Kp)
|
||||||
: Kp(Kp),
|
: Kp(Kp)
|
||||||
default_Kp(Kp)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,10 +10,4 @@ double P_controller::update(double ref, double actual)
|
|||||||
double error = ref - actual;
|
double error = ref - actual;
|
||||||
|
|
||||||
return Kp * error;
|
return Kp * error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void P_controller::restore_defaults()
|
|
||||||
{
|
|
||||||
Kp = default_Kp;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
+4
-17
@@ -1,6 +1,6 @@
|
|||||||
#include "pi_controller.h"
|
#include "pi_controller.h"
|
||||||
|
|
||||||
PI_controller::PI_controller(double Kp, double Ti, double T) : P_controller(Kp), Ti(Ti), default_Ti(Ti), T(T), integral(0.0)
|
PI_controller::PI_controller(double Kp, double Ti, double T) : P_controller(Kp), Ti(Ti), T(T), integral(0.0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ double PI_controller::update(double ref, double actual)
|
|||||||
integral = new_integral;
|
integral = new_integral;
|
||||||
}
|
}
|
||||||
return u;
|
return u;
|
||||||
|
// return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PI_controller::reset()
|
void PI_controller::reset()
|
||||||
@@ -30,21 +31,7 @@ void PI_controller::reset()
|
|||||||
integral = 0.0;
|
integral = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PI_controller::set_Ti(double value)
|
void PI_controller::set_Ti(double value)
|
||||||
{
|
{
|
||||||
if (value > 0.0)
|
Ti = value;
|
||||||
{
|
|
||||||
Ti = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PI_controller::restore_defaults()
|
|
||||||
{
|
|
||||||
P_controller::restore_defaults();
|
|
||||||
Ti = default_Ti;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
#include "pre_operational_state.h"
|
#include "pre_operational_state.h"
|
||||||
#include "motor_controller.h"
|
#include "motor_controller.h"
|
||||||
|
|
||||||
// #include "p_controller.h"
|
#include "p_controller.h"
|
||||||
|
|
||||||
|
//#include <Arduino.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/sys/printk.h>
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
// PreOperationalState::PreOperationalState(P_controller &p_controller, P_controller &pi_controller)
|
|
||||||
PreOperationalState::PreOperationalState()
|
|
||||||
// : p_controller(p_controller),
|
PreOperationalState::PreOperationalState(P_controller &p_controller, P_controller &pi_controller)
|
||||||
// pi_controller(pi_controller),
|
: p_controller(p_controller),
|
||||||
// pi_selected(true),
|
pi_controller(pi_controller),
|
||||||
: previous_time(0),
|
pi_selected(true),
|
||||||
|
previous_time(0),
|
||||||
led_state(false),
|
led_state(false),
|
||||||
|
//waiting_for_kp(false),
|
||||||
|
//waiting_for_ti(false),
|
||||||
input_mode(NONE),
|
input_mode(NONE),
|
||||||
input_index(0)
|
input_index(0)
|
||||||
{
|
{
|
||||||
@@ -21,23 +26,22 @@ PreOperationalState::PreOperationalState()
|
|||||||
|
|
||||||
void PreOperationalState::enter()
|
void PreOperationalState::enter()
|
||||||
{
|
{
|
||||||
|
//Serial.println("State: Pre-operational");
|
||||||
printk("State: Pre-operational\n");
|
printk("State: Pre-operational\n");
|
||||||
|
|
||||||
// The selection survives a reset, so report it (used by capture_serial.py).
|
|
||||||
printk("Controller: %s\n", (context->get_controller_type() == ControllerType::PI) ? "PI" : "P");
|
|
||||||
// Prófaði þetta, veit ekki hvort þetta skili bool
|
|
||||||
|
|
||||||
context->stop_motor();
|
context->stop_motor();
|
||||||
|
|
||||||
context->led_off();
|
context->led_off();
|
||||||
|
|
||||||
led_state = false;
|
led_state = false;
|
||||||
|
|
||||||
|
//previous_time = millis();
|
||||||
previous_time = k_uptime_get();
|
previous_time = k_uptime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreOperationalState::update()
|
void PreOperationalState::update()
|
||||||
{
|
{
|
||||||
|
//unsigned long current_time = millis();
|
||||||
const int64_t current_time = k_uptime_get();
|
const int64_t current_time = k_uptime_get();
|
||||||
|
|
||||||
if (current_time - previous_time >= 500)
|
if (current_time - previous_time >= 500)
|
||||||
@@ -77,24 +81,17 @@ void PreOperationalState::command(char c)
|
|||||||
{
|
{
|
||||||
context->set_Kp(value);
|
context->set_Kp(value);
|
||||||
|
|
||||||
printk("Kp set to: %.4f\n", value);
|
// Serial.print("Kp set to: ");
|
||||||
|
// Serial.println(value);
|
||||||
|
printk("Kp updated\n");
|
||||||
}
|
}
|
||||||
else if (input_mode == ENTER_TI)
|
else if (input_mode == ENTER_TI)
|
||||||
{
|
{
|
||||||
if (context->set_Ti(value))
|
context->set_Ti(value);
|
||||||
{
|
|
||||||
printk("Ti set to: %.4f\n", value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printk("Ti rejected, must be > 0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (input_mode == ENTER_RPM)
|
|
||||||
{
|
|
||||||
context->set_target_rpm(value);
|
|
||||||
|
|
||||||
printk("Target RPM set to: %.0f\n", value);
|
// Serial.print("Ti set to: ");
|
||||||
|
// Serial.println(value);
|
||||||
|
printk("Ti updated\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +110,7 @@ void PreOperationalState::command(char c)
|
|||||||
if (input_index < sizeof(input_buffer) - 1)
|
if (input_index < sizeof(input_buffer) - 1)
|
||||||
{
|
{
|
||||||
input_buffer[input_index++] = c;
|
input_buffer[input_index++] = c;
|
||||||
|
//input_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +121,7 @@ void PreOperationalState::command(char c)
|
|||||||
// Normal state commands
|
// Normal state commands
|
||||||
if (c == 'o' || c == 'O')
|
if (c == 'o' || c == 'O')
|
||||||
{
|
{
|
||||||
|
//Serial.println("Command: set operational");
|
||||||
printk("Command: set operational\n");
|
printk("Command: set operational\n");
|
||||||
|
|
||||||
context->to_operational();
|
context->to_operational();
|
||||||
@@ -130,6 +129,7 @@ void PreOperationalState::command(char c)
|
|||||||
|
|
||||||
else if (c == 'r' || c == 'R')
|
else if (c == 'r' || c == 'R')
|
||||||
{
|
{
|
||||||
|
//Serial.println("Command: reset");
|
||||||
printk("Command: reset\n");
|
printk("Command: reset\n");
|
||||||
|
|
||||||
context->to_initialization();
|
context->to_initialization();
|
||||||
@@ -140,6 +140,7 @@ void PreOperationalState::command(char c)
|
|||||||
input_mode = ENTER_KP;
|
input_mode = ENTER_KP;
|
||||||
input_index = 0;
|
input_index = 0;
|
||||||
|
|
||||||
|
//Serial.println("Enter Kp and press Enter:");
|
||||||
printk("Enter Kp and press Enter:\n");
|
printk("Enter Kp and press Enter:\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,31 +149,26 @@ void PreOperationalState::command(char c)
|
|||||||
input_mode = ENTER_TI;
|
input_mode = ENTER_TI;
|
||||||
input_index = 0;
|
input_index = 0;
|
||||||
|
|
||||||
|
//Serial.println("Enter Ti and press Enter:");
|
||||||
printk("Enter Ti and press Enter:\n");
|
printk("Enter Ti and press Enter:\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (c == 'v' || c == 'V')
|
|
||||||
{
|
|
||||||
input_mode = ENTER_RPM;
|
|
||||||
input_index = 0;
|
|
||||||
|
|
||||||
printk("Enter RPM and press Enter:\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select controller algorithm, the controller is passed to the motor controller
|
// Select controller algorithm, the controller is passed to the motor controller
|
||||||
else if (c == 'c' || c == 'C')
|
else if (c == 'c' || c == 'C')
|
||||||
{
|
{
|
||||||
if (context->get_controller_type() == ControllerType::P)
|
pi_selected = !pi_selected;
|
||||||
|
|
||||||
|
if (pi_selected)
|
||||||
{
|
{
|
||||||
context->select_controller(ControllerType::PI);
|
context->set_controller(pi_controller);
|
||||||
printk("Controller: PI, Kp=%.4f, Ti=%.4f\n",
|
//Serial.println("Controller: PI");
|
||||||
context->get_Kp(), context->get_Ti());
|
printk("Controller: PI\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context->select_controller(ControllerType::P);
|
context->set_controller(p_controller);
|
||||||
printk("Controller: P, Kp=%.4f\n",
|
//Serial.println("Controller: P");
|
||||||
context->get_Kp());
|
printk("Controller: P\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,4 +176,4 @@ void PreOperationalState::command(char c)
|
|||||||
void PreOperationalState::exit()
|
void PreOperationalState::exit()
|
||||||
{
|
{
|
||||||
context->led_off();
|
context->led_off();
|
||||||
}
|
}
|
||||||
@@ -6,13 +6,18 @@
|
|||||||
#include <zephyr/devicetree.h>
|
#include <zephyr/devicetree.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
|
||||||
|
|
||||||
|
static const struct gpio_dt_spec encoder_c1 =
|
||||||
|
GPIO_DT_SPEC_GET(DT_ALIAS(encoder_c1), gpios);
|
||||||
|
|
||||||
|
static const struct gpio_dt_spec encoder_c2 =
|
||||||
|
GPIO_DT_SPEC_GET(DT_ALIAS(encoder_c2), gpios);
|
||||||
|
|
||||||
RotaryEncoderDriver::RotaryEncoderDriver()
|
RotaryEncoderDriver::RotaryEncoderDriver()
|
||||||
: count(0),
|
: count(0),
|
||||||
delta(0),
|
delta(0),
|
||||||
speed_ready(false),
|
speed_ready(false),
|
||||||
hist_idx(0),
|
hist_idx(0)
|
||||||
eA(Digital_in::ENCODER_C1),
|
|
||||||
eB(Digital_in::ENCODER_C2)
|
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < HIST_LEN; ++i) {
|
for (uint8_t i = 0; i < HIST_LEN; ++i) {
|
||||||
history[i] = 0;
|
history[i] = 0;
|
||||||
@@ -21,16 +26,35 @@ RotaryEncoderDriver::RotaryEncoderDriver()
|
|||||||
|
|
||||||
int RotaryEncoderDriver::begin()
|
int RotaryEncoderDriver::begin()
|
||||||
{
|
{
|
||||||
eA.init();
|
if (!gpio_is_ready_dt(&encoder_c1) ||
|
||||||
eB.init();
|
!gpio_is_ready_dt(&encoder_c2)) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
// Max count 1: the main loop only needs to know that a new sample
|
int ret = gpio_pin_configure_dt(&encoder_c1, GPIO_INPUT);
|
||||||
// exists, not how many it missed.
|
if (ret < 0) {
|
||||||
k_sem_init(&sample_sem, 0, 1);
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
const int ret = eA.enable_rising_edge_interrupt(
|
ret = gpio_pin_configure_dt(&encoder_c2, GPIO_INPUT);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gpio_init_callback(
|
||||||
&encoder_c1_callback,
|
&encoder_c1_callback,
|
||||||
gpio_edge_handler);
|
gpio_edge_handler,
|
||||||
|
BIT(encoder_c1.pin));
|
||||||
|
|
||||||
|
ret = gpio_add_callback(encoder_c1.port, &encoder_c1_callback);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_pin_interrupt_configure_dt(
|
||||||
|
&encoder_c1,
|
||||||
|
GPIO_INT_EDGE_TO_ACTIVE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -47,16 +71,17 @@ void RotaryEncoderDriver::gpio_edge_handler(
|
|||||||
struct gpio_callback *callback,
|
struct gpio_callback *callback,
|
||||||
uint32_t pins)
|
uint32_t pins)
|
||||||
{
|
{
|
||||||
// Zephyr only calls this handler for C1, the one pin in the callback.
|
(void)dev; // This handler does not need the device pointer.
|
||||||
(void)dev;
|
|
||||||
(void)pins;
|
|
||||||
|
|
||||||
auto *encoder = CONTAINER_OF(
|
auto *encoder = CONTAINER_OF(
|
||||||
callback,
|
callback,
|
||||||
RotaryEncoderDriver,
|
RotaryEncoderDriver,
|
||||||
encoder_c1_callback);
|
encoder_c1_callback);
|
||||||
|
|
||||||
encoder->update();
|
if ((pins & BIT(encoder_c1.pin)) != 0U) {
|
||||||
|
encoder->update(); // Reads C2 and updates the count
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RotaryEncoderDriver::timer_expired(void *context)
|
void RotaryEncoderDriver::timer_expired(void *context)
|
||||||
@@ -68,10 +93,13 @@ void RotaryEncoderDriver::timer_expired(void *context)
|
|||||||
void RotaryEncoderDriver::update()
|
void RotaryEncoderDriver::update()
|
||||||
{
|
{
|
||||||
// C2's level at the C1 edge tells us the direction.
|
// C2's level at the C1 edge tells us the direction.
|
||||||
const bool forward = eB.is_hi();
|
const int direction = gpio_pin_get_dt(&encoder_c2);
|
||||||
|
if (direction < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned int key = irq_lock();
|
const unsigned int key = irq_lock();
|
||||||
count += forward ? 1 : -1;
|
count += direction ? 1 : -1;
|
||||||
irq_unlock(key);
|
irq_unlock(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,14 +126,6 @@ void RotaryEncoderDriver::update_delta()
|
|||||||
speed_ready = true;
|
speed_ready = true;
|
||||||
|
|
||||||
irq_unlock(key);
|
irq_unlock(key);
|
||||||
|
|
||||||
// Wake the main loop so the controller runs right after the sample.
|
|
||||||
k_sem_give(&sample_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RotaryEncoderDriver::wait_for_sample(k_timeout_t timeout)
|
|
||||||
{
|
|
||||||
(void)k_sem_take(&sample_sem, timeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float RotaryEncoderDriver::count_per_sec()
|
float RotaryEncoderDriver::count_per_sec()
|
||||||
@@ -152,4 +172,4 @@ void RotaryEncoderDriver::reset_speed_measurement()
|
|||||||
hist_idx = 0;
|
hist_idx = 0;
|
||||||
|
|
||||||
irq_unlock(key);
|
irq_unlock(key);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
// #include "rotary_encoder_driver.h"
|
||||||
|
// #include <avr/interrupt.h>
|
||||||
|
|
||||||
|
// static RotaryEncoderDriver* driver = nullptr;
|
||||||
|
|
||||||
|
// RotaryEncoderDriver::RotaryEncoderDriver()
|
||||||
|
// : count(0)
|
||||||
|
// , delta(0)
|
||||||
|
// , speed_ready(false)
|
||||||
|
// , hist_idx(0)
|
||||||
|
// , eA(2)
|
||||||
|
// , eB(5)
|
||||||
|
// {
|
||||||
|
// for (uint8_t i = 0; i < HIST_LEN; i++)
|
||||||
|
// {
|
||||||
|
// history[i] = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// void RotaryEncoderDriver::begin()
|
||||||
|
// {
|
||||||
|
// driver = this;
|
||||||
|
|
||||||
|
// eA.init();
|
||||||
|
// eB.init();
|
||||||
|
|
||||||
|
// EICRA |= (1<<ISC01) | (1<<ISC00);
|
||||||
|
// EIMSK |= (1<<INT0);
|
||||||
|
|
||||||
|
// timer.init(SAMPLE_PERIOD_US);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void RotaryEncoderDriver::update()
|
||||||
|
// {
|
||||||
|
// count += eB.is_hi() ? 1 : -1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// int32_t RotaryEncoderDriver::position()
|
||||||
|
// {
|
||||||
|
|
||||||
|
// uint8_t oldSREG = SREG;
|
||||||
|
|
||||||
|
// cli();
|
||||||
|
|
||||||
|
// int32_t local_count = count;
|
||||||
|
|
||||||
|
// SREG = oldSREG;
|
||||||
|
|
||||||
|
// return local_count;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void RotaryEncoderDriver::update_delta()
|
||||||
|
// {
|
||||||
|
|
||||||
|
// int32_t oldest = history[hist_idx];
|
||||||
|
|
||||||
|
// history[hist_idx] = count;
|
||||||
|
|
||||||
|
// hist_idx = (hist_idx + 1) % HIST_LEN;
|
||||||
|
|
||||||
|
// delta = (int16_t)(count - oldest); // breyting yfir HIST_LEN * Ts (16 ms) glugga
|
||||||
|
|
||||||
|
// speed_ready = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// float RotaryEncoderDriver::count_per_sec()
|
||||||
|
// {
|
||||||
|
|
||||||
|
// uint8_t oldSREG = SREG;
|
||||||
|
|
||||||
|
// cli();
|
||||||
|
|
||||||
|
// int16_t local_delta = delta;
|
||||||
|
|
||||||
|
// SREG = oldSREG;
|
||||||
|
|
||||||
|
// return local_delta / (HIST_LEN * SAMPLE_PERIOD_US * 1e-6f);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// float RotaryEncoderDriver::rpm()
|
||||||
|
// {
|
||||||
|
// return count_per_sec() * 60.0f / COUNTS_PER_REV;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// bool RotaryEncoderDriver::new_speed_available()
|
||||||
|
// {
|
||||||
|
// uint8_t oldSREG = SREG;
|
||||||
|
|
||||||
|
// cli();
|
||||||
|
|
||||||
|
// bool ready = speed_ready;
|
||||||
|
|
||||||
|
// if (ready)
|
||||||
|
// {
|
||||||
|
// speed_ready = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// SREG = oldSREG;
|
||||||
|
|
||||||
|
// return ready;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// void RotaryEncoderDriver::reset_speed_measurement()
|
||||||
|
// {
|
||||||
|
// uint8_t oldSREG = SREG;
|
||||||
|
|
||||||
|
// cli();
|
||||||
|
|
||||||
|
// delta = 0;
|
||||||
|
// speed_ready = false;
|
||||||
|
|
||||||
|
// for (uint8_t i = 0; i < HIST_LEN; i++)
|
||||||
|
// {
|
||||||
|
// history[i] = count;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// hist_idx = 0;
|
||||||
|
|
||||||
|
// SREG = oldSREG;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ISR(INT0_vect)
|
||||||
|
// {
|
||||||
|
// driver->update();
|
||||||
|
// }
|
||||||
|
// ISR(TIMER1_COMPA_vect)
|
||||||
|
// {
|
||||||
|
// driver->update_delta();
|
||||||
|
// }
|
||||||
+13
-1
@@ -1,9 +1,12 @@
|
|||||||
#include "stopped_state.h"
|
#include "stopped_state.h"
|
||||||
#include "motor_controller.h"
|
#include "motor_controller.h"
|
||||||
|
|
||||||
|
//#include <Arduino.h>
|
||||||
|
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/sys/printk.h>
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
|
|
||||||
StoppedState::StoppedState()
|
StoppedState::StoppedState()
|
||||||
: previous_time(0),
|
: previous_time(0),
|
||||||
led_state(false)
|
led_state(false)
|
||||||
@@ -13,6 +16,8 @@ StoppedState::StoppedState()
|
|||||||
|
|
||||||
void StoppedState::enter()
|
void StoppedState::enter()
|
||||||
{
|
{
|
||||||
|
//Serial.println("State: Stopped");
|
||||||
|
|
||||||
printk("State: Stopped\n");
|
printk("State: Stopped\n");
|
||||||
|
|
||||||
context->stop_motor();
|
context->stop_motor();
|
||||||
@@ -21,6 +26,7 @@ void StoppedState::enter()
|
|||||||
|
|
||||||
context->led_off();
|
context->led_off();
|
||||||
|
|
||||||
|
//previous_time = millis();
|
||||||
previous_time = k_uptime_get();
|
previous_time = k_uptime_get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +34,7 @@ void StoppedState::enter()
|
|||||||
|
|
||||||
void StoppedState::update()
|
void StoppedState::update()
|
||||||
{
|
{
|
||||||
|
//unsigned long current_time = millis();
|
||||||
const int64_t current_time = k_uptime_get();
|
const int64_t current_time = k_uptime_get();
|
||||||
|
|
||||||
if (current_time - previous_time >= 250)
|
if (current_time - previous_time >= 250)
|
||||||
@@ -49,10 +56,12 @@ void StoppedState::update()
|
|||||||
|
|
||||||
|
|
||||||
// Commands for Stopped [ r = reset and o for operational]
|
// Commands for Stopped [ r = reset and o for operational]
|
||||||
|
|
||||||
void StoppedState::command(char c)
|
void StoppedState::command(char c)
|
||||||
{
|
{
|
||||||
if (c == 'r' || c == 'R')
|
if (c == 'r' || c == 'R')
|
||||||
{
|
{
|
||||||
|
//Serial.println("Command: reset");
|
||||||
printk("Command: reset\n");
|
printk("Command: reset\n");
|
||||||
|
|
||||||
context->to_initialization();
|
context->to_initialization();
|
||||||
@@ -60,6 +69,7 @@ void StoppedState::command(char c)
|
|||||||
|
|
||||||
else if (c == 'o' || c == 'O')
|
else if (c == 'o' || c == 'O')
|
||||||
{
|
{
|
||||||
|
//Serial.println("Command: set operational");
|
||||||
printk("Command: set operational\n");
|
printk("Command: set operational\n");
|
||||||
|
|
||||||
context->to_operational();
|
context->to_operational();
|
||||||
@@ -67,13 +77,15 @@ void StoppedState::command(char c)
|
|||||||
|
|
||||||
else if (c == 'p' || c == 'P')
|
else if (c == 'p' || c == 'P')
|
||||||
{
|
{
|
||||||
|
//Serial.println("Command: set pre-operational");
|
||||||
printk("Command: set pre-operational\n");
|
printk("Command: set pre-operational\n");
|
||||||
|
|
||||||
context->to_pre_operational();
|
context->to_pre_operational();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StoppedState::exit()
|
void StoppedState::exit()
|
||||||
{
|
{
|
||||||
context->led_off();
|
context->led_off();
|
||||||
}
|
}
|
||||||
+15
-2
@@ -1,9 +1,11 @@
|
|||||||
#include "timer_usec.h"
|
#include "timer_usec.h"
|
||||||
|
|
||||||
Timer_usec::Timer_usec()
|
Timer_usec::Timer_usec() //= default;
|
||||||
: callback(nullptr),
|
: callback(nullptr),
|
||||||
callback_context(nullptr)
|
callback_context(nullptr)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer_usec::set_callback(
|
void Timer_usec::set_callback(
|
||||||
@@ -15,6 +17,17 @@ void Timer_usec::set_callback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// void Timer_usec::init(uint16_t period_us) { // for custom duty cycle duration in ms in part 1
|
||||||
|
// TCCR1A = 0;
|
||||||
|
// TCCR1B = 0;
|
||||||
|
// TCNT1 = 0;
|
||||||
|
|
||||||
|
// OCR1A = period_us * 2 - 1;
|
||||||
|
// TCCR1B |= (1<<WGM12);
|
||||||
|
// TIMSK1 |= (1<<OCIE1A);
|
||||||
|
// TCCR1B |= (1<<CS11);
|
||||||
|
// }
|
||||||
|
|
||||||
void Timer_usec::init(uint16_t period_us)
|
void Timer_usec::init(uint16_t period_us)
|
||||||
{
|
{
|
||||||
if (period_us == 0) {
|
if (period_us == 0) {
|
||||||
@@ -36,4 +49,4 @@ void Timer_usec::expiry_handler(struct k_timer *expired_timer)
|
|||||||
if (self != nullptr && self->callback != nullptr) {
|
if (self != nullptr && self->callback != nullptr) {
|
||||||
self->callback(self->callback_context);
|
self->callback(self->callback_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
#include <unity.h>
|
||||||
|
|
||||||
|
// #ifdef ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
#include "p_controller.h"
|
||||||
|
#include "pi_controller.h"
|
||||||
|
|
||||||
|
static const double KP = 2.0;
|
||||||
|
static const double TI = 1.0;
|
||||||
|
static const double T = 0.1;
|
||||||
|
static const double TOL = 1e-4; // double er 32-bita float a AVR
|
||||||
|
|
||||||
|
// void setUp(void) {}
|
||||||
|
// void tearDown(void) {}
|
||||||
|
|
||||||
|
// Normal flow: engin villa -> ekkert úttak
|
||||||
|
void test_zero_error_gives_zero_output(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, 0.0, c.update(10.0, 10.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal flow: fyrsta skref u = Kp * (e + e*T/Ti)
|
||||||
|
void test_first_step(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
double e = 5.0;
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, KP * (e + e * T / TI), c.update(e, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal flow: stöðug villa safnast upp, u_n = Kp * (e + n*e*T/Ti)
|
||||||
|
void test_integral_accumulates(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
double e = 5.0;
|
||||||
|
double u = 0.0;
|
||||||
|
for (int n = 1; n <= 5; n++)
|
||||||
|
{
|
||||||
|
u = c.update(e, 0.0);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, KP * (e + n * e * T / TI), u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal flow: þegar villan er orðin 0 heldur integralið úttakinu uppi
|
||||||
|
void test_output_held_when_error_zero(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
double e = 5.0;
|
||||||
|
for (int n = 0; n < 5; n++)
|
||||||
|
{
|
||||||
|
c.update(e, 0.0);
|
||||||
|
}
|
||||||
|
double E = 5 * e * T;
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, KP * E / TI, c.update(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt flow: villa langt utan getu -> mettun í +100 %
|
||||||
|
void test_saturates_high(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, 100.0, c.update(10000.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt flow: mettun í -100 %
|
||||||
|
void test_saturates_low(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, -100.0, c.update(-10000.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt flow: anti-windup. Eftir langa mettun á lítil neikvæð villa
|
||||||
|
// strax að gefa úttak undir mettun (integralið óx ekki)
|
||||||
|
void test_no_integral_windup(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
for (int n = 0; n < 1000; n++)
|
||||||
|
{
|
||||||
|
c.update(10000.0, 0.0);
|
||||||
|
}
|
||||||
|
double u = c.update(0.0, 1.0); // e = -1
|
||||||
|
TEST_ASSERT_TRUE(u < 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt flow: reset (stopped -> operational) hreinsar integralið
|
||||||
|
void test_reset_clears_integral(void)
|
||||||
|
{
|
||||||
|
PI_controller c(KP, TI, T);
|
||||||
|
for (int n = 0; n < 5; n++)
|
||||||
|
{
|
||||||
|
c.update(5.0, 0.0);
|
||||||
|
}
|
||||||
|
c.reset();
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, 0.0, c.update(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Polymorphism: kall í gegnum P_controller& gefur PI hegðun
|
||||||
|
void test_polymorphic_update(void)
|
||||||
|
{
|
||||||
|
PI_controller pi(KP, TI, T);
|
||||||
|
P_controller& c = pi;
|
||||||
|
double e = 5.0;
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, KP * (e + e * T / TI), c.update(e, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// P_controller hegðar sér óbreytt
|
||||||
|
void test_p_controller_unchanged(void)
|
||||||
|
{
|
||||||
|
P_controller c(KP);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(TOL, KP * 5.0, c.update(5.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// init();
|
||||||
|
delay(2000);
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_zero_error_gives_zero_output);
|
||||||
|
RUN_TEST(test_first_step);
|
||||||
|
RUN_TEST(test_integral_accumulates);
|
||||||
|
RUN_TEST(test_output_held_when_error_zero);
|
||||||
|
RUN_TEST(test_saturates_high);
|
||||||
|
RUN_TEST(test_saturates_low);
|
||||||
|
RUN_TEST(test_no_integral_windup);
|
||||||
|
RUN_TEST(test_reset_clears_integral);
|
||||||
|
RUN_TEST(test_polymorphic_update);
|
||||||
|
RUN_TEST(test_p_controller_unchanged);
|
||||||
|
// return UNITY_END();
|
||||||
|
UNITY_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {}
|
||||||
|
|
||||||
|
// #ifdef ARDUINO
|
||||||
|
// void setup()
|
||||||
|
// {
|
||||||
|
// delay(2000); // bida eftir serial tengingu eftir reset
|
||||||
|
// run_tests();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void loop()
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// #else
|
||||||
|
// int main(int argc, char** argv)
|
||||||
|
// {
|
||||||
|
// return run_tests();
|
||||||
|
// }
|
||||||
|
// #endif
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <zephyr/dt-bindings/gpio/gpio.h>
|
#include <zephyr/dt-bindings/gpio/gpio.h>
|
||||||
#include <zephyr/dt-bindings/pwm/pwm.h>
|
#include <zephyr/dt-bindings/pwm/pwm.h>
|
||||||
#include <zephyr/dt-bindings/pwm/stm32_pwm.h>
|
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
chosen {
|
chosen {
|
||||||
@@ -12,7 +11,7 @@
|
|||||||
encoder-c2 = &encoder_c2;
|
encoder-c2 = &encoder_c2;
|
||||||
|
|
||||||
motor-bin1-pwm = &motor_bin1_pwm;
|
motor-bin1-pwm = &motor_bin1_pwm;
|
||||||
motor-bin2-pwm = &motor_bin2_pwm;
|
motor-bin2 = &motor_bin2;
|
||||||
motor-flt = &motor_flt;
|
motor-flt = &motor_flt;
|
||||||
motor-slp = &motor_slp;
|
motor-slp = &motor_slp;
|
||||||
fault-test-button = &fault_test_button;
|
fault-test-button = &fault_test_button;
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
gpios = <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* A0, PA0 */
|
gpios = <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* A0, PA0 */
|
||||||
label = "Motor fault";
|
label = "Motor fault";
|
||||||
};
|
};
|
||||||
|
|
||||||
fault_test_button: fault_test_button {
|
fault_test_button: fault_test_button {
|
||||||
gpios = <&gpiob 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* D3, PB0 */
|
gpios = <&gpiob 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; /* D3, PB0 */
|
||||||
label = "Fault test button";
|
label = "Fault test button";
|
||||||
@@ -45,6 +44,11 @@
|
|||||||
motor_gpio_outputs {
|
motor_gpio_outputs {
|
||||||
compatible = "gpio-leds";
|
compatible = "gpio-leds";
|
||||||
|
|
||||||
|
motor_bin2: motor_bin2 {
|
||||||
|
gpios = <&gpiob 1 GPIO_ACTIVE_HIGH>; /* D6, PB1 */
|
||||||
|
label = "Motor BIN2";
|
||||||
|
};
|
||||||
|
|
||||||
motor_slp: motor_slp {
|
motor_slp: motor_slp {
|
||||||
gpios = <&gpioa 1 GPIO_ACTIVE_HIGH>; /* A1, PA1 */
|
gpios = <&gpioa 1 GPIO_ACTIVE_HIGH>; /* A1, PA1 */
|
||||||
label = "Motor sleep";
|
label = "Motor sleep";
|
||||||
@@ -58,33 +62,22 @@
|
|||||||
pwms = <&pwm1 1 PWM_USEC(50) 0>; /* D9, PA8; 20 kHz */
|
pwms = <&pwm1 1 PWM_USEC(50) 0>; /* D9, PA8; 20 kHz */
|
||||||
label = "Motor BIN1 PWM";
|
label = "Motor BIN1 PWM";
|
||||||
};
|
};
|
||||||
|
|
||||||
/* PB1 has no regular TIM1 channel, only the complementary output
|
|
||||||
* TIM1_CH3N. With CH3 itself disabled, CH3N outputs the
|
|
||||||
* non-inverted CH3 reference, so it behaves like a normal channel. */
|
|
||||||
motor_bin2_pwm: motor_bin2_pwm {
|
|
||||||
pwms = <&pwm1 3 PWM_USEC(50) (PWM_POLARITY_NORMAL | STM32_PWM_COMPLEMENTARY)>; /* D6, PB1; 20 kHz */
|
|
||||||
label = "Motor BIN2 PWM";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
&timers1 {
|
&timers1 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
/* No prescaler: 80 MHz timer clock, so a 50 us period is 4000 counts. */
|
st,prescaler = <79>;
|
||||||
st,prescaler = <0>;
|
|
||||||
|
|
||||||
pwm1: pwm {
|
pwm1: pwm {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
pinctrl-0 = <&tim1_ch1_pa8 &tim1_ch3n_pb1>;
|
pinctrl-0 = <&tim1_ch1_pa8>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/* CAN1 to the transceiver: RX on D10 (PA11), TX on D2 (PA12).
|
|
||||||
* The bitrate is set in the code with can_set_bitrate(). */
|
|
||||||
&can1 {
|
&can1 {
|
||||||
pinctrl-0 = <&can1_rx_pa11 &can1_tx_pa12>;
|
pinctrl-0 = <&can1_rx_pa11 &can1_tx_pa12>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
+2
-2
@@ -6,7 +6,7 @@ CONFIG_PWM=y
|
|||||||
CONFIG_SERIAL=y
|
CONFIG_SERIAL=y
|
||||||
CONFIG_CONSOLE=y
|
CONFIG_CONSOLE=y
|
||||||
CONFIG_UART_CONSOLE=y
|
CONFIG_UART_CONSOLE=y
|
||||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
|
||||||
CONFIG_PRINTK=y
|
CONFIG_PRINTK=y
|
||||||
CONFIG_CBPRINTF_FP_SUPPORT=y
|
CONFIG_CBPRINTF_FP_SUPPORT=y
|
||||||
CONFIG_CAN=y
|
|
||||||
|
CONFIG_CAN=y
|
||||||
Reference in New Issue
Block a user