Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video output via I2C OLED module #8

Open
alex-arknetworx opened this issue Jul 27, 2024 · 28 comments
Open

Video output via I2C OLED module #8

alex-arknetworx opened this issue Jul 27, 2024 · 28 comments

Comments

@alex-arknetworx
Copy link

Hi Evansm7,

Great work on bringing another emulated system to the RP2040! It's great to see the growing list like neo6502, pico-rv32ima and decstation2040!

I noticed you mentioned some video improvements you would like to make in the readme.md alluding to LCD support. I was wondering if you think it would be possible to write the mac framebuffer (or even a sub set of the pixel space for tiny panels) out to a SPI connected LCD module (e.g. to a ST7735 tft controller)?

If i were to experiment with this, is video.c a good place to start understanding where the mac framebuffer is being accessed?

@evansm7
Copy link
Owner

evansm7 commented Jul 28, 2024

Yo! Have a look at main.c, and the video setup. It sets the framebuffer scan-out from umac_ram + umac_get_fb_offset(), the address in the host of the framebuffer. Today that’s a constant, but is technically wrong as the Mac can change the FB base (via a VIA IO bit), but applications in general don’t seem to ever do this. Anyway, from that base you have a plain 512x342x1 buffer you could output to another LCD! Looking forward to hearing how your experiments progress 🙂

@alex-arknetworx
Copy link
Author

Hi Evansm7,

Appreciate your comments and the heads up about the variable nature of the Mac FB index. I will keep you posted on any developments :)

@alex-arknetworx
Copy link
Author

Hi Evansm7,

I followed your advice but decided to downgrade from SPI and use a 128x64 monochrome OLED via I2C. Based on the SSD1306 driver and using GitHub repository daschr/pico-ssd1306 I added minimal code and have successfully managed to get the System 3 desktop to display on the OLED and can click on the menus and move the mouse around with excellent refresh rate without changing any timing in your code.

For reference I am using a WaveShare pico-zero board with 2Mb. See attached photo as proof of concept, which was hard to capture as the camera refresh rate can see a moving black line as the OLED updates. To the eye there is no black line.

My next development would be to pan my 128x64 bit selection to keep the mouse centered so you can access the full System 3 screen. Also probably would like to remove the VGA as not required anymore :)

Happy to share the modified source files with you directly.

pico_mac_oled

@alex-arknetworx alex-arknetworx changed the title Video output via SPI LCD module Video output via I2C OLED module Aug 17, 2024
@evansm7
Copy link
Owner

evansm7 commented Aug 21, 2024

That’s really cool! (I like those screens a lot.) well done, hope you’re pushing to a repo somewhere? Fork away!
If you remove the VGA it’ll be slightly faster (maybe imperceptible but can’t hurt). I like the follow-mouse thing; are there Mac global variables with the pointer coordinates?

@alex-arknetworx
Copy link
Author

Hi Evansm7, yes, you have correctly predicted that using umac_cursor_x and _y will introduce pointer drift :) however the result is still pretty cool for a quick first go and i can pan around the screen and see and click on everything as long as the pointer doesn't hit the screen bounds as the relative movement still coming from HID then causes drift. Something for me to solve, either by trying to find the memory location of the System OS pointer or maybe something fancy like right mouse click to pan the screen without passing HID updates to umac, in order to re-center it.

I really need to spend time in GitHub and sort out all my forking: all my edits are on local clones. None the less, for everyone interested in trying an OLED or that want to get the project running on a WaveShare RP2040-Zero, here are my modified source files, which still include VGA support. You will also need to put ssd1306.c in /src/ and ssd1306.h and font.h in /include/ from GitHub repository daschr/pico-ssd1306

main.c.txt
hw.h.txt
CMakeLists.txt

@alex-arknetworx
Copy link
Author

Now that the OLED is usable, how about a new app that targets a small screen for Pico-Mac? I managed to get MacASM V1.2 to run on System 1 on Pico-Mac. It won't compile assembly on the pico because the disk is locked / write-protected but writing some assembly on the disk image on mini-vmac, compiling it there and then transferring the img back to the pico build.... a new application!
pico_mac_firstNewApp

@all2coolnick
Copy link

all2coolnick commented Sep 18, 2024

Now that the OLED is usable, how about a new app that targets a small screen for Pico-Mac? I managed to get MacASM V1.2 to run on System 1 on Pico-Mac. It won't compile assembly on the pico because the disk is locked / write-protected but writing some assembly on the disk image on mini-vmac, compiling it there and then transferring the img back to the pico build.... a new application! pico_mac_firstNewApp

Hi alex-arknetworx
I've been trying to get pico-mac running on a Pico Zero and am having no joy. You have managed it so I wondered if you could tell me if you had to do anything other than change the GPIO assignments in hw.h.
I've defined them as follows (though I'm not using GPIO_VID_CLK as I did not need to on my normal Pico):
#define GPIO_VID_DATA 6
#define GPIO_VID_VS 7
#define GPIO_VID_CLK 9
#define GPIO_VID_HS 8

I'm getting nothing on the VGA monitor after re-building and flashing.
I also re-assinged the LED pin so I could at least see the flashing LED
#define GPIO_LED_PIN 14
But that did not flash. I know all the GPIOs I'm using are functioning as I have tested each of them with an external LED and simple Micropython blink program.
Did you use the extended 208K memory in the umac and pico-mac builds? I did but can't see why that would matter.

@alex-arknetworx
Copy link
Author

alex-arknetworx commented Sep 18, 2024

Hi all2coolnick,

I've had a look back at my 'VGA Only' build and I think the only modifications I made was to the hw.h GPIO assignments. However I used GPIO 10 through 13 as these were along the bottom edge of the board. In later revisions I did change the LED GPIO to 16 as per the zero pin assignment guide (picture attached), but actually it still doesn't blink maybe because it might need to be set up as a PWM.

I would have a go with your own build using:

#define GPIO_VID_DATA 10
#define GPIO_VID_VS 11
#define GPIO_VID_CLK 12
#define GPIO_VID_HS 13

For reference I was using the original 128K memory version of the repo along with a Philips 241V LCD monitor via VGA and the correct resistor network. I can send my UF2 file from this build which you could try with VGA wired as above (10 thru 13) to see if its a build problem or a hardware problem. Also my board has part number RP2040-Zero SKU 20187 with 2Mb flash. If you still have problems let me know and I can also send you my whole source directory.

ce08607-rp2040-zero-pinout_3130485a4f6

@all2coolnick
Copy link

Thanks for the reply alex-arknetworx and for the download. I’ll give that a try. From what I’ve read, using the onboard RGB led is a little complicated and needs the Neopixel library.

@all2coolnick
Copy link

I’ve just seen Matt’s reply in another issue thread saying that for the VGA lines “the pins must be contiguous and in ascending order of data, VS, CLK, HS.”
I bet that will be it as I swapped round the last two so that I had the three I needed on the edge pins which are easier to breadboard.

@alex-arknetworx
Copy link
Author

I’ve just seen Matt’s reply in another issue thread saying that for the VGA lines “the pins must be contiguous and in ascending order of data, VS, CLK, HS.” I bet that will be it as I swapped round the last two so that I had the three I needed on the edge pins which are easier to breadboard.

Good pickup there all2coolnick. Hope you've managed to get VGA going on your Zero board.

@all2coolnick
Copy link

FINALLY got pico-mac working on the Pico Zero. Turned out the Zero I had was duff (serves me right for buying an unbranded Waveshare knockoff). I've also got my hands on a couple of the 2.0" 480x640 display modules I want to use. It's the smallest I can find that can show the full 512x342 Mac image (Matt, you may remember I X'd you about wanting to output to a small LCD to build a miniature Mac 128K).
It's a DXWY D200N2409V0 2.0" 480x640 TFT using the ST7701S driver (https://www.aliexpress.com/item/1005007523031911.html). It supports RGB 565 parallel input so once it's in that mode, it should be quite straight forward but I believe it needs initialising via SPI and here I've hit a wall due to my lack of C knowledge. I have hunted high and low but can't find an existing driver for that driver written in C that I can compile for the rp2040.
DXWY sent me the data sheets for their module and the ST7701S driver, plus an LCD init string text file which I'm not sure what to do with (all attached).
I think I can probably figure it out if I'm pushed in the right direction and find a thread to pull on, so hoping one of you might be able to help .
Thanks
ST7701S.pdf
D200N2409V0 SPEC.pdf
2.0VGA LCD init.txt

@alex-arknetworx
Copy link
Author

Hi @all2coolnick great you sorted out the zero board.

In regards to LCD output, when i posted this thread i was initially going to use a Waveshare color 1.8" 128x160 SPI connected LCD, however I quickly realized I could achieve what i wanted far easier and cheaper and just used an OLED over I2C - this was in keeping with @evansm7 minimal cost approach :)

However: my color LCD has a ST7735 controller, which uses SPI for initial config and ALSO to cyclically receive a bytestream containing the latest frame. Having a quick look at your specs, it seems there are multiple ways to get the FB data into it: MIPI, 16-bit RGB (TTL) parallel, but these introduce unique issues like either having to PIO a MIPI interface (is this even doable without a MIPI PHY) or clock out the FB bytes onto the individual RGB bit lines ... are there even enough I/O's on a Zero, possibly if you tie a bunch together to sort of create a 1bit to 16bit conversion, definitely sounds PIO and more a question for @evansm7

If the ST7701S can receive FB data via SPI your in luck without having to add additional hardware or make major source code changes. I have had success with using GitHub repo /bablokb/pico-st7735 to get a Zero to drive my Waveshare LCD. Have a look there and you might be able to to work out how to get the SPI up and running at least.

As far as code changes, i had to introduce my own code for OLED support because VGA is totally different to what you need when sending data over a serial bus. If you look at my main.c posted above in previous comments this is my basic structure for getting my OLED to work - you will have to write a similar program but for SPI.

#include the additional headers for the OLED library

edit static void io_init() to also init io pins for I2C

create void oledInit() and call this from main() to create an SSD1306 object pointed at the I2C port

create void writeFB(uint8_t *fb_in) and call this from main() and pass in the pointer to the umac frame buffer - this is where all the magic happens, and you can think of this as some sort of display driver. Ignoring what i'm doing to only select part of the framebuffer dependent on mouse movements, every call of this function clears the entire OLED and then conditionally sets each pixel if it is set in the umac Framebuffer. I also have to reverse the order as technically the Macintosh FB is big endian and I need little endian logic to work hence the funny 7 - bit % 8. To work with a color panel you would have to expand every 1 bit from the Macintosh FB out to suit a color RGB565 format, a simple copy would not work as your going from 1 bit to 16bit.

Hope this gives you some direction to head in.

DustinZrm added a commit to DustinZrm/pico-mac that referenced this issue Nov 4, 2024
@all2coolnick
Copy link

all2coolnick commented Nov 5, 2024 via email

@evansm7 evansm7 reopened this Dec 13, 2024
@evansm7
Copy link
Owner

evansm7 commented Dec 13, 2024

Another thread I have taken WAY too long to respond to, sorry. You've both got a few ideas going on so not much for me to add, but there was a question about PIO. (And MIPI -- totally concur that may be a fun side project for the Pico but probably good to avoid!) So for PIO video and colour LCDs, expanding 1BPP framebuffer "automatically" to N bits, it's something I'd like to try out. The DMA stuff doesn't have an inbuilt way, AFAIK, to do a lookup table on the data passing through (shame), but could the interpolator do it?
I've an LCD arriving soon and hope to have a play; I'll implement the DE line which should help other parallel LCD projects. At first I'll do as @all2coolnick is, and band together all the RGB pins to the one data output, but then will try wiring say 9 bits (3/channel) and doing a LUT. I want to make a setup for other experiments (in colour), but then usable with pico-mac too ideally (in mono).

Anyway reopening this issue (dunno how it got closed) as excellent things going on. :). @alex-arknetworx I was picking through ROM disassembly and, need to check notes, but the pointer-move task does update pointer coordinates somewhere. I can try to get you some Mac RAM addresses for the X/Y variables for your screen-follows-pointer idea IYL.

