Friday, September 4, 2020

Program any AVR microcontrollers using WinAVR and USBTinyISP - Getting started with AVR tutorial for beginners

Nowadays Arduino is the platform of choice for programming AVR microcontrollers and for good reasons. But there are times when you want to have full control over what is added to your code. 

For example Arduino is enabling by default Timer0 for use in millis function and other functions and includes some interrupt routines that perhaps your project is not using and so adding to the code size or perhaps those interrupts can interfere with your code.

What you need:

  • ATmega328P (used in this tutorial as an example)
  • USBTinyISP programmer 
  • WinAVR software (more on this later)

What you can learn:

  • how to program an AVR microcontroller using an In-System Programmer such as USBTinyISP and WinAVR
  • some bitwise operations for handling the registers

There are two ways to program a microcontroller:

- ISP (In System Programming) using SPI protocol and a ISP programmer
- With a bootloader using UART protocol and a USB to Serial programmer. Some microcontrollers come with a bootloader already pre-programmed on them.
 
This tutorial will cover the ISP programming way.

Programming software

There are many tools for programming an AVR microcontroller such as Atmel Studio, PlatformIO, Eclipse with an AVR plugin, etc but the simplest and light weight solution that I found is using WinAVR.

Programming hardware

Apart from development software there is also the need of a hardware programmer that the software uses to communicate with the microcontroller and upload the code to it. Searching online for 'avr programmer' reveals lots of options. The most popular I believe is the USBTinyISP and is very cheap. There is also Atmel-ICE from Atmel. A bit more expensive but it has the benefit of being able to debug and see in real time what happens inside the microcontroller.


WinAVR and USBTinyISP - Getting started with AVR


Getting started with WinAVR

WinAVR is a free suite of executable open source software development tools for the Atmel AVR series for Windows. WinAVR contains all the tools for developing on the AVR such as avr-gcc compiler, avrdude for uploading the code and avr-gdb for debugging.

To download WinAVR visit the project homepage at http://winavr.sourceforge.net and in the download section you will find the link https://sourceforge.net/projects/winavr/files and on that page click on download the latest version.

After installation search on windows start for WinAVR and you should see Programmer's Notepad app.

Programmers Notepad

Programmer's Notepad does everything for you. Is calling the make utility, which executes your makefile, which in turn calls the compiler, linker, and other utilities used to build your software. You just need to write the code and click Make then Program. 
 

Starting first WinAVR project

Inside Programmer's Notepad click File -> New -> Project. Choose a name for the project then navigate to a folder where you want to put the project files. I named mine Blink LED.

Programmer's Notepad make project folder

Now you should see the project in the left side panel. Here you can also make/rename a project group.
 
Programmers Notepad adding project files
 
Now to add a file first add some code and save the file as main.c in the folder where you saved the project file. The name must be main and the extension can be .c for C or .cpp for C++ languages. Bellow is a code template that I made for my future projects.



/*************************************************************
	INCLUDES
**************************************************************/
#include <avr io.h>
#include <util delay.h>
#include <avr interrupt.h>


/*************************************************************
	DEFINES
**************************************************************/



/*************************************************************
	GLOBAL VARIABLES
**************************************************************/



/*************************************************************
	FUNCTION PROTOTYPES
**************************************************************/
 
 
 
/*************************************************************
	MAIN FUNCTION
**************************************************************/
int main(void){
	
	
	while(1){
	

		}
	}
}

/*************************************************************
	FUNCTIONS
**************************************************************/



/*************************************************************
	ISR Handlers
**************************************************************/

By right clicking on the project name use Add Files to add the main.c file to the project.
To display the line numbers go to View -> Line Numbers.

Make file 

Now it is time to setup the make file. This file tells WinAVR what microcontroller do we use, what CPU frequency, what programmer, on what port, etc. For this, search like before for WinAVR but this time open MFile [WinAVR]. Then select Makefile -> MCU type -> ATmega -> atmega328p

make file select mcu type

From Port select usb. Then on the bottom click Enable Editing of Makefile. Now from Programmer choose your programmer. If you use USBTinyISP and it doesn't appear in the list simply pick a random one and then change it with usbtiny.
The last setting is the CPU frequency. Scroll up in the file until you see F_CPU and put your CPU frequency. For ATmega328 use 8000000 which means 8MHz - 8 with 6 zeros.
 
 
Makefile F_CPU frequency

 
Finally save the file as Makefile (without any extension) inside the project folder. Later it can be open and edited using Programmers Notepad.
The following section talks about configuring a port, bitwise and binary operations. If you are already familiar with this, skip to this section uploading the code to the microcontroller

Learning AVR programming by blinking a LED using ATmega328P


