Browse Source

initial code, working LCD

Ondřej Hruška 2 years ago
commit
c7b83a9ba4
Signed by: Ondřej Hruška <ondra@ondrovo.com> GPG key ID: 2C5FD5035250423D
18 changed files with 1882 additions and 0 deletions
  1. 47 0
      .gitignore
  2. 29 0
      CMakeLists.txt
  3. 21 0
      LICENSE
  4. 140 0
      Makefile
  5. 354 0
      lcd.c
  6. 135 0
      lcd.h
  7. 77 0
      lib/adc.c
  8. 44 0
      lib/adc.h
  9. 154 0
      lib/calc.h
  10. 277 0
      lib/iopins.c
  11. 213 0
      lib/iopins.h
  12. 21 0
      lib/nsdelay.h
  13. 62 0
      lib/spi.c
  14. 69 0
      lib/spi.h
  15. 83 0
      lib/usart.c
  16. 86 0
      lib/usart.h
  17. 56 0
      main.c
  18. 14 0
      style.astylerc

+ 47 - 0
.gitignore View File

@@ -0,0 +1,47 @@
1
+# Object files
2
+*.o
3
+*.ko
4
+*.obj
5
+*.elf
6
+*.axf
7
+
8
+# Precompiled Headers
9
+*.gch
10
+*.pch
11
+
12
+# Libraries
13
+*.lib
14
+*.a
15
+*.la
16
+*.lo
17
+
18
+# Shared objects (inc. Windows DLLs)
19
+*.dll
20
+*.so
21
+*.so.*
22
+*.dylib
23
+
24
+# Executables
25
+*.exe
26
+*.out
27
+*.app
28
+*.i*86
29
+*.x86_64
30
+*.hex
31
+
32
+# Debug files
33
+*.dSYM/
34
+
35
+# AVR specific stuff
36
+*.lst
37
+*.dis
38
+*.disasm
39
+*.list
40
+*.eeprom
41
+*.pre
42
+
43
+# QtCreator user-specific
44
+*.pro.user
45
+
46
+.idea/
47
+cmake-build-debug/

+ 29 - 0
CMakeLists.txt View File

@@ -0,0 +1,29 @@
1
+cmake_minimum_required(VERSION 3.7)
2
+project(firmware)
3
+
4
+# Fake CMake config for CLion
5
+
6
+set(CMAKE_CXX_STANDARD GNU99)
7
+
8
+set(SOURCE_FILES
9
+        main.c
10
+        lib/calc.h
11
+        lib/iopins.c
12
+        lib/iopins.h
13
+        lib/nsdelay.h
14
+        lib/spi.c
15
+        lib/spi.h
16
+        lib/adc.c
17
+        lib/adc.h
18
+        lib/usart.c
19
+        lib/usart.h
20
+        lcd.c
21
+        lcd.h
22
+        )
23
+
24
+include_directories(lib
25
+        /usr/avr/include/)
26
+
27
+add_definitions(-D__AVR_ATmega328P__)
28
+
29
+add_executable(firmware ${SOURCE_FILES})

+ 21 - 0
LICENSE View File

@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2016 Ondřej Hruška
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.

+ 140 - 0
Makefile View File

@@ -0,0 +1,140 @@
1
+#############################################
2
+
3
+# CPU type
4
+MCU   = atmega328p
5
+
6
+# CPU frequency [Hz]
7
+F_CPU = 16000000
8
+
9
+# Fuses (refer to datasheet)
10
+LFUSE = 0xFF
11
+HFUSE = 0xDE
12
+EFUSE = 0x05
13
+
14
+# AVRDUDE settings
15
+PROG_BAUD = 57600
16
+PROG_DEV  = /dev/ttyUSB0
17
+PROG_TYPE = arduino
18
+
19
+# Build the final AVRDUDE arguments
20
+PROG_ARGS = -c $(PROG_TYPE) -p $(MCU) -b $(PROG_BAUD) -P $(PROG_DEV)
21
+
22
+#############################################
23
+
24
+# Main file
25
+BINARY = main
26
+
27
+# Obj files to be built <- add .o for any .c files you add!
28
+OBJS  = $(BINARY).o
29
+OBJS += lib/usart.o
30
+OBJS += lib/iopins.o
31
+OBJS += lib/spi.o
32
+OBJS += lib/adc.o
33
+OBJS += lcd.o
34
+
35
+# Dirs with header files
36
+INCL_DIRS = . lib/
37
+
38
+# Pre-defined macros
39
+DEFS = -DF_CPU=$(F_CPU)UL
40
+
41
+#############################################
42
+
43
+# C flags
44
+CFLAGS = -std=gnu99 -mmcu=$(MCU) $(DEFS) $(INCL_DIRS:%=-I%)
45
+CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -ffreestanding
46
+CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment
47
+CFLAGS += -g2 -Wextra -Wfatal-errors -Wno-unused-but-set-variable
48
+CFLAGS += -ffunction-sections -fdata-sections -Os
49
+
50
+LDFLAGS = -Wl,--gc-sections -Wl,--relax -lm
51
+
52
+#LD_FLAGS += -Wl,-u,vfprintf -lprintf_flt -lm  ## for floating-point printf
53
+#LD_FLAGS += -Wl,-u,vfprintf -lprintf_min      ## for smaller printf
54
+
55
+#############################################
56
+
57
+## Defined programs / locations
58
+CC = avr-gcc
59
+OBJCOPY = avr-objcopy
60
+OBJDUMP = avr-objdump
61
+AVRSIZE = avr-size
62
+AVRDUDE = avrdude
63
+
64
+JUNK = *.o *.d *.elf *.bin *.hex *.srec *.list *.lst *.map *.dis *.disasm *.eep *.eeprom *.lss
65
+
66
+.SUFFIXES: .elf .bin .hex .lst .map .eeprom .pre
67
+.SECONDEXPANSION:
68
+.SECONDARY:
69
+
70
+.PHONY: all elf bin hex lst pre ee eeprom dis size clean flash flashe shell fuses show_fuses set_default_fuses
71
+
72
+all: hex size
73
+
74
+elf: $(BINARY).elf
75
+bin: $(BINARY).bin
76
+hex: $(BINARY).hex
77
+lst: $(BINARY).lst
78
+pre: $(BINARY).pre
79
+ee: $(BINARY).eeprom
80
+eeprom: $(BINARY).eeprom
81
+dis: lst
82
+
83
+# Show how big the resulting program is
84
+size: elf
85
+	$(AVRSIZE) -C --mcu=$(MCU) $(BINARY).elf
86
+
87
+
88
+
89
+# --- Magic build targets ----------------
90
+
91
+%.hex: %.elf
92
+	$(OBJCOPY) -R .eeprom -O ihex $< $@
93
+
94
+%.elf %.map: $(OBJS)
95
+	$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(*).elf
96
+
97
+%.pre: %.c
98
+	$(CC) $(CFLAGS) -E $(*).c --output $@
99
+
100
+%.eeprom: %.elf
101
+	$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
102
+
103
+%.lst: %.elf
104
+	$(Q)$(OBJDUMP) -S $(*).elf > $(*).lst
105
+
106
+%.o: %.c
107
+	$(CC) $(CFLAGS) -o $(*).o -c $(*).c
108
+
109
+%.o: %.s
110
+	$(CC) $(CFLAGS) -o $(*).o -c $(*).s
111
+
112
+
113
+# Clean all produced trash
114
+clean:
115
+	rm -f $(JUNK)
116
+	cd lib && rm -f $(JUNK)
117
+
118
+
119
+## === avrdude ===
120
+
121
+flash: $(BINARY).hex
122
+	$(AVRDUDE) $(PROG_ARGS) -U flash:w:$<
123
+
124
+flashe: $(BINARY).eeprom
125
+	$(AVRDUDE) $(PROG_ARGS) -U eeprom:w:$<
126
+
127
+shell:
128
+	$(AVRDUDE) $(PROG_ARGS) -nt
129
+
130
+# === fuses ===
131
+
132
+# this may not work with the arduino programmer, I haven't tried.
133
+
134
+FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
135
+
136
+fuses:
137
+	$(AVRDUDE) $(PROG_ARGS) $(FUSE_STRING)
138
+
139
+show_fuses:
140
+	$(AVRDUDE) $(PROG_ARGS) -nv

+ 354 - 0
lcd.c View File

