sd1306.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include "pico/stdlib.h"
  5. #include "pico/binary_info.h"
  6. #include "hardware/i2c.h"
  7. #include "sd1306.h"
  8. // commands (see datasheet)
  9. #define OLED_SET_CONTRAST _u(0x81)
  10. #define OLED_SET_ENTIRE_ON _u(0xA4)
  11. #define OLED_SET_NORM_INV _u(0xA6)
  12. #define OLED_SET_DISP _u(0xAE)
  13. #define OLED_SET_MEM_ADDR _u(0x20)
  14. #define OLED_SET_COL_ADDR _u(0x21)
  15. #define OLED_SET_PAGE_ADDR _u(0x22)
  16. #define OLED_SET_DISP_START_LINE _u(0x40)
  17. #define OLED_SET_SEG_REMAP _u(0xA0)
  18. #define OLED_SET_MUX_RATIO _u(0xA8)
  19. #define OLED_SET_COM_OUT_DIR _u(0xC0)
  20. #define OLED_SET_DISP_OFFSET _u(0xD3)
  21. #define OLED_SET_COM_PIN_CFG _u(0xDA)
  22. #define OLED_SET_DISP_CLK_DIV _u(0xD5)
  23. #define OLED_SET_PRECHARGE _u(0xD9)
  24. #define OLED_SET_VCOM_DESEL _u(0xDB)
  25. #define OLED_SET_CHARGE_PUMP _u(0x8D)
  26. #define OLED_SET_HORIZ_SCROLL _u(0x26)
  27. #define OLED_SET_SCROLL _u(0x2E)
  28. #define OLED_ADDR _u(0x3C)
  29. #define OLED_WRITE_MODE _u(0xFE)
  30. #define OLED_READ_MODE _u(0xFF)
  31. void fill(uint8_t buf[], uint8_t fill) {
  32. // fill entire buffer with the same byte
  33. for (int i = 0; i < OLED_BUF_LEN; i++) {
  34. buf[i] = fill;
  35. }
  36. };
  37. void fill_page(uint8_t *buf, uint8_t fill, uint8_t page) {
  38. // fill entire page with the same byte
  39. memset(buf + (page * OLED_WIDTH), fill, OLED_WIDTH);
  40. };
  41. // convenience methods for printing out a buffer to be rendered
  42. // mostly useful for debugging images, patterns, etc
  43. void print_buf_page(uint8_t buf[], uint8_t page) {
  44. // prints one page of a full length (128x4) buffer
  45. for (int j = 0; j < OLED_PAGE_HEIGHT; j++) {
  46. for (int k = 0; k < OLED_WIDTH; k++) {
  47. printf("%u", (buf[page * OLED_WIDTH + k] >> j) & 0x01);
  48. }
  49. printf("\n");
  50. }
  51. }
  52. void print_buf_pages(uint8_t buf[]) {
  53. // prints all pages of a full length buffer
  54. for (int i = 0; i < OLED_NUM_PAGES; i++) {
  55. printf("--page %d--\n", i);
  56. print_buf_page(buf, i);
  57. }
  58. }
  59. void print_buf_area(uint8_t *buf, struct render_area *area) {
  60. // print a render area of generic size
  61. int area_width = area->end_col - area->start_col + 1;
  62. int area_height = area->end_page - area->start_page + 1; // in pages, not pixels
  63. for (int i = 0; i < area_height; i++) {
  64. for (int j = 0; j < OLED_PAGE_HEIGHT; j++) {
  65. for (int k = 0; k < area_width; k++) {
  66. printf("%u", (buf[i * area_width + k] >> j) & 0x01);
  67. }
  68. printf("\n");
  69. }
  70. }
  71. }
  72. void calc_render_area_buflen(struct render_area *area) {
  73. // calculate how long the flattened buffer will be for a render area
  74. area->buflen = (area->end_col - area->start_col + 1) * (area->end_page - area->start_page + 1);
  75. }
  76. #ifdef i2c_default
  77. void oled_send_cmd(uint8_t cmd) {
  78. // I2C write process expects a control byte followed by data
  79. // this "data" can be a command or data to follow up a command
  80. // Co = 1, D/C = 0 => the driver expects a command
  81. uint8_t buf[2] = {0x80, cmd};
  82. i2c_write_blocking(i2c_default, (OLED_ADDR & OLED_WRITE_MODE), buf, 2, false);
  83. }
  84. void oled_send_buf(uint8_t buf[], int buflen) {
  85. // in horizontal addressing mode, the column address pointer auto-increments
  86. // and then wraps around to the next page, so we can send the entire frame
  87. // buffer in one gooooooo!
  88. // copy our frame buffer into a new buffer because we need to add the control byte
  89. // to the beginning
  90. // TODO find a more memory-efficient way to do this..
  91. // maybe break the data transfer into pages?
  92. uint8_t *temp_buf = (uint8_t *)malloc(buflen + 1);
  93. for (int i = 1; i < buflen + 1; i++) {
  94. temp_buf[i] = buf[i - 1];
  95. }
  96. // Co = 0, D/C = 1 => the driver expects data to be written to RAM
  97. temp_buf[0] = 0x40;
  98. i2c_write_blocking(i2c_default, (OLED_ADDR & OLED_WRITE_MODE), temp_buf, buflen + 1, false);
  99. free(temp_buf);
  100. }
  101. void oled_init(void) {
  102. // some of these commands are not strictly necessary as the reset
  103. // process defaults to some of these but they are shown here
  104. // to demonstrate what the initialization sequence looks like
  105. // some configuration values are recommended by the board manufacturer
  106. oled_send_cmd(OLED_SET_DISP | 0x00); // set display off
  107. /* memory mapping */
  108. oled_send_cmd(OLED_SET_MEM_ADDR); // set memory address mode
  109. oled_send_cmd(0x00); // horizontal addressing mode
  110. /* resolution and layout */
  111. oled_send_cmd(OLED_SET_DISP_START_LINE); // set display start line to 0
  112. oled_send_cmd(OLED_SET_SEG_REMAP | 0x01); // set segment re-map
  113. // column address 127 is mapped to SEG0
  114. oled_send_cmd(OLED_SET_MUX_RATIO); // set multiplex ratio
  115. oled_send_cmd(OLED_HEIGHT - 1); // our display is only 32 pixels high
  116. oled_send_cmd(OLED_SET_COM_OUT_DIR | 0x08); // set COM (common) output scan direction
  117. // scan from bottom up, COM[N-1] to COM0
  118. oled_send_cmd(OLED_SET_DISP_OFFSET); // set display offset
  119. oled_send_cmd(0x00); // no offset
  120. oled_send_cmd(OLED_SET_COM_PIN_CFG); // set COM (common) pins hardware configuration
  121. oled_send_cmd(0x02); // manufacturer magic number
  122. /* timing and driving scheme */
  123. oled_send_cmd(OLED_SET_DISP_CLK_DIV); // set display clock divide ratio
  124. oled_send_cmd(0x80); // div ratio of 1, standard freq
  125. oled_send_cmd(OLED_SET_PRECHARGE); // set pre-charge period
  126. oled_send_cmd(0xF1); // Vcc internally generated on our board
  127. oled_send_cmd(OLED_SET_VCOM_DESEL); // set VCOMH deselect level
  128. oled_send_cmd(0x30); // 0.83xVcc
  129. /* display */
  130. oled_send_cmd(OLED_SET_CONTRAST); // set contrast control
  131. oled_send_cmd(0xFF);
  132. oled_send_cmd(OLED_SET_ENTIRE_ON); // set entire display on to follow RAM content
  133. oled_send_cmd(OLED_SET_NORM_INV); // set normal (not inverted) display
  134. oled_send_cmd(OLED_SET_CHARGE_PUMP); // set charge pump
  135. oled_send_cmd(0x14); // Vcc internally generated on our board
  136. oled_send_cmd(OLED_SET_SCROLL | 0x00); // deactivate horizontal scrolling if set
  137. // this is necessary as memory writes will corrupt if scrolling was enabled
  138. oled_send_cmd(OLED_SET_DISP | 0x01); // turn display on
  139. }
  140. void render(uint8_t *buf, struct render_area *area) {
  141. // update a portion of the display with a render area
  142. oled_send_cmd(OLED_SET_COL_ADDR);
  143. oled_send_cmd(area->start_col);
  144. oled_send_cmd(area->end_col);
  145. oled_send_cmd(OLED_SET_PAGE_ADDR);
  146. oled_send_cmd(area->start_page);
  147. oled_send_cmd(area->end_page);
  148. oled_send_buf(buf, area->buflen);
  149. }
  150. #endif