aboutsummaryrefslogtreecommitdiffhomepage
path: root/workspace/TS100/src/OLED.cpp
blob: 8945f37ac5afba240ad61e349d2cd7d4cd2af359 (plain)
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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*
 * OLED.cpp
 *
 *  Created on: 29Aug.,2017
 *      Author: Ben V. Brown
 */

#include <OLED.hpp>
#include <string.h>
#include "Translation.h"
#include "cmsis_os.h"
/*Setup params for the OLED screen*/
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
/*All commands are prefixed with 0x80*/
/*Data packets are prefixed with 0x40*/
const uint8_t configLength = 50;
uint8_t OLED_Setup_Array[] = { /**/
0x80, 0xAE,/*Display off*/
0x80, 0xD5,/*Set display clock divide ratio / osc freq*/
0x80, 0x52,/*Divide ratios*/
0x80, 0xA8,/*Set Multiplex Ratio*/
0x80, 0x0F,/*16 == max brightness,39==dimmest*/
0x80, 0xC0,/*Set COM Scan direction*/
0x80, 0xD3,/*Set vertical Display offset*/
0x80, 0x00,/*0 Offset*/
0x80, 0x40,/*Set Display start line to 0*/
0x80, 0xA0,/*Set Segment remap to normal*/
0x80, 0x8D,/*Charge Pump*/
0x80, 0x14,/*Charge Pump settings*/
0x80, 0xDA,/*Set VCOM Pins hardware config*/
0x80, 0x02,/*Combination 2*/
0x80, 0x81,/*Contrast*/
0x80, 0x33,/*^51*/
0x80, 0xD9,/*Set pre-charge period*/
0x80, 0xF1,/*Pre charge period*/
0x80, 0xDB,/*Adjust VCOMH regulator ouput*/
0x80, 0x30,/*VCOM level*/
0x80, 0xA4,/*Enable the display GDDR*/
0x80, 0XA6,/*Normal display*/
0x80, 0x20,/*Memory Mode*/
0x80, 0x00,/*Wrap memory*/
0x80, 0xAF /*Display on*/
};
//Setup based on the SSD1307 and modified for the SSD1306

OLED::OLED(I2C_HandleTypeDef* i2cHandle) {
	i2c = i2cHandle;
	cursor_x = cursor_y = 0;
	currentFont = FONT_12;
	fontWidth = 12;
	inLeftHandedMode = false;
	firstStripPtr = &screenBuffer[13];
	secondStripPtr = &screenBuffer[13 + 96];
	fontHeight = 16;
	fontWidth = 12;
	displayOffset = 0;
	displayOnOffState = true;

}

void OLED::initialize() {
	HAL_Delay(5);
	HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
	HAL_Delay(5);
	//Send the setup settings
	HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, configLength, 0xFFFF);
	//displayOnOff(true);

}

//Write out the buffer to the OLEd & call any rendering objects
void OLED::refresh() {
	screenBuffer[0] = 0x80;
	screenBuffer[1] = 0x21;
	screenBuffer[2] = 0x80;
	screenBuffer[3] = inLeftHandedMode ? 0 : 32;    //display is shifted by 32 in left handed mode as driver ram is 128 wide
	screenBuffer[4] = 0x80;
	screenBuffer[5] = inLeftHandedMode ? 95 : 0x7F;    //End address of the ram segment we are writing to (96 wide)

	screenBuffer[6] = 0x80;    //Set pages to rollover after 2
	screenBuffer[7] = 0x22;
	screenBuffer[8] = 0x80;
	screenBuffer[9] = 0x00;    //start page 0
	screenBuffer[10] = 0x80;
	screenBuffer[11] = 0x01;

	screenBuffer[12] = 0x40;    //start of data marker
	taskENTER_CRITICAL();
	//Because I2C is shared, we cant task switch in the middle of the xfer

	HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, screenBuffer, 12 + 96 * 2 + 1, 0xFFFF);
	taskEXIT_CRITICAL();

}

void OLED::drawChar(char c, char PrecursorCommand) {
//prints a char to the screen
	if (c < ' ')
		return;
	//We are left with
	uint8_t* charPointer;
	//Fonts are offset to start at the space char.
	/*
	 * UTF font handling is done using the two input chars
	 * Precursor is the command char that is used to select the table
	 *
	 */
	uint16_t index = 0;
	if (PrecursorCommand == 0)
		index = (c - ' ');
	else {

		//This is for extended range
		//We decode the precursor command to find the offset
		//Latin stats at 96
		c -= 0x80;
		if (PrecursorCommand == 0xC3)
			index = (128) + (c);
		else if (PrecursorCommand == 0xC2)
			index = (96) + (c);
		else if (PrecursorCommand == 0xD0)
			index = (192) + (c);
		else if (PrecursorCommand == 0xD1)
			index = (256) + (c);
		else
			return;
	}
	charPointer = ((uint8_t*) currentFont) + ((fontWidth * (fontHeight / 8)) * index);

	if (cursor_x >= 0 && cursor_x < 96)
		drawArea(cursor_x, cursor_y, fontWidth, fontHeight, charPointer);
	cursor_x += fontWidth;
}

