AVR
Powering on my STK 500
You can use any polarity, but if you can, please use a negative center connector 10-15V DC.
Background from STK500 Manual:
An external 10 - 15V DC power supply is required. The input circuit is a full bridge rectifier, and the STK500 automatically handles both positive or negative center connectors. If a positive center connector is used, it can be impossible to turn the STK500 off since the power switch disconnects the GND terminal. In this case, GND can be supplied through the RS-232 cable shield if connected or through alternative GND connections.
Getting on and connecting the STK500
$ dmesg
pl2303 converter now attached to ttyUSB0
Which Socket to use?
- ATMEGA16L8PU -> SCKT3100A3
- ATMEGA8 -> SCKT3200A2
STK500 Jumper Settings
- VTARGET -> Connected
- AREF -> Connected
- RESET -> Connected
- XTAL1 -> Connected
- OSCSEL -> PIN 2-3 (Select Xtal as clock source)
Clock Settings
- XTAL1 connected: STK500 internal clock is used
- OSCSEL1-2: On-board software clock
- OSCSEL2-3: On-board crystal signal
AVRDUDE commands
List supported MCUs:
avrdude -c stk500v2 -p ?
Read lfuse:
avrdude -c stk500v2 -p m16 -P /dev/ttyUSB0 -U lfuse:r:lfuse.hex:b
Read hfuse:
avrdude -c stk500v2 -p m16 -P /dev/ttyUSB0 -U hfuse:r:hfuse.hex:b
Set lfuse:
avrdude -c stk500v2 -p m16 -P /dev/ttyUSB0 -U lfuse:w:0b11110000:m
ATMEGA16L Fuses
Read the datasheet, and search for "Fuse Low Byte" and "Fuse High Byte". For
my configuration, for an external clock, 0000 needs to be written to
CKSEL3...CKSEL0
ATMEGA8 Fuses
STK500 is having a resonator on it's own, so AVR has to be configured to use an external clock source. For this, the external clock options needs to be selected, and that is the same as in the case of ATMEGA16L.
Trying out V-USB
This is using an ATTINY2313 chip. First I want to see if I can get the lfuse to read.
Read lfuse:
$ avrdude -c stk500v2 -p t2313 -P /dev/ttyUSB0 -U lfuse:r:lfuse.hex:b
$ cat lfuse.hex
0b1100100
Read hfuse:
avrdude -c stk500v2 -p t2313 -P /dev/ttyUSB0 -U hfuse:r:hfuse.hex:b
$ cat hfuse.hex
0b11011111
This more or less matches the datasheet. I say more or less, as one section
suggests that the default value for CKSEL3..1 is 001 whereas the document
also mentions:
The device is shipped with CKSEL =
0100, SUT =10, and CKDIV8 programmed.
Clock configuration
For an xtal configuration, I need CKSEL3..1 to be 111 for crystal above 8 Mhz.
CKSEL0 fuse together with SUT1..0 determines startup.
Also looking at examples with V-USB:
$(AVRDUDE) -U hfuse:w:0xdb:m -U lfuse:w:0xef:m
Meaning hfuse to be on 0b11011011 and lfuse to be on 0b11101111
ATTINY2313 HFUSE
- DWEN Enables debugWIRE:
- Factory:
1 - VUSB:
1 - EESAVE Preserves EEPROM memory during Chip Erase operation
- Factory:
1 - VUSB:
1 - SPIEN Enables serial programming and downloading of data to device
- Factory:
0 - VUSB:
0 - WDTON Sets watchdog timer permanently on
- Factory:
1 - VUSB:
1 - BODLEVEL2
- Factory:
1 - VUSB:
1 - BODLEVEL1
- Factory:
1 - VUSB:
0 - BODLEVEL0
- Factory:
1 - VUSB:
1 - RSTDISBL Disables external reset
- Factory:
1 - VUSB:
1
What changes in VUSB configuration is that Brown-Out detection voltage is put to 2.7V
ATTINY2313 LFUSE
- CKDIV8 Divides clock by 8
- Factory:
0 - VUSB:
1 - CKOUT Outputs system clock on port pin
- Factory:
1 - VUSB:
1 - SUT1
- Factory:
1 - VUSB:
1 - SUT0
- Factory:
0 - VUSB:
0 - CKSEL3
- Factory:
0 - VUSB:
1 - CKSEL2
- Factory:
1 - VUSB:
1 - CKSEL1
- Factory:
0 - VUSB:
1 - CKSEL0
- Factory:
0 - VUSB:
1
This means that we will not use a prescaler and CKSEL3..1 will be 111
meaning external Xtal above 8 MHz. CKSEL0 becomes 1 which togther with
SUT0 being 0 and SUT1 being 1 means Crystal Oscillator, fast
rising power configuration.
Let's program those bytes:
avrdude -c stk500v2 -p t2313 -P /dev/ttyUSB0 -U hfuse:w:0xdb:m -U lfuse:w:0xef:m
And now let's try to blink it. This is blink.c
#define F_CPU 12000000
#define LED_DELAY 200
#include <avr/io.h>
#include <util/delay.h>
int main(void) {
DDRB = 0xff;
for(;;) {
_delay_ms(LED_DELAY);
PORTB = 0b00001000;
_delay_ms(LED_DELAY);
PORTB = 0b00000100;
_delay_ms(LED_DELAY);
PORTB = 0b00000010;
_delay_ms(LED_DELAY);
PORTB = 0b00000001;
_delay_ms(LED_DELAY);
PORTB = 0b00000010;
_delay_ms(LED_DELAY);
PORTB = 0b00000100;
}
}
And this is my Makefile:
:::make
MMCU=attiny2313
AVRDUDE_DEVICE=t2313
CC=avr-gcc
OBJCOPY=avr-objcopy
CMPNAME=blink
STKPORT=/dev/ttyUSB0
CFLAGS=-g -mmcu=$(MMCU) -v
$(CMPNAME).hex : $(CMPNAME).out
$(OBJCOPY) -j .text -O ihex $(CMPNAME).out $@
$(CMPNAME).out : $(CMPNAME).o
$(CC) $(CFLAGS) -o $@ -Wl,-Map,$(CMPNAME).map $<
$(CMPNAME).o : $(CMPNAME).c
$(CC) $(CFLAGS) -Os -c $<
$(CMPNAME).s: $(CMPNAME).c
$(CC) -S $(CFLAGS) -o $<
program: $(CMPNAME).hex
avrdude -p $(AVRDUDE_DEVICE) -c stk500v2 -P $(STKPORT) -U flash:w:$(CMPNAME).hex:i
fuses:
avrdude -p $(AVRDUDE_DEVICE) -c stk500v2 -P $(STKPORT) -U hfuse:w:0xdb:m -U lfuse:w:0xef:m
clean:
rm -f *.o *.out *.map *.hex
.PHONY: clean program fuses
Making a timer out of ATMEGA8-16U
Goal is to creata timer that measures 10 minutes using the internal oscillator and nothing more.
Linux setup
- You want to add yourself to the
uucpgroup to be able to access serial devices.
STK500 Setup
Hardware Setup
- Microcontroller goes to the green socket
- ISP cable runs to
SPROG2fromISP6PIN - Jumper setup:
VTARGET- connectedAREF- connectedRESET- connectedXTAL1- disconnected (we do not provide clock from STK500)OSCSEL- does not matterBSEL2- disconnected (related to high voltage programming)PJUMP- disconnected (related to high voltage programming)
Software
Read lfuse and hfuse (I had to re-connect xtal for these)
avrdude -c stk500v2 -P /dev/ttyUSB0 -p m8 -U lfuse:r:lfuse-origin.hex:b
avrdude -c stk500v2 -P /dev/ttyUSB0 -p m8 -U hfuse:r:hfuse-origin.hex:b
Fuses were:
- lfuse: 0b11100000
- hfuse: 0b11011001
Write fuses so chip will use internal oscillator. From datasheet, in order
to have internal RC oscillator used I need CKSEL3..0 set as: 0100 - 0001
For the 1MHz option CKSEL3..0 needs to be 0001
HFUSE explanation (highest bit first)
- RSTDISBL- 1 - PC6 is reset pin
- WDTON - 1 - WDT enabled by WDTCR
- SPIEN - 0 - Serial programming enabled
- CKOPT - 1 - Oscillator options
- EESAVE - 1 - EEPROM not preserved through chip erase
- BOOTSZ1 - 0 - Boot size related
- BOOTSZ0 - 0 - Boot size related
- BOOTRST - 1 - Boot reset vector
HFUSE can stay as it is
LFUSE explanation (highest bit first)
- BODLEVEL - 1 - Brown out related
- BODEN - 1 - Brown out related
- SUT1 - 1 - start-up time related
- SUT0 - 0 - start-up time related
- CKSEL3 - 0 - OK
- CKSEL2 - 0 - OK
- CKSEL1 - 0 - OK
- CKSEL0 - 0 - Shall be changed to 1
New values then:
- lfuse 0b11100001 - 0xe1
- hfuse 0b11011001 - 0xd9
avrdude -c stk500v2 -p m8 -P /dev/ttyUSB0 -U hfuse:w:0xd9:m -U lfuse:w:0xe1:m
Now xtal jumper removed from STK500, and I can read fuses again.
And now let's try to blink it via PORTC. This is blink.c
#define F_CPU 1000000
#define LED_DELAY 200
#include <avr/io.h>
#include <util/delay.h>
int main(void) {
DDRC = 0xff;
for(;;) {
_delay_ms(LED_DELAY);
PORTC = 0b00001000;
_delay_ms(LED_DELAY);
PORTC = 0b00000100;
_delay_ms(LED_DELAY);
PORTC = 0b00000010;
_delay_ms(LED_DELAY);
PORTC = 0b00000001;
_delay_ms(LED_DELAY);
PORTC = 0b00000010;
_delay_ms(LED_DELAY);
PORTC = 0b00000100;
}
}
And this is my Makefile:
:::make
MMCU=atmega8
AVRDUDE_DEVICE=m8
CC=avr-gcc
OBJCOPY=avr-objcopy
CMPNAME=blink
PROGRAMMER=stk500v2
STKPORT=/dev/ttyUSB0
CFLAGS=-g -mmcu=$(MMCU) -v
$(CMPNAME).hex : $(CMPNAME).out
$(OBJCOPY) -j .text -O ihex $(CMPNAME).out $@
$(CMPNAME).out : $(CMPNAME).o
$(CC) $(CFLAGS) -o $@ -Wl,-Map,$(CMPNAME).map $<
$(CMPNAME).o : $(CMPNAME).c
$(CC) $(CFLAGS) -Os -c $<
$(CMPNAME).s: $(CMPNAME).c
$(CC) -S $(CFLAGS) -o $<
program: $(CMPNAME).hex
avrdude -p $(AVRDUDE_DEVICE) -c $(PROGRAMMER) -P $(STKPORT) -U flash:w:$(CMPNAME).hex:i
fuses:
avrdude -p $(AVRDUDE_DEVICE) -c $(PROGRAMMER) -P $(STKPORT) -U hfuse:w:0xd9:m -U lfuse:w:0xe1:m
clean:
rm -f *.o *.out *.map *.hex
.PHONY: clean program fuses
Interrupt Handling of TIMER0 overflow
- https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
#define F_CPU 1000000
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int port_value;
ISR(TIMER0_OVF_vect)
{
if(port_value==0) {
port_value=1;
} else {
port_value=0;
}
}
int main(void) {
// SETTING PORTC DIRECTION AS OUTPUT
DDRC = 0xff;
// INITIALIZING DISPLAY VALUE
port_value = 0;
// SETTING TIMER0 SOURCE TO CLKIO/1024
TCCR0|=(1<<CS02)|(1<<CS00);
TCCR0&=~(1<<CS01);
// ENABLE TIMER0 INTERRUPT
TIMSK|=(1<<TOIE0);
// ENABLE GLOBAL INTERRUPT
sei();
for(;;) {
PORTC=port_value;
}
}
Real Time Clock with Timer 2
STK500 Setup
When XTAL1 jumper is not mounted, the internal clock system is disconnected. This allows external clock signals or crystals to be used as tar- get clock source for the AVR.
When the XTAL1 jumper is not mounted, an external clock source or crystal can be con- nected to the PORTE header.
Fuse Setup
Fuses shall remain the same - still using an internal 1MHz clock, see above
Register Setup
TCCR2:- To select CTC (Clear Timer on Compare Match) operation mode:
WGM21has to be1WGM20has to be0This way the max value shall be inOCR2register
- To make sure that a prescaler of 1024 is used:
CS2has to be1CS1has to be1CS0has to be1
ASSRAsynchronous Status Register- To select external crystal
AS2has to be1
Example Code
This code fragment will display the counter's
value on the STK500 LEDs.
Note: I was looking at /usr/avr/include/avr/iom8.h for finding
register names.
:::c
#define F_CPU 1000000
#include <avr/io.h>
volatile int port_value;
int main(void) {
// SETTING PORTC DIRECTION AS OUTPUT
DDRC = 0xff;
// SHOW WHERE WE ARE
PORTC = 0x01;
// TIMER2 SETTINGS
// 1.) DISABLE INTERRUPTS
// - DISABLE OUTPUT COMPARE MATCH INTERRUPT
TIMSK &= ~(1<<OCIE2);
// - DISABLE OVERFLOW INTERRUPT
TIMSK &= ~(1<<TOIE2);
// 2.) SELECT ASYNC OPERATION
ASSR|=(1<<AS2);
// 3.) INITIALIZE VALUES
// - COUNTER VALUE
TCNT2 = 0;
// - COMPARE MATCH VALUE
OCR2 = 0xff;
// - CONFIGURE TIMER2 FOR 1024 PRESCALE
TCCR2 |= (1<<WGM21)|(1<<CS22)|(1<<CS21)|(1<<CS20);
// 4.) WAIT FOR ASYNC OPERATION TO FINISH
// - Wait for TCN2UB, OCR2UB, and TCR2UB
PORTC = 0x02;
while((ASSR & (1<<TCN2UB))) {;};
PORTC = 0x03;
while((ASSR & (1<<OCR2UB))) {;};
PORTC = 0x04;
while((ASSR & (1<<TCR2UB))) {;};
// 5.) CLEAR INTERRUPT FLAGS
TIFR &= ~((1<<OCF2)|(1<<TOV2));
// ENABLE INTERRUPTS
for(;;) {
PORTC=TCNT2;
}
}
Example 1sec blink
:::c
#define F_CPU 1000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile int one_sec_flag;
#define LED_DELAY 50
ISR(TIMER2_COMP_vect)
{
one_sec_flag = 1;
}
int main(void) {
// SETTING GLOBAL VALUE
one_sec_flag = 0;
// SETTING PORTC DIRECTION AS OUTPUT
DDRC = 0xff;
// DURING INIT SETTING PORT TO ALL 0 TO LIGHT ALL THE LEDS
PORTC = 0x0;
// TIMER2 SETTINGS
// 1.) DISABLE INTERRUPTS
// - DISABLE OUTPUT COMPARE MATCH INTERRUPT
TIMSK &= ~(1<<OCIE2);
// - DISABLE OVERFLOW INTERRUPT
TIMSK &= ~(1<<TOIE2);
// 2.) SELECT ASYNC OPERATION
ASSR|=(1<<AS2);
// 3.) INITIALIZE VALUES
// - COUNTER VALUE
TCNT2 = 0;
// - COMPARE MATCH VALUE
OCR2 = 31;
// - CONFIGURE TIMER2 FOR 1024 PRESCALE
TCCR2 |= (1<<WGM21)|(1<<CS22)|(1<<CS21)|(1<<CS20);
// 4.) WAIT FOR ASYNC OPERATION TO FINISH
// - Wait for TCN2UB, OCR2UB, and TCR2UB
while((ASSR & (1<<TCN2UB))) {;};
while((ASSR & (1<<OCR2UB))) {;};
while((ASSR & (1<<TCR2UB))) {;};
// 5.) CLEAR INTERRUPT FLAGS
TIFR &= ~((1<<OCF2)|(1<<TOV2));
// 6.) ENABLE INTERRUPTS
// - Output compare match interrupt enable
TIMSK |= (1<<OCIE2);
sei();
for(;;) {
if(one_sec_flag == 1) {
PORTC = ~(1<<0);
_delay_ms(LED_DELAY);
PORTC = ~(0);
one_sec_flag = 0;
}
}
}