@@ -0,0 +1,354 @@
1
+#include <stdbool.h>
2
+#include <stdint.h>
3
+#include <avr/io.h>
4
+#include <avr/pgmspace.h>
5
+#include <util/delay.h>
6
+
7
+#include "calc.h"
8
+#include "iopins.h"
9
+#include "nsdelay.h"
10
+#include "lcd.h"
11
+
12
+// Start address of rows
13
+const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54};
14
+
15
+// Internal prototypes
16
+void _lcd_mode_r();
17
+void _lcd_mode_w();
18
+void _lcd_clk();
19
+void _lcd_wait_bf();
20
+void _lcd_write_byte(uint8_t bb);
21
+uint8_t _lcd_read_byte();
22
+
23
+
24
+// Write utilities
25
+#define _lcd_write_low(bb)  _lcd_write_nibble((bb) & 0x0F)
26
+#define _lcd_write_high(bb) _lcd_write_nibble(((bb) & 0xF0) >> 4)
27
+#define _lcd_write_nibble(nib) do {                \
28
+	pin_set(LCD_D7, get_bit((nib), 3));     \
29
+	pin_set(LCD_D6, get_bit((nib), 2));     \
30
+	pin_set(LCD_D5, get_bit((nib), 1));     \
31
+	pin_set(LCD_D4, get_bit((nib), 0));     \
32
+} while(0)
33
+
34
+
35
+// 0 W, 1 R
36
+bool _lcd_mode;
37
+
38
+struct
39
+{
40
+	uint8_t x;
41
+	uint8_t y;
42
+} _pos;
43
+
44
+enum
45
+{
46
+	TEXT = 0,
47
+	CG = 1
48
+} _addrtype;
49
+
50
+
51
+/** Initialize the display */
52
+void lcd_init()
53
+{
54
+	// configure pins as output
55
+	as_output(LCD_E);
56
+	as_output(LCD_RW);
57
+	as_output(LCD_RS);
58
+	_lcd_mode = 1;  // force data pins to output
59
+	_lcd_mode_w();
60
+
61
+	// Magic sequence to invoke Cthulhu (or enter 4-bit mode)
62
+	_delay_ms(16);
63
+	_lcd_write_nibble(0b0011);
64
+	_lcd_clk();
65
+	_delay_ms(5);
66
+	_lcd_clk();
67
+	_delay_ms(5);
68
+	_lcd_clk();
69
+	_delay_ms(5);
70
+	_lcd_write_nibble(0b0010);
71
+	_lcd_clk();
72
+	_delay_us(100);
73
+
74
+	// Configure the display
75
+	lcd_command(LCD_IFACE_4BIT_2LINE);
76
+	lcd_command(LCD_DISABLE);
77
+	lcd_command(LCD_CLEAR);
78
+	lcd_command(LCD_MODE_INC);
79
+
80
+	// mark as enabled
81
+	lcd_enable();
82
+
83
+	_pos.x = 0;
84
+	_pos.y = 0;
85
+	_addrtype = TEXT;
86
+}
87
+
88
+
89
+/** Send a pulse on the ENABLE line */
90
+void _lcd_clk()
91
+{
92
+	pin_up(LCD_E);
93
+	delay_ns(450);
94
+	pin_down(LCD_E);
95
+}
96
+
97
+
98
+/** Enter READ mode */
99
+void _lcd_mode_r()
100
+{
101
+	if (_lcd_mode == 1) return;  // already in R mode
102
+
103
+	pin_up(LCD_RW);
104
+
105
+	as_input_pu(LCD_D7);
106
+	as_input_pu(LCD_D6);
107
+	as_input_pu(LCD_D5);
108
+	as_input_pu(LCD_D4);
109
+
110
+	_lcd_mode = 1;
111
+}
112
+
113
+
114
+/** Enter WRITE mode */
115
+void _lcd_mode_w()
116
+{
117
+	if (_lcd_mode == 0) return;  // already in W mode
118
+
119
+	pin_down(LCD_RW);
120
+
121
+	as_output(LCD_D7);
122
+	as_output(LCD_D6);
123
+	as_output(LCD_D5);
124
+	as_output(LCD_D4);
125
+
126
+	_lcd_mode = 0;
127
+}
128
+
129
+
130
+/** Read a byte */
131
+uint8_t _lcd_read_byte()
132
+{
133
+	_lcd_mode_r();
134
+
135
+	uint8_t res = 0;
136
+
137
+	_lcd_clk();
138
+	res = (pin_read(LCD_D7) << 7) | (pin_read(LCD_D6) << 6) | (pin_read(LCD_D5) << 5) | (pin_read(LCD_D4) << 4);
139
+
140
+	_lcd_clk();
141
+	res |= (pin_read(LCD_D7) << 3) | (pin_read(LCD_D6) << 2) | (pin_read(LCD_D5) << 1) | (pin_read(LCD_D4) << 0);
142
+
143
+	return res;
144
+}
145
+
146
+
147
+/** Write an instruction byte */
148
+void lcd_command(uint8_t bb)
149
+{
150
+	_lcd_wait_bf();
151
+	pin_down(LCD_RS);  // select instruction register
152
+	_lcd_write_byte(bb);    // send instruction byte
153
+}
154
+
155
+
156
+/** Write a data byte */
157
+void lcd_write(uint8_t bb)
158
+{
159
+	if (_addrtype == TEXT)
160
+	{
161
+		if (bb == '\r')
162
+		{
163
+			// CR
164
+			_pos.x = 0;
165
+			lcd_xy(_pos.x, _pos.y);
166
+			return;
167
+		}
168
+
169
+		if (bb == '\n')
170
+		{
171
+			// LF
172
+			_pos.y++;
173
+			lcd_xy(_pos.x, _pos.y);
174
+			return;
175
+		}
176
+
177
+		_pos.x++;
178
+	}
179
+
180
+	_lcd_wait_bf();
181
+	pin_up(LCD_RS);  // select data register
182
+	_lcd_write_byte(bb);  // send data byte
183
+}
184
+
185
+
186
+/** Read BF & Address */
187
+uint8_t lcd_read_bf_addr()
188
+{
189
+	pin_down(LCD_RS);
190
+	return _lcd_read_byte();
191
+}
192
+
193
+
194
+/** Read CGRAM or DDRAM */
195
+uint8_t lcd_read()
196
+{
197
+	if (_addrtype == TEXT) _pos.x++;
198
+
199
+	pin_up(LCD_RS);
200
+	return _lcd_read_byte();
201
+}
202
+
203
+
204
+/** Write a byte using the 4-bit interface */
205
+void _lcd_write_byte(uint8_t bb)
206
+{
207
+	_lcd_mode_w();  // enter W mode
208
+
209
+	_lcd_write_high(bb);
210
+	_lcd_clk();
211
+
212
+	_lcd_write_low(bb);
213
+	_lcd_clk();
214
+}
215
+
216
+
217
+
218
+/** Wait until the device is ready */
219
+void _lcd_wait_bf()
220
+{
221
+	uint8_t d = 0;
222
+	while (d++ < 120 && (lcd_read_bf_addr() & _BV(7)))
223
+		_delay_us(1);
224
+}
225
+
226
+
227
+/** Send a string to LCD */
228
+void lcd_puts(char* str_p)
229
+{
230
+	char c;
231
+	while ((c = *str_p++))
232
+		lcd_putc(c);
233
+}
234
+
235
+
236
+/** Print from progmem */
237
+void lcd_puts_P(const char* str_p)
238
+{
239
+	char c;
240
+	while ((c = pgm_read_byte(str_p++)))
241
+		lcd_putc(c);
242
+}
243
+
244
+
245
+/** Sedn a char to LCD */
246
+void lcd_putc(const char c)
247
+{
248
+	lcd_write(c);
249
+}
250
+
251
+
252
+/** Set cursor position */
253
+void lcd_xy(const uint8_t x, const uint8_t y)
254
+{
255
+	_pos.x = x;
256
+	_pos.y = y;
257
+	lcd_addr(LCD_ROW_ADDR[y] + (x));
258
+}
259
+
260
+
261
+uint8_t _lcd_old_cursor = CURSOR_NONE;
262
+bool _lcd_enabled = false;
263
+
264
+/** Set LCD cursor. If not enabled, only remember it. */
265
+void lcd_cursor(uint8_t type)
266
+{
267
+	_lcd_old_cursor = (type & CURSOR_BOTH);
268
+
269
+	if (_lcd_enabled) lcd_command(LCD_CURSOR_NONE | _lcd_old_cursor);
270
+}
271
+
272
+
273
+/** Display display (preserving cursor) */
274
+void lcd_disable()
275
+{
276
+	lcd_command(LCD_DISABLE);
277
+	_lcd_enabled = false;
278
+}
279
+
280
+
281
+/** Enable display (restoring cursor) */
282
+void lcd_enable()
283
+{
284
+	_lcd_enabled = true;
285
+	lcd_cursor(_lcd_old_cursor);
286
+}
287
+
288
+
289
+/** Go home */
290
+void lcd_home()
291
+{
292
+	lcd_command(LCD_HOME);
293
+	_pos.x = 0;
294
+	_pos.y = 0;
295
+	_addrtype = TEXT;
296
+	_delay_ms(1);
297
+}
298
+
299
+
300
+/** Clear the screen */
301
+void lcd_clear()
302
+{
303
+	lcd_command(LCD_CLEAR);
304
+	_pos.x = 0;
305
+	_pos.y = 0;
306
+	_addrtype = TEXT;
307
+	_delay_ms(1);
308
+}
309
+
310
+
311
+/** Define a glyph */
312
+void lcd_glyph(const uint8_t index, const uint8_t* array)
313
+{
314
+	lcd_addr_cg(index * 8);
315
+	for (uint8_t i = 0; i < 8; ++i)
316
+	{
317
+		lcd_write(array[i]);
318
+	}
319
+
320
+	// restore previous position
321
+	lcd_xy(_pos.x, _pos.y);
322
+	_addrtype = TEXT;
323
+}
324
+
325
+
326
+/** Define a glyph */
327
+void lcd_glyph_P(const uint8_t index, const uint8_t* array)
328
+{
329
+	lcd_addr_cg(index * 8);
330
+	for (uint8_t i = 0; i < 8; ++i)
331
+	{
332
+		lcd_write(pgm_read_byte(&array[i]));
333
+	}
334
+
335
+	// restore previous position
336
+	lcd_xy(_pos.x, _pos.y);
337
+	_addrtype = TEXT;
338
+}
339
+
340
+
341
+/** Set address in CGRAM */
342
+void lcd_addr_cg(const uint8_t acg)
343
+{
344
+	_addrtype = CG;
345
+	lcd_command(0b01000000 | ((acg) & 0b00111111));
346
+}
347
+
348
+
349
+/** Set address in DDRAM */
350
+void lcd_addr(const uint8_t add)
351
+{
352
+	_addrtype = TEXT;
353
+	lcd_command(0b10000000 | ((add) & 0b01111111));
354
+}

+ 135 - 0
lcd.h View File

@@ -0,0 +1,135 @@
1
+#pragma once
2
+
3
+//  HD44780 LCD display driver - 4-bit mode
4
+//
5
+//  LCD pins are configured using a file lcd_config.h, which you
6
+//  have to add next to your main.c file.
7
+//
8
+//  Content can be something like this:
9
+//
10
+//
11
+
12
+#include <stdint.h>
13
+#include <stdbool.h>
14
+
15
+#define LCD_RS D2
16
+#define LCD_RW D3
17
+#define LCD_E  D4
18
+#define LCD_D4 D5
19
+#define LCD_D5 D6
20
+#define LCD_D6 D7
21
+#define LCD_D7 D8
22
+
23
+
24
+// --- Commands ---
25
+
26
+// Clear screen (reset)
27
+#define LCD_CLEAR 0b00000001
28
+// Move cursor to (0,0), unshift...
29
+#define LCD_HOME  0b00000010
30
+
31
+// Set mode: Increment + NoShift
32
+#define LCD_MODE_INC         0b00000110
33
+// Set mode: Increment + Shift
34
+#define LCD_MODE_INC_SHIFT   0b00000111
35
+
36
+// Set mode: Decrement + NoShift
37
+#define LCD_MODE_DEC         0b00000100
38
+// Set mode: Decrement + Shift
39
+#define LCD_MODE_DEC_SHIFT   0b00000101
40
+
41
+// Disable display (data remains untouched)
42
+#define LCD_DISABLE          0b00001000
43
+
44
+// Disable cursor
45
+#define LCD_CURSOR_NONE      0b00001100
46
+// Set cursor to still underscore
47
+#define LCD_CURSOR_BAR       0b00001110
48
+// Set cursor to blinking block
49
+#define LCD_CURSOR_BLINK     0b00001101
50
+// Set cursor to both of the above at once
51
+#define LCD_CURSOR_BOTH      (LCD_CURSOR_BAR | LCD_CURSOR_BLINK)
52
+
53
+// Move cursor
54
+#define LCD_MOVE_LEFT  0b00010000
55
+#define LCD_MOVE_RIGHT 0b00010100
56
+
57
+// Shift display
58
+#define LCD_SHIFT_LEFT  0b00011000
59
+#define LCD_SHIFT_RIGHT 0b00011100
60
+
61
+// Set iface to 5x7 font, 1-line
62
+#define LCD_IFACE_4BIT_1LINE 0b00100000
63
+#define LCD_IFACE_8BIT_1LINE 0b00110000
64
+// Set iface to 5x7 font, 2-line
65
+#define LCD_IFACE_4BIT_2LINE 0b00101000
66
+#define LCD_IFACE_8BIT_2LINE 0b00111000
67
+
68
+extern const uint8_t LCD_ROW_ADDR[4];
69
+
70
+/** Initialize the display */
71
+void lcd_init();
72
+
73
+/** Write an instruction byte */
74
+void lcd_command(uint8_t bb);
75
+
76
+/** Write a data byte */
77
+void lcd_write(uint8_t bb);
78
+
79
+/** Read BF & Address */
80
+uint8_t lcd_read_bf_addr();
81
+
82
+/** Read CGRAM or DDRAM */
83
+uint8_t lcd_read();
84
+
85
+/** Send a string to LCD */
86
+void lcd_puts(char* str_p);
87
+
88
+/** Send a string to LCD from program memory */
89
+void lcd_puts_P(const char* str_p);
90
+
91
+/** Sedn a char to LCD */
92
+void lcd_putc(const char c);
93
+
94
+/** Show string at X, Y */
95
+#define lcd_puts_xy(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts((str_p)); } while(0)
96
+
97
+/** Show string at X, Y */
98
+#define lcd_puts_xy_P(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts_P((str_p)); } while(0)
99
+
100
+/** Show char at X, Y */
101
+#define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0)
102
+
103
+/** Set cursor position */
104
+void lcd_xy(const uint8_t x, const uint8_t y);
105
+
106
+/** Set LCD cursor. If not enabled, only remember it. */
107
+#define CURSOR_NONE  0b00
108
+#define CURSOR_BAR   0b10
109
+#define CURSOR_BLINK 0b01
110
+#define CURSOR_BOTH  0b11
111
+void lcd_cursor(uint8_t type);
112
+
113
+/** Display display (preserving cursor) */
114
+void lcd_disable();
115
+
116
+/** Enable display (restoring cursor) */
117
+void lcd_enable();
118
+
119
+/** Go home */
120
+void lcd_home();
121
+
122
+/** Clear the screen */
123
+void lcd_clear();
124
+
125
+/** Define a glyph - 8 bytes, right 5 bits are used */
126
+void lcd_glyph(const uint8_t index, const uint8_t* array);
127
+
128
+/** Define a glyph that's in PROGMEM */
129
+void lcd_glyph_P(const uint8_t index, const uint8_t* array);
130
+
131
+/** Set address in CGRAM */
132
+void lcd_addr_cg(const uint8_t acg);
133
+
134
+/** Set address in DDRAM */
135
+void lcd_addr(const uint8_t add);

+ 77 - 0
lib/adc.c View File

