LCOV - code coverage report
Current view: top level - AP_Notify - Display_SH1106_I2C.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 43 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 7 0.0 %

          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 : }

Generated by: LCOV version 1.14