Will assembler help me here?

This is the place for any magazine-related discussions that don't fit in any of the column discussion boards below.
Post Reply
Newz2000
Posts: 507
Joined: Wed May 18, 2005 1:01 am
Location: Des Moines, Iowa, USA
Contact:

Will assembler help me here?

Post by Newz2000 »

I've written a program for my pic 16f84a (I'm gonna upgrade soon, really!). It has a button, a small speaker and 8 LEDs. The LEDs represent an 8 bit number in binary that is incremented each time the button is pressed.<p>I've also added a bit of code to play a short beep whenever the button is pressed.<p>I've experimented with different beep lengths and at some settings, if you press the button very fast, some of the presses will not be detected.<p>I've been experimenting with different development tools, and I've found that programming in C is the fastest way for me to get my work done. I've tried ASM (MPLAB), Basic (provided by www.mikroelektronika.co.yu) and C (also provided by www.mikroelektronika.co.yu).<p>I'll paste the program I've written in C below, but first I'll ask my question: If I program in ASM, will I be able to avoid this problem of the longer beeps slowing down the button detecting code? I'll be adding serial port capabilities in a moment, and I'm curious if that will also slow things down.<p>Using the code below on a 16f84a @ 3.6864MHz I pressed the button 32 times and it detected 23 presses.
<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">code:</font><hr><pre>unsigned short CurState;
unsigned short Counter;<p>void LedIncrement()
{
/** increment the counter variable and update the LED display
* play a sound on the speaker
**/

PORTB = CurState++;
Sound_Play(75, 50);<p>}//~<p>void main()
{
INTCON = 0;
TRISB = 0; // port b is outputs
TRISA = 1; // port a0 is input, all others output

// initialize
CurState = 0;
Counter = 0;
Sound_Init(&PORTA, 1);
PORTB = 0; // all LEDs off

// start our incrementing
LedIncrement();
Delay_ms(1000);
LedIncrement();
Delay_ms(1000);

// back to 0 then begin real program
PORTB = 0;
CurState = 0;

do
{
// loop
if (Button(&PORTA, 0, 0, 0))
{
LedIncrement();
do {
// do nothing
Delay_us(10);
} while (Button(&PORTA, 0, 0, 0));
}
} while (1);
<p>}//~!
</pre><hr></blockquote><p>[ July 11, 2005: Message edited by: Matt Nuzum ]</p>
User avatar
philba
Posts: 2050
Joined: Tue Nov 30, 2004 1:01 am
Location: Seattle
Contact:

Re: Will assembler help me here?

Post by philba »

<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>Originally posted by Matt Nuzum:
...
If I program in ASM, will I be able to avoid this problem of the longer beeps slowing down the button detecting code?
...
<hr></blockquote><p>Writing in asm won't really help. Your problem is not the language you used to write the program but the structure of your program itself.<p>The problem, I'm sure, occurs because play sound takes a long time to complete. You don't get back to check for button presses until it is complete and thus you miss the button next press(es). the delay routine also takes time and could cause you to miss presses (thouhg less likely).<p>You need to detect button presses independently of where the program is executing. The most common approach would be to use interrupts for either play sound or button presses. I would make play sound interrupt driven but since play sound looks like a library function, you may not have access to the code. <p>I would check out this PIC tutorial - 12 is on interrupts but its a good general introduction to PIC programming and worth going through from the beginning. http://www.mstracey.btinternet.co.uk/pi ... icmain.htm<p>Phil
rstofer
Posts: 115
Joined: Sun May 15, 2005 1:01 am
Contact:

Re: Will assembler help me here?

Post by rstofer »

I guess one of the first questions is what to do if a second button is pressed while the first tone is still playing. Is it desired to abort the playback? Continue the playback for the first button but process the second button and ignore playback? Continue the playback for the first button, process the second button and queue up a request for a later playback? This last choice could result in tones play LONG after the last of ten quick pushes.<p>If you really want to solve this you should consider using 'interrupt on change'. This means that PORTB would need to be used for inputs, that all of those inputs would be 'interrupt on change' and that the port would not be polled.<p>Another way to go is to use the INT input on PORTB.0.<p>Either way, each depression would result in an interrupt handler being called. The handler would deal with debounce and incrementing the output. There would be a flag set every time the sound_play subroutine was entered via a call in the mainline code. If the flag was clear the interrupt handler would set a flag for the mainline code to call the sound_play subroutine. If the flag was set, the interrupt handler wouldn't set a flag for the mainline code as the sound_play subroutine was already busy. Or the handler could increment a counter of the number of times the mainline should call the sound_play subroutine.<p>Something like that...
User avatar
philba
Posts: 2050
Joined: Tue Nov 30, 2004 1:01 am
Location: Seattle
Contact:

Re: Will assembler help me here?

Post by philba »

my preference would be to do the play function via interrupts. Its likely to be the longer duration function. Its a mystery how it currently works but I'd use a timer interrupt to toggle the sound output pin (and filter with a simple RC). Timer value would be set to 1/2 the sound period (ie 1/2f). Use a variable to determine duration of the sound - number_of_cycles - that the timer ISR decrements. When it reaches 0, turn off the timer. Sound_play sets number_of_cycles and enables the timer. When there is a new button press, sound_play just updates number_of_cycles so the sound continues longer. assuming continuing the sound is the desired outcome of a button press.
Newz2000
Posts: 507
Joined: Wed May 18, 2005 1:01 am
Location: Des Moines, Iowa, USA
Contact:

Re: Will assembler help me here?

Post by Newz2000 »

Thanks for the suggestions. Phil, I've been to that tutorial site a number of times and even have it bookmarked; that is a great site.<p>I've temporarily solved the problem by ensuring the sounds are very short. Its difficult to press a button more than 8 times per second, so keeping the duration down to .125 seconds should be good. As I've mentioned before, my experiments are for human-computer interaction, and I'm exploring "postive feedback" techniques. Short high sounds seem to have a "good" feel, while slightly longer low sounds warn the user of a problem.<p>I'm not sure if I can use interupts, now that I think of it. It seems like something I was planning on using required them off, maybe the bootloader?<p>rstofer: you've read my mind. I was actually going to pose a question in another day or two about that very subject. Especially in the case of a key-pad where you use 7 i/o pins for a 3x4 array of buttons. I haven't tried it yet, or even thought seriously about it, which was part of the reason why I didn't ask. I'll do some experimentation when I can and report my results and ask for help. ;) <p>This "halt while playing sounds" reminds me of programming my Vic 20. Or, does anyone remember the PC Speaker sound driver for Windows 3.1? It worked fine, except you couldn't move the mouse or do anything while the computer played sounds. I'm really curious how that will affect serial communication. I suspect it will prevent communication from occurring while sounds are being played.
User avatar
HighFrequency
Posts: 122
Joined: Sun Apr 17, 2005 1:01 am
Location: Victoria BC
Contact:

Re: Will assembler help me here?

Post by HighFrequency »

How do you debounce the button? If it's a simple time delay, you could simply put the sound output in there. Might not be long enough though. Oh well, just thinking out loud.
There is only one correct answer, mine.
Newz2000
Posts: 507
Joined: Wed May 18, 2005 1:01 am
Location: Des Moines, Iowa, USA
Contact:

Re: Will assembler help me here?

Post by Newz2000 »

<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>Originally posted by HighFrequency:
How do you debounce the button?<hr></blockquote>
I used the built in Button() function for debouncing.
<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">code:</font><hr><pre>
Button(&PORTA, 0, 0, 0)
Port ----^ ^ ^ ^
Pin --------+ | |
delay ----+ |
Invert ----+
</pre><hr></blockquote>
Interestingly, the docs don't tell what the unit of measurement for the delay is. :) The default is 1, but I didn't notice a difference when using 0 or 1.
User avatar
philba
Posts: 2050
Joined: Tue Nov 30, 2004 1:01 am
Location: Seattle
Contact:

Re: Will assembler help me here?

Post by philba »

<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>Originally posted by Matt Nuzum:
...
I'm not sure if I can use interupts, now that I think of it. It seems like something I was planning on using required them off, maybe the bootloader?...
<hr></blockquote><p>which bootloader? There really should be no reason not to use interrupts. If the bootloader is stealing the interrupt vector and not giving you a way to use it, its not a very good bootloader at all. check out the boot loader at sparkfun - it seems pretty well designed.<p>Using the mikro stuff is easy but it doesn't seem very robust. I'd recommend you write your own routines. By the way, the MikroC code is really fat. I did an application in both mikroC and cc5x and cc5x was half the code size of mikroC!<p>The problem with polled operation (whether playing back sounds, debouncing switches, ...) is that you are slave to the timing of the polled loop. If the debounce takes, say, 50 mS, then you can't do anything else for that period of time. The beauty of interrupts is that you can service the need immediately (or very soon thereafter). In my projects, the main loop usually just checks to see if something has happened (via interrupts) and then acts on that.<p>For example, serial I/O - if you use polling for receiving characters from the UART, you can only get characters when you get around to executing the receive code. However, if you use interrupts, you can get the character when it is sent and save it for when your code gets around to using it. <p>Using interrupts is a little more complex but well worth the effort.<p>One comment on interrupts for buttons/switches. This is more tricky than you would think due to button bounce. Some switches are pretty clean while others bounce all over the place. I find debouncing interrupt driven switches to be more complex than debouncing polled switches. I recommend not using interrupts for switches at all. I've had good results using a timer interrupt (1 or 2 mS) that checks switch/button state and debounces it. I'll detail it out if you are interested.<p>phil
Newz2000
Posts: 507
Joined: Wed May 18, 2005 1:01 am
Location: Des Moines, Iowa, USA
Contact:

Re: Will assembler help me here?

Post by Newz2000 »

<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>Originally posted by philba:
which bootloader? There really should be no reason not to use interrupts. If the bootloader is stealing the interrupt vector and not giving you a way to use it, its not a very good bootloader at all. check out the boot loader at sparkfun - it seems pretty well designed.<hr></blockquote>
I've looked at so many... some had problems. The one I liked the best was the "tiny" boot loader, but I haven't tried any yet. I think Sparkfun's has some licensing limitations that I'm not 100% comfortable with.
<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>One comment on interrupts for buttons/switches. This is more tricky than you would think due to button bounce... I recommend not using interrupts for switches at all.<hr></blockquote>
Yeah, I actually thought this very same thing. I'm glad to hear it confirmed. My original thought was that with buttons, a dozen or two micro seconds don't mean much, so deal with them in a lower priority fashion. That leads to...
<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>I've had good results using a timer interrupt (1 or 2 mS) that checks switch/button state and debounces it. I'll detail it out if you are interested.<hr></blockquote>
Brilliant! That is an excellent idea. Since checking switches takes only a couple instructions, this seems perfect. I can spend my time in a loop handling things that are important and then get a little reminder every now and then to check my buttons.<p>I have not yet experimented with Pic timers, but now I see that this definately needs investigation.
Gorgon
Posts: 325
Joined: Wed May 04, 2005 1:01 am
Location: Norway
Contact:

Re: Will assembler help me here?

Post by Gorgon »

Hi Matt,
Depending on your max frequency to play I don't think it matters much what sort of language you use. Personally I prefer assembler, but that's me. What I do in my projects is to make a general interrupt driven timer system, with a resolution of the least time or highest(2times) frequency. In your case this wil be the generation of sound. For the other 'users' I just count down this time to intervals usable for the different use. For manual inputs I normally use about 5-10ms intervals and 15 to 30 ms debounce time. 30ms debounce gives you about a resolution of 15 to 30 presses each second. I normally use 3 samples to validate the input. Make your software input independent. Each input fight for itself! if you have more than one input. Your sound generation is generated in the interrupt routine, but defined in the background program. Please remember to turn of interrupt enable whenever you change parameters used in the interrupt routine. This will save you for headaches debugging the program.(turn it on again afterwards!!)<p>Points from a hotel in Sweden, after som wine. (a holiday letter)<p>TOK ;)
Gorgon the Caretaker - Character in a childrens TV-show from 1968. ;)
User avatar
philba
Posts: 2050
Joined: Tue Nov 30, 2004 1:01 am
Location: Seattle
Contact:

Re: Will assembler help me here?

Post by philba »

by the way, my standard "system" timer is 50 uS. I then take "taps" off of it for the desired delay intervals. For 1mS, for example, I have a variable called tcnt1ms. tcnt1ms is used in the ISR to count to 1mS (start at 20 and count down). When it hits zero, I reset it to 20 and call what ever routines need 1ms timing. This way, I can have an "unlimited" number of timers with 50 uS resolution and only use a single physical timer.
Gorgon
Posts: 325
Joined: Wed May 04, 2005 1:01 am
Location: Norway
Contact:

Re: Will assembler help me here?

Post by Gorgon »

Hi Phil,
Your timersystem is about the same as the one I tried to describe. I normally only count down basic timers in the ISR, and set flags(semaphores) for all other things to be done in the main loop. This of course depends on how crowded your system is.<p>When handling keyboard scanning I normally use a sample buffer with three samples and a fourth 'valid state' buffer and a scratchpad. These are normally bytes. With a series of XOR, AND and OR-ing the current stable and new changed inputs can be found very easy and with few instructions.<p>Matt,
The bootloader you talk about, is this some sort of OS? If it is, and doesn't support interrupt routines, get rid of it!<p>When you need an OS, you have outgrown any 8 bit microcontroller! :D <p>TOK ;)<p>[ July 13, 2005: Message edited by: Gorgon ]</p>
Gorgon the Caretaker - Character in a childrens TV-show from 1968. ;)
Newz2000
Posts: 507
Joined: Wed May 18, 2005 1:01 am
Location: Des Moines, Iowa, USA
Contact:

