However, there is a theme which always comes up when this issue is being debated: C is not suitable for use on PICs such as the PIC10, PIC12, and PIC16 families. It's a rather baffling assertion, since clearly many people (myself included) are successfully developing commercial products using C on the smaller PICs. The arguments against C on PICs generally fall into one of these categories:
too much overheadfor use on such small devices. No one can deny that some things can be done more efficiently on a PIC in assembly language than they can be done in C. But generally, the claims of
too much overheadare talking about something much more insidious. Take, for example, this question asked on the PICList mailing list:
When you only have a three word hardware stack, I don't even think it's possible to use C, is it?There is a common belief among people who don't use C that vast quantities of the processor's resources are mysteriously consumed by
overhead. I've included an example C program below for the 10F200 in an attempt to disprove this belief.
compiled stack, which analyzes stack usage at compile time, and statically allocates memory for function arguments and auto variables. This works fine for most applications, but it does generally preclude the use of recursion and of re-entrant functions. Another feature missing from the PIC's architecture is a shift instruction, but even the larger,
C-friendlyPIC18 family doesn't have this, so it's not a handicap unique to the low-end PICs.
Below is an actual commercial application written for the PIC 10F200, which is the smallest PIC. This is a US$0.39, six-pin device with 256 words of program memory and 16 bytes of RAM. The application takes a pulse stream coming in on an input pin, changes its duty cycle, and sends the result to an output pin. It's an almost trivial application, but it serves to demonstrate the amount of overhead used by a C compiler on the tiniest of PICs.
void main(void)
{
for (;;) {
TRIS = 0x01;
OPTION = 0x47; /* GPIO wakeup; PU off; 256:1 PS */
if (GPWUF) { /* reset from pin change? */
GPWUF = 0;
if (PULSE_IN) { /* act on rising edge */
PULSE_OUT = 1;
TMR0 = NO_PR_TMR(60, TMR0_PS) - 1;
while (TMR0 != 0) /* busy wait for new high period */
;
}
}
PULSE_OUT = 0;
GPIO; /* clear change condition */
SLEEP(); /* wait for next edge */
/* a pin change resets us; we should never get here, but loop
* back to the top if we do.
* */
}
}
Here is the assembly language list file as generated by HI-TECH PICC
9.50PL2:
23 ;main.c: 16: void main(void)
24 ;main.c: 20: TRIS = 0x01;
25 002 C02 movlw 1
26 003 006 tris 6
27 ;main.c: 21: OPTION = 0x47;
28 004 C47 movlw 71
29 005 002 option
30 ;main.c: 23: if (GPWUF) {
31 006 7E3 btfss 3,7
32 007 A11 goto l7
33 ;main.c: 24: GPWUF = 0;
34 008 4E3 bcf 3,7
35 ;main.c: 25: if (GP0) {
36 009 706 btfss 6,0
37 00A A11 goto l7
38 ;main.c: 26: GP1 = 1;
39 00B 526 bsf 6,1
40 ;main.c: 27: TMR0 = (u16)(256 - (u16)((60) * (4
+ 000000 / 4 / 256 / 1000.) - 1 + 0.5)) - 1;
41 00C C16 movlw 22
42 00D 021 movwf 1 ;volatile
43 ;main.c: 29: while (TMR0 != 0)
44 00E l9
45 ;main.c: 30: ;
46 00E 201 movf 1,w ;volatile
47 00F 743 btfss 3,2
48 010 A0E goto l9
49 011 l7
50 ;main.c: 31: }
;main.c: 33: GP1 = 0;
51 011 426 bcf 6,1
52 ;main.c: 35: GPIO;
53 012 206 movf 6,w ;volatile
54 ;main.c: 37: asm("sleep");
55 013 003 sleep ;#
56 ;main.c: 42: }
57 014 A02 goto l4
The list file doesn't show the C startup code at addresses 0 and 1:
000 movwf OSCCAL
001 goto main
The movwf OSCCAL is necessary to calibrate the oscillator on
the 10F200, but the goto main is unnecessary overhead in this
application. Note that the compiler automatically excludes
standardC startup code such as data initialization, since this application does not require it. The entire application is 21 words in length; there are no vast amounts of missing code space or stack levels. Barring the one unnecessary instruction in the startup code, I doubt that an assembly language programmer could best this.