Pages: [1]   Go Down
Author Topic: Library problems  (Read 854 times)
0 Members and 1 Guest are viewing this topic.
Seattle WA
Offline Offline
Full Member
***
Karma: 1
Posts: 208
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm writing a library to support steppers and other goodness on the high current sixteen channel shift register controlled board I sell (http://www.logos-electro.com/ard-srg-ips4x4-1/). I'm having a terrible problem with what I think is the linker that's causing it to fail in my example code.

Here's the header file:
Code:
/*
  ShiftStepper.h - ShiftStepper library for Arduino
  v0.01a Alpha
  (c) Logos Electromechanical LLC 2010
  Licensed under CC-BY-SA 3.0

  This library is intended to drive Logos Electromechanical
  shift register boards

  This library is free software; you can redistribute it and/or
  modify it under the terms of the Creative Commons By-Attribution
  Share-Alike 3.0 Unported license.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

*/

#ifndef ShiftStepper_h
#define ShiftStepper_h

#include <stdint.h>
#include <inttypes.h>
#include <avr/io.h>
#include <inttypes.h>

#define __channelsPerMotor__      4
#define __maxMotorSpeed__            256

// static const uint8_t default4StepSequence[4] = {0x1, 0x2, 0x4, 0x8};
static const uint8_t default4StepSequence[4] = {0x2, 0x4, 0x1, 0x8};
static const uint8_t default8StepSequence[8] = {0x1,0x3,0x2,0x6,0x4,0xc,0x8,0x9};

class shiftDevice
{
      protected:      
            uint8_t       *channels;            // array of channels in the object relative to the board
            uint8_t       chanCount;            // number of channels per device, in device units
      public:
            virtual void       doDevTick (uint8_t bytesPerBoard, uint8_t *boardBytes);
            uint8_t             *getChannels (void);      // gets the byte array that holds the channels
            uint8_t             getChanCount (void);      // gets the number of channels, in object units
};      

class shiftStepMotor : public shiftDevice
{
      private:
            uint8_t            stepState;            // current index in the step sequence
            int16_t            stepSpeed;            // sign gives directions, 32768 - magnitude is
                                                      // the number of timer ticks per step
            uint16_t      lastStep;            // number of timer ticks since last step
            uint16_t      stepsToGo;            // number of steps to go until we finish the current set
                                                      // set to -1 for continuous rotation
            const uint8_t            *sequence;            // an array of pin configurations that correspond to the steps
            uint8_t            seqSteps;            // number of steps in the sequence
            uint8_t            channels[__channelsPerMotor__];            
                                                      // the array of channels in the board that correspond to the
                                                      // drive channels for this motor, measured in bits
            uint8_t            chanCount;            // number of channels in the array
            void             incrSeq (void); // increment the step sequence by 1
            void             decrSeq      (void); // decrement the step sequence by 1
      public:
            shiftStepMotor (const uint8_t seqSteps, const uint8_t *sequence, const uint8_t *channels);      
                                                                                                // creates a shiftStepMotor object
            uint8_t            incrStep (int8_t dir);                                    // go one step in the commanded direction
            uint8_t            doSteps (uint16_t steps, int16_t speed);            // do steps at speed
            uint8_t            setSpeed (int16_t speed);                              // set speed
            int16_t       getSpeed (void);                                          // get the current speed
            int16_t       getStepsToGo (void);                                    // get the number of steps to go in the current move
            void             doDevTick (uint8_t bytesPerBoard, uint8_t *boardBytes);
};

class shiftBoard
{
      protected:
            uint8_t                  byteCount;      // number of bytes of shift register on the board
            uint8_t                  firstChan;      // first active channel/byte/bit on the board
            uint8_t             lastChan;      // last active channel/byte/bit on the board
            shiftDevice            **devices;      // an array of pointers to all of the objects associated with this board
            uint8_t                  devCount;      // number of entries in the device array
      public:
            virtual void       doBoardTick (uint8_t chainSize, uint8_t firstByte, uint8_t *chainBytes);
            uint8_t                  getDevCount (void);                  // get the number of objects associated with the board
            shiftDevice       *getDev (uint8_t index);      // get object at index
            uint8_t                  getBoardSize (void);            // returns the size of the board's datastream, in bytes
};

class shiftSixteen : public shiftBoard
{
      public:
            shiftSixteen (uint8_t devCount, shiftDevice **devices);
            void doBoardTick(uint8_t chainSize, uint8_t firstByte, uint8_t *chainBytes);
};

class shiftChain
{
      private:
            shiftBoard      **boards;                  // array of pointers to the board objects
            uint8_t            boardCount;                  // number of boards in the chain
            uint8_t            byteCount;                  // nuber of bytes in the chain
            uint8_t            timerPrescaler;            // prescaler value to set timer frequency
            uint8_t            timerVal;                  // the value the timer starts off at after each tick
                                                            // together, these two values control the tick frequency
            uint8_t            timer;                        // the timer number we're using
            uint8_t            dataPort;                  // shift register data pin
            uint8_t            clockPort;                  // shift register clock pin
            uint8_t            latchPort;                  // shift register latch pin
            uint8_t            resetPort;
            uint8_t            dataPinMask;            // shift register data pin
            uint8_t            clockPinMask;            // shift register clock pin
            uint8_t            latchPinMask;            // shift register latch pin
            uint8_t            resetPinMask;            // shift register master reset pin
            volatile uint8_t       *clk;
            volatile uint8_t       *dat;
            volatile uint8_t       *lat;
            volatile uint8_t       *rst;
            void             fastShiftOut (uint8_t byteCount, uint8_t *bytes);
      public:
            shiftChain (uint8_t      boardCount,
                              shiftBoard      **boards,
                              uint8_t dataPin,
                              uint8_t clockPin,
                              uint8_t latchPin,
                              uint8_t resetPin);
            uint8_t            getBoardCount (void);
            shiftBoard      *getBoard (uint8_t index);
            uint8_t       startTimer (uint8_t prescaler, uint8_t timerVal, uint8_t timer);
            uint8_t            stopTimer (void);
            void             doTick (void);
};

// This is some strange linker food required to make it all work
// see http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=410870

__extension__ typedef int __guard __attribute__((mode (__DI__)));

extern "C" int __cxa_guard_acquire(__guard *);
extern "C" void __cxa_guard_release (__guard *);
extern "C" void __cxa_guard_abort (__guard *);

#endif

Continued in the next message...
Logged

Seattle WA
Offline Offline
Full Member
***
Karma: 1
Posts: 208
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the implementation, with some irrelevant stuff trimmed to meet length limits:

Code:
#include <stdio.h>
#include <stdint.h>
#include <avr/io.h>
#include <inttypes.h>
#include "wiring.h"
#include "wiring_private.h"
#include "pins_arduino.h"

#include "shiftStepper.h"

// defines for the sixteen channel board
#define __shiftSixteenBytes__       2
#define __shiftSixteenFirst__       0
#define __shiftSixteenLast__      15

/////////////////
// shiftDevice //
/////////////////

inline uint8_t *shiftDevice::getChannels (void) {
      return this->channels;
}

inline uint8_t shiftDevice::getChanCount (void) {
      return this->chanCount;
}

////////////////////
// shiftStepMotor //
////////////////////

// Constructor //

shiftStepMotor::shiftStepMotor (const uint8_t seqSteps,
      const uint8_t *sequence,
      const uint8_t *channels)
{
      uint8_t i;
      
      this->stepState       = 0;
      this->stepSpeed            = 0;
      this->lastStep            = 0;
      this->stepsToGo            = 0;
      this->seqSteps             = seqSteps;
      this->sequence            = sequence;
      this->chanCount            = __channelsPerMotor__;
      for (i=0; i < this->chanCount; i++)
      {
            this->channels[i] = channels[i];
      }
}

// Public Members //

uint8_t shiftStepMotor::incrStep (int8_t dir)
{
      
      if (this->stepsToGo)                                           // if it's currently in the process of
                                                                              // doing some steps, increment the count
      {
            if (this->stepsToGo < 0) { return 0; }            // if motor is currently set for continuous
                                                                              // rotation, do nothing
            if (this->stepSpeed > 0) {                              // if the motor is currently turning in the
                                                                              // positive direction...
                  if (dir > 0) { this->stepsToGo++; }      // if dir is positive, increment the count
                  else { this->stepsToGo--; }            // if it's non-positive, decrement
            } else if (this->stepSpeed < 0) {                  // if the motor is currently turning in the
                                                                              // negative direction...
                  if (dir > 0) { this->stepsToGo--; }      // if dir is positive, decrement the count
                  else { this->stepsToGo++; }            // if non-positive, increment the count
            }
      } else if (dir > 0)                                           // if it's not currently doing some steps,
                                                                              // and want to do a positive one...
      {
            this->incrSeq();
            this->lastStep = 0;
      } else
      {
            this->decrSeq();
            this->lastStep = 0;
      }
      return 0;
      
}

uint8_t shiftStepMotor::doSteps (uint16_t steps, int16_t speed)
{
      this->stepsToGo += steps;
      return this->setSpeed(speed);
}

uint8_t shiftStepMotor::setSpeed (int16_t speed)
{
      constrain(speed, -__maxMotorSpeed__, __maxMotorSpeed__);
      if (speed)
      {
            if (speed > 0) { this->stepSpeed = (__maxMotorSpeed__ - speed + 1); }
            else { this->stepSpeed = (-__maxMotorSpeed__ - speed - 1); }
                                                                                    // since speed is stored internally
                                                                                    // as the number of ticks to wait before
                                                                                    // incrementing the step sequence, we need
                                                                                    // to rejigger the entered speed a bit
      } else { this->stepSpeed = 0; }
      return 0;
}

int16_t shiftStepMotor::getSpeed (void)
{
      if (this->stepSpeed)
      {
            if (this->stepSpeed > 0)
            {
                  return (__maxMotorSpeed__ - this->stepSpeed + 1);
            } else
            {
                  return (-__maxMotorSpeed__ - this->stepSpeed - 1);
            }
      } else { return 0; }
}

int16_t shiftStepMotor::getStepsToGo (void)
{
      return this->stepsToGo;
}

void shiftStepMotor::doDevTick (uint8_t bytesPerBoard,
      uint8_t *boardBytes)
{
      uint8_t i, j, k, m, n;

      this->lastStep++;
      if (this->stepSpeed > 0)
      {
            if (this->lastStep >= this->stepSpeed)
            {
                  this->incrSeq();
                  this->lastStep = 0;
            }
      } else if (this->stepSpeed > 0)
      {
            if (this->lastStep >= -this->stepSpeed)
            {
                  this->decrSeq();
                  this->lastStep = 0;
            }
      }
      n = this->sequence[this->stepState];      // grab the current switch state
      for (i = 0; i < __channelsPerMotor__; i++)
      {
            j = this->channels[i];            // get the channel number
            k = (uint8_t)(j / 8);            // bytes in which this channel is
            m = j % 8;                              // get the bit within that bytes            
            if (k > (bytesPerBoard - 1)) { break; }      // if we're going to break out of our
                                                                              // array, then break out of the loop
            if ((n >> i) & 0x1)             // if we need to set this bit
            {
                  boardBytes[k] |= (0x1 << m);      // set the mth bit of the kth byte
            } else
            {
                  boardBytes[k] &= ~(0x1 << m);      // clear the mth bit of the kth byte
            }
      }
}

////////////////
// shiftBoard //
////////////////

// Public Members //

inline uint8_t shiftBoard::getDevCount (void)
{
      return this->devCount;
}

inline shiftDevice* shiftBoard::getDev (uint8_t index)
{
      return this->devices[index];
}
            
inline uint8_t shiftBoard::getBoardSize (void)
{
      return this->byteCount;
}

//////////////////
// shiftSixteen //
//////////////////

// Constructor //

shiftSixteen::shiftSixteen (uint8_t devCount,
      shiftDevice **devices)
{
      this->byteCount            = __shiftSixteenBytes__;
      this->firstChan            = __shiftSixteenFirst__;
      this->lastChan            = __shiftSixteenLast__;
      this->devCount            = devCount;
      this->devices            = devices;
}

// Public Members //

void shiftSixteen::doBoardTick(uint8_t chainSize,
      uint8_t firstByte,
      uint8_t *chainBytes)
{
      uint8_t boardBytes[this->byteCount];
      uint8_t i;
      
      for (i = 0; i < this->byteCount; i++)
      {
            boardBytes[i] = 0;
      }
      for (i = 0; i < this->devCount; i++)
      {
            devices[i]->doDevTick(this->byteCount, boardBytes);
      }
      
      chainBytes[firstByte + 1]       = boardBytes[0];      // flip the order of the bytes
      chainBytes[firstByte]             = boardBytes[1];      // so everything goes out in the right order
      
}

////////////////
// ShiftChain //
////////////////

// Constructor //

shiftChain::shiftChain (uint8_t      boardCount,
      shiftBoard      **boards,
      uint8_t dataPin,
      uint8_t clockPin,
      uint8_t latchPin,
      uint8_t resetPin)
{
      int i;
      
      this->boards                   = boards;
      this->boardCount            = boardCount;                  
      this->timerPrescaler      = 0;            
      this->timerVal                  = 0;
      this->dataPort                  = digitalPinToPort(dataPin);
      this->clockPort                  = digitalPinToPort(clockPin);            
      this->latchPort                  = digitalPinToPort(latchPin);
      this->resetPort                  = digitalPinToPort(resetPin);
      this->dataPinMask            = digitalPinToBitMask(dataPin);      
      this->clockPinMask            = digitalPinToBitMask(clockPin);
      this->latchPinMask            = digitalPinToBitMask(latchPin);
      this->resetPinMask            = digitalPinToBitMask(resetPin);
      this->clk                        = portOutputRegister(this->clockPort);
      this->dat                        = portOutputRegister(this->dataPort);
      this->lat                        = portOutputRegister(this->latchPort);
      this->rst                        = portOutputRegister(this->resetPort);
      this->byteCount            = 0;
      
      for(i = 0; i < this->boardCount; i++)
      {
            this->byteCount += this->boards[i]->getBoardSize();
      }
}

// Private Members //

void shiftChain::fastShiftOut (uint8_t byteCount,
      uint8_t *bytes)
{
      uint8_t i, j, k;
      
      // start the outbound shift
      *rst |= this->resetPinMask;
      *lat &= ~(this->latchPinMask);
      *dat &= ~(this->dataPinMask);
      *clk &= ~(this->clockPinMask);
      
      for (i = 0; i < byteCount; i++)
      {
            for (k = 7; k >= 0; k--)
            {
                  if (bytes[i] & (0x1 << k))
                  {
                        *clk &= ~(this->clockPinMask);
                        *dat |= (this->dataPinMask);
                        *clk |= this->clockPinMask;
                  } else {
                        *clk &= ~(this->clockPinMask);
                        *dat &= ~(this->dataPinMask);
                        *clk |= this->clockPinMask;
                  }
            }
      }
      
      *lat |= this->latchPinMask;
      *clk &= ~(this->clockPinMask);
}

// Public Members //

uint8_t shiftChain::startTimer (uint8_t prescaler, uint8_t timerVal, uint8_t timer)
{
      this->timerVal = timerVal;
      this->timer = timer;
      constrain(prescaler, 0, 7);
      
      if (timer == 2) {
            TIMSK2 &= ~(1<<TOIE2);                              // disable the overflow interrupt while we set things up
            TCCR2A &= ~((1<<WGM21) | (1<<WGM20));      // set counter mode to normal, counting up
            TCCR2B &= ~(1<<WGM22);                              // set counter mode to normal, counting up
            ASSR &= ~(1<<AS2);                                    // set clock source to I/O clock
            TIMSK2 &= ~(1<<OCIE2A);                        // disable compare match interrupt
            /*TCCR2B |= ((prescaler & 0x1)<<CS20);
            TCCR2B |= (((prescaler >> 1) & 0x1)<<CS21);                  
            TCCR2B |= (((prescaler >> 2) & 0x1)<<CS22);*/
            TCCR2B |= (1<<CS22)  | (1<<CS21) | (1<<CS20);    // set prescaler to 1024            
            TIMSK2 |= (1<<TOIE2);                              // enable the overflow interrupt
            return 0;
      }
      else return -1;
}

uint8_t shiftChain::stopTimer (void)
{
      TIMSK2 &= ~(1<<TOIE2);
}

uint8_t shiftChain::getBoardCount (void)
{
      return this->boardCount;
}

shiftBoard* shiftChain::getBoard (uint8_t index)
{
      return this->boards[index];
}

void shiftChain::doTick (void)
{
      uint8_t i, bytesSoFar = 0;
      uint8_t outBytes[this->byteCount];
      
      switch (this->timer) {
            case (2):
                  TCNT2 = this->timerVal;
                  break;
      }      
      for(i = 0; i < this->byteCount; i++)
      {
            outBytes[i] = 0;
      }
      for(i = 0; i < this->boardCount; i++)
      {
            this->boards[i]->doBoardTick(this->byteCount, bytesSoFar, outBytes);
            bytesSoFar += this->boards[i]->getBoardSize();
      }
      
      fastShiftOut(this->byteCount, outBytes);
      
      
}

// This is some strange linker food required to make it all work
// see http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=410870

int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);};
void __cxa_guard_release (__guard *g) {*(char *)g = 1;};
void __cxa_guard_abort (__guard *) {};
Logged

