Forum Rule: Always post complete source code & details to reproduce any issue!
Page 4 of 4 FirstFirst ... 2 3 4
Results 76 to 90 of 90

Thread: Hardware Quadrature Code for Teensy 3.x

  1. #76
    Junior Member
    Join Date
    Apr 2018
    Posts
    2
    I have had a look at the code by TLB, and I am quite puzzled by the fact that the addresses of the registers that are set up do not match the addresses as specified in the memory map from the K20 reference manual.

    All the adresses seem to be off by 0x1000 compared to the datasheet (or kinetis.h for that matter...).

    What am I missing here?
    Last edited by polwel; 04-10-2018 at 09:37 PM. Reason: typos

  2. #77
    Junior Member
    Join Date
    Apr 2018
    Posts
    2
    OK, nevermind, I had been looking at FTM0 in the reference instead of FTM1... D'oh.


  3. #78
    I'm new to the Arduino IDE.
    What is the best way to install the five files by tlb for use in the IDE?

  4. #79
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,543
    The programs attached are:

    QuadDecode.h, QuadDecode_def.h - the important ones. Code to utilize the hardware quadrature decode channels on Teensy3.x

    main.cpp - an example of use. As I mentioned, it is templated, and I did not Arduinofy it, so this shows how to use the templates.

    GenEncoder.h, GenEncoder.cpp - program that generates simulated encoder signals for debug and development.


    That means that you do not forcibly have to include all 5 files.

    As tlb wrote, he did not "Arduinofy" it. That means that you can use the example file main.cpp as inspiration and documentation to create your own .ino sketch based on that. You'd have to copy QuadDecode.h and QuadDecode_def.h in your sketch folder and using #include tags to use their functions in the .ino file.

  5. #80
    Theremingenieur,
    Thanks, got it.

  6. #81
    Junior Member
    Join Date
    Sep 2018
    Posts
    2
    I feel like this is a silly question but I don't see a way to download tlb's attached files... Where can I find them?

  7. #82
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,543
    In the very first post of this thread. Depending on your browser and OS, these files are either opened or downloaded when you click on them. In case they open, just copy/paste the content in new tabs with identical names in the Arduino IDE

  8. #83
    Junior Member
    Join Date
    Sep 2018
    Posts
    2
    Ahhh, for some reason on my laptop the list of files wasn't showing but I see them now.

  9. #84
    Junior Member
    Join Date
    Sep 2017
    Posts
    11
    Quote Originally Posted by BrianC View Post
    Hi TLB,

    For 4x count mode, 4 counts are made per pulse (low, rising, high, falling)
    For 2x count mode, 2 counts are made per pulse (low, high)
    For 1x count mdoe, 1 count is made per pulse (just the high I believe).

    The encoder library is in 4x count mode https://www.pjrc.com/teensy/td_libs_Encoder.html
    Is there any way to set 2x or 1x mode when using the Encoder library?

    I’d like to reduce the amount of times the interrupts are triggered and would be okay with resolution being lowered.

    Thanks

  10. #85
    Junior Member
    Join Date
    Nov 2015
    Posts
    15
    Sorry for the necro. This is a great library, but there's one catch - tlb uses pointers and non-template functions in the headers.

    If you try to include the library in your sketch and into another library (e.g. trying to allow a communications library to use encoder data), you'll get a pile of multiple definition errors from the linker (despite the compile guards and even a #pragma once). It's a particularly tricky thing to debug if you haven't worked much with template classes.

    I separated out those non-template parts into its own cpp. I'm uploading it here in case it helps someone else with the same problem.

    I haven't changed anything except the arrangement of the code in files.

    I also added an arduino sketch showing example use of the library (but not the encoder simulator).

    QuadDecode.zip
    Attached Files Attached Files

  11. #86
    Senior Member xxxajk's Avatar
    Join Date
    Nov 2013
    Location
    Buffalo, NY USA
    Posts
    532
    It is worth noting that C++ templates are actually considered a separate language. I try to avoid them when possible.
    Sometimes they make sense, though, and in some instances I have found that templates can produce smaller code footprints.
    No idea on why, but it is an observation of mine.

  12. #87
    Junior Member
    Join Date
    Aug 2016
    Posts
    10
    This code was extremely useful for 3.X’s Can teensy 4.0 use this code in any way?

  13. #88
    Junior Member
    Join Date
    Aug 2019
    Posts
    8
    Hello everyone,

    thanks for the code! I'm fairly new to uC-design and it offered me a great help and a bit of guidance through all these registers.
    But I still need help.

    I want to use the quadrature decoder not for positioning, but for speed. It is used to read out the speed of a motor, so it needs to run continuously. I migrated the project from an Arduino Due (with built in quad decoder) to a teensy 3.6. On the Arduino, I had an "extra" timer which just counted the main clock up to create a user-given interval, when the counter reached a limit it resets and at the same time it triggers the readout from the quadrature decoder which was stored in a variable.
    But FTM1 and FTM2 on the Teensy only have two channels which are used for the encoder input. I tried to play around with some software waiting, but that contradicts the idea of doing the decoding on the hardware.
    I hope you understand, what I try to do, and I'm not exactly sure if that is even possible. Can you help me out?

    Right now I use the quaddecoder.h from this post. Since the readout should be continuous, there is also no problem with only having 16bit CNT since that value should not be reached within the desired time frame (motor has 2000 increments per rotation, is used around 1 rps)

  14. #89
    Junior Member
    Join Date
    Aug 2019
    Posts
    8
    Hello,

    I got the encoder working, works nice. It is used to control a piezo motor. The motor is pulsed, so the pin for turning the motor on is set HIGH at the beginning of a cycle and the encoder throws an interrupt once the desired encoder flanks are counted, which turns the motor off. I managed to use an IntervalTimer with the cycle, so i got away from the loop()-method at all.
    Using a template from this thread, i setup FTM2 in quadrature decode modus and in the setup it is given the MOD count, when to interrupt.
    The speed of the motor is defined by this MOD count and the intervaltime of a cycle. For varying speed while the program/motor is running, I want to be able to change the MOD. But writing to the register never updates it. Tried different variations (reading FTM2_SC = 0, ...) which I found in some threads around here, but none if them worked.
    Looking at the chip manual, it says that the MOD register is writen independent from FTMEN bit, if CLKS[1:0]=0:0, which is the case for my quadrature encoding since no internal clock is used?
    Do I need to reset anything or disable the write protection which is setup?

    My whole code is here:

    The QuadratureDecode Subclass
    Code:
    #ifndef QUADDECODE_H
    #define QUADDECODE_H
    
    #include <stdint.h>
    #include "mk20dx128.h"
    #include "core_pins.h"
    
    /*
    * - MAGIE - 
    * https://forum.pjrc.com/threads/26803-Hardware-Quadrature-Code-for-Teensy-3-x?p=90774&viewfull=1#post90774
    */
    
    class QuadDecode_t {
      public:
        QuadDecode_t(int cts) {
    
          // Pin Assignments
    
          // FTM2 Pins
          // K20 pin 41,42
          // Bit 8-10 is Alt Assignment
          PORTB_PCR18 = 0x00000612;   //Alt6-QD_FTM2,FilterEnable,Pulldown
          PORTB_PCR19 = 0x00000612;   //Alt6-QD_FTM2,FilterEnable,Pulldown
    
          //Set FTMEN to be able to write registers
          FTM2_MODE=0x04;	    // Write protect disable - reset value
          FTM2_MODE=0x05;	    // Set FTM Enable
    
          // Set registers written in pins_teensy.c back to default
          FTM2_CNT = 0;
          FTM2_MOD = 0;
          FTM2_C0SC =0;
          FTM2_C1SC =0;
          FTM2_SC = 0x40;     //enable hardware trigger
    
          FTM2_FILTER=0x22;	// 2x4 clock filters on both channels
          FTM2_CNTIN=0;
          FTM2_MOD=cts;	    // Maximum value of counter
          FTM2_CNT=0;	    	// Updates counter with CNTIN
    
          FTM2_QDCTRL=0b11000001;	    // Quadrature control
          //        Filter enabled, QUADEN set
    
          // Write Protect Enable
          FTM2_FMS=0x40;		// Write Protect, WPDIS=1
    
          
          NVIC_ENABLE_IRQ(IRQ_FTM2);  
        }
    
        void setCounter2( int16_t c ) {
          FTM2_CNT = c;
        }
    
        int16_t getCounter2( ) {
          int16_t c = FTM2_CNT;
    
          return c;
        }
    
      void call_changeCTS(int count){
        FTM2_MOD = count;
        Serial.println(count);
      }
    };
    
    
    
    #endif

    The MAIN CLASS which controls the motor interval and basically just switches counts from 10 to 50 (but nothing happens)
    Code:
    #include "QuadDecode.h"
    #include "AD9837.h"
     
    #define pin_motor_direction 9 // for toggling the direction (left and right) of the motor
    #define pin_enable_pushpull_converter 17 // to turn the push pull converters on and off
    #define pin_test 20 // test pin for various debugging
    
    const long interval = 107000; //Intervall in MIKROSEKUNDEN!
    const int cts = 19; //cts pro Intervall - 1 weniger als angegeben! (!!!Schon vorher prüfen?!!!)
    
    volatile bool motorTurning = false; 
    bool dutyCycleReached = true;
    
    const double minfrequency = 48000;
    double frequency = 55000;
    double initialfrequency;
    float dutyCycle = 0.25;
    
    AD9837 ad9837(frequency*4);
    QuadDecode_t QuadDecode(cts);
    
    IntervalTimer motorTimer; //Intervaltimer des Motors
    IntervalTimer dutyCycleTimer; //Hilfsintervall zur Überprüfung vom eingestellten Duty Cycle
    
    elapsedMillis changeCTS;
    bool switched = false;
      
    void setup() {
      tone(2, 2000, 200);
          // TURN OFF PUSH PULL CONVERTER
      pinMode(pin_enable_pushpull_converter,OUTPUT);
      digitalWrite(pin_enable_pushpull_converter, LOW);
    
      pinMode(pin_test, OUTPUT);
      digitalWrite(pin_test, LOW);
    
        // SETUP MOTOR DIRECTION
      pinMode(pin_motor_direction,OUTPUT); // set TOGGLE to OUTPUT
      digitalWrite(pin_motor_direction, LOW);  // set high for one motor direction
    
      Serial.begin(115200); 
      delay(1000);
     
      ad9837.setfrequency(frequency * 4);
     
        // TURN ON PUSH PULL CONVERTER
      digitalWrite(pin_enable_pushpull_converter, HIGH);
      motorTurning = false;
      while(!motorTurning){
        if(frequency < minfrequency){
          frequency = 49000;
        }
        frequency = frequency - 100;
        ad9837.setfrequency(frequency * 4);
        delay(300);
        Serial.println(frequency);
      }
      digitalWrite(pin_enable_pushpull_converter, LOW);
      frequency = frequency - 1000;
      Serial.println(frequency);
      ad9837.setfrequency(frequency * 4);
      initialfrequency = frequency;
    
    
    
      tone(2, 400, 200);
      delay(200);
      tone(2, 800, 200);
      delay(200);
      tone(2, 2000, 200);
      Serial.println("Start");
    
      digitalWrite(pin_enable_pushpull_converter, HIGH);
      motorTimer.begin(motorLoop, interval);
      dutyCycleTimer.begin(dutyCycleLoop, interval*dutyCycle);
      changeCTS = 0;
    }
     
     
     
    void loop() {
    
    	if(changeCTS >= 10000){
    		changeCTS = 0;
    		if(switched){
    			QuadDecode.call_changeCTS(50);
    		}
    		else{
    			QuadDecode.call_changeCTS(10);
    		}
    		switched = !switched;
    	}
    
    }
    
    
    //ISR FUNKTIONEN
    
    //ISR Counter-Overflow: Erforderliche Anzahl Flanken erreicht
    void ftm2_isr(void) {
      digitalWrite(pin_enable_pushpull_converter, LOW);
      motorTurning = true;
      // clear overflow flag 
      FTM2_SC&=0x7F;
      //digitalWrite(pin_test, LOW);
    }
    
    //Duty Cycle Überprüfung
    void dutyCycleLoop(){
      if(motorTurning) {
        dutyCycleReached = true;
      }
      dutyCycleTimer.end();
    }
    
    //Motor Cycle Loop
    void motorLoop(){
      //falls Flanken NICHT erreicht!
      if(!motorTurning){
        Serial.println("Dreht nicht! Dreht zu langsam! Erreicht erforderliche Flanken nicht! Frequenz herabsetzen.");
        frequency -= 250;
        ad9837.setfrequency(frequency * 4);
      }
      //falls Duty Cycle nicht erreicht!
      else if(!dutyCycleReached){
        Serial.println("Dreht, aber Duty Cycle wurde nicht erreicht! Frequenz herabsetzen.");
        frequency -= 250;
        ad9837.setfrequency(frequency * 4);
      }
      //falls unter die Mindestfrequenz gerutscht - setze Duty Cycle rauf, nochmal von vorne!
      if(frequency <= minfrequency){
        Serial.println("Innerhalb des vorgegebenen Duty Cycle nicht möglich! Erlaube höheren Duty Cycle.");
        frequency = initialfrequency;
        dutyCycle += 0.1;
        ad9837.setfrequency(frequency * 4);
        if(dutyCycle > 0.95){
          while(1){
            tone(2, 4500, 100);
            delay(200);            
          }
        }
      }
      //ansonsten: "ganz normaler" Durchlauf, setze Variablen zurück, "starte" Motor erneut
      motorTurning = false;
      dutyCycleReached = false;
      dutyCycleTimer.begin(dutyCycleLoop, interval * dutyCycle);
      digitalWrite(pin_enable_pushpull_converter, HIGH);
    }

  15. #90
    Junior Member
    Join Date
    Aug 2019
    Posts
    8
    I got it working, by adjusting the FTM_PWMLOAD register. Not very intuitive to look there.
    The code snippet in the ISR where the MOD is changed is:

    Code:
    void ftm2_isr(void) {
    	if((FTM2_SC & FTM_SC_TOF) != 0){
    		FTM2_SC &= ~FTM_SC_TOF;
    	}
      digitalWrite(pin_enable_pushpull_converter, LOW);
      motorTurning = true;
    
      digitalWrite(pin_test, LOW);
    
      if(newMOD != 0){
    	  Serial.println(newMOD);
    	  FTM2_CNTIN = 0;
    	  FTM2_MOD = newMOD;
    	  FTM2_CNT = 0;
    	  FTM2_PWMLOAD = 0x200; //sets the LDOK bit in PWNLOAD register
    	  newMOD = 0;
    	  Serial.println("MOD should be changed.");
      }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •