Documentation:Tutorials gcc AT90UsbKey

From AVRFreaks Wiki

Jump to: navigation, search

This tutorial is about programing the AT90UsbKey (AT90USB1287) with gcc in the AVR Studio.

Please note that these code examples don't need any other hardware than the AT90UsbKey a usb cable and a PC.

Contents

[edit] What is the AT90USBKey

This small board (3x9cm), is generally priced at slightly more than an AVR Butterfly (£20-25 in the UK). It includes an AT90USB1287 with USB-OTG support, 16 MB of DataFlash, LEDs, a small joystick, and a temperature sensor (thermistor). The Key is clocked at 8 MHz with the onboard crystal, and comes with software which lets it act as a USB Mass Storage device (its documentation is shipped on the DataFlash), a USB joystick, and more. To support the USB host capability, it must be operated from an external power source (battery or mains), but when running as a USB peripheral, it only needs the power provided over USB.

The AT90USB1287 is a USB On-The-Go (OTG) Microcontroller, which has a 128-Kbyte self-programming Flash Program Memory, 8-Kbyte SRAM, 4-Kbyte EEPROM, 8 Channel 10-bit A/D-converter, JTAG interface for on-chip-debug. Up to 16 MIPS throughput at 16 MHz. 2.7 - 5.5 Volt operation.

Only the JTAG port uses conventional 2.54mm pinout. All the other AVR I/O ports require more compact 1.27mm headers.

Since it has 128 KB of flash memory, a JTAGICE mkII is the best debug solution; a Dragon can program it, but can't examine its status. You can also program it through USB from a Windows or Linux host, using the USB "Device Firmware Update" protocols. Atmel ships proprietary (source code included but distribution restricted) example programs and a USB protocol stack with the device. MyUSB is a third party free software (MIT license) USB protocol stack for the USBKey.

[edit] Read this first

This may also be helpful

[edit] AVR Studio 4

AVR Studio 4 is the official Atmel IDE, as default it only supports assembler but together with WinAVR you can use gcc as your c compiler.

Since WinAVR has official Atmel support the integration of gcc into AVR Studio is very good, to be honest it is transparent. The only thing you need to do is download them both and install them (see links below).

The Studio also has a simple but effective simulator, so it is easy to test the different parts of your program. It tends to have some problems with interrupts and timers but overall it is very nice.


AVR Studio 4:

WinAVR:

[edit] Eclipse with WinAVR Plugin

[edit] Eclipse:

Eclipse is a free, powerful, and full-featured development environment that can be set up to work with AVR. Beware this isn't necessarily your easiest option for programming (Eclipse is a very powerful IDE, but can be quite complicated - be prepared for a steeper learning curve than AVR Studio)

[edit] AVR Eclipse Plugin:

AVR-Eclipse is a plugin for the Eclipse IDE which adds toolchain support, through CDT, for the gcc compiler for the Atmel AVR series of microcontrollers, and adds a toolbar button to allow quick upload of produced code to a target device.

[edit] WinAVR:

[edit] Flip

The easy way to flash the AT90UsbKey under Windows is to use the Flip program. Flip programs the AT90UsbKey with a usb cable. (if you run Linux, try the opensource dfu-programmer)

The quick steps

  1. Build your program in AVR Studio
  2. Open FLIP and connect the AT90UsbKey.
  3. Open your hex file
  4. Make sure the all check boxes are set in the "operation flow area", that is Erase, Blank check, program and verify.
  5. Press the Run button in the "operation flow area".
  6. Press Start Application or remove the usb cable, and reinsert the cable if you want to power up your program over usb.

Please note that the AT90UsbKey comes with the look fuses set, so there is no way to read data from the AT90UsbKey. To remove the look you must reprogram the AT90UsbKey with a ISP or JTAG, FLIP can not remove the look fuses. This seems to be a very stupid state to ship demo cards, but this it is the way it is.

[The following is by uncle_O from an avrFreaks forum]

...There is a paragraph in the bootloader doc that describes security. It appears that it is not the lock bits that are preventing us from reading the key.

Rather, one of the "features" of the DFU protocol is that the first command must be a "Full Chip Erase." This is a misleading name, as it doesn't erase the full chip, just everything in the flash but the bootloader. It doesn't matter if the lock bits are unprogrammed; the security is within the bootloader itself.

I discovered that after a "Full Chip Erase," I was still able to read the contents of the eeprom that my program had written there. And of course, the bootloader is still there running. ...

If you are using Flip for the first time, you will need to make the AT90UsbKey look like a Flip Programming interface before it installs properly. To do this you need to put it in "programming mode" by executing the following sequence on the AT90USBKEY board before trying to connect from FLIP. - press HWB - press RST - release RST - release HWB.

If that does not work with the message:"AtLibUsbDfu.dll not found". The advice on the Atmel site (http://support.atmel.no/bin/customer?custSessionKey=&customerLang=en&noCookies=true&action=viewKbEntry&id=336) is "The cause can be that the wrong driver is installed for the USB flip device. You must uninstall the USB device and then put the board in programming mode with the reset and HWB jumpers. When windows will discover the new USB device, you will be able to load the correct driver from the ...\filp 3.2.2\usb directory."

[edit] Some AVR background

All AT90USB1287 ports have Read-Modify-Write functionality when used as general digital I/O ports. They are bi-directional IO ports with optional internal pull-up resistors. The direction, drive value (if output), and use (or not) of pull-up resistors for each pin can be changed independently of any other. Each pin buffer can drive and sink source. The pin driver is strong enough to drive an LED directly. All port pins have internally selectable pull-up resistors. All pins have protection diodes to both VCC and GND.


Each port pin consists of three register bits DDRx, PORTx and PINx (where x means port letter). DDRx (Data Direction Register) selects direction of port pins (input or output).


OUTPUT:

If DDRx=1 then port is configured to be an output.

Then if PORTx=1 then port pin is driven high. Or if PORTx=0, then port is driven low.


INPUT:

If DDRx=0 then that port is configured as an input.

Then if PORTx=1 then port is configured as input with internal pull-up resistor.

Otherwise if PORTx=0 then port is configured as input but pins are set to tri-state and you might need to connect external pull-up resistors.

The port can be read using PINx:-

eg i=PIND; //reads all 8 pins of port D

Generally when programming in C you would read the individual pins by reading each bit (using masks or shifts). However in the following examples GET_BIT is defined in the my_bits.h header file (lower down) to make it a bit (:-p) easier.

[edit] AT90USBKey On-board Resources

(shorthand: Port x, Pin y = Pxy)


USB

  D- - 
  D+ - 
  UGND - 
  VBUS - 
  PE3 - 

Joystick (4+1 directions) - NB uses pins from 2 ports

  PB5 - Select (centre button)
  PB6 - Left
  PB7 - Up
  PE4 - Right
  PE5 - Down

LEDs (2 bicolor)

  PD4 - LED D2 Red
  PD5 - LED D2 Green
  PD6 - LED D5 Green
  PD7 - LED D5 Red

Temperature Sensor

  PF0 - thermistor R29

DataFlash Memory

   Two (U2 and U3) 64-megabit Serial Interface Flash memories (AT45DB642D, CASON8 package) connected 
     to the AT90USB Serial Port Interface (SPI).
   
   Chip1 (U2)
   PE0 - U2 pin 4 - Chip Select signal
   PB1 - U2 pin 2 - SCK - Serial Clock
   PB2 - U2 pin 1 - SI - Serial Input
   PB3 - U2 pin 4 - SO - Serial Output
   
   Chip2 (U3)
   PE1 - U3 pin 4 - Chip Select signal
   PB1 - U3 pin 2 - SCK - Serial Clock
   PB2 - U3 pin 1 - SI - Serial Input
   PB3 - U3 pin 4 - SO - Serial Output

DataFlash Pins
   1. SI - Serial Input: The SI pin is used to shift data into the device. 
      The SI pin is used for all data input including command and address sequences. 
      Data on the SI pin is always latched on the RISING edge of SCK.
   2. SCK - Serial Clock: External signal used to control the flow of data to and from the device. 
      Inputs are latched on the rising edge, while output on the falling edge of SCK/CLK.
   3. RESET - Reset: A low state will terminate all operations and reset the internal state machine to idle. 
      Work will resume when the RESET pin is brought back to a high level.
   4. CS - Chip Select: Asserting the CS pin selects the device. 
      When deasserted, the device will normally be placed in the standby mode (not Deep Power-Down mode), 
      output pin goes into a high-impedance state, data will not be accepted on the input pin.
   5. WP - Write Protect: This pin is hardwired high on the key. Usually When WP pin is asserted, 
      all sectors specified for protection by the Sector Protection Register will be protected against 
      program and erase operations regardless of whether the Enable Sector Protection command has been issued or not.
   6. VCC - Device Power Supply: The VCC pin is connected to system Vcc.
   7. GND - Ground: The ground reference for the power supply (connected to the system ground)
   8. SO - Serial Output: The SO pin is used to shift data out from the device. 
      Data on the SO pin is always clocked out on the FALLING edge of SCK.

[edit] AT90USB1287 Chip Pin Connections (with on-board functions)

1 – PE6 = User port (not mounted)

2 – PE7 = UVCON

3 – UVcc = VCC - ferrite and capacitors (closed to MCU)

4 – D- = D- on USB MiniABF

5 – D+ = D+ on USB MiniABF

6 - UGND = D- on USB MiniABF; Resistor closed to device

7 – UCAP – Capacitor (closed to MCU)

8 - VBUS= VBUS on USB MiniABF

9 – PE3 = User port (not mounted); ID on USB MiniABF

10 – PB0 = User port (not mounted)

11 – PB1 = User port (not mounted); Data Flash – U2 SI; Data Flash – U3 SI

12 – PB2 = User port (not mounted); Data Flash – U2 SCK; Data Flash – U3 SCK

13 – PB3 = User port (not mounted); Data Flash – U2 SO; Data Flash – U3 SO

14 – PB4 = User port (not mounted)

15 – PB5 = User port (not mounted) ; Joystick Interface – Select Button

16 – PB6 = User port (not mounted) ; Joystick Interface – Left Button

17 – PB7 = User port (not mounted); Joystick Interface – Up Button

18 – PE4 = User port (not mounted);; Joystick Interface – Right Button

19 – PE5 = User port (not mounted);; Joystick Interface – Down Button

20 – RESET = Reset Button (Warm-Reset [the board also resets the chip on power-on])

21 – VCC = ferrite and capacitors (closed to MCU)

22 - GND

23 – XTAL2 – 8MHz crystal

24 – XTAL1 – 8MHz crystal

25 – PD0 = User port (not mounted)

26 – PD1 = User port (not mounted)

27 – PD2 = User port (not mounted)

28 – PD3 = User port (not mounted)

29 – PD4 = User port (not mounted); In-line Grouped LEDs – D2 Red

30 – PD5 = User port (not mounted) ; In-line Grouped LEDs – D2 Green

31 – PD6 = User port (not mounted) ; In-line Grouped LEDs – D5 Green

32 – PD7 = User port (not mounted) ; In-line Grouped LEDs – D5 Red

33 – PE0 = User port (not mounted); Data Flash – U2 CS (Select)

34 – PE1 = User port (not mounted); Data Flash – U3 CS (Select)

35 – PC0 = User port (not mounted)

36 – PC1 = User port (not mounted)

37 – PC2 = User port (not mounted)

38 – PC3 = User port (not mounted)

39 – PC4 = User port (not mounted)

40 – PC5 = User port (not mounted)

41 – PC6 = User port (not mounted)

42 – PC7 = User port (not mounted)

43 – PE2 = User port (not mounted); HWB button (runs bootloader if pressed while reset button is released)

44 – PA7 = User port (not mounted); AD7 - ADC multiplexed with other port A pins (10bit resolution)

45 – PA6 = User port (not mounted); AD6 - ADC multiplexed with other port A pins (10bit res)

46 – PA5 = User port (not mounted); AD5 - ADC multiplexed with other port A pins (10bit res)

47 – PA4 = User port (not mounted); AD4 - ADC multiplexed with other port A pins (10bit res)

48 – PA3 = User port (not mounted); AD3 - ADC multiplexed with other port A pins (10bit res)

49 – PA2 = User port (not mounted); AD2 - ADC multiplexed with other port A pins (10bit res)

50 – PA1 = User port (not mounted); AD1 - ADC multiplexed with other port A pins (10bit res)

51 – PA0 = User port (not mounted); AD0 - ADC multiplexed with other port A pins (10bit res)

52 - VCC = ferrite and capacitors (closed to MCU)

53 - GND

54 – PF7 = User port (not mounted) ; JTAG Interface - TDI; ADC7

55 – PF6 = User port (not mounted) ; JTAG Interface - TDO; ADC6

56 – PF5 = User port (not mounted) ; JTAG Interface - TMS; ADC5

57 – PF4 = User port (not mounted); JTAG Interface - TCK; ADC4

58 – PF3 = User port (not mounted); Battery Voltage Monitor; ADC3 - with gain of 1x, 10x (8 bit resolution), or 200x (7 bit res)

59 – PF2 = User port (not mounted); ADC2 - with gain of 1x, 10x (8 bit resolution), or 200x (7 bit res)

60 – PF1 = User port (not mounted); ADC1 - with gain of 1x, 10x (8 bit resolution), or 200x (7 bit res)

61 – PF0 = User port (not mounted); Temp Sensor (thermistor); ADC0 - with gain of 1x, 10x (8 bit resolution), or 200x (7 bit res)

62 - AREF

63 – AGND = Resistor closed to device

64 - AVCC = ferrite and capacitors (closed to MCU)

[edit] Fundamental code examples

The following examples (may) require header files called 'my_bit.h' or 'my_types.h' see the next section for the text for these files.

[edit] Blinky

Blinky is the embedded version of Hello World, it flashes with one or more LED for the world....

[edit] Blinky, stupid_sleep

This version has a minimum of dependences the only external thing is the avr/io.h and that is located in the WinAVR directory. If the program dosen't compile make sure that your program knows where to find this file. In Studio4, locations to search for include files can be set via "Project/Configuration Options/Include Directories" and browse to the location where AVR is installed

#include <avr/io.h>

#include "my_bit.h"
#include "my_types.h"

//300 ~ 3s 
//100 ~ 1s
#define SLEEP_TIME 25

void stupid_sleep(uInt time){
	uInt i = 0;
	uInt j = 0;
	for(i=0;i<time;i++){
		for(j=0;j<530;j++){
			//Do nothing
			asm volatile ("NOP");
		}
	}
}

int main(void){

	//The AT90USBKey includes 2 bi-color LEDs (green/red) implemented on one line. 
	//They are connected to the high nibble of Port D of AT90USB (PORTD[4..7]).
	//To light on a LED, the corresponding port pin must drive a high level. 
	//To light off a LED, the corresponding port pin must drive a low level.

	//Set portD pins 4..7 to output mode (this doesn't set what the output will be)
	DDRD SET_BIT4;
	DDRD SET_BIT5;
	DDRD SET_BIT6;
	DDRD SET_BIT7;


	while(1){

		PORTD SET_BIT4;			//Drive portD pin 4 high
		stupid_sleep(SLEEP_TIME);
		PORTD CLEAR_BIT4;		//Drive portD pin 4 low

		PORTD SET_BIT5;
		stupid_sleep(SLEEP_TIME);
		PORTD CLEAR_BIT5;
	
		PORTD SET_BIT7;
		stupid_sleep(SLEEP_TIME);
		PORTD CLEAR_BIT7;

		PORTD SET_BIT6;
		stupid_sleep(SLEEP_TIME);
		PORTD CLEAR_BIT6;
	}
}


[edit] 1ms or 0.4ms

Or the same thing but with 1ms or 0.4ms delay, and output is PortC bit7 so we can measure the delay i.e. with a oscilloscope. And this example is compiled with -Os optimization, compile with other and the times will be wrong.

#include <avr/io.h>
#include "my_bit.h"

// 150 => 1,05ms
// 145 => 1,04ms
// 140 => 1,00ms
// 138 => 0,99ms
// 130 => 0,92ms
// 120 => 0,86ms
void delay_1ms(){
    unsigned int j = 0;
    for(j=0;j<140;j++){
        //Do nothing
        asm volatile ("NOP");
    }
    asm volatile ("NOP");
}


//55 => 0,39ms 
//56 => 0,41ms
void delay_0_4ms(){
    unsigned int j = 0;
    for(j=0;j<55;j++){
        //Do nothing
        asm volatile ("NOP");
    }
    asm volatile ("NOP");
}


int main(void){
    //PD4..7
    DDRD SET_BIT4;
    DDRD SET_BIT5;
    DDRD SET_BIT6;
    DDRD SET_BIT7;

    DDRC SET_BIT0;

    while(1){
        PORTC SET_BIT0;
        delay_0_4ms();
        delay_1ms();
        PORTC CLEAR_BIT0;
        delay_0_4ms();
        delay_1ms();
    }
}

[edit] Blinky, delay.h

Because the first version dosen't have exact timing you can use the delay.h. delay.h does basically the same thing but in a more precise manner.

The only thing that is really important here is that F_CPU is set to the correct frequency, in this case it should be 8000000 Hz (8 MHz).

Be careful not to have a longer delay than is supported by delay.h

#include <avr/io.h>

#include "my_bit.h"
#include "my_types.h"


//Se project config freq in Hz
//#ifdef F_CPU
//	#undef F_CPU
//#endif
//#define F_CPU 8000000UL  // 8 MHz
#include <util/delay.h>

#define SLEEP_TIME 250

int main(void){
//PD4..7
DDRD SET_BIT4;
DDRD SET_BIT5;
DDRD SET_BIT6;
DDRD SET_BIT7;


    while(1)
    {
	    PORTD SET_BIT4;
	    _delay_ms(SLEEP_TIME);
	    PORTD CLEAR_BIT4;

	    PORTD SET_BIT5;
	    _delay_ms(SLEEP_TIME);
	    PORTD CLEAR_BIT5;
	
	    PORTD SET_BIT7;
	    _delay_ms(SLEEP_TIME);
	    PORTD CLEAR_BIT7;

	    PORTD SET_BIT6;
	    _delay_ms(SLEEP_TIME);
 	    PORTD CLEAR_BIT6;

    }
}

[edit] Blinky, interrupt.h

#include <avr/io.h>
#include <avr/interrupt.h>

#include "my_bit.h"

unsigned char blink = 0;
ISR(TIMER2_OVF_vect) 
{ 
  // Code to be executed when ISR fires 
	// 
	blink++;
	switch(blink){
	case 1: {
		PORTD SET_BIT6;
		PORTD CLEAR_BIT7;
		break;
	}
	case 2: {
		PORTD CLEAR_BIT6;
		PORTD SET_BIT4;
		break;
	}
	case 3: {
		PORTD CLEAR_BIT4;
		PORTD SET_BIT5;
		break;
	}
	case 4: {
		PORTD CLEAR_BIT5;
		PORTD SET_BIT7;
		blink = 0;
		break;
	}
	}
}

int main(void){
	//PD4..7 
	DDRD SET_BIT4; //Led D2 Red
	DDRD SET_BIT5; //Led D2 Green
	DDRD SET_BIT6; //Led D5 Green
	DDRD SET_BIT7; //Led D5 Red

	TCCR2B = 0x07; //When in simulation, use 0x01 up to 0x05.
	TIMSK2 = 0x01; 
	asm("sei"); 

	while(1){
		asm("nop");
	}
}

[edit] Push the Button

[edit] Button and blinky

This program blinks one LED and turns the other on when the user presses and holds the HWB-button on the AT90UsbKey.

#include <avr/io.h>

#include "my_bit.h"
#include "my_types.h"

//Se project config freq in Hz
//#define F_CPU 1000000UL  // 1 MHz
//#define F_CPU 14.7456E6
#include <util/delay.h>

#define SLEEP_TIME 500

int main(void){

DDRD SET_BIT5; //Led D2 Green
DDRD SET_BIT6; //Led D5 Green


//HWB, the button
DDRE CLEAR_BIT2;

uChar button=0;

while(1){
	//Has someone pressed the HWB-button? (HWB is the one used for bootloader activation)
	//Normaly high, button drives level low.
	GET_BIT(button, PINE, 2);
	if(button==0){
		PORTD SET_BIT5;
	} else {	
		PORTD CLEAR_BIT5;
	}	

	//Led D5, green
	PORTD SET_BIT6;
	_delay_ms(SLEEP_TIME);
	PORTD CLEAR_BIT6;
	_delay_ms(SLEEP_TIME);
}
}

[edit] Button with background blinky

If this program doesn’t blink with a stable frequency, make sure that you compile with optimization set to -Os.

#include <avr/io.h>
#include <avr/interrupt.h>

#include "my_bit.h"
#include "my_types.h"

uChar blink = 0;

ISR(TIMER2_OVF_vect){ 
	if(blink == 0){
		PORTD SET_BIT6; //Led D5, green
		blink++;
	} else {
		PORTD CLEAR_BIT6; //Led D5, green
		blink = 0;
	}
}


int main(void){
	uChar button=0;

	DDRD SET_BIT5; //Led D2 Green
	DDRD SET_BIT6; //Led D5 Green

	//HWB, the button
	DDRE CLEAR_BIT2;

	TCCR2B = 0x07; 
	TIMSK2 = 0x01; 
	asm("sei");

	while(1){
		//Has someone pressed the HWB-button?
		//Normaly high, button drives level low.
		GET_BIT(button, PINE, 2);
		if(button==0){
			PORTD SET_BIT5;
		} else {	
			PORTD CLEAR_BIT5;
		}	

	}
}


[edit] Report the Temperature (aka read the thermistor)

The temperature sensor uses a thermistor (R29), or temperature-sensitive resistor. This thermistor has a negative temperature coefficient (NTC), meaning the resistance goes up as temperature goes down. Of all passive temperature measurement sensors, thermistors have the highest sensitivity (resistance change per degree of temperature change). Thermistors do not have a linear temperature/resistance curve. The voltage across the NTC can be found using the A/D converter (connected to channel 0). This ADC outputs 10bits, and the pin has a potential max gain of 200 (this example uses no gain). See the AT90USB1287 Datasheet, and AT90USBKey Hardware Guide for more information on how to use the ADC.


The input voltage to the ADC (VADC0) is calculated with the following:-

VADC0 = (ADC * Vref) /1023

Where: VADC0 = voltage across NTC as read by the ADC

ADC = the value reported by the ADC. For a 10 bit ADC, this can be between 0 and 1023
Vref = reference voltage (we will assume the key is powered by the USB, so Vref = Vcc = 5V)


The thermistor value (RT) is calculated with the following expression:

RT= (RH .VADC0) / (VCC- VADC0)

Where: RT = Thermistor value (Ω) at T temperature (°Kelvin)

RH = Second resistor of the bridge ie 100 KΩ ±10% at 25°C
VADC0 = Voltage value on ADC-0 input (V)
VCC = Board power supply


The NTC thermistor used in AT90USBKey has a resistance of 100 KΩ ±5% at 25°C (T0) and a beta-value of 4250 ±3%. By the use of the following equation, the temperature (in °Celsius) can be calculated:

T = ß / (ln(RT/R0) + ß/T0) - 273

Where: RT = Thermistor value (Ω) at T temperature (Kelvin)

ß = 4250 ±3%
R0 = 100 kΩ ±5% at 25°C (=298K)
T0 = 298K (Kelvin)
273 = subtracting 273 converts Kelvin to celsius

(If it is not known, beta can be calculated by measuring resistance at two known temperatures by ß = ln(r_t2/r_t1)/(1/t2 - 1/t1)

Using the above formulae, temperatures can can be calculated from ADC return value (indicating thermistor resistance). However this uses floating point math, and it is likely the on-chip code will be bulky. This example therefore uses a simple look-up table (created in a spreadsheet from the formulae) to convert the ADC value to a temperature.


The on chip Analog Digital Converter (ADC) is controlled by five registers:

ADMUX - ADC Multiplexer Selection Register (REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0)
ADCSRA - ADC Control and Status Register A (ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0)
ADCL and ADCH - The ADC Data Register (depends on ADLAR – see sec 25.8.3.1, p329)
ADCSRB - ADC Control and Status Register B (ADHSM ACME –--- –--- –--- ADTS2 ADTS1 ADTS0)
DIDR0 - Digital Input Disable Register 0 (ADC7D ADC6D ADC5D ADC4D ADC3D ADC2D ADC1D ADC0D)

The chip reset process sets all bits in all registers to “0” (so you don’t have to clear any bits on ADC Setup)

Review the AT90USB1287 Datasheet section 25.8 (p326) for full documentation of these registers.

/**
 * Procedure for sampling analog source
 *  1. Set up the various registers
 *  2. Write to ADCSRA register to start sample
 *  3. Poll ADCSRA register until sample finished
 *  4. Read result in ADCH and ADCL. (High and low bits - We use only ADCH.)
 *  5. Convert to temperature using lookup table
 *  6. Display temperature using LEDs
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#include "my_bit.h"
#include "my_types.h"

#define ADC_PRESCALE_MASK (BIT(ADPS2) | BIT(ADPS1))  // Prescale 8MHz crystal to get ADC clock between 50-200kHz
                                                     //   See sec 25.4 (p318) & 25.8.2 (p328)
							//   8MHz / 64 = 125kHz (set prescaler to “110” to divide by 64)
#define ADC_NOT_DONE()     (ADCSRA & BIT(ADSC))

                               //Red                  Green
#define LED_D2_GREEN    PORTD &= ~(1<<PORTD4) ; PORTD |=  (1<<PORTD5)
#define LED_D2_RED      PORTD |=  (1<<PORTD4) ; PORTD &= ~(1<<PORTD5)
#define LED_D5_GREEN    PORTD &= ~(1<<PORTD7) ; PORTD |=  (1<<PORTD6)
#define LED_D5_RED      PORTD |=  (1<<PORTD7) ; PORTD &= ~(1<<PORTD6)
#define LED_D2_ORANGE   PORTD |=  ((1<<PORTD4) | (1<<PORTD5))
#define LED_D5_ORANGE   PORTD |=  ((1<<PORTD7) | (1<<PORTD6))
#define LED_ALL_ON      PORTD |=  ((1<<PORTD4) | (1<<PORTD5) | (1<<PORTD7) | (1<<PORTD6))
#define LED_ALL_OFF     PORTD &= ~((1<<PORTD4) | (1<<PORTD5) | (1<<PORTD7) | (1<<PORTD6))


/* 8-bit Temperature Lookup Table – the 2 least significant bits of the ADC will be ignored
   Converts ADC 8bit value direct to temp (deg Celsius)
   This table is calculated using the formulae:  
      VADC0 = (ADC * Vref) /1023
      RT= (RH .VADC0) / (VCC- VADC0)
      T = ß / (ln(RT/R0) + ß/T0) - 273
*/
const signed long ADCtoTempTable[256] = { 
300, 214.3, 178.2, 159.4, 147, 137.7, 130.5, 124.5, 119.5, 115.1, 111.3, 107.8, 104.7, 101.9, 99.4, 97, 94.8, 92.7, 90.8, 89, 87.3,
85.7, 84.2, 82.7, 81.3, 80, 78.7, 77.5, 76.3, 75.2, 74.1, 73.1, 72, 71, 70.1, 69.2, 68.3, 67.4, 66.5, 65.7, 64.9, 64.1, 63.4, 62.6,
61.9, 61.2, 60.5, 59.8, 59.1, 58.4, 57.8, 57.2, 56.5, 55.9, 55.3, 54.7, 54.2, 53.6, 53, 52.5, 51.9, 51.4, 50.9, 50.3, 49.8, 49.3,
48.8, 48.3, 47.8, 47.3, 46.9, 46.4, 45.9, 45.5, 45, 44.6, 44.1, 43.7, 43.2, 42.8, 42.4, 42, 41.5, 41.1, 40.7, 40.3, 39.9, 39.5,
39.1, 38.7, 38.3, 37.9, 37.5, 37.2, 36.8, 36.4, 36, 35.6, 35.3, 34.9, 34.5, 34.2, 33.8, 33.5, 33.1, 32.7, 32.4, 32, 31.7, 31.3, 31,
30.6, 30.3, 30, 29.6, 29.3, 28.9, 28.6, 28.3, 27.9, 27.6, 27.3, 26.9, 26.6, 26.3, 25.9, 25.6, 25.3, 25, 24.6, 24.3, 24, 23.6, 23.3,
23, 22.7, 22.4, 22, 21.7, 21.4, 21.1, 20.8, 20.4, 20.1, 19.8, 19.5, 19.1, 18.8, 18.5, 18.2, 17.9, 17.5, 17.2, 16.9, 16.6, 16.3,
15.9, 15.6, 15.3, 15, 14.6, 14.3, 14, 13.7, 13.3, 13, 12.7, 12.4, 12, 11.7, 11.4, 11, 10.7, 10.4, 10, 9.7, 9.3, 9, 8.7, 8.3, 8,
7.6, 7.3, 6.9, 6.6, 6.2, 5.8, 5.5, 5.1, 4.7, 4.4, 4, 3.6, 3.3, 2.9, 2.5, 2.1, 1.7, 1.3, 0.9, 0.5, 0.1, -0.3, -0.7, -1.1, -1.5, -2,
-2.4, -2.8, -3.3, -3.7, -4.2, -4.6, -5.1, -5.6, -6.1, -6.6, -7.1, -7.6, -8.1, -8.6, -9.1, -9.7, -10.3, -10.8, -11.4, -12,
-12.6,-13.3, -13.9, -14.6, -15.2, -15.9, -16.7, -17.4, -18.2, -19, -19.9, -20.7, -21.6, -22.6, -23.6, -24.7, -25.8, -27, -28.3,
-29.7, -31.2, -32.9, -34.7, -36.8, -39.2, -42.1, -45.7, -50.6, -58.4
};


int setupADC(void){
  // Set up ADC
  // ADMUX - ADC Multiplexer Selection Register (REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0)
  // BIT(REFS0): Set reference to AVcc
  // BIT(ADLAR): Set left-alignment for easy 8-bit reading of most significant bits of ADCH
  // MUX0:       Enable ADC0, 0x Gain (available gain: 0x, 1x, 10x, 200x)
  ADMUX = BIT(REFS0)|BIT(ADLAR)|MUX0;

  // ADCSRA - ADC Control and Status Register A
  // BIT(ADEN):         Enable ADC
  // ADC_PRESCALE_MASK: Set clock prescale
  ADCSRA = BIT(ADEN)|ADC_PRESCALE_MASK;

  //ADCSRB - ADC Control and Status Register B
  // No changes required from default

  // DIDR0 - Digital Input Disable Register 0
  SET_BIT(DIDR0, BIT(ADC0D));  // Disable digital input buffer on the ADC pin (reduces power consumption)

  return 1;
}

int main(void){

  uLong temperature;

  setupADC();

  // Init LEDs
  LED_ALL_OFF;    // Turn off LEDs
  DDRD = 0xf0;    // set bits 4..7 to output mode


  while (1) {
    SET_BIT(ADCSRA, BIT(ADSC));  // Start conversion. Hardware will clear this bit when conversion is complete
    while (ADC_NOT_DONE()){};  // Now wait for ADSC to be set (indicating conversion is finished)

    // use lookup table to convert ADC to temp
    temperature = ADCtoTempTable [ADCH];   

    // Temp is shown using LED colour
       // >35 LEDs Off;  >32 RR;   >28 RO;   >25 RG;   >22 OR;  >18 OO;   
       // >15 OG;  >11 GR ;  >7  GO ;  >0  GG;  <=0 LEDs Off

    // If the goal was purely to switch LEDs, the lookup table section could be done away with 
    //   and just use ADC values, but it shows how to do it in more complicated cases...
    if (temperature > 35) { LED_ALL_OFF; }  // too hot, LED1,2 Off - be nice to the key!! :-(
    else if (temperature > 32) { LED_D2_RED; LED_D5_RED; } 
    else if (temperature > 28) { LED_D2_RED; LED_D5_ORANGE; }
    else if (temperature > 25) { LED_D2_RED; LED_D5_GREEN; }
    else if (temperature > 22) { LED_D2_ORANGE; LED_D5_RED; }
    else if (temperature > 18) { LED_D2_ORANGE; LED_D5_ORANGE; }
    else if (temperature > 15) { LED_D2_ORANGE; LED_D5_GREEN; }
    else if (temperature > 11) { LED_D2_GREEN; LED_D5_RED; }
    else if (temperature > 7) { LED_D2_GREEN; LED_D5_ORANGE; }
    else if (temperature > 0) { LED_D2_GREEN;LED_D5_GREEN; }
    else { LED_ALL_OFF; }       // too cold, LED1,2 Off - be nice to the key!! :-(
  }

}


[edit] DataFlash Storage/Retrevial

This is currently beyond the scope of this wiki

For some (potentially) useful code see:

  • AVRFreaks - Search in the forums and projects for "AT45" or "AT45DB642D" (The AVR DataFLASH Help Topic has some good code discussion.)

Don't forget to logon so you can see any code examples

Download the sourcecode (currently LUFA 081217.zip as of Dec 2008)
Extract this, then check out the code under Demos\MassStorage.
Thanks to Dean Camera for creating it...

[edit] Header files that this tutorial depends on

[edit] my_bit.h

/**
 * My bitwise operations.
 * "~" (tilde) = NOT ... "|" (pipe) = OR ... "^" (caret) = XOR ... "&" (ampersand) = AND
 * ">>" = right shift ... "<<" = left shift
 * See the Bitwise_operation Wiki for more info - http://en.wikipedia.org/wiki/Bitwise_operation
 */

#ifndef __MY_BIT
#define __MY_BIT

#define SET_ALL  |= 0xFF; 
#define SET_BIT0 |= 0x01; 
#define SET_BIT1 |= 0x02;
#define SET_BIT2 |= 0x04;
#define SET_BIT3 |= 0x08;
#define SET_BIT4 |= 0x10;
#define SET_BIT5 |= 0x20;
#define SET_BIT6 |= 0x40;
#define SET_BIT7 |= 0x80;

#define CLEAR_ALL  &= 0x00;
#define CLEAR_BIT0 &= 0xFE;
#define CLEAR_BIT1 &= 0xFD;
#define CLEAR_BIT2 &= 0xFB;
#define CLEAR_BIT3 &= 0xF7;
#define CLEAR_BIT4 &= 0xEF;
#define CLEAR_BIT5 &= 0xDF;
#define CLEAR_BIT6 &= 0xBF;
#define CLEAR_BIT7 &= 0x7F;


//TO_VAR, FROM_VAR unsigned char
//BIT_NUM the bit to get 0..7
// input byte is FROM_VAR
// return value: TO_VAR = 0 or TO_VAR = 1
// process: set TO = FROM; right shift it so the relevant bit is now bit 0; 
// & with 1 to remove all other bits (TO_VAR can now only equal 1 or 0)
#define GET_BIT(TO_VAR, FROM_VAR, BIT_NUM)  {\
	TO_VAR=FROM_VAR;\
	TO_VAR>>=BIT_NUM;\
	TO_VAR&=0x01;\
}

// Some alternative macros for bit manipulation
// Usage eg: BIT_SET(ADMUX, BIT(REFS0));  // Set reference to AVcc
#define BIT(x)                (1 << (x))  // replacement for the poorly named _BV

#define SET_BIT(var, mask)    ((var) |= (unsigned char)(mask))
#define CLEAR_BIT(var, mask)  ((var) &= (unsigned char)~(mask))
#define TOGGLE_BIT(var, mask) ((var) ^= (unsigned char)(mask))
#define READ_BIT(var, mask)   ((var) &  (unsigned char)(mask))

#define ASSIGN_BIT(var, val, mask) ((var) = (((var)&~(unsigned char)(mask))|((val)&(unsigned char)(mask))))

#endif //__MY_BIT

[edit] my_types.h

/**
* My typedef:s so I don't have to type unsigned all the time.
*/

#ifndef __MY_TYPES
#define __MY_TYPES

typedef unsigned char uChar;
typedef unsigned int  uInt;
typedef unsigned long uLong; 

#endif //__MY_TYPES

[edit] Other good programming techniques

[edit] Function, inline function or macro

How to implement our functions is a interesting qustion and it depends on what result you want.

Let's look at the GET_BIT function that we first saw in the "Push the Button" exampels.

The first way is to implement it as normal function.

uint8_t getBit(uint8_t data, uint8_t bitPos);
uint8_t getBit(uint8_t data, uint8_t bitPos){
	data >>= bitPos;
	data &= 0x01;
	return data;
}

But we could also change this into a inline function.

inline uint8_t getBit(uint8_t data, uint8_t bitPos) __attribute__((always_inline));
inline uint8_t getBit(uint8_t data, uint8_t bitPos){
	data >>= bitPos;
	data &= 0x01;
	return data;
}


Or we could do the entire thing as a macro.

#define GET_BIT(TO_VAR, FROM_VAR, BIT_NUM)  {\
	TO_VAR=FROM_VAR;\
	TO_VAR>>=BIT_NUM;\
	TO_VAR&=0x01;\
}

