diff options
Diffstat (limited to 'platforms/avr')
-rw-r--r-- | platforms/avr/_print.h | 33 | ||||
-rw-r--r-- | platforms/avr/_timer.h | 19 | ||||
-rw-r--r-- | platforms/avr/_wait.h | 49 | ||||
-rw-r--r-- | platforms/avr/atomic_util.h | 22 | ||||
-rw-r--r-- | platforms/avr/bootloader.c | 293 | ||||
-rw-r--r-- | platforms/avr/bootloader_size.c | 21 | ||||
-rw-r--r-- | platforms/avr/gpio.h | 49 | ||||
-rw-r--r-- | platforms/avr/pin_defs.h | 128 | ||||
-rw-r--r-- | platforms/avr/platform.c | 21 | ||||
-rw-r--r-- | platforms/avr/platform.mk | 179 | ||||
-rw-r--r-- | platforms/avr/platform_deps.h | 20 | ||||
-rw-r--r-- | platforms/avr/printf.c | 20 | ||||
-rw-r--r-- | platforms/avr/printf.mk | 2 | ||||
-rw-r--r-- | platforms/avr/sleep_led.c | 124 | ||||
-rw-r--r-- | platforms/avr/suspend.c | 152 | ||||
-rw-r--r-- | platforms/avr/timer.c | 133 | ||||
-rw-r--r-- | platforms/avr/timer_avr.h | 39 | ||||
-rw-r--r-- | platforms/avr/xprintf.S | 498 | ||||
-rw-r--r-- | platforms/avr/xprintf.h | 103 |
19 files changed, 1905 insertions, 0 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) | ||
59 | uint16_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 | ||
74 | uint32_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() */ | ||
272 | void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3"))); | ||
273 | void 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 | ||
21 | AVR_SIZE: FLASHEND + 1 - BOOTLOADER_SIZE | ||
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 | |||
21 | typedef 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 | |||
40 | typedef 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 | |||
19 | void 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 | # | ||
5 | CC = $(CC_PREFIX) avr-gcc | ||
6 | OBJCOPY = avr-objcopy | ||
7 | OBJDUMP = avr-objdump | ||
8 | SIZE = avr-size | ||
9 | AR = avr-ar | ||
10 | NM = avr-nm | ||
11 | HEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature | ||
12 | EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) | ||
13 | BIN = | ||
14 | |||
15 | COMPILEFLAGS += -funsigned-char | ||
16 | COMPILEFLAGS += -funsigned-bitfields | ||
17 | COMPILEFLAGS += -ffunction-sections | ||
18 | COMPILEFLAGS += -fdata-sections | ||
19 | COMPILEFLAGS += -fpack-struct | ||
20 | COMPILEFLAGS += -fshort-enums | ||
21 | |||
22 | ASFLAGS += $(AVR_ASFLAGS) | ||
23 | |||
24 | CFLAGS += $(COMPILEFLAGS) $(AVR_CFLAGS) | ||
25 | CFLAGS += -fno-inline-small-functions | ||
26 | CFLAGS += -fno-strict-aliasing | ||
27 | |||
28 | CXXFLAGS += $(COMPILEFLAGS) | ||
29 | CXXFLAGS += -fno-exceptions -std=c++11 | ||
30 | |||
31 | LDFLAGS +=-Wl,--gc-sections | ||
32 | |||
33 | OPT_DEFS += -DF_CPU=$(F_CPU)UL | ||
34 | |||
35 | MCUFLAGS = -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. | ||
41 | EXTRALIBDIRS = | ||
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 | |||
54 | EXTMEMOPTS = | ||
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. | ||
62 | DEBUG = dwarf-2 | ||
63 | |||
64 | # For simulavr only - target MCU frequency. | ||
65 | DEBUG_MFREQ = $(F_CPU) | ||
66 | |||
67 | # Set the DEBUG_UI to either gdb or insight. | ||
68 | # DEBUG_UI = gdb | ||
69 | DEBUG_UI = insight | ||
70 | |||
71 | # Set the debugging back-end to either avarice, simulavr. | ||
72 | DEBUG_BACKEND = avarice | ||
73 | #DEBUG_BACKEND = simulavr | ||
74 | |||
75 | # GDB Init Filename. | ||
76 | GDBINIT_FILE = __avr_gdbinit | ||
77 | |||
78 | # When using avarice settings for the JTAG | ||
79 | JTAG_DEV = /dev/com1 | ||
80 | |||
81 | # Debugging port used to communicate between GDB / avarice / simulavr. | ||
82 | DEBUG_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. | ||
87 | DEBUG_HOST = localhost | ||
88 | |||
89 | #============================================================================ | ||
90 | |||
91 | # Convert hex to bin. | ||
92 | bin: $(BUILD_DIR)/$(TARGET).hex | ||
93 | ifeq ($(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) | ||
96 | else | ||
97 | $(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin | ||
98 | endif | ||
99 | $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin; | ||
100 | |||
101 | # copy bin to FLASH.bin | ||
102 | flashbin: 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(). | ||
108 | gdb-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) | ||
115 | ifeq ($(DEBUG_BACKEND),simulavr) | ||
116 | @echo load >> $(GDBINIT_FILE) | ||
117 | endif | ||
118 | @echo break main >> $(GDBINIT_FILE) | ||
119 | |||
120 | debug: gdb-config $(BUILD_DIR)/$(TARGET).elf | ||
121 | ifeq ($(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 | |||
127 | else | ||
128 | @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \ | ||
129 | $(DEBUG_MFREQ) --port $(DEBUG_PORT) | ||
130 | endif | ||
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. | ||
137 | COFFCONVERT = $(OBJCOPY) --debugging | ||
138 | COFFCONVERT += --change-section-address .data-0x800000 | ||
139 | COFFCONVERT += --change-section-address .bss-0x800000 | ||
140 | COFFCONVERT += --change-section-address .noinit-0x800000 | ||
141 | COFFCONVERT += --change-section-address .eeprom-0x810000 | ||
142 | |||
143 | |||
144 | |||
145 | coff: $(BUILD_DIR)/$(TARGET).elf | ||
146 | @$(SECHO) $(MSG_COFF) $(BUILD_DIR)/$(TARGET).cof | ||
147 | $(COFFCONVERT) -O coff-avr $< $(BUILD_DIR)/$(TARGET).cof | ||
148 | |||
149 | |||
150 | extcoff: $(BUILD_DIR)/$(TARGET).elf | ||
151 | @$(SECHO) $(MSG_EXTENDED_COFF) $(BUILD_DIR)/$(TARGET).cof | ||
152 | $(COFFCONVERT) -O coff-ext-avr $< $(BUILD_DIR)/$(TARGET).cof | ||
153 | |||
154 | ifeq ($(strip $(BOOTLOADER)), qmk-dfu) | ||
155 | QMK_BOOTLOADER_TYPE = DFU | ||
156 | else ifeq ($(strip $(BOOTLOADER)), qmk-hid) | ||
157 | QMK_BOOTLOADER_TYPE = HID | ||
158 | endif | ||
159 | |||
160 | bootloader: | ||
161 | ifeq ($(strip $(QMK_BOOTLOADER_TYPE)),) | ||
162 | $(error Please set BOOTLOADER to "qmk-dfu" or "qmk-hid" first!) | ||
163 | else | ||
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 | ||
173 | endif | ||
174 | |||
175 | production: $(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 | /* | ||
2 | Copyright 2011 Jun Wako <wakojun@gmail.com> | ||
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 | #include "xprintf.h" | ||
18 | #include "sendchar.h" | ||
19 | |||
20 | void 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 @@ | |||
1 | TMK_COMMON_SRC += $(PLATFORM_COMMON_DIR)/xprintf.S | ||
2 | TMK_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 | ||
29 | error("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 | */ | ||
49 | void 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 | */ | ||
66 | void 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 | */ | ||
75 | void 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 | */ | ||
84 | void 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 | */ | ||
96 | static 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 | |||
98 | ISR(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 | */ | ||
23 | void 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 | */ | ||
67 | static uint8_t wdt_timeout = 0; | ||
68 | |||
69 | /** \brief Power down | ||
70 | * | ||
71 | * FIXME: needs doc | ||
72 | */ | ||
73 | static 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 | */ | ||
100 | void 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) {} | ||
120 | bool 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 | */ | ||
134 | void 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 */ | ||
143 | ISR(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 | /* | ||
2 | Copyright 2011 Jun Wako <wakojun@gmail.com> | ||
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 | #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; }} | ||
27 | volatile uint32_t timer_count; | ||
28 | |||
29 | /** \brief timer initialization | ||
30 | * | ||
31 | * FIXME: needs doc | ||
32 | */ | ||
33 | void 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 | */ | ||
75 | inline void timer_clear(void) { | ||
76 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { timer_count = 0; } | ||
77 | } | ||
78 | |||
79 | /** \brief timer read | ||
80 | * | ||
81 | * FIXME: needs doc | ||
82 | */ | ||
83 | inline 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 | */ | ||
95 | inline 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 | */ | ||
107 | inline 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 | */ | ||
119 | inline 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 | ||
133 | ISR(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 | /* | ||
2 | Copyright 2011 Jun Wako <wakojun@gmail.com> | ||
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 | #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. | ||
51 | xfunc_out: .ds.w 1 | ||
52 | .section .text | ||
53 | |||
54 | |||
55 | .func xputc | ||
56 | .global xputc | ||
57 | xputc: | ||
58 | #if CR_CRLF | ||
59 | cpi r24, 10 ;LF --> CRLF | ||
60 | brne 1f ; | ||
61 | ldi r24, 13 ; | ||
62 | rcall 1f ; | ||
63 | ldi r24, 10 ;/ | ||
64 | 1: | ||
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 | ||
73 | 2: 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 | ||
88 | xputs: | ||
89 | _MOVW ZH,ZL, r25,r24 ; Z = pointer to rom string | ||
90 | 1: _LPMI r24 | ||
91 | cpi r24, 0 | ||
92 | breq 2f | ||
93 | rcall xputc | ||
94 | rjmp 1b | ||
95 | 2: 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 | ||
109 | xitoa: | ||
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 ;/ | ||
128 | 0: 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 | ||
133 | 1: ldi r21, 32 ;r26 = r25:r22 % r20 | ||
134 | clr r26 ;r25:r22 /= r20 | ||
135 | 2: 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 ; | ||
144 | 3: dec r21 ; | ||
145 | brne 2b ;/ | ||
146 | cpi r26, 10 ;r26 is a numeral digit '0'-'F' | ||
147 | brcs 4f ; | ||
148 | subi r26, -7 ; | ||
149 | 4: 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 ;/ | ||
162 | 5: cp r31, r18 ;Filler | ||
163 | brcc 6f ; | ||
164 | push r19 ; | ||
165 | inc r31 ; | ||
166 | rjmp 5b ;/ | ||
167 | |||
168 | 6: 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 | ||
190 | xvprintf: | ||
191 | ld ZL, Y+ ;Z = pointer to format string | ||
192 | ld ZH, Y+ ;/ | ||
193 | |||
194 | 0: _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 ;/ | ||
199 | 1: rcall xputc ;Put a normal character | ||
200 | rjmp 0b ;/ | ||
201 | 90: ret | ||
202 | |||
203 | 20: 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 ;/ | ||
211 | 22: _LPMI r21 ;Get width | ||
212 | 23: 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 | |||
224 | 24: brtc 25f ;get value (low word) | ||
225 | neg r18 ; | ||
226 | 25: 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 ;/ | ||
244 | 26: 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 ;/ | ||
253 | 27: 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 | ||
263 | 40: push ZH ;Output the value | ||
264 | push ZL ; | ||
265 | rcall xitoa ; | ||
266 | 42: pop ZL ; | ||
267 | pop ZH ; | ||
268 | rjmp 0b ;/ | ||
269 | |||
270 | 50: push ZH ;Put a string on the RAM | ||
271 | push ZL | ||
272 | _MOVW ZH,ZL, r25,r24 | ||
273 | 51: ld r24, Z+ | ||
274 | cpi r24, 0 | ||
275 | breq 42b | ||
276 | rcall xputc | ||
277 | rjmp 51b | ||
278 | |||
279 | 60: 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 | ||
308 | putram: | ||
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 | ||
402 | xatoi: | ||
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 ;/ | ||
415 | 40: adiw ZL, 1 ;Z++; | ||
416 | 41: 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 ;} | ||
424 | 42: 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 ;} | ||
438 | 43: cpi r22, 'x' ;if(r22 != 'x') error; | ||
439 | brne 51f ;/ | ||
440 | ldi r25, 16 ;r25 = 16; | ||
441 | |||
442 | 50: adiw ZL, 1 ;Z++; | ||
443 | ld r22, Z ;r22 = *Z; | ||
444 | 51: 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 ;/ | ||
449 | 52: 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 ;} | ||
456 | 53: cp r22, r25 ;if(r22 >= r25) error; | ||
457 | brcc 70f ;/ | ||
458 | 60: ldi r24, 33 ;r21:r18 *= r25; | ||
459 | sub r23, r23 ; | ||
460 | 61: brcc 62f ; | ||
461 | add r23, r25 ; | ||
462 | 62: 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 | |||
475 | 70: ldi r24, 0 | ||
476 | rjmp 81f | ||
477 | 80: ldi r24, 1 | ||
478 | 81: 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 | ||
488 | 82: 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 | ||
11 | extern "C" { | ||
12 | #endif | ||
13 | |||
14 | extern 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 | |||
21 | void 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 | /*-----------------------------------------------------------------------------*/ | ||
28 | void xputs(const char *string_p); | ||
29 | |||
30 | /* The string placed in the ROM is forwarded to xputc() directly. | ||
31 | */ | ||
32 | |||
33 | /*-----------------------------------------------------------------------------*/ | ||
34 | void 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 | |||
54 | void __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 | /*-----------------------------------------------------------------------------*/ | ||
84 | char 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 | ||