Skip to content

Commit 0b124a2

Browse files
faxe1008kartben
authored andcommitted
drivers: stepper: Add timing source for step dir stepper
Adds a timing source api which is used by the step-dir stepper common code. This allows the reusable common code to configure different timing sources, since the initial delayable work implementation was inacurate for higher maximum velocities. Signed-off-by: Fabian Blatz <[email protected]>
1 parent fc25679 commit 0b124a2

12 files changed

+473
-37
lines changed

drivers/stepper/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/stepper.h)
66
# zephyr-keep-sorted-start
77
add_subdirectory_ifdef(CONFIG_STEPPER_ADI_TMC adi_tmc)
88
add_subdirectory_ifdef(CONFIG_STEPPER_TI ti)
9+
add_subdirectory_ifdef(CONFIG_STEP_DIR_STEPPER step_dir)
910
# zephyr-keep-sorted-stop
1011

1112
zephyr_library()
1213
zephyr_library_property(ALLOW_EMPTY TRUE)
1314

1415
zephyr_library_sources_ifdef(CONFIG_FAKE_STEPPER fake_stepper_controller.c)
1516
zephyr_library_sources_ifdef(CONFIG_GPIO_STEPPER gpio_stepper_controller.c)
16-
zephyr_library_sources_ifdef(CONFIG_STEP_DIR_STEPPER step_dir_stepper_common.c)
1717
zephyr_library_sources_ifdef(CONFIG_STEPPER_SHELL stepper_shell.c)

drivers/stepper/Kconfig

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ config STEPPER_SHELL
2424
help
2525
Enable stepper shell for testing.
2626

27-
config STEP_DIR_STEPPER
28-
bool
29-
help
30-
Enable library used for step direction stepper drivers.
27+
comment "Stepper Driver Common"
28+
29+
rsource "step_dir/Kconfig"
3130

3231
comment "Stepper Drivers"
3332

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2024 Fabian Blatz <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS
5+
bool "$(module-str) guarantee non ISR callbacks upon stepper events"
6+
help
7+
Enable the dispatch of stepper generated events via
8+
a message queue to guarantee that the event handler
9+
code is not run inside of an ISR. Can be disabled, but
10+
then registered stepper event callback must be ISR safe.
11+
12+
config STEPPER_$(module)_EVENT_QUEUE_LEN
13+
int "$(module-str) maximum number of pending stepper events"
14+
default 4
15+
depends on STEPPER_$(module)_GENERATE_ISR_SAFE_EVENTS
16+
help
17+
The maximum number of stepper events that can be pending before new events
18+
are dropped.

drivers/stepper/adi_tmc/adi_tmc22xx_stepper_controller.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
#include "../step_dir_stepper_common.h"
6+
#include "../step_dir/step_dir_stepper_common.h"
77

88
#include <zephyr/logging/log.h>
99
LOG_MODULE_REGISTER(tmc22xx, CONFIG_STEPPER_LOG_LEVEL);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 Fabian Blatz
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
zephyr_library()
5+
6+
zephyr_library_sources(step_dir_stepper_common.c)
7+
zephyr_library_sources(step_dir_stepper_work_timing.c)
8+
zephyr_library_sources_ifdef(CONFIG_STEP_DIR_STEPPER_COUNTER_TIMING step_dir_stepper_counter_timing.c)

drivers/stepper/step_dir/Kconfig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2024 Fabian Blatz <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config STEP_DIR_STEPPER
5+
bool
6+
help
7+
Enable library used for step direction stepper drivers.
8+
9+
if STEP_DIR_STEPPER
10+
11+
config STEP_DIR_STEPPER_COUNTER_TIMING
12+
bool "Counter use for stepping"
13+
select COUNTER
14+
default y
15+
help
16+
Enable usage of a counter device for accurate stepping.
17+
18+
module = STEP_DIR
19+
module-str = step_dir
20+
rsource "../Kconfig.stepper_event_template"
21+
22+
endif # STEP_DIR_STEPPER

drivers/stepper/step_dir_stepper_common.c renamed to drivers/stepper/step_dir/step_dir_stepper_common.c

Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,70 @@ static inline int step_dir_stepper_perform_step(const struct device *dev)
4747
return 0;
4848
}
4949

