source: asp3_tinet_ecnl_arm/trunk/asp3_dcre/gdic/adafruit_ssd1306/adafruit_ssd1306.c@ 400

Last change on this file since 400 was 400, checked in by coas-nagasima, 5 years ago

ファイルヘッダーの更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 23.1 KB
Line 
1/*********************************************************************
2This is a library for our Monochrome OLEDs based on SSD1306 drivers
3
4 Pick one up today in the adafruit shop!
5 ------> http://www.adafruit.com/category/63_98
6
7These displays use SPI to communicate, 4 or 5 pins are required to
8interface
9
10Adafruit invests time and resources providing this open source code,
11please support Adafruit and open-source hardware by purchasing
12products from Adafruit!
13
14Written by Limor Fried/Ladyada for Adafruit Industries.
15BSD license, check license.txt for more information
16All text above, and the splash screen below must be included in any redistribution
17*********************************************************************/
18
19#include <string.h>
20#include <kernel.h>
21#include <t_syslog.h>
22#include <t_stdlib.h>
23#include <target_syssvc.h>
24#include <time.h>
25#include "syssvc/serial.h"
26#include "syssvc/syslog.h"
27#include "adafruit_ssd1306.h"
28
29#define HIGH 1
30#define LOW 0
31
32uint16_t lcd_init_height = SSD1306_LCDHEIGHT;
33uint16_t lcd_init_width = SSD1306_LCDWIDTH;
34
35// the memory buffer for the LCD
36
37static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = {
380x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
390x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
400x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
410x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
420x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
470x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00,
480x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
490x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF,
50#if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16)
510xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
520x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80,
530x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8,
540xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
550xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80,
560x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
570x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01,
580x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF,
590xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00,
600x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF,
610x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF,
620xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
630x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F,
640x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC,
650xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03,
660x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01,
670x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00,
680x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
690x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03,
700x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71#if (SSD1306_LCDHEIGHT == 64)
720x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x1F, 0x0F,
730x87, 0xC7, 0xF7, 0xFF, 0xFF, 0x1F, 0x1F, 0x3D, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0x7C, 0x7D, 0xFF,
740xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x00, 0x30, 0x30, 0x00, 0x00,
750x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
760x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
770x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00,
780x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
790x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
800x00, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F,
810x0F, 0x07, 0x1F, 0x7F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0,
820x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00,
830x00, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xF8, 0x1C, 0x0E,
840x06, 0x06, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFC,
850xFE, 0xFC, 0x00, 0x18, 0x3C, 0x7E, 0x66, 0xE6, 0xCE, 0x84, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x06,
860x06, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xC0, 0xF8,
870xFC, 0x4E, 0x46, 0x46, 0x46, 0x4E, 0x7C, 0x78, 0x40, 0x18, 0x3C, 0x76, 0xE6, 0xCE, 0xCC, 0x80,
880x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
890x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x03,
900x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
910x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x0C,
920x18, 0x18, 0x0C, 0x06, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0E, 0x0C, 0x18, 0x0C, 0x0F,
930x07, 0x01, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00,
940x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x07,
950x07, 0x0C, 0x0C, 0x18, 0x1C, 0x0C, 0x06, 0x06, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07,
960x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
970x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
980x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
990x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1000x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1010x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1020x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1030x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
104#endif
105#endif
106};
107
108void lcd_fastSPIwrite(LCD_Handler_t *lcd, uint8_t d);
109#define lcd_swap(a, b) { int16_t t = a; a = b; b = t; }
110
111// the most basic function, set a single pixel
112void lcd_drawPixel(LCD_Handler_t *lcd, int16_t x, int16_t y, uint16_t color) {
113 if ((x < 0) || (x >= lcd->_width) || (y < 0) || (y >= lcd->_height))
114 return;
115
116 // check rotation, move pixel around if necessary
117 switch (lcd->rotation) {
118 case 1:
119 lcd_swap(x, y);
120 x = lcd->_width - x - 1;
121 break;
122 case 2:
123 x = lcd->_width - x - 1;
124 y = lcd->_height - y - 1;
125 break;
126 case 3:
127 lcd_swap(x, y);
128 y = lcd->_height - y - 1;
129 break;
130 }
131
132 // x is which column
133 switch (color)
134 {
135 case WHITE: buffer[x + (y / 8)*SSD1306_LCDWIDTH] |= (1 << (y & 7)); break;
136 case BLACK: buffer[x + (y / 8)*SSD1306_LCDWIDTH] &= ~(1 << (y & 7)); break;
137 case INVERSE: buffer[x + (y / 8)*SSD1306_LCDWIDTH] ^= (1 << (y & 7)); break;
138 }
139}
140
141void lcd_init1(LCD_Handler_t *lcd, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS)
142{
143 lcd->_width = SSD1306_LCDWIDTH;
144 lcd->_height = SSD1306_LCDHEIGHT;
145 gpio_init(&lcd->cs, CS);
146 gpio_init(&lcd->rst, RST);
147 gpio_init(&lcd->dc, DC);
148 gpio_init(&lcd->sclk, SCLK);
149 gpio_init(&lcd->sid, SID);
150 lcd->hwSPI = false;
151}
152
153// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset
154void lcd_init2(LCD_Handler_t *lcd, int8_t DC, int8_t RST, int8_t CS)
155{
156 lcd->_width = SSD1306_LCDWIDTH;
157 lcd->_height = SSD1306_LCDHEIGHT;
158 gpio_init(&lcd->dc, DC);
159 gpio_init(&lcd->rst, RST);
160 gpio_init(&lcd->cs, CS);
161 lcd->hwSPI = true;
162}
163
164// initializer for I2C - we only indicate the reset pin!
165void lcd_init3(LCD_Handler_t *lcd, int8_t reset)
166{
167 lcd->_width = SSD1306_LCDWIDTH;
168 lcd->_height = SSD1306_LCDHEIGHT;
169 gpio_init(&lcd->sclk, NC);
170 gpio_init(&lcd->dc, NC);
171 gpio_init(&lcd->cs, NC);
172 gpio_init(&lcd->sid, NC);
173 gpio_init(&lcd->rst, reset);
174}
175
176void lcd_begin(LCD_Handler_t *lcd, uint8_t vccstate, uint8_t i2caddr, bool_t reset) {
177 lcd->_vccstate = vccstate;
178 lcd->_i2caddr = i2caddr;
179
180 // set pin directions
181 if (lcd->sid.pin != NC) {
182 gpio_dir(&lcd->dc, PIN_OUTPUT);
183 gpio_dir(&lcd->cs, PIN_OUTPUT);
184 if (!lcd->hwSPI) {
185 // set pins for software-SPI
186 gpio_dir(&lcd->sid, PIN_OUTPUT);
187 gpio_dir(&lcd->sclk, PIN_OUTPUT);
188 }
189 if (lcd->hwSPI) {
190 spi_format(&lcd->spi, 8, 0, 0);
191 spi_master_write(&lcd->spi, 8000000);
192 }
193 }
194 else
195 {
196 // I2C Init
197 //i2c_begin(&lcd->i2c);
198#ifdef __SAM3X8E__
199 // Force 400 KHz I2C, rawr! (Uses pins 20, 21 for SDA, SCL)
200 TWI1->TWI_CWGR = 0;
201 TWI1->TWI_CWGR = ((VARIANT_MCK / (2 * 400000)) - 4) * 0x101;
202#endif
203 }
204 if ((reset) && (lcd->rst.pin != NC)) {
205 // Setup reset pin direction (used by both SPI and I2C)
206 gpio_dir(&lcd->rst, PIN_OUTPUT);
207 gpio_write(&lcd->rst, HIGH);
208 // VDD (3.3V) goes high at start, lets just chill for a ms
209 dly_tsk(1 * 1000);
210 // bring reset low
211 gpio_write(&lcd->rst, LOW);
212 // wait 10ms
213 dly_tsk(10 * 1000);
214 // bring out of reset
215 gpio_write(&lcd->rst, HIGH);
216 // turn on VCC (9V?)
217 }
218
219 // Init sequence
220 lcd_command(lcd, SSD1306_DISPLAYOFF); // 0xAE
221 lcd_command(lcd, SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
222 lcd_command(lcd, 0x80); // the suggested ratio 0x80
223
224 lcd_command(lcd, SSD1306_SETMULTIPLEX); // 0xA8
225 lcd_command(lcd, SSD1306_LCDHEIGHT - 1);
226
227 lcd_command(lcd, SSD1306_SETDISPLAYOFFSET); // 0xD3
228 lcd_command(lcd, 0x0); // no offset
229 lcd_command(lcd, SSD1306_SETSTARTLINE | 0x0); // line #0
230 lcd_command(lcd, SSD1306_CHARGEPUMP); // 0x8D
231 if (vccstate == SSD1306_EXTERNALVCC)
232 {
233 lcd_command(lcd, 0x10);
234 }
235 else
236 {
237 lcd_command(lcd, 0x14);
238 }
239 lcd_command(lcd, SSD1306_MEMORYMODE); // 0x20
240 lcd_command(lcd, 0x00); // 0x0 act like ks0108
241 lcd_command(lcd, SSD1306_SEGREMAP | 0x1);
242 lcd_command(lcd, SSD1306_COMSCANDEC);
243
244#if defined SSD1306_128_32
245 lcd_command(lcd, SSD1306_SETCOMPINS); // 0xDA
246 lcd_command(lcd, 0x02);
247 lcd_command(lcd, SSD1306_SETCONTRAST); // 0x81
248 lcd_command(lcd, 0x8F);
249
250#elif defined SSD1306_128_64
251 lcd_command(lcd, SSD1306_SETCOMPINS); // 0xDA
252 lcd_command(lcd, 0x12);
253 lcd_command(lcd, SSD1306_SETCONTRAST); // 0x81
254 if (vccstate == SSD1306_EXTERNALVCC)
255 {
256 lcd_command(lcd, 0x9F);
257 }
258 else
259 {
260 lcd_command(lcd, 0xCF);
261 }
262
263#elif defined SSD1306_96_16
264 lcd_command(lcd, SSD1306_SETCOMPINS); // 0xDA
265 lcd_command(lcd, 0x2); //ada x12
266 lcd_command(lcd, SSD1306_SETCONTRAST); // 0x81
267 if (vccstate == SSD1306_EXTERNALVCC)
268 {
269 lcd_command(lcd, 0x10);
270 }
271 else
272 {
273 lcd_command(lcd, 0xAF);
274 }
275
276#endif
277
278 lcd_command(lcd, SSD1306_SETPRECHARGE); // 0xd9
279 if (vccstate == SSD1306_EXTERNALVCC)
280 {
281 lcd_command(lcd, 0x22);
282 }
283 else
284 {
285 lcd_command(lcd, 0xF1);
286 }
287 lcd_command(lcd, SSD1306_SETVCOMDETECT); // 0xDB
288 lcd_command(lcd, 0x40);
289 lcd_command(lcd, SSD1306_DISPLAYALLON_RESUME); // 0xA4
290 lcd_command(lcd, SSD1306_NORMALDISPLAY); // 0xA6
291
292 lcd_command(lcd, SSD1306_DEACTIVATE_SCROLL);
293
294 lcd_command(lcd, SSD1306_DISPLAYON);//--turn on oled panel
295}
296
297
298void lcd_invertDisplay(LCD_Handler_t *lcd, uint8_t i) {
299 if (i) {
300 lcd_command(lcd, SSD1306_INVERTDISPLAY);
301 }
302 else {
303 lcd_command(lcd, SSD1306_NORMALDISPLAY);
304 }
305}
306
307void lcd_command(LCD_Handler_t *lcd, uint8_t c) {
308 if (lcd->sid.pin != NC)
309 {
310 // SPI
311 gpio_write(&lcd->cs, HIGH);
312 gpio_write(&lcd->dc, LOW);
313 gpio_write(&lcd->cs, LOW);
314 lcd_fastSPIwrite(lcd, c);
315 gpio_write(&lcd->cs, HIGH);
316 }
317 else
318 {
319 // I2C
320 uint8_t data[2] = { 0x00/* Co = 0, D/C = 0*/, c };
321 i2c_write(&lcd->i2c, lcd->_i2caddr, data, sizeof(data), 1);
322 }
323}
324
325// startscrollright
326// Activate a right handed scroll for rows start through stop
327// Hint, the display is 16 rows tall. To scroll the whole display, run:
328// display.scrollright(0x00, 0x0F)
329void lcd_startscrollright(LCD_Handler_t *lcd, uint8_t start, uint8_t stop) {
330 lcd_command(lcd, SSD1306_RIGHT_HORIZONTAL_SCROLL);
331 lcd_command(lcd, 0X00);
332 lcd_command(lcd, start);
333 lcd_command(lcd, 0X00);
334 lcd_command(lcd, stop);
335 lcd_command(lcd, 0X00);
336 lcd_command(lcd, 0XFF);
337 lcd_command(lcd, SSD1306_ACTIVATE_SCROLL);
338}
339
340// startscrollleft
341// Activate a right handed scroll for rows start through stop
342// Hint, the display is 16 rows tall. To scroll the whole display, run:
343// display.scrollright(0x00, 0x0F)
344void lcd_startscrollleft(LCD_Handler_t *lcd, uint8_t start, uint8_t stop) {
345 lcd_command(lcd, SSD1306_LEFT_HORIZONTAL_SCROLL);
346 lcd_command(lcd, 0X00);
347 lcd_command(lcd, start);
348 lcd_command(lcd, 0X00);
349 lcd_command(lcd, stop);
350 lcd_command(lcd, 0X00);
351 lcd_command(lcd, 0XFF);
352 lcd_command(lcd, SSD1306_ACTIVATE_SCROLL);
353}
354
355// startscrolldiagright
356// Activate a diagonal scroll for rows start through stop
357// Hint, the display is 16 rows tall. To scroll the whole display, run:
358// display.scrollright(0x00, 0x0F)
359void lcd_startscrolldiagright(LCD_Handler_t *lcd, uint8_t start, uint8_t stop) {
360 lcd_command(lcd, SSD1306_SET_VERTICAL_SCROLL_AREA);
361 lcd_command(lcd, 0X00);
362 lcd_command(lcd, SSD1306_LCDHEIGHT);
363 lcd_command(lcd, SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
364 lcd_command(lcd, 0X00);
365 lcd_command(lcd, start);
366 lcd_command(lcd, 0X00);
367 lcd_command(lcd, stop);
368 lcd_command(lcd, 0X01);
369 lcd_command(lcd, SSD1306_ACTIVATE_SCROLL);
370}
371
372// startscrolldiagleft
373// Activate a diagonal scroll for rows start through stop
374// Hint, the display is 16 rows tall. To scroll the whole display, run:
375// display.scrollright(0x00, 0x0F)
376void lcd_startscrolldiagleft(LCD_Handler_t *lcd, uint8_t start, uint8_t stop) {
377 lcd_command(lcd, SSD1306_SET_VERTICAL_SCROLL_AREA);
378 lcd_command(lcd, 0X00);
379 lcd_command(lcd, SSD1306_LCDHEIGHT);
380 lcd_command(lcd, SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
381 lcd_command(lcd, 0X00);
382 lcd_command(lcd, start);
383 lcd_command(lcd, 0X00);
384 lcd_command(lcd, stop);
385 lcd_command(lcd, 0X01);
386 lcd_command(lcd, SSD1306_ACTIVATE_SCROLL);
387}
388
389void lcd_stopscroll(LCD_Handler_t *lcd) {
390 lcd_command(lcd, SSD1306_DEACTIVATE_SCROLL);
391}
392
393// Dim the display
394// dim = true: display is dimmed
395// dim = false: display is normal
396void lcd_dim(LCD_Handler_t *lcd, bool_t dim) {
397 uint8_t contrast;
398
399 if (dim) {
400 contrast = 0; // Dimmed display
401 }
402 else {
403 if (lcd->_vccstate == SSD1306_EXTERNALVCC) {
404 contrast = 0x9F;
405 }
406 else {
407 contrast = 0xCF;
408 }
409 }
410 // the range of contrast to too small to be really useful
411 // it is useful to dim the display
412 lcd_command(lcd, SSD1306_SETCONTRAST);
413 lcd_command(lcd, contrast);
414}
415
416void lcd_display(LCD_Handler_t *lcd) {
417 lcd_command(lcd, SSD1306_COLUMNADDR);
418 lcd_command(lcd, 0); // Column start address (0 = reset)
419 lcd_command(lcd, SSD1306_LCDWIDTH - 1); // Column end address (127 = reset)
420
421 lcd_command(lcd, SSD1306_PAGEADDR);
422 lcd_command(lcd, 0); // Page start address (0 = reset)
423#if SSD1306_LCDHEIGHT == 64
424 lcd_command(lcd, 7); // Page end address
425#endif
426#if SSD1306_LCDHEIGHT == 32
427 lcd_command(lcd, 3); // Page end address
428#endif
429#if SSD1306_LCDHEIGHT == 16
430 lcd_command(lcd, 1); // Page end address
431#endif
432
433 if (lcd->sid.pin != NC)
434 {
435 // SPI
436 gpio_write(&lcd->cs, HIGH);
437 gpio_write(&lcd->dc, HIGH);
438 gpio_write(&lcd->cs, LOW);
439
440 for (uint16_t i = 0; i < (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT / 8); i++) {
441 lcd_fastSPIwrite(lcd, buffer[i]);
442 }
443 gpio_write(&lcd->cs, HIGH);
444 }
445 else
446 {
447 // save I2C bitrate
448#ifdef TWBR
449 uint8_t twbrbackup = TWBR;
450 TWBR = 12; // upgrade to 400KHz!
451#endif
452 //Serial.println(TWBR, DEC);
453 //Serial.println(TWSR & 0x3, DEC);
454
455 // I2C
456 uint8_t data[17] = { 0x40, };
457 for (uint16_t i = 0; i < (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT / 8); i+=16) {
458 // send a bunch of data in one xmission
459 memcpy(&data[1], &buffer[i], 16);
460 i2c_write(&lcd->i2c, lcd->_i2caddr, data, sizeof(data), 1);
461 }
462#ifdef TWBR
463 TWBR = twbrbackup;
464#endif
465 }
466}
467
468// clear everything
469void lcd_clearDisplay(LCD_Handler_t *lcd) {
470 memset(buffer, 0, (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT / 8));
471}
472
473void lcd_fastSPIwrite(LCD_Handler_t *lcd, uint8_t d)
474{
475 if (lcd->hwSPI) {
476 spi_master_write(&lcd->spi, d);
477 }
478 else {
479 for (uint8_t bit = 0x80; bit; bit >>= 1) {
480 gpio_write(&lcd->sclk, LOW);
481 if (d & bit) gpio_write(&lcd->sid, HIGH);
482 else gpio_write(&lcd->sid, LOW);
483 gpio_write(&lcd->sclk, HIGH);
484 }
485 }
486}
487
488void lcd_drawFastHLine(LCD_Handler_t *lcd, int16_t x, int16_t y, int16_t w, uint16_t color) {
489 bool_t bSwap = false;
490 switch (lcd->rotation) {
491 case 0:
492 // 0 degree rotation, do nothing
493 break;
494 case 1:
495 // 90 degree rotation, swap x & y for rotation, then invert x
496 bSwap = true;
497 lcd_swap(x, y);
498 x = lcd->_width - x - 1;
499 break;
500 case 2:
501 // 180 degree rotation, invert x and y - then shift y around for height.
502 x = lcd->_width - x - 1;
503 y = lcd->_height - y - 1;
504 x -= (w - 1);
505 break;
506 case 3:
507 // 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h)
508 bSwap = true;
509 lcd_swap(x, y);
510 y = lcd->_height - y - 1;
511 y -= (w - 1);
512 break;
513 }
514
515 if (bSwap) {
516 lcd_drawFastVLineInternal(lcd, x, y, w, color);
517 }
518 else {
519 lcd_drawFastHLineInternal(lcd, x, y, w, color);
520 }
521}
522
523void lcd_drawFastHLineInternal(LCD_Handler_t *lcd, int16_t x, int16_t y, int16_t w, uint16_t color) {
524 // Do bounds/limit checks
525 if (y < 0 || y >= lcd->_height) { return; }
526
527 // make sure we don't try to draw below 0
528 if (x < 0) {
529 w += x;
530 x = 0;
531 }
532
533 // make sure we don't go off the edge of the display
534 if ((x + w) > lcd->_width) {
535 w = (lcd->_width - x);
536 }
537
538 // if our width is now negative, punt
539 if (w <= 0) { return; }
540
541 // set up the pointer for movement through the buffer
542 register uint8_t *pBuf = buffer;
543 // adjust the buffer pointer for the current row
544 pBuf += ((y / 8) * SSD1306_LCDWIDTH);
545 // and offset x columns in
546 pBuf += x;
547
548 register uint8_t mask = 1 << (y & 7);
549
550 switch (color)
551 {
552 case WHITE: while (w--) { *pBuf++ |= mask; }; break;
553 case BLACK: mask = ~mask; while (w--) { *pBuf++ &= mask; }; break;
554 case INVERSE: while (w--) { *pBuf++ ^= mask; }; break;
555 }
556}
557
558void lcd_drawFastVLine(LCD_Handler_t *lcd, int16_t x, int16_t y, int16_t h, uint16_t color) {
559 bool_t bSwap = false;
560 switch (lcd->rotation) {
561 case 0:
562 break;
563 case 1:
564 // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
565 bSwap = true;
566 lcd_swap(x, y);
567 x = lcd->_width - x - 1;
568 x -= (h - 1);
569 break;
570 case 2:
571 // 180 degree rotation, invert x and y - then shift y around for height.
572 x = lcd->_width - x - 1;
573 y = lcd->_height - y - 1;
574 y -= (h - 1);
575 break;
576 case 3:
577 // 270 degree rotation, swap x & y for rotation, then invert y
578 bSwap = true;
579 lcd_swap(x, y);
580 y = lcd->_height - y - 1;
581 break;
582 }
583
584 if (bSwap) {
585 lcd_drawFastHLineInternal(lcd, x, y, h, color);
586 }
587 else {
588 lcd_drawFastVLineInternal(lcd, x, y, h, color);
589 }
590}
591
592
593void lcd_drawFastVLineInternal(LCD_Handler_t *lcd, int16_t x, int16_t __y, int16_t __h, uint16_t color) {
594
595 // do nothing if we're off the left or right side of the screen
596 if (x < 0 || x >= lcd->_width) { return; }
597
598 // make sure we don't try to draw below 0
599 if (__y < 0) {
600 // __y is negative, this will subtract enough from __h to account for __y being 0
601 __h += __y;
602 __y = 0;
603
604 }
605
606 // make sure we don't go past the height of the display
607 if ((__y + __h) > lcd->_height) {
608 __h = (lcd->_height - __y);
609 }
610
611 // if our height is now negative, punt
612 if (__h <= 0) {
613 return;
614 }
615
616 // this display doesn't need ints for coordinates, use local byte registers for faster juggling
617 register uint8_t y = __y;
618 register uint8_t h = __h;
619
620
621 // set up the pointer for fast movement through the buffer
622 register uint8_t *pBuf = buffer;
623 // adjust the buffer pointer for the current row
624 pBuf += ((y / 8) * SSD1306_LCDWIDTH);
625 // and offset x columns in
626 pBuf += x;
627
628 // do the first partial byte, if necessary - this requires some masking
629 register uint8_t mod = (y & 7);
630 if (mod) {
631 // mask off the high n bits we want to set
632 mod = 8 - mod;
633
634 // note - lookup table results in a nearly 10% performance improvement in fill* functions
635 // register uint8_t mask = ~(0xFF >> (mod));
636 static uint8_t premask[8] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
637 register uint8_t mask = premask[mod];
638
639 // adjust the mask if we're not going to reach the end of this byte
640 if (h < mod) {
641 mask &= (0XFF >> (mod - h));
642 }
643
644 switch (color)
645 {
646 case WHITE: *pBuf |= mask; break;
647 case BLACK: *pBuf &= ~mask; break;
648 case INVERSE: *pBuf ^= mask; break;
649 }
650
651 // fast exit if we're done here!
652 if (h < mod) { return; }
653
654 h -= mod;
655
656 pBuf += SSD1306_LCDWIDTH;
657 }
658
659 // write solid bytes while we can - effectively doing 8 rows at a time
660 if (h >= 8) {
661 if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop
662 do {
663 *pBuf = ~(*pBuf);
664
665 // adjust the buffer forward 8 rows worth of data
666 pBuf += SSD1306_LCDWIDTH;
667
668 // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
669 h -= 8;
670 } while (h >= 8);
671 }
672 else {
673 // store a local value to work with
674 register uint8_t val = (color == WHITE) ? 255 : 0;
675
676 do {
677 // write our value in
678 *pBuf = val;
679
680 // adjust the buffer forward 8 rows worth of data
681 pBuf += SSD1306_LCDWIDTH;
682
683 // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
684 h -= 8;
685 } while (h >= 8);
686 }
687 }
688
689 // now do the final partial byte, if necessary
690 if (h) {
691 mod = h & 7;
692 // this time we want to mask the low bits of the byte, vs the high bits we did above
693 // register uint8_t mask = (1 << mod) - 1;
694 // note - lookup table results in a nearly 10% performance improvement in fill* functions
695 static uint8_t postmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
696 register uint8_t mask = postmask[mod];
697 switch (color)
698 {
699 case WHITE: *pBuf |= mask; break;
700 case BLACK: *pBuf &= ~mask; break;
701 case INVERSE: *pBuf ^= mask; break;
702 }
703 }
704}
Note: See TracBrowser for help on using the repository browser.