Skip to content

Commit

Permalink
ISR conserves remaining bits of PORTC, experimental ATmega88 support
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Kroll <[email protected]>
  • Loading branch information
tunix79 committed May 7, 2016
1 parent ab7ab78 commit 3e48c59
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 57 deletions.
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ Implementation details

The Laborlicht is quite an old project with commits dating back to early 2006.
After roughly ten years, the code was in a rather bad shape. It was not
compilable with recent versions of avr-gcc and avr-libc, because the way how
interrupt handlers have to be implemented has changed over the years, amongst
other things.
compatible with recent versions of avr-gcc and avr-libc anymore, because the way
interrupt handlers are declared has changed over the years, amongst other
things.

So here it is, a rewrite of the basic Laborlicht firmware. The PWM interrupts
are now generated by the second 8 bit timer of the ATmega8, allowing for a
Expand All @@ -164,8 +164,3 @@ different brightness levels.
However, support for 8 MHz devices is gone (unless you can live with a refresh
rate of 40 Hz), so you better resolder your board if you happen to have a
different clock than 16 MHz.

Another caveat: In order to save some code size in the interrupt handler, even
the pins of PORTC not connected to the color channels are cleared on every
interrupt. Keep that in mind if you plan to connect additional stuff to that
port.
148 changes: 99 additions & 49 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,51 @@
#include <avr/pgmspace.h>
#include <stdlib.h>

#define MASK_RED _BV(PC0)
#define MASK_GREEN _BV(PC1)
#define MASK_BLUE _BV(PC2)
#define xstr(s) str(s)
#define str(s) #s

#define PORTFL PORTC
#define DDRFL DDRC

#define MASK_RED _BV(PC0)
#define MASK_GREEN _BV(PC1)
#define MASK_BLUE _BV(PC2)
// bitwise "and" to avoid int promotion (important for "M" constraint)
#define INV_MASK_ALL (~(MASK_RED | MASK_GREEN | MASK_BLUE) & 0xff)

#define PW(a) pgm_read_word(&(a))

// wait timer
static void init_timer0(void) {
#if defined(__AVR_ATmega8__) || \
defined(__AVR_ATmega8A__)
# define MY_TIFR TIFR
// clk/64
TCCR0 = _BV(CS01) | _BV(CS00);
#elif defined(__AVR_ATmega48__) || \
defined(__AVR_ATmega48P__) || \
defined(__AVR_ATmega88__) || \
defined(__AVR_ATmega88P__) || \
defined(__AVR_ATmega168__) || \
defined(__AVR_ATmega168P__) || \
defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__)
# define MY_TIFR TIFR0
// clk/64
TCCR0B = _BV(CS01) | _BV(CS00);
#else
# error MCU not supported
#endif
}

