pfth - An ANS Forth Interpreter for the Prop

15678911»

Comments

  • twm47099twm47099 Posts: 858
    edited 2014-03-15 - 22:06:45
    Dave,
    I have a new question about some strange behavior I am seeing when I use the cut and paste method of loading new definitions into pfth.
    Loading some colon definitions result in a value being added to the stack. In other words starting with an empty stack, cutting and pasting some colon words (everything is between : and ; ) results in a value being printed if i type '.s'

    It seems that loading any definition that transmits a character using fds_tx leaves a value on the stack. The value seems to be an address. Loading definitions that only use fds_rx does not leave anything on the stack. Some examples below that I use to receive or transmit a string through Bluetooth.

    First the code that does leave a value on the stack. I've included some preliminary code that makes the string that will be transmitted. After I loaded the preliminary code, I checked the stack and it was empty. Then I loaded the colon definition str>bt and there was a value (d22356) on the stack.
    \ ###########################################################
    \ #  str>bt -- Send string to BT terminal
    \ #   Uses fds.fth
    \ #   To use enter name of string variable (svn) then str>bt
    \ #     element [0] must contain string length
    \ #
    \ #  Example below uses the string 'greeting' 
    \ #    Type: greeting str>bt <ret>
    \ #  The words between these comments and str>bt are not needed
    \ #  if the string variable is defined elsewhere.
    \ #############################################################
    
    
    : place over over >r >r char+ swap chars cmove r> r> c! ; \ st const to st variable
    : length$ count swap drop ; 
    
    
    create greeting 64 chars allot                         \ define string greeting
      s" Hello! This is bluetooth. " greeting place        \ make string variable
      greeting length$ greeting C!                         \ get string length & store
    
    
    : str>bt  ( string name with length in zero element -- )  
       9 8 0 115200 fds_start  ( rxpin txpin mode 115200 -- cognum )
       drop               ( str variable name )
       2dup c@            ( svn svn cnt )
       1+ 1 do            ( svn svn )
        i + c@  fds_tx    ( svn )
        dup               ( svn svn )
       next
       2drop 
       13 fds_tx          ( send CR )
       100 ms             ( wait before stopping )
       fds_stop
       500 ms             ( seems to minimize extra chars )
       1 30 lshift dira!  ( turns terminal back on )
    ;
    
    

    The follwing code gets a string from the bluetooth device. When I load that the stack is empty as expected. Right now all I do is add an imediate "drop" after the closing semi colon when I load definitions that leave a value on the stack. Any ideas whats happening?
    Thanks
    Tom
    \ ####################################################
    \ #   bt>str - get string from bluetooth
    \ #     Uses fds.fth
    \ #     String is returned in bt$, length is in bt$[0]
    \ #     Max length = 80 chars, ends input or  
    \ #     Carriage return ends input
    \ ####################################################
    
    create bt$ 81 chars allot
    
    : bt>str   \ get a string from bluetooth, carrige return ends string input
              \ 
       9 8 0 115200 fds_start  ( rxpin txpin mode 115200 )
       drop 0 bt$ c!
       begin
        fds_rx                        \ have ascii value 
        dup 13 <> bt$ c@ 80 < and     \ end on CR or on 80 char line
        while     ( ascii )
         bt$ c@ 1 + bt$ c! bt$ bt$ c@ + c!
       repeat
       drop
       200 ms
       fds_stop
       300 ms
       1 30 lshift dira!  ( turns terminal back on )
    ;
    
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-03-16 - 06:16:09
    You're using do...next instead of do...loop. It should be do...loop or for...next. Maybe someday I'll add some error checking to the interpreter to check for matched words, but most Forth interpreters don't check for that, at least not on the Prop.
  • twm47099twm47099 Posts: 858
    edited 2014-03-16 - 07:44:57
    Dave Hein wrote: »
    You're using do...next instead of do...loop. It should be do...loop or for...next. Maybe someday I'll add some error checking to the interpreter to check for matched words, but most Forth interpreters don't check for that, at least not on the Prop.

    Thanks, that was a pretty stupid mistake that I've copied to a number of words. Don't know how I missed it, (and kept missing it), but maybe it has something to do with my habit of coding at 3 AM. For some reason the code works, but that's probably why I was also getting an extra character printed to the regular terminal when I switched back from Bluetooth.

    Tom
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-03-16 - 09:51:02
    I derived the definitions for the for/next words from the do/loop words so they are very similar. do/loop is actually wrapped in an if..then pair so I could also pair the ?do word with loop. The if word compiles to jmp0, and the then word write the address after it to the word after _jmp0. The address just after the _jmp0 word is put on the stack during compilation, and the loop takes it off the stack. The next word doesn't use this address, so it was being left on the stack. I hope that isn't too much information, but I believe that's what was happening.
  • twm47099twm47099 Posts: 858
    edited 2014-03-16 - 12:36:44
    Dave Hein wrote: »
    I derived the definitions for the for/next words from the do/loop words so they are very similar. do/loop is actually wrapped in an if..then pair so I could also pair the ?do word with loop. The if word compiles to jmp0, and the then word write the address after it to the word after _jmp0. The address just after the _jmp0 word is put on the stack during compilation, and the loop takes it off the stack. The next word doesn't use this address, so it was being left on the stack. I hope that isn't too much information, but I believe that's what was happening.

    That does make sense. I also found that I had to change the starting index when I switched from 'do next' to 'do loop'. I appreciate the info you give re how you developed pfth. I'm trying to understand how the Prop works and learn a bit of PASM.

    Today's dumb question: What is happening at the Propeller level when I start a new cog in pfth? Some observations - I use the method you showed in an earlier post for each cog I open. For example the code below has a word 'gobtrgbcog' that starts 2 words, each in a different cog. (gocog1 starts a word 'rbgledtest' that controls an RGB Led in its own cog, and gocogbluergb starts a word 'bluergb' in its own cog) . I run rbgledtest in a separate cog so that I can enter keyboard commands that store values in R B G variables that are read by rbgledtest and set the intensities of the different colors. Running bluergb in its own cog allows the same thing except from a bluetooth terminal. I can actually enter the color values from either/both the Teraterm terminal or the BT terminal while the code is running in the 2 other cogs. I do make sure that the BT cog is stopped after I reset the terminal (fds_stop 1 30 lshift dira! ) but before the ending ; or else I lose control and have to reset.

    This is powerful, but confusing. Each of the words running in separate cogs are using words, constants, and variables that are defined before the new cog is started. The stacks for each cog are also defined outside of the cogs they are supporting.
    What is in each cog? What is running in each cog? What limits the size of the code that I can start in a separate cog?

    Thanks again,
    Tom
    create cgstk_btgb  80 allot  
    create cgrtn_btgb 80 allot 
    
    create cgcfg_btgb 
      ' bluergb >body , 
      cgstk_btgb , 
      cgstk_btgb , 
      cgrtn_btgb , 
      cgrtn_btgb , 
    
    \ # words for starting  
    
    : gocogbluergb forth @ cgcfg_btgb cognew dup cgnum_btgb !         ( red, blue, green Leds )
      10 ms ." 'bluergb' started in cog number " . ." , cogid stored in cgnum_btgb" cr ;
    
    : gobtrgbcog  0 cgnum_btgb ! go1cog gocogbluergb ;                    \ start bluetooth rbg
    
    
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-03-16 - 20:26:33
    twm47099 wrote: »
    What is in each cog? What is running in each cog?
    Each cog that is started is loaded with the Forth kernel. For a Forth kernel to run it needs to know the addresses of the data and return stack, and also the address of a list of words to execute. This is specified in the cog configuration structure. The first Forth cog is started up by a short Spin program. The initial cog configuration structure is define in a Spin DAT section as follows:
    dat
    pfthconfig              long      @xboot_1+Q            ' Initial word to execute
                            long      @stack+Q+16           ' Starting stack pointer
                            long      @stack+Q+16           ' Empty stack pointer value
                            long      @retstk+Q             ' Starting return pointer
                            long      @retstk+Q             ' Empty return pointer value
    
    This runs XBOOT, which is a simple Forth interpreter and compiler. This is used to build the ANS Forth dictionary, and start up the full interpreter and compiler. The starting stack pointer is set up 16 bytes (4 longs) past the actual beginning of the stack, which allows for recovery from underflows when running the interpreter. When an underflow occurs the interpreter will reset the stack to the "Empty stack pointer value". The two values in the structure specify the starting and empty values of the return stack. Normally, these are the same, but there are cases where they could be different.

    Additional cogs use a configuration structure defined in the Forth dictionary, such as your example.
    create cgcfg_btgb 
      ' bluergb >body , 
      cgstk_btgb , 
      cgstk_btgb , 
      cgrtn_btgb , 
      cgrtn_btgb , 
    
    The expression " ' bluergb >body " is used to get the address of list of execution tokens for bluergb. Possibly in a future version of pfth this will be simplified to just " ' bluergb ". The starting and empty values for the stacks are just set to the address given by cgstk_btgb and cgrtn_btgb. This is fine since there should be no chance of underflowing the stacks.

    The expression " forth @ cgcfg_btgb cognew " puts the addresses of the Forth kernel and the configuration structure on the stack, and then starts the cog with cognew.

    Note that the data stack could be preloaded with calling parameters prior to starting up a cog, which would require that the starting value be greater than the empty value.
    twm47099 wrote: »
    What limits the size of the code that I can start in a separate cog?
    Since the word that is executed is located in hub RAM, the size is limited by the size of hub RAM. A separate cog can run a very small program, or it can run a large program that uses most of the 32K of RAM. All of the cogs can execute from the same dictionary words because these are only read from memory, and not modified.

    Any variables that are shared and modified by one or more cogs require some form of coordination among the cogs. The coordination could be as simple as allowing only one code to write to a variable, or could use locks to allow more than one cog to modify a variable.
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2014-03-17 - 07:18:57
    The real beauty of having 8 cogs is that you not only can run separate tasks simultaneously, but you that the tasks can stop running and free the cog for something completely different. Unlike preemptive multitasking that bumps one task to do another; you don't preempt. As long as your programming scheme doesn't use up all the available cogs, you march through a lot of multitasking without waiting for a turn.

    It is rather naive to use all 8 cogs in infinite loops when the real power is to use as few as possible in infinite loops and leave several for limited duration tasks. pfth does this very nicely. Of course, you need at least one cog for the user interface that is an infinite loop; but the other 7 behave very well in sharing the Forth dictionary.
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2014-03-19 - 08:48:49
    The real beauty of having 8 cogs is that you not only can run separate tasks simultaneously, but you that the tasks can stop running and free the cog for something completely different. Unlike preemptive multitasking that bumps one task to do another; you don't preempt. As long as your programming scheme doesn't use up all the available cogs, you march through a lot of multitasking without waiting for a turn.

    It is rather naive to use all 8 cogs in infinite loops when the real power is to use as few as possible in infinite loops and leave several for limited duration tasks. pfth does this very nicely. Of course, you need at least one cog for the user interface that is an infinite loop; but the other 7 behave very well in sharing the Forth dictionary.

    I didn't mention the code necessary to run limited duration tasks on an independent cog.

    It is quite simple, obviously you need to call COGSTOP. But you first have to call COGID from within the task so that COGSTOP will correctly know which cog to stop. Once you get the hang of this, pfth Forth becomes really powerful at getting high speed use of the Propeller.
  • twm47099twm47099 Posts: 858
    edited 2014-04-19 - 22:43:15
    Dave,
    I have a problem with a pfth program I wrote that takes data from an android app that acts as a joystick and also has 4 buttons. It sends joystick values and button values via bluetooth. There was some discussion of it here:

    http://forums.parallax.com/showthread.php/154687-EZ-Bluetooth-in-***C***-and-a-nice-App

    The app and some info is here:
    https://play.google.com/store/apps/details?id=org.projectproto.btjoystick

    The author had published an arduino sketch which gave the data format that was sent by the BT device.
    The format for buttons is 3 bytes: STX (2), 1-byte data, ETX (3). The data byte is a value from 49 to 56. Turning button 1 on = 49, Turning button 1 off = 50. Button 2 on = 51, Off = 52, continue for buttons 3 and 4.

    The format for Joystick is 6 bytes: STX, high byte x, low byte x, high byte y, low byte y, ETX.
    The Joystick values -100 to +100 for x and y are encoded so that a premature 3 won't occur.
    After receiving the value from BT, the x and y is decoded: x = (7 lshift(xHi ) + xLo) - 200 {same for y}

    I've been able to write a Simple IDE C program that reads the joystick and controls the colors of an RBG Led and controls a standard servo. It reads the button values and toggles the LEDs on the quickstart board.

    I've also written pfth words that uses fds.fth to receive the Joystick value from the BT app and also controls a servo or the RBG led and receives the button values and sets the QS leds.

    I run the bluetooth word and the RBG Led word (or servo word) in separate cogs.

    When running the RBG Led, the BT word puts x & y on the stack and calls a color calculating words. Those words store the r g b results into variables ( rval, bval, and gval). The RBG Led refreshing code (in its own cog) fetches rval, bval, and gval loads them into the counter frqa. I do something similar for the servo word. Both work well.

    But when I try to do the same with the button codes things don't work. I am able to take the button byte on the top of the stack and use this pfth routine in the BT word:
    33 - 1 swap lshift outasetbit
    
    to turn on the QS Led.

    But if I try to put it in a variable and use it in another cog, the value is not stable. Sometimes the value is the button value (49-56), other times it is a weird combination of digits or even just a blank. If I stop the program (pressing button 4 on the BT device ends the pfth program) and read the variable, it gives the correct value. I have also used an 80 byte string read routine to collect all the bytes sent by multiple button presses, and when I read the values out I see that the 3 byte reads (STX, val, ETX) are correct with no screwy values.

    I have tried using a '63 and' mask, but still can get 4 digit values. I suspect that there may be interference between the 2 cogs reading and writing the variable. I have tried to use flags to stop conflicts, and there has been a little improvement but not enough to be able to use the variable in another cog.

    I'd like to try to use locks. I see the "locknew" and "lockret" words in the propwords.fth file, but I don't see "lockset" or "lockclear". How would I use locks.

    I've included the BT word and the word that I use to try to print out the button value (.butv) below. This is my latest version with flags. I've tried adding time delays also. I'd appreciate any ideas.

    Thanks
    Tom
    decimal
    
    : carray                       \ declare: #chars carray variable_name  
    CREATE chars ALLOT             \ to store or fetch from an element: ele#   variable_name  ! or @
    DOES> ( i -- 'char) SWAP chars + ;
    
    decimal 
    variable cgnum_bjoy2
    variable butval
    variable bflg
    variable bflg2
    8 carray bt$
    
    : bluejoy2     \ works to get button or joystick code, decode it and use it 
                       \ JS controls rbgled, buttons control QS Leds
                       \ and repeats until quit button (B4) in app is pressed
        0 bflg ! 1 bflg2 !
        16711680 outaclrbit        \ sets QS Led pins to 0 
        9 8 0 115200 fds_start    ( rxpin txpin mode 115200 )
        drop 10 ms 
       begin
         begin
           fds_rx 2 = until         \ stall until STX (2) is received
         fds_rx 10 ms 2 bt$ c!
         2 bt$ c@ 55 <            \ if byte 2 is 55 or 56 quit button (button 4) was pressed
       while
         2 bt$ c@ 3 <          \ if byte 2 is < 3 that means x MSB Joystick
         if                      \ this gets next 3 bytes from joystick
           fds_rx 3 bt$ c!       \ byte 3  x lo
           fds_rx 4 bt$ c!       \ byte 4  y hi
           fds_rx 5 bt$ c!       \ byte 5  y lo
           fds_rx 6 bt$ c!       \ byte 6 should be 3 (ETX)
            
           2 bt$ c@ 7 lshift 3 bt$ c@ + 200 -           \ decode x from bytes 2 and 3
           4 bt$ c@ 7 lshift 5 bt$ c@ + 200 -           \ decode y from bytes 4 and 5
    
                                \ ****** Call word to use x and y 
           rgbbox                        \ converts x & y to R B G hundreths increments
                                \ ****** End of word to use x and y
         else                  \ byte 2 >= 3 means byte from button press & only that byte is data
           ( button code goes here,  )
    
           16711680 dirasetbit  \ set QS Leds to output
           16711680 outaclrbit  \ turn off all
           2 bt$ c@     ( bt$2 )
           dup                        \ only need for btval ! below can delete both
           33 - 1 swap lshift outasetbit    \ turn on led corresponding to button code
           0 bflg ! 5 ms
           begin bflg2 @ until 5 ms
           0 butval !  butval !       ( this takes a number of reads in other cogs to stabilize? )
           10 ms 
           1 bflg ! 5 ms
    
    \         drop  \ if button value is not used drop it
    
           ( end of button code )       
         then   
       repeat
        500 ms                          \ give it time to send -may not be needed here
        fds_stop
        1 30 lshift dira!               \ turns terminal back on
        cgnum_rbgt @ cogstop            \ stop rgbledtest's cog
        cgnum_bjoy2 @ dup 0 > if 
          cogstop                        \ stop bluejoy2's cog 
        else drop
        then
    ;
    
    create cgstk_bjoy2 80 allot  
    create cgrtn_bjoy2 80 allot
    
    create cgcfg_bjoy2 
      ' bluejoy2 >body , 
      cgstk_bjoy2 , 
      cgstk_bjoy2 , 
      cgrtn_bjoy2 , 
      cgrtn_bjoy2 , 
    
    \ # words for starting  
    
    : gocogbluejoy2 forth @ cgcfg_bjoy2 cognew dup cgnum_bjoy2 !     ( starts the BT cog )
      10 ms ." 'bluejoy2' started in cog number " . ." , cogid stored in cgnum_bjoy2" cr ;
    
    : gobluejoy2  0 cgnum_bjoy2 ! go1cog gocogbluejoy2 ;        \ go1gog starts rbg cog  
    
    : .butv           \ For printing butval - run from keyboard 
       begin bflg @ until 
       5 ms 0 bflg2 !
       5 ms butval @ . cr  
       1 bflg2 ! 5 ms ;  
    
    
  • twm47099twm47099 Posts: 858
    edited 2014-04-20 - 14:24:03
    I tried three other things. Two didn't work, and 1 did work, but it's not an optimum solution.

    1. After the lines:
         else   \ byte 2 >= 3 means byte from button press & only that byte is data        ( button code goes here,  )
    
    I added an if test for the bt$ + 2 greater than 48 and less than 55
    (with 'then' added before the existing then.) When I typed .butv sometimes it gave me a correct value and sometimes not.

    2. I tried adding a 'case' statement
      ( button code goes here,  )
       16711680 dirasetbit  \ set QS Leds to output
       16711680 outaclrbit  \ turn off all
       2 bt$ @ dup case
         49 of 33 - 1 swap lshift outasetbit endof
         50 ...  ( continue of statements for 50, 51, 52, 53, 54 )
      endcase
    

    That didn't work at all, locked out the keyboard, and pressing buttons (including the quit button) did nothing. I had to reset.

    3. I added code to the ".butv" word that stopped fds and the bluejoy2 cog. Then printed "butval", tested a case statement and restarted the bluejoy2 cog. That worked. The new ".butv" word definition is:
    : .butv  begin bflg @ until 5 ms   \ by stopping bluejoy2 cog bflg is printed correctly
        fds_stop
        1 30 lshift dira!               \ turns terminal back on
        cgnum_bjoy2 @ cogstop             
        0 bflg2 ! 5 ms butval @ dup . 1 bflg2 ! 5 ms     \  False flag, print butval, True flag 
       48 - 
       case                                               \ test "case"
          1 of ."   button 1 on " cr endof
          2 of ."   button 1 off " cr endof
          3 of ."   button 2 on " cr endof
          4 of ."   button 2 off " cr endof
          5 of ."   button 3 on " cr endof
          6 of ."   button 3 off " cr endof
       endcase 
       gocogbluejoy2                               \ restart bluejoy2 cog
    ;
    

    I still don't understand why the joystick values can be used in a different cog, but the button values can't be unless the BT cog is stopped??

    Tom
  • twm47099twm47099 Posts: 858
    edited 2015-08-07 - 17:14:52
    Further update: The CASE method in the post above didn't always work. butval would print correctly, but sometimes the program would freeze. I added a "dup . cr" after the 48 - and that would print the correct value (1 - 6), but the case statements would not always print out and the program would freeze.

    I changed the case statements to a series of IF ELSE THEN statements as shown below, and that works.
    : .btv                         \ by stopping bluejoy2 cog bflg is printed correctly
         begin bflg @ until 5 ms   \ wait until other cog finishes storing, then stop other cog
           fds_stop
           1 30 lshift dira!               \ turns terminal back on
           cgnum_bjoy2 @ cogstop           \ completes stopping of other cog  
         0 bflg2 ! 5 ms butval @ dup .     \ False flag, print butval
         1 bflg2 ! 5 ms                    \ Tell other cog ok to STORE 
         48 - dup . cr  
         dup 1 =    
           if 
             ."   button 1 on " cr
           else dup 2 = if ."   button 1 off " cr
           else dup 3 = if ."   button 2 on " cr
           else dup 4 = if ."   button 2 off " cr
           else dup 5 = if ."   button 3 on " cr
           else dup 6 = if ."   button 3 off " cr
           then then then then then then
         drop 10 ms
         gocogbluejoy2                               \ restart bluejoy2 cog
    ;
    
    
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-04-21 - 17:52:28
    I am away from home right now, and I have limited internet access. I hope to have better internet access tomorrow, and I'll look into the problem more at that time.
  • twm47099twm47099 Posts: 858
    edited 2015-08-07 - 17:13:56
    After I had gotten the android Bluetooth Joystick app (in posts 310 - 312 above) working and printing the correct values, I changed it into just a general control program. The app has a joystick (with a number of options) and 4 push buttons. I decided to use Button 4 as an "exit" button, and the other 3 to set the control mode. I have my Quick Start board set up with an RGB Led and a couple of standard servos. Button 1 activates the RGB control program and provides the data it needs. Button 2 activates the servo control and provides it data. I plan to use button 3 for a Continuous Rotation Servo, but for now it sets the RGB control.

    The RGB Led and servo control run in different cogs. I can run the bluetooth word in its own cog or in the main cog. Plus my version of pfth uses the SD card reader which takes an additional cog. I think that when the bluetooth code calls FDS another cog may be started, but I'm not sure. So that's 3 to 5 cogs being used.

    The RGB Led and servo use counters, so my counter words are "include"ed from the SD card. These words are shown in post 284 of this thread.

    The RGBLed code, servo code, and bluetooth code are listed below. The only interaction between the bluetooth code and RGBLed and Servo codes are the 1 word calls (RGBBOX and XY>Servo) in the Joystick part of the BT code, the 2 lines of code to stop the rgbled and servo code, and the words (GO1COG and GOSERVOBT) in the starting words (GOBLUEJOY3 or GOBJ3) so using the BT code to control other devices should be straight forward.

    Tom
    ( rgbcathv100.fth  Uses Common Cathode RBG Led  )
    
    \ #  Method uses 1 ctr and 1 cog and time shares (3ms) each color
    \ #   'go1cog' to start and adjust color intensities (see below) 
    \ #   'stop1cog' to stop
    \ #
    \ #  Can Input values for each color using variables to pass values
    \ #     - After counter is running and terminal shows 'ok',
    \ #       then enter decimal integer value from 1 to 10 and 'rv', 'bv', or 'gv'.
    \ #       These defined words take the value, scale it, and store it in 
    \ #       3 variables rval, bval, or gval.  The running cog fetches the variable and 
    \ #       stores it in frqx.
    \ #
    \ #  Can use more than d 10 steps for each color
    \ #   For 20 steps store BFFF333 in r10th, b10th, and g10th
    \ #   For 100 steps store 26663D7 in r100th, b100th, and g100th
    \ #       and use rvh, bvh, and gvh
    \ #
    \ #   RGBBOX is the word to translate a 2D joystick input (x = -100 to 100, y = -100 to 100)
    \ #        into x, y, z values ( 0 to 100 ) that are input to rvh, bvh, gvh
    \ #
    \ #  (c) T. Montemarano 2014, MIT License
    \ #######################################################################
    
    
    decimal
    
    12 constant redpin         \ anodes on three pins
    13 constant blupin
    14 constant grnpin
    
    variable rval          \ used to transfer intensity values for frqx between cogs
    variable gval
    variable bval
    
    6 26 lshift redpin + constant rctra     \ values for dutycycle-se ctrx for each color
    6 26 lshift blupin + constant bctra    
    6 26 lshift grnpin + constant gctra
    
    1 redpin lshift constant rdira          \ values for dira for each color
    1 blupin lshift constant bdira          
    1 grnpin lshift constant gdira
    
    hex
    
    17ffe666 constant r10th       \ 1/10 of max - min for intensity
    17ffe666 constant g10th       \ can change to balance colors
    17ffe666 constant b10th       \ or to increase number of steps
    
    \ -------- Words for interactive intensity adjustment 
    : rv  r10th * dup rval ! .x ;     \ calcs, stores & prints rval
    : bv  b10th * dup bval ! .x ;
    : gv  g10th * dup gval ! .x ;
    
    26663d7 constant r100th       \ 1/100 of max - min for intensity
    26663d7 constant g100th       \ can change to balance colors
    26663d7 constant b100th       \ or to change number of steps
    
    : rvh  r100th * rval ! ;      \ for 1/100 increments, does not print value
    : bvh  b100th * bval ! ;      \ these words are primarly for BLUEJOY
    : gvh  g100th * gval ! ; 
    
    \ --------
    
    : rbgledtest         ( starts 1 cog and 1 ctr to run red, blue, and green )
       77ff7ffe dup rval !      \ This starts all at mid brightness
       dup bval !
       gval !
       begin             ( start endless loop for timesharing each color on 1 ctr )
    \ ---------------      Turn on red
        gdira diraclrbit         \ set green pin to input
        rval @ frqa!             \ load red intensity setting into frqa
        rctra ctra!              \ set ctra to red pin 
        rdira dirasetbit         \ turn on red  - set red pin to output
        3 ms                     \ keep on for 3ms
    \ ---------------      Turn on blue
        rdira diraclrbit         \ set red pin to input
        bval @ frqa!             \ load blue setting in frqa
        bctra ctra!              \ set ctra to blue pin 
        bdira dirasetbit         \ turn on blue  - set blue pin to output
        3 ms
    \ ---------------      Turn on green
        bdira diraclrbit
        gval @ frqa!
        gctra ctra!
        gdira dirasetbit
        3 ms
       again
    ;
    
    \ #  Set up for starting words in new cogs from Dave Hein
    
    decimal
    
    create cgstk_rbgt  80 allot  
    create cgrtn_rbgt 80 allot 
    variable cgnum_rbgt
    
    create cgcfg_rbgt 
      ' rbgledtest >body , 
      cgstk_rbgt , 
      cgstk_rbgt , 
      cgrtn_rbgt , 
      cgrtn_rbgt , 
    
    \ # words for starting  
    
    : gocogrbgtest forth @ cgcfg_rbgt cognew dup cgnum_rbgt !         ( red, blue, green Leds )
      ." 'rbgledtest' started in cog number " . ." , cogid stored in cgnum_rbgt" cr ;
    
    : go1cog  gocogrbgtest ;                    \ start rbgledtest in new cog
    
    : stop1cog  cgnum_rbgt @ cogstop ;          \ stop rbgledtest & free cog 
    
    : bb 16 lshift dup bval ! .x cr ;       \ these 3 words directly enter values for FRQA to
    : gg 16 lshift dup gval ! .x cr ;       \ check min and max values and determine increment to use
    : rr 16 lshift dup rval ! .x cr ;       \ in R(GB)10th and R(GB)100th 
    
    decimal
    
    : rgbbox     ( x y -- calcs z, scales and calls rvh, bvh, gvh ) 
                      \ x and y on stack range from -100 to +100
       100 + swap 100 +       ( y' x' )
       2 / gvh dup  2 / rvh 2 / 100 swap - bvh 
    ;
    
    
    \ #########################################################################
    \ #  BLUEJOY3
    \ #  Uses Android Bluetooth JS Commander to control devices.
    \ #  This version uses button value to select function.
    \ #     Button 1 - RBG Led color mix
    \ #     Button 2 - Std Servo
    \ #     Button 3 - Reserved (probably CR servos)
    \ #  The buttons also turn on QuickStart Leds 16 - 23
    \ #  The app sends the following:
    \ #     For button press: 3 chars STX(2), Button value(49-56), ETX(3)
    \ #     For Joystick: 
    \ #          6 chars: STX, xMSB, xLSB, yMSB, yLSB, ETX. 
    \ #          The values in app are -100 to +100 for x and y. 
    \ #          When received the values need to be decoded.
    \ #          Decode x and y by left shifting MSB 7 bits, adding to LSB, and subtracting 200.
    \ #          Results in x & y values from -100 (x-left or y-down) to +100 (x-right, y-up).
    \ #  A number can be sent to the app.  The sending format is:  STX, Button, Data, ETX
    \ #     General format is 
    \ #     For this word Button = 0, Data = value + 4.  (All buttons on, data = mode ) 
    \ #     Forth code used is:  2 fds_tx 0 fds_tx funflg @ 4 + fds_tx 3 fds_tx
    \ #
    \ #  RGBCATH.FTH uses a common cathode RBG Led with pins:  Red (12), Blue (13), Green (14)
    \ # 
    \ #  Note CARRAY.  It results in a string array being used as ELE# VARNAME @ or !
    \ #    without requiring explicit adding the element number to the base.  
    \ #    However, the element number is REQUIRED for all elements including the zero element.
    \ #
    \ #     Before Loading this -- 1. INCLUDE FDS.FTH,  
    \ #                            2. INCLUDE RGBCATH.FTH   
    \ #     Then Copy & Paste code from ( servobt ) to end
    \ #                   
    \ #     To Start enter GOBLUEJOY3 starts go1cog, servobt, and bluejoy3.
    \ #        Starts in RBG Led mode.
    \ #     To Stop press Button 4 
    \ #
    \ ##########################################################################
    \ ##########################################
    \ #  SERVOBT 
    \ #  Controls one servo using android Bluetooth JS Commander
    \ #   and BLUEJOY3 
    \ #   Connect BT - set JS - box, 50ms refresh, timeout off
    \ #   Servo follows as JS is swiped horizontally
    
    \ #################################################
    
    ( servobt )
    decimal
    variable serval        \ Servo pulse width in micro-sec 
    variable serflg        \ 1 = continue, 0 = stop cog
    11 constant serpin     \ Servo is using pin 11
    
    : servobt  ( cycles a servo using CTRa controlled by BT JS )
      clrctrcmd 
      ctrncose setctrmode 
      serpin setctrapin 
      1 frqa! ldctra  
      1 serpin lshift dirasetbit 
      -600 microsec serval !
      1 serflg !
        begin
         serflg @ while
         serval @ phsa! 
         20 ms 
        repeat
       xctra  
       cogid 0 > if cogid cogstop then  \ if run in a new cog release the cog
    ;
    
    create cogstack8  80 allot  
    create cogreturn8 80 allot 
    variable cognum8
    
    create cogconfig8 
      ' servobt >body , 
      cogstack8 , 
      cogstack8 , 
      cogreturn8 ,
      cogreturn8 , 
    
    \ This word starts a cog running the SERVOBT word 
      
    : goservobt ( -- )  forth @ cogconfig8 cognew dup cognum8 !  
      ." 'servobt' started in cog number " . ." cogid stored in cognum8" cr ;
    
    : xy>servo  ( x y -- )    \ xy from JS to servo pulse width (spw), 
                              \ only need x, x is -100 to +100
                              \ make x=0 to 200 then want spw = 2120 to 520 us )
       drop 100 + 8 * 2120 swap - -1 * microsec serval ! 
    ;
    
    : xx xy>servo serval @ dup . ."  " 80 / . cr ;  \ for testing
    
    
    ( bluejoy3 )
    
    decimal
    
    : carray                       \ declare: #chars carray variable_name  
    CREATE chars ALLOT             \ to store or fetch from an element: ele# var_name ! or @
    DOES> ( i -- 'char) SWAP chars + ;
    
    decimal 
    variable cgnum_bjoy3
    variable butval
    variable funflg
    8 carray bt$
    
    : bluejoy3     \ works to get button or joystick code, decode it and use it 
                   \ JS controls rbgled and servo, buttons set mode and control QS Leds
                   \ and repeats until quit button (B4) in app is pressed
    
        1 funflg !         \ set mode to RBG Led at start
        16711680 outaclrbit 
        9 8 0 115200 fds_start    ( rxpin txpin mode 115200 )
        drop 10 ms 
        2 fds_tx 0 fds_tx funflg @ 4 + fds_tx 3 fds_tx      \ send mode# 1, 2, or 3 to android
    5 ms
     fds_rx fds_rx drop drop      \ get rid of the android response to the sent bytes on startup
          \ then resend same mode command to android, since no button change android will not echo
    2 fds_tx 0 fds_tx funflg @ 4 + fds_tx 3 fds_tx      \ send mode# 1 to android
       begin
          begin            \ stall until STX (2) is received
            fds_rx 2 = 
          until         
         fds_rx 10 ms 2 bt$ c!
         2 bt$ c@ 56 <>            \ if byte 2 is 56 quit button was pressed
       while
         2 bt$ c@ 3 <          \ if byte 2 is < 3 byte 2 is MSB of Joystick x-value
         if                      \ this gets next 3 bytes from joystick
           fds_rx 3 bt$ c!       \ byte 3  x lo
           fds_rx 4 bt$ c!       \ byte 4  y hi
           fds_rx 5 bt$ c!       \ byte 5  y lo
           fds_rx 6 bt$ c!       \ byte 6 should be 3 (ETX)
            
           2 bt$ c@ 7 lshift 3 bt$ c@ + 200 -      \ decode x from bytes 2 and 3
           4 bt$ c@ 7 lshift 5 bt$ c@ + 200 -      \ decode y from bytes 4 and 5
    
         \ ****** Call words to use JS x and y ( x y onstack ) ******************
    
           funflg @ 1 = if 
             rgbbox then                \ converts x & y to R B G hundreths increments
           funflg @ 2 = if 
             xy>servo then              \ converts x to 0 - 180 deg for servo
           funflg @ 3 = if  
             rgbbox then                \ reserved for button 3, temp use for rgb
     
         \ ****** End of word calls to use JS x and y  ***************************
    
         else           \ byte 2 >= 3 means byte from button press & only that byte is data
    
         \ *** button code starts here, button value is in bt$ element 2, not on stack ****
    
           16711680 dirasetbit  \ set QS Leds to output
           16711680 outaclrbit  \ turn off all
           2 bt$ c@ 
           dup 
           33 - 1 swap lshift outasetbit         \ turn on led corresponding to button code
           0 butval !  butval !  
             butval @ 48 - 2 /mod + funflg !   \ set JS mode ( 1=RBGLed, 2=Std Servo, 3=xxxx )
           10 ms 
           2 fds_tx 0 fds_tx funflg @ 4 + fds_tx 3 fds_tx      \ send mode# 1, 2, or 3 to android
    
         \ *** end of button code ***************************************************
           
         then 
    
       repeat
        0 serflg !         \ ****** This line sets a flag=0 that tells SERVOBT to end
        fds_stop
        1 30 lshift dira!               \ turns terminal back on
        cgnum_rbgt @ cogstop            \ stop rgbledtest's cog
        cgnum_bjoy3 @ dup 0 > if 
         cogstop                        \ stop bluejoy3's cog 
        else drop
        then
    ;
    
    create cgstk_bjoy3 80 allot  
    create cgrtn_bjoy3 80 allot
    
    create cgcfg_bjoy3 
      ' bluejoy3 >body , 
      cgstk_bjoy3 , 
      cgstk_bjoy3 , 
      cgrtn_bjoy3 , 
      cgrtn_bjoy3 , 
    
    \ # words for starting  
    
    : gobj3quiet  ( silent start bluejoy3 cog ) 
       0 cgnum_bjoy3 ! go1cog goservobt forth @ cgcfg_bjoy3 cognew cgnum_bjoy3 ! 10 ms ;
    
    : gocogbluejoy3 forth @ cgcfg_bjoy3 cognew dup cgnum_bjoy3 !     
       10 ms ." 'bluejoy3' started in cog number " . ." , cogid stored in cgnum_bjoy3" cr ;
    
    \ Word below starts rbg, servo, and bluejoy3
    : gobluejoy3  0 cgnum_bjoy3 ! go1cog goservobt gocogbluejoy3 ;   \ start bluejoy3 in new cog
    : gobj3 go1cog goservobt bluejoy3 ;      \ don't start newcog for bluejoy3
    
    
    {delete this paragraph:
    In the code above, there is a word I'm using as garbage collection : cs which clears the stack.
    It needs to be loaded before BLUEJOY3. It wasn't defined in my earlier utility words post. The definition is:
    : cs depth dup 0> if 0 do drop loop then ;
    to here}


    Edit 4/29 -- I deleted the "cs" (clear stack) in the bluejoy3 definition above. I never like to clear the stack in a word. But the android app sends characters in response to code sent to it. When I added the line at the begining that sent the mode # 1 to the android device (to display), the transmission includes the buttons state. When there is a change in button state, the android app sends the button values back to the propeller. That resulted in a variable number of bytes coming into the forth program and an uncertain button state. The result was that the stack could fill with unused values. By adding a duplcate line sending the mode and button state fixes that, and I was able to get rid of the cs word.
  • twm47099twm47099 Posts: 858
    edited 2014-05-23 - 11:31:54
    David,
    I am trying to learn to use LOCKS in pfth. The example I am basing my efforts on is in:
    Programming & Customizing the Multicore Propeller Microcontroller, page 97.

    The following spin code is used to prevent conflicts with a different cog while transferring minutes, seconds, ms from the cog that counts to the main cog that does something with the results.
      repeat until not lockset(semID)       ' ***Fix Wait for lock, set
      longmove(@minutes, @m, 3)             ' Copy timestamp vars
      lockclr(semID)                        ' ***Fix Clear lock
    

    In the pfth propwords.fth file you have defined 'locknew' and 'lockret' but not lockset or lockclr.
    (I think there may be a typo in lockret which is defined as:
      : lockret ( locknum ... ) 0cfc0005 cogx1 drop ;
    
    

    Based on my reading of the PASM opcode lockret I think the hex value should be 0c7c0005, but since I have no experience with PASM, I could be mistaken.

    I would like to get some idea of how lockset and lockclr could be defined.and used.

    Again based on my lack of PASM knowledge I would guess that they could be defined as:
    : lockset ( locknum -- previous set/unset condition ) 0c7c0006 cogx1 ;
    : lockclr ( locknum -- previous set/unset condition ) 0c7c0007 cogx1 ;
    

    A couple of questions:
    1. Am I correct in the hex opcodes?
    2. Am I correct on the return values?

    If not please HELP!

    Thanks, I really appreciate the effort you put into pfth and the help you have given.

    Tom
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-05-23 - 15:37:21
    I think the reason I didn't implement lockset and lockclr is because cogx1 only returns the result, and does not return the values of the carry and zero flags. A new kernel word would be required to implement lockset and lockclr. You're welcome to try implementing it yourself. You would basically need to copy the cogx1 cog code and dictionary entry, and modify it to support a new word that returns the value of the carry flag.

    I'll look into it myself, but I won't be able to work on it until sometime next week.
  • twm47099twm47099 Posts: 858
    edited 2014-05-23 - 18:10:45
    Dave,
    Thanks. I looked at cog1funct in the spin file, and I have a lot to learn before I can even understand what the existing code is doing. I'm still working through the Prop Education Manual and the early chapters of Programming & Customizing the Multicore Propeller Microcontroller. One of the things I do is translate the spin programs into pfth words. I feel that if I can do that I understand what the Spin code is doing. I also find when writing new code that I can write pseudo code and more easily code that in pfth. Once I get the pfth words working I can translate them into Spin or C. (I find going directly from pseudo code into Spin and C is still somewhat difficult.) But I still have trouble following what a PASM routine is doing. But it is a goal.

    Tom
  • twm47099twm47099 Posts: 858
    edited 2014-06-07 - 11:42:19
    Dave,
    One of the things I am working on now is interfacing the Pixy ( http://www.cmucam.org/projects/cmucam5 and https://www.kickstarter.com/projects/254449872/pixy-cmucam5-a-fast-easy-to-use-vision-sensor ) vision sensor to the Propeller. The Pixy uses a hue-based color filtering algorithm to detect and track objects.

    Pixy has a number of possible interface connections including SPI (for direct connect to Arduino), I2C, UART, and DAC. The SPI, I2C, and UART can transmit data (signature number -- 1 to 7), x-position, y-position, object width, and object height) for a number of different objects. The objects are transmitted in a frame in priority of size order. Every 20ms the Pixy starts a new frame of objects and can transmit up to 135 objects per frame. At this point in time, I don't see any way to stop or start the data transmission in software.

    The DAC is much more limited. The Pixy can send out a voltage (0 - 3.3v) linearly proportional to the X or Y position (not both) of the largest object (only) that it has a color signature for. I have gotten that mode working in C (Simple IDE using the ADC on the Activity Board).

    I chose to use pfth for working with the UART since I can interactively collect data and then view / manipulate it as I choose. My experience with the UART (using FDS.fth are discussed here
    http://forums.parallax.com/showthread.php/155501-Pixy-for-Propeller?p=1272545#post1272545
    in posts 15 and 16.

    The format of the data is 7 or 8 words (LSB, MSB):
    1. 0x55, 0xaa - sync word starts a new block of data from an object. Two sync words signify the start of a new frame. A new frame can cancel the transmission of data for a block in progress.
    2. Checksum (sum of values from words 3 - 7.
    3. Color signature number (1 - 7)
    4. x position of center
    5. y position of center
    6. width
    7. height

    The code I am using to collect the values is here:
      create bt$ 241 chars allot
    
       
    
          : bt>str   \ get a string from serial
    
                \ 
         9 8 0 9600 fds_start  ( rxpin txpin mode 115200 )  ( I had to drop baud to 9600 to get complete data sets )
    
         drop 0 bt$ c!
         begin
          fds_rx                        \ have ascii value 
          bt$ c@ 241 <      \ end on 240 char 
    
          while     
    
           bt$ c@ 1 + bt$ c! bt$ bt$ c@ + c!
         repeat
         drop
         200 ms
         fds_stop
         300 ms
         1 30 lshift dira!  ( turns terminal back on )
      ;
    

    After collecting 240 bytes into bt$ that word stops, and I just use another word to print out the value of each element in hex. Then I look for a 4 byte series ( 55, aa, 55, aa) to indicate the start of a new frame, and separate each block to see the quality of the data.

    Summarizing the posts I linked to, at 115,200 baud, I did not collect any whole blocks of data. Reducing the baud to 19200 ( the default of the Pixy, although the wiki states that it can go up to 230,000) I received some good blocks, but not a full frame. At that baud, I should be able to receive approx 3 blocks of data before a new frame starts. Reducing the baud to 9600 resulted in collecting good data. But the slow baud anly allows receiving 1 block per frame. The Pixy priority of sending means that only the block corresponding the the largest object will be transmitted.

    I recall you writing that you were working on a fast forth, and was wondering if if that was getting close to being issued, and if you thought it would be fast enough to capture more than 1 block of data using the code above.

    Some other questions:
    Do you have any suggestions for faster code in pfth in place of what I'm doing?

    Would SPI or I2C be any better (faster) than FDS?

    I generally use forth to help me figure out how to interface to something new. It almost seems like programming in psuedo code since I don't have to concern myself with all of the fine details of the other languages, but now that I understand what the Pixy is sending, I will write some code in Spin and C (Simple IDE) using their serial methods. Do you think they will be (could be) significantly faster?

    Thanks again for your help.
    Tom
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-06-09 - 09:19:00
    Your bt>str executes 18 Forth words in each loop. fds_fx runs in a loop calling fds_rxcheck, so it also executes several words. That's why it can't run faster than 9600 baud. You could speed it up by calling fds_rxcheck instead of fds_rx, or you could even fold the code in fds_rxcheck into bt>str. This should let you run at 19200 or faster. To reach 115200 you probably need a version of FDS that uses a larger receive buffer.

    A tight loop in Spin code that call the RX method will max out at about 57600 baud. I've been able to achieve higher speeds with Spin by either using a larger receive buffer or by writing an RXBLOCK method that transfers blocks of data from the receive buffer using BYTEMOVE. I think C should be able to handle 115200 without any problems.

    I pursued the Fast Forth interpreter for a while, but I stopped working on it after testing in on the chess program, and it ran at about the same speed as pfth. I was able to achieve higher speeds with Fast on small tight loops, where it ran at about the same speed as Tachyon. I also wrote a Forth compiler for Fast that would fold small words into larger words, and improve performance that way. I may post an update on Fast at some point, but I've lost interest in pursuing it any further.
  • twm47099twm47099 Posts: 858
    edited 2014-06-09 - 10:40:39
    Dave,
    Thanks for the response.
    Am I correct in assuming that when the fds receive buffer fills that any subsequent bytes are just lost, as opposed to replacing the bytes in the buffer?

    Also could I expect any increase in speed connecting using an I2C or SPI interface? (The Pixy has both).

    I wrote a short C program (using Simple IDE defaults) using the serial library function that just reads a value into an integer and then prints the value to the Simple IDE terminal. I was able to increase the baud to 19200 and get 3 to 4 blocks of data per frame, but with some occasional errors. At 14400 baud, I got 2 to 3 per frame without any errors.

    Tom
  • Dave HeinDave Hein Posts: 6,049
    edited 2014-06-09 - 12:43:58
    fds.fth used the PASM code from FullDuplexSerial.spin. That object uses 16-byte circular buffers for both transmit and receive. The Forth code is just a translation of the Spin code in FullDuplexSerial.spin. On receive, the buffer will wrap around if data is received faster than it is read. This causes the 16 bytes in the buffer to be lost because the buffer looks like it is empty instead of containing 16 bytes.

    PropGCC uses a simple serial driver by default. You would need to use _FullDuplexSerialDriver instead to handle higher rates. This is done by including the following code in your program.
    #include <propeller.h>
    
    
    extern _Driver _FullDuplexSerialDriver;
    
    _Driver *_driverlist[] = {
      &_SimpleSerialDriver,
      NULL
    };
    
Sign In or Register to comment.