On page 12 you can find the pinout. Say you want to connect the LED to pin 28 of the 28 PDIP package. Near the pin number 28, PC5 indicates that this pin belongs to port C and has the port pin number 5. By looking at the other pins we notice that there are 3 ports: B, C and D each one having 8 pins (0 to 7) except port C that has only from 0 to 6. Inside the parenthesis there are shown what functions the pin can have, like ADC for example (Analog to Digital Converter).

To setup the pins we use the defines

#define LED_DDR			DDRC
#define LED_PORT		PORTC
#define LED_PIN			PC5
After #define any name can be used. Then press TAB a few times to make the lines look nice and readable and then write the values.

DDRx stands for Data Direction Register and selects the direction of the pin - input or output. You want it to be an output when driving an LED or transistor and an input when reading a button press. The x after DDR is the port and in our case is C.

*A register in an 8 bit microcontroller like this one is just a set of 8 bits that holds a number and the microcontroller uses the bit values to configures itself.
 

AVR microcontrollers, binary, ports and bitwise operators

Configuring a pin as a digital output

DDRC |= 1 << PC5

Let's analyze the above line. Somewhere in the avr_io.h included file, PC5 is associated with the value 5 and so PC5 will be replaced with 5. So is the case with the defines. When we write 
 
LED_DDR |= 1 << LED_PIN 
 
the pre-processor will replace LED_DDR with DDRC and LED_PIN with PC5.
<< is called the left shift and makes part of the bitwise operations. 1 << 5 is saying move the binary representation of 1, 5 times to the left.
 
In binary 1 is 0b00000001 and after it has been shifted 5 times to the left is 0b00100000
We can also write it like this:

DDRC |= 0b00100000

The 8 bits represents our DDRC register and each bit is the pin. 
Pin 0 is 0b00000001
Pin 1 is 0b00000010
Pin 2 is 0b00000100
Pin 3 is 0b00001000
Pin 4 is 0b00010000
Pin 5 is 0b00100000
Pin 6 is 0b01000000
Pin 7 is 0b10000000
By putting 1 on that bit position we set the DDR (direction) of the pin to be an output. Leaving it or setting it to 0 will make it as an input.
So what is with the | symbol? It is called a bitwise or operator.

0b00100000 | 0b00000010 = 0b00100010

because as the name suggests 
0 or 1 = 1
1 or 1 = 1
0 or 0 = 0
In a bitwise operation two bytes are compared against each other bit by bit. Notice the colors: red (bit 0) is compared to the other red, blue (bit 1), green (bit 2) and so on.
The purpose of the or | operator is to change our desired pin but leave the others the same as before.
If we have some other pin controlling a motor for example PC1 and we change pin 5 (PC5)

DDRC before is 0b00000010 - PC1 is 1 (output)

DDRC |= 1 << PC5

DDRC after is 0b00100010
Notice that the use of the or operator, left the PC1 bit unchanged.

To practice or to solve bitwise operations there is this nice website http://bitwisecmd.com/
The browser's search bar can also be used without pressing enter.
 

Setting a digital output pin HIGH

After setting the pin as an output, let's make it HIGH to drive the LED.
PORTC |= 1 << PC5
This leaves the other pins unchanged. To make only this pin high and the rest low (0) we could write:
PORTC = 1 << PC5

Setting a digital output pin LOW

PORTC &= ~(1 << PC5)
Now the pin is connected to ground.

Configuring a pin as a digital input

As before but instead of 1 we write 0 to the specific pin
 
DDRC &=  ~(1 << PC5)
 
~ is the not operator so 0b00100000 becomes 0b11011111

DDRC before is 0b00100000

DDRC &= 0b11011111 (after not operation)

DDRC after is 0b00000000

because with & bitwise operator both values must be 1
1 and 1 is 1
1 and 0 is 0
0 and 1 is 0

When a pin is set as an input and we write this

PORTC |= 1 << PC5

then the internal pull-up resistor is activated. Now we could add a push button between the pin and ground and when the button is pressed the pin will be pulled to ground and when the button is not pressed the pin will be high due to the pull-up resistor. This is how we can read if a button is pressed.

if(PINC & (1 << PIN5))

PINC represents all the pins of port C and by some bitwise operations we check only if pin 5 in 1 or 0.

Toggling a pin

DDRC ^= 1 << PC5
 
If the pin is low (0) will be made high (1) and vice versa.
 
In summary to set a bit in a register to 1 use

DDRC |= 1 << PC5

and to set a bit to 0 use

DDRC &= ~(1 << PC5)

Another way is to use the _BV() macro function that means bit value. So the lines above can be replaced by

DDRC |= _BV(PC5)
DDRC &= ~_BV(PC5)
Now finally let's blink that LED with the following code:
 
int main(void){
	// Set the pin as output
	LED_DDR |= 1<<LED_PIN;
	
	// Set the pin HIGH
	LED_PORT |= 1<<LED_PIN;
	
	while(1){
		// Toggle the pin
		LED_PORT ^= 1 << LED_PIN;
		
		// Wait 500 milliseconds
		_delay_ms(500);
	}
}

