Programming a Microcontroller in C for a Complete Beginner

A guide by Paul Filitchkin ( ) EE student of Oregon State University


Full Brightness

Dim Lighting

Project Description

This project uses an inexpensive programmable microcontroller to sense voltage across a photosistor and send output to a 7 segment LED.  It outputs the digit 0 for very dim lighting and 9 for relatively bright lighting (and values in between).  The goal is to learn how to program the Tiny26L Microcontroller through a simple, entertaining, and educational project. 

The Hardware

This guide is meant to be fairly general and will work with any AVR series microcontroller and virtually 7 segment displays and sensors.
If you have never dealt with microcontrollers I recommend buying a kit such as the kit used in this tutorial (available from the OSU TekBots Marketplace)
You can also build everything from scratch on a breadboard or protoboard (included is a schematic of the OSU board with the 7 segment display).

A note on power

The onboard 5V regulator (LM78L05) will accept from 6V to about 20 V and still output 5 V, but if you run anything below 6 volts the regulator will start to drop out and supply less voltage.  For the Tiny26L this does not pose a major problem because it can run on as little as 2.7 V.  Powering the chip with 4 AA batteries in series will work fine ( 1.2V x 4 batteries = 4.8 V, which will be around 3 volts from voltage regulator).  Make sure the chip does not see more than 5.5V (absolute maximum rating) and .  Always measure your input voltage before connecting because if the voltage is too high or you plug in power backwards you will fry the chip!

Initial Calculations

We will be using the Fairchild MAN6980, one digit, common cathode led display.  The MAN6980 will be powered directly from the chip using resistors to control the current through each LED segment.  The data sheet states that each segment has a maximum forward current of 30mA and a maximum voltage drop of 2.5 volts.  By looking at the Tiny26L datasheet we know that it can run at a maximum of 5.5V so the theoretical maximum each pin can supply is 5.5V (this will most likely never happen in actual operation, we'll take the precautions for absolute maximum calculations). 

From V=IR: (5.5 V - 2.5 V) = (0.03A)(R ohm)  R=100 ohm

That means if a pin from the Tiny26L supplies 5.5V and the LED segment has a drop of 2.5V then the current through the segment will be 30 mA.  Again, in practical applications this much current should never flow through each segment, but if a minor spike occurs a segment will not burn out. 

The sensor

You can use any sort of voltage varying sensor for this project.  Some simple examples include: photosistor, thermistor, infrared sensor.   

As a sensor I will use a photosistor which, unfortunately, has no available datasheet.  Determining the setup, however, is quite easy and the same process can be applied to other resistive sensors that you do not have datasheets for.  There are two approaches you can take:  1.) You can measure the resistance for the maximum and minimum conditions, take the average, and use that resistance value for the voltage divider.  2.) You can measure the resistance in the condition you would expect to be the most common.  For a photosistor this might be average interior lighting. 

Connections


The MAN6980 has two common cathodes (pin 3 & 8) and they should be connected to ground.  The center diagram shows each segment numbered by the corresponding pin.  The diagram on the right is from the data sheet and it is a schematic representation where each segment is drawn as a diode. 

This is the pinout diagram from the
MAN6980 Datasheet
Tiny26L Datasheet

Connection Table:

MAN6980 Pin Tiny26L Pin
1 PA7
2 PA6
3 (GND)
4 PA5
5 PA4
6 PA3
7 PA2
8 (GND)
9 PA1
10 NC

Logic Table:

Digit to Display
Which pins need to be set logic high on MAN6980
Signal to be written to PortA on Tiny26L
PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0
0 2,4,5,6,7,9 0 1 1 1 1 1 1 0
1 5,9 0 0 0 1 0 0 1 0
2 1,4,5,6,7 1 0 1 1 1 1 0 0
3 1,4,5,7,9 1 0 1 1 0 1 1 0
4 1,2,5,9 1 1 0 1 0 0 1 0
5 1,2,4,7,9 1 1 1 0 0 1 1 0
6 1,2,4,6,7,9 1 1 1 0 1 1 1 0
7 4,5,9 0 0 1 1 0 0 1 0
8 1,2,4,5,6,7,8,9 1 1 1 1 1 1 1 0
9 1,2,4,5,7, 9 1 1 1 1 0 1 1 0


Complete Schematic (Includes OSU Tiny26L board):

 
Original OSU Tiny26L Schematic (21.3 kB pdf)
Bill or materials included below.

Compiling Code

I assume a typical user will be running Windows XP, however, all of these tools are available under Linux. To compile/program the code you will need to download WinAVR. WinAVR is a very useful set of tools which includes the GNU C Compiler (GCC) and the required AVR libraries for translating your C code into AVR assembly instructions. In addition, it gives your DOS command prompt some Linux/Unix commands. To compile C-code simply fire up the command prompt, navigate to the code directory and type "make" (Note: you must have a makefile in the directory for this to work.) Another approach to compiling your code is using Atmel's AVR studio. This has a nice IDE and makes the compile/build process transparent to the user.












Now Lets Look at the Code (source included below)

C Source file
Makefile

The Tiny26L is an 8-bit RISC processor which on a very simplified level means the microcontroller manipulates one 8-bit data value at a time. As a result it is efficient to use 8-bit (char) variable types when coding in C. There are many 8-bit registers onboard the microcontroller that govern how it operates (page 170 of the datasheet lists a summary of all registers). We'll start with the most basics ones - the I/O registers.

I/O register set up
DDRA = 0b11111111; //Set all pins as outputs
PORTA = 0b00000000; //Output logic low on all pins of port A

DDRB = 0b01011111; //PA7 = input, PA5 = input, the rest are outputs
PORTB = 0b00000000; //Output logic low on output pins and no pull-up resistors on input pins

