aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr')
-rw-r--r--platforms/avr/_print.h33
-rw-r--r--platforms/avr/_timer.h19
-rw-r--r--platforms/avr/_wait.h49
-rw-r--r--platforms/avr/atomic_util.h22
-rw-r--r--platforms/avr/bootloader.c293
-rw-r--r--platforms/avr/bootloader_size.c21
-rw-r--r--platforms/avr/drivers/analog.c23
-rw-r--r--platforms/avr/drivers/analog.h3
-rw-r--r--platforms/avr/drivers/audio_pwm.h17
-rw-r--r--platforms/avr/drivers/audio_pwm_hardware.c332
-rw-r--r--platforms/avr/drivers/i2c_master.c56
-rw-r--r--platforms/avr/drivers/i2c_master.h2
-rw-r--r--platforms/avr/drivers/ps2/ps2_io.c51
-rw-r--r--platforms/avr/drivers/ps2/ps2_usart.c227
-rw-r--r--platforms/avr/drivers/uart.c26
-rw-r--r--platforms/avr/drivers/uart.h8
-rw-r--r--platforms/avr/flash.mk9
-rw-r--r--platforms/avr/gpio.h49
-rw-r--r--platforms/avr/pin_defs.h128
-rw-r--r--platforms/avr/platform.c21
-rw-r--r--platforms/avr/platform.mk179
-rw-r--r--platforms/avr/platform_deps.h20
-rw-r--r--platforms/avr/printf.c20
-rw-r--r--platforms/avr/printf.mk2
-rw-r--r--platforms/avr/sleep_led.c124
-rw-r--r--platforms/avr/suspend.c152
-rw-r--r--platforms/avr/timer.c133
-rw-r--r--platforms/avr/timer_avr.h39
-rw-r--r--platforms/avr/xprintf.S498
-rw-r--r--platforms/avr/xprintf.h103
30 files changed, 2625 insertions, 34 deletions
diff --git a/platforms/avr/_print.h b/platforms/avr/_print.h
new file mode 100644
index 000000000..5c1fdd26d
--- /dev/null
+++ b/platforms/avr/_print.h
@@ -0,0 +1,33 @@
1/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
2/* Very basic print functions, intended to be used with usb_debug_only.c
3 * http://www.pjrc.com/teensy/
4 * Copyright (c) 2008 PJRC.COM, LLC
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#pragma once
25
26#include "avr/xprintf.h"
27
28// Create user & normal print defines
29#define print(s) xputs(PSTR(s))
30#define println(s) xputs(PSTR(s "\r\n"))
31#define uprint(s) xputs(PSTR(s))
32#define uprintln(s) xputs(PSTR(s "\r\n"))
33#define uprintf(fmt, ...) __xprintf(PSTR(fmt), ##__VA_ARGS__) \ No newline at end of file
diff --git a/platforms/avr/_timer.h b/platforms/avr/_timer.h
new file mode 100644
index 000000000..b81e0f68b
--- /dev/null
+++ b/platforms/avr/_timer.h
@@ -0,0 +1,19 @@
1/* Copyright 2021 Simon Arlott
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#pragma once
17
18// The platform is 8-bit, so prefer 16-bit timers to reduce code size
19#define FAST_TIMER_T_SIZE 16
diff --git a/platforms/avr/_wait.h b/platforms/avr/_wait.h
new file mode 100644
index 000000000..683db6ae5
--- /dev/null
+++ b/platforms/avr/_wait.h
@@ -0,0 +1,49 @@
1/* Copyright 2021 QMK
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 <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <util/delay.h>
19
20#define wait_ms(ms) \
21 do { \
22 if (__builtin_constant_p(ms)) { \
23 _delay_ms(ms); \
24 } else { \
25 for (uint16_t i = ms; i > 0; i--) { \
26 _delay_ms(1); \
27 } \
28 } \
29 } while (0)
30#define wait_us(us) \
31 do { \
32 if (__builtin_constant_p(us)) { \
33 _delay_us(us); \
34 } else { \
35 for (uint16_t i = us; i > 0; i--) { \
36 _delay_us(1); \
37 } \
38 } \
39 } while (0)
40#define wait_cpuclock(n) __builtin_avr_delay_cycles(n)
41#define CPU_CLOCK F_CPU
42
43/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.
44 * But here's more margin to make it two clocks. */
45#ifndef GPIO_INPUT_PIN_DELAY
46# define GPIO_INPUT_PIN_DELAY 2
47#endif
48
49#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
diff --git a/platforms/avr/atomic_util.h b/platforms/avr/atomic_util.h
new file mode 100644
index 000000000..7c5d2e7dc
--- /dev/null
+++ b/platforms/avr/atomic_util.h
@@ -0,0 +1,22 @@
1/* Copyright 2021 QMK
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 <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18/* atomic macro for AVR */
19#include <util/atomic.h>
20
21#define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
22#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)
diff --git a/platforms/avr/bootloader.c b/platforms/avr/bootloader.c
new file mode 100644
index 000000000..c0272903b
--- /dev/null
+++ b/platforms/avr/bootloader.c
@@ -0,0 +1,293 @@
1#include <stdint.h>
2#include <stdbool.h>
3#include <avr/io.h>
4#include <avr/eeprom.h>
5#include <avr/interrupt.h>
6#include <avr/wdt.h>
7#include <util/delay.h>
8#include "bootloader.h"
9#include <avr/boot.h>
10
11#ifdef PROTOCOL_LUFA
12# include <LUFA/Drivers/USB/USB.h>
13#endif
14
15/** \brief Bootloader Size in *bytes*
16 *
17 * AVR Boot section size are defined by setting BOOTSZ fuse in fact. Consult with your MCU datasheet.
18 * Note that 'Word'(2 bytes) size and address are used in datasheet while TMK uses 'Byte'.
19 *
20 * Size of Bootloaders in bytes:
21 * Atmel DFU loader(ATmega32U4) 4096
22 * Atmel DFU loader(AT90USB128) 8192
23 * LUFA bootloader(ATmega32U4) 4096
24 * Arduino Caterina(ATmega32U4) 4096
25 * USBaspLoader(ATmega***) 2048
26 * Teensy halfKay(ATmega32U4) 512
27 * Teensy++ halfKay(AT90USB128) 1024
28 *
29 * AVR Boot section is located at the end of Flash memory like the followings.
30 *
31 * byte Atmel/LUFA(ATMega32u4) byte Atmel(AT90SUB128)
32 * 0x0000 +---------------+ 0x00000 +---------------+
33 * | | | |
34 * | | | |
35 * | Application | | Application |
36 * | | | |
37 * = = = =
38 * | | 32KB-4KB | | 128KB-8KB
39 * 0x7000 +---------------+ 0x1E000 +---------------+
40 * | Bootloader | 4KB | Bootloader | 8KB
41 * 0x7FFF +---------------+ 0x1FFFF +---------------+
42 *
43 *
44 * byte Teensy(ATMega32u4) byte Teensy++(AT90SUB128)
45 * 0x0000 +---------------+ 0x00000 +---------------+
46 * | | | |
47 * | | | |
48 * | Application | | Application |
49 * | | | |
50 * = = = =
51 * | | 32KB-512B | | 128KB-1KB
52 * 0x7E00 +---------------+ 0x1FC00 +---------------+
53 * | Bootloader | 512B | Bootloader | 1KB
54 * 0x7FFF +---------------+ 0x1FFFF +---------------+
55 */
56#define FLASH_SIZE (FLASHEND + 1L)
57
58#if !defined(BOOTLOADER_SIZE)
59uint16_t bootloader_start;
60#endif
61
62// compatibility between ATMega8 and ATMega88
63#if !defined(MCUCSR)
64# if defined(MCUSR)
65# define MCUCSR MCUSR
66# endif
67#endif
68
69/** \brief Entering the Bootloader via Software
70 *
71 * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html
72 */
73#define BOOTLOADER_RESET_KEY 0xB007B007
74uint32_t reset_key __attribute__((section(".noinit,\"aw\",@nobits;")));
75
76/** \brief initialize MCU status by watchdog reset
77 *
78 * FIXME: needs doc
79 */
80__attribute__((weak)) void bootloader_jump(void) {
81#if !defined(BOOTLOADER_SIZE)
82 uint8_t high_fuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
83
84 if (high_fuse & ~(FUSE_BOOTSZ0 & FUSE_BOOTSZ1)) {
85 bootloader_start = (FLASH_SIZE - 512) >> 1;
86 } else if (high_fuse & ~(FUSE_BOOTSZ1)) {
87 bootloader_start = (FLASH_SIZE - 1024) >> 1;
88 } else if (high_fuse & ~(FUSE_BOOTSZ0)) {
89 bootloader_start = (FLASH_SIZE - 2048) >> 1;
90 } else {
91 bootloader_start = (FLASH_SIZE - 4096) >> 1;
92 }
93#endif
94
95 // Something like this might work, but it compiled larger than the block above
96 // bootloader_start = FLASH_SIZE - (256 << (~high_fuse & 0b110 >> 1));
97
98#if defined(BOOTLOADER_HALFKAY)
99 // http://www.pjrc.com/teensy/jump_to_bootloader.html
100 cli();
101 // disable watchdog, if enabled (it's not)
102 // disable all peripherals
103 // a shutdown call might make sense here
104 UDCON = 1;
105 USBCON = (1 << FRZCLK); // disable USB
106 UCSR1B = 0;
107 _delay_ms(5);
108# if defined(__AVR_AT90USB162__) // Teensy 1.0
109 EIMSK = 0;
110 PCICR = 0;
111 SPCR = 0;
112 ACSR = 0;
113 EECR = 0;
114 TIMSK0 = 0;
115 TIMSK1 = 0;
116 UCSR1B = 0;
117 DDRB = 0;
118 DDRC = 0;
119 DDRD = 0;
120 PORTB = 0;
121 PORTC = 0;
122 PORTD = 0;
123 asm volatile("jmp 0x3E00");
124# elif defined(__AVR_ATmega32U4__) // Teensy 2.0
125 EIMSK = 0;
126 PCICR = 0;
127 SPCR = 0;
128 ACSR = 0;
129 EECR = 0;
130 ADCSRA = 0;
131 TIMSK0 = 0;
132 TIMSK1 = 0;
133 TIMSK3 = 0;
134 TIMSK4 = 0;
135 UCSR1B = 0;
136 TWCR = 0;
137 DDRB = 0;
138 DDRC = 0;
139 DDRD = 0;
140 DDRE = 0;
141 DDRF = 0;
142 TWCR = 0;
143 PORTB = 0;
144 PORTC = 0;
145 PORTD = 0;
146 PORTE = 0;
147 PORTF = 0;
148 asm volatile("jmp 0x7E00");
149# elif defined(__AVR_AT90USB646__) // Teensy++ 1.0
150 EIMSK = 0;
151 PCICR = 0;
152 SPCR = 0;
153 ACSR = 0;
154 EECR = 0;
155 ADCSRA = 0;
156 TIMSK0 = 0;
157 TIMSK1 = 0;
158 TIMSK2 = 0;
159 TIMSK3 = 0;
160 UCSR1B = 0;
161 TWCR = 0;
162 DDRA = 0;
163 DDRB = 0;
164 DDRC = 0;
165 DDRD = 0;
166 DDRE = 0;
167 DDRF = 0;
168 PORTA = 0;
169 PORTB = 0;
170 PORTC = 0;
171 PORTD = 0;
172 PORTE = 0;
173 PORTF = 0;
174 asm volatile("jmp 0xFC00");
175# elif defined(__AVR_AT90USB1286__) // Teensy++ 2.0
176 EIMSK = 0;
177 PCICR = 0;
178 SPCR = 0;
179 ACSR = 0;
180 EECR = 0;
181 ADCSRA = 0;
182 TIMSK0 = 0;
183 TIMSK1 = 0;
184 TIMSK2 = 0;
185 TIMSK3 = 0;
186 UCSR1B = 0;
187 TWCR = 0;
188 DDRA = 0;
189 DDRB = 0;
190 DDRC = 0;
191 DDRD = 0;
192 DDRE = 0;
193 DDRF = 0;
194 PORTA = 0;
195 PORTB = 0;
196 PORTC = 0;
197 PORTD = 0;
198 PORTE = 0;
199 PORTF = 0;
200 asm volatile("jmp 0x1FC00");
201# endif
202
203#elif defined(BOOTLOADER_CATERINA)
204 // this block may be optional
205 // TODO: figure it out
206
207 uint16_t *const bootKeyPtr = (uint16_t *)0x0800;
208
209 // Value used by Caterina bootloader use to determine whether to run the
210 // sketch or the bootloader programmer.
211 uint16_t bootKey = 0x7777;
212
213 *bootKeyPtr = bootKey;
214
215 // setup watchdog timeout
216 wdt_enable(WDTO_60MS);
217
218 while (1) {
219 } // wait for watchdog timer to trigger
220
221#elif defined(BOOTLOADER_USBASP)
222 // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c
223 wdt_enable(WDTO_15MS);
224 wdt_reset();
225 asm volatile("cli \n\t"
226 "ldi r29 , %[ramendhi] \n\t"
227 "ldi r28 , %[ramendlo] \n\t"
228# if (FLASHEND > 131071)
229 "ldi r18 , %[bootaddrhi] \n\t"
230 "st Y+, r18 \n\t"
231# endif
232 "ldi r18 , %[bootaddrme] \n\t"
233 "st Y+, r18 \n\t"
234 "ldi r18 , %[bootaddrlo] \n\t"
235 "st Y+, r18 \n\t"
236 "out %[mcucsrio], __zero_reg__ \n\t"
237 "bootloader_startup_loop%=: \n\t"
238 "rjmp bootloader_startup_loop%= \n\t"
239 :
240 : [mcucsrio] "I"(_SFR_IO_ADDR(MCUCSR)),
241# if (FLASHEND > 131071)
242 [ramendhi] "M"(((RAMEND - 2) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 2) >> 0) & 0xff), [bootaddrhi] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 16) & 0xff),
243# else
244 [ramendhi] "M"(((RAMEND - 1) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 1) >> 0) & 0xff),
245# endif
246 [bootaddrme] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff), [bootaddrlo] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff));
247
248#else // Assume remaining boards are DFU, even if the flag isn't set
249
250# if !(defined(__AVR_ATmega32A__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATtiny85__)) // no USB - maybe BOOTLOADER_BOOTLOADHID instead though?
251 UDCON = 1;
252 USBCON = (1 << FRZCLK); // disable USB
253 UCSR1B = 0;
254 _delay_ms(5); // 5 seems to work fine
255# endif
256
257# ifdef BOOTLOADER_BOOTLOADHID
258 // force bootloadHID to stay in bootloader mode, so that it waits
259 // for a new firmware to be flashed
260 eeprom_write_byte((uint8_t *)1, 0x00);
261# endif
262
263 // watchdog reset
264 reset_key = BOOTLOADER_RESET_KEY;
265 wdt_enable(WDTO_250MS);
266 for (;;)
267 ;
268#endif
269}
270
271/* this runs before main() */
272void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3")));
273void bootloader_jump_after_watchdog_reset(void) {
274#ifndef BOOTLOADER_HALFKAY
275 if ((MCUCSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
276 reset_key = 0;
277
278 // My custom USBasploader requires this to come up.
279 MCUCSR = 0;
280
281 // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog.
282 MCUCSR &= ~(1 << WDRF);
283 wdt_disable();
284
285// This is compled into 'icall', address should be in word unit, not byte.
286# ifdef BOOTLOADER_SIZE
287 ((void (*)(void))((FLASH_SIZE - BOOTLOADER_SIZE) >> 1))();
288# else
289 asm("ijmp" ::"z"(bootloader_start));
290# endif
291 }
292#endif
293}
diff --git a/platforms/avr/bootloader_size.c b/platforms/avr/bootloader_size.c
new file mode 100644
index 000000000..a029f9321
--- /dev/null
+++ b/platforms/avr/bootloader_size.c
@@ -0,0 +1,21 @@
1// Copyright 2017 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#include <avr/io.h>
17#include <avr/boot.h>
18
19// clang-format off
20// this is not valid C - it's for computing the size available on the chip
21AVR_SIZE: FLASHEND + 1 - BOOTLOADER_SIZE
diff --git a/platforms/avr/drivers/analog.c b/platforms/avr/drivers/analog.c
index 8d299ffdb..628835cce 100644
--- a/platforms/avr/drivers/analog.c
+++ b/platforms/avr/drivers/analog.c
@@ -23,29 +23,6 @@ static uint8_t aref = ADC_REF_POWER;
23 23
24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); } 24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); }
25 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)); } 26int16_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); }
50 27
51uint8_t pinToMux(pin_t pin) { 28uint8_t pinToMux(pin_t pin) {
diff --git a/platforms/avr/drivers/analog.h b/platforms/avr/drivers/analog.h
index 058882450..fa2fb0d89 100644
--- a/platforms/avr/drivers/analog.h
+++ b/platforms/avr/drivers/analog.h
@@ -22,8 +22,7 @@
22#ifdef __cplusplus 22#ifdef __cplusplus
23extern "C" { 23extern "C" {
24#endif 24#endif
25void analogReference(uint8_t mode); 25void analogReference(uint8_t mode);
26int16_t analogRead(uint8_t pin);
27 26
28int16_t analogReadPin(pin_t pin); 27int16_t analogReadPin(pin_t pin);
29uint8_t pinToMux(pin_t pin); 28uint8_t pinToMux(pin_t pin);
diff --git a/platforms/avr/drivers/audio_pwm.h b/platforms/avr/drivers/audio_pwm.h
new file mode 100644
index 000000000..d6eb3571d
--- /dev/null
+++ b/platforms/avr/drivers/audio_pwm.h
@@ -0,0 +1,17 @@
1/* Copyright 2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#pragma once
diff --git a/platforms/avr/drivers/audio_pwm_hardware.c b/platforms/avr/drivers/audio_pwm_hardware.c
new file mode 100644
index 000000000..df03a4558
--- /dev/null
+++ b/platforms/avr/drivers/audio_pwm_hardware.c
@@ -0,0 +1,332 @@
1/* Copyright 2016 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#if defined(__AVR__)
19# include <avr/pgmspace.h>
20# include <avr/interrupt.h>
21# include <avr/io.h>
22#endif
23
24#include "audio.h"
25
26extern bool playing_note;
27extern bool playing_melody;
28extern uint8_t note_timbre;
29
30#define CPU_PRESCALER 8
31
32/*
33 Audio Driver: PWM
34
35 drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
36
37 the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
38 and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
39
40 alternatively, the PWM pins on PORTB can be used as only/primary speaker
41*/
42
43#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
44# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
45#endif
46
47#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
48# define AUDIO1_PIN_SET
49# define AUDIO1_TIMSKx TIMSK3
50# define AUDIO1_TCCRxA TCCR3A
51# define AUDIO1_TCCRxB TCCR3B
52# define AUDIO1_ICRx ICR3
53# define AUDIO1_WGMx0 WGM30
54# define AUDIO1_WGMx1 WGM31
55# define AUDIO1_WGMx2 WGM32
56# define AUDIO1_WGMx3 WGM33
57# define AUDIO1_CSx0 CS30
58# define AUDIO1_CSx1 CS31
59# define AUDIO1_CSx2 CS32
60
61# if (AUDIO_PIN == C6)
62# define AUDIO1_COMxy0 COM3A0
63# define AUDIO1_COMxy1 COM3A1
64# define AUDIO1_OCIExy OCIE3A
65# define AUDIO1_OCRxy OCR3A
66# define AUDIO1_PIN C6
67# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
68# elif (AUDIO_PIN == C5)
69# define AUDIO1_COMxy0 COM3B0
70# define AUDIO1_COMxy1 COM3B1
71# define AUDIO1_OCIExy OCIE3B
72# define AUDIO1_OCRxy OCR3B
73# define AUDIO1_PIN C5
74# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
75# elif (AUDIO_PIN == C4)
76# define AUDIO1_COMxy0 COM3C0
77# define AUDIO1_COMxy1 COM3C1
78# define AUDIO1_OCIExy OCIE3C
79# define AUDIO1_OCRxy OCR3C
80# define AUDIO1_PIN C4
81# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
82# endif
83#endif
84
85#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
86# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
87#endif
88
89#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
90# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
91#endif
92
93#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
94# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
95#endif
96
97#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
98# define AUDIO2_PIN_SET
99# define AUDIO2_TIMSKx TIMSK1
100# define AUDIO2_TCCRxA TCCR1A
101# define AUDIO2_TCCRxB TCCR1B
102# define AUDIO2_ICRx ICR1
103# define AUDIO2_WGMx0 WGM10
104# define AUDIO2_WGMx1 WGM11
105# define AUDIO2_WGMx2 WGM12
106# define AUDIO2_WGMx3 WGM13
107# define AUDIO2_CSx0 CS10
108# define AUDIO2_CSx1 CS11
109# define AUDIO2_CSx2 CS12
110
111# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
112# define AUDIO2_COMxy0 COM1A0
113# define AUDIO2_COMxy1 COM1A1
114# define AUDIO2_OCIExy OCIE1A
115# define AUDIO2_OCRxy OCR1A
116# define AUDIO2_PIN B5
117# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
118# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
119# define AUDIO2_COMxy0 COM1B0
120# define AUDIO2_COMxy1 COM1B1
121# define AUDIO2_OCIExy OCIE1B
122# define AUDIO2_OCRxy OCR1B
123# define AUDIO2_PIN B6
124# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
125# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
126# define AUDIO2_COMxy0 COM1C0
127# define AUDIO2_COMxy1 COM1C1
128# define AUDIO2_OCIExy OCIE1C
129# define AUDIO2_OCRxy OCR1C
130# define AUDIO2_PIN B7
131# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
132# elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
133# pragma message "Audio support for ATmega32A is experimental and can cause crashes."
134# undef AUDIO2_TIMSKx
135# define AUDIO2_TIMSKx TIMSK
136# define AUDIO2_COMxy0 COM1A0
137# define AUDIO2_COMxy1 COM1A1
138# define AUDIO2_OCIExy OCIE1A
139# define AUDIO2_OCRxy OCR1A
140# define AUDIO2_PIN D5
141# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
142# endif
143#endif
144
145// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
146#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
147# pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
148// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
149#endif
150// -----------------------------------------------------------------------------
151
152#ifdef AUDIO1_PIN_SET
153static float channel_1_frequency = 0.0f;
154void channel_1_set_frequency(float freq) {
155 if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
156 {
157 // disable the output, but keep the pwm-ISR going (with the previous
158 // frequency) so the audio-state keeps getting updated
159 // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
160 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
161 return;
162 } else {
163 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
164 }
165
166 channel_1_frequency = freq;
167
168 // set pwm period
169 AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
170 // and duty cycle
171 AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
172}
173
174void channel_1_start(void) {
175 // enable timer-counter ISR
176 AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
177 // enable timer-counter output
178 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
179}
180
181void channel_1_stop(void) {
182 // disable timer-counter ISR
183 AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
184 // disable timer-counter output
185 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
186}
187#endif
188
189#ifdef AUDIO2_PIN_SET
190static float channel_2_frequency = 0.0f;
191void channel_2_set_frequency(float freq) {
192 if (freq == 0.0f) {
193 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
194 return;
195 } else {
196 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
197 }
198
199 channel_2_frequency = freq;
200
201 AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
202 AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
203}
204
205float channel_2_get_frequency(void) { return channel_2_frequency; }
206
207void channel_2_start(void) {
208 AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
209 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
210}
211
212void channel_2_stop(void) {
213 AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
214 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
215}
216#endif
217
218void audio_driver_initialize() {
219#ifdef AUDIO1_PIN_SET
220 channel_1_stop();
221 setPinOutput(AUDIO1_PIN);
222#endif
223
224#ifdef AUDIO2_PIN_SET
225 channel_2_stop();
226 setPinOutput(AUDIO2_PIN);
227#endif
228
229 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
230 // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
231 // OC3A -- PC6
232 // OC3B -- PC5
233 // OC3C -- PC4
234 // OC1A -- PB5
235 // OC1B -- PB6
236 // OC1C -- PB7
237
238 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
239 // OCR3A - PC6
240 // OCR3B - PC5
241 // OCR3C - PC4
242 // OCR1A - PB5
243 // OCR1B - PB6
244 // OCR1C - PB7
245
246 // Clock Select (CS3n) = 0b010 = Clock / 8
247#ifdef AUDIO1_PIN_SET
248 // initialize timer-counter
249 AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
250 AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
251#endif
252
253#ifdef AUDIO2_PIN_SET
254 AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
255 AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
256#endif
257}
258
259void audio_driver_stop() {
260#ifdef AUDIO1_PIN_SET
261 channel_1_stop();
262#endif
263
264#ifdef AUDIO2_PIN_SET
265 channel_2_stop();
266#endif
267}
268
269void audio_driver_start(void) {
270#ifdef AUDIO1_PIN_SET
271 channel_1_start();
272 if (playing_note) {
273 channel_1_set_frequency(audio_get_processed_frequency(0));
274 }
275#endif
276
277#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
278 channel_2_start();
279 if (playing_note) {
280 channel_2_set_frequency(audio_get_processed_frequency(0));
281 }
282#endif
283}
284
285static volatile uint32_t isr_counter = 0;
286#ifdef AUDIO1_PIN_SET
287ISR(AUDIO1_TIMERx_COMPy_vect) {
288 isr_counter++;
289 if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
290
291 isr_counter = 0;
292 bool state_changed = audio_update_state();
293
294 if (!playing_note && !playing_melody) {
295 channel_1_stop();
296# ifdef AUDIO2_PIN_SET
297 channel_2_stop();
298# endif
299 return;
300 }
301
302 if (state_changed) {
303 channel_1_set_frequency(audio_get_processed_frequency(0));
304# ifdef AUDIO2_PIN_SET
305 if (audio_get_number_of_active_tones() > 1) {
306 channel_2_set_frequency(audio_get_processed_frequency(1));
307 } else {
308 channel_2_stop();
309 }
310# endif
311 }
312}
313#endif
314
315#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
316ISR(AUDIO2_TIMERx_COMPy_vect) {
317 isr_counter++;
318 if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
319
320 isr_counter = 0;
321 bool state_changed = audio_update_state();
322
323 if (!playing_note && !playing_melody) {
324 channel_2_stop();
325 return;
326 }
327
328 if (state_changed) {
329 channel_2_set_frequency(audio_get_processed_frequency(0));
330 }
331}
332#endif
diff --git a/platforms/avr/drivers/i2c_master.c b/platforms/avr/drivers/i2c_master.c
index 2773e0077..111b55d6b 100644
--- a/platforms/avr/drivers/i2c_master.c
+++ b/platforms/avr/drivers/i2c_master.c
@@ -202,6 +202,25 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data,
202 return status; 202 return status;
203} 203}
204 204
205i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
206 i2c_status_t status = i2c_start(devaddr | 0x00, timeout);
207 if (status >= 0) {
208 status = i2c_write(regaddr >> 8, timeout);
209
210 if (status >= 0) {
211 status = i2c_write(regaddr & 0xFF, timeout);
212
213 for (uint16_t i = 0; i < length && status >= 0; i++) {
214 status = i2c_write(data[i], timeout);
215 }
216 }
217 }
218
219 i2c_stop();
220
221 return status;
222}
223
205i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { 224i2c_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); 225 i2c_status_t status = i2c_start(devaddr, timeout);
207 if (status < 0) { 226 if (status < 0) {
@@ -235,6 +254,43 @@ error:
235 return (status < 0) ? status : I2C_STATUS_SUCCESS; 254 return (status < 0) ? status : I2C_STATUS_SUCCESS;
236} 255}
237 256
257i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
258 i2c_status_t status = i2c_start(devaddr, timeout);
259 if (status < 0) {
260 goto error;
261 }
262
263 status = i2c_write(regaddr >> 8, timeout);
264 if (status < 0) {
265 goto error;
266 }
267 status = i2c_write(regaddr & 0xFF, timeout);
268 if (status < 0) {
269 goto error;
270 }
271
272 status = i2c_start(devaddr | 0x01, timeout);
273
274 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
275 status = i2c_read_ack(timeout);
276 if (status >= 0) {
277 data[i] = status;
278 }
279 }
280
281 if (status >= 0) {
282 status = i2c_read_nack(timeout);
283 if (status >= 0) {
284 data[(length - 1)] = status;
285 }
286 }
287
288error:
289 i2c_stop();
290
291 return (status < 0) ? status : I2C_STATUS_SUCCESS;
292}
293
238void i2c_stop(void) { 294void i2c_stop(void) {
239 // transmit STOP condition 295 // transmit STOP condition
240 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); 296 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
diff --git a/platforms/avr/drivers/i2c_master.h b/platforms/avr/drivers/i2c_master.h
index e5af73364..2d95846db 100644
--- a/platforms/avr/drivers/i2c_master.h
+++ b/platforms/avr/drivers/i2c_master.h
@@ -39,5 +39,7 @@ int16_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); 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); 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); 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_writeReg16(uint8_t devaddr, uint16_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); 43i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
44i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
43void i2c_stop(void); 45void i2c_stop(void);
diff --git a/platforms/avr/drivers/ps2/ps2_io.c b/platforms/avr/drivers/ps2/ps2_io.c
new file mode 100644
index 000000000..7c826fbf1
--- /dev/null
+++ b/platforms/avr/drivers/ps2/ps2_io.c
@@ -0,0 +1,51 @@
1#include <stdbool.h>
2#include "ps2_io.h"
3#include "gpio.h"
4#include "wait.h"
5
6/* Check port settings for clock and data line */
7#if !(defined(PS2_CLOCK_PIN))
8# error "PS/2 clock setting is required in config.h"
9#endif
10
11#if !(defined(PS2_DATA_PIN))
12# error "PS/2 data setting is required in config.h"
13#endif
14
15/*
16 * Clock
17 */
18void clock_init(void) {}
19
20void clock_lo(void) {
21 // Transition from input with pull-up to output low via Hi-Z instead of output high
22 writePinLow(PS2_CLOCK_PIN);
23 setPinOutput(PS2_CLOCK_PIN);
24}
25
26void clock_hi(void) { setPinInputHigh(PS2_CLOCK_PIN); }
27
28bool clock_in(void) {
29 setPinInputHigh(PS2_CLOCK_PIN);
30 wait_us(1);
31 return readPin(PS2_CLOCK_PIN);
32}
33
34/*
35 * Data
36 */
37void data_init(void) {}
38
39void data_lo(void) {
40 // Transition from input with pull-up to output low via Hi-Z instead of output high
41 writePinLow(PS2_DATA_PIN);
42 setPinOutput(PS2_DATA_PIN);
43}
44
45void data_hi(void) { setPinInputHigh(PS2_DATA_PIN); }
46
47bool data_in(void) {
48 setPinInputHigh(PS2_DATA_PIN);
49 wait_us(1);
50 return readPin(PS2_DATA_PIN);
51}
diff --git a/platforms/avr/drivers/ps2/ps2_usart.c b/platforms/avr/drivers/ps2/ps2_usart.c
new file mode 100644
index 000000000..151cfcd68
--- /dev/null
+++ b/platforms/avr/drivers/ps2/ps2_usart.c
@@ -0,0 +1,227 @@
1/*
2Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
3
4This software is licensed with a Modified BSD License.
5All of this is supposed to be Free Software, Open Source, DFSG-free,
6GPL-compatible, and OK to use in both free and proprietary applications.
7Additions and corrections to this file are welcome.
8
9
10Redistribution and use in source and binary forms, with or without
11modification, are permitted provided that the following conditions are met:
12
13* Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16* Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21* Neither the name of the copyright holders nor the names of
22 contributors may be used to endorse or promote products derived
23 from this software without specific prior written permission.
24
25THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35POSSIBILITY OF SUCH DAMAGE.
36*/
37
38/*
39 * PS/2 protocol USART version
40 */
41
42#include <stdbool.h>
43#include <avr/interrupt.h>
44#include <util/delay.h>
45#include "gpio.h"
46#include "ps2.h"
47#include "ps2_io.h"
48#include "print.h"
49
50#ifndef PS2_CLOCK_DDR
51# define PS2_CLOCK_DDR PORTx_ADDRESS(PS2_CLOCK_PIN)
52#endif
53#ifndef PS2_CLOCK_BIT
54# define PS2_CLOCK_BIT (PS2_CLOCK_PIN & 0xF)
55#endif
56#ifndef PS2_DATA_DDR
57# define PS2_DATA_DDR PORTx_ADDRESS(PS2_DATA_PIN)
58#endif
59#ifndef PS2_DATA_BIT
60# define PS2_DATA_BIT (PS2_DATA_PIN & 0xF)
61#endif
62
63#define WAIT(stat, us, err) \
64 do { \
65 if (!wait_##stat(us)) { \
66 ps2_error = err; \
67 goto ERROR; \
68 } \
69 } while (0)
70
71uint8_t ps2_error = PS2_ERR_NONE;
72
73static inline uint8_t pbuf_dequeue(void);
74static inline void pbuf_enqueue(uint8_t data);
75static inline bool pbuf_has_data(void);
76static inline void pbuf_clear(void);
77
78void ps2_host_init(void) {
79 idle(); // without this many USART errors occur when cable is disconnected
80 PS2_USART_INIT();
81 PS2_USART_RX_INT_ON();
82 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
83 //_delay_ms(2500);
84}
85
86uint8_t ps2_host_send(uint8_t data) {
87 bool parity = true;
88 ps2_error = PS2_ERR_NONE;
89
90 PS2_USART_OFF();
91
92 /* terminate a transmission if we have */
93 inhibit();
94 _delay_us(100); // [4]p.13
95
96 /* 'Request to Send' and Start bit */
97 data_lo();
98 clock_hi();
99 WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
100
101 /* Data bit[2-9] */
102 for (uint8_t i = 0; i < 8; i++) {
103 _delay_us(15);
104 if (data & (1 << i)) {
105 parity = !parity;
106 data_hi();
107 } else {
108 data_lo();
109 }
110 WAIT(clock_hi, 50, 2);
111 WAIT(clock_lo, 50, 3);
112 }
113
114 /* Parity bit */
115 _delay_us(15);
116 if (parity) {
117 data_hi();
118 } else {
119 data_lo();
120 }
121 WAIT(clock_hi, 50, 4);
122 WAIT(clock_lo, 50, 5);
123
124 /* Stop bit */
125 _delay_us(15);
126 data_hi();
127
128 /* Ack */
129 WAIT(data_lo, 50, 6);
130 WAIT(clock_lo, 50, 7);
131
132 /* wait for idle state */
133 WAIT(clock_hi, 50, 8);
134 WAIT(data_hi, 50, 9);
135
136 idle();
137 PS2_USART_INIT();
138 PS2_USART_RX_INT_ON();
139 return ps2_host_recv_response();
140ERROR:
141 idle();
142 PS2_USART_INIT();
143 PS2_USART_RX_INT_ON();
144 return 0;
145}
146
147uint8_t ps2_host_recv_response(void) {
148 // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
149 uint8_t retry = 25;
150 while (retry-- && !pbuf_has_data()) {
151 _delay_ms(1);
152 }
153 return pbuf_dequeue();
154}
155
156uint8_t ps2_host_recv(void) {
157 if (pbuf_has_data()) {
158 ps2_error = PS2_ERR_NONE;
159 return pbuf_dequeue();
160 } else {
161 ps2_error = PS2_ERR_NODATA;
162 return 0;
163 }
164}
165
166ISR(PS2_USART_RX_VECT) {
167 // TODO: request RESEND when error occurs?
168 uint8_t error = PS2_USART_ERROR; // USART error should be read before data
169 uint8_t data = PS2_USART_RX_DATA;
170 if (!error) {
171 pbuf_enqueue(data);
172 } else {
173 xprintf("PS2 USART error: %02X data: %02X\n", error, data);
174 }
175}
176
177/* send LED state to keyboard */
178void ps2_host_set_led(uint8_t led) {
179 ps2_host_send(0xED);
180 ps2_host_send(led);
181}
182
183/*--------------------------------------------------------------------
184 * Ring buffer to store scan codes from keyboard
185 *------------------------------------------------------------------*/
186#define PBUF_SIZE 32
187static uint8_t pbuf[PBUF_SIZE];
188static uint8_t pbuf_head = 0;
189static uint8_t pbuf_tail = 0;
190static inline void pbuf_enqueue(uint8_t data) {
191 uint8_t sreg = SREG;
192 cli();
193 uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
194 if (next != pbuf_tail) {
195 pbuf[pbuf_head] = data;
196 pbuf_head = next;
197 } else {
198 print("pbuf: full\n");
199 }
200 SREG = sreg;
201}
202static inline uint8_t pbuf_dequeue(void) {
203 uint8_t val = 0;
204
205 uint8_t sreg = SREG;
206 cli();
207 if (pbuf_head != pbuf_tail) {
208 val = pbuf[pbuf_tail];
209 pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
210 }
211 SREG = sreg;
212
213 return val;
214}
215static inline bool pbuf_has_data(void) {
216 uint8_t sreg = SREG;
217 cli();
218 bool has_data = (pbuf_head != pbuf_tail);
219 SREG = sreg;
220 return has_data;
221}
222static inline void pbuf_clear(void) {
223 uint8_t sreg = SREG;
224 cli();
225 pbuf_head = pbuf_tail = 0;
226 SREG = sreg;
227}
diff --git a/platforms/avr/drivers/uart.c b/platforms/avr/drivers/uart.c
index c6abcb6fe..01cf6b1fb 100644
--- a/platforms/avr/drivers/uart.c
+++ b/platforms/avr/drivers/uart.c
@@ -100,7 +100,7 @@ void uart_init(uint32_t baud) {
100} 100}
101 101
102// Transmit a byte 102// Transmit a byte
103void uart_putchar(uint8_t c) { 103void uart_write(uint8_t data) {
104 uint8_t i; 104 uint8_t i;
105 105
106 i = tx_buffer_head + 1; 106 i = tx_buffer_head + 1;
@@ -110,27 +110,39 @@ void uart_putchar(uint8_t c) {
110 while (tx_buffer_tail == i) 110 while (tx_buffer_tail == i)
111 ; // wait until space in buffer 111 ; // wait until space in buffer
112 // cli(); 112 // cli();
113 tx_buffer[i] = c; 113 tx_buffer[i] = data;
114 tx_buffer_head = i; 114 tx_buffer_head = i;
115 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn) | (1 << UDRIEn); 115 UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn) | (1 << UDRIEn);
116 // sei(); 116 // sei();
117} 117}
118 118
119// Receive a byte 119// Receive a byte
120uint8_t uart_getchar(void) { 120uint8_t uart_read(void) {
121 uint8_t c, i; 121 uint8_t data, i;
122 122
123 while (rx_buffer_head == rx_buffer_tail) 123 while (rx_buffer_head == rx_buffer_tail)
124 ; // wait for character 124 ; // wait for character
125 i = rx_buffer_tail + 1; 125 i = rx_buffer_tail + 1;
126 if (i >= RX_BUFFER_SIZE) i = 0; 126 if (i >= RX_BUFFER_SIZE) i = 0;
127 c = rx_buffer[i]; 127 data = rx_buffer[i];
128 rx_buffer_tail = i; 128 rx_buffer_tail = i;
129 return c; 129 return data;
130}
131
132void uart_transmit(const uint8_t *data, uint16_t length) {
133 for (uint16_t i = 0; i < length; i++) {
134 uart_write(data[i]);
135 }
136}
137
138void uart_receive(uint8_t *data, uint16_t length) {
139 for (uint16_t i = 0; i < length; i++) {
140 data[i] = uart_read();
141 }
130} 142}
131 143
132// Return whether the number of bytes waiting in the receive buffer is nonzero. 144// 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 145// Call this before uart_read() to check if it will need
134// to wait for a byte to arrive. 146// to wait for a byte to arrive.
135bool uart_available(void) { 147bool uart_available(void) {
136 uint8_t head, tail; 148 uint8_t head, tail;
diff --git a/platforms/avr/drivers/uart.h b/platforms/avr/drivers/uart.h
index 602eb3d8b..e2dc664ed 100644
--- a/platforms/avr/drivers/uart.h
+++ b/platforms/avr/drivers/uart.h
@@ -28,8 +28,12 @@
28 28
29void uart_init(uint32_t baud); 29void uart_init(uint32_t baud);
30 30
31void uart_putchar(uint8_t c); 31void uart_write(uint8_t data);
32 32
33uint8_t uart_getchar(void); 33uint8_t uart_read(void);
34
35void uart_transmit(const uint8_t *data, uint16_t length);
36
37void uart_receive(uint8_t *data, uint16_t length);
34 38
35bool uart_available(void); 39bool uart_available(void);
diff --git a/platforms/avr/flash.mk b/platforms/avr/flash.mk
index 985cb60e5..6d50e7253 100644
--- a/platforms/avr/flash.mk
+++ b/platforms/avr/flash.mk
@@ -130,6 +130,15 @@ avrdude-split-right: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware
130 $(call EXEC_AVRDUDE,eeprom-righthand.eep) 130 $(call EXEC_AVRDUDE,eeprom-righthand.eep)
131 131
132define EXEC_USBASP 132define EXEC_USBASP
133 if $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp 2>&1 | grep -q "could not find USB device with"; then \
134 printf "$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)" ;\
135 sleep $(BOOTLOADER_RETRY_TIME) ;\
136 until $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp 2>&1 | (! grep -q "could not find USB device with"); do\
137 printf "." ;\
138 sleep $(BOOTLOADER_RETRY_TIME) ;\
139 done ;\
140 printf "\n" ;\
141 fi
133 $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp -U flash:w:$(BUILD_DIR)/$(TARGET).hex 142 $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp -U flash:w:$(BUILD_DIR)/$(TARGET).hex
134endef 143endef
135 144
diff --git a/platforms/avr/gpio.h b/platforms/avr/gpio.h
new file mode 100644
index 000000000..e9be68491
--- /dev/null
+++ b/platforms/avr/gpio.h
@@ -0,0 +1,49 @@
1/* Copyright 2021 QMK
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 <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <avr/io.h>
19#include "pin_defs.h"
20
21typedef uint8_t pin_t;
22
23/* Operation of GPIO by pin. */
24
25#define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
26#define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
27#define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low")
28#define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))
29
30#define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
31#define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
32#define writePin(pin, level) ((level) ? writePinHigh(pin) : writePinLow(pin))
33
34#define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))
35
36#define togglePin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF))
37
38/* Operation of GPIO by port. */
39
40typedef uint8_t port_data_t;
41
42#define readPort(port) PINx_ADDRESS(port)
43
44#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
45#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF))
46#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF))
47
48#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
49#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF))
diff --git a/platforms/avr/pin_defs.h b/platforms/avr/pin_defs.h
new file mode 100644
index 000000000..23d948041
--- /dev/null
+++ b/platforms/avr/pin_defs.h
@@ -0,0 +1,128 @@
1/* Copyright 2021 QMK
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 <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <avr/io.h>
19
20#define PORT_SHIFTER 4 // this may be 4 for all AVR chips
21
22// If you want to add more to this list, reference the PINx definitions in these header
23// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr
24
25#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
26# define ADDRESS_BASE 0x00
27# define PINB_ADDRESS 0x3
28# define PINC_ADDRESS 0x6
29# define PIND_ADDRESS 0x9
30# define PINE_ADDRESS 0xC
31# define PINF_ADDRESS 0xF
32#elif defined(__AVR_AT90USB162__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
33# define ADDRESS_BASE 0x00
34# define PINB_ADDRESS 0x3
35# define PINC_ADDRESS 0x6
36# define PIND_ADDRESS 0x9
37#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
38# define ADDRESS_BASE 0x00
39# define PINA_ADDRESS 0x0
40# define PINB_ADDRESS 0x3
41# define PINC_ADDRESS 0x6
42# define PIND_ADDRESS 0x9
43# define PINE_ADDRESS 0xC
44# define PINF_ADDRESS 0xF
45#elif defined(__AVR_ATmega32A__)
46# define ADDRESS_BASE 0x10
47# define PIND_ADDRESS 0x0
48# define PINC_ADDRESS 0x3
49# define PINB_ADDRESS 0x6
50# define PINA_ADDRESS 0x9
51#elif defined(__AVR_ATtiny85__)
52# define ADDRESS_BASE 0x10
53# define PINB_ADDRESS 0x6
54#else
55# error "Pins are not defined"
56#endif
57
58#define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)
59
60#define _PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + ((p) >> PORT_SHIFTER) + (offset))
61// Port X Input Pins Address
62#define PINx_ADDRESS(p) _PIN_ADDRESS(p, 0)
63// Port X Data Direction Register, 0:input 1:output
64#define DDRx_ADDRESS(p) _PIN_ADDRESS(p, 1)
65// Port X Data Register
66#define PORTx_ADDRESS(p) _PIN_ADDRESS(p, 2)
67
68/* I/O pins */
69#ifdef PORTA
70# define A0 PINDEF(A, 0)
71# define A1 PINDEF(A, 1)
72# define A2 PINDEF(A, 2)
73# define A3 PINDEF(A, 3)
74# define A4 PINDEF(A, 4)
75# define A5 PINDEF(A, 5)
76# define A6 PINDEF(A, 6)
77# define A7 PINDEF(A, 7)
78#endif
79#ifdef PORTB
80# define B0 PINDEF(B, 0)
81# define B1 PINDEF(B, 1)
82# define B2 PINDEF(B, 2)
83# define B3 PINDEF(B, 3)
84# define B4 PINDEF(B, 4)
85# define B5 PINDEF(B, 5)
86# define B6 PINDEF(B, 6)
87# define B7 PINDEF(B, 7)
88#endif
89#ifdef PORTC
90# define C0 PINDEF(C, 0)
91# define C1 PINDEF(C, 1)
92# define C2 PINDEF(C, 2)
93# define C3 PINDEF(C, 3)
94# define C4 PINDEF(C, 4)
95# define C5 PINDEF(C, 5)
96# define C6 PINDEF(C, 6)
97# define C7 PINDEF(C, 7)
98#endif
99#ifdef PORTD
100# define D0 PINDEF(D, 0)
101# define D1 PINDEF(D, 1)
102# define D2 PINDEF(D, 2)
103# define D3 PINDEF(D, 3)
104# define D4 PINDEF(D, 4)
105# define D5 PINDEF(D, 5)
106# define D6 PINDEF(D, 6)
107# define D7 PINDEF(D, 7)
108#endif
109#ifdef PORTE
110# define E0 PINDEF(E, 0)
111# define E1 PINDEF(E, 1)
112# define E2 PINDEF(E, 2)
113# define E3 PINDEF(E, 3)
114# define E4 PINDEF(E, 4)
115# define E5 PINDEF(E, 5)
116# define E6 PINDEF(E, 6)
117# define E7 PINDEF(E, 7)
118#endif
119#ifdef PORTF
120# define F0 PINDEF(F, 0)
121# define F1 PINDEF(F, 1)
122# define F2 PINDEF(F, 2)
123# define F3 PINDEF(F, 3)
124# define F4 PINDEF(F, 4)
125# define F5 PINDEF(F, 5)
126# define F6 PINDEF(F, 6)
127# define F7 PINDEF(F, 7)
128#endif
diff --git a/platforms/avr/platform.c b/platforms/avr/platform.c
new file mode 100644
index 000000000..3e35b4fe4
--- /dev/null
+++ b/platforms/avr/platform.c
@@ -0,0 +1,21 @@
1/* Copyright 2021 QMK
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 <http://www.gnu.org/licenses/>.
15 */
16
17#include "platform_deps.h"
18
19void platform_setup(void) {
20 // do nothing
21}
diff --git a/platforms/avr/platform.mk b/platforms/avr/platform.mk
new file mode 100644
index 000000000..b45108736
--- /dev/null
+++ b/platforms/avr/platform.mk
@@ -0,0 +1,179 @@
1# Hey Emacs, this is a -*- makefile -*-
2##############################################################################
3# Compiler settings
4#
5CC = $(CC_PREFIX) avr-gcc
6OBJCOPY = avr-objcopy
7OBJDUMP = avr-objdump
8SIZE = avr-size
9AR = avr-ar
10NM = avr-nm
11HEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature
12EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT)
13BIN =
14
15COMPILEFLAGS += -funsigned-char
16COMPILEFLAGS += -funsigned-bitfields
17COMPILEFLAGS += -ffunction-sections
18COMPILEFLAGS += -fdata-sections
19COMPILEFLAGS += -fpack-struct
20COMPILEFLAGS += -fshort-enums
21
22ASFLAGS += $(AVR_ASFLAGS)
23
24CFLAGS += $(COMPILEFLAGS) $(AVR_CFLAGS)
25CFLAGS += -fno-inline-small-functions
26CFLAGS += -fno-strict-aliasing
27
28CXXFLAGS += $(COMPILEFLAGS)
29CXXFLAGS += -fno-exceptions -std=c++11
30
31LDFLAGS +=-Wl,--gc-sections
32
33OPT_DEFS += -DF_CPU=$(F_CPU)UL
34
35MCUFLAGS = -mmcu=$(MCU)
36
37# List any extra directories to look for libraries here.
38# Each directory must be seperated by a space.
39# Use forward slashes for directory separators.
40# For a directory that has spaces, enclose it in quotes.
41EXTRALIBDIRS =
42
43
44#---------------- External Memory Options ----------------
45
46# 64 KB of external RAM, starting after internal RAM (ATmega128!),
47# used for variables (.data/.bss) and heap (malloc()).
48#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
49
50# 64 KB of external RAM, starting after internal RAM (ATmega128!),
51# only used for heap (malloc()).
52#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff
53
54EXTMEMOPTS =
55
56#---------------- Debugging Options ----------------
57
58# Debugging format.
59# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
60# AVR Studio 4.10 requires dwarf-2.
61# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
62DEBUG = dwarf-2
63
64# For simulavr only - target MCU frequency.
65DEBUG_MFREQ = $(F_CPU)
66
67# Set the DEBUG_UI to either gdb or insight.
68# DEBUG_UI = gdb
69DEBUG_UI = insight
70
71# Set the debugging back-end to either avarice, simulavr.
72DEBUG_BACKEND = avarice
73#DEBUG_BACKEND = simulavr
74
75# GDB Init Filename.
76GDBINIT_FILE = __avr_gdbinit
77
78# When using avarice settings for the JTAG
79JTAG_DEV = /dev/com1
80
81# Debugging port used to communicate between GDB / avarice / simulavr.
82DEBUG_PORT = 4242
83
84# Debugging host used to communicate between GDB / avarice / simulavr, normally
85# just set to localhost unless doing some sort of crazy debugging when
86# avarice is running on a different computer.
87DEBUG_HOST = localhost
88
89#============================================================================
90
91# Convert hex to bin.
92bin: $(BUILD_DIR)/$(TARGET).hex
93ifeq ($(BOOTLOADER),lufa-ms)
94 $(eval BIN_PADDING=$(shell n=`expr 32768 - $(BOOTLOADER_SIZE)` && echo $$(($$n)) || echo 0))
95 $(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin --pad-to $(BIN_PADDING)
96else
97 $(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
98endif
99 $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;
100
101# copy bin to FLASH.bin
102flashbin: bin
103 $(COPY) $(BUILD_DIR)/$(TARGET).bin FLASH.bin;
104
105# Generate avr-gdb config/init file which does the following:
106# define the reset signal, load the target file, connect to target, and set
107# a breakpoint at main().
108gdb-config:
109 @$(REMOVE) $(GDBINIT_FILE)
110 @echo define reset >> $(GDBINIT_FILE)
111 @echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
112 @echo end >> $(GDBINIT_FILE)
113 @echo file $(BUILD_DIR)/$(TARGET).elf >> $(GDBINIT_FILE)
114 @echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE)
115ifeq ($(DEBUG_BACKEND),simulavr)
116 @echo load >> $(GDBINIT_FILE)
117endif
118 @echo break main >> $(GDBINIT_FILE)
119
120debug: gdb-config $(BUILD_DIR)/$(TARGET).elf
121ifeq ($(DEBUG_BACKEND), avarice)
122 @echo Starting AVaRICE - Press enter when "waiting to connect" message displays.
123 @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \
124 $(BUILD_DIR)/$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
125 @$(WINSHELL) /c pause
126
127else
128 @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \
129 $(DEBUG_MFREQ) --port $(DEBUG_PORT)
130endif
131 @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)
132
133
134
135
136# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
137COFFCONVERT = $(OBJCOPY) --debugging
138COFFCONVERT += --change-section-address .data-0x800000
139COFFCONVERT += --change-section-address .bss-0x800000
140COFFCONVERT += --change-section-address .noinit-0x800000
141COFFCONVERT += --change-section-address .eeprom-0x810000
142
143
144
145coff: $(BUILD_DIR)/$(TARGET).elf
146 @$(SECHO) $(MSG_COFF) $(BUILD_DIR)/$(TARGET).cof
147 $(COFFCONVERT) -O coff-avr $< $(BUILD_DIR)/$(TARGET).cof
148
149
150extcoff: $(BUILD_DIR)/$(TARGET).elf
151 @$(SECHO) $(MSG_EXTENDED_COFF) $(BUILD_DIR)/$(TARGET).cof
152 $(COFFCONVERT) -O coff-ext-avr $< $(BUILD_DIR)/$(TARGET).cof
153
154ifeq ($(strip $(BOOTLOADER)), qmk-dfu)
155QMK_BOOTLOADER_TYPE = DFU
156else ifeq ($(strip $(BOOTLOADER)), qmk-hid)
157QMK_BOOTLOADER_TYPE = HID
158endif
159
160bootloader:
161ifeq ($(strip $(QMK_BOOTLOADER_TYPE)),)
162 $(error Please set BOOTLOADER to "qmk-dfu" or "qmk-hid" first!)
163else
164 make -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ clean
165 $(QMK_BIN) generate-dfu-header --quiet --keyboard $(KEYBOARD) --output lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Keyboard.h
166 $(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) -D__ASSEMBLER__ $(CFLAGS) $(OPT_DEFS) platforms/avr/bootloader_size.c 2> /dev/null | sed -ne 's/\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0))
167 $(eval PROGRAM_SIZE_KB=$(shell n=`expr $(MAX_SIZE) / 1024` && echo $$(($$n)) || echo 0))
168 $(eval BOOT_SECTION_SIZE_KB=$(shell n=`expr $(BOOTLOADER_SIZE) / 1024` && echo $$(($$n)) || echo 0))
169 $(eval FLASH_SIZE_KB=$(shell n=`expr $(PROGRAM_SIZE_KB) + $(BOOT_SECTION_SIZE_KB)` && echo $$(($$n)) || echo 0))
170 make -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ MCU=$(MCU) ARCH=$(ARCH) F_CPU=$(F_CPU) FLASH_SIZE_KB=$(FLASH_SIZE_KB) BOOT_SECTION_SIZE_KB=$(BOOT_SECTION_SIZE_KB)
171 printf "Bootloader$(QMK_BOOTLOADER_TYPE).hex copied to $(TARGET)_bootloader.hex\n"
172 cp lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Bootloader$(QMK_BOOTLOADER_TYPE).hex $(TARGET)_bootloader.hex
173endif
174
175production: $(BUILD_DIR)/$(TARGET).hex bootloader cpfirmware
176 @cat $(BUILD_DIR)/$(TARGET).hex | awk '/^:00000001FF/ == 0' > $(TARGET)_production.hex
177 @cat $(TARGET)_bootloader.hex >> $(TARGET)_production.hex
178 echo "File sizes:"
179 $(SIZE) $(TARGET).hex $(TARGET)_bootloader.hex $(TARGET)_production.hex
diff --git a/platforms/avr/platform_deps.h b/platforms/avr/platform_deps.h
new file mode 100644
index 000000000..45d9dcebf
--- /dev/null
+++ b/platforms/avr/platform_deps.h
@@ -0,0 +1,20 @@
1/* Copyright 2021 QMK
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 <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <avr/pgmspace.h>
19#include <avr/io.h>
20#include <avr/interrupt.h>
diff --git a/platforms/avr/printf.c b/platforms/avr/printf.c
new file mode 100644
index 000000000..9ad7a3869
--- /dev/null
+++ b/platforms/avr/printf.c
@@ -0,0 +1,20 @@
1/*
2Copyright 2011 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17#include "xprintf.h"
18#include "sendchar.h"
19
20void print_set_sendchar(sendchar_func_t func) { xdev_out(func); }
diff --git a/platforms/avr/printf.mk b/platforms/avr/printf.mk
new file mode 100644
index 000000000..060ad88c5
--- /dev/null
+++ b/platforms/avr/printf.mk
@@ -0,0 +1,2 @@
1TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/xprintf.S
2TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/printf.c
diff --git a/platforms/avr/sleep_led.c b/platforms/avr/sleep_led.c
new file mode 100644
index 000000000..9a3b52abe
--- /dev/null
+++ b/platforms/avr/sleep_led.c
@@ -0,0 +1,124 @@
1#include <stdint.h>
2#include <avr/io.h>
3#include <avr/interrupt.h>
4#include <avr/pgmspace.h>
5#include "led.h"
6#include "sleep_led.h"
7
8#ifndef SLEEP_LED_TIMER
9# define SLEEP_LED_TIMER 1
10#endif
11
12#if SLEEP_LED_TIMER == 1
13# define TCCRxB TCCR1B
14# define TIMERx_COMPA_vect TIMER1_COMPA_vect
15# if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register
16# define TIMSKx TIMSK
17# else
18# define TIMSKx TIMSK1
19# endif
20# define OCIExA OCIE1A
21# define OCRxx OCR1A
22#elif SLEEP_LED_TIMER == 3
23# define TCCRxB TCCR3B
24# define TIMERx_COMPA_vect TIMER3_COMPA_vect
25# define TIMSKx TIMSK3
26# define OCIExA OCIE3A
27# define OCRxx OCR3A
28#else
29error("Invalid SLEEP_LED_TIMER config")
30#endif
31
32/* Software PWM
33 * ______ ______ __
34 * | ON |___OFF___| ON |___OFF___| ....
35 * |<-------------->|<-------------->|<- ....
36 * PWM period PWM period
37 *
38 * 256 interrupts/period[resolution]
39 * 64 periods/second[frequency]
40 * 256*64 interrupts/second
41 * F_CPU/(256*64) clocks/interrupt
42 */
43#define SLEEP_LED_TIMER_TOP F_CPU / (256 * 64)
44
45/** \brief Sleep LED initialization
46 *
47 * FIXME: needs doc
48 */
49void sleep_led_init(void) {
50 /* Timer1 setup */
51 /* CTC mode */
52 TCCRxB |= _BV(WGM12);
53 /* Clock selelct: clk/1 */
54 TCCRxB |= _BV(CS10);
55 /* Set TOP value */
56 uint8_t sreg = SREG;
57 cli();
58 OCRxx = SLEEP_LED_TIMER_TOP;
59 SREG = sreg;
60}
61
62/** \brief Sleep LED enable
63 *
64 * FIXME: needs doc
65 */
66void sleep_led_enable(void) {
67 /* Enable Compare Match Interrupt */
68 TIMSKx |= _BV(OCIExA);
69}
70
71/** \brief Sleep LED disable
72 *
73 * FIXME: needs doc
74 */
75void sleep_led_disable(void) {
76 /* Disable Compare Match Interrupt */
77 TIMSKx &= ~_BV(OCIExA);
78}
79
80/** \brief Sleep LED toggle
81 *
82 * FIXME: needs doc
83 */
84void sleep_led_toggle(void) {
85 /* Disable Compare Match Interrupt */
86 TIMSKx ^= _BV(OCIExA);
87}
88
89/** \brief Breathing Sleep LED brighness(PWM On period) table
90 *
91 * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle
92 *
93 * https://www.wolframalpha.com/input/?i=sin%28x%2F64*pi%29**8+*+255%2C+x%3D0+to+63
94 * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }
95 */
96static const uint8_t breathing_table[64] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
97
98ISR(TIMERx_COMPA_vect) {
99 /* Software PWM
100 * timer:1111 1111 1111 1111
101 * \_____/\/ \_______/____ count(0-255)
102 * \ \______________ duration of step(4)
103 * \__________________ index of step table(0-63)
104 */
105 static union {
106 uint16_t row;
107 struct {
108 uint8_t count : 8;
109 uint8_t duration : 2;
110 uint8_t index : 6;
111 } pwm;
112 } timer = {.row = 0};
113
114 timer.row++;
115
116 // LED on
117 if (timer.pwm.count == 0) {
118 led_set(1 << USB_LED_CAPS_LOCK);
119 }
120 // LED off
121 if (timer.pwm.count == pgm_read_byte(&breathing_table[timer.pwm.index])) {
122 led_set(0);
123 }
124}
diff --git a/platforms/avr/suspend.c b/platforms/avr/suspend.c
new file mode 100644
index 000000000..b614746e6
--- /dev/null
+++ b/platforms/avr/suspend.c
@@ -0,0 +1,152 @@
1#include <stdbool.h>
2#include <avr/sleep.h>
3#include <avr/wdt.h>
4#include <avr/interrupt.h>
5#include "matrix.h"
6#include "action.h"
7#include "suspend.h"
8#include "timer.h"
9#include "led.h"
10#include "host.h"
11
12#ifdef PROTOCOL_LUFA
13# include "lufa.h"
14#endif
15#ifdef PROTOCOL_VUSB
16# include "vusb.h"
17#endif
18
19/** \brief Suspend idle
20 *
21 * FIXME: needs doc
22 */
23void suspend_idle(uint8_t time) {
24 cli();
25 set_sleep_mode(SLEEP_MODE_IDLE);
26 sleep_enable();
27 sei();
28 sleep_cpu();
29 sleep_disable();
30}
31
32// TODO: This needs some cleanup
33
34#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
35
36// clang-format off
37#define wdt_intr_enable(value) \
38__asm__ __volatile__ ( \
39 "in __tmp_reg__,__SREG__" "\n\t" \
40 "cli" "\n\t" \
41 "wdr" "\n\t" \
42 "sts %0,%1" "\n\t" \
43 "out __SREG__,__tmp_reg__" "\n\t" \
44 "sts %0,%2" "\n\t" \
45 : /* no outputs */ \
46 : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
47 "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
48 "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | _BV(WDIE) | (value & 0x07))) \
49 : "r0" \
50)
51// clang-format on
52
53/** \brief Power down MCU with watchdog timer
54 *
55 * wdto: watchdog timer timeout defined in <avr/wdt.h>
56 * WDTO_15MS
57 * WDTO_30MS
58 * WDTO_60MS
59 * WDTO_120MS
60 * WDTO_250MS
61 * WDTO_500MS
62 * WDTO_1S
63 * WDTO_2S
64 * WDTO_4S
65 * WDTO_8S
66 */
67static uint8_t wdt_timeout = 0;
68
69/** \brief Power down
70 *
71 * FIXME: needs doc
72 */
73static void power_down(uint8_t wdto) {
74 wdt_timeout = wdto;
75
76 // Watchdog Interrupt Mode
77 wdt_intr_enable(wdto);
78
79 // TODO: more power saving
80 // See PicoPower application note
81 // - I/O port input with pullup
82 // - prescale clock
83 // - BOD disable
84 // - Power Reduction Register PRR
85 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
86 sleep_enable();
87 sei();
88 sleep_cpu();
89 sleep_disable();
90
91 // Disable watchdog after sleep
92 wdt_disable();
93}
94#endif
95
96/** \brief Suspend power down
97 *
98 * FIXME: needs doc
99 */
100void suspend_power_down(void) {
101#ifdef PROTOCOL_LUFA
102 if (USB_DeviceState == DEVICE_STATE_Configured) return;
103#endif
104#ifdef PROTOCOL_VUSB
105 if (!vusb_suspended) return;
106#endif
107
108 suspend_power_down_quantum();
109
110#ifndef NO_SUSPEND_POWER_DOWN
111 // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt)
112# if defined(WDT_vect)
113 power_down(WDTO_15MS);
114# endif
115#endif
116}
117
118__attribute__((weak)) void matrix_power_up(void) {}
119__attribute__((weak)) void matrix_power_down(void) {}
120bool suspend_wakeup_condition(void) {
121 matrix_power_up();
122 matrix_scan();
123 matrix_power_down();
124 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
125 if (matrix_get_row(r)) return true;
126 }
127 return false;
128}
129
130/** \brief run immediately after wakeup
131 *
132 * FIXME: needs doc
133 */
134void suspend_wakeup_init(void) {
135 // clear keyboard state
136 clear_keyboard();
137
138 suspend_wakeup_init_quantum();
139}
140
141#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
142/* watchdog timeout */
143ISR(WDT_vect) {
144 // compensate timer for sleep
145 switch (wdt_timeout) {
146 case WDTO_15MS:
147 timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
148 break;
149 default:;
150 }
151}
152#endif
diff --git a/platforms/avr/timer.c b/platforms/avr/timer.c
new file mode 100644
index 000000000..c2e6c6e08
--- /dev/null
+++ b/platforms/avr/timer.c
@@ -0,0 +1,133 @@
1/*
2Copyright 2011 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <avr/io.h>
19#include <avr/interrupt.h>
20#include <util/atomic.h>
21#include <stdint.h>
22#include "timer_avr.h"
23#include "timer.h"
24
25// counter resolution 1ms
26// NOTE: union { uint32_t timer32; struct { uint16_t dummy; uint16_t timer16; }}
27volatile uint32_t timer_count;
28
29/** \brief timer initialization
30 *
31 * FIXME: needs doc
32 */
33void timer_init(void) {
34#if TIMER_PRESCALER == 1
35 uint8_t prescaler = _BV(CS00);
36#elif TIMER_PRESCALER == 8
37 uint8_t prescaler = _BV(CS01);
38#elif TIMER_PRESCALER == 64
39 uint8_t prescaler = _BV(CS00) | _BV(CS01);
40#elif TIMER_PRESCALER == 256
41 uint8_t prescaler = _BV(CS02);
42#elif TIMER_PRESCALER == 1024
43 uint8_t prescaler = _BV(CS00) | _BV(CS02);
44#else
45# error "Timer prescaler value is not valid"
46#endif
47
48#if defined(__AVR_ATmega32A__)
49 // Timer0 CTC mode
50 TCCR0 = _BV(WGM01) | prescaler;
51
52 OCR0 = TIMER_RAW_TOP;
53 TIMSK = _BV(OCIE0);
54#elif defined(__AVR_ATtiny85__)
55 // Timer0 CTC mode
56 TCCR0A = _BV(WGM01);
57 TCCR0B = prescaler;
58
59 OCR0A = TIMER_RAW_TOP;
60 TIMSK = _BV(OCIE0A);
61#else
62 // Timer0 CTC mode
63 TCCR0A = _BV(WGM01);
64 TCCR0B = prescaler;
65
66 OCR0A = TIMER_RAW_TOP;
67 TIMSK0 = _BV(OCIE0A);
68#endif
69}
70
71/** \brief timer clear
72 *
73 * FIXME: needs doc
74 */
75inline void timer_clear(void) {
76 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { timer_count = 0; }
77}
78
79/** \brief timer read
80 *
81 * FIXME: needs doc
82 */
83inline uint16_t timer_read(void) {
84 uint32_t t;
85
86 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; }
87
88 return (t & 0xFFFF);
89}
90
91/** \brief timer read32
92 *
93 * FIXME: needs doc
94 */
95inline uint32_t timer_read32(void) {
96 uint32_t t;
97
98 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; }
99
100 return t;
101}
102
103/** \brief timer elapsed
104 *
105 * FIXME: needs doc
106 */
107inline uint16_t timer_elapsed(uint16_t last) {
108 uint32_t t;
109
110 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; }
111
112 return TIMER_DIFF_16((t & 0xFFFF), last);
113}
114
115/** \brief timer elapsed32
116 *
117 * FIXME: needs doc
118 */
119inline uint32_t timer_elapsed32(uint32_t last) {
120 uint32_t t;
121
122 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { t = timer_count; }
123
124 return TIMER_DIFF_32(t, last);
125}
126
127// excecuted once per 1ms.(excess for just timer count?)
128#ifndef __AVR_ATmega32A__
129# define TIMER_INTERRUPT_VECTOR TIMER0_COMPA_vect
130#else
131# define TIMER_INTERRUPT_VECTOR TIMER0_COMP_vect
132#endif
133ISR(TIMER_INTERRUPT_VECTOR, ISR_NOBLOCK) { timer_count++; }
diff --git a/platforms/avr/timer_avr.h b/platforms/avr/timer_avr.h
new file mode 100644
index 000000000..c1b726bd0
--- /dev/null
+++ b/platforms/avr/timer_avr.h
@@ -0,0 +1,39 @@
1/*
2Copyright 2011 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdint.h>
21
22#ifndef TIMER_PRESCALER
23# if F_CPU > 16000000
24# define TIMER_PRESCALER 256
25# elif F_CPU > 2000000
26# define TIMER_PRESCALER 64
27# elif F_CPU > 250000
28# define TIMER_PRESCALER 8
29# else
30# define TIMER_PRESCALER 1
31# endif
32#endif
33#define TIMER_RAW_FREQ (F_CPU / TIMER_PRESCALER)
34#define TIMER_RAW TCNT0
35#define TIMER_RAW_TOP (TIMER_RAW_FREQ / 1000)
36
37#if (TIMER_RAW_TOP > 255)
38# error "Timer0 can't count 1ms at this clock freq. Use larger prescaler."
39#endif
diff --git a/platforms/avr/xprintf.S b/platforms/avr/xprintf.S
new file mode 100644
index 000000000..c5a414c35
--- /dev/null
+++ b/platforms/avr/xprintf.S
@@ -0,0 +1,498 @@
1;---------------------------------------------------------------------------;
2; Extended itoa, puts, printf and atoi (C)ChaN, 2011
3;---------------------------------------------------------------------------;
4
5 // Base size is 152 bytes
6#define CR_CRLF 0 // Convert \n to \r\n (+10 bytes)
7#define USE_XPRINTF 1 // Enable xprintf function (+194 bytes)
8#define USE_XSPRINTF 0 // Add xsprintf function (+78 bytes)
9#define USE_XFPRINTF 0 // Add xfprintf function (+54 bytes)
10#define USE_XATOI 0 // Enable xatoi function (+182 bytes)
11
12
13#if FLASHEND > 0x1FFFF
14#error xitoa module does not support 256K devices
15#endif
16
17.nolist
18#include <avr/io.h> // Include device specific definitions.
19.list
20
21#ifdef SPM_PAGESIZE // Recent devices have "lpm Rd,Z+" and "movw".
22.macro _LPMI reg
23 lpm \reg, Z+
24.endm
25.macro _MOVW dh,dl, sh,sl
26 movw \dl, \sl
27.endm
28#else // Earlier devices do not have "lpm Rd,Z+" nor "movw".
29.macro _LPMI reg
30 lpm
31 mov \reg, r0
32 adiw ZL, 1
33.endm
34.macro _MOVW dh,dl, sh,sl
35 mov \dl, \sl
36 mov \dh, \sh
37.endm
38#endif
39
40
41
42;---------------------------------------------------------------------------
43; Stub function to forward to user output function
44;
45;Prototype: void xputc (char chr // a character to be output
46; );
47;Size: 12/12 words
48
49.section .bss
50.global xfunc_out ; xfunc_out must be initialized before using this module.
51xfunc_out: .ds.w 1
52.section .text
53
54
55.func xputc
56.global xputc
57xputc:
58#if CR_CRLF
59 cpi r24, 10 ;LF --> CRLF
60 brne 1f ;
61 ldi r24, 13 ;
62 rcall 1f ;
63 ldi r24, 10 ;/
641:
65#endif
66 push ZH
67 push ZL
68 lds ZL, xfunc_out+0 ;Pointer to the registered output function.
69 lds ZH, xfunc_out+1 ;/
70 sbiw ZL, 0 ;Skip if null
71 breq 2f ;/
72 icall
732: pop ZL
74 pop ZH
75 ret
76.endfunc
77
78
79
80;---------------------------------------------------------------------------
81; Direct ROM string output
82;
83;Prototype: void xputs (const char *str_p // rom string to be output
84; );
85
86.func xputs
87.global xputs
88xputs:
89 _MOVW ZH,ZL, r25,r24 ; Z = pointer to rom string
901: _LPMI r24
91 cpi r24, 0
92 breq 2f
93 rcall xputc
94 rjmp 1b
952: ret
96.endfunc
97
98
99;---------------------------------------------------------------------------
100; Extended direct numeral string output (32bit version)
101;
102;Prototype: void xitoa (long value, // value to be output
103; char radix, // radix
104; char width); // minimum width
105;
106
107.func xitoa
108.global xitoa
109xitoa:
110 ;r25:r22 = value, r20 = base, r18 = digits
111 clr r31 ;r31 = stack level
112 ldi r30, ' ' ;r30 = sign
113 ldi r19, ' ' ;r19 = filler
114 sbrs r20, 7 ;When base indicates signd format and the value
115 rjmp 0f ;is minus, add a '-'.
116 neg r20 ;
117 sbrs r25, 7 ;
118 rjmp 0f ;
119 ldi r30, '-' ;
120 com r22 ;
121 com r23 ;
122 com r24 ;
123 com r25 ;
124 adc r22, r1 ;
125 adc r23, r1 ;
126 adc r24, r1 ;
127 adc r25, r1 ;/
1280: sbrs r18, 7 ;When digits indicates zero filled,
129 rjmp 1f ;filler is '0'.
130 neg r18 ;
131 ldi r19, '0' ;/
132 ;----- string conversion loop
1331: ldi r21, 32 ;r26 = r25:r22 % r20
134 clr r26 ;r25:r22 /= r20
1352: lsl r22 ;
136 rol r23 ;
137 rol r24 ;
138 rol r25 ;
139 rol r26 ;
140 cp r26, r20 ;
141 brcs 3f ;
142 sub r26, r20 ;
143 inc r22 ;
1443: dec r21 ;
145 brne 2b ;/
146 cpi r26, 10 ;r26 is a numeral digit '0'-'F'
147 brcs 4f ;
148 subi r26, -7 ;
1494: subi r26, -'0' ;/
150 push r26 ;Stack it
151 inc r31 ;/
152 cp r22, r1 ;Repeat until r25:r22 gets zero
153 cpc r23, r1 ;
154 cpc r24, r1 ;
155 cpc r25, r1 ;
156 brne 1b ;/
157
158 cpi r30, '-' ;Minus sign if needed
159 brne 5f ;
160 push r30 ;
161 inc r31 ;/
1625: cp r31, r18 ;Filler
163 brcc 6f ;
164 push r19 ;
165 inc r31 ;
166 rjmp 5b ;/
167
1686: pop r24 ;Flush stacked digits and exit
169 rcall xputc ;
170 dec r31 ;
171 brne 6b ;/
172
173 ret
174.endfunc
175
176
177
178;---------------------------------------------------------------------------;
179; Formatted string output (16/32bit version)
180;
181;Prototype:
182; void __xprintf (const char *format_p, ...);
183; void __xsprintf(char*, const char *format_p, ...);
184; void __xfprintf(void(*func)(char), const char *format_p, ...);
185;
186
187#if USE_XPRINTF
188
189.func xvprintf
190xvprintf:
191 ld ZL, Y+ ;Z = pointer to format string
192 ld ZH, Y+ ;/
193
1940: _LPMI r24 ;Get a format char
195 cpi r24, 0 ;End of format string?
196 breq 90f ;/
197 cpi r24, '%' ;Is format?
198 breq 20f ;/
1991: rcall xputc ;Put a normal character
200 rjmp 0b ;/
20190: ret
202
20320: ldi r18, 0 ;r18: digits
204 clt ;T: filler
205 _LPMI r21 ;Get flags
206 cpi r21, '%' ;Is a %?
207 breq 1b ;/
208 cpi r21, '0' ;Zero filled?
209 brne 23f ;
210 set ;/
21122: _LPMI r21 ;Get width
21223: cpi r21, '9'+1 ;
213 brcc 24f ;
214 subi r21, '0' ;
215 brcs 90b ;
216 lsl r18 ;
217 mov r0, r18 ;
218 lsl r18 ;
219 lsl r18 ;
220 add r18, r0 ;
221 add r18, r21 ;
222 rjmp 22b ;/
223
22424: brtc 25f ;get value (low word)
225 neg r18 ;
22625: ld r24, Y+ ;
227 ld r25, Y+ ;/
228 cpi r21, 'c' ;Is type character?
229 breq 1b ;/
230 cpi r21, 's' ;Is type RAM string?
231 breq 50f ;/
232 cpi r21, 'S' ;Is type ROM string?
233 breq 60f ;/
234 _MOVW r23,r22,r25,r24 ;r25:r22 = value
235 clr r24 ;
236 clr r25 ;
237 clt ;/
238 cpi r21, 'l' ;Is long int?
239 brne 26f ;
240 ld r24, Y+ ;get value (high word)
241 ld r25, Y+ ;
242 set ;
243 _LPMI r21 ;/
24426: cpi r21, 'd' ;Is type signed decimal?
245 brne 27f ;/
246 ldi r20, -10 ;
247 brts 40f ;
248 sbrs r23, 7 ;
249 rjmp 40f ;
250 ldi r24, -1 ;
251 ldi r25, -1 ;
252 rjmp 40f ;/
25327: cpi r21, 'u' ;Is type unsigned decimal?
254 ldi r20, 10 ;
255 breq 40f ;/
256 cpi r21, 'X' ;Is type hexdecimal?
257 ldi r20, 16 ;
258 breq 40f ;/
259 cpi r21, 'b' ;Is type binary?
260 ldi r20, 2 ;
261 breq 40f ;/
262 ret ;abort
26340: push ZH ;Output the value
264 push ZL ;
265 rcall xitoa ;
26642: pop ZL ;
267 pop ZH ;
268 rjmp 0b ;/
269
27050: push ZH ;Put a string on the RAM
271 push ZL
272 _MOVW ZH,ZL, r25,r24
27351: ld r24, Z+
274 cpi r24, 0
275 breq 42b
276 rcall xputc
277 rjmp 51b
278
27960: push ZH ;Put a string on the ROM
280 push ZL
281 rcall xputs
282 rjmp 42b
283.endfunc
284
285
286.func __xprintf
287.global __xprintf
288__xprintf:
289 push YH
290 push YL
291 in YL, _SFR_IO_ADDR(SPL)
292#ifdef SPH
293 in YH, _SFR_IO_ADDR(SPH)
294#else
295 clr YH
296#endif
297 adiw YL, 5 ;Y = pointer to arguments
298 rcall xvprintf
299 pop YL
300 pop YH
301 ret
302.endfunc
303
304
305#if USE_XSPRINTF
306
307.func __xsprintf
308putram:
309 _MOVW ZH,ZL, r15,r14
310 st Z+, r24
311 _MOVW r15,r14, ZH,ZL
312 ret
313.global __xsprintf
314__xsprintf:
315 push YH
316 push YL
317 in YL, _SFR_IO_ADDR(SPL)
318#ifdef SPH
319 in YH, _SFR_IO_ADDR(SPH)
320#else
321 clr YH
322#endif
323 adiw YL, 5 ;Y = pointer to arguments
324 lds ZL, xfunc_out+0 ;Save registered output function
325 lds ZH, xfunc_out+1 ;
326 push ZL ;
327 push ZH ;/
328 ldi ZL, lo8(pm(putram));Set local output function
329 ldi ZH, hi8(pm(putram));
330 sts xfunc_out+0, ZL ;
331 sts xfunc_out+1, ZH ;/
332 push r15 ;Initialize pointer to string buffer
333 push r14 ;
334 ld r14, Y+ ;
335 ld r15, Y+ ;/
336 rcall xvprintf
337 _MOVW ZH,ZL, r15,r14 ;Terminate string
338 st Z, r1 ;
339 pop r14 ;
340 pop r15 ;/
341 pop ZH ;Restore registered output function
342 pop ZL ;
343 sts xfunc_out+0, ZL ;
344 sts xfunc_out+1, ZH ;/
345 pop YL
346 pop YH
347 ret
348.endfunc
349#endif
350
351
352#if USE_XFPRINTF
353.func __xfprintf
354.global __xfprintf
355__xfprintf:
356 push YH
357 push YL
358 in YL, _SFR_IO_ADDR(SPL)
359#ifdef SPH
360 in YH, _SFR_IO_ADDR(SPH)
361#else
362 clr YH
363#endif
364 adiw YL, 5 ;Y = pointer to arguments
365 lds ZL, xfunc_out+0 ;Save registered output function
366 lds ZH, xfunc_out+1 ;
367 push ZL ;
368 push ZH ;/
369 ld ZL, Y+ ;Set output function
370 ld ZH, Y+ ;
371 sts xfunc_out+0, ZL ;
372 sts xfunc_out+1, ZH ;/
373 rcall xvprintf
374 pop ZH ;Restore registered output function
375 pop ZL ;
376 sts xfunc_out+0, ZL ;
377 sts xfunc_out+1, ZH ;/
378 pop YL
379 pop YH
380 ret
381.endfunc
382#endif
383
384#endif
385
386
387
388;---------------------------------------------------------------------------
389; Extended numeral string input
390;
391;Prototype:
392; char xatoi ( /* 1: Successful, 0: Failed */
393; const char **str, /* pointer to pointer to source string */
394; long *res /* result */
395; );
396;
397
398
399#if USE_XATOI
400.func xatoi
401.global xatoi
402xatoi:
403 _MOVW r1, r0, r23, r22
404 _MOVW XH, XL, r25, r24
405 ld ZL, X+
406 ld ZH, X+
407 clr r18 ;r21:r18 = 0;
408 clr r19 ;
409 clr r20 ;
410 clr r21 ;/
411 clt ;T = 0;
412
413 ldi r25, 10 ;r25 = 10;
414 rjmp 41f ;/
41540: adiw ZL, 1 ;Z++;
41641: ld r22, Z ;r22 = *Z;
417 cpi r22, ' ' ;if(r22 == ' ') continue
418 breq 40b ;/
419 brcs 70f ;if(r22 < ' ') error;
420 cpi r22, '-' ;if(r22 == '-') {
421 brne 42f ; T = 1;
422 set ; continue;
423 rjmp 40b ;}
42442: cpi r22, '9'+1 ;if(r22 > '9') error;
425 brcc 70f ;/
426 cpi r22, '0' ;if(r22 < '0') error;
427 brcs 70f ;/
428 brne 51f ;if(r22 > '0') cv_start;
429 ldi r25, 8 ;r25 = 8;
430 adiw ZL, 1 ;r22 = *(++Z);
431 ld r22, Z ;/
432 cpi r22, ' '+1 ;if(r22 <= ' ') exit;
433 brcs 80f ;/
434 cpi r22, 'b' ;if(r22 == 'b') {
435 brne 43f ; r25 = 2;
436 ldi r25, 2 ; cv_start;
437 rjmp 50f ;}
43843: cpi r22, 'x' ;if(r22 != 'x') error;
439 brne 51f ;/
440 ldi r25, 16 ;r25 = 16;
441
44250: adiw ZL, 1 ;Z++;
443 ld r22, Z ;r22 = *Z;
44451: cpi r22, ' '+1 ;if(r22 <= ' ') break;
445 brcs 80f ;/
446 cpi r22, 'a' ;if(r22 >= 'a') r22 =- 0x20;
447 brcs 52f ;
448 subi r22, 0x20 ;/
44952: subi r22, '0' ;if((r22 -= '0') < 0) error;
450 brcs 70f ;/
451 cpi r22, 10 ;if(r22 >= 10) {
452 brcs 53f ; r22 -= 7;
453 subi r22, 7 ; if(r22 < 10)
454 cpi r22, 10 ;
455 brcs 70f ;}
45653: cp r22, r25 ;if(r22 >= r25) error;
457 brcc 70f ;/
45860: ldi r24, 33 ;r21:r18 *= r25;
459 sub r23, r23 ;
46061: brcc 62f ;
461 add r23, r25 ;
46262: lsr r23 ;
463 ror r21 ;
464 ror r20 ;
465 ror r19 ;
466 ror r18 ;
467 dec r24 ;
468 brne 61b ;/
469 add r18, r22 ;r21:r18 += r22;
470 adc r19, r24 ;
471 adc r20, r24 ;
472 adc r21, r24 ;/
473 rjmp 50b ;repeat
474
47570: ldi r24, 0
476 rjmp 81f
47780: ldi r24, 1
47881: brtc 82f
479 clr r22
480 com r18
481 com r19
482 com r20
483 com r21
484 adc r18, r22
485 adc r19, r22
486 adc r20, r22
487 adc r21, r22
48882: st -X, ZH
489 st -X, ZL
490 _MOVW XH, XL, r1, r0
491 st X+, r18
492 st X+, r19
493 st X+, r20
494 st X+, r21
495 clr r1
496 ret
497.endfunc
498#endif
diff --git a/platforms/avr/xprintf.h b/platforms/avr/xprintf.h
new file mode 100644
index 000000000..80834f171
--- /dev/null
+++ b/platforms/avr/xprintf.h
@@ -0,0 +1,103 @@
1/*---------------------------------------------------------------------------
2 Extended itoa, puts and printf (C)ChaN, 2011
3-----------------------------------------------------------------------------*/
4
5#pragma once
6
7#include <inttypes.h>
8#include <avr/pgmspace.h>
9
10#ifdef __cplusplus
11extern "C" {
12#endif
13
14extern void (*xfunc_out)(uint8_t);
15#define xdev_out(func) xfunc_out = (void (*)(uint8_t))(func)
16
17/* This is a pointer to user defined output function. It must be initialized
18 before using this modle.
19*/
20
21void xputc(char chr);
22
23/* This is a stub function to forward outputs to user defined output function.
24 All outputs from this module are output via this function.
25*/
26
27/*-----------------------------------------------------------------------------*/
28void xputs(const char *string_p);
29
30/* The string placed in the ROM is forwarded to xputc() directly.
31 */
32
33/*-----------------------------------------------------------------------------*/
34void xitoa(long value, char radix, char width);
35
36/* Extended itoa().
37
38 value radix width output
39 100 10 6 " 100"
40 100 10 -6 "000100"
41 100 10 0 "100"
42 4294967295 10 0 "4294967295"
43 4294967295 -10 0 "-1"
44 655360 16 -8 "000A0000"
45 1024 16 0 "400"
46 0x55 2 -8 "01010101"
47*/
48
49/*-----------------------------------------------------------------------------*/
50#define xprintf(format, ...) __xprintf(PSTR(format), ##__VA_ARGS__)
51#define xsprintf(str, format, ...) __xsprintf(str, PSTR(format), ##__VA_ARGS__)
52#define xfprintf(func, format, ...) __xfprintf(func, PSTR(format), ##__VA_ARGS__)
53
54void __xprintf(const char *format_p, ...); /* Send formatted string to the registered device */
55// void __xsprintf(char*, const char *format_p, ...); /* Put formatted string to the memory */
56// void __xfprintf(void(*func)(uint8_t), const char *format_p, ...); /* Send formatted string to the specified device */
57
58/* Format string is placed in the ROM. The format flags is similar to printf().
59
60 %[flag][width][size]type
61
62 flag
63 A '0' means filled with '0' when output is shorter than width.
64 ' ' is used in default. This is effective only numeral type.
65 width
66 Minimum width in decimal number. This is effective only numeral type.
67 Default width is zero.
68 size
69 A 'l' means the argument is long(32bit). Default is short(16bit).
70 This is effective only numeral type.
71 type
72 'c' : Character, argument is the value
73 's' : String placed on the RAM, argument is the pointer
74 'S' : String placed on the ROM, argument is the pointer
75 'd' : Signed decimal, argument is the value
76 'u' : Unsigned decimal, argument is the value
77 'X' : Hexdecimal, argument is the value
78 'b' : Binary, argument is the value
79 '%' : '%'
80
81*/
82
83/*-----------------------------------------------------------------------------*/
84char xatoi(char **str, long *ret);
85
86/* Get value of the numeral string.
87
88 str
89 Pointer to pointer to source string
90
91 "0b11001010" binary
92 "0377" octal
93 "0xff800" hexdecimal
94 "1250000" decimal
95 "-25000" decimal
96
97 ret
98 Pointer to return value
99*/
100
101#ifdef __cplusplus
102}
103#endif