To compile the code click Tools -> Make All. There is a bug in WinAVR and the message error should appear:
'fatal error: opening dependency file .dep/main.o.d: no such file or directory'

To fix it visit this forum https://www.avrfreaks.net/forum/windows-81-compilation-error?page=all and you can find a link to a .dll file. Copy this file in the WinAVR installation folder -> utils -> bin and replace the existing file. Now it should compile without errors.

To upload the code to the microcontroller click Tools -> Program. But first lets see how to connect the microcontroller to the programmer.

Connecting USBTinyISP to ATmega328P AVR

USBTinyISP header pinout

USBTinyISP has 2 headers and each one can be used but I prefer the small one. In my case the pin headers are mirrored so you should check with a multimeter first with the power of. 

The corresponding pins on the microcontroller can be found in the datasheet.

MOSI - master out, slave in
MISO - master in, slave out
SCK - system clock
RST - reset

This protocol of communication is called SPI (Serial Peripheral Interface) .
A 10k resistor is needed from VCC to the reset pin of the microcontroller to keep it high. If the reset pin is pulled to ground then the microcontroller will be kept in reset  mode.
 
On the USBTinyISP there is a yellow jumper. If removed the programmer will not provide 5V power to the microcontroller (MCU) and you need to provide power from other source but be sure to have a common ground. This is useful when you want to power the micro with 3.3V for example.

Now if everything is connected properly you should be able to program the micro using Tools - > Program.

Having a LED blink using the delay function is useful for checking if the F_CPU is set correctly. If not the LED will blink either to slow or too fast. ATmega328 comes from the factory with their cpu clock  divided by 8. In the datasheet you will see the term prescaler. The CPU clock can be divided with a prescaler of 1 (no prescaler), divided by 8, 16... The micro has an internal RC oscillator at 8MHz and has a fuse set to divide this by 8 so the actual speed is 1MHz. You could either change the speed in the makefile to 1000000 or change the fuse and set the CPU divider to 1 and keep the 8MHz specified in the makefile.

There is also the option of connecting a 16MHz crystal oscillator to the microcontroller and get a more precise clock. For most applications the internal RC oscillator is just fine but don't build a clock with that.

AVR Fuses

There are 3 bytes stored in a special area of flash memory called fuse bits. These are Low, High and Extended fuse bytes. Setting some of this bits wrong can make the micro unusable and will be needed a special high voltage programmer to make it work again. For this reason there are fuse calculators and as long as you are careful what options you select it will be fine.

Here is a good online tool for fuse calculation https://www.engbedded.com/fusecalc Be sure to select the exact part. Default fuse values for ATmega328P look like this
 
ATmega328P default fuse values fuse calculator

Clock Selection

 
With the first selection menu the source and type for the CPU clock can be selected. The Clock Startup can be either of the following: 14CK + 0 ms, 14CK + 4 ms, 14CK + 65 ms. 
The Startup Time is just how long it take for the clock source to stabilize from when power is first applied. Always go with the longest setting 14CK + 65ms unless you know for a fact your clock source needs less time and 65ms is too long to wait. 
 

Clock Output

 
Checking Clock output on PORTB0 will output a square wave with the clock frequency at pin PB0 and is useful when you want to debug the clock rate or to drive other chips. 
 

Clock Divide

 
This divides the clock by 8 and it's the default factory value. You may want to uncheck this to get the full speed. Although on battery powered devices the slower the clock the less power is consumed. 
 

Reset Disable 

 
Warning. This fuse turns the reset pin into a normal pin and after doing this the chip cannot be programmed anymore. 
 

Brown-out Detect (BOD)

 
When the voltage will drop beneath the specified BOD voltage the micro will turn off until the voltage returns to normal. This must be tuned on if the EEPROM memory is used to prevent write errors. The chip has a lower voltage limit at which it can work reliably and beneath that voltage the chip can behave erratically, erasing or overwriting the RAM and EEPROM. 
 

Preserve EEPROM

 
If this is checked the EEPROM will not be erased during programming. Here usually settings are kept so is a good idea to check this to preserve the settings

To calculate the fuses click Apply feature settings. I don't recommend using the Manual fuse bits configuration unless you know what you are doing. 
 

Burning the fuses 

To program the fuses open a command prompt window and paste
avrdude -c usbtiny -p atmega328p -U lfuse:w:<0xHH>:m
avrdude -c usbtiny -p atmega328p -U hfuse:w:<0xHH>:m
avrdude -c usbtiny -p atmega328p -U efuse:w:<0xHH>:m

Replace 0xHH with the values from the fuse calculator.

 

Further reading

No comments:

Post a Comment