ATtiny2313 based design

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys, I'm new at programming microcontrollers, and fairly new at electronics in general, although I have done a few previous projects.

As a way to learn more about AVRs (and just to make a gift for a friend), I decided to put together a simple ATtiny2313 based RGB LED controller. My goal is to basically make a few preset color combinations and PWM settings, and use the LEDs for a little display.

Anyway, I was wondering if there are any obvious flaws in the schematic (I'm sure there are), or if there's a better way to do it, or just some tips in general.

I've attached PNGs of both the schematic and the PCB. I didn't put much effort into the PCB because I wasn't sure if the design was okay to begin with.

If anyone wants to get the actual Eagle files, let me know.

Thanks a lot!

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first thing you MUST DO is to put 100nF bypass caps on your regulator (input and output) and across VCC\GND close to the chip.

I would also use PNP transistors with a common emitter circuit. Use the resistor in the collectors. Use the same value base resistors.
The way you have it you will lose at least 0.7V across BE + whatever the resistor in the base would drop. You may need the extra bit of voltage with the Blue led.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is not a good idea to put LEDs in parallel. Unless the diodes are perfectly matched (and they will not be), one will draw the majority if not all of the current. You will get the same amount of light with just one LED inserted.

I guess you are actually using the Tiny2313 instead of your circuit's 90S2313 symbol. Otherwise you need a crystal.

If I was laying out your pcb, I would have all the transistors and their associated resistors on the same side as the tiny2313 pins ... instead of "threading" traces betwen the IC pins. This is important if you are going to attempt to make the pcb at home ... especially if it is your first attempt. Nothing discourages more than first time failure. :lol:

Cheers,

Ross

ps ... welcome to the group.

pps ... I would be surprised if your pcb could not be done as a single sided version.

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I like to include a Version Number, Date, and my Name/Initials/Company Name, usually in copper, (I don't usually use a silkscreen), on the PCB.
It helps to identify the PCB at a latter time, particularly if any revisions in the board are ever made.

I like big markers for Pin 1 for the uC, ISP Header, and any additional connectors. It helps make it "fool proof" when one is working on the board late at night, when one is tired, and a deadline is approaching...

As you have so many spare pins and board space I would bring a few of them out to spare pads, and add pads for an LED or two. You don't have to install the "extra" LED, but it can be useful for debugging if you do it the old way, (no JTAG...). A spare Pad or LED also makes it easy to pulse a pin for a scope trigger, if needed, for debugging.

I also usually have V+ and Gnd pads near the edge of the PCB to easily connect a scope probe to ground, and to check the V+ supply.

Mounting holes are helpful for the PCB.

Of course if all of your projects work first time then all this extra stuff to facilitate debugging isn't necessary!

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the helpful replies, everyone!

John, I may not understand this completely, but when you say to put 100nF caps across the regular, do you mean at the same positions as the two existing caps?

Ross, thanks for the tips. The reason why I had 2 LEDs was because of what I was hoping to use this circuit for. I was going to make a little acrylic panel with a message engraved in it, and was going to use the LEDs to light up the acrylic from both ends. Do you think it will still work nicely with just one LED?
Also, if I were to use one LED, then would I need the transistors?

I was thinking about etching my own board... I have etched boards before, but I also wanted to try using Sparkfun's PCB fabrication service, just for fun. And I'm sure it'd turn out better. :D

DocJC, thanks for the info, I'll try to use some of your tips.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PlasmaCube wrote:

John, I may not understand this completely, but when you say to put 100nF caps across the regular, do you mean at the same positions as the two existing caps?

Ross, thanks for the tips. The reason why I had 2 LEDs was because of what I was hoping to use this circuit for. I was going to make a little acrylic panel with a message engraved in it, and was going to use the LEDs to light up the acrylic from both ends. Do you think it will still work nicely with just one LED?
Also, if I were to use one LED, then would I need the transistors?

I was thinking about etching my own board... I have etched boards before, but I also wanted to try using Sparkfun's PCB fabrication service, just for fun. And I'm sure it'd turn out better.

If I can presume to answer for John ...

Yes the 100nF caps should be very close to the actual regulator pins. And one more very close to the Tiny2313 supply and ground pins.

Two leds would produce a more even light I suspect for this application. The way to solve the present "problem" would be to provide each LED with its own current setting resistor instead of the one in the collector.

If you were starting this again, I would use NPN transistors and use common anode LEDS instead of common cathode versions.

I have an appointment that I need to head off to now ... but if you send me your eagle files I will show you what I mean.

Cheers,

Ross

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
If I can presume to answer for John ...
You can. :-)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone!
Well, thanks to Ross, I've made some changes to the circuit and now I've created the electronics and finished writing the firmware.

I didn't know about interrupts before I made this, so I had to poll the pin for input. I also used software PWM. Either way, it seems to work fairly well.

I'd like to post the code and, if someone sees a mistake or possible location of failure or maybe just something that could be done better, I'd appreciate it if you let me know.

Here's the C code:

/**
    KanjiDisplay

    Firmware for the KanjiDisplay,
    controls various settings for
    the RGB LEDs, as well as controlling
    input.

    9/30/10
**/
#define F_CPU 1000000UL // 1 MHz
#include 
#include 
#include 

#define NONE    0b00000111
#define RED     0b00000011
#define GREEN   0b00000101  
#define BLUE    0b00000110
#define YELLOW  0b00000001
#define CYAN    0b00000100
#define MAGENTA 0b00000010
#define WHITE   0b00000000

#define SET(c) ( (uint8_t)(c) )

#define LEDSET(c) ( PORTB = (c) )
#define LEDTOGGLE(c) ( PORTB ^= ~(c) )

// Function prototypes
void updateSelector(uint8_t* selector);

const uint8_t colors[] = {
    SET(RED),
    SET(GREEN),
    SET(BLUE),
    SET(YELLOW),
    SET(CYAN),
    SET(MAGENTA),
    SET(WHITE)
};

uint8_t EEMEM SelectorMemory;
uint8_t EEMEM ModeMemory;

int main(void)
{
    DDRB = 0b11111111; // Configure port B for output (Only 3 pins are used)
    DDRD = 0b11111110; // Configure port D for input on pin 0 (low)

    uint8_t selector = eeprom_read_byte(&SelectorMemory);
    uint8_t mode = eeprom_read_byte(&ModeMemory);
    uint8_t btnFlag = 0; // Button flag, to suppress continuous input
    uint16_t btnCount = 0; // Count, for special function detection
    int timer = 0; // Timer for PWM
    int pwm = 0; // PWM duty cycle
    uint8_t pwmFlow = 1;
    
    LEDSET(colors[selector]);
    
    while (1)
    {
        if (bit_is_clear(PIND, PD0)) // Check pin 0 on port D
        {
            if (btnFlag != 1)
            {
                btnFlag = 1;
            }
            else if (btnCount < 30000) // Count how long button is held down
            {
                btnCount++;
            }
            else if (btnCount == 30000) // Trigger has been reached
            {
                if (mode == 0)
                {
                    mode = 1;
                    timer = 0;
                    pwm = 0;
                    pwmFlow = 1;
                }
                else
                {
                    mode = 0;
                    LEDSET(colors[selector]);
                }
                    
                eeprom_write_byte(&ModeMemory, mode);
                btnCount++;
            }
        }
        else
        {
            if (btnFlag == 1)
            {
                if (mode == 0)
                {
                    if (btnCount < 30000 && btnCount > 1000) // The button did not change mode
                    {
                        updateSelector(&selector);
                        LEDSET(colors[selector]);
                    }
                }
                else
                {
                    if (btnCount < 30000 && btnCount > 1000)
                    {
                        updateSelector(&selector);
                    }
                }

                btnFlag = 0;
                btnCount = 0;
            }
        }

        if (mode == 1)
        {
            if (timer < 300)
            {
                if (timer == pwm)
                {
                    LEDSET(NONE);
                }
            }
            else
            {
                if (pwmFlow == 1)
                {
                    pwm++;
                    if (pwm == 300)
                        pwmFlow = 0;
                }
                else
                {
                    pwm--;
                    if (pwm == 0)
                        pwmFlow = 1;
                }
                timer = 0;
                if (pwm != 0)
                    LEDSET(colors[selector]);
            }

            timer++;
        }
    }

    return 0;
}

void updateSelector(uint8_t* selector)
{
    if ((*selector) != 6)
    {
        (*selector)++;
        eeprom_write_byte(&SelectorMemory, (*selector)); // Write to EEPROM
    }
    else
    {
        (*selector) = 0;
        eeprom_write_byte(&SelectorMemory, (*selector)); // Write to EEPROM
    }
}

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a few suggestions:

1) you may want to check the regulator's datasheet to see how they recommend the capacitors to be. bigger isn't always better.

