Shop OBEX P1 Docs P2 Docs Learn Events
beyond monolithic code . . . — Parallax Forums

beyond monolithic code . . .

Don PomplunDon Pomplun Posts: 116
edited 2011-07-07 16:33 in Propeller 1
Haven't been on the forum in a looongtime. My only project worthy of Propellerism has been as my house controller. Originally written as one big monolithic piece of code (3 cogs, but no objects), it's become too cumbersome to modify . . .
So I'm going to modularize it. But I'm already running into "issues". Problem (first) is having a serial LCD alpha display that wants to be written to by different methods. If all are in the same cog, or different cogs in the same object, no problem. Here's a compacted example: [Don]

**************************** 2 cogs, 1 object - OK ***************************************
{
This initializes the serial object in cog 0.
The string sent from Aux in cog 1 works OK & the LED lights.
}

CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

VAR
long AuxStack[30]

OBJ
ser : "fullduplexserial"

PUB Start
dira[4]~~ ' RS422 hardware direction control
outa[4]~~

dira[16]~~ ' diagnostic LED pin

if ser.start( 5,3,2,9600 ) == -1 ' RS422 port for LCD screen
abort
cognew( Aux, @AuxStack )
repeat

PUB Aux
dira[16]~~ ' each cog needs to set direction of I/O
outa[16]~~ ' turn on test LED
ser.str( @Teststr )
repeat

DAT
Teststr byte "test 1 2 3 4",0




************************************************************************************

**************************** 2 cogs in 2 objects - NG ***************************
{
This initializes the serial object in cog 0.
The aux method is started in anothercog in another object (Test5).
When run, the aux method lights the LED, but no string prints.
}

CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

VAR
long AuxStack[30]

OBJ
ser : "fullduplexserial"
NextCog : "test5"

PUB Start
dira[4]~~ ' RS422 direction control
outa[4]~~

dira[16]~~ ' diagnostic LED pin

if ser.start( 5,3,2,9600 ) == -1 ' RS422 port for LCD screen
abort
cognew( NextCog.aux, @AuxStack )
repeat

**********************************************************************

