« First ‹ Prev 1 2 3 4 5 6 7 8 9 10 Next › Last »

The importance of a good library

Posted on March 30th, 2012 00:26:19 by landon
Comments

While working on the latest project for the blog (a small game using an HD44780 display), I realized that my code was going to be a mess! Instead of delaying for another half month, I think the next few posts will be for supporting libraries. Soon I'll have a decent library up for detecting button presses (or other single bit port changes). One of the more recent additions to my technical library, [Making Embedded Systems](http://shop.oreilly.com/product/0636920017776.do), is getting me off to a good start. It's already given me a couple of good ideas, so I just need to give in and start the project over from scratch.

Introducing the Keypad

Posted on March 1st, 2012 22:18:54 by landon
Comments

For this project, I'm going to introduce a 4x3 (3x4?) keypad. The 0-9 keys will determine the frequency of the flashing LED, from 0Hz to 9Hz. In addition, the * and # keys will determine which LED I'm setting the frequency for.

The Terminology

State Machine - A state machine is a collection of different states. For example, you could have two states IN_WORD and OUT_OF_WORD, which both process a string. While in the IN_WORD state, you could, for example, increment a word length counter. When a space appears, you'll set the state to OUT_OF_WORD. While you're in the OUT_OF_WORD state, you could set the word length counter to 0, but when you encounter something that's not a space, you switch to the state IN_WORD.

Example State Machine

Debounce - One annoying feature of keypads and switches in general is that when they're pressed they will bounce from open to closed. To account for this, my code has a built in delay in the state machine to filter out rapid changes that would come from debouncing.

Design Considerations

There are two main parts to this project:

  • Keypad Entry
  • LED Flashing

There's a fairly simple state machine that I can implement to decode the key presses. (The one in the code has been modified a bit from the picture to loop in decode_state a bit to debounce the keypad, but is basically the same.)

Keypad State Machine

Once a key is decoded, I'll stick it in a buffer so it can be processed in the main loop.

To get LED flashing correct, the timer will be set to interrupt so all of the toggle frequencies are multiples of the interrupt frequency. The exact multiples will be stored in a lookup table indexed by the keypad numbers.

Working With the Keypad

The 3x4 keypad has 8 pins. 4 pins are for the rows and 3 for the columns, along with one pin that won't be connected. There are a couple of ways to decode it, but since I have plenty of free pins, I'll hook them all up to a port and continuously poll that for changes and then scan it for the row/column combination. When a button on the keypad is pressed, it connects the corresponding row and column lines. So what I'll do is set the pins connected to the row lines as inputs with the pullup resistor enabled (this sets them high when there's no other signal). The pins connected to the column lines will be set as outputs and all will be set low. When a button is pressed, a single one of the row inputs will also go low. Once this change is detected, then for each column I toggle the output high until I find one that makes all of the rows read high. Then I use the column number as an index into a short lookup table to determine which button was pressed. The code should make the process clear!

Getting the Right LED Frequency

To simplify matters, I'm going to use a separate timer for LED flashing interrupts. I've decided the interrupt should trigger every millisecond. This isn't perfect for all frequencies. However, the greatest calculated error is only .2% for 6Hz, which is acceptable because I won't be able to tell the difference with my eye. (For reference, the errors from 1Hz to 9Hz are: 0%, 0%, .1%, 0%, 0%, .2%, .1%, 0%, .1%).

Hooking It Up

Schematic

Keypad Off

Keypad On

Keypad Pins

The Code

Unfortunately code length is quickly growing larger than I want to keep directly in the blog post. If you want syntax highlighting, you'll have to open it into the editor of your choice.

keypad.h

keypad.c

main.c

The Video

Coming soon!


Blinking an LED - This time with interrupts

Posted on March 1st, 2012 01:12:02 by landon
Comments

In this example, I'll switch over to using the ATtiny2313's 8-bit timer module for toggling the LEDs. Everything is still connected exactly as it was in the last example.

Before I get too far along with my examples, I'm going to introduce a new section. Ideally people with little experience in microcontrollers should be able to follow along and pick up useful tips (if not just able to take and compile my code). So, I'll be introducing the new pieces as I understand it.

The Terminology

Timer module - The ATtiny2313 has many modules that will do a lot of the hard work for you, one set of these is the 8-bit timer and the 16-bit timer. The most basic operation involves setting up a prescaler

Prescaler - The prescaler effectively divides the clock. So, with a prescaler setting of /64 on the timer module, the timer will not tick on every clock cycle, but only every 64 cycles.

Interrupts - One of the more powerful features of working at a low level, interrupts allow modules to stop all code execution and jump to their own interrupt subroutine (ISR). When the ISR returns, everything will pick up as it left off. More precautions have to be taken with interrupts in assembly than in C, such as making sure registers, especially the status register, are preserved. If the status register isn't preserved, then an interrupt could potentially foul up the results of a simple statement such as "if (foo==3)".

What's new

I'll be using the 8-bit timer module and interrupts to count off the 1 second LED changes.

The other major change from the previous example is that I decided that one of the LEDs should flash twice as slow. To do this, I'm going to simply XOR its current state with the other LEDs new state. No seperate counters required.

Design Considerations

One of the main concerns with using the 8-bit timer to count off seconds is getting it to divide evenly into the clock frequency. The clock frequency is a power of 10, but the prescalers and various division methods are powers of 2.

As you can see in the code comments, I set the timer up to have a prescaler of 64 and interrupt on overflow (256) as well as only toggling the LEDs when another counter hit 61. All told, this gets to 1 second with .06% error. One thing I could have done differently was to change the value the timer would interrupt on. Keeping the prescaler the same (64), the factorization of 1000000/64 is 5^6. If you split that into 5^3*5^3, then you can have the timer interrupt at 125 and toggle when the counter in the code equals 125. That equals exactly 1000000, so it should be as close to 1 second as you can get.