50+
static void stepper_trigger_callback(const struct device *dev, enum stepper_event event)
51+
{
52+
struct step_dir_stepper_common_data *data = dev->data;
53+
54+
if (!data->callback) {
55+
LOG_WRN_ONCE("No callback set");
56+
return;
57+
}
58+
59+
if (!k_is_in_isr()) {
60+
data->callback(dev, event, data->event_cb_user_data);
61+
return;
62+
}
63+
64+
#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS
65+
/* Dispatch to msgq instead of raising directly */
66+
int ret = k_msgq_put(&data->event_msgq, &event, K_NO_WAIT);
67+
68+
if (ret != 0) {
69+
LOG_WRN("Failed to put event in msgq: %d", ret);
70+
}
71+
72+
ret = k_work_submit(&data->event_callback_work);
73+
if (ret < 0) {
74+
LOG_ERR("Failed to submit work item: %d", ret);
75+
}
76+
#else
77+
LOG_WRN_ONCE("Event callback called from ISR context without ISR safe events enabled");
78+
#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */
79+
}
80+
81+
#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS
82+
static void stepper_work_event_handler(struct k_work *work)
83+
{
84+
struct step_dir_stepper_common_data *data =
85+
CONTAINER_OF(work, struct step_dir_stepper_common_data, event_callback_work);
86+
enum stepper_event event;
87+
int ret;
88+
89+
ret = k_msgq_get(&data->event_msgq, &event, K_NO_WAIT);
90+
if (ret != 0) {
91+
return;
92+
}
93+
94+
/* Run the callback */
95+
if (data->callback != NULL) {
96+
data->callback(data->dev, event, data->event_cb_user_data);
97+
}
98+
99+
/* If there are more pending events, resubmit this work item to handle them */
100+
if (k_msgq_num_used_get(&data->event_msgq) > 0) {
101+
k_work_submit(work);
102+
}
103+
}
104+
#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */
105+
50106
static void update_remaining_steps(struct step_dir_stepper_common_data *data)
51107
{
52108
if (data->step_count > 0) {
53109
data->step_count--;
54-
(void)k_work_reschedule(&data->stepper_dwork, K_USEC(data->delay_in_us));
55110
} else if (data->step_count < 0) {
56111
data->step_count++;
57-
(void)k_work_reschedule(&data->stepper_dwork, K_USEC(data->delay_in_us));
58112
} else {
59-
if (!data->callback) {
60-
LOG_WRN_ONCE("No callback set");
61-
return;
62-
}
63-
data->callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED, data->event_cb_user_data);
113+
stepper_trigger_callback(data->dev, STEPPER_EVENT_STEPS_COMPLETED);
64114
}
65115
}
66116

@@ -80,34 +130,41 @@ static void update_direction_from_step_count(const struct device *dev)
80130
static void position_mode_task(const struct device *dev)
81131
{
82132
struct step_dir_stepper_common_data *data = dev->data;
133+
const struct step_dir_stepper_common_config *config = dev->config;
83134

84135
if (data->step_count) {
85136
(void)step_dir_stepper_perform_step(dev);
86137
}
138+
87139
update_remaining_steps(dev->data);
140+
141+
if (config->timing_source->needs_reschedule(dev) && data->step_count != 0) {
142+
(void)config->timing_source->start(dev);
143+
}
88144
}
89145

90146
static void velocity_mode_task(const struct device *dev)
91147
{
92-
struct step_dir_stepper_common_data *data = dev->data;
148+
const struct step_dir_stepper_common_config *config = dev->config;
93149

94150
(void)step_dir_stepper_perform_step(dev);
95-
(void)k_work_reschedule(&data->stepper_dwork, K_USEC(data->delay_in_us));
151+
152+
if (config->timing_source->needs_reschedule(dev)) {
153+
(void)config->timing_source->start(dev);
154+
}
96155
}
97156