Re: Will assembler help me here?

Post by Newz2000 »

<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>Originally posted by Gorgon:
The bootloader you talk about, is this some sort of OS? If it is, and doesn't support interrupt routines, get rid of it!<p>When you need an OS, you have outgrown any 8 bit microcontroller! :D <hr></blockquote><p>I've wished for an OS some times (can you say, multitasking?) but no, in this case, the boot loader merely allows sending new programs to the chip without using a burner. There are several free ones out there, some big, some tiny. At least one of the ones I've investigated required interupts off, but that doesn't mean they all do.<p>I've looked at this one, tiny pic bootloader uses about 100 words,
the one at spark fun uses 300 words, has license limitations though,
and one from mikroelektronika (maybe this is the one that prevents interupts?) but its too big.<p>It's all moot for now. On a previous thread I discussed a circuit that might allow programming a chip in circuit and based on the conversation, felt that bootloading was the solution. Since I'm still in the "designing circuit" stage, I don't need to worry about it for a while. That's step 61 and I'm still on step 7. ;) <p>Thanks a lot for your help, I really appreciate your advice.<p>[ July 13, 2005: Message edited by: Matt Nuzum ]</p>
User avatar
philba
Posts: 2050
Joined: Tue Nov 30, 2004 1:01 am
Location: Seattle
Contact:

Re: Will assembler help me here?

Post by philba »

<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>Originally posted by Gorgon:
Hi Phil,
Your timersystem is about the same as the one I tried to describe. I normally only count down basic timers in the ISR, and set flags(semaphores) for all other things to be done in the main loop. This of course depends on how crowded your system is.
<hr></blockquote><p>one thing I do differently is using semaphore that has a signal count. that way you will know if yo missed something. Its not a true semaphore since it doesn't imply context switching (which the PIC is pathetic at).<p> <blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>
When handling keyboard scanning I normally use a sample buffer with three samples and a fourth 'valid state' buffer and a scratchpad. These are normally bytes. With a series of XOR, AND and OR-ing the current stable and new changed inputs can be found very easy and with few instructions.
<hr></blockquote><p>my debounce code maintains n states where n is a power of 2. Here's pseudo C code for a 4 state debouncer:
<blockquote><font size="1" face="Verdana, Helvetica, sans-serif">code:</font><hr><pre>
char next_state[4] = { 1, 2, 3, 3 }
char prev_state[4] = { 0, 0, 1, 2 }
char sw_state = ST_OFF
#define SW_OFF 0
#define SW_ON 3<p>switchISR(){
if(switch_on) // depends low or hi
sw_state = next_state[sw_state]
else
sw_state = prev_state[sw_state]
}<p>// to use in the main line
if(sw_state == SW_ON) <do switch stuff><p></pre><hr></blockquote><p>When the switch gets pressed, it takes at least 4 calls to get to SW_ON and each bounce will require at least 2 additional calls.<p>You can control the debounce time by choosing the interval between calls to switchISR() and the size of the state table. I have found that that 4 is a good size and most switches do fine with 1 mS timing though I often will use 2 or 4 mS as well.<p>You can collapse the two state tables into one 6 byte array (0,0,1,2,3,3) and always offset next state by 2. cc5x will do this automatically for you!<p>In asm, you can completely optimize out the tables though they are pretty small. I prefer the readablility of the table approach, though.<p>Also, you can pack multiple switches into a byte but the code is more complex.<p> <blockquote><font size="1" face="Verdana, Helvetica, sans-serif">quote:</font><hr>
Matt,
The bootloader you talk about, is this some sort of OS? If it is, and doesn't support interrupt routines, get rid of it!<p>When you need an OS, you have outgrown any 8 bit microcontroller! :D <p>TOK ;) <p>[ July 13, 2005: Message edited by: Gorgon ]
<hr></blockquote><p>Actually, a simple executive is quite approriate on 8 bit micros though PIC is just not built to make it easy. AVR and, I believe, Z8 can support this kind of "OS" as the stack is easily accessable. A simple tasking model with signal/wait kind of capability makes writing real time code a LOT easier.<p>Gorgon, hope that Swedish wine is good!
Gorgon
Posts: 325
Joined: Wed May 04, 2005 1:01 am
Location: Norway
Contact:

Re: Will assembler help me here?

Post by Gorgon »

Phil,
It was, but now I'm home again. Need to do some work on the house while the good weather(and holiday) last.<p>TOK ;)<p>[ July 13, 2005: Message edited by: Gorgon ]</p>
Gorgon the Caretaker - Character in a childrens TV-show from 1968. ;)
Post Reply

Who is online

Users browsing this forum: No registered users and 56 guests