Shop OBEX P1 Docs P2 Docs Learn Events
Run Serial_LCD driver in its own cog? — Parallax Forums

Run Serial_LCD driver in its own cog?

hitolisolohitolisolo Posts: 11
edited 2013-11-05 07:15 in Propeller 1
Hello,
I've been trying to pick up Spin recently, but honestly I'm not finding it that easy.
Currently I'm trying to understand using the other cogs.
I'd like to launch an object (namely the LCD driver, at the moment) into its own cog.
However, from what I can follow I can only launch single methods into a cog (not an entire object - so that any and all of its methods would run in the other cog when called).
So is there a (somewhat) simple way to run the driver in its own cog?
Or do I need to write my own that's contained within a single function and update global/HUB variables within the other methods with the information to send?

Thanks for your help.

Comments

  • StefanL38StefanL38 Posts: 2,292
    edited 2013-11-01 07:44
    Hi Peter,

    welcome to the propeller-world and -forum.
    objects and cogs are different things.

    You can have one object using more than one cog
    you can have one cog executing code from more than one object.

    objects are pieces of code that are stored in a second, third ... *.spin-file.

    Now it depends on the code if other cogs are engaged or not.
    starting a cog is always done through the command cognew.

    There can be a great variant of locations where inside the code the command cognew is placed.

    Wherever the command cognew is executed a new cog will be start running and will execute the code
    named as the parameter

    example
      cognew(MyMethod @MyStackSpace)
    

    In the example above the new cog starts executing the code written in the Method "MyMethod"
    all commands or other methods that are "called" from the code inside "MyMethod" will be executed in the new cog.

    Some objects use an assemblerdriver. If this is the case the new cog executes only the assembler-code and no spincode.
    Anyway SPIN-code is always executed by the calling cog.

    Please attach your full code or at least post the exact name of the object that you are using.
    The explanations will be easier to understand if the forum-members can refer to a certain object.
    It will be helpful too if you describe what you want to do in the end.
    It highly depends on the main-purpose which solution can be chosen or firts best.

    I guess you have some problems
    a) to wrap your brain about parallel executed code
    b) your code behaves weird or unexpected

    to help you more with this you should gives an overview of what you want to do in the end and describe with more details
    what the actual problem is.

    Another way to learn about objects and cogs is to read and work through the Propeller-Education KIT
    click on help inside the propeller-tool and choose propeller education KIT (pdf)...
    You don't need to have a PE-Kit hardware to learn from the labs.
    Take a look into it and just start reading. If you have concrete questions on how adapating code to any other propeller-hardware
    just post a question here

    best regards
    Stefan
  • T ChapT Chap Posts: 4,223
    edited 2013-11-01 08:47
    The way I use the Parallax 2x16 LCD is with a 4port Full Duplex Object. You only launch one instance of the 4 port object. This way, you have 3 other serial ports available for other uses.

    ser.tx(0, $00)
    
    or
    
    ser.str(0, "hello")     '   0 = port number for the LCD
    
    PUB go(line,col) | lcdline
        if line == 0
          lcdline := $80
        elseif line == 1
          lcdline := $94
        ser.putc(3,lcdline + col)
    
    
    PUB CLS    'clear screen
        ser.cls(3)
    
    
    
  • JonnyMacJonnyMac Posts: 9,107
    edited 2013-11-01 09:31
    So is there a (somewhat) simple way to run the driver in its own cog?

    Sure, write the driver in PASM which must be launched into its own cog.

    Here's the key: for any object that uses IO, you can only control that IO from the cog that setup the pins you're using. For example, if you make P0 an output in COG0, manipulating the OUTA bit in COG1 won't do anything (until you make that bit an output in COG1 as well). For objects that use PASM, the Spin section is just an interface to the PASM code which is in the cog that setup the pins.

    If you're using a Spin-only interface (perhaps a simple, 4-bit LCD driver), then you need to start that object in the cog where you want control. I've done this. I helped a guy with an HVAC project that uses an LCD with buttons; that interface needed to run while the system also received parameters from an RS-485 link. We solved this by running the LCD interface code in its own cog -- my LCD object was started in that cog so the IO lines were setup where they would be used. The system parameters where all globals in the hub which allowed the RS-485 and LCD cogs to see and manipulate them at the same time.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2013-11-03 15:46
    Hi Peter,

    I take it that you are referring to a serial LCD rather than parallel and if so then you only need a single I/O pin and a few lines of Spin code to transmit the ASCII data serially. The code is trivial but you could look at the Simple Serial object and just use the few lines of transmit code. There is no need to run this in it's own cog unless your application software can't afford the time it takes to send a handful of characters serially. Most of the time we are talking about 16x2 displays and that means 32 characters tops with infrequent updates.
  • hitolisolohitolisolo Posts: 11
    edited 2013-11-04 02:51
    Thanks for the replies, everyone.
    StefanL38 wrote: »
    Hi Peter,

    welcome to the propeller-world and -forum.
    objects and cogs are different things.

    You can have one object using more than one cog
    you can have one cog executing code from more than one object.

    objects are pieces of code that are stored in a second, third ... *.spin-file.

    Another way to learn about objects and cogs is to read and work through the Propeller-Education KIT
    click on help inside the propeller-tool and choose propeller education KIT (pdf)...
    You don't need to have a PE-Kit hardware to learn from the labs.
    Take a look into it and just start reading. If you have concrete questions on how adapating code to any other propeller-hardware
    just post a question here
    I could follow that much. Unfortunately, all the examples I could find in the PropTool documentation simply launched self contained methods (i.e. they didn't need more than the originally passed arguments).
    I'd tried the PDF you mentioned, unfortunately I didn't see where they had an example of passing information to an already running cog.
    And I'm thinking that's what I'll need to do.
    StefanL38 wrote: »
    Please attach your full code or at least post the exact name of the object that you are using.
    The explanations will be easier to understand if the forum-members can refer to a certain object.
    It will be helpful too if you describe what you want to do in the end.
    It highly depends on the main-purpose which solution can be chosen or firts best.

    I guess you have some problems
    a) to wrap your brain about parallel executed code
    b) your code behaves weird or unexpected

    to help you more with this you should gives an overview of what you want to do in the end and describe with more details
    what the actual problem is.
    Sorry I didn't realize how vague that came across.
    I'd written a program for my Stamp to use as an interface for some old memory devices I have about.
    However, it was taking the Stamp ~4 hours to read a single MB. So I ported the program to my Propeller (a QuickStart board) and Spin hoping for more speed.
    The program actually works, it just doesn't have the speed I wanted. Reading 2 MBs took ~12 hours!
    I tracked the speed loss to the code that updated the LCD [Parallax's 2x16 #27977] every byte (originally as debugging feedback, but I'd like to keep it if it won't cost that much in speed).
    I'm using the Serial_Lcd.spin object I found in the propeller library folder. I copied the Hex and Bin functions from the Parallax Serial Terminal object into it to retain the formatting I had in the PBASIC version.
    This is the function that kills the speed:
    PRI ReadByte(address) : data
      outa[addrMSB..addrLSB] := address
      outa[rdPin]~                
      waitcnt(clkfreq / 100000 + cnt)
      data := ina[dataMSB..dataLSB]
      outa[rdPin]~~
      {lcd.clrln(1)
      lcd.str(string("RD "))
      lcd.Hex(address, 4)
      lcd.putc(" ")
      lcd.Bin(data, 8)}
      return
    
    With the lcd lines commented out as they currently are it read the 2 MBs within 6 minutes. So if my math is still decent that's just over 21 seconds to execute those lines (for the LCD).
    Granted the lcd.clrln(1) is a pointless waste of time here and should be replaced with lcd.gotoxy(0, 1), however that shouldn't quite cut the time in half and 10+ seconds a byte is still awfully slow.
    I'd like to get an independent cog to process/format the information and update the LCD, but I'm unsure how I can implement that (as even if Serial_Lcd used a cog to output the data, the Hex and Bin functions would be processed within my main cog - and I figure they're the next big hit to speed).
    Perhaps the better question is, estimating ~176ms to read each byte without updating the LCD, is it even plausible to try updating the LCD that quickly?
    The driver would have to be able to parse the hex and binary output faster, surely.

    T Chap wrote: »
    The way I use the Parallax 2x16 LCD is with a 4port Full Duplex Object. You only launch one instance of the 4 port object. This way, you have 3 other serial ports available for other uses.
    What do you do for the Rx pin? I don't have a spare pin to set it to. And I didn't think it had an option not to use one (although I'm unable to double check just now).

    JonnyMac wrote: »
    Sure, write the driver in PASM which must be launched into its own cog.

    Here's the key: for any object that uses IO, you can only control that IO from the cog that setup the pins you're using. For example, if you make P0 an output in COG0, manipulating the OUTA bit in COG1 won't do anything (until you make that bit an output in COG1 as well). For objects that use PASM, the Spin section is just an interface to the PASM code which is in the cog that setup the pins.

    If you're using a Spin-only interface (perhaps a simple, 4-bit LCD driver), then you need to start that object in the cog where you want control. I've done this. I helped a guy with an HVAC project that uses an LCD with buttons; that interface needed to run while the system also received parameters from an RS-485 link. We solved this by running the LCD interface code in its own cog -- my LCD object was started in that cog so the IO lines were setup where they would be used. The system parameters where all globals in the hub which allowed the RS-485 and LCD cogs to see and manipulate them at the same time.
    That's good to be aware of - thank you. I've never programmed anything in assembly before, though. Is there a good introduction to assembly programming somewhere?

    Hi Peter,

    I take it that you are referring to a serial LCD rather than parallel and if so then you only need a single I/O pin and a few lines of Spin code to transmit the ASCII data serially. The code is trivial but you could look at the Simple Serial object and just use the few lines of transmit code. There is no need to run this in it's own cog unless your application software can't afford the time it takes to send a handful of characters serially. Most of the time we are talking about 16x2 displays and that means 32 characters tops with infrequent updates.
    Yes, I'm using the #27977 2x16 display from Parallax. I'm using the Serial_Lcd.spin file in the library folder (slightly modified) that itself uses the Simple Serial object. My problem is the code is taking ~21 seconds to update the LCD and I was hoping to update it with the individual byte address and data, but that took ~12 hours to read just 2 MBs. Since I'm using parallel buses would it be possible to have another cog monitor the I/O pins and automatically send the correct output to the LCD instead of calling that function from within my main cog?


    One slight glitch with the program is when I first plug the board in, the Parallax Serial Terminal object - which I'm using to communicate with the computer - is supposed to send a byte to clear the screen at the end of the initialization function. However, for some reason it doesn't until I try to send something to it. At which time it appears to reboot, but does send the byte afterward. And it works like it should after that until it's unplugged and has to boot again. Any idea what might be causing that? And is there a reason the PST implies its buffer can't be set higher than 256?
    Thanks for the suggestions everyone.
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-11-04 04:39
    I guess there is more going on than just reading a byte. The ReadByte function is called from somewhere and that code also adds time to the total runtime.

    One possible improvement is, using a ringbuffer which is read from the code that currently calls ReadByte and written continuously by the ReadByte function which has to be turned into a loop. This could than be started in it's own COG.

    I doubt that you'll read the whole 2MB output. So, does it maybe make sense to display a percentage bar (or number) instead?
    In this case the function ReadByte simply increments a counter which is used by the LCD-output routine (which can run in a 3rd COG) to calculate and output the percentage.

    The whole thing about sharing data between COGs is to use the same memory location in HUB-RAM and use that for passing data. As long as you stay in SPIN it is as easy as using the same variable.

    If you don't work on secret code, it's best to archive it and attach the whole code to the post. Then we can show you much more possibilities to improve the code. From SD card the propeller can read ~1.6MB per second, so 6 min. is quite a long time!
  • hitolisolohitolisolo Posts: 11
    edited 2013-11-04 05:36
    MagIO2 wrote: »
    I guess there is more going on than just reading a byte. The ReadByte function is called from somewhere and that code also adds time to the total runtime.

    One possible improvement is, using a ringbuffer which is read from the code that currently calls ReadByte and written continuously by the ReadByte function which has to be turned into a loop. This could than be started in it's own COG.

    I doubt that you'll read the whole 2MB output. So, does it maybe make sense to display a percentage bar (or number) instead?
    In this case the function ReadByte simply increments a counter which is used by the LCD-output routine (which can run in a 3rd COG) to calculate and output the percentage.

    The whole thing about sharing data between COGs is to use the same memory location in HUB-RAM and use that for passing data. As long as you stay in SPIN it is as easy as using the same variable.

    If you don't work on secret code, it's best to archive it and attach the whole code to the post. Then we can show you much more possibilities to improve the code. From SD card the propeller can read ~1.6MB per second, so 6 min. is quite a long time!
    While I didn't quote the entire program in the post itself, I did attach the files. Are they not at the bottom of the post for you?
    Since the data passes quicker than it can actually be read, your idea of a progress bar is a good one.
    I'll have a look at writing that up.

    Also, updating the LCD elsewhere is causing more slow down. When I remove all the LCD updates I can get it down to two minutes / MB.
    Granted that's still really slow when compared to your SD card.
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-11-04 08:30
    So, besides the LCD the other limiting factor is the PC connection, which has a speed of 115200 baud. 2MB divided by this tells you what is possible at max - round about 2min. per 2MB.

    So, you're half way on that road ;o)

    The idea still is to use buffers and let LCD, Pc-sender/receiver and the RAM-driver work with this buffer more or less independently, because then you can start all of these tasks in separate COGs.

    When optimizing, first thing to look at are the loops. For example you have loops counting up RAM addresses and calling the read function. For 1000 addresses this makes 1000 function calls, where each call adds time to the runtime. If you change the function to accept a variable address and the address-range and let it loop itself, there is only one function call.

    There is another loop which has an if inside. I can't see that the variable you check is changed inside of the loop. In this case it would be better to have 2 different loops. One for the if-case and a slightly different one for the else-case. The if would then be outside the loop and would only be executed once instead of 1000times.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2013-11-04 18:58
    Peter, I'm a bit confused as to why you are using this "old memory device" as it sounds very slow, it would be faster just to use an SPI Flash. The serial interface to the PC can be a lot lot faster than 115200 baud and I've run it up to 3M baud. Also why are you using a serial LCD object? I'm not sure which one you are using (it's always good to link these references).

    So do you have a description of what it is you are trying to do (rather than how you are doing it)?
  • hitolisolohitolisolo Posts: 11
    edited 2013-11-05 07:15
    MagIO2 wrote: »
    So, besides the LCD the other limiting factor is the PC connection, which has a speed of 115200 baud. 2MB divided by this tells you what is possible at max - round about 2min. per 2MB.

    So, you're half way on that road ;o)

    The idea still is to use buffers and let LCD, Pc-sender/receiver and the RAM-driver work with this buffer more or less independently, because then you can start all of these tasks in separate COGs.

    When optimizing, first thing to look at are the loops. For example you have loops counting up RAM addresses and calling the read function. For 1000 addresses this makes 1000 function calls, where each call adds time to the runtime. If you change the function to accept a variable address and the address-range and let it loop itself, there is only one function call.

    There is another loop which has an if inside. I can't see that the variable you check is changed inside of the loop. In this case it would be better to have 2 different loops. One for the if-case and a slightly different one for the else-case. The if would then be outside the loop and would only be executed once instead of 1000times.
    That's good advice - I'll look at re-writing some functions and loops so as to reduce the unnecessary calls/checks. A single function that accepted a beginning address, end address and buffer pointer (or have it send the data directly to the PST) would allow the same information to be read with a single function call and speed up the process well, yes?
    As for the buffers, the Parallax Serial Terminal object seems to imply its buffer can't be set larger than 256, but it seems to use HUB RAM as a buffer - do you see a reason why I couldn't use any value as long as I have that much room in the HUB?

    Peter, I'm a bit confused as to why you are using this "old memory device" as it sounds very slow, it would be faster just to use an SPI Flash. The serial interface to the PC can be a lot lot faster than 115200 baud and I've run it up to 3M baud. Also why are you using a serial LCD object? I'm not sure which one you are using (it's always good to link these references).

    So do you have a description of what it is you are trying to do (rather than how you are doing it)?
    Yes, if I just wanted to store data an SD card would be a much simpler and faster, I'm sure, solution. I'm interfacing with these cartridges in order to recovered data from them (so switching to an SD card obviously won't work here).
    I'm using a file in my Propeller library folder called Serial_Lcd.spin (with two extra functions) to interface with a 2x16 Serial LCD from Parallax (#27977). The exact (modified) file I'm using I attached a couple posts ago - here. The LCD was originally for debugging feedback, however I like having the status shown even though it's not really needed now.
    While the transfer rate can probably be faster in the end, at the moment I'm losing data somewhere on occasion and a faster rate will only increase that (I think it's on the computer side, but I'm not certain, yet) and I'm content at the moment with what I'm getting (minus the huge delays caused by updating the LCD).
    Finally, as my thread title suggests (and I thought I'd said this in a post of two directly), I'd like to be able to update my LCD more quickly (it's currently taking ~15 seconds to update a single line) and I thought being able to run the driver in its own cog (since I have extra) would help with that. It seems like it shouldn't take that long to send 16 characters to the LCD.
Sign In or Register to comment.