************************** additional object *******************************
OBJ
ser : "fullduplexserial"
{ need to put the OBJ reference in to satisfy the str call.
Don't want to run fullduplexserial's Start method because
it would start yet another cog.


PUB Aux
dira[4]~~ ' RS422 direction control
outa[4]~~
dira[16]~~ ' each cog needs to set direction of I/O
outa[16]~~ ' turn on test LED
ser.str( @Teststr )
repeat


DAT
Teststr byte "test 1 2 3 4",0



TIA,
Don

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-06-21 15:02
    attachment.php?attachmentid=78421&d=1297987572
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-06-21 15:15
    FullDuplexSerial uses VAR for its buffers and variables, which means that each instance of the FullDuplexSerial object will have its own independent set of variables and buffers. You call ser.start from the top object, but you call ser.str from the other object. All calls to FullDuplexSerial must be from the same object. If you only need one serial port you can fix this by creating a modified version of FullDuplexSerial that uses DAT instead of VAR for its variables.
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-21 16:11
    Thanx Dave -- will work on it.

    . . . and thanx Phil -- looked fine when I pasted it, and didn't Preview. Good thing it wasn't very complicated code with multi-multi-multi indents ;=)

    Don
  • StefanL38StefanL38 Posts: 2,292
    edited 2011-06-22 04:02
    for writing to a serial display from multiple cogs use a buffer.
    There is one cog reading the buffer and comparing it with the content of the last transmitted buffer-content. If something has changed transmitt buffer-content new.

    all other methods just write into the buffer. The buffer is an array of bytes where each bytes represents a character on the display including the position on the display.

    In this way multiple methods from anywhere can write towards the display but just write into the buffer. Then the LCD-sendbuffer-cog sends the characters really to the display.

    keep the questions coming
    best regards

    Stefan
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-22 08:45
    Here's the next (unsuccessful) iteration. "LCD Interface" is now a common method that I hoped would be acc essible from anywhere and get around the previous problem. But as you see from the comments, it works from another cog only as long as it's not in another object.

    On the suggestion about the buffer, where would I put the buffer so it could be accessed from all objects?

    TIA
    Don
    **************  Top Object File  ********************************
    {
    This calls LCD Interface init which initializes the serial object in cog 0.
    
    }
    
    CON
    _clkmode = xtal1 + pll16x
    _xinfreq = 5_000_000
    
    VAR
      long AuxStack[30]
      
    OBJ
      LCD : "LCD Interface"
      NextCog : "test5"
      
    PUB  Start
      dira[4]~~  ' RS422 direction control
      outa[4]~~
    
      dira[16]~~ ' diagnostic LED pin
      
      LCD.init
    '  LCD.reset  ' if I uncomment this line it DOES reset/blank the display
      cognew( NextCog.aux, @AuxStack )
      repeat
    
    ***************************************************************
    
    *************  object in new cog  *****************************
    
    OBJ
      LCD :  "LCD Interface" 
    
    PUB Aux
      dira[16]~~  ' each cog needs to set direction of I/O
      outa[16]~~  ' turn on test LED
      LCD.reset   '  I think this should reset/blank the display, but it doesn't  --  why?
      repeat
      
    
    
    ********************************************************************
    ***********  single common object that interfaces to fullduplexserial  *******
    
    
    CON
      esc = 27
    
    OBJ
      ser : "fullduplexserial"
    
    PUB init
      dira[4]~~
      outa[4]~~
      dira[3]~~
      if ser.start( 5,3,2,9600 ) == -1  ' RS422 port for LCD screen
        abort
    
    PUB reset  '  clear display and turn off backlight
      ser.tx( esc )
      ser.tx( "c" )
    
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-06-22 09:11
    The problem is that you are accessing "LCD Interface" from two differents objects, so they are not using the same VAR space. The easiest solution is to convert the VAR variables to DAT variables in a modified copy of FullDuplexSerial. FDS defines it's variables as
    VAR
    
      long  cog                     'cog flag/id
    
      long  rx_head                 '9 contiguous longs
      long  rx_tail
      long  tx_head
      long  tx_tail
      long  rx_pin
      long  tx_pin
      long  rxtx_mode
      long  bit_ticks
      long  buffer_ptr
                         
      byte  rx_buffer[16]           'transmit and receive buffers
      byte  tx_buffer[16]  
    
    Change this to
    DAT
      cog long 0                     'cog flag/id
    
      rx_head long 0                 '9 contiguous longs
      rx_tail long 0
      tx_head long 0
      tx_tail long 0
      rx_pin long 0
      tx_pin long 0
      rxtx_mode long 0
      bit_ticks long 0
      buffer_ptr long 0
                         
      rx_buffer byte 0[16]           'transmit and receive buffers
      tx_buffer byte 0[16]  
    
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-22 21:31
    Ahhhhhh, either a new revelation/understanding (or a recall from the past) . . .

    After a little pseudocoding, I conclude that
    a. items declared in a VAR block are global to all methods in an object (no matter how many VAR blocks are declared - probably bad form anyway).
    b. items declared in a DAT block are global to all objects in an app (cog association is irrelevant)

    That seems to mesh with your statements. Will pursue that approach.

    thanx,
    Don
  • Mike GreenMike Green Posts: 23,101
    edited 2011-06-22 21:38
    Not quite ...

    Items declared in a VAR block are global to all methods in an object, but there's a separate instance of all VAR items for each use of the object in an OBJ declaration in the program. When a method of the object is called, the method uses the particular instance of the VAR items associated with the object name used as a prefix for the method.

    Items declared in a DAT block are global to all methods in an object and there's a single instance of the DAT items common to all declarations of the object in the program.
  • RockyDRockyD Posts: 17
    edited 2011-06-22 21:55
    For one project I made a specially modified version of FullDuplexSerial that had a method that would return pointers to buffers and such. I was able to pass these pointers to another object. There might be a better way to do it than what I did.
    PUB get_buffers(prx_head, prx_tail, ptx_head, ptx_tail, prx_buffer, ptx_buffer) | pointer
    
      pointer := @rx_head
      longmove(prx_head, @pointer, 1)
    
      pointer := @rx_tail
      longmove(prx_tail, @pointer, 1)
    
      pointer := @tx_head
      longmove(ptx_head, @pointer, 1)
    
      pointer := @tx_tail
      longmove(ptx_tail, @pointer, 1)
    
      pointer := @rx_buffer
      longmove(prx_buffer, @pointer, 1)
    
      pointer := @tx_buffer
      longmove(ptx_buffer, @pointer, 1)
    
    
    
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-23 16:14
    I used Dave's suggestion and modified FDS, but seemed to run into what looked like race conditions when asynchronous calls tried to write to the display.
    Combining some of the suggestions made, I decided to try having my display interface declare a screen buffer in a DAT block. Since I have cogs-a-plenty, one would just keep reading the DAT buffer area and refresh the display contniuously. Worked fine until I tried sending data to that block from another object. Whops . . . Reread Mike's comments and realized that the DAT block isn't accessible outside an object. Bummer.
    Next attempt is sending a set of params (line, col, string) from other object/methods. Beginning to seem over-complicated just to gain object aloofness. Since I've NEVER reused any of my old code ;=) maybe this isn't worth the effort. I can write it all as one big document, like before, but break up my VAR into multiple chunks, geographically located with their respective methods. Same for DAT blocks.
    Not discouraged; just don't want this project to become and end-of-life issue ;=)
    [lamenting hitting my medicare b'day]
    Don
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-25 10:03
    "Items declared in a DAT block are global to all methods in an object and there's a single instance of the DAT items common to all declarations of the object in the program. "

    Do DAT blocks go in Propeller "main memory"? If I declare an area in DAT in one object (like the buffer the object uses to write to the display), I'm not sure how to write into that same area from another object. I can't just declare a DAT block in obj#2 and expect it to be the same space. Do I somehow pass the address of the DAT from obj#1 to obj#2, and then use offsets in #2 to put data in the table?
    TIA
    Don
  • Mike GreenMike Green Posts: 23,101
    edited 2011-06-25 11:44
    Items declared in a DAT block do go into "main" (hub) memory just like everything else in a Propeller program including VAR items, code, assembly code, the stack, etc. although COGNEW and COGINIT may copy assembly code into a cog's memory for execution.
  • graffixgraffix Posts: 389
    edited 2011-06-25 11:54
    Thanks Phil, I was wondering how to post my code like that!!!
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-25 11:59
    Mike Green wrote: »
    Items declared in a DAT block do go into "main" (hub) memory just like everything else in a Propeller program including VAR items, code, assembly code, the stack, etc. although COGNEW and COGINIT may copy assembly code into a cog's memory for execution.

    . . . so if I want to access a DAT area defined in one object from another object, how do I find it?
  • Mike GreenMike Green Posts: 23,101
    edited 2011-06-25 12:17
    You have to pass its address around. Let's say both objects are declared in your main object. You call the initialization routine for one object and that initialization routine returns the address of its DAT area. Your main program save that address somewhere and then calls the initialization routine for the 2nd object and passes the DAT address from the 1st object. Now the 2nd object knows where the DAT area of the 1st object is located. If you want to make this mutual, the 2nd object's initialization routine will have to return the address of its DAT area as well which the main program saves in some variable. The 1st object has to have another routine that's passed the address of the 2nd object's DAT area and that routine saves the DAT address somewhere in the object's variables.
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-06-25 12:55
    OK, I think I can do that. Thanx
  • Don PomplunDon Pomplun Posts: 116
    edited 2011-07-07 16:33
    OK,
    in my top object I have a declared array, X, in its DAT block. I can pass its address to another object as @X.
    In that subsidiary object I can use Byte or ByteMove to put stuff in the top's array.
    But can I declare an array in the subsidiary object that will start at X's address so that I can easily reference this common area with normal subscripts without a lot of address machinations?
    TIA
    Don
Sign In or Register to comment.