Need help with my first µC project - Page 1
Forum section: electronics / beginners

author: david77
author_rank: Frequent Contributor
timestamp: September 27, 2010, 09:02:29 pm
content: Ok, yesterday I was bored and got the idea to try and get into micorcontrollers again.I decided on a small project that I've had in mind quite some time now:A electronic kitchentimer. That shouldn't be too hard to realise.What I want:- max. count time: 99 min.- operation with three buttons: Start/Stop, +1 min., +10 min.The timer should be able to count from 99 minutes to zero. As display I want to use two 7-segmentdiplays. I want to drive them using two BCD-to-7-segment drivers (74LS249) , so I only need 8I/O ports.Further I need three I/O's for my buttons, one I/O for a blinking LED to signal the timer is running andone I/O to activate some sort of buzzer thingy. So 13 I/O lines are enough.I decided to use an AtMega8L, simply because I had some around and I have a small breadboard forATMegas.I elected BASCOM-AVR to be my language of choice, the reason being: I know BASIC and not much else.I haven't got a circuit diagram to show you, but it is all rather simple: ATMega8, 2x74LS49, displays andsome buttons and LED's.And here's my code so far:Code:$regfile = "m8def.dat" ' specify the used micro$crystal = 4000000 ' used crystal frequency$hwstack = 100 ' default use 32 for the hardware stack$swstack = 100 ' default use 10 for the SW stack$framesize = 100 ' default use 40 for the frame spaceConfig Portd = OutputConfig Portc = OutputConfig Portd.2 = Input 'definiert Portd als OutputConfig Portb = InputDeclare Sub Bcdout(byval Mincount As Integer )Dim Digit1 As Integer , Digit2 As IntegerDim Mincount As Integer , Seccount As IntegerDim Sdisplay As String * 2 , Sdigit1 As String * 1 , Sdigit2 As String * 2Dim Stopped As Integer , Preset As IntegerLet Mincount = 0 'Anzeige auf "00" setzen beim einschalteLet Seccount = 0 'Sekundenzähler auf 0 setzenLet Stopped = 1 ' Stopped: 0 = Run / 1 = Stop'bei PowerUp soll Zähler stehen'Config Timer0 = Timer , Prescale = 64'Enable Timer0'On Timer0 Isr_pollkeysConfig Timer1 = Timer , Prescale = 256 'Konfiguriere Timer1Enable Timer1On Timer1 Isr_von_timer1 'verzweige bei Timer1 ueberlauf zu Isr_von_Timer1Timer1 = 49910 'Timer auf 49910 setzen (15625 counts = 1sec.)'4000000/256=15625 == 1 sec.Config Int0 = Falling 'Konfiguriere INT0 für Start/Stop TasteEnable Int0On Int0 Isr_resettaste 'Sprung bei INT0 Start/Stop TasteEnable Interrupts 'Interrupts aktivierenDo 'HauptprogrammIf Stopped = 1 ThenPortc.4 = 1Bcdout MincountIf Pinb.0 = 0 Then Gosub Inkrement1Elseif Stopped = 0 ThenPortc.4 = 0If Seccount = 60 ThenLet Seccount = 0Bcdout MincountIf Mincount <> 0 ThenLet Mincount = Mincount - 1Elseif Mincount = 0 ThenLet Stopped = 1Let Mincount = 0Let Portc.1 = 1Waitms 500Let Portc.1 = 0End IfEnd IfEnd IfLoopEndInkrement1:If Stopped = 1 Then Let Mincount = Mincount + 1If Mincount >= 99 Then Let Mincount = 0ReturnIsr_von_timer1: 'ISR von Timer1Timer1 = 49910 'Timer1 soll wieder von 49910 wegzõhlenLet Seccount = Seccount + 1Toggle Portc.0ReturnIsr_resettaste: 'Reagiert auf Start/Stop TasteIf Stopped = 1 And Mincount > 0 Then 'Wenn Stopped=1 und Min-counter > 0 ==STARTTASTELet Stopped = 0 'setze Stopped = 0, so dass Zähler laufen kannElseif Stopped = 0 Then 'Wenn Stopped = 0 (Zähler läuft) ==STOP/RESET TASTELet Stopped = 1Let Mincount = 0 'Minutenzähler zurücksetzenEnd IfReturnIsr_pollkeys:'If Pinb.0 = 0 Then Gosub Inkrement1'Debounce Pinb.0 , 0 , Inkrement1 , SubReturnSub Bcdout(byval Mincount) 'SUB gibt BCD Kodiert die Anzeige ausSdisplay = Str(mincount) 'Hier wird Minutenzähler (Integer) in StringIf Len(sdisplay) = 1 Then Let Sdisplay = "0" + Sdisplay 'gewandelt, zerlegt, wieder in INT gewandeltSdigit1 = Left(sdisplay , 1) ' und einzeln an die CASE Anweisungen gegeben.Sdigit2 = Right(sdisplay , 1) 'wenn nötig wird eine fuehrende Null angefuegtDigit1 = Val(sdigit1)Digit2 = Val(sdigit2)Select Case Digit1Case 1Portd.0 = 0Portc.3 = 0Portc.2 = 0Portd.3 = 1Case 2Portd.0 = 0Portc.3 = 0Portc.2 = 1Portd.3 = 0Case 3Portd.0 = 0Portc.3 = 0Portc.2 = 1Portd.3 = 1Case 4Portd.0 = 0Portc.3 = 1Portc.2 = 0Portd.3 = 0Case 5Portd.0 = 0Portc.3 = 1Portc.2 = 0Portd.3 = 1Case 6Portd.0 = 0Portc.3 = 1Portc.2 = 1Portd.3 = 0Case 7Portd.0 = 0Portc.3 = 1Portc.2 = 1Portd.3 = 1Case 8Portd.0 = 1Portc.3 = 0Portc.2 = 0Portd.3 = 0Case 9Portd.0 = 1Portc.3 = 0Portc.2 = 0Portd.3 = 1Case 0Portd.0 = 0Portc.3 = 0Portc.2 = 0Portd.3 = 0End SelectSelect Case Digit2Case 1Portd.4 = 0Portd.5 = 0Portd.6 = 0Portd.7 = 1Case 2Portd.4 = 0Portd.5 = 0Portd.6 = 1Portd.7 = 0Case 3Portd.4 = 0Portd.5 = 0Portd.6 = 1Portd.7 = 1Case 4Portd.4 = 0Portd.5 = 1Portd.6 = 0Portd.7 = 0Case 5Portd.4 = 0Portd.5 = 1Portd.6 = 0Portd.7 = 1Case 6Portd.4 = 0Portd.5 = 1Portd.6 = 1Portd.7 = 0Case 7Portd.4 = 0Portd.5 = 1Portd.6 = 1Portd.7 = 1Case 8Portd.4 = 1Portd.5 = 0Portd.6 = 0Portd.7 = 0Case 9Portd.4 = 1Portd.5 = 0Portd.6 = 0Portd.7 = 1Case 0Portd.4 = 0Portd.5 = 0Portd.6 = 0Portd.7 = 0End SelectEnd SubSorry, the commentary is in German but I reckon it's quite self explainingPORTC.0: Blinking light, toggles every secondPORTC.4: Is high when counter stopped / low when running. Just for testingPORTD.0/2 PORTC.1/2 & PORTD.4...7: Output for 2 digits BCD code to 74LS249'sPORTC.1: The output for the buzzy thing.PIND2/INT0: Start/stop keyPINB.0: Increase 1 min. keyVariables:Mincount: counter get increased by pressing +1, decreases when timer runsSeccount: counts the seconds. Gets increased by Timer1 interruptStopped: flag, indicates if counter is running (=0) or stopped (=1)My problem so far is as follows:When I compile the program with a pre-set Mincount (maybe 10) then everything works fine.At this stage I do not read PINB.0.The counter powers up with "10" on the display. I press "Start" and it counts down to "00", PORTC.1gets high for a bit and then the thing stops, sets the Stopped flag to 1, the display back to "00".If, however I add code to read PINB.0 it all goes wrong. The counter increases when the key is pressedas it should, when I press "Start" the Stopped flag changes, but the counter doesn't count!It just sits at whatever value I keyed into Mincount.I tried just polling the key in the main loop (also using DEBOUNCE) and I tried using Timer0 interruptto poll the key outside the main loop (the code is there -> Isr_pollkeys:).The blinking LED on PORTC.0 keeps on blinking, so the Timer1 interrupt seems to work, I guess.I realise this should be a very simple project, thus I thought I might be able to do it. It is howevermy first "real" own µC project, I'm quite a noob here.Maybe the pro's would choose other hardware or another way to realise it, or choose ASM or C aslanguage but this is the way I chose to tackle it and I think I can't be that far off. There is probablysomething quite obvious I overlook or am not aware of.Oh, and guys: No BASIC bashing please! I really like that language and I know what the "real" programmersthink of it.So a little nudge in the right direction would be appreciated!David

