Posted on sam. 17 septembre 2016 (last updated on jeu. 10 novembre 2022)

After a summer pause, I'm back on my HP34970A repair project. In the previous post in this series, I've started to reverse engineer the serial protocol between the CPU board and the front panel, and implement a prototype of replacement display.

OLED display

I bought the bigest OLED display I could find (at a reasonable price) that would fit in the front panel assembly. It's a blue 256x64 OLED display drived by a SSD1322 controler.

3"2 blue OLED module used as a replacement display for the HP34970A.

My first problem was to drive the display from my Nucleo board. I couldn't find graphic library that worked directly. I was already using the UniGraphic library with the small TFT display, so I decided to implement support for this OLED module in the library.

I spent quite some time to make this work for several reasons, among others:

  • I was first using my Nucleo F031K6 board; it's nice, it's small, but it only have 4k of RAM, so the malloc I as doing in my test code to allocate the "framebuffer" in which drawings are done before being tranferred to the display module... well let's say if I took time to check the return value of the malloc call (which one should always do, even on small embedded devices), I would have not lost a couple of hours, so to speak (tip: allocating 8k in a 4k RAM will not fit),
  • The SSD1322 controller is reasonnably well documented, but what's not documented is how it is used on the OLED display... In fact, once I could at least see some pixels on my display, I spent quite some time trying to control where my pixels are located on the module. There is not that much demo material available for this module besides a poorly written sample C code (in a .txt file!) for the 80C51 processor. What's hidden in this file (which I could not fing any where else) is the magic 0x1C value which must be given as first argument to the SET_COLUMN_ADDR "function" of the SSD1322.
  • I also lost some time trying to implement the support for this module as a class derived from UniGraphic's LCD class (like several other supported modules like the SSD1306), but that was a mistake, since this kind of OLED module is too different from both a TFT display or a monochrome LCD display. So I've reimplemented the class as a derived class of the GraphicDisplay. This work is far for being finished yet (and ready for a Pull Request to submit to the UniGrahic project), but at least, I can continue my work on the HP34970's display.
The replacement 3"2 blue OLED module for the HP34970A.

Improving the serial sniffer

Once I could draw pixels on my OLED module, I put this code in my existing prototype replacing the simple display code I used with the TFT module.

And one problem I already had with the previous "working" prototype became bigger: I was missing many characters on the serial bus between the CPU and the front panel.

My first implementation of the protocol sniffing was very tedious and poorly written. It was not reading at all the traffic on the Tx line (ie the key strokes), so I was never sure if a 0x66 was the beginning of a transmission or some payload for which I missed the beginning.

Using the interrupt-based (asynchronous) BufferedSerial implementation did not help that much.

So I rewrote this part of the using 2 serial ports, using only the Rx line on both of them, one plugged on the Rx line of the serial communication, and the other on the Tx line of this comm. Doing so I can sniff the whole communication between the CPU and the display panel and make a way better protocol interpreter.

The result was way better than before, and now I was able to detect the protocol interpretation errors, but still, I was missing many parts of the transmission.

I ended up to figure that the culprit was my SSD1322::copy_to_lcd() method: it takes almost 20ms to transmit the whole display content the module. And with the way my code was written, even with interrupt driven serial handlers, these interrupt were not captured during the execution of this copy routine.

At this point, I've started to read a bit more the asynchronous and event driven APIs of the mbed platform, but could not clearly see a pattern to prevent this long "frozen" periods during which I was unable to capture serial communications.

I was almost ready to use 2 microcontrollers for the job: one to sniff and interpret the serial communication and one to drive the display.

Improvment of the POC based on the Nucleo F446RE and using a blue OLED module.

Improving the tasks

But I've discovered a few days ago that the RTOS project is now part of the mbed platform, allowing to use threads instead of pure event driven code. So I tried to put the copy_to_lcd routine executions in a dedicated thread, and bingo, I've got now something that begins to work enough to make it usable.

What's next

There is still a lot of work to do:

  • improve the display reactivity: I'll probably try to implement partial copy of the display instead of sending the whole "framebuffer" each time something is changed on the display (the SSD1322 allows to specify a "window" in which to send data),
  • improve overall display organisation and find the missing flags (I still have not found the flag values for OC, MIN, MAX, AVG, ONCE, AUTO and MEM),
  • try to install the display module and the Nucleo board in the front panel assembly (I don't want to go for a specifically designed PCB, at least for now),
  • publish the code.