Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 17 of 17

Thread: port and toggle question T4

  1. #1
    Junior Member
    Join Date
    Sep 2019
    Posts
    11

    port and toggle question T4

    Dear readers,

    Just started with Teensy 4.
    Trying to find out how I can toggle a pin like in 3.6 with
    PORTC_PCR4


    and how the mapping : PORT to pin?
    Like in T3.6
    PORTC_PCR4


    Thanks,

    hanz

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,945
    Quick answer/example for T_3.6 after : pinMode(LED_BUILTIN, OUTPUT);

    Either of these will toggle the current state of LED Pin #13:

    #define qBlink() {GPIOC_PTOR=32;}

    #define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) ))

  3. #3
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    Quote Originally Posted by defragster View Post
    Quick answer/example for T_3.6 after : pinMode(LED_BUILTIN, OUTPUT);

    Either of these will toggle the current state of LED Pin #13:

    #define qBlink() {GPIOC_PTOR=32;}

    #define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) ))

    Hello defragster,
    Sorry I mean how to do it in T4:
    GPIOC_PTOR=0x10;


    hanz

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,682
    The hardware register you seek is documented on page 1037 of the IMXRT1060 reference manual (rev 1, 12/2018).

    https://www.pjrc.com/teensy/IMXRT1060RM_rev1.pdf

    For the mapping, first check out the schematic to get the pad name.

    https://www.pjrc.com/teensy/schematic.html

    For example, if you choose pin 5, the pad name is EMC_08. Then to figure out which GPIO unit controls it, look up that pin in the IOMUX chapter. For the case of EMC_08, it's on page 434.

    Click image for larger version. 

Name:	gpio3.png 
Views:	16 
Size:	92.8 KB 
ID:	17698
    (click for full size)

    Unfortunately GPIO4_IO08 is not the end of the story. Each pin can be controlled by either of 2 different GPIO units. GPIO 1 to 5 are accessed through a (slow) peripheral bridge. GPIO 6 to 9 are accessed by the fast AHB bus. By default (due to the startup code), we use the fast ones on Teensy 4.0.

    This detail is buried in the reference manual, on pages 371-372.

    Click image for larger version. 

Name:	gpio2.png 
Views:	19 
Size:	97.9 KB 
ID:	17697

    GPIO9 is actually used.

    So you could toggle the pin with this code:

    Code:
    void setup() {
      pinMode(5, OUTPUT);
    }
    
    void loop() {
      while (1) {
        GPIO9_DR_TOGGLE = 1 << 8;
        delayMicroseconds(1);
      }
    }
    I ran it here on a Teensy 4.0. Here's the waveform on pin 5:

    Click image for larger version. 

Name:	file.png 
Views:	13 
Size:	27.3 KB 
ID:	17699

    However, if you want to toggle the pin at extreme speed, there is only more detail you need. By default, pinMode() configures the pin for slew rate limiting and moderate bandwidth. Normally that's a very helpful feature to reduce noise. If you want maximum speed, you need to write to IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_08, which is documented on page 569-570.

    For example:

    Code:
    void setup() {
      pinMode(5, OUTPUT);
      IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_08 = 0xFF;
      noInterrupts();
    }
    
    void loop() {
      while (1) {
        GPIO9_DR_TOGGLE = 1 << 8;
      }
    }
    This will produce a 150 MHz waveform.

    Sadly, my oscilloscope has only 200 MHz bandwidth (and passive probes rated for only 700 MHz), so when I try to measure this I get pretty much just the 150 MHz fundamental. Here's how it looks:

    Click image for larger version. 