author: djsb
author_rank: Super Contributor
timestamp: September 27, 2010, 09:23:19 pm
content: Can you set breakpoints and put watches on the variables that you use to check that they are behaving as you expect?Have you tried simplifying your program and checking each subroutine one at a time. You could build in some kind of test into the program that uses the LED's as an output (in place of print/printf statements and a serial terminal).David.

author: david77
author_rank: Frequent Contributor
timestamp: September 27, 2010, 10:00:50 pm
content: Uh, well, the most obvious I didn't see. Ok, thanks David.I let the display show my Seccount variable and it turned out I didn't put that back to zero whenpressing stop... So the µC never got to the line decreasing my Mincounter.Now I can ask you guys about another thing that puzzles me. How the hell do I debounce theswitches properly? I want the +1 key to add 1 to the counter each time I press it.At the moment it's got a mind of it's own...Do I poll it in the main loop or do I poll it using a Timer0 interrupt every few ms? And if so, how many msapart? Or should I use the DEBOUNCE command?Or should I just whack in some old fashioned hardware debouncing (couple of R's, a C and a 7414)?So many questions... But I already start to like the µC.Some years ago I did a kitchen timer in "discrete" TTL logic...what a pain!

author: PeterG
author_rank: Frequent Contributor
timestamp: September 27, 2010, 11:57:07 pm
content: David,i normally de-bounce with a "Delayms 2" or similar command to handle the button bounce. A small cap across the button also helps.RegardsPeter

author: ThoWaBu
author_rank: Newbie
timestamp: September 28, 2010, 05:25:37 am
content: I think Delay isn´t really a debouncing...I very short :In fact of switch prellen there are short "spikes", but you want only count on a minimum KeyPressing Time.This could be 50ms or something -- it depends on the User interface and how bad the switch is.So I do the Follow and it works _very_ well :On switch state Released set your TimeCounter to 0.On switch state Pressed increase your Counter ervery Timebase.(Timer interrupt)If the Counter overruns your Time-Limit (i.e. 50ms) your Key is Pressed.Don´t let your counter overrun.You can switch between several Time-Limits to detect long and short pressed Buttons.(Maybe some overkill)

author: Time
author_rank: Frequent Contributor
timestamp: September 28, 2010, 02:27:22 pm
content: small cap on the switch has ever been all i have needed.

author: ThoWaBu
author_rank: Newbie
timestamp: September 28, 2010, 03:15:09 pm
content: @Time :What Tau do you create by adding a C ?What Tau is needed to get the SmithTrigger at the Input stage doing well ?

author: nyo
author_rank: Contributor
timestamp: September 28, 2010, 05:52:12 pm
content: I have successfully used a "delay_ms(100);" for button debounce

author: Zero999
author_rank: Super Contributor
timestamp: September 28, 2010, 06:26:25 pm
content: I don't use BASIC but delaying for 10ms is normally more than enough.A more robust debouncing scheme is to only consider the button to have changed state if it's remained in that state for longer than a given time e.g. 10ms. This is harder to do but it's better because it spikes or induced EMI is less likely to cause false triggering. This can be done using a loop and a timer which is reset every time the switch changes state, if the timer counts down to zero and is not reset then the switch is considered to have settled. I can post some code if you like but it'll either be assembler or pseudo as I don't know PIC BASIC.

author: david77
author_rank: Frequent Contributor
timestamp: September 29, 2010, 01:44:07 pm
content: Well. The DEBOUNCE command in BASCOM works quite well actually.I t didn't work for me because I bollocksed up the port wiring on my bread board.There's a socket for ATMega16/32 and one for the ATMega8. I wired the pins for PORTB/C/D from the24pin socket to the ZIF socket and then onto pins where I plug my jump leads in.It turned out I wired PORTB of the ATMega8 to the pins for PORTA of the ATMega16/32.As a result my switches were connected to nothing and the PORTB pins I used as inputs were alsoconnected to nothing. Funnily, the controller reacted to a pressed button. Not in any meaningful andpredictable way, but good enough for me not to notice it sooner.After correcting my mistake (I just swapped the jump leads to the PORTA pins) it immediately worked,who'd have thought?The hard & software for my kitchen timer works now. So it's time to make it into a real product.This is going to be this years christmas present for various friends and family.

author: Zero999
author_rank: Super Contributor
timestamp: September 29, 2010, 08:04:28 pm
content: Quote from: david77 on September 29, 2010, 01:44:07 pmDo you know what it actually does though?Is it a simple time delay or something more complicated as I described above?If you don't know, then you've found one of the flaws with BAISC: it's too easy so you don't learn as much. If you do know, you can be smug and tell me to shut up.I'm not meaning to BASIC bash, just make you aware of some of the things it's doing for and that if you let someone wipe your arse for you, you'll never learn to do it yourself.

author: ThoWaBu
author_rank: Newbie
timestamp: September 29, 2010, 09:14:01 pm
content: I read the BASIC DEBOUNCE does only an double check of state.Do you have a Scope ?Please draw the Voltage.You can read the bounce time and set to an correct value.With my Algorithm even you can increment your digit faster at longer push...

author: Zero999
author_rank: Super Contributor
timestamp: September 30, 2010, 07:20:56 am
content: Quote from: ThoWaBu on September 29, 2010, 09:14:01 pmSomething like: Is switch (de)pressed? If yes, wait for a certain length of time. Is switch still (de)pressed?QuoteWhat about using the MCU to time it?It shouldn't be hard to write a program to time how long it takes for a switch to settle when it's pressed or released.

author: david77
author_rank: Frequent Contributor
timestamp: September 30, 2010, 07:53:23 am
content: Well, I'm aware that BASIC keeps a lot of stuff from me. But I don't really think that's such a bad thing.I tried my luck with C and have to say it makes my head hurt. There's so much you have to look out for,especially with variables, memory and how you access it... I honestly can't be bothered with all that.At least not for simple stuff, I realise there are things where BASIC wouldn't cut the mustard, but heyI'm happy with a few blinking lights and a few buttons at the moment.I read about debouncing and realise it's not a simple matter to do it properly, I've already made thatexperience with hardware debouncing in the past.Comes time, comes knowledge

author: PeterG
author_rank: Frequent Contributor
timestamp: September 30, 2010, 08:14:38 am
content: I use Crownhills Proton Picbasic for my PIC programming. It lets me use the rapid dev of Basic and also supports inline asm when it is required.

author: ThoWaBu
author_rank: Newbie
timestamp: September 30, 2010, 07:34:46 pm
content: Quote from: Hero999 on September 30, 2010, 07:20:56 amOK, so do it.Maybe you can draw down an structure chart, or explain your Algorithm.How can you predict if it is Low or High ?The only thing is to measure the High an the Low times.But how long is the minimun High/low Time ?If you´re smart enough your Algo will detect an Failure.I think You need a causality -- You need two things to compare.But the Only thing you get is : 111111111011101101010000111110000000000011101010010010100101111111111111110000010010101010010101Package : aaaaaaaaa xxxxxbbbbbxxxxxxxxxxxxccc dddddddddddddddxxxxxMy Algo above active high : 12345678901230120101000012345000000000001230101001001010010123456789ABCDE0000010010101010010101Package a is 9 times long, sure everyone would say its pushed.But b is only 5 is it pushed ? I thing a Statistcal analysis would say yes (conditional probability)Package c is nothing real...Package d is E long, it must be a long pushed...The x could be detected as Low.Everything between is garbage.What about switch aging ? The bouncing will increase.The simplest Method (which are the best) is an increase by a save factor of 2...5 !In real, i saw switches bouncing up to 120ms !!!!Thats near to the human speed of reaction.(And it doen´t took 10ms to push/release a switch)At the Oszi You can estimate the time you need in statistical fluke and multiply by a save factor.Next I read this : "I tried just polling the key in the main loop (also using DEBOUNCE) and I tried using Timer0 interruptto poll the key outside the main loop (the code is there -> Isr_pollkeys:)."Correct me if I am wrong.He executes the Basic-Debounce-Monster (i explain later) inside the InterruptServiceRoutine ?(I can´t read basic -- I am using C)When right, he´s doing a very nasty thing !Debounce WAITS for some time, but the ISR is called every second.It looks not very critical, but *waiting* for an event must not done in an ISR.ISR´s should be short as possible, so it don´t overrun.But waiting is arrrghhh.This got no name !!!A non waiting algo is the above one.It must be in the ISR.It´s not wasting time by setting flags and increase counters...Sorry.GreetsThomasPS: Waiting is never a good thing for a Processor, but in the main loop it´s OK. Maybr sleep mode is good for you ?

author: david77
author_rank: Frequent Contributor
timestamp: September 30, 2010, 10:29:54 pm
content: Thomas,I have to confess I don't understand most of your post, not to worry, that's just me being thick.The one thing I can say, though, is that I'm not using an interrupt to poll my keys.The polling of the +/- buttons is done in the main loop with the mentioned DEBOUNCE command.I do however use INT0 for the start/stop button.I have understood that the ISR's are to be kept as short as possible.David

author: ThoWaBu
author_rank: Newbie
timestamp: October 01, 2010, 04:44:14 am
content: Was für eine Prellzeit hast Du angenommen ?

author: david77
author_rank: Frequent Contributor
timestamp: October 01, 2010, 12:01:43 pm
content: Der debounce Befehl wartet standardmässig 25ms. Ich habe mit anderen Werten rumgespielt, unter 10mskommt das Prellen dann wieder durch, länger als 25ms hatte keine spürbare Auswirkung.Kommt ja auch auf die Taster an, im Versuchsaufbau sind's noch andere als ich dann nachher verwende.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 01, 2010, 12:43:07 pm
content: wow! its getting.... "internationalle"!

author: david77
author_rank: Frequent Contributor
timestamp: October 01, 2010, 12:53:29 pm
content: Sorry.I know it's not polite to speak your mother tongue in company.

author: ThoWaBu
author_rank: Newbie
timestamp: October 01, 2010, 02:08:17 pm
content: Fine,to be save (in your product) you can double this.The User won´t recognize this delay because he´s to slow...But why don´t you measure the typical button pushed time, min and max ?You got an good Hardware setup to do so...