@@ -0,0 +1,77 @@
1
+#include <avr/io.h>
2
+#include <stdint.h>
3
+#include <stdbool.h>
4
+
5
+#include "calc.h"
6
+#include "adc.h"
7
+
8
+/** Initialize the ADC */
9
+void adc_init(enum ADC_Prescaller presc)
10
+{
11
+	ADCSRA |= presc;  // 128 prescaler -> 125 kHz
12
+	ADMUX |= _BV(REFS0);  // Voltage reference
13
+	sbi(ADCSRA, ADEN);  // Enable ADC
14
+}
15
+
16
+/** Disable ADC */
17
+void adc_disable(void)
18
+{
19
+	cbi(ADCSRA, ADEN);
20
+}
21
+
22
+/** Enable ADC */
23
+void adc_enable(void)
24
+{
25
+	sbi(ADCSRA, ADEN);
26
+}
27
+
28
+/** Start a new conversion */
29
+void adc_start_conversion(uint8_t channel)
30
+{
31
+	set_low_nibble(ADMUX, channel);  // Select channel to sample
32
+	cbi(ADMUX, ADLAR);  // Align result to right
33
+	sbi(ADCSRA, ADSC);  // Start conversion
34
+}
35
+
36
+/** Check if ADC is done converting */
37
+bool adc_ready(void)
38
+{
39
+	return bit_is_low(ADCSRA, ADSC);
40
+}
41
+
42
+/** Read the result of last conversion with 8bit precision */
43
+uint8_t adc_read_8bit()
44
+{
45
+	uint8_t low = ADCL;
46
+	uint8_t high = ADCH;
47
+	return low >> 2 | high << 6;
48
+}
49
+
50
+/** Read the result of last conversion with 10bit precision */
51
+uint16_t adc_read_10bit()
52
+{
53
+	uint8_t low = ADCL;
54
+	uint8_t high = ADCH;
55
+	return ((uint16_t) high << 8) | low;
56
+}
57
+
58
+/** Start ADC conversion and wait for the result */
59
+static void adc_convert(uint8_t channel)
60
+{
61
+	adc_start_conversion(channel);
62
+	while (!adc_ready()); // Wait for it...
63
+}
64
+
65
+/** Sample analog pin with 8-bit precision */
66
+uint8_t adc_convert_8bit(uint8_t channel)
67
+{
68
+	adc_convert(channel);
69
+	return adc_read_8bit();
70
+}
71
+
72
+/** Sample analog pin with 10-bit precision */
73
+uint16_t adc_convert_10bit(uint8_t channel)
74
+{
75
+	adc_convert(channel);
76
+	return adc_read_10bit();
77
+}

+ 44 - 0
lib/adc.h View File

@@ -0,0 +1,44 @@
1
+#pragma once
2
+
3
+//
4
+// Utilities for build-in A/D converter
5
+//
6
+
7
+#include <stdint.h>
8
+
9
+enum ADC_Prescaller {
10
+	ADC_PRESC_2 = 1,
11
+	ADC_PRESC_4 = 2,
12
+	ADC_PRESC_8 = 3,
13
+	ADC_PRESC_16 = 4,
14
+	ADC_PRESC_32 = 5,
15
+	ADC_PRESC_64 = 6,
16
+	ADC_PRESC_128 = 7,
17
+};
18
+
19
+/** Initialize the ADC and enable it */
20
+void adc_init(enum ADC_Prescaller presc);
21
+
22
+/** Disable ADC (for power saving) */
23
+void adc_disable(void);
24
+
25
+/** Enable (already initialized) ADC */
26
+void adc_enable(void);
27
+
28
+/** Start a new conversion */
29
+void adc_start_conversion(uint8_t channel);
30
+
31
+/** Check if ADC is done converting */
32
+bool adc_ready(void);
33
+
34
+/** Read the result of last conversion with 8bit precision */
35
+uint8_t adc_read_8bit(void);
36
+
37
+/** Read the result of last conversion with 10bit precision */
38
+uint16_t adc_read_10bit(void);
39
+
40
+/** Sample analog pin with 8-bit precision. Blocking! */
41
+uint8_t adc_convert_8bit(uint8_t channel);
42
+
43
+/** Sample analog pin with 10-bit precision. Blocking! */
44
+uint16_t adc_convert_10bit(uint8_t channel);

+ 154 - 0
lib/calc.h View File

@@ -0,0 +1,154 @@
1
+#pragma once
2
+
3
+//
4
+// Bit and byte manipulation utilities
5
+//
6
+
7
+#include <stdint.h>
8
+
9
+
10
+// --- Increment in range ---
11
+// when overflown, wraps within range. Lower bound < upper bound.
12
+// ..., upper bound excluded
13
+#define inc_wrap(var, min, max) \
14
+    do {                             \
15
+        if ((var) >= ((max) - 1)) {  \
16
+            (var) = (min);           \
17
+        } else {                     \
18
+            (var)++;                 \
19
+        }                            \
20
+    } while(0)
21
+
22
+// ..., upper bound included
23
+#define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1)
24
+
25
+
26
+// --- Decrement in range ---
27
+// when underflown, wraps within range. Lower bound < upper bound.
28
+// ..., upper bound excluded
29
+#define dec_wrap(var, min, max) \
30
+    do {                             \
31
+        if ((var) <= (min)) {        \
32
+            (var) = (max) - 1;       \
33
+        } else {                     \
34
+            (var)--;                 \
35
+        }                            \
36
+    } while(0)
37
+
38
+
39
+// ..., upper bound included
40
+#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1)
41
+
42
+
43
+// --- Bit manipulation --
44
+
45
+// Set bit
46
+#define sbi(reg, n)  do { (reg) |=  (1 << (uint8_t)(n)); } while(0)
47
+
48
+// Clear bit
49
+#define cbi(reg, n)  do { (reg) &= ~(1 << (uint8_t)(n)); } while(0)
50
+
51
+// Get n-th bit
52
+#define get_bit(reg, n) (((reg) >> (uint8_t)(n)) & 1)
53
+
54
+
55
+// Test n-th bit (Can't use bit_is_set, as it's defined in sfr_def.h)
56
+#define bit_is_high(reg, n) get_bit((reg), (n))
57
+#define bit_is_low(reg, n)  (!get_bit((reg), (n)))
58
+
59
+// Write value to n-th bit
60
+#define set_bit(reg, n, val) \
61
+    do {                                          \
62
+        (reg) = ((reg) & ~(1 << (uint8_t)(n)))    \
63
+              | (((val) & 1) << (uint8_t)(n));    \
64
+    } while(0)
65
+
66
+// Invert n-th bit
67
+#define toggle_bit(reg, n) do { (reg) ^= (1 << (uint8_t)(n)); } while(0)
68
+
69
+
70
+// --- Bit manipulation with pointer to variable ---
71
+
72
+// Set n-th bit in pointee
73
+#define sbi_p(reg_p, n) do { (*(reg_p)) |=  (1 << (uint8_t)(n)); } while(0)
74
+// Clear n-th bit in pointee
75
+#define cbi_p(reg_p, n) do { (*(reg_p)) &= ~(1 << (uint8_t)(n)); } while(0)
76
+
77
+// Get n-th bit in pointee
78
+#define get_bit_p(reg_p, n) ((*(reg_p) >> (uint8_t)(n)) & 1)
79
+
80
+// Test n-th bit in pointee (Can't use bit_is_set, as it's redefined in sfr_def.h)
81
+#define bit_is_high_p(reg_p, bit) get_bit_p(reg_p, bit)
82
+#define bit_is_low_p(reg_p, bit) (!get_bit_p(reg_p, bit))
83
+
84
+
85
+// Write value to a bit in pointee
86
+#define set_bit_p(reg_p, n, value) \
87
+    do {                                             \
88
+        *(reg_p) = (*(reg_p) & ~(1 << (uint8_t)(n))) \
89
+                 | (((value) & 1) << (uint8_t)(n));  \
90
+    } while(0)
91
+
92
+
93
+#define toggle_bit_p(reg_p, n) do { *(reg_p) ^= (1 << (uint8_t)(n)); } while(0)
94
+
95
+
96
+// --- Nibble manipulation ---
97
+
98
+// Replace nibble in a byte
99
+#define set_low_nibble(reg, value) \
100
+    do {                                    \
101
+        (reg) = ((reg) & 0xF0)              \
102
+              | ((uint8_t)(value) & 0xF);   \
103
+    } while(0)
104
+
105
+
106
+#define set_high_nibble(reg, value) \
107
+    do {                                            \
108
+        (reg) = ((reg) & 0x0F)                      \
109
+              | (((uint8_t)(value) & 0xF) << 4);    \
110
+    } while(0)
111
+
112
+
113
+#define set_low_nibble_p(reg_p, value) \
114
+    do {                                            \
115
+        *(reg_p) = (*(reg_p) & 0xF0)                \
116
+                 | ((uint8_t)(value) & 0xF);        \
117
+    } while(0)
118
+
119
+
120
+#define set_high_nibble_p(reg_p, value) \
121
+    do {                                            \
122
+        *(reg_p) = (*(reg_p) & 0x0F)                \
123
+                 | (((uint8_t)(value) & 0xF) << 4); \
124
+    } while(0)
125
+
126
+
127
+#define low_nibble(x)   ((uint8_t)(x) & 0x0F)
128
+#define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4)
129
+
130
+
131
+// --- Range tests ---
132
+
133
+// Test if X is within low..high, regardless of bounds order
134
+#define in_range(x, low, high) \
135
+    ((low) < (high) ? ((x) >= (low) && (x) < (high))  \
136
+                    : ((x) >= (high) && (x) < (low)))  \
137
+
138
+
139
+// ..., include greater bound
140
+#define in_rangei(x, low, high) \
141
+    ((low) < (high) ? ((x) >= (low) && (x) <= (high))  \
142
+                    : ((x) >= (high) && (x) <= (low)))  \
143
+
144
+
145
+// Test if X in low..high, wrap around ends if needed.
146
+#define in_range_wrap(x, low, high) \
147
+    (((low) < (high)) ? ((x) >= (low) && (x) < (high))  \
148
+                      : ((x) >= (low) || (x) < (high)))
149
+
150
+
151
+// ..., include upper bound
152
+#define in_range_wrapi(x, low, high) \
153
+    (((low) <= (high)) ? ((x) >= (low) && (x) <= (high))  \
154
+                       : ((x) >= (low) || (x) <= (high)))

