Jump to content

measuring RPM on motors using uC32 + Basic I/O + IR Proximity


LariSan

Recommended Posts

This is a project that was done by the Erik and Keith to test the NI myRIO High Current adapter and NI myRIO Motor Adapter. (I am courtesy posting because I like the project so much, because this is a small set up of motors you will find in many power electronics and controls labs!). 
 
The set up was made by Erik to be able to measure the speed of a dyno motor pair. 
The motors came from http://www.andymark.com/(robotics supplier). 
The mounting brackets, the shafts, the coupler, front mount and key have been 3D printed on an Ultimaker. 
All of these were mounted on an oak board. 
 

16301731417_1dd368132e.jpg
dyno setup by laraswanland, on Flickr

 
The motors require 12V and a large amount of current to drive. 
Keith was able to use his desktop variable power supply to get the motors spinning.
 
The speed is sensed through the optocoupler (or Digilent IR Sensor Part# 240-012P )
TILE_IRROSC_150.jpg
 
it is connected to the chipKIT protoshield with some resistor values added to it (for the IR LED).
The protoshield is connected to a uC32 that is doing the measurement (by counting how many "clicks" on the gear go by the IR LED) and displaying the RPM value on the Basic I/O Shield (mostly only using the OLED Display). 
 

16300206780_86ef150502_c.jpg
2015-02-09 16.24.46 by laraswanland, on Flickr

 
Here's the video of it in action:
 
https://www.youtube.com/watch?v=Vbp0SGlOynY



 

/************************************************************************/
/*                                                                      */
/*    MotorSpeed.pde                                                    */
/*                                                                      */
/*    Reads the motor speed of the dyno motor pair                      */
/*    And displays it on the Basic IO shield                            */
/*                                                                      */
/************************************************************************/
/*    Author:     Keith Vogel                                           */
/*    Copyright 2013, Digilent Inc.                                     */
/************************************************************************/
/* 
*
* Copyright © 2013-2014, Digilent <www.digilentinc.com>
* Contact Digilent for the latest version.
*
* This program is free software; distributed under the terms of 
* BSD 3-clause license ("Revised BSD License", "New BSD License", or "Modified BSD License")
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1.    Redistributions of source code must retain the above copyright notice, this
*        list of conditions and the following disclaimer.
* 2.    Redistributions in binary form must reproduce the above copyright notice,
*        this list of conditions and the following disclaimer in the documentation
*        and/or other materials provided with the distribution.
* 3.    Neither the name(s) of the above-listed copyright holder(s) nor the names
*        of its contributors may be used to endorse or promote products derived
*        from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/************************************************************************/
/*  Revision History:                                                   */
/*    2/9/2014(KeithV): Created                                         */
/************************************************************************/
#include <IOShieldOled.h>

void displayRPM(uint32_t value);

/************************************************************************/
/*  This requires a uC32 and the proto shield and the                   */
/*  IR Proximity Sensor                                                 */
/*  LED Resistor == 100 ohms                                            */
/*  50K pot; set at 9K                                                  */
/*  15 teeth on on gear sensor                                          */
/*  Basic IO Shield for OLED display of RPM                             */
/*                                                                      */
/*  NOTE: chipKIT Pin 7 must be bent out so it does not conflict        */
/*      with the switch on the IO Shield                                */
/*                                                                      */
/*  Timing:                                                             */
/*                                                                      */
/*  Timer2 = pbclk / clock presaler == Clk ticks / sec                  */
/*  IC2 = teeth per rotation / IC2 presaler == IC ticks / rotation      */
/*  deltaTick = Clk ticks / IC ticks                                    */
/*  CRPM = rotations / sec * 60 sec / min                               */
/*                                                                      */
/*  RPM = (60 * Timer2) /(IC2 * deltaTick) == rotation / sec            */
/*                                                                      */
/************************************************************************/

// please keep constants as long long as we will overflow otherwise
// these are the clock and input capture constants
#define CLK_PRESCALE        256ull      // divide by 256
#define CLK_REG_PRESCALE    0b111       // see PIC32 family ref

#define IC_PRESCAL          4ull       // input capture prescaler, divide by 16
#define IC_REG_PRESCALE     0b100       // see PIC32 family Ref

#define TEETH_PER_ROTATION  15ull       // teeth on the gear
#define SEC_PER_MIN         60ull       // 60 sec per min

// this is the constant for RPM except for the deltaTick which is calulated when running
// this is carefully done to prevent overflow of the calculation
#define RPM_SCALER ((SEC_PER_MIN * ((uint64_t) __PIC32_pbClk) * IC_PRESCAL) / (TEETH_PER_ROTATION * CLK_PRESCALE))
#define RPM(a) ((uint32_t) (RPM_SCALER / (a)))

// Determine if there is no signal
#define MISSED_INPUT_TIME_UNTIL_NO_SIGNAL    1000
static uint32_t tStartTimeout = 0;
static bool fNoSignal = false;

// How often to update the display
#define MS_UPDATE_DISPLAY   250
static uint32_t tStartDisplayUpdate = 0;
static uint32_t rpmAverage = 0;

// instantiate the Oled object
IOShieldOledClass  oled;

/* ------------------------------------------------------------ */
/***  setup
**
**  Parameters:
**    none
**
**  Return Value:
**    none
**
**  Errors:
**    none
**
**  Description:
**      Set up timer, input capture, and Oled display
*/
void setup()
{
  Serial.begin(9600);
  Serial.println("MotorSpeed 1.0");
  Serial.println("Copyright © 2014, Digilent Inc.");
  Serial.println("Written by: Keith Vogel");

  // set up Timer 2
  T2CON = 0;                            // intialize timer2
  T2CONbits.TCKPS = CLK_REG_PRESCALE;   // divide by 256
  TMR2 = 0;                             // clear timer value
  PR2 = 0xFFFF;                         // max value
  
  // Input capture 2 on pin 7
  pinMode(7, INPUT);

  // set up input capture 2
  IC2CON = 0;                           // initailize input capture 2
  IC2CONbits.ICTMR = 1;                 // user timer 2
  IC2CONbits.ICM = IC_REG_PRESCALE;     // every 16th rising edge

  // turn on timer2 and input capture 2
  IC2CONbits.ON = 1;
  T2CONbits.ON = 1;

  // start the Oled Display
  oled.begin();

  tStartTimeout = millis() - MISSED_INPUT_TIME_UNTIL_NO_SIGNAL;
  tStartDisplayUpdate = millis() - MS_UPDATE_DISPLAY;
}

/* ------------------------------------------------------------ */
/***  loop
**
**  Parameters:
**    none
**
**  Return Value:
**    none
**
**  Errors:
**    none
**
**  Description:
**      Main program loop
*/
void loop()
{
    if(!fNoSignal && (millis() - tStartTimeout >= MISSED_INPUT_TIME_UNTIL_NO_SIGNAL))
    {
        // display no signal
        displayRPM(0);
        fNoSignal = true;
    }

    else if(millis() - tStartDisplayUpdate >= MS_UPDATE_DISPLAY)
    {

        if(!fNoSignal)
        {
                displayRPM(rpmAverage);
        }

        tStartDisplayUpdate = millis();
    }

    else if(IC2CONbits.ICOV == 1)
    {
        volatile uint16_t value1 = IC2BUF;
        volatile uint16_t value2 = IC2BUF;
        uint16_t valueDiff = value2 - value1;
        uint16_t rpm = RPM(valueDiff);

        // clean out the remaining input capture FIFO
        while(IC2CONbits.ICBNE == 1)
        {
            volatile uint16_t value = IC2BUF;
        }

        // calculate a weighted RPM value
        rpmAverage = (rpm + 3 * rpmAverage) / 4;
        fNoSignal = false;
        tStartTimeout = millis();
    }

}

/* ------------------------------------------------------------ */
/***  displayRPM
**
**  Parameters:
**    value: The RPM value to display
**
**  Return Value:
**    none
**
**  Errors:
**    none
**
*/
void displayRPM(uint32_t value)
{

  /* Clear the display. Note that clear has the same effect as
  ** calling clearBuffer and updateDisplay.
  */
  oled.clear();
  oled.setCursor(0, 2);

    if(value == 0)
    {
        oled.putString("No Signal");
    }

    else
    {
        char sz[32];
        itoa(value, sz, 10);
        oled.putString(sz);
        oled.putString(" RPM");
    } 
}

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...