aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr')
-rw-r--r--platforms/avr/drivers/analog.c138
-rw-r--r--platforms/avr/drivers/analog.h53
-rw-r--r--platforms/avr/drivers/glcdfont.c23
-rw-r--r--platforms/avr/drivers/hd44780.c536
-rw-r--r--platforms/avr/drivers/hd44780.h348
-rw-r--r--platforms/avr/drivers/i2c_master.c241
-rw-r--r--platforms/avr/drivers/i2c_master.h43
-rw-r--r--platforms/avr/drivers/i2c_slave.c111
-rw-r--r--platforms/avr/drivers/i2c_slave.h41
-rw-r--r--platforms/avr/drivers/serial.c529
-rw-r--r--platforms/avr/drivers/spi_master.c180
-rw-r--r--platforms/avr/drivers/spi_master.h59
-rw-r--r--platforms/avr/drivers/ssd1306.c319
-rw-r--r--platforms/avr/drivers/ssd1306.h87
-rw-r--r--platforms/avr/drivers/uart.c170
-rw-r--r--platforms/avr/drivers/uart.h35
-rw-r--r--platforms/avr/drivers/ws2812.c176
-rw-r--r--platforms/avr/drivers/ws2812_i2c.c27
18 files changed, 3116 insertions, 0 deletions
diff --git a/platforms/avr/drivers/analog.c b/platforms/avr/drivers/analog.c
new file mode 100644
index 000000000..8d299ffdb
--- /dev/null
+++ b/platforms/avr/drivers/analog.c
@@ -0,0 +1,138 @@
1/* Copyright 2015 Jack Humbert
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <avr/io.h>
18#include <avr/pgmspace.h>
19#include <stdint.h>
20#include "analog.h"
21
22static uint8_t aref = ADC_REF_POWER;
23
24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); }
25
26// Arduino compatible pin input
27int16_t analogRead(uint8_t pin) {
28#if defined(__AVR_ATmega32U4__)
29 // clang-format off
30 static const uint8_t PROGMEM pin_to_mux[] = {
31 //A0 A1 A2 A3 A4 A5
32 //F7 F6 F5 F4 F1 F0
33 0x07, 0x06, 0x05, 0x04, 0x01, 0x00,
34 //A6 A7 A8 A9 A10 A11
35 //D4 D7 B4 B5 B6 D6
36 0x20, 0x22, 0x23, 0x24, 0x25, 0x21
37 };
38 // clang-format on
39 if (pin >= 12) return 0;
40 return adc_read(pgm_read_byte(pin_to_mux + pin));
41#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
42 if (pin >= 8) return 0;
43 return adc_read(pin);
44#else
45 return 0;
46#endif
47}
48
49int16_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); }
50
51uint8_t pinToMux(pin_t pin) {
52 switch (pin) {
53 // clang-format off
54#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
55 case F0: return 0; // ADC0
56 case F1: return _BV(MUX0); // ADC1
57 case F2: return _BV(MUX1); // ADC2
58 case F3: return _BV(MUX1) | _BV(MUX0); // ADC3
59 case F4: return _BV(MUX2); // ADC4
60 case F5: return _BV(MUX2) | _BV(MUX0); // ADC5
61 case F6: return _BV(MUX2) | _BV(MUX1); // ADC6
62 case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
63 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
64#elif defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
65 case F0: return 0; // ADC0
66 case F1: return _BV(MUX0); // ADC1
67 case F4: return _BV(MUX2); // ADC4
68 case F5: return _BV(MUX2) | _BV(MUX0); // ADC5
69 case F6: return _BV(MUX2) | _BV(MUX1); // ADC6
70 case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
71 case D4: return _BV(MUX5); // ADC8
72 case D6: return _BV(MUX5) | _BV(MUX0); // ADC9
73 case D7: return _BV(MUX5) | _BV(MUX1); // ADC10
74 case B4: return _BV(MUX5) | _BV(MUX1) | _BV(MUX0); // ADC11
75 case B5: return _BV(MUX5) | _BV(MUX2); // ADC12
76 case B6: return _BV(MUX5) | _BV(MUX2) | _BV(MUX0); // ADC13
77 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
78#elif defined(__AVR_ATmega32A__)
79 case A0: return 0; // ADC0
80 case A1: return _BV(MUX0); // ADC1
81 case A2: return _BV(MUX1); // ADC2
82 case A3: return _BV(MUX1) | _BV(MUX0); // ADC3
83 case A4: return _BV(MUX2); // ADC4
84 case A5: return _BV(MUX2) | _BV(MUX0); // ADC5
85 case A6: return _BV(MUX2) | _BV(MUX1); // ADC6
86 case A7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // ADC7
87 default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
88#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
89 case C0: return 0; // ADC0
90 case C1: return _BV(MUX0); // ADC1
91 case C2: return _BV(MUX1); // ADC2
92 case C3: return _BV(MUX1) | _BV(MUX0); // ADC3
93 case C4: return _BV(MUX2); // ADC4
94 case C5: return _BV(MUX2) | _BV(MUX0); // ADC5
95 // ADC7:6 not present in DIP package and not shared by GPIO pins
96 default: return _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); // 0V
97#endif
98 // clang-format on
99 }
100 return 0;
101}
102
103int16_t adc_read(uint8_t mux) {
104 uint16_t low;
105
106 // Enable ADC and configure prescaler
107 ADCSRA = _BV(ADEN) | ADC_PRESCALER;
108
109#if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
110 // High speed mode and ADC8-13
111 ADCSRB = _BV(ADHSM) | (mux & _BV(MUX5));
112#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
113 // High speed mode only
114 ADCSRB = _BV(ADHSM);
115#endif
116
117 // Configure mux input
118#if defined(MUX4)
119 ADMUX = aref | (mux & (_BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));
120#else
121 ADMUX = aref | (mux & (_BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));
122#endif
123
124 // Start the conversion
125 ADCSRA |= _BV(ADSC);
126 // Wait for result
127 while (ADCSRA & _BV(ADSC))
128 ;
129 // Must read LSB first
130 low = ADCL;
131 // Must read MSB only once!
132 low |= (ADCH << 8);
133
134 // turn off the ADC
135 ADCSRA &= ~(1 << ADEN);
136
137 return low;
138}
diff --git a/platforms/avr/drivers/analog.h b/platforms/avr/drivers/analog.h
new file mode 100644
index 000000000..058882450
--- /dev/null
+++ b/platforms/avr/drivers/analog.h
@@ -0,0 +1,53 @@
1/* Copyright 2015 Jack Humbert
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include "quantum.h"
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25void analogReference(uint8_t mode);
26int16_t analogRead(uint8_t pin);
27
28int16_t analogReadPin(pin_t pin);
29uint8_t pinToMux(pin_t pin);
30
31int16_t adc_read(uint8_t mux);
32#ifdef __cplusplus
33}
34#endif
35
36#define ADC_REF_EXTERNAL 0 // AREF, Internal Vref turned off
37#define ADC_REF_POWER _BV(REFS0) // AVCC with external capacitor on AREF pin
38#define ADC_REF_INTERNAL (_BV(REFS1) | _BV(REFS0)) // Internal 2.56V Voltage Reference with external capacitor on AREF pin (1.1V for 328P)
39
40// These prescaler values are for high speed mode, ADHSM = 1
41#if F_CPU == 16000000L || F_CPU == 12000000L
42# define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS1)) // /64
43#elif F_CPU == 8000000L
44# define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS0)) // /32
45#elif F_CPU == 4000000L
46# define ADC_PRESCALER (_BV(ADPS2)) // /16
47#elif F_CPU == 2000000L
48# define ADC_PRESCALER (_BV(ADPS1) | _BV(ADPS0)) // /8
49#elif F_CPU == 1000000L
50# define ADC_PRESCALER _BV(ADPS1) // /4
51#else
52# define ADC_PRESCALER _BV(ADPS0) // /2
53#endif
diff --git a/platforms/avr/drivers/glcdfont.c b/platforms/avr/drivers/glcdfont.c
new file mode 100644
index 000000000..5e763b054
--- /dev/null
+++ b/platforms/avr/drivers/glcdfont.c
@@ -0,0 +1,23 @@
1// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
2// See gfxfont.h for newer custom bitmap font info.
3
4#include "progmem.h"
5
6// Standard ASCII 5x7 font
7
8static const unsigned char font[] PROGMEM = {
9 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, 0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24, 0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89, 0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
10 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41, 0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,
11 0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, 0x03, 0x04, 0x78, 0x04, 0x03,
12 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, 0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,
13 0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40, 0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41, 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
14 0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E, 0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54, 0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00, 0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
15 0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54, 0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49, 0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
16 0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42, 0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
17 0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E, 0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6, 0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00, 0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40, 0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D, 0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48, 0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A, 0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
18 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
19 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
20 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17, 0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F,
21 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0, 0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44, 0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
22 0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55, 0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E, 0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D, 0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D, 0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44, 0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0, 0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36, 0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F, 0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
23};
diff --git a/platforms/avr/drivers/hd44780.c b/platforms/avr/drivers/hd44780.c
new file mode 100644
index 000000000..f71069dec
--- /dev/null
+++ b/platforms/avr/drivers/hd44780.c
@@ -0,0 +1,536 @@
1/****************************************************************************
2 Title: HD44780U LCD library
3 Author: Peter Fleury <pfleury@gmx.ch> http://tinyurl.com/peterfleury
4 License: GNU General Public License Version 3
5 File: $Id: lcd.c,v 1.15.2.2 2015/01/17 12:16:05 peter Exp $
6 Software: AVR-GCC 3.3
7 Target: any AVR device, memory mapped mode only for AT90S4414/8515/Mega
8
9 DESCRIPTION
10 Basic routines for interfacing a HD44780U-based text lcd display
11
12 Originally based on Volker Oth's lcd library,
13 changed lcd_init(), added additional constants for lcd_command(),
14 added 4-bit I/O mode, improved and optimized code.
15
16 Library can be operated in memory mapped mode (LCD_IO_MODE=0) or in
17 4-bit IO port mode (LCD_IO_MODE=1). 8-bit IO port mode not supported.
18
19 Memory mapped mode compatible with Kanda STK200, but supports also
20 generation of R/W signal through A8 address line.
21
22 USAGE
23 See the C include lcd.h file for a description of each function
24
25*****************************************************************************/
26#include <inttypes.h>
27#include <avr/io.h>
28#include <avr/pgmspace.h>
29#include <util/delay.h>
30#include "hd44780.h"
31
32/*
33** constants/macros
34*/
35#define DDR(x) (*(&x - 1)) /* address of data direction register of port x */
36#if defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
37/* on ATmega64/128 PINF is on port 0x00 and not 0x60 */
38# define PIN(x) (&PORTF == &(x) ? _SFR_IO8(0x00) : (*(&x - 2)))
39#else
40# define PIN(x) (*(&x - 2)) /* address of input register of port x */
41#endif
42
43#if LCD_IO_MODE
44# define lcd_e_delay() _delay_us(LCD_DELAY_ENABLE_PULSE)
45# define lcd_e_high() LCD_E_PORT |= _BV(LCD_E_PIN);
46# define lcd_e_low() LCD_E_PORT &= ~_BV(LCD_E_PIN);
47# define lcd_e_toggle() toggle_e()
48# define lcd_rw_high() LCD_RW_PORT |= _BV(LCD_RW_PIN)
49# define lcd_rw_low() LCD_RW_PORT &= ~_BV(LCD_RW_PIN)
50# define lcd_rs_high() LCD_RS_PORT |= _BV(LCD_RS_PIN)
51# define lcd_rs_low() LCD_RS_PORT &= ~_BV(LCD_RS_PIN)
52#endif
53
54#if LCD_IO_MODE
55# if LCD_LINES == 1
56# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_1LINE
57# else
58# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_2LINES
59# endif
60#else
61# if LCD_LINES == 1
62# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_8BIT_1LINE
63# else
64# define LCD_FUNCTION_DEFAULT LCD_FUNCTION_8BIT_2LINES
65# endif
66#endif
67
68#if LCD_CONTROLLER_KS0073
69# if LCD_LINES == 4
70
71# define KS0073_EXTENDED_FUNCTION_REGISTER_ON 0x2C /* |0|010|1100 4-bit mode, extension-bit RE = 1 */
72# define KS0073_EXTENDED_FUNCTION_REGISTER_OFF 0x28 /* |0|010|1000 4-bit mode, extension-bit RE = 0 */
73# define KS0073_4LINES_MODE 0x09 /* |0|000|1001 4 lines mode */
74
75# endif
76#endif
77
78/*
79** function prototypes
80*/
81#if LCD_IO_MODE
82static void toggle_e(void);
83#endif
84
85/*
86** local functions
87*/
88
89/*************************************************************************
90delay for a minimum of <us> microseconds
91the number of loops is calculated at compile-time from MCU clock frequency
92*************************************************************************/
93#define delay(us) _delay_us(us)
94
95#if LCD_IO_MODE
96/* toggle Enable Pin to initiate write */
97static void toggle_e(void) {
98 lcd_e_high();
99 lcd_e_delay();
100 lcd_e_low();
101}
102#endif
103
104/*************************************************************************
105Low-level function to write byte to LCD controller
106Input: data byte to write to LCD
107 rs 1: write data
108 0: write instruction
109Returns: none
110*************************************************************************/
111#if LCD_IO_MODE
112static void lcd_write(uint8_t data, uint8_t rs) {
113 unsigned char dataBits;
114
115 if (rs) { /* write data (RS=1, RW=0) */
116 lcd_rs_high();
117 } else { /* write instruction (RS=0, RW=0) */
118 lcd_rs_low();
119 }
120 lcd_rw_low(); /* RW=0 write mode */
121
122 if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)) {
123 /* configure data pins as output */
124 DDR(LCD_DATA0_PORT) |= 0x0F;
125
126 /* output high nibble first */
127 dataBits = LCD_DATA0_PORT & 0xF0;
128 LCD_DATA0_PORT = dataBits | ((data >> 4) & 0x0F);
129 lcd_e_toggle();
130
131 /* output low nibble */
132 LCD_DATA0_PORT = dataBits | (data & 0x0F);
133 lcd_e_toggle();
134
135 /* all data pins high (inactive) */
136 LCD_DATA0_PORT = dataBits | 0x0F;
137 } else {
138 /* configure data pins as output */
139 DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN);
140 DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN);
141 DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN);
142 DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN);
143
144 /* output high nibble first */
145 LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN);
146 LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN);
147 LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN);
148 LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);
149 if (data & 0x80) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
150 if (data & 0x40) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
151 if (data & 0x20) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
152 if (data & 0x10) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
153 lcd_e_toggle();
154
155 /* output low nibble */
156 LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN);
157 LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN);
158 LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN);
159 LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN);
160 if (data & 0x08) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
161 if (data & 0x04) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
162 if (data & 0x02) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
163 if (data & 0x01) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
164 lcd_e_toggle();
165
166 /* all data pins high (inactive) */
167 LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN);
168 LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN);
169 LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN);
170 LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN);
171 }
172}
173#else
174# define lcd_write(d, rs) \
175 if (rs) \
176 *(volatile uint8_t *)(LCD_IO_DATA) = d; \
177 else \
178 *(volatile uint8_t *)(LCD_IO_FUNCTION) = d;
179/* rs==0 -> write instruction to LCD_IO_FUNCTION */
180/* rs==1 -> write data to LCD_IO_DATA */
181#endif
182
183/*************************************************************************
184Low-level function to read byte from LCD controller
185Input: rs 1: read data
186 0: read busy flag / address counter
187Returns: byte read from LCD controller
188*************************************************************************/
189#if LCD_IO_MODE
190static uint8_t lcd_read(uint8_t rs) {
191 uint8_t data;
192
193 if (rs)
194 lcd_rs_high(); /* RS=1: read data */
195 else
196 lcd_rs_low(); /* RS=0: read busy flag */
197 lcd_rw_high(); /* RW=1 read mode */
198
199 if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)) {
200 DDR(LCD_DATA0_PORT) &= 0xF0; /* configure data pins as input */
201
202 lcd_e_high();
203 lcd_e_delay();
204 data = PIN(LCD_DATA0_PORT) << 4; /* read high nibble first */
205 lcd_e_low();
206
207 lcd_e_delay(); /* Enable 500ns low */
208
209 lcd_e_high();
210 lcd_e_delay();
211 data |= PIN(LCD_DATA0_PORT) & 0x0F; /* read low nibble */
212 lcd_e_low();
213 } else {
214 /* configure data pins as input */
215 DDR(LCD_DATA0_PORT) &= ~_BV(LCD_DATA0_PIN);
216 DDR(LCD_DATA1_PORT) &= ~_BV(LCD_DATA1_PIN);
217 DDR(LCD_DATA2_PORT) &= ~_BV(LCD_DATA2_PIN);
218 DDR(LCD_DATA3_PORT) &= ~_BV(LCD_DATA3_PIN);
219
220 /* read high nibble first */
221 lcd_e_high();
222 lcd_e_delay();
223 data = 0;
224 if (PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN)) data |= 0x10;
225 if (PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN)) data |= 0x20;
226 if (PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN)) data |= 0x40;
227 if (PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN)) data |= 0x80;
228 lcd_e_low();
229
230 lcd_e_delay(); /* Enable 500ns low */
231
232 /* read low nibble */
233 lcd_e_high();
234 lcd_e_delay();
235 if (PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN)) data |= 0x01;
236 if (PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN)) data |= 0x02;
237 if (PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN)) data |= 0x04;
238 if (PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN)) data |= 0x08;
239 lcd_e_low();
240 }
241 return data;
242}
243#else
244# define lcd_read(rs) (rs) ? *(volatile uint8_t *)(LCD_IO_DATA + LCD_IO_READ) : *(volatile uint8_t *)(LCD_IO_FUNCTION + LCD_IO_READ)
245/* rs==0 -> read instruction from LCD_IO_FUNCTION */
246/* rs==1 -> read data from LCD_IO_DATA */
247#endif
248
249/*************************************************************************
250loops while lcd is busy, returns address counter
251*************************************************************************/
252static uint8_t lcd_waitbusy(void)
253
254{
255 register uint8_t c;
256
257 /* wait until busy flag is cleared */
258 while ((c = lcd_read(0)) & (1 << LCD_BUSY)) {
259 }
260
261 /* the address counter is updated 4us after the busy flag is cleared */
262 delay(LCD_DELAY_BUSY_FLAG);
263
264 /* now read the address counter */
265 return (lcd_read(0)); // return address counter
266
267} /* lcd_waitbusy */
268
269/*************************************************************************
270Move cursor to the start of next line or to the first line if the cursor
271is already on the last line.
272*************************************************************************/
273static inline void lcd_newline(uint8_t pos) {
274 register uint8_t addressCounter;
275
276#if LCD_LINES == 1
277 addressCounter = 0;
278#endif
279#if LCD_LINES == 2
280 if (pos < (LCD_START_LINE2))
281 addressCounter = LCD_START_LINE2;
282 else
283 addressCounter = LCD_START_LINE1;
284#endif
285#if LCD_LINES == 4
286# if KS0073_4LINES_MODE
287 if (pos < LCD_START_LINE2)
288 addressCounter = LCD_START_LINE2;
289 else if ((pos >= LCD_START_LINE2) && (pos < LCD_START_LINE3))
290 addressCounter = LCD_START_LINE3;
291 else if ((pos >= LCD_START_LINE3) && (pos < LCD_START_LINE4))
292 addressCounter = LCD_START_LINE4;
293 else
294 addressCounter = LCD_START_LINE1;
295# else
296 if (pos < LCD_START_LINE3)
297 addressCounter = LCD_START_LINE2;
298 else if ((pos >= LCD_START_LINE2) && (pos < LCD_START_LINE4))
299 addressCounter = LCD_START_LINE3;
300 else if ((pos >= LCD_START_LINE3) && (pos < LCD_START_LINE2))
301 addressCounter = LCD_START_LINE4;
302 else
303 addressCounter = LCD_START_LINE1;
304# endif
305#endif
306 lcd_command((1 << LCD_DDRAM) + addressCounter);
307
308} /* lcd_newline */
309
310/*
311** PUBLIC FUNCTIONS
312*/
313
314/*************************************************************************
315Send LCD controller instruction command
316Input: instruction to send to LCD controller, see HD44780 data sheet
317Returns: none
318*************************************************************************/
319void lcd_command(uint8_t cmd) {
320 lcd_waitbusy();
321 lcd_write(cmd, 0);
322}
323
324/*************************************************************************
325Send data byte to LCD controller
326Input: data to send to LCD controller, see HD44780 data sheet
327Returns: none
328*************************************************************************/
329void lcd_data(uint8_t data) {
330 lcd_waitbusy();
331 lcd_write(data, 1);
332}
333
334/*************************************************************************
335Set cursor to specified position
336Input: x horizontal position (0: left most position)
337 y vertical position (0: first line)
338Returns: none
339*************************************************************************/
340void lcd_gotoxy(uint8_t x, uint8_t y) {
341#if LCD_LINES == 1
342 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
343#endif
344#if LCD_LINES == 2
345 if (y == 0)
346 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
347 else
348 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
349#endif
350#if LCD_LINES == 4
351 if (y == 0)
352 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
353 else if (y == 1)
354 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
355 else if (y == 2)
356 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE3 + x);
357 else /* y==3 */
358 lcd_command((1 << LCD_DDRAM) + LCD_START_LINE4 + x);
359#endif
360
361} /* lcd_gotoxy */
362
363/*************************************************************************
364*************************************************************************/
365int lcd_getxy(void) { return lcd_waitbusy(); }
366
367/*************************************************************************
368Clear display and set cursor to home position
369*************************************************************************/
370void lcd_clrscr(void) { lcd_command(1 << LCD_CLR); }
371
372/*************************************************************************
373Set cursor to home position
374*************************************************************************/
375void lcd_home(void) { lcd_command(1 << LCD_HOME); }
376
377/*************************************************************************
378Display character at current cursor position
379Input: character to be displayed
380Returns: none
381*************************************************************************/
382void lcd_putc(char c) {
383 uint8_t pos;
384
385 pos = lcd_waitbusy(); // read busy-flag and address counter
386 if (c == '\n') {
387 lcd_newline(pos);
388 } else {
389#if LCD_WRAP_LINES == 1
390# if LCD_LINES == 1
391 if (pos == LCD_START_LINE1 + LCD_DISP_LENGTH) {
392 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE1, 0);
393 }
394# elif LCD_LINES == 2
395 if (pos == LCD_START_LINE1 + LCD_DISP_LENGTH) {
396 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE2, 0);
397 } else if (pos == LCD_START_LINE2 + LCD_DISP_LENGTH) {
398 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE1, 0);
399 }
400# elif LCD_LINES == 4
401 if (pos == LCD_START_LINE1 + LCD_DISP_LENGTH) {
402 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE2, 0);
403 } else if (pos == LCD_START_LINE2 + LCD_DISP_LENGTH) {
404 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE3, 0);
405 } else if (pos == LCD_START_LINE3 + LCD_DISP_LENGTH) {
406 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE4, 0);
407 } else if (pos == LCD_START_LINE4 + LCD_DISP_LENGTH) {
408 lcd_write((1 << LCD_DDRAM) + LCD_START_LINE1, 0);
409 }
410# endif
411 lcd_waitbusy();
412#endif
413 lcd_write(c, 1);
414 }
415
416} /* lcd_putc */
417
418/*************************************************************************
419Display string without auto linefeed
420Input: string to be displayed
421Returns: none
422*************************************************************************/
423void lcd_puts(const char *s)
424/* print string on lcd (no auto linefeed) */
425{
426 register char c;
427
428 while ((c = *s++)) {
429 lcd_putc(c);
430 }
431
432} /* lcd_puts */
433
434/*************************************************************************
435Display string from program memory without auto linefeed
436Input: string from program memory be be displayed
437Returns: none
438*************************************************************************/
439void lcd_puts_p(const char *progmem_s)
440/* print string from program memory on lcd (no auto linefeed) */
441{
442 register char c;
443
444 while ((c = pgm_read_byte(progmem_s++))) {
445 lcd_putc(c);
446 }
447
448} /* lcd_puts_p */
449
450/*************************************************************************
451Initialize display and select type of cursor
452Input: dispAttr LCD_DISP_OFF display off
453 LCD_DISP_ON display on, cursor off
454 LCD_DISP_ON_CURSOR display on, cursor on
455 LCD_DISP_CURSOR_BLINK display on, cursor on flashing
456Returns: none
457*************************************************************************/
458void lcd_init(uint8_t dispAttr) {
459#if LCD_IO_MODE
460 /*
461 * Initialize LCD to 4 bit I/O mode
462 */
463
464 if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (&LCD_RS_PORT == &LCD_DATA0_PORT) && (&LCD_RW_PORT == &LCD_DATA0_PORT) && (&LCD_E_PORT == &LCD_DATA0_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) && (LCD_RS_PIN == 4) && (LCD_RW_PIN == 5) && (LCD_E_PIN == 6)) {
465 /* configure all port bits as output (all LCD lines on same port) */
466 DDR(LCD_DATA0_PORT) |= 0x7F;
467 } else if ((&LCD_DATA0_PORT == &LCD_DATA1_PORT) && (&LCD_DATA1_PORT == &LCD_DATA2_PORT) && (&LCD_DATA2_PORT == &LCD_DATA3_PORT) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3)) {
468 /* configure all port bits as output (all LCD data lines on same port, but control lines on different ports) */
469 DDR(LCD_DATA0_PORT) |= 0x0F;
470 DDR(LCD_RS_PORT) |= _BV(LCD_RS_PIN);
471 DDR(LCD_RW_PORT) |= _BV(LCD_RW_PIN);
472 DDR(LCD_E_PORT) |= _BV(LCD_E_PIN);
473 } else {
474 /* configure all port bits as output (LCD data and control lines on different ports */
475 DDR(LCD_RS_PORT) |= _BV(LCD_RS_PIN);
476 DDR(LCD_RW_PORT) |= _BV(LCD_RW_PIN);
477 DDR(LCD_E_PORT) |= _BV(LCD_E_PIN);
478 DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN);
479 DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN);
480 DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN);
481 DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN);
482 }
483 delay(LCD_DELAY_BOOTUP); /* wait 16ms or more after power-on */
484
485 /* initial write to lcd is 8bit */
486 LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN); // LCD_FUNCTION>>4;
487 LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN); // LCD_FUNCTION_8BIT>>4;
488 lcd_e_toggle();
489 delay(LCD_DELAY_INIT); /* delay, busy flag can't be checked here */
490
491 /* repeat last command */
492 lcd_e_toggle();
493 delay(LCD_DELAY_INIT_REP); /* delay, busy flag can't be checked here */
494
495 /* repeat last command a third time */
496 lcd_e_toggle();
497 delay(LCD_DELAY_INIT_REP); /* delay, busy flag can't be checked here */
498
499 /* now configure for 4bit mode */
500 LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN); // LCD_FUNCTION_4BIT_1LINE>>4
501 lcd_e_toggle();
502 delay(LCD_DELAY_INIT_4BIT); /* some displays need this additional delay */
503
504 /* from now the LCD only accepts 4 bit I/O, we can use lcd_command() */
505#else
506 /*
507 * Initialize LCD to 8 bit memory mapped mode
508 */
509
510 /* enable external SRAM (memory mapped lcd) and one wait state */
511 MCUCR = _BV(SRE) | _BV(SRW);
512
513 /* reset LCD */
514 delay(LCD_DELAY_BOOTUP); /* wait 16ms after power-on */
515 lcd_write(LCD_FUNCTION_8BIT_1LINE, 0); /* function set: 8bit interface */
516 delay(LCD_DELAY_INIT); /* wait 5ms */
517 lcd_write(LCD_FUNCTION_8BIT_1LINE, 0); /* function set: 8bit interface */
518 delay(LCD_DELAY_INIT_REP); /* wait 64us */
519 lcd_write(LCD_FUNCTION_8BIT_1LINE, 0); /* function set: 8bit interface */
520 delay(LCD_DELAY_INIT_REP); /* wait 64us */
521#endif
522
523#if KS0073_4LINES_MODE
524 /* Display with KS0073 controller requires special commands for enabling 4 line mode */
525 lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_ON);
526 lcd_command(KS0073_4LINES_MODE);
527 lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_OFF);
528#else
529 lcd_command(LCD_FUNCTION_DEFAULT); /* function set: display lines */
530#endif
531 lcd_command(LCD_DISP_OFF); /* display off */
532 lcd_clrscr(); /* display clear */
533 lcd_command(LCD_MODE_DEFAULT); /* set entry mode */
534 lcd_command(dispAttr); /* display/cursor control */
535
536} /* lcd_init */
diff --git a/platforms/avr/drivers/hd44780.h b/platforms/avr/drivers/hd44780.h
new file mode 100644
index 000000000..08e60f8a4
--- /dev/null
+++ b/platforms/avr/drivers/hd44780.h
@@ -0,0 +1,348 @@
1/*************************************************************************
2 Title : C include file for the HD44780U LCD library (lcd.c)
3 Author: Peter Fleury <pfleury@gmx.ch> http://tinyurl.com/peterfleury
4 License: GNU General Public License Version 3
5 File: $Id: lcd.h,v 1.14.2.4 2015/01/20 17:16:07 peter Exp $
6 Software: AVR-GCC 4.x
7 Hardware: any AVR device, memory mapped mode only for AVR with
8 memory mapped interface (AT90S8515/ATmega8515/ATmega128)
9***************************************************************************/
10
11/**
12 @mainpage
13 Collection of libraries for AVR-GCC
14 @author Peter Fleury pfleury@gmx.ch http://tinyurl.com/peterfleury
15 @copyright (C) 2015 Peter Fleury, GNU General Public License Version 3
16
17 @file
18 @defgroup pfleury_lcd LCD library <lcd.h>
19 @code #include <lcd.h> @endcode
20
21 @brief Basic routines for interfacing a HD44780U-based character LCD display
22
23 LCD character displays can be found in many devices, like espresso machines, laser printers.
24 The Hitachi HD44780 controller and its compatible controllers like Samsung KS0066U have become an industry standard for these types of displays.
25
26 This library allows easy interfacing with a HD44780 compatible display and can be
27 operated in memory mapped mode (LCD_IO_MODE defined as 0 in the include file lcd.h.) or in
28 4-bit IO port mode (LCD_IO_MODE defined as 1). 8-bit IO port mode is not supported.
29
30 Memory mapped mode is compatible with old Kanda STK200 starter kit, but also supports
31 generation of R/W signal through A8 address line.
32
33 @see The chapter <a href=" http://homepage.hispeed.ch/peterfleury/avr-lcd44780.html" target="_blank">Interfacing a HD44780 Based LCD to an AVR</a>
34 on my home page, which shows example circuits how to connect an LCD to an AVR controller.
35
36 @author Peter Fleury pfleury@gmx.ch http://tinyurl.com/peterfleury
37
38 @version 2.0
39
40 @copyright (C) 2015 Peter Fleury, GNU General Public License Version 3
41
42*/
43
44#pragma once
45
46#include <inttypes.h>
47#include <avr/pgmspace.h>
48
49#if (__GNUC__ * 100 + __GNUC_MINOR__) < 405
50# error "This library requires AVR-GCC 4.5 or later, update to newer AVR-GCC compiler !"
51#endif
52
53/**@{*/
54
55/*
56 * LCD and target specific definitions below can be defined in a separate include file with name lcd_definitions.h instead modifying this file
57 * by adding -D_LCD_DEFINITIONS_FILE to the CDEFS section in the Makefile
58 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
59 */
60#ifdef _LCD_DEFINITIONS_FILE
61# include "lcd_definitions.h"
62#endif
63
64/**
65 * @name Definition for LCD controller type
66 * Use 0 for HD44780 controller, change to 1 for displays with KS0073 controller.
67 */
68#ifndef LCD_CONTROLLER_KS0073
69# define LCD_CONTROLLER_KS0073 0 /**< Use 0 for HD44780 controller, 1 for KS0073 controller */
70#endif
71
72/**
73 * @name Definitions for Display Size
74 * Change these definitions to adapt setting to your display
75 *
76 * These definitions can be defined in a separate include file \b lcd_definitions.h instead modifying this file by
77 * adding -D_LCD_DEFINITIONS_FILE to the CDEFS section in the Makefile.
78 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
79 *
80 */
81#ifndef LCD_LINES
82# define LCD_LINES 2 /**< number of visible lines of the display */
83#endif
84#ifndef LCD_DISP_LENGTH
85# define LCD_DISP_LENGTH 16 /**< visibles characters per line of the display */
86#endif
87#ifndef LCD_LINE_LENGTH
88# define LCD_LINE_LENGTH 0x40 /**< internal line length of the display */
89#endif
90#ifndef LCD_START_LINE1
91# define LCD_START_LINE1 0x00 /**< DDRAM address of first char of line 1 */
92#endif
93#ifndef LCD_START_LINE2
94# define LCD_START_LINE2 0x40 /**< DDRAM address of first char of line 2 */
95#endif
96#ifndef LCD_START_LINE3
97# define LCD_START_LINE3 0x14 /**< DDRAM address of first char of line 3 */
98#endif
99#ifndef LCD_START_LINE4
100# define LCD_START_LINE4 0x54 /**< DDRAM address of first char of line 4 */
101#endif
102#ifndef LCD_WRAP_LINES
103# define LCD_WRAP_LINES 0 /**< 0: no wrap, 1: wrap at end of visibile line */
104#endif
105
106/**
107 * @name Definitions for 4-bit IO mode
108 *
109 * The four LCD data lines and the three control lines RS, RW, E can be on the
110 * same port or on different ports.
111 * Change LCD_RS_PORT, LCD_RW_PORT, LCD_E_PORT if you want the control lines on
112 * different ports.
113 *
114 * Normally the four data lines should be mapped to bit 0..3 on one port, but it
115 * is possible to connect these data lines in different order or even on different
116 * ports by adapting the LCD_DATAx_PORT and LCD_DATAx_PIN definitions.
117 *
118 * Adjust these definitions to your target.\n
119 * These definitions can be defined in a separate include file \b lcd_definitions.h instead modifying this file by
120 * adding \b -D_LCD_DEFINITIONS_FILE to the \b CDEFS section in the Makefile.
121 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
122 *
123 */
124#define LCD_IO_MODE 1 /**< 0: memory mapped mode, 1: IO port mode */
125
126#if LCD_IO_MODE
127
128# ifndef LCD_PORT
129# define LCD_PORT PORTA /**< port for the LCD lines */
130# endif
131# ifndef LCD_DATA0_PORT
132# define LCD_DATA0_PORT LCD_PORT /**< port for 4bit data bit 0 */
133# endif
134# ifndef LCD_DATA1_PORT
135# define LCD_DATA1_PORT LCD_PORT /**< port for 4bit data bit 1 */
136# endif
137# ifndef LCD_DATA2_PORT
138# define LCD_DATA2_PORT LCD_PORT /**< port for 4bit data bit 2 */
139# endif
140# ifndef LCD_DATA3_PORT
141# define LCD_DATA3_PORT LCD_PORT /**< port for 4bit data bit 3 */
142# endif
143# ifndef LCD_DATA0_PIN
144# define LCD_DATA0_PIN 4 /**< pin for 4bit data bit 0 */
145# endif
146# ifndef LCD_DATA1_PIN
147# define LCD_DATA1_PIN 5 /**< pin for 4bit data bit 1 */
148# endif
149# ifndef LCD_DATA2_PIN
150# define LCD_DATA2_PIN 6 /**< pin for 4bit data bit 2 */
151# endif
152# ifndef LCD_DATA3_PIN
153# define LCD_DATA3_PIN 7 /**< pin for 4bit data bit 3 */
154# endif
155# ifndef LCD_RS_PORT
156# define LCD_RS_PORT LCD_PORT /**< port for RS line */
157# endif
158# ifndef LCD_RS_PIN
159# define LCD_RS_PIN 3 /**< pin for RS line */
160# endif
161# ifndef LCD_RW_PORT
162# define LCD_RW_PORT LCD_PORT /**< port for RW line */
163# endif
164# ifndef LCD_RW_PIN
165# define LCD_RW_PIN 2 /**< pin for RW line */
166# endif
167# ifndef LCD_E_PORT
168# define LCD_E_PORT LCD_PORT /**< port for Enable line */
169# endif
170# ifndef LCD_E_PIN
171# define LCD_E_PIN 1 /**< pin for Enable line */
172# endif
173
174#elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || defined(__AVR_ATmega64__) || defined(__AVR_ATmega8515__) || defined(__AVR_ATmega103__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega161__) || defined(__AVR_ATmega162__)
175/*
176 * memory mapped mode is only supported when the device has an external data memory interface
177 */
178# define LCD_IO_DATA 0xC000 /* A15=E=1, A14=RS=1 */
179# define LCD_IO_FUNCTION 0x8000 /* A15=E=1, A14=RS=0 */
180# define LCD_IO_READ 0x0100 /* A8 =R/W=1 (R/W: 1=Read, 0=Write */
181
182#else
183# error "external data memory interface not available for this device, use 4-bit IO port mode"
184
185#endif
186
187/**
188 * @name Definitions of delays
189 * Used to calculate delay timers.
190 * Adapt the F_CPU define in the Makefile to the clock frequency in Hz of your target
191 *
192 * These delay times can be adjusted, if some displays require different delays.\n
193 * These definitions can be defined in a separate include file \b lcd_definitions.h instead modifying this file by
194 * adding \b -D_LCD_DEFINITIONS_FILE to the \b CDEFS section in the Makefile.
195 * All definitions added to the file lcd_definitions.h will override the default definitions from lcd.h
196 */
197#ifndef LCD_DELAY_BOOTUP
198# define LCD_DELAY_BOOTUP 16000 /**< delay in micro seconds after power-on */
199#endif
200#ifndef LCD_DELAY_INIT
201# define LCD_DELAY_INIT 5000 /**< delay in micro seconds after initialization command sent */
202#endif
203#ifndef LCD_DELAY_INIT_REP
204# define LCD_DELAY_INIT_REP 64 /**< delay in micro seconds after initialization command repeated */
205#endif
206#ifndef LCD_DELAY_INIT_4BIT
207# define LCD_DELAY_INIT_4BIT 64 /**< delay in micro seconds after setting 4-bit mode */
208#endif
209#ifndef LCD_DELAY_BUSY_FLAG
210# define LCD_DELAY_BUSY_FLAG 4 /**< time in micro seconds the address counter is updated after busy flag is cleared */
211#endif
212#ifndef LCD_DELAY_ENABLE_PULSE
213# define LCD_DELAY_ENABLE_PULSE 1 /**< enable signal pulse width in micro seconds */
214#endif
215
216/**
217 * @name Definitions for LCD command instructions
218 * The constants define the various LCD controller instructions which can be passed to the
219 * function lcd_command(), see HD44780 data sheet for a complete description.
220 */
221
222/* instruction register bit positions, see HD44780U data sheet */
223#define LCD_CLR 0 /* DB0: clear display */
224#define LCD_HOME 1 /* DB1: return to home position */
225#define LCD_ENTRY_MODE 2 /* DB2: set entry mode */
226#define LCD_ENTRY_INC 1 /* DB1: 1=increment, 0=decrement */
227#define LCD_ENTRY_SHIFT 0 /* DB2: 1=display shift on */
228#define LCD_ON 3 /* DB3: turn lcd/cursor on */
229#define LCD_ON_DISPLAY 2 /* DB2: turn display on */
230#define LCD_ON_CURSOR 1 /* DB1: turn cursor on */
231#define LCD_ON_BLINK 0 /* DB0: blinking cursor ? */
232#define LCD_MOVE 4 /* DB4: move cursor/display */
233#define LCD_MOVE_DISP 3 /* DB3: move display (0-> cursor) ? */
234#define LCD_MOVE_RIGHT 2 /* DB2: move right (0-> left) ? */
235#define LCD_FUNCTION 5 /* DB5: function set */
236#define LCD_FUNCTION_8BIT 4 /* DB4: set 8BIT mode (0->4BIT mode) */
237#define LCD_FUNCTION_2LINES 3 /* DB3: two lines (0->one line) */
238#define LCD_FUNCTION_10DOTS 2 /* DB2: 5x10 font (0->5x7 font) */
239#define LCD_CGRAM 6 /* DB6: set CG RAM address */
240#define LCD_DDRAM 7 /* DB7: set DD RAM address */
241#define LCD_BUSY 7 /* DB7: LCD is busy */
242
243/* set entry mode: display shift on/off, dec/inc cursor move direction */
244#define LCD_ENTRY_DEC 0x04 /* display shift off, dec cursor move dir */
245#define LCD_ENTRY_DEC_SHIFT 0x05 /* display shift on, dec cursor move dir */
246#define LCD_ENTRY_INC_ 0x06 /* display shift off, inc cursor move dir */
247#define LCD_ENTRY_INC_SHIFT 0x07 /* display shift on, inc cursor move dir */
248
249/* display on/off, cursor on/off, blinking char at cursor position */
250#define LCD_DISP_OFF 0x08 /* display off */
251#define LCD_DISP_ON 0x0C /* display on, cursor off */
252#define LCD_DISP_ON_BLINK 0x0D /* display on, cursor off, blink char */
253#define LCD_DISP_ON_CURSOR 0x0E /* display on, cursor on */
254#define LCD_DISP_ON_CURSOR_BLINK 0x0F /* display on, cursor on, blink char */
255
256/* move cursor/shift display */
257#define LCD_MOVE_CURSOR_LEFT 0x10 /* move cursor left (decrement) */
258#define LCD_MOVE_CURSOR_RIGHT 0x14 /* move cursor right (increment) */
259#define LCD_MOVE_DISP_LEFT 0x18 /* shift display left */
260#define LCD_MOVE_DISP_RIGHT 0x1C /* shift display right */
261
262/* function set: set interface data length and number of display lines */
263#define LCD_FUNCTION_4BIT_1LINE 0x20 /* 4-bit interface, single line, 5x7 dots */
264#define LCD_FUNCTION_4BIT_2LINES 0x28 /* 4-bit interface, dual line, 5x7 dots */
265#define LCD_FUNCTION_8BIT_1LINE 0x30 /* 8-bit interface, single line, 5x7 dots */
266#define LCD_FUNCTION_8BIT_2LINES 0x38 /* 8-bit interface, dual line, 5x7 dots */
267
268#define LCD_MODE_DEFAULT ((1 << LCD_ENTRY_MODE) | (1 << LCD_ENTRY_INC))
269
270/**
271 * @name Functions
272 */
273
274/**
275 @brief Initialize display and select type of cursor
276 @param dispAttr \b LCD_DISP_OFF display off\n
277 \b LCD_DISP_ON display on, cursor off\n
278 \b LCD_DISP_ON_CURSOR display on, cursor on\n
279 \b LCD_DISP_ON_CURSOR_BLINK display on, cursor on flashing
280 @return none
281*/
282extern void lcd_init(uint8_t dispAttr);
283
284/**
285 @brief Clear display and set cursor to home position
286 @return none
287*/
288extern void lcd_clrscr(void);
289
290/**
291 @brief Set cursor to home position
292 @return none
293*/
294extern void lcd_home(void);
295
296/**
297 @brief Set cursor to specified position
298
299 @param x horizontal position\n (0: left most position)
300 @param y vertical position\n (0: first line)
301 @return none
302*/
303extern void lcd_gotoxy(uint8_t x, uint8_t y);
304
305/**
306 @brief Display character at current cursor position
307 @param c character to be displayed
308 @return none
309*/
310extern void lcd_putc(char c);
311
312/**
313 @brief Display string without auto linefeed
314 @param s string to be displayed
315 @return none
316*/
317extern void lcd_puts(const char *s);
318
319/**
320 @brief Display string from program memory without auto linefeed
321 @param progmem_s string from program memory be be displayed
322 @return none
323 @see lcd_puts_P
324*/
325extern void lcd_puts_p(const char *progmem_s);
326
327/**
328 @brief Send LCD controller instruction command
329 @param cmd instruction to send to LCD controller, see HD44780 data sheet
330 @return none
331*/
332extern void lcd_command(uint8_t cmd);
333
334/**
335 @brief Send data byte to LCD controller
336
337 Similar to lcd_putc(), but without interpreting LF
338 @param data byte to send to LCD controller, see HD44780 data sheet
339 @return none
340*/
341extern void lcd_data(uint8_t data);
342
343/**
344 @brief macros for automatically storing string constant in program memory
345*/
346#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))
347
348/**@}*/
diff --git a/platforms/avr/drivers/i2c_master.c b/platforms/avr/drivers/i2c_master.c
new file mode 100644
index 000000000..2773e0077
--- /dev/null
+++ b/platforms/avr/drivers/i2c_master.c
@@ -0,0 +1,241 @@
1/* Copyright (C) 2019 Elia Ritterbusch
2 +
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-master-lib
18 */
19
20#include <avr/io.h>
21#include <util/twi.h>
22
23#include "i2c_master.h"
24#include "timer.h"
25#include "wait.h"
26
27#ifndef F_SCL
28# define F_SCL 400000UL // SCL frequency
29#endif
30
31#ifndef I2C_START_RETRY_COUNT
32# define I2C_START_RETRY_COUNT 20
33#endif // I2C_START_RETRY_COUNT
34
35#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)
36
37#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
38
39void i2c_init(void) {
40 TWSR = 0; /* no prescaler */
41 TWBR = (uint8_t)TWBR_val;
42
43#ifdef __AVR_ATmega32A__
44 // set pull-up resistors on I2C bus pins
45 PORTC |= 0b11;
46
47 // enable TWI (two-wire interface)
48 TWCR |= (1 << TWEN);
49
50 // enable TWI interrupt and slave address ACK
51 TWCR |= (1 << TWIE);
52 TWCR |= (1 << TWEA);
53#endif
54}
55
56static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) {
57 // reset TWI control register
58 TWCR = 0;
59 // transmit START condition
60 TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
61
62 uint16_t timeout_timer = timer_read();
63 while (!(TWCR & (1 << TWINT))) {
64 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
65 return I2C_STATUS_TIMEOUT;
66 }
67 }
68
69 // check if the start condition was successfully transmitted
70 if (((TW_STATUS & 0xF8) != TW_START) && ((TW_STATUS & 0xF8) != TW_REP_START)) {
71 return I2C_STATUS_ERROR;
72 }
73
74 // load slave address into data register
75 TWDR = address;
76 // start transmission of address
77 TWCR = (1 << TWINT) | (1 << TWEN);
78
79 timeout_timer = timer_read();
80 while (!(TWCR & (1 << TWINT))) {
81 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
82 return I2C_STATUS_TIMEOUT;
83 }
84 }
85
86 // check if the device has acknowledged the READ / WRITE mode
87 uint8_t twst = TW_STATUS & 0xF8;
88 if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) {
89 return I2C_STATUS_ERROR;
90 }
91
92 return I2C_STATUS_SUCCESS;
93}
94
95i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
96 // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled.
97 uint16_t timeout_timer = timer_read();
98 uint16_t time_slice = MAX(1, (timeout == (I2C_TIMEOUT_INFINITE)) ? 5 : (timeout / (I2C_START_RETRY_COUNT))); // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries
99 i2c_status_t status;
100 do {
101 status = i2c_start_impl(address, time_slice);
102 } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout)));
103 return status;
104}
105
106i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {
107 // load data into data register
108 TWDR = data;
109 // start transmission of data
110 TWCR = (1 << TWINT) | (1 << TWEN);
111
112 uint16_t timeout_timer = timer_read();
113 while (!(TWCR & (1 << TWINT))) {
114 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
115 return I2C_STATUS_TIMEOUT;
116 }
117 }
118
119 if ((TW_STATUS & 0xF8) != TW_MT_DATA_ACK) {
120 return I2C_STATUS_ERROR;
121 }
122
123 return I2C_STATUS_SUCCESS;
124}
125
126int16_t i2c_read_ack(uint16_t timeout) {
127 // start TWI module and acknowledge data after reception
128 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
129
130 uint16_t timeout_timer = timer_read();
131 while (!(TWCR & (1 << TWINT))) {
132 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
133 return I2C_STATUS_TIMEOUT;
134 }
135 }
136
137 // return received data from TWDR
138 return TWDR;
139}
140
141int16_t i2c_read_nack(uint16_t timeout) {
142 // start receiving without acknowledging reception
143 TWCR = (1 << TWINT) | (1 << TWEN);
144
145 uint16_t timeout_timer = timer_read();
146 while (!(TWCR & (1 << TWINT))) {
147 if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
148 return I2C_STATUS_TIMEOUT;
149 }
150 }
151
152 // return received data from TWDR
153 return TWDR;
154}
155
156i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {
157 i2c_status_t status = i2c_start(address | I2C_WRITE, timeout);
158
159 for (uint16_t i = 0; i < length && status >= 0; i++) {
160 status = i2c_write(data[i], timeout);
161 }
162
163 i2c_stop();
164
165 return status;
166}
167
168i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {
169 i2c_status_t status = i2c_start(address | I2C_READ, timeout);
170
171 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
172 status = i2c_read_ack(timeout);
173 if (status >= 0) {
174 data[i] = status;
175 }
176 }
177
178 if (status >= 0) {
179 status = i2c_read_nack(timeout);
180 if (status >= 0) {
181 data[(length - 1)] = status;
182 }
183 }
184
185 i2c_stop();
186
187 return (status < 0) ? status : I2C_STATUS_SUCCESS;
188}
189
190i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
191 i2c_status_t status = i2c_start(devaddr | 0x00, timeout);
192 if (status >= 0) {
193 status = i2c_write(regaddr, timeout);
194
195 for (uint16_t i = 0; i < length && status >= 0; i++) {
196 status = i2c_write(data[i], timeout);
197 }
198 }
199
200 i2c_stop();
201
202 return status;
203}
204
205i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
206 i2c_status_t status = i2c_start(devaddr, timeout);
207 if (status < 0) {
208 goto error;
209 }
210
211 status = i2c_write(regaddr, timeout);
212 if (status < 0) {
213 goto error;
214 }
215
216 status = i2c_start(devaddr | 0x01, timeout);
217
218 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
219 status = i2c_read_ack(timeout);
220 if (status >= 0) {
221 data[i] = status;
222 }
223 }
224
225 if (status >= 0) {
226 status = i2c_read_nack(timeout);
227 if (status >= 0) {
228 data[(length - 1)] = status;
229 }
230 }
231
232error:
233 i2c_stop();
234
235 return (status < 0) ? status : I2C_STATUS_SUCCESS;
236}
237
238void i2c_stop(void) {
239 // transmit STOP condition
240 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
241}
diff --git a/platforms/avr/drivers/i2c_master.h b/platforms/avr/drivers/i2c_master.h
new file mode 100644
index 000000000..e5af73364
--- /dev/null
+++ b/platforms/avr/drivers/i2c_master.h
@@ -0,0 +1,43 @@
1/* Copyright (C) 2019 Elia Ritterbusch
2 +
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-master-lib
18 */
19
20#pragma once
21
22#define I2C_READ 0x01
23#define I2C_WRITE 0x00
24
25typedef int16_t i2c_status_t;
26
27#define I2C_STATUS_SUCCESS (0)
28#define I2C_STATUS_ERROR (-1)
29#define I2C_STATUS_TIMEOUT (-2)
30
31#define I2C_TIMEOUT_IMMEDIATE (0)
32#define I2C_TIMEOUT_INFINITE (0xFFFF)
33
34void i2c_init(void);
35i2c_status_t i2c_start(uint8_t address, uint16_t timeout);
36i2c_status_t i2c_write(uint8_t data, uint16_t timeout);
37int16_t i2c_read_ack(uint16_t timeout);
38int16_t i2c_read_nack(uint16_t timeout);
39i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
40i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
41i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
42i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
43void i2c_stop(void);
diff --git a/platforms/avr/drivers/i2c_slave.c b/platforms/avr/drivers/i2c_slave.c
new file mode 100644
index 000000000..2907f164c
--- /dev/null
+++ b/platforms/avr/drivers/i2c_slave.c
@@ -0,0 +1,111 @@
1/* Copyright (C) 2019 Elia Ritterbusch
2 +
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18 */
19
20#include <stddef.h>
21#include <avr/io.h>
22#include <util/twi.h>
23#include <avr/interrupt.h>
24#include <stdbool.h>
25
26#include "i2c_slave.h"
27
28#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
29# include "transactions.h"
30
31static volatile bool is_callback_executor = false;
32#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
34volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
35
36static volatile uint8_t buffer_address;
37static volatile bool slave_has_register_set = false;
38
39void i2c_slave_init(uint8_t address) {
40 // load address into TWI address register
41 TWAR = address;
42 // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt
43 TWCR = (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWEN);
44}
45
46void i2c_slave_stop(void) {
47 // clear acknowledge and enable bits
48 TWCR &= ~((1 << TWEA) | (1 << TWEN));
49}
50
51ISR(TWI_vect) {
52 uint8_t ack = 1;
53
54 switch (TW_STATUS) {
55 case TW_SR_SLA_ACK:
56 // The device is now a slave receiver
57 slave_has_register_set = false;
58#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
59 is_callback_executor = false;
60#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
61 break;
62
63 case TW_SR_DATA_ACK:
64 // This device is a slave receiver and has received data
65 // First byte is the location then the bytes will be writen in buffer with auto-increment
66 if (!slave_has_register_set) {
67 buffer_address = TWDR;
68
69 if (buffer_address >= I2C_SLAVE_REG_COUNT) { // address out of bounds dont ack
70 ack = 0;
71 buffer_address = 0;
72 }
73 slave_has_register_set = true; // address has been received now fill in buffer
74
75#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
76 // Work out if we're attempting to execute a callback
77 is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;
78#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
79 } else {
80 i2c_slave_reg[buffer_address] = TWDR;
81 buffer_address++;
82
83#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
84 // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID
85 if (is_callback_executor) {
86 split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];
87 if (trans->slave_callback) {
88 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
89 }
90 }
91#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
92 }
93 break;
94
95 case TW_ST_SLA_ACK:
96 case TW_ST_DATA_ACK:
97 // This device is a slave transmitter and master has requested data
98 TWDR = i2c_slave_reg[buffer_address];
99 buffer_address++;
100 break;
101
102 case TW_BUS_ERROR:
103 // We got an error, reset i2c
104 TWCR = 0;
105 default:
106 break;
107 }
108
109 // Reset i2c state machine to be ready for next interrupt
110 TWCR |= (1 << TWIE) | (1 << TWINT) | (ack << TWEA) | (1 << TWEN);
111}
diff --git a/platforms/avr/drivers/i2c_slave.h b/platforms/avr/drivers/i2c_slave.h
new file mode 100644
index 000000000..a8647c9da
--- /dev/null
+++ b/platforms/avr/drivers/i2c_slave.h
@@ -0,0 +1,41 @@
1/* Copyright (C) 2019 Elia Ritterbusch
2 +
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16/* Library made by: g4lvanix
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18
19 Info: Inititate the library by giving the required address.
20 Read or write to the necessary buffer according to the opperation.
21 */
22
23#pragma once
24
25#ifndef I2C_SLAVE_REG_COUNT
26
27# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
28# include "transport.h"
29# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t)
30# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
31# define I2C_SLAVE_REG_COUNT 30
32# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
34#endif // I2C_SLAVE_REG_COUNT
35
36_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte");
37
38extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
39
40void i2c_slave_init(uint8_t address);
41void i2c_slave_stop(void);
diff --git a/platforms/avr/drivers/serial.c b/platforms/avr/drivers/serial.c
new file mode 100644
index 000000000..9a7345a53
--- /dev/null
+++ b/platforms/avr/drivers/serial.c
@@ -0,0 +1,529 @@
1/*
2 * WARNING: be careful changing this code, it is very timing dependent
3 *
4 * 2018-10-28 checked
5 * avr-gcc 4.9.2
6 * avr-gcc 5.4.0
7 * avr-gcc 7.3.0
8 */
9
10#ifndef F_CPU
11# define F_CPU 16000000
12#endif
13
14#include <avr/io.h>
15#include <avr/interrupt.h>
16#include <util/delay.h>
17#include <stddef.h>
18#include <stdbool.h>
19#include "serial.h"
20
21#ifdef SOFT_SERIAL_PIN
22
23# if !(defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
24# error serial.c is not supported for the currently selected MCU
25# endif
26// if using ATmega32U4/2, AT90USBxxx I2C, can not use PD0 and PD1 in soft serial.
27# if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
28# if defined(USE_AVR_I2C) && (SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1)
29# error Using I2C, so can not use PD0, PD1
30# endif
31# endif
32// PD0..PD3, common config
33# if SOFT_SERIAL_PIN == D0
34# define EIMSK_BIT _BV(INT0)
35# define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01)))
36# define SERIAL_PIN_INTERRUPT INT0_vect
37# define EICRx EICRA
38# elif SOFT_SERIAL_PIN == D1
39# define EIMSK_BIT _BV(INT1)
40# define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11)))
41# define SERIAL_PIN_INTERRUPT INT1_vect
42# define EICRx EICRA
43# elif SOFT_SERIAL_PIN == D2
44# define EIMSK_BIT _BV(INT2)
45# define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21)))
46# define SERIAL_PIN_INTERRUPT INT2_vect
47# define EICRx EICRA
48# elif SOFT_SERIAL_PIN == D3
49# define EIMSK_BIT _BV(INT3)
50# define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))
51# define SERIAL_PIN_INTERRUPT INT3_vect
52# define EICRx EICRA
53# endif
54
55// ATmegaxxU2/AT90USB162 specific config
56# if defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB162__)
57// PD4(INT5), PD6(INT6), PD7(INT7), PC7(INT4)
58# if SOFT_SERIAL_PIN == D4
59# define EIMSK_BIT _BV(INT5)
60# define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51)))
61# define SERIAL_PIN_INTERRUPT INT5_vect
62# define EICRx EICRB
63# elif SOFT_SERIAL_PIN == D6
64# define EIMSK_BIT _BV(INT6)
65# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
66# define SERIAL_PIN_INTERRUPT INT6_vect
67# define EICRx EICRB
68# elif SOFT_SERIAL_PIN == D7
69# define EIMSK_BIT _BV(INT7)
70# define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71)))
71# define SERIAL_PIN_INTERRUPT INT7_vect
72# define EICRx EICRB
73# elif SOFT_SERIAL_PIN == C7
74# define EIMSK_BIT _BV(INT4)
75# define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41)))
76# define SERIAL_PIN_INTERRUPT INT4_vect
77# define EICRx EICRB
78# endif
79# endif
80
81// ATmegaxxU4 specific config
82# if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
83// PE6(INT6)
84# if SOFT_SERIAL_PIN == E6
85# define EIMSK_BIT _BV(INT6)
86# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
87# define SERIAL_PIN_INTERRUPT INT6_vect
88# define EICRx EICRB
89# endif
90# endif
91
92// AT90USBxxx specific config
93# if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
94// PE4..PE7(INT4..INT7)
95# if SOFT_SERIAL_PIN == E4
96# define EIMSK_BIT _BV(INT4)
97# define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41)))
98# define SERIAL_PIN_INTERRUPT INT4_vect
99# define EICRx EICRB
100# elif SOFT_SERIAL_PIN == E5
101# define EIMSK_BIT _BV(INT5)
102# define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51)))
103# define SERIAL_PIN_INTERRUPT INT5_vect
104# define EICRx EICRB
105# elif SOFT_SERIAL_PIN == E6
106# define EIMSK_BIT _BV(INT6)
107# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
108# define SERIAL_PIN_INTERRUPT INT6_vect
109# define EICRx EICRB
110# elif SOFT_SERIAL_PIN == E7
111# define EIMSK_BIT _BV(INT7)
112# define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71)))
113# define SERIAL_PIN_INTERRUPT INT7_vect
114# define EICRx EICRB
115# endif
116# endif
117
118# ifndef SERIAL_PIN_INTERRUPT
119# error invalid SOFT_SERIAL_PIN value
120# endif
121
122# define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
123# define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))
124# define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
125# define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
126# define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))
127
128# define ALWAYS_INLINE __attribute__((always_inline))
129# define NO_INLINE __attribute__((noinline))
130# define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
131
132// parity check
133# define ODD_PARITY 1
134# define EVEN_PARITY 0
135# define PARITY EVEN_PARITY
136
137# ifdef SERIAL_DELAY
138// custom setup in config.h
139// #define TID_SEND_ADJUST 2
140// #define SERIAL_DELAY 6 // micro sec
141// #define READ_WRITE_START_ADJUST 30 // cycles
142// #define READ_WRITE_WIDTH_ADJUST 8 // cycles
143# else
144// ============ Standard setups ============
145
146# ifndef SELECT_SOFT_SERIAL_SPEED
147# define SELECT_SOFT_SERIAL_SPEED 1
148// 0: about 189kbps (Experimental only)
149// 1: about 137kbps (default)
150// 2: about 75kbps
151// 3: about 39kbps
152// 4: about 26kbps
153// 5: about 20kbps
154# endif
155
156# if __GNUC__ < 6
157# define TID_SEND_ADJUST 14
158# else
159# define TID_SEND_ADJUST 2
160# endif
161
162# if SELECT_SOFT_SERIAL_SPEED == 0
163// Very High speed
164# define SERIAL_DELAY 4 // micro sec
165# if __GNUC__ < 6
166# define READ_WRITE_START_ADJUST 33 // cycles
167# define READ_WRITE_WIDTH_ADJUST 3 // cycles
168# else
169# define READ_WRITE_START_ADJUST 34 // cycles
170# define READ_WRITE_WIDTH_ADJUST 7 // cycles
171# endif
172# elif SELECT_SOFT_SERIAL_SPEED == 1
173// High speed
174# define SERIAL_DELAY 6 // micro sec
175# if __GNUC__ < 6
176# define READ_WRITE_START_ADJUST 30 // cycles
177# define READ_WRITE_WIDTH_ADJUST 3 // cycles
178# else
179# define READ_WRITE_START_ADJUST 33 // cycles
180# define READ_WRITE_WIDTH_ADJUST 7 // cycles
181# endif
182# elif SELECT_SOFT_SERIAL_SPEED == 2
183// Middle speed
184# define SERIAL_DELAY 12 // micro sec
185# define READ_WRITE_START_ADJUST 30 // cycles
186# if __GNUC__ < 6
187# define READ_WRITE_WIDTH_ADJUST 3 // cycles
188# else
189# define READ_WRITE_WIDTH_ADJUST 7 // cycles
190# endif
191# elif SELECT_SOFT_SERIAL_SPEED == 3
192// Low speed
193# define SERIAL_DELAY 24 // micro sec
194# define READ_WRITE_START_ADJUST 30 // cycles
195# if __GNUC__ < 6
196# define READ_WRITE_WIDTH_ADJUST 3 // cycles
197# else
198# define READ_WRITE_WIDTH_ADJUST 7 // cycles
199# endif
200# elif SELECT_SOFT_SERIAL_SPEED == 4
201// Very Low speed
202# define SERIAL_DELAY 36 // micro sec
203# define READ_WRITE_START_ADJUST 30 // cycles
204# if __GNUC__ < 6
205# define READ_WRITE_WIDTH_ADJUST 3 // cycles
206# else
207# define READ_WRITE_WIDTH_ADJUST 7 // cycles
208# endif
209# elif SELECT_SOFT_SERIAL_SPEED == 5
210// Ultra Low speed
211# define SERIAL_DELAY 48 // micro sec
212# define READ_WRITE_START_ADJUST 30 // cycles
213# if __GNUC__ < 6
214# define READ_WRITE_WIDTH_ADJUST 3 // cycles
215# else
216# define READ_WRITE_WIDTH_ADJUST 7 // cycles
217# endif
218# else
219# error invalid SELECT_SOFT_SERIAL_SPEED value
220# endif /* SELECT_SOFT_SERIAL_SPEED */
221# endif /* SERIAL_DELAY */
222
223# define SERIAL_DELAY_HALF1 (SERIAL_DELAY / 2)
224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
225
226# define SLAVE_INT_WIDTH_US 1
227# define SLAVE_INT_ACK_WIDTH_UNIT 2
228# define SLAVE_INT_ACK_WIDTH 4
229
230inline static void serial_delay(void) ALWAYS_INLINE;
231inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); }
232
233inline static void serial_delay_half1(void) ALWAYS_INLINE;
234inline static void serial_delay_half1(void) { _delay_us(SERIAL_DELAY_HALF1); }
235
236inline static void serial_delay_half2(void) ALWAYS_INLINE;
237inline static void serial_delay_half2(void) { _delay_us(SERIAL_DELAY_HALF2); }
238
239inline static void serial_output(void) ALWAYS_INLINE;
240inline static void serial_output(void) { setPinOutput(SOFT_SERIAL_PIN); }
241
242// make the serial pin an input with pull-up resistor
243inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
244inline static void serial_input_with_pullup(void) { setPinInputHigh(SOFT_SERIAL_PIN); }
245
246inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
247inline static uint8_t serial_read_pin(void) { return !!readPin(SOFT_SERIAL_PIN); }
248
249inline static void serial_low(void) ALWAYS_INLINE;
250inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
251
252inline static void serial_high(void) ALWAYS_INLINE;
253inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
254
255void soft_serial_initiator_init(void) {
256 serial_output();
257 serial_high();
258}
259
260void soft_serial_target_init(void) {
261 serial_input_with_pullup();
262
263 // Enable INT0-INT7
264 EIMSK |= EIMSK_BIT;
265 EICRx &= EICRx_BIT;
266}
267
268// Used by the sender to synchronize timing with the reciver.
269static void sync_recv(void) NO_INLINE;
270static void sync_recv(void) {
271 for (uint8_t i = 0; i < SERIAL_DELAY * 5 && serial_read_pin(); i++) {
272 }
273 // This shouldn't hang if the target disconnects because the
274 // serial line will float to high if the target does disconnect.
275 while (!serial_read_pin())
276 ;
277}
278
279// Used by the reciver to send a synchronization signal to the sender.
280static void sync_send(void) NO_INLINE;
281static void sync_send(void) {
282 serial_low();
283 serial_delay();
284 serial_high();
285}
286
287// Reads a byte from the serial line
288static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
289static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
290 uint8_t byte, i, p, pb;
291
292 _delay_sub_us(READ_WRITE_START_ADJUST);
293 for (i = 0, byte = 0, p = PARITY; i < bit; i++) {
294 serial_delay_half1(); // read the middle of pulses
295 if (serial_read_pin()) {
296 byte = (byte << 1) | 1;
297 p ^= 1;
298 } else {
299 byte = (byte << 1) | 0;
300 p ^= 0;
301 }
302 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
303 serial_delay_half2();
304 }
305 /* recive parity bit */
306 serial_delay_half1(); // read the middle of pulses
307 pb = serial_read_pin();
308 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
309 serial_delay_half2();
310
311 *pterrcount += (p != pb) ? 1 : 0;
312
313 return byte;
314}
315
316// Sends a byte with MSB ordering
317void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
318void serial_write_chunk(uint8_t data, uint8_t bit) {
319 uint8_t b, p;
320 for (p = PARITY, b = 1 << (bit - 1); b; b >>= 1) {
321 if (data & b) {
322 serial_high();
323 p ^= 1;
324 } else {
325 serial_low();
326 p ^= 0;
327 }
328 serial_delay();
329 }
330 /* send parity bit */
331 if (p & 1) {
332 serial_high();
333 } else {
334 serial_low();
335 }
336 serial_delay();
337
338 serial_low(); // sync_send() / senc_recv() need raise edge
339}
340
341static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
342static void serial_send_packet(uint8_t *buffer, uint8_t size) {
343 for (uint8_t i = 0; i < size; ++i) {
344 uint8_t data;
345 data = buffer[i];
346 sync_send();
347 serial_write_chunk(data, 8);
348 }
349}
350
351static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
352static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
353 uint8_t pecount = 0;
354 for (uint8_t i = 0; i < size; ++i) {
355 uint8_t data;
356 sync_recv();
357 data = serial_read_chunk(&pecount, 8);
358 buffer[i] = data;
359 }
360 return pecount == 0;
361}
362
363inline static void change_sender2reciver(void) {
364 sync_send(); // 0
365 serial_delay_half1(); // 1
366 serial_low(); // 2
367 serial_input_with_pullup(); // 2
368 serial_delay_half1(); // 3
369}
370
371inline static void change_reciver2sender(void) {
372 sync_recv(); // 0
373 serial_delay(); // 1
374 serial_low(); // 3
375 serial_output(); // 3
376 serial_delay_half1(); // 4
377}
378
379static inline uint8_t nibble_bits_count(uint8_t bits) {
380 bits = (bits & 0x5) + (bits >> 1 & 0x5);
381 bits = (bits & 0x3) + (bits >> 2 & 0x3);
382 return bits;
383}
384
385// interrupt handle to be used by the target device
386ISR(SERIAL_PIN_INTERRUPT) {
387 // recive transaction table index
388 uint8_t tid, bits;
389 uint8_t pecount = 0;
390 sync_recv();
391 bits = serial_read_chunk(&pecount, 8);
392 tid = bits >> 3;
393 bits = (bits & 7) != (nibble_bits_count(tid) & 7);
394 if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {
395 return;
396 }
397 serial_delay_half1();
398
399 serial_high(); // response step1 low->high
400 serial_output();
401 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);
402 split_transaction_desc_t *trans = &split_transaction_table[tid];
403 serial_low(); // response step2 ack high->low
404
405 // If the transaction has a callback, we can execute it now
406 if (trans->slave_callback) {
407 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
408 }
409
410 // target send phase
411 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
412 // target switch to input
413 change_sender2reciver();
414
415 // target recive phase
416 if (trans->initiator2target_buffer_size > 0) {
417 if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
418 *trans->status = TRANSACTION_ACCEPTED;
419 } else {
420 *trans->status = TRANSACTION_DATA_ERROR;
421 }
422 } else {
423 *trans->status = TRANSACTION_ACCEPTED;
424 }
425
426 sync_recv(); // weit initiator output to high
427}
428
429/////////
430// start transaction by initiator
431//
432// int soft_serial_transaction(int sstd_index)
433//
434// Returns:
435// TRANSACTION_END
436// TRANSACTION_NO_RESPONSE
437// TRANSACTION_DATA_ERROR
438// this code is very time dependent, so we need to disable interrupts
439int soft_serial_transaction(int sstd_index) {
440 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
441 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
442
443 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
444
445 cli();
446
447 // signal to the target that we want to start a transaction
448 serial_output();
449 serial_low();
450 _delay_us(SLAVE_INT_WIDTH_US);
451
452 // send transaction table index
453 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));
454 sync_send();
455 _delay_sub_us(TID_SEND_ADJUST);
456 serial_write_chunk(tid, 8);
457 serial_delay_half1();
458
459 // wait for the target response (step1 low->high)
460 serial_input_with_pullup();
461 while (!serial_read_pin()) {
462 _delay_sub_us(2);
463 }
464
465 // check if the target is present (step2 high->low)
466 for (int i = 0; serial_read_pin(); i++) {
467 if (i > SLAVE_INT_ACK_WIDTH + 1) {
468 // slave failed to pull the line low, assume not present
469 serial_output();
470 serial_high();
471 *trans->status = TRANSACTION_NO_RESPONSE;
472 sei();
473 return TRANSACTION_NO_RESPONSE;
474 }
475 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
476 }
477
478 // initiator recive phase
479 // if the target is present syncronize with it
480 if (trans->target2initiator_buffer_size > 0) {
481 if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
482 serial_output();
483 serial_high();
484 *trans->status = TRANSACTION_DATA_ERROR;
485 sei();
486 return TRANSACTION_DATA_ERROR;
487 }
488 }
489
490 // initiator switch to output
491 change_reciver2sender();
492
493 // initiator send phase
494 if (trans->initiator2target_buffer_size > 0) {
495 serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
496 }
497
498 // always, release the line when not in use
499 sync_send();
500
501 *trans->status = TRANSACTION_END;
502 sei();
503 return TRANSACTION_END;
504}
505
506int soft_serial_get_and_clean_status(int sstd_index) {
507 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
508 cli();
509 int retval = *trans->status;
510 *trans->status = 0;
511 ;
512 sei();
513 return retval;
514}
515#endif
516
517// Helix serial.c history
518// 2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
519// 2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
520// (adjusted with avr-gcc 4.9.2)
521// 2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
522// (adjusted with avr-gcc 4.9.2)
523// 2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
524// (adjusted with avr-gcc 4.9.2)
525// 2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
526// (adjusted with avr-gcc 7.3.0)
527// 2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
528// (adjusted with avr-gcc 5.4.0, 7.3.0)
529// 2018-12-17 copy to TOP/quantum/split_common/ and remove backward compatibility code (#4669)
diff --git a/platforms/avr/drivers/spi_master.c b/platforms/avr/drivers/spi_master.c
new file mode 100644
index 000000000..4e8fd3bcd
--- /dev/null
+++ b/platforms/avr/drivers/spi_master.c
@@ -0,0 +1,180 @@
1/* Copyright 2020
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17#include "spi_master.h"
18
19#include "timer.h"
20
21#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
22# define SPI_SCK_PIN B1
23# define SPI_MOSI_PIN B2
24# define SPI_MISO_PIN B3
25#elif defined(__AVR_ATmega32A__)
26# define SPI_SCK_PIN B7
27# define SPI_MOSI_PIN B5
28# define SPI_MISO_PIN B6
29#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
30# define SPI_SCK_PIN B5
31# define SPI_MOSI_PIN B3
32# define SPI_MISO_PIN B4
33#endif
34
35#ifndef SPI_TIMEOUT
36# define SPI_TIMEOUT 100
37#endif
38
39static pin_t currentSlavePin = NO_PIN;
40static uint8_t currentSlaveConfig = 0;
41static bool currentSlave2X = false;
42
43void spi_init(void) {
44 writePinHigh(SPI_SS_PIN);
45 setPinOutput(SPI_SCK_PIN);
46 setPinOutput(SPI_MOSI_PIN);
47 setPinInput(SPI_MISO_PIN);
48
49 SPCR = (_BV(SPE) | _BV(MSTR));
50}
51
52bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
53 if (currentSlavePin != NO_PIN || slavePin == NO_PIN) {
54 return false;
55 }
56
57 currentSlaveConfig = 0;
58
59 if (lsbFirst) {
60 currentSlaveConfig |= _BV(DORD);
61 }
62
63 switch (mode) {
64 case 1:
65 currentSlaveConfig |= _BV(CPHA);
66 break;
67 case 2:
68 currentSlaveConfig |= _BV(CPOL);
69 break;
70 case 3:
71 currentSlaveConfig |= (_BV(CPOL) | _BV(CPHA));
72 break;
73 }
74
75 uint16_t roundedDivisor = 1;
76 while (roundedDivisor < divisor) {
77 roundedDivisor <<= 1;
78 }
79
80 switch (roundedDivisor) {
81 case 16:
82 currentSlaveConfig |= _BV(SPR0);
83 break;
84 case 64:
85 currentSlaveConfig |= _BV(SPR1);
86 break;
87 case 128:
88 currentSlaveConfig |= (_BV(SPR1) | _BV(SPR0));
89 break;
90 case 2:
91 currentSlave2X = true;
92 break;
93 case 8:
94 currentSlave2X = true;
95 currentSlaveConfig |= _BV(SPR0);
96 break;
97 case 32:
98 currentSlave2X = true;
99 currentSlaveConfig |= _BV(SPR1);
100 break;
101 }
102
103 SPCR |= currentSlaveConfig;
104 if (currentSlave2X) {
105 SPSR |= _BV(SPI2X);
106 }
107 currentSlavePin = slavePin;
108 setPinOutput(currentSlavePin);
109 writePinLow(currentSlavePin);
110
111 return true;
112}
113
114spi_status_t spi_write(uint8_t data) {
115 SPDR = data;
116
117 uint16_t timeout_timer = timer_read();
118 while (!(SPSR & _BV(SPIF))) {
119 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {
120 return SPI_STATUS_TIMEOUT;
121 }
122 }
123
124 return SPDR;
125}
126
127spi_status_t spi_read() {
128 SPDR = 0x00; // Dummy
129
130 uint16_t timeout_timer = timer_read();
131 while (!(SPSR & _BV(SPIF))) {
132 if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {
133 return SPI_STATUS_TIMEOUT;
134 }
135 }
136
137 return SPDR;
138}
139
140spi_status_t spi_transmit(const uint8_t *data, uint16_t length) {
141 spi_status_t status;
142
143 for (uint16_t i = 0; i < length; i++) {
144 status = spi_write(data[i]);
145
146 if (status < 0) {
147 return status;
148 }
149 }
150
151 return SPI_STATUS_SUCCESS;
152}
153
154spi_status_t spi_receive(uint8_t *data, uint16_t length) {
155 spi_status_t status;
156
157 for (uint16_t i = 0; i < length; i++) {
158 status = spi_read();
159
160 if (status >= 0) {
161 data[i] = status;
162 } else {
163 return status;
164 }
165 }
166
167 return SPI_STATUS_SUCCESS;
168}
169
170void spi_stop(void) {
171 if (currentSlavePin != NO_PIN) {
172 setPinOutput(currentSlavePin);
173 writePinHigh(currentSlavePin);
174 currentSlavePin = NO_PIN;
175 SPSR &= ~(_BV(SPI2X));
176 SPCR &= ~(currentSlaveConfig);
177 currentSlaveConfig = 0;
178 currentSlave2X = false;
179 }
180}
diff --git a/platforms/avr/drivers/spi_master.h b/platforms/avr/drivers/spi_master.h
new file mode 100644
index 000000000..8a30f47ae
--- /dev/null
+++ b/platforms/avr/drivers/spi_master.h
@@ -0,0 +1,59 @@
1/* Copyright 2020
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdbool.h>
20
21#include "gpio.h"
22
23typedef int16_t spi_status_t;
24
25// Hardware SS pin is defined in the header so that user code can refer to it
26#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
27# define SPI_SS_PIN B0
28#elif defined(__AVR_ATmega32A__)
29# define SPI_SS_PIN B4
30#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
31# define SPI_SS_PIN B2
32#endif
33
34#define SPI_STATUS_SUCCESS (0)
35#define SPI_STATUS_ERROR (-1)
36#define SPI_STATUS_TIMEOUT (-2)
37
38#define SPI_TIMEOUT_IMMEDIATE (0)
39#define SPI_TIMEOUT_INFINITE (0xFFFF)
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44void spi_init(void);
45
46bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
47
48spi_status_t spi_write(uint8_t data);
49
50spi_status_t spi_read(void);
51
52spi_status_t spi_transmit(const uint8_t *data, uint16_t length);
53
54spi_status_t spi_receive(uint8_t *data, uint16_t length);
55
56void spi_stop(void);
57#ifdef __cplusplus
58}
59#endif
diff --git a/platforms/avr/drivers/ssd1306.c b/platforms/avr/drivers/ssd1306.c
new file mode 100644
index 000000000..1a09a2bcb
--- /dev/null
+++ b/platforms/avr/drivers/ssd1306.c
@@ -0,0 +1,319 @@
1#ifdef SSD1306OLED
2
3# include "ssd1306.h"
4# include "i2c.h"
5# include <string.h>
6# include "print.h"
7# include "glcdfont.c"
8# ifdef PROTOCOL_LUFA
9# include "lufa.h"
10# endif
11# include "sendchar.h"
12# include "timer.h"
13
14struct CharacterMatrix display;
15
16// Set this to 1 to help diagnose early startup problems
17// when testing power-on with ble. Turn it off otherwise,
18// as the latency of printing most of the debug info messes
19// with the matrix scan, causing keys to drop.
20# define DEBUG_TO_SCREEN 0
21
22// static uint16_t last_battery_update;
23// static uint32_t vbat;
24//#define BatteryUpdateInterval 10000 /* milliseconds */
25# define ScreenOffInterval 300000 /* milliseconds */
26# if DEBUG_TO_SCREEN
27static uint8_t displaying;
28# endif
29static uint16_t last_flush;
30
31// Write command sequence.
32// Returns true on success.
33static inline bool _send_cmd1(uint8_t cmd) {
34 bool res = false;
35
36 if (i2c_start_write(SSD1306_ADDRESS)) {
37 xprintf("failed to start write to %d\n", SSD1306_ADDRESS);
38 goto done;
39 }
40
41 if (i2c_master_write(0x0 /* command byte follows */)) {
42 print("failed to write control byte\n");
43
44 goto done;
45 }
46
47 if (i2c_master_write(cmd)) {
48 xprintf("failed to write command %d\n", cmd);
49 goto done;
50 }
51 res = true;
52done:
53 i2c_master_stop();
54 return res;
55}
56
57// Write 2-byte command sequence.
58// Returns true on success
59static inline bool _send_cmd2(uint8_t cmd, uint8_t opr) {
60 if (!_send_cmd1(cmd)) {
61 return false;
62 }
63 return _send_cmd1(opr);
64}
65
66// Write 3-byte command sequence.
67// Returns true on success
68static inline bool _send_cmd3(uint8_t cmd, uint8_t opr1, uint8_t opr2) {
69 if (!_send_cmd1(cmd)) {
70 return false;
71 }
72 if (!_send_cmd1(opr1)) {
73 return false;
74 }
75 return _send_cmd1(opr2);
76}
77
78# define send_cmd1(c) \
79 if (!_send_cmd1(c)) { \
80 goto done; \
81 }
82# define send_cmd2(c, o) \
83 if (!_send_cmd2(c, o)) { \
84 goto done; \
85 }
86# define send_cmd3(c, o1, o2) \
87 if (!_send_cmd3(c, o1, o2)) { \
88 goto done; \
89 }
90
91static void clear_display(void) {
92 matrix_clear(&display);
93
94 // Clear all of the display bits (there can be random noise
95 // in the RAM on startup)
96 send_cmd3(PageAddr, 0, (DisplayHeight / 8) - 1);
97 send_cmd3(ColumnAddr, 0, DisplayWidth - 1);
98
99 if (i2c_start_write(SSD1306_ADDRESS)) {
100 goto done;
101 }
102 if (i2c_master_write(0x40)) {
103 // Data mode
104 goto done;
105 }
106 for (uint8_t row = 0; row < MatrixRows; ++row) {
107 for (uint8_t col = 0; col < DisplayWidth; ++col) {
108 i2c_master_write(0);
109 }
110 }
111
112 display.dirty = false;
113
114done:
115 i2c_master_stop();
116}
117
118# if DEBUG_TO_SCREEN
119# undef sendchar
120static int8_t capture_sendchar(uint8_t c) {
121 sendchar(c);
122 iota_gfx_write_char(c);
123
124 if (!displaying) {
125 iota_gfx_flush();
126 }
127 return 0;
128}
129# endif
130
131bool iota_gfx_init(void) {
132 bool success = false;
133
134 send_cmd1(DisplayOff);
135 send_cmd2(SetDisplayClockDiv, 0x80);
136 send_cmd2(SetMultiPlex, DisplayHeight - 1);
137
138 send_cmd2(SetDisplayOffset, 0);
139
140 send_cmd1(SetStartLine | 0x0);
141 send_cmd2(SetChargePump, 0x14 /* Enable */);
142 send_cmd2(SetMemoryMode, 0 /* horizontal addressing */);
143
144# ifdef OLED_ROTATE180
145 // the following Flip the display orientation 180 degrees
146 send_cmd1(SegRemap);
147 send_cmd1(ComScanInc);
148# endif
149# ifndef OLED_ROTATE180
150 // Flips the display orientation 0 degrees
151 send_cmd1(SegRemap | 0x1);
152 send_cmd1(ComScanDec);
153# endif
154
155 send_cmd2(SetComPins, 0x2);
156 send_cmd2(SetContrast, 0x8f);
157 send_cmd2(SetPreCharge, 0xf1);
158 send_cmd2(SetVComDetect, 0x40);
159 send_cmd1(DisplayAllOnResume);
160 send_cmd1(NormalDisplay);
161 send_cmd1(DeActivateScroll);
162 send_cmd1(DisplayOn);
163
164 send_cmd2(SetContrast, 0); // Dim
165
166 clear_display();
167
168 success = true;
169
170 iota_gfx_flush();
171
172# if DEBUG_TO_SCREEN
173 print_set_sendchar(capture_sendchar);
174# endif
175
176done:
177 return success;
178}
179
180bool iota_gfx_off(void) {
181 bool success = false;
182
183 send_cmd1(DisplayOff);
184 success = true;
185
186done:
187 return success;
188}
189
190bool iota_gfx_on(void) {
191 bool success = false;
192
193 send_cmd1(DisplayOn);
194 success = true;
195
196done:
197 return success;
198}
199
200void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c) {
201 *matrix->cursor = c;
202 ++matrix->cursor;
203
204 if (matrix->cursor - &matrix->display[0][0] == sizeof(matrix->display)) {
205 // We went off the end; scroll the display upwards by one line
206 memmove(&matrix->display[0], &matrix->display[1], MatrixCols * (MatrixRows - 1));
207 matrix->cursor = &matrix->display[MatrixRows - 1][0];
208 memset(matrix->cursor, ' ', MatrixCols);
209 }
210}
211
212void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) {
213 matrix->dirty = true;
214
215 if (c == '\n') {
216 // Clear to end of line from the cursor and then move to the
217 // start of the next line
218 uint8_t cursor_col = (matrix->cursor - &matrix->display[0][0]) % MatrixCols;
219
220 while (cursor_col++ < MatrixCols) {
221 matrix_write_char_inner(matrix, ' ');
222 }
223 return;
224 }
225
226 matrix_write_char_inner(matrix, c);
227}
228
229void iota_gfx_write_char(uint8_t c) { matrix_write_char(&display, c); }
230
231void matrix_write(struct CharacterMatrix *matrix, const char *data) {
232 const char *end = data + strlen(data);
233 while (data < end) {
234 matrix_write_char(matrix, *data);
235 ++data;
236 }
237}
238
239void iota_gfx_write(const char *data) { matrix_write(&display, data); }
240
241void matrix_write_P(struct CharacterMatrix *matrix, const char *data) {
242 while (true) {
243 uint8_t c = pgm_read_byte(data);
244 if (c == 0) {
245 return;
246 }
247 matrix_write_char(matrix, c);
248 ++data;
249 }
250}
251
252void iota_gfx_write_P(const char *data) { matrix_write_P(&display, data); }
253
254void matrix_clear(struct CharacterMatrix *matrix) {
255 memset(matrix->display, ' ', sizeof(matrix->display));
256 matrix->cursor = &matrix->display[0][0];
257 matrix->dirty = true;
258}
259
260void iota_gfx_clear_screen(void) { matrix_clear(&display); }
261
262void matrix_render(struct CharacterMatrix *matrix) {
263 last_flush = timer_read();
264 iota_gfx_on();
265# if DEBUG_TO_SCREEN
266 ++displaying;
267# endif
268
269 // Move to the home position
270 send_cmd3(PageAddr, 0, MatrixRows - 1);
271 send_cmd3(ColumnAddr, 0, (MatrixCols * FontWidth) - 1);
272
273 if (i2c_start_write(SSD1306_ADDRESS)) {
274 goto done;
275 }
276 if (i2c_master_write(0x40)) {
277 // Data mode
278 goto done;
279 }
280
281 for (uint8_t row = 0; row < MatrixRows; ++row) {
282 for (uint8_t col = 0; col < MatrixCols; ++col) {
283 const uint8_t *glyph = font + (matrix->display[row][col] * (FontWidth - 1));
284
285 for (uint8_t glyphCol = 0; glyphCol < FontWidth - 1; ++glyphCol) {
286 uint8_t colBits = pgm_read_byte(glyph + glyphCol);
287 i2c_master_write(colBits);
288 }
289
290 // 1 column of space between chars (it's not included in the glyph)
291 i2c_master_write(0);
292 }
293 }
294
295 matrix->dirty = false;
296
297done:
298 i2c_master_stop();
299# if DEBUG_TO_SCREEN
300 --displaying;
301# endif
302}
303
304void iota_gfx_flush(void) { matrix_render(&display); }
305
306__attribute__((weak)) void iota_gfx_task_user(void) {}
307
308void iota_gfx_task(void) {
309 iota_gfx_task_user();
310
311 if (display.dirty) {
312 iota_gfx_flush();
313 }
314
315 if (timer_elapsed(last_flush) > ScreenOffInterval) {
316 iota_gfx_off();
317 }
318}
319#endif
diff --git a/platforms/avr/drivers/ssd1306.h b/platforms/avr/drivers/ssd1306.h
new file mode 100644
index 000000000..6eecdcfaa
--- /dev/null
+++ b/platforms/avr/drivers/ssd1306.h
@@ -0,0 +1,87 @@
1#pragma once
2
3#include <stdbool.h>
4#include <stdio.h>
5#include "config.h"
6
7enum ssd1306_cmds {
8 DisplayOff = 0xAE,
9 DisplayOn = 0xAF,
10
11 SetContrast = 0x81,
12 DisplayAllOnResume = 0xA4,
13
14 DisplayAllOn = 0xA5,
15 NormalDisplay = 0xA6,
16 InvertDisplay = 0xA7,
17 SetDisplayOffset = 0xD3,
18 SetComPins = 0xda,
19 SetVComDetect = 0xdb,
20 SetDisplayClockDiv = 0xD5,
21 SetPreCharge = 0xd9,
22 SetMultiPlex = 0xa8,
23 SetLowColumn = 0x00,
24 SetHighColumn = 0x10,
25 SetStartLine = 0x40,
26
27 SetMemoryMode = 0x20,
28 ColumnAddr = 0x21,
29 PageAddr = 0x22,
30
31 ComScanInc = 0xc0,
32 ComScanDec = 0xc8,
33 SegRemap = 0xa0,
34 SetChargePump = 0x8d,
35 ExternalVcc = 0x01,
36 SwitchCapVcc = 0x02,
37
38 ActivateScroll = 0x2f,
39 DeActivateScroll = 0x2e,
40 SetVerticalScrollArea = 0xa3,
41 RightHorizontalScroll = 0x26,
42 LeftHorizontalScroll = 0x27,
43 VerticalAndRightHorizontalScroll = 0x29,
44 VerticalAndLeftHorizontalScroll = 0x2a,
45};
46
47// Controls the SSD1306 128x32 OLED display via i2c
48
49#ifndef SSD1306_ADDRESS
50# define SSD1306_ADDRESS 0x3C
51#endif
52
53#define DisplayHeight 32
54#define DisplayWidth 128
55
56#define FontHeight 8
57#define FontWidth 6
58
59#define MatrixRows (DisplayHeight / FontHeight)
60#define MatrixCols (DisplayWidth / FontWidth)
61
62struct CharacterMatrix {
63 uint8_t display[MatrixRows][MatrixCols];
64 uint8_t *cursor;
65 bool dirty;
66};
67
68extern struct CharacterMatrix display;
69
70bool iota_gfx_init(void);
71void iota_gfx_task(void);
72bool iota_gfx_off(void);
73bool iota_gfx_on(void);
74void iota_gfx_flush(void);
75void iota_gfx_write_char(uint8_t c);
76void iota_gfx_write(const char *data);
77void iota_gfx_write_P(const char *data);
78void iota_gfx_clear_screen(void);
79
80void iota_gfx_task_user(void);
81
82void matrix_clear(struct CharacterMatrix *matrix);
83void matrix_write_char_inner(struct CharacterMatrix *matrix, uint8_t c);
84void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c);
85void matrix_write(struct CharacterMatrix *matrix, const char *data);
86void matrix_write_P(struct CharacterMatrix *matrix, const char *data);
87void matrix_render(struct CharacterMatrix *matrix);
diff --git a/platforms/avr/drivers/uart.c b/platforms/avr/drivers/uart.c
new file mode 100644
index 000000000..c6abcb6fe
--- /dev/null
+++ b/platforms/avr/drivers/uart.c
@@ -0,0 +1,170 @@
1/* UART Example for Teensy USB Development Board
2 * http://www.pjrc.com/teensy/
3 * Copyright (c) 2009 PJRC.COM, LLC
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
13 * all 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
21 * THE SOFTWARE.
22 */
23
24// Version 1.0: Initial Release
25// Version 1.1: Add support for Teensy 2.0, minor optimizations
26
27#include <avr/io.h>
28#include <avr/interrupt.h>
29
30#include "uart.h"
31
32#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
33# define UDRn UDR1
34# define UBRRnL UBRR1L
35# define UCSRnA UCSR1A
36# define UCSRnB UCSR1B
37# define UCSRnC UCSR1C
38# define U2Xn U2X1
39# define RXENn RXEN1
40# define TXENn TXEN1
41# define RXCIEn RXCIE1
42# define UCSZn1 UCSZ11
43# define UCSZn0 UCSZ10
44# define UDRIEn UDRIE1
45# define USARTn_UDRE_vect USART1_UDRE_vect
46# define USARTn_RX_vect USART1_RX_vect
47#elif defined(__AVR_ATmega32A__)
48# define UDRn UDR
49# define UBRRnL UBRRL
50# define UCSRnA UCSRA
51# define UCSRnB UCSRB
52# define UCSRnC UCSRC
53# define U2Xn U2X
54# define RXENn RXEN
55# define TXENn TXEN
56# define RXCIEn RXCIE
57# define UCSZn1 UCSZ1
58# define UCSZn0 UCSZ0
59# define UDRIEn UDRIE
60# define USARTn_UDRE_vect USART_UDRE_vect
61# define USARTn_RX_vect USART_RX_vect
62#elif defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
63# define UDRn UDR0
64# define UBRRnL UBRR0L
65# define UCSRnA UCSR0A
66# define UCSRnB UCSR0B
67# define UCSRnC UCSR0C
68# define U2Xn U2X0
69# define RXENn RXEN0
70# define TXENn TXEN0
71# define RXCIEn RXCIE0
72# define UCSZn1 UCSZ01
73# define UCSZn0 UCSZ00
74# define UDRIEn UDRIE0
75# define USARTn_UDRE_vect USART_UDRE_vect
76# define USARTn_RX_vect USART_RX_vect
77#endif
78
79// These buffers may be any size from 2 to 256 bytes.
80#define RX_BUFFER_SIZE 64
81#define TX_BUFFER_SIZE 256
82
83static volatile uint8_t tx_buffer[TX_BUFFER_SIZE];
84static volatile uint8_t tx_buffer_head;
85static volatile uint8_t tx_buffer_tail;
86static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
87static volatile uint8_t rx_buffer_head;
88static volatile uint8_t rx_buffer_tail;
89
90// Initialize the UART
91void uart_init(uint32_t baud) {
92 cli();
93 UBRRnL = (F_CPU / 4 / baud - 1) / 2;
94 UCSRnA = (1 << U2Xn);
95 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn);
96 UCSRnC = (1 << UCSZn1) | (1 << UCSZn0);
97 tx_buffer_head = tx_buffer_tail = 0;
98 rx_buffer_head = rx_buffer_tail = 0;
99 sei();
100}
101
102// Transmit a byte
103void uart_putchar(uint8_t c) {
104 uint8_t i;
105
106 i = tx_buffer_head + 1;
107 if (i >= TX_BUFFER_SIZE) i = 0;
108 // return immediately to avoid deadlock when interrupt is disabled(called from ISR)
109 if (tx_buffer_tail == i && (SREG & (1 << SREG_I)) == 0) return;
110 while (tx_buffer_tail == i)
111 ; // wait until space in buffer
112 // cli();
113 tx_buffer[i] = c;
114 tx_buffer_head = i;
115 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn) | (1 << UDRIEn);
116 // sei();
117}
118
119// Receive a byte
120uint8_t uart_getchar(void) {
121 uint8_t c, i;
122
123 while (rx_buffer_head == rx_buffer_tail)
124 ; // wait for character
125 i = rx_buffer_tail + 1;
126 if (i >= RX_BUFFER_SIZE) i = 0;
127 c = rx_buffer[i];
128 rx_buffer_tail = i;
129 return c;
130}
131
132// Return whether the number of bytes waiting in the receive buffer is nonzero.
133// Call this before uart_getchar() to check if it will need
134// to wait for a byte to arrive.
135bool uart_available(void) {
136 uint8_t head, tail;
137
138 head = rx_buffer_head;
139 tail = rx_buffer_tail;
140 if (head >= tail) return (head - tail) > 0;
141 return (RX_BUFFER_SIZE + head - tail) > 0;
142}
143
144// Transmit Interrupt
145ISR(USARTn_UDRE_vect) {
146 uint8_t i;
147
148 if (tx_buffer_head == tx_buffer_tail) {
149 // buffer is empty, disable transmit interrupt
150 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn);
151 } else {
152 i = tx_buffer_tail + 1;
153 if (i >= TX_BUFFER_SIZE) i = 0;
154 UDRn = tx_buffer[i];
155 tx_buffer_tail = i;
156 }
157}
158
159// Receive Interrupt
160ISR(USARTn_RX_vect) {
161 uint8_t c, i;
162
163 c = UDRn;
164 i = rx_buffer_head + 1;
165 if (i >= RX_BUFFER_SIZE) i = 0;
166 if (i != rx_buffer_tail) {
167 rx_buffer[i] = c;
168 rx_buffer_head = i;
169 }
170}
diff --git a/platforms/avr/drivers/uart.h b/platforms/avr/drivers/uart.h
new file mode 100644
index 000000000..602eb3d8b
--- /dev/null
+++ b/platforms/avr/drivers/uart.h
@@ -0,0 +1,35 @@
1/* UART Example for Teensy USB Development Board
2 * http://www.pjrc.com/teensy/
3 * Copyright (c) 2009 PJRC.COM, LLC
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
13 * all 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
21 * THE SOFTWARE.
22 */
23
24#pragma once
25
26#include <stdint.h>
27#include <stdbool.h>
28
29void uart_init(uint32_t baud);
30
31void uart_putchar(uint8_t c);
32
33uint8_t uart_getchar(void);
34
35bool uart_available(void);
diff --git a/platforms/avr/drivers/ws2812.c b/platforms/avr/drivers/ws2812.c
new file mode 100644
index 000000000..77c492cd4
--- /dev/null
+++ b/platforms/avr/drivers/ws2812.c
@@ -0,0 +1,176 @@
1/*
2 * light weight WS2812 lib V2.0b
3 *
4 * Controls WS2811/WS2812/WS2812B RGB-LEDs
5 * Author: Tim (cpldcpu@gmail.com)
6 *
7 * Jan 18th, 2014 v2.0b Initial Version
8 * Nov 29th, 2015 v2.3 Added SK6812RGBW support
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23#include "ws2812.h"
24#include <avr/interrupt.h>
25#include <avr/io.h>
26#include <util/delay.h>
27
28#define pinmask(pin) (_BV((pin)&0xF))
29
30/*
31 * Forward declare internal functions
32 *
33 * The functions take a byte-array and send to the data output as WS2812 bitstream.
34 * The length is the number of bytes to send - three per LED.
35 */
36
37static inline void ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t masklo, uint8_t maskhi);
38
39void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds) {
40 DDRx_ADDRESS(RGB_DI_PIN) |= pinmask(RGB_DI_PIN);
41
42 uint8_t masklo = ~(pinmask(RGB_DI_PIN)) & PORTx_ADDRESS(RGB_DI_PIN);
43 uint8_t maskhi = pinmask(RGB_DI_PIN) | PORTx_ADDRESS(RGB_DI_PIN);
44
45 ws2812_sendarray_mask((uint8_t *)ledarray, number_of_leds * sizeof(LED_TYPE), masklo, maskhi);
46
47 _delay_us(WS2812_TRST_US);
48}
49
50/*
51 This routine writes an array of bytes with RGB values to the Dataout pin
52 using the fast 800kHz clockless WS2811/2812 protocol.
53*/
54
55// Timing in ns
56#define w_zeropulse 350
57#define w_onepulse 900
58#define w_totalperiod 1250
59
60// Fixed cycles used by the inner loop
61#define w_fixedlow 2
62#define w_fixedhigh 4
63#define w_fixedtotal 8
64
65// Insert NOPs to match the timing, if possible
66#define w_zerocycles (((F_CPU / 1000) * w_zeropulse) / 1000000)
67#define w_onecycles (((F_CPU / 1000) * w_onepulse + 500000) / 1000000)
68#define w_totalcycles (((F_CPU / 1000) * w_totalperiod + 500000) / 1000000)
69
70// w1_nops - nops between rising edge and falling edge - low
71#if w_zerocycles >= w_fixedlow
72# define w1_nops (w_zerocycles - w_fixedlow)
73#else
74# define w1_nops 0
75#endif
76
77// w2_nops - nops between fe low and fe high
78#if w_onecycles >= (w_fixedhigh + w1_nops)
79# define w2_nops (w_onecycles - w_fixedhigh - w1_nops)
80#else
81# define w2_nops 0
82#endif
83
84// w3_nops - nops to complete loop
85#if w_totalcycles >= (w_fixedtotal + w1_nops + w2_nops)
86# define w3_nops (w_totalcycles - w_fixedtotal - w1_nops - w2_nops)
87#else
88# define w3_nops 0
89#endif
90
91// The only critical timing parameter is the minimum pulse length of the "0"
92// Warn or throw error if this timing can not be met with current F_CPU settings.
93#define w_lowtime ((w1_nops + w_fixedlow) * 1000000) / (F_CPU / 1000)
94#if w_lowtime > 550
95# error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
96#elif w_lowtime > 450
97# warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
98# warning "Please consider a higher clockspeed, if possible"
99#endif
100
101#define w_nop1 "nop \n\t"
102#define w_nop2 "rjmp .+0 \n\t"
103#define w_nop4 w_nop2 w_nop2
104#define w_nop8 w_nop4 w_nop4
105#define w_nop16 w_nop8 w_nop8
106
107static inline void ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t masklo, uint8_t maskhi) {
108 uint8_t curbyte, ctr, sreg_prev;
109
110 sreg_prev = SREG;
111 cli();
112
113 while (datlen--) {
114 curbyte = (*data++);
115
116 asm volatile(" ldi %0,8 \n\t"
117 "loop%=: \n\t"
118 " out %2,%3 \n\t" // '1' [01] '0' [01] - re
119#if (w1_nops & 1)
120 w_nop1
121#endif
122#if (w1_nops & 2)
123 w_nop2
124#endif
125#if (w1_nops & 4)
126 w_nop4
127#endif
128#if (w1_nops & 8)
129 w_nop8
130#endif
131#if (w1_nops & 16)
132 w_nop16
133#endif
134 " sbrs %1,7 \n\t" // '1' [03] '0' [02]
135 " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
136 " lsl %1 \n\t" // '1' [04] '0' [04]
137#if (w2_nops & 1)
138 w_nop1
139#endif
140#if (w2_nops & 2)
141 w_nop2
142#endif
143#if (w2_nops & 4)
144 w_nop4
145#endif
146#if (w2_nops & 8)
147 w_nop8
148#endif
149#if (w2_nops & 16)
150 w_nop16
151#endif
152 " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
153#if (w3_nops & 1)
154 w_nop1
155#endif
156#if (w3_nops & 2)
157 w_nop2
158#endif
159#if (w3_nops & 4)
160 w_nop4
161#endif
162#if (w3_nops & 8)
163 w_nop8
164#endif
165#if (w3_nops & 16)
166 w_nop16
167#endif
168
169 " dec %0 \n\t" // '1' [+2] '0' [+2]
170 " brne loop%=\n\t" // '1' [+3] '0' [+4]
171 : "=&d"(ctr)
172 : "r"(curbyte), "I"(_SFR_IO_ADDR(PORTx_ADDRESS(RGB_DI_PIN))), "r"(maskhi), "r"(masklo));
173 }
174
175 SREG = sreg_prev;
176}
diff --git a/platforms/avr/drivers/ws2812_i2c.c b/platforms/avr/drivers/ws2812_i2c.c
new file mode 100644
index 000000000..1c332e24b
--- /dev/null
+++ b/platforms/avr/drivers/ws2812_i2c.c
@@ -0,0 +1,27 @@
1#include "ws2812.h"
2#include "i2c_master.h"
3
4#ifdef RGBW
5# error "RGBW not supported"
6#endif
7
8#ifndef WS2812_ADDRESS
9# define WS2812_ADDRESS 0xb0
10#endif
11
12#ifndef WS2812_TIMEOUT
13# define WS2812_TIMEOUT 100
14#endif
15
16void ws2812_init(void) { i2c_init(); }
17
18// Setleds for standard RGB
19void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
20 static bool s_init = false;
21 if (!s_init) {
22 ws2812_init();
23 s_init = true;
24 }
25
26 i2c_transmit(WS2812_ADDRESS, (uint8_t *)ledarray, sizeof(LED_TYPE) * leds, WS2812_TIMEOUT);
27}