static void wait(unsigned int ms){
for (; ms--;){
TCNT0 = 6; // overflow after 250 ticks, 1000 Hz
while (!(TIFR & _BV(TOV0))); // wait for timer overflow flag
TIFR = _BV(TOV0); // reset overflow flag
for (; ms--;) {
TCNT0 = 6; // overflow after 250 ticks, 1000 Hz
while (!(MY_TIFR & _BV(TOV0))); // wait for timer overflow flag
MY_TIFR = _BV(TOV0); // reset overflow flag
}
}


typedef enum color_channel_e {
channel_red,
channel_green,
Expand Down Expand Up @@ -151,66 +173,95 @@ unsigned char const PROGMEM g_cie_table[256] = {

// ISR timer
static void init_timer2(void) {
#if defined(__AVR_ATmega8__) || \
defined(__AVR_ATmega8A__)
# define TIMER_ISR TIMER2_COMP_vect
# define OUT_STS out
# define OCR OCR2
// CTC mode, OC2 disconnected, clk/8
TCCR2 = _BV(WGM21) | _BV(CS21);
// initialize compare match and counter registers
OCR2 = PW(g_cie_table[0]);
TCNT2 = 0;
// clear compare match flag and enable compare match interrupt handler
TIFR |= _BV(OCF2);
TIMSK |= _BV(OCIE2);
TIFR |= _BV(OCF2);
TIMSK |= _BV(OCIE2);
#elif defined(__AVR_ATmega48__) || \
defined(__AVR_ATmega48P__) || \
defined(__AVR_ATmega88__) || \
defined(__AVR_ATmega88P__) || \
defined(__AVR_ATmega168__) || \
defined(__AVR_ATmega168P__) || \
defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__)
# define TIMER_ISR TIMER2_COMPA_vect
# define OUT_STS sts
# define OCR OCR2A
// CTC mode, OC2A/OC2B disconnected, clk/8
TCCR2A = _BV(WGM21);
TCCR2B = _BV(CS21);
// initialize compare match and counter registers
OCR2A = PW(g_cie_table[0]);
TCNT2 = 0;
// clear compare match flag and enable compare match interrupt handler
TIFR2 |= _BV(OCF2A);
TIMSK2 |= _BV(OCIE2A);
#else
# error MCU not supported
#endif
}

ISR(TIMER2_COMP_vect) {
ISR(TIMER_ISR) {
// duty cycle from 0 (0%) to 255 (100%)
static unsigned char duty_cycle = 0;

__asm__ volatile(
"isr_start:" "\n\t"
"clr r16" "\n\t"
"inc %[duty_cycle]" "\n\t"

"cie_to_ocr2:" "\n\t"
"add r30,%[duty_cycle]; ZL" "\n\t"
"adc r31,__zero_reg__ ; ZH" "\n\t"
"lpm __tmp_reg__,Z" "\n\t"
"out %[ocr2],__tmp_reg__" "\n\t"

"red_cmp:" "\n\t"
"ld __tmp_reg__,Y+" "\n\t"
"cp __tmp_reg__,%[duty_cycle]" "\n\t"
"brlo green_cmp" "\n\t"
"ori r16,%[mask_red]" "\n\t"

"green_cmp:" "\n\t"
"ld __tmp_reg__,Y+" "\n\t"
"cp __tmp_reg__,%[duty_cycle]" "\n\t"
"brlo blue_cmp" "\n\t"
"ori r16,%[mask_green]" "\n\t"

"blue_cmp:" "\n\t"
"ld __tmp_reg__,Y+" "\n\t"
"cp __tmp_reg__,%[duty_cycle]" "\n\t"
"brlo color_set" "\n\t"
"ori r16,%[mask_blue]" "\n\t"

"color_set:" "\n\t"
"out %[portfl],r16" "\n\t"
"isr_start:" "\n\t"
"in r16,%[portfl]" "\n\t"
"andi r16,%[inv_mask_all]" "\n\t"
"inc %[duty_cycle]" "\n\t"

"cie_to_ocr2:" "\n\t"
"add r30,%[duty_cycle]; ZL" "\n\t"
"adc r31,__zero_reg__ ; ZH" "\n\t"
"lpm __tmp_reg__,Z" "\n\t"
xstr(OUT_STS) " %[ocr],__tmp_reg__" "\n\t"

"red_cmp:" "\n\t"
"ld __tmp_reg__,Y+" "\n\t"
"cp __tmp_reg__,%[duty_cycle]" "\n\t"
"brlo green_cmp" "\n\t"
"ori r16,%[mask_red]" "\n\t"

"green_cmp:" "\n\t"
"ld __tmp_reg__,Y+" "\n\t"
"cp __tmp_reg__,%[duty_cycle]" "\n\t"
"brlo blue_cmp" "\n\t"
"ori r16,%[mask_green]" "\n\t"

"blue_cmp:" "\n\t"
"ld __tmp_reg__,Y+" "\n\t"
"cp __tmp_reg__,%[duty_cycle]" "\n\t"
"brlo color_set" "\n\t"
"ori r16,%[mask_blue]" "\n\t"

"color_set:" "\n\t"
"out %[portfl],r16" "\n\t"

: [duty_cycle] "+d" (duty_cycle)
: "0" (duty_cycle),
"y" (g_color),
"z" (&g_cie_table[0]) ,
[ocr2] "M" _SFR_IO_ADDR(OCR2),
[portfl] "M" _SFR_IO_ADDR(PORTFL),
[mask_red] "M" (MASK_RED),
[mask_green] "M" (MASK_GREEN),
[mask_blue] "M" (MASK_BLUE)
[ocr] "M" _SFR_IO_ADDR(OCR),
[portfl] "M" _SFR_IO_ADDR(PORTFL),
[mask_red] "M" (MASK_RED),
[mask_green] "M" (MASK_GREEN),
[mask_blue] "M" (MASK_BLUE),
[inv_mask_all] "M" (INV_MASK_ALL)
: "r16"
);
}


static unsigned short get_seed()
{
unsigned short seed = 0;
Expand All @@ -223,7 +274,6 @@ static unsigned short get_seed()
return seed;
}


// hardware initialization
static void init_hw(void) {
srand(get_seed());
Expand All @@ -236,7 +286,7 @@ static void init_hw(void) {
int main(int argc, char *argv[]) {
init_hw();

// to avoid a compiler warning regarding an unused function, here is a quick
// to avoid a compiler warnings regarding unused functions, here is a quick
// demostration of the set_color_preset() function
set_color_preset(color_black); // on startup, it's black anyway

Expand Down

0 comments on commit 3e48c59

Please sign in to comment.