Hands On AVR: Difference between revisions
Line 206: | Line 206: | ||
</syntaxhighlight > | </syntaxhighlight > | ||
== 2 x better Analog-in 10-bit & 2 x PWM out & threshold value == | |||
<syntaxhighlight lang="c"> | |||
/* | |||
Analog sensor input at ADC3 (PB3) - Pin 2 | |||
Analog sensor input at ADC1 (PB2) - Pin 7 | |||
PWM LED at OC0A (PB0) - Pin 5 | |||
PWM LED at OC0A (PB1) - Pin 6 | |||
Digital FAN at (PB4) - Pin 3 | |||
*/ | |||
#define F_CPU 9600000UL // Define software reference clock for delay duration 1 MHz | |||
#include <avr/io.h> | |||
#include <util/delay.h> | |||
#define ADC3 PB3 | |||
#define ADC1 PB7 | |||
#define FAN PB4 | |||
#define PWM0 PB0 | |||
#define PWM1 PB1 | |||
unsigned int read_adc(unsigned char channel); | |||
int i; | |||
unsigned int sensorValue; //16bit | |||
unsigned int temperatureValue; //16bit | |||
unsigned int outputVoltage; | |||
void adc_init() { //ADC init | |||
ADMUX = 0b00000011; //ADC3 (default),right,VCc | |||
//ADMUX = 0b00100011; //ADC3 (default),ADLAR left,VCc | |||
ADCSRA = 0b10000010; //prescaled by 4 | |||
} | |||
void pwm_init() { //PWM Init | |||
TCCR0A |= (1 << COM0A1) | (1 << COM0A0); // COM0A1 - COM0A0 (Set OC0A on Compare Match, clear OC0A at TOP) | |||
TCCR0A |= (1 << COM0B1) | (1 << COM0B0); // COM0B1 - COM0B0 (Set OC0B on Compare Match, clear OC0B at TOP) | |||
TCCR0A |= (1 << WGM01) | (1 << WGM00); // WGM01 - WGM00 (set fast PWM) | |||
OCR0A = 0; // initialize Output Compare Register A to 0 | |||
OCR0B = 0; // initialize Output Compare Register B to 0 | |||
//TCCR0B |= (1 << CS00); // Start timer at Fcpu / 1 | |||
TCCR0B |= (1 << CS01); // Start timer at Fcpu / 8 | |||
//TCCR0B |= (1 << CS02) | (1 << CS00); // Start timer at Fcpu / 1024 | |||
} | |||
int main(void) { | |||
//Initialization: | |||
DDRB &= ~(1 << ADC3); // Set direction of (PB3) to input for ADC3 | |||
DDRB |= (1 << FAN); // Set direction of (PB4) to output for FAN | |||
DDRB |= (1 << PWM0) | (1 << PWM1); // OC0A on PB0 & P01 | |||
adc_init(); | |||
pwm_init(); | |||
//Actual Program: | |||
while (1) { // loop forever | |||
sensorValue = read_adc(3); | |||
OCR0A = (255-(sensorValue>>2)); | |||
OCR0B = ((sensorValue>>2)); | |||
temperatureValue = read_adc(1); | |||
// map it to the range 0 to 5000 mV: | |||
//outputVoltage = ((sensorValue*50) / 255); | |||
// Calculate Temperature, 10mV/°C -50°C for TMP36, check data sheet | |||
//temperatureValue = (2 + (outputVoltage*10)); | |||
if (temperatureValue >= sensorValue) { | |||
PORTB = (1 << FAN); | |||
} | |||
else { | |||
PORTB = (0 << FAN); | |||
} | |||
} | |||
} | |||
unsigned int read_adc(unsigned char channel) { | |||
unsigned char k; | |||
unsigned int adcvalue=0; | |||
ADMUX = ADMUX&(0b11111100); //clear channel select bits | |||
ADMUX |= channel; | |||
//neglect first reading after changing channel | |||
ADCSRA |= 1<<ADSC; | |||
while(ADCSRA&(1<<ADSC));//Wait | |||
adcvalue=ADCL + (ADCH<<8); | |||
adcvalue=0;//neglectreading | |||
for(k=0;k<=7;k++) | |||
{ | |||
ADCSRA |= 1<<ADSC; | |||
while(ADCSRA&(1<<ADSC));//Wait | |||
adcvalue += (ADCL + (ADCH<<8)); | |||
_delay_ms (2); | |||
} | |||
return (adcvalue>>3); //divide by 8 | |||
} | |||
</syntaxhighlight > | |||
== Analog in & PWM out == | == Analog in & PWM out == |
Revision as of 10:33, 21 February 2012
Overview
This wiki page is dedicated to AVR development with focus on the ATtiny13.
Memory
The ATtiny13 has two main memories:
Program memory is 1K Bytes. It's organized as 512x16 Bits, because all AVR instructions are 16 or 32 bits wide.
Data memory is 64 bytes of SRAM (internal memory) + 64 I/O Registers + 32 general purpose registers.
Furthermore there's electrically erasable programmable read-only memory (EEPROM) of 64 Bytes. Single bytes can be read and written.
I/O Ports
Individual I/O ports can be set as input or output. Each output can deliver 40mA.
The ATtiny13 has 6 I/O pins named: PB0, PB1, PB2, PB3, PB4 and PB5. Generally AVR ports are named Pxn, x is the letter of the port, n is the pin number.
All pins of PORTB of the attiny13 can be configured with only three registers:
- Data Register - PORTB - write values to port PBx / set internal pull-up of PBx
- Data Direction Register – DDRB - set PBx as input (0) or output (1).
- Port Input Pins – PINB - read values at port PBx
Notes:
- If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated.
- The pin numbers don't correspond to the acutal hardware pin numbers, for example PB0 is located at hardware pin 5.
- All ports have also alternate functions (ADC, PWM, ..).
ArduinoISP
Arduino can be used as an In-system programming (ISP) interface for the ATtiny and ATmega microcontrollers.
Here are step-by-step instructions on how to program an attiny13 with an LED blinking program:
- On the Arduino, the ArduinoISP code must be running. Open the Arduino IDE -> examples -> ArduinoISP and flash it (File->Upload).
- Build the circuit as shown above. Pins are documented in the source code. Use a 470Ω resistor between PB4 (that's pin 3 on the attiny13) and the LED.
- Install AVR toolchain: Windows, Mac, Linux users: install avr-gcc, avr-gcc-c++ and avr-libc
the code:
/*
hello, LED!
attiny13 Pins:
1: RESET (Arduino 10)
2: NC (not connected)
3: R (470Ω) to LED to GND
4: GND (Arduino Gnd)
5: MOSI (Arduino 11)
6: MISO (Arduino 12)
7: SCK (Arduino 13)
8: VCC (Arduino 5V)
*/
#define F_CPU 9600000 // Define software reference clock for delay duration
#include <avr/io.h>
#include <util/delay.h>
#define LED PB4 // Define led output on PB4
int main()
{
DDRB |= (1 << LED); // Set direction to output for LED
for (;;) { // forever
PORTB ^= (1 << LED); // toggle pin
_delay_ms(1000);
}
return 0;
}
- download and run File:Led.zip and extract it somewhere. open the cmd/shell and run the command
make all
. this will generate the file "lex.hex" - upload it manually using avrdude (which is part of the WinAVR software), change correct com port and baudrate:
avrdude -P COM1 -b 19200 -c avrisp -p attiny13 -U flash:w:led.hex:i
linux: use /dev/ttyUSB.. instead of COM1.
- now the LED should be blinking, but too slow.
We have to change the fuse bits.
- Windows:
avrdude -P COM*** -b 19200 -c avrisp -p t13 -U lfuse:w:0x7a:m -U hfuse:w:0xff:m
(***select appropriate usb device, can be COM1, COM2, etc. and baudrate -b ...) - Linux:
avrdude -P /dev/ttyUSB*** -c avrisp -p t13 -U lfuse:w:0x7a:m -U hfuse:w:0xff:m
(***select appropriate usb device)
- enjoy the blinking LED (1 second on, 1 second off)!
ISP Programming
AVR can be programmed using the In-System-Programming interface ISP. Different programmers are available: AVR pocket programmer, USBtinyISP, official Atmel programmers.
Breadboard setup:
Solder a breakout cable. You could also connect the lines from the programmer directly to the tiny pins. If you're too lazy, buy the sparkfun Programming Adapter.
LED Example:
- setup the circuit as shown in the image above. See also #ArduinoISP.
- download File:Led.zip and extract the files (Makefile and led.c) to a new folder on the system.
- Open the Makefile in a texteditor and check the following (IMPORTANT):
- TARGET: i called the source file "led.c", thus in the makefile the TARGET has to be named "led" (without extension).
- AVRDUDE_PROGRAMMER: here "usbtiny" must be set, because we're using AVR Pocket programmer. make your appropriate choice, if you use another programmer. (see note below).
- AVRDUDE_PORT: set the correct comport. windows: COM1, COM2, etc. Linux: /dev/ttyUSB...
Note: other programmers can be defined, if you are for example using the "AVR Pocket programmer", AVRDUDE_PROGRAMMER must be set to "usbtiny", for "AVRISP mkII", set it to 'stk500'. all programmers can be listed with the command avrdude -c ?
- make sure the Makefile is in the same folder as the source file is in.
- run these commands from the cmd (windows) or shell (linux):
- to check, if we are using the correct compiler:
avr-gcc --version
(this should give you something like "avr-gcc (WinAVR 20100110) 4.3.3") - make hex file:
make all
- download hex file to AVR:
make program
- at this stage the LED should already blink, but too slow, because the fuse bits are not set correctly yet.
- to check, if we are using the correct compiler:
We have to change the fuse bits.
- Windows:
avrdude -P COM*** -c usbtiny -p t13 -U lfuse:w:0x7a:m -U hfuse:w:0xff:m
(***select appropriate usb device, can be COM1, COM2, etc.) - Linux:
avrdude -P /dev/ttyUSB*** -c usbtiny -p t13 -U lfuse:w:0x7a:m -U hfuse:w:0xff:m
(***select appropriate usb device)
- enjoy the blinking LED!
High Voltage Fuse Resetter
If the fusebits are messed up, the AVR can be factory resetted. This can be done with "high-voltage serial programming". Here is a excellent project to reset attiny13/25/45/85 chips: hvsp fuse resetter
tips:
- The schema on the website seems to have bugs, i fixed them, check my copy File:Hvsp-fuse-resetter-schema.pdf.
- The 7x4 LED module can be found on ebay. If the display doesn't work, you probably have to comment out "#define _REVERSE" in the code, this switches common anode/common cathode of the display.
AVR
more on AVR soon.
Code Examples by dusjagr
Heartbeat PWM out
/*
Heartbeat - Hello World
PWM LED at OC0A (PB0) - Pin 5
*/
#define F_CPU 9600000UL // Define software reference clock for delay duration 1 MHz
#include <avr/io.h>
#include <util/delay.h>
#define PWM0 PB0
int i;
int delay;
void pwm_init() { //PWM Init
TCCR0A |= (1 << COM0A1) | (1 << COM0A0); // COM0A1 - COM0A0 (Set OC0A on Compare Match, clear OC0A at TOP)
TCCR0A |= (1 << WGM01) | (1 << WGM00); // WGM01 - WGM00 (set fast PWM)
OCR0A = 0; // initialize Output Compare Register A to 0
//TCCR0B |= (1 << CS00); // Start timer at Fcpu / 1
TCCR0B |= (1 << CS01); // Start timer at Fcpu / 8
//TCCR0B |= (1 << CS02) | (1 << CS00); // Start timer at Fcpu / 1024
}
int main(void) {
//Initialization:
DDRB |= (1 << PWM0) | (1 << PWM1); // OC0A on PB0 & P01
pwm_init();
//Actual Program:
while (1) { // loop forever
for (i=0; i<=255; i++) {
OCR0A = (i);
_delay_us (200);
}
for (i=254; i>=0; i--) {
OCR0A = (i);
_delay_us (200);
}
}
}
2 x better Analog-in 10-bit & 2 x PWM out & threshold value
/*
Analog sensor input at ADC3 (PB3) - Pin 2
Analog sensor input at ADC1 (PB2) - Pin 7
PWM LED at OC0A (PB0) - Pin 5
PWM LED at OC0A (PB1) - Pin 6
Digital FAN at (PB4) - Pin 3
*/
#define F_CPU 9600000UL // Define software reference clock for delay duration 1 MHz
#include <avr/io.h>
#include <util/delay.h>
#define ADC3 PB3
#define ADC1 PB7
#define FAN PB4
#define PWM0 PB0
#define PWM1 PB1
unsigned int read_adc(unsigned char channel);
int i;
unsigned int sensorValue; //16bit
unsigned int temperatureValue; //16bit
unsigned int outputVoltage;
void adc_init() { //ADC init
ADMUX = 0b00000011; //ADC3 (default),right,VCc
//ADMUX = 0b00100011; //ADC3 (default),ADLAR left,VCc
ADCSRA = 0b10000010; //prescaled by 4
}
void pwm_init() { //PWM Init
TCCR0A |= (1 << COM0A1) | (1 << COM0A0); // COM0A1 - COM0A0 (Set OC0A on Compare Match, clear OC0A at TOP)
TCCR0A |= (1 << COM0B1) | (1 << COM0B0); // COM0B1 - COM0B0 (Set OC0B on Compare Match, clear OC0B at TOP)
TCCR0A |= (1 << WGM01) | (1 << WGM00); // WGM01 - WGM00 (set fast PWM)
OCR0A = 0; // initialize Output Compare Register A to 0
OCR0B = 0; // initialize Output Compare Register B to 0
//TCCR0B |= (1 << CS00); // Start timer at Fcpu / 1
TCCR0B |= (1 << CS01); // Start timer at Fcpu / 8
//TCCR0B |= (1 << CS02) | (1 << CS00); // Start timer at Fcpu / 1024
}
int main(void) {
//Initialization:
DDRB &= ~(1 << ADC3); // Set direction of (PB3) to input for ADC3
DDRB |= (1 << FAN); // Set direction of (PB4) to output for FAN
DDRB |= (1 << PWM0) | (1 << PWM1); // OC0A on PB0 & P01
adc_init();
pwm_init();
//Actual Program:
while (1) { // loop forever
sensorValue = read_adc(3);
OCR0A = (255-(sensorValue>>2));
OCR0B = ((sensorValue>>2));
temperatureValue = read_adc(1);
// map it to the range 0 to 5000 mV:
//outputVoltage = ((sensorValue*50) / 255);
// Calculate Temperature, 10mV/°C -50°C for TMP36, check data sheet
//temperatureValue = (2 + (outputVoltage*10));
if (temperatureValue >= sensorValue) {
PORTB = (1 << FAN);
}
else {
PORTB = (0 << FAN);
}
}
}
unsigned int read_adc(unsigned char channel) {
unsigned char k;
unsigned int adcvalue=0;
ADMUX = ADMUX&(0b11111100); //clear channel select bits
ADMUX |= channel;
//neglect first reading after changing channel
ADCSRA |= 1<<ADSC;
while(ADCSRA&(1<<ADSC));//Wait
adcvalue=ADCL + (ADCH<<8);
adcvalue=0;//neglectreading
for(k=0;k<=7;k++)
{
ADCSRA |= 1<<ADSC;
while(ADCSRA&(1<<ADSC));//Wait
adcvalue += (ADCL + (ADCH<<8));
_delay_ms (2);
}
return (adcvalue>>3); //divide by 8
}
Analog in & PWM out
/*
Analog sensor input at ADC3 (PB3) - Pin 2
PWM LED at OC0A (PB0) - Pin 5
Digital FAN at (PB4) - Pin 3
*/
#define F_CPU 9600000UL // Define software reference clock for delay duration 1 MHz
#include <avr/io.h>
#include <util/delay.h>
#define FAN PB4
#define PWM PB0
int i ;
int TEMP;
void adc_init() { //ADC Init.
ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS2) | (1<<ADATE); //Turn on ADC, with prescaler 64
ADCSRB= 0x00; //Free running mode
ADMUX= 0x23; //VCC Reference, Left Adjust, Channel: PB3 (ADC3)
DIDR0= 0x04; //Disable Digital Input on PB3
ADCSRA |= (1 << ADSC); //start adc conversion
}
void pwm_init() { //PWM Init
DDRB |= (1 << PWM); // OC0A on PB0
TCCR0A |= ((1 << COM0A1) | (1 << COM0A0) // COM0A1 - COM0A0 (Set OC0A on Compare Match, clear OC0A at TOP)
| (1 << WGM01) | (1 << WGM00)); // WGM01 - WGM00 (set fast PWM)
OCR0A = 0; // initialize Output Compare Register A to 0
TCCR0B |= (1 << CS01); // Start timer at Fcpu / 256
}
int main(void) {
//Initialization:
adc_init();
pwm_init();
DDRB |= (1 << FAN); // Set direction of (PB4) to output for FAN
//Actual Program:
while (1) { // loop forever
OCR0A = (255-(ADCH));
TEMP = (2 + (2*ADCH));
if (TEMP >= 28) {
PORTB = (1 << FAN);
}
else {
PORTB = (0 << FAN);
}
}
}