Skip to content

Commit 87fbf2d

Browse files
Update stepper_motor module to work on esp32s3 and migrate to newer pcnt driver (#191)
* add changes for s3 * fixed stepper motor on esp32s3 & migrate to newer pcnt driver * refactor * fix auto-formatting for reference table * add missing error checks * add missing include * wrap lines for readability and smaller diffs * use designated initializers for PCNT config structs * add defaults to prevent warnings --------- Co-authored-by: Falko Schindler <[email protected]>
1 parent 52cb9b2 commit 87fbf2d

File tree

5 files changed

+80
-51
lines changed

5 files changed

+80
-51
lines changed

docs/module_reference.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,13 @@ When the wheels are disabled, they will stop and ignore movement commands.
604604
The stepper motor module controls a stepper motor via "step" and "direction" pins.
605605
It uses the ESP LED Control API to generate pulses with sufficiently high frequencies and the Pulse Counter API to count steps.
606606

607-
| Constructor | Description | Arguments |
608-
| --------------------------------------------------------- | ----------------------- | --------- |
609-
| `motor = StepperMotor(step, dir[, pu[, cp[, lt[, lc]]]])` | Step and direction pins | 6x `int` |
607+
| Constructor | Description | Arguments |
608+
| --------------------------------------------- | ----------------------- | ---------- |
609+
| `motor = StepperMotor(step, dir[, lt[, lc]])` | Step and direction pins | 2–4x `int` |
610610

611-
The constructor arguments `pu` (pulse counter unit), `pc` (pulse counter channel), `lt` (LED timer) and `lc` (LED channel) are optional and default to 0.
612-
When using multiple stepper motors, they can be set to different values to avoid conflicts.
611+
The constructor arguments `lt` (LEDC timer) and `lc` (LEDC channel) are optional and default to 0.
612+
When using multiple stepper motors, set different timer/channel pairs to avoid conflicts.
613+
The pulse counter used for position feedback is created internally.
613614

614615
| Properties | Description | Data type |
615616
| ---------------- | ------------------------------ | --------- |

main/CMakeLists.txt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,24 @@ file(GLOB SRC_FILES
99
idf_component_register(
1010
SRCS ${SRC_FILES}
1111
INCLUDE_DIRS "." "compilation" "modules" "utils" "${CMAKE_BINARY_DIR}/generated"
12-
REQUIRES driver esp_wifi esp_timer efuse lwip esp_event esp_netif esp_adc esp32-zeug esp32-serial-flasher app_update esp_driver_uart esp_https_ota spi_flash bt nvs_flash
12+
REQUIRES
13+
app_update
14+
bt
15+
driver
16+
efuse
17+
esp32-serial-flasher
18+
esp32-zeug
19+
esp_adc
20+
esp_driver_pcnt
21+
esp_driver_uart
22+
esp_event
23+
esp_https_ota
24+
esp_netif
25+
esp_timer
26+
esp_wifi
27+
lwip
28+
nvs_flash
29+
spi_flash
1330
)
1431
add_compile_definitions(OWL_TOKEN_RUN_LENGTH=256)
1532
component_compile_options(-std=gnu++17 -Wsuggest-override)

main/modules/module.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "d1_motor.h"
1212
#include "driver/gpio.h"
1313
#include "driver/ledc.h"
14-
#include "driver/pcnt.h"
1514
#include "dunker_motor.h"
1615
#include "dunker_wheels.h"
1716
#include "expander.h"
@@ -288,17 +287,15 @@ Module_ptr Module::create(const std::string type,
288287
const RoboClawMotor_ptr right_motor = get_module_paramter<RoboClawMotor>(arguments[1], roboclaw_motor, "roboclaw motor");
289288
return std::make_shared<RoboClawWheels>(name, left_motor, right_motor);
290289
} else if (type == "StepperMotor") {
291-
if (arguments.size() < 2 || arguments.size() > 6) {
290+
if (arguments.size() < 2 || arguments.size() > 4) {
292291
throw std::runtime_error("unexpected number of arguments");
293292
}
294-
Module::expect(arguments, -1, integer, integer, integer, integer, integer, integer);
293+
Module::expect(arguments, -1, integer, integer, integer, integer);
295294
gpio_num_t step_pin = (gpio_num_t)arguments[0]->evaluate_integer();
296295
gpio_num_t dir_pin = (gpio_num_t)arguments[1]->evaluate_integer();
297-
pcnt_unit_t pcnt_unit = arguments.size() > 2 ? (pcnt_unit_t)arguments[2]->evaluate_integer() : PCNT_UNIT_0;
298-
pcnt_channel_t pcnt_channel = arguments.size() > 3 ? (pcnt_channel_t)arguments[3]->evaluate_integer() : PCNT_CHANNEL_0;
299-
ledc_timer_t ledc_timer = arguments.size() > 4 ? (ledc_timer_t)arguments[4]->evaluate_integer() : LEDC_TIMER_0;
300-
ledc_channel_t ledc_channel = arguments.size() > 5 ? (ledc_channel_t)arguments[5]->evaluate_integer() : LEDC_CHANNEL_0;
301-
return std::make_shared<StepperMotor>(name, step_pin, dir_pin, pcnt_unit, pcnt_channel, ledc_timer, ledc_channel);
296+
ledc_timer_t ledc_timer = arguments.size() > 2 ? (ledc_timer_t)arguments[2]->evaluate_integer() : LEDC_TIMER_0;
297+
ledc_channel_t ledc_channel = arguments.size() > 3 ? (ledc_channel_t)arguments[3]->evaluate_integer() : LEDC_CHANNEL_0;
298+
return std::make_shared<StepperMotor>(name, step_pin, dir_pin, ledc_timer, ledc_channel);
302299
} else if (type == "MotorAxis") {
303300
Module::expect(arguments, 3, identifier, identifier, identifier);
304301
const std::string name = arguments[0]->evaluate_identifier();

main/modules/stepper_motor.cpp

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
#include "stepper_motor.h"
22
#include "../utils/timing.h"
33
#include "../utils/uart.h"
4-
#include "esp32/rom/gpio.h"
4+
#include "rom/gpio.h"
55
#include "soc/gpio_sig_map.h"
66
#include <algorithm>
77
#include <driver/ledc.h>
8-
#include <driver/pcnt.h>
9-
#include <esp_rom_gpio.h>
10-
#include <math.h>
11-
#include <memory>
8+
#include <driver/pulse_cnt.h>
129
#include <stdexcept>
1310

1411
#define MIN_SPEED 490
1512

13+
static void check_error(esp_err_t err, const char *msg) {
14+
if (err != ESP_OK) {
15+
throw std::runtime_error(std::string(msg) + ": " + esp_err_to_name(err));
16+
}
17+
}
18+
1619
#ifdef CONFIG_IDF_TARGET_ESP32S3
1720
#define SPEED_MODE LEDC_LOW_SPEED_MODE
1821
#define SPEED_OUT_IDX LEDC_LS_SIG_OUT0_IDX
22+
#define DUTY_RESOLUTION LEDC_TIMER_8_BIT
23+
#define DUTY_VALUE 128 // 50% of 256 with 8-bit resolution
1924
#else
2025
#define SPEED_MODE LEDC_HIGH_SPEED_MODE
2126
#define SPEED_OUT_IDX LEDC_HS_SIG_OUT0_IDX
27+
#define DUTY_RESOLUTION LEDC_TIMER_1_BIT
28+
#define DUTY_VALUE 1 // 50% of max 1 with 1-bit resolution
2229
#endif
2330

2431
REGISTER_MODULE_DEFAULTS(StepperMotor)
@@ -35,48 +42,56 @@ const std::map<std::string, Variable_ptr> StepperMotor::get_defaults() {
3542
StepperMotor::StepperMotor(const std::string name,
3643
const gpio_num_t step_pin,
3744
const gpio_num_t dir_pin,
38-
const pcnt_unit_t pcnt_unit,
39-
const pcnt_channel_t pcnt_channel,
4045
const ledc_timer_t ledc_timer,
4146
const ledc_channel_t ledc_channel)
4247
: Module(stepper_motor, name),
4348
step_pin(step_pin),
4449
dir_pin(dir_pin),
45-
pcnt_unit(pcnt_unit),
46-
pcnt_channel(pcnt_channel),
4750
ledc_timer(ledc_timer),
4851
ledc_channel(ledc_channel) {
4952
gpio_reset_pin(step_pin);
5053
gpio_reset_pin(dir_pin);
5154

5255
this->properties = StepperMotor::get_defaults();
5356

54-
pcnt_config_t pcnt_config = {
55-
.pulse_gpio_num = step_pin,
56-
.ctrl_gpio_num = dir_pin,
57-
.lctrl_mode = PCNT_MODE_REVERSE,
58-
.hctrl_mode = PCNT_MODE_KEEP,
59-
.pos_mode = PCNT_COUNT_INC,
60-
.neg_mode = PCNT_COUNT_DIS,
61-
.counter_h_lim = 30000,
62-
.counter_l_lim = -30000,
63-
.unit = this->pcnt_unit,
64-
.channel = this->pcnt_channel,
57+
pcnt_unit_config_t unit_config = {
58+
.low_limit = -30000,
59+
.high_limit = 30000,
60+
.intr_priority = 0,
61+
.flags = {},
62+
};
63+
check_error(pcnt_new_unit(&unit_config, &this->pcnt_unit), "failed to create PCNT unit");
64+
65+
pcnt_chan_config_t chan_config = {
66+
.edge_gpio_num = step_pin,
67+
.level_gpio_num = dir_pin,
68+
.flags = {},
6569
};
66-
pcnt_unit_config(&pcnt_config);
67-
pcnt_counter_pause(this->pcnt_unit);
68-
pcnt_counter_clear(this->pcnt_unit);
69-
pcnt_counter_resume(this->pcnt_unit);
70+
check_error(pcnt_new_channel(this->pcnt_unit, &chan_config, &this->pcnt_channel), "failed to create PCNT channel");
71+
72+
check_error(pcnt_channel_set_edge_action(this->pcnt_channel,
73+
PCNT_CHANNEL_EDGE_ACTION_INCREASE,
74+
PCNT_CHANNEL_EDGE_ACTION_HOLD),
75+
"failed to set PCNT edge action");
76+
77+
check_error(pcnt_channel_set_level_action(this->pcnt_channel,
78+
PCNT_CHANNEL_LEVEL_ACTION_KEEP,
79+
PCNT_CHANNEL_LEVEL_ACTION_INVERSE),
80+
"failed to set PCNT level action");
81+
82+
check_error(pcnt_unit_enable(this->pcnt_unit), "failed to enable PCNT unit");
83+
check_error(pcnt_unit_clear_count(this->pcnt_unit), "failed to clear PCNT count");
84+
check_error(pcnt_unit_start(this->pcnt_unit), "failed to start PCNT unit");
7085

7186
ledc_timer_config_t timer_config = {
7287
.speed_mode = SPEED_MODE,
73-
.duty_resolution = LEDC_TIMER_1_BIT,
88+
.duty_resolution = DUTY_RESOLUTION,
7489
.timer_num = this->ledc_timer,
7590
.freq_hz = 1000,
7691
.clk_cfg = LEDC_AUTO_CLK,
7792
.deconfigure = false,
7893
};
79-
ledc_timer_config(&timer_config);
94+
check_error(ledc_timer_config(&timer_config), "failed to configure LEDC timer");
8095

8196
ledc_channel_config_t channel_config = {
8297
.gpio_num = step_pin,
@@ -88,16 +103,16 @@ StepperMotor::StepperMotor(const std::string name,
88103
.hpoint = 0,
89104
.flags = {},
90105
};
91-
ledc_channel_config(&channel_config);
106+
check_error(ledc_channel_config(&channel_config), "failed to configure LEDC channel");
92107

93108
gpio_set_direction(step_pin, GPIO_MODE_INPUT_OUTPUT);
94109
gpio_set_direction(dir_pin, GPIO_MODE_INPUT_OUTPUT);
95110
}
96111

97112
void StepperMotor::read_position() {
98-
int16_t count;
99-
pcnt_get_counter_value(this->pcnt_unit, &count);
100-
int16_t d_count = count - this->last_count;
113+
int count;
114+
pcnt_unit_get_count(this->pcnt_unit, &count);
115+
int d_count = count - this->last_count;
101116
if (d_count > 15000) {
102117
d_count -= 30000;
103118
}
@@ -113,7 +128,7 @@ void StepperMotor::set_state(StepperState new_state) {
113128
this->properties.at("idle")->boolean_value = (new_state == Idle);
114129

115130
gpio_matrix_out(this->step_pin, new_state == Idle ? SIG_GPIO_OUT_IDX : SPEED_OUT_IDX + this->ledc_channel, 0, 0);
116-
ledc_set_duty(SPEED_MODE, this->ledc_channel, new_state == Idle ? 0 : 1);
131+
ledc_set_duty(SPEED_MODE, this->ledc_channel, new_state == Idle ? 0 : DUTY_VALUE);
117132
ledc_update_duty(SPEED_MODE, this->ledc_channel);
118133
}
119134

main/modules/stepper_motor.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#include "driver/gpio.h"
44
#include "driver/ledc.h"
5-
#include "driver/pcnt.h"
5+
#include "driver/pulse_cnt.h"
66
#include "module.h"
77
#include "motor.h"
88

@@ -19,13 +19,14 @@ class StepperMotor : public Module, virtual public Motor {
1919
private:
2020
const gpio_num_t step_pin;
2121
const gpio_num_t dir_pin;
22-
const pcnt_unit_t pcnt_unit;
23-
const pcnt_channel_t pcnt_channel;
2422
const ledc_timer_t ledc_timer;
2523
const ledc_channel_t ledc_channel;
2624

25+
pcnt_unit_handle_t pcnt_unit = nullptr;
26+
pcnt_channel_handle_t pcnt_channel = nullptr;
27+
2728
uint32_t last_micros = 0;
28-
int16_t last_count = 0;
29+
int last_count = 0;
2930

3031
StepperState state = Idle;
3132
int32_t target_position = 0;
@@ -40,8 +41,6 @@ class StepperMotor : public Module, virtual public Motor {
4041
StepperMotor(const std::string name,
4142
const gpio_num_t step_pin,
4243
const gpio_num_t dir_pin,
43-
const pcnt_unit_t pcnt_unit,
44-
const pcnt_channel_t pcnt_channel,
4544
const ledc_timer_t ledc_timer,
4645
const ledc_channel_t ledc_channel);
4746
void step() override;

0 commit comments

Comments
 (0)