Oh yeah, something to watch out for:
That code example assumes that the Watchdog timer (WDT) is disabled. If it were enabled, then the while(1) loop needs to have a CLRWDT() statement in it.
Without it, the WDT will expire every ‘x’ mS, and reset your PIC without warning. This can cause bizarre behavior if you’re not aware of it.
Nick, the sleep mode in a PIC is nothing like putting a thread to sleep in windows.
In a PICs sleep mode, the chip powers down to bare minimum, and as Pete eluded to, many of the peripherals are shut down. If you are intending to do some work while the thing is waiting for a timeout to expire, you don’t want to put the chip to sleep.
The so called busy-wait that I steered you away from is a concept that has a very different meaning when you take it out of a multitasking OS. On the PIC, there isn’t any code to worry about stealing processor time from except the code you write. So unless you’ve got something better to be doing than sitting in the loop, there’s no real reason not to sit in the loop (Except Pete’s portability concerns).
That’s not to say that Pete’s solution isn’t a good way, and Timers happen to be about the most pain free entry into working with interrupts.
By the way, Andy, I was a good boy and read up on bitwise operations (and most significant bit and then bits and bytes in general, and then… well, let’s just say I spent four hours on the Wiki ).
Not sure exactly how that’s going to fit in with the micro, yet, but that’ll come later, I suppose…
I know stuff?!?!
Since when?
Hmmmm…
Question: Is there an advantage to using bitmasking to turn on and off pins, as opposed to something like RA0 = 0?
Well… it does, which can’t be a good thing.
Scary.
Woopsies!
Fixed it.
Agh!
VBE and QB have taught me bad habits.
In the words of the great Homer Simpson: “DOH!”
Yet another thing that VBE and QB keep all nice-nice.
When I overfill a variable, I get a nice error on screen telling me so.
Not so, here.
In mind I shall keep that, but I’m going to stop fixing the above example, give it up as a bad job, and rewrite it with a timer and interupts as you explained in a below post.
Ahh!
That’s the ticket.
And, indeed, the .h file seems to confirm this.
I try to stay away from programming things like: “the sky != blue”.
Alright, Pete, you’ve finally got me.
I’ll be a good little boy and learn how to do it the proper way.
An initial question (fear not, I shall have more when I come back at noon and try to write my own version of this ):
I noticed that you didn’t explicitly declare (unless my eyes aren’t working) your counters (i.e. “ms20_tick++;”).
Is that a normal practice?
Doh!
I knew there was a reason for “#pragma WDT = OFF” in the example program.
In that case, would it be wise to turn off any timers that I’m not using?
Ahhhh!
Good point.
The sleep mode doesn’t seem as wise as I had thought it to be, now that I think of it, since whatever minimal power that it’ll be saving isn’t anything compared to the near 3Ah of my batteries.
Wait a sec…
Pete finally got me to give in and learn about interupts, and now you’re telling me not to bother?!
Just kidding.
I had thought that microcontroller programming would take a very long time to get the grasp of, so I didn’t want to jump into non-intuitive methods first thing.
But, now that I see how things work out, it doesn’t look to be too hard.
Microcontroller programming is all about taking a very few simple commands and being very creative in order to get the job done.
That’s right up my alley.
Wheras high-level OS programming gives you WAY more than you need, but you spend your time finding, learning, and memorizing each one.
It’s nice to see that at least one part of this won’t be so scary.
Now, if only I can get the electronics going…
::shudders::
Oooooooooh!
My ADD just kicked in and I noticed something!
I’ve broken 1k posts!
Wooohooo!
::does happy dance::
Now, where’s my damn cookie?!?!
Uh-oh…
Guys… look what it says under my name…
“Robot Guru”
Uh… Andy or Pete… you better take that away from me.
We don’t want people starting to think that I know things!
I say we petition Jim to get my “guru” license revoked.
I’d much rather have a cookie, instead.
I’ll answer some questions from your latest post, but I’ll forgo the quoting thing:
The “RA0 = 0” thing and “bitmasking” are the same thing - if RA0 = 0 works, then the bitmasking has already been declared in the .H file.
Regarding ‘overfilling a variable’ - the C compiler will catch it at compile-time if it involves a constant, such as:
“jj = 100000000;” will not compile if jj is an ordinary int.
But if you do this:
while(1) { jj++; } // increment jj forever
then it will run, and jj will wrap around to 0 and keep going.
Yes, I didn’t init the ms20_tick var and such. Good catch! I suppose I broke my own little rule. The reason I didn’t init those particular vars is because they are constantly incrementing due to interrupts, and I expect them to overflow and wrap around to 0, so there is no “unexpected value” for those vars - I don’t care if they initialize randomly.
No, you don’t need to turn off timers that you’re not using (although they will default to off). You just need to disable the interrupts for any unused peripherals (although they all default to disabled).
Andy is quite right that sitting in an ‘idle loop’ does no harm, if you have nothing else to do. But I’m thinking that pretty soon, you will have something else to do.
For example, in my robot-sensor code that I’m working on:
The interrupt routine does (among other things) decides when it is time to do an A/D reading from the IRPD. But the A/D readings are ‘relatively’ slow, so I don’t want to stay in the interrupt routine and wait for it to finish. So, the int routine sets a boolean flag that says “new A/D reading wanted”, then it returns back to the main() loop. In the main loop, it sees the flag, and does the A/D reading for however long it takes. This way, if another type of int were to occur, it would immediately go handle it. Note that in general when you’re in the int routine, you cannot (or should not) handle other ints of the same priority. I’m guessing that your PIC has 2 interrupt priorities - the 16F88 has only one.
//***********************************************************************//
//* This program blinks an LED hooked up to RA0 every 20ms. *//
//* It also blinks an LED hooked up to RA1 every second. *//
//***********************************************************************//
//* The Timer0's 20ms intervals was calculated in the following way: *//
//* Timer0 clock = 8,000,000Hz(Oscillator Frequency) / 4 = 2,000,000Hz *//
//* 2,000,000Hz / 256 (Prescalar) = 7812.5Hz *//
//* 7812.5Hz / 156 (amount of registers when "TMR0L = 0x64") = 50.080Hz *//
//* 1 second / 50.080Hz = 19.968 millisecond interval *//
#include <PIC18F4620.h>; // Specify the micro being used.
#pragma config WDT = OFF; // Turn off Watch Dog Timer; otherwise, it would reset the micro.
#pragma config OSC = INTIO2; // Use the 8 MHz internal oscillator without externals.
#define TICKS_PER_SECOND; 50 // The number of interrupts in 1 second.
int tick_counter = 0; // Create and zero a global Timer0 counter.
void main(void)
{
T0CON = 0b11000111 // Timer on, 8-bits, 1:256 prescaler.
TMR0L = 0x64; // Set the timer's start (low) bit as 100.
INTCON = 0b11100000; // Turns on Global, Peripheral, and TMR0 interupts.
TRISA = 0; // Sets bank A for output.
while(1) // Infinite loop that keeps program from stopping.
{
// Do your normal stuff here...
}
}
static void interrupt timer0_handler(void)
if (TMR0IF) // If that flag bit is true
// then the interupt is from TMRO.
// This occurs every 20 ms.
{
tick_counter++; // Count how many interupts occured.
TMR0IF = 0; // Clear the interrupt flag.
TMR0L = 0x00; // Reset the timer period.
/******************* Blink an LED really fast. *******************/
if (RA0 == 0) // If the LED is off...
{RA0 = 1;} // ...turn the LED on.
else // If the LED is on...
{RA0 = 0} // ...turn the LED off.
/******************* Blink an LED more slowly. *******************/
if (tick_counter > TICKS_PER_SECOND)
// This is true every second.
{
second_counter = 0; // Reset the counter.
if (RA1 == 0) // If the LED is off...
{RA1 = 1;} // ...turn the LED on.
else // If the LED is on...
{RA1 = 0;} // ...turn the LED off.
}
}
return; // Return to main code.
}
Basically, I went through your program and reformatted everything the way that I like to see it.
Then I went through and searched for everything that I didn’t understand in the 4620 User’s Manual and replaced them with “duh” explanations.
Now I’m down to only a handful of things that I don’t understand:
(1) Are your “rough numbers” experimentally or mathematically determined? (If so, how?)
(2) Where did “OPTION = 0b00011111;” come from?
I’m guessing that OPTION is an 8-bit register, just like INTCON, but it doesn’t seem to be in my User’s Manual.
(3) You have two variables (“LED” and “led_image”) that don’t seem to be declared anywhere. Is that just a generic variable that you threw in there for me to insert the right pins with?
(4) Your comment says that “TMR0 = 0x66” sets the timer for 128uS interupts. Where did you get that number from? Eenie-meenie-minee-moh?
Me so happy!
Your “INTCON = 0b11100000;” line showed me the right syntax for using the 8-bit registers (and what those were), which I had been hunting for.
Now the User’s Manual makes a LOT more sense!
The 7812 and 156 values are calculated based on ints every 0.128 mS (7812 X 0.128 mS = 1000 mS = 1 second). The 0x66 value was at first ‘calculated’, but later I had to adjust it because the calculation didn’t take into account the overhead of interrupt processing and such. This is only a problem if you interrupt relatively often (like in fractions of a millisecond). I’m going to change this soon in my own code, because I no longer need to do timer-ints so often. I’ll use one of the other timers, and make the period be 20 mS, instead of 0.128 mS like it is now.
To figure out the timer value settings, study the PICs datasheet chapter for that particular timer.
Your code uses vars i and j, but they are not declared anywhere except in main(). I suggest you go back to a more meaningful var name (like in my code sample), and declare them globally (above the main() routine, not inside it), since those values need to survive when you exit the int routine.
I suggest using a slightly more generic name for the int routine, since that same int routine will be used for any type of int at that same priority. Check the C18 docs (or a C18 code sample) for the exact syntax of how to declare an int routine. My example is for PICC.
Yep, I forgot to include the declarations for led_image and LED in my sample. The LED is a single-bit port, such as RB5 or something. The led_image var is a global unsigned char type.
The OPTION_REG is described under the Memory Organization chapter in the PIC datasheet (not so obvious, I know). It happens to include some settings related to TMR0. All the other timers have their own registers elsewhere.
So, changing the value of the register which it overflows at (defaults to FFh) is inversely proportional to the time at which it occurs.
Now, I have to ask the Wiki about hexadecimal…
Question, though:
Couldn’t I just use the last three bits of the T0CON register to set the prescalar value?
Or am I misunderstanding what prescalars do?
I suggest using a slightly more generic name for the int routine, since that same int routine will be used for any type of int at that same priority.
Agreed.
However, I’m actually going to use this example, as is when I get my micro, so the name fits.
When I expand and alter it to actually use it for the robot (and it looks like I will be able to do just that with it), I’ll make the name more “general”.
Probably something intuitive like: “highest_interupt_priority” or something.
Aye-aye, cap’n!
I’ll give the “C18 Getting Started Guide” a deeper look-see, since I should be able to understand most of it, now.
I believe they give an interupt code example in there (although, no tmr example >.<").
Speaking of that… they use premade delay functions to drive interupts, instead of timers.
I’m guessing that these delay routines are doing exactly what your code does.
Any reason not to use them?
Uh… I think I’m going blind, mate.
I went to and read that whole chapter (and understood <1% of it, I might add ), but I didn’t see OPTION, OPTION_REG, or even anything mentioned about timers…
Then hit Ctrl + F and searched for OPTION and OPTION_REG and they don’t appear in the document (well, “OPTION” does, but not what we’re looking for).
Perhaps you have a different manual that I?
Could you copy + paste the part that deals with it?
Oh… btw…
I’ve edited the previous code post with the current changes (that way I don’t overfill this thread with multiple code).
One mistake I caught was with the oscillator.
I had it set for HS mode, but now it’s in INTIO2, which (if I’ve read the manual right) requires no external oscillator inputs and frees up two I/O ports.
For simplicity’s sake, I’ll keep to that, until I get more accustomed.
I still need a bit o’ work until I fully understand your code, but I’m almost there.
I gots my stuff in from Sparkfun and ServoCity, today.
That Bluetooth SMiRF really is a tiny little bugger!
Still waiting for the bulk of my order: the Jameco, Microchip, eXtreme-DIY, and Lynxmotion stuff.
Those are all scheduled to come on Monday, I believe, so don’t expect to see my lovely face for a couple days after that.
I’ll be having way too much fun to talk to you boring humans.
Almost. The timer always overflows at 0xFF. What you’re changing is the starting point for the counter. For example, if you set it at 0xFE, then only 2 counts later it rolls over to 0, and the TMR0IF flag becomes set. And if you have TMR0IE also set in advance, then an actual interrupt occurs.
The prescaler is to give you different ranges of time-delay to work with. So if the prescaler is set at 1:1 in the example above, then the timer expires after every two clocks as I said. But if you set the prescaler to say 1:4, then you’re dividing the timer’s clock freq by 4, so the timer goes off every 8 clocks. In the code sample from the other day, I think I’m using the maximum prescaler value, to slow it down as much as possible. But it’s still relatively fast, because TMR0 is only an 8-bit counter. A 16-bit counter (as used in some of the other timers) allows for longer delays.
I don’t think I follow you… Generally some piece of hardware has to initiate an interrupt. The TMRs in the PIC are hardware devices, and are tied into the CPU’s interrupt system. Perhaps what you mean is that the code you’ve seen does the loop-delay approach that we talked about the other day - but that does not involve interrupts at all.
The OPTION register:
The example I gave is for the 16F88. I just looked at an 18F24xx sheet, and I see that there’s no such thing. It’s probably a basic diff between the 16F and 18F PICs. Just go to the chapter that explains TMR0 (and the other timers), and it will show you what registers are relevant to each timer.
Yes, INTIO2 is the clock mode you want. It allows you to use RB6 and RB7 for whatever you want. Don’t use the PLL mode until you’ve got some basic stuff working without it.
Meanwhile, today at work the Microchip sales and app-engineer dudes visited us…
Arg!
I got that backwards, too!
Well, that does indeed make sense, now.
Well, I’d love to, but…
I don’t know what registers “OPTION = 0b00011111;” turns on and off.
If you can tell me that, I can find the equivalent ones in my data sheet.
What?!?!?!
You mean I got something right?
Freaky…
Damn…
I can’t wait until I start working in a real job…
As an embedded programmer (which I’ll probably be, until I can find a job that deals directly with robotics), I’ll be able to get my hands on a lot of goodies just like you.
::me so jealous!::
That’s what I’m waiting for also, to apply to the first manufacturing company that specializes in robotic products! Instead of HP, work for HRi Humanoid robotics incorporated!
For chips like the 16F88, the OPTION register controls the following things:
PORTB pullup enable.
Interrupt edge select for RB0/INT.
TMR0 clock source select.
TMR0 source edge select.
Prescaler assignment.
Prescaler rate selection (3 bits).
With the 18F4620, you should probably start with settings like this:
T0CON = 0b11000111;
This makes TMR0 an 8-bit timer, with the max prescaler.
In my earlier code example where I used register “TMR0”, you will use “TMR0L” instead.
I think the rest of my example should work the same.
In the 4620, TMR0 has a “16-bit mode”, but I recommend starting with 8-bit mode first.
I’ll finish the program this afternoon and post the changes.
I just finished testing my SMiRF and dongle, and it seems to be working.
I can’t tell, truly, because I don’t have anything to send data to.
But, it’s established a good connection…
I had foregone the use of caps to smooth out the regulator portion, since I didn’t get my Jameco shipment, yet.
It doesn’t seem to bother it, probably since this bugger is rated for a pretty wide voltage range.
Hopefully, Jameco and eXtreme-DIY will come in soon, so I can stop wire-wrapping my prototyping and use a real perfboard.
I have been following along in this thread, but there is alot of stuff that sounds jiberish to me. Is there a book I can get that you would recomend I read. It does not have to be comprehensive, just the basics for starters.
Well, the best books are the .pdf documents that come with the micros.
Without it… I would know nothing.
They’re very hard to understand, at first (unlike the Parallax’s great .pdfs that you’re used to).
They seem to be made for those who are already familiar with PICs, but haven’t used that exact model, yet.
Once you get a good understanding of how to use binary to toggle on and off register options, the rest goes a lot easier.
I’d recommend a general-purpose C book, as well, if you don’t know C.
Learning C won’t be hard, since you only really use the most basic commands.
The most difficult part there will probably be pointers, and those aren’t too difficult.
Dan’s got a good PIC book that he said he’d bring me.
When I get it, I’ll let you know what the title is.
A good C book is: The C Programming Language by Kernighan and Ritchie.
So really the only way to use PICs is to learn the C programming language? I have never been very good with programming, perhaps I lack the programming logic, or any logic for that matter.
I will give it a shot because if I can learn how to use PICs then I could do a whole lot more in the future.
No, but your only serious alternative is assembly.
These days it’s really hard to think of a case where assembly on a PIC would be a better choice than C. The original arguments of “assembly is smaller and more efficient” is only true in the most extreme cases. And the ease of understanding of C over assembly is a huge benefit.
And like Nick said, don’t get bogged down in the deeper aspects of C - some of it is not useful or not possible on a PIC.
For us bot-dudes, everything we do boils down to:
Manipulating registers and I/O ports.
Dealing with timing (polling, interrupts, etc.).
Dealing with interface buses, such as I2C, SPI, and RS-232.
That’s about it.
Okeydokes.
Methinks me finally got the code the way it’s supposed to be.
I changed around the timer values so that the interupts would occur as infrequently as possible.
As you’ll see, this has decreased it’s accuracy:
8,000,000 Hz (ticks per minute) / 60 s / 1 minute = 133333.333333ticks per second
133333.33333ticks per second / 256 prescaler = 520.833ticks per second
So, "SECOND_TICKS = 521
And, since 20ms occurs 50 times every second…
520.833ticks per second / 50 times = 10.416 ticks in 20 ms.
So, "MS20_TICKS = 10
The integer 10 is about 4 percent too small.
Do you think that error will stack up too greatly to be of use?
I mean, after just one full second, it’ll be off almost 40ms.
Of course, it’ll be accurate enough for an LED, but that can’t be accurate enough for a servo, right?
Did I get all that math right?
I also checked the interupt setup.
It seems like the PIR1 = 0 is unneeded, since it doesn’t deal with TMR0, at all, so I took it out.
TOCON = 0b11100000 clears the Timer0 interupt flag, so that’s covered.
As for the proper syntax, I’ve found this code example, but I’ve spent a couple hours trying to sort it out and have gotten nowhere:
[code]#pragma config OSC = HS #pragma config WDT = OFF #pragma config LVP = OFF #pragma config DEBUG = ON
/*
For high interrupts, control is transferred to address 0x8. /
void toggle_buzzer (void); / prototype needed for ‘goto’ below / #pragma code HIGH_INTERRUPT_VECTOR = 0x8
void high_ISR (void)
{
_asm
goto toggle_buzzer
_endasm
} #pragma code / allow the linker to locate the remaining code /
/
If the buzzer is on, turn it off. If it is off, turn it on. / #pragma interrupt toggle_buzzer
void toggle_buzzer (void)
{
CCP1CON = ~CCP1CON & 0x0F; / turn the buzzer off or on /
INTCONbits.INT0IF = 0; / clear flag to avoid another interrupt /
}
void EnableHighInterrupts (void)
{
RCONbits.IPEN = 1; / enable interrupt priority levels /
INTCONbits.GIEH = 1; / enable all high priority interrupts /
}
void InitializeBuzzer (void)
{
T2CON = 0x05; / postscale 1:1, Timer2 ON, prescaler 4 /
TRISCbits.TRISC2 = 0; / configure the CCP1 module for the buzzer /
PR2 = 0x80; / initialize the PWM period /
CCPR1L = 0x80; / initialize the PWM duty cycle /
}
void SoundBuzzer (void)
{
CCP1CON = 0x0F; / turn the buzzer on /
while (1); / wait for the S3 button to be pressed /
}
void main (void)
{
EnableHighInterrupts ( );
InitializeBuzzer ( );
OpenRB0INT (PORTB_CHANGE_INT_ON & / enable the RB0/INT0 interrupt /
PORTB_PULLUPS_ON & / configure the RB0 pin for input /
FALLING_EDGE_INT); / trigger interrupt upon S3
button depression */
SoundBuzzer ( );
}[/code]
Does it seem like your way and this way are the same?
A couple of boo-boos here.
First, an 8 Mhz clock is 8,000,000 clocks per second, not per minute.
Second, the timer’s clock is “Fosc/4”, which would be 2 Mhz.
Third, you didn’t take into account the 256 ticks that TMR0L will count (assuming that you let TMR0L default to 0).
So, with a prescaler of 256, you are dividing the clock by (256*256=65536). So, 2000000/65536 is about 30.5 interrupts per second (I hope I got that right).
By putting a non-0 value in TMR0L, you would be able to get very close to 50 Hz (20 mS) interrupts.
But for the moment, I suggest you don’t try to talk to servos yet, just concentrate on making a LED flash at (say) 1 Hz.