TTK4155 Lab Node 1
Term project in the NTNU course TTK4155 Embedded and Industrial Computer Systems Design
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1#include <avr/pgmspace.h>
2#include <stdint.h>
3#include <util/delay.h>
4
5#include "adc.h"
6#include "can.h"
7#include "io.h"
8#include "log.h"
9#include "spi.h"
10#include "uart.h"
11#include "utils.h"
12#include <stdio.h>
13
15
16#define JOYSTICK_THRESHOLD 10
17
18#define IO_AVR_TOUCH_PAD 0x01
19#define IO_AVR_TOUCH_SLIDER 0x02
20#define IO_AVR_JOYSTICK 0x03
21#define IO_AVR_BUTTONS 0x04
22#define IO_AVR_LED_TOGGLE 0x05
23#define IO_AVR_LED_PWM 0x06
24#define IO_AVR_INFO 0x07
25
26//------------------//
27// Joystick //
28//------------------//
29
31 int status = 0;
32
33 ADC_init();
34
35 return status;
36}
37
39 struct io_joystick_position *buffer) {
40 struct ADC_meas data;
41
42 ADC_read_all(&data);
43
44 uint8_t raw_x = data.channel[dev->adc_channel_x];
45 uint8_t raw_y = data.channel[dev->adc_channel_y];
46
47 if (raw_x < dev->x_min)
48 raw_x = dev->x_min;
49 if (raw_x > dev->x_max)
50 raw_x = dev->x_max;
51 if (raw_y < dev->y_min)
52 raw_y = dev->y_min;
53 if (raw_y > dev->y_max)
54 raw_y = dev->y_max;
55
56 buffer->x = map(raw_x, dev->x_min, dev->x_max, -127, 127);
57 buffer->y = map(raw_y, dev->y_min, dev->y_max, -127, 127);
58
59 if (buffer->x > -5 && buffer->x < 5)
60 buffer->x = 0;
61 if (buffer->y > -5 && buffer->y < 5)
62 buffer->y = 0;
63
64 return 0;
65}
67 enum io_joystick_direction *direction) {
68 int status = 0;
69
71 status = io_joystick_read_position(dev, &pos);
72 if (status) {
73 return status;
74 }
75
76 if (pos.x > (0 - JOYSTICK_THRESHOLD) && pos.x < (0 + JOYSTICK_THRESHOLD) &&
77 pos.y > (0 - JOYSTICK_THRESHOLD) && pos.y < (0 + JOYSTICK_THRESHOLD)) {
78 *direction = NEUTRAL;
79 return status;
80 }
81
82 // Fill inn the remaining directions...
83
84 return status;
85}
86
87// int io_joystick_calibrate(struct io_joystick_device *dev) {
88// int status = 0;
89//
90// struct io_joystick_position pos;
91// status = io_joystick_read_position(dev, &pos);
92// if (status) {
93// return status;
94// }
95//
96// dev->x_offset = pos.x * -1;
97// dev->y_offset = pos.y * -1;
98//
99// return 0;
100// }
101
102//-------------------//
103// Button Inputs //
104//-------------------//
105
106int io_avr_init(struct io_avr_device *dev) {
107
108 int status = 0;
109
110 while (spi_ready()) {
111 status = spi_init();
112 }
113 return status;
114}
115int io_avr_buttons_read(struct io_avr_device *dev, struct io_avr_buttons *btn) {
116
117 unsigned char recbuf[3];
119 spi_recieve_n(&dev->spi, recbuf, 3);
120 spi_set_slave_select(&dev->spi, 1);
121
122 btn->right = recbuf[0];
123 btn->left = recbuf[1];
124 btn->nav = recbuf[2];
125
126 return 0;
127}
128int io_avr_led_set(struct io_avr_device *dev, unsigned char led,
129 unsigned char brightness) {
130 unsigned char cmd[3] = {IO_AVR_LED_PWM, led, brightness};
131 spi_send_n(&dev->spi, cmd, 3);
132 return 0;
133}
134
135//------------------//
136// OLED //
137//------------------//
138
139//-----------Low level OLED---------------
140
141static int _io_oled_cmd(struct io_oled_device *dev, uint8_t command) {
142 PORTB &= ~(1 << PB1); // D/!C=0
143 // PB2 DISP_CS Low, Chipselect is driven by SPI
144 spi_send(&dev->spi, command);
145 //_delay_us(3);
146 return 0;
147}
148
149static int _io_oled_write(struct io_oled_device *dev, uint8_t data) {
150 PORTB |= (1 << PB1); // D/!C set high
151 // PB2 DISP_CS Low, Chipselect is driven by SPI
152 spi_send(&dev->spi, data);
153 //_delay_us(3);
154 dev->current_column++;
155
156 return 0;
157}
158
159//---------------OLED commands-----------
160
161int io_oled_init(struct io_oled_device *dev) {
162 int status = 0;
163
164 while (spi_ready()) {
165 status = spi_init();
166 }
167
168 // --- Configure OLED control pins --- //
169 // Set directions *after* setting desired logic states to avoid glitching
170 DDRB |= (1 << PB4) | (1 << PB1) | (1 << PB2); // Set PB0,PB1,PB2 to outputs
171
172 // --- Hardware reset sequence --- //
173 PORTB &= ~(1 << PB4);
174 _delay_us(5);
175 PORTB |= (1 << PB4);
176 _delay_ms(1);
177
178 // Display OFF
179 _io_oled_cmd(dev, 0xAE);
180
181 // Recommended config for 128x64 OLED, page 19 OLED datasheet
182 _io_oled_cmd(dev, 0xA1); // Segment remap: column address 127->0
183 _io_oled_cmd(dev, 0xC8); // Scan direction: remapped C8 or C0
184
185 _io_oled_cmd(dev, 0xDA); // Common pads hardware
186 _io_oled_cmd(dev, 0x12); // Required parameter for 128x64
187
188 _io_oled_cmd(dev, 0xC8); // Common output scan direciton
189
190 _io_oled_cmd(dev, 0xA8); // Set multiplex ratio
191 _io_oled_cmd(dev, 0x3F); // Multiplex ratio: 63 (64MUX)
192
193 _io_oled_cmd(dev, 0xD5); // set display clock devider ratio
194 _io_oled_cmd(dev, 0x80); // freq.mode
195
196 _io_oled_cmd(dev, 0x81); // Contrast control
197 _io_oled_cmd(dev, 0xFF);
198
199 _io_oled_cmd(dev, 0xD9); // Set precharged period
200 _io_oled_cmd(dev, 0x21);
201
202 _io_oled_cmd(dev, 0x20); // Set memory adressing mode
203 _io_oled_cmd(dev, 0x02); // 0x02 = Page (alternativly 0x00 = Horizontal)
204
205 _io_oled_cmd(dev, 0xDB); // VCOM deselect level mode
206 _io_oled_cmd(dev, 0x30);
207
208 _io_oled_cmd(dev, 0xAD); // Master config
209 _io_oled_cmd(dev, 0x00); // Slave config
210
211 _io_oled_cmd(dev, 0xA4); // Resume to RAM content
212
213 _io_oled_cmd(dev, 0xA6); // Normal display
214
215 // Clear GDDRAM once at init
216 for (uint8_t page = 0; page < 8; page++) {
217 io_oled_goto_line(dev, page);
218 io_oled_goto_column(dev, 0);
219 for (uint8_t x = 0; x < 128; x++) {
220 _io_oled_write(dev, 0x00);
221 }
222 }
223
224 // Display ON
225 _io_oled_cmd(dev, 0xAF);
226
227 return status;
228}
229
231
232 // Display off
233 _io_oled_cmd(dev, 0xAE);
234
235 // --- Hardware reset sequence --- //
236 PORTB &= ~(1 << PB4); // Set PB4 low
237 _delay_us(5); // Wait 5us
238 PORTB |= (1 << PB4); // Set PB4 high
239 _delay_ms(1); // Making sure its restarted before we start sending commands
240
241 // Turn display on
242 _io_oled_cmd(dev, 0xAF);
243
244 return 0;
245}
246
247int io_oled_home(struct io_oled_device *dev) {
248
249 io_oled_goto_line(dev, 0);
250 io_oled_goto_column(dev, 0);
251
252 return 0;
253}
254
255int io_oled_goto_line(struct io_oled_device *dev, int line) {
256
257 if (line > 7)
258 line = 7; // Out of bounds
259 _io_oled_cmd(dev, 0xB0 | (line & 0x07)); // 0xB0-0xB7
260 dev->current_page = line; // Update index of current page
261
262 return 0;
263}
264
265int io_oled_goto_column(struct io_oled_device *dev, int column) {
266
267 // Columns are split into low/high area commands 0x00-0x0F and 0x10-0x1F
268 _io_oled_cmd(dev, 0x00 | (column & 0x0F)); // lower
269 _io_oled_cmd(dev, 0x10 | ((column >> 4) & 0x0F)); // higher
270 dev->current_column = column; // Update index of current column
271
272 return 0;
273}
274
275int io_oled_clear_line(struct io_oled_device *dev, int line) {
276
277 if (line < 0 || line > 7)
278 return -1;
279 io_oled_goto_line(dev, line);
280 io_oled_goto_column(dev, 0);
281 for (uint8_t x = 0; x < 128; x++) {
282 _io_oled_write(dev, 0x00);
283 }
284 return 0;
285}
286
293 int status = 0;
294
295 // There are 8 pages (0–7) on a 64-pixel-tall display
296 for (uint8_t line = 0; line < 8; line++) {
297 status = io_oled_clear_line(dev, line);
298 if (status < 0) {
299 return status; // early exit on failure
300 }
301 }
302
303 return 0;
304}
305
306int io_oled_pos(struct io_oled_device *dev, int line, int column) {
307
308 io_oled_goto_line(dev, line);
309 io_oled_goto_column(dev, column);
310
311 return 0;
312}
313
314int io_oled_set_brightness(struct io_oled_device *dev, uint8_t brightness) {
315
316 _io_oled_cmd(dev, 0x81);
317 _io_oled_cmd(dev, brightness);
318
319 return 0;
320}
321
323
324 _io_oled_cmd(dev, 0x81);
325 _io_oled_cmd(dev, 0x7F);
326
327 return 0;
328}
329
330int io_oled_print_arrow(struct io_oled_device *dev, uint8_t row, uint8_t col) {
331
332 io_oled_pos(dev, row, col);
333 _io_oled_write(dev, 0b00011000);
334 _io_oled_write(dev, 0b00011000);
335 _io_oled_write(dev, 0b01111110);
336 _io_oled_write(dev, 0b00111100);
337 _io_oled_write(dev, 0b00011000);
338
339 return 0;
340}
341
342// Write one glyph from specified font in font.h (ASCII 32..126).
344 const struct oled_font *font, char character_id) {
345
346 uint8_t start_page = dev->current_page;
347 uint8_t start_column = dev->current_column;
348
349 // --- Draw the glyph on the current page --- //
350 uint8_t glyph_id = 0; // Space as default value
351
352 // Make sure char_id in range
353 if ((uint8_t)character_id >= 32 && (uint8_t)character_id <= 126) {
354 glyph_id = (uint8_t)character_id - 32;
355 }
356
357 // Find glyph in fonts.h
358 uint8_t *base = (uint8_t *)font->table + (glyph_id * font->bytes_per_glyph);
359
360 // Print glyph to oled
361 for (uint8_t c = 0; c < font->bytes_per_glyph; ++c) {
362 // function from <avr/pgmspace.h> that reads from PROGMEM
363 uint8_t col = pgm_read_byte(base + c);
364 _io_oled_write(dev, col);
365 }
366
367 // Add spacing after glyph
368 for (uint8_t s = 0; s < font->spacing; ++s) {
369 _io_oled_write(dev, 0x00);
370 }
371
372 uint8_t total_cols = (uint8_t)(font->bytes_per_glyph + font->spacing);
373
374 return total_cols;
375}
376
378 const struct oled_font *font, char *text) {
379 if (!dev || !font || !text)
380 return -1;
381
382 for (char *p = text; *p; ++p) {
383 if (*p == '\n') {
384 continue;
385 }
386 io_oled_write_glyph(dev, font, *p);
387 }
388 return 0;
389}
390
391int io_oled_test(struct io_oled_device *dev) {
392
393 io_oled_home(dev);
394
395 // print half a line
396 for (uint8_t i = 0; i < 64; i++) {
397 _io_oled_write(dev, 0xff);
398 }
399
400 io_oled_goto_line(dev, 1); // new line
401
402 // print half a line
403 for (uint8_t i = 0; i < 64; i++) {
404 _io_oled_write(dev, 0xff);
405 }
406
407 io_oled_goto_line(dev, 2); // new line
408 io_oled_goto_column(dev, 0); // new carrige return
409
410 // fill every other column in a row
411 for (uint8_t i = 0; i < 128; i++) {
412 if (i % 2 == 0) {
413 _io_oled_write(dev, 0xff);
414 } else {
415 _io_oled_write(dev, 0x00);
416 }
417 }
418
419 io_oled_goto_line(dev, 3); // new line
420 io_oled_goto_column(dev, 0); // new carrige return
421
422 // fill a row with chess pattern
423 for (uint8_t i = 0; i < 128; i++) {
424 if (i % 2 == 0) {
425 _io_oled_write(dev, 0b01010101);
426 } else {
427 _io_oled_write(dev, 0b10101010);
428 }
429 }
430
431 io_oled_goto_line(dev, 4); // new line
432 io_oled_goto_column(dev, 0); // new carrige return
433 io_oled_print_with_font(dev, &OLED_FONT_8x8, "Text 8x8");
434
435 io_oled_goto_line(dev, 5); // new line
436 io_oled_goto_column(dev, 0); // new carrige return
437 io_oled_print_with_font(dev, &OLED_FONT_5x7, "Text 5x7");
438
439 io_oled_goto_line(dev, 6); // new line
440 io_oled_goto_column(dev, 0); // new carrige return
441 io_oled_print_with_font(dev, &OLED_FONT_4x6, "Text 4x6");
442
443 io_oled_goto_line(dev, 7); // new line
444 io_oled_goto_column(dev, 0); // new carrige return
445 io_oled_print_with_font(dev, &OLED_FONT_8x8, "Test:Complete");
446
447 return 0;
448}
449
450int io_oled_blink(struct io_oled_device *dev, uint8_t blinks) {
451
452 io_oled_home(dev);
453
454 for (uint8_t blink = 0; blink < 2 * blinks; blink++) {
455 for (uint8_t row = 0; row < 8; row++) {
456 io_oled_goto_line(dev, row);
457 io_oled_goto_column(dev, 0);
458 for (uint8_t col = 0; col < 128; col++) {
459 if (blink % 2 == 0) {
460 _io_oled_write(dev, 0xFF);
461 } else {
462 _io_oled_write(dev, 0x00);
463 }
464 }
465 }
466 _delay_ms(500);
467 }
468 return 0;
469}
470
471int io_oled_write_data(struct io_oled_device *dev, uint8_t data) {
472 return _io_oled_write(dev, data);
473}
474
475int io_oled_write_command(struct io_oled_device *dev, uint8_t command) {
476 return _io_oled_cmd(dev, command);
477}
ADC memory-mapped interface.
CAN driver.
void ADC_init(void)
Initialize the external memory–mapped ADC interface.
Definition adc.c:12
void ADC_read_all(struct ADC_meas *output)
Perform a full four-channel ADC conversion and return all results.
Definition adc.c:36
int io_avr_led_set(struct io_avr_device *dev, unsigned char led, unsigned char brightness)
Definition io.c:128
io_joystick_direction
Predefined joystick directions.
Definition io.h:78
int io_avr_init(struct io_avr_device *dev)
Definition io.c:106
int io_joystick_read_position(struct io_joystick_device *dev, struct io_joystick_position *buffer)
Read the position of a joystick as a percentage from 0 in each axis.
Definition io.c:38
int io_oled_clear_all(struct io_oled_device *dev)
Clears all lines on the OLED (fills entire display with black).
Definition io.c:292
int io_avr_buttons_read(struct io_avr_device *dev, struct io_avr_buttons *btn)
Definition io.c:115
static const struct oled_font OLED_FONT_4x6
Predefined 4x6 font for OLED.
Definition io.h:34
static const struct oled_font OLED_FONT_8x8
Predefined 8x8 font for OLED.
Definition io.h:30
int io_oled_goto_line(struct io_oled_device *dev, int line)
Go to specific line in OLED.
Definition io.c:255
int io_oled_write_command(struct io_oled_device *dev, uint8_t command)
Definition io.c:475
int io_oled_print_arrow(struct io_oled_device *dev, uint8_t row, uint8_t col)
Prints an arrow at a specified postion.
Definition io.c:330
int io_oled_clear_line(struct io_oled_device *dev, int line)
Clear specific line in OLED.
Definition io.c:275
int io_oled_home(struct io_oled_device *dev)
Loads homescreen to OLED.
Definition io.c:247
int io_oled_blink(struct io_oled_device *dev, uint8_t blinks)
Alternates screen between black and white.
Definition io.c:450
int io_oled_write_data(struct io_oled_device *dev, uint8_t data)
Definition io.c:471
int io_oled_reset(struct io_oled_device *dev)
Sends command to OLED.
Definition io.c:230
int io_oled_reset_brightness(struct io_oled_device *dev)
Reset OLED brightness.
Definition io.c:322
static const struct oled_font OLED_FONT_5x7
Predefined 5x7 font for OLED.
Definition io.h:32
uint8_t io_oled_write_glyph(struct io_oled_device *dev, const struct oled_font *font, char character_id)
Prints a single glyph in a specifiec font.
Definition io.c:343
int io_oled_pos(struct io_oled_device *dev, int line, int column)
Set specific position in OLED.
Definition io.c:306
int io_joystick_read_direction(struct io_joystick_device *dev, enum io_joystick_direction *direction)
Fetches the direction of a joystick.
Definition io.c:66
int io_oled_set_brightness(struct io_oled_device *dev, uint8_t brightness)
Set OLED brightness.
Definition io.c:314
int io_joystick_init(struct io_joystick_device *dev)
Initialise and calibrate a joystick.
Definition io.c:30
int io_oled_goto_column(struct io_oled_device *dev, int column)
Go to specific column in OLED.
Definition io.c:265
int io_oled_print_with_font(struct io_oled_device *dev, const struct oled_font *font, char *text)
Prints an arrow at a specified postion.
Definition io.c:377
int io_oled_test(struct io_oled_device *dev)
Prints a known image to the OLED to confirm that it can print and navigate the screen.
Definition io.c:391
int io_oled_init(struct io_oled_device *dev)
Initialise OLED.
Definition io.c:161
@ NEUTRAL
Definition io.h:83
#define LOG_MODULE_DEFINE(name)
Definition log.h:39
int spi_recieve_n(enum spi_slave *slave, unsigned char *out, int length)
Receive several bytes from a slave.
Definition spi.c:96
int spi_send_n(enum spi_slave *slave, unsigned char *data, int length)
Send a sequence of bytes to a slave without reading response.
Definition spi.c:81
int spi_ready(void)
Check if the SPI subsystem is initialized.
Definition spi.c:41
int spi_set_slave_select(enum spi_slave *slave, unsigned char state)
Set the slave-select line for the given device.
Definition spi.c:43
int spi_push(enum spi_slave *slave, unsigned char data, unsigned char *out)
Push one byte to a slave and optionally read the response.
Definition spi.c:154
int spi_send(enum spi_slave *slave, unsigned char data)
Send a single byte to a slave.
Definition spi.c:86
int spi_init(void)
Initialize SPI in master mode.
Definition spi.c:9
#define TRASHCAN
Definition utils.h:19
int16_t map(uint8_t x, uint8_t in_min, uint8_t in_max, int16_t out_min, int16_t out_max)
Maps a value from one range to another (taken from https://docs.arduino.cc/language-reference/en/func...
Definition utils.c:63
#define JOYSTICK_THRESHOLD
Definition io.c:16
static int _io_oled_write(struct io_oled_device *dev, uint8_t data)
Definition io.c:149
static int _io_oled_cmd(struct io_oled_device *dev, uint8_t command)
Definition io.c:141
#define IO_AVR_LED_PWM
Definition io.c:23
#define IO_AVR_BUTTONS
Definition io.c:21
Driver for joystick, buttons, and OLED.
Logging interface for debug messages.
struct io_avr_buttons btn
Definition main.c:59
struct io_joystick_position pos
Definition main.c:58
SPI driver.
Container for ADC measurement results.
Definition adc.h:20
uint8_t channel[4]
Definition adc.h:21
Represents an SPI-connected AVR-I/O board.
Definition io.h:39
enum spi_slave spi
Definition io.h:40
Represents a joystick input device.
Definition io.h:55
unsigned int adc_channel_x
Definition io.h:56
unsigned int adc_channel_y
Definition io.h:57
Represents the position of a joystick relative to center.
Definition io.h:70
Represents an OLED display device controlled via SPI.
Definition io.h:46
enum spi_slave spi
Definition io.h:47
uint8_t current_page
Definition io.h:48
uint8_t current_column
Definition io.h:49
Represents a font for OLED displays.
Definition io.h:22
uint8_t spacing
Definition io.h:26
const void * table
Definition io.h:23
uint8_t bytes_per_glyph
Definition io.h:24
UART communication driver.
Utility and helper functions for joystick, LED debugging, and mapping.