Ditching the Arduino IDE
Some time ago, I started playing around with the Atmega328p microcontroller without the Arduino Uno board. Now it’s time to try doing the same on the software side, even as just an experiment. I’ve been getting started with AtmelStudio which allows you to program Atmel microcontrollers in C. I’m still not quite clear on what to call the Arduino programming language other than C, if not a simpler form of C because the Arduino language takes care of a lot of things for you to make it as easy as it is to program an Arduino.
I learned about microcontroller programming in college. I believe we used a PIC microcontroller for the labs so, while I recognize certain things, there seems to be a little variation in abbreviations. In the labs, the microcontrollers were part of a board my professor designed that had a bunch of peripherals like buttons, switches, LEDs, and an LCD. It was a very thorough course with loads of information to take in. I hope I can remember it all!
Anyways, I’d really like to continue playing around with it and see if I can make a full project with one of my Atmega328p boards programmed using AtmelStudio. I’m taking baby steps to get up to a good speed in AtmelStudio, and I hope that writing small tutorials on the blog will help me firm up my knowledge. If you spot anything that needs correcting, please leave a comment!
The rest of this post will introduce you to the little work I’ve done so far, and may be helpful for anyone else who may be interested in trying it out too.
I made two Hello World programs, with the help of a bunch of guides which I will link to throughout the rest of this post. Both programs blink a bunch of LEDs but using different methods. One uses a delay, while the other uses an interrupt routine.
USBtinyISP with AtmelStudio
AtmelStudio does not work right away with the USBtinyISP, which is what I used to program the Atmega328p. You must add it as an external tool. I followed this guide to add it to AtmelStudio. You’ll need to download WinAVR to complete the guide.
As the guide says, to upload code with the USBtinyISP, you must go to Tools>USBtinyISP (or whatever you named it). Just remember to build your code first before uploading it.
Using a Delay (and Beginner’s Introduction)
As I mentioned, I made two programs that blink LEDs. One uses a delay while the other uses a timer interrupt. Let’s first take a look at the delay version.
The first line defines the clock speed, which is 16MHz. The next line includes io.h header file, which actually just includes the appropriate file for the project’s microcontroller (which you select when you create the project, in this case the Atmega328p) that contains definitions for register addresses and things like that. The third line includes delay.h header file which enables us to use _delay_ms(); to cause the delay.
Now, into the main program! But first, let’s talk about ports.
This is the pinout of the Atmega328p-pu. Some of the pins are labelled PBx, PCx, and PDx, where x is a number. These stand for PORTB, PORTC, and PORTD, which are 8-bit data registers where each bit is a pin. The pins on the uC aren’t exactly laid out as cleanly as an Arduino Uno when looking up and down the rows of pins, but you can always order them in a way that works best for you when you’re actually wiring up your project.
Anyways, back to the code. In the first two lines of code in our main function, we set all of the pins in PORTB and PORT D to outputs. This is equivalent to the pinMode() function of the Arduino which is used to set certain pins as inputs or outputs. DDR stands for Data Direction Register where setting a bit/pin in the DDR to 1 makes it an output and setting it to 0 makes it an input. To make things easier, I put it in hexadecimal format (“0xFF”). You could also write “0b11111111” which is the binary equivalent and is more helpful when pins in a port are a mix of inputs and outputs. The third line in the main function sets all of the bits in PORTD to 1.
Everything in them main function up to this point is like the setup() function in the Arduino IDE. This code only runs once when the uC is powered on. Everything in the while loop will loop forever, like loop() in the Arduino IDE.
We set all of the bits in PORTD to 1 earlier so that we can toggle all of the bits on PORTB and PORTD at the same time and they will alternate. If we didn’t set them before we started the loop, all the bits in both ports would be the same. In another case, we could put in some logic that would toggle or set all of the bits in PORTD to 1 on the first loop, but that’s unnecessary since we could do it this way.
The exclusive OR (abbreviated as XOR, symbolized as ^) operation can be used to toggle bits high and low whenever the line is executed. In this code, the bits in PORTB and PORTD are toggled, and then there is a delay set for 250ms on the last line of the while loop. It loops around and toggles all of the bits again, and so on.
Using an Interrupt
A lot of the core stuff was covered in the previous code section so I’m not going to go into as much detail for any repeating lines in this section. Also, I’m rusty when it comes interrupts especially so don’t quote me on anything. Check out this and this for the information that I used to help me get this working. Also see the Atmega328p datasheet.
This program sets up Timer1 interrupt so that the ISR, or Interrupt Service Routine, will execute every 250ms. Let’s go line by line where we initialize Timer1.
cli(); disables interrupts. This is used so that important code that shouldn’t be interrupted isn’t.
The next two lines set all of the bits in the TCCR1A and TCCR1B, or Timer/Counter Control Registers for Timer1, to 0.
See the large comment block for information on calculating the Output Compare Register (OCR1A) (“Compare Match Register” in the comment block).
The next line enables the Clear Timer on Compare Match mode, which clears the timer once a match is made.
The next two lines are to set the prescaler to 1024, which is a value that was used to calculate the value for the Output Compare Register. The following table, found in the Atmega328p datasheet, shows how to set the bits in the TCCR1B register to set the prescaler.
The next line enables the Output Compare A Match interrupt.
The last line enables interrupts.
There’s nothing in the while loop since all of our code is in the interrupt routine.
The bottom of the code is the Interrupt Service Routine (ISR) which is the code that runs when there is an output compare match with Timer1. In the ISR, we toggle the bits on PORTB and PORTD like we did in the other Hello World example. The ISR code is run every time there is an output compare match, which is set to 250ms.
So, uh, that’s it.
Trying to write this post was difficult. I need some more reading up and experimenting to do. I hope you got something out of this, at least a link to somewhere that’s more helpful.
Thanks for reading!