Shop OBEX P1 Docs P2 Docs Learn Events
I have begun my Propeller 2 journey! - Page 7 — Parallax Forums

I have begun my Propeller 2 journey!

123457

Comments

  • @JonnyMac said:
    Doesn't Catalina's _pinw() function mimic pinwrite() from Spin2?

    How do I do a pin group with _pinw()? I saw this from another thread: pingroup := basepin ADDPINS count
    is ADDPINS defined somewhere? How would I write this in c? I need to control 8 bits from pin 32 to 39. Or any group of 8 bits...

    I also saw this example:

    pingroup := 56 addpins 3
    pinwrite(pingroup, %0101)
    repeat
    waitms(250)
    pintoggle(pingroup)

    The addpins again is the missing link here.

  • I don't think there is a function like that in C.

    _pinw(int pin, int val);
    _pinl(int pin);
    _pinh(int pin);
    _pinnot(int pin);
    _pinrnd(int pin);
    _pinf(int pin);
    _pinr(int pin);
    
    OUTA = Pins 0 - 31
    OUTB = Pins 32 - 63
    DIRA = Pins 0 - 31
    DIRB = Pins 32 - 63
    
    DIRA |= 0xff; // set pins 0 - 7 to output
    OUTA |= 0xFF; // set pins 0 - 7 to high
    

    Mike

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-04-22 22:15

    The Spin2 addpins feature works like this: pingroup = LSB | ((MSB-LSB) << 6)

    I don't think there is a function like that in C.

    I don't know how inline assembly works in Catalina. If it's available, one could borrow the code from the Spin2 interpreter. This is from the interpreter moved to inline PASM in Spin2

    pub spin_pinw(pingroup, value) | mask
    
    
      org
                            ror       pingroup, #6          wc
                            bitc      pinw_reg, #9                  ' get & set out? register
    
                            bmask     mask, pingroup                ' mask for added pin count
                            rol       pingroup, #6                  ' restore pin group
    
                            rol       mask, pingroup                ' align with lsb
                            rol       value, pingroup               ' align with lsb
    
                            setq      mask                          ' prep selected pins
    pinw_reg                muxq      outa, value                   ' update
                            dirh      pingroup                      ' make outputs
    
      end
    
  • RossHRossH Posts: 5,477

    @enorton said:

    Ok, so that worked for the B port but does not work for the A port. I wrote the same code but replaced the B with A and nothing. What I am trying to do is toggle based on an eight-bit value.

    Works for me on a P2 EDGE with a breakout board. I tried several different groups of 8 pins and it works on both port A and B.

    What board and pins are you using specifically?

    Ross.

  • RossHRossH Posts: 5,477
    edited 2023-04-23 00:43

    @JonnyMac said:
    I don't know how inline assembly works in Catalina. If it's available, one could borrow the code from the Spin2 interpreter. This is from the interpreter moved to inline PASM in Spin2

    Here is JonnyMac's code in Catalina inline PASM:

    #include <stdint.h>
    
    #ifdef __CATALINA_NATIVE
    
    void spin_pinw(uint32_t pingroup, uint32_t value) {
       // note: 'value' is passed in r2, 'pingroup' in r3
       //           for 'mask' we can use r0
       PASM(
         "            ror       r3, #6        wc\n"
         "            bitc      pinw_reg, #9      ' get & set out? register\n"
    
         "            bmask     r0, r3            ' mask for added pin count\n"
         "            rol       r3, #6            ' restore pin group\n"
    
         "            rol       r0, r3            ' align with lsb\n"
         "            rol       r2, r3            ' align with lsb\n"
    
         "            setq      r0                ' prep selected pins\n"
         "pinw_reg    muxq      outa, r2          ' update\n"
         "            dirh      r3                ' make outputs\n"
       );
    }
    
    #else
    
    #error This implementation of spin_pinw will only work in NATIVE mode.
    
    #endif
    

    NOTE: I have no idea what this does and have not tested it!

    NOTE 2: The above PASM is only for NATIVE mode - the syntax of PASM is different in other modes (e.g. COMPACT, LARGE). So I have added a #error statement to warn about this.

    Ross.

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-04-22 23:47

    NOTE: I have no idea what this does and have not tested it!

    It writes value to one or more outputs pins -- though the group must be confined to OUTA or OUTB. The lower six bits of pingroup is the LSB, and bits 6..10 determine how many additional (contiguous) pins are part of the group.

  • RossHRossH Posts: 5,477

    @JonnyMac said:

    NOTE: I have no idea what this does and have not tested it!

    It writes value to one or more outputs pins -- though the group must be confined to OUTA or OUTB. The lower six bits of pingroup is the LSB, and bits 6..10 determine how many additional (contiguous) pins are part of the group.

    Ok, thanks. Should work ok in Catalina.

  • @RossH said:

    @JonnyMac said:
    I don't know how inline assembly works in Catalina. If it's available, one could borrow the code from the Spin2 interpreter. This is from the interpreter moved to inline PASM in Spin2

    Here is JonnyMac's code in Catalina inline PASM:

    #include <stdint.h>
    
    void spin_pinw(uint32_t pingroup, uint32_t value) {
       // note: 'value' is passed in r2, 'pingroup' in r3
       //           for 'mask' we can use r0
       PASM(
         "            ror       r3, #6        wc\n"
         "            bitc      pinw_reg, #9      ' get & set out? register\n"
    
         "            bmask     r0, r3            ' mask for added pin count\n"
         "            rol       r3, #6            ' restore pin group\n"
    
         "            rol       r0, r3            ' align with lsb\n"
         "            rol       r2, r3            ' align with lsb\n"
    
         "            setq      r0                ' prep selected pins\n"
         "pinw_reg    muxq      outa, r2          ' update\n"
         "            dirh      r3                ' make outputs\n"
       );
    }
    

    NOTE: I have no idea what this does and have not tested it!

    Ross.

    ****Ok here is what I am trying to do:****

    uint8_t drive_port;

    // Direction Bits
    drive_port = ((drive_port & ~DIR_MASK) | (st.dir_outbits & DIR_MASK));
    _OUTB = drive_port;

    // Outbits
    drive_port = ((drive_port & ~STEP_MASK) | st.step_outbits);
    _OUTB = drive_port;

    After some delay, I have yet to implement:

    drive_port = ((drive_port & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK));
    _OUTB = drive_port;

    This works on another microcontroller and the function they have is:

    /**
    * @brief Set GPIO Port OUT Data
    *
    * @param[in] port GPIO port. It could be PA, PB, PC, PD, PE, PF, PG or PH.
    * @param[in] u32Data GPIO port data.
    *
    * @return None
    *
    * @details Set the Data into specified GPIO port.
    * \hideinitializer
    */

    define GPIO_SET_OUT_DATA(port, u32Data) ((port)->DOUT = (u32Data))

    The other microcontroller I call this:

    GPIO_SET_OUT_DATA(PB, drive_port);

    As you can see it is pretty similar to the Propeller 2 by sending the data to the 32-bit pins output register. The way I have done it with the other microcontroller works fantastic and with the Propeller 2 it isn't working.

    I wonder if I have _pinh() and _pinl() functions active would they overwrite the OUTA and OUTB writes? I have eeprom and two other I2C devices on P0 - P5 and I have serial ports on P48 - P55. Just curious if those pins are active would they overwrite the 8 bits data trying to go out?

  • @enorton said:

    @RossH said:

    @JonnyMac said:
    I don't know how inline assembly works in Catalina. If it's available, one could borrow the code from the Spin2 interpreter. This is from the interpreter moved to inline PASM in Spin2

    Here is JonnyMac's code in Catalina inline PASM:

    #include <stdint.h>
    
    void spin_pinw(uint32_t pingroup, uint32_t value) {
       // note: 'value' is passed in r2, 'pingroup' in r3
       //           for 'mask' we can use r0
       PASM(
         "            ror       r3, #6        wc\n"
         "            bitc      pinw_reg, #9      ' get & set out? register\n"
    
         "            bmask     r0, r3            ' mask for added pin count\n"
         "            rol       r3, #6            ' restore pin group\n"
    
         "            rol       r0, r3            ' align with lsb\n"
         "            rol       r2, r3            ' align with lsb\n"
    
         "            setq      r0                ' prep selected pins\n"
         "pinw_reg    muxq      outa, r2          ' update\n"
         "            dirh      r3                ' make outputs\n"
       );
    }
    

    NOTE: I have no idea what this does and have not tested it!

    Ross.

    ****Ok here is what I am trying to do:****

    uint8_t drive_port;

    // Direction Bits
    drive_port = ((drive_port & ~DIR_MASK) | (st.dir_outbits & DIR_MASK));
    _OUTB = drive_port;

    // Outbits
    drive_port = ((drive_port & ~STEP_MASK) | st.step_outbits);
    _OUTB = drive_port;

    After some delay, I have yet to implement:

    drive_port = ((drive_port & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK));
    _OUTB = drive_port;

    This works on another microcontroller and the function they have is:

    /**
    * @brief Set GPIO Port OUT Data
    *
    * @param[in] port GPIO port. It could be PA, PB, PC, PD, PE, PF, PG or PH.
    * @param[in] u32Data GPIO port data.
    *
    * @return None
    *
    * @details Set the Data into specified GPIO port.
    * \hideinitializer
    */

    define GPIO_SET_OUT_DATA(port, u32Data) ((port)->DOUT = (u32Data))

    The other microcontroller I call this:

    GPIO_SET_OUT_DATA(PB, drive_port);

    As you can see it is pretty similar to the Propeller 2 by sending the data to the 32-bit pins output register. The way I have done it with the other microcontroller works fantastic and with the Propeller 2 it isn't working.

    I wonder if I have _pinh() and _pinl() functions active would they overwrite the OUTA and OUTB writes? I have eeprom and two other I2C devices on P0 - P5 and I have serial ports on P48 - P55. Just curious if those pins are active would they overwrite the 8 bits data trying to go out?

    I can confirm that the "drive_port" bits toggle when commanded through a debug serial connection I set up.

  • RossHRossH Posts: 5,477

    @enorton said:
    ****Ok here is what I am trying to do:****

    uint8_t drive_port;

    // Direction Bits
    drive_port = ((drive_port & ~DIR_MASK) | (st.dir_outbits & DIR_MASK));
    _OUTB = drive_port;

    // Outbits
    drive_port = ((drive_port & ~STEP_MASK) | st.step_outbits);
    _OUTB = drive_port;

    After some delay, I have yet to implement:

    drive_port = ((drive_port & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK));
    _OUTB = drive_port;

    It would be better if you posted actual code. For instance, what is "_OUTB"?

    I wonder if I have _pinh() and _pinl() functions active would they overwrite the OUTA and OUTB writes? I have eeprom and two other I2C devices on P0 - P5 and I have serial ports on P48 - P55. Just curious if those pins are active would they overwrite the 8 bits data trying to go out?

    If you are using the same pins, then that seems very likely. Try testing with pins that are not used for other purposes.

    Ross.

  • @RossH said:

    @enorton said:
    ****Ok here is what I am trying to do:****

    uint8_t drive_port;

    // Direction Bits
    drive_port = ((drive_port & ~DIR_MASK) | (st.dir_outbits & DIR_MASK));
    _OUTB = drive_port;

    // Outbits
    drive_port = ((drive_port & ~STEP_MASK) | st.step_outbits);
    _OUTB = drive_port;

    After some delay, I have yet to implement:

    drive_port = ((drive_port & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK));
    _OUTB = drive_port;

    It would be better if you posted actual code. For instance, what is "_OUTB"? _OUTB is from the propeller2.h file. Is it not the same as "OUTB" from the propeller.h file?

    I wonder if I have _pinh() and _pinl() functions active would they overwrite the OUTA and OUTB writes? I have eeprom and two other I2C devices on P0 - P5 and I have serial ports on P48 - P55. Just curious if those pins are active would they overwrite the 8 bits data trying to go out?

    If you are using the same pins, then that seems very likely. Try testing with pins that are not used for other purposes.

    I am using pins 32 to 39 for the 8-bit output port. These pins are dedicated to outputting the 8-bits data only and not used for any other function.

    Ross.

    Where can I find the functions for _pinh(), pinl() etc? When calling these functions do, they also write to OUTA or OUTB registers? When I do something like this:

    _DIRB |= 0x000000FF;
    _OUTB = 0x000000FF;

    Then all eight outputs turn on but this is called when the chip initializes. When used in the code I attached earlier I do not see the outputs pulsing...

  • @RossH said:

    @enorton said:

    Ok, so that worked for the B port but does not work for the A port. I wrote the same code but replaced the B with A and nothing. What I am trying to do is toggle based on an eight-bit value.

    Works for me on a P2 EDGE with a breakout board. I tried several different groups of 8 pins and it works on both port A and B.

    What board and pins are you using specifically?

    Ross.

    I have the P2 EVAL board. Pins 32 to 39 are the outputs I am trying to write to.

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-04-23 01:33

    Given you're at the LSB end of outb and always want to write eight bits, it seems like this might be the way to go (it works in when tested inline with Spin2).

    void write3239(uint8_t bits)
    {
      PASM(
        "                       andn      outb, #$FF                    ' clear p32..p39 \n"   
        "                       or        outb, r2                      ' write to p32..p39 \n"    
        "                       or        dirb, #$FF                    ' set to outputs \n"    
      );
    }
    

    Note: I could never get productive with Catalina so I have it, but don't use it. The first parameter passed seems to be in r2 based on Ross's update of my code (above) and examples I've found. BTW, look for .s files in the Catalina folder; they seem to hold function source code..

  • You can't do OUTB = 0xff, This will set all the 32 pins to that state IE, 32 - 40 will be high and 41 - 63 low.

    Also unlike STM, once a cog accesses a pin that pin is owned by that cog and no other cog can make use of those pins.

    Mike

  • RossHRossH Posts: 5,477
    edited 2023-04-23 02:32

    @enorton said:

    Where can I find the functions for _pinh(), pinl() etc? When calling these functions do, they also write to OUTA or OUTB registers? When I do something like this:

    _DIRB |= 0x000000FF;
    _OUTB = 0x000000FF;

    Then all eight outputs turn on but this is called when the chip initializes. When used in the code I attached earlier I do not see the outputs pulsing...

    My bad - I had forgotten that I had agreed with the other C compiler makers that on the P2 we would use _DIRA etc, not DIRA. Actually, if you include both <propeller.h> and <propeller2.h> you can use either or both in Catalina. But that's not the issue.

    I suspect your problem is actually that initialization code, or some similar code elsewhere. If you set an output 'high' in one cog, you cannot then set it 'low' from another. The logic of how pins are combined is described quite well in the Propeller 1 documentation - I could not find the equivalent description in the Propeller 2 documentation, but it would be similar:

    The cog collective determines Pin Directions and Pin Outputs as follows:
    1. Pin Directions are the result of OR'ing the Direction Registers of the cogs together.
    2. Pin Outputs are the result of OR'ing the output states of the cogs together. A cog's output state consists of the bits of its I/O modules (the Counters, the Video Generator, and the I/O Output Register) OR'd together then AND'd with the bits of its Direction Register.
    In essence, each I/O pin’s direction and output state is the “wired-OR” of the entire cog collective. This allows the cogs to access and influence the I/O pins simultaneously without the need for any resource arbiter and without any possibility of electrical contention between the cogs.

    The result of this I/O pin wiring configuration can easily be described in the following simple rules:
    A. A pin is an input only if no active cog sets it to an output.
    B. A pin outputs low only if all active cogs that set it to output also set it to low.
    C. A pin outputs high if any active cog sets it to an output and also sets it high.

    Ross.

  • RossHRossH Posts: 5,477
    edited 2023-04-23 02:33

    @JonnyMac said:
    Given you're at the LSB end of outb and always want to write eight bits, it seems like this might be the way to go (it works in when tested inline with Spin2).

    void write3239(uint8_t bits)
    {
      PASM(
        "                       andn      outb, #$FF                    ' clear p32..p39 \n"   
        "                       or        outb, r2                      ' write to p32..p39 \n"    
        "                       or        dirb, #$FF                    ' set to outputs \n"    
      );
    }
    

    Note: I could never get productive with Catalina so I have it, but don't use it. The first parameter passed seems to be in r2 based on Ross's update of my code (above) and examples I've found. BTW, look for .s files in the Catalina folder; they seem to hold function source code..

    Some people prefer Spin. I prefer C :)

    Yes, your code is correct - in Catalina, up to 4 function parameters are passed in registers, using local registers r2 .. r5 (if there are more than 4 parameters they are passed on the stack) - but it starts from the right. So when you wrap an inline PASM call in a function, such as func(int a, int b, int c, int d), it would result in d being passed in r2, c in r3, b in r4 and a in r5. Local registers r0 and r1 can be used as scratch registers, and to return a value put it in r0 before exiting the function:

    // this function returns a+b+c+d ...
    int func(int a, int b, inc c, int d) {
       return PASM(   // note the 'return' here to return r0
          "                       mov r0, r2\n" // r2 = d
          "                       add r0, r3\n" // r3 = c
          "                       add r0, r4\n" // r4 = b
          "                       add r0, r5\n" // r5 = a
       );
    }
    

    The .s files are the compiled output of Catalina (which is actually just PASM). The sources are in /source/lib - either .c for C sources, or .e for PASM sources.

  • AribaAriba Posts: 2,690

    The P2 has Assembler instructions to set a byte of a 32bit register (OUTB here) without affecting the other bits. So this may be a fast way (untested):

    void write3239(uint8_t bits)
    {
      PASM(
        "                       setbyte   outb, r2, #0                  ' write 8 bits to p32..p39 \n"   
      );
    }
    
      ...
      _DIRB |= 0xFF;          //set once to output
      for(i=0; i<256; i++)
          write3239(i);       //write 8 bits
      ...
    

    You don't need to set the direction bits each time you write a new value, just do it once when the direction needs to change.

    Andy

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-04-23 02:58

    Thanks for the reminder, Andy. I plugged that into my inline PASM and it works as expected/intended. I tend to be a little verbose (e.g., writing to dirb in that code) so that routines can be atomic.

  • OK I figured it out and thanks to you all for saving the day once again :smiley:

    It turns out that it DOES matter where you set up the variables etc. I had it initialized in another cog and not the intended one. One question though, is there any way I can access the outputs from another cog by way of a variable of some sort and not by changing the outputs directly per se?

  • This whole multi-processor thing is taking some time for me to get used to, unfortunately. I'm sure as I work with it more it becomes second nature but right now, I struggle with the dumbest things like OUTPUTS for instance lol... I feel like a real idiot sometimes doing this.

  • RossHRossH Posts: 5,477

    @enorton said:
    OK I figured it out and thanks to you all for saving the day once again :smiley:

    It turns out that it DOES matter where you set up the variables etc. I had it initialized in another cog and not the intended one. One question though, is there any way I can access the outputs from another cog by way of a variable of some sort and not by changing the outputs directly per se?

    Not exactly sure what you want, but it would be easy enough to set up a global variable and then have all writes to the outputs use a function that updated both the outputs and the variable.

    Ross.

  • RossHRossH Posts: 5,477

    @enorton said:
    This whole multi-processor thing is taking some time for me to get used to, unfortunately. I'm sure as I work with it more it becomes second nature but right now, I struggle with the dumbest things like OUTPUTS for instance lol... I feel like a real idiot sometimes doing this.

    The Propeller makes all of us look like idiots sometimes! :)

  • @RossH said:

    @enorton said:
    OK I figured it out and thanks to you all for saving the day once again :smiley:

    It turns out that it DOES matter where you set up the variables etc. I had it initialized in another cog and not the intended one. One question though, is there any way I can access the outputs from another cog by way of a variable of some sort and not by changing the outputs directly per se?

    Not exactly sure what you want, but it would be easy enough to set up a global variable and then have all writes to the outputs use a function that updated both the outputs and the variable.

    Ross.

    Hmm very interesting :smiley:

    I will give this some thought. Btw the basic functionality is working pretty good now. Just need the updates from you, fix a few issues and port the rest of the code over.

  • @RossH said:

    @enorton said:
    This whole multi-processor thing is taking some time for me to get used to, unfortunately. I'm sure as I work with it more it becomes second nature but right now, I struggle with the dumbest things like OUTPUTS for instance lol... I feel like a real idiot sometimes doing this.

    The Propeller makes all of us look like idiots sometimes! :)

    It's pretty confusing that's for sure. Some days I'm working great and think this really isn't bad at all and then all the sudden nope. It's a fun chip to work with so far. Thank you all very much for helping me out. It is greatly appreciated :smiley:

  • RossHRossH Posts: 5,477

    @enorton said:
    Just need the updates from you, fix a few issues and port the rest of the code over.

    I have uploaded a beta pre-release of 5.9 here

    Note that this is a Windows only release, intended to give you a preview of the new functionality, and it has not been extensively tested. Install it to a different directory than Catalina 5.8 (by default it will install to "Catalina_5.9 PRE_RELEASE BETA 1") in case you need to revert (note that to swap between installed versions you must change the LCCDIR environment variable - simply selecting other installed versions on the Start Menu is not sufficient).

    Here is the relevant extract from the README.TXT:

    New Functionality
    -----------------
    
    1. The multiport serial libraries (serial2, serial4, serial8) have each had two
       new functions added:
    
          int sX_txcount(unsigned port)
          int sX_rxcount(unsigned port)
    
       where X = 2, 4 or 8. The functions return the current count of characters 
       waiting in the transmit or receive buffers.
    
    Other Changes
    -------------
    
    1. The Catalina Propeller 2 Reference Manual said the 8 port serial functions 
       s8_openport() and s8_closeport() returned an int. In fact, they both return 
       a void - i.e. they don't return anything. The manual has been updated.
    
    2. The Catalina maths functions round() and trunc() were incorrectly defined 
       in catalina_float.h as returning floats - in fact, they both returned long 
       values. These functions have been retained, but have been renamed as
       _round() and _trunc(). 
    
       Also, these functions were only available when a maths co-processor was 
       loaded - e.g. if -lma, -lmb or -lmc (P2 only). New software implementation
       called lround() and ltrunc() which return longs have been defined so that
       they are available when any maths library option is used - i.e. including 
       the -lm option.
    
       Also, new functions have been added called fround() and ftrunc() which DO 
       return floats. So now a program that needs round() or trunc() can define 
    
       EITHER:
    
          #define round(x) lround(x)
          #define trunc(x) ltrunc(x)
    
       OR:
    
          #define round(x) fround(x)
          #define trunc(x) ftrunc(x)
    
       OR:
    
          #define round(x) _round(x)
          #define trunc(x) _trunc(x)
    
       depending on whether it needs versions that returns longs, versions that
       return floats (or doubles - in Catalina doubles are the same as floats, 
       so if the program expects these functions to return doubles, it will work 
       perfectly well with the new versions) or it wants to retain the original
       versions.
    
    
    3. Catalina's original implementation of trunc(), which was also used when 
       a float was "cast" to an integer, was based on Cam Thompson's original 
       Float32_A co-processor, which unnecessarily limited the range in which 
       it would return a value. That range has been extended to include the 
       maximum range that can be encoded accurately in a float. Note that 
       this does not mean all longs in this range can be encoded - the way 
       IEEE457 floats are implemented means there are "gaps" in the integers
       that can be encoded as floats once you exceed the number of bits in the 
       mantissa. However, within the newly extended range (which corresponds to
       the integer range of -2,147,483,583 to 2,147,483,583) the implementation 
       of both ltrunc() and lround() will now return the correct integer value. 
       Outside that range they will return zero.
    
    4. The documentation regarding the 8 Port Serial plugin was a little 
       contradictory regarding the meaning and use of S8_MAX_PORTS. Setting this 
       determines the maximum number of ports that CAN BE OPENED, not the number 
       of ports that will be opened automatically. A port will only be opened 
       automatically if the port number is less than S8_MAX_PORTS, AND it has 
       pins specified in the range 0 .. 63 in the platform configuration file. 
       If the port has pins specified outside this range (e.g. as -1) then the 
       port will not be opened automatically but can be opened manually using
       the s8_openport() function. A port number greater or equal to S8_MAX_PORTS 
       will never be opened. If you want different baud rates or large buffer 
       sizes, it is recommended to open the ports manually.
    
    5. The accuracy of the various _wait functions (i.e. _waitsec(), _waitms(), 
       _waitus()) has been improved significantly. Also, the calculations of how
       long each unit of time is in clock ticks are now only done on the first
       call and on any call where a change in clock frequency is detected, not  
       on every call as in previous releases. This speeds up the calls, but it
       means that the very first call in such cases will be very slightly longer
       than subsequent calls - if this is a problem, add a call like:
          _waitus(0);
       at the start of the program to calculate the waits for the current
       clock frequency, and all subsequent waits will be more accurate. Note 
       that any call calculates the times for all the _wait functions, not just 
       the one actually called. Also note that for _waitus() there is a minimum 
       number of milliseconds that can be waited for accurately - especially on
       the Propeller 1. The actual value depends on the memory model used, but
       for Propeller 2 NATIVE it is less than 10us. If shorter accurate wait 
       times are required, consider using an inline PASM assembly language 
       function instead of the C _wait functions.
    
       On the Propeller 1, these functions use the WAITCNT instruction, and are
       "thread-safe", but on the Propeller 2 they use timer 2 and the WAITCT2
       instruction. This means they are not "thread-safe" since waiting on a 
       timer also stalls interrupts. However, there are also "interrupt-safe" 
       versions provided for the Propeller 2, called _iwaitsec(), _iwaitms() 
       and _iwaitus(). These use the POLLCT2 instruction and so do not stall 
       interrupts, but they busy-wait. This means they consume more power, and
       they will also be very slightly less accurate, but accuracy is never 
       guaranteed in a multi-threaded program, or one that uses interrupts.
    

    Ross.

  • @RossH said:

    @enorton said:
    Just need the updates from you, fix a few issues and port the rest of the code over.

    I have uploaded a beta pre-release of 5.9 here

    Note that this is a Windows only release, intended to give you a preview of the new functionality, and it has not been extensively tested. Install it to a different directory than Catalina 5.8 (by default it will install to "Catalina_5.9 PRE_RELEASE BETA 1") in case you need to revert (note that to swap between installed versions you must change the LCCDIR environment variable - simply selecting other installed versions on the Start Menu is not sufficient).

    Here is the relevant extract from the README.TXT:

    New Functionality
    -----------------
    
    1. The multiport serial libraries (serial2, serial4, serial8) have each had two
       new functions added:
    
          int sX_txcount(unsigned port)
          int sX_rxcount(unsigned port)
    
       where X = 2, 4 or 8. The functions return the current count of characters 
       waiting in the transmit or receive buffers.
    
    Other Changes
    -------------
    
    1. The Catalina Propeller 2 Reference Manual said the 8 port serial functions 
       s8_openport() and s8_closeport() returned an int. In fact, they both return 
       a void - i.e. they don't return anything. The manual has been updated.
    
    2. The Catalina maths functions round() and trunc() were incorrectly defined 
       in catalina_float.h as returning floats - in fact, they both returned long 
       values. These functions have been retained, but have been renamed as
       _round() and _trunc(). 
    
       Also, these functions were only available when a maths co-processor was 
       loaded - e.g. if -lma, -lmb or -lmc (P2 only). New software implementation
       called lround() and ltrunc() which return longs have been defined so that
       they are available when any maths library option is used - i.e. including 
       the -lm option.
    
       Also, new functions have been added called fround() and ftrunc() which DO 
       return floats. So now a program that needs round() or trunc() can define 
       
       EITHER:
    
          #define round(x) lround(x)
          #define trunc(x) ltrunc(x)
    
       OR:
    
          #define round(x) fround(x)
          #define trunc(x) ftrunc(x)
    
       OR:
    
          #define round(x) _round(x)
          #define trunc(x) _trunc(x)
    
       depending on whether it needs versions that returns longs, versions that
       return floats (or doubles - in Catalina doubles are the same as floats, 
       so if the program expects these functions to return doubles, it will work 
       perfectly well with the new versions) or it wants to retain the original
       versions.
    
    
    3. Catalina's original implementation of trunc(), which was also used when 
       a float was "cast" to an integer, was based on Cam Thompson's original 
       Float32_A co-processor, which unnecessarily limited the range in which 
       it would return a value. That range has been extended to include the 
       maximum range that can be encoded accurately in a float. Note that 
       this does not mean all longs in this range can be encoded - the way 
       IEEE457 floats are implemented means there are "gaps" in the integers
       that can be encoded as floats once you exceed the number of bits in the 
       mantissa. However, within the newly extended range (which corresponds to
       the integer range of -2,147,483,583 to 2,147,483,583) the implementation 
       of both ltrunc() and lround() will now return the correct integer value. 
       Outside that range they will return zero.
    
    4. The documentation regarding the 8 Port Serial plugin was a little 
       contradictory regarding the meaning and use of S8_MAX_PORTS. Setting this 
       determines the maximum number of ports that CAN BE OPENED, not the number 
       of ports that will be opened automatically. A port will only be opened 
       automatically if the port number is less than S8_MAX_PORTS, AND it has 
       pins specified in the range 0 .. 63 in the platform configuration file. 
       If the port has pins specified outside this range (e.g. as -1) then the 
       port will not be opened automatically but can be opened manually using
       the s8_openport() function. A port number greater or equal to S8_MAX_PORTS 
       will never be opened. If you want different baud rates or large buffer 
       sizes, it is recommended to open the ports manually.
    
    5. The accuracy of the various _wait functions (i.e. _waitsec(), _waitms(), 
       _waitus()) has been improved significantly. Also, the calculations of how
       long each unit of time is in clock ticks are now only done on the first
       call and on any call where a change in clock frequency is detected, not  
       on every call as in previous releases. This speeds up the calls, but it
       means that the very first call in such cases will be very slightly longer
       than subsequent calls - if this is a problem, add a call like:
          _waitus(0);
       at the start of the program to calculate the waits for the current
       clock frequency, and all subsequent waits will be more accurate. Note 
       that any call calculates the times for all the _wait functions, not just 
       the one actually called. Also note that for _waitus() there is a minimum 
       number of milliseconds that can be waited for accurately - especially on
       the Propeller 1. The actual value depends on the memory model used, but
       for Propeller 2 NATIVE it is less than 10us. If shorter accurate wait 
       times are required, consider using an inline PASM assembly language 
       function instead of the C _wait functions.
    
       On the Propeller 1, these functions use the WAITCNT instruction, and are
       "thread-safe", but on the Propeller 2 they use timer 2 and the WAITCT2
       instruction. This means they are not "thread-safe" since waiting on a 
       timer also stalls interrupts. However, there are also "interrupt-safe" 
       versions provided for the Propeller 2, called _iwaitsec(), _iwaitms() 
       and _iwaitus(). These use the POLLCT2 instruction and so do not stall 
       interrupts, but they busy-wait. This means they consume more power, and
       they will also be very slightly less accurate, but accuracy is never 
       guaranteed in a multi-threaded program, or one that uses interrupts.
    

    Ross.

    Thank you Ross. I will give it a whirl soon after I get this annoying small issue figure out :smiley:

  • @RossH said:

    @enorton said:
    Just need the updates from you, fix a few issues and port the rest of the code over.

    I have uploaded a beta pre-release of 5.9 here

    Note that this is a Windows only release, intended to give you a preview of the new functionality, and it has not been extensively tested. Install it to a different directory than Catalina 5.8 (by default it will install to "Catalina_5.9 PRE_RELEASE BETA 1") in case you need to revert (note that to swap between installed versions you must change the LCCDIR environment variable - simply selecting other installed versions on the Start Menu is not sufficient).

    Here is the relevant extract from the README.TXT:

    New Functionality
    -----------------
    
    1. The multiport serial libraries (serial2, serial4, serial8) have each had two
       new functions added:
    
          int sX_txcount(unsigned port)
          int sX_rxcount(unsigned port)
    
       where X = 2, 4 or 8. The functions return the current count of characters 
       waiting in the transmit or receive buffers.
    
    Other Changes
    -------------
    
    1. The Catalina Propeller 2 Reference Manual said the 8 port serial functions 
       s8_openport() and s8_closeport() returned an int. In fact, they both return 
       a void - i.e. they don't return anything. The manual has been updated.
    
    2. The Catalina maths functions round() and trunc() were incorrectly defined 
       in catalina_float.h as returning floats - in fact, they both returned long 
       values. These functions have been retained, but have been renamed as
       _round() and _trunc(). 
    
       Also, these functions were only available when a maths co-processor was 
       loaded - e.g. if -lma, -lmb or -lmc (P2 only). New software implementation
       called lround() and ltrunc() which return longs have been defined so that
       they are available when any maths library option is used - i.e. including 
       the -lm option.
    
       Also, new functions have been added called fround() and ftrunc() which DO 
       return floats. So now a program that needs round() or trunc() can define 
       
       EITHER:
    
          #define round(x) lround(x)
          #define trunc(x) ltrunc(x)
    
       OR:
    
          #define round(x) fround(x)
          #define trunc(x) ftrunc(x)
    
       OR:
    
          #define round(x) _round(x)
          #define trunc(x) _trunc(x)
    
       depending on whether it needs versions that returns longs, versions that
       return floats (or doubles - in Catalina doubles are the same as floats, 
       so if the program expects these functions to return doubles, it will work 
       perfectly well with the new versions) or it wants to retain the original
       versions.
    
    
    3. Catalina's original implementation of trunc(), which was also used when 
       a float was "cast" to an integer, was based on Cam Thompson's original 
       Float32_A co-processor, which unnecessarily limited the range in which 
       it would return a value. That range has been extended to include the 
       maximum range that can be encoded accurately in a float. Note that 
       this does not mean all longs in this range can be encoded - the way 
       IEEE457 floats are implemented means there are "gaps" in the integers
       that can be encoded as floats once you exceed the number of bits in the 
       mantissa. However, within the newly extended range (which corresponds to
       the integer range of -2,147,483,583 to 2,147,483,583) the implementation 
       of both ltrunc() and lround() will now return the correct integer value. 
       Outside that range they will return zero.
    
    4. The documentation regarding the 8 Port Serial plugin was a little 
       contradictory regarding the meaning and use of S8_MAX_PORTS. Setting this 
       determines the maximum number of ports that CAN BE OPENED, not the number 
       of ports that will be opened automatically. A port will only be opened 
       automatically if the port number is less than S8_MAX_PORTS, AND it has 
       pins specified in the range 0 .. 63 in the platform configuration file. 
       If the port has pins specified outside this range (e.g. as -1) then the 
       port will not be opened automatically but can be opened manually using
       the s8_openport() function. A port number greater or equal to S8_MAX_PORTS 
       will never be opened. If you want different baud rates or large buffer 
       sizes, it is recommended to open the ports manually.
    
    5. The accuracy of the various _wait functions (i.e. _waitsec(), _waitms(), 
       _waitus()) has been improved significantly. Also, the calculations of how
       long each unit of time is in clock ticks are now only done on the first
       call and on any call where a change in clock frequency is detected, not  
       on every call as in previous releases. This speeds up the calls, but it
       means that the very first call in such cases will be very slightly longer
       than subsequent calls - if this is a problem, add a call like:
          _waitus(0);
       at the start of the program to calculate the waits for the current
       clock frequency, and all subsequent waits will be more accurate. Note 
       that any call calculates the times for all the _wait functions, not just 
       the one actually called. Also note that for _waitus() there is a minimum 
       number of milliseconds that can be waited for accurately - especially on
       the Propeller 1. The actual value depends on the memory model used, but
       for Propeller 2 NATIVE it is less than 10us. If shorter accurate wait 
       times are required, consider using an inline PASM assembly language 
       function instead of the C _wait functions.
    
       On the Propeller 1, these functions use the WAITCNT instruction, and are
       "thread-safe", but on the Propeller 2 they use timer 2 and the WAITCT2
       instruction. This means they are not "thread-safe" since waiting on a 
       timer also stalls interrupts. However, there are also "interrupt-safe" 
       versions provided for the Propeller 2, called _iwaitsec(), _iwaitms() 
       and _iwaitus(). These use the POLLCT2 instruction and so do not stall 
       interrupts, but they busy-wait. This means they consume more power, and
       they will also be very slightly less accurate, but accuracy is never 
       guaranteed in a multi-threaded program, or one that uses interrupts.
    

    Ross.

    Hi Ross,

    I have installed the Catalina 5.9 beta release and I have uploaded the code to the chip. It connects as it normally does which is good but noticed the buffer used is always showing 1. Not sure why. I will check into this more and see what is going on. I checked in the P2_CUSTOM file and cannot for the life of me see where I can set the UART buffer sizes. I am using the 8-port serial port driver with 4 active UART ports and the rest disabled.

    I also tried the lround() and ltrunc() functions and those seem to work which is good. It's getting late and have yet to try the _wait (delay) stuff. I will dive into all of this in the morning. Thanks for all your help with this. I know you are pretty busy so I will try to do as much testing as I can and hopefully not bother you too much :smile:

  • RossHRossH Posts: 5,477
    edited 2023-04-24 06:28

    @enorton said:

    I have installed the Catalina 5.9 beta release and I have uploaded the code to the chip. It connects as it normally does which is good but noticed the buffer used is always showing 1. Not sure why.

    Are you sure that is wrong? I wrote a test program that fills the send buffer with a long strings until it cannot fit any more in and then checks the s8_txcount and it does indeed show more than one 1 character waiting to be sent.

    Similarly, the program checks for received characters only after it has done the above processing and it does indeed see the s8_rxcount go higher than 1 if more characters have been received in the meantime. But if your program is sending characters one by one and is processing received characters as they arrive, then perhaps these counts never do get higher than 1.

    The test program is in demos\serial8\test_serial8_count.c

    I will check into this more and see what is going on. I checked in the P2_CUSTOM file and cannot for the life of me see where I can set the UART buffer sizes. I am using the 8-port serial port driver with 4 active UART ports and the rest disabled.

    See the test program for this as well - the buffer sizes are not set in the platform file. By default, the buffer size for all the ports that are auto-opened using the 8 port serial library is 64 bytes for both transmit and receive buffers. This is hard coded in the serial8 library - see source\lib\catalina_serial8\s8_core.c - and changing this default requires recompiling the library. But you don't need to do that - the test program also shows how to close a port (if it has been auto-opened) and then re-open it to use any buffer size you want (the test program uses 512 byte buffers).

    I also tried the lround() and ltrunc() functions and those seem to work which is good. It's getting late and have yet to try the _wait (delay) stuff. I will dive into all of this in the morning. Thanks for all your help with this. I know you are pretty busy so I will try to do as much testing as I can and hopefully not bother you too much :smile:

    No worries. When you get time. There is a test program for the wait functions in demos\examples\ex_wait.c

    Ross.

  • @RossH said:

    @enorton said:

    I have installed the Catalina 5.9 beta release and I have uploaded the code to the chip. It connects as it normally does which is good but noticed the buffer used is always showing 1. Not sure why.

    Are you sure that is wrong? I wrote a test program that fills the send buffer with a long strings until it cannot fit any more in and then checks the s8_txcount and it does indeed show more than one 1 character waiting to be sent.

    Similarly, the program checks for received characters only after it has done the above processing and it does indeed see the s8_rxcount go higher than 1 if more characters have been received in the meantime. But if your program is sending characters one by one and is processing received characters as they arrive, then perhaps these counts never do get higher than 1.

    The test program is in demos\serial8\test_serial8_count.c

    I will check into this more and see what is going on. I checked in the P2_CUSTOM file and cannot for the life of me see where I can set the UART buffer sizes. I am using the 8-port serial port driver with 4 active UART ports and the rest disabled.

    See the test program for this as well - the buffer sizes are not set in the platform file. By default, the buffer size for all the ports that are auto-opened using the 8 port serial library is 64 bytes for both transmit and receive buffers. This is hard coded in the serial8 library - see source\lib\catalina_serial8\s8_core.c - and changing this default requires recompiling the library. But you don't need to do that - the test program also shows how to close a port (if it has been auto-opened) and then re-open it to use any buffer size you want (the test program uses 512 byte buffers).

    I also tried the lround() and ltrunc() functions and those seem to work which is good. It's getting late and have yet to try the _wait (delay) stuff. I will dive into all of this in the morning. Thanks for all your help with this. I know you are pretty busy so I will try to do as much testing as I can and hopefully not bother you too much :smile:

    No worries. When you get time. There is a test program for the wait functions in demos\examples\ex_wait.c

    Ross.

    I just tested one of my products and the buffer function shows the maximum buffer size and when the buffer is used it subtracts from it. The way you have your buffers set up is I assume increments by how many are sent or available and the same for receive buffer. Is this correct? I had to build some boards today and have not been able to devote time to development today. I may get back on the project tomorrow afternoon and will take a closer look at how all of this works and report back to you.

  • RossHRossH Posts: 5,477

    @enorton said:

    I just tested one of my products and the buffer function shows the maximum buffer size and when the buffer is used it subtracts from it. The way you have your buffers set up is I assume increments by how many are sent or available and the same for receive buffer. Is this correct? I had to build some boards today and have not been able to devote time to development today. I may get back on the project tomorrow afternoon and will take a closer look at how all of this works and report back to you.

    What you describe is true of the s8_txcheck() function for the tx buffer (but note that s8_rxcheck() does not implement the corresponding function for the rx buffer - it does not return a count, it returns either -1 or the next character in the rx buffer).

    The new functions are s8_txcount() and s8_rxcount(), which return the number of characters in the respective buffer i.e. from 0 to the buffer length - 1.

    For example, the ex_wait.c test program fills the tx buffer, then fetches the value of s8_txcount() after a wait of a few milliseconds, so you will see this value change if you change the value of the _waitms() function call (which is 5 by default) which allows more or less time for characters to be sent. For example, here are the values I see for various wait times:

    _waitms(0)  : 410
    _waitms(5)  : 297
    _waitms(10) : 184
    _waitms(15) : 71
    _waitms(20) : 0
    

    To see the value of s8_rxcount() change, simply press a few keys faster than the test program prints them, and the rx count will keep rising, then fall again when you stop pressing keys. It should never go higher than the buffer size even if you just hold a key down forever, but I just noticed while writing this answer that it wraps back to zero, which is a bug!

    I will fix that and post a new version soon.

    Ross.

Sign In or Register to comment.