Last active
February 18, 2020 13:03
-
-
Save stecman/1216fa8a89d0c21be895cdd22b13a4fe to your computer and use it in GitHub Desktop.
ATTiny13A Button Pusher - Remember how many times a button was pushed and push it electronically on startup
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Button pusher with memory | |
* | |
* This is a simple program to push a button automatically after power-on. | |
* The controller watches for button presses and remembers how many times | |
* the button should be pushed next time a reset/power-on occurs. | |
*/ | |
#include <avr/io.h> | |
#include <util/delay.h> | |
#include <avr/power.h> | |
#include <avr/sleep.h> | |
#include <avr/interrupt.h> | |
#include <avr/eeprom.h> | |
// How many presses before the button loops back to the first option | |
#define MAX_PRESSES 8 | |
// How long to wait after power/reset to simulate button presses | |
#define POWER_UP_DELAY_MS 600 | |
// How long to pause between simulated button presses | |
#define TIME_BETWEEN_PRESSES_MS 100 | |
// How long to hold each simulated button press | |
#define PRESS_LENGTH_MS 50 | |
// Software debounce timeout for button reads | |
#define DEBOUNCE_TIME_MS 10 | |
#define PLISTEN PB1 | |
#define POUTPUT PB3 | |
/** | |
* Simulate a single button press | |
* This pulls the target's button input low for a short time | |
*/ | |
void simulate_press() | |
{ | |
PORTB &= ~(1<<POUTPUT); | |
_delay_ms(PRESS_LENGTH_MS); | |
PORTB |= (1<<POUTPUT); | |
} | |
void simulate_presses(uint8_t count) | |
{ | |
// Press button n times from memory | |
for (uint8_t i = 0; i < count; i++) { | |
simulate_press(); | |
_delay_ms(TIME_BETWEEN_PRESSES_MS); | |
} | |
} | |
uint8_t load_saved_press_count() | |
{ | |
uint8_t data = eeprom_read_byte((uint8_t*) 0); | |
if (data > MAX_PRESSES) { | |
data = 0; | |
} | |
return data; | |
} | |
inline void setup() | |
{ | |
// Disable power to all peripherals | |
PRR = 0xFF; | |
DDRB &= ~_BV(PLISTEN); // target button as input | |
PORTB |= _BV(PLISTEN); // pull line high to prevent floating | |
DDRB |= _BV(POUTPUT); // button as output | |
PORTB |= _BV(POUTPUT); // Initial high output as target is active low | |
// Enable INT0 interrupt for waking and processing button presses | |
// Triggers on a falling edge | |
GIMSK |= (1<<INT0); // Enable INT0 | |
MCUCR |= (1<<ISC01); | |
} | |
/** | |
* Put the MCU in the "power down" sleep mode | |
* This stops almost everything until another external interrupt occurs | |
*/ | |
inline void power_off() | |
{ | |
// Prepare for sleep | |
cli(); | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); | |
sleep_enable(); | |
// Go to sleep | |
sei(); | |
sleep_cpu(); | |
// Clean up after sleep | |
sleep_disable(); | |
} | |
// Count of external button pushes that haven't been stored | |
volatile uint8_t pendingPresses = 0; | |
ISR (INT0_vect) { | |
_delay_ms(DEBOUNCE_TIME_MS); | |
// Process if we're still low after the debounce period | |
if ((PINB & (1<<PLISTEN)) == 0) { | |
pendingPresses++; | |
} | |
// Ignore any pending interrupt that occurred during debounce | |
GIFR |= (1<<INTF0); | |
} | |
int main(void) | |
{ | |
setup(); | |
// Running memory of number of button presses to make | |
uint8_t presses = load_saved_press_count(); | |
uint8_t lastWritten = presses; | |
// Simulate button presses on external device | |
_delay_ms(POWER_UP_DELAY_MS); | |
simulate_presses(presses); | |
// Start listening for button presses | |
// Pending interrupts from us changing the PLISTEN line are cleared | |
GIFR |= (1<<INTF0); | |
sei(); | |
for (;;) { | |
if (pendingPresses != 0) { | |
pendingPresses--; | |
presses = (presses + 1) % MAX_PRESSES; | |
} | |
// Write any change in value to EEPROM | |
else if (presses != lastWritten) { | |
cli(); | |
eeprom_write_byte((uint8_t*) 0, presses); | |
lastWritten = presses; | |
sei(); | |
// Wait for more button presses | |
// We don't handle wake-up interrupts very reliably, so this is a hack to not miss presses | |
_delay_ms(2000); | |
// Go to a low power state if there areno pending increments | |
if (pendingPresses == 0) { | |
power_off(); | |
} | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
DEVICE = attiny13a | |
CLOCK = 1200000 | |
PROGRAMMER = -c dragon_isp | |
SOURCES = $(shell find . -name '*.c' -or -name '*.cpp' -or -name '*.S') | |
OBJECTS = $(SOURCES:.c=.o) | |
AVRDUDE = avrdude $(PROGRAMMER) -p t13 | |
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) | |
COMPILE += -I -I. -I./lib/ | |
COMPILE += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums | |
COMPILE += -ffunction-sections -fdata-sections -Wl,--gc-sections | |
COMPILE += -Wl,--relax -mcall-prologues | |
COMPILE += -std=gnu99 | |
# symbolic targets: | |
all: $(SOURCES) main.hex | |
.c.o: | |
$(COMPILE) -c $< -o $@ | |
.S.o: | |
$(COMPILE) -x assembler-with-cpp -c $< -o $@ | |
# "-x assembler-with-cpp" should not be necessary since this is the default | |
# file type for the .S (with capital S) extension. However, upper case | |
# characters are not always preserved on Windows. To ensure WinAVR | |
# compatibility define the file type manually. | |
.c.s: | |
$(COMPILE) -S $< -o $@ | |
flash: all | |
$(AVRDUDE) -U flash:w:main.hex:i | |
fuse: | |
# 1.2MHz, preserve EEPROM, BOD enable | |
echo "Fuse with:" | |
echo " -U lfuse:w:0x2a:m -U hfuse:w:0xfb:m" | |
# Xcode uses the Makefile targets "", "clean" and "install" | |
install: flash fuse | |
# if you use a bootloader, change the command below appropriately: | |
load: all | |
bootloadHID main.hex | |
clean: | |
find . -name '*.d' -exec rm {} + | |
find . -name '*.o' -exec rm {} + | |
rm -f main.hex main.elf | |
# file targets: | |
main.elf: $(OBJECTS) | |
$(COMPILE) -o main.elf $(OBJECTS) | |
main.hex: main.elf | |
rm -f main.hex | |
avr-objcopy -j .text -j .data -O ihex main.elf main.hex | |
avr-size --format=avr --mcu=$(DEVICE) main.elf | |
# If you have an EEPROM section, you must also create a hex file for the | |
# EEPROM and add it to the "flash" target. | |
# Targets for code debugging and analysis: | |
disasm: main.elf | |
avr-objdump -d main.elf | |
cpp: | |
$(COMPILE) -E main.c |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More info about this project and schematics can be found on the Hackaday.io project page.