DDRA is an 8-bit register which stands for Data Direction Register for Port A.  The binary value 11111111 tells the chip that all the pins on the side of port A are outputs. 

0 = INPUT
1 = OUTPUT

DDRB is 01011111 in binary.  This means PB7 and PB5 are used as inputs and the rest of the pins are outputs.  If you look back at chips pinout diagram you will notice PB7 is an inverted reset control so it is important to make this an input. Note that this is an inverted reset meaning logic low will active it and cause the microcontroller to reset all operation. PB5 will be used as an input for the internal analog-to-digital converter (notice by looking at the chip pinout that PA0-PA7 and PB4-PB7 can all be used to read analog to digital values). 

This is how DDRB is set up in the code:
DDRB - Data Direction Port B
Bit
7
6
5
4
3
2
1
0
Value
0
1
0
1
1
1
1
1
Controls
PB7
PB6
PB5
PB4
PB3
PB2
PB1
PA0
Function
IN
OUT
IN
OUT
OUT
OUT
OUT
OUT

PORTA & PORTB are also 8-bit registers which can tell the chip two things:
1.) If a pin is set up as an output the corresponding value will write logic high or logic low on that pin
2.) If a pin is set up as an input the corresponding value will determine if an internal pull-up resistor is used or not

DDxn PORTxn I/O Pull-up Comment
0 0 Input No Tri-state (HI-Z)
0 1 Input Yes Can source current
1 0 Output No Output low (sink)
1 1 Output No Output high (source)

This is how PORTB is initially set up in the code:
PORTB
Bit
7
6
5
4
3
2
1
0
Value
0
0
0
0
0
0
0
0
Controls
PB7
PB6
PB5
PB4
PB3
PB2
PB1
PA0
Function
IN
(no pull-up)
OUT
(logic low)
IN
(no pull-up)
OUT
(logic low)
OUT
(logic low)
OUT
(logic low)
OUT
(logic low)
OUT
(logic low)


Analog to Digital Conversions

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input){

     // Select pin for conversion
     ADMUX = adc_input|ADC_VREF_TYPE;

     // Start the AD conversion
     ADCSR|=0b01000000;

     // Wait for the AD conversion to complete
     while ((ADCSR & 0b00010000) == 0);
     ADCSR|=0b00010000;

     // Return 8-bit conversion value
     return ADCH;
}

The Tiny26L has a 10-bit analog-to-digital converter which can be read by the ADCH and ADCL registers.
ADMUX and ADCSR registers enable and control the operation of the the analog-to-digital converter.

ADCSR (Analog to Digital Converter Set up Register)
Bit 7 6 5 4 3 2 1 0
Name ADEN ADSC ADFR ADIF ADIE ADPS2 ADPS1 ADPS0

Each bit of the ADCSR corresponds to a certain behavior of the ADC (refer to page 105 of the datasheet for more a more detailed description of each bit).

Bit 7 – ADEN: ADC Enable
Setting this bit to "1" enables the AD converter

Bit 6 – ADSC: ADC Start Conversion
Setting this bit to "1" starts an AD conversion

Bit 5 – ADFR: ADC Free Running Select
Setting this bit to "1" runs the ADC at a continuously updated rate.

Bit 4 – ADIF: ADC Interrupt Flag
This bit is automatically set to "1" when an AD conversion has completed

Bit 3 – ADIE: ADC Interrupt Enable
Setting this bit to "1" enables the ADC interrupt.

Bits 2-0 - ADPS Bits: ADC Prescaler Select Bits
These bits govern the frequency of the AD conversions based on the following table of values:

ADPS2 ADPS1 ADPS0 Division Factor
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128

The actual speed of the AD conversion is determined by dividing the chips speed (1Mhz with no external oscillators and no fuse bits programmed) by the division factor.


ADC Comparisons

Var = read_adc(0x08);

if (Var > 200)        //very bright
    PORTA = 0xF6;     //9
else if(Var > 180)
    PORTA = 0xFF;     //8
else if(Var > 160)
    PORTA = 0x32;     //7
else if(Var > 140)
    PORTA = 0xEE;     //6
else if(Var > 120)
    PORTA = 0xE6;     //5 average room lighting
else if(Var > 100)
    PORTA = 0xD2;     //4
else if(Var > 80)
    PORTA = 0xB6;     //3
else if(Var > 60)
    PORTA = 0xBC;     //2
else if(Var > 40)
    PORTA = 0x12;     //1
else                  
    PORTA = 0x7E;     //0 very dim

This is just a simple if-else construct that calls the adc function described earlier and outputs to Port A where the 7 segment display resides.

 

Bill of materials:

Description Qty Manufact. Part # Supplier Part # Cost Supplier
Atmel Tiny26L Microcontroller kit from Oregon State University: 1       TekBots
      Atmel Tiny26L chip 1       Digi-Key
      5V Linear Regulator 1 LM78L05 LM78L05ACZFS-ND   Digi-Key
      ISP connector, 2.5 pin, boxed 1       Digi-Key
      1nF 100V Capacitor 10% 1 D102K20Y5PH63L6R 1445PH-ND $0.12 Digi-Key
      0.1uf Decoupling Capacitor 3       Digi-Key
      .33uF 50V Capacitor Z5U 20% 1       Digi-Key
      CONN HEADER .100 SNGL STR 36POS 1       Digi-Key
      100 kOhm resistor, 1/6 watt 1       Digi-Key
AVR Parallel Port Programmer w/ Cable (Kanda STK200 programmer) 1       TekBots
Fairchild 7-segment LED display () 1 MAN6980     Digi-Key
100 Ohm resistor, 1/2 watt 7       Digi-Key
1k Ohm resistor, 1/2 watt (these will depend on what sort of sensor you use) 2       Digi-Key

Copyright 2006 Paul Filitchkin