samples: stepper: add generic stepper sample

Add a generic stepper sample with gpio-stepper

Signed-off-by: Jilay Pandya <jilay.pandya@outlook.com>
diff --git a/drivers/stepper/Kconfig.gpio b/drivers/stepper/Kconfig.gpio
index 99e3505..4b43140 100644
--- a/drivers/stepper/Kconfig.gpio
+++ b/drivers/stepper/Kconfig.gpio
@@ -2,13 +2,7 @@
 # SPDX-FileCopyrightText: Copyright (c) 2024 Jilay Sandeep Pandya
 # SPDX-License-Identifier: Apache-2.0
 
-menu "GPIO stepper driver"
-
 config GPIO_STEPPER
 	bool "Activate driver for gpio stepper control"
 	depends on DT_HAS_ZEPHYR_GPIO_STEPPER_ENABLED
 	default y
-	help
-	  GPIO Stepper driver for stepper motor control with darlington arrays or dual H-bridge.
-
-endmenu
diff --git a/samples/drivers/stepper/generic/CMakeLists.txt b/samples/drivers/stepper/generic/CMakeLists.txt
new file mode 100644
index 0000000..3139442
--- /dev/null
+++ b/samples/drivers/stepper/generic/CMakeLists.txt
@@ -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})
diff --git a/samples/drivers/stepper/generic/Kconfig b/samples/drivers/stepper/generic/Kconfig
new file mode 100644
index 0000000..a9259ff
--- /dev/null
+++ b/samples/drivers/stepper/generic/Kconfig
@@ -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"
diff --git a/samples/drivers/stepper/generic/README.rst b/samples/drivers/stepper/generic/README.rst
new file mode 100644
index 0000000..be4bb7b
--- /dev/null
+++ b/samples/drivers/stepper/generic/README.rst
@@ -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>
diff --git a/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay b/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay
new file mode 100644
index 0000000..e392a02
--- /dev/null
+++ b/samples/drivers/stepper/generic/boards/nucleo_g071rb.overlay
@@ -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 */
+	};
+};
diff --git a/samples/drivers/stepper/generic/prj.conf b/samples/drivers/stepper/generic/prj.conf
new file mode 100644
index 0000000..7b579de
--- /dev/null
+++ b/samples/drivers/stepper/generic/prj.conf
@@ -0,0 +1,3 @@
+CONFIG_STEPPER=y
+CONFIG_LOG=y
+CONFIG_INPUT=y
diff --git a/samples/drivers/stepper/generic/sample.yaml b/samples/drivers/stepper/generic/sample.yaml
new file mode 100644
index 0000000..765b3b3
--- /dev/null
+++ b/samples/drivers/stepper/generic/sample.yaml
@@ -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
diff --git a/samples/drivers/stepper/generic/src/main.c b/samples/drivers/stepper/generic/src/main.c
new file mode 100644
index 0000000..1262ecd
--- /dev/null
+++ b/samples/drivers/stepper/generic/src/main.c
@@ -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);