+ 277 - 0
lib/iopins.c View File

@@ -0,0 +1,277 @@
1
+#include <avr/io.h>
2
+#include <stdbool.h>
3
+#include <stdint.h>
4
+
5
+#include "calc.h"
6
+#include "iopins.h"
7
+
8
+
9
+void set_dir_n(uint8_t pin, uint8_t d)
10
+{
11
+	switch(pin) {
12
+		case 0: set_dir(0, d); return;
13
+		case 1: set_dir(1, d); return;
14
+		case 2: set_dir(2, d); return;
15
+		case 3: set_dir(3, d); return;
16
+		case 4: set_dir(4, d); return;
17
+		case 5: set_dir(5, d); return;
18
+		case 6: set_dir(6, d); return;
19
+		case 7: set_dir(7, d); return;
20
+		case 8: set_dir(8, d); return;
21
+		case 9: set_dir(9, d); return;
22
+		case 10: set_dir(10, d); return;
23
+		case 11: set_dir(11, d); return;
24
+		case 12: set_dir(12, d); return;
25
+		case 13: set_dir(13, d); return;
26
+		case 14: set_dir(14, d); return;
27
+		case 15: set_dir(15, d); return;
28
+		case 16: set_dir(16, d); return;
29
+		case 17: set_dir(17, d); return;
30
+		case 18: set_dir(18, d); return;
31
+		case 19: set_dir(19, d); return;
32
+		case 20: set_dir(20, d); return;
33
+		case 21: set_dir(21, d); return;
34
+	}
35
+}
36
+
37
+
38
+void as_input_n(uint8_t pin)
39
+{
40
+	switch(pin) {
41
+		case 0: as_input(0); return;
42
+		case 1: as_input(1); return;
43
+		case 2: as_input(2); return;
44
+		case 3: as_input(3); return;
45
+		case 4: as_input(4); return;
46
+		case 5: as_input(5); return;
47
+		case 6: as_input(6); return;
48
+		case 7: as_input(7); return;
49
+		case 8: as_input(8); return;
50
+		case 9: as_input(9); return;
51
+		case 10: as_input(10); return;
52
+		case 11: as_input(11); return;
53
+		case 12: as_input(12); return;
54
+		case 13: as_input(13); return;
55
+		case 14: as_input(14); return;
56
+		case 15: as_input(15); return;
57
+		case 16: as_input(16); return;
58
+		case 17: as_input(17); return;
59
+		case 18: as_input(18); return;
60
+		case 19: as_input(19); return;
61
+		case 20: as_input(20); return;
62
+		case 21: as_input(21); return;
63
+	}
64
+}
65
+
66
+
67
+void as_input_pu_n(uint8_t pin)
68
+{
69
+	switch(pin) {
70
+		case 0: as_input_pu(0); return;
71
+		case 1: as_input_pu(1); return;
72
+		case 2: as_input_pu(2); return;
73
+		case 3: as_input_pu(3); return;
74
+		case 4: as_input_pu(4); return;
75
+		case 5: as_input_pu(5); return;
76
+		case 6: as_input_pu(6); return;
77
+		case 7: as_input_pu(7); return;
78
+		case 8: as_input_pu(8); return;
79
+		case 9: as_input_pu(9); return;
80
+		case 10: as_input_pu(10); return;
81
+		case 11: as_input_pu(11); return;
82
+		case 12: as_input_pu(12); return;
83
+		case 13: as_input_pu(13); return;
84
+		case 14: as_input_pu(14); return;
85
+		case 15: as_input_pu(15); return;
86
+		case 16: as_input_pu(16); return;
87
+		case 17: as_input_pu(17); return;
88
+		case 18: as_input_pu(18); return;
89
+		case 19: as_input_pu(19); return;
90
+		case 20: as_input_pu(20); return;
91
+		case 21: as_input_pu(21); return;
92
+	}
93
+}
94
+
95
+
96
+void as_output_n(uint8_t pin)
97
+{
98
+	switch(pin) {
99
+		case 0: as_output(0); return;
100
+		case 1: as_output(1); return;
101
+		case 2: as_output(2); return;
102
+		case 3: as_output(3); return;
103
+		case 4: as_output(4); return;
104
+		case 5: as_output(5); return;
105
+		case 6: as_output(6); return;
106
+		case 7: as_output(7); return;
107
+		case 8: as_output(8); return;
108
+		case 9: as_output(9); return;
109
+		case 10: as_output(10); return;
110
+		case 11: as_output(11); return;
111
+		case 12: as_output(12); return;
112
+		case 13: as_output(13); return;
113
+		case 14: as_output(14); return;
114
+		case 15: as_output(15); return;
115
+		case 16: as_output(16); return;
116
+		case 17: as_output(17); return;
117
+		case 18: as_output(18); return;
118
+		case 19: as_output(19); return;
119
+		case 20: as_output(20); return;
120
+		case 21: as_output(21); return;
121
+	}
122
+}
123
+
124
+void pin_set_n(uint8_t pin, uint8_t v)
125
+{
126
+	switch(pin) {
127
+		case 0: pin_set(0, v); return;
128
+		case 1: pin_set(1, v); return;
129
+		case 2: pin_set(2, v); return;
130
+		case 3: pin_set(3, v); return;
131
+		case 4: pin_set(4, v); return;
132
+		case 5: pin_set(5, v); return;
133
+		case 6: pin_set(6, v); return;
134
+		case 7: pin_set(7, v); return;
135
+		case 8: pin_set(8, v); return;
136
+		case 9: pin_set(9, v); return;
137
+		case 10: pin_set(10, v); return;
138
+		case 11: pin_set(11, v); return;
139
+		case 12: pin_set(12, v); return;
140
+		case 13: pin_set(13, v); return;
141
+		case 14: pin_set(14, v); return;
142
+		case 15: pin_set(15, v); return;
143
+		case 16: pin_set(16, v); return;
144
+		case 17: pin_set(17, v); return;
145
+		case 18: pin_set(18, v); return;
146
+		case 19: pin_set(19, v); return;
147
+		case 20: pin_set(20, v); return;
148
+		case 21: pin_set(21, v); return;
149
+	}
150
+}
151
+
152
+void pin_down_n(uint8_t pin)
153
+{
154
+	switch(pin) {
155
+		case 0: pin_down(0); return;
156
+		case 1: pin_down(1); return;
157
+		case 2: pin_down(2); return;
158
+		case 3: pin_down(3); return;
159
+		case 4: pin_down(4); return;
160
+		case 5: pin_down(5); return;
161
+		case 6: pin_down(6); return;
162
+		case 7: pin_down(7); return;
163
+		case 8: pin_down(8); return;
164
+		case 9: pin_down(9); return;
165
+		case 10: pin_down(10); return;
166
+		case 11: pin_down(11); return;
167
+		case 12: pin_down(12); return;
168
+		case 13: pin_down(13); return;
169
+		case 14: pin_down(14); return;
170
+		case 15: pin_down(15); return;
171
+		case 16: pin_down(16); return;
172
+		case 17: pin_down(17); return;
173
+		case 18: pin_down(18); return;
174
+		case 19: pin_down(19); return;
175
+		case 20: pin_down(20); return;
176
+		case 21: pin_down(21); return;
177
+	}
178
+}
179
+
180
+void pin_up_n(uint8_t pin)
181
+{
182
+	switch(pin) {
183
+		case 0: pin_up(0); return;
184
+		case 1: pin_up(1); return;
185
+		case 2: pin_up(2); return;
186
+		case 3: pin_up(3); return;
187
+		case 4: pin_up(4); return;
188
+		case 5: pin_up(5); return;
189
+		case 6: pin_up(6); return;
190
+		case 7: pin_up(7); return;
191
+		case 8: pin_up(8); return;
192
+		case 9: pin_up(9); return;
193
+		case 10: pin_up(10); return;
194
+		case 11: pin_up(11); return;
195
+		case 12: pin_up(12); return;
196
+		case 13: pin_up(13); return;
197
+		case 14: pin_up(14); return;
198
+		case 15: pin_up(15); return;
199
+		case 16: pin_up(16); return;
200
+		case 17: pin_up(17); return;
201
+		case 18: pin_up(18); return;
202
+		case 19: pin_up(19); return;
203
+		case 20: pin_up(20); return;
204
+		case 21: pin_up(21); return;
205
+	}
206
+}
207
+
208
+
209
+void pin_toggle_n(uint8_t pin)
210
+{
211
+	switch(pin) {
212
+		case 0: pin_toggle(0); return;
213
+		case 1: pin_toggle(1); return;
214
+		case 2: pin_toggle(2); return;
215
+		case 3: pin_toggle(3); return;
216
+		case 4: pin_toggle(4); return;
217
+		case 5: pin_toggle(5); return;
218
+		case 6: pin_toggle(6); return;
219
+		case 7: pin_toggle(7); return;
220
+		case 8: pin_toggle(8); return;
221
+		case 9: pin_toggle(9); return;
222
+		case 10: pin_toggle(10); return;
223
+		case 11: pin_toggle(11); return;
224
+		case 12: pin_toggle(12); return;
225
+		case 13: pin_toggle(13); return;
226
+		case 14: pin_toggle(14); return;
227
+		case 15: pin_toggle(15); return;
228
+		case 16: pin_toggle(16); return;
229
+		case 17: pin_toggle(17); return;
230
+		case 18: pin_toggle(18); return;
231
+		case 19: pin_toggle(19); return;
232
+		case 20: pin_toggle(20); return;
233
+		case 21: pin_toggle(21); return;
234
+	}
235
+}
236
+
237
+
238
+bool pin_read_n(uint8_t pin)
239
+{
240
+	switch(pin) {
241
+		case 0: return pin_read(0);
242
+		case 1: return pin_read(1);
243
+		case 2: return pin_read(2);
244
+		case 3: return pin_read(3);
245
+		case 4: return pin_read(4);
246
+		case 5: return pin_read(5);
247
+		case 6: return pin_read(6);
248
+		case 7: return pin_read(7);
249
+		case 8: return pin_read(8);
250
+		case 9: return pin_read(9);
251
+		case 10: return pin_read(10);
252
+		case 11: return pin_read(11);
253
+		case 12: return pin_read(12);
254
+		case 13: return pin_read(13);
255
+		case 14: return pin_read(14);
256
+		case 15: return pin_read(15);
257
+		case 16: return pin_read(16);
258
+		case 17: return pin_read(17);
259
+		case 18: return pin_read(18);
260
+		case 19: return pin_read(19);
261
+		case 20: return pin_read(20);
262
+		case 21: return pin_read(21);
263
+	}
264
+	return false;
265
+}
266
+
267
+
268
+bool pin_is_low_n(uint8_t pin)
269
+{
270
+	return !pin_read_n(pin);
271
+}
272
+
273
+
274
+bool pin_is_high_n(uint8_t pin)
275
+{
276
+	return pin_read_n(pin);
277
+}

