SSD1306 OLED display driver 1.8.2 This library is developed to control SSD1306/SSD1331/SSD1351/IL9163/PCD8554 RGB i2c/spi LED displays

platform.c
1 /*
2 MIT License
3
4 Copyright (c) 2018-2019, Alexey Dynda
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24
25 #if defined(__linux__) && !defined(ARDUINO)
26
27 #include "ssd1306_hal/io.h"
28 #include "intf/ssd1306_interface.h"
29 #include "intf/i2c/ssd1306_i2c.h"
30 #include "intf/spi/ssd1306_spi_conf.h"
31 #include "intf/spi/ssd1306_spi.h"
32
33 #ifndef __KERNEL__
34
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/ioctl.h>
43 #include <linux/i2c-dev.h>
44 #include <linux/spi/spidev.h>
45
46 #if defined(CONFIG_PLATFORM_SPI_AVAILABLE) && defined(CONFIG_PLATFORM_SPI_ENABLE) \
47 && !defined(SDL_EMULATION)
48 #define LINUX_SPI_AVAILABLE
49 #endif
50
51 #define MAX_GPIO_COUNT 256
52
53 #ifdef IN
54 #undef IN
55 #endif
56 #define IN 0
57
58 #ifdef OUT
59 #undef OUT
60 #endif
61 #define OUT 1
62
63 #ifdef LINUX_SPI_AVAILABLE
64 static void platform_spi_send_cache();
65 #endif
66
67 int gpio_export(int pin)
68 {
69 char buffer[4];
70 ssize_t bytes_written;
71 int fd;
72 char path[64];
73
74 snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", pin);
75
76 if (access(path, F_OK) == 0)
77 {
78 return 0;
79 }
80
81 fd = open("/sys/class/gpio/export", O_WRONLY);
82 if (-1 == fd)
83 {
84 fprintf(stderr, "Failed to allocate gpio pin[%d]: %s%s!\n",
85 pin, strerror (errno), getuid() == 0 ? "" : ", need to be root");
86 return(-1);
87 }
88
89 bytes_written = snprintf(buffer, sizeof(buffer), "%d", pin);
90 if (write(fd, buffer, bytes_written) < 0)
91 {
92 fprintf(stderr, "Failed to allocate gpio pin[%d]: %s%s!\n",
93 pin, strerror (errno), getuid() == 0 ? "" : ", need to be root");
94 close(fd);
95 return -1;
96 }
97 close(fd);
98 return(0);
99 }
100
101 int gpio_unexport(int pin)
102 {
103 char buffer[4];
104 ssize_t bytes_written;
105 int fd;
106
107 fd = open("/sys/class/gpio/unexport", O_WRONLY);
108 if (-1 == fd)
109 {
110 fprintf(stderr, "Failed to free gpio pin resources!\n");
111 return(-1);
112 }
113
114 bytes_written = snprintf(buffer, sizeof(buffer), "%d", pin);
115 if (write(fd, buffer, bytes_written) < 0)
116 {
117 fprintf(stderr, "Failed to free gpio pin resources!\n");
118 }
119 close(fd);
120 return(0);
121 }
122
123 int gpio_direction(int pin, int dir)
124 {
125 static const char s_directions_str[] = "in\0out";
126
127 char path[64];
128 int fd;
129
130 snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);
131 fd = open(path, O_WRONLY);
132 if (-1 == fd)
133 {
134 fprintf(stderr, "Failed to set gpio pin direction1[%d]: %s!\n",
135 pin, strerror(errno));
136 return(-1);
137 }
138
139 if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3))
140 {
141 fprintf(stderr, "Failed to set gpio pin direction2[%d]: %s!\n",
142 pin, strerror(errno));
143 return(-1);
144 }
145
146 close(fd);
147 return(0);
148 }
149
150 int gpio_read(int pin)
151 {
152 char path[32];
153 char value_str[3];
154 int fd;
155
156 snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
157 fd = open(path, O_RDONLY);
158 if (-1 == fd)
159 {
160 fprintf(stderr, "Failed to read gpio pin value!\n");
161 return(-1);
162 }
163
164 if (-1 == read(fd, value_str, 3))
165 {
166 fprintf(stderr, "Failed to read gpio pin value!\n");
167 return(-1);
168 }
169
170 close(fd);
171
172 return(atoi(value_str));
173 }
174
175 int gpio_write(int pin, int value)
176 {
177 static const char s_values_str[] = "01";
178
179 char path[64];
180 int fd;
181
182 snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
183 fd = open(path, O_WRONLY);
184 if (-1 == fd)
185 {
186 fprintf(stderr, "Failed to set gpio pin value[%d]: %s%s!\n",
187 pin, strerror (errno), getuid() == 0 ? "" : ", need to be root");
188 return(-1);
189 }
190
191 if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1))
192 {
193 fprintf(stderr, "Failed to set gpio pin value[%d]: %s%s!\n",
194 pin, strerror (errno), getuid() == 0 ? "" : ", need to be root");
195 return(-1);
196 }
197
198 close(fd);
199 return(0);
200 }
201
202 #if !defined(SDL_EMULATION)
203
204 static uint8_t s_exported_pin[MAX_GPIO_COUNT] = {0};
205 static uint8_t s_pin_mode[MAX_GPIO_COUNT] = {0};
206
207 void pinMode(int pin, int mode)
208 {
209 if (!s_exported_pin[pin])
210 {
211 if ( gpio_export(pin)<0 )
212 {
213 return;
214 }
215 s_exported_pin[pin] = 1;
216 }
217 if (mode == OUTPUT)
218 {
219 gpio_direction(pin, OUT);
220 s_pin_mode[pin] = 1;
221 }
222 if (mode == INPUT)
223 {
224 gpio_direction(pin, IN);
225 s_pin_mode[pin] = 0;
226 }
227 }
228
229 void digitalWrite(int pin, int level)
230 {
231 #ifdef LINUX_SPI_AVAILABLE
232 if (s_ssd1306_dc == pin)
233 {
234 platform_spi_send_cache();
235 }
236 #endif
237
238 if (!s_exported_pin[pin])
239 {
240 if ( gpio_export(pin)<0 )
241 {
242 return;
243 }
244 s_exported_pin[pin] = 1;
245 }
246 if (!s_pin_mode[pin])
247 {
248 pinMode(pin, OUTPUT);
249 }
250 gpio_write( pin, level );
251 }
252
253 #endif // SDL_EMULATION
254
256 // LINUX I2C IMPLEMENTATION
258 #if defined(CONFIG_PLATFORM_I2C_AVAILABLE) && defined(CONFIG_PLATFORM_I2C_ENABLE)
259
260
261
262 #if !defined(SDL_EMULATION)
263
264
265 static uint8_t s_sa = SSD1306_SA;
266 static int s_fd = -1;
267 static uint8_t s_buffer[128];
268 static uint8_t s_dataSize = 0;
269
270 static void platform_i2c_start(void)
271 {
272 s_dataSize = 0;
273 }
274
275 static void platform_i2c_stop(void)
276 {
277 if (write(s_fd, s_buffer, s_dataSize) != s_dataSize)
278 {
279 fprintf(stderr, "Failed to write to the i2c bus: %s.\n", strerror(errno));
280 }
281 s_dataSize = 0;
282 }
283
284 static void platform_i2c_send(uint8_t data)
285 {
286 s_buffer[s_dataSize] = data;
287 s_dataSize++;
288 if (s_dataSize == sizeof(s_buffer))
289 {
290 /* Send function puts all data to internal buffer. *
291 * Restart transmission if internal buffer is full. */
292 ssd1306_intf.stop();
293 ssd1306_intf.start();
294 ssd1306_intf.send(0x40);
295 }
296 }
297
298 static void platform_i2c_send_buffer(const uint8_t *buffer, uint16_t size)
299 {
300 while (size--)
301 {
302 platform_i2c_send(*buffer);
303 buffer++;
304 }
305 }
306
307 static void platform_i2c_close()
308 {
309 if (s_fd >= 0)
310 {
311 close(s_fd);
312 s_fd = -1;
313 }
314 }
315
316 static void empty_function()
317 {
318 }
319
320 static void empty_function_single_arg(uint8_t arg)
321 {
322 }
323
324 static void empty_function_two_args(const uint8_t *arg1, uint16_t arg2)
325 {
326 }
327
328 void ssd1306_platform_i2cInit(int8_t busId, uint8_t sa, ssd1306_platform_i2cConfig_t * cfg)
329 {
330 char filename[20];
331 if (busId < 0)
332 {
333 busId = 1;
334 }
335 snprintf(filename, 19, "/dev/i2c-%d", busId);
336 ssd1306_intf.start = empty_function;
337 ssd1306_intf.stop = empty_function;
338 ssd1306_intf.close = empty_function;
339 ssd1306_intf.send = empty_function_single_arg;
340 ssd1306_intf.send_buffer = empty_function_two_args;
341 if ((s_fd = open(filename, O_RDWR)) < 0)
342 {
343 fprintf(stderr, "Failed to open the i2c bus %s\n",
344 getuid() == 0 ? "": ": need to be root");
345 return;
346 }
347 if (sa)
348 {
349 s_sa = sa;
350 }
351 if (ioctl(s_fd, I2C_SLAVE, s_sa) < 0)
352 {
353 fprintf(stderr, "Failed to acquire bus access and/or talk to slave.\n");
354 return;
355 }
356 ssd1306_intf.start = platform_i2c_start;
357 ssd1306_intf.stop = platform_i2c_stop;
358 ssd1306_intf.send = platform_i2c_send;
359 ssd1306_intf.send_buffer = platform_i2c_send_buffer;
360 ssd1306_intf.close = platform_i2c_close;
361 }
362
363 #else /* SDL_EMULATION */
364
365 #include "sdl_core.h"
366
367 static void platform_i2c_send_buffer(const uint8_t *buffer, uint16_t size)
368 {
369 while (size--)
370 {
371 sdl_send_byte(*buffer);
372 buffer++;
373 };
374 }
375
376 void ssd1306_platform_i2cInit(int8_t busId, uint8_t sa, ssd1306_platform_i2cConfig_t * cfg)
377 {
378 sdl_core_init();
379 ssd1306_intf.spi = 0;
380 ssd1306_intf.start = sdl_send_init;
381 ssd1306_intf.stop = sdl_send_stop;
382 ssd1306_intf.send = sdl_send_byte;
383 ssd1306_intf.send_buffer = platform_i2c_send_buffer;
384 ssd1306_intf.close = sdl_core_close;
385 }
386
387 #endif /* SDL_EMULATION */
388
389 #endif // CONFIG_PLATFORM_I2C_AVAILABLE
390
391
393 // LINUX SPI IMPLEMENTATION
395 #if defined(CONFIG_PLATFORM_SPI_AVAILABLE) && defined(CONFIG_PLATFORM_SPI_ENABLE)
396
397 #if !defined(SDL_EMULATION)
398
399 static int s_spi_fd = -1;
400 extern uint32_t s_ssd1306_spi_clock;
401 static uint8_t s_spi_cache[1024];
402 static int s_spi_cached_count = 0;
403
404 static void platform_spi_start(void)
405 {
406 s_spi_cached_count = 0;
407 }
408
409 static void platform_spi_stop(void)
410 {
411 platform_spi_send_cache();
412 }
413
414 static void platform_spi_send_cache()
415 {
416 /* TODO: Yeah, sending single bytes is too slow, but *
417 * need to figure out how to detect data/command bytes *
418 * to send bytes as one block */
419 if ( s_spi_cached_count == 0 )
420 {
421 return;
422 }
423 struct spi_ioc_transfer mesg;
424 memset(&mesg, 0, sizeof mesg);
425 mesg.tx_buf = (unsigned long)&s_spi_cache[0];
426 mesg.rx_buf = 0;
427 mesg.len = s_spi_cached_count;
428 mesg.delay_usecs = 0;
429 mesg.speed_hz = 0;
430 mesg.bits_per_word = 8;
431 mesg.cs_change = 0;
432 if (ioctl(s_spi_fd, SPI_IOC_MESSAGE(1), &mesg) < 1)
433 {
434 fprintf(stderr, "SPI failed to send SPI message: %s\n", strerror (errno)) ;
435 }
436 s_spi_cached_count = 0;
437 }
438
439 static void platform_spi_send(uint8_t data)
440 {
441 s_spi_cache[s_spi_cached_count] = data;
442 s_spi_cached_count++;
443 if ( s_spi_cached_count >= sizeof( s_spi_cache ) )
444 {
445 platform_spi_send_cache();
446 }
447 }
448
449 static void platform_spi_close(void)
450 {
451 if (s_spi_fd >= 0)
452 {
453 close(s_spi_fd);
454 s_spi_fd = -1;
455 }
456 }
457
458 static void platform_spi_send_buffer(const uint8_t *data, uint16_t len)
459 {
460 while (len--)
461 {
462 platform_spi_send(*data);
463 data++;
464 }
465 }
466
467 static void empty_function_spi(void)
468 {
469 }
470
471 static void empty_function_arg_spi(uint8_t byte)
472 {
473 }
474
475 static void empty_function_args_spi(const uint8_t *buffer, uint16_t bytes)
476 {
477 }
478
479 void ssd1306_platform_spiInit(int8_t busId,
480 int8_t ces,
481 int8_t dcPin)
482 {
483 char filename[20];
484 if (busId < 0)
485 {
486 busId = 0;
487 }
488 if (ces < 0)
489 {
490 ces = 0;
491 }
492 s_ssd1306_cs = -1; // SPI interface does't need separate ces pin
493 s_ssd1306_dc = dcPin;
494 ssd1306_intf.spi = 1;
495 ssd1306_intf.start = empty_function_spi;
496 ssd1306_intf.stop = empty_function_spi;
497 ssd1306_intf.send = empty_function_arg_spi;
498 ssd1306_intf.send_buffer = empty_function_args_spi;
499 ssd1306_intf.close = empty_function;
500
501 snprintf(filename, 19, "/dev/spidev%d.%d", busId, ces);
502 if ((s_spi_fd = open(filename, O_RDWR)) < 0)
503 {
504 printf("Failed to initialize SPI: %s%s!\n",
505 strerror(errno), getuid() == 0 ? "": ", need to be root");
506 return;
507 }
508 unsigned int speed = s_ssd1306_spi_clock;
509 if (ioctl(s_spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0)
510 {
511 printf("Failed to set speed on SPI line: %s!\n", strerror(errno));
512 }
513 uint8_t mode = SPI_MODE_0;
514 if (ioctl (s_spi_fd, SPI_IOC_WR_MODE, &mode) < 0)
515 {
516 printf("Failed to set SPI mode: %s!\n", strerror(errno));
517 }
518 uint8_t spi_bpw = 8;
519 if (ioctl (s_spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw) < 0)
520 {
521 printf("Failed to set SPI BPW: %s!\n", strerror(errno));
522 }
523
524 ssd1306_intf.spi = 1;
525 ssd1306_intf.start = platform_spi_start;
526 ssd1306_intf.stop = platform_spi_stop;
527 ssd1306_intf.send = platform_spi_send;
528 ssd1306_intf.send_buffer = platform_spi_send_buffer;
529 ssd1306_intf.close = platform_spi_close;
530 }
531
532 #else /* SDL_EMULATION */
533
534 #include "sdl_core.h"
535
536 static void sdl_send_bytes(const uint8_t *buffer, uint16_t size)
537 {
538 while (size--)
539 {
540 sdl_send_byte(*buffer);
541 buffer++;
542 };
543 }
544
545 void ssd1306_platform_spiInit(int8_t busId, int8_t ces, int8_t dcPin)
546 {
547 sdl_core_init();
548 if (ces >= 0)
549 {
550 s_ssd1306_cs = ces;
551 }
552 if (dcPin >= 0)
553 {
554 s_ssd1306_dc = dcPin;
555 }
556 sdl_set_dc_pin(dcPin);
557 ssd1306_intf.spi = 1;
558 ssd1306_intf.start = sdl_send_init;
559 ssd1306_intf.stop = sdl_send_stop;
560 ssd1306_intf.send = sdl_send_byte;
561 ssd1306_intf.send_buffer = sdl_send_bytes;
562 ssd1306_intf.close = sdl_core_close;
563 }
564
565 #endif /* SDL_EMULATION */
566
567 #endif // CONFIG_PLATFORM_SPI_AVAILABLE
568
569 #else // end of !KERNEL, KERNEL is below
570
571 void ssd1306_platform_i2cInit(int8_t busId, uint8_t sa, ssd1306_platform_i2cConfig_t * cfg)
572 {
573 }
574
575 void ssd1306_platform_spiInit(int8_t busId,
576 int8_t ces,
577 int8_t dcPin)
578 {
579 }
580
581 #endif // KERNEL
582
583 #endif // __linux__
s_ssd1306_dc
int8_t s_ssd1306_dc
Definition: ssd1306_spi.c:34
ssd1306_platform_i2cConfig_t
Definition: io.h:80
ssd1306_platform_spiInit
void ssd1306_platform_spiInit(int8_t busId, int8_t cesPin, int8_t dcPin)
Initializes spi interface for platform being used.
ssd1306_interface_t::send
void(* send)(uint8_t data)
Definition: ssd1306_interface.h:70
ssd1306_interface_t::stop
void(* stop)(void)
Definition: ssd1306_interface.h:65
s_ssd1306_spi_clock
uint32_t s_ssd1306_spi_clock
Definition: ssd1306_spi.c:35
io.h
ssd1306_i2c.h
ssd1306_platform_i2cInit
void ssd1306_platform_i2cInit(int8_t busId, uint8_t addr, ssd1306_platform_i2cConfig_t *cfg)
Initializes i2c interface for platform being used.
ssd1306_spi_conf.h
ssd1306_spi.h
ssd1306_interface_t::start
void(* start)(void)
Definition: ssd1306_interface.h:61
ssd1306_interface_t::close
void(* close)(void)
deinitializes internal resources, allocated for interface.
Definition: ssd1306_interface.h:88
ssd1306_intf
ssd1306_interface_t ssd1306_intf
Definition: ssd1306_interface.c:31
SSD1306_SA
#define SSD1306_SA
Definition: ssd1306_i2c_conf.h:47
ssd1306_interface.h
ssd1306_interface_t::spi
uint8_t spi
Definition: ssd1306_interface.h:57
ssd1306_interface_t::send_buffer
void(* send_buffer)(const uint8_t *buffer, uint16_t size)
Sends bytes to SSD1306 device.
Definition: ssd1306_interface.h:80
s_ssd1306_cs
int8_t s_ssd1306_cs
Definition: ssd1306_spi.c:33