Now looking at the three way of doing basically the same way, one might wonder wy!

And in this example there is really not that much off a difference, but depending on how big your own function is and how often it is called it can vary a lot. The answer in your case lays in the assembler code that gcc produces, lock at it and choose the way that fits your needs.

[edit] Links

[edit] Function name abstraction with macros

#include <avr/io.h>

int cnt = 0;

//#define DOUBLE_INC

#ifdef DOUBLE_INC
	#define function functionNr2
#else 
	#define function functionNr1
#endif

void functionNr1(){
	cnt++;
}

void functionNr2(){
	cnt+=2;
}


int main(void){
	while(1){
		function();
	}
}

Note: This can be seen in the USB Firmware in the schedule part, the scheduler.c only calls Scheduler_task_1, Scheduler_task_2 etc but those name is never seen in the code since they are translated in conf_scheduler.h with "#define Scheduler_task_1 usb_task".

[edit] USB Interfacing

Atmel provides drivers and some example code to support a variety of USB classes: Mass Storage Device (MSD), Human Interface Device (HID), Device Firmware Upgrade (DFU), Communication Device Class (CDC), Audio Class, etc. There is a generic HID implementation which simplifies communication between PC and the Key.


Providing the factory installed bootloader is retained, DFU (ie loading new firmware onto the device) is possible via USB. This does not provide access to the 1287 fuses (this must be done via JTAG or SPT interfaces), the bootloader is not overwritten in this process.