+ 213 - 0
lib/iopins.h View File

@@ -0,0 +1,213 @@
1
+#pragma once
2
+
3
+//
4
+// * Utilities for pin aliasing / numbering. *
5
+//
6
+// Designed for Arduino.
7
+//
8
+// If you know the pin number beforehand, you can use the macros.
9
+//
10
+// If you need to use a variable for pin number, use the `_n` functions.
11
+// They are much slower, so always check if you really need them
12
+// - and they aren't fit for things where precise timing is required.
13
+//
14
+
15
+#include <avr/io.h>
16
+#include <stdbool.h>
17
+#include <stdint.h>
18
+
19
+#include "calc.h"
20
+
21
+
22
+// type: pointer to port
23
+typedef volatile uint8_t* PORT_P;
24
+
25
+
26
+/** Pin numbering reference */
27
+#define D0 0
28
+#define D1 1
29
+#define D2 2
30
+#define D3 3
31
+#define D4 4
32
+#define D5 5
33
+#define D6 6
34
+#define D7 7
35
+#define D8 8
36
+#define D9 9
37
+#define D10 10
38
+#define D11 11
39
+#define D12 12
40
+#define D13 13
41
+#define D14 14
42
+#define D15 15
43
+#define D16 16
44
+#define D17 17
45
+#define D18 18
46
+#define D19 19
47
+#define D20 20
48
+#define D21 21
49
+#define A0 14
50
+#define A1 15
51
+#define A2 16
52
+#define A3 17
53
+#define A4 18
54
+#define A5 19
55
+#define A6 20
56
+#define A7 21
57
+
58
+
59
+#define _ddr(pin)  _DDR_##pin
60
+#define _pin(pin)  _PIN_##pin
61
+#define _pn(pin)   _PN_##pin
62
+#define _port(pin) _PORT_##pin
63
+
64
+
65
+/** Set pin direction */
66
+#define set_dir(pin, d)  set_bit( _ddr(pin), _pn(pin), d )
67
+void    set_dir_n(const uint8_t pin, const uint8_t d);
68
+
69
+
70
+/** Configure pin as input */
71
+#define as_input(pin)    cbi( _ddr(pin), _pn(pin) )
72
+void    as_input_n(const uint8_t pin);
73
+
74
+
75
+/** Configure pin as input, with pull-up enabled */
76
+#define as_input_pu(pin) { as_input(pin); pin_up(pin); }
77
+void    as_input_pu_n(const uint8_t pin);
78
+
79
+
80
+/** Configure pin as output */
81
+#define as_output(pin)   sbi( _ddr(pin), _pn(pin) )
82
+void    as_output_n(const uint8_t pin);
83
+
84
+
85
+/** Write value to a pin */
86
+#define pin_set(pin, v) set_bit( _port(pin), _pn(pin), v )
87
+void    pin_set_n(const uint8_t pin, const uint8_t v);
88
+
89
+
90
+/** Write 0 to a pin */
91
+#define pin_down(pin)    cbi( _port(pin), _pn(pin) )
92
+void    pin_down_n(const uint8_t pin);
93
+
94
+
95
+/** Write 1 to a pin */
96
+#define pin_up(pin)   sbi( _port(pin), _pn(pin) )
97
+void    pin_up_n(uint8_t pin);
98
+
99
+
100
+/** Toggle a pin state */
101
+#define pin_toggle(pin)   sbi( _pin(pin), _pn(pin) )
102
+void    pin_toggle_n(uint8_t pin);
103
+
104
+
105
+/** Read a pin value */
106
+#define pin_read(pin)  get_bit( _pin(pin), _pn(pin) )
107
+bool    pin_read_n(uint8_t pin);
108
+
109
+
110
+/** CHeck if pin is low */
111
+#define pin_is_low(pin)   (pin_read(pin) == 0)
112
+bool    pin_is_low_n(uint8_t pin);
113
+
114
+
115
+/** CHeck if pin is high */
116
+#define pin_is_high(pin)  (pin_read(pin) != 0)
117
+bool    pin_is_high_n(uint8_t pin);
118
+
119
+
120
+
121
+// Helper macros
122
+
123
+#define _PORT_0  PORTD
124
+#define _PORT_1  PORTD
125
+#define _PORT_2  PORTD
126
+#define _PORT_3  PORTD
127
+#define _PORT_4  PORTD
128
+#define _PORT_5  PORTD
129
+#define _PORT_6  PORTD
130
+#define _PORT_7  PORTD
131
+#define _PORT_8  PORTB
132
+#define _PORT_9  PORTB
133
+#define _PORT_10 PORTB
134
+#define _PORT_11 PORTB
135
+#define _PORT_12 PORTB
136
+#define _PORT_13 PORTB
137
+#define _PORT_14 PORTC
138
+#define _PORT_15 PORTC
139
+#define _PORT_16 PORTC
140
+#define _PORT_17 PORTC
141
+#define _PORT_18 PORTC
142
+#define _PORT_19 PORTC
143
+#define _PORT_20 PORTC
144
+#define _PORT_21 PORTC
145
+
146
+#define _PIN_0  PIND
147
+#define _PIN_1  PIND
148
+#define _PIN_2  PIND
149
+#define _PIN_3  PIND
150
+#define _PIN_4  PIND
151
+#define _PIN_5  PIND
152
+#define _PIN_6  PIND
153
+#define _PIN_7  PIND
154
+#define _PIN_8  PINB
155
+#define _PIN_9  PINB
156
+#define _PIN_10 PINB
157
+#define _PIN_11 PINB
158
+#define _PIN_12 PINB
159
+#define _PIN_13 PINB
160
+#define _PIN_14 PINC
161
+#define _PIN_15 PINC
162
+#define _PIN_16 PINC
163
+#define _PIN_17 PINC
164
+#define _PIN_18 PINC
165
+#define _PIN_19 PINC
166
+#define _PIN_20 PINC
167
+#define _PIN_21 PINC
168
+
169
+#define _DDR_0  DDRD
170
+#define _DDR_1  DDRD
171
+#define _DDR_2  DDRD
172
+#define _DDR_3  DDRD
173
+#define _DDR_4  DDRD
174
+#define _DDR_5  DDRD
175
+#define _DDR_6  DDRD
176
+#define _DDR_7  DDRD
177
+#define _DDR_8  DDRB
178
+#define _DDR_9  DDRB
179
+#define _DDR_10 DDRB
180
+#define _DDR_11 DDRB
181
+#define _DDR_12 DDRB
182
+#define _DDR_13 DDRB
183
+#define _DDR_14 DDRC
184
+#define _DDR_15 DDRC
185
+#define _DDR_16 DDRC
186
+#define _DDR_17 DDRC
187
+#define _DDR_18 DDRC
188
+#define _DDR_19 DDRC
189
+#define _DDR_20 DDRC
190
+#define _DDR_21 DDRC
191
+
192
+#define _PN_0  0
193
+#define _PN_1  1
194
+#define _PN_2  2
195
+#define _PN_3  3
196
+#define _PN_4  4
197
+#define _PN_5  5
198
+#define _PN_6  6
199
+#define _PN_7  7
200
+#define _PN_8  0
201
+#define _PN_9  1
202
+#define _PN_10 2
203
+#define _PN_11 3
204
+#define _PN_12 4
205
+#define _PN_13 5
206
+#define _PN_14 0
207
+#define _PN_15 1
208
+#define _PN_16 2
209
+#define _PN_17 3
210
+#define _PN_18 4
211
+#define _PN_19 5
212
+#define _PN_20 6
213
+#define _PN_21 7

