My new friend, Atmel Studio, and how easy everything is when you can see clear

This article is in a series about making a shelf with lights controlled by an AVR.
Read about 
 - designing and building the book-case
 - understanding Mosfets
 - and coding the AVR, and improving it using Atmel Studio

A returning stick in my wheels of programming is the lack of a decent step by step debugger. Often I fly off a mental cliff wondering what hit me, and how I caused this mess.

My previous setup with Notepad++, AVRDude and ArduinoISP worked well, but wouldn’t let me go through my code step by step to evaluate and debug. I found that Atmel Studio has step by step debugging of code. Since the cheap and versatile Atmega168 chips use are from the same manufacturer, I gave the free Atmel Studio a try.

The coding environment is a little slow at startup, but does the work elegantly. It has nice color codings for C, and it has quick setup features to get going with making. Atmel studio is based on Microsoft Visual Studio Express 2010, which I am familiar with from some game programming in Python and C#. So with the already mentioned debug function built in, this is a better tool than my previous setup.

But Microsoft Visual Studio takes care of the C coding and the color layout, the big feature of Atmel Studio is the parts library, readily available datasheets, and the Simulator. The simulator shows every register in the chip, and indicates if a bit is set or zero, and if it changed in the latest clock cycle. The parts library I haven’t dived into as I am quite satisfied with the Atmega168 for now, and I’m used to look for datasheets online anyway, so the biggest feature for me (without looking for more features) is the simulator, which I will talk more about.

I do appreciate the strong datasheets for the Atmega AVR’s, but nothing beats seeing every change that occurs in the processor as the code progresses, in terms of understanding (not in terms of speed). Let’s bring in my shelf light project to show better how the simulator helped me.

PWM fader

Preparing for the Wife Acceptance Test (WAT) of my homemade shelf, built after the expensive one she wants to buy, adding a fade function to my shelf lighting will give me some extra points, and increase the chances of my shelf (and AVR) ending up in the living room.

In my original code, PWM switched the lights to another intensity at every button press. Now I’m adding a gradient to the way the lights change. So for a case where I wanted the lights to reduce gently down to a given setting, the code ended up like this:

		//set output to medium
		while(OCR2B > 100){
			OCR2B -= 1;
			_delay_ms(5);
		}

Now, before I added the _delay_ms(5), I got no fading effect of the lights. I checked the datasheet over and over again, and looked at other peoples code, but I could not see how my code differed from anyone else, or why my code failed.

In the Atmel Studio Simulator I can see what happens every tick of time in the CPU (and timers), and I found that the OCR2B register is only updated with a new value whenever the Timer reaches 255 (or zero), to avoid a broken PWM signal. When I set a new OCR2B value for almost every clock tick (it takes some ticks for each while loop), the OCR2B might not be set before the while loop has been run so many times that the fading is over.

I probably wouldn’t have understood this without the visualization from the simulator in Atmel Studio, but I found that the light intensity changed after every fade cycle, when I let the code run freely to wait for a new click on the button. So I clicked my way through 255 timer steps, to get my suspicion verified, to find that the OCR2B register was updated with the value I set only when the timer tipped over from max to min.

The solution was to add a very short delay, to allow the timer to reach to 255 and loop back to zero, and set the new value of OCR2B, and hence fade the light down for every loop.

I haven’t figured out the timer registers fully, there are some flags that can be set and zeroed out, maybe one of these can be used to get a faster setting of the OCR2B register.

The studio feels like home

Atmel Studio has been a very good new friend on my path, where software and hardware merges in an AVR. The visualization from the simulator and the debugger has made coding experience much better. Not that I necessarily make less mistakes, but Atmel Studio equips me with some much clearer glasses in the struggle against deep cliffs and frustrating hours.

BTW: I’m not very fan of using the _delay function, as it locks the CPU down to doing nothing. In my later code I will try to implement use of the timers built into the Atmega for making breaks, such as wait until the timer1 has counted to 255 10k times before progressing.

BTW2: I can only handle a certain amount of software challenges pr day, so I quickly gave up using the Atmel Studio to run AVRDude and ArduinoISP to burn my code to my chip. Maybe another day I’ll feel braver.

Code for the shelf

Much code ends up on the shelf for a later project. My project is making a shelf with lights coming out of a couple of LED Strips, and the code goes into an Atmega 168, installed in the shelf.

This article is in a series about making a shelf with lights controlled by an AVR.
Read about 
 - designing and building the book-case
 - understanding Mosfets
 - and coding the AVR, and improving it using Atmel Studio

Before we look at the C code that goes into the Atmega, let’s have a quick look at how to get started with AVR programming. I have read an excellent book on AVR programming, which game insight to how the Atmel chip works, and how to utilize its amazing features. I bough the Atmega chips from RS Online, who has a very good selection, a decent web page and fast and free delivery over 500 NOKs in Norway. Other stuff you need is a breadboard, some wires, a text editor (Notepad ++), and WINAVR to make you able to speak C to your chip. There are many good tutorials on getting started with AVR coding online.

Ok, to my own work. The Atmega 168 is coded to feed a MOSFET with varying voltage, controlled by a single button on the PCB. The Atmega will cycle through a Pulse Width Modulation (PWM) sequence where the output voltage is reduced, to choke the MOSFET and dim the attached LED strip.

First, some declarations to get the code started.

Atmega 168 pinout
Atmega 168 pinout
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define LED_PORT                PORTC
#define LED_PIN                 PINC
#define LED_DDR                 DDRC

#define STBYLED                 PC3
#define PWRLED                	PC4

#define BUTTON_PORT             PORTD
#define BUTTON_PIN              PIND
#define BUTTON_DDR              DDRD

#define BUTTON                  PD1
#define STRIP_DDR               DDRD
#define LED_STRIP		PD3	//OC2B

The #define syntax define constants that cannot change during the program. This saves space compared to a variable defined later in the program.

With constants from the io.h library, I give my own name to the PORTs, PINs and DDRs I will use in the code. Note that the LED strip is connected to PD3, which is one of six PWM outputs in the Atmega. There are 3 timers that can be used, and they each have 2 pins they can run interrupts on. The reason I chose PD3 is the position of the pin compared to the PCB layout.

The button is on the same PORT as he LED strip, PORTD, so I will have to be very specific on how I define input and output DDRs in the program.

The delay.h library is necessary to waste time in the program.

Moving on,

static inline void initTimers(void){
	// timer 2
	TCCR2A |= (1 << WGM20); //set fast PWM bit 1
	TCCR2A |= (1 << WGM21); // set fast PWM bit 2
	TCCR2B |= (1 << CS21); // set divide by 8 (frequency)
	TCCR2A |= (1 << COM2B1); // set output to OC2B == PD3
}
Atmega 168 Timer register
Atmega 168 Timer register

This function is described in the previous blog post, setting up the timer register for PWM.

void updateState(uint8_t LIGHTSTATE){
	// TODO add dimming / smooth transitions
	switch (LIGHTSTATE){
		case 1:
		BUTTON_PORT |=(1 << LED_STRIP); //LED STRIP on button port, power on
		// turn PWM on and output to pin
		TCCR2A |= (1 << WGM20); //set fast PWM bit 1
		TCCR2A |= (1 << WGM21); // set fast PWM bit 2
		TCCR2B |= (1 << CS21); // set divide by 8 (frequency)
		TCCR2A |= (1 << COM2B1); // set output to OC2B == PD3
		//set output to max
		while(OCR2B < 255){
			OCR2B += 1;
			_delay_ms(3);
		}
		break;
		case 2:
		//set output to medium
		//OCR2B = 127; // 2.39V
		while(OCR2B > 100){
			OCR2B -= 1;
			_delay_ms(5);
		}
		break;
		case 3:
		//set output to low
		while(OCR2B > 5){
			OCR2B -= 1;
			_delay_ms(10);
		}
		break;
		case 0:
		//set output to 0
		TCCR2A = 0x00; // turn off PWM and interrupts from timer
		BUTTON_PORT &= ~(1 << LED_STRIP); // turn off LED_STRIP, sink to ground
		break;
	}
}

