Skip to content

Commit

Permalink
v0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
bjaan committed Dec 19, 2022
1 parent d0fa1c6 commit c345bdc
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 86 deletions.
16 changes: 16 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"files.associations": {
"array": "cpp",
"*.tcc": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"string_view": "cpp",
"memory": "cpp",
"random": "cpp",
"initializer_list": "cpp",
"ranges": "cpp"
}
}
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ In Action: See [Video](docs/new_display_in_action.mp4)
## 1. Bill of Materials

- 1 x Raspberry Pi Pico
- 1 x LCD Display ILI9488 5.0" module (variant without touchscreen) https://www.aliexpress.com/item/1005003033844928.html
- 17 x Wires to solder together boards (5 cm long white ones in my case: very easy to solder as they were pre-tinned https://nl.aliexpress.com/item/32822880152.html)
- 1 x LCD Display 320X480 ILI9488 4.0" module (variant without touchscreen) https://www.aliexpress.com/item/1005003033844928.html
- 17 x Wires to solder together boards (5 cm long white ones in my case: very easy to solder as they were pre-tinned https://www.aliexpress.com/item/32822880152.html)
- 9 x Jumper wires to connect headers on the display and to be soldered on the Raspberry Pi Pico like these https://www.amazon.de/-/en/gp/product/B07KFQ6483
- 1 x piece of black of paper (can be printed) or black tape
- A bit of isolating tape (e.g. Kapton tape)
Expand Down Expand Up @@ -50,7 +50,7 @@ These instructions are for Windows 10 and will be similar for other OS's support

![Hardware plugged in](docs/hardware_plugged_in.png)

3. In _Visual Studio Code_, open the folder where have the project located
3. In _Visual Studio Code_, open the folder where have the project is located
4. _PlatformIO_ should now load and be selectable in the left-hand side, click on the alien-icon and then select _Build_ to build the firmware:

![Build Action](docs/platform_io_build.png)
Expand Down Expand Up @@ -141,7 +141,7 @@ Using the jumper wires connect the LCD module to Raspberry Pi Pico. Slide the f
|BL|BL|GP17|
|SDO|MISO|GP16|

Connect the _JUNO G LCD BOARD ASSY JPN 733402078_ to the main board again and apply power again and ensure that the new display is working. It will show the message _Roland JUNO-G LCD Emulator v0.4_ for a short while and it should start showing the actual display's contents.
Connect the _JUNO G LCD BOARD ASSY JPN 733402078_ to the main board again and apply power again and ensure that the new display is working. It will show the message _Roland JUNO-G LCD Emulator v0.5_ for a short while and it should start showing the actual display's contents.

![Disassembly](docs/first_in_action.png)

Expand Down Expand Up @@ -219,7 +219,7 @@ Disconnect cables from the boards as needed and reassemble the keyboard again.

## Notes

This project uses the Programmable I/O (PIO) feature on Raspberry Pi Pico instead of "bitbanging" to read the original display protocol.
This project uses the Programmed I/O (PIO) feature on Raspberry Pi Pico instead of "bitbanging" to read the original display protocol.

Contrary to bitbanging, PIO uses no CPU cycles. Here is an overview about PIO on the Pico https://blues.io/blog/raspberry-pi-pico-pio/.

Expand Down
162 changes: 81 additions & 81 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ uint tft_yoffset = 0;

volatile uint16_t buffer_cs1[12*123];
volatile uint16_t buffer_cs2[12*123];
volatile uint8_t back_buffer_cs1[12*123];
volatile uint8_t back_buffer_cs2[12*123];
volatile uint8_t back_buffer[240][96/8]; /* max X address register for CS1 and CS2 times max Y register */
volatile uint16_t pixel_x[240]; /* X location (pre-calculated) of actual pixel on new screen */
volatile uint16_t pixel_y[96]; /* Y location (pre-calculated) of actual pixel on new screen */

void fillscreenInterlaced(uint32_t bgcolor) {
tft.startWrite();
Expand All @@ -49,6 +50,23 @@ void fillscreenInterlaced(uint32_t bgcolor) {
tft.endWrite();
}

void drawPixels(uint8_t val, uint8_t xx, uint8_t yy) {
int16_t x = pixel_x[yy];
int8_t y = xx * 8;
//for (int b = 0; b < 8; b++ ) {
//if (((val >> b) & 0x1) == 1) tft.drawPixel(x, pixel_y[y+b], TFT_BLACK); else tft.drawPixel(x, pixel_y[y+b], TFT_WHITE);
//unrolled:
if ((val & 0x1) == 0x1) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++] , TFT_WHITE);
if ((val & 0x2) == 0x2) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++], TFT_WHITE);
if ((val & 0x4) == 0x4) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++], TFT_WHITE);
if ((val & 0x8) == 0x8) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++], TFT_WHITE);
if ((val & 0x10) == 0x10) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++], TFT_WHITE);
if ((val & 0x20) == 0x20) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++], TFT_WHITE);
if ((val & 0x40) == 0x40) tft.drawPixel(x, pixel_y[y++], TFT_BLACK); else tft.drawPixel(x, pixel_y[y++], TFT_WHITE);
if ((val & 0x80) == 0x80) tft.drawPixel(x, pixel_y[y ], TFT_BLACK); else tft.drawPixel(x, pixel_y[y ], TFT_WHITE);
//}
}

