P2 and SSD1306 OLED display driver [solved]
ManAtWork
Posts: 2,176
I think I saw that there is a driver or some demo software available for the P2 and the SSD1306 chip. But I can't find it anymore. The search engine only returns results for the P1.
Is this the only thing available? Or has anybody already written something for the P2?
Comments
Ah, I think I've found it, again: https://github.com/avsa242/ssd130x-spin
The serach function here in the forum is so terrible. We really need a well maintained OBEX library.
Wow, it worked right away!
I had to download the libraries: https://github.com/avsa242/p2-spin-standard-library
This is a really extensive piece of work! Thanks, Jesse! ( @avsa242) Lot's of useful stuff in there.
It includes a lot of files I don't want to copy into my project directory so I moved it to C:/flexspin/library and added "-LC:/flexprop/library" to the tasks.json file of VSC (compileP2 ... "args": ...).
It's a custom board with the crystal connected to the ethernet controller rather than the P2 directly. So I also had to change
You're welcome! There's also a demo that shows the usage of more of the drawing primitives in the p2-spin-standard-library, in the
demos/display
subdir.It's definitely not super-fast as it just uses a generic SPI engine (or I2C, if that's the case), not custom-tailored for the display or anything, but it provides I think most of the usual drawing primitives. Of course if you have
plot()
you can draw anythingBTW it's great to see an application built around the P2. It makes me wish I had a CNC machine (just one of those cheap "super-safe" open-frame laser engravers) or a place to put one!
Cheers
Yes, I just looked at the code and it does everything pixel by pixel. Not very fast but the library code is very clean and straightforward. I don't care much about speed (at least for the display) because I use it only for status display and diagnosis. Costs only $3 and is much better than a row of discrete LEDs.
BTW, to save the users from needing lenses I tried to use a bigger font and just replaced
fnt: "font.5x8"
by"font.8x16"
. But that doesn't work and only displays pixel garbage. Do I have to modify anything else or is this font not suitable for this type of display?Laser engravers or 3D printers are quite affordable because the mechanics don't need to withstand high forces. The real metal cutting machines need to be much more rigid and heavier. But it's no wizardry. The easiest way is to convert an old manual mill with some stepper motors.
That's something I've been meaning to address lately. When I first started writing that driver (and really any of the others), that was the only font I had at the time and so wrote the
putchar()
routine around that size, which is obviously not ideal. I don't think it'd be too hard to change it to accept up to 16 or 32 pixel-high sizes. I'm also not 100% sure what "format" that 8x16 font is in. I think I liberated that from Eric's P2 VGA text driver. I'm sure it's just a bitmap but I don't know how the glyphs are oriented compared to the 5x8 font.I can look into this today, as I'm working on graphics-related things anyway.
EDIT: Sorry, that was Mark_T that provided that font. I was thinking of something else.
Don't spend too much time on it. I have a 8x12 font from another project which might fit better, anyway.
That shouldn't apply, I think. The PutChar() function does call plot(9 for every pixel so it should work with almost any size, even really odd ones.
The 5x8 font is oriented in the Y direction per byte so I have to flip my 8x12 font over by exchanging X and Y in the PutChar() function, right?
Oh, that's in reference to coordinates for scrolling setup in the hardware-accelerated scrolling demo only. The font rendering is still limited, but not in the same way. It just uses some calculations that are hardcoded for 8 pixel-high fonts (like finding the offset in the font table - see
putchar()
in graphics.common.spin2h)I haven't found another 8x12 or 8x16 font to try yet, but in the meantime, I've expanded the text scaling capability with experimental independent x,y scaling. Try updating p2-standard-library and loading up SSD130X-Demo.spin2. Change the params in the call to
font_scl()
to scale up:The text will have a bit of a "bold" look to it since it's just using filled boxes for scaled up pixels. See if it works for you.
As far as swapping the x,y coordinates for fonts oriented 90deg, I'm not sure...I haven't tried it It sounds like it'd work, but I'm not really sure...
Okay I just updated
graphics.common.spin2h
; update p2-spin-standard-library to use.-Dputchar=putchar_0deg
at build time, or-Dputchar=putchar_90deg
for fonts like the 5x8 font.-DFNT_GLYPH_REV
for reverse order (applies to both putchar() variants)TERMINAL
symbol for use withchar_attrs()
to turn control code processing on or off during run-time (e.g.,oled.char_attrs(oled.TERMINAL)
to turn it on); with this bit set, a few of the chars like LF, CR will move the cursor, otherwise it'll just show the glyph. Note this behavior was essentially hardcoded on before all these changes, so any apps that were expecting it before would need the char_attrs() call added now. So things likeoled.printf(@"line1\n\rline2")
would need it turned on for the CR, LF to actually go to the next line.I'm not sure I'll keep this the way it's implemented right now forever. I'm not 100% crazy about making the putchar() variants switchable at build-time vs run-time, though I guess it really depends on the application and the font files whether more than one type would be needed. I didn't want to overload the existing putchar any more than it already is though. Will figure out a better way some time in the future
For those fonts in that github repo I referenced above, or similar ones, you can use them in spin2 like:
Just tried the rest of the fonts in that epto repository. They're all the same rotation, so all work with the following defined on the flexspin command line:
-Dputchar=putchar_0deg
Thanks a lot, Jesse. It works. I just noticed that the keywords
are defined but not supported. So I'd suggest to add this to the graphics.common.spin2h:
Hmm, if I compile with "-g" I get an error message that stdio.h redefines putchar. Is there a way to declare that the OLED display is not the "standard terminal"? I fear I have to cut and paste the functions I use from your library to my own version and rename them. It seems to be "too clever" and re-uses as much code as possible for any imaginable scenario but that creates a conflict with serial debugging.
hmmm not sure what's going on there... I haven't used debugging features yet. That'd be an unfortunate side effect. 😕 Yes i include a set of common terminal i/o routines with every display or other similar driver so doing things like printf, str, etc works the same way.
@ersmith does flexspin insert a putchar() into spin2 code when debugging is enabled? I name my elemental char method 'putchar' in all of my drivers that have terminal output.
If plain -g is used (as opposed to -gbrk) then Spin2 DEBUG() calls are turned into calls to the C printf() function. That may be pulling in putchar() indirectly. If you're on a P2, it's probably best to use -gbrk debugging, both to avoid this and to allow debugging of assembly language.
Never mind. I think I have to modify Jesse's library anyway. I'll need switching between different fonts at runtime. I'll use the biggger 8x12 font for the normal status display so it's clearly visible and a smaller 5x8 font for diagnosis so that as much information as possible fits on the small screen. I simply rename the putchar function.
I think that there was a problem with -gbrk when Spin2 and C are used at the same time. I'll check it out.
Ok, I have both fonts selectable at run time and with debugging enabled.
The 8x12 font looks a bit ugly as it is a downscaled version of the P1 ROM font and suffers from aliasing. I'll take care of that later if a customer pays me for implementing a logo...
What would you think about something like this added to the graphics library:
The font files would have a small setup block added to them to replace the constants in there now like:
So then in the application, you could switch between different fonts at runtime with something like:
I haven't added it to the library yet but have tested the above and it works.
EDIT:
A little more info:
set_font_rotation_0deg()
andset_font_rotation_90deg()
actually make putchar a pointer to the putchar_0deg and putchar_90deg functions (so putchar is now a VAR long in the graphics lib)I just had about the same idea. I've implemented it in almost the same way but not that elegant...
PS: I've also modified position() aka pos_xy() to take pixel coordinates instead of character columns/rows so that position is independent of the current font setting.
Hahah I'd forgotten I intended to do that at some point, too... https://github.com/avsa242/libgfxbitmap-spin/issues/8
So many projects, so little time!
Okay runtime-putchar switching (including an available BYOP - bring your own putchar; call
set_putchar()
with a pointer to your own putchar function), and pixel-level positioning are implemented now (only on P2 atm). The position code has to explicitly be switched on at build-time with-DFNT_POS_NOGRID
- I didn't want to break existing code that assumes grid positioning (I have a lot of it).Cheers
Haha, I've just found a bug that is both funny and very evil. Although I'm sure it was coded with the best intentions it behaved like it came directly from hell.
This works perfectly as long as it's executed on the P1 (where addresses are always below 32kB) or if your code size is small on the P2. Add some code and sooner or later it goes boom. addr becomes >32k and _ptr_drawbuffer is not set and remains ==0 resulting in memory being overwritten as soon as any display function is called.
This took me several days to debug. I've commented out some lines of my code and the display immediately started working, again. No crashes and messed up results. So I blamed the functions I've commented out and searched for bugs but, of course, with no luck. Even rearranging function calls caused the bug to randomly appear and disappear because it influenced how the linker put the compiled code and also the buffer into different memory regions.
I have great respect for Jesse's work. The library is written very cleanly with as much code shared and reused between different drivers and modules. But I also have some critizism here. IMHO, functions should serve a single purpose only, if possible. Overloading them for dual purpose has no real advantage. I think, two seperate functions set_address() and get_address() should be better and haven't caused unnecessary errors.
@ManAtWork
Hah! I thought I'd written the spin2 version of that with enough headroom for the P2's 512k.
Criticism taken and considered...this had crossed my mind in the past, I think, but I hadn't made a note of it anywhere to revisit, so forgot about it. I think I just wrote it like that originally out of habit, because so many of my drivers for devices operate in a read-modify-write fashion where I allowed a range of valid values for setting and anything invalid would just return the current setting instead (for most of the sensors or other devices that in order to modify one bit in a register without clobbering other unrelated bits in the same register). In this case however I agree there's no clear advantage.
Sorry for all the grief this caused and thanks for the complements!
Cheers
One more question: I'm looking for a way to display small pictures, e.g. symbols that are larger than text characters, say 32x32 pixels. There's a function bitmap() in graphics.common.spin2 but I don't know if it's suitable. As it uses the same offset for both source and destination bitmap I think it expects both to have the same size which is not true in my case. It also supports only X coordinates divisible by 8 as it only copies whole bytes.
I could build a special symbol font that has only a few characters with larger size. The putch() functions draws them pixel by pixel so there are no restrictions about the coordinates. It's not very efficient but as I don't need moving sprites but only <1fps update rate I don't care much.
I think this should be possible. When I wrote
bitmap()
I think I was trying to get it to be faster, hence not re-usingplot()
, but this is probably another fundamental design flaw: it probably should useplot()
, just so it behaves essentially the same way no matter the graphics driver (that's what I'd intended when writing this thing originally, anyway). I'll start with code from theputchar()
methods since as you hinted, that's got pixel-level freedom, and is a bitmap drawer already.Hmm, I looked at the code and it seems that putchar_0deg(ch) was not designed to work with fonts wider than 8 pixels.
Given the complexity of handling all special cases (bytes per pixel format, reversed vs. normal bit order, 0 vs. 90° orientation) I think it's easier if I write my own copy_bitmap() function that directly copies the bytes into the display buffer.
Okay, there's an experimental
bitmap_1bpp()
added to just the spin2 library. You can try it from the latest at https://github.com/avsa242/libgfxbitmap-spinWhen testing this, I ran into a similar issue to the font orientation one I ran into when making those putchar() changes: the bitmap can be horizontal or vertical in its source form (bits within each bitmap image byte can represent different columns, or different rows of the image). I have a monochrome bitmap of the Propeller Beanie hat in the p2-spin-standard-library repo, and it wasn't rendering correctly with this new code (I borrowed it from Adafruit). I tried creating another bitmap and converting it to a byte array using LCD Assistant (https://en.radzio.dxp.pl/bitmap_converter/) and found with Adafruit's code, it needs to be oriented in the 'Horizontal' byte orientation.
After doing that, the bitmap seems to display correctly and I can move it to various parts of the screen.
Try it and see if it works for what you're doing.
Cheers
I also need some code for this. This looks like a good place to start!
Wait... I thought this was for the Parallax OLED module... Seems similar, but Parallax one has SSD1331 ... Probably almost the same though, I imagine...
https://github.com/avsa242/ssd1331-spin - might be quite a bit different at the low-level...I think I remember that chip being a bit of an oddball, with the accelerated primitives and faster command interface, but the driver is written with as much the same interface as the 1306 driver, so practically speaking, no different.