2) I would dial down the size of those base resistors. to minimize the power dissipation on the switching transistors, you want to saturate them as much as possible. thank means that you want to use as smaller of base resistors as you can without damaging the mcu's output pins. I think those chips are usually good for at least 4ma. at 4v (5v - 0.7v Vbe), you are talking about 1kohm, at most.
3) the design for pd0 can be problematic, especially when the capacitor is large: when the button is closed, the discharge current through the button and the parasitic inductance can cause the pin to go negative and I have seen that resetting chips.

I would get rid of the resistor and capacitor and use weak pull-up on that pin instead.

4) you could design the code to be more portable. think about maybe defining BUTTON_PORT as PORTD,and BUTTON as 0 and write your code around BUTTON_PORT / BUTTON so that in the future if you wish to more the button to a different pin/port, you can just recompile.

5) I would look into using interrupt to handle the pwm.

6) the flow of the code is poor and difficult to read. I think you can make it more structured by using fewer "if"s.

7) I would also investigate ways to increase pwm frequency, either through interrupt or innovative programming.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very much for your suggestions, millwood.

The hardware is already completed, and it functions very well for the time being. However, I will most definitely keep those points that you mentioned in mind for future projects.

Thanks for your code suggestions as well. Today I took some time to rewrite the code and add one final bit of functionality that I was wanting.

