grovemd: add support for 'mode1' stepping and add stepper examples

The current grovemd driver supported 'mode2' stepping, where the
driver simply passed various stepper commands to the board for it to
carry out on it's own.

This doesn't work very well (or at all if you have old/buggy firmware)
so add a new 'mode1' stepper capability.  This mode lets the driver
manually control the stepping operation without requiring special
firmware.

This is now the default and recommended mode to use for stepper motors
on this device.  It is also more flexible in terms of the maximum
number of steps you can do (mode2 was limited to 254 steps max).

This was tested using a bipolar NEMA-17 stepper motor with an
external 12v power supply.

Note: 'Mode1' and 'Mode2' are the Seeed Studio terms for these different
stepping modes.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
Signed-off-by: Mihai Tudor Panu <mihai.tudor.panu@intel.com>
This commit is contained in:
Jon Trulson
2015-09-16 17:33:23 -06:00
committed by Mihai Tudor Panu
parent d94a6d00fb
commit 83e62aabba
6 changed files with 440 additions and 49 deletions

View File

@ -33,39 +33,36 @@ using namespace upm;
using namespace std;
GroveMD::GroveMD(int bus, uint8_t address)
GroveMD::GroveMD(int bus, uint8_t address) :
m_i2c(bus)
{
m_addr = address;
// setup our i2c link
if ( !(m_i2c = mraa_i2c_init(bus)) )
{
throw std::invalid_argument(std::string(__FUNCTION__) +
": mraa_i2c_init() failed");
return;
}
// this board *requires* 100Khz i2c bus only
mraa_result_t rv;
if ( (rv = mraa_i2c_frequency(m_i2c, MRAA_I2C_STD)) != MRAA_SUCCESS )
mraa::Result rv;
if ( (rv = m_i2c.frequency(mraa::I2C_STD)) != mraa::SUCCESS )
{
throw std::invalid_argument(std::string(__FUNCTION__) +
": mraa_i2c_frequency(MRAA_I2C_STD) failed");
": I2c.frequency(I2C_STD) failed");
return;
}
if (mraa_i2c_address(m_i2c, m_addr))
if (m_i2c.address(m_addr))
{
throw std::runtime_error(std::string(__FUNCTION__) +
": mraa_i2c_address() failed");
": I2c.address() failed");
return;
}
initClock();
// default to mode1 stepper operation, 200 steps per rev.
configStepper(200, STEP_MODE1);
}
GroveMD::~GroveMD()
{
setMotorSpeeds(0, 0);
mraa_i2c_stop(m_i2c);
writePacket(SET_DIRECTION, 0, GROVEMD_NOOP);
}
bool GroveMD::writePacket(REG_T reg, uint8_t data1, uint8_t data2)
@ -76,11 +73,10 @@ bool GroveMD::writePacket(REG_T reg, uint8_t data1, uint8_t data2)
buf[1] = data1;
buf[2] = data2;
mraa_result_t rv;
if ( (rv = mraa_i2c_address(m_i2c, m_addr)) != MRAA_SUCCESS )
if ( m_i2c.write(buf, 3) != mraa::SUCCESS )
{
throw std::runtime_error(std::string(__FUNCTION__) +
": mraa_i2c_address() failed");
": I2c.write() failed");
return false;
}
@ -91,13 +87,6 @@ bool GroveMD::writePacket(REG_T reg, uint8_t data1, uint8_t data2)
usleep(100);
if ( (rv = mraa_i2c_write(m_i2c, buf, 3)) != MRAA_SUCCESS )
{
throw std::runtime_error(std::string(__FUNCTION__) +
": mraa_i2c_write() failed");
return false;
}
return true;
}
@ -119,25 +108,137 @@ bool GroveMD::setMotorDirections(DC_DIRECTION_T dirA, DC_DIRECTION_T dirB)
bool GroveMD::enableStepper(STEP_DIRECTION_T dir, uint8_t speed)
{
return writePacket(STEPPER_ENABLE, dir, speed);
// If mode 2, send the command and return immediately
if (m_stepMode == STEP_MODE2)
return writePacket(STEPPER_ENABLE, dir, speed);
// otherwise, mode 1, setup the basics and start stepping.
m_stepDelay = 60 * 1000 / m_stepsPerRev / speed;
m_stepDirection = ((dir == STEP_DIR_CW) ? 1 : -1);
// seeed says speed should always be 255,255 for stepper operation
setMotorSpeeds(255, 255);
while (m_totalSteps > 0)
{
if (getMillis() >= m_stepDelay)
{
// reset the clock
initClock();
m_currentStep += m_stepDirection;
if (m_stepDirection == 1)
{
if (m_currentStep >= m_stepsPerRev)
m_currentStep = 0;
}
else
{
if (m_currentStep <= 0)
m_currentStep = m_stepsPerRev;
}
m_totalSteps--;
stepperStep();
}
}
// and... we're done
return true;
}
bool GroveMD::disableStepper()
{
return writePacket(STEPPER_DISABLE, GROVEMD_NOOP, GROVEMD_NOOP);
if (m_stepMode == STEP_MODE2)
return writePacket(STEPPER_DISABLE, GROVEMD_NOOP, GROVEMD_NOOP);
// else, mode 1
writePacket(SET_DIRECTION, 0, GROVEMD_NOOP);
return setMotorSpeeds(0, 0);
}
bool GroveMD::setStepperSteps(uint8_t steps)
bool GroveMD::setStepperSteps(unsigned int steps)
{
if (steps == 0)
if (m_stepMode == STEP_MODE2)
{
// invalid
throw std::out_of_range(std::string(__FUNCTION__) +
": invalid number of steps. " +
"Valid values are between 1 and 255.");
return false;
if (steps == 0)
{
// invalid
throw std::out_of_range(std::string(__FUNCTION__) +
": invalid number of steps. " +
"Valid values are between 1 and 255.");
return false;
}
return writePacket(STEPPER_NUM_STEPS, steps, GROVEMD_NOOP);
}
return writePacket(STEPPER_NUM_STEPS, steps, GROVEMD_NOOP);
// for mode one, just store it for future use by enableStepper()
m_totalSteps = steps;
return true;
}
void GroveMD::initClock()
{
gettimeofday(&m_startTime, NULL);
}
uint32_t GroveMD::getMillis()
{
struct timeval elapsed, now;
uint32_t elapse;
// get current time
gettimeofday(&now, NULL);
// compute the delta since m_startTime
if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 )
{
elapsed.tv_usec += 1000000;
elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1;
}
else
{
elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec;
}
elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000));
// never return 0
if (elapse == 0)
elapse = 1;
return elapse;
}
void GroveMD::configStepper(unsigned int stepsPerRev, STEP_MODE_T mode)
{
m_stepsPerRev = stepsPerRev;
m_stepMode = mode;
m_currentStep = 0;
m_stepDelay = 0;
m_stepDirection = 1;
m_totalSteps = 0;
}
void GroveMD::stepperStep()
{
int step = m_currentStep % 4;
switch (step)
{
case 0:
writePacket(SET_DIRECTION, 0b0101, GROVEMD_NOOP);
break;
case 1:
writePacket(SET_DIRECTION, 0b0110, GROVEMD_NOOP);
break;
case 2:
writePacket(SET_DIRECTION, 0b1010, GROVEMD_NOOP);
break;
case 3:
writePacket(SET_DIRECTION, 0b1001, GROVEMD_NOOP);
break;
}
}