Cleanup interface and a4988 implementation

Change step frequency from step/ms to step/s
Add DRV8825 stepper driver
This commit is contained in:
Philippe Leduc 2023-10-15 10:10:29 +02:00 committed by Xiang Xiao
parent 444b6d9635
commit 98b15b1409
10 changed files with 517 additions and 37 deletions

View File

@ -32,3 +32,7 @@ endif()
if(CONFIG_STEPPER_A4988)
target_sources(drivers PRIVATE a4988.c)
endif()
if(CONFIG_STEPPER_DRV8825)
target_sources(drivers PRIVATE drv8825.c)
endif()

View File

@ -81,4 +81,10 @@ config STEPPER_A4988
---help---
Enables A4988 stepper driver.
config STEPPER_DRV8825
bool "DRV8825 Stepper Motor Driver"
default n
---help---
Enables DRV8825 stepper driver.
endif # STEPPER

View File

@ -40,6 +40,9 @@ ifeq ($(CONFIG_STEPPER_A4988),y)
CSRCS += a4988.c
endif
ifeq ($(CONFIG_STEPPER_DRV8825),y)
CSRCS += drv8825.c
endif
# Include motor drivers in the build

View File

@ -39,7 +39,6 @@
struct a4988_dev_s
{
FAR struct a4988_ops_s *ops; /* A4988 ops */
int32_t position;
uint8_t auto_idle; /* If true, go in idle mode between movement */
};
@ -51,8 +50,7 @@ static int a4988_setup(FAR struct stepper_lowerhalf_s *dev);
static int a4988_shutdown(FAR struct stepper_lowerhalf_s *dev);
static int a4988_work(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_job_s const *param);
static int a4988_state(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_state_s *state);
static int a4988_update_status(FAR struct stepper_lowerhalf_s *dev);
static int a4988_clear(FAR struct stepper_lowerhalf_s *dev, uint8_t fault);
static int a4988_idle(FAR struct stepper_lowerhalf_s *dev, uint8_t idle);
static int a4988_microstepping(FAR struct stepper_lowerhalf_s *dev,
@ -69,7 +67,7 @@ static const struct stepper_ops_s g_a4988_ops =
a4988_setup, /* setup */
a4988_shutdown, /* shutdown */
a4988_work, /* work */
a4988_state, /* state */
a4988_update_status, /* update status */
a4988_clear, /* clear */
a4988_idle, /* idle */
a4988_microstepping, /* microstepping */
@ -114,12 +112,15 @@ static int a4988_work(FAR struct stepper_lowerhalf_s *dev,
/* Compute delay between pulse */
delay = USEC_PER_MSEC / job->speed;
delay = USEC_PER_SEC / job->speed;
if (delay < 1)
{
delay = 1;
stpwarn("Delay is clamped to 1 us\n");
}
stpinfo("Delay is %ld us\n", delay);
/* Set direction */
if (job->steps > 0)
@ -139,6 +140,7 @@ static int a4988_work(FAR struct stepper_lowerhalf_s *dev,
usleep(USEC_PER_MSEC);
}
dev->status.state = STEPPER_STATE_RUN;
for (int32_t i = 0; i < count; ++i)
{
priv->ops->step(true);
@ -147,30 +149,32 @@ static int a4988_work(FAR struct stepper_lowerhalf_s *dev,
up_udelay(delay);
}
dev->status.state = STEPPER_STATE_READY;
if (priv->auto_idle)
{
priv->ops->idle(true);
}
/* Update position */
/* Update steps done (a4988 cannot detect miss steps) */
priv->position += job->steps;
dev->status.position += job->steps;
return 0;
}
static int a4988_state(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_state_s *state)
static int a4988_update_status(FAR struct stepper_lowerhalf_s *dev)
{
FAR struct a4988_dev_s *priv = (FAR struct a4988_dev_s *)dev->priv;
state->position = priv->position;
/* No state to fetch */
return 0;
}
static int a4988_clear(FAR struct stepper_lowerhalf_s *dev, uint8_t fault)
{
return -ENOSYS;
/* No fault to clear ever */
return 0;
}
static int a4988_idle(FAR struct stepper_lowerhalf_s *dev, uint8_t idle)
@ -188,11 +192,13 @@ static int a4988_idle(FAR struct stepper_lowerhalf_s *dev, uint8_t idle)
if (idle == STEPPER_ENABLE_IDLE)
{
priv->ops->idle(true);
dev->status.state = STEPPER_STATE_IDLE;
}
else
{
priv->ops->idle(false);
usleep(USEC_PER_MSEC);
dev->status.state = STEPPER_STATE_READY;
}
return 0;
@ -274,7 +280,6 @@ int a4988_register(FAR const char *devpath, FAR struct a4988_ops_s *ops)
}
priv->ops = ops;
priv->position = 0;
lower = kmm_malloc(sizeof(struct stepper_lowerhalf_s));
if (priv == NULL)
@ -285,9 +290,9 @@ int a4988_register(FAR const char *devpath, FAR struct a4988_ops_s *ops)
}
lower->priv = priv;
lower->state.fault = STEPPER_FAULT_CLEAR;
lower->state.state = STEPPER_STATE_INIT;
lower->state.position = 0;
lower->status.fault = STEPPER_FAULT_CLEAR;
lower->status.state = STEPPER_STATE_READY;
lower->status.position = 0;
lower->ops = &g_a4988_ops;
/* Initialize lower layer (only once) */

