This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RM48L952 N2HET Input rotary encoder

Other Parts Discussed in Thread: MOTORWARE, RM48L952

Hi all,

what is the best way to do input increment rotary encoder using N2HET, to get position of the motor.

Let say that we have a time window of 5 and we make 3 steps are forward with the motor and 2 steps are backward, i would get a result back 1 (window interrupt). And also that this window could be extended or reduced.  

Im not familiar with N2HET yet, so is this even possible?

Thanks in advance. 

  • Yes, this is possible.
    There's some examples of this implemented as a state machine.  I'll see if I can dig it up.

     

  • Great to hear that! I hope you will find a example.

  • Dejan,
    The HET program that comes with Motorware http://www.ti.com/tool/motorware has an example of how to handle a quadrature encoder.   If you intall this package, you'll find a file:

    <install_dir>\MotorWare\v1.0.3.03\sw\drivers\het\pwm_bldc\src\32b\hercules\rm48L952\8301bldc.het

    this contains the HET program source for the demo.  Line 175 should start with

    ;---------------------------------------------QEP-------------------------------
    ;---------------------------INDEX-CLR Counter ----------------------------------

    This is the start of the quadrature encoder state machine.  I believe the full function is from lines 175 to 221; but you can simulate the code in the HET IDE to make sure.   In fact you might start by simulating in the HET IDE to understand how it works.

  • On motoware page it only shows v1.01.00.11  version and not v1.0.3.03, therefore i don't see het folder.

    can you post a file here?

  • Sorry Dejan,
    I posted the wrong link - it was the link to motorware for the C2000.

    Hercules Motorware is here:  http://www.ti.com/tool/DRV8301-RM48-KIT

    And that's still v1.00.03.03.


    Best Regards,

    Anthony

  • Thanks for reply. Link is now correct.I will look into it. In case of problems, we will be in touch.

  • I have a problem running this on HET IDE. It's always stays in SPI configuration. 

    Could you please explain logic? Goal is to set the same solution as is done on STM32F4  encoder mode (http://www.micromouseonline.com/2013/02/16/quadrature-encoders-with-the-stm32f4/#axzz2sH8E0CiR)

    Just I want to do it on RM48L952, using N2HET. 

  • Dejan,

    Hi, the first instruction controls whether or not the program executes in SPI mode or not.  The default is SPI mode.

    To change this,  load the program into the HET IDE and setup your view so you can see (1) in the first screenshot.   See how the data field of the 1st instruction starts out at 0.  This value will select SPI mode.

    Normally the driver on the ARM cpu would write a 1 to this location to set the other mode, but the HET IDE doesn't include an ARM CPU so you manually change this field by double clicking on it and entering 0x00000001 so that it looks like (2).  Then step a few times and you should see that the program jumps into the timer function instead of the SPI function. (3).

    Regarding the algorithm, see http://www.ti.com/lit/ug/spnu514a/spnu514a.pdf,  the chapter on the eQEP which is the hardware quadrature encoder module that is on the RM46 and RM42 series parts.   The truth table (table 21-2 pg 859) is pretty close to what that portion of the HET program is implementing.  To me it just looks like the direction of the count is backwards from the table. (See below).

    In the HET program the first thing that is done is to check the index pin and clear the counter if there is an edge on the index

    ;---------------------------INDEX-CLR Counter ----------------------------------

    ;IND1     ECNT    { next = AFE1, cond_addr = CLRCNT1, event = Rise, pin = INDEX1, reg = NONE, data = 0} ;for anaheim motor

    IND1     ECNT    { next = AFE1, cond_addr = CLRCNT1, event = Fall, pin = INDEX1, reg = NONE, data = 0} ;for teknic motor
    CLRCNT1  MOV32   { next = AFE1, remote = FOR1, type = IMTOREG&REM, reg = A, Data = 0}

    Then there are checks for edges on the A, B pins.  For example this code:

    ;---------------------------check: aFbL  /   aFbH ------------------------------
    AFE1     BR      { next = ARE1, cond_addr = AFL1, event = Fall, pin = Pin_A1}
    AFL1     BR      { next =  BAC1, cond_addr = FOR1, event = Low, pin = Pin_B1}

    What do these two lines do?   Basically this:

    if (falling edge on A)    				; implemented by AFE1
         if (B is Low)  goto FOR1  else goto BAC1   		; implemented by AFL1
    else 					; implemented by AFE1
        goto ARE1  ; check for a rising edge on A next...	; implemented by AFE1
    end

    And you can line this up to the state diagram transitions highlighted:

     

    Note that for the backward direction it's a bit tricky in the sense that the SSUB1 instruction is implemented with an ADM32 (addition).   But the value that is added (0x1FFFFFF) is effectively '-1' since the addition is a 25-bit operation.

    I'd try running this code if you can without changing it first,  if that gets too complicated you can try just excerpting the QEP section of the code plus any '.equ' that are needed for it to compile, and try running that in the HET IDE.

    Ok here are the screenshots of how to set the program to go into the counter mode and leave the SPI mode.

     

  • Hi Anthony,

    First I would like to tell you, big thanks for detail report and support. I will try this steps tomorrow. Now is already 11PM ;)

     Still something is bordering me. Unfortunate for me, I don't have index pin. I have only two pins from the motor. That sad, could I replace index pin with pin A, or i'm missing something?

  • Hi Dejan,

    It's no problem if you don't have an index pin.  The index is just to get you an *absolute* indication of positon.  Without the index you know if the encoder rotates one direction or the other but you never know exactly where it is.  That's ok for many applications for example if you just want to pick up rotation speed.

    There's a couple different ways to disable the index pin - let's start out at the top of the HET program in the equate section:

    Pin_A1     .equ  25  ; Signal A from QEP encoder 

    Pin_B1     .equ  27  ; Signal B from QEP encoder

    INDEX1     .equ  21  ; Index from QEP encoder

    This means that the INDEX1 pin is on NHET[21].   If you don't want it there you can just change the .equ statement but I'd suggest leaving it on pin 21 to start with before making any code modifications.

    The index pin instruction looks for a Falling edge on the pin if you have this line uncommented:

    IND1     ECNT    { next = AFE1, cond_addr = CLRCNT1, event = Fall, pin = INDEX1, reg = NONE, data = 0} ;for teknic motor

    So, to keep the index pin from getting in the way you can just make sure it is high all the time.

    In the HET IDE the easiest way to accomplish this is through the stimulus tab, which shows up *after* you load your program into the simulator.  (so you have to hit the Assemble and Load button first... it's the red/orange 'star' icon on the toolbar)

    Below are some screenshots.  I've also setup pin A1 to be a square wave input.  You can *add* two more stimulus to your own project to do the same for pin B1 just make B1 out of phase from A1 by delaying the start time.  This should give you some good stimulus for the QEP program. 

    On the real silicon you could either pull the Index pin up with a resistor, or set the index pin to gio mode and drive a '1' out on the pin.    But by the time you are ready to do that you could just as easily remove the index function from the HET program.    Note:  Please don't try removing it first, before you try running as is.  Reason is that the HET program can jump around a lot, since each instruction has a 'next =' field which is essentially a 'goto'.  There's no guarantee that the program executes in a straight line, and if you delete a line you might break the program flow.  If I were deleting the line with label IND1 first I would search the entire program for any instructions that reference this label either as 'next=IND1' or 'cond_addr=IND1' or 'remote=IND1' and understand what other instructions might interact with it before deleting it.   So I'd leave this task until later when you are a little more comfortable with how the HET works, and the best way to get comfortable IMO is to take the working program and step through it, watching the disassembly, memory and register windows. 

    ABOVE: you have to have your program loaded in the simulator before the Stimulus Creater tab will be visible in the upper right.

     

     

    ABOVE:  Stimulus example to just drive the INDEX pin (pin 21) high at time 0.  no need to repeat....

    ABOVE: here's 1/2 of the stimulus needed to make a square wave on pin A1 (NHET[25]).  This drives the pin to a '0' starting at time 0 and repeating every 100us.

    The other half is a second stimulus that drives the pin to a value '1' starting at time 50us and repeating every 100us.

    Above, All three stimulii together.   One for the index, and 2 for the square wave on A1.

    You can add two more for a square wave on B1 and you'll  have a simple Quadrature input that you can use to test the program...

    ABOVE: You can see the INDEX pin high at time 0 which for this simulation should disable the index function.

    And you can see the pin A of a simple quadrature encoder model on #25.   Just add B and give it a try.

  • Excellent work!

    I have modified the code to my specification and works in HET IDE. Time is to test on the hardware.

    Here is the code:

    ;---------------------------------Variable Definitions--------------------------
    
    ;---------------------------------Input Encoder---------------------------------
    
    ENCODER_MAX_STEPS_VALUE   .equ  0x0FFFFFF
    ENCODER_MINUS_ONE_STEP    .equ  0x1FFFFFF
    
    ;---------------------Input Encoder Channel 1: Pin Configuration----------------
    
    Pin_A1     .equ  0		; Signal A from QEP encoder -> N2HET1[0]
    Pin_B1     .equ  1		; Signal B from QEP encoder -> N2HET1[1]
    
    ;---------------------Input Encoder Channel 2: Pin Configuration----------------
    
    Pin_A2     .equ  2		; Signal A from QEP encoder -> N2HET1[2]
    Pin_B2     .equ  3		; Signal B from QEP encoder -> N2HET1[3]
    
    ;-------------------------------------------------------------------------------
    ;---------------------------------HET Program-----------------------------------
    ;-------------------------------------------------------------------------------
    
    ;---------------------------------INPUT ENCODER - QEP---------------------------
    
    ;---------------------------------Start Channel 1-------------------------------
    
    ;---------------------------------Test Case 1----------------------------------- 
    ;----Check: Pin_A1 Fall and Pin_B1 Low or is Pin_A1 Fall and Pin_B1 High----
    ;-------------------------------------------------------------------------------
    
    AFE1     BR      { next = ARE1, cond_addr = AFL1, event = Fall, pin = Pin_A1}
    
    AFL1     BR      { next = BAC1, cond_addr = FOR1, event = Low, pin = Pin_B1}
    
    ;---------------------------------Test Case 2----------------------------------- 
    ;----Check: Pin_A1 Rise and Pin_B1 Low or is Pin_A1 Rise and Pin_B1 High----
    ;-------------------------------------------------------------------------------
    
    ARE1     BR      { next = BFE1, cond_addr = ARL1, event = rise, pin = Pin_A1}
    
    ARL1     BR      { next = BAC1, cond_addr = FOR1, event = high, pin = Pin_B1}
    
    ;---------------------------------Test Case 3----------------------------------- 
    ;----Check: Pin_B1 Fall and Pin_A1 Low or is Pin_B1 Fall and Pin_A1 High----
    ;-------------------------------------------------------------------------------
    
    BFE1     BR      { next = BRE1, cond_addr = BFL1, event = Fall, pin = Pin_B1}
    
    BFL1     BR      { next = BAC1, cond_addr = FOR1, event = high, pin = Pin_A1}
    
    ;---------------------------------Test Case 4----------------------------------- 
    ;----Check: Pin_B1 Rise and Pin_A1 High or is Pin_B1 Rise and Pin_A1 High---
    ;-------------------------------------------------------------------------------
    
    BRE1     BR      { next = AFE2, cond_addr = BRL1, event = rise, pin = Pin_B1}
    
    BRL1     BR      { next = BAC1, cond_addr = FOR1, event = low, pin = Pin_A1}
    
    ;----------------------------Turn Forward---------------------------------------
    ; This instruction presents moving steps, value is store in data field (range: 0 to ENCODER_MAX_STEPS_VALUE)
    ; In case data field is 0 and motor, turns backward, data = ENCODER_MAX_STEPS_VALUE 
    FOR1     CNT     { next = AFE2, reg = NONE, max = ENCODER_MAX_STEPS_VALUE}
    
    ;----------------------------Turn Backward--------------------------------------
    
    BAC1     MOV32   { next = LIM1, remote = FOR1, type = REMTOREG, reg = A}
    
    LIM1     ECMP    { next = SSUB1, cond_addr= HIL1, hr_lr=LOW, en_pin_action=off, pin=CC25, reg= A, data=0}
    
    HIL1     ADM32   { next = AFE2, remote = FOR1, type = IM&REGTOREM, reg = A, data = ENCODER_MAX_STEPS_VALUE}
    
    SSUB1    ADM32   { next = AFE2, remote = FOR1, type = IM&REGTOREM, reg = A, data = ENCODER_MINUS_ONE_STEP}
    
    ;---------------------------------End Channel 1---------------------------------
    
    ;---------------------------------Start Channel 2-------------------------------
    
    ;---------------------------------Test Case 1----------------------------------- 
    ;----Check: Pin_A2 Fall and Pin_B2 Low or is Pin_A2 Fall and Pin_B2 High----
    ;-------------------------------------------------------------------------------
    
    AFE2     BR      { next = ARE2, cond_addr = AFL2, event = Fall, pin = Pin_A2}
    
    AFL2     BR      { next = BAC2, cond_addr = FOR2, event = Low, pin = Pin_B2}
    
    ;---------------------------------Test Case 2----------------------------------- 
    ;----Check: Pin_A2 Rise and Pin_B2 Low or is Pin_A2 Rise and Pin_B2 High----
    ;-------------------------------------------------------------------------------
    
    ARE2     BR      { next = BFE2, cond_addr = ARL2, event = rise, pin = Pin_A2}
    
    ARL2     BR      { next = BAC2, cond_addr = FOR2, event = high, pin = Pin_B2}
    
    ;---------------------------------Test Case 3----------------------------------- 
    ;----Check: Pin_B2 Fall and Pin_A2 Low or is Pin_B2 Fall and Pin_A2 High----
    ;-------------------------------------------------------------------------------
    
    BFE2     BR      { next = BRE2, cond_addr = BFL2, event = Fall, pin = Pin_B2}
    
    BFL2     BR      { next = BAC2, cond_addr = FOR2, event = high, pin = Pin_A2}
    
    ;---------------------------------Test Case 4----------------------------------- 
    ;----Check: Pin_B2 Rise and Pin_A2 High or is Pin_B2 Rise and Pin_A2 High---
    ;-------------------------------------------------------------------------------
    
    BRE2     BR      { next = AFE1, cond_addr = BRL2, event = rise, pin = Pin_B2}
    
    BRL2     BR      { next = BAC2, cond_addr = FOR2, event = low, pin = Pin_A2}
    
    ;----------------------------Turn Forward---------------------------------------
    
    FOR2     CNT     { next = AFE1, reg = NONE, max = ENCODER_MAX_STEPS_VALUE}
    
    ;----------------------------Turn Backward--------------------------------------
    
    BAC2     MOV32   { next = LIM2, remote = FOR2, type = REMTOREG, reg = A}
    
    LIM2     ECMP    { next= SSUB2, cond_addr= HIL2, hr_lr=LOW, en_pin_action=off, pin=CC27, reg= A, data=0}
    
    HIL2     ADM32   { next = AFE1, remote = FOR2, type = IM&REGTOREM, reg = A, data = ENCODER_MAX_STEPS_VALUE}
    
    SSUB2     ADM32   { next = AFE1, remote = FOR2, type = IM&REGTOREM, reg = A, data = ENCODER_MINUS_ONE_STEP}
    
    ;---------------------------------End Channel 2---------------------------------
    
    ;---------------------------------End Input Encoder-----------------------------
    
    ;---------------------------------End Program-----------------------------------


    I have still one question:

    LIM1     ECMP    { next = SSUB1, cond_addr= HIL1, hr_lr=LOW, en_pin_action=off, pin=CC25, reg= A, data=0}
    Here is pin=CC25 selected. What this really means? Which is CC25 pin?

    Logic for direction and number of steps done in window (base on old CNT value and new CNT value), I will put outside in C.   Maybe I will extend HET program and add configurate timer for window. What do you think?

    BTW: Do you know a good way to also put PWM signal into this program with constant freq, but duty cycle could be modified by CPU? I notice that Z-flag goes on in this just encoder program, and makes the thing a little tricky.

    This HET module is hard to understand from the start, but once you get the hang of it, you can do lot of thing. The most hard thing for beginner is that each instruction has some many parameters.

  • Hi Dejan,

    Sounds like great progress and glad to hear you are ramping up on the HET IDE and programming so quickly.  I agree with you that it's difficult to learn and part of that is each instruction does so many different things at once.  It's almost like microcode.

    Thank you for posting your example - I think others will find it useful in the future.

    Regarding:

    Dejan Pojbic said:
    LIM1     ECMP    { next = SSUB1, cond_addr= HIL1, hr_lr=LOW, en_pin_action=off, pin=CC25, reg= A, data=0}
    Here is pin=CC25 selected. What this really means? Which is CC25 pin?

    CC25 is the old syntax that dates back from HET on the TMS470R1x series of devices from mid-late 1990s.
    The pins used to be called 'Capture Compare' hence CC.   Now we usually just use the number, like pin=25;  but pin=CC25 means the same thing to the assembler - no difference.   Sorry for the mixed notation that's used - it does make it confusing.


    Dejan Pojbic said:
    Logic for direction and number of steps done in window (base on old CNT value and new CNT value), I will put outside in C.   Maybe I will extend HET program and add configurate timer for window. What do you think?

    I'm not sure I understand but I'm guessing you are meaning that you want to get a speed in steps/time interval out of the QEP - is that right?     I think this could be worked into the HET program pretty easily.  You could put a periodic counter that when it matches reads from the QEP counter  (use an instruction with REMOTE capability to read from the data field of another instruction).  You could then subtract the last two counts and get a delta-counts for that constant time interval.

    Dejan Pojbic said:
    BTW: Do you know a good way to also put PWM signal into this program with constant freq, but duty cycle could be modified by CPU? I notice that Z-flag goes on in this just encoder program, and makes the thing a little tricky.

    I think you should be able to just insert the PWM at the top of the program (or the bottom but top could be easier since you want it to run every loop of the HET.).
    The HET IDE has some code templates for PWM that you can use.

  • Thanks man! For now this is all folks!

  • I have one more question is about debugging n2het interrupts in CSS. I notice that OFFSET flag is never clear.
    Is this correct?

    I clear the flag by N2HET1_REG->OFF1 - 1u;

  • Dejan,

    It's possible that OFF1 is never cleared for a number of reasons.

    However, first we should make a distinction between the interrupt flags themselves (which are stored in the HETFLG register) and the value that is in the OFF1 (and OFF2) register.  In the flag register there is a bit for each interrupt flag and you can check the status of all the flags at any one time.

    The offset register prioritizes the bits in the HETFLG register so you don't need to spend time doing this in software.  A side-effect of reading the OFF1 register is to clear the flag associated with the encoded value it returns, another time saving feature.

    Now, there could be some reasons why OFF1 is never cleared:

      1) If you are reading the OFF1 register through the emulator then the clear side-effect is disabled on emulator reads.  This is a debug feature otherwise inspecting the register would change it's state.

      2) If the HET stays running, it could be setting the flag again as soon as you clear it.   So it might look like it's not being cleared when in fact it's just being set over and over in rapid succession.

    Probably it's #2 that's occurring and you can setup the HET to halt on debug instead of running free in order to determine this with certainty.   Try making sure IS = 0 in HETGCR and then halt / step through the code that reads the OFF1 register. You should see your flag being cleared. 

    Alternatively, there could be some issue with this line of code:    N2HET1_REG->OFF1 - 1u;  since it's not really assigning anything.  Is the compiler generating code for this?  You actually should be reading the offset register into some varialbe like 'offset = N2HET1_REG->OFF1' and then using the variable in a switch statement to handle your different N2HET interrupts.

     

  • Sometimes it feel like you sad in 1. point and sometimes like 2. point. I will look IS flag.

    For the clearing the flag:
    I copy to little code. I do assignment and handle each interrupt in one handler. But thank for pointing it out. 

  • Hi Dejan
    I have one question about your code... You wrote "BRE2 BR { next = AFE1, cond_addr = BRL2, event = rise, pin = Pin_B2}". Why next=AFE1?? what that line does??
    best regards
  • Hi Miguel,

    its been 2 years and i dont really remember :) i think program is for two rotary motors (channel 1 and channel 2) so in case that conditional is not set you would go to start of the program and don't increment the movement.  Because you cant say if the motor is going forward or backward. I would suggest that you write this code into NHET and play with signals. And see how the program behaves. sorry.  

  • oh... its ok haha dont worry. just one last question.. you never cleared the count right? your application didnt give a full roud right?

    thanks!!
  • In the NHET is not cleared, but i cleared it from C code. the last question i dont understand.
  • your motors gave a full round?