+ 21 - 0
lib/nsdelay.h View File

@@ -0,0 +1,21 @@
1
+#pragma once
2
+
3
+//
4
+// Functions for precise delays (nanoseconds / cycles)
5
+//
6
+
7
+#include <avr/io.h>
8
+#include <util/delay_basic.h>
9
+#include <stdint.h>
10
+
11
+/* Convert nanoseconds to cycle count */
12
+#define ns2cycles(ns)  ( (ns) / (1000000000L / (signed long) F_CPU) )
13
+
14
+/** Wait c cycles */
15
+#define delay_c(c)  (((c) > 0) ? __builtin_avr_delay_cycles(c) :  __builtin_avr_delay_cycles(0))
16
+
17
+/** Wait n nanoseconds, plus c cycles  */
18
+#define delay_ns_c(ns, c)  delay_c(ns2cycles(ns) + (c))
19
+
20
+/** Wait n nanoseconds  */
21
+#define delay_ns(ns)  delay_c(ns2cycles(ns))

+ 62 - 0
lib/spi.c View File

@@ -0,0 +1,62 @@
1
+#include <avr/io.h>
2
+#include <stdint.h>
3
+#include <stdbool.h>
4
+
5
+#include "iopins.h"
6
+#include "spi.h"
7
+
8
+
9
+/** Init SPI (for SD card communication) */
10
+void spi_init_master(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha, enum SPI_clk_div clkdiv)
11
+{
12
+	as_output(PIN_SS); // SS output - we control slave
13
+	as_output(PIN_MOSI); // MOSI output - we talk to slave
14
+	as_output(PIN_SCK); // SCK output - we're generating clock
15
+	// MISO is configured automatically as input
16
+
17
+	SPCR = 0;
18
+	SPCR |= (1 << MSTR);
19
+	SPCR |= (order << DORD);
20
+	SPCR |= (cpol << CPOL);
21
+	SPCR |= (cpha << CPHA);
22
+
23
+	// speed
24
+	SPCR |= (clkdiv & 0b11);
25
+	SPSR = (bool)(clkdiv & 0b100); // write SPI2X flag
26
+
27
+	// enable SPI
28
+	SPCR |= (1 << SPE);
29
+}
30
+
31
+
32
+/** Init SPI (for SD card communication) */
33
+void spi_init_slave(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha)
34
+{
35
+	as_output(PIN_MISO); // we're listening to master
36
+	// MOSI, SS, SCK are configured automatically
37
+
38
+	SPCR = 0;
39
+	SPCR |= (order << DORD);
40
+	SPCR |= (cpol << CPOL);
41
+	SPCR |= (cpha << CPHA);
42
+
43
+	// enable SPI
44
+	SPCR |= (1 << SPE);
45
+}
46
+
47
+/** Write a byte to SPI. Returns received byte. */
48
+uint8_t spi_send(uint8_t byte)
49
+{
50
+	SPDR = byte;
51
+	while (bit_is_low(SPSR, SPIF));
52
+
53
+	return SPDR;
54
+}
55
+
56
+/** Receive (as slave). Blocking. */
57
+uint8_t spi_receive(uint8_t reply)
58
+{
59
+	SPDR = reply;
60
+	while (bit_is_low(SPSR, SPIF));
61
+	return SPDR;
62
+}

+ 69 - 0
lib/spi.h View File

@@ -0,0 +1,69 @@
1
+#pragma once
2
+
3
+#include <avr/io.h>
4
+#include <stdint.h>
5
+
6
+#include "calc.h"
7
+#include "iopins.h"
8
+
9
+#define PIN_MISO 12
10
+#define PIN_MOSI 11
11
+#define PIN_SCK 13
12
+#define PIN_SS 10
13
+
14
+/** Bit order */
15
+enum SPI_order {
16
+	SPI_LSB_FIRST = 0,
17
+	SPI_MSB_FIRST = 1
18
+};
19
+
20
+/** Clock polarity */
21
+enum SPI_cpol {
22
+	CPOL_0 = 0,
23
+	CPOL_1 = 1
24
+};
25
+
26
+/** Clock phase */
27
+enum SPI_cpha {
28
+	CPHA_0 = 0,
29
+	CPHA_1 = 1
30
+};
31
+
32
+/** Clock prescaller <SPI2X><SPR1><SPR0> */
33
+enum SPI_clk_div {
34
+	SPI_DIV_2   = 0b100, // 2x (master only, can't receive at this speed)
35
+	SPI_DIV_4   = 0b000,
36
+	SPI_DIV_8   = 0b101, // 2x
37
+	SPI_DIV_16  = 0b001,
38
+	SPI_DIV_32  = 0b110, // 2x
39
+	SPI_DIV_64  = 0b010,
40
+	SPI_DIV_128 = 0b011
41
+};
42
+
43
+
44
+/** Set SS to active state (LOW) */
45
+#define spi_ss_enable()  pin_down(PIN_SS)
46
+
47
+
48
+/** Set SS to disabled state (HIGH) */
49
+#define spi_ss_disable() pin_up(PIN_SS)
50
+
51
+
52
+/** Enable SPI ISR */
53
+#define spi_isr_enable(yes) set_bit(SPCR, SPIE, yes)
54
+
55
+
56
+/** Init SPI (for SD card communication) */
57
+void spi_init_master(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha, enum SPI_clk_div clkdiv);
58
+
59
+
60
+/** Init SPI (for SD card communication) */
61
+void spi_init_slave(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha);
62
+
63
+
64
+/** Write a byte to SPI. Returns received byte. */
65
+uint8_t spi_send(uint8_t byte);
66
+
67
+
68
+/** Receive (as slave). Blocking. */
69
+uint8_t spi_receive(uint8_t reply);

