diff options
author | tmk <hasu@tmk-kbd.com> | 2015-04-10 01:32:04 +0900 |
---|---|---|
committer | tmk <hasu@tmk-kbd.com> | 2015-04-10 01:32:04 +0900 |
commit | 1a02ebcc612e9a9c0d87e02295c7258de3a70ccc (patch) | |
tree | e517f3c70bb2d542797e57d13e9023c84af230fb /tmk_core/common/avr | |
parent | 6746e37088ce8ba03529c1226bd216705edb2b1f (diff) | |
parent | a074364c3731d66b56d988c8a6c960a83ea0e0a1 (diff) |
Merge commit 'a074364c3731d66b56d988c8a6c960a83ea0e0a1' as 'tmk_core'
Diffstat (limited to 'tmk_core/common/avr')
-rw-r--r-- | tmk_core/common/avr/bootloader.c | 148 | ||||
-rw-r--r-- | tmk_core/common/avr/eeconfig.c | 45 | ||||
-rw-r--r-- | tmk_core/common/avr/suspend.c | 122 | ||||
-rw-r--r-- | tmk_core/common/avr/suspend_avr.h | 27 | ||||
-rw-r--r-- | tmk_core/common/avr/timer.c | 117 | ||||
-rw-r--r-- | tmk_core/common/avr/timer_avr.h | 42 | ||||
-rw-r--r-- | tmk_core/common/avr/xprintf.S | 500 | ||||
-rw-r--r-- | tmk_core/common/avr/xprintf.h | 111 |
8 files changed, 1112 insertions, 0 deletions
diff --git a/tmk_core/common/avr/bootloader.c b/tmk_core/common/avr/bootloader.c new file mode 100644 index 0000000000..cda295b181 --- /dev/null +++ b/tmk_core/common/avr/bootloader.c @@ -0,0 +1,148 @@ +#include <stdint.h> +#include <stdbool.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/wdt.h> +#include <util/delay.h> +#include "bootloader.h" + +#ifdef PROTOCOL_LUFA +#include <LUFA/Drivers/USB/USB.h> +#endif + + +/* Boot Section Size in *BYTEs* + * Teensy halfKay 512 + * Teensy++ halfKay 1024 + * Atmel DFU loader 4096 + * LUFA bootloader 4096 + * USBaspLoader 2048 + */ +#ifndef BOOTLOADER_SIZE +#warning To use bootloader_jump() you need to define BOOTLOADER_SIZE in config.h. +#define BOOTLOADER_SIZE 4096 +#endif + +#define FLASH_SIZE (FLASHEND + 1L) +#define BOOTLOADER_START (FLASH_SIZE - BOOTLOADER_SIZE) + + +/* + * Entering the Bootloader via Software + * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html + */ +#define BOOTLOADER_RESET_KEY 0xB007B007 +uint32_t reset_key __attribute__ ((section (".noinit"))); + +/* initialize MCU status by watchdog reset */ +void bootloader_jump(void) { +#ifdef PROTOCOL_LUFA + USB_Disable(); + cli(); + _delay_ms(2000); +#endif + +#ifdef PROTOCOL_PJRC + cli(); + UDCON = 1; + USBCON = (1<<FRZCLK); + UCSR1B = 0; + _delay_ms(5); +#endif + + // watchdog reset + reset_key = BOOTLOADER_RESET_KEY; + wdt_enable(WDTO_250MS); + for (;;); +} + + +/* this runs before main() */ +void bootloader_jump_after_watchdog_reset(void) __attribute__ ((used, naked, section (".init3"))); +void bootloader_jump_after_watchdog_reset(void) +{ + if ((MCUSR & (1<<WDRF)) && reset_key == BOOTLOADER_RESET_KEY) { + reset_key = 0; + + // My custom USBasploader requires this to come up. + MCUSR = 0; + + // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog. + MCUSR &= ~(1<<WDRF); + wdt_disable(); + + // This is compled into 'icall', address should be in word unit, not byte. + ((void (*)(void))(BOOTLOADER_START/2))(); + } +} + + +#if 0 +/* Jumping To The Bootloader + * http://www.pjrc.com/teensy/jump_to_bootloader.html + * + * This method doen't work when using LUFA. idk why. + * - needs to initialize more regisers or interrupt setting? + */ +void bootloader_jump(void) { +#ifdef PROTOCOL_LUFA + USB_Disable(); + cli(); + _delay_ms(2000); +#endif + +#ifdef PROTOCOL_PJRC + cli(); + UDCON = 1; + USBCON = (1<<FRZCLK); + UCSR1B = 0; + _delay_ms(5); +#endif + + /* + * Initialize + */ +#if defined(__AVR_AT90USB162__) + EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; + TIMSK0 = 0; TIMSK1 = 0; UCSR1B = 0; + DDRB = 0; DDRC = 0; DDRD = 0; + PORTB = 0; PORTC = 0; PORTD = 0; +#elif defined(__AVR_ATmega32U4__) + EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0; + TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0; + DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0; + PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0; +#elif defined(__AVR_AT90USB646__) + EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0; + TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0; + DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; + PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0; +#elif defined(__AVR_AT90USB1286__) + EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0; + TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; TIMSK3 = 0; UCSR1B = 0; TWCR = 0; + DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; + PORTA = 0; PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0; +#endif + + /* + * USBaspLoader + */ +#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) + // This makes custom USBasploader come up. + MCUSR = 0; + + // initialize ports + PORTB = 0; PORTC= 0; PORTD = 0; + DDRB = 0; DDRC= 0; DDRD = 0; + + // disable interrupts + EIMSK = 0; EECR = 0; SPCR = 0; + ACSR = 0; SPMCSR = 0; WDTCSR = 0; PCICR = 0; + TIMSK0 = 0; TIMSK1 = 0; TIMSK2 = 0; + ADCSRA = 0; TWCR = 0; UCSR0B = 0; +#endif + + // This is compled into 'icall', address should be in word unit, not byte. + ((void (*)(void))(BOOTLOADER_START/2))(); +} +#endif diff --git a/tmk_core/common/avr/eeconfig.c b/tmk_core/common/avr/eeconfig.c new file mode 100644 index 0000000000..5bd47dc6ad --- /dev/null +++ b/tmk_core/common/avr/eeconfig.c @@ -0,0 +1,45 @@ +#include <stdint.h> +#include <stdbool.h> +#include <avr/eeprom.h> +#include "eeconfig.h" + +void eeconfig_init(void) +{ + eeprom_write_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); + eeprom_write_byte(EECONFIG_DEBUG, 0); + eeprom_write_byte(EECONFIG_DEFAULT_LAYER, 0); + eeprom_write_byte(EECONFIG_KEYMAP, 0); + eeprom_write_byte(EECONFIG_MOUSEKEY_ACCEL, 0); +#ifdef BACKLIGHT_ENABLE + eeprom_write_byte(EECONFIG_BACKLIGHT, 0); +#endif +} + +void eeconfig_enable(void) +{ + eeprom_write_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); +} + +void eeconfig_disable(void) +{ + eeprom_write_word(EECONFIG_MAGIC, 0xFFFF); +} + +bool eeconfig_is_enabled(void) +{ + return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); +} + +uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); } +void eeconfig_write_debug(uint8_t val) { eeprom_write_byte(EECONFIG_DEBUG, val); } + +uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); } +void eeconfig_write_default_layer(uint8_t val) { eeprom_write_byte(EECONFIG_DEFAULT_LAYER, val); } + +uint8_t eeconfig_read_keymap(void) { return eeprom_read_byte(EECONFIG_KEYMAP); } +void eeconfig_write_keymap(uint8_t val) { eeprom_write_byte(EECONFIG_KEYMAP, val); } + +#ifdef BACKLIGHT_ENABLE +uint8_t eeconfig_read_backlight(void) { return eeprom_read_byte(EECONFIG_BACKLIGHT); } +void eeconfig_write_backlight(uint8_t val) { eeprom_write_byte(EECONFIG_BACKLIGHT, val); } +#endif diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c new file mode 100644 index 0000000000..80243f02bc --- /dev/null +++ b/tmk_core/common/avr/suspend.c @@ -0,0 +1,122 @@ +#include <stdbool.h> +#include <avr/sleep.h> +#include <avr/wdt.h> +#include <avr/interrupt.h> +#include "matrix.h" +#include "action.h" +#include "backlight.h" +#include "suspend_avr.h" +#include "suspend.h" +#include "timer.h" +#ifdef PROTOCOL_LUFA +#include "lufa.h" +#endif + + +#define wdt_intr_enable(value) \ +__asm__ __volatile__ ( \ + "in __tmp_reg__,__SREG__" "\n\t" \ + "cli" "\n\t" \ + "wdr" "\n\t" \ + "sts %0,%1" "\n\t" \ + "out __SREG__,__tmp_reg__" "\n\t" \ + "sts %0,%2" "\n\t" \ + : /* no outputs */ \ + : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \ + "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \ + "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \ + _BV(WDIE) | (value & 0x07)) ) \ + : "r0" \ +) + + +void suspend_idle(uint8_t time) +{ + cli(); + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_enable(); + sei(); + sleep_cpu(); + sleep_disable(); +} + +/* Power down MCU with watchdog timer + * wdto: watchdog timer timeout defined in <avr/wdt.h> + * WDTO_15MS + * WDTO_30MS + * WDTO_60MS + * WDTO_120MS + * WDTO_250MS + * WDTO_500MS + * WDTO_1S + * WDTO_2S + * WDTO_4S + * WDTO_8S + */ +static uint8_t wdt_timeout = 0; +static void power_down(uint8_t wdto) +{ +#ifdef PROTOCOL_LUFA + if (USB_DeviceState == DEVICE_STATE_Configured) return; +#endif + wdt_timeout = wdto; + + // Watchdog Interrupt Mode + wdt_intr_enable(wdto); + + // TODO: more power saving + // See PicoPower application note + // - I/O port input with pullup + // - prescale clock + // - BOD disable + // - Power Reduction Register PRR + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_enable(); + sei(); + sleep_cpu(); + sleep_disable(); + + // Disable watchdog after sleep + wdt_disable(); +} + +void suspend_power_down(void) +{ + power_down(WDTO_15MS); +} + +bool suspend_wakeup_condition(void) +{ + matrix_power_up(); + matrix_scan(); + matrix_power_down(); + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + if (matrix_get_row(r)) return true; + } + return false; +} + +// run immediately after wakeup +void suspend_wakeup_init(void) +{ + // clear keyboard state + clear_keyboard(); +#ifdef BACKLIGHT_ENABLE + backlight_init(); +#endif +} + +#ifndef NO_SUSPEND_POWER_DOWN +/* watchdog timeout */ +ISR(WDT_vect) +{ + // compensate timer for sleep + switch (wdt_timeout) { + case WDTO_15MS: + timer_count += 15 + 2; // WDTO_15MS + 2(from observation) + break; + default: + ; + } +} +#endif diff --git a/tmk_core/common/avr/suspend_avr.h b/tmk_core/common/avr/suspend_avr.h new file mode 100644 index 0000000000..357102da44 --- /dev/null +++ b/tmk_core/common/avr/suspend_avr.h @@ -0,0 +1,27 @@ +#ifndef SUSPEND_AVR_H +#define SUSPEND_AVR_H + +#include <stdint.h> +#include <stdbool.h> +#include <avr/sleep.h> +#include <avr/wdt.h> +#include <avr/interrupt.h> + + +#define wdt_intr_enable(value) \ +__asm__ __volatile__ ( \ + "in __tmp_reg__,__SREG__" "\n\t" \ + "cli" "\n\t" \ + "wdr" "\n\t" \ + "sts %0,%1" "\n\t" \ + "out __SREG__,__tmp_reg__" "\n\t" \ + "sts %0,%2" "\n\t" \ + : /* no outputs */ \ + : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \ + "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \ + "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \ + _BV(WDIE) | (value & 0x07)) ) \ + : "r0" \ +) + +#endif diff --git a/tmk_core/common/avr/timer.c b/tmk_core/common/avr/timer.c new file mode 100644 index 0000000000..292b41c3a6 --- /dev/null +++ b/tmk_core/common/avr/timer.c @@ -0,0 +1,117 @@ +/* +Copyright 2011 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <stdint.h> +#include "timer_avr.h" +#include "timer.h" + + +// counter resolution 1ms +// NOTE: union { uint32_t timer32; struct { uint16_t dummy; uint16_t timer16; }} +volatile uint32_t timer_count = 0; + +void timer_init(void) +{ + // Timer0 CTC mode + TCCR0A = 0x02; + +#if TIMER_PRESCALER == 1 + TCCR0B = 0x01; +#elif TIMER_PRESCALER == 8 + TCCR0B = 0x02; +#elif TIMER_PRESCALER == 64 + TCCR0B = 0x03; +#elif TIMER_PRESCALER == 256 + TCCR0B = 0x04; +#elif TIMER_PRESCALER == 1024 + TCCR0B = 0x05; +#else +# error "Timer prescaler value is NOT vaild." +#endif + + OCR0A = TIMER_RAW_TOP; + TIMSK0 = (1<<OCIE0A); +} + +inline +void timer_clear(void) +{ + uint8_t sreg = SREG; + cli(); + timer_count = 0; + SREG = sreg; +} + +inline +uint16_t timer_read(void) +{ + uint32_t t; + + uint8_t sreg = SREG; + cli(); + t = timer_count; + SREG = sreg; + + return (t & 0xFFFF); +} + +inline +uint32_t timer_read32(void) +{ + uint32_t t; + + uint8_t sreg = SREG; + cli(); + t = timer_count; + SREG = sreg; + + return t; +} + +inline +uint16_t timer_elapsed(uint16_t last) +{ + uint32_t t; + + uint8_t sreg = SREG; + cli(); + t = timer_count; + SREG = sreg; + + return TIMER_DIFF_16((t & 0xFFFF), last); +} + +inline +uint32_t timer_elapsed32(uint32_t last) +{ + uint32_t t; + + uint8_t sreg = SREG; + cli(); + t = timer_count; + SREG = sreg; + + return TIMER_DIFF_32(t, last); +} + +// excecuted once per 1ms.(excess for just timer count?) +ISR(TIMER0_COMPA_vect) +{ + timer_count++; +} diff --git a/tmk_core/common/avr/timer_avr.h b/tmk_core/common/avr/timer_avr.h new file mode 100644 index 0000000000..0e85eb1017 --- /dev/null +++ b/tmk_core/common/avr/timer_avr.h @@ -0,0 +1,42 @@ +/* +Copyright 2011 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef TIMER_AVR_H +#define TIMER_AVR_H 1 + +#include <stdint.h> + +#ifndef TIMER_PRESCALER +# if F_CPU > 16000000 +# define TIMER_PRESCALER 256 +# elif F_CPU > 2000000 +# define TIMER_PRESCALER 64 +# elif F_CPU > 250000 +# define TIMER_PRESCALER 8 +# else +# define TIMER_PRESCALER 1 +# endif +#endif +#define TIMER_RAW_FREQ (F_CPU/TIMER_PRESCALER) +#define TIMER_RAW TCNT0 +#define TIMER_RAW_TOP (TIMER_RAW_FREQ/1000) + +#if (TIMER_RAW_TOP > 255) +# error "Timer0 can't count 1ms at this clock freq. Use larger prescaler." +#endif + +#endif diff --git a/tmk_core/common/avr/xprintf.S b/tmk_core/common/avr/xprintf.S new file mode 100644 index 0000000000..0cec70ce22 --- /dev/null +++ b/tmk_core/common/avr/xprintf.S @@ -0,0 +1,500 @@ +;---------------------------------------------------------------------------;
+; Extended itoa, puts, printf and atoi (C)ChaN, 2011
+;---------------------------------------------------------------------------;
+
+ // Base size is 152 bytes
+#define CR_CRLF 0 // Convert \n to \r\n (+10 bytes)
+#define USE_XPRINTF 1 // Enable xprintf function (+194 bytes)
+#define USE_XSPRINTF 0 // Add xsprintf function (+78 bytes)
+#define USE_XFPRINTF 0 // Add xfprintf function (+54 bytes)
+#define USE_XATOI 0 // Enable xatoi function (+182 bytes)
+
+
+#if FLASHEND > 0x1FFFF
+#error xitoa module does not support 256K devices
+#endif
+
+.nolist
+#include <avr/io.h> // Include device specific definitions.
+.list
+
+#ifdef SPM_PAGESIZE // Recent devices have "lpm Rd,Z+" and "movw".
+.macro _LPMI reg
+ lpm \reg, Z+
+.endm
+.macro _MOVW dh,dl, sh,sl
+ movw \dl, \sl
+.endm
+#else // Earlier devices do not have "lpm Rd,Z+" nor "movw".
+.macro _LPMI reg
+ lpm
+ mov \reg, r0
+ adiw ZL, 1
+.endm
+.macro _MOVW dh,dl, sh,sl
+ mov \dl, \sl
+ mov \dh, \sh
+.endm
+#endif
+
+
+
+;---------------------------------------------------------------------------
+; Stub function to forward to user output function
+;
+;Prototype: void xputc (char chr // a character to be output
+; );
+;Size: 12/12 words
+
+.section .bss
+.global xfunc_out ; xfunc_out must be initialized before using this module.
+xfunc_out: .ds.w 1
+.section .text
+
+
+.func xputc
+.global xputc
+xputc:
+#if CR_CRLF
+ cpi r24, 10 ;LF --> CRLF
+ brne 1f ;
+ ldi r24, 13 ;
+ rcall 1f ;
+ ldi r24, 10 ;/
+1:
+#endif
+ push ZH
+ push ZL
+ lds ZL, xfunc_out+0 ;Pointer to the registered output function.
+ lds ZH, xfunc_out+1 ;/
+ sbiw ZL, 0 ;Skip if null
+ breq 2f ;/
+ icall
+2: pop ZL
+ pop ZH
+ ret
+.endfunc
+
+
+
+;---------------------------------------------------------------------------
+; Direct ROM string output
+;
+;Prototype: void xputs (const char *str_p // rom string to be output
+; );
+
+.func xputs
+.global xputs
+xputs:
+ _MOVW ZH,ZL, r25,r24 ; Z = pointer to rom string
+1: _LPMI r24
+ cpi r24, 0
+ breq 2f
+ rcall xputc
+ rjmp 1b
+2: ret
+.endfunc
+
+
+;---------------------------------------------------------------------------
+; Extended direct numeral string output (32bit version)
+;
+;Prototype: void xitoa (long value, // value to be output
+; char radix, // radix
+; char width); // minimum width
+;
+
+.func xitoa
+.global xitoa
+xitoa:
+ ;r25:r22 = value, r20 = base, r18 = digits
+ clr r31 ;r31 = stack level
+ ldi r30, ' ' ;r30 = sign
+ ldi r19, ' ' ;r19 = filler
+ sbrs r20, 7 ;When base indicates signd format and the value
+ rjmp 0f ;is minus, add a '-'.
+ neg r20 ;
+ sbrs r25, 7 ;
+ rjmp 0f ;
+ ldi r30, '-' ;
+ com r22 ;
+ com r23 ;
+ com r24 ;
+ com r25 ;
+ adc r22, r1 ;
+ adc r23, r1 ;
+ adc r24, r1 ;
+ adc r25, r1 ;/
+0: sbrs r18, 7 ;When digits indicates zero filled,
+ rjmp 1f ;filler is '0'.
+ neg r18 ;
+ ldi r19, '0' ;/
+ ;----- string conversion loop
+1: ldi r21, 32 ;r26 = r25:r22 % r20
+ clr r26 ;r25:r22 /= r20
+2: lsl r22 ;
+ rol r23 ;
+ rol r24 ;
+ rol r25 ;
+ rol r26 ;
+ cp r26, r20 ;
+ brcs 3f ;
+ sub r26, r20 ;
+ inc r22 ;
+3: dec r21 ;
+ brne 2b ;/
+ cpi r26, 10 ;r26 is a numeral digit '0'-'F'
+ brcs 4f ;
+ subi r26, -7 ;
+4: subi r26, -'0' ;/
+ push r26 ;Stack it
+ inc r31 ;/
+ cp r22, r1 ;Repeat until r25:r22 gets zero
+ cpc r23, r1 ;
+ cpc r24, r1 ;
+ cpc r25, r1 ;
+ brne 1b ;/
+
+ cpi r30, '-' ;Minus sign if needed
+ brne 5f ;
+ push r30 ;
+ inc r31 ;/
+5: cp r31, r18 ;Filler
+ brcc 6f ;
+ push r19 ;
+ inc r31 ;
+ rjmp 5b ;/
+
+6: pop r24 ;Flush stacked digits and exit
+ rcall xputc ;
+ dec r31 ;
+ brne 6b ;/
+
+ ret
+.endfunc
+
+
+
+;---------------------------------------------------------------------------;
+; Formatted string output (16/32bit version)
+;
+;Prototype:
+; void __xprintf (const char *format_p, ...);
+; void __xsprintf(char*, const char *format_p, ...);
+; void __xfprintf(void(*func)(char), const char *format_p, ...);
+;
+
+#if USE_XPRINTF
+
+.func xvprintf
+xvprintf:
+ ld ZL, Y+ ;Z = pointer to format string
+ ld ZH, Y+ ;/
+
+0: _LPMI r24 ;Get a format char
+ cpi r24, 0 ;End of format string?
+ breq 90f ;/
+ cpi r24, '%' ;Is format?
+ breq 20f ;/
+1: rcall xputc ;Put a normal character
+ rjmp 0b ;/
+90: ret
+
+20: ldi r18, 0 ;r18: digits
+ clt ;T: filler
+ _LPMI r21 ;Get flags
+ cpi r21, '%' ;Is a %?
+ breq 1b ;/
+ cpi r21, '0' ;Zero filled?
+ brne 23f ;
+ set ;/
+22: _LPMI r21 ;Get width
+23: cpi r21, '9'+1 ;
+ brcc 24f ;
+ subi r21, '0' ;
+ brcs 90b ;
+ lsl r18 ;
+ mov r0, r18 ;
+ lsl r18 ;
+ lsl r18 ;
+ add r18, r0 ;
+ add r18, r21 ;
+ rjmp 22b ;/
+
+24: brtc 25f ;get value (low word)
+ neg r18 ;
+25: ld r24, Y+ ;
+ ld r25, Y+ ;/
+ cpi r21, 'c' ;Is type character?
+ breq 1b ;/
+ cpi r21, 's' ;Is type RAM string?
+ breq 50f ;/
+ cpi r21, 'S' ;Is type ROM string?
+ breq 60f ;/
+ _MOVW r23,r22,r25,r24 ;r25:r22 = value
+ clr r24 ;
+ clr r25 ;
+ clt ;/
+ cpi r21, 'l' ;Is long int?
+ brne 26f ;
+ ld r24, Y+ ;get value (high word)
+ ld r25, Y+ ;
+ set ;
+ _LPMI r21 ;/
+26: cpi r21, 'd' ;Is type signed decimal?
+ brne 27f ;/
+ ldi r20, -10 ;
+ brts 40f ;
+ sbrs r23, 7 ;
+ rjmp 40f ;
+ ldi r24, -1 ;
+ ldi r25, -1 ;
+ rjmp 40f ;/
+27: cpi r21, 'u' ;Is type unsigned decimal?
+ ldi r20, 10 ;
+ breq 40f ;/
+ cpi r21, 'X' ;Is type hexdecimal?
+ ldi r20, 16 ;
+ breq 40f ;/
+ cpi r21, 'b' ;Is type binary?
+ ldi r20, 2 ;
+ breq 40f ;/
+ ret ;abort
+40: push ZH ;Output the value
+ push ZL ;
+ rcall xitoa ;
+42: pop ZL ;
+ pop ZH ;
+ rjmp 0b ;/
+
+50: push ZH ;Put a string on the RAM
+ push ZL
+ _MOVW ZH,ZL, r25,r24
+51: ld r24, Z+
+ cpi r24, 0
+ breq 42b
+ rcall xputc
+ rjmp 51b
+
+60: push ZH ;Put a string on the ROM
+ push ZL
+ rcall xputs
+ rjmp 42b
+.endfunc
+
+
+.func __xprintf
+.global __xprintf
+__xprintf:
+ push YH
+ push YL
+ in YL, _SFR_IO_ADDR(SPL)
+#ifdef SPH
+ in YH, _SFR_IO_ADDR(SPH)
+#else
+ clr YH
+#endif
+ adiw YL, 5 ;Y = pointer to arguments
+ rcall xvprintf
+ pop YL
+ pop YH
+ ret
+.endfunc
+
+
+#if USE_XSPRINTF
+
+.func __xsprintf
+putram:
+ _MOVW ZH,ZL, r15,r14
+ st Z+, r24
+ _MOVW r15,r14, ZH,ZL
+ ret
+.global __xsprintf
+__xsprintf:
+ push YH
+ push YL
+ in YL, _SFR_IO_ADDR(SPL)
+#ifdef SPH
+ in YH, _SFR_IO_ADDR(SPH)
+#else
+ clr YH
+#endif
+ adiw YL, 5 ;Y = pointer to arguments
+ lds ZL, xfunc_out+0 ;Save registered output function
+ lds ZH, xfunc_out+1 ;
+ push ZL ;
+ push ZH ;/
+ ldi ZL, lo8(pm(putram));Set local output function
+ ldi ZH, hi8(pm(putram));
+ sts xfunc_out+0, ZL ;
+ sts xfunc_out+1, ZH ;/
+ push r15 ;Initialize pointer to string buffer
+ push r14 ;
+ ld r14, Y+ ;
+ ld r15, Y+ ;/
+ rcall xvprintf
+ _MOVW ZH,ZL, r15,r14 ;Terminate string
+ st Z, r1 ;
+ pop r14 ;
+ pop r15 ;/
+ pop ZH ;Restore registered output function
+ pop ZL ;
+ sts xfunc_out+0, ZL ;
+ sts xfunc_out+1, ZH ;/
+ pop YL
+ pop YH
+ ret
+.endfunc
+#endif
+
+
+#if USE_XFPRINTF
+.func __xfprintf
+.global __xfprintf
+__xfprintf:
+ push YH
+ push YL
+ in YL, _SFR_IO_ADDR(SPL)
+#ifdef SPH
+ in YH, _SFR_IO_ADDR(SPH)
+#else
+ clr YH
+#endif
+ adiw YL, 5 ;Y = pointer to arguments
+ lds ZL, xfunc_out+0 ;Save registered output function
+ lds ZH, xfunc_out+1 ;
+ push ZL ;
+ push ZH ;/
+ ld ZL, Y+ ;Set output function
+ ld ZH, Y+ ;
+ sts xfunc_out+0, ZL ;
+ sts xfunc_out+1, ZH ;/
+ rcall xvprintf
+ pop ZH ;Restore registered output function
+ pop ZL ;
+ sts xfunc_out+0, ZL ;
+ sts xfunc_out+1, ZH ;/
+ pop YL
+ pop YH
+ ret
+.endfunc
+#endif
+
+#endif
+
+
+
+;---------------------------------------------------------------------------
+; Extended numeral string input
+;
+;Prototype:
+; char xatoi ( /* 1: Successful, 0: Failed */
+; const char **str, /* pointer to pointer to source string */
+; long *res /* result */
+; );
+;
+
+
+#if USE_XATOI
+.func xatoi
+.global xatoi
+xatoi:
+ _MOVW r1, r0, r23, r22
+ _MOVW XH, XL, r25, r24
+ ld ZL, X+
+ ld ZH, X+
+ clr r18 ;r21:r18 = 0;
+ clr r19 ;
+ clr r20 ;
+ clr r21 ;/
+ clt ;T = 0;
+
+ ldi r25, 10 ;r25 = 10;
+ rjmp 41f ;/
+40: adiw ZL, 1 ;Z++;
+41: ld r22, Z ;r22 = *Z;
+ cpi r22, ' ' ;if(r22 == ' ') continue
+ breq 40b ;/
+ brcs 70f ;if(r22 < ' ') error;
+ cpi r22, '-' ;if(r22 == '-') {
+ brne 42f ; T = 1;
+ set ; continue;
+ rjmp 40b ;}
+42: cpi r22, '9'+1 ;if(r22 > '9') error;
+ brcc 70f ;/
+ cpi r22, '0' ;if(r22 < '0') error;
+ brcs 70f ;/
+ brne 51f ;if(r22 > '0') cv_start;
+ ldi r25, 8 ;r25 = 8;
+ adiw ZL, 1 ;r22 = *(++Z);
+ ld r22, Z ;/
+ cpi r22, ' '+1 ;if(r22 <= ' ') exit;
+ brcs 80f ;/
+ cpi r22, 'b' ;if(r22 == 'b') {
+ brne 43f ; r25 = 2;
+ ldi r25, 2 ; cv_start;
+ rjmp 50f ;}
+43: cpi r22, 'x' ;if(r22 != 'x') error;
+ brne 51f ;/
+ ldi r25, 16 ;r25 = 16;
+
+50: adiw ZL, 1 ;Z++;
+ ld r22, Z ;r22 = *Z;
+51: cpi r22, ' '+1 ;if(r22 <= ' ') break;
+ brcs 80f ;/
+ cpi r22, 'a' ;if(r22 >= 'a') r22 =- 0x20;
+ brcs 52f ;
+ subi r22, 0x20 ;/
+52: subi r22, '0' ;if((r22 -= '0') < 0) error;
+ brcs 70f ;/
+ cpi r22, 10 ;if(r22 >= 10) {
+ brcs 53f ; r22 -= 7;
+ subi r22, 7 ; if(r22 < 10)
+ cpi r22, 10 ;
+ brcs 70f ;}
+53: cp r22, r25 ;if(r22 >= r25) error;
+ brcc 70f ;/
+60: ldi r24, 33 ;r21:r18 *= r25;
+ sub r23, r23 ;
+61: brcc 62f ;
+ add r23, r25 ;
+62: lsr r23 ;
+ ror r21 ;
+ ror r20 ;
+ ror r19 ;
+ ror r18 ;
+ dec r24 ;
+ brne 61b ;/
+ add r18, r22 ;r21:r18 += r22;
+ adc r19, r24 ;
+ adc r20, r24 ;
+ adc r21, r24 ;/
+ rjmp 50b ;repeat
+
+70: ldi r24, 0
+ rjmp 81f
+80: ldi r24, 1
+81: brtc 82f
+ clr r22
+ com r18
+ com r19
+ com r20
+ com r21
+ adc r18, r22
+ adc r19, r22
+ adc r20, r22
+ adc r21, r22
+82: st -X, ZH
+ st -X, ZL
+ _MOVW XH, XL, r1, r0
+ st X+, r18
+ st X+, r19
+ st X+, r20
+ st X+, r21
+ clr r1
+ ret
+.endfunc
+#endif
+
+
diff --git a/tmk_core/common/avr/xprintf.h b/tmk_core/common/avr/xprintf.h new file mode 100644 index 0000000000..59c6f25312 --- /dev/null +++ b/tmk_core/common/avr/xprintf.h @@ -0,0 +1,111 @@ +/*---------------------------------------------------------------------------
+ Extended itoa, puts and printf (C)ChaN, 2011
+-----------------------------------------------------------------------------*/
+
+#ifndef XPRINTF_H
+#define XPRINTF_H
+
+#include <inttypes.h>
+#include <avr/pgmspace.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void (*xfunc_out)(uint8_t);
+#define xdev_out(func) xfunc_out = (void(*)(uint8_t))(func)
+
+/* This is a pointer to user defined output function. It must be initialized
+ before using this modle.
+*/
+
+void xputc(char chr);
+
+/* This is a stub function to forward outputs to user defined output function.
+ All outputs from this module are output via this function.
+*/
+
+
+/*-----------------------------------------------------------------------------*/
+void xputs(const char *string_p);
+
+/* The string placed in the ROM is forwarded to xputc() directly.
+*/
+
+
+/*-----------------------------------------------------------------------------*/
+void xitoa(long value, char radix, char width);
+
+/* Extended itoa().
+
+ value radix width output
+ 100 10 6 " 100"
+ 100 10 -6 "000100"
+ 100 10 0 "100"
+ 4294967295 10 0 "4294967295"
+ 4294967295 -10 0 "-1"
+ 655360 16 -8 "000A0000"
+ 1024 16 0 "400"
+ 0x55 2 -8 "01010101"
+*/
+
+
+/*-----------------------------------------------------------------------------*/
+#define xprintf(format, ...) __xprintf(PSTR(format), ##__VA_ARGS__)
+#define xsprintf(str, format, ...) __xsprintf(str, PSTR(format), ##__VA_ARGS__)
+#define xfprintf(func, format, ...) __xfprintf(func, PSTR(format), ##__VA_ARGS__)
+
+void __xprintf(const char *format_p, ...); /* Send formatted string to the registered device */
+void __xsprintf(char*, const char *format_p, ...); /* Put formatted string to the memory */
+void __xfprintf(void(*func)(uint8_t), const char *format_p, ...); /* Send formatted string to the specified device */
+
+/* Format string is placed in the ROM. The format flags is similar to printf().
+
+ %[flag][width][size]type
+
+ flag
+ A '0' means filled with '0' when output is shorter than width.
+ ' ' is used in default. This is effective only numeral type.
+ width
+ Minimum width in decimal number. This is effective only numeral type.
+ Default width is zero.
+ size
+ A 'l' means the argument is long(32bit). Default is short(16bit).
+ This is effective only numeral type.
+ type
+ 'c' : Character, argument is the value
+ 's' : String placed on the RAM, argument is the pointer
+ 'S' : String placed on the ROM, argument is the pointer
+ 'd' : Signed decimal, argument is the value
+ 'u' : Unsigned decimal, argument is the value
+ 'X' : Hexdecimal, argument is the value
+ 'b' : Binary, argument is the value
+ '%' : '%'
+
+*/
+
+
+/*-----------------------------------------------------------------------------*/
+char xatoi(char **str, long *ret);
+
+/* Get value of the numeral string.
+
+ str
+ Pointer to pointer to source string
+
+ "0b11001010" binary
+ "0377" octal
+ "0xff800" hexdecimal
+ "1250000" decimal
+ "-25000" decimal
+
+ ret
+ Pointer to return value
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
|