44 changed files with 826 additions and 825 deletions
+2
View File
@@ -0,0 +1,2 @@
* text=auto eol=lf
*.bat text eol=crlf
+3
View File
@@ -3,3 +3,6 @@
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json
.vscode/launch.json .vscode/launch.json
.vscode/ipch .vscode/ipch
scripts
__pycache__/
*.typ
+1 -1
View File
@@ -1,3 +1,3 @@
# ESP_Project4_H26 # ESP_Project4_H26
A repository for project 3, done in the Embedded System Programming class in Reykjavík University in the Fall semester of 2026 A repository for project 4, done in the Embedded System Programming class in Reykjavík University in the Fall semester of 2026
+80
View File
@@ -0,0 +1,80 @@
@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
+29
View File
@@ -0,0 +1,29 @@
@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
-50
View File
@@ -1,50 +0,0 @@
@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
+12 -3
View File
@@ -4,19 +4,28 @@
#include <stdint.h> #include <stdint.h>
#include <zephyr/drivers/gpio.h> #include <zephyr/drivers/gpio.h>
struct Digital_in { class 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
View File
@@ -4,10 +4,7 @@
#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
}; };
@@ -21,7 +18,6 @@ struct Digital_out {
private: private:
const struct gpio_dt_spec *gpio; const struct gpio_dt_spec *gpio;
}; };
#endif #endif
+2 -4
View File
@@ -5,13 +5,9 @@
#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;
@@ -26,6 +22,8 @@ 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
+40 -15
View File
@@ -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_REFERENCE_RPM = -40.0; static constexpr double DEFAULT_TARGET_RPM = 0.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,22 +28,32 @@ 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 reference_rpm; double target_rpm; // Það sem beðið er um (RPDO1)
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 &controller, P_controller &p_controller,
P_controller &pi_controller,
State &initialization, State &initialization,
State &pre_operational, State &pre_operational,
State &operational, State &operational,
@@ -60,13 +70,15 @@ public:
void transition_to(State &new_state); void transition_to(State &new_state);
void to_initialization(); void to_initialization() override;
void to_pre_operational(); void to_pre_operational() override;
void to_operational(); void to_operational() override;
void to_stopped(); void to_stopped() override;
NodeState get_state() const override;
bool fault_active(); bool fault_active();
@@ -74,21 +86,34 @@ 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 { return last_actual_rpm; } double get_actual_rpm() const override { 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); void set_Kp(double value) override;
double get_Kp() const override { return controller->get_Kp(); }
void set_Ti(double value); bool set_Ti(double value) override;
double get_Ti() const override { return controller->get_Ti(); }
void set_controller(P_controller &new_controller); void select_controller(ControllerType type) override;
ControllerType get_controller_type() const override;
void led_on(); void led_on();
+51
View File
@@ -0,0 +1,51 @@
#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
+18
View File
@@ -0,0 +1,18 @@
#ifndef NODE_TYPES_H
#define NODE_TYPES_H
enum class ControllerType
{
P,
PI
};
enum class NodeState
{
INITIALIZATION,
PRE_OPERATIONAL,
OPERATIONAL,
STOPPED
};
#endif
+2
View File
@@ -24,6 +24,8 @@ 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
+7 -1
View File
@@ -5,21 +5,27 @@ 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 void set_Ti(double value) virtual bool set_Ti(double value)
{ {
// P controller does not use Ti // P controller does not use Ti
(void)value;
return true;
} }
}; };
+4 -1
View File
@@ -10,14 +10,17 @@ 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;
void set_Ti(double value) override; bool set_Ti(double value) override;
double get_Ti() const override { return Ti; }
}; };
#endif #endif
+10 -9
View File
@@ -4,27 +4,25 @@
#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;
@@ -33,12 +31,15 @@ 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
+10 -28
View File
@@ -3,8 +3,9 @@
#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 {
@@ -25,6 +26,10 @@ 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
@@ -35,12 +40,11 @@ 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(
@@ -49,28 +53,6 @@ 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
+3 -1
View File
@@ -1,6 +1,8 @@
#ifndef STATE_H #ifndef STATE_H
#define STATE_H #define STATE_H
#include "node_types.h"
class MotorController; class MotorController;
class State class State
@@ -11,7 +13,6 @@ protected:
public: public:
virtual ~State() virtual ~State()
{ {
} }
void set_context(MotorController* context) void set_context(MotorController* context)
@@ -27,6 +28,7 @@ public:
virtual void exit() = 0; // Stop motor virtual void exit() = 0; // Stop motor
virtual NodeState id() const = 0; //
}; };
#endif #endif
+2 -2
View File
@@ -8,9 +8,8 @@
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; // Ditto bool led_state;
public: public:
StoppedState(); StoppedState();
@@ -23,6 +22,7 @@ public:
void exit() override; void exit() override;
NodeState id() const override { return NodeState::STOPPED; }
}; };
#endif #endif
-7
View File
@@ -1,16 +1,9 @@
#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);
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/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
+21
View File
@@ -18,3 +18,24 @@ 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
+49
View File
@@ -0,0 +1,49 @@
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/can.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
/* Devicetree node selected by zephyr,canbus in the overlay */
#define CANBUS_NODE DT_CHOSEN(zephyr_canbus)
// L4.4 example. Built only with the can_frame_test environment
// (-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);
if (!device_is_ready(dev)) {
printk("CAN device not ready\n");
return;
}
int err = can_set_bitrate(dev, 125000);
if (err != 0) {
printk("Error setting CAN bitrate (err %d)\n", err);
return;
}
err = can_start(dev);
if (err != 0) {
printk("Error starting CAN controller (err %d)\n", err);
return;
}
struct can_frame frame = {
.flags = 0,
.id = 0x456,
.dlc = 4,
.data = {1, 2, 3, 4, 5, 6, 7, 8},
};
while (1) {
k_msleep(500);
err = can_send(dev, &frame, K_MSEC(100), NULL, NULL);
if (err != 0) {
printk("Sending failed (err %d)\n", err);
return;
}
}
}
+48 -48
View File
@@ -1,8 +1,3 @@
// 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>
@@ -17,12 +12,11 @@
#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;
@@ -40,7 +34,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(p_controller, pi_controller); static PreOperationalState pre_operational_state;
static OperationalState operational_state; static OperationalState operational_state;
static StoppedState stopped_state; static StoppedState stopped_state;
@@ -48,6 +42,7 @@ 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,
@@ -56,42 +51,34 @@ 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)) {
@@ -99,6 +86,9 @@ 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);
@@ -109,17 +99,27 @@ extern "C" void motor_application_main(void)
motor_controller.begin(); motor_controller.begin();
while (true) { while (true) {
unsigned char received_char; char received_char;
// Read a command if a character is waiting. // Handle every character the ISR has queued since the last pass.
if (uart_poll_in(console_dev, &received_char) == 0) { while (k_msgq_get(&rx_queue, &received_char, K_NO_WAIT) == 0) {
motor_controller.command( motor_controller.command(received_char);
static_cast<char>(received_char));
} }
motor_controller.update(); motor_controller.update();
// Give Zephyr's scheduler time to run other threads. static NodeState last_state = NodeState::INITIALIZATION;
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));
} }
} }
+80
View File
@@ -0,0 +1,80 @@
// 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");
}
}
+61
View File
@@ -0,0 +1,61 @@
// 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));
}
}
+21 -11
View File
@@ -1,17 +1,8 @@
//#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);
@@ -62,3 +53,22 @@ 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);
}
-9
View File
@@ -1,16 +1,7 @@
#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);
-26
View File
@@ -1,25 +1,11 @@
#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();
@@ -32,30 +18,18 @@ void InitializationState::enter()
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");
} }
+13 -5
View File
@@ -1,12 +1,20 @@
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#ifndef PIO_UNIT_TESTING
void motor_application_main(void); void motor_application_main(void);
void can_send_test_main(void);
void can_frame_test_main(void);
void can_rx_test_main(void);
void main(void) int main(void)
{ {
#if CAN_SEND_TEST
can_send_test_main();
#elif CAN_FRAME_TEST
can_frame_test_main();
#elif CAN_RX_TEST
can_rx_test_main();
#else
motor_application_main(); motor_application_main();
}
#endif #endif
return 0;
}
+58 -24
View File
@@ -1,13 +1,11 @@
#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 &controller, P_controller &p_controller,
P_controller &pi_controller,
State &initialization, State &initialization,
State &pre_operational, State &pre_operational,
State &operational, State &operational,
@@ -20,9 +18,14 @@ MotorController::MotorController(
stopped_state(stopped), stopped_state(stopped),
encoder(encoder), encoder(encoder),
motor(motor), motor(motor),
controller(&controller), p_controller(p_controller),
pi_controller(pi_controller),
controller(&pi_controller), // PI er sjálfgefin stýring
led(LED_PIN), led(LED_PIN),
reference_rpm(DEFAULT_REFERENCE_RPM), target_rpm(DEFAULT_TARGET_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)
{ {
@@ -33,18 +36,6 @@ 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();
@@ -73,7 +64,6 @@ void MotorController::update()
} }
} }
void MotorController::command(char c) void MotorController::command(char c)
{ {
if (current_state != nullptr) if (current_state != nullptr)
@@ -82,7 +72,6 @@ void MotorController::command(char c)
} }
} }
void MotorController::to_initialization() void MotorController::to_initialization()
{ {
transition_to(initialization_state); transition_to(initialization_state);
@@ -107,6 +96,8 @@ 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 =
@@ -150,28 +141,71 @@ 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);
} }
void MotorController::set_Ti(double value) bool MotorController::set_Ti(double value)
{ {
controller->set_Ti(value); return controller->set_Ti(value);
} }
void MotorController::set_controller(P_controller& new_controller) void MotorController::select_controller(ControllerType type)
{ {
controller = &new_controller; if (type == ControllerType::PI)
{
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();
+25 -112
View File
@@ -1,46 +1,14 @@
#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 gpio_dt_spec bin2 = static const struct pwm_dt_spec bin2_pwm =
GPIO_DT_SPEC_GET(DT_ALIAS(motor_bin2), gpios); PWM_DT_SPEC_GET(DT_ALIAS(motor_bin2_pwm));
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);
@@ -52,7 +20,6 @@ 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)
{ {
@@ -61,42 +28,17 @@ 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) ||
!gpio_is_ready_dt(&bin2) || !pwm_is_ready_dt(&bin2_pwm) ||
!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(&bin2, GPIO_OUTPUT_INACTIVE) < 0 || if (gpio_pin_configure_dt(&motor_flt, GPIO_INPUT) < 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;
@@ -107,10 +49,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();
} }
@@ -130,22 +72,17 @@ 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);
gpio_pin_set_dt(&bin2, 0); pwm_set_dt(&bin2_pwm, bin2_pwm.period, 0);
} }
void MotorDriver::set(double pwm) void MotorDriver::set(double pwm)
{ {
if (!initialized) { if (!initialized) {
return; return;
} }
@@ -164,50 +101,27 @@ void MotorDriver::set(double pwm)
} }
// uint8_t duty = (uint8_t)(pwm > 0 ? pwm : -pwm); const double magnitude = pwm > 0.0 ? pwm : -pwm;
// if (duty == 0) // Pulse width in nanoseconds, kept as a double until the end so the
// { // 4000-count timer resolution is not lost to integer truncation.
// stop(); const uint32_t pulse = static_cast<uint32_t>(
// } 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: BIN2 low; BIN1 PWM duty equals the command magnitude. // Forward: BIN1 PWM, BIN2 low.
gpio_pin_set_dt(&bin2, 0); pwm_set_dt(&bin2_pwm, bin2_pwm.period, 0);
bin1_pulse = requested_pulse; pwm_set_dt(&bin1_pwm, bin1_pwm.period, pulse);
} else { } else {
// Reverse: BIN2 high; BIN1 pulse is inverted for reverse PWM. // Reverse: BIN1 low, BIN2 PWM.
gpio_pin_set_dt(&bin2, 1); pwm_set_dt(&bin1_pwm, bin1_pwm.period, 0);
bin1_pulse = bin1_pwm.period - requested_pulse; pwm_set_dt(&bin2_pwm, bin2_pwm.period, pulse);
} }
pwm_set_dt(&bin1_pwm, bin1_pwm.period, bin1_pulse);
} }
@@ -231,11 +145,10 @@ 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 ||
+30 -25
View File
@@ -1,14 +1,10 @@
#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();
@@ -17,26 +13,20 @@ 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();
@@ -50,13 +40,6 @@ 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(),
@@ -71,21 +54,43 @@ void OperationalState::command(char c)
{ {
if (c == 'r' || c == 'R') if (c == 'r' || c == 'R')
{ {
//Serial.println("Command: reset"); printk("Command: reset communication\n"); // Heldur Kp og Ti gildum
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()
{ {
+8 -1
View File
@@ -1,7 +1,8 @@
#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,3 +12,9 @@ double P_controller::update(double ref, double actual)
return Kp * error; return Kp * error;
} }
void P_controller::restore_defaults()
{
Kp = default_Kp;
reset();
}
+17 -4
View File
@@ -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), T(T), integral(0.0) PI_controller::PI_controller(double Kp, double Ti, double T) : P_controller(Kp), Ti(Ti), default_Ti(Ti), T(T), integral(0.0)
{ {
} }
@@ -23,7 +23,6 @@ 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()
@@ -31,7 +30,21 @@ void PI_controller::reset()
integral = 0.0; integral = 0.0;
} }
void PI_controller::set_Ti(double value) bool PI_controller::set_Ti(double value)
{ {
Ti = value; if (value > 0.0)
{
Ti = value;
return true;
}
else
{
return false;
}
}
void PI_controller::restore_defaults()
{
P_controller::restore_defaults();
Ti = default_Ti;
} }
+40 -36
View File
@@ -1,24 +1,19 @@
#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()
PreOperationalState::PreOperationalState(P_controller &p_controller, P_controller &pi_controller) // : p_controller(p_controller),
: p_controller(p_controller), // pi_controller(pi_controller),
pi_controller(pi_controller), // pi_selected(true),
pi_selected(true), : previous_time(0),
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)
{ {
@@ -26,22 +21,23 @@ PreOperationalState::PreOperationalState(P_controller &p_controller, P_controlle
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)
@@ -81,17 +77,24 @@ void PreOperationalState::command(char c)
{ {
context->set_Kp(value); context->set_Kp(value);
// Serial.print("Kp set to: "); printk("Kp set to: %.4f\n", value);
// Serial.println(value);
printk("Kp updated\n");
} }
else if (input_mode == ENTER_TI) else if (input_mode == ENTER_TI)
{ {
context->set_Ti(value); if (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);
// Serial.print("Ti set to: "); printk("Target RPM set to: %.0f\n", value);
// Serial.println(value);
printk("Ti updated\n");
} }
} }
@@ -110,7 +113,6 @@ 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++;
} }
} }
@@ -121,7 +123,6 @@ 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();
@@ -129,7 +130,6 @@ 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,7 +140,6 @@ 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");
} }
@@ -149,26 +148,31 @@ 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')
{ {
pi_selected = !pi_selected; if (context->get_controller_type() == ControllerType::P)
if (pi_selected)
{ {
context->set_controller(pi_controller); context->select_controller(ControllerType::PI);
//Serial.println("Controller: PI"); printk("Controller: PI, Kp=%.4f, Ti=%.4f\n",
printk("Controller: PI\n"); context->get_Kp(), context->get_Ti());
} }
else else
{ {
context->set_controller(p_controller); context->select_controller(ControllerType::P);
//Serial.println("Controller: P"); printk("Controller: P, Kp=%.4f\n",
printk("Controller: P\n"); context->get_Kp());
} }
} }
} }
+24 -54
View File
@@ -6,22 +6,13 @@
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h> #include <zephyr/drivers/gpio.h>
//#define ISR_PROBE_NODE DT_PATH(zephyr_user)
static const struct gpio_dt_spec isr_probe =
GPIO_DT_SPEC_GET(DT_ALIAS(encoder_isr_probe), gpios);
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;
@@ -30,40 +21,16 @@ RotaryEncoderDriver::RotaryEncoderDriver()
int RotaryEncoderDriver::begin() int RotaryEncoderDriver::begin()
{ {
if (!gpio_is_ready_dt(&encoder_c1) || eA.init();
!gpio_is_ready_dt(&encoder_c2) || eB.init();
!gpio_is_ready_dt(&isr_probe)) {
return -ENODEV;
}
int ret = gpio_pin_configure_dt(&isr_probe, GPIO_OUTPUT_INACTIVE); // Max count 1: the main loop only needs to know that a new sample
if (ret < 0) { // exists, not how many it missed.
return ret; k_sem_init(&sample_sem, 0, 1);
}
ret = gpio_pin_configure_dt(&encoder_c1, GPIO_INPUT); const int ret = eA.enable_rising_edge_interrupt(
if (ret < 0) {
return ret;
}
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;
} }
@@ -80,18 +47,16 @@ void RotaryEncoderDriver::gpio_edge_handler(
struct gpio_callback *callback, struct gpio_callback *callback,
uint32_t pins) uint32_t pins)
{ {
(void)dev; // This handler does not need the device pointer. // Zephyr only calls this handler for C1, the one pin in the callback.
(void)dev;
(void)pins;
auto *encoder = CONTAINER_OF( auto *encoder = CONTAINER_OF(
callback, callback,
RotaryEncoderDriver, RotaryEncoderDriver,
encoder_c1_callback); encoder_c1_callback);
if ((pins & BIT(encoder_c1.pin)) != 0U) { encoder->update();
encoder->update(); // Reads C2 and updates the count
gpio_pin_set_dt(&isr_probe, 1); // Marker rising edge
gpio_pin_set_dt(&isr_probe, 0); // Reset for the next interrupt
}
} }
void RotaryEncoderDriver::timer_expired(void *context) void RotaryEncoderDriver::timer_expired(void *context)
@@ -103,13 +68,10 @@ 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 int direction = gpio_pin_get_dt(&encoder_c2); const bool forward = eB.is_hi();
if (direction < 0) {
return;
}
const unsigned int key = irq_lock(); const unsigned int key = irq_lock();
count += direction ? 1 : -1; count += forward ? 1 : -1;
irq_unlock(key); irq_unlock(key);
} }
@@ -136,6 +98,14 @@ 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()
-132
View File
@@ -1,132 +0,0 @@
// #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();
// }
-12
View File
@@ -1,12 +1,9 @@
#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)
@@ -16,8 +13,6 @@ 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();
@@ -26,7 +21,6 @@ void StoppedState::enter()
context->led_off(); context->led_off();
//previous_time = millis();
previous_time = k_uptime_get(); previous_time = k_uptime_get();
} }
@@ -34,7 +28,6 @@ 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)
@@ -56,12 +49,10 @@ 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();
@@ -69,7 +60,6 @@ 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();
@@ -77,14 +67,12 @@ 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();
+1 -14
View File
@@ -1,11 +1,9 @@
#include "timer_usec.h" #include "timer_usec.h"
Timer_usec::Timer_usec() //= default; Timer_usec::Timer_usec()
: callback(nullptr), : callback(nullptr),
callback_context(nullptr) callback_context(nullptr)
{ {
} }
void Timer_usec::set_callback( void Timer_usec::set_callback(
@@ -17,17 +15,6 @@ 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) {
-150
View File
@@ -1,150 +0,0 @@
#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
+25 -17
View File
@@ -1,17 +1,21 @@
#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 {
zephyr,canbus = &can1;
};
aliases { aliases {
encoder-c1 = &encoder_c1; encoder-c1 = &encoder_c1;
encoder-c2 = &encoder_c2; encoder-c2 = &encoder_c2;
motor-bin1-pwm = &motor_bin1_pwm; motor-bin1-pwm = &motor_bin1_pwm;
motor-bin2 = &motor_bin2; motor-bin2-pwm = &motor_bin2_pwm;
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;
encoder-isr-probe = &encoder_isr_probe;
}; };
buttons { buttons {
@@ -41,20 +45,10 @@
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";
}; };
encoder_isr_probe: encoder_isr_probe {
gpios = <&gpioa 12 GPIO_ACTIVE_HIGH>; /* D2, PA12 */
label = "Encoder ISR probe";
};
}; };
motor_pwm_outputs { motor_pwm_outputs {
@@ -64,19 +58,33 @@
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";
}; };
};
zephyr,user { /* PB1 has no regular TIM1 channel, only the complementary output
encoder-isr-probe-gpios = <&gpioa 12 GPIO_ACTIVE_HIGH>; /* D2, PA12 */ * 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";
st,prescaler = <79>; /* No prescaler: 80 MHz timer clock, so a 50 us period is 4000 counts. */
st,prescaler = <0>;
pwm1: pwm { pwm1: pwm {
status = "okay"; status = "okay";
pinctrl-0 = <&tim1_ch1_pa8>; pinctrl-0 = <&tim1_ch1_pa8 &tim1_ch3n_pb1>;
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 {
pinctrl-0 = <&can1_rx_pa11 &can1_tx_pa12>;
pinctrl-names = "default";
status = "okay";
};
+2
View File
@@ -6,5 +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