Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   C/C++ (http://www.chiefdelphi.com/forums/forumdisplay.php?f=183)
-   -   Magnetic Encoder Programming Question\Consult (http://www.chiefdelphi.com/forums/showthread.php?t=82096)

jerdelyan 02-07-2010 03:09 AM

Magnetic Encoder Programming Question\Consult
 
Ok, it is 11:30pm. I have been working on this dilemma off and on since 10am this morning; researching the WPI Library, USFirst Forum, FIRST Forge, Chief Delphi, and NI Communities and i have not been able to figure out if there is a class designed to communicate with the magnetic encoders over PWM. I am looking for something along the lines of the Jaguar class (used to control the Jaguar motor controllers) or the existing encoder class (optical only or requires the usage of 2 channels A and B) but to use the single PWM unfiltered out put from the Magnetic Encoder that was included in the KOP. I have not been able to find anything to date...:( If there is something out there please let me know...:yikes:

With the understanding that there is nothing that I can find that exists already I am looking at creating something to do this task for me. I am thinking of using a Digital Input class with the Get() function (not sure if it will work or what it will 'get' me) or an interrupt in combination with a timer to time the rise and fall of the PWM signal to calculate the time of the pulse.

The only other idea i have could be to use the Counter class with the Timer class to accomplish a similar effect.

Any way it is after midnight now and my pregnant wife is telling me to shut the light off and go to sleep. So i look forward to your ideas...!

Thanks for your help in this mater...
John Erdelyan
Team 2910 Jack in the Bot

Greg McKaskle 02-07-2010 10:28 AM

Re: Magnetic Encoder Programming Question\Consult
 
The LabVIEW example for the magnetic encoder treats the sensor output as analog. It takes the default analog values and multiplies by 72. It also shows a simple filter to handle rollover.

If you have LV installed, you might run the example so you can debug the wiring and such.

Greg McKaskle

charrisTTI 02-08-2010 06:59 AM

Re: Magnetic Encoder Programming Question\Consult
 
I spent the better part of a day looking at the magnetic encoder I/O options. We ruled out the analog angle output because of the slow response at the rollover point. I did not give the PWM output much thought. I tried two different approaches for which I am including code samples. The first reads the data digitally using the SPI support and the second uses two analog channels to read the sin/cos outputs (be sure to remove the solder jumper to enable these ports).

Here is the digital one using SPI:

Code:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.wpi.first.wpilibj;

import edu.wpi.first.wpilibj.fpga.tSPI;

/**
 *
 * @author charris
 */
public class AS5030FourWireSPI extends SensorBase {

    public class AS5030Result
    {
        public double angle;
        public int agc;
    }


    tSPI spi;
    DigitalModule module;
    int slot;
    int clockPin;
    int dataOutPin;
    int dataInPin;
    int chipSelectPin;


    public AS5030FourWireSPI( int slotParam, int clockPinParam, int dataOutPinParam, int dataInPinParam, int chipSelectPinParam )
    {
        slot = slotParam;
        clockPin = clockPinParam;
        dataOutPin = dataOutPinParam;
        dataInPin = dataInPinParam;
        chipSelectPin = chipSelectPinParam;
       
        // get our module
        module = DigitalModule.getInstance(slot);

        // create SPI instance
        //spi = new tSPI();

        // allocate our pins
        module.allocateDIO(clockPin, false);
        module.allocateDIO(dataOutPin, false);
        module.allocateDIO(dataInPin, true);
        module.allocateDIO(chipSelectPin, false);

        // setup for SPI
        tSPI.writeChannels_SCLK_Module(DigitalModule.slotToIndex(slot));
        tSPI.writeChannels_SCLK_Channel(DigitalModule.remapDigitalChannel(clockPin-1));

        tSPI.writeChannels_MOSI_Module(DigitalModule.slotToIndex(slot));
        tSPI.writeChannels_MOSI_Channel(DigitalModule.remapDigitalChannel(dataOutPin-1));

        tSPI.writeChannels_MISO_Module(DigitalModule.slotToIndex(slot));
        tSPI.writeChannels_MISO_Channel(DigitalModule.remapDigitalChannel(dataInPin-1));
    }

    public void initialize()
    {
        // set chip select low to start
        module.setDIO(chipSelectPin, true);

        // data is valid on rising edge of clock
        tSPI.writeConfig_DataOnFalling(false);

        // set the bit order
        tSPI.writeConfig_MSBfirst(true);

        tSPI.strobeReset();
        tSPI.strobeClearReceivedData();
    }

    public AS5030Result getData( )
    {
        // select the chip
        module.setDIO(chipSelectPin, false);

        // write the 5 bit command
        // set the width
        tSPI.writeConfig_BusBitWidth(5);

        // write the data to load
        tSPI.writeDataToLoad(0x1F);

        // strobe out the data
        tSPI.strobeLoad();

        // wait for completion
        while( !tSPI.readStatus_Idle() )
        {
            java.lang.Thread.yield();
        }

        // change back to 16 bits
        tSPI.writeConfig_BusBitWidth(16);
        // clear the data to load
        tSPI.writeDataToLoad(0x0);

        // clear received data
        tSPI.strobeClearReceivedData();

        // strobe in the data
        tSPI.strobeLoad();


        // wait for completion
        //while( !tSPI.readStatus_Idle() )
        while( tSPI.readReceivedElements() == 0 )
        {
            java.lang.Thread.yield();
        }

        // strobe in the received data
        tSPI.strobeReadReceivedData();

        // read the data
        long rawData = tSPI.readReceivedData();

        // deselect the chip
        module.setDIO(chipSelectPin, true);

        AS5030Result result = new AS5030Result();

        long rawAngle = rawData & 0xFF;

        int agc = (int)((rawData >> 8) & 0x3F);

        result.angle = (double)rawAngle * 360.0 / 256.0;
        result.agc = agc;
       
        return result;
    }
}

Here is the dual analog input approach:

Code:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.wpi.first.wpilibj;

import  com.sun.squawk.util.MathUtils;

/**
 *
 * @author charris
 */
public class AS5030DualAnalog extends SensorBase implements PIDSource {

    private static final double biasVoltage = 2.25;
    private AnalogChannel sinChannel;
    private AnalogChannel cosChannel;

    public AS5030DualAnalog( int slotParam, int sinChannelParam, int cosChannelParam)
    {
        sinChannel = new AnalogChannel( slotParam, sinChannelParam );
        cosChannel = new AnalogChannel( slotParam, cosChannelParam );
    }

    protected double getCos()
    {
        return cosChannel.getVoltage() - biasVoltage;
    }

    protected double getSin()
    {
        return sinChannel.getVoltage() - biasVoltage;
    }

    public double getAngle()
    {
        // calculate the angle
        double theta = MathUtils.atan2( getSin(), getCos() );
        // convert to degrees
        return Math.toDegrees(theta) + 180.0;
    }

    /**
    * Get the angle of the encoder for use with PIDControllers
    * @return the current angle according to the encoder
    */
    public double pidGet() {
        return getAngle();
    }


}

Give them a try.

jerdelyan 02-08-2010 02:34 PM

Re: Magnetic Encoder Programming Question\Consult
 
Thanks for the help on this. In looking at your code it appears that there are some other functions out there that may work for me as well.

I noticed that this code is in Java but i hope that the functions are names are similar (i remember reading that somewhere). If i get something to work today i will post the code later tonight...!

JE

jerdelyan 02-10-2010 01:24 AM

Re: Magnetic Encoder Programming Question\Consult
 
Ok, so i wanted to get the PWM working without modifing the chip any more (we already burnt one up). so i played with the code and was able to get code working to sample the PWM on a digital input port of the digital sidecar. i have attached the example below in c++.

Code:

void OperatorControl(void)
{
        GetWatchdog().SetEnabled(false);
        timeStart = 0.0;
        timeEnd = 0.0;
        avgPulseTime = 0.0;
        pulseCounter = 0;
        positionDegree = 0;
        const UINT32 slot = 4;
        char *c = NULL;
        DigitalModule *dm = DigitalModule::GetInstance(slot);// (slot);               
        DriverStationLCD *dsLCD = DriverStationLCD::GetInstance();
        dm->AllocateDIO(1,true);
        //dsLCD->UpdateLCD();
               
        while (IsOperatorControl())
        {       
                if(dm->GetDIO(1) == 1)
                {
                        //start the timer and get the time
                        tim1.Start();
                        timeStart=tim1.Get();

                        while(dm->GetDIO(1) == 1) //repeat until there is a falling edge
                        {
                                Wait(.000001);  //Pause for 1usec
                        }
                        timeEnd=tim1.Get();
                        tim1.Stop(); //stop the timer
                        if(0 < (timeEnd - timeStart) < .000578)  //filter out noise
                        {
                                avgPulseTime += (timeEnd-timeStart); //sum up all of the pulses
                                pulseCounter++;
                        }
                }
                //looking to average 10 pulses together
                if (pulseCounter == 10)
                {
                        avgPulseTime /= pulseCounter;
                        avgPulseTime *= 1000000;
                        positionDegree = avgPulseTime * 360 / 578;
                        pulseCounter = 0;
                        //display on the screen
                        sprintf(c,"%03d",positionDegree);
                        dsLCD->Printf(dsLCD->kUser_Line4,1,c);
                        dsLCD->UpdateLCD();
                        tim1.Reset();
                        avgPulseTime = 0.0;
                        *c = NULL;
                }       
                Wait(.0000005);
        }
}

I am basically sampling the code at 1usec and capturing the time of the pulse. from there i can take the different times and convert it to degrees. it is then displayed on the dashboard...
I hope that this can help somone else who is thinking of doing the same thing...:D

John K. Erdelyan
Lead Mentor & Programming Mentor
Team 2910


All times are GMT -5. The time now is 10:41 AM.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi