I fail to understand what grudge match or language war has to do with this topic.
Seems like the original idea was to prove that Spin was better than C at least for this application. I guess I should stop allowing myself to be drawn into these debates! :-)
I guess if you're unwilling to use PASM with C then it will perform worse than Spin+PASM. I'm not sure where the complexity of the sketch came in. Are you talking about the sketch itself plus all of the libraries required to use it? Anyway, it is certainly much easier to use Spin+PASM than it is to use C+PASM. However, the need to use Spin+PASM is mostly due to the fact that Spin often doesn't perform well enough to use on its own.
Yes, I was referring to the original Arduino Sketch file collection.
and also the (uncounted) conditional compilation switches.
It's a lot of megabytes to dig through to find a kilobyte of gold.
On the other topic, I may be wrong about this, but it seem to me that Spin IS the high level language for the Propeller.
It's back to that whole Von-Gracey architecture thing.
Spin is an interpreter-in-ROM, somewhat like the original Apple, TRS-80' etc computers of the olden days, but the
whole machine was boiled down to a single chip (ok and eeprom, but you get the idea).
It's a whole lot of power in a very tiny and unique package.
It's FUN.
This particular project issue comes from trying to adapt an example from the Arduino world.
That's where all the good toys are found these days.
But the example software is so involved and convoluted that I simply can't follow the thread.
It's easy to lose heart when it's that involved (aka hard) to follow.
The amount of complication involved - not fun.
So if you wish,yes, that's a failing on my part, for not embracing C.
...and this fixes FillCircle. Cavelamb, could you see if this adjustment fixes your text crash too?
Edit: I tried deliberately running text off the edge and it seems to work OK. The problem is that DrawVerticalLine is bound checked but if it's completely out of bounds the write length is zero, which was getting decremented to $FFFF_FFFF. So excluding zero from the repeat fixes the problem.
That fixed the crash problem quite well.
I tried the same thing, running off the edge.
drawString(string("LiftOff!"),200,240,4,WHITE)
All that prints is the "L" on the extreme right side.
It looks like the text is probably trying to wrap around back to the left side.
But not advancing past the first character (overwriting the previous chars?)
It doesn't lock it up at all, so that's a graceful failure.
edit:
Also, just so you know, I moved the DAT sections to the end of the source file.
for my project work.
I think I vaguely remember that the DAT declaration will align on a long boundary?
But if not, somebody holler!
On the other topic, I may be wrong about this, but it seem to me that Spin IS the high level language for the Propeller.
It's back to that whole Von-Gracey architecture thing.
Spin is an interpreter-in-ROM, somewhat like the original Apple, TRS-80' etc computers of the olden days, but the
whole machine was boiled down to a single chip (ok and eeprom, but you get the idea).
It's a whole lot of power in a very tiny and unique package.
It's FUN.
Spin is a nice language. It may be the best language for programming the Propeller. Partly, this is because its 2K VM is in ROM so it doesn't waste space in hub memory. It also generates more compact code than C although it also tends to be slower as well. However, Spin is not a perfect language from my point of view. Objects are not first-class data types. It doesn't have any concept of structures. In its original form it had no mechanism for conditional compilation. I think even PBASIC has that. And, most of all, it's very slow thus requiring more code to be written in assembly language in order for a program to achieve decent performance. This isn't true of C on other CPU architectures. I also object to writing a program in a language that can only be run on a single type of processor and please don't say that all microcontroller code is processor specific anyway because the existence of this thread proves otherwise. Many people have ported C code from the Arduino to the Propeller with minimal changes. This just isn't going to be possible for code written in Spin going in the other direction. Of course, Parallax would probably rather that people didn't migrate from the Propeller to some other processor but I'd like to have that option should it become necessary for some reason. I have C code that I wrote back in 1983 that I can still run. It has been ported to numerous different processors without any significant problems. I'd like to continue to be able to do that and not end up building up a library of code that is stuck on a the Propeller. I'm not even totally convinced that Spin code for the P1 will be able to run on Spin for the P2 without rework.
Anyway, Spin is fun. The Propeller is a lot of fun. I love both of them but if I'm going to write code that I hope to have a life beyond just one project I'd rather write it in a language that I have some hope will be supported on other processors and platforms.
New numbers. All tests were run printing the text "Happy!" (I say this because I'm noticing significant differences in both C++ and Spin when the characters change - even with the same number of characters). PropWare::SeeedTFT is basically the original Arduino code, just ported to use some PropWare classes instead of Arduino. PropWare::SeeedTFTFast has been modified to use the PASM cog exactly as localroger posted in his example.
These results sure do intrigue me! I have no idea why printing text from PropWare::SeeedTFTFast with LMM is so much slower than Spin.
This link provides the exact commit with the code used in PropWare.
And I've attached the Spin file that I used (I just added the Parallax Serial Terminal and some print statements)
That's fascinating, SwimDude. I wonder if the bottleneck in C text drawing is the overhead for calling FillRectangle for each drawn pixel. As I found when I tried manually macro-izing the Spin version there doesn't seem to be any noticeable overhead for calling a Spin method, but C might need to put more effort into building a stack frame for the passed parameters and local varaibles. C should be faster at the actual rectangle drawing for each pixel since it's faster at clearing the screen.
It's not too surprising that different text changes the overhead because undrawn pixels are skipped, so it's really the number of pixels rather than the number of characters that determines the workload.
New numbers. All tests were run printing the text "Happy!" (I say this because I'm noticing significant differences in both C++ and Spin when the characters change - even with the same number of characters). PropWare::SeeedTFT is basically the original Arduino code, just ported to use some PropWare classes instead of Arduino. PropWare::SeeedTFTFast has been modified to use the PASM cog exactly as localroger posted in his example.
These results sure do intrigue me! I have no idea why printing text from PropWare::SeeedTFTFast with LMM is so much slower than Spin.
This link provides the exact commit with the code used in PropWare.
And I've attached the Spin file that I used (I just added the Parallax Serial Terminal and some print statements)
No grudge here, just curious how some of this code translates to Forth so I converted up to the init function and was quite surprised how "slow" it was. But then again I am not even using a little PASM module helper function, this is all bytecode, nothing fancy.
Tachyon SS_TFT.fth
- iNIT: 3.433 seconds
- Text: (not converted)
I can see that even with just a few longs of PASM in a RUNMOD module (same cog as Tachyon) that this should run a lot faster. I think there is a lot of time wasted with operands being pushed and popped. I also removed some redundancies and fixed what looked like what could be timing problems but I don't have one of these displays to play with. Interestingly I find SPI serial far more efficient than bit-bashing parallel data buses, I can transfer 16-bits of data in around 4.8us using the SPIWR instructions.
{
SeeedStudio Touchscreen TFT Driver
This code is more or less directly translated from the Spin code by Jazzed
which was more or less directly translated from the Arduino source.
Some code and timing optimizations made.
}
--- define some aliases to match the Spin code better
ALIAS SHL <<
ALIAS SHR >>
ALIAS OUTCLR LOW
ALIAS OUTSET HIGH
DECIMAL
11_1111_1100 == tftdat --- all pins
'' Basic Colors
$F800 == RED
$07E0 == GREEN
$001F == BLUE
$0000 == BLACK
$FFE0 == YELLOW
$FFFF == WHITE
'' Other Colors
$07FF == CYAN
$F810 == BRIGHT_RED
$8410 == GRAY1
$4208 == GRAY2
'' TFT resolution 240*320
0 == MIN_X
0 == MIN_Y
240 == MAX_X
320 == MAX_Y
'' pin definitions from tft.h. The pin manipulation
$04 8 << == CS
$08 8 << == RS
$10 8 << == WR
$20 8 << == RD
'' Macro definitions for char display direction
0 == LEFT2RIGHT
1 == DOWN2UP
2 == RIGHT2LEFT
3 == UP2DOWN
byte DisplayDirect
( define a couple of words to make the tables look more like the Spin version )
pub GETCSV "," delim C! GETWORD NUMBER ;
pub bytes IMMEDIATE BEGIN GETCSV WHILE codes W@ C! DROP 1 ALLOT REPEAT ;
pub words IMMEDIATE BEGIN GETCSV WHILE codes W@ W! DROP 2 ALLOT REPEAT ;
( Table includes delays and EOT codes )
TABLE InitSeq
words $FFFF,#0100,
words $0001,$0100,$0002,$0700,$0003,$1030,$0004,$0000,
words $0008,$0302,$000A,$0000,$000C,$0000,$000D,$0000,$000F,$0000,
words $FFFF,#0100,
words $0030,$0000,$0031,$0405,$0032,$0203,$0035,$0004,$0036,$0B07,
words $0037,$0000,$0038,$0405,$0039,$0203,$003C,$0004,$003D,$0B07,
words $0020,$0000,$0021,$0000,$0050,$0000,$0051,$00EF,$0052,$0000,
words $0053,$013F,$FFFF,#0100,
words $0060,$A700,$0061,$0001,$0090,$003A,$0095,$021E,$0080,$0000,
words $0081,$0000,$0082,$0000,$0083,$0000,$0084,$0000,$0085,$0000,
words $00FF,$0001,$00B0,$140D,$00FF,$0000,
words $FFFF,#0100,$FFFF,#0000,
pub getData
1 ms P@ 2 >> >B
;
pub pushData ( byte --- ) --- 10.8us
tftdat LOW
2 << HIGH
CLOCK CLOCK --- strobe the WR line low high
;
pub sendData ( data -- ) --- 35.6us
\\\ RS HIGH ( redundant)
pri send16
\\\ RD HIGH ( redundant)
CS LOW
W>B pushData pushData --- WriteData (16-bits)
CS HIGH
;
pub sendCommand ( index -- ) --- 45.2us
RS LOW send16
RS HIGH --- returns RS back to default data select
;
pub readRegister ( index -- data )
sendCommand
tftdat INPUTS --- place data bus into read state
\\\ RS HIGH ( redundant)
--- strobe RD and read data
RD LOW RD HIGH getData 8 <<
RD LOW RD HIGH getData OR
CS HIGH
;
pri SendCmdSeq ( ptr -- )
BEGIN
DUP W@ --- fetch a code from the table
$FFFF =
IF ( sequence control code )
2+ DUP W@ --- read next word as either a delay or an end of table (0)
?DUP IF ms ELSE DROP EXIT THEN
ELSE
DUP W@ sendCommand
2+ DUP W@ sendData
THEN
2+
AGAIN
;
pub exitStandBy
$0010 sendCommand
$14E0 sendData
100 ms
$0007 sendCommand
$0133 sendData
;
pub init --- 3.433sec
CS HIGH --- set control pins as inactive outputs
RD HIGH
WR HIGH
RS HIGH
tftdat LOW
WR 4 COGREG! --- setup WR as a STROBE op (slightly faster)
InitSeq SendCmdSeq --- 403.6ms (inc 400ms of delays)
$0007 sendCommand
$0133 sendData
50 ms
exitstandby
$0022 sendCommand
--- init cascades into the clearScreen function
pub clearScreen --- 4.8sec ; 3.7sec using CLOCK CLOCK for WR strobe ; 3.1sec by disabling redundant ops
MAX_X MAX_Y * FOR BLACK sendData NEXT
;
This whole thread seems to be comparing apples to oranges.
I'm not sure what's going on with the various versions posted above, but it's pretty easy to do an apples-to-apples comparison: just convert the Spin code to C++ with spin2cpp. I had to change the name of the Parallax Serial Terminal file to do that, but that's the only change needed to the spin file. The results are what I would expect: C++ in CMM mode is twice as fast as Spin at text drawing but is larger:
Spin + PASM:
compiled with openspin SS_TFT_PASMR.spin
size: 3868 bytes
Initialization time: 638 ms
Text print time: 225 ms
CMM + PASM:
compiled with spin2cpp --binary -Os -mcmm SS_TFT_PASMR.spin
size: 7236 bytes (5700 bytes w/o kernel)
Initialization time: 635 ms
Text print time: 106 ms
LMM + PASM:
compiled with spin2cpp --binary -Os -mlmm SS_TFT_PASMR.spin
size: 11308 bytes (9996 bytes w/o kernel)
Initialization time: 550 ms
Text print time: 26 ms
I've attached the code I used, along with the spin2cpp output and the binaries.
I seem to have done a Smile job coding PropWare::SeeedTFT and PropWare::SeeedTFTFast. I can't replicate the results at all. I think @ersmith has the best approach.
Comments
Thanks!!
2770070 - while they last.
Yes, I was referring to the original Arduino Sketch file collection.
and also the (uncounted) conditional compilation switches.
It's a lot of megabytes to dig through to find a kilobyte of gold.
On the other topic, I may be wrong about this, but it seem to me that Spin IS the high level language for the Propeller.
It's back to that whole Von-Gracey architecture thing.
Spin is an interpreter-in-ROM, somewhat like the original Apple, TRS-80' etc computers of the olden days, but the
whole machine was boiled down to a single chip (ok and eeprom, but you get the idea).
It's a whole lot of power in a very tiny and unique package.
It's FUN.
This particular project issue comes from trying to adapt an example from the Arduino world.
That's where all the good toys are found these days.
But the example software is so involved and convoluted that I simply can't follow the thread.
It's easy to lose heart when it's that involved (aka hard) to follow.
The amount of complication involved - not fun.
So if you wish,yes, that's a failing on my part, for not embracing C.
...and this fixes FillCircle. Cavelamb, could you see if this adjustment fixes your text crash too?
Edit: I tried deliberately running text off the edge and it seems to work OK. The problem is that DrawVerticalLine is bound checked but if it's completely out of bounds the write length is zero, which was getting decremented to $FFFF_FFFF. So excluding zero from the repeat fixes the problem.
And the whole image with the update: SS_TFT_PASMX.spin
I tried the same thing, running off the edge.
drawString(string("LiftOff!"),200,240,4,WHITE)
All that prints is the "L" on the extreme right side.
It looks like the text is probably trying to wrap around back to the left side.
But not advancing past the first character (overwriting the previous chars?)
It doesn't lock it up at all, so that's a graceful failure.
edit:
Also, just so you know, I moved the DAT sections to the end of the source file.
for my project work.
I think I vaguely remember that the DAT declaration will align on a long boundary?
But if not, somebody holler!
Anyway, Spin is fun. The Propeller is a lot of fun. I love both of them but if I'm going to write code that I hope to have a life beyond just one project I'd rather write it in a language that I have some hope will be supported on other processors and platforms.
PropWare::SeeedTFT w/ CMM
- Init: 12.758 seconds
- Text: 7.883 seconds
PropWare::SeeedTFT w/ LMM
- Init: 3.040 seconds
- Text: 1.678 seconds
PropWare::SeeedTFTFast w/ CMM
- Init: 0.552 seconds
- Text: 1.490 seconds
PropWare::SeeedTFTFast w/ LMM
- Init: 0.550 seconds
- Text: 0.354 seconds
SS_TFT_PASMR.spin
- Init: 0.638 seconds
- Text: 0.225 seconds
These results sure do intrigue me! I have no idea why printing text from PropWare::SeeedTFTFast with LMM is so much slower than Spin.
This link provides the exact commit with the code used in PropWare.
And I've attached the Spin file that I used (I just added the Parallax Serial Terminal and some print statements)
It's not too surprising that different text changes the overhead because undrawn pixels are skipped, so it's really the number of pixels rather than the number of characters that determines the workload.
No grudge here, just curious how some of this code translates to Forth so I converted up to the init function and was quite surprised how "slow" it was. But then again I am not even using a little PASM module helper function, this is all bytecode, nothing fancy.
Tachyon SS_TFT.fth
- iNIT: 3.433 seconds
- Text: (not converted)
I can see that even with just a few longs of PASM in a RUNMOD module (same cog as Tachyon) that this should run a lot faster. I think there is a lot of time wasted with operands being pushed and popped. I also removed some redundancies and fixed what looked like what could be timing problems but I don't have one of these displays to play with. Interestingly I find SPI serial far more efficient than bit-bashing parallel data buses, I can transfer 16-bits of data in around 4.8us using the SPIWR instructions.
I don't quite follow what you have done. You have results:
PropWare::SeeedTFTFast w/ CMM
- Init: 0.552 seconds
- Text: 1.490 seconds
PropWare::SeeedTFTFast w/ LMM
- Init: 0.550 seconds
- Text: 0.354 seconds
SS_TFT_PASMR.spin
- Init: 0.638 seconds
- Text: 0.225 seconds
and then say "I have no idea why printing text with LMM is so much slower than Spin."
Now the name of the Spin file implies it uses PASM where as there is no mention of PASM for the C files.
That implies the Spin code contains PASM to do the heavy lifting but the C code does not.
Is this the case or not? If it is you are not comparing Spin and C but rather PASM and C.
Exactly
I'm not sure what's going on with the various versions posted above, but it's pretty easy to do an apples-to-apples comparison: just convert the Spin code to C++ with spin2cpp. I had to change the name of the Parallax Serial Terminal file to do that, but that's the only change needed to the spin file. The results are what I would expect: C++ in CMM mode is twice as fast as Spin at text drawing but is larger:
I've attached the code I used, along with the spin2cpp output and the binaries.