-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathssd1306.c
262 lines (206 loc) · 6.81 KB
/
ssd1306.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#include <string.h>
#include "hal.h"
#include "ssd1306.h"
#include "ssd1306_font.c"
/* Steal from https://stm32f4-discovery.net/2015/05/library-61-ssd1306-oled-i2c-lcd-for-stm32f4xx/ */
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static msg_t wrCmd(void *ip, uint8_t cmd) {
const SSD1306Driver *drvp = (const SSD1306Driver *)ip;
msg_t ret;
uint8_t txbuf[] = { 0x00, cmd };
i2cAcquireBus(drvp->config->i2cp);
i2cStart(drvp->config->i2cp, drvp->config->i2ccfg);
ret = i2cMasterTransmitTimeout(drvp->config->i2cp, drvp->config->sad,
txbuf, sizeof(txbuf), NULL, 0, TIME_INFINITE);
i2cReleaseBus(drvp->config->i2cp);
return ret;
}
static msg_t wrDat(void *ip, uint8_t *txbuf, uint16_t len) {
const SSD1306Driver *drvp = (const SSD1306Driver *)ip;
msg_t ret;
i2cAcquireBus(drvp->config->i2cp);
i2cStart(drvp->config->i2cp, drvp->config->i2ccfg);
ret = i2cMasterTransmitTimeout(drvp->config->i2cp, drvp->config->sad,
txbuf, len, NULL, 0, TIME_INFINITE);
i2cReleaseBus(drvp->config->i2cp);
return ret;
}
static void updateScreen(void *ip) {
SSD1306Driver *drvp = (SSD1306Driver *)ip;
uint8_t idx;
for (idx = 0; idx < 8; idx++) {
wrCmd(drvp, 0xB0 + idx);
wrCmd(drvp, 0x00);
wrCmd(drvp, 0x10);
// Write multi data
wrDat(drvp, &drvp->fb[SSD1306_WIDTH_FIXED * idx], SSD1306_WIDTH_FIXED);
}
}
static void toggleInvert(void *ip) {
SSD1306Driver *drvp = (SSD1306Driver *)ip;
uint16_t idx;
// Toggle invert
drvp->inv = !drvp->inv;
for (idx = 0; idx < sizeof(drvp->fb); idx++) {
if (idx % SSD1306_WIDTH_FIXED == 0) continue;
drvp->fb[idx] = ~drvp->fb[idx];
}
}
static void fillScreen(void *ip, ssd1306_color_t color) {
SSD1306Driver *drvp = (SSD1306Driver *)ip;
uint8_t idx;
for (idx = 0; idx < 8; idx++) {
drvp->fb[SSD1306_WIDTH_FIXED * idx] = 0x40;
memset(&drvp->fb[SSD1306_WIDTH_FIXED * idx + 1],
color == SSD1306_COLOR_BLACK ? 0x00 : 0xff, SSD1306_WIDTH);
}
}
static void drawPixel(void *ip, uint8_t x, uint8_t y, ssd1306_color_t color) {
SSD1306Driver *drvp = (SSD1306Driver *)ip;
if (x > SSD1306_WIDTH || y > SSD1306_HEIGHT) return;
// Check if pixels are inverted
if (drvp->inv) {
color = (ssd1306_color_t)!color;
}
// Set color
if (color == SSD1306_COLOR_WHITE) {
drvp->fb[x + (y / 8) * SSD1306_WIDTH_FIXED + 1] |= 1 << (y % 8);
} else {
drvp->fb[x + (y / 8) * SSD1306_WIDTH_FIXED + 1] &= ~(1 << (y % 8));
}
}
static void gotoXy(void *ip, uint8_t x, uint8_t y) {
SSD1306Driver *drvp = (SSD1306Driver *)ip;
drvp->x = x;
drvp->y = y;
}
static char PUTC(void *ip, char ch, const ssd1306_font_t *font, ssd1306_color_t color) {
SSD1306Driver *drvp = (SSD1306Driver *)ip;
uint32_t i, b, j;
// Check available space in OLED
if (drvp->x + font->fw >= SSD1306_WIDTH ||
drvp->y + font->fh >= SSD1306_HEIGHT) {
return 0;
}
// Go through font
for (i = 0; i < font->fh; i++) {
b = font->dt[(ch - 32) * font->fh + i];
for (j = 0; j < font->fw; j++) {
if ((b << j) & 0x8000) {
drawPixel(drvp, drvp->x + j, drvp->y + i, color);
} else {
drawPixel(drvp, drvp->x + j, drvp->y + i,(ssd1306_color_t)! color);
}
}
}
//dirty hack for 7x10 font
/* for (i = 0; i < font->fh; i++) {
b = font->dt[(ch - 32) * font->fh + i];
for (j = 1; j < (uint32_t)(font->fw + 1); j++) {
if ((b << j) & 0x8000) {
drawPixel(drvp, drvp->x + j, drvp->y + i + 1, color);
} else {
drawPixel(drvp, drvp->x + j, drvp->y + i + 1,(ssd1306_color_t)! color);
}
}
}
*/
// Increase pointer
drvp->x += font->fw;
// Return character written
return ch;
}
static char PUTS(void *ip, char *str, const ssd1306_font_t *font, ssd1306_color_t color) {
// Write characters
while (*str) {
// Write character by character
if (PUTC(ip, *str, font, color) != *str) {
// Return error
return *str;
}
// Increase string pointer
str++;
}
// Everything OK, zero should be returned
return *str;
}
static void setDisplay(void *ip, uint8_t on) {
wrCmd(ip, 0x8D);
wrCmd(ip, on ? 0x14 : 0x10);
wrCmd(ip, 0xAE);
}
static const struct SSD1306VMT vmt_ssd1306 = {
updateScreen, toggleInvert, fillScreen, drawPixel,
gotoXy, PUTC, PUTS, setDisplay
};
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void ssd1306ObjectInit(SSD1306Driver *devp) {
devp->vmt = &vmt_ssd1306;
devp->config = NULL;
devp->state = SSD1306_STOP;
}
void ssd1306Start(SSD1306Driver *devp, const SSD1306Config *config) {
const uint8_t cmds[] = {
0xAE, // display off
0x20, // Set memory address
0x10, // 0x00: horizontal addressing mode, 0x01: vertical addressing mode
// 0x10: Page addressing mode(RESET), 0x11: invalid
0xB0, // Set page start address for page addressing mode: 0 ~ 7
0xC8, // Set COM output scan direction
0x00, // Set low column address
0x10, // Set height column address
0x40, // Set start line address
0x81, // Set contrast control register
0xFF,
0xA1, // Set segment re-map 0 to 127
0xA6, // Set normal display
0xA8, // Set multiplex ratio(1 to 64)
0x3F,
0xA4, // 0xa4: ouput follows RAM content, 0xa5: ouput ignores RAM content
0xD3, // Set display offset
0x00, // Not offset
0xD5, // Set display clock divide ratio/oscillator frequency
0xF0, // Set divide ration
0xD9, // Set pre-charge period
0x22,
0xDA, // Set COM pins hardware configuration
0x12,
0xDB, // Set VCOMH
0x20, // 0x20: 0.77*Vcc
0x8D, // Set DC-DC enable
0x14,
0xAF, // turn on SSD1306panel
};
uint8_t idx;
chDbgCheck((devp != NULL) && (config != NULL));
chDbgAssert((devp->state == SSD1306_STOP) || (devp->state == SSD1306_READY),
"ssd1306Start(), invalid state");
devp->config = config;
// A little delay
chThdSleepMilliseconds(100);
// OLED initialize
for (idx = 0; idx < sizeof(cmds) / sizeof(cmds[0]); idx++) {
wrCmd(devp, cmds[idx]);
}
// Clear screen
fillScreen(devp, SSD1306_COLOR_WHITE);
// Update screen
updateScreen(devp);
// Set default value
devp->x = 0;
devp->y = 0;
devp->state = SSD1306_READY;
}
void ssd1306Stop(SSD1306Driver *devp) {
chDbgAssert((devp->state == SSD1306_STOP) || (devp->state == SSD1306_READY),
"ssd1306Stop(), invalid state");
if (devp->state == SSD1306_READY) {
// Turn off display
setDisplay(devp, 0);
}
devp->state = SSD1306_STOP;
}