+ 83 - 0
lib/usart.c View File

@@ -0,0 +1,83 @@
1
+#include <avr/io.h>
2
+#include <avr/pgmspace.h>
3
+#include <util/delay.h>
4
+
5
+#include <stdbool.h>
6
+#include <stdint.h>
7
+#include <stdlib.h>
8
+
9
+#include "calc.h"
10
+#include "usart.h"
11
+
12
+
13
+void usart_init(uint16_t ubrr)
14
+{
15
+	/*Set baud rate */
16
+	UBRR0H = (uint8_t)(ubrr >> 8);
17
+	UBRR0L = (uint8_t) ubrr;
18
+
19
+	// Enable Rx and Tx
20
+	UCSR0B = (1 << RXEN0) | (1 << TXEN0);
21
+
22
+	// Clear U2X0
23
+	cbi(UCSR0A, U2X0);
24
+
25
+	// 8-bit data, 1 stop bit
26
+	UCSR0C = (0b11 << UCSZ00);
27
+}
28
+
29
+
30
+/** Set Double Speed Asynchronous mode */
31
+void usart_set_2x(bool set)
32
+{
33
+	set_bit(UCSR0A, U2X0, set);
34
+}
35
+
36
+
37
+/** Send byte over USART */
38
+void usart_tx(uint8_t data)
39
+{
40
+	// Wait for transmit buffer
41
+	while (!usart_tx_ready());
42
+	// send it
43
+	UDR0 = data;
44
+}
45
+
46
+
47
+/** Receive one byte over USART */
48
+uint8_t usart_rx(void)
49
+{
50
+	// Wait for data to be received
51
+	while (!usart_rx_ready());
52
+	// Get and return received data from buffer
53
+	return UDR0;
54
+}
55
+
56
+
57
+/** Send string over USART */
58
+void usart_puts(const char* str)
59
+{
60
+	while (*str) {
61
+		usart_tx(*str++);
62
+	}
63
+}
64
+
65
+
66
+/** Send progmem string over USART */
67
+void usart_puts_P(const char* str)
68
+{
69
+	char c;
70
+	while ((c = pgm_read_byte(str++))) {
71
+		usart_tx(c);
72
+	}
73
+}
74
+
75
+
76
+/** Clear receive buffer */
77
+void usart_flush_rx(void)
78
+{
79
+	uint8_t dummy;
80
+	while (bit_is_high(UCSR0A, RXC0)) {
81
+		dummy = UDR0;
82
+	}
83
+}

+ 86 - 0
lib/usart.h View File

@@ -0,0 +1,86 @@
1
+#pragma once
2
+
3
+//
4
+// Utilities for UART communication.
5
+//
6
+// First, init uart with usart_init().
7
+// Then enable interrupts you want with usart_XXX_isr_enable().
8
+//
9
+
10
+#include <avr/io.h>
11
+#include <avr/pgmspace.h>
12
+#include <util/delay.h>
13
+
14
+#include <stdbool.h>
15
+#include <stdint.h>
16
+
17
+#include "calc.h"
18
+
19
+
20
+/* USART BAUD RATE REGISTER values at 16 MHz */
21
+enum {
22
+	BAUD_9600 = 103,
23
+	BAUD_14400 = 68,
24
+	BAUD_19200 = 51,
25
+	BAUD_28800 = 34,
26
+	BAUD_38400 = 25,
27
+	BAUD_57600 = 16,
28
+	BAUD_76800 = 12,
29
+	BAUD_115200 = 8,
30
+	BAUD_250k = 3,
31
+	BAUD_500k = 1,
32
+	BAUD_1M = 0,
33
+};
34
+
35
+/** Init UART with a UBRR value - can use the BAUD_* constants for 16 MHz */
36
+void usart_init(uint16_t ubrr);
37
+
38
+
39
+/** Set Double Speed Asynchronous mode on or off */
40
+void usart_set_2x(bool set);
41
+
42
+
43
+/** Check if there's a byte in the RX register */
44
+#define usart_rx_ready() bit_is_high(UCSR0A, RXC0)
45
+
46
+
47
+/** Check if USART is ready to accept new byte to send */
48
+#define usart_tx_ready() bit_is_high(UCSR0A, UDRE0)
49
+
50
+
51
+// ---- Enable UART interrupts ------------
52
+
53
+/** Enable or disable RX ISR */
54
+#define usart_isr_rx_enable(yes) set_bit(UCSR0B, RXCIE0, (yes))
55
+
56
+
57
+/** Enable or disable TX ISR (all data sent) */
58
+#define usart_isr_tx_enable(yes) set_bit(UCSR0B, TXCIE0, (yes))
59
+
60
+
61
+/** Enable or disable DRE ISR (data register empty) */
62
+#define usart_isr_dre_enable(yes) set_bit(UCSR0B, UDRIE0, (yes))
63
+
64
+
65
+// ---- Basic IO --------------------------
66
+
67
+/** Send byte over USART */
68
+void usart_tx(uint8_t data);
69
+
70
+
71
+/** Receive one byte over USART */
72
+uint8_t usart_rx(void);
73
+
74
+
75
+/** Clear receive buffer */
76
+void usart_flush_rx(void);
77
+
78
+
79
+// ---- Strings ---------------------------
80
+
81
+/** Send string over UART */
82
+void usart_puts(const char* str);
83
+
84
+
85
+/** Send progmem string `PSTR("foobar")` over UART  */
86
+void usart_puts_P(const char* str);

+ 56 - 0
main.c View File

@@ -0,0 +1,56 @@
1
+#include <avr/io.h>          // register definitions
2
+#include <avr/pgmspace.h>    // storing data in program memory
3
+#include <avr/interrupt.h>   // interrupt vectors
4
+#include <util/delay.h>      // delay functions
5
+
6
+#include <stdint.h>          // C header for int types like uint8_t
7
+#include <stdbool.h>         // C header for the bool type
8
+#include <stdlib.h>
9
+
10
+// Include stuff from the library
11
+#include "lib/iopins.h"
12
+#include "lib/usart.h"
13
+#include "lcd.h"
14
+
15
+
16
+// Pins
17
+#define LED 13
18
+
19
+void _lcd_wait_bf();
20
+void _lcd_write_byte(uint8_t bb);
21
+
22
+// UART receive handler
23
+ISR(USART_RX_vect)
24
+{
25
+	// "ECHO" function:
26
+	uint8_t b = usart_rx();
27
+	usart_tx(b); // send back
28
+}
29
+
30
+
31
+void main()
32
+{
33
+	usart_init(BAUD_115200);
34
+	usart_isr_rx_enable(true); // enable RX interrupt handler
35
+
36
+	// configure pins
37
+	as_output(LED);
38
+
39
+	lcd_init();
40
+
41
+	// globally enable interrupts (for the USART_RX handler)
42
+	sei();
43
+	uint8_t cnt = 0;
44
+	while (1) {
45
+		lcd_clear();
46
+
47
+		lcd_xy(cnt, 0);
48
+		lcd_putc('A'+cnt);
49
+
50
+		cnt = (cnt+1)%20;
51
+
52
+		pin_toggle(13); // blink the LED
53
+
54
+		_delay_ms(100);
55
+	}
56
+}

+ 14 - 0
style.astylerc View File

@@ -0,0 +1,14 @@
1
+style=kr
2
+indent=tab
3
+max-instatement-indent=60
4
+
5
+convert-tabs
6
+
7
+indent-switches
8
+keep-one-line-statements
9
+
10
+pad-oper
11
+unpad-paren
12
+pad-header
13
+
14
+verbose