I read about the AVR timers and managed to set up a CTC interrupt system to handle PWM.

I also added new code to handle a third "layer" of functionality (since I'm only using a single button). I'm probably breaking every rule in the book here... If there's a more obvious way to do things, again, I'd appreciate any feedback.

Anyway, besides the fact that the code is still not best practice, and I might have actually made it harder to read, but nonetheless the functionality works great for the time being.

The values that the interrupt routine uses are easily changeable and can give different PWM frequencies with different actual durations of functionality. I think right now it's going somewhere around 300Hz, but I may be wrong.

And at this point, the code eats up more than half of the 2KB memory of the ATtiny2313. Ouch...

Anyway, here's the code, v0.2:

/**
    KanjiDisplay

    Firmware for the KanjiDisplay,
    controls various settings for
    the RGB LEDs, as well as controlling
    input.

    Last modified:  10/14/10
    Version:        0.2
**/
#define F_CPU 1000000UL // 1 MHz
#include 
#include 
#include 
#include 

#define NONE    0b00000111
#define RED     0b00000011
#define GREEN   0b00000101  
#define BLUE    0b00000110
#define YELLOW  0b00000001
#define CYAN    0b00000100
#define MAGENTA 0b00000010
#define WHITE   0b00000000

#define SETCOLOR(c) ( (uint8_t)(c) )

#define MODEFULL 0
#define MODEPULSE 1
#define MODEOMNI 2

#define FLOWUP 1
#define FLOWDOWN 0

#define RAISED 1
#define CLEARED 0

#define BIT(x) ( 0x01 << x )
#define BITSET(x, y) ( (x) |= (y) )
#define BITCLEAR(x, y) ( (x) &= ~(y) )

#define ISBITSET(x, y) ( (x) & (y) )

#define LEDSET(x) ( PORTB = (x) )
#define LEDCLEAR(x) ( PORTB &= (x) )

// Function prototypes
void updateSelector(void);
void updateMode(uint8_t newMode);
void startPwm(void);
uint8_t rol(uint8_t bits);
uint8_t ror(uint8_t bits);

const uint8_t colors[] = {
    SETCOLOR(RED),
    SETCOLOR(GREEN),
    SETCOLOR(BLUE),
    SETCOLOR(YELLOW),
    SETCOLOR(CYAN),
    SETCOLOR(MAGENTA),
    SETCOLOR(WHITE)
};

uint8_t EEMEM SelectorMemory;
uint8_t EEMEM ModeMemory;

volatile uint8_t selector;
volatile uint8_t mode;

int timer; // Timer for PWM
int subTimer;
int pwm; // PWM duty cycle
uint8_t pwmFlow;
int8_t omniFlow;
uint8_t omniBits;

int main(void)
{
    BITSET(DDRB, 0b00000111); // Configure port B for output (Only 3 pins are used)
    BITSET(DDRD, 0b11111110); // Configure port D for input on pin 0 (low)

	selector = eeprom_read_byte(&SelectorMemory);
    mode = eeprom_read_byte(&ModeMemory);
	
	uint8_t btnFlag = 0; // Button flag, to suppress continuous input
    uint8_t modeFlag = 0;
    uint16_t btnCount = 0; // Count, for special function detection
    
	BITSET(TCCR1B, BIT(WGM12)); // Configure timer 1 for CTC mode (timer 1 is 16-bit)
	
	BITSET(TIMSK, BIT(OCIE1A)); // Enable CTC interrupts
	
	sei(); // Enable global interrupts
	
	OCR1A = 64; // Set CTC compare value

	if (mode == MODEFULL)
    {
        LEDSET(colors[selector]);
    }
    else
    {
        startPwm();
    }
    
    while (1)
    {        
        if (bit_is_clear(PIND, PD0)) // Check pin 0 on port D
        {
            if (btnFlag != RAISED)
            {
                btnFlag = RAISED;
            }
            else if (mode == MODEFULL)
            {
                if (modeFlag == CLEARED)
                {
                    if (btnCount < 40000) // Count how long button is held down
                    {
                        btnCount++;
                    }
                    else if (btnCount == 40000) // Trigger has been reached
                    {
                        modeFlag = RAISED;
                        btnCount = 0;
                    }
                }
                else
                {
                    if (btnCount < 40000)
                    {
                        btnCount++;
                    }
                    else if (btnCount == 40000)
                    {
                        updateMode(MODEOMNI);
                        startPwm();

                        modeFlag = CLEARED;
                        btnCount++;
                    }
                }
            }
            else
            {
                if (btnCount < 800)
                {
                    btnCount++;
                }
                else if (btnCount == 800)
                {
                    updateMode(MODEFULL);
                    BITCLEAR(TCCR1B, BIT(CS10)); // Turn off timer, stopping PWM
                    LEDSET(colors[selector]);
                    
                    btnCount = 40001;
                }
            } 
        }
        else
        {
            if (btnFlag == RAISED) // Button has been released
            {
                if (mode == MODEFULL)
                {
                    if (modeFlag == CLEARED)
                    {
                        if (btnCount > 1000 && btnCount < 40000)
                        {
                            updateSelector();
                            LEDSET(colors[selector]);
                        }
                    }
                    else
                    {
                        if (btnCount < 40000 && modeFlag == RAISED)
                        {
                            updateMode(MODEPULSE);
                            startPwm();
                                
                            modeFlag = CLEARED;
                        }
                    }
                }
                else
                {
                    if (btnCount > 50 && btnCount < 800)
                    {
                        updateSelector();
                        if (mode == MODEOMNI)
                        {
                            if (omniFlow == 1)
                            {
                                ATOMIC_BLOCK(ATOMIC_FORCEON)
                                {
                                    omniBits = ror(selector);
                                }
                            }
                            else
                            {
                                ATOMIC_BLOCK(ATOMIC_FORCEON)
                                {
                                    omniBits = rol(selector);
                                }
                            }
                        }
                    }
                }

                btnFlag = CLEARED;
                btnCount = 0;
            }
        }

    }

    return 0;
}

ISR(TIMER1_COMPA_vect)
{
    if (mode == MODEPULSE)
    {
        if (timer < 50)
        {
            if (timer == pwm)
            {
                LEDSET(NONE);
            }
        }
        else
        {
            if (subTimer == 3)
            {
                subTimer = 0;
                if (pwmFlow == FLOWUP)
                {
                    pwm++;
                    if (pwm == 50)
                        pwmFlow = FLOWDOWN;
                }
                else
                {
                    pwm--;
                    if (pwm == 0)
                        pwmFlow = FLOWUP;
                }
            }
            else
            {
                subTimer++;
            }
            
            timer = 0;
            if (pwm != 0)
                LEDSET(colors[selector]);
        }
    }
    else if (mode == MODEOMNI)
    {
        if (timer < 50)
        {
            if (timer == pwm)
            {
                LEDSET(colors[selector]);
            }
        }
        else
        {
            if (subTimer == 5)
            {
                subTimer = 0;
                if (pwmFlow == FLOWUP)
                {
                    pwm++;
                    if (pwm == 40)
                    {
                        pwmFlow = FLOWDOWN;
                    }
                }
                else
                {
                    pwm--;
                    if (pwm == 0)
                    {
                        pwmFlow = FLOWUP;
                        if (omniFlow == 1)
                        {
                            omniFlow = -1;
                            omniBits = rol(selector);
                        }
                        else
                        {
                            omniFlow = 1;
                            omniBits = ror(selector);
                        }
                    }
                }
            }
            else
            {
                subTimer++;
            }
            
            timer = 0;
            if (pwm != 0)
                LEDSET(omniBits);
        }
    }
    
    timer++;
}

void updateSelector()
{
    uint8_t new;
    if (selector == 6)
    {
        new = 0;
    }
    else
    {
        new = selector + 1;
    }
    ATOMIC_BLOCK(ATOMIC_FORCEON)
    {
        selector = new;
    }
    eeprom_write_byte(&SelectorMemory, selector); // Write to EEPROM
}

void updateMode(uint8_t newMode)
{
    ATOMIC_BLOCK(ATOMIC_FORCEON)
    {
        mode = newMode;
    }
    eeprom_write_byte(&ModeMemory, mode);
}

void startPwm()
{
    timer = 0;
    subTimer = 0;
    pwm = 0;
    pwmFlow = FLOWUP;
    omniFlow = 1;
    
    BITSET(TCCR1B, BIT(CS10)); // Start timer 1 with prescalar 1, initializing PWM
}

uint8_t rol(uint8_t bits)
{
    uint8_t highbit;
    if (bits & 0b00000100)
        highbit = 1;
    else
        highbit = 0;

    bits <<= 1;
    return bits | highbit;
}

uint8_t ror(uint8_t bits)
{
    uint8_t lowbit;
    if (bits & 0b00000001)
        lowbit = 1;
    else
        lowbit = 0;

    bits >>= 1;
    return bits | (lowbit << 2);
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Worming my way through the crowd of cooks to get my stirring spoon into the broth, I'd toss in these bits of advice:

    1) Move the current-limiting resistors to the emitter sides of your transistors (that is, between emitter and LED anode, and hook all collectors to the LED supply
    1a) Drop the resistance of the resistors in series with the bases down to about 51 ohms
    2) Switch to a 4.5V battery and eliminate the voltage regulator IC
Dropping the supply voltage is optional; I understand the appeal of a 9V battery's small size, snap connector, etc.