98-
static void stepper_work_step_handler(struct k_work *work)
157+
void stepper_handle_timing_signal(const struct device *dev)
99158
{
100-
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
101-
struct step_dir_stepper_common_data *data =
102-
CONTAINER_OF(dwork, struct step_dir_stepper_common_data, stepper_dwork);
159+
struct step_dir_stepper_common_data *data = dev->data;
103160

104161
K_SPINLOCK(&data->lock) {
105162
switch (data->run_mode) {
106163
case STEPPER_RUN_MODE_POSITION:
107-
position_mode_task(data->dev);
164+
position_mode_task(dev);
108165
break;
109166
case STEPPER_RUN_MODE_VELOCITY:
110-
velocity_mode_task(data->dev);
167+
velocity_mode_task(dev);
111168
break;
112169
default:
113170
LOG_WRN("Unsupported run mode: %d", data->run_mode);
@@ -119,7 +176,6 @@ static void stepper_work_step_handler(struct k_work *work)
119176
int step_dir_stepper_common_init(const struct device *dev)
120177
{
121178
const struct step_dir_stepper_common_config *config = dev->config;
122-
struct step_dir_stepper_common_data *data = dev->data;
123179
int ret;
124180

125181
if (!gpio_is_ready_dt(&config->step_pin) || !gpio_is_ready_dt(&config->dir_pin)) {
@@ -139,25 +195,41 @@ int step_dir_stepper_common_init(const struct device *dev)
139195
return ret;
140196
}
141197

142-
k_work_init_delayable(&data->stepper_dwork, stepper_work_step_handler);
198+
if (config->timing_source->init) {
199+
ret = config->timing_source->init(dev);
200+
if (ret < 0) {
201+
LOG_ERR("Failed to initialize timing source: %d", ret);
202+
return ret;
203+
}
204+
}
205+
206+
#ifdef CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS
207+
struct step_dir_stepper_common_data *data = dev->data;
208+
209+
k_msgq_init(&data->event_msgq, data->event_msgq_buffer, sizeof(enum stepper_event),
210+
CONFIG_STEPPER_STEP_DIR_EVENT_QUEUE_LEN);
211+
k_work_init(&data->event_callback_work, stepper_work_event_handler);
212+
#endif /* CONFIG_STEPPER_STEP_DIR_GENERATE_ISR_SAFE_EVENTS */
143213

144214
return 0;
145215
}
146216

147217
int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micro_steps)
148218
{
149219
struct step_dir_stepper_common_data *data = dev->data;
220+
const struct step_dir_stepper_common_config *config = dev->config;
150221

151-
if (data->delay_in_us == 0) {
222+
if (data->max_velocity == 0) {
152223
LOG_ERR("Velocity not set or invalid velocity set");
153224
return -EINVAL;
154225
}
155226

156227
K_SPINLOCK(&data->lock) {
157228
data->run_mode = STEPPER_RUN_MODE_POSITION;
158229
data->step_count = micro_steps;
230+
config->timing_source->update(dev, data->max_velocity);
159231
update_direction_from_step_count(dev);
160-
(void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT);
232+
config->timing_source->start(dev);
161233
}
162234

163235
return 0;
@@ -166,6 +238,7 @@ int step_dir_stepper_common_move_by(const struct device *dev, const int32_t micr
166238
int step_dir_stepper_common_set_max_velocity(const struct device *dev, const uint32_t velocity)
167239
{
168240
struct step_dir_stepper_common_data *data = dev->data;
241+
const struct step_dir_stepper_common_config *config = dev->config;
169242

170243
if (velocity == 0) {
171244
LOG_ERR("Velocity cannot be zero");
@@ -178,7 +251,8 @@ int step_dir_stepper_common_set_max_velocity(const struct device *dev, const uin
178251
}
179252

180253
K_SPINLOCK(&data->lock) {
181-
data->delay_in_us = USEC_PER_SEC / velocity;
254+
data->max_velocity = velocity;
255+
config->timing_source->update(dev, velocity);
182256
}
183257

184258
return 0;
@@ -209,43 +283,47 @@ int step_dir_stepper_common_get_actual_position(const struct device *dev, int32_
209283
int step_dir_stepper_common_move_to(const struct device *dev, const int32_t value)
210284
{
211285
struct step_dir_stepper_common_data *data = dev->data;
286+
const struct step_dir_stepper_common_config *config = dev->config;
212287

213-
if (data->delay_in_us == 0) {
288+
if (data->max_velocity == 0) {
214289
LOG_ERR("Velocity not set or invalid velocity set");
215290
return -EINVAL;
216291
}
217292

218293
K_SPINLOCK(&data->lock) {
219294
data->run_mode = STEPPER_RUN_MODE_POSITION;
220295
data->step_count = value - data->actual_position;
296+
config->timing_source->update(dev, data->max_velocity);
221297
update_direction_from_step_count(dev);
222-
(void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT);
298+
config->timing_source->start(dev);
223299
}
224300

225301
return 0;
226302
}
227303

228304
int step_dir_stepper_common_is_moving(const struct device *dev, bool *is_moving)
229305
{
230-
struct step_dir_stepper_common_data *data = dev->data;
306+
const struct step_dir_stepper_common_config *config = dev->config;
231307

232-
*is_moving = k_work_delayable_is_pending(&data->stepper_dwork);
308+
*is_moving = config->timing_source->is_running(dev);
233309
return 0;
234310
}
235311

236312
int step_dir_stepper_common_run(const struct device *dev, const enum stepper_direction direction,
237313
const uint32_t velocity)
238314
{
239315
struct step_dir_stepper_common_data *data = dev->data;
316+
const struct step_dir_stepper_common_config *config = dev->config;
240317

241318
K_SPINLOCK(&data->lock) {
242319
data->run_mode = STEPPER_RUN_MODE_VELOCITY;
243320
data->direction = direction;
321+
data->max_velocity = velocity;
322+
config->timing_source->update(dev, velocity);
244323
if (velocity != 0) {
245-
data->delay_in_us = USEC_PER_SEC / velocity;
246-
(void)k_work_reschedule(&data->stepper_dwork, K_NO_WAIT);
324+
config->timing_source->start(dev);
247325
} else {
248-
(void)k_work_cancel_delayable(&data->stepper_dwork);
326+
config->timing_source->stop(dev);
249327
}
250328
}
251329

0 commit comments

Comments
 (0)