Skip to content

Instantly share code, notes, and snippets.

@Jartza
Last active December 11, 2015 18:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jartza/3b869fa4b6a4afcc19b2 to your computer and use it in GitHub Desktop.
Save Jartza/3b869fa4b6a4afcc19b2 to your computer and use it in GitHub Desktop.
/*
* multitask.c
*
* very quick'n'dirty multitasker-example for attiny85
*
* Created: 11.12.15 00:24:17
* Author : jartza
*/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile uint8_t stack_hi = 0x1; // Second task stack end hi (stack begin 0x1FF)
volatile uint8_t stack_lo = 0xDC; // Second task stack end lo (stack begin 0x1FF)
//
// task_1() and task_2() run simultaneously.
//
// Note: Most likely the delays are much longer than you'd expect as they also run simultaneously :)
//
void task_1() {
while (1) {
PORTB |= (1 << PB0);
_delay_ms(100);
PORTB &= ~(1 << PB0);
_delay_ms(100);
}
}
void task_2() {
while (1) {
PORTB |= (1 << PB2);
_delay_ms(100);
PORTB &= ~(1 << PB2);
_delay_ms(100);
}
}
void setup() {
DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2);
// Set up timer interrupt to run every 256 clock cycles
TCCR1 = 0; // Stop the timer
TCNT1 = 0; // Zero the timer
GTCCR = (1 << PSR1); // Reset the prescaler
OCR1A = 255; // Set the compare value
OCR1C = 255;
TIMSK = (1 << OCIE1A); // Interrupt on Compare Match A
// Start timer in CTC mode, set prescaler / 8. Switch every 2048 cycles.
TCCR1 = (1 << CTC1) | (1 << CS13);
// Initialize second stack
void (*ptr_to_task)() = task_2;
(*(volatile unsigned char *)0x1DD) = 128; // Store fake SREG to new stack :) 0x1E0 = beginning of stack stack...
(*(volatile unsigned char *)0x1FE) = ((unsigned char *)&ptr_to_task)[1]; // Push task_2 "return address" to the end of new stack
(*(volatile unsigned char *)0x1FF) = ((unsigned char *)&ptr_to_task)[0]; // return address being actually start address of task_2...
}
int main(void)
{
setup();
sei();
task_1();
return 0;
}
OBJECTS = main.o task.o
DEVICE = attiny85
CLOCK = 8000000
PROGRAMMER = -c usbasp
FUSES = -U lfuse:w:0xe2:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m -B 12
AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)
all: main.hex
.c.o:
$(COMPILE) -c $< -o $@
.S.o:
$(COMPILE) -x assembler-with-cpp -c $< -o $@
.c.s:
$(COMPILE) -S $< -o $@
flash: all
$(AVRDUDE) -U flash:w:main.hex:i
fuse:
$(AVRDUDE) $(FUSES)
install: flash fuse
load: all
main.hex
clean:
rm -f main.hex main.elf $(OBJECTS)
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
disasm: main.elf
avr-objdump -d main.elf
cpp:
$(COMPILE) -E main.c
#include <avr/io.h>
.global TIMER1_COMPA_vect
.extern stack_hi
.extern stack_lo
TIMER1_COMPA_vect:
; Push all registers
;
push r31
push r30
push r29
push r28
push r27
push r26
push r25
push r24
push r23
push r22
push r21
push r20
push r19
push r18
push r17
push r16
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push r7
push r6
push r5
push r4
push r3
push r2
push r1
push r0
; Push status register
;
in r17, _SFR_IO_ADDR(SREG)
push r17
; Switch stack address
;
in r18, _SFR_IO_ADDR(SPL)
in r19, _SFR_IO_ADDR(SPH)
lds r20, stack_lo
lds r21, stack_hi
sts stack_lo, r18
sts stack_hi, r19
out _SFR_IO_ADDR(SPL), r20
out _SFR_IO_ADDR(SPH), r21
; Return status register
;
pop r17
out _SFR_IO_ADDR(SREG), r17
; Return other registers
;
pop r0
pop r1
pop r2
pop r3
pop r4
pop r5
pop r6
pop r7
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
pop r16
pop r17
pop r18
pop r19
pop r20
pop r21
pop r22
pop r23
pop r24
pop r25
pop r26
pop r27
pop r28
pop r29
pop r30
pop r31
; Finally, return to other task :)
reti
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment