Display_SSD1306_I2C.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. #include "Display_SSD1306_I2C.h"
  14. #include <utility>
  15. #include <AP_HAL/AP_HAL.h>
  16. #include <AP_HAL/I2CDevice.h>
  17. // constructor
  18. Display_SSD1306_I2C::Display_SSD1306_I2C(AP_HAL::OwnPtr<AP_HAL::Device> dev) :
  19. _dev(std::move(dev))
  20. {
  21. }
  22. Display_SSD1306_I2C::~Display_SSD1306_I2C()
  23. {
  24. }
  25. Display_SSD1306_I2C *Display_SSD1306_I2C::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev)
  26. {
  27. Display_SSD1306_I2C *driver = new Display_SSD1306_I2C(std::move(dev));
  28. if (!driver || !driver->hw_init()) {
  29. delete driver;
  30. return nullptr;
  31. }
  32. return driver;
  33. }
  34. bool Display_SSD1306_I2C::hw_init()
  35. {
  36. struct PACKED {
  37. uint8_t reg;
  38. uint8_t seq[31];
  39. } init_seq = { 0x0, {
  40. // LEGEND:
  41. // *** is out of sequence for init steps recommended in datasheet
  42. // +++ not listed in sequence for init steps recommended in datasheet
  43. 0xAE, // Display OFF
  44. 0xD5, 0x80, // *** Set Display Clock Divide Ratio and Oscillator Frequency
  45. // Clock Divide Ratio: 0b (== 1)
  46. // Oscillator Frequency: 1000b (== +0%)
  47. 0xA8, 0x3F, // MUX Ratio: 111111b (== 64MUX)
  48. 0xD3, 0x00, // Display Offset: 0b (== 0)
  49. 0x40, // Display Start Line: 0b (== 0)
  50. 0x8D, 0x14, // *** Enable charge pump regulator: 1b (== Enable)
  51. 0x20, 0x00, // *** Memory Addressing Mode: 00b (== Horizontal Addressing Mode)
  52. 0xA1, // Segment re-map: 1b (== column address 127 is mapped to SEG0)
  53. 0xC8, // COM Output Scan Direction: 1b (== remapped mode. Scan from COM[N-1] to COM0)
  54. 0xDA, 0x12, // COM Pins hardware configuration: 01b (POR)
  55. // (== Alternative COM pin configuration + Disable COM Left/Right remap)
  56. 0x81, 0xCF, // Contrast Control: 0xCF (== 207 decimal, range 0..255)
  57. 0xD9, 0xF1, // +++ Pre-charge Period: 0xF1 (== 1 DCLK P1 + 15 DCLK P2)
  58. 0xDB, 0x40, // +++ VCOMH Deselect Level: 100b (INVALID?!) (== ?!)
  59. 0xA4, // Entire Display ON (ignoring RAM): (== OFF)
  60. 0xA6, // Normal/Inverse Display: 0b (== Normal)
  61. 0xAF, // Display ON: 1b (== ON)
  62. 0x21, 0, 127, // +++ Column Address: (== start:0, end:127)
  63. 0x22, 0, 7 // +++ Page Address: (== start:0, end:7)
  64. } };
  65. memset(_displaybuffer, 0, SSD1306_COLUMNS * SSD1306_ROWS_PER_PAGE);
  66. // take i2c bus semaphore
  67. if (!_dev || !_dev->get_semaphore()->take(HAL_SEMAPHORE_BLOCK_FOREVER)) {
  68. return false;
  69. }
  70. // init display
  71. bool success = _dev->transfer((uint8_t *)&init_seq, sizeof(init_seq), nullptr, 0);
  72. // give back i2c semaphore
  73. _dev->get_semaphore()->give();
  74. if (success) {
  75. _need_hw_update = true;
  76. _dev->register_periodic_callback(20000, FUNCTOR_BIND_MEMBER(&Display_SSD1306_I2C::_timer, void));
  77. }
  78. return success;
  79. }
  80. void Display_SSD1306_I2C::hw_update()
  81. {
  82. _need_hw_update = true;
  83. }
  84. void Display_SSD1306_I2C::_timer()
  85. {
  86. if (!_need_hw_update) {
  87. return;
  88. }
  89. _need_hw_update = false;
  90. struct PACKED {
  91. uint8_t reg;
  92. uint8_t cmd[6];
  93. } command = { 0x0, {0x21, 0, 127, 0x22, 0, 7} };
  94. struct PACKED {
  95. uint8_t reg;
  96. uint8_t db[SSD1306_COLUMNS/2];
  97. } display_buffer = { 0x40, {} };
  98. // write buffer to display
  99. for (uint8_t i = 0; i < (SSD1306_ROWS / SSD1306_ROWS_PER_PAGE); i++) {
  100. command.cmd[4] = i;
  101. _dev->transfer((uint8_t *)&command, sizeof(command), nullptr, 0);
  102. memcpy(&display_buffer.db[0], &_displaybuffer[i * SSD1306_COLUMNS], SSD1306_COLUMNS/2);
  103. _dev->transfer((uint8_t *)&display_buffer, SSD1306_COLUMNS/2 + 1, nullptr, 0);
  104. memcpy(&display_buffer.db[0], &_displaybuffer[i * SSD1306_COLUMNS + SSD1306_COLUMNS/2 ], SSD1306_COLUMNS/2);
  105. _dev->transfer((uint8_t *)&display_buffer, SSD1306_COLUMNS/2 + 1, nullptr, 0);
  106. }
  107. }
  108. void Display_SSD1306_I2C::set_pixel(uint16_t x, uint16_t y)
  109. {
  110. // check x, y range
  111. if ((x >= SSD1306_COLUMNS) || (y >= SSD1306_ROWS)) {
  112. return;
  113. }
  114. // set pixel in buffer
  115. _displaybuffer[x + (y / 8 * SSD1306_COLUMNS)] |= 1 << (y % 8);
  116. }
  117. void Display_SSD1306_I2C::clear_pixel(uint16_t x, uint16_t y)
  118. {
  119. // check x, y range
  120. if ((x >= SSD1306_COLUMNS) || (y >= SSD1306_ROWS)) {
  121. return;
  122. }
  123. // clear pixel in buffer
  124. _displaybuffer[x + (y / 8 * SSD1306_COLUMNS)] &= ~(1 << (y % 8));
  125. }
  126. void Display_SSD1306_I2C::clear_screen()
  127. {
  128. memset(_displaybuffer, 0, SSD1306_COLUMNS * SSD1306_ROWS_PER_PAGE);
  129. }