Seattle WA
Offline Offline
Full Member
***
Karma: 1
Posts: 208
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the test code:

Code:

/*

shiftStepper 0.0.1 Alpha

This is example code for driving unipolar stepper motors off of a Logos Electromechanical
sixteen channel high current driver shield for Arduino.

(c) Logos Electromechanical LLC 2010
Licensed under CC-BY-NC-SA 3.0

*/

#include <ShiftStepper.h>

#define DATPIN    13  
#define SCLPIN    12
#define LATPIN    7
#define MRPIN     8  

// Set up the step sequence for a random motor...
static const uint8_t stepSequence[4] = {0x2, 0x4, 0x1, 0x8};
// Set up channel combinations for the motors...
static const uint8_t motChans0[__channelsPerMotor__] = {0,1,2,3};
static const uint8_t motChans1[__channelsPerMotor__] = {4,5,6,7};
static const uint8_t motChans2[__channelsPerMotor__] = {8,9,10,11};
static const uint8_t motChans3[__channelsPerMotor__] = {12,13,14,15};

// Declare a required global

shiftChain *myChain = 0; // initializes so everyone can see it, but doesn't call the constructor

void setup (void)
{
  // we allocate all the storage that gets used by declaring these things static
  // this saves fooling around with explicit memory allocation
  static shiftStepMotor motor0(__channelsPerMotor__, stepSequence, motChans0);
  static shiftStepMotor motor1(__channelsPerMotor__, stepSequence, motChans1);
  static shiftStepMotor motor2(__channelsPerMotor__, stepSequence, motChans2);
  static shiftStepMotor motor3(__channelsPerMotor__, stepSequence, motChans3);
  static shiftDevice *motors[4] = {&motor0, &motor1, &motor2, &motor3};
  static shiftSixteen board0(4, motors);
  static shiftBoard *boards[1] = {&board0};
  static shiftChain storeChain(1, boards, DATPIN, SCLPIN, LATPIN, MRPIN);
  myChain = &storeChain;
  Serial.begin(57600);
  Serial.println("I live!");
  myChain->startTimer(0x7, 0, 2);
}