@evansm7
Copy link
Owner

evansm7 commented Dec 13, 2024

Thought you might find this funny: I'm hacking on changing the Mac ROM/OS screen resolution (code to umac soon...) in order to get 640x480 out of it. (See other issues for screenshot.) But I also tried some ridiculous small resolutions too, and it actually works! It's not exactly usable (a panning window onto a larger desktop is much better IMHO) but take a look at this madness at 320x240:

Screenshot

@alex-arknetworx
Copy link
Author

Hi @evansm7, the 320x240 frame buffer looks awesome :) shows just how powerful the early MAC ROM + System really was. Apple could have launched the iPhone or iPod years earlier if only they had a chip like the RP2040.

Yes please, if you come across the Mac RAM addresses for the X/Y variables, i would be interested in updating my OLED code to give a better scrolling experience.

On the topic of tiny madness: a work colleague threw together a 3D printed case for my OLED+ZERO+SDCARD combination. Happy to share the STL or STEP file on request.

pico_mac_case

@evansm7
Copy link
Owner

evansm7 commented Dec 30, 2024

@alex-arknetworx Hola! Mac mem offsets 0x82c and 0x82e seem to be them: big-endian uint16 cursor coordinates for Y and X (sic) respectively.

That 3DP case is really cool! :)

@all2coolnick
Copy link

Great to see the project is still evolving. Got sidetracked over Christmas with sickness and my new 3D printer (the former giving me lots of time to play with the latter). I’m pretty much done creating a Macintosh shell that will fit my 2” LCD and a WaveShare Pico Zero.
Now I’ve just got to get back to getting it working!
IMG_2631
IMG_2629

@all2coolnick
Copy link

Finally got picomac running perfectly today on my 2” LCD via RGB565 interface albeit at a slightly reduced screen width of 480px. This is using the WaveShare Pico Zero.

IMG_2737

@evansm7
Copy link
Owner

evansm7 commented Jan 3, 2025

@all2coolnick The 3D printing looks excellent! That's going to be a really cool item. :). I'm gonna reply (with congrats) to the LCD stuff in the other issue/thread re FB rotation, but well done! 👏

@alex-arknetworx
Copy link
Author

Finally got picomac running perfectly today on my 2” LCD via RGB565 interface albeit at a slightly reduced screen width of 480px. This is using the WaveShare Pico Zero.

IMG_2737

Great work @all2coolnick the 3D printed case and your port to color LCD look fantastic! Is your solution more hardware or software, did you have to modify much of the code?

@alex-arknetworx
Copy link
Author

@alex-arknetworx Hola! Mac mem offsets 0x82c and 0x82e seem to be them: big-endian uint16 cursor coordinates for Y and X (sic) respectively.

That 3DP case is really cool! :)

Thanks @evansm7 when I get a chance I will have a go at changing my X and Y references from HID to MEM and let you know how i go.

Also attached in the zip are the .stl and .step for the tiny OLED case suitable for waveshare RP2040-Zero, an OLED and I even squeezed an SD card breakout floating in the back.

mini_mac_case.zip

@all2coolnick
Copy link