[edit] The USB CDC Atmel example

The RS232 (RS232, Serial port, UART) has been replaced by the USB port in newer computers. An interface between the PC and the AT90USBkey can be implemented using the CDC (Communication Device Class) specification to create a virtual COM Port and UART to USB bridge so older applications (based on RS232) can be more easily ported to the new hardware.

Note: For some reason Atmel has choosed not to publish some of the datasheets regarding their usb-firmware, including a bare bone version of the code. Please also note that the firmware is NOT under an open source license which probably makes it illegal to make a complete documentation of the code (since that documentation would publish the secret code), so the sections below will focus on how to use the code (only publish half code examples).

[edit] Recommended reading

  • doc7603.pdf - AVR329: USB Firmware Architecture, (This document can be found on the UsbKey in dir \starterkits\STK525-USBKEY\Docs)



[edit] Test out the USB CDC Demonstration UART to USB Bridge

  1. Read doc7619.pdf and download at90usb128-demo-cdc-1_0_3.zip
  2. Burn the UsbKey with the cdc.a90 file
  3. Start the UsbKey (driver is located in at90usb128-demo-cdc\inf)
  4. Open the Device Manager and look under the Ports (COM & LPT) section, the device should be there.
  5. Remember what com-port-number the device got.
  6. Open HyperTerminal (hypertrm.exe) with the com-port-number found in the device manager
  7. On the UsbKey, move the joystick around, press the joystick down and press the HWB button.
  8. Each action produces a different string.