346
drivers/motor/drv8825.c Normal file
View File

@ -0,0 +1,346 @@
/****************************************************************************
* drivers/motor/drv8825.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/kmalloc.h>
#include <nuttx/motor/drv8825.h>
#include <errno.h>
#include <debug.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct drv8825_dev_s
{
FAR struct drv8825_ops_s *ops; /* drv8825 ops */
uint8_t auto_idle; /* If true, go in idle mode between movement */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int drv8825_setup(FAR struct stepper_lowerhalf_s *dev);
static int drv8825_shutdown(FAR struct stepper_lowerhalf_s *dev);
static int drv8825_work(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_job_s const *param);
static int drv8825_update_status(FAR struct stepper_lowerhalf_s *dev);
static int drv8825_clear(FAR struct stepper_lowerhalf_s *dev, uint8_t fault);
static int drv8825_idle(FAR struct stepper_lowerhalf_s *dev, uint8_t idle);
static int drv8825_microstepping(FAR struct stepper_lowerhalf_s *dev,
uint16_t resolution);
static int drv8825_ioctl(FAR struct stepper_lowerhalf_s *dev, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct stepper_ops_s g_drv8825_ops =
{
drv8825_setup, /* setup */
drv8825_shutdown, /* shutdown */
drv8825_work, /* work */
drv8825_update_status, /* update status */
drv8825_clear, /* clear */
drv8825_idle, /* idle */
drv8825_microstepping, /* microstepping */
drv8825_ioctl /* ioctl */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int drv8825_setup(FAR struct stepper_lowerhalf_s *dev)
{
FAR struct drv8825_dev_s *priv = (FAR struct drv8825_dev_s *)dev->priv;
priv->ops->idle(false);
priv->ops->enable(true);
return 0;
}
static int drv8825_shutdown(FAR struct stepper_lowerhalf_s *dev)
{
FAR struct drv8825_dev_s *priv = (FAR struct drv8825_dev_s *)dev->priv;
priv->ops->idle(true);
priv->ops->enable(false);
return 0;
}
static int drv8825_work(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_job_s const *job)
{
FAR struct drv8825_dev_s *priv = (FAR struct drv8825_dev_s *)dev->priv;
int delay;
int count;
if (priv->ops->fault())
{
/* In fault: do not proceed */
return -EIO;
}
if (job->steps == 0)
{
/* Nothing to do */
return 0;
}
/* Compute delay between pulse */
delay = USEC_PER_SEC / job->speed;
if (delay < 2)
{
delay = 2;
stpwarn("Delay is clamped to 2 us\n");
}
stpinfo("Delay is %ld us\n", delay);
/* Set direction */
if (job->steps > 0)
{
priv->ops->direction(true);
count = job->steps;
}
else
{
priv->ops->direction(false);
count = -job->steps;
}
if (priv->auto_idle)
{
priv->ops->idle(false);
usleep(USEC_PER_MSEC * 2);
}
dev->status.state = STEPPER_STATE_RUN;
for (int32_t i = 0; i < count; ++i)
{
priv->ops->step(true);
up_udelay(2);
priv->ops->step(false);
up_udelay(delay);
}
dev->status.state = STEPPER_STATE_READY;
if (priv->auto_idle)
{
priv->ops->idle(true);
}
/* Update steps done (drv8825 cannot detect miss steps) */
dev->status.position += job->steps;
return 0;
}
static int drv8825_update_status(FAR struct stepper_lowerhalf_s *dev)
{
FAR struct drv8825_dev_s *priv = (FAR struct drv8825_dev_s *)dev->priv;
if (priv->ops->fault())
{
/* The same pin is used for overtemp and overcurrent fault.
* Since the current implementation is blocking and fetching
* is on demand (no interrupt), it is impossible to detect
* overcurrent.
*/
dev->status.fault = STEPPER_FAULT_OVERTEMP;
dev->status.state = STEPPER_STATE_FAULT;
}
return 0;
}
static int drv8825_clear(FAR struct stepper_lowerhalf_s *dev, uint8_t fault)
{
/* No fault to clear ever */
dev->status.fault &= ~fault;
if (dev->status.fault == STEPPER_FAULT_CLEAR)
{
dev->status.state = STEPPER_STATE_READY;
}
return 0;
}
static int drv8825_idle(FAR struct stepper_lowerhalf_s *dev, uint8_t idle)
{
FAR struct drv8825_dev_s *priv = (FAR struct drv8825_dev_s *)dev->priv;
if (idle == STEPPER_AUTO_IDLE)
{
priv->auto_idle = true;
return 0;
}
priv->auto_idle = false;
if (idle == STEPPER_ENABLE_IDLE)
{
priv->ops->idle(true);
dev->status.state = STEPPER_STATE_IDLE;
}
else
{
priv->ops->idle(false);
usleep(USEC_PER_MSEC * 2);
dev->status.state = STEPPER_STATE_READY;
}
return 0;
}
static int drv8825_microstepping(FAR struct stepper_lowerhalf_s *dev,
uint16_t resolution)
{
FAR struct drv8825_dev_s *priv = (FAR struct drv8825_dev_s *)dev->priv;
switch (resolution)
{
case 1:
{
priv->ops->microstepping(false, false, false);
}
break;
case 2:
{
priv->ops->microstepping(true, false, false);
}
break;
case 4:
{
priv->ops->microstepping(false, true, false);
}
break;
case 8:
{
priv->ops->microstepping(true, true, false);
}
break;
case 16:
{
priv->ops->microstepping(false, false, true);
}
break;
case 32:
{
priv->ops->microstepping(true, true, true);
}
break;
default:
{
return -EINVAL;
}
}
return 0;
}
static int drv8825_ioctl(FAR struct stepper_lowerhalf_s *dev, int cmd,
unsigned long arg)
{
return -ENOSYS;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int drv8825_register(FAR const char *devpath, FAR struct drv8825_ops_s *ops)
{
FAR struct drv8825_dev_s *priv;
FAR struct stepper_lowerhalf_s *lower;
int ret = 0;
/* Sanity check */
DEBUGASSERT(ops != NULL);
/* Initialize the drv8825 dev structure */
priv = kmm_malloc(sizeof(struct drv8825_dev_s));
if (priv == NULL)
{
stperr("Failed to allocate instance\n");
return -ENOMEM;
}
priv->ops = ops;
lower = kmm_malloc(sizeof(struct stepper_lowerhalf_s));
if (priv == NULL)
{
stperr("Failed to allocate instance\n");
kmm_free(priv);
return -ENOMEM;
}
lower->priv = priv;
lower->status.fault = STEPPER_FAULT_CLEAR;
lower->status.state = STEPPER_STATE_READY;
lower->status.position = 0;
lower->ops = &g_drv8825_ops;
/* Initialize lower layer (only once) */
priv->ops->initialize();
/* Register the character driver */
ret = stepper_register(devpath, lower);
if (ret < 0)
{
stperr("Failed to register driver: %d\n", ret);
kmm_free(priv);
kmm_free(lower);
return ret;
}
stpinfo("drv8825 registered at %s\n", devpath);
return ret;
}

View File

@ -168,23 +168,24 @@ static ssize_t stepper_read(FAR struct file *filep, FAR char *buffer,
FAR struct inode *inode = filep->f_inode;
FAR struct stepper_upperhalf_s *stepper = inode->i_private;
FAR struct stepper_lowerhalf_s *lower = stepper->lower;
FAR struct stepper_state_s *state;
FAR struct stepper_status_s *status;
int ret;
if (buflen != sizeof(struct stepper_state_s))
if (buflen != sizeof(struct stepper_status_s))
{
return -EINVAL;
}
state = (FAR struct stepper_state_s *)buffer;
ret = lower->ops->state(lower, state);
status = (FAR struct stepper_status_s *)buffer;
ret = lower->ops->update_status(lower);
if (ret < 0)
{
stperr("Get stepper state failed: %d\n", ret);
stperr("Update stepper state failed: %d\n", ret);
return ret;
}
return sizeof(struct stepper_state_s);
*status = lower->status;
return sizeof(struct stepper_status_s);
}
/****************************************************************************
@ -261,6 +262,15 @@ static int stepper_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
}
break;
case STEPIOC_SET_CURRENT_POS:
{
lower->status.position = (int32_t)arg;
ret = 0;
stpinfo("STEPIOC_SET_CURRENT_POS: new position is %ld\n",
lower->status.position);
}
break;
default:
{
ret = lower->ops->ioctl(lower, cmd, arg);

View File

@ -83,7 +83,7 @@ extern "C"
* Register the a4988 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/pwrmntr0"
* devpath - The full path to the driver to register. E.g., "/dev/stepper0"
* Returned Value:
* ops - operations on the concrete hardware
* Zero (OK) on success; a negated errno value on failure.

View File

@ -0,0 +1,105 @@
/****************************************************************************
* include/nuttx/motor/drv8825.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_MOTOR_DRV8825_H
#define __INCLUDE_NUTTX_MOTOR_DRV8825_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/motor/stepper.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if defined(CONFIG_STEPPER_DRV8825)
/****************************************************************************
* Public Types
****************************************************************************/
struct drv8825_ops_s
{
/* Initialize the control, called at register step */
CODE void (*initialize)(void);
/* Control step output */
CODE void (*step)(int level);
/* Direction */
CODE void (*direction)(int level);
/* Configure microstepping */
CODE void (*microstepping)(int ms1, int ms2, int ms3);
/* Enable control */
CODE void (*enable)(int level);
/* Idle control */
CODE void (*idle)(int level);
/* Fault fetch */
CODE int (*fault)(void);
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: drv8825_register
*
* Description:
* Register the drv8825 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/stepper0"
* Returned Value:
* ops - operations on the concrete hardware
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int drv8825_register(FAR const char *devpath, FAR struct drv8825_ops_s *ops);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_STEPPER_DRV8825 */
#endif /* __INCLUDE_NUTTX_DRIVERS_MOTOR_DRV8825_H */

View File

@ -61,8 +61,9 @@ enum stepper_state_e
{
STEPPER_STATE_INIT = 0, /* Initial state */
STEPPER_STATE_IDLE = 1, /* IDLE state */
STEPPER_STATE_RUN = 2, /* Run state */
STEPPER_STATE_FAULT = 3 /* Fault state */
STEPPER_STATE_READY = 2, /* Ready to work */
STEPPER_STATE_RUN = 3, /* Run state */
STEPPER_STATE_FAULT = 4 /* Fault state */
};
/* Stepper driver fault type */
@ -89,13 +90,13 @@ enum stepper_idle_e
STEPPER_AUTO_IDLE = 2, /* Set automaticaly IDLE when stepper not in movement */
};
/* Stepper driver state */
/* Stepper driver status */
struct stepper_state_s
struct stepper_status_s
{
uint8_t state; /* Stepper driver state */
uint8_t fault; /* Stepper driver faults */
int32_t position; /* Feedback from motor - absolute position */
int32_t position; /* Current absolute position */
};
/* Stepper parameters. */
@ -103,7 +104,7 @@ struct stepper_state_s
struct stepper_job_s
{
int32_t steps; /* Steps to do. Position: CW, Negative: CCW */
uint16_t speed; /* Stepper speed in step/ms */
uint32_t speed; /* Stepper speed in step/s */
};
/* Stepper operations used to call from the upper-half, generic stepper driver
@ -121,15 +122,14 @@ struct stepper_ops_s
CODE int (*shutdown)(FAR struct stepper_lowerhalf_s *dev);
/* work */
/* Work */
CODE int (*work)(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_job_s const *param);
/* Get motor state */
/* Update motor/driver status */
CODE int (*state)(FAR struct stepper_lowerhalf_s *dev,
FAR struct stepper_state_s *state);
CODE int (*update_status)(FAR struct stepper_lowerhalf_s *dev);
/* Clear fault state */
@ -158,7 +158,7 @@ struct stepper_lowerhalf_s
{
FAR const struct stepper_ops_s *ops; /* Arch-specific operations */
struct stepper_job_s param; /* Motor settings */
struct stepper_state_s state; /* Motor state */
struct stepper_status_s status; /* Motor status */
FAR void *priv; /* Private data */
};

View File

@ -33,8 +33,9 @@
* Pre-processor Definitions
****************************************************************************/
#define STEPIOC_IDLE _STEPIOC(1)
#define STEPIOC_CLEAR_FAULT _STEPIOC(2)
#define STEPIOC_MICROSTEPPING _STEPIOC(3)
#define STEPIOC_IDLE _STEPIOC(1)
#define STEPIOC_CLEAR_FAULT _STEPIOC(2)
#define STEPIOC_MICROSTEPPING _STEPIOC(3)
#define STEPIOC_SET_CURRENT_POS _STEPIOC(4)
#endif /* __INCLUDE_NUTTX_MOTOR_STEPPER_IOCTL_H */