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