ChipCenter Questlink
SEARCH CHIPCENTER
Search Type:
Search for:




Knowledge Centers
Product Reviews
Data Sheets
Guides & Experts
News
International
Ask Us
Circuit Cellar Online
App Notes
NetSeminars
Careers
Resources
FAQ
EE Times Network
Electronics Group Sites

EE Expert Darren Ashby
SpacersProduct Engineering

Click Here to Go to the Product Engineering ArchiveClick Here to Go to Darren Ashby's Main EE Expert PageClick Here to Go to the Guides and Experts Main Page

TOP SECRET Code
The Hard Part…Getting Harder - Part 2
Page 1 of 2
by Darren Ashby

Exercising muscles that have atrophied ever since my managerial promotion, I reminisce of blissful days with a soldering iron. There is something truly therapeutic about soldering a board together and making a part do what you want. Till you get stumped, that is, then you can lose some hair. It could be some type of downward spiral. Get into management, get sick of it, go back to engineering, lose some more hair. Now you are even more like the pointy haired manager in Dilbert, so back into management you go. The process repeats until you've lost all your thinking hairs, and the only ones left are VERY pointed. Well, bear with me while I dull the points on my dome a little and try to actually make something work (without telling someone else to do itJ).

Structure

The first place to start is structure. Most microcontroller projects involve two things: wanting to do something, and then doing it every so often. I start with every so often first. Once you get that going, it is not nearly as tough to do something. In this case, I set up a timer interrupt using T0 in the Tiny 15. Here is a piece of code that does just that.

    T0_OVF: ;Interrupt routine:
    ;timer/counter0 overflow interrupt happens every 1 ms
    ldi timer0seed_reg,timer0seed ;set timer0 seed
    out TCNT0,timer0seed_reg	
    begin_2mS:
    inc count_2ms
    cpi count_2ms,2
    brne end_int
    ldi count_2ms,$0
    ;clear count
    sbr time_flags,mask_2ms
    ;use mask to set, clear, flag to test!
    begin_10mS: ;.01 second
    inc count_10ms
    cpi count_10ms,5
    brne end_int
    ldi count_10ms,$0
    ;clear count
    sbr time_flags,hundreth_mask
    begin_100mS: ;.1 second
    inc count_100ms
    cpi count_100ms,10
    brne end_int
    ldi count_100ms,$0
    ;clear count
    sbr time_flags,tenth_mask
    begin_1sec: ;1 second
    inc count_1sec
    cpi count_1sec,10
    brne end_int
    ldi count_1sec,$0
    ;clear count
    sbr time_flags,onesec_mask
    end_int:
    reti

Basically what happens here is keeping track of four timing flags—one every 2 ms, 10 ms, 100 ms, and every 1 second. (I will get into why they are labeled mask later.) The reason I only set the flags in the interrupt is to minimize the actual time spent in the interrupt. There may be a lot of things that may or may not need to be done every tenth of a second. If you put them all in the interrupt, and it overruns into the next interrupt, you'll lose time that you'll never gain back. But if you set a flag and then check for the flag, the next interrupt might be late, but the next time around, when the processor isn't as busy, the interrupt "catches up,"* so to speak. One reason flashing an LED is so important is that it proves the basic structure is working.

I/O

After a few late nights, I've finally got my "O" going,** now to work on the "I." To begin with, Atmel provides three registers to work with I/O, the data direction register, DDRB, the Port B register, and the Pin B register. After defining the direction of each pin with DDRB, you can write to the output with the Port B address, but to read from the input, you use the Pin B address. This creates a neat feature of being able to see if your output is being overloaded. You can write to the output and then look at the pins to see if it is outputting what you thought it was. Hmmmm, kinda' cool, you could sense a bad condition and go into a safe mode. Don't know if I'll use it on this project, but it could come in handy later!

Begin by setting up the buttons. As you may remember from TOP SECRET Motor Control, I decided to put all my buttons on one port and differentiate between them using various resistors and the A/D conversion capabilities of the Tiny 15. As I began setting that up, found myself facing a quandary…set a top level to the comparison window, or will a bottom level be sufficient? I selected the bottom level only for now…why complicate things if you don't need to. But what levels should be set? It is easy to calculate the voltage levels, but something is always lost in measurement that you need to account for. To do this the easy way, I first set up the PWM output on the Tiny 15 to use it to get an accurate read on the buttons. What better way to see what the micro thinks it is reading than to have it output it on a port? Besides, I need an operating PWM anyway.

The PWM mode uses three 8-bit counters: timer counter 1 (TCNT1), and Output compare register 1A and 1B (OCR1A and OCR1B respectively). As you set up the PWM output, you pre-define a clock prescaler, and load OCRB1 with the value that is the maximum count of your PWM. An important note: the resolution of the PWM signal depends on the value in OCRB1.

Taking the "baby-steps" approach, I start by setting up a simple ramp for the PWM to follow, incrementing a counter until it reaches maximum PWM, and then start over. I also hook the output up to an LED, just in case I made a hardware mistake; the damage wouldn't be as bad as the motor might cause. To verify the microcontroller knows that I'm pressing a button, I toggle one of the LEDs. Here is a smattering of simple code to do that. (Note: this all happens every 0.1 s as I mentioned in the Structure section above.)

    sbis pinB,mode ;check keyboard
    ; bnot_io portB,greenLED ;debug lines
    bnot_io portB,yellowLED
    sbis pinB,mode ;test pwm with a ramp
    inc pwm_duty
    cp pwm_duty,max_pwm
    brlo output
    clr pwm_duty
    output:
    out pwm_out,pwm_duty

Once I'm satisfied that the code is working correctly, I simply set up the A/D converter on the Tiny 15 to feed the value it reads from the keyboard to the PWM output. Now I know exactly what the Tiny 15 is reading from my keys, so I assign values to each button. We're cruisin' now!

Next >>

Product Engineering Archive

Guides and Experts   Analog Avenue   EDA Tools   PLD   DSP   EDA   Embedded Systems   Power   Test
Click here to get your listing up.

Copyright © 2003 ChipCenter-QuestLink
About ChipCenter-Questlink  Contact Us  Privacy Statement   Advertising Information  FAQ