This week we experimented with different technologies for programming microcontrollers. I documented my process in this tutorial.
This week I focused on studying the ATTiny datasheet.
#include <avr/io.h>
#define F_CPU 2500000UL
#include <util/delay.h>
#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define BIT(x) (0x01 << (x))
void blink(int seconds)
{
int i;
for (i = 0; i < seconds; i++)
{
_delay_ms(500);
bit_set(PORTA,BIT(7));
_delay_ms(500);
bit_clear(PORTA,BIT(7));
}
}
int main()
{
// Button PA3 (pin 10)
// LED PA7 (pin 6)
bit_set(PORTA,BIT(3)); // Turn button pull up resistor on by setting PA3(input) high
bit_set(DDRA,BIT(7)); // Enable output on the LED pin (PB2)
blink(5); // blink for 5 seconds
while (1)
{
if(bit_get(PINA,BIT(3))) // Button is up, turn on
bit_set(PORTA,BIT(7));
else // Button is down, turn off
bit_clear(PORTA,BIT(7));
}
}
Assume you are using the avr-gcc toolchain and you want to #include <util/delay.h>
. As described in the delay.h documentation you need to specify the clock rate with #define F_CPU
With the default settings, use:
#define F_CPU 2500000UL
Now when you use a line like _delay_ms(1000)
your program's execution will pause for the correct amount of time.
Why?
avrdude -p t44 -P usb -c usbtiny -U lfuse:w:0x7E:m
How many bits in a long/int/etc? (source)
On my installation delay.h
and io.h
are located here:
/usr/local/CrossPack-AVR-20130212/avr/include/avr/
I wanted to actually read the .h files that are hidden in the include path. To find them i used this command:
$ avr-gcc -v -c hello.ftdi.44.echo.c
Even with the extra info provided by the -v option, I still had to do some searching. Is there a better way to find #include files?
4 software selectable power saving modes
Additional options
Named memory locations that have some specialized functionality.
In C code, we are provided with variable names that are assigned to the appropriate memory addresses.
Interrupt Vector - The memory address of an interrupt handler.
sei(); // Enables interrupts by setting the global interrupt mask.
cli(); // Disable interrupts by setting the global interrupt mask.
Timers work by incrementing a counter on each clock cycle.
An interrupt can move the stack pointer to a new memory location, thus interrupting the flow of execution.
By default, nested interrupts are disabled.
ATTINY44A Interrupt config:
PCI0 (Pin Change Interrupt 0) Interrupts are triggered by pins on PORTA
--------------------
Pin13 PA0 PCINT0
Pin12 PA1 PCINT1
Pin11 PA2 PCINT2
Pin10 PA3 PCINT3
Pin09 PA4 PCINT4
Pin08 PA5 PCINT5
Pin07 PA6 PCINT6
Pin06 PA7 PCINT7
PCI1 (Pin Change Interrupt 1) Interrupts are triggered by pins on PORTB
--------------------
PIN05 PB2 PCINT10 INT0
PIN04 PB3 PCINT11
Quote from the datasheet:
Pin change interrupts on PCINT11..0 are detected asynchronously, which means that these interrupts can be used for waking the part also from sleep modes other than Idle mode.
Why is the connection between PCINT10 and INT0 (on pin PB2)? What does toggling INT0 in the GIMSK register do?
Why don't they just #define "toggles" differently so that you can:
GIMSK |= PCIE0;
Instead of the very silly:
GIMSK |= (1 << PCIE0);
Here's a simple program for Using interrupts using interrupts for blinking
#include <avr/io.h>
#define F_CPU 2500000UL
#include <util/delay.h>
#include <avr/interrupt.h>
#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define BIT(x) (0x01 << (x))
void blink()
{
bit_clear(PORTA,BIT(7)); // off
_delay_ms(100);
bit_set(PORTA,BIT(7)); // on
_delay_ms(100);
bit_clear(PORTA,BIT(7)); // off
_delay_ms(100);
bit_set(PORTA,BIT(7)); // on
}
// For some reason PCINT0_vect AND PCINT3_vect both work???
// This syntax is WTF weird
ISR(PCINT0_vect){
blink();
}
int main()
{
// Button PA3 (pin 10)
// LED PA7 (pin 6)
bit_set(PORTA,BIT(3)); // Turn button pull up resistor on by setting PA3(input) high
bit_set(DDRA,BIT(7)); // Enable output on the LED pin
// GIMSK - General Interrupt Mask Register
// When the PCIE0 bit is set (one) and the I-bit in the
// Status Register (SREG) is set (one), pin change interrupt
// 0 is enabled. Any change on any enabled PCINT7..0 pin
// will cause an interrupt. The corresponding interrupt of
// Pin Change Interrupt Request is executed from the PCI0
// Interrupt Vector.
// PCINT7..0 pins are enabled individually by the PCMSK0 Register.
GIMSK |= (1 << PCIE0);
// PCMSK0 - Pin Change Mask Register 0 - one bit for each pin on PortA
PCMSK0 |= (1 << PCINT3); // enable interrupt for change on pin3
// globally enable interrupts
sei();
blink();
while (1)
{
}
}
An AVR port is a grouping of <= 8 General Purpose I/O pins
Each port has three data register bits associated with it.
The eight bits in each register each control the behavior of the eight corresponding GPIO pins.