The Code

#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 1000000UL // Running at 1 MHz

char counter;

int main(void)
{
    DDRA = 0x03; // Set PA0 and PA1 to be outputs
    TCCR0B |= (1<<CS01)|(1<<CS00); //set prescaler to /64, which divides evenly into 1MHz

TIMSK |= (1<<TOIE0); // Enable timer 0 interrupts

counter = 0;

sei(); // Enable interrupts

while (1)
    {
        // Do nothing. Alternatively, a sleep instruction could be used so the CPU only wakes
        // on interrupts and immediately goes back to sleep.
    }
}

// Set an interrupt on the Timer0 Overflow vector
ISR(TIMER0_OVF_vect)
{
    counter++;
    if (counter == 61) // This gets VERY close to 1 second
                       // The timer prescaler (64) * 8 bit overflow (256) * 61 = .999424 seconds
                       // .06% error should be close enough for this application
    {
        char led1, led2;
        led1 = PINA & 0x01; // Get PA0's pin value
        led1 ^= 0x01; // Toggle PA0
        led2 = (PINA>>1) & 0x01; // Get PA1's pin value
        led2 ^= led1; // Xor PA1 with the current state of PA0, effectively doubling the period
        PORTA = (led2 << 1) | led1;
        counter = 0;
    }
}

Closing Thoughts

Some improvements I've noticed so far using C over assembly? Control flow is much less effort. I don't need to figure out what label I'm going to use or how I'm going to set up a loop or if statement. Setting bits in registers is more concise and using the << and >> syntax, I can easily determine which bits are being set/cleared.


Blinking an LED - The C Way

Posted on February 29th, 2012 00:50:17 by landon
Comments

After doing a couple of projects in assembly, I've started over again in C, but this time I'll be publishing the projects. The purpose of this is to:

  • Figure out how the usual C code translates into assembly and vice versa.
  • Provide a series of simple projects building on each other for people interested in microcontrollers.
  • Give employers some public examples to look through.
  • Get started building an awesome project for Christmas gifts.

To start off this series of posts, I'll use the simplest example possible, which is blinking an LED. I'll be using the avr-gcc toolchain to compile. The microcontroller I'll be using is the Atmel AVR ATtiny2313 until I start to run out of space or pins. Unless an example requires it, the fuses will be set so that the microcontroller runs at 1MHz.

Hooking It Up

Schematic All wired up

I have a two color LED with the common cathode connected to ground by a 1kΩ resistor. The two anodes are hooked up to the PA0 and PA1 pins on the microcontroller. It doesn't matter which color is hooked up to which pin for this example. Since I don't have a good bench power supply available, the microcontroller is powered by a 4xAA battery pack which provides around 5-5.3V (right under the maximum operating voltage of 6.0V for the microcontroller.)

The Terminology

Fuses - To change clockspeed and other basic features such as using the reset pin as IO, AVR microcontrollers uses fuses that are set when programming. Other microncontrollers may do things differently, right now my only experience has been with AVRs that uses fuses and a Freescale HCS08 chip where the clock was set in code. The fuse calculator provided by Engbedded is what I personally use to calculate what I need to set. Fuses only need to be set once, even if you program the chip multiple times.

Anode/Cathode - An anode is where current flows into an element, the cathode is where it flows out. In the LED I'm using, there is a common cathode, so I only need to connect that to ground (with the resistor of course). The two anodes of the LED are connected to their respective pins.

Design Considerations

One thing to note about using _delay_ms() is the resolution. From the <util/delay.h> reference page.

The maximal possible delay is 262.14 ms / F_CPU in MHz.

Uhoh. With our clock set at 1 MHz, this means the max delay is 262.14ms, just over a quarter of a second.

However,

When the user request delay which exceed the maximum possible one, _delay_ms() provides a decreased resolution functionality.
In this mode _delay_ms() will work with a resolution of 1/10 ms, providing delays up to 6.5535 seconds (independent from CPU frequency).
The user will not be informed about decreased resolution.

This isn't a problem, since we don't need such a small resolution.

But finally,

If the avr-gcc toolchain has __builtin_avr_delay_cycles(unsigned long) support, maximal possible delay is 4294967.295 ms/ F_CPU in MHz.
For values greater than the maximal possible delay, overflows results in no delay i.e., 0ms.

Checking /usr/lib/avr/include/avr/builtins.h, this builtin is defined along with the accompanying __HAS_DELAY_CYCLES macro. So there's nothing to worry about in the first place.

The Code

#include <avr/io.h>
#include <util/delay.h>

#define F_CPU 1000000UL // 1MHz

int main(void)
{
    DDRA |= 0x03; // Set PA0 and PA1 to be outputs
    while (1)
    {
        PORTA ^= 0x03; // Toggle PA0 and PA1
        _delay_ms(1000.0);
    }
}

If the microcontroller is programmed properly, the LED should be flashing a combination of the two colors (an orange-y color in my case).


Interim Activities

Posted on February 28th, 2012 00:07:04 by landon
Comments

Since my last post I've put Cube IRC on the back burner and focused on finishing a few other projects:

Starting with Saints Row: The Third

Saints Row: The Third

Followed by Bastion

Bastion

And then some Torchlight

Torchlight

With a little Super Meat Boy thrown in

Super Meat Boy

Culminating in World of Warcraft

World of Warcraft

I dare say I'm all gamed out and ready to put up some awesome posts about avr-gcc!