1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2024 Navimatix GmbH
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT ti_drv8424
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/drivers/stepper.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/stepper/stepper_drv8424.h>
12 #include "../step_dir/step_dir_stepper_common.h"
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(drv8424, CONFIG_STEPPER_LOG_LEVEL);
16
17 /**
18 * @brief DRV8424 stepper driver configuration data.
19 *
20 * This structure contains all of the devicetree specifications for the pins
21 * needed by a given DRV8424 stepper driver.
22 */
23 struct drv8424_config {
24 struct step_dir_stepper_common_config common;
25 struct gpio_dt_spec sleep_pin;
26 struct gpio_dt_spec en_pin;
27 struct gpio_dt_spec m0_pin;
28 struct gpio_dt_spec m1_pin;
29 };
30
31 /* Struct for storing the states of output pins. */
32 struct drv8424_pin_states {
33 uint8_t sleep: 1;
34 uint8_t en: 1;
35 uint8_t m0: 2;
36 uint8_t m1: 2;
37 };
38
39 /**
40 * @brief DRV8424 stepper driver data.
41 *
42 * This structure contains mutable data used by a DRV8424 stepper driver.
43 */
44 struct drv8424_data {
45 const struct step_dir_stepper_common_data common;
46 bool enabled;
47 struct drv8424_pin_states pin_states;
48 enum stepper_micro_step_resolution ustep_res;
49 };
50
51 STEP_DIR_STEPPER_STRUCT_CHECK(struct drv8424_config, struct drv8424_data);
52
drv8424_set_microstep_pin(const struct device * dev,const struct gpio_dt_spec * pin,int value)53 static int drv8424_set_microstep_pin(const struct device *dev, const struct gpio_dt_spec *pin,
54 int value)
55 {
56 int ret;
57
58 /* Reset microstep pin as it may have been disconnected. */
59 ret = gpio_pin_configure_dt(pin, GPIO_OUTPUT_INACTIVE);
60 if (ret != 0) {
61 LOG_ERR("%s: Failed to reset micro-step pin (error: %d)", dev->name, ret);
62 return ret;
63 }
64
65 /* Set microstep pin */
66 switch (value) {
67 case 0:
68 ret = gpio_pin_set_dt(pin, 0);
69 break;
70 case 1:
71 ret = gpio_pin_set_dt(pin, 1);
72 break;
73 case 2:
74 /* Hi-Z is set by configuring pin as disconnected, not
75 * all gpio controllers support this.
76 */
77 ret = gpio_pin_configure_dt(pin, GPIO_DISCONNECTED);
78 break;
79 default:
80 break;
81 }
82
83 if (ret != 0) {
84 LOG_ERR("%s: Failed to set micro-step pin (error: %d)", dev->name, ret);
85 return ret;
86 }
87 return 0;
88 }
89
90 /*
91 * If microstep setter fails, attempt to recover into previous state.
92 */
drv8424_microstep_recovery(const struct device * dev)93 int drv8424_microstep_recovery(const struct device *dev)
94 {
95 const struct drv8424_config *config = dev->config;
96 struct drv8424_data *data = dev->data;
97 int ret;
98
99 uint8_t m0_value = data->pin_states.m0;
100 uint8_t m1_value = data->pin_states.m1;
101
102 ret = drv8424_set_microstep_pin(dev, &config->m0_pin, m0_value);
103 if (ret != 0) {
104 LOG_ERR("%s: Failed to restore microstep configuration (error: %d)", dev->name,
105 ret);
106 return ret;
107 }
108
109 ret = drv8424_set_microstep_pin(dev, &config->m1_pin, m1_value);
110 if (ret != 0) {
111 LOG_ERR("%s: Failed to restore microstep configuration (error: %d)", dev->name,
112 ret);
113 return ret;
114 }
115
116 return 0;
117 }
118
drv8424_enable(const struct device * dev,bool enable)119 static int drv8424_enable(const struct device *dev, bool enable)
120 {
121 int ret;
122 const struct drv8424_config *config = dev->config;
123 struct drv8424_data *data = dev->data;
124 bool has_enable_pin = config->en_pin.port != NULL;
125 bool has_sleep_pin = config->sleep_pin.port != NULL;
126
127 /* Check availability of sleep and enable pins, as these might be hardwired. */
128 if (!has_sleep_pin && !has_enable_pin) {
129 LOG_ERR("%s: Failed to enable/disable device, neither sleep pin nor enable pin are "
130 "available. The device is always on.",
131 dev->name);
132 return -ENOTSUP;
133 }
134
135 if (has_sleep_pin) {
136 ret = gpio_pin_set_dt(&config->sleep_pin, !enable);
137 if (ret != 0) {
138 LOG_ERR("%s: Failed to set sleep_pin (error: %d)", dev->name, ret);
139 return ret;
140 }
141 data->pin_states.sleep = enable ? 0U : 1U;
142 }
143
144 if (has_enable_pin) {
145 ret = gpio_pin_set_dt(&config->en_pin, enable);
146 if (ret != 0) {
147 LOG_ERR("%s: Failed to set en_pin (error: %d)", dev->name, ret);
148 return ret;
149 }
150 data->pin_states.en = enable ? 1U : 0U;
151 }
152
153 data->enabled = enable;
154 if (!enable) {
155 config->common.timing_source->stop(dev);
156 gpio_pin_set_dt(&config->common.step_pin, 0);
157 }
158
159 return 0;
160 }
161
drv8424_set_micro_step_res(const struct device * dev,enum stepper_micro_step_resolution micro_step_res)162 static int drv8424_set_micro_step_res(const struct device *dev,
163 enum stepper_micro_step_resolution micro_step_res)
164 {
165 const struct drv8424_config *config = dev->config;
166 struct drv8424_data *data = dev->data;
167 int ret;
168
169 uint8_t m0_value = 0;
170 uint8_t m1_value = 0;
171
172 /* 0: low
173 * 1: high
174 * 2: Hi-Z
175 * 3: 330kΩ
176 */
177 switch (micro_step_res) {
178 case STEPPER_MICRO_STEP_1:
179 m0_value = 0;
180 m1_value = 0;
181 break;
182 case STEPPER_MICRO_STEP_2:
183 m0_value = 2;
184 m1_value = 0;
185 break;
186 case STEPPER_MICRO_STEP_4:
187 m0_value = 0;
188 m1_value = 1;
189 break;
190 case STEPPER_MICRO_STEP_8:
191 m0_value = 1;
192 m1_value = 1;
193 break;
194 case STEPPER_MICRO_STEP_16:
195 m0_value = 2;
196 m1_value = 1;
197 break;
198 case STEPPER_MICRO_STEP_32:
199 m0_value = 0;
200 m1_value = 2;
201 break;
202 case STEPPER_MICRO_STEP_64:
203 m0_value = 2;
204 m1_value = 3;
205 break;
206 case STEPPER_MICRO_STEP_128:
207 m0_value = 2;
208 m1_value = 2;
209 break;
210 case STEPPER_MICRO_STEP_256:
211 m0_value = 1;
212 m1_value = 2;
213 break;
214 default:
215 return -EINVAL;
216 };
217
218 ret = drv8424_set_microstep_pin(dev, &config->m0_pin, m0_value);
219 if (ret != 0) {
220 return ret;
221 }
222
223 ret = drv8424_set_microstep_pin(dev, &config->m1_pin, m1_value);
224 if (ret != 0) {
225 return ret;
226 }
227
228 data->ustep_res = micro_step_res;
229 data->pin_states.m0 = m0_value;
230 data->pin_states.m1 = m1_value;
231
232 return 0;
233 }
234
drv8424_get_micro_step_res(const struct device * dev,enum stepper_micro_step_resolution * micro_step_res)235 static int drv8424_get_micro_step_res(const struct device *dev,
236 enum stepper_micro_step_resolution *micro_step_res)
237 {
238 struct drv8424_data *data = dev->data;
239 *micro_step_res = data->ustep_res;
240 return 0;
241 }
242
drv8424_move_to(const struct device * dev,int32_t target)243 static int drv8424_move_to(const struct device *dev, int32_t target)
244 {
245 struct drv8424_data *data = dev->data;
246
247 if (!data->enabled) {
248 LOG_ERR("Failed to move to target position, device is not enabled");
249 return -ECANCELED;
250 }
251
252 return step_dir_stepper_common_move_to(dev, target);
253 }
254
drv8424_move_by(const struct device * dev,int32_t steps)255 static int drv8424_move_by(const struct device *dev, int32_t steps)
256 {
257 struct drv8424_data *data = dev->data;
258
259 if (!data->enabled) {
260 LOG_ERR("Failed to move by delta, device is not enabled");
261 return -ECANCELED;
262 }
263
264 return step_dir_stepper_common_move_by(dev, steps);
265 }
266
drv8424_run(const struct device * dev,enum stepper_direction direction,uint32_t velocity)267 static int drv8424_run(const struct device *dev, enum stepper_direction direction,
268 uint32_t velocity)
269 {
270 struct drv8424_data *data = dev->data;
271
272 if (!data->enabled) {
273 LOG_ERR("Failed to run stepper, device is not enabled");
274 return -ECANCELED;
275 }
276
277 return step_dir_stepper_common_run(dev, direction, velocity);
278 }
279
drv8424_init(const struct device * dev)280 static int drv8424_init(const struct device *dev)
281 {
282 const struct drv8424_config *const config = dev->config;
283 struct drv8424_data *const data = dev->data;
284 int ret;
285
286 /* Configure sleep pin if it is available */
287 if (config->sleep_pin.port != NULL) {
288 ret = gpio_pin_configure_dt(&config->sleep_pin, GPIO_OUTPUT_ACTIVE);
289 if (ret != 0) {
290 LOG_ERR("%s: Failed to configure sleep_pin (error: %d)", dev->name, ret);
291 return ret;
292 }
293 data->pin_states.sleep = 1U;
294 }
295
296 /* Configure enable pin if it is available */
297 if (config->en_pin.port != NULL) {
298 ret = gpio_pin_configure_dt(&config->en_pin, GPIO_OUTPUT_INACTIVE);
299 if (ret != 0) {
300 LOG_ERR("%s: Failed to configure en_pin (error: %d)", dev->name, ret);
301 return ret;
302 }
303 data->pin_states.en = 0U;
304 }
305
306 /* Configure microstep pin 0 */
307 ret = gpio_pin_configure_dt(&config->m0_pin, GPIO_OUTPUT_INACTIVE);
308 if (ret != 0) {
309 LOG_ERR("%s: Failed to configure m0_pin (error: %d)", dev->name, ret);
310 return ret;
311 }
312 data->pin_states.m0 = 0U;
313
314 /* Configure microstep pin 1 */
315 ret = gpio_pin_configure_dt(&config->m1_pin, GPIO_OUTPUT_INACTIVE);
316 if (ret != 0) {
317 LOG_ERR("%s: Failed to configure m1_pin (error: %d)", dev->name, ret);
318 return ret;
319 }
320 data->pin_states.m1 = 0U;
321
322 ret = drv8424_set_micro_step_res(dev, data->ustep_res);
323 if (ret != 0) {
324 return ret;
325 }
326
327 ret = step_dir_stepper_common_init(dev);
328 if (ret != 0) {
329 LOG_ERR("Failed to initialize common step direction stepper (error: %d)", ret);
330 return ret;
331 }
332
333 return 0;
334 }
335
336 static DEVICE_API(stepper, drv8424_stepper_api) = {
337 .enable = drv8424_enable,
338 .move_by = drv8424_move_by,
339 .move_to = drv8424_move_to,
340 .is_moving = step_dir_stepper_common_is_moving,
341 .set_reference_position = step_dir_stepper_common_set_reference_position,
342 .get_actual_position = step_dir_stepper_common_get_actual_position,
343 .set_max_velocity = step_dir_stepper_common_set_max_velocity,
344 .run = drv8424_run,
345 .set_micro_step_res = drv8424_set_micro_step_res,
346 .get_micro_step_res = drv8424_get_micro_step_res,
347 .set_event_callback = step_dir_stepper_common_set_event_callback,
348 };
349
350 #define DRV8424_DEVICE(inst) \
351 \
352 static const struct drv8424_config drv8424_config_##inst = { \
353 .common = STEP_DIR_STEPPER_DT_INST_COMMON_CONFIG_INIT(inst), \
354 .sleep_pin = GPIO_DT_SPEC_INST_GET_OR(inst, sleep_gpios, {0}), \
355 .en_pin = GPIO_DT_SPEC_INST_GET_OR(inst, en_gpios, {0}), \
356 .m0_pin = GPIO_DT_SPEC_INST_GET(inst, m0_gpios), \
357 .m1_pin = GPIO_DT_SPEC_INST_GET(inst, m1_gpios), \
358 }; \
359 \
360 static struct drv8424_data drv8424_data_##inst = { \
361 .common = STEP_DIR_STEPPER_DT_INST_COMMON_DATA_INIT(inst), \
362 .ustep_res = DT_INST_PROP(inst, micro_step_res), \
363 }; \
364 \
365 DEVICE_DT_INST_DEFINE(inst, &drv8424_init, NULL, &drv8424_data_##inst, \
366 &drv8424_config_##inst, POST_KERNEL, CONFIG_STEPPER_INIT_PRIORITY, \
367 &drv8424_stepper_api);
368
369 DT_INST_FOREACH_STATUS_OKAY(DRV8424_DEVICE)
370