/* ISR functions to shift it all out */

ISR (TIMER2_OVF_vect)
{
  myChain->doTick();
}

void loop (void) {
  shiftStepMotor *thisMotor;
  shiftBoard *thisBoard;
  char in = 0;                        // input character
  
  if (Serial.available() > 0) {
    in = Serial.read();
    switch (in) {
      case '+':
        thisBoard = (shiftBoard*)myChain->getBoard(0);
        Serial.println(thisBoard->getBoardSize());
        thisMotor = (shiftStepMotor*)thisBoard->getDev(0);
        thisMotor->incrStep(1);
        Serial.println("Positive one step");
        break;
      case '-':
        thisBoard = (shiftBoard*)myChain->getBoard(0);
        thisMotor = (shiftStepMotor*)thisBoard->getDev(0);
        thisMotor->incrStep(-1);
        Serial.println("Negative one step");
        break;
    }
  }
}
  
Logged

Seattle WA
Offline Offline
Full Member
***
Karma: 1
Posts: 208
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

And here's the compiler link line and errors:

Code:

C:\Users\Pierce\Desktop\arduino-0021\arduino-0021\hardware\tools\avr\bin\avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -IC:\Users\Pierce\Desktop\arduino-0021\arduino-0021\hardware\arduino\cores\arduino -IC:\Users\Pierce\Desktop\arduino-0021\arduino-0021\libraries\shiftStepper C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\IncrementExample.cpp -oC:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\IncrementExample.cpp.o
C:\Users\Pierce\Desktop\arduino-0021\arduino-0021\hardware\tools\avr\bin\avr-gcc -Os -Wl,--gc-sections -mmcu=atmega328p -o C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\IncrementExample.cpp.elf C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\shiftStepper\ShiftStepper.cpp.o C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\IncrementExample.cpp.o C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\core.a -LC:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp -lm
C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp\IncrementExample.cpp.o: In function `loop':
C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp/IncrementExample.cpp:70: undefined reference to `shiftBoard::getBoardSize()'
C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp/IncrementExample.cpp:71: undefined reference to `shiftBoard::getDev(unsigned char)'
C:\Users\Pierce\AppData\Local\Temp\build795910974161220392.tmp/IncrementExample.cpp:77: undefined reference to `shiftBoard::getDev(unsigned char)'

I'm completely stumped. I think it's a link error, but not only does the link line look good but I appear to link the constructor correctly. Halp?
Logged

Seattle WA
Offline Offline
Full Member
***
Karma: 1
Posts: 208
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

One more thing -- the linker food in the source files doesn't affect the error one way or the other. It makes another set of odd linker errors go away.
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 197
Posts: 12741
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
[glow]inline[/glow] uint8_t shiftBoard::getBoardSize (void)
I suggest not using inline until: 1. You've determined that you really do need it; 2. You understand how it works.

Quote
One more thing -- the linker food in the source files doesn't affect the error one way or the other. It makes another set of odd linker errors go away.
You may want to understand why those "linker food" things need to be included.  I suspect you're covering up a problem that will later bite you in the hindquarters.
Logged

Pages: [1]   Go Up
Jump to: