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_SH1106_I2C.h"
16 :
17 : #include <AP_HAL/AP_HAL.h>
18 : #include <AP_HAL/Device.h>
19 :
20 0 : Display_Backend *Display_SH1106_I2C::probe(AP_HAL::Device &_dev)
21 : {
22 0 : Display_SH1106_I2C *driver = NEW_NOTHROW Display_SH1106_I2C(_dev);
23 0 : if (!driver || !driver->hw_init()) {
24 0 : delete driver;
25 0 : return nullptr;
26 : }
27 0 : return driver;
28 : }
29 :
30 :
31 0 : bool Display_SH1106_I2C::hw_init()
32 : {
33 : struct PACKED {
34 : uint8_t reg;
35 : uint8_t seq[26];
36 0 : } init_seq = { 0x0, {
37 : 0xAE, // Display OFF
38 : 0xA1, // Segment re-map
39 : 0xC8, // COM Output Scan Direction
40 : 0xA8, 0x3F, // MUX Ratio
41 : 0xD5, 0x50, // Display Clock Divide Ratio and Oscillator Frequency: (== +0%)
42 : 0xD3, 0x00, // Display Offset
43 : 0xDB, 0x40, // VCOMH Deselect Level
44 : 0x81, 0xCF, // Contrast Control
45 : 0xAD, 0x8B, // DC-DC Control Mode: 1b (== internal DC-DC enabled) (AKA: Enable charge pump regulator)
46 : 0x40, // Display Start Line
47 : 0xDA, 0x12, // +++ COM Pins hardware configuration
48 : 0xD9, 0xF1, // +++ Pre-charge Period
49 : 0xA4, // +++ Entire Display ON (ignoring RAM): 0b (== OFF)
50 : 0xA6, // +++ Normal/Inverse Display: 0b (== Normal)
51 : 0xAF, // Display ON
52 : 0xB0, // Page Address
53 : 0x02, 0x10 // Column Address
54 : } };
55 :
56 0 : memset(_displaybuffer, 0, SH1106_COLUMNS * SH1106_ROWS_PER_PAGE);
57 :
58 0 : WITH_SEMAPHORE(dev.get_semaphore());
59 :
60 : // init display
61 0 : bool success = dev.transfer((uint8_t *)&init_seq, sizeof(init_seq), nullptr, 0);
62 :
63 0 : if (success) {
64 0 : _need_hw_update = true;
65 0 : dev.register_periodic_callback(20000, FUNCTOR_BIND_MEMBER(&Display_SH1106_I2C::_timer, void));
66 : }
67 :
68 0 : return success;
69 0 : }
70 :
71 0 : void Display_SH1106_I2C::hw_update()
72 : {
73 0 : _need_hw_update = true;
74 0 : }
75 :
76 0 : void Display_SH1106_I2C::_timer()
77 : {
78 0 : if (!_need_hw_update) {
79 0 : return;
80 : }
81 0 : _need_hw_update = false;
82 :
83 : struct PACKED {
84 : uint8_t reg;
85 : uint8_t column_0_3;
86 : uint8_t column_4_7;
87 : uint8_t page;
88 0 : } command = { 0x0, 0x2, 0x10, 0xB0 };
89 :
90 : struct PACKED {
91 : uint8_t reg;
92 : uint8_t db[SH1106_COLUMNS/2];
93 0 : } display_buffer = { 0x40, {} };
94 :
95 : // write buffer to display
96 0 : for (uint8_t i = 0; i < (SH1106_ROWS / SH1106_ROWS_PER_PAGE); i++) {
97 0 : command.page = 0xB0 | (i & 0x0F);
98 0 : dev.transfer((uint8_t *)&command, sizeof(command), nullptr, 0);
99 :
100 0 : memcpy(&display_buffer.db[0], &_displaybuffer[i * SH1106_COLUMNS], SH1106_COLUMNS/2);
101 0 : dev.transfer((uint8_t *)&display_buffer, SH1106_COLUMNS/2 + 1, nullptr, 0);
102 0 : memcpy(&display_buffer.db[0], &_displaybuffer[i * SH1106_COLUMNS + SH1106_COLUMNS/2 ], SH1106_COLUMNS/2);
103 0 : dev.transfer((uint8_t *)&display_buffer, SH1106_COLUMNS/2 + 1, nullptr, 0);
104 : }
105 : }
106 :
107 0 : void Display_SH1106_I2C::set_pixel(uint16_t x, uint16_t y)
108 : {
109 : // check x, y range
110 0 : if ((x >= SH1106_COLUMNS) || (y >= SH1106_ROWS)) {
111 0 : return;
112 : }
113 : // set pixel in buffer
114 0 : _displaybuffer[x + (y / 8 * SH1106_COLUMNS)] |= 1 << (y % 8);
115 : }
116 :
117 0 : void Display_SH1106_I2C::clear_pixel(uint16_t x, uint16_t y)
118 : {
119 : // check x, y range
120 0 : if ((x >= SH1106_COLUMNS) || (y >= SH1106_ROWS)) {
121 0 : return;
122 : }
123 : // clear pixel in buffer
124 0 : _displaybuffer[x + (y / 8 * SH1106_COLUMNS)] &= ~(1 << (y % 8));
125 : }
126 :
127 0 : void Display_SH1106_I2C::clear_screen()
128 : {
129 0 : memset(_displaybuffer, 0, SH1106_COLUMNS * SH1106_ROWS_PER_PAGE);
130 0 : }
|