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"
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();
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__
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 ssd1306_platform_i2cInit(int8_t busId, uint8_t addr, ssd1306_platform_i2cConfig_t *cfg)
Initializes i2c interface for platform being used.
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