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