void setup()
{
analogReadResolution(12);
Expand All @@ -74,14 +92,24 @@ void setup()
tft.setTextSize(2);

tft.setTextDatum(TC_DATUM);
tft.drawString("Roland JUNO-G LCD Emulator v0.4", tft.width() /2, tft.height() / 2 - 20 );
tft.drawString("Roland JUNO-G LCD Emulator v0.5", tft.width() /2, tft.height() / 2 - 20 );
//tft.drawString("CPU_FREQ:" + String(rp2040.f_cpu()), tft.width() /2, tft.height() / 2 + 10 );
delay(500);
{ //precalculate new display pixel indices
for (uint8_t x = 0; x < 240; x++) {
pixel_x[x] = tft_xoffset + x * ZOOM_X;
}
for (uint8_t y = 0; y < 96; y++) {
pixel_y[y] = tft_yoffset + y * ZOOM_Y;
}
}
{ //initialize back buffer
for (uint8_t x = 0; x < 239; x++) {
for (uint8_t y = 0; y < 96/8; y++) {
back_buffer[x][y] = 255;
}
}
}
fillscreenInterlaced(TFT_WHITE);

//set back-buffers to full black
memset((void *) back_buffer_cs1, 255, sizeof back_buffer_cs1);
memset((void *) back_buffer_cs2, 255, sizeof back_buffer_cs2);
#endif

#ifdef DRAW_PINOUT
Expand All @@ -101,12 +129,10 @@ void setup()
pinMode(LED_BUILTIN, OUTPUT);
}

volatile int start_index_cs1 = 0;
volatile int start_index_cs2 = 0;
volatile uint8_t page_cs1 = 0;
volatile uint8_t page_cs2 = 0;
volatile uint8_t xx_cs1 = 0;
volatile uint8_t xx_cs2 = 0;
volatile uint8_t x_cs1 = 0; //X address register for CS1: 4 bit: values 0 up and to including 15: actually max. 12 as 12*8 = 96
volatile uint8_t x_cs2 = 0; //X address register for CS2: 4 bit: values 0 up and to including 15: actually max. 12 as 12*8 = 96
volatile uint8_t y_cs1 = 0; //Y address counter for CS1: 7 bit: values 0 up and to including 127: actually max. 120
volatile uint8_t y_cs2 = 120; //Y address counter for CS2: 7 bit: values 0 up and to including 127: actually max. 120. Here it is set, and later reset to 120, to avoid having to add an offset of 120 all the time

