1 /*
2  * Author: Marc Graham <marc@m2ag.net>
3  * Copyright (c) 2015 Intel Corporation
4  *
5  * Adapted from ssd1308 library.
6  * Author: Yevgeniy Kiveisha <yevgeniy.kiveisha@intel.com>
7  * Copyright (c) 2014 Intel Corporation.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files (the
11  * "Software"), to deal in the Software without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28 
29 #include <string>
30 #include <unistd.h>
31 
32 #include "hd44780_bits.h"
33 #include "ssd1306.h"
34 
35 using namespace upm;
36 
SSD1306(int bus_in,int addr_in)37 SSD1306::SSD1306(int bus_in, int addr_in) : m_i2c_lcd_control(bus_in)
38 {
39     int vccstate = SSD1306_SWITCHCAPVCC;
40     _vccstate = vccstate;
41 
42     int LCD_CMD = 0x00;
43 
44     m_lcd_control_address = addr_in;
45     m_name = "SSD1306";
46 
47     mraa::Result error = m_i2c_lcd_control.address(m_lcd_control_address);
48 
49     if (error != mraa::SUCCESS) {
50         throw std::runtime_error(std::string(__FUNCTION__) +
51                                 ": mraa_i2c_address() failed");
52         return;
53     }
54 
55     error = m_i2c_lcd_control.frequency(mraa::I2C_FAST);
56 
57     if (error != mraa::SUCCESS) {
58         throw std::invalid_argument(std::string(__FUNCTION__) +
59                                 ": mraa_i2c_frequency(MRAA_I2C_FAST) failed");
60         return;
61     }
62 
63     m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off
64     usleep(4500);
65     //ADD 1306 stuff
66     // Init sequence for 128x64 OLED module                 // 0xAE
67     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
68     m_i2c_lcd_control.writeReg(LCD_CMD, 0x80);                                  // the suggested ratio 0x80
69     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETMULTIPLEX);                  // 0xA8
70     m_i2c_lcd_control.writeReg(LCD_CMD, 0x3F);
71     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYOFFSET);              // 0xD3
72     m_i2c_lcd_control.writeReg(LCD_CMD, 0x0);                                   // no offset
73     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETSTARTLINE | 0x0);            // line #0
74     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_CHARGEPUMP);                    // 0x8D
75     if (vccstate == SSD1306_EXTERNALVCC) {
76         m_i2c_lcd_control.writeReg(LCD_CMD, 0x10);
77     } else {
78         m_i2c_lcd_control.writeReg(LCD_CMD,0x14);
79     }
80     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_MEMORYMODE);                    // 0x20
81     m_i2c_lcd_control.writeReg(LCD_CMD, 0x00);                                  // 0x0 act like ks0108
82     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SEGREMAP | 0x1);
83     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_COMSCANDEC);
84     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCOMPINS);                    // 0xDA
85     m_i2c_lcd_control.writeReg(LCD_CMD, 0x12);
86     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCONTRAST);                   // 0x81
87     if (vccstate == SSD1306_EXTERNALVCC) {
88         m_i2c_lcd_control.writeReg(LCD_CMD, 0x9F);
89     } else {
90         m_i2c_lcd_control.writeReg(LCD_CMD, 0xCF);
91     }
92     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETPRECHARGE);                  // 0xd9
93     if (vccstate == SSD1306_EXTERNALVCC) {
94       m_i2c_lcd_control.writeReg(LCD_CMD, 0x22);
95     } else {
96       m_i2c_lcd_control.writeReg(LCD_CMD,0xF1);
97     }
98     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETVCOMDETECT);                 // 0xDB
99     m_i2c_lcd_control.writeReg(LCD_CMD, 0x40);
100     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_DISPLAYALLON_RESUME);           // 0xA4
101     m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_SET_NORMAL_1306);                 // 0xA6
102 
103     //END 1306 Stuff
104     m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on
105     usleep(4500);
106     setNormalDisplay(); // set to normal display '1' is ON
107 
108     clear();
109     setAddressingMode(PAGE);
110 }
111 
~SSD1306()112 SSD1306::~SSD1306()
113 {
114 }
115 
116 mraa::Result
draw(uint8_t * data,int bytes)117 SSD1306::draw(uint8_t* data, int bytes)
118 {
119     mraa::Result error = mraa::SUCCESS;
120 
121     setAddressingMode(HORIZONTAL);
122     for (int idx = 0; idx < bytes; idx++) {
123         m_i2c_lcd_control.writeReg(LCD_DATA, data[idx]);
124     }
125 
126     return error;
127 }
128 
129 /*
130  * **************
131  *  virtual area
132  * **************
133  */
134 mraa::Result
write(std::string msg)135 SSD1306::write(std::string msg)
136 {
137     mraa::Result error = mraa::SUCCESS;
138 
139     setAddressingMode(PAGE);
140     for (std::string::size_type i = 0; i < msg.size(); ++i) {
141         writeChar(msg[i]);
142     }
143 
144     return error;
145 }
146 
147 mraa::Result
setCursor(int row,int column)148 SSD1306::setCursor(int row, int column)
149 {
150     mraa::Result error = mraa::SUCCESS;
151 
152     error = m_i2c_lcd_control.writeReg(LCD_CMD, BASE_PAGE_START_ADDR + row); // set page address
153     error = m_i2c_lcd_control.writeReg(LCD_CMD,
154                                        BASE_LOW_COLUMN_ADDR + (8 * column & 0x0F)); // set column
155                                                                                     // lower address
156     error = m_i2c_lcd_control.writeReg(LCD_CMD,
157                                        BASE_HIGH_COLUMN_ADDR +
158                                        ((8 * column >> 4) & 0x0F)); // set column higher address
159 
160     return error;
161 }
162 
163 mraa::Result
clear()164 SSD1306::clear()
165 {
166     mraa::Result error = mraa::SUCCESS;
167     uint8_t columnIdx, rowIdx;
168 
169     m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off
170     for (rowIdx = 0; rowIdx < 8; rowIdx++) {
171         setCursor(rowIdx, 0);
172 
173         // clear all columns
174         for (columnIdx = 0; columnIdx < 16; columnIdx++) {
175             writeChar(' ');
176         }
177     }
178     m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on
179     home();
180 
181     return error;
182 }
183 
184 mraa::Result
home()185 SSD1306::home()
186 {
187     return setCursor(0, 0);
188 }
189 
190 /*
191  * **************
192  *  private area
193  * **************
194  */
195 mraa::Result
writeChar(uint8_t value)196 SSD1306::writeChar(uint8_t value)
197 {
198     mraa::Result rv;
199     if (value < 0x20 || value > 0x7F) {
200         value = 0x20; // space
201     }
202 
203     for (uint8_t idx = 0; idx < 8; idx++) {
204         rv = m_i2c_lcd_control.writeReg(LCD_DATA, BasicFont[value - 32][idx]);
205     }
206 
207     return rv;
208 }
209 
210 mraa::Result
setNormalDisplay()211 SSD1306::setNormalDisplay()
212 {
213     return m_i2c_lcd_control.writeReg(LCD_CMD,
214                                       DISPLAY_CMD_SET_NORMAL_1306); // set to normal display '1' is
215                                                                     // ON
216 }
217 
218 mraa::Result
setAddressingMode(displayAddressingMode mode)219 SSD1306::setAddressingMode(displayAddressingMode mode)
220 {
221     mraa::Result rv;
222     rv =m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_MEM_ADDR_MODE); // set addressing mode
223     rv =m_i2c_lcd_control.writeReg(LCD_CMD, mode);                      // set page addressing mode
224     return rv;
225 }
226 
227 
228 mraa::Result
invert(bool i)229 SSD1306::invert(bool i)
230 {
231     mraa::Result rv;
232     if(i){
233         rv = m_i2c_lcd_control.writeReg(LCD_CMD,  DISPLAY_CMD_SET_INVERT_1306);
234     } else {
235         rv = m_i2c_lcd_control.writeReg(LCD_CMD,  DISPLAY_CMD_SET_NORMAL_1306);
236     }
237     return rv;
238 }
239 
240 
startscrollright(uint8_t start,uint8_t stop)241 void SSD1306::startscrollright(uint8_t start, uint8_t stop){
242     m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_RIGHT_HORIZONTAL_SCROLL);
243     m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
244     m_i2c_lcd_control.writeReg(LCD_CMD,start);
245     m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
246     m_i2c_lcd_control.writeReg(LCD_CMD,stop);
247     m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
248     m_i2c_lcd_control.writeReg(LCD_CMD,0XFF);
249     m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL);
250 }
251 
252 
startscrollleft(uint8_t start,uint8_t stop)253 void SSD1306::startscrollleft(uint8_t start, uint8_t stop){
254     m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_LEFT_HORIZONTAL_SCROLL);
255     m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
256     m_i2c_lcd_control.writeReg(LCD_CMD,start);
257     m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
258     m_i2c_lcd_control.writeReg(LCD_CMD,stop);
259     m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
260     m_i2c_lcd_control.writeReg(LCD_CMD,0XFF);
261     m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL);
262 }
263 
startscrolldiagright(uint8_t start,uint8_t stop)264 void SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){
265     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA);
266     m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
267     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT);
268     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
269     m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
270     m_i2c_lcd_control.writeReg(LCD_CMD, start);
271     m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
272     m_i2c_lcd_control.writeReg(LCD_CMD, stop);
273     m_i2c_lcd_control.writeReg(LCD_CMD, 0X01);
274     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL);
275 }
276 
startscrolldiagleft(uint8_t start,uint8_t stop)277 void SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){
278     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA);
279     m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
280     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT);
281     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
282     m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
283     m_i2c_lcd_control.writeReg(LCD_CMD, start);
284     m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
285     m_i2c_lcd_control.writeReg(LCD_CMD, stop);
286     m_i2c_lcd_control.writeReg(LCD_CMD, 0X01);
287     m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL);
288 }
289 
stopscroll(void)290 void SSD1306::stopscroll(void){
291     m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_DEACTIVATE_SCROLL);
292 }
293 
294 // Dim the display
295 // dim = true: display is dimmed
296 // dim = false: display is normal
dim(bool dim)297 void SSD1306::dim(bool dim) {
298     uint8_t contrast;
299 
300     if (dim) {
301     contrast = 0; // Dimmed display
302     } else {
303     if (_vccstate == SSD1306_EXTERNALVCC) {
304       contrast = 0x9F;
305     } else {
306       contrast = 0xCF;
307     }
308     }
309     // the range of contrast to too small to be really useful
310     // it is useful to dim the display
311     m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_SETCONTRAST);
312     m_i2c_lcd_control.writeReg(LCD_CMD,contrast);
313 }
314