Coverage Report

Created: 2026-03-15 15:38

/home/runner/work/EmbeddedWatch/EmbeddedWatch/src/oled.c
Line
Count
Source (jump to first uncovered line)
1
#include <stdbool.h>
2
#include <string.h>
3
#include <math.h>
4
#include "hal/hal.h"
5
#include "hal/flash.h"
6
#include "common.h"
7
#include "twi.h"
8
#include "oled.h"
9
#include "oled_defs.h"
10
#include "datasources.h"
11
12
#define DATA_BUFFER_SIZE 100
13
14
typedef struct OledDisplaySettings
15
{
16
    OledCursorPosition cursorPosition;
17
18
    FontSize fontSize;
19
    WrappingStyle wrappingStyle;
20
} OledDisplaySettings;
21
22
typedef struct OledCharacterWriteMetadata
23
{
24
    FlashChar flashChar;
25
26
    uint8_t columnStart;
27
    uint8_t columnEnd;
28
    
29
    uint8_t pageStart;
30
    uint8_t pageEnd;
31
} OledCharacterWriteMetadata;
32
33
static OledDisplaySettings oled_settings = 
34
{
35
    .cursorPosition.column = 0,
36
    .cursorPosition.row = 0,
37
    .fontSize = FONT_SMALL,
38
    .wrappingStyle = WRAP_NEXTLINE
39
};
40
41
typedef enum OLEDState
42
{
43
    OLED_IDLE,
44
    OLED_INIT,
45
    OLED_CLEAR_ADDR,
46
    OLED_CLEAR_DATA,
47
    OLED_WRITE_ADDR,
48
    OLED_WRITE_DATA
49
} OLEDState;
50
51
typedef struct OLEDContext
52
{
53
    OLEDState state;
54
    OLEDState requestedState;
55
    char string[DATA_BUFFER_SIZE];
56
    uint8_t index;
57
    OledCharacterWriteMetadata writeMetadata;
58
    uint8_t data[DATA_BUFFER_SIZE];
59
    ram_ctx_t ramContext;
60
    nop_ctx_t nopContext;
61
} OLEDContext;
62
63
static OLEDContext oled =
64
{
65
    .state = OLED_INIT,
66
    .requestedState = OLED_IDLE,
67
    .index = 0
68
};
69
70
void oled_init(void) 
71
0
{
72
0
    oled.ramContext.data = oled.data;
73
74
0
    uint8_t command[] = 
75
0
    {
76
0
        COMMAND_MODE,
77
0
        DISPLAY_OFF,
78
0
        CLOCK_DIV_RATIO, CLOCK_DIV_RATIO_RESET,
79
0
        MULTIPLEX_RATIO, MULTIPLEX_32_ROWS,
80
0
        DISPLAY_OFFSET, ZERO_BYTE,
81
0
        DISPLAY_START_LINE, ZERO_BYTE,
82
0
        CHARGE_PUMP, CHARGE_PUMP_ON,
83
0
        ADDRESSING_MODE, ADDRESSING_HORIZONTAL,
84
0
        HORIZONTAL_ORIENTATION_FLIPPED,
85
0
        VERTICAL_ORIENTATION_FLIPPED,
86
0
        COM_PINS, COM_32_ROWS,
87
0
        CONTRAST_CONTROL, CONTRAST_HIGH,
88
0
        PRE_CHARGE_PERIOD, PRE_CHARGE_DEFAULT,
89
0
        VCOMH, VCOMH_DEFAULT,
90
0
        DISPLAY_FROM_RAM,
91
0
        DISPLAY_NORMAL,
92
0
        DISPLAY_ON
93
0
    };
94
    
95
0
    memcpy(oled.data, command, sizeof(command));
96
97
0
    twi_write_to_slave(SSD_OLED_ADDRESS, &oled.ramContext, ram_read, sizeof(command));
98
0
}
99
100
OledCharacterWriteMetadata oled_get_display_configuration(char character)
101
0
{
102
0
    OledCharacterWriteMetadata config;
103
104
0
    config.flashChar.character = character;
105
0
    config.flashChar.fontSize = oled_settings.fontSize;
106
107
0
    flash_get_char_image(&config.flashChar);
108
109
0
    config.columnStart = (oled_settings.cursorPosition.column*config.flashChar.fontMetaData->dimension);
110
0
    config.columnEnd = config.columnStart + config.flashChar.fontMetaData->dimension - 1;
111
112
0
    config.pageStart = oled_settings.cursorPosition.row;
113
0
    config.pageEnd = config.pageStart + config.flashChar.fontMetaData->pagesPerRow - 1;
114
115
0
    return config;
116
0
}
117
118
void oled_send_cursor(uint8_t columnStart, uint8_t columnEnd, uint8_t pageStart, uint8_t pageEnd) 
119
0
{
120
0
    uint8_t cursorCommand[] = 
121
0
    {
122
0
        COMMAND_MODE,
123
0
        COLUMN_SELECTOR, columnStart, columnEnd,
124
0
        PAGE_SELECTOR, pageStart, pageEnd
125
0
    };
126
    
127
0
    memcpy(oled.data, cursorCommand, sizeof(cursorCommand));
128
129
0
    twi_write_to_slave(SSD_OLED_ADDRESS, &oled.ramContext, ram_read, sizeof(cursorCommand));
130
0
}
131
132
void oled_write_character(OledCharacterWriteMetadata config) 
133
0
{
134
0
    oled.data[0] = DATA_MODE;
135
0
    memcpy(oled.data + 1, config.flashChar.data, config.flashChar.fontMetaData->dataLength);
136
137
0
    twi_write_to_slave(SSD_OLED_ADDRESS, &oled.ramContext, ram_read, config.flashChar.fontMetaData->dataLength+1);
138
0
}
139
140
void oled_put_character(char character)
141
0
{
142
0
    OledCharacterWriteMetadata config = oled_get_display_configuration(character);
143
144
0
    oled_send_cursor(
145
0
        config.columnStart, config.columnEnd, 
146
0
        config.pageStart, config.pageEnd);
147
0
    oled_write_character(config);
148
0
}
149
150
void oled_set_cursor(OledCursorPosition cursorPosition)
151
0
{
152
0
    oled_settings.cursorPosition = cursorPosition;
153
0
}
154
155
void oled_set_font_size(FontSize fontSize)
156
0
{
157
0
    oled_settings.fontSize = fontSize;
158
0
}
159
160
void oled_set_wrapping_style(WrappingStyle wrappingStyle)
161
0
{
162
0
    oled_settings.wrappingStyle = wrappingStyle;
163
0
}
164
165
void oled_set_invert(bool invert)
166
0
{
167
0
    oled.ramContext.invert = invert;
168
0
}
169
170
void oled_clear_screen(void) 
171
0
{
172
0
    oled.nopContext.firstByte = DATA_MODE;
173
174
0
    twi_write_to_slave(SSD_OLED_ADDRESS, &oled.nopContext, nop_read, OLED_BYTE_SIZE+1);
175
0
}
176
177
void oled_clear_cursor(void)
178
0
{
179
0
    oled_put_character(' ');
180
0
}
181
182
void oled_fill_cursor(void)
183
0
{
184
0
    oled_set_invert(true);
185
0
    oled_put_character(' ');
186
0
    oled_set_invert(false);
187
0
}
188
189
void oled_write(char *string, uint16_t stringLength)
190
0
{
191
0
    oled.index = 0;
192
0
    memcpy(oled.string, string, stringLength);
193
0
    if(oled.string[oled.index] == STRING_END)
194
0
    {
195
0
        return;
196
0
    }
197
198
0
    oled.requestedState = OLED_WRITE_ADDR;
199
0
}
200
201
bool oled_update(void) 
202
0
{
203
0
    if(twi_busy)
204
0
    {
205
0
        return true;
206
0
    }
207
0
    switch (oled.state)
208
0
    {
209
0
        case OLED_IDLE:
210
0
            if(oled.requestedState != OLED_IDLE)
211
0
            {
212
0
                oled.state = oled.requestedState;
213
0
                oled.requestedState = OLED_IDLE;
214
0
            }
215
0
            return false;
216
0
            break;
217
0
        case OLED_INIT:
218
0
            oled_init();
219
0
            oled.state = OLED_CLEAR_ADDR;
220
0
            break;
221
0
        case OLED_CLEAR_ADDR:
222
0
            oled_send_cursor(ZERO_BYTE, OLED_LAST_COLUMN, ZERO_BYTE, OLED_LAST_ROW);
223
0
            oled.state = OLED_CLEAR_DATA;
224
0
            break;
225
0
        case OLED_CLEAR_DATA:
226
0
            oled_clear_screen();
227
0
            oled.state = OLED_IDLE;
228
0
            break;
229
0
        case OLED_WRITE_ADDR:
230
0
            oled.writeMetadata = oled_get_display_configuration(oled.string[oled.index]);
231
0
            oled_send_cursor(
232
0
                oled.writeMetadata.columnStart, oled.writeMetadata.columnEnd, 
233
0
                oled.writeMetadata.pageStart, oled.writeMetadata.pageEnd);
234
0
            oled.state = OLED_WRITE_DATA;
235
0
            break;
236
0
        case OLED_WRITE_DATA:
237
0
            oled_write_character(oled.writeMetadata);
238
0
            oled_settings.cursorPosition.column++;
239
0
            oled.index++;
240
0
            if(oled.string[oled.index] == STRING_END)
241
0
            {
242
0
                oled.state = OLED_IDLE;
243
0
                break;
244
0
            }
245
0
            oled.state = OLED_WRITE_ADDR;
246
0
            break;
247
0
        default:
248
0
            break;
249
0
    }
250
0
    return true;
251
0
}