CuVoodoo STM32F1 firmware template
busvoodoo_oled.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
21 /* standard libraries */
22 #include <stdint.h> // standard integer types
23 #include <stdbool.h> // boolean type
24 #include <string.h> // string utilities
25 
26 /* own libraries */
27 #include "global.h" // global utilities
28 #include "busvoodoo_oled.h" // own definitions
29 #include "oled_ssd1306.h" // OLED display utilities
30 #include "font.h" // font glyphs
31 
33 static bool busvoodoo_oled_present = false;
34 
36 static uint8_t busvoodoo_oled_display[128*8] = {0};
37 
41 static const uint8_t bit_order_switch_lut[256] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, };
42 
44 {
45 
46  // setup SSD1306 OLED display
47  busvoodoo_oled_clear(); // clean display buffer
48  busvoodoo_oled_present = oled_ssd1306_setup(); // setup OLED display
50 #if DEBUG
51  oled_ssd1306_test(); // test OLED display
52 #endif
53  busvoodoo_oled_update(); // send display buffer
54  };
55 }
56 
58 {
59  // write all buffer to 0
60  for (uint16_t i=0; i<LENGTH(busvoodoo_oled_display); i++) {
62  }
63 }
64 
65 #include "print.h"
66 void busvoodoo_oled_text_pos(uint8_t column, uint8_t row, enum font_name font_name, const char *text)
67 {
68  // sanity checks
69  if (column>=128) {
70  return;
71  }
72  if (row>=64) {
73  return;
74  }
75  if (font_name>=FONT_MAX) {
76  return;
77  }
78  if (NULL==text) {
79  return;
80  }
81 
82  const struct font_s *font = &fonts[font_name]; // get selected font
83  while (*text && column<128) {
84  char c = *text;
85  if (c>=' ' && c<' '+FONT_GLYPH_NUMBERS) {
86  for (uint8_t i=0; i<font->width; i++) { // draw glyph from left to right
87  uint8_t col = column+i; // calculate destination column position
88  if (col>=128) {
89  break; // end of screen reached
90  }
91  uint16_t glyph_column = font->glyphs[font->width*(c-' ')+i]; // get glyph column to draw
92  // draw bottom part of glyph
93  uint16_t pixel_byte_row = 128*((row/8)-0)+col;
94  uint8_t glyph_byte_row = (glyph_column<<(7-(row%8)))>>0;
95  glyph_byte_row = bit_order_switch_lut[glyph_byte_row];
96  busvoodoo_oled_display[pixel_byte_row] |= glyph_byte_row;
97  // draw middle part of glyph
98  if (row>=8 && font->height>8-(row%8)) {
99  pixel_byte_row -= 128;
100  glyph_byte_row = (glyph_column<<(7-(row%8)))>>8;
101  glyph_byte_row = bit_order_switch_lut[glyph_byte_row];
102  busvoodoo_oled_display[pixel_byte_row] |= glyph_byte_row;
103  }
104  // draw top part of glyph
105  if (row>=16 && font->height>8+(row%8)) {
106  pixel_byte_row -= 128;
107  glyph_byte_row = ((uint32_t)glyph_column<<(7-(row%8)))>>16;
108  glyph_byte_row = bit_order_switch_lut[glyph_byte_row];
109  busvoodoo_oled_display[pixel_byte_row] |= glyph_byte_row;
110  }
111  }
112  }
113  text++; // go to next character
114  column += font->width+1;
115  }
116 }
117 
118 void busvoodoo_oled_text_left(char* text)
119 {
120  // clear top (yellow) display part
121  for (uint16_t i=0; i<128*2; i++) {
122  busvoodoo_oled_display[i] = 0;
123  }
124 
125  // verify input
126  if (NULL==text) {
127  return;
128  }
129 
130  busvoodoo_oled_text_pos(1, 14, FONT_KING14, text); // draw text on the left of top line
131 }
132 
134 {
135  // verify input
136  if (NULL==text) {
137  return;
138  }
139 
140  // calculate column on which to start drawing
141  uint8_t column = 0;
142  if ((fonts[FONT_KING14].width+1)*strlen(text)<128) {
143  column = 128-(fonts[FONT_KING14].width+1)*strlen(text);
144  }
145  busvoodoo_oled_text_pos(column, 14, FONT_KING14, text); // draw text on the right of the top line
146 }
147 
148 void busvoodoo_oled_text_pinout(const char* pins[10], bool io_connector)
149 {
150  // clear bottom (blue) display part
151  for (uint16_t i=128*2; i<LENGTH(busvoodoo_oled_display); i++) {
152  busvoodoo_oled_display[i] = 0;
153  }
154 
155  // check input
156  if (NULL==pins) {
157  return;
158  }
159 
160  // draw outline
161  if (io_connector) {
162  // top line
163  for (uint8_t i=0; i<2+2+24*2; i++) {
164  busvoodoo_oled_display[128*2+i] |= 0x03; // set left top two pixels
165  busvoodoo_oled_display[128*2+2+2+24*3+i] |= 0x03; // set right top two pixels
166  }
167  // left and right lines
168  for (uint8_t page=2; page<8; page++) {
169  busvoodoo_oled_display[128*page+0] |= 0xff; // set left two pixels
170  busvoodoo_oled_display[128*page+1] |= 0xff; // set left two pixels
171  busvoodoo_oled_display[128*(page+1)-2] |= 0xff; // set right two pixels
172  busvoodoo_oled_display[128*(page+1)-1] |= 0xff; // set right two pixels
173  }
174  // bottom line
175  for (uint16_t i=128*7; i<128*8; i++) {
176  busvoodoo_oled_display[i] |= 0xc0; // set bottom two pixels
177  }
178  } else {
179  // middle line
180  for (uint8_t i=0; i<2+2+24*2; i++) {
181  busvoodoo_oled_display[128*4+i] |= 0x80; // set left bottom pixel
182  busvoodoo_oled_display[128*4+2+2+24*3+i] |= 0x80; // set right bottom pixel
183  busvoodoo_oled_display[128*5+i] |= 0x01; // set left top pixel
184  busvoodoo_oled_display[128*5+2+2+24*3+i] |= 0x01; // set right top pixel
185  }
186  // left and right lines
187  for (uint8_t page=5; page<8; page++) {
188  busvoodoo_oled_display[128*page+0] |= 0xff; // set left two pixels
189  busvoodoo_oled_display[128*page+1] |= 0xff; // set left two pixels
190  busvoodoo_oled_display[128*(page+1)-2] |= 0xff; // set right two pixels
191  busvoodoo_oled_display[128*(page+1)-1] |= 0xff; // set right two pixels
192  }
193  }
194 
195  for (uint8_t pin=0; pin<10; pin++) { // go through pin names
196  if (NULL==pins[pin]) { // no text -> draw cross
197  uint16_t column = 2+2+24*(4-pin/2)+2; // the start column to start drawing (from left)
198  if (0==pin%2) {
199  for (uint8_t col=0; col<20; col++) {
200  uint32_t cross = (1<<col)|(1<<(19-col)); // the two dots of the cross
201  if (io_connector) {
202  busvoodoo_oled_display[128*2+column+col] |= cross<<3;
203  busvoodoo_oled_display[128*3+column+col] |= cross>>5;
204  busvoodoo_oled_display[128*4+column+col] |= cross>>13;
205  } else {
206  busvoodoo_oled_display[128*2+column+col] |= cross<<1;
207  busvoodoo_oled_display[128*3+column+col] |= cross>>7;
208  busvoodoo_oled_display[128*4+column+col] |= cross>>15;
209  }
210  }
211  } else {
212  for (uint8_t col=0; col<20; col++) {
213  uint32_t cross = (1<<col)|(1<<(19-col)); // the two dots of the cross
214  if (io_connector) {
215  busvoodoo_oled_display[128*5+column+col] |= cross<<1;
216  busvoodoo_oled_display[128*6+column+col] |= cross>>7;
217  busvoodoo_oled_display[128*7+column+col] |= cross>>15;
218  } else {
219  busvoodoo_oled_display[128*5+column+col] |= cross<<3;
220  busvoodoo_oled_display[128*6+column+col] |= cross>>5;
221  busvoodoo_oled_display[128*7+column+col] |= cross>>13;
222  }
223  }
224  }
225  } else if (0==strlen(pins[pin])) {
226  // leave blank
227  } else if (strlen(pins[pin])<4) {
228  // calculate start position on x-axis based on number of characters to put in 24x20 px box
229  uint8_t column = 2+2+24*(4-pin/2)+(24-(fonts[FONT_KING10].width+1)*strlen(pins[pin])-1)/2+1;
230  if (0==pin%2) {
231  if (io_connector) {
232  busvoodoo_oled_text_pos(column, 16+2+1+20/2+fonts[FONT_KING10].height/2, FONT_KING10, pins[pin]); // draw pin name
233  } else {
234  busvoodoo_oled_text_pos(column, 16+2+20/2+fonts[FONT_KING10].height/2, FONT_KING10, pins[pin]); // draw pin name
235  }
236  } else {
237  if (io_connector) {
238  busvoodoo_oled_text_pos(column, 16+2+1+20+1+1+20/2+fonts[FONT_KING10].height/2, FONT_KING10, pins[pin]); // draw pin name
239  } else {
240  busvoodoo_oled_text_pos(column, 16+2+20+1+2+1+20/2+fonts[FONT_KING10].height/2, FONT_KING10, pins[pin]); // draw pin name
241  }
242  }
243  } else if (4==strlen(pins[pin])) {
244  uint16_t column = 2+2+24*(4-pin/2); // start position on x-axis (for 4 characters on a 24 px box)
245  if (0==pin%2) {
246  if (io_connector) {
247  busvoodoo_oled_text_pos(column, 16+2+1+20/2+fonts[FONT_KING8].height/2, FONT_KING8, pins[pin]); // draw pin name
248  } else {
249  busvoodoo_oled_text_pos(column, 16+2+20/2+fonts[FONT_KING8].height/2, FONT_KING8, pins[pin]); // draw pin name
250  }
251  } else {
252  if (io_connector) {
253  busvoodoo_oled_text_pos(column, 16+2+1+20+1+1+20/2+fonts[FONT_KING8].height/2, FONT_KING8, pins[pin]); // draw pin name
254  } else {
255  busvoodoo_oled_text_pos(column, 16+2+20+1+2+1+20/2+fonts[FONT_KING8].height/2, FONT_KING8, pins[pin]); // draw pin name
256  }
257  }
258  } else {
259  uint16_t column = 2+2+24*(4-pin/2); // start position on x-axis (for 4 characters on a 24 px box)
260  char line_top[5] = {0, 0, 0, 0, 0};
261  for (uint8_t c=0; c<4 && c<strlen(pins[pin]); c++) {
262  line_top[c] = pins[pin][c];
263  }
264  char line_bottom[5] = {0, 0, 0, 0, 0};
265  for (uint8_t c=4; c<8 && c<strlen(pins[pin]); c++) {
266  line_bottom[c-4] = pins[pin][c];
267  }
268  if (0==pin%2) {
269  if (io_connector) {
270  busvoodoo_oled_text_pos(column, 16+2+1+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_top); // draw pin name
271  busvoodoo_oled_text_pos(column, 16+2+1+20/2+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_bottom); // draw pin name
272  } else {
273  busvoodoo_oled_text_pos(column, 16+2+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_top); // draw pin name
274  busvoodoo_oled_text_pos(column, 16+2+20/2+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_bottom); // draw pin name
275  }
276  } else {
277  if (io_connector) {
278  busvoodoo_oled_text_pos(column, 16+2+1+20+1+1+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_top); // draw pin name
279  busvoodoo_oled_text_pos(column, 16+2+1+20+1+1+20/2+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_bottom); // draw pin name
280  } else {
281  busvoodoo_oled_text_pos(column, 16+2+20+1+2+1+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_top); // draw pin name
282  busvoodoo_oled_text_pos(column, 16+2+20+1+2+1+1+20/2+20/4+fonts[FONT_KING8].height/2, FONT_KING8, line_bottom); // draw pin name
283  }
284  }
285  }
286  }
287 }
288 
290 {
291  if (busvoodoo_oled_present) { // only do something if the display is present
292  oled_ssd1306_off(); // switch display off to not see the update
294  oled_ssd1306_on(); // switch display back on
295  }
296 }
const struct font_s fonts[FONT_MAX]
list of all available fonts
Definition: font.c:321
static const uint8_t bit_order_switch_lut[256]
look-up table to swap the bit order in a byte
void busvoodoo_oled_text_pinout(const char *pins[10], bool io_connector)
draw pin names on bottom (blue) part in display buffer
void oled_ssd1306_on(void)
switch OLED display on
Definition: oled_ssd1306.c:74
void busvoodoo_oled_setup(void)
setup OLED display
void busvoodoo_oled_update(void)
update OLED display RAM with current display buffer
const uint16_t * glyphs
font glyphs: width glyph columns (left to right) times FONT_GLYPH_NUMBERS (MSb is glyph top pixel) ...
Definition: font.h:34
font_name
list of available font names
Definition: font.h:23
global definitions and methods (API)
void oled_ssd1306_off(void)
switch OLED display off
Definition: oled_ssd1306.c:83
#define FONT_GLYPH_NUMBERS
number of available glyphs (starting with &#39; &#39; and ending with &#39;~&#39;)
Definition: font.h:38
bool oled_ssd1306_setup(void)
setup OLED display
Definition: oled_ssd1306.c:43
number of fonts available
Definition: font.h:27
void oled_ssd1306_test(void)
test OLED display: switch entire screen on for a brief time
Definition: oled_ssd1306.c:92
custom 8x5 monospace font
Definition: font.h:24
static bool busvoodoo_oled_present
if the OLED display is present and setup
void oled_ssd1306_display(const uint8_t *display_data, uint16_t display_length)
send data to display to OLED display
Definition: oled_ssd1306.c:109
font structure containing all properties
Definition: font.h:31
uint8_t height
font height in pixels (max 16)
Definition: font.h:33
void busvoodoo_oled_text_left(char *text)
draw mode text on top (yellow) left side in display buffer
#define LENGTH(x)
get the length of an array
Definition: global.h:26
uint8_t width
font width in pixels
Definition: font.h:32
void busvoodoo_oled_text_right(char *text)
draw mode text on top (yellow) right side in display buffer
static uint8_t busvoodoo_oled_display[128 *8]
display pixel buffer
void busvoodoo_oled_clear(void)
clear display buffer
library to show BusVoodoo mode information on SSD1306 OLED display: name, activity, pinout (API)
custom 14x9 monospace font
Definition: font.h:26
void busvoodoo_oled_text_pos(uint8_t column, uint8_t row, enum font_name font_name, const char *text)
draw text in display buffer
monospace pixel fonts collection (API)
SSD1306 OLED library (API)
custom 10x6 monospace font
Definition: font.h:25