Name:	file2.png 
Views:	10 
Size:	44.3 KB 
ID:	17700

    In another thread, Greg at Stanford University used a 1.5 GHz bandwidth scope and active probe to measure these waveforms. Even with that amazing gear, good measurements are tough due to the length of a ground wire.

    I should also point out, you can get this exact same 150 MHz waveform using digitalWriteFast().

    Code:
    void setup() {
      pinMode(5, OUTPUT);
      IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_08 = 0xFF;
      noInterrupts();
    }
    
    void loop() {
      while (1) {
        digitalWriteFast(5, HIGH);
        digitalWriteFast(5, LOW);
      }
    }

    But if you *really* want to mess with direct register access to the toggle register, there's all the info you'll need.

  5. #5
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    Many many Thanks Paul i'm going to dig in what you have written.


    best regards hanz

  6. #6
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    Hello Paul,
    I got the 150Mhz output, but got one more question.
    I'm using the Pit lifetime timer for counting, in the document I see snibbets for setting the pit. I see in one example PIT at 50mhz and an other one PIT is 100mhz, how can I set this?
    I'm using a state machine and this counter to generate on different pins different duty cycle outputs, but they have to start all at the same time.

    hanz

  7. #7
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,231
    Quote Originally Posted by hanz View Post
    Hello Paul,
    I got the 150Mhz output, but got one more question.
    I'm using the Pit lifetime timer for counting, in the document I see snibbets for setting the pit. I see in one example PIT at 50mhz and an other one PIT is 100mhz, how can I set this?
    I'm using a state machine and this counter to generate on different pins different duty cycle outputs, but they have to start all at the same time.

    hanz
    In startup.c T4 configures PIT and GPT to run at 24 mhz. To run PIT and GPT at 150mhz, in your setup() do
    CCM_CSCMR1 &= ~CCM_CSCMR1_PERCLK_CLK_SEL;
    (this will break IntervalTimer which expects 24mhz)

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,945
    Quote Originally Posted by PaulStoffregen View Post
    The hardware register you seek is documented on page 1037 of the IMXRT1060 reference manual (rev 1, 12/2018).

    https://www.pjrc.com/teensy/IMXRT1060RM_rev1.pdf

    For the mapping, first check out the schematic to get the pad name.

    https://www.pjrc.com/teensy/schematic.html

    For example, if you choose pin 5, the pad name is EMC_08. Then to figure out which GPIO unit controls it, look up that pin in the IOMUX chapter. For the case of EMC_08, it's on page 434.


    ...
    Great Post Paul! This would make a great BlogPost on interpreting the RefMan. Maybe the "Teensy on Arduino IS Bare Metal" series!

    Given that digitalReadFast and digitalWriteFast do it as well is nice - but if the BlogPost extended to Group/Parallel Read/Write like two recent threads. For instance your simple SDIO POGO test 6 pin running frequency

    Teensy-4-0-First-Beta-Test :: Create 6 different frequencies on the SD card pins
    Parallel-IO-is-it-possible
    Teensy-3-5-or-Teensy-4-0-Parallel-Synchronous-DMA-output

  9. #9
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    Thank you very much Manitu, i'm digging in. Learning a lot on the way.
    Many thanks.

    hanz

  10. #10
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    Thank you all for the answers it helps me a lot.
    If I may, I try to find in the T4 environment the isr routines for the PIT. In 3.6 I had this running but do not get it how to do it in 4.

    Thanks hanz

  11. #11
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,231
    Quote Originally Posted by hanz View Post
    Thank you all for the answers it helps me a lot.
    If I may, I try to find in the T4 environment the isr routines for the PIT. In 3.6 I had this running but do not get it how to do it in 4.

    Thanks hanz
    See this and its following posts https://forum.pjrc.com/threads/54711...l=1#post195605

  12. #12
    Junior Member
    Join Date
    Sep 2019
    Posts
    11
    Quote Originally Posted by manitou View Post
    Again Thanks a lot :-)

  13. #13
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Quote Originally Posted by PaulStoffregen View Post
    Unfortunately GPIO4_IO08 is not the end of the story. Each pin can be controlled by either of 2 different GPIO units. GPIO 1 to 5 are accessed through a (slow) peripheral bridge. GPIO 6 to 9 are accessed by the fast AHB bus. By default (due to the startup code), we use the fast ones on Teensy 4.0.

    This detail is buried in the reference manual, on pages 371-372.

    Click image for larger version. 

Name:	gpio2.png 
Views:	19 
Size:	97.9 KB 
ID:	17697

    GPIO9 is actually used.

    ...

    But if you *really* want to mess with direct register access to the toggle register, there's all the info you'll need.
    @Paul or other... I noticed the register you mentioned that changed using GPIO1->GPIO6

    But What I have not noticed is anything in manual that describes the differences between the two. Or really for example describes GPIO6-9?
    I am probably missing it, but ...

    As for Toggle, At times I wish we would actually define: digitalToggleFast()... That uses the toggle register!

  14. #14
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,231
    Quote Originally Posted by KurtE View Post
    @Paul or other... I noticed the register you mentioned that changed using GPIO1->GPIO6

    But What I have not noticed is anything in manual that describes the differences between the two. Or really for example describes GPIO6-9?
    I am probably missing it, but ...
    lots of posts on T4-beta-test thread about fast GPIO on 1062, also see https://www.nxp.com/docs/en/applicat...te/AN12240.pdf

  15. #15
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    @manitou - Thanks, I remember reading posts as they went through, but did not remember the details on the speed...

    Yep that document has lots more details. Maybe @PaulStoffregen may want to put link up in the documents section...

    But I still wonder about some of the trade offs, and if in some cases we might want to make it more easily configurable.

    If I understand some of this correctly (maybe big assumption). Now when have all possible IO pins routed to GPIO6-9, there is only one one IRQ (157) that is assigned to all 4 of these ports, or all 128 possible GPIO pins. So when a sketch does something like attachInterrupt(0, &my_isr, RISING);

    All of these will funnel through one internal interrupt handler.
    Code:
    FASTRUN static inline __attribute__((always_inline))
    inline void irq_anyport(volatile uint32_t *gpio, voidFuncPtr *table)
    {
    	uint32_t status = gpio[ISR] & gpio[IMR];
    	if (status) {
    		gpio[ISR] = status;
    		while (status) {
    			uint32_t index = __builtin_ctz(status);
    			table[index]();
    			status = status & ~(1 << index);
    			//status = status & (status - 1);
    		}
    	}
    }
    
    FASTRUN
    void irq_gpio6789(void)
    {
    	irq_anyport(&GPIO6_DR, isr_table_gpio1);
    	irq_anyport(&GPIO7_DR, isr_table_gpio2);
    	irq_anyport(&GPIO8_DR, isr_table_gpio3);
    	irq_anyport(&GPIO9_DR, isr_table_gpio4);
    }
    Where when we are not using this Fast GPIO mode, there are several more Interrupts defined, especially on GPIO1
    Where ISRs 72-79 Go directly with GPIO1 Pins 0-7 which in our case I think we only have Pin 0 (1.3) and Pin1(1.2)
    But in addition:
    80 - GPIO 1.0-15
    81 - GPIO 1.16-31
    82 - GPIO 2.1-5
    ...
    89 - GPIO 4.16-31

    So lots less pins to scan for changes. Also Some of these ranges actually only have one valid T4 pin on them
    Example on 89 there is only 4.31 on pin 29... So again could be real fast.

    Some only have 2: 83 (2.17, 2.16) on pins 7, 8
    ...

    So again if I need/want fast ISRs, some of these pins may work great to simply set attachInterruptVector and bypass most of the overhead.

    But again I am probably missing something obvious.

    If not, wonder if we should allow some easy way to configure this...

  16. #16
    Junior Member
    Join Date
    Sep 2019
    Location
    Essex, UK
    Posts
    8
    What a great thread - only this week was I poring through the documentation to work out how to map the physical pins to the controlling registers, particularly with respect being able to handle fast interrupts on pin changes!
    This has at least given me a way in even though my programming skills are somewhat basic.
    I look forward to any follow-ups on this from Paul - if he has time!!
    Cheers
    Ed

  17. #17
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    @all... I thought I would sort of answer my self in wondering about ISR Speeds... So I mucked up a test program to get a general idea of differences in overhead...

    So did a simple test program to see differences in ISR overhead. What it sort of shows to me, that one can still muck their way through and setup direct ISR if they want, but not sure worth the hassle. Setup where I did one pin as normal attachInterrupt() and had a jumper to another pin that I toggle N times... In the ISR I read pin state and echo it out to another pin. I then look to see delta time between the two pins changing state with Logic Analyzer. I also did with attachInterruptVector to different IO pin (0), swtiched that pin back to use GPIO1, and then tried as well..

    Code sort of primitive, but shows some of the delta time differences. Would be better to hook up to external fast ISR generator like an encoder...
    Code:
    #define IRQ_PIN 0
    #define ECHO_PIN 1
    #define TRIGGER_PIN 2
    
    #define IRQ2_PIN 3
    #define ECHO2_PIN 4
    #define TRIGGER2_PIN 5
    
    #define CORE_PIN0_PINREG_SLOW  GPIO1_PSR
    #define readFastIRQPin() ((CORE_PIN0_PINREG_SLOW & CORE_PIN0_BITMASK) ? 1 : 0)
    uint32_t cycles_per_second = 100;  //
    void setup() {
      while (!Serial && millis() < 5000) ;
      Serial.begin(115200);
      pinMode(IRQ_PIN, INPUT);
      pinMode(ECHO_PIN, OUTPUT);
      pinMode(TRIGGER_PIN, OUTPUT);
      digitalWrite(TRIGGER_PIN, LOW);
      Serial.printf("Test IRQ timing:\n    Pins IRQ:%d ECHO:%d TRIGGER: %d\n", IRQ_PIN, ECHO_PIN, TRIGGER_PIN);
      pinMode(IRQ2_PIN, INPUT);
      pinMode(ECHO2_PIN, OUTPUT);
      pinMode(TRIGGER2_PIN, OUTPUT);
      digitalWrite(TRIGGER2_PIN, LOW);
      Serial.printf("    Normal pins: IRQ:%d ECHO:%d TRIGGER: %d\n", IRQ2_PIN, ECHO2_PIN, TRIGGER2_PIN);
      delay(500);
    
      //---------------------------------------------
      // First lets setup pin 0 to slow mode and direct ISR...
      CCM_CCGR1 |= CCM_CCGR1_GPIO1(CCM_CCGR_ON);
      attachInterruptVector(IRQ_GPIO1_0_15, &pin_isr);
      NVIC_ENABLE_IRQ(IRQ_GPIO1_0_15);
      Serial.println("After Attach"); Serial.flush();
      // I think this will have GPIO1 handle its pin 3
      IOMUXC_GPR_GPR26 = 0xFFFFFFFF;
      Serial.println("After set IOMUXC"); Serial.flush();
      GPIO1_ICR1 = 0x00; // set to 0
      Serial.println("After set ICR1"); Serial.flush();
      GPIO1_GDIR &= ~0x08;  // Make sure set as input in GPIO1
      GPIO1_EDGE_SEL = 0x08; // set to 0
      GPIO1_ISR = 0xffff;
      Serial.println("After set ISR1"); Serial.flush();
      GPIO1_IMR = 0x08;
      Serial.println("After set GPIO"); Serial.flush();
    
      //---------------------------------------------
      // Next setup pin 3 to use attach interrupt in normal mode
      attachInterrupt(IRQ2_PIN, &pin2_isr, CHANGE);
    }
    
    volatile uint32_t irq_count = 0;
    
    void pin_isr(void) {
      digitalWriteFast(ECHO_PIN, digitalReadFast(IRQ_PIN));
      irq_count++;
      GPIO1_ISR = 0x08; // clear the IRQ
      asm("dsb");
    }
    
    void pin2_isr(void) {
      digitalWriteFast(ECHO2_PIN, digitalReadFast(IRQ2_PIN));
      irq_count++;
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      Serial.printf("Enter cycles per second default(%d):", cycles_per_second);
      while (!Serial.available()) ;
      uint32_t cps = 0;
      int ch;
      while ((ch = Serial.read()) != -1) {
        if ((ch >= '0') && (ch <= '9')) cps = cps * 10 + ch - '0';
      }
      if (cps) {
        cycles_per_second = cps;
      }
      uint32_t delay_per_cycle = 1000000 / (cycles_per_second * 2);
    
      irq_count = 0;
      elapsedMicros em = 0;
      for (uint32_t i = 0; i < cycles_per_second; i++) {
        digitalWriteFast(TRIGGER_PIN, HIGH);
        delayMicroseconds(delay_per_cycle);
        digitalWriteFast(TRIGGER_PIN, LOW);
        delayMicroseconds(delay_per_cycle);
      }
    
      uint32_t delta_time = em;
      Serial.printf("\nDirect IRQs processed:  %u dt: %d calc:%d\n", irq_count,
                    delta_time, delay_per_cycle * 2 * cycles_per_second);
    
      // Now do normal way
    
      irq_count = 0;
      em = 0;
      for (uint32_t i = 0; i < cycles_per_second; i++) {
        digitalWriteFast(TRIGGER2_PIN, HIGH);
        delayMicroseconds(delay_per_cycle);
        digitalWriteFast(TRIGGER2_PIN, LOW);
        delayMicroseconds(delay_per_cycle);
      }
    
      delta_time = em;
      Serial.printf("Normal IRQs processed:  %u dt: %d calc:%d\n", irq_count,
                    delta_time, delay_per_cycle * 2 * cycles_per_second);
    
    }
    Note: The differences in elapsed micros times between direct and Normal, also gives an indication of how much more time was taken processing the ISR, as the normal loop is not running when the ISR code is running...
    Code:
    Test IRQ timing:
        Pins IRQ:0 ECHO:1 TRIGGER: 2
        Normal pins: IRQ:3 ECHO:4 TRIGGER: 5
    After Attach
    After set IOMUXC
    After set ICR1
    After set ISR1
    After set GPIO
    Enter cycles per second default(100):
    Direct IRQs processed:  200 dt: 1000004 calc:1000000
    Normal IRQs processed:  200 dt: 1000005 calc:1000000
    Enter cycles per second default(100):
    Direct IRQs processed:  200 dt: 1000004 calc:1000000
    Normal IRQs processed:  200 dt: 1000005 calc:1000000
    Enter cycles per second default(100):
    Direct IRQs processed:  2000 dt: 1000040 calc:1000000
    Normal IRQs processed:  2000 dt: 1000043 calc:1000000
    Enter cycles per second default(1000):
    Could show some Logic Analyzer output, to show some differences, but see differences like:
    My direct way the delta time between the IO pin changing and my echo was something like: 55-66ns (looking at 500mhz so ...)
    The way using attachInterrupt something like: 180-190ns

    So again the simple answer is yes you can process pin change interrupts faster, but probably in majority of cases it aint worth it! As this is a really fast processor!

Posting Permissions

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