Browse Source
Add a generic stepper sample with gpio-stepper Signed-off-by: Jilay Pandya <jilay.pandya@outlook.com>pull/84672/head
8 changed files with 229 additions and 6 deletions
@ -0,0 +1,10 @@ |
|||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0) |
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) |
||||||
|
project(stepper_generic) |
||||||
|
|
||||||
|
FILE(GLOB app_sources src/*.c) |
||||||
|
target_sources(app PRIVATE ${app_sources}) |
@ -0,0 +1,26 @@ |
|||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
mainmenu "Stepper generic sample application" |
||||||
|
|
||||||
|
config STEPS_PER_REV |
||||||
|
int "Steps per revolution" |
||||||
|
default 200 |
||||||
|
|
||||||
|
config STEP_INTERVAL_NS |
||||||
|
int "Step interval (ns)" |
||||||
|
default 1000000 |
||||||
|
|
||||||
|
config PING_PONG_N_REV |
||||||
|
int "Change direction every N revolutions" |
||||||
|
default 1 |
||||||
|
|
||||||
|
config MONITOR_THREAD_TIMEOUT_MS |
||||||
|
int "Monitor thread timeout (ms)" |
||||||
|
default 1000 |
||||||
|
|
||||||
|
config MONITOR_THREAD_STACK_SIZE |
||||||
|
int "Monitor thread stack size" |
||||||
|
default 1024 |
||||||
|
|
||||||
|
source "Kconfig.zephyr" |
@ -0,0 +1,53 @@ |
|||||||
|
.. zephyr:code-sample:: stepper |
||||||
|
:name: Stepper |
||||||
|
:relevant-api: stepper_interface |
||||||
|
|
||||||
|
Rotate a stepper motor in 4 different modes. |
||||||
|
|
||||||
|
Description |
||||||
|
*********** |
||||||
|
|
||||||
|
This sample demonstrates how to use the stepper driver API to control a stepper motor. The sample |
||||||
|
spins the stepper and outputs the events to the console. |
||||||
|
|
||||||
|
The stepper spins in 4 different modes: ping_pong_relative, ping_pong_absolute, continuous_clockwise |
||||||
|
and continuous_anticlockwise. The micro-step interval in nanoseconds can be configured using the |
||||||
|
:kconfig:option:`CONFIG_STEP_INTERVAL_NS`. The sample also demonstrates how to use the stepper callback |
||||||
|
to change the direction of the stepper after a certain number of steps. |
||||||
|
|
||||||
|
Pressing any button should change the mode of the stepper. |
||||||
|
|
||||||
|
The sample also has a monitor thread that prints the actual position of the stepper motor every |
||||||
|
:kconfig:option:`CONFIG_MONITOR_THREAD_TIMEOUT_MS` milliseconds. |
||||||
|
|
||||||
|
Building and Running |
||||||
|
******************** |
||||||
|
|
||||||
|
This project spins the stepper and outputs the events to the console. |
||||||
|
|
||||||
|
.. zephyr-app-commands:: |
||||||
|
:zephyr-app: samples/drivers/stepper/generic |
||||||
|
:board: nucleo_g071rb |
||||||
|
:goals: build flash |
||||||
|
|
||||||
|
Sample Output |
||||||
|
============= |
||||||
|
|
||||||
|
.. code-block:: console |
||||||
|
|
||||||
|
*** Booting Zephyr OS build v4.0.0-5289-g0c368e85b117 *** |
||||||
|
Actual position: 910 |
||||||
|
Actual position: 1821 |
||||||
|
mode: ping pong absolute |
||||||
|
Actual position: 2410 |
||||||
|
mode: rotate cw |
||||||
|
Actual position: 2162 |
||||||
|
Actual position: 3073 |
||||||
|
mode: rotate ccw |
||||||
|
Actual position: 3793 |
||||||
|
mode: ping pong relative |
||||||
|
Actual position: 4607 |
||||||
|
Actual position: 5518 |
||||||
|
Actual position: 6428 |
||||||
|
|
||||||
|
<repeats endlessly> |
@ -0,0 +1,17 @@ |
|||||||
|
/ { |
||||||
|
aliases { |
||||||
|
stepper = &gpio_stepper; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
/ { |
||||||
|
gpio_stepper: gpio_stepper { |
||||||
|
compatible = "zephyr,gpio-stepper"; |
||||||
|
status = "okay"; |
||||||
|
micro-step-res = <2>; |
||||||
|
gpios = <&gpioa 9 GPIO_ACTIVE_HIGH>, /* D8 */ |
||||||
|
<&gpioc 7 GPIO_ACTIVE_HIGH>, /* D9 */ |
||||||
|
<&gpiob 0 GPIO_ACTIVE_HIGH>, /* D10 */ |
||||||
|
<&gpioa 7 GPIO_ACTIVE_HIGH>; /* D11 */ |
||||||
|
}; |
||||||
|
}; |
@ -0,0 +1,3 @@ |
|||||||
|
CONFIG_STEPPER=y |
||||||
|
CONFIG_LOG=y |
||||||
|
CONFIG_INPUT=y |
@ -0,0 +1,8 @@ |
|||||||
|
sample: |
||||||
|
name: Generic Stepper Sample |
||||||
|
tests: |
||||||
|
sample.drivers.stepper.generic: |
||||||
|
harness: stepper |
||||||
|
tags: stepper |
||||||
|
platform_allow: nucleo_g071rb |
||||||
|
depends_on: gpio input |
@ -0,0 +1,112 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: Copyright (c) 2025 Jilay Sandeep Pandya. |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/drivers/gpio.h> |
||||||
|
#include <zephyr/drivers/stepper.h> |
||||||
|
#include <zephyr/input/input.h> |
||||||
|
#include <zephyr/kernel.h> |
||||||
|
|
||||||
|
static const struct device *stepper = DEVICE_DT_GET(DT_ALIAS(stepper)); |
||||||
|
|
||||||
|
enum stepper_mode { |
||||||
|
STEPPER_MODE_PING_PONG_RELATIVE, |
||||||
|
STEPPER_MODE_PING_PONG_ABSOLUTE, |
||||||
|
STEPPER_MODE_ROTATE_CW, |
||||||
|
STEPPER_MODE_ROTATE_CCW, |
||||||
|
}; |
||||||
|
|
||||||
|
static atomic_t stepper_mode = ATOMIC_INIT(STEPPER_MODE_PING_PONG_RELATIVE); |
||||||
|
|
||||||
|
static int32_t ping_pong_target_position = |
||||||
|
CONFIG_STEPS_PER_REV * CONFIG_PING_PONG_N_REV * DT_PROP(DT_ALIAS(stepper), micro_step_res); |
||||||
|
|
||||||
|
static K_SEM_DEFINE(stepper_generic_sem, 0, 1); |
||||||
|
|
||||||
|
static void stepper_callback(const struct device *dev, const enum stepper_event event, |
||||||
|
void *user_data) |
||||||
|
{ |
||||||
|
switch (event) { |
||||||
|
case STEPPER_EVENT_STEPS_COMPLETED: |
||||||
|
k_sem_give(&stepper_generic_sem); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void button_pressed(struct input_event *event, void *user_data) |
||||||
|
{ |
||||||
|
ARG_UNUSED(user_data); |
||||||
|
|
||||||
|
if (event->value == 0 && event->type == INPUT_EV_KEY) { |
||||||
|
return; |
||||||
|
} |
||||||
|
enum stepper_mode mode = atomic_get(&stepper_mode); |
||||||
|
|
||||||
|
if (mode == STEPPER_MODE_ROTATE_CCW) { |
||||||
|
atomic_set(&stepper_mode, STEPPER_MODE_PING_PONG_RELATIVE); |
||||||
|
} else { |
||||||
|
atomic_inc(&stepper_mode); |
||||||
|
} |
||||||
|
k_sem_give(&stepper_generic_sem); |
||||||
|
} |
||||||
|
|
||||||
|
INPUT_CALLBACK_DEFINE(NULL, button_pressed, NULL); |
||||||
|
|
||||||
|
int main(void) |
||||||
|
{ |
||||||
|
printf("Starting generic stepper sample\n"); |
||||||
|
if (!device_is_ready(stepper)) { |
||||||
|
printf("Device %s is not ready\n", stepper->name); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
printf("stepper is %p, name is %s\n", stepper, stepper->name); |
||||||
|
|
||||||
|
stepper_set_event_callback(stepper, stepper_callback, NULL); |
||||||
|
stepper_enable(stepper, true); |
||||||
|
stepper_set_reference_position(stepper, 0); |
||||||
|
stepper_set_microstep_interval(stepper, CONFIG_STEP_INTERVAL_NS); |
||||||
|
stepper_move_by(stepper, ping_pong_target_position); |
||||||
|
|
||||||
|
for (;;) { |
||||||
|
k_sem_take(&stepper_generic_sem, K_FOREVER); |
||||||
|
switch (atomic_get(&stepper_mode)) { |
||||||
|
case STEPPER_MODE_ROTATE_CW: |
||||||
|
stepper_run(stepper, STEPPER_DIRECTION_POSITIVE); |
||||||
|
printf("mode: rotate cw\n"); |
||||||
|
break; |
||||||
|
case STEPPER_MODE_ROTATE_CCW: |
||||||
|
stepper_run(stepper, STEPPER_DIRECTION_NEGATIVE); |
||||||
|
printf("mode: rotate ccw\n"); |
||||||
|
break; |
||||||
|
case STEPPER_MODE_PING_PONG_RELATIVE: |
||||||
|
ping_pong_target_position *= -1; |
||||||
|
stepper_move_by(stepper, ping_pong_target_position); |
||||||
|
printf("mode: ping pong relative\n"); |
||||||
|
break; |
||||||
|
case STEPPER_MODE_PING_PONG_ABSOLUTE: |
||||||
|
ping_pong_target_position *= -1; |
||||||
|
stepper_move_to(stepper, ping_pong_target_position); |
||||||
|
printf("mode: ping pong absolute\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void monitor_thread(void) |
||||||
|
{ |
||||||
|
for (;;) { |
||||||
|
int32_t actual_position; |
||||||
|
|
||||||
|
stepper_get_actual_position(stepper, &actual_position); |
||||||
|
printf("Actual position: %d\n", actual_position); |
||||||
|
k_sleep(K_MSEC(CONFIG_MONITOR_THREAD_TIMEOUT_MS)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
K_THREAD_DEFINE(monitor_tid, CONFIG_MONITOR_THREAD_STACK_SIZE, monitor_thread, NULL, NULL, NULL, 5, |
||||||
|
0, 0); |
Loading…
Reference in new issue