Learning PIC microcontrollers and C programming

For stand-alone microprocessors

Re: Learning PIC microcontrollers and C programming

Postby William A Washburn » Tue Jun 21, 2011 8:00 pm

Doug,

Excellent preview you put together for Joe. This is extremely helpful to a starting C programmer.

Personally I like to align braces and have used the freeware formatting software that does this when I have
gotten myself into a situation where I've not known exactly where I was in the code.

The other thing that is important is the admonition you mentioned that reminds us; "The good thing
about standards is that there are so many of them to choose from".

Good work...
User avatar
William A Washburn
 
Posts: 93
Joined: Fri Oct 15, 2010 8:12 am

Re: Learning PIC microcontrollers and C programming

Postby Doug Coulter » Wed Jun 22, 2011 9:33 am

Yes, I like to align braces too, but I use "short" tabs for indenting because I like to keep lines short enough not to wrap on a standard printer page. Several of the editors I use will help with that "prettifying the code". And most will let you put the cursor on one brace or paren, and will then either jump to or highlight its matching one for you. I'm a big believer in color syntax highlighting, and usually set it up a little more garish than the "stock" settings. I make my comments the color of money, because that's what they are down the road -- many editors default to making them light gray or italicized - I don't like that, if anything they should be the most visible. Further, I comment braces, particularly the closing ones -- "this is the end of the for i loop" kind of thing. When a brace pair is separated by more than a single displayable page, this really pays off in fewer mistakes.
Keeps things from getting too messy when at the end of something there's three or more closing braces for nested if's and while loops.
When things get deep, I only indent one space. I've never liked putting the opening brace on the same line as the thing that starts a block -- I like putting it on the next line so the indents can match with the closing one -- and I comment both.

Example:
if (some hairy test of something)
{ //handle case of hairy test being true
// some code here
} // end of handle particular hairy case


I use the following scheme usually:
Keywords/builtins -- bright blue
Strings - purple (makes it real obvious when you forget a quote)
Numbers -- red, because using a number is bad programming practice -- use a define instead so it's easy to find, comment, and change for the whole file/program.
Comments -- color of money, and there's nothing wrong with having more of them than code

I use the block comment style at the beginning of routines I supply (like say the rs232 stuff, or the LCD stuff) with some fairly detailed stuff about how to use it, what to avoid, what it does under the hood. This helps work with a team.

If there's a call with a ton of parameters or it's just real long, I will break up the line into a couple, aligning the next lines with the opening paren of the call. This can be dangerous if there's anything past the line break character, some compilers get messed up with that one and if the "bad" character is a space, you can't see it. In Gedit, this is nicely fixed by having it show whitespace characters -- spaces are gray periods and tabs are little gray arrows. For some things there's a big difference. I recall some MS stuff where it had to be either spaces or tabs (idiots didn't know how to look for whitespace in whatever form). Some makefiles are like that, and some of the junk in OLE specifications. It can be a big issue if your editor automatically changes spaces to tabs or vice versa. When I can, I use spaces -- because depending on things, a printer or another editor might not have the same tab points and it will look terrible on another platform or printout. I DO really like color syntax highlighting for the rare times I have to print something so I can study it finely, but sometimes the colors that look great on screen stink on paper and you have to adjust.

I put my includes up top -- commented for why they're there, then global variables and structure definitions, things like that, then subroutines, then main at the end. This is just my convention, but it makes things easier to find, and dependencies clearer, while reducing the need for forward declarations (less noise in the file).

On real big and complex projects, I'll use my own form of Hungarian notation for naming, but I'm sort of lax about that. I will often put a g as the first character of a global, or an i for a variable used mainly by an interrupt. Perhaps a b for a boolean (matters in a pic where a bool is one bit and can't hold a real number) I rarely (anymore) use characters to describe the type of variable (char, integer, array, struct, float) because I really hope I've made that obvious another way and I like readable names. I use all uppercase for most defines -- makes them stand out when used, and in that case use underscores between words in a name. The rest of the time I use selective caps to make the words more readable, but this one some people don't like. I just don't like having to find the underscore key all the time as it's harder to touch-type quickly, though I should say that cut and paste are things I use a ton. That lets me use reasonably long and hopefully descriptive names more. As the ancients used to say -- there is a lot of power in a name, and you should really put some upfront effort into that, it will pay nice dividends later -- and if you find yourself not liking a name because the design shifted -- then you get a nice flag that you need to acknowledge that and re-think and maybe re-factor some. Some are easy -- SerialOutputBuffer should be clear by itself, for example.

Some advanced editors will have both multiple clipboards for cutting and pasting (which can save some hassle if you need to grab more than one thing and move it to more than one place, say in another file) and also templates. For example, something I use a lot might be "for (i=0;i++; i<?) {}. Some editors will let you pop that in with one keystroke, and put the cursor on the ? for you to fill in. And yes, I use i all over -- to me it means "iterator" and many of my programs just define i,j,k for that use (shades of fortran).

These are mostly style things -- but style can matter both for working with others, and for when you revisit code a long time after you've written it.

I think doing things this way gives you the best "force multiplication" out of the system. The more you can have the compiler detect errors, and make them instantly visible to a quick visual scan, the less time you have to spend "in the trenches" digging for junk. No compiler can catch conceptual errors, that's on you -- but I even get a leg up on that the with naming - if I find myself using a name that doesn't "look right" in use -- I know I've screwed up my organization somewhere along the line. I like it when I can read the lines and they are almost sentences that make sense -- the code should probably flow into the comment like you're making a statement (which you are, after all). While the coupling between beauty and truth is less tight here than in advanced physics math, it's there.

Always remember that none of this makes the code going into the target any bigger or slower (assuming you designed it right in the first place). It's fine to have a huge text file that compiles down to diddly, the PC where that part lives can hack it easy, and anything whatever you can do to make the code more readable and easier to work with later is well worth it. Especially when you want to grab some little tidbit out of it for the next project...the longer you do this, the bigger that nice old bag 'o tricks becomes, and the faster you can get good results using already known-good chunks of code.
Posting as just me, not as the forum owner. Everything I say is "in my opinion" and YMMV -- which should go for everyone without saying.
User avatar
Doug Coulter
 
Posts: 3515
Joined: Wed Jul 14, 2010 7:05 pm
Location: Floyd county, VA, USA

Re: Learning PIC microcontrollers and C programming

Postby Joe Jarski » Thu Jun 23, 2011 12:36 am

I've been plugging away at this for the last few days and trying to adjust how I do things and develop a "style". I haven't found the setting for case sensitivity in the compiler yet - may have just overlooked it. I've used the caps instead of underscores thing for years in filenaming on both unix and windows, so it's good to see that you use it here too - that'll be easy to get used to.
Doug Coulter wrote:I'd bet that they had you blinking lights using their provided "delay()" stuff

Yup, you guessed right. They were using it for both the blinking and the switch debouncing.

I downloaded your opsys just to look it over for an example and was surprised how small it is - I don't understand much yet, but I keep referring back to it.

I had another question here on how to read the value from the ADC converter, but I think I might have finally found the answer as I was typing this so I'm going to give it a try tomorrow before I ask and report back.
User avatar
Joe Jarski
 
Posts: 231
Joined: Thu Sep 16, 2010 8:37 pm
Location: SouthEast Michigan

Re: Learning PIC microcontrollers and C programming

Postby William A Washburn » Thu Jun 23, 2011 10:13 am

Joe,

I would like to add a few thoughts to Doug's recent code sample for you.

The importance of commenting your code cannot be overstated. With any good-sized program
you will have need in a few years, say, to go back to it and add to it or repair it. Without comments
placed in the code when it was written this becomes an extremely difficult task.

Back when I first started coding (40 years back) I wrote mostly Cobol. However, we had a group of
at least five programmers at the plant. Back then commenting your code wasn't so important. Now,
however, with all the personnel cutting that has happened I hear this all the time from the head
office (the only place where we still have any serious group of programmers left): "Well, Bill,
I'm trying to fix that piece of VB code we bought from the South African code specialists. Since
they left almost no comments in the code I'm having serious problems. That group is no longer
in business and I can't find the programmer who wrote the code.

This happens all the time and it can happen to you even if YOU wrote the code.

SO COMMENT...COMMENT...COMMENT!

Good Luck, Bill
User avatar
William A Washburn
 
Posts: 93
Joined: Fri Oct 15, 2010 8:12 am

Re: Learning PIC microcontrollers and C programming

Postby Doug Coulter » Thu Jun 23, 2011 11:12 am

I would add that comments are handy on other than just the "code" part. Big headers that say not only what this block of code does but WHY it's organized that way are super nice to have later.
Also, as someone said - "don't show me your flow charts, document your data structures and I can figure the rest out easy" - it's not far off, which is one reason I put as much of that up at the top as I can. The tops and bottoms of the source files are the easiest places to find -- so use them for the stuff you'll be looking for the most.

For an interrupt driven multitasking system an overall flow chart is meaningless...but you can do them for individual pieces. I don't really do that anymore, it's intuitive now. You just have to be aware there are other threads running that can do things in between instructions in the one you're looking at and plan accordingly so they don't step on each other. The main issue is usually not making a multibyte copy of some data an ISR is writing to without using some flag or just knowing the timing so you don't get a bad copy. Because in that case, you could get an old first byte and a new second byte if the ISR changes things between those reads....even though C makes it look atomic to copy a 16 or 32 bit integer, it's not....After awhile you get a feel for that kind of issue and it's no sweat. If the design is slick enough, you might not even need to check. If you know you told the A/D (for example) to convert a second ago, you don't need to check for ready if you're reading it a second later, then telling it to go again...to read it another second later. That's another case of "beware the wires that ain't there" -- no checks are needed due to the design of the code.

We'll do a walk-through on the opsys whenever you want to -- let's put it on that thread so all that's in one place. Yes, it's small as I could possibly make it and still do what's needed. I don't judge productivity by lines of code, but by results, and more often than not "less is more" if you're clever enough to do it with less. In that case, I was able to do with less fancy error checking by eliminating most causes of errors by design, up front, so the underlying understanding might be more complex than it first appears to be. Or as we say "beware the wires that aren't there" as some sorts of modifications might violate the underlying design principles that made them not required in this implementation.

The directive that does case is....#CASE It's in the book.

There is some setup for the A/D you have to do before reading the thing. It's not too bad, but you have to assign pins to it, set a clock source for the thing, and turn it on at a minimum.
Also, the result can be left or right justified in the result integer as you wish -- but you have to set it up, and that's another compiler directive.

Then read_adc(); does the rest, but is a blocking call if you use the simple form, for about 10us or more (heck, that's a hundred cycles!). I use interrupts to read results and re-channel and restart the a/d. In the older pics that can have "issues" as you have to wait some microseconds for the sample/hold to acquire after changing input channels and before telling the A/D to convert. The newer ones have more hardware that does this waiting for you if you set it up that way. While that compiler book has a lot of junk for pic builtins the ones we're working on don't have it IS a good place to look. They did some builtin functions that are pretty useful. Depending on what your overall system is doing, the built-ins might be just fine. When you're trying to wring more out of the chip, you tend to write your own to better accommodate system responsiveness and avoid the heck out of "poll till done" loops that block until done. My opsys makes that easy.

The upshot of this kind of thing philosophically is that this compiler makes a lot of things easy to do "at all" with a simple call to their support code. But once you get some experience, you find that to be pretty limiting for realtime, hard deadline, multithread code -- all that stuff is "blocking calls" or needs more than one to be scheduled after some delay for the hardware to do its thing. One of the things my opsys tries to do is improve on that for a better code writer, and make it nearly as easy to do it "really really right". But the CCS builtins are great for just getting your feet under you, and all the setup issues are the same either way for this. For serial I/O I had to completely ditch their stuff, however, to get non blocking and the protocol I wanted.

The way my opsys handles this is to have each "task", if it needs to poll something, just check once, and stay in the "check for done" state for the next time it's called (most tasks are built up as state machines with a big "switch(CurrentState) up top. If each task does this, the loop that calls them all in turn goes really fast, so many things can "check for done" at a time without making any of them wait for some other task to complete. It's almost identical to the "cooperative multitasking" in windows 3.1, actually. But here since you write both the opsys and the apps, it's a lot more stable...
Posting as just me, not as the forum owner. Everything I say is "in my opinion" and YMMV -- which should go for everyone without saying.
User avatar
Doug Coulter
 
Posts: 3515
Joined: Wed Jul 14, 2010 7:05 pm
Location: Floyd county, VA, USA

Re: Learning PIC microcontrollers and C programming

Postby Joe Jarski » Thu Jun 23, 2011 10:54 pm

Joe Jarski wrote:I had another question here on how to read the value from the ADC converter, but I think I might have finally found the answer as I was typing this so I'm going to give it a try tomorrow before I ask and report back.

I gave up on this for the time being and will come back to it. I was trying to use the 3 LEDs as a bar graph to display the voltage from the pot as the next exercise, but I have to do more reading on how to set up the ADC for that, so for the time being I'm going to continue on with the C side of things rather than how to work the ADC. For the record, I was starting out with this program from the previous exercise that just switched from one LED to the other.
Code: Select all
#include <18F45K22.h>
#device ICD=TRUE
#fuses HSM,NOLVP,NOWDT,PLLEN
#use delay(clock=64M, crystal=16M)

#define GREEN_LED    PIN_A5
#define YELLOW_LED   PIN_B4
#define RED_LED      PIN_B5

void main()
{
   setup_adc_ports (ALL_ANALOG);
   set_tris_a (0xFF);
   
   setup_comparator (CP2_A0_VREF | CP2_OUT_ON_A5);
   
   setup_dac (DAC_VSS_VDD | DAC_OUTPUT);
   dac_write (16);
   
   while (TRUE)
   {
      output_bit (RED_LED, ! C2OUT);
   }
}

I think I follow what's going on here and I know how to write the LED functions, but I don't think I can make use of the way the ADC is setup here. :?
User avatar
Joe Jarski
 
Posts: 231
Joined: Thu Sep 16, 2010 8:37 pm
Location: SouthEast Michigan

Re: Learning PIC microcontrollers and C programming

Postby Doug Coulter » Thu Jun 23, 2011 11:41 pm

Wow, I didn't know that chip could go 64 mhz (really 16 mhz instruction rate in that case). Darn fast for a PIC!

I'm going to assume that this one has a dac, and that it's output is one of the comparator inputs? This stuff is one reason the data sheet is so many pages! I'm guessing that if you didn't have one, the compiler would let you know that -- it's good about that once it knows what part you're using.

When you get the a/d going -- for one thing I'd only make one input analog and match the tris to that for now. You have to set which a/d channel to use at least once even if it's only one and the first one, unless you count on it powering up that way. You should also set which part of the 16 bit int result read_adc() returns gets the 10 bit answer in it (assuming it's ten bits in this pic, I've not downloaded its data sheet yet). You'll then get a number from zero to 1023 assuming it's right justified, with 0 at ground, and 1023 at whatever vref for the a/d is set to - it's a simple unsigned number. You'll have to read that part of the data sheet to set up vref the way you want, there are several options. (the header file for the chip will usually also have the info) I generally just used ground and Vdd for that -- pic power tends to be quiet enough for noise on the rails not to mess the a/d up -- these are good that way unless you're putting out real power on some i/o pin. Some of this the compiler might do "by magic" but it's truly best to say it all explicitly to make the code more readable later on -- and more portable to other chips in the family in case there is some difference there - it will flag what you have to change to accommodate any difference in chips you wind up putting the code in. Not important now but good habits are worth getting early to save hassle later.

Oh, and you have to setup a clock source for the a/d too -- see the data sheet (and the overview section of the compiler book). It can only go so fast, and there's a divider and usually a choice of a much slower RC oscillator for that function to setup the way you like it. Use the divided clock if you can, it's more stable and less noisy. The main reason for the RC option is to have the a/d be able to run when the chip is "asleep" to wake it up on done, or for cases when the divide by won't take the clock down slow enough for the a/d to function.
Posting as just me, not as the forum owner. Everything I say is "in my opinion" and YMMV -- which should go for everyone without saying.
User avatar
Doug Coulter
 
Posts: 3515
Joined: Wed Jul 14, 2010 7:05 pm
Location: Floyd county, VA, USA

Previous

Return to Embedded software

Who is online

Users browsing this forum: No registered users and 3 guests