Thanks @alex-arknetworx
It’s pretty much all software and to be honest, I didn’t have to do much. Once I had the SPI initialisation commands from the panel manufacturer and dusted off enough of my ancient C knowledge, it all came together. It’s mostly just additional video unit code.
I did have to modify the Mac ROM to output a 480px wide desktop rather than 512.
Once I’ve tidied up the additional code and put it in an include, I’ll post it.
I’m still working on the internal chassis for the Mac case to mount the components. Then I need to design a PCB to interconnect the LCD and Pico Zero because the breakout board I’m currently using is too wide for the case.

@all2coolnick
Copy link

all2coolnick commented Jan 25, 2025

I’ve been on a detour learning Blender and refining the case and internal supports but today I ran picomac with LCD in the case for the first time. I’m very happy with the result. This was with an FFC cable coming out the back of the case as I still need to finish designing the internal PCB.
Interestingly, I’m only driving 3 inputs tied together (the MSB of each of the RGB lines) from the video out pin and given how bright the backlight is on these, I’m not sure I need to drive any others. Certainly not more than the top 2 bits of each colour so I don’t think I’ll need buffers.

Image

Image

@evansm7
Copy link
Owner

evansm7 commented Jan 30, 2025

@all2coolnick That’s fantastic, well done!

@all2coolnick
Copy link

all2coolnick commented Jan 30, 2025 via email

@alex-arknetworx
Copy link
Author

alex-arknetworx commented Feb 23, 2025

@alex-arknetworx Hola! Mac mem offsets 0x82c and 0x82e seem to be them: big-endian uint16 cursor coordinates for Y and X (sic) respectively.

That 3DP case is really cool! :)

Hi @evansm7,

I've been meaning to give this a go and finally found some time to get it sorted. Those are indeed the Macintosh System pointer coordinates and using them has solved the drifting. Being able to effectively center the pointer while the frame moves behind makes the OLED so much more usable, and then when the pointer is within 64x32 of an edge, the mouse appears to move instead. I will fork your repo and put all my OLED patches together for others, however the current committed state has broken my loading from SD card, so I will sort that first. In the meantime, for others following this discussion, simply replace my old oledWriteFB routine found in my version of /src/main.c with this new version.

void oledWriteFB(uint8_t *fb_in)
{
ssd1306_clear(&disp);

uint16_t system_mouse_x, system_mouse_y, system_cursor_x, system_cursor_y;

/* read Macintosh System RAM for high and low bytes and combine into 16 bit big endian coordinate */ 
system_mouse_x = ((uint16_t)umac_ram[0x82F] << 8) | umac_ram[0x82E];
system_mouse_y = ((uint16_t)umac_ram[0x82D] << 8) | umac_ram[0x82C];

/* convert 16 bit big endian coordinate to little endian */
/* add an offset so pointer will remain in the centre of the OLED unless near to the edges of a 128x64 frame */
system_cursor_x = ((system_mouse_x >> 8) | (system_mouse_x << 8)) - 64;
system_cursor_y = ((system_mouse_y >> 8) | (system_mouse_y << 8)) - 32;

int frame_pos_x, frame_pos_y;

for (int h = 0; h < 63; h++)
{
	for (int w = 0; w < 127; w++)
	{
		if (( system_cursor_x >= 0 ) && ( system_cursor_x <= (512 - 128)))
		{
			/* nice 1 pixel screen scrolling */
			frame_pos_x = system_cursor_x / 1;
		}
		if (( system_cursor_y >= 0 ) && ( system_cursor_y <= (342 - 64)))
		{
			/* nice 1 pixel screen scrolling */
			frame_pos_y = system_cursor_y / 1;
		}
			/* nice 1 pixel screen scrolling */
		int bit = ((h + (1 * frame_pos_y)) * 512) + (w + (1 * frame_pos_x));
		if (((fb_in[bit / 8] >> (7 - bit % 8)) & 1) == 0)
		{
			ssd1306_draw_pixel(&disp, w, h);
		}
	}
}
ssd1306_show(&disp);

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants