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: Tutorial on digital I/O, ATMega PIN/PORT/DDR D/B registers vs. ARM GPIO_PDIR / _PDOR

  1. #76
    Thanks Leray,

    I had my doubts about applying the mask to GPIOB_PDOR. So the following should work?


    GPIOB_PDOR = (GPIOB_PDIR & ~b_mask) | (send_value & b_mask); // Modify only 6 bits in Port B


    I was thinking that if the syntax above will safely modify the value on the port, it wouldn't actually matter if the bits within the port are contiguous. With the correct mask I can access all the available bits within a given port and access them all simultaneously.

    Although a more straight forward parallel interface would be great, if I can get away with manipulating the ports using this approach, it would be versatile enough for my needs.

  2. #77
    Member
    Join Date
    Feb 2015
    Location
    Rians in Provence, France
    Posts
    71
    Quote Originally Posted by RichardPeteSharp View Post
    Thanks Leray,

    I had my doubts about applying the mask to GPIOB_PDOR. So the following should work?


    GPIOB_PDOR = (GPIOB_PDIR & ~b_mask) | (send_value & b_mask); // Modify only 6 bits in Port B


    I was thinking that if the syntax above will safely modify the value on the port, it wouldn't actually matter if the bits within the port are contiguous. With the correct mask I can access all the available bits within a given port and access them all simultaneously.

    Although a more straight forward parallel interface would be great, if I can get away with manipulating the ports using this approach, it would be versatile enough for my needs.
    You could be completely safe using GPIOB_GPIR (With 'I' as INPUT)for reading the state of the whole B register. And relaoad it after your boolean operations.

  3. #78
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,628
    I could be wrong but I have no idea why you would be looking at the state of the GPIOB_PDIR register? This is the Input state (read only register), of those IO pins on that object...
    You can readn GPIOB_PDOR - For the current output state of all IO pins on GPIOB... This register is Read/Write. (Page 2189 of my T3.6 PDF...)

    So you can do things like:
    Code:
    GPIOB_PDOR = (GPIOB_PDOR & ~b_mask) | (send_value & b_mask);
    I am not sure which N pins of the GPIOB you are using. But hypothetically you are using: Bits 0-5 for 6 bits, which quick look is something like pins:
    (16, 17, 19, 18, 49, 50)

    However I assume you realize that you run a risk with this code. For example suppose your code uses some form of interrupts, example an intervalTimer, and suppose your interval timer procedure does something like:

    Code:
    void myIntervalTimerProc() {
        digitalWriteFast(0, !digitalReadFast(0));
         (it's work)
    }
    And suppose you have the above code like: GPIOB_PDOR = (GPIOB_PDOR & ~0x3f) | (send_value & 0x3f);

    Now suppose your code is running, and just after it reads the GPIOB_PDOR but before it updates it, your ISR runs and changes the state of D0 (which is B16)
    When the ISR returns and your code completes the update of the GPIOB_PDOR you just throw away the updated state from the ISR...

    And these are fun bugs to track down.. Been there!


    Again there are ways to fix this, like:
    Code:
    	__disable_irq();
    	GPIOB_PDOR = (GPIOB_PDOR & ~b_mask) | (send_value & b_mask);
    	__enable_irq();
    or if you can stand the timings and a pins changing in two steps... Maybe something like:
    Code:
    	GPIOB_PSOR = send_value;   // assumes send_value only has the N bits set or cleared in it if not mask it.
    	GPIOB_PCOR = ~send_value & b_mask;
    These operators only muck with the bits you are using and as such are interrupt safe...

  4. #79
    That is a huge help. Your 2nd example that allows me to clear or set only the bits I'm using, without touching anything else, is absolutely perfect. Since I only send brief signals and already zero out the bits afterwards, it's actually more of a one step process for me to use GPIOx_PSOR to put a value on the port.

    I followed your lead and looked at the data sheets at https://www.pjrc.com/teensy/datasheets.html

    The "General-Purpose Input/Output (GPIO)" chapter in the T3.5 and T3.6 manuals was very concise and easy to follow. It's obvious once you know it, but that chapter put everything discussed in this thread into perfect context for me.

  5. #80
    Senior Member
    Join Date
    May 2019
    Location
    Brisbane, QLD
    Posts
    102
    *where i = uint32_t

    I've been doing some code optimization using GPIO ports on the T.36, for some reason I'm having troubles with GPIOB that I'm not seeing on other ports.

    Why doesn't this second equation work equally to the first (which does work)?

    example 1 works.
    Code:
    GPIOB_PSOR = ((i & 0x40)    << 12);
    GPIOB_PSOR = ((i & 0x80)    << 12);
    example 2 doesn't.
    Code:
    GPIOB_PSOR = ((i & 0xC0)    << 12);
    its the same port and the same shift, so i should be able to combine the two. Works well on other ports, not GPIOB. Any thoughts?

  6. #81
    It looks like you are setting bits 18 and 19 in GPIOB. In the second example, both bits are set from i at the same time. In the first example, the first line sets bit 18 from i with bit 19 set to 0, and the second line sets bit 19 from i with bit 18 set to 0. At the end, bit 18 will always be 0.

    To make example 1 work like example 2, you would OR the previous value of GPIOB_PSOR:

    Code:
    GPIOB_PSOR = (GPIOB_PSOR & ~0x40000) | ((i & 0x40)    << 12);
    GPIOB_PSOR = (GPIOB_PSOR & ~0x80000) | ((i & 0x80)    << 12);
    That should make example 1 fail just like example 2

  7. #82
    Senior Member
    Join Date
    May 2019
    Location
    Brisbane, QLD
    Posts
    102
    Actually still confused. I thought a value of "0" had no effect on bits for PSOR? I've been clearing the bitmasks at the beginning of the loop with PCOR...

    PSOR: Port Set Output Register: 0 has no effect. 1 sets the corresponding bit/pin to 1.
    Quote Originally Posted by LAtimes View Post
    It looks like you are setting bits 18 and 19 in GPIOB. In the second example, both bits are set from i at the same time. In the first example, the first line sets bit 18 from i with bit 19 set to 0, and the second line sets bit 19 from i with bit 18 set to 0. At the end, bit 18 will always be 0.

    To make example 1 work like example 2, you would OR the previous value of GPIOB_PSOR:

    Code:
    GPIOB_PSOR = (GPIOB_PSOR & ~0x40000) | ((i & 0x40)    << 12);
    GPIOB_PSOR = (GPIOB_PSOR & ~0x80000) | ((i & 0x80)    << 12);
    That should make example 1 fail just like example 2

  8. #83
    Sorry, I've been using PDOR and didn't notice the one letter difference. It looks like it should work with PSOR, but I've never used those registers so don't have any experience with them. I'll give it a try this evening.

  9. #84
    I was not able to duplicate your problem. Both ways always set both pins. I first set and cleared the bits individually, then set and cleared them together.

    Click image for larger version. 

Name:	Untitled2.jpg 
Views:	2 
Size:	114.5 KB 
ID:	19923

    Here is my code:

    Code:
    pinMode (29, OUTPUT);
    pinMode (30, OUTPUT);
    
    uint32_t i = 0xFFFFFFFF;
    
    while (1)
    {
      GPIOB_PSOR = ((i & 0x40)    << 12);
      GPIOB_PSOR = ((i & 0x80)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PCOR = ((i & 0x40)    << 12);
      GPIOB_PCOR = ((i & 0x80)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PSOR = ((i & 0xC0)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PCOR = ((i & 0xC0)    << 12);
    
      delay (1000);
    }

  10. #85
    Senior Member
    Join Date
    May 2019
    Location
    Brisbane, QLD
    Posts
    102
    I switched over to PDOR and havenít experienced the same issues.

    I will revisit this again, but given that I can write and clear bits in one operation with PDOR, it turns out to be more efficient vs PSOR + PCOR.

    For most of my ports, the global setting of pins shouldnít be an issue, but one port (GPIOB) has my ADC hooked up to it (B1)and Iím concerned it may erase that value.

    Is there a way to ignore writing specific port bit when using PDOR?

    Appreciate you taking the time to look into this.

    Cheers.

    Quote Originally Posted by LAtimes View Post
    I was not able to duplicate your problem. Both ways always set both pins. I first set and cleared the bits individually, then set and cleared them together.

    Click image for larger version. 

Name:	Untitled2.jpg 
Views:	2 
Size:	114.5 KB 
ID:	19923

    Here is my code:

    Code:
    pinMode (29, OUTPUT);
    pinMode (30, OUTPUT);
    
    uint32_t i = 0xFFFFFFFF;
    
    while (1)
    {
      GPIOB_PSOR = ((i & 0x40)    << 12);
      GPIOB_PSOR = ((i & 0x80)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PCOR = ((i & 0x40)    << 12);
      GPIOB_PCOR = ((i & 0x80)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PSOR = ((i & 0xC0)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PCOR = ((i & 0xC0)    << 12);
    
      delay (1000);
    }

  11. #86
    Senior Member
    Join Date
    May 2019
    Location
    Brisbane, QLD
    Posts
    102
    Think my issue could be related to setting optimize to "Fastest with LTO" in arduino IDE. When i switch it to "Faster with LTO", the corruption goes away...

    I did a test, recompiling both ways. Either it's problematic with GPIO port commands, or just the way I'm coding it has to be phrased a differnet way to play nice...

    Quote Originally Posted by LAtimes View Post
    I was not able to duplicate your problem. Both ways always set both pins. I first set and cleared the bits individually, then set and cleared them together.

    Click image for larger version. 

Name:	Untitled2.jpg 
Views:	2 
Size:	114.5 KB 
ID:	19923

    Here is my code:

    Code:
    pinMode (29, OUTPUT);
    pinMode (30, OUTPUT);
    
    uint32_t i = 0xFFFFFFFF;
    
    while (1)
    {
      GPIOB_PSOR = ((i & 0x40)    << 12);
      GPIOB_PSOR = ((i & 0x80)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PCOR = ((i & 0x40)    << 12);
      GPIOB_PCOR = ((i & 0x80)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PSOR = ((i & 0xC0)    << 12);
    
      delayMicroseconds (1);
    
      GPIOB_PCOR = ((i & 0xC0)    << 12);
    
      delay (1000);
    }

  12. #87
    Think my issue could be related to setting optimize to "Fastest with LTO" in arduino IDE. When i switch it to "Faster with LTO", the corruption goes away...
    I tried my example above compiled with "Fastest with LTO" at speeds up to 240 MHz and it worked every time. It may be a subtle timing issue with your setup - what defines 'not working'.

    For most of my ports, the global setting of pins shouldnít be an issue, but one port (GPIOB) has my ADC hooked up to it (B1)and Iím concerned it may erase that value.
    If a pin is not assigned to GPIO via the MUX (by calling pinMode (x, OUTPUT)), then the value in GPIO_PDOR does not affect the pin.

  13. #88
    Senior Member
    Join Date
    May 2019
    Location
    Brisbane, QLD
    Posts
    102
    I suspect youíre right. I think maybe the code is ďtoo fastĒ in some situations for the memory Iím controlling.

    Iím going to try and program in some delay (without using the delay()) and see if that helps. The memory wants specific timing once an address line is written before itís ready to read the data, maybe 70ns.

    Since delayMicroseconds() is too slow (1us min), I was just going to wing it using a while or for loop and experiment with the count max. Unless someone has built a delayNanoseconds() libray...

    Quote Originally Posted by LAtimes View Post
    I tried my example above compiled with "Fastest with LTO" at speeds up to 240 MHz and it worked every time. It may be a subtle timing issue with your setup - what defines 'not working'.



    If a pin is not assigned to GPIO via the MUX (by calling pinMode (x, OUTPUT)), then the value in GPIO_PDOR does not affect the pin.

  14. #89
    Teensy 4 has a delayNanoseconds(), but not Teensy 3.

    This line has about 40 nanosecond overhead plus 40 nanosecond delay per loop at 240 MHz (80 nanoseconds at 120 MHz):

    Code:
    for (int volatile index = 0; index < 1; index++);
    So 40 nanoseconds for 0, 80 for 1, 120 for 2, 160 for 3, etc.
    Last edited by LAtimes; 05-02-2020 at 05:10 AM.

  15. #90
    Senior Member
    Join Date
    May 2019
    Location
    Brisbane, QLD
    Posts
    102
    Bullseye!

    I plugged that in before i read the datalines and BINGO. It adds about 0.8% more CPU overhead (vs without delay), but allows me to compile using Fastest With LTO.

    Is this more efficient than using a timer?

    Thanks for your help, 3 days ive been struggling with this issue.

    Quote Originally Posted by LAtimes View Post
    Teensy 4 has a delayNanoseconds(), but not Teensy 3.

    This line has about 40 nanosecond overhead plus 40 nanosecond delay per loop at 240 MHz (80 nanoseconds at 120 MHz):

    Code:
    for (int volatile index = 0; index < 1; index++);
    So 80 nanosecond for 1, 120 for 2, 160 for 3, etc.

Posting Permissions

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