arduino-0018-windows
This commit is contained in:
parent
157fd6f1a1
commit
f39fc49523
5182 changed files with 950586 additions and 0 deletions
|
@ -0,0 +1,221 @@
|
|||
# Hey Emacs, this is a -*- makefile -*-
|
||||
|
||||
# AVR-GCC Makefile template, derived from the WinAVR template (which
|
||||
# is public domain), believed to be neutral to any flavor of "make"
|
||||
# (GNU make, BSD make, SysV make)
|
||||
|
||||
|
||||
MCU = attiny13
|
||||
#MCU = attiny45
|
||||
FORMAT = ihex
|
||||
TARGET = asmdemo
|
||||
SRC = $(TARGET).c
|
||||
ASRC = isrs.S
|
||||
OPT = s
|
||||
|
||||
# Name of this Makefile (used for "make depend").
|
||||
MAKEFILE = Makefile
|
||||
|
||||
# Compiler flag to set the C Standard level.
|
||||
# c89 - "ANSI" C
|
||||
# gnu89 - c89 plus GCC extensions
|
||||
# c99 - ISO C99 standard (not yet fully implemented)
|
||||
# gnu99 - c99 plus GCC extensions
|
||||
CSTANDARD = -std=gnu99
|
||||
|
||||
# Place -D or -U options here
|
||||
CDEFS =
|
||||
|
||||
# Place -I options here
|
||||
CINCS =
|
||||
|
||||
|
||||
CDEBUG = -g
|
||||
CWARN = -Wall -Wstrict-prototypes
|
||||
CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
|
||||
#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)
|
||||
CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
|
||||
|
||||
|
||||
#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
|
||||
|
||||
|
||||
#Additional libraries.
|
||||
|
||||
# Minimalistic printf version
|
||||
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
|
||||
|
||||
# Floating point printf version (requires MATH_LIB = -lm below)
|
||||
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
|
||||
|
||||
PRINTF_LIB =
|
||||
|
||||
# Minimalistic scanf version
|
||||
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
|
||||
|
||||
# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
|
||||
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
|
||||
|
||||
SCANF_LIB =
|
||||
|
||||
MATH_LIB = -lm
|
||||
|
||||
# External memory options
|
||||
|
||||
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
|
||||
# used for variables (.data/.bss) and heap (malloc()).
|
||||
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
|
||||
|
||||
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
|
||||
# only used for heap (malloc()).
|
||||
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
|
||||
|
||||
EXTMEMOPTS =
|
||||
|
||||
#LDMAP = $(LDFLAGS) -Wl,-Map=$(TARGET).map,--cref
|
||||
LDFLAGS = $(EXTMEMOPTS) $(LDMAP) $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
|
||||
|
||||
|
||||
# Programming support using avrdude. Settings and variables.
|
||||
|
||||
AVRDUDE_PROGRAMMER = stk500v2
|
||||
AVRDUDE_PORT = /dev/cuaa1
|
||||
|
||||
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
|
||||
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
|
||||
|
||||
|
||||
# Uncomment the following if you want avrdude's erase cycle counter.
|
||||
# Note that this counter needs to be initialized first using -Yn,
|
||||
# see avrdude manual.
|
||||
#AVRDUDE_ERASE_COUNTER = -y
|
||||
|
||||
# Uncomment the following if you do /not/ wish a verification to be
|
||||
# performed after programming the device.
|
||||
#AVRDUDE_NO_VERIFY = -V
|
||||
|
||||
# Increase verbosity level. Please use this when submitting bug
|
||||
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude>
|
||||
# to submit bug reports.
|
||||
#AVRDUDE_VERBOSE = -v -v
|
||||
|
||||
AVRDUDE_BASIC = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
|
||||
AVRDUDE_FLAGS = $(AVRDUDE_BASIC) $(AVRDUDE_NO_VERIFY) $(AVRDUDE_VERBOSE) $(AVRDUDE_ERASE_COUNTER)
|
||||
|
||||
|
||||
CC = avr-gcc
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
SIZE = avr-size
|
||||
NM = avr-nm
|
||||
AVRDUDE = avrdude
|
||||
REMOVE = rm -f
|
||||
MV = mv -f
|
||||
|
||||
# Define all object files.
|
||||
OBJ = $(SRC:.c=.o) $(ASRC:.S=.o)
|
||||
|
||||
# Define all listing files.
|
||||
LST = $(ASRC:.S=.lst) $(SRC:.c=.lst)
|
||||
|
||||
# Combine all necessary flags and optional flags.
|
||||
# Add target processor to flags.
|
||||
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS)
|
||||
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
|
||||
|
||||
|
||||
# Default target.
|
||||
all: build
|
||||
|
||||
build: elf hex eep
|
||||
|
||||
elf: $(TARGET).elf
|
||||
hex: $(TARGET).hex
|
||||
eep: $(TARGET).eep
|
||||
lss: $(TARGET).lss
|
||||
sym: $(TARGET).sym
|
||||
|
||||
|
||||
# Program the device.
|
||||
program: $(TARGET).hex $(TARGET).eep
|
||||
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM)
|
||||
|
||||
|
||||
|
||||
|
||||
# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
|
||||
COFFCONVERT=$(OBJCOPY) --debugging \
|
||||
--change-section-address .data-0x800000 \
|
||||
--change-section-address .bss-0x800000 \
|
||||
--change-section-address .noinit-0x800000 \
|
||||
--change-section-address .eeprom-0x810000
|
||||
|
||||
|
||||
coff: $(TARGET).elf
|
||||
$(COFFCONVERT) -O coff-avr $(TARGET).elf $(TARGET).cof
|
||||
|
||||
|
||||
extcoff: $(TARGET).elf
|
||||
$(COFFCONVERT) -O coff-ext-avr $(TARGET).elf $(TARGET).cof
|
||||
|
||||
|
||||
.SUFFIXES: .elf .hex .eep .lss .sym
|
||||
|
||||
.elf.hex:
|
||||
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
|
||||
|
||||
.elf.eep:
|
||||
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
|
||||
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@
|
||||
|
||||
# Create extended listing file from ELF output file.
|
||||
.elf.lss:
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# Create a symbol table from ELF output file.
|
||||
.elf.sym:
|
||||
$(NM) -n $< > $@
|
||||
|
||||
|
||||
|
||||
# Link: create ELF output file from object files.
|
||||
$(TARGET).elf: $(OBJ)
|
||||
$(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS)
|
||||
|
||||
|
||||
# Compile: create object files from C source files.
|
||||
.c.o:
|
||||
$(CC) -c $(ALL_CFLAGS) $< -o $@
|
||||
|
||||
|
||||
# Compile: create assembler files from C source files.
|
||||
.c.s:
|
||||
$(CC) -S $(ALL_CFLAGS) $< -o $@
|
||||
|
||||
|
||||
# Assemble: create object files from assembler source files.
|
||||
.S.o:
|
||||
$(CC) -c $(ALL_ASFLAGS) $< -o $@
|
||||
|
||||
|
||||
|
||||
# Target: clean project.
|
||||
clean:
|
||||
$(REMOVE) $(TARGET).hex $(TARGET).eep $(TARGET).cof $(TARGET).elf \
|
||||
$(TARGET).map $(TARGET).sym $(TARGET).lss \
|
||||
$(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d)
|
||||
|
||||
depend:
|
||||
if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \
|
||||
then \
|
||||
sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \
|
||||
$(MAKEFILE).$$$$ && \
|
||||
$(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \
|
||||
fi
|
||||
echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \
|
||||
>> $(MAKEFILE); \
|
||||
$(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE)
|
||||
|
||||
.PHONY: all build elf hex eep lss sym program coff extcoff clean depend
|
||||
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Joerg Wunsch wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Demo combining C and assembly source files.
|
||||
*
|
||||
* This demo implements an RC model type PWM decoder. The incoming
|
||||
* PWM signal consists of a pulse sequence with a pulse width of 920
|
||||
* microseconds up to 2120 microseconds (1520 microseconds being the
|
||||
* neutral point). Depending on the value of the decoded incoming
|
||||
* PWM, an outgoing PWM is controlled between 0 and 100 %.
|
||||
*
|
||||
* The project is intented to be run on an ATtiny13 that has only one
|
||||
* timer channel (timer 0), so both the incoming signal discrimination
|
||||
* as well as the outgoing PWM need to run on the same timer.
|
||||
*
|
||||
* For verification purposes, the same project can also be run on an
|
||||
* ATtiny25/45/85, where timer 1 can be used to evaluate the incoming
|
||||
* PWM signal, and timer 0 to generate the outgoing PWM. In that
|
||||
* case, no additional assembly code is needed.
|
||||
*
|
||||
* $Id: asmdemo.c,v 1.1 2006/08/29 19:45:06 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the main C source file for the demo.
|
||||
*/
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#include "project.h"
|
||||
|
||||
volatile uint16_t pwm_incoming;
|
||||
volatile struct
|
||||
{
|
||||
uint8_t pwm_received: 1;
|
||||
}
|
||||
intbits;
|
||||
|
||||
void
|
||||
ioinit(void)
|
||||
{
|
||||
counter_hi = 0;
|
||||
flags = 0;
|
||||
|
||||
/*
|
||||
* Timer 0 runs as phase-correct PWM at full clock, OC0B connects to
|
||||
* the PWM engine.
|
||||
*/
|
||||
TCCR0A = (1 << COM0B1) | (1 << WGM00);
|
||||
TCCR0B = (1 << CS00);
|
||||
OCR0A = 255;
|
||||
|
||||
#if defined(__AVR_ATtiny13__)
|
||||
TIMSK0 = (1 << TOIE0) | (1 << OCIE0A);
|
||||
|
||||
# define F_CPU 1200000ul
|
||||
/* Minimal PWM pulse width is 920 us. */
|
||||
# define MIN_PWM_VAL ((920ul * F_CPU) / 1000000ul)
|
||||
/* Maximal PWM pulse width is 2120 us */
|
||||
# define MAX_PWM_VAL ((2120ul * F_CPU) / 1000000ul)
|
||||
|
||||
#elif defined(__AVR_ATtiny25__) ||\
|
||||
defined(__AVR_ATtiny45__) ||\
|
||||
defined(__AVR_ATtiny85__)
|
||||
|
||||
# define F_CPU 1000000ul
|
||||
/*
|
||||
* We use a prescaler of 16 here to avoid the 32-bit calculations
|
||||
* below.
|
||||
*/
|
||||
/* Minimal PWM pulse width is 920 us. */
|
||||
# define MIN_PWM_VAL ((920ul * F_CPU) / 16 / 1000000ul)
|
||||
/* Maximal PWM pulse width is 2120 us */
|
||||
# define MAX_PWM_VAL ((2120ul * F_CPU) / 16 / 1000000ul)
|
||||
|
||||
#else
|
||||
# error "Don't know how to run on your MCU_TYPE."
|
||||
#endif
|
||||
|
||||
PCMSK = (1 << 4);
|
||||
GIFR = (1 << PCIF);
|
||||
GIMSK = (1 << PCIE);
|
||||
|
||||
DDRB = (1 << PB1);
|
||||
PORTB = 0;
|
||||
|
||||
sei();
|
||||
}
|
||||
|
||||
#if defined(__AVR_ATtiny25__) ||\
|
||||
defined(__AVR_ATtiny45__) ||\
|
||||
defined(__AVR_ATtiny85__)
|
||||
ISR(PCINT0_vect)
|
||||
{
|
||||
uint8_t tcnt1;
|
||||
|
||||
if (PINB & (1 << 4))
|
||||
{
|
||||
/* Start timer 1 with a prescaler of 16. */
|
||||
TCNT1 = 0;
|
||||
TCCR1 = (1 << CS12) | (1 << CS10);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop timer 1, current value is pulse width. */
|
||||
tcnt1 = TCNT1;
|
||||
TCCR1 = 0;
|
||||
GIMSK &= ~(1 << PCIE);
|
||||
|
||||
pwm_incoming = tcnt1;
|
||||
intbits.pwm_received = 1;
|
||||
}
|
||||
#endif /* ATtinyX5 */
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
|
||||
ioinit();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (intbits.pwm_received)
|
||||
{
|
||||
intbits.pwm_received = 0;
|
||||
#if defined(__AVR_ATtiny13__)
|
||||
if (pwm_incoming < MIN_PWM_VAL)
|
||||
pwm_incoming = MIN_PWM_VAL;
|
||||
else if (pwm_incoming > MAX_PWM_VAL)
|
||||
pwm_incoming = MAX_PWM_VAL;
|
||||
OCR0B = (pwm_incoming - MIN_PWM_VAL) * 255ul / (MAX_PWM_VAL - MIN_PWM_VAL);
|
||||
#else
|
||||
OCR0B = (pwm_incoming - MIN_PWM_VAL) * 255u / (MAX_PWM_VAL - MIN_PWM_VAL);
|
||||
#endif
|
||||
GIFR = (1 << PCIF);
|
||||
GIMSK |= (1 << PCIE);
|
||||
}
|
||||
sleep_mode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Joerg Wunsch wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Demo combining C and assembly source files.
|
||||
*
|
||||
* $Id: isrs.S,v 1.1 2006/08/29 19:45:06 joerg_wunsch Exp $
|
||||
*/
|
||||
/*
|
||||
* This file contains the interrupt service routine implementations
|
||||
* when compiling the project for the ATtiny13 target.
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "project.h"
|
||||
|
||||
#if defined(__AVR_ATtiny13__)
|
||||
|
||||
/*
|
||||
* Timer 0 hit TOP (0xff), i.e. it turns from up-counting
|
||||
* into down-counting direction.
|
||||
*/
|
||||
.global TIM0_COMPA_vect
|
||||
TIM0_COMPA_vect:
|
||||
in sreg_save, _SFR_IO_ADDR(SREG)
|
||||
inc counter_hi
|
||||
clr flags
|
||||
out _SFR_IO_ADDR(SREG), sreg_save
|
||||
reti
|
||||
|
||||
/*
|
||||
* Timer 0 hit BOTTOM (0x00), i.e. it turns from down-counting
|
||||
* into up-counting direction.
|
||||
*/
|
||||
.global TIM0_OVF_vect
|
||||
TIM0_OVF_vect:
|
||||
in sreg_save, _SFR_IO_ADDR(SREG)
|
||||
inc counter_hi
|
||||
ser flags
|
||||
out _SFR_IO_ADDR(SREG), sreg_save
|
||||
reti
|
||||
|
||||
;;; one 16-bit word to store our rising edge's timestamp
|
||||
.lcomm starttime.0, 2
|
||||
|
||||
.extern pwm_incoming
|
||||
.extern intbits
|
||||
|
||||
.global PCINT0_vect
|
||||
PCINT0_vect:
|
||||
in sreg_save, _SFR_IO_ADDR(SREG)
|
||||
|
||||
;; save our working registers
|
||||
push r18
|
||||
push r19
|
||||
push r20
|
||||
push r21
|
||||
|
||||
;; Now that we are ready to fetch the current
|
||||
;; value of TCNT0, allow interrupts for a
|
||||
;; moment. As the effect of the SEI will be
|
||||
;; deferred by one instruction, any possible
|
||||
;; rollover of TCNT0 (hitting BOTTOM when
|
||||
;; counting down, or MAX when counting up) will
|
||||
;; allow the above ISRs to trigger right here,
|
||||
;; and update their status, so our combined
|
||||
;; 16-bit time from [counter_hi, TCNT0] will
|
||||
;; be correct.
|
||||
sei
|
||||
in r20, _SFR_IO_ADDR(TCNT0)
|
||||
cli
|
||||
;; Now, make our working copy of the status,
|
||||
;; so we can re-enable interrupts again.
|
||||
mov r21, counter_hi
|
||||
mov r19, flags
|
||||
sei
|
||||
|
||||
;; what direction were we counting?
|
||||
sbrs r19, 0
|
||||
;; we are down-counting, invert TCNT0
|
||||
com r20
|
||||
;; at this point, r21:20 has our current
|
||||
;; 16-bit time
|
||||
|
||||
;; now, look which of the edges triggered
|
||||
;; our pin-change interrupt
|
||||
sbis _SFR_IO_ADDR(PINB), 4
|
||||
rjmp 10f
|
||||
;; rising edge detected, just record starttime
|
||||
sts (starttime.0) + 1, r21
|
||||
sts starttime.0, r20
|
||||
rjmp 99f ; we are done here
|
||||
|
||||
;; Falling edge: compute pulse width, store it
|
||||
;; into pwm_incoming, disable pin-change
|
||||
;; interrupt until the upper layers had a chance
|
||||
;; to fetch the result.
|
||||
|
||||
10: in r18, _SFR_IO_ADDR(GIMSK)
|
||||
andi r18, ~(1 << PCIE)
|
||||
out _SFR_IO_ADDR(GIMSK), r18
|
||||
|
||||
;; pwm_incoming = current_time - starttime
|
||||
lds r19, (starttime.0) + 1
|
||||
lds r18, starttime.0
|
||||
sub r20, r18
|
||||
sbc r21, r19
|
||||
sts (pwm_incoming) + 1, r21
|
||||
sts pwm_incoming, r20
|
||||
|
||||
;; signal upper layer
|
||||
lds r18, intbits
|
||||
ori r18, 1
|
||||
sts intbits, r18
|
||||
99:
|
||||
pop r21
|
||||
pop r20
|
||||
pop r19
|
||||
pop r18
|
||||
|
||||
out _SFR_IO_ADDR(SREG), sreg_save
|
||||
reti
|
||||
#endif /* ATtiny13 */
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* Joerg Wunsch wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Demo combining C and assembly source files.
|
||||
*
|
||||
* $Id: project.h,v 1.1 2006/08/29 19:45:06 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Global register variables.
|
||||
*/
|
||||
#ifdef __ASSEMBLER__
|
||||
|
||||
# define sreg_save r2
|
||||
# define flags r16
|
||||
# define counter_hi r4
|
||||
|
||||
#else /* !ASSEMBLER */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
register uint8_t sreg_save asm("r2");
|
||||
register uint8_t flags asm("r16");
|
||||
register uint8_t counter_hi asm("r4");
|
||||
|
||||
#endif /* ASSEMBLER */
|
|
@ -0,0 +1,145 @@
|
|||
PRG = demo
|
||||
OBJ = demo.o
|
||||
#MCU_TARGET = at90s2313
|
||||
#MCU_TARGET = at90s2333
|
||||
#MCU_TARGET = at90s4414
|
||||
#MCU_TARGET = at90s4433
|
||||
#MCU_TARGET = at90s4434
|
||||
#MCU_TARGET = at90s8515
|
||||
#MCU_TARGET = at90s8535
|
||||
#MCU_TARGET = atmega128
|
||||
#MCU_TARGET = atmega1280
|
||||
#MCU_TARGET = atmega1281
|
||||
#MCU_TARGET = atmega1284p
|
||||
#MCU_TARGET = atmega16
|
||||
#MCU_TARGET = atmega163
|
||||
#MCU_TARGET = atmega164p
|
||||
#MCU_TARGET = atmega165
|
||||
#MCU_TARGET = atmega165p
|
||||
#MCU_TARGET = atmega168
|
||||
#MCU_TARGET = atmega169
|
||||
#MCU_TARGET = atmega169p
|
||||
#MCU_TARGET = atmega2560
|
||||
#MCU_TARGET = atmega2561
|
||||
#MCU_TARGET = atmega32
|
||||
#MCU_TARGET = atmega324p
|
||||
#MCU_TARGET = atmega325
|
||||
#MCU_TARGET = atmega3250
|
||||
#MCU_TARGET = atmega329
|
||||
#MCU_TARGET = atmega3290
|
||||
#MCU_TARGET = atmega48
|
||||
#MCU_TARGET = atmega64
|
||||
#MCU_TARGET = atmega640
|
||||
#MCU_TARGET = atmega644
|
||||
#MCU_TARGET = atmega644p
|
||||
#MCU_TARGET = atmega645
|
||||
#MCU_TARGET = atmega6450
|
||||
#MCU_TARGET = atmega649
|
||||
#MCU_TARGET = atmega6490
|
||||
MCU_TARGET = atmega8
|
||||
#MCU_TARGET = atmega8515
|
||||
#MCU_TARGET = atmega8535
|
||||
#MCU_TARGET = atmega88
|
||||
#MCU_TARGET = attiny2313
|
||||
#MCU_TARGET = attiny24
|
||||
#MCU_TARGET = attiny25
|
||||
#MCU_TARGET = attiny26
|
||||
#MCU_TARGET = attiny261
|
||||
#MCU_TARGET = attiny44
|
||||
#MCU_TARGET = attiny45
|
||||
#MCU_TARGET = attiny461
|
||||
#MCU_TARGET = attiny84
|
||||
#MCU_TARGET = attiny85
|
||||
#MCU_TARGET = attiny861
|
||||
OPTIMIZE = -O2
|
||||
|
||||
DEFS =
|
||||
LIBS =
|
||||
|
||||
# You should not have to change anything below here.
|
||||
|
||||
CC = avr-gcc
|
||||
|
||||
# Override is only needed by avr-lib build system.
|
||||
|
||||
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
|
||||
override LDFLAGS = -Wl,-Map,$(PRG).map
|
||||
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
|
||||
all: $(PRG).elf lst text eeprom
|
||||
|
||||
$(PRG).elf: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
# dependency:
|
||||
demo.o: demo.c iocompat.h
|
||||
|
||||
clean:
|
||||
rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak
|
||||
rm -rf *.lst *.map $(EXTRA_CLEAN_FILES)
|
||||
|
||||
lst: $(PRG).lst
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# Rules for building the .text rom images
|
||||
|
||||
text: hex bin srec
|
||||
|
||||
hex: $(PRG).hex
|
||||
bin: $(PRG).bin
|
||||
srec: $(PRG).srec
|
||||
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O ihex $< $@
|
||||
|
||||
%.srec: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O srec $< $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O binary $< $@
|
||||
|
||||
# Rules for building the .eeprom rom images
|
||||
|
||||
eeprom: ehex ebin esrec
|
||||
|
||||
ehex: $(PRG)_eeprom.hex
|
||||
ebin: $(PRG)_eeprom.bin
|
||||
esrec: $(PRG)_eeprom.srec
|
||||
|
||||
%_eeprom.hex: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ \
|
||||
|| { echo empty $@ not generated; exit 0; }
|
||||
|
||||
%_eeprom.srec: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ \
|
||||
|| { echo empty $@ not generated; exit 0; }
|
||||
|
||||
%_eeprom.bin: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ \
|
||||
|| { echo empty $@ not generated; exit 0; }
|
||||
|
||||
# Every thing below here is used by avr-libc's build system and can be ignored
|
||||
# by the casual user.
|
||||
|
||||
FIG2DEV = fig2dev
|
||||
EXTRA_CLEAN_FILES = *.hex *.bin *.srec
|
||||
|
||||
dox: eps png pdf
|
||||
|
||||
eps: $(PRG).eps
|
||||
png: $(PRG).png
|
||||
pdf: $(PRG).pdf
|
||||
|
||||
%.eps: %.fig
|
||||
$(FIG2DEV) -L eps $< $@
|
||||
|
||||
%.pdf: %.fig
|
||||
$(FIG2DEV) -L pdf $< $@
|
||||
|
||||
%.png: %.fig
|
||||
$(FIG2DEV) -L png $< $@
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Simple AVR demonstration. Controls a LED that can be directly
|
||||
* connected from OC1/OC1A to GND. The brightness of the LED is
|
||||
* controlled with the PWM. After each period of the PWM, the PWM
|
||||
* value is either incremented or decremented, that's all.
|
||||
*
|
||||
* $Id: demo.c,v 1.9 2006/01/05 21:30:10 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#include "iocompat.h" /* Note [1] */
|
||||
|
||||
enum { UP, DOWN };
|
||||
|
||||
ISR (TIMER1_OVF_vect) /* Note [2] */
|
||||
{
|
||||
static uint16_t pwm; /* Note [3] */
|
||||
static uint8_t direction;
|
||||
|
||||
switch (direction) /* Note [4] */
|
||||
{
|
||||
case UP:
|
||||
if (++pwm == TIMER1_TOP)
|
||||
direction = DOWN;
|
||||
break;
|
||||
|
||||
case DOWN:
|
||||
if (--pwm == 0)
|
||||
direction = UP;
|
||||
break;
|
||||
}
|
||||
|
||||
OCR = pwm; /* Note [5] */
|
||||
}
|
||||
|
||||
void
|
||||
ioinit (void) /* Note [6] */
|
||||
{
|
||||
/* Timer 1 is 10-bit PWM (8-bit PWM on some ATtinys). */
|
||||
TCCR1A = TIMER1_PWM_INIT;
|
||||
/*
|
||||
* Start timer 1.
|
||||
*
|
||||
* NB: TCCR1A and TCCR1B could actually be the same register, so
|
||||
* take care to not clobber it.
|
||||
*/
|
||||
TCCR1B |= TIMER1_CLOCKSOURCE;
|
||||
/*
|
||||
* Run any device-dependent timer 1 setup hook if present.
|
||||
*/
|
||||
#if defined(TIMER1_SETUP_HOOK)
|
||||
TIMER1_SETUP_HOOK();
|
||||
#endif
|
||||
|
||||
/* Set PWM value to 0. */
|
||||
OCR = 0;
|
||||
|
||||
/* Enable OC1 as output. */
|
||||
DDROC = _BV (OC1);
|
||||
|
||||
/* Enable timer 1 overflow interrupt. */
|
||||
TIMSK = _BV (TOIE1);
|
||||
sei ();
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
|
||||
ioinit ();
|
||||
|
||||
/* loop forever, the interrupts are doing the rest */
|
||||
|
||||
for (;;) /* Note [7] */
|
||||
sleep_mode();
|
||||
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* IO feature compatibility definitions for various AVRs.
|
||||
*
|
||||
* $Id: iocompat.h,v 1.6.2.1 2008/03/17 22:08:46 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#if !defined(IOCOMPAT_H)
|
||||
#define IOCOMPAT_H 1
|
||||
|
||||
/*
|
||||
* Device-specific adjustments:
|
||||
*
|
||||
* Supply definitions for the location of the OCR1[A] port/pin, the
|
||||
* name of the OCR register controlling the PWM, and adjust interrupt
|
||||
* vector names that differ from the one used in demo.c
|
||||
* [TIMER1_OVF_vect].
|
||||
*/
|
||||
#if defined(__AVR_AT90S2313__)
|
||||
# define OC1 PB3
|
||||
# define OCR OCR1
|
||||
# define DDROC DDRB
|
||||
# define TIMER1_OVF_vect TIMER1_OVF1_vect
|
||||
#elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__)
|
||||
# define OC1 PB1
|
||||
# define DDROC DDRB
|
||||
# define OCR OCR1
|
||||
#elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || \
|
||||
defined(__AVR_AT90S4434__) || defined(__AVR_AT90S8535__) || \
|
||||
defined(__AVR_ATmega163__) || defined(__AVR_ATmega8515__) || \
|
||||
defined(__AVR_ATmega8535__) || \
|
||||
defined(__AVR_ATmega164P__) || defined(__AVR_ATmega324P__) || \
|
||||
defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || \
|
||||
defined(__AVR_ATmega1284P__)
|
||||
# define OC1 PD5
|
||||
# define DDROC DDRD
|
||||
# define OCR OCR1A
|
||||
# if !defined(TIMSK) /* new ATmegas */
|
||||
# define TIMSK TIMSK1
|
||||
# endif
|
||||
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || \
|
||||
defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__)
|
||||
# define OC1 PB1
|
||||
# define DDROC DDRB
|
||||
# define OCR OCR1A
|
||||
# if !defined(TIMSK) /* ATmega48/88/168 */
|
||||
# define TIMSK TIMSK1
|
||||
# endif /* !defined(TIMSK) */
|
||||
#elif defined(__AVR_ATtiny2313__)
|
||||
# define OC1 PB3
|
||||
# define OCR OCR1A
|
||||
# define DDROC DDRB
|
||||
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || \
|
||||
defined(__AVR_ATtiny84__)
|
||||
# define OC1 PA6
|
||||
# define DDROC DDRA
|
||||
# if !defined(OCR1A)
|
||||
# /* work around misspelled name in avr-libc 1.4.[0..1] */
|
||||
# define OCR OCRA1
|
||||
# else
|
||||
# define OCR OCR1A
|
||||
# endif
|
||||
# define TIMSK TIMSK1
|
||||
# define TIMER1_OVF_vect TIM1_OVF_vect /* XML and datasheet mismatch */
|
||||
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || \
|
||||
defined(__AVR_ATtiny85__)
|
||||
/* Timer 1 is only an 8-bit timer on these devices. */
|
||||
# define OC1 PB1
|
||||
# define DDROC DDRB
|
||||
# define OCR OCR1A
|
||||
# define TCCR1A TCCR1
|
||||
# define TCCR1B TCCR1
|
||||
# define TIMER1_OVF_vect TIM1_OVF_vect
|
||||
# define TIMER1_TOP 255 /* only 8-bit PWM possible */
|
||||
# define TIMER1_PWM_INIT _BV(PWM1A) | _BV(COM1A1)
|
||||
# define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
|
||||
#elif defined(__AVR_ATtiny26__)
|
||||
/* Rather close to ATtinyX5 but different enough for its own section. */
|
||||
# define OC1 PB1
|
||||
# define DDROC DDRB
|
||||
# define OCR OCR1A
|
||||
# define TIMER1_OVF_vect TIMER1_OVF1_vect
|
||||
# define TIMER1_TOP 255 /* only 8-bit PWM possible */
|
||||
# define TIMER1_PWM_INIT _BV(PWM1A) | _BV(COM1A1)
|
||||
# define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
|
||||
/*
|
||||
* Without setting OCR1C to TOP, the ATtiny26 does not trigger an
|
||||
* overflow interrupt in PWM mode.
|
||||
*/
|
||||
# define TIMER1_SETUP_HOOK() OCR1C = 255
|
||||
#elif defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || \
|
||||
defined(__AVR_ATtiny861__)
|
||||
# define OC1 PB1
|
||||
# define DDROC DDRB
|
||||
# define OCR OCR1A
|
||||
# define TIMER1_PWM_INIT _BV(WGM10) | _BV(PWM1A) | _BV(COM1A1)
|
||||
/*
|
||||
* While timer 1 could be operated in 10-bit mode on these devices,
|
||||
* the handling of the 10-bit IO registers is more complicated than
|
||||
* that of the 16-bit registers of other AVR devices (no combined
|
||||
* 16-bit IO operations possible), so we restrict this demo to 8-bit
|
||||
* mode which is pretty standard.
|
||||
*/
|
||||
# define TIMER1_TOP 255
|
||||
# define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
|
||||
#elif defined(__AVR_ATmega32__) || defined(__AVR_ATmega16__)
|
||||
# define OC1 PD5
|
||||
# define DDROC DDRD
|
||||
# define OCR OCR1A
|
||||
#elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || \
|
||||
defined(__AVR_ATmega165__) || defined(__AVR_ATmega169__) || \
|
||||
defined(__AVR_ATmega325__) || defined(__AVR_ATmega3250__) || \
|
||||
defined(__AVR_ATmega645__) || defined(__AVR_ATmega6450__) || \
|
||||
defined(__AVR_ATmega329__) || defined(__AVR_ATmega3290__) || \
|
||||
defined(__AVR_ATmega649__) || defined(__AVR_ATmega6490__) || \
|
||||
defined(__AVR_ATmega640__) || \
|
||||
defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || \
|
||||
defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
|
||||
# define OC1 PB5
|
||||
# define DDROC DDRB
|
||||
# define OCR OCR1A
|
||||
# if !defined(PB5) /* work around missing bit definition */
|
||||
# define PB5 5
|
||||
# endif
|
||||
# if !defined(TIMSK) /* new ATmegas */
|
||||
# define TIMSK TIMSK1
|
||||
# endif
|
||||
#else
|
||||
# error "Don't know what kind of MCU you are compiling for"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Map register names for older AVRs here.
|
||||
*/
|
||||
#if !defined(COM1A1)
|
||||
# define COM1A1 COM11
|
||||
#endif
|
||||
|
||||
#if !defined(WGM10)
|
||||
# define WGM10 PWM10
|
||||
# define WGM11 PWM11
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provide defaults for device-specific macros unless overridden
|
||||
* above.
|
||||
*/
|
||||
#if !defined(TIMER1_TOP)
|
||||
# define TIMER1_TOP 1023 /* 10-bit PWM */
|
||||
#endif
|
||||
|
||||
#if !defined(TIMER1_PWM_INIT)
|
||||
# define TIMER1_PWM_INIT _BV(WGM10) | _BV(WGM11) | _BV(COM1A1)
|
||||
#endif
|
||||
|
||||
#if !defined(TIMER1_CLOCKSOURCE)
|
||||
# define TIMER1_CLOCKSOURCE _BV(CS10) /* full clock */
|
||||
#endif
|
||||
|
||||
#endif /* !defined(IOCOMPAT_H) */
|
|
@ -0,0 +1,90 @@
|
|||
PRG = largedemo
|
||||
OBJ = largedemo.o
|
||||
MCU_TARGET = atmega16
|
||||
#MCU_TARGET = atmega8
|
||||
#MCU_TARGET = atmega48
|
||||
#MCU_TARGET = atmega88
|
||||
#MCU_TARGET = atmega168
|
||||
#MCU_TARGET = attiny2313
|
||||
OPTIMIZE = -Os
|
||||
|
||||
DEFS =
|
||||
LIBS =
|
||||
|
||||
# You should not have to change anything below here.
|
||||
|
||||
CC = avr-gcc
|
||||
|
||||
# Override is only needed by avr-lib build system.
|
||||
|
||||
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
|
||||
override LDFLAGS = -Wl,-Map,$(PRG).map
|
||||
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
|
||||
all: $(PRG).elf lst text eeprom
|
||||
|
||||
$(PRG).elf: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak
|
||||
rm -rf *.lst *.map $(EXTRA_CLEAN_FILES)
|
||||
|
||||
lst: $(PRG).lst
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# Rules for building the .text rom images
|
||||
|
||||
text: hex bin srec
|
||||
|
||||
hex: $(PRG).hex
|
||||
bin: $(PRG).bin
|
||||
srec: $(PRG).srec
|
||||
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O ihex $< $@
|
||||
|
||||
%.srec: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O srec $< $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O binary $< $@
|
||||
|
||||
# Rules for building the .eeprom rom images
|
||||
|
||||
eeprom: ehex ebin esrec
|
||||
|
||||
ehex: $(PRG)_eeprom.hex
|
||||
ebin: $(PRG)_eeprom.bin
|
||||
esrec: $(PRG)_eeprom.srec
|
||||
|
||||
%_eeprom.hex: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
|
||||
|
||||
%_eeprom.srec: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@
|
||||
|
||||
%_eeprom.bin: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@
|
||||
|
||||
# Every thing below here is used by avr-libc's build system and can be ignored
|
||||
# by the casual user.
|
||||
|
||||
JPEGFILES = largedemo-setup.jpg largedemo-wiring.jpg \
|
||||
largedemo-wiring2.jpg
|
||||
|
||||
JPEG2PNM = jpegtopnm
|
||||
PNM2EPS = pnmtops
|
||||
JPEGRESOLUTION = 180
|
||||
EXTRA_CLEAN_FILES = *.hex *.bin *.srec *.eps
|
||||
|
||||
dox: ${JPEGFILES:.jpg=.eps}
|
||||
|
||||
%.eps: %.jpg
|
||||
$(JPEG2PNM) $< |\
|
||||
$(PNM2EPS) -noturn -dpi $(JPEGRESOLUTION) -equalpixels \
|
||||
> $@
|
|
@ -0,0 +1,545 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* More advanced AVR demonstration. Controls a LED attached to OCR1A.
|
||||
* The brightness of the LED is controlled with the PWM. A number of
|
||||
* methods are implemented to control that PWM.
|
||||
*
|
||||
* $Id: largedemo.c,v 1.3 2007/01/19 22:17:10 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <avr/wdt.h>
|
||||
|
||||
/* Part 1: Macro definitions */
|
||||
|
||||
#define CONTROL_PORT PORTD
|
||||
#define CONTROL_DDR DDRD
|
||||
|
||||
#if defined(__AVR_ATtiny2313__)
|
||||
/* no PD7 and no ADC available on ATtiny2313 */
|
||||
# define TRIGGER_DOWN PD2
|
||||
# define TRIGGER_UP PD3
|
||||
# define FLASH PD4
|
||||
# define CLOCKOUT PD6
|
||||
#else
|
||||
# define TRIGGER_DOWN PD2
|
||||
# define TRIGGER_UP PD3
|
||||
# define TRIGGER_ADC PD4
|
||||
# define CLOCKOUT PD6
|
||||
# define FLASH PD7
|
||||
#endif
|
||||
|
||||
#if defined(__AVR_ATmega16__)
|
||||
# define PWMDDR DDRD
|
||||
# define PWMOUT PD5
|
||||
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) ||\
|
||||
defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__)
|
||||
# define PWMDDR DDRB
|
||||
# define PWMOUT PB1
|
||||
#elif defined(__AVR_ATtiny2313__)
|
||||
# define PWMDDR DDRB
|
||||
# define PWMOUT PB3
|
||||
# define HAVE_ADC 0
|
||||
# define USART_RXC_vect USART_RX_vect
|
||||
# define MCUCSR MCUSR
|
||||
#else
|
||||
# error "Unsupported MCU type"
|
||||
#endif
|
||||
|
||||
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) ||\
|
||||
defined(__AVR_ATmega168__)
|
||||
/* map ATmega8/16 names to ATmegaX8 names */
|
||||
# define USART_RXC_vect USART_RX_vect
|
||||
# define UDR UDR0
|
||||
# define UCSRA UCSR0A
|
||||
# define UCSRB UCSR0B
|
||||
# define FE FE0
|
||||
# define TXEN TXEN0
|
||||
# define RXEN RXEN0
|
||||
# define RXCIE RXCIE0
|
||||
# define UDRE UDRE0
|
||||
# define U2X U2X0
|
||||
# define UBRRL UBRR0L
|
||||
|
||||
# define TIMSK TIMSK1
|
||||
# define MCUCSR MCUSR
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_ADC)
|
||||
# define HAVE_ADC 1
|
||||
#endif
|
||||
|
||||
#define F_CPU 1000000UL /* CPU clock in Hertz */
|
||||
|
||||
#define SOFTCLOCK_FREQ 100 /* internal software clock */
|
||||
|
||||
/*
|
||||
* Timeout to wait after last PWM change till updating the EEPROM.
|
||||
* Measured in internal clock ticks (approx. 100 Hz).
|
||||
*/
|
||||
#define EE_UPDATE_TIME (3 * SOFTCLOCK_FREQ) /* ca. 3 seconds */
|
||||
|
||||
/*
|
||||
* Timer1 overflow interrupt will be called with F_CPU / 2048
|
||||
* frequency. This interrupt routine further divides that value,
|
||||
* resulting in an internal update interval of approx. 10 ms.
|
||||
* (The complicated looking scaling by 10 / addition of 9 is
|
||||
* poor man's fixed-point rounding algorithm...)
|
||||
*/
|
||||
#define TMR1_SCALE ((F_CPU * 10) / (2048UL * SOFTCLOCK_FREQ) + 9) / 10
|
||||
|
||||
/* Part 2: Variable definitions */
|
||||
|
||||
/*
|
||||
* Bits that are set inside interrupt routines, and watched outside in
|
||||
* the program's main loop.
|
||||
*/
|
||||
volatile struct
|
||||
{
|
||||
uint8_t tmr_int: 1;
|
||||
uint8_t adc_int: 1;
|
||||
uint8_t rx_int: 1;
|
||||
}
|
||||
intflags;
|
||||
|
||||
/*
|
||||
* Last character read from the UART.
|
||||
*/
|
||||
volatile char rxbuff;
|
||||
|
||||
/*
|
||||
* Last value read from ADC.
|
||||
*/
|
||||
volatile uint16_t adcval;
|
||||
|
||||
/*
|
||||
* Where to store the PWM value in EEPROM. This is used in order
|
||||
* to remember the value across a RESET or power cycle.
|
||||
*/
|
||||
uint16_t ee_pwm __attribute__((section(".eeprom"))) = 42;
|
||||
|
||||
/*
|
||||
* Current value of the PWM.
|
||||
*/
|
||||
int16_t pwm;
|
||||
|
||||
/*
|
||||
* EEPROM backup timer. Bumped by the PWM update routine. If it
|
||||
* expires, the current PWM value will be written to EEPROM.
|
||||
*/
|
||||
int16_t pwm_backup_tmr;
|
||||
|
||||
/*
|
||||
* Mirror of the MCUCSR register, taken early during startup.
|
||||
*/
|
||||
uint8_t mcucsr __attribute__((section(".noinit")));
|
||||
|
||||
/* Part 3: Interrupt service routines */
|
||||
|
||||
ISR(TIMER1_OVF_vect)
|
||||
{
|
||||
static uint8_t scaler = TMR1_SCALE;
|
||||
|
||||
if (--scaler == 0)
|
||||
{
|
||||
scaler = TMR1_SCALE;
|
||||
intflags.tmr_int = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_ADC
|
||||
/*
|
||||
* ADC conversion complete. Fetch the 10-bit value, and feed the
|
||||
* PWM with it.
|
||||
*/
|
||||
ISR(ADC_vect)
|
||||
{
|
||||
adcval = ADCW;
|
||||
ADCSRA &= ~_BV(ADIE); /* disable ADC interrupt */
|
||||
intflags.adc_int = 1;
|
||||
}
|
||||
#endif /* HAVE_ADC */
|
||||
|
||||
/*
|
||||
* UART receive interrupt. Fetch the character received and buffer
|
||||
* it, unless there was a framing error. Note that the main loop
|
||||
* checks the received character only once per 10 ms.
|
||||
*/
|
||||
ISR(USART_RXC_vect)
|
||||
{
|
||||
uint8_t c;
|
||||
|
||||
c = UDR;
|
||||
if (bit_is_clear(UCSRA, FE))
|
||||
{
|
||||
rxbuff = c;
|
||||
intflags.rx_int = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Part 4: Auxiliary functions */
|
||||
|
||||
/*
|
||||
* Read out and reset MCUCSR early during startup.
|
||||
*/
|
||||
void handle_mcucsr(void)
|
||||
__attribute__((section(".init3")))
|
||||
__attribute__((naked));
|
||||
void handle_mcucsr(void)
|
||||
{
|
||||
mcucsr = MCUCSR;
|
||||
MCUCSR = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do all the startup-time peripheral initializations.
|
||||
*/
|
||||
static void
|
||||
ioinit(void)
|
||||
{
|
||||
uint16_t pwm_from_eeprom;
|
||||
|
||||
/*
|
||||
* Set up the 16-bit timer 1.
|
||||
*
|
||||
* Timer 1 will be set up as a 10-bit phase-correct PWM (WGM10 and
|
||||
* WGM11 bits), with OC1A used as PWM output. OC1A will be set when
|
||||
* up-counting, and cleared when down-counting (COM1A1|COM1A0), this
|
||||
* matches the behaviour needed by the STK500's low-active LEDs.
|
||||
* The timer will runn on full MCU clock (1 MHz, CS10 in TCCR1B).
|
||||
*/
|
||||
TCCR1A = _BV(WGM10) | _BV(WGM11) | _BV(COM1A1) | _BV(COM1A0);
|
||||
TCCR1B = _BV(CS10);
|
||||
|
||||
OCR1A = 0; /* set PWM value to 0 */
|
||||
|
||||
/* enable pull-ups for pushbuttons */
|
||||
#if HAVE_ADC
|
||||
CONTROL_PORT = _BV(TRIGGER_DOWN) | _BV(TRIGGER_UP) | _BV(TRIGGER_ADC);
|
||||
#else
|
||||
CONTROL_PORT = _BV(TRIGGER_DOWN) | _BV(TRIGGER_UP);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable Port D outputs: PD6 for the clock output, PD7 for the LED
|
||||
* flasher. PD1 is UART TxD but not DDRD setting is provided for
|
||||
* that, as enabling the UART transmitter will automatically turn
|
||||
* this pin into an output.
|
||||
*/
|
||||
CONTROL_DDR = _BV(CLOCKOUT) | _BV(FLASH);
|
||||
|
||||
/*
|
||||
* As the location of OC1A differs between supported MCU types, we
|
||||
* enable that output separately here. Note that the DDRx register
|
||||
* *might* be the same as CONTROL_DDR above, so make sure to not
|
||||
* clobber it.
|
||||
*/
|
||||
PWMDDR |= _BV(PWMOUT);
|
||||
|
||||
UCSRA = _BV(U2X); /* improves baud rate error @ F_CPU = 1 MHz */
|
||||
UCSRB = _BV(TXEN)|_BV(RXEN)|_BV(RXCIE); /* tx/rx enable, rx complete intr */
|
||||
UBRRL = (F_CPU / (8 * 9600UL)) - 1; /* 9600 Bd */
|
||||
|
||||
#if HAVE_ADC
|
||||
/*
|
||||
* enable ADC, select ADC clock = F_CPU / 8 (i.e. 125 kHz)
|
||||
*/
|
||||
ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS0);
|
||||
#endif
|
||||
|
||||
TIMSK = _BV(TOIE1);
|
||||
sei(); /* enable interrupts */
|
||||
|
||||
/*
|
||||
* Enable the watchdog with the largest prescaler. Will cause a
|
||||
* watchdog reset after approximately 2 s @ Vcc = 5 V
|
||||
*/
|
||||
wdt_enable(WDTO_2S);
|
||||
|
||||
/*
|
||||
* Read the value from EEPROM. If it is not 0xffff (erased cells),
|
||||
* use it as the starting value for the PWM.
|
||||
*/
|
||||
if ((pwm_from_eeprom = eeprom_read_word(&ee_pwm)) != 0xffff)
|
||||
OCR1A = (pwm = pwm_from_eeprom);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some simple UART IO functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Send character c down the UART Tx, wait until tx holding register
|
||||
* is empty.
|
||||
*/
|
||||
static void
|
||||
putchr(char c)
|
||||
{
|
||||
|
||||
loop_until_bit_is_set(UCSRA, UDRE);
|
||||
UDR = c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a C (NUL-terminated) string down the UART Tx.
|
||||
*/
|
||||
static void
|
||||
printstr(const char *s)
|
||||
{
|
||||
|
||||
while (*s)
|
||||
{
|
||||
if (*s == '\n')
|
||||
putchr('\r');
|
||||
putchr(*s++);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as above, but the string is located in program memory,
|
||||
* so "lpm" instructions are needed to fetch it.
|
||||
*/
|
||||
static void
|
||||
printstr_p(const char *s)
|
||||
{
|
||||
char c;
|
||||
|
||||
for (c = pgm_read_byte(s); c; ++s, c = pgm_read_byte(s))
|
||||
{
|
||||
if (c == '\n')
|
||||
putchr('\r');
|
||||
putchr(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the PWM value. If it has changed, send the new value down
|
||||
* the serial line.
|
||||
*/
|
||||
static void
|
||||
set_pwm(int16_t new)
|
||||
{
|
||||
char s[8];
|
||||
|
||||
if (new < 0)
|
||||
new = 0;
|
||||
else if (new > 1000)
|
||||
new = 1000;
|
||||
|
||||
if (new != pwm)
|
||||
{
|
||||
OCR1A = (pwm = new);
|
||||
|
||||
/*
|
||||
* Calculate a "percentage". We just divide by 10, as we
|
||||
* limited the max value of the PWM to 1000 above.
|
||||
*/
|
||||
new /= 10;
|
||||
itoa(new, s, 10);
|
||||
printstr(s);
|
||||
putchr(' ');
|
||||
|
||||
pwm_backup_tmr = EE_UPDATE_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
/* Part 5: main() */
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
/*
|
||||
* Our modus of operation. MODE_UPDOWN means we watch out for
|
||||
* either PD2 or PD3 being low, and increase or decrease the
|
||||
* PWM value accordingly. This is the default.
|
||||
* MODE_ADC means the PWM value follows the value of ADC0 (PA0).
|
||||
* This is enabled by applying low level to PC1.
|
||||
* MODE_SERIAL means we get commands via the UART. This is
|
||||
* enabled by sending a valid V.24 character at 9600 Bd to the
|
||||
* UART.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
MODE_UPDOWN,
|
||||
MODE_ADC,
|
||||
MODE_SERIAL
|
||||
} __attribute__((packed)) mode = MODE_UPDOWN;
|
||||
uint8_t flash = 0;
|
||||
|
||||
ioinit();
|
||||
|
||||
if ((mcucsr & _BV(WDRF)) == _BV(WDRF))
|
||||
printstr_p(PSTR("\nOoops, the watchdog bit me!"));
|
||||
|
||||
printstr_p(PSTR("\nHello, this is the avr-gcc/libc "
|
||||
"demo running on an "
|
||||
#if defined(__AVR_ATmega16__)
|
||||
"ATmega16"
|
||||
#elif defined(__AVR_ATmega8__)
|
||||
"ATmega8"
|
||||
#elif defined(__AVR_ATmega48__)
|
||||
"ATmega48"
|
||||
#elif defined(__AVR_ATmega88__)
|
||||
"ATmega88"
|
||||
#elif defined(__AVR_ATmega168__)
|
||||
"ATmega168"
|
||||
#elif defined(__AVR_ATtiny2313__)
|
||||
"ATtiny2313"
|
||||
#else
|
||||
"unknown AVR"
|
||||
#endif
|
||||
"\n"));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
wdt_reset();
|
||||
|
||||
if (intflags.tmr_int)
|
||||
{
|
||||
/*
|
||||
* Our periodic 10 ms interrupt happened. See what we can
|
||||
* do about it.
|
||||
*/
|
||||
intflags.tmr_int = 0;
|
||||
/*
|
||||
* toggle PD6, just to show the internal clock; should
|
||||
* yield ~ 48 Hz on PD6
|
||||
*/
|
||||
CONTROL_PORT ^= _BV(CLOCKOUT);
|
||||
/*
|
||||
* flash LED on PD7, approximately once per second
|
||||
*/
|
||||
flash++;
|
||||
if (flash == 5)
|
||||
CONTROL_PORT |= _BV(FLASH);
|
||||
else if (flash == 100)
|
||||
{
|
||||
flash = 0;
|
||||
CONTROL_PORT &= ~_BV(FLASH);
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_SERIAL:
|
||||
/*
|
||||
* In serial mode, there's nothing to do anymore here.
|
||||
*/
|
||||
break;
|
||||
|
||||
case MODE_UPDOWN:
|
||||
/*
|
||||
* Query the pushbuttons.
|
||||
*
|
||||
* NB: watch out to use PINx for reading, as opposed
|
||||
* to using PORTx which would be the mirror of the
|
||||
* _output_ latch register (resp. pullup configuration
|
||||
* bit for input pins)!
|
||||
*/
|
||||
if (bit_is_clear(PIND, TRIGGER_DOWN))
|
||||
set_pwm(pwm - 10);
|
||||
else if (bit_is_clear(PIND, TRIGGER_UP))
|
||||
set_pwm(pwm + 10);
|
||||
#if HAVE_ADC
|
||||
else if (bit_is_clear(PIND, TRIGGER_ADC))
|
||||
mode = MODE_ADC;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case MODE_ADC:
|
||||
#if HAVE_ADC
|
||||
if (bit_is_set(PIND, TRIGGER_ADC))
|
||||
mode = MODE_UPDOWN;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Start one conversion.
|
||||
*/
|
||||
ADCSRA |= _BV(ADIE);
|
||||
ADCSRA |= _BV(ADSC);
|
||||
}
|
||||
#endif /* HAVE_ADC */
|
||||
break;
|
||||
}
|
||||
|
||||
if (pwm_backup_tmr && --pwm_backup_tmr == 0)
|
||||
{
|
||||
/*
|
||||
* The EEPROM backup timer expired. Save the current
|
||||
* PWM value in EEPROM. Note that this function might
|
||||
* block for a few milliseconds (after writing the
|
||||
* first byte).
|
||||
*/
|
||||
eeprom_write_word(&ee_pwm, pwm);
|
||||
printstr_p(PSTR("[EEPROM updated] "));
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_ADC
|
||||
if (intflags.adc_int)
|
||||
{
|
||||
intflags.adc_int = 0;
|
||||
set_pwm(adcval);
|
||||
}
|
||||
#endif /* HAVE_ADC */
|
||||
|
||||
if (intflags.rx_int)
|
||||
{
|
||||
intflags.rx_int = 0;
|
||||
|
||||
if (rxbuff == 'q')
|
||||
{
|
||||
printstr_p(PSTR("\nThank you for using serial mode."
|
||||
" Good-bye!\n"));
|
||||
mode = MODE_UPDOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode != MODE_SERIAL)
|
||||
{
|
||||
printstr_p(PSTR("\nWelcome at serial control, "
|
||||
"type +/- to adjust, or 0/1 to turn on/off\n"
|
||||
"the LED, q to quit serial mode, "
|
||||
"r to demonstrate a watchdog reset\n"));
|
||||
mode = MODE_SERIAL;
|
||||
}
|
||||
switch (rxbuff)
|
||||
{
|
||||
case '+':
|
||||
set_pwm(pwm + 10);
|
||||
break;
|
||||
|
||||
case '-':
|
||||
set_pwm(pwm - 10);
|
||||
break;
|
||||
|
||||
case '0':
|
||||
set_pwm(0);
|
||||
break;
|
||||
|
||||
case '1':
|
||||
set_pwm(1000);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
printstr_p(PSTR("\nzzzz... zzz..."));
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
sleep_mode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
PRG = stdiodemo
|
||||
OBJ = stdiodemo.o hd44780.o lcd.o uart.o
|
||||
MCU_TARGET = atmega16
|
||||
OPTIMIZE = -Os
|
||||
|
||||
DEFS =
|
||||
LIBS =
|
||||
|
||||
# You should not have to change anything below here.
|
||||
|
||||
CC = avr-gcc
|
||||
|
||||
CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
|
||||
LDFLAGS = -Wl,-Map,$(PRG).map
|
||||
|
||||
OBJCOPY = avr-objcopy
|
||||
OBJDUMP = avr-objdump
|
||||
|
||||
all: $(PRG).elf lst text eeprom
|
||||
|
||||
$(PRG).elf: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak
|
||||
rm -rf *.lst *.map $(EXTRA_CLEAN_FILES)
|
||||
|
||||
lst: $(PRG).lst
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# Rules for building the .text rom images
|
||||
|
||||
text: hex bin srec
|
||||
|
||||
hex: $(PRG).hex
|
||||
bin: $(PRG).bin
|
||||
srec: $(PRG).srec
|
||||
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O ihex $< $@
|
||||
|
||||
%.srec: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O srec $< $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -O binary $< $@
|
||||
|
||||
# Rules for building the .eeprom rom images
|
||||
|
||||
eeprom: ehex ebin esrec
|
||||
|
||||
ehex: $(PRG)_eeprom.hex
|
||||
ebin: $(PRG)_eeprom.bin
|
||||
esrec: $(PRG)_eeprom.srec
|
||||
|
||||
%_eeprom.hex: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
|
||||
|
||||
%_eeprom.srec: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@
|
||||
|
||||
%_eeprom.bin: %.elf
|
||||
$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@
|
||||
|
||||
# Every thing below here is used by avr-libc's build system and can be ignored
|
||||
# by the casual user.
|
||||
|
||||
JPEGFILES = stdiodemo-setup.jpg
|
||||
|
||||
JPEG2PNM = jpegtopnm
|
||||
PNM2EPS = pnmtops
|
||||
JPEGRESOLUTION = 180
|
||||
EXTRA_CLEAN_FILES = *.hex *.bin *.srec *.eps
|
||||
|
||||
dox: ${JPEGFILES:.jpg=.eps}
|
||||
|
||||
%.eps: %.jpg
|
||||
$(JPEG2PNM) $< |\
|
||||
$(PNM2EPS) -noturn -dpi $(JPEGRESOLUTION) -equalpixels \
|
||||
> $@
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* General stdiodemo defines
|
||||
*
|
||||
* $Id: defines.h,v 1.2 2006/10/08 21:47:36 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
/* CPU frequency */
|
||||
#define F_CPU 1000000UL
|
||||
|
||||
/* UART baud rate */
|
||||
#define UART_BAUD 9600
|
||||
|
||||
/* HD44780 LCD port connections */
|
||||
#define HD44780_PORT A
|
||||
#define HD44780_RS PORT6
|
||||
#define HD44780_RW PORT4
|
||||
#define HD44780_E PORT5
|
||||
/* The data bits have to be in ascending order. */
|
||||
#define HD44780_D4 PORT0
|
||||
#define HD44780_D5 PORT1
|
||||
#define HD44780_D6 PORT2
|
||||
#define HD44780_D7 PORT3
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* HD44780 LCD display driver
|
||||
*
|
||||
* The LCD controller is used in 4-bit mode with a full bi-directional
|
||||
* interface (i.e. R/~W is connected) so the busy flag can be read.
|
||||
*
|
||||
* $Id: hd44780.c,v 1.3 2006/10/08 21:47:36 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
#include "hd44780.h"
|
||||
|
||||
#define GLUE(a, b) a##b
|
||||
#define PORT(x) GLUE(PORT, x)
|
||||
#define PIN(x) GLUE(PIN, x)
|
||||
#define DDR(x) GLUE(DDR, x)
|
||||
|
||||
#define HD44780_PORTOUT PORT(HD44780_PORT)
|
||||
#define HD44780_PORTIN PIN(HD44780_PORT)
|
||||
#define HD44780_DDR DDR(HD44780_PORT)
|
||||
|
||||
#define HD44780_DATABITS \
|
||||
(_BV(HD44780_D4)|_BV(HD44780_D5)|_BV(HD44780_D6)|_BV(HD44780_D7))
|
||||
|
||||
#define HD44780_BUSYFLAG 0x80
|
||||
|
||||
/*
|
||||
* Send one pulse to the E signal (enable). Mind the timing
|
||||
* constraints. If readback is set to true, read the HD44780 data
|
||||
* pins right before the falling edge of E, and return that value.
|
||||
*/
|
||||
static inline uint8_t
|
||||
hd44780_pulse_e(bool readback) __attribute__((always_inline));
|
||||
|
||||
static inline uint8_t
|
||||
hd44780_pulse_e(bool readback)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
HD44780_PORTOUT |= _BV(HD44780_E);
|
||||
/*
|
||||
* Guarantee at least 500 ns of pulse width. For high CPU
|
||||
* frequencies, a delay loop is used. For lower frequencies, NOPs
|
||||
* are used, and at or below 1 MHz, the native pulse width will
|
||||
* already be 1 us or more so no additional delays are needed.
|
||||
*/
|
||||
#if F_CPU > 4000000UL
|
||||
_delay_us(0.5);
|
||||
#else
|
||||
/*
|
||||
* When reading back, we need one additional NOP, as the value read
|
||||
* back from the input pin is sampled close to the beginning of a
|
||||
* CPU clock cycle, while the previous edge on the output pin is
|
||||
* generated towards the end of a CPU clock cycle.
|
||||
*/
|
||||
if (readback)
|
||||
__asm__ volatile("nop");
|
||||
# if F_CPU > 1000000UL
|
||||
__asm__ volatile("nop");
|
||||
# if F_CPU > 2000000UL
|
||||
__asm__ volatile("nop");
|
||||
__asm__ volatile("nop");
|
||||
# endif /* F_CPU > 2000000UL */
|
||||
# endif /* F_CPU > 1000000UL */
|
||||
#endif
|
||||
if (readback)
|
||||
x = HD44780_PORTIN & HD44780_DATABITS;
|
||||
else
|
||||
x = 0;
|
||||
HD44780_PORTOUT &= ~_BV(HD44780_E);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send one nibble out to the LCD controller.
|
||||
*/
|
||||
static void
|
||||
hd44780_outnibble(uint8_t n, uint8_t rs)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
HD44780_PORTOUT &= ~_BV(HD44780_RW);
|
||||
if (rs)
|
||||
HD44780_PORTOUT |= _BV(HD44780_RS);
|
||||
else
|
||||
HD44780_PORTOUT &= ~_BV(HD44780_RS);
|
||||
x = (HD44780_PORTOUT & ~HD44780_DATABITS) | ((n << HD44780_D4) & HD44780_DATABITS);
|
||||
HD44780_PORTOUT = x;
|
||||
(void)hd44780_pulse_e(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send one byte to the LCD controller. As we are in 4-bit mode, we
|
||||
* have to send two nibbles.
|
||||
*/
|
||||
void
|
||||
hd44780_outbyte(uint8_t b, uint8_t rs)
|
||||
{
|
||||
hd44780_outnibble(b >> 4, rs);
|
||||
hd44780_outnibble(b & 0xf, rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read one nibble from the LCD controller.
|
||||
*/
|
||||
static uint8_t
|
||||
hd44780_innibble(uint8_t rs)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
HD44780_PORTOUT |= _BV(HD44780_RW);
|
||||
HD44780_DDR &= ~HD44780_DATABITS;
|
||||
if (rs)
|
||||
HD44780_PORTOUT |= _BV(HD44780_RS);
|
||||
else
|
||||
HD44780_PORTOUT &= ~_BV(HD44780_RS);
|
||||
x = hd44780_pulse_e(true);
|
||||
HD44780_DDR |= HD44780_DATABITS;
|
||||
HD44780_PORTOUT &= ~_BV(HD44780_RW);
|
||||
|
||||
return (x & HD44780_DATABITS) >> HD44780_D4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read one byte (i.e. two nibbles) from the LCD controller.
|
||||
*/
|
||||
uint8_t
|
||||
hd44780_inbyte(uint8_t rs)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
x = hd44780_innibble(rs) << 4;
|
||||
x |= hd44780_innibble(rs);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait until the busy flag is cleared.
|
||||
*/
|
||||
void
|
||||
hd44780_wait_ready(void)
|
||||
{
|
||||
while (hd44780_incmd() & HD44780_BUSYFLAG) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the LCD controller.
|
||||
*
|
||||
* The initialization sequence has a mandatory timing so the
|
||||
* controller can safely recognize the type of interface desired.
|
||||
* This is the only area where timed waits are really needed as
|
||||
* the busy flag cannot be probed initially.
|
||||
*/
|
||||
void
|
||||
hd44780_init(void)
|
||||
{
|
||||
|
||||
HD44780_DDR = _BV(HD44780_RS) | _BV(HD44780_RW) | _BV(HD44780_E)
|
||||
| HD44780_DATABITS;
|
||||
|
||||
_delay_ms(15); /* 40 ms needed for Vcc = 2.7 V */
|
||||
hd44780_outnibble(HD44780_FNSET(1, 0, 0) >> 4, 0);
|
||||
_delay_ms(4.1);
|
||||
hd44780_outnibble(HD44780_FNSET(1, 0, 0) >> 4, 0);
|
||||
_delay_ms(0.1);
|
||||
hd44780_outnibble(HD44780_FNSET(1, 0, 0) >> 4, 0);
|
||||
|
||||
hd44780_outnibble(HD44780_FNSET(0, 1, 0) >> 4, 0);
|
||||
hd44780_wait_ready();
|
||||
hd44780_outcmd(HD44780_FNSET(0, 1, 0));
|
||||
hd44780_wait_ready();
|
||||
hd44780_outcmd(HD44780_DISPCTL(0, 0, 0));
|
||||
hd44780_wait_ready();
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* HD44780 LCD display driver
|
||||
*
|
||||
* $Id: hd44780.h,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Send byte b to the LCD. rs is the RS signal (register select), 0
|
||||
* selects instruction register, 1 selects the data register.
|
||||
*/
|
||||
void hd44780_outbyte(uint8_t b, uint8_t rs);
|
||||
|
||||
/*
|
||||
* Read one byte from the LCD controller. rs is the RS signal, 0
|
||||
* selects busy flag (bit 7) and address counter, 1 selects the data
|
||||
* register.
|
||||
*/
|
||||
uint8_t hd44780_inbyte(uint8_t rs);
|
||||
|
||||
/*
|
||||
* Wait for the busy flag to clear.
|
||||
*/
|
||||
void hd44780_wait_ready(void);
|
||||
|
||||
/*
|
||||
* Initialize the LCD controller hardware.
|
||||
*/
|
||||
void hd44780_init(void);
|
||||
|
||||
|
||||
/* Send a command to the LCD controller. */
|
||||
#define hd44780_outcmd(n) hd44780_outbyte((n), 0)
|
||||
|
||||
/* Send a data byte to the LCD controller. */
|
||||
#define hd44780_outdata(n) hd44780_outbyte((n), 1)
|
||||
|
||||
/* Read the address counter and busy flag from the LCD. */
|
||||
#define hd44780_incmd() hd44780_inbyte(0)
|
||||
|
||||
/* Read the current data byte from the LCD. */
|
||||
#define hd44780_indata() hd44780_inbyte(1)
|
||||
|
||||
|
||||
/* Clear LCD display command. */
|
||||
#define HD44780_CLR \
|
||||
0x01
|
||||
|
||||
/* Home cursor command. */
|
||||
#define HD44780_HOME \
|
||||
0x02
|
||||
|
||||
/*
|
||||
* Select the entry mode. inc determines whether the address counter
|
||||
* auto-increments, shift selects an automatic display shift.
|
||||
*/
|
||||
#define HD44780_ENTMODE(inc, shift) \
|
||||
(0x04 | ((inc)? 0x02: 0) | ((shift)? 1: 0))
|
||||
|
||||
/*
|
||||
* Selects disp[lay] on/off, cursor on/off, cursor blink[ing]
|
||||
* on/off.
|
||||
*/
|
||||
#define HD44780_DISPCTL(disp, cursor, blink) \
|
||||
(0x08 | ((disp)? 0x04: 0) | ((cursor)? 0x02: 0) | ((blink)? 1: 0))
|
||||
|
||||
/*
|
||||
* With shift = 1, shift display right or left.
|
||||
* With shift = 0, move cursor right or left.
|
||||
*/
|
||||
#define HD44780_SHIFT(shift, right) \
|
||||
(0x10 | ((shift)? 0x08: 0) | ((right)? 0x04: 0))
|
||||
|
||||
/*
|
||||
* Function set. if8bit selects an 8-bit data path, twoline arranges
|
||||
* for a two-line display, font5x10 selects the 5x10 dot font (5x8
|
||||
* dots if clear).
|
||||
*/
|
||||
#define HD44780_FNSET(if8bit, twoline, font5x10) \
|
||||
(0x20 | ((if8bit)? 0x10: 0) | ((twoline)? 0x08: 0) | \
|
||||
((font5x10)? 0x04: 0))
|
||||
|
||||
/*
|
||||
* Set the next character generator address to addr.
|
||||
*/
|
||||
#define HD44780_CGADDR(addr) \
|
||||
(0x40 | ((addr) & 0x3f))
|
||||
|
||||
/*
|
||||
* Set the next display address to addr.
|
||||
*/
|
||||
#define HD44780_DDADDR(addr) \
|
||||
(0x80 | ((addr) & 0x7f))
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo, upper layer of LCD driver.
|
||||
*
|
||||
* $Id: lcd.c,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#include <util/delay.h>
|
||||
|
||||
#include "hd44780.h"
|
||||
#include "lcd.h"
|
||||
|
||||
/*
|
||||
* Setup the LCD controller. First, call the hardware initialization
|
||||
* function, then adjust the display attributes we want.
|
||||
*/
|
||||
void
|
||||
lcd_init(void)
|
||||
{
|
||||
|
||||
hd44780_init();
|
||||
|
||||
/*
|
||||
* Clear the display.
|
||||
*/
|
||||
hd44780_outcmd(HD44780_CLR);
|
||||
hd44780_wait_ready();
|
||||
|
||||
/*
|
||||
* Entry mode: auto-increment address counter, no display shift in
|
||||
* effect.
|
||||
*/
|
||||
hd44780_outcmd(HD44780_ENTMODE(1, 0));
|
||||
hd44780_wait_ready();
|
||||
|
||||
/*
|
||||
* Enable display, activate non-blinking cursor.
|
||||
*/
|
||||
hd44780_outcmd(HD44780_DISPCTL(1, 1, 0));
|
||||
hd44780_wait_ready();
|
||||
}
|
||||
|
||||
/*
|
||||
* Send character c to the LCD display. After a '\n' has been seen,
|
||||
* the next character will first clear the display.
|
||||
*/
|
||||
int
|
||||
lcd_putchar(char c, FILE *unused)
|
||||
{
|
||||
static bool nl_seen;
|
||||
|
||||
if (nl_seen && c != '\n')
|
||||
{
|
||||
/*
|
||||
* First character after newline, clear display and home cursor.
|
||||
*/
|
||||
hd44780_wait_ready();
|
||||
hd44780_outcmd(HD44780_CLR);
|
||||
hd44780_wait_ready();
|
||||
hd44780_outcmd(HD44780_HOME);
|
||||
hd44780_wait_ready();
|
||||
hd44780_outcmd(HD44780_DDADDR(0));
|
||||
|
||||
nl_seen = false;
|
||||
}
|
||||
if (c == '\n')
|
||||
{
|
||||
nl_seen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hd44780_wait_ready();
|
||||
hd44780_outdata(c);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo, upper layer of LCD driver.
|
||||
*
|
||||
* $Id: lcd.h,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize LCD controller. Performs a software reset.
|
||||
*/
|
||||
void lcd_init(void);
|
||||
|
||||
/*
|
||||
* Send one character to the LCD.
|
||||
*/
|
||||
int lcd_putchar(char c, FILE *stream);
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo
|
||||
*
|
||||
* $Id: stdiodemo.c,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include <util/delay.h>
|
||||
|
||||
#include "lcd.h"
|
||||
#include "uart.h"
|
||||
|
||||
/*
|
||||
* Do all the startup-time peripheral initializations.
|
||||
*/
|
||||
static void
|
||||
ioinit(void)
|
||||
{
|
||||
uart_init();
|
||||
lcd_init();
|
||||
}
|
||||
|
||||
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
|
||||
FILE lcd_str = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
|
||||
static void
|
||||
delay_1s(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
_delay_ms(10);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
uint8_t i;
|
||||
char buf[20], s[20];
|
||||
|
||||
ioinit();
|
||||
|
||||
stdout = stdin = &uart_str;
|
||||
stderr = &lcd_str;
|
||||
|
||||
fprintf(stderr, "Hello world!\n");
|
||||
|
||||
for (;;)
|
||||
{
|
||||
printf_P(PSTR("Enter command: "));
|
||||
if (fgets(buf, sizeof buf - 1, stdin) == NULL)
|
||||
break;
|
||||
if (tolower(buf[0]) == 'q')
|
||||
break;
|
||||
|
||||
switch (tolower(buf[0]))
|
||||
{
|
||||
default:
|
||||
printf("Unknown command: %s\n", buf);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (sscanf(buf, "%*s %s", s) > 0)
|
||||
{
|
||||
fprintf(&lcd_str, "Got %s\n", s);
|
||||
printf("OK\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("sscanf() failed\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
if (sscanf(buf, "%*s %s", s) > 0)
|
||||
{
|
||||
fprintf(&uart_str, "Got %s\n", s);
|
||||
printf("OK\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("sscanf() failed\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Bye-bye");
|
||||
delay_1s();
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
putc('.', stderr);
|
||||
delay_1s();
|
||||
}
|
||||
fprintf(stderr, "\n ");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo, UART implementation
|
||||
*
|
||||
* $Id: uart.c,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "uart.h"
|
||||
|
||||
/*
|
||||
* Initialize the UART to 9600 Bd, tx/rx, 8N1.
|
||||
*/
|
||||
void
|
||||
uart_init(void)
|
||||
{
|
||||
#if F_CPU < 2000000UL && defined(U2X)
|
||||
UCSRA = _BV(U2X); /* improve baud rate error by using 2x clk */
|
||||
UBRRL = (F_CPU / (8UL * UART_BAUD)) - 1;
|
||||
#else
|
||||
UBRRL = (F_CPU / (16UL * UART_BAUD)) - 1;
|
||||
#endif
|
||||
UCSRB = _BV(TXEN) | _BV(RXEN); /* tx/rx enable */
|
||||
}
|
||||
|
||||
/*
|
||||
* Send character c down the UART Tx, wait until tx holding register
|
||||
* is empty.
|
||||
*/
|
||||
int
|
||||
uart_putchar(char c, FILE *stream)
|
||||
{
|
||||
|
||||
if (c == '\a')
|
||||
{
|
||||
fputs("*ring*\n", stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
uart_putchar('\r', stream);
|
||||
loop_until_bit_is_set(UCSRA, UDRE);
|
||||
UDR = c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a character from the UART Rx.
|
||||
*
|
||||
* This features a simple line-editor that allows to delete and
|
||||
* re-edit the characters entered, until either CR or NL is entered.
|
||||
* Printable characters entered will be echoed using uart_putchar().
|
||||
*
|
||||
* Editing characters:
|
||||
*
|
||||
* . \b (BS) or \177 (DEL) delete the previous character
|
||||
* . ^u kills the entire input buffer
|
||||
* . ^w deletes the previous word
|
||||
* . ^r sends a CR, and then reprints the buffer
|
||||
* . \t will be replaced by a single space
|
||||
*
|
||||
* All other control characters will be ignored.
|
||||
*
|
||||
* The internal line buffer is RX_BUFSIZE (80) characters long, which
|
||||
* includes the terminating \n (but no terminating \0). If the buffer
|
||||
* is full (i. e., at RX_BUFSIZE-1 characters in order to keep space for
|
||||
* the trailing \n), any further input attempts will send a \a to
|
||||
* uart_putchar() (BEL character), although line editing is still
|
||||
* allowed.
|
||||
*
|
||||
* Input errors while talking to the UART will cause an immediate
|
||||
* return of -1 (error indication). Notably, this will be caused by a
|
||||
* framing error (e. g. serial line "break" condition), by an input
|
||||
* overrun, and by a parity error (if parity was enabled and automatic
|
||||
* parity recognition is supported by hardware).
|
||||
*
|
||||
* Successive calls to uart_getchar() will be satisfied from the
|
||||
* internal buffer until that buffer is emptied again.
|
||||
*/
|
||||
int
|
||||
uart_getchar(FILE *stream)
|
||||
{
|
||||
uint8_t c;
|
||||
char *cp, *cp2;
|
||||
static char b[RX_BUFSIZE];
|
||||
static char *rxp;
|
||||
|
||||
if (rxp == 0)
|
||||
for (cp = b;;)
|
||||
{
|
||||
loop_until_bit_is_set(UCSRA, RXC);
|
||||
if (UCSRA & _BV(FE))
|
||||
return _FDEV_EOF;
|
||||
if (UCSRA & _BV(DOR))
|
||||
return _FDEV_ERR;
|
||||
c = UDR;
|
||||
/* behaviour similar to Unix stty ICRNL */
|
||||
if (c == '\r')
|
||||
c = '\n';
|
||||
if (c == '\n')
|
||||
{
|
||||
*cp = c;
|
||||
uart_putchar(c, stream);
|
||||
rxp = b;
|
||||
break;
|
||||
}
|
||||
else if (c == '\t')
|
||||
c = ' ';
|
||||
|
||||
if ((c >= (uint8_t)' ' && c <= (uint8_t)'\x7e') ||
|
||||
c >= (uint8_t)'\xa0')
|
||||
{
|
||||
if (cp == b + RX_BUFSIZE - 1)
|
||||
uart_putchar('\a', stream);
|
||||
else
|
||||
{
|
||||
*cp++ = c;
|
||||
uart_putchar(c, stream);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'c' & 0x1f:
|
||||
return -1;
|
||||
|
||||
case '\b':
|
||||
case '\x7f':
|
||||
if (cp > b)
|
||||
{
|
||||
uart_putchar('\b', stream);
|
||||
uart_putchar(' ', stream);
|
||||
uart_putchar('\b', stream);
|
||||
cp--;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r' & 0x1f:
|
||||
uart_putchar('\r', stream);
|
||||
for (cp2 = b; cp2 < cp; cp2++)
|
||||
uart_putchar(*cp2, stream);
|
||||
break;
|
||||
|
||||
case 'u' & 0x1f:
|
||||
while (cp > b)
|
||||
{
|
||||
uart_putchar('\b', stream);
|
||||
uart_putchar(' ', stream);
|
||||
uart_putchar('\b', stream);
|
||||
cp--;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w' & 0x1f:
|
||||
while (cp > b && cp[-1] != ' ')
|
||||
{
|
||||
uart_putchar('\b', stream);
|
||||
uart_putchar(' ', stream);
|
||||
uart_putchar('\b', stream);
|
||||
cp--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c = *rxp++;
|
||||
if (c == '\n')
|
||||
rxp = 0;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Stdio demo, UART declarations
|
||||
*
|
||||
* $Id: uart.h,v 1.1 2005/12/28 21:38:59 joerg_wunsch Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Perform UART startup initialization.
|
||||
*/
|
||||
void uart_init(void);
|
||||
|
||||
/*
|
||||
* Send one character to the UART.
|
||||
*/
|
||||
int uart_putchar(char c, FILE *stream);
|
||||
|
||||
/*
|
||||
* Size of internal line buffer used by uart_getchar().
|
||||
*/
|
||||
#define RX_BUFSIZE 80
|
||||
|
||||
/*
|
||||
* Receive one character from the UART. The actual reception is
|
||||
* line-buffered, and one character is returned from the buffer at
|
||||
* each invokation.
|
||||
*/
|
||||
int uart_getchar(FILE *stream);
|
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# $Id: Makefile,v 1.1 2002/12/18 22:35:38 joerg_wunsch Exp $
|
||||
#
|
||||
CC= avr-gcc
|
||||
#MCU=atmega8
|
||||
#MCU=atmega16
|
||||
#MCU=atmega32
|
||||
#MCU=atmega163
|
||||
#MCU=atmega323
|
||||
MCU=atmega128
|
||||
|
||||
CFLAGS= -O -g -Wall -ffreestanding -mmcu=$(MCU)
|
||||
|
||||
.SUFFIXES: .s .bin .out .hex .srec
|
||||
|
||||
.c.s:
|
||||
$(CC) $(CFLAGS) -S $<
|
||||
|
||||
.S.o:
|
||||
$(CC) $(ASFLAGS) -c $<
|
||||
|
||||
.o.out:
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
.out.bin:
|
||||
avr-objcopy -O binary $< $@
|
||||
|
||||
.out.hex:
|
||||
avr-objcopy -O ihex $< $@
|
||||
|
||||
.out.srec:
|
||||
avr-objcopy -O srec $< $@
|
||||
|
||||
all: twitest.bin
|
||||
|
||||
OBJS=twitest.o
|
||||
twitest.out: $(OBJS)
|
||||
$(CC) -o twitest.out $(CFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS)
|
||||
|
||||
clean:
|
||||
rm -f *~ *.out *.bin *.hex *.srec *.s *.o *.pdf *core
|
|
@ -0,0 +1,514 @@
|
|||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* $Id: twitest.c,v 1.6 2005/11/05 22:32:46 joerg_wunsch Exp $ */
|
||||
|
||||
/*
|
||||
* Simple demo program that talks to a 24Cxx I²C EEPROM using the
|
||||
* builtin TWI interface of an ATmega device.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <util/twi.h> /* Note [1] */
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
/*
|
||||
* System clock in Hz.
|
||||
*/
|
||||
#define F_CPU 14745600UL /* Note [2] */
|
||||
|
||||
/*
|
||||
* Compatibility defines. This should work on ATmega8, ATmega16,
|
||||
* ATmega163, ATmega323 and ATmega128 (IOW: on all devices that
|
||||
* provide a builtin TWI interface).
|
||||
*
|
||||
* On the 128, it defaults to USART 1.
|
||||
*/
|
||||
#ifndef UCSRB
|
||||
# ifdef UCSR1A /* ATmega128 */
|
||||
# define UCSRA UCSR1A
|
||||
# define UCSRB UCSR1B
|
||||
# define UBRR UBRR1L
|
||||
# define UDR UDR1
|
||||
# else /* ATmega8 */
|
||||
# define UCSRA USR
|
||||
# define UCSRB UCR
|
||||
# endif
|
||||
#endif
|
||||
#ifndef UBRR
|
||||
# define UBRR UBRRL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Note [3]
|
||||
* TWI address for 24Cxx EEPROM:
|
||||
*
|
||||
* 1 0 1 0 E2 E1 E0 R/~W 24C01/24C02
|
||||
* 1 0 1 0 E2 E1 A8 R/~W 24C04
|
||||
* 1 0 1 0 E2 A9 A8 R/~W 24C08
|
||||
* 1 0 1 0 A10 A9 A8 R/~W 24C16
|
||||
*/
|
||||
#define TWI_SLA_24CXX 0xa0 /* E2 E1 E0 = 0 0 0 */
|
||||
|
||||
/*
|
||||
* Maximal number of iterations to wait for a device to respond for a
|
||||
* selection. Should be large enough to allow for a pending write to
|
||||
* complete, but low enough to properly abort an infinite loop in case
|
||||
* a slave is broken or not present at all. With 100 kHz TWI clock,
|
||||
* transfering the start condition and SLA+R/W packet takes about 10
|
||||
* µs. The longest write period is supposed to not exceed ~ 10 ms.
|
||||
* Thus, normal operation should not require more than 100 iterations
|
||||
* to get the device to respond to a selection.
|
||||
*/
|
||||
#define MAX_ITER 200
|
||||
|
||||
/*
|
||||
* Number of bytes that can be written in a row, see comments for
|
||||
* ee24xx_write_page() below. Some vendor's devices would accept 16,
|
||||
* but 8 seems to be the lowest common denominator.
|
||||
*
|
||||
* Note that the page size must be a power of two, this simplifies the
|
||||
* page boundary calculations below.
|
||||
*/
|
||||
#define PAGE_SIZE 8
|
||||
|
||||
/*
|
||||
* Saved TWI status register, for error messages only. We need to
|
||||
* save it in a variable, since the datasheet only guarantees the TWSR
|
||||
* register to have valid contents while the TWINT bit in TWCR is set.
|
||||
*/
|
||||
uint8_t twst;
|
||||
|
||||
/*
|
||||
* Do all the startup-time peripheral initializations: UART (for our
|
||||
* debug/test output), and TWI clock.
|
||||
*/
|
||||
void
|
||||
ioinit(void)
|
||||
{
|
||||
|
||||
#if F_CPU <= 1000000UL
|
||||
/*
|
||||
* Note [4]
|
||||
* Slow system clock, double Baud rate to improve rate error.
|
||||
*/
|
||||
UCSRA = _BV(U2X);
|
||||
UBRR = (F_CPU / (8 * 9600UL)) - 1; /* 9600 Bd */
|
||||
#else
|
||||
UBRR = (F_CPU / (16 * 9600UL)) - 1; /* 9600 Bd */
|
||||
#endif
|
||||
UCSRB = _BV(TXEN); /* tx enable */
|
||||
|
||||
/* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
|
||||
#if defined(TWPS0)
|
||||
/* has prescaler (mega128 & newer) */
|
||||
TWSR = 0;
|
||||
#endif
|
||||
|
||||
#if F_CPU < 3600000UL
|
||||
TWBR = 10; /* smallest TWBR value, see note [5] */
|
||||
#else
|
||||
TWBR = (F_CPU / 100000UL - 16) / 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Note [6]
|
||||
* Send character c down the UART Tx, wait until tx holding register
|
||||
* is empty.
|
||||
*/
|
||||
int
|
||||
uart_putchar(char c, FILE *unused)
|
||||
{
|
||||
|
||||
if (c == '\n')
|
||||
uart_putchar('\r', 0);
|
||||
loop_until_bit_is_set(UCSRA, UDRE);
|
||||
UDR = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note [7]
|
||||
*
|
||||
* Read "len" bytes from EEPROM starting at "eeaddr" into "buf".
|
||||
*
|
||||
* This requires two bus cycles: during the first cycle, the device
|
||||
* will be selected (master transmitter mode), and the address
|
||||
* transfered. Address bits exceeding 256 are transfered in the
|
||||
* E2/E1/E0 bits (subaddress bits) of the device selector.
|
||||
*
|
||||
* The second bus cycle will reselect the device (repeated start
|
||||
* condition, going into master receiver mode), and transfer the data
|
||||
* from the device to the TWI master. Multiple bytes can be
|
||||
* transfered by ACKing the client's transfer. The last transfer will
|
||||
* be NACKed, which the client will take as an indication to not
|
||||
* initiate further transfers.
|
||||
*/
|
||||
int
|
||||
ee24xx_read_bytes(uint16_t eeaddr, int len, uint8_t *buf)
|
||||
{
|
||||
uint8_t sla, twcr, n = 0;
|
||||
int rv = 0;
|
||||
|
||||
/* patch high bits of EEPROM address into SLA */
|
||||
sla = TWI_SLA_24CXX | (((eeaddr >> 8) & 0x07) << 1);
|
||||
|
||||
/*
|
||||
* Note [8]
|
||||
* First cycle: master transmitter mode
|
||||
*/
|
||||
restart:
|
||||
if (n++ >= MAX_ITER)
|
||||
return -1;
|
||||
begin:
|
||||
|
||||
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_REP_START: /* OK, but should not happen */
|
||||
case TW_START:
|
||||
break;
|
||||
|
||||
case TW_MT_ARB_LOST: /* Note [9] */
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
return -1; /* error: not in start condition */
|
||||
/* NB: do /not/ send stop condition */
|
||||
}
|
||||
|
||||
/* Note [10] */
|
||||
/* send SLA+W */
|
||||
TWDR = sla | TW_WRITE;
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MT_SLA_ACK:
|
||||
break;
|
||||
|
||||
case TW_MT_SLA_NACK: /* nack during select: device busy writing */
|
||||
/* Note [11] */
|
||||
goto restart;
|
||||
|
||||
case TW_MT_ARB_LOST: /* re-arbitrate */
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
goto error; /* must send stop condition */
|
||||
}
|
||||
|
||||
TWDR = eeaddr; /* low 8 bits of addr */
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MT_DATA_ACK:
|
||||
break;
|
||||
|
||||
case TW_MT_DATA_NACK:
|
||||
goto quit;
|
||||
|
||||
case TW_MT_ARB_LOST:
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
goto error; /* must send stop condition */
|
||||
}
|
||||
|
||||
/*
|
||||
* Note [12]
|
||||
* Next cycle(s): master receiver mode
|
||||
*/
|
||||
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send (rep.) start condition */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_START: /* OK, but should not happen */
|
||||
case TW_REP_START:
|
||||
break;
|
||||
|
||||
case TW_MT_ARB_LOST:
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* send SLA+R */
|
||||
TWDR = sla | TW_READ;
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MR_SLA_ACK:
|
||||
break;
|
||||
|
||||
case TW_MR_SLA_NACK:
|
||||
goto quit;
|
||||
|
||||
case TW_MR_ARB_LOST:
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (twcr = _BV(TWINT) | _BV(TWEN) | _BV(TWEA) /* Note [13] */;
|
||||
len > 0;
|
||||
len--)
|
||||
{
|
||||
if (len == 1)
|
||||
twcr = _BV(TWINT) | _BV(TWEN); /* send NAK this time */
|
||||
TWCR = twcr; /* clear int to start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MR_DATA_NACK:
|
||||
len = 0; /* force end of loop */
|
||||
/* FALLTHROUGH */
|
||||
case TW_MR_DATA_ACK:
|
||||
*buf++ = TWDR;
|
||||
rv++;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
quit:
|
||||
/* Note [14] */
|
||||
TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* send stop condition */
|
||||
|
||||
return rv;
|
||||
|
||||
error:
|
||||
rv = -1;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write "len" bytes into EEPROM starting at "eeaddr" from "buf".
|
||||
*
|
||||
* This is a bit simpler than the previous function since both, the
|
||||
* address and the data bytes will be transfered in master transmitter
|
||||
* mode, thus no reselection of the device is necessary. However, the
|
||||
* EEPROMs are only capable of writing one "page" simultaneously, so
|
||||
* care must be taken to not cross a page boundary within one write
|
||||
* cycle. The amount of data one page consists of varies from
|
||||
* manufacturer to manufacturer: some vendors only use 8-byte pages
|
||||
* for the smaller devices, and 16-byte pages for the larger devices,
|
||||
* while other vendors generally use 16-byte pages. We thus use the
|
||||
* smallest common denominator of 8 bytes per page, declared by the
|
||||
* macro PAGE_SIZE above.
|
||||
*
|
||||
* The function simply returns after writing one page, returning the
|
||||
* actual number of data byte written. It is up to the caller to
|
||||
* re-invoke it in order to write further data.
|
||||
*/
|
||||
int
|
||||
ee24xx_write_page(uint16_t eeaddr, int len, uint8_t *buf)
|
||||
{
|
||||
uint8_t sla, n = 0;
|
||||
int rv = 0;
|
||||
uint16_t endaddr;
|
||||
|
||||
if (eeaddr + len < (eeaddr | (PAGE_SIZE - 1)))
|
||||
endaddr = eeaddr + len;
|
||||
else
|
||||
endaddr = (eeaddr | (PAGE_SIZE - 1)) + 1;
|
||||
len = endaddr - eeaddr;
|
||||
|
||||
/* patch high bits of EEPROM address into SLA */
|
||||
sla = TWI_SLA_24CXX | (((eeaddr >> 8) & 0x07) << 1);
|
||||
|
||||
restart:
|
||||
if (n++ >= MAX_ITER)
|
||||
return -1;
|
||||
begin:
|
||||
|
||||
/* Note [15] */
|
||||
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_REP_START: /* OK, but should not happen */
|
||||
case TW_START:
|
||||
break;
|
||||
|
||||
case TW_MT_ARB_LOST:
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
return -1; /* error: not in start condition */
|
||||
/* NB: do /not/ send stop condition */
|
||||
}
|
||||
|
||||
/* send SLA+W */
|
||||
TWDR = sla | TW_WRITE;
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MT_SLA_ACK:
|
||||
break;
|
||||
|
||||
case TW_MT_SLA_NACK: /* nack during select: device busy writing */
|
||||
goto restart;
|
||||
|
||||
case TW_MT_ARB_LOST: /* re-arbitrate */
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
goto error; /* must send stop condition */
|
||||
}
|
||||
|
||||
TWDR = eeaddr; /* low 8 bits of addr */
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MT_DATA_ACK:
|
||||
break;
|
||||
|
||||
case TW_MT_DATA_NACK:
|
||||
goto quit;
|
||||
|
||||
case TW_MT_ARB_LOST:
|
||||
goto begin;
|
||||
|
||||
default:
|
||||
goto error; /* must send stop condition */
|
||||
}
|
||||
|
||||
for (; len > 0; len--)
|
||||
{
|
||||
TWDR = *buf++;
|
||||
TWCR = _BV(TWINT) | _BV(TWEN); /* start transmission */
|
||||
while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */
|
||||
switch ((twst = TW_STATUS))
|
||||
{
|
||||
case TW_MT_DATA_NACK:
|
||||
goto error; /* device write protected -- Note [16] */
|
||||
|
||||
case TW_MT_DATA_ACK:
|
||||
rv++;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
quit:
|
||||
TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* send stop condition */
|
||||
|
||||
return rv;
|
||||
|
||||
error:
|
||||
rv = -1;
|
||||
goto quit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper around ee24xx_write_page() that repeats calling this
|
||||
* function until either an error has been returned, or all bytes
|
||||
* have been written.
|
||||
*/
|
||||
int
|
||||
ee24xx_write_bytes(uint16_t eeaddr, int len, uint8_t *buf)
|
||||
{
|
||||
int rv, total;
|
||||
|
||||
total = 0;
|
||||
do
|
||||
{
|
||||
#if DEBUG
|
||||
printf("Calling ee24xx_write_page(%d, %d, %p)",
|
||||
eeaddr, len, buf);
|
||||
#endif
|
||||
rv = ee24xx_write_page(eeaddr, len, buf);
|
||||
#if DEBUG
|
||||
printf(" => %d\n", rv);
|
||||
#endif
|
||||
if (rv == -1)
|
||||
return -1;
|
||||
eeaddr += rv;
|
||||
len -= rv;
|
||||
buf += rv;
|
||||
total += rv;
|
||||
}
|
||||
while (len > 0);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
void
|
||||
error(void)
|
||||
{
|
||||
|
||||
printf("error: TWI status %#x\n", twst);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
|
||||
void
|
||||
main(void)
|
||||
{
|
||||
uint16_t a;
|
||||
int rv;
|
||||
uint8_t b[16];
|
||||
uint8_t x;
|
||||
|
||||
ioinit();
|
||||
|
||||
stdout = &mystdout;
|
||||
|
||||
for (a = 0; a < 256;)
|
||||
{
|
||||
printf("%#04x: ", a);
|
||||
rv = ee24xx_read_bytes(a, 16, b);
|
||||
if (rv <= 0)
|
||||
error();
|
||||
if (rv < 16)
|
||||
printf("warning: short read %d\n", rv);
|
||||
a += rv;
|
||||
for (x = 0; x < rv; x++)
|
||||
printf("%02x ", b[x]);
|
||||
putchar('\n');
|
||||
}
|
||||
#define EE_WRITE(addr, str) ee24xx_write_bytes(addr, sizeof(str)-1, str)
|
||||
rv = EE_WRITE(55, "The quick brown fox jumps over the lazy dog.");
|
||||
if (rv < 0)
|
||||
error();
|
||||
printf("Wrote %d bytes.\n", rv);
|
||||
for (a = 0; a < 256;)
|
||||
{
|
||||
printf("%#04x: ", a);
|
||||
rv = ee24xx_read_bytes(a, 16, b);
|
||||
if (rv <= 0)
|
||||
error();
|
||||
if (rv < 16)
|
||||
printf("warning: short read %d\n", rv);
|
||||
a += rv;
|
||||
for (x = 0; x < rv; x++)
|
||||
printf("%02x ", b[x]);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
printf("done.\n");
|
||||
|
||||
}
|
Reference in a new issue