void OLED::displayOnOff(bool on) {

	if (on != displayOnOffState) {
		uint8_t data[6] = { 0x80, 0X8D, 0x80, 0X14, 0x80, 0XAF };    //on
		if (!on) {
			data[3] = 0x10;
			data[5] = 0xAE;
		}
		taskENTER_CRITICAL();

		HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, data, 6, 0xFFFF);
		taskEXIT_CRITICAL();
		displayOnOffState = on;
	}
}

void OLED::setRotation(bool leftHanded) {
	if (inLeftHandedMode != leftHanded) {
		//send command struct again with changes
		if (leftHanded == 1) {
			OLED_Setup_Array[11] = 0xC8;    //c1?
			OLED_Setup_Array[19] = 0xA1;
		} else if (leftHanded == 0) {
			OLED_Setup_Array[11] = 0xC0;
			OLED_Setup_Array[19] = 0xA0;
		}
		taskENTER_CRITICAL();

		HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, 50, 0xFFFF);
		taskEXIT_CRITICAL();
		inLeftHandedMode = leftHanded;
	}
}

//print a string to the current cursor location
void OLED::print(const char* str) {
	while (str[0]) {
		if (str[0] >= 0x80) {
			drawChar(str[1], str[0]);
			str++;    //skip this marker
		} else
			drawChar(str[0]);
		str++;
	}
}

void OLED::setCursor(int16_t x, int16_t y) {
	cursor_x = x;
	cursor_y = y;
}

void OLED::setFont(uint8_t fontNumber) {
	if (fontNumber == 1) {
		//small font
		currentFont = ASCII6x8;
		fontHeight = 8;
		fontWidth = 6;
	} else if (fontNumber == 2) {
		currentFont = ExtraFontChars;
		fontHeight = 16;
		fontWidth = 12;
	} else {
		currentFont = FONT_12;
		fontHeight = 16;
		fontWidth = 12;
	}
}

void OLED::drawImage(const uint8_t* buffer, uint8_t x, uint8_t width) {
	drawArea(x, 0, width, 16, buffer);
}

//maximum places is 5
void OLED::printNumber(uint16_t number, uint8_t places) {
	char buffer[6];
	buffer[5] = 0;    //null
	if (places == 5) {
		buffer[4] = '0' + number % 10;
		number /= 10;
	} else
		buffer[4] = 0;

	if (places > 3) {
		buffer[3] = '0' + number % 10;
		number /= 10;
	} else
		buffer[3] = 0;

	if (places > 2) {
		buffer[2] = '0' + number % 10;
		number /= 10;
	} else
		buffer[2] = 0;

	if (places > 1) {
		buffer[1] = '0' + number % 10;
		number /= 10;
	} else
		buffer[1] = 0;
	buffer[0] = '0' + number % 10;
	number /= 10;
	print(buffer);
}

void OLED::clearScreen() {
	memset(firstStripPtr, 0, 96);
	memset(secondStripPtr, 0, 96);
}

bool OLED::getRotation() {
	return inLeftHandedMode;
}
void OLED::drawBattery(uint8_t state) {
	if (state > 10)
		state = 10;
	drawSymbol(3 + state);
}
void OLED::drawSymbol(uint8_t symbolID) {
	//draw a symbol to the current cursor location
	setFont(2);
	drawChar(' ' + symbolID);    // space offset is in all fonts, so we pad it here and remove it later
	setFont(0);
}

//Draw an area, but y must be aligned on 0/8 offset
void OLED::drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t* ptr) {
	// Splat this from x->x+wide in two strides
	if (x < 0)
		return;    //cutoffleft
	if ((x + wide) > 96)
		return;    //cutoff right
	if (y == 0) {
		//Splat first line of data
		for (uint8_t xx = 0; xx < (wide); xx++) {
			firstStripPtr[xx + x] = ptr[xx];
		}
	}
	if (y == 8 || height == 16) {
		// Splat the second line
		for (uint8_t xx = 0; xx < wide; xx++) {
			secondStripPtr[x + xx] = ptr[xx + (height == 16 ? wide : 0)];

		}
	}
}