SSD1306 OLED display driver  1.7.13
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"
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 resources[%d]: %s!\n", pin, strerror (errno));
85  return(-1);
86  }
87 
88  bytes_written = snprintf(buffer, sizeof(buffer), "%d", pin);
89  if (write(fd, buffer, bytes_written) < 0)
90  {
91  fprintf(stderr, "Failed to allocate gpio pin resources[%d]: %s!\n", pin, strerror (errno));
92  close(fd);
93  return -1;
94  }
95  close(fd);
96  return(0);
97 }
98 
99 int gpio_unexport(int pin)
100 {
101  char buffer[4];
102  ssize_t bytes_written;
103  int fd;
104 
105  fd = open("/sys/class/gpio/unexport", O_WRONLY);
106  if (-1 == fd)
107  {
108  fprintf(stderr, "Failed to free gpio pin resources!\n");
109  return(-1);
110  }
111 
112  bytes_written = snprintf(buffer, sizeof(buffer), "%d", pin);
113  if (write(fd, buffer, bytes_written) < 0)
114  {
115  fprintf(stderr, "Failed to free gpio pin resources!\n");
116  }
117  close(fd);
118  return(0);
119 }
120 
121 int gpio_direction(int pin, int dir)
122 {
123  static const char s_directions_str[] = "in\0out";
124 
125  char path[64];
126  int fd;
127 
128  snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);
129  fd = open(path, O_WRONLY);
130  if (-1 == fd)
131  {
132  fprintf(stderr, "Failed to set gpio pin direction1[%d]: %s!\n", pin, strerror(errno));
133  return(-1);
134  }
135 
136  if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3))
137  {
138  fprintf(stderr, "Failed to set gpio pin direction2[%d]: %s!\n", pin, strerror(errno));
139  return(-1);
140  }
141 
142  close(fd);
143  return(0);
144 }
145 
146 int gpio_read(int pin)
147 {
148  char path[32];
149  char value_str[3];
150  int fd;
151 
152  snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
153  fd = open(path, O_RDONLY);
154  if (-1 == fd)
155  {
156  fprintf(stderr, "Failed to read gpio pin value!\n");
157  return(-1);
158  }
159 
160  if (-1 == read(fd, value_str, 3))
161  {
162  fprintf(stderr, "Failed to read gpio pin value!\n");
163  return(-1);
164  }
165 
166  close(fd);
167 
168  return(atoi(value_str));
169 }
170 
171 int gpio_write(int pin, int value)
172 {
173  static const char s_values_str[] = "01";
174 
175  char path[64];
176  int fd;
177 
178  snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
179  fd = open(path, O_WRONLY);
180  if (-1 == fd)
181  {
182  fprintf(stderr, "Failed to set gpio pin value[%d]: %s!\n", pin, strerror(errno));
183  return(-1);
184  }
185 
186  if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1))
187  {
188  fprintf(stderr, "Failed to set gpio pin value[%d]: %s!\n", pin, strerror (errno));
189  return(-1);
190  }
191 
192  close(fd);
193  return(0);
194 }
195 
196 #if !defined(SDL_EMULATION)
197 
198 static uint8_t s_exported_pin[MAX_GPIO_COUNT] = {0};
199 static uint8_t s_pin_mode[MAX_GPIO_COUNT] = {0};
200 
201 void pinMode(int pin, int mode)
202 {
203  if (!s_exported_pin[pin])
204  {
205  if ( gpio_export(pin)<0 )
206  {
207  return;
208  }
209  s_exported_pin[pin] = 1;
210  }
211  if (mode == OUTPUT)
212  {
213  gpio_direction(pin, OUT);
214  s_pin_mode[pin] = 1;
215  }
216  if (mode == INPUT)
217  {
218  gpio_direction(pin, IN);
219  s_pin_mode[pin] = 0;
220  }
221 }
222 
223 void digitalWrite(int pin, int level)
224 {
225 #ifdef LINUX_SPI_AVAILABLE
226  if (s_ssd1306_dc == pin)
227  {
228  platform_spi_send_cache();
229  }
230 #endif
231 
232  if (!s_exported_pin[pin])
233  {
234  if ( gpio_export(pin)<0 )
235  {
236  return;
237  }
238  s_exported_pin[pin] = 1;
239  }
240  if (!s_pin_mode[pin])
241  {
242  pinMode(pin, OUTPUT);
243  }
244  gpio_write( pin, level );
245 }
246 
247 #endif // SDL_EMULATION
248 
250 // LINUX I2C IMPLEMENTATION
252 #if defined(CONFIG_PLATFORM_I2C_AVAILABLE) && defined(CONFIG_PLATFORM_I2C_ENABLE)
253 
254 
255 
256 #if !defined(SDL_EMULATION)
257 
258 
259 static uint8_t s_sa = SSD1306_SA;
260 static int s_fd = -1;
261 static uint8_t s_buffer[128];
262 static uint8_t s_dataSize = 0;
263 
264 static void platform_i2c_start(void)
265 {
266  s_dataSize = 0;
267 }
268 
269 static void platform_i2c_stop(void)
270 {
271  if (write(s_fd, s_buffer, s_dataSize) != s_dataSize)
272  {
273  fprintf(stderr, "Failed to write to the i2c bus: %s.\n", strerror(errno));
274  }
275  s_dataSize = 0;
276 }
277 
278 static void platform_i2c_send(uint8_t data)
279 {
280  s_buffer[s_dataSize] = data;
281  s_dataSize++;
282  if (s_dataSize == sizeof(s_buffer))
283  {
284  /* Send function puts all data to internal buffer. *
285  * Restart transmission if internal buffer is full. */
286  ssd1306_intf.stop();
288  ssd1306_intf.send(0x40);
289  }
290 }
291 
292 static void platform_i2c_send_buffer(const uint8_t *buffer, uint16_t size)
293 {
294  while (size--)
295  {
296  platform_i2c_send(*buffer);
297  buffer++;
298  }
299 }
300 
301 static void platform_i2c_close()
302 {
303  if (s_fd >= 0)
304  {
305  close(s_fd);
306  s_fd = -1;
307  }
308 }
309 
310 static void empty_function()
311 {
312 }
313 
314 static void empty_function_single_arg(uint8_t arg)
315 {
316 }
317 
318 static void empty_function_two_args(const uint8_t *arg1, uint16_t arg2)
319 {
320 }
321 
322 void ssd1306_platform_i2cInit(int8_t busId, uint8_t sa, int8_t arg)
323 {
324  char filename[20];
325  if (busId < 0)
326  {
327  busId = 1;
328  }
329  snprintf(filename, 19, "/dev/i2c-%d", busId);
330  ssd1306_intf.start = empty_function;
331  ssd1306_intf.stop = empty_function;
332  ssd1306_intf.close = empty_function;
333  ssd1306_intf.send = empty_function_single_arg;
334  ssd1306_intf.send_buffer = empty_function_two_args;
335  if ((s_fd = open(filename, O_RDWR)) < 0)
336  {
337  fprintf(stderr, "Failed to open the i2c bus\n");
338  return;
339  }
340  if (sa)
341  {
342  s_sa = sa;
343  }
344  if (ioctl(s_fd, I2C_SLAVE, s_sa) < 0)
345  {
346  fprintf(stderr, "Failed to acquire bus access and/or talk to slave.\n");
347  return;
348  }
349  ssd1306_intf.start = platform_i2c_start;
350  ssd1306_intf.stop = platform_i2c_stop;
351  ssd1306_intf.send = platform_i2c_send;
352  ssd1306_intf.send_buffer = platform_i2c_send_buffer;
353  ssd1306_intf.close = platform_i2c_close;
354 }
355 
356 #else /* SDL_EMULATION */
357 
358 #include "sdl_core.h"
359 
360 static void platform_i2c_send_buffer(const uint8_t *buffer, uint16_t size)
361 {
362  while (size--)
363  {
364  sdl_send_byte(*buffer);
365  buffer++;
366  };
367 }
368 
369 void ssd1306_platform_i2cInit(int8_t busId, uint8_t sa, int8_t arg)
370 {
371  sdl_core_init();
372  ssd1306_intf.spi = 0;
373  ssd1306_intf.start = sdl_send_init;
374  ssd1306_intf.stop = sdl_send_stop;
375  ssd1306_intf.send = sdl_send_byte;
376  ssd1306_intf.send_buffer = platform_i2c_send_buffer;
377  ssd1306_intf.close = sdl_core_close;
378 }
379 
380 #endif /* SDL_EMULATION */
381 
382 #endif // CONFIG_PLATFORM_I2C_AVAILABLE
383 
384 
386 // LINUX SPI IMPLEMENTATION
388 #if defined(CONFIG_PLATFORM_SPI_AVAILABLE) && defined(CONFIG_PLATFORM_SPI_ENABLE)
389 
390 #if !defined(SDL_EMULATION)
391 
392 static int s_spi_fd = -1;
393 extern uint32_t s_ssd1306_spi_clock;
394 static uint8_t s_spi_cache[1024];
395 static int s_spi_cached_count = 0;
396 
397 static void platform_spi_start(void)
398 {
399  s_spi_cached_count = 0;
400 }
401 
402 static void platform_spi_stop(void)
403 {
404  platform_spi_send_cache();
405 }
406 
407 static void platform_spi_send_cache()
408 {
409  /* TODO: Yeah, sending single bytes is too slow, but *
410  * need to figure out how to detect data/command bytes *
411  * to send bytes as one block */
412  if ( s_spi_cached_count == 0 )
413  {
414  return;
415  }
416  struct spi_ioc_transfer mesg;
417  memset(&mesg, 0, sizeof mesg);
418  mesg.tx_buf = (unsigned long)&s_spi_cache[0];
419  mesg.rx_buf = 0;
420  mesg.len = s_spi_cached_count;
421  mesg.delay_usecs = 0;
422  mesg.speed_hz = 0;
423  mesg.bits_per_word = 8;
424  mesg.cs_change = 0;
425  if (ioctl(s_spi_fd, SPI_IOC_MESSAGE(1), &mesg) < 1)
426  {
427  fprintf(stderr, "SPI failed to send SPI message: %s\n", strerror (errno)) ;
428  }
429  s_spi_cached_count = 0;
430 }
431 
432 static void platform_spi_send(uint8_t data)
433 {
434  s_spi_cache[s_spi_cached_count] = data;
435  s_spi_cached_count++;
436  if ( s_spi_cached_count >= sizeof( s_spi_cache ) )
437  {
438  platform_spi_send_cache();
439  }
440 }
441 
442 static void platform_spi_close(void)
443 {
444  if (s_spi_fd >= 0)
445  {
446  close(s_spi_fd);
447  s_spi_fd = -1;
448  }
449 }
450 
451 static void platform_spi_send_buffer(const uint8_t *data, uint16_t len)
452 {
453  while (len--)
454  {
455  platform_spi_send(*data);
456  data++;
457  }
458 }
459 
460 static void empty_function_spi(void)
461 {
462 }
463 
464 static void empty_function_arg_spi(uint8_t byte)
465 {
466 }
467 
468 static void empty_function_args_spi(const uint8_t *buffer, uint16_t bytes)
469 {
470 }
471 
472 void ssd1306_platform_spiInit(int8_t busId,
473  int8_t ces,
474  int8_t dcPin)
475 {
476  char filename[20];
477  if (busId < 0)
478  {
479  busId = 0;
480  }
481  if (ces < 0)
482  {
483  ces = 0;
484  }
485  s_ssd1306_cs = -1; // SPI interface does't need separate ces pin
486  s_ssd1306_dc = dcPin;
487  ssd1306_intf.spi = 1;
488  ssd1306_intf.start = empty_function_spi;
489  ssd1306_intf.stop = empty_function_spi;
490  ssd1306_intf.send = empty_function_arg_spi;
491  ssd1306_intf.send_buffer = empty_function_args_spi;
492  ssd1306_intf.close = empty_function;
493 
494  snprintf(filename, 19, "/dev/spidev%d.%d", busId, ces);
495  if ((s_spi_fd = open(filename, O_RDWR)) < 0)
496  {
497  printf("Failed to initialize SPI: %s!\n", strerror(errno));
498  return;
499  }
500  unsigned int speed = s_ssd1306_spi_clock;
501  if (ioctl(s_spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0)
502  {
503  printf("Failed to set speed on SPI line: %s!\n", strerror(errno));
504  }
505  uint8_t mode = SPI_MODE_0;
506  if (ioctl (s_spi_fd, SPI_IOC_WR_MODE, &mode) < 0)
507  {
508  printf("Failed to set SPI mode: %s!\n", strerror(errno));
509  }
510  uint8_t spi_bpw = 8;
511  if (ioctl (s_spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bpw) < 0)
512  {
513  printf("Failed to set SPI BPW: %s!\n", strerror(errno));
514  }
515 
516  ssd1306_intf.spi = 1;
517  ssd1306_intf.start = platform_spi_start;
518  ssd1306_intf.stop = platform_spi_stop;
519  ssd1306_intf.send = platform_spi_send;
520  ssd1306_intf.send_buffer = platform_spi_send_buffer;
521  ssd1306_intf.close = platform_spi_close;
522 }
523 
524 #else /* SDL_EMULATION */
525 
526 #include "sdl_core.h"
527 
528 static void sdl_send_bytes(const uint8_t *buffer, uint16_t size)
529 {
530  while (size--)
531  {
532  sdl_send_byte(*buffer);
533  buffer++;
534  };
535 }
536 
537 void ssd1306_platform_spiInit(int8_t busId, int8_t ces, int8_t dcPin)
538 {
539  sdl_core_init();
540  if (ces >= 0)
541  {
542  s_ssd1306_cs = ces;
543  }
544  if (dcPin >= 0)
545  {
546  s_ssd1306_dc = dcPin;
547  }
548  sdl_set_dc_pin(dcPin);
549  ssd1306_intf.spi = 1;
550  ssd1306_intf.start = sdl_send_init;
551  ssd1306_intf.stop = sdl_send_stop;
552  ssd1306_intf.send = sdl_send_byte;
553  ssd1306_intf.send_buffer = sdl_send_bytes;
554  ssd1306_intf.close = sdl_core_close;
555 }
556 
557 #endif /* SDL_EMULATION */
558 
559 #endif // CONFIG_PLATFORM_SPI_AVAILABLE
560 
561 #else // end of !KERNEL, KERNEL is below
562 
563 void ssd1306_platform_i2cInit(int8_t busId, uint8_t sa, int8_t arg)
564 {
565 }
566 
567 void ssd1306_platform_spiInit(int8_t busId,
568  int8_t ces,
569  int8_t dcPin)
570 {
571 }
572 
573 #endif // KERNEL
574 
575 #endif // __linux__
void ssd1306_platform_i2cInit(int8_t busId, uint8_t addr, int8_t arg)
Initializes i2c interface for platform being used.
int8_t s_ssd1306_dc
Definition: ssd1306_spi.c:34
void ssd1306_platform_spiInit(int8_t busId, int8_t cesPin, int8_t dcPin)
Initializes spi interface for platform being used.
void(* send)(uint8_t data)
uint32_t s_ssd1306_spi_clock
Definition: ssd1306_spi.c:35
void(* close)(void)
deinitializes internal resources, allocated for interface.
ssd1306_interface_t ssd1306_intf
#define SSD1306_SA
void(* send_buffer)(const uint8_t *buffer, uint16_t size)
Sends bytes to SSD1306 device.
int8_t s_ssd1306_cs
Definition: ssd1306_spi.c:33