Later, we change and recompile the demo code.
Our change will make "Hello world!" appear when we press the HWB button.

[edit] Rebuild the demo program

  1. Unzip at90usb128-demo-cdc-1_0_3.zip and put the at90usb128-demo-cdc dir where you want it to be.
  2. Edit at90usb128-demo-cdc\at90usb128\demo\cdc\gcc\cdc.aps and replace all C:\Atmel\at90usb128-demo-cdc\... to your new path C:\my\own\dir\at90usb128-demo-cdc\...
  3. Open the cdc.aps with AVR Studio and try to compile (it should complile ok)
  4. Edit files config.h, Makefile and cdc_task.c (as described below)

Now we have a nice little program as a base to debug with, with a usb printf.

[edit] Makefile

From:

## Objects that must be built in order to link
OBJECTS = usb_specific_request.o cdc_task.o usb_descriptors.o main.o power_drv.o usb_drv.o usb_task.o 
  usb_standard_request.o usb_device_task.o scheduler.o stk_525.o uart_lib.o uart_usb_lib.o

...

stk_525.o: ../../../lib_board/stk_525/stk_525.c
	$(CC) $(INCLUDES) $(CFLAGS) -c  $<

To:

## Objects that must be built in order to link
OBJECTS = usb_specific_request.o cdc_task.o usb_descriptors.o main.o power_drv.o usb_drv.o usb_task.o 
  usb_standard_request.o usb_device_task.o scheduler.o usb_key.o uart_lib.o uart_usb_lib.o

