Jump to content
OMRON Forums

Ychu

Members
  • Posts

    17
  • Joined

  • Last visited

Posts posted by Ychu

  1. We have PowerUMAC with ACC59E option installed. This option allows me to make ADC reading manually which is what we need for our application. Can I do that with Power Clipper ADC option as well? I don't see it is mentioned in the Clipper manual.

     

    Thanks.

  2. Yun,

     

    The ADC on this card is a MAX180 by Maxim. Its datasheet states that the conversion time varies between 7.5 us and 8.125 us in "asynchronous hold mode," which is what we use, I believe.

     

    To access ACC59E structures in C, you can create a pointer to the card:

     

    volatile GateIOStruct *ACC59E_0;

     

    // Initialize it

    ACC59E_0 = GetGateIoMemPtr(0); // Change 0 to whatever your actual card index is

     

    Then you should be able to address any of the elements like

     

    ACC59E_0->ConvertCode

    ACC59E_0->ADCu

    ACC59E_0->ADCs

    ACC59E_0->ADCRdy

     

    You can also use the library of C functions listed in the ACC-59E manual starting on page 77. This should be faster than using GetPmacVar().

     

    ACC-59E Manual Link:

     

    http://www.deltatau.com/manuals/pdfs/ACC-59E.pdf?id=635787783275493378

     

    Thanks Charles.

    By the way this manual has 71 pages if I am not mistaken.

     

    I just tried the code but getting the following compilation error.

     

    'GateIOStruct' has no member named 'ConvertCode'

    'GateIOStruct' has no member named 'ADCRdy'

    'GateIOStruct' has no member named 'ADCs'

     

    When I looked into the header file RtGpShm.h, GateIOStruct only has three members;

     

    typedef struct GateIOStruct

    {

    unsigned DataReg[6];

    unsigned IntrReg;

    unsigned CtrlReg;

    } GateIOStruct;

     

    The structure that has ConvertCode is;

    typedef struct ADCDemux

    {

    int Enable;

    int Index;

    unsigned ConvertCode[ADCDEMUX_SIZE];

    int Address[ADCDEMUX_SIZE];

    int ResultLow[ADCDEMUX_SIZE];

    int ResultHigh[ADCDEMUX_SIZE];

    } ADCDemux;

     

    Then it does not have ADCRdy and ADCs.

     

    Thanks.

  3. Yun,

     

    The ADC on this card is a MAX180 by Maxim. Its datasheet states that the conversion time varies between 7.5 us and 8.125 us in "asynchronous hold mode," which is what we use, I believe.

     

    To access ACC59E structures in C, you can create a pointer to the card:

     

    volatile GateIOStruct *ACC59E_0;

     

    // Initialize it

    ACC59E_0 = GetGateIoMemPtr(0); // Change 0 to whatever your actual card index is

     

    Then you should be able to address any of the elements like

     

    ACC59E_0->ConvertCode

    ACC59E_0->ADCu

    ACC59E_0->ADCs

    ACC59E_0->ADCRdy

     

    You can also use the library of C functions listed in the ACC-59E manual starting on page 77. This should be faster than using GetPmacVar().

     

    ACC-59E Manual Link:

     

    http://www.deltatau.com/manuals/pdfs/ACC-59E.pdf?id=635787783275493378

     

    Thanks Charles.

    By the way this manual has 71 pages if I am not mistaken.

  4. The sample rate is set by PMAC’s phase clock. The actual conversion time of the ADC chip is very fast, it should support sample rates up to about 30KHz.

     

    Thanks Steve.

    Isn't the sample rate applicable only in automatic read mode? In manual read mode, the application function can set the convert code any time, wait for the ready-bit and then read the value. If I understand correctly the time it takes for the value to be ready will be just the A/D (chip) conversion time and maybe some small overhead. What I am looking for is the A/D conversion time, for instance the last A/D that I worked with had 17usec conversion time. Since I am going to make the reading from the ISR routine, it is important that the time to read is in the range of microseconds.

     

    Another question is I have ACC-59E and I need to set convert code, in bipolar mode, for ADC#1 as ACC59E[1].ADCs = 8. How would I do that in the 'C' routine using shared memory pointer? The SetPmacVar() method will not compile. Even if it does, I think shared memory would be much faster than SetPmacVar() call.

     

    Thanks again.

  5. Your application? Do you mean a host program (e.g. GUI)?

     

    Are you using our PDK in C#? You can use the GetResponse method in the ISyncGpasciiCommunicationInterface class to send variable names.

     

    If you are going through a Telnet interface, make sure you issued "gpascii -2" to enter the ASCII communication terminal of the PPMAC.

     

    Thanks Charles.

    No I am not using PDK but open source SSH library. I still get the error even when I launched gpascii with option -2. Any suggestion?

    Thanks.

  6. I am trying to read a digital I/O input from my application. I know the pointer name that is mapped to the register. For example,

     

    // set in the global definitions.pmh

    ptr IoCard1RegIn1->u.io:$C00000.8.8

     

    I could just type IoCard1RegIn1 at IDE terminal but when I tried to send this same string from my application, I would get ILLEGAL PARAMETER error.

     

    Thanks.

  7. Not sure then. I suggest you try with a "clean," new project and try that code alone. Maybe something else "hiding" in your project caused the problem?

     

    You can spin the motor's encoder by hand even after setting Motor[x].ServoCtrl=1 and you should see counts if it is a standard digital quadrature encoder.

     

    Thanks Charles.

    I did send my project to Delta Tau support yesterday although I am not sure if it is the cause. For now, I plan to just manually move the axis to trigger the home switch with UserAlgo.CaptCompIntr set to 1 to see if makes any difference.

  8. This works fine for me -- what is your Gate3.IntCtrl setting?

     

    I just tested it with the following:

     

    // Script command to set up interrupt on first channel capture

    Gate3[0].IntCtrl = $10000 // Unmask PosCapt[0] (not saved)

    // Script command to initialize trigger counter

    Sys.Idata[65535] = 0

    // Script command to enable capture/compare ISR

    UserAlgo.CaptCompIntr = 1

     

    Also what firmware are you using? I tested it with 2.0.2.291

     

    Thanks Charles.

    I did do the same as what you have shown here as they came right out of the manual. According to the response of 'ver' command, the firmware version is 2.0.2.14.

  9. Here are settings you can use for a virtual motor. I'm posting settings for 4 channels, so you can just take the settings corresponding to the channel you want to use.

     

    /* Gate3 Version */

    Sys.WpKey = $AAAAAAAA // Permit write access to Gate3 structures

     

    /* Activate motors 1-4 */

    Motor[1].ServoCtrl=1

    Motor[2].ServoCtrl=1

    Motor[3].ServoCtrl=1

    Motor[4].ServoCtrl=1

     

    // Write servo output to channel’s PFM register

    Motor[1].pDac=Gate3[0].Chan[0].Pfm.a // 1st channel PFM

    Motor[2].pDac=Gate3[0].Chan[1].Pfm.a // 2nd channel PFM

    Motor[3].pDac=Gate3[0].Chan[2].Pfm.a // 3rd channel PFM

    Motor[4].pDac=Gate3[0].Chan[3].Pfm.a // 4th channel PFM

     

    // Set PFM Clock Divisor

    Gate3[0].PfmClockDiv=5

     

     

    // Set up channel output for PFM signal

    // Not needed for simulation; only to monitor output signal

    Gate3[0].Chan[0].OutputMode=8 // PFM on Phase D

    Gate3[0].Chan[1].OutputMode=8 // PFM on Phase D

    Gate3[0].Chan[2].OutputMode=8 // PFM on Phase D

    Gate3[0].Chan[3].OutputMode=8 // PFM on Phase D

     

    // Wrap PFM output back into channel’s encoder counter

    Gate3[0].Chan[0].EncCtrl=8 // Internal pulse and direction clockwise

    Gate3[0].Chan[1].EncCtrl=8 // Internal pulse and direction clockwise

    Gate3[0].Chan[2].EncCtrl=8 // Internal pulse and direction clockwise

    Gate3[0].Chan[3].EncCtrl=8 // Internal pulse and direction clockwise

     

    // Disable overtravel limit inputs

    // May be needed if there are no physical switches present

    Motor[1].pLimits=0

    Motor[2].pLimits=0

    Motor[3].pLimits=0

    Motor[4].pLimits=0

     

    // Disable amplifier enable output

    // May be needed if channel is also connected to real amplifier

    Motor[1].pAmpEnable=0

    Motor[2].pAmpEnable=0

    Motor[3].pAmpEnable=0

    Motor[4].pAmpEnable=0

     

    // Disable amplifier fault input

    // May be needed if channel is also connected to real amplifier

    Motor[1].pAmpFault=0

    Motor[2].pAmpFault=0

    Motor[3].pAmpFault=0

    Motor[4].pAmpFault=0

     

    // Set derivative gain term in servo loop to zero

    // This is a Type 1 servo (single integration); does not need Kvfb

    Motor[1].Servo.Kvfb=0

    Motor[2].Servo.Kvfb=0

    Motor[3].Servo.Kvfb=0

    Motor[4].Servo.Kvfb=0

     

    // Lower proportional gain term from default

    Motor[1].Servo.Kp=40

    Motor[2].Servo.Kp=40

    Motor[3].Servo.Kp=40

    Motor[4].Servo.Kp=40

     

    // Add Kvff and Ki

    Motor[1].Servo.Kvff=40;

    Motor[2].Servo.Kvff=40;

    Motor[3].Servo.Kvff=40;

    Motor[4].Servo.Kvff=40;

     

    Motor[1].Servo.Ki=0.001;

    Motor[2].Servo.Ki=0.001;

    Motor[3].Servo.Ki=0.001;

    Motor[4].Servo.Ki=0.001;

     

    // Deactivate commutation for all motors

    Motor[0].PhaseCtrl=0 // No phase commutation active

    Motor[2].PhaseCtrl=0 // No phase commutation active

    Motor[3].PhaseCtrl=0 // No phase commutation active

    Motor[4].PhaseCtrl=0 // No phase commutation active

     

    // Create encoder conversion table entries for all motors

    EncTable[1].type=1;

    EncTable[1].pEnc=Gate3[0].Chan[0].ServoCapt.a;

    EncTable[1].pEnc1=Sys.pushm;

    EncTable[1].index1=0;

    EncTable[1].index2=0;

    EncTable[1].index3=0;

    EncTable[1].index4=0;

    EncTable[1].ScaleFactor=1.0/256.0;

    EncTable[1].MaxDelta=0;

    EncTable[1].SinBias=0;

    EncTable[1].CosBias=0;

     

    EncTable[2].type=1;

    EncTable[2].pEnc=Gate3[0].Chan[1].ServoCapt.a;

    EncTable[2].pEnc1=Sys.pushm;

    EncTable[2].index1=0;

    EncTable[2].index2=0;

    EncTable[2].index3=0;

    EncTable[2].index4=0;

    EncTable[2].ScaleFactor=1.0/256.0;

    EncTable[2].MaxDelta=0;

    EncTable[2].SinBias=0;

    EncTable[2].CosBias=0;

     

    EncTable[3].type=1;

    EncTable[3].pEnc=Gate3[0].Chan[2].ServoCapt.a;

    EncTable[3].pEnc1=Sys.pushm;

    EncTable[3].index1=0;

    EncTable[3].index2=0;

    EncTable[3].index3=0;

    EncTable[3].index4=0;

    EncTable[3].ScaleFactor=1.0/256.0;

    EncTable[3].MaxDelta=0;

    EncTable[3].SinBias=0;

    EncTable[3].CosBias=0;

     

    EncTable[4].type=1;

    EncTable[4].pEnc=Gate3[0].Chan[3].ServoCapt.a;

    EncTable[4].pEnc1=Sys.pushm;

    EncTable[4].index1=0;

    EncTable[4].index2=0;

    EncTable[4].index3=0;

    EncTable[4].index4=0;

    EncTable[4].ScaleFactor=1.0/256.0;

    EncTable[4].MaxDelta=0;

    EncTable[4].SinBias=0;

    EncTable[4].CosBias=0;

     

    // Point motors to encoder conversion table entries

    Motor[1].pEnc=EncTable[1].a

    Motor[2].pEnc=EncTable[2].a

    Motor[3].pEnc=EncTable[3].a

    Motor[4].pEnc=EncTable[4].a

     

    Motor[1].pEnc2=EncTable[1].a

    Motor[2].pEnc2=EncTable[2].a

    Motor[3].pEnc2=EncTable[3].a

    Motor[4].pEnc2=EncTable[4].a

     

    /* Set up various PFM-Related Gate3 settings */

    Gate3[0].Chan[0].OutFlagD=1;

    Gate3[0].Chan[1].OutFlagD=1;

    Gate3[0].Chan[2].OutFlagD=1;

    Gate3[0].Chan[3].OutFlagD=1;

     

    Gate3[0].chan[0].packoutdata=0

    Gate3[0].chan[0].packindata=0

    Gate3[0].Chan[0].PfmWidth=10 // Can be adjusted for smaller pulse width

     

    Gate3[0].chan[1].packoutdata=0

    Gate3[0].chan[1].packindata=0

    Gate3[0].Chan[1].PfmWidth=10 // Can be adjusted for smaller pulse width

     

    Gate3[0].chan[2].packoutdata=0

    Gate3[0].chan[2].packindata=0

    Gate3[0].Chan[2].PfmWidth=10 // Can be adjusted for smaller pulse width

     

    Gate3[0].chan[3].packoutdata=0

    Gate3[0].chan[3].packindata=0

    Gate3[0].Chan[3].PfmWidth=10 // Can be adjusted for smaller pulse width

     

    Sys.WpKey=$0 // Write-protect Gate3 registers

     

    Thanks Charles.

     

    At the moment, I am having trouble with the CaptCompISR() routine (see below) causing disconnect to PowerPMAC when trying to exercise with homing routine. I will continue with the laser pulsing function as soon as I get the disconnect problem resolved.

     

    void CaptCompISR(void)

    {

    volatile GateArray3 *MyGate3; // ASIC structure pointer

    int *CaptCounter; // Logs number of triggers

    int Temp;

    MyGate3 = GetGate3MemPtr(0); // Pointer to IC base

    CaptCounter = (int *)pushm + 65535; // Sys.Idata[65535]

    (*CaptCounter)++; // Increment counter

    Temp = MyGate3->Chan[0].HomeCapt;

    MyGate3->IntCtrl = 1;

    }

  10. Thanks Charles.

     

    Yes I am aware of using ISR routine which is supported only for Gate3. I do have a need for Gate2 as well but for now, I will focus on Gate3 only.

     

    When you said about using virtual motor to trigger the edges, where do I do that? Will that be in virtual motor's user-servo routine? The way I understood from the manual(s), I would setup the COMPA/COMPB value for the channel (assuming motor #3) that will generate the pulses and implement the user-servo routine for the virtual motor (motor #2) which updates the motor #3's position. Is this what you are saying? If so, the rate of motor #2's user-servo routine call will depend on the servo-period (ServoClockDiv) for the system so I am limited to that frequency. For example, my system is currently have ServoClockDiv set to ~2.25K due to the hardware (I think), which only gives me ~444us. My minimum pulse width requirement is 5usec at least to be compatible with what we have today and of course the smaller we could get the better.

     

    I need to be able to generate pulse(s) with or without moving my XYZ Stage. My understanding is I will use EQU (CompA/CompB/CompAdd) logic which will require somewhere (user-servo or user-phase routine) to update the position of the motor that is assigned to the EQU channel.

     

    Thanks.

  11. Probably the capture/compare interrupt service routine (ISR) is what you want. You could preload all of the on/off locations in the user buffer and then run a virtual motor to trigger all of the edges. That would be better than timing the on/off and would have no jitter.

     

    What are you trying to achieve actually? Just need to pulse a device occasionally? What kind of pulse frequency? Does the "on-time" need to vary or is it constant? Give me a few more details and I can make you a more concrete example.

  12. I need to generate a single pulse from EQU with a variable time. I thought I would use background real-time C program to do that because of the time I need, between 5usec to 10msec.

     

    I put together a test program, see below. My program seems to work reliably starting from ~25usec and up. The smallest pulse I could get is ~15usec but jumps around between ~15usec to ~35usec when I set my delay to below 25usec.

     

    Below is the sample of my code;

     

    #define EQUWRITE_1 0xC0

    #define EQUWRITE_0 0x40

     

    int main(void)

    {

    struct sched_param param;

    volatile GateArray3 *Gate3IC;

    struct timespec sleeptime = {0};

     

    param.__sched_priority = 50;

    pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);

     

    InitLibrary();

    Gate3IC = GetGate3MemPtr(0);

     

    sleeptime.tv_nsec = 10000; // 10usec

    Gate3IC->Chan[chanIdx].OutCtrl = Gate3IC->Chan[chanIdx].OutCtrl | EQUWRITE_1; // set EQU state to 1

    nanosleep(&sleeptime, NULL);

    Gate3IC->Chan[chanIdx].OutCtrl = Gate3IC->Chan[chanIdx].OutCtrl & ~EQUWRITE_1;

    Gate3IC->Chan[chanIdx].OutCtrl = Gate3IC->Chan[chanIdx].OutCtrl | EQUWRITE_0; // set EQU state to 0

     

    CloseLibrary();

    return 0;

    }

     

    I did not try changing the sched_priority value as I am not sure if it is okay to go below 50 (that I got from Delta Tau IDE sample code).

     

    What is the best 'time' resolution I could get from this background program?

    Is there any other way besides using this technique to accomplish what I need?

  13. Gate3.Chan[j].EquWrite constitutes bits 06 – 07 of the full-word element Gate3.Chan[j].OutCtrl and must be accessed through the full-word element in C as in:

     

    Gate3IC->Chan[j].OutCtrl = Gate3IC->Chan[j].OutCtrl | (EquWriteVal <<6);

     

     

    Thanks Steve.

×
×
  • Create New...