bool led_on = false;
long latest_packet_timestamp_cs1 = 0;
Expand All @@ -123,88 +149,62 @@ void loop()
for (uint i = 0; i < 123*12; i++)
{
{ //CS 1
uint8_t val = buffer_cs1[i] & 0xff;
uint8_t rs = (buffer_cs1[i] >> 9) & 1;
if (rs == 0) {
/*
buffer_cs1[i]
bits:
??????s? vvvvvvvv
s = R/S (INSTRUCTION/DATA REGISTER SELECTION) pin GP6
v = value bits (8 bits: values 0 up to and including 255)
*/
uint8_t val = buffer_cs1[i] & 0xff /* 00000000 11111111*/;
uint8_t rs = (buffer_cs1[i] /*000000s0 00000000*/ >> 9) /*00000000 0000000s*/ & 1;
if (rs == 0) { //INSTRUCTION REGISTER
#ifdef SHOWCMD
showcmd( cs, val );
#endif
if ((val >> 4) == 0xb) {
page_cs1 = val & 0xf;
xx_cs1 = 0;
}
} else if (rs == 1 ) {
if (xx_cs1 < 120) { // avoid overlap of the right area
if (back_buffer_cs1[i] != val) {
int16_t x = tft_xoffset + xx_cs1 * ZOOM_X;
int16_t y = tft_yoffset + (page_cs1 * 8) * ZOOM_Y;
//for (int b = 0; b < 8; b++ ) {
//if (((val >> b) & 0x1) == 1) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
//y+= ZOOM_Y;
//unrolled:
if ((val & 0x1) == 0x1) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x2) == 0x2) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x4) == 0x4) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x8) == 0x8) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x10) == 0x10) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x20) == 0x20) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x40) == 0x40) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x80) == 0x80) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
//}
if ((val & 0xf0 /*11110000*/) == 0xb0 /*1011000*/) { //Sets the X address at the X address register
x_cs1 = val & 0x0f /*00001111*/;
y_cs1 = 0;
}
} else if (rs == 1) { //DATA REGISTER
//writes data on JUNO_D0 to JUNO_D7 pins into display data RAM.
if (y_cs1 < 120) { // avoid overflow
if (back_buffer[y_cs1][x_cs1] != val) {
back_buffer[y_cs1][x_cs1] = val;
drawPixels(val, x_cs1, y_cs1);
}
xx_cs1++;
y_cs1++; //After the writing instruction, Y address is increased by 1 automatically.
}
}
back_buffer_cs1[i] = val;
}
}
{ //CS 2
/*
buffer_cs2[i]
bits:
??????s? vvvvvvvv
s = R/S (INSTRUCTION/DATA REGISTER SELECTION) pin GP6
v = value bits (8 bits: values 0 up to and including 255)
*/
uint8_t val = buffer_cs2[i] & 0xff;
uint8_t rs = (buffer_cs2[i] >> 9) & 1;
if (rs == 0) {
if (rs == 0) { //INSTRUCTION REGISTER
#ifdef SHOWCMD
showcmd( cs, val );
#endif
if ((val >> 4) == 0xb) {
page_cs2 = val & 0xf;
xx_cs2 = 0;
}
} else if (rs == 1) {
if (xx_cs2 < 120) { // avoid overlap of the right area
if (back_buffer_cs2[i] != val) {
int16_t x = tft_xoffset + xx_cs2 * ZOOM_X + 120 * ZOOM_X;
int16_t y = tft_yoffset + (page_cs2 * 8) * ZOOM_Y;
//for (int b = 0; b < 8; b++ ) {
//if (((val >> b) & 0x1) == 1) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
//y+= ZOOM_Y;
//unrolled:
if ((val & 0x1) == 0x1) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x2) == 0x2) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x4) == 0x4) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x8) == 0x8) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x10) == 0x10) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x20) == 0x20) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x40) == 0x40) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
y+= ZOOM_Y;
if ((val & 0x80) == 0x80) tft.drawPixel(x, y, TFT_BLACK); else tft.drawPixel(x, y, TFT_WHITE);
//}
if ((val & 0xf0 /*11110000*/) == 0xb0 /*1011000*/) { //Sets the X address at the X address register
x_cs2 = val & 0x0f /*00001111*/;
y_cs2 = 120; // 120 is left most pixel of the right hand part
}
} else if (rs == 1) { //DATA REGISTER
//writes data on JUNO_D0 to JUNO_D7 pins into display data RAM.
if (y_cs2 >= 120 && y_cs2 < 240) { // avoid overflow
if (back_buffer[y_cs2][x_cs2] != val) {
back_buffer[y_cs2][x_cs2] = val;
drawPixels(val, x_cs2, y_cs2);
}
xx_cs2++;
y_cs2++; //After the writing instruction, Y address is increased by 1 automatically.
}
}
back_buffer_cs2[i] = val;
}
}
tft.endWrite();
Expand Down

0 comments on commit c345bdc

Please sign in to comment.