...


usb_key.o: ../../../lib_board/usb_key/usb_key.c
	$(CC) $(INCLUDES) $(CFLAGS) -c  $<

##stk_525.o: ../../../lib_board/stk_525/stk_525.c
##	$(CC) $(INCLUDES) $(CFLAGS) -c  $<


[edit] config.h

From:

//! To include proper target hardware definitions, select
//! target board (USBKEY or STK525)
#define TARGET_BOARD USBKEY 

#if (TARGET_BOARD==USBKEY)
   //! @warning for #define USBKEY_HAS_321_DF, only first prototypes versions have AT45DB321C memories
   //! should be undefined for std series
   #define USBKEY_HAS_321_DF
   #include "lib_board\usb_key\usb_key.h"

To:

//! To include proper target hardware definitions, select
//! target board (USBKEY or STK525)
//#define TARGET_BOARD STK525
#define TARGET_BOARD USBKEY 

#if (TARGET_BOARD==USBKEY)
   //! @warning for #define USBKEY_HAS_321_DF, only first prototypes versions have AT45DB321C memories
   //! should be undefined for std series
   //! #define USBKEY_HAS_321_DF
   //! The memories are AT45DB6420-CNH on my unit -Bob..
   #include "lib_board\usb_key\usb_key.h"

[edit] cdc_task.c

This change is just to prove that we really recompiled and changed the program.

From:

while (rx_counter)
{
  uart_putchar(uart_usb_getchar());   // loop back USB to USART
  Led3_toggle();
}

...

if(Is_hwb())
  printf("Hello from AT90USBXXX !\r\n");

To:

while (rx_counter)
{
  //uart_putchar(uart_usb_getchar());   // loop back USB to USART
  uart_usb_putchar(uart_usb_getchar()); //loop back USB to USB
  Led3_toggle();
}

...

if(Is_hwb())
  printf("Hello world!\r\n");

[edit] Combining Mass Storage AND CDC Interface

This is described in this tutorial found on AVR Freaks:-

NB you need to be logged in to see the link to the pdf!

[edit] External links

ATMEL


AT90 Software

AT90USB Information


Information about programming the key

The LUFA site has has a number of demo projects (for AVR Studio/AVR-GCC) and libraries for use with various USB-enabled AVR microcontrollers


General Information on AVR programming

Personal tools