The updateState function takes an integer LIGHTSTATE, which runs through a switch statement to set the PWM output of pin PD3 (defined in the previous function). If LIGHTSTATE == 1, the timer2 registry is set in the Atmega hardware, overkill the first time it is run, but when LIGHTSTATE == 0, the PWM settings are removed, and hence they must be redefined here every time.

The OCR2B sets the number where the timer will send a low signal to the output pin PD3. The timer continuously counts from zero to 255, turning back to zero again. The timer turns the output pin high (5V) every time it drops to zero, and turns it low again (ground) when it reaches the number set in the OCR2B register. Through the cases one, two and three, the OCR2B value is set lower, giving a lower average output voltage that the MOSFET will read.

When LIGHTSTATE == 0, the PWM is turned off, and the current in PIN PD3 is drained to ground. This is to fully turn off the power on the MOSFET.

Note that the output pin can only output HIGH and LOW (5V and ground), and the PWM is turning the pin on and off so fast that the voltage read from the output is somewhere between 5V and ground. It is not possible to output an intermediate voltage without use of external circuitry. PWM is a very useful tool, and as the code shows, it runs without using CPU power, all is done by separate timers, built in to the Atmega chip.

Now the main program with a setup part, and a loop, running as long as there is power on the PCB.

int main (void){
	uint8_t lightState = 0;
	initTimers();
	// --init AVR banks --//
	BUTTON_DDR = 0x00; // button ddr to input (default)
	STRIP_DDR |=(1<< LED_STRIP); // LED Strip on same DDR as button, set to output
	BUTTON_PORT |= (1<< BUTTON); // set pull-up on button pin
	
	LED_DDR = 0xff; // turn set LED DDR to output
	LED_PORT |= (1<<PWRLED); //Turn on power light
	while(1){
		// if ((BUTTON_PIN & (1<<BUTTON)) ==0){
		if (bit_is_clear(BUTTON_PIN, BUTTON)){
			LED_PORT |= (1<<STBYLED); //Turn on standby light
			_delay_ms(500); //doubles as debounce  NOTE: rewrite for timer if more actions
			LED_PORT &= ~(1<<STBYLED); //Turn off standby light
			lightState += 1;
			if (lightState >3){
				lightState = 0;
			}
			updateState(lightState);
		}
	}
	return(0); //will never happen
}

The setup part is PORT, DDR and PIN handling to setup the LEDs on the PCB for power and standby. The standby light is used as an indicator that the button is pressed (no real standby mode used, the Atmega is not using much current anyway).

The STRIP_DDR and BUTTON_DDR points to the same Data Direction Register (DDR), and I chose to set all pins on the port to input, except for the LED strip, which is output. The button is set as an input pin, and with the internal pull-up resistor activated. The pull-up is a resistor connected to 5V, so when the switch is closed, currents flow the voltage on the pin drops to zero. The resistor limits the flow of current, so the chip is not shorted. The pull-up resistor is built into the pins of the Atmega, so no external resistor is needed, just a 1 in the BUTTON_PORT register.

For the LED lights, the DDR is set to output, and the PWRLED (red) is always turned on. (I got the LED lights a bit too cheap on ebay, so I think they will last shorter than the PCB and shelf. One green light failed during testing).

The bit_is_clear() function is defined in io.h, and checks a given pin to see if it is high or low. When the BUTTON (PD1) is clear, it means the button is pressed, as the voltage goes low, when the switch shorts the pin to ground. When the button is pressed, the lightState integer is increased with 1, and the updateState function is run with the lightState variable as input.

And that’s the magic, the next post will be on how I built the shelf itself, and how (if) it all fits together in our living room. (Or the shed if the Wife Approval Test (WAT) fails).