Print This Post Print This Post

Using a ‘broken’ PIC: What to do, when it dosn’t GOTO

Now my Dad drummed into me from a very early age not to use a broken tool, nor, for that matter, one which was blunt. He also showed me that if I didn’t have the proper tool for a job, then that job was compromised in some small way, and that methods should be sought to work around the problem. Finally, (this was 50’s Britain) he also showed me how to make a new tool from scratch, or modify an existing one to complete the job.

His (and mine at the time) was a largely practical and tangible world, with workpieces and tools made of metal, wood and leather. But I never forgot the lessons.

I recently purchased some 18F6620 PIC microcontrollers at the give-away price of £2.00 per pop. Given that these are currently being sold at around £8.00 by Farnell et al, I thought that this was a bargain. I still do, even after discovering that these 64-pin TQFP animals have a hardware bug where a GOTO located at the RESET vector is either ignored or spuriously interpreted, resulting in a jump into La-La-Land.

After experiencing the problem, and installing test-code, I read the device’s revision ID (it’s 3) and consulted the Microchip documentation. In: DS80129J (PIC18F6620/8620/6720/8720 Rev. A3 Silicon/Data Sheet Errata 2005) page 4, the terrible truth was revealed, and I quote: It has been observed that in certain Reset conditions, including power-up, the first GOTO instruction at address 0×0000 may not be executed. This occurrence is rare and affects very few applications.
Despite taking issue at the last statement above, I read on and discovered that the suggested workaround is very simple - place a NOP before the GOTO at 0×000000.
Fair enough, and indeed taking a local copy of the ‘C’ startup code and carrying out the small hack of inserting a NOP before the 1st GOTO, cures the problem. Job done? Well, not quite.

If you are using an ICSP programmer for your iterative PIC development, then the workaround above is fine. I dislike having this dongle attached to my hardware, and usually program the PIC with a bootloader - my first choice TinyBld, written by Claudiu Chiculita. And yes, you’ve already guessed, TinyBld will not work if it dosn’t find a valid GOTO at the 1st location in memory of your application.
Neither does the command-line clone TinyBld2, nor for that matter the latest bootloader offering from Microchip themselves, the AN1310 Serial Bootloader.
Well, I hear you saying, fix the bootloader!

In the case of the original TinyBld, the PC Application source is not available, however the TInyBld2 command-line clone is, and I downloaded and installed the Watcom-C compiler, created a project in the IDE, dumped in the source-files and it compiled 1st-time. I hacked a work-around and sure-enough, got myself back in business. I then spent some time removing all the scat that the Watcom compiler had decorated my environment variables with, because it’s installation had broken my Qt compiler. Still, job done, but I really prefer a GUI, rather than the command-line, so I turned next to the Microchip offering.

I notice that in the PIC firmware assembly-source, provision is made to specify a NOP for certain other PICs with the same hardware bug, and modifying the assembler-code similarly for the 18F6620, I burned-in the produced HEX and tried the bootloader. Nada, Nothing, Zilch. In fact, if the firmware source was written by the same guy who wrote the PC application, he suffers badly from some sort of cognitive dissonance problem, because the PC application blithely undoes any corrective measures you have put in place to ensure the bootloader firmware boots correctly, by scribbling the same unworthy nonsense at address 0×000000 whatever you do. Moving then to the source of the PC app, I hacked a workaround, but although the NOP and GOTO at vector 0×000000 were being retained correctly the bootloader still refused to work. Close inspection of the produced disassembly showed me why - it’s quite simply rubbish - produced from source that is a mass of assumptions and mis-perceptions, all of which are obfuscated by a sea of conditional-assembly directives. I re-wrote the code and now my 18F6620 bootloader works fine. At reset it jumps, as it should, straight into my application if present, otherwise it executes the bootloader code where it waits for a command on the serial port. When an application is already loaded at reset, invoking the bootloader is the simple invocation of the serial break, followed by a reset of the hardware and pressing the bootloader button - all as documented.

A carpenter has to accept, make provision for, and work around the knots in his timber - when are some tool programmers going to accept that not all PICs do ‘what it says on the tin’?

I’m not detailing the hacks I’ve done to TinyBld2, the PC and firmware sources for AN1310, but I’ve included links to zips of the hacked source-code for all three below. Those interested can do a ‘diff’ with the original sources.

One final point worth mentioning in respect to use of the 18f6620 with the TinyBld firmware and my serial setup (FTDI-USB TTL lead). I had to re-instate the commented-out lines shown below, otherwise TinyBld only booted a ‘tiny’ bit of my apps into the PIC!


writebyte
	movf POSTINC0,w			; put 1 byte
	movwf TABLAT
	tblwt+*
	decfsz counter_lo
	bra writebyte

	movlw	b'10000100'		; Setup writes
	rcall Write
	decfsz counter_hi
	bra writesloop
waitwre
	; !!! See my note in header - JWB 16th July 2010
    ; re-instate the following 2 lines of code
	btfsc EECON1,WR		;for eeprom writes (wait to finish write)
	bra waitwre			;no need: round trip time with PC bigger than 4ms

	bcf EECON1,WREN			;disable writes
	bra MainLoop

ziieroare					;CRC failed

The hacked PC Source for Microchip AN1310 Serial Bootloader is here: http://picprojects.info/Bootloaders/AN1310_SerialBoot.zip
The hacked firmware source for Microchip AN1310 Serial Bootloader is here: http://picprojects.info/Bootloaders/PIC18Bldr.zip
The hacked PC Source for TinyBld2 command-line Bootloader is here: http://picprojects.info/Bootloaders/Tinybld2_18f6620_hack.zip
The modified TinyBld source (as per modification above) for 18F6620 is here: http://picprojects.info/Bootloaders/Tinybld18f6620.zip

  • Share/Save/Bookmark
Print This Post Print This Post

16-bit Software SPI Implementation on PIC (PIC18F4620)

This article is part of a series describing communicating with the Sparkfun VS1103b BOB (Break-out board)
Please note: The project from which this article is taken is still ‘under development’. A full library of routines to drive the VS1103b will be made available when this is completed.
A link to a zip of a working set of the current software is included at the foot of the page.

The code below shows a re-written clone of Microchips WriteSWSPI routine, but now supporting 16-bit word transmit/receive and correcting the sloppy original which demonstrated glitches on the data line.
Using a PIC18F4620 with a 10Mhz crystal and the HSPLL operating, the pulse period is ~2.67us or a frequency of 374.5 Khz.
I’ve included commented-out sections showing calls to the Delay10TCYx delay routine (in delays.h) to indicate where these should be inserted if a lower frequency is required.

/********************************************************************
*       Function Name:  WriteIntSWSPI                               *
*       Return Value:   int: received data (throw away)             *
*       Parameters:     int output : data to transmit               *
*       Description:    Write/Read 16 bit to/from custom SPI port   *
*                       Re-written from Microchip original to       *
*                       support 16 bits                             *
*                       JWB 14th June 2010                          *
********************************************************************/
//
// JWB 12th May 2010 - NOTE!! Have to add 40 ck cycles delay to each part of waveform
//                     if using CMOS level-shift as CD40109 not responding to fast waveforms.
//
unsigned int WriteIntSWSPI(unsigned int command, unsigned int output)
{
		static unsigned long spi_val;
        char BitCount;                  // Bit counter

		spi_val = (long)command << 16;  // catenate command, reg addr and data to write into 32-bit value
		spi_val |= (unsigned long)output; 

        BitCount = 32;  // ! Important! VS1103b transaction must be an atomic 32-bits                   

        // Mode 0: This MODE is suitable for VS1103b
		// SCK idles low
		// Data output after falling edge of SCK
		// Data sampled before rising edge of SCK

		// ====================================================================
		// JWB : rather than making the port pin 0, THEN 1 if the data bit is 1
		// as in the Microchip-supplied code, test the putative outgoing bit
		// properly before writing it to the pin
		// ====================================================================

        // Set Dout to MSB of data output (confusingly called input!)
        if (spi_val & 0x80000000)
            SW_DOUT_PIN = 1;
		else
       		SW_DOUT_PIN = 0;                

        Nop();                          // Adjust for jitter
        Nop();
        do                              // Loop for bit count
        {
				// ========================================================================
			    // JWB Again, rather than writing to the STATUS register TWICE in one case
				// and ONCE in the other - test the bit BEFORE writing the STATUS carry bit
				// ========================================================================

                // Set the carry bit according to the Din pin
                if (SW_DIN_PIN)
                    STATUSbits.C = 1;
				else
					STATUSbits.C = 0;
                SW_SCK_PIN = 1;         // Set the SCK pin high
                _asm
                rlcf  spi_val,1,1         // rotate carry into least-sig bit of 'input'
				rlcf  spi_val+1,1,1
				rlcf  spi_val+2,1,1
				rlcf  spi_val+3,1,1
                _endasm
                Nop();                  // Produces a 50% duty cycle clock
                Nop();
                Nop();
                Nop();
                Nop();
                Nop();
                Nop();
                Nop();
                Nop();
                Nop();
                Nop();
					//		Delay10TCYx(4); // set to 40 (4 * 10) ck delays - CMOS 40109 does not responding to higher freq
                SW_SCK_PIN = 0;         // Set the SCK pin low
        		// Set Dout to MSB of data output
        		if (spi_val & 0x80000000)
            		SW_DOUT_PIN = 1;
				else
       				SW_DOUT_PIN = 0;
                BitCount--;             // Count iterations through loop for full bitcount
					//			Delay10TCYx(4); // set to 40 (4 * 10) ck delays - CMOS 40109 does not responding to higher freq
        } while (BitCount);
        return(spi_val & 0x0000ffff);                  // Return the received data
}

To control specific hardware, the above code should be suitably wrapped. A wrapper supporting writes to registers on the VLSI VS1103b MIDI synth DSP is shown below. This is taken from the routines supplied in the zip:

#include "sw_spi.h"
#include "VS1103b.h"

////////////////////////////////////////
//
// WriteSCI - Wait until the VS1103b
// signals it is read to receive, then
// Write a 16-bit word to its command
// interface, and/or read a 16-bit word back.
//
// Leaves VS1103b XCS & XDCS lines high
// (inactive)
// JWB 08 May 2010
////////////////////////////////////////
unsigned int WriteSCI (unsigned int command, unsigned int param)
{
	unsigned int result;
	while (SW_DREQ_PIN == 0); // wait 'til DREQ goes high

	SW_XDCS_PIN = 1;
    SW_XCS_PIN = 0; // select SCI

	result = WriteIntSWSPI(command, param);
    SW_XCS_PIN = 1; // de-select SCI
	return result;
}

The above is further wrapped, together with a read routine in a procedure called: SCI_Command, provided in the source-code.
An example of using the SCI_Command routine to read a register is given below (my calls to the serial port are for debug purposes only):


#include "sw_spi.h"
#include "VS1103b.h" // software SPI for VS1103b interface
.
.
.
.
             union SCI_REG_VAL dummy;
	result = SCI_Command(READ_OP, SCI_VOL, dummy);
	// output to serial port to observe result
	TxSerial ( ( result >> 8 ) & 0x00ff);
	TxSerial (result & 0x00ff);

Another example, this time writing LH and RH volume levels into the SCI_VOL register (at address 0xb)

#include "sw_spi.h"
#include "VS1103b.h" // software SPI for VS1103b interface
.
.
.
.
                     union SCI_REG_VAL sound_val;
                     sound_val.word_part = 0x407;
	        result = SCI_Command(WRITE_OP, SCI_VOL, sound_val);

The current source for the project is here. You should add these files to your project, and put the two includes shown above in your client code, as in my two examples above.

  • Share/Save/Bookmark
Print This Post Print This Post

PIC Tips & Tricks

If you haven’t got at least an Acrobat reader on your machine, then you won’t be able to see what is embedded below.

  • Share/Save/Bookmark
Print This Post Print This Post

Microchip Assembler Quick Reference


img021
Back to TOP

img0221
Back to TOP

img023
Back to TOP

img024
Back to TOP

img025
Back to TOP

img026
Back to TOP

img027
Back to TOP

  • Share/Save/Bookmark
Print This Post Print This Post

Asynchronous Events Example

This site is under construction - please be patient

Inside the PIC microcontroller, it’s a very ordered world. Almost everthing that happens is regulated by the internal clock. (usually crystal controlled) In our world unfortunately, things tend to happen without any sort of order or regularity. In fact when two events that concern us happen together, the reaction to some of these coincidences is to regard these as ’spooky’. For myself, I’ve never understood why this should so regarded, as there are six billion of us on this planet and therefore phenomena are occurring in a massively-parallel way - but I digress.

In our microcontroller these asynchronous events have to be handled in some way, and in the case of the PIC it is usual for dedicated hardware to handle the event and then inform us that it has occured, in other words generate an interrupt. A lot of beginners are afraid of interrupts, which is a shame because non-use will limit the functionality of your firmware very drastically, and anyway interrupts make handling asynchronicity very easy.

In the following example, use is made of a timer interrupt simply to demonstrate handling an asynchronous event. Purists may argue that this is not an asynchronous event, because it happens at strictly timed intervals. Yes & No. Although the timer events themselves are synchronous, the handling of these may be delayed or postponed altogether by the foreground program, so I believe it is a valid example.

Once again, I am dipping into an example supplied by Microchip for the 44-pin demo board, as it illustrates some of the points above. You will also discover that, as an exemplar, the example is deeply flawed. Because the program is a little bigger than the Hello World example I’ve split it up into sections, each of which I will comment on.

; *******************************************************************
; PICkit 2 Lesson 10 - Interrupts
;
; This shows how to configure and use the Timer 0 interrupt to
; trigger reading the A2D every time Timer 0 overflows.
;
; *******************************************************************
; *    See 44-pin Demo Board User's Guide for Lesson Information    *
; *******************************************************************

#include <p16F887.inc>

	__CONFIG    _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

	__CONFIG    _CONFIG2, _WRT_OFF & _BOR21V

     cblock     0x20
Delay1               ; Assign an address to label Delay1
Delay2
Display              ; define a variable to hold the diplay
Direction
LookingFor
T0Semaphore
     endc

; Flag Definitions
     cblock 0x70     ; put these up in unbanked RAM
W_Save
STATUS_Save
     endc


There are two interesting and important points to note from the above allocation of program variables. In the code that will handle an interrupt, we can’t assume which ram bank is currently in use and we want to keep executable statements to an absolute minimum. Using ram addresses of 0×70 and above for saving the controller STATUS and W registers is a good plan, because irrespective of the contents of the bank registers these addresses are always available. The bald comment ‘put these up in unbanked ram’ dosn’t really cut it as an explanation for a beginner. Although most of the variable names are self-explanatory ‘LookingFor’ begs the question looking for what? The comment ‘Assign an address to label Delay1′ is just plain unnecessary, and brings nothing to the feast. As an example for beginners, the two comments and variable naming noted both fail my ‘just good enough’ test.
 

     org 0
     goto      Start

     org 4
ISR:
     movwf     W_Save              ; Save context
     movf      STATUS,w
     movwf     STATUS_Save

;     btfsc     PIR1,T1IF           ; Uncomment to check Timer 1 (routine needed)
;     goto      ServiceTimer1
     btfsc     INTCON,T0IF
     goto      ServiceTimer0
;     btfsc     PIC1,ADIF           ; Uncomment to check the ADC (routine needed)
;     goto      ServiceADC
     goto      ExitISR          

ServiceTimer0:
     bcf       STATUS,RP0          ; Ensure ISR executes in Register Bank 0
     bcf       STATUS,RP1
     bcf       INTCON,T0IF         ; clear the interrupt flag. (must be done in software)
     bsf       T0Semaphore,0       ; signal the main routine that the Timer has expired
     bsf       ADCON0,GO_DONE      ; start conversion
     btfss     ADCON0,GO_DONE      ; this bit will change to zero when the conversion is complete
     goto      $-1
     comf      ADRESH,w            ; Form the 1's complement of ADresult
     movwf     TMR0                ; Also clears the prescaler
     goto      ExitISR

ExitISR:
     movf      STATUS_Save,w       ; Restore context
     movwf     STATUS
     swapf     W_Save,f            ; swapf doesn't affect Status bits, but MOVF would
     swapf     W_Save,w
     retfie


The above represents one way to handle an interrupt:

  1. The context (STATUS & W registers) is saved.
  2. Test the interrupt - if it is one we are interested in then service it.
  3. Restore the context and exit.

I have a few minor and one major criticism here, the first minor one is with the: goto $-1. In the days of programming on a teletype machine, being terse was a virtue, here it serves only to confuse a beginner. Adding a label and referring to that makes the code far more readable:

     bsf       ADCON0,GO_DONE      ; start conversion
testadcdone:
     btfss     ADCON0,GO_DONE      ; this bit will change to zero when the conversion is complete
     goto      testadcdone


Secondly the code sequence:

     comf      ADRESH,w           ; Form the 1's complement of ADresult
     movwf     TMR0                ; Also clears the prescaler
     goto      ExitISR

ExitISR:
     movf      STATUS_Save,w       ; Restore context


only makes sense if another (missing) service routine is interposed after the: goto ExitISR instruction - another possible cause for puzzlement by a beginner. A check of the disassembly will show that the operation has been left in by the assembler, and represents both a waste of time and space. However, it dosn’t stop the code working, and the author obviously felt it was just good enough! The final minor criticism is with the comments the statement: ; Form the 1’s complement of ADresult merely states the obvious. What should be said here is that the analogue voltage just converted, will affect the time until the next interrupt, also When assigned to the Timer0 module, all instructions writing to the TMR0 register will clear the prescaler.

I could halt our discussion here, as the code above satisfies the 1st rule - i.e. it works, but I won’t. My major criticism is both with the concept and implementation of the example. The author apparently sets out to do two things in the above routine:

  1. Demonstrate handling a timer interrupt.
  2. Invoking analog conversion then await it’s completion.

In an earlier paragraph I stated that in an interrupt routine, we should keep the instruction sequence as short as possible. In fact this service routine contains a loop - the goto just discussed. I’m going to be bold here - never ever use polling of this type inside an interrupt routine. A quick look at the datasheet for the PIC16F887 will show you that there is the facility to generate an interrupt when the ADC conversion completes, and use should have been made of this. (RTFM) In his effort to make the timer interrupt routine appear to be a little more useful, the author has shown you a really bad way to carry out an ADC conversion. Unfortunately, if you are a beginner, you may have regarded the above as a nifty way of reading the ADC at regular intervals - sadly it is not. An important lesson can be learned here - leaving out the label and using the sequence: goto $-1 has obfuscated the fact that the routine contains a loop. Such code, if offered up for peer review in a commercial software outfit, would be rejected out of hand.

The remainder of the code is relatively trivial and any other important features are discussed elsewhere. A corrected version of the above is given here:

  • Share/Save/Bookmark
Print This Post Print This Post

The PIC ‘Hello World’ Code Example

This site is under construction - please be patient

Sanity Testing Your First Prototype
Your test program to perform the above task can be either in assembly-language or ‘C’. It does two things - make a port pin an output, and set that output to a logical ‘1′. (usually +5volts) On some of my projects, this piece of code is actually left in and a LED/resistor combination installed permanently. In these cases I usually switch on the LED and then switch it off after a short delay. I call this LED a ‘reassurance’ or ‘comfort’ LED for obvious reasons.

The following assembler code example has been shamelessly lifted from Microchips PICKit 2 Debug Express software, and is set up to run on a 16F887 on the 44-pin Demo board supplied with the PICKit 2 Debug Express kit:

;*******************************************************************
; PICkit 2 Lesson 1 - "Hello World"
;
; This turns on LED 0 on the 44-Pin Demo Board.
;
; *******************************************************************
; *    See 44-pin Demo Board User's Guide for Lesson Information    *
; *******************************************************************
#include <p16F887.inc>
 __CONFIG    _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT
 __CONFIG    _CONFIG2, _WRT_OFF & _BOR21V
     org 0
Start:
     bsf     STATUS,RP0  ; select Register Bank 1
     bcf     TRISD,0     ; make IO Pin RD0 an output
     bcf     STATUS,RP0  ; back to Register Bank 0
     bsf     PORTD,0     ; turn on LED RD0 (DS0)
     goto     $          ; wait here
     end


This piece of code demonstrates a few simple rules, that if remembered, will stand you in good stead with your future projects.

  1. The first ‘rule’ of a program - it should work.
  2. Keep what is necessary, throw the rest away.
  3. Comment your code properly - make each comment add to understanding the code. If you don’t comment properly, your code (especially assembler) will become ‘write only’ with time. In other words at a later date you may have to unpick it because you’ve forgotten what it does.
  4. Set up the configuration correctly. This is essential.
  5. Initialise the resources you are going to use on the PIC.
  6. Perform operation(s) needed before execution of the program loop - in this case simply light up an LED.
  7. Loop forever - performing more operations if necessary (in this case none)

If you build the above program in MPLAB, then take a look at the disassembly listing, you won’t be surprised to see that there is a one-to-one correspondence between your source-code and the machine-code produced.

  0000    1683     BSF 0x3, 0x5       34:         bsf     STATUS,RP0  ; select Register Bank 1
  0001    1008     BCF 0x8, 0          35:         bcf     TRISD,0     ; make IO Pin RD0 an output
  0002    1283     BCF 0x3, 0x5       36:         bcf     STATUS,RP0  ; back to Register Bank 0
  0003    1408     BSF 0x8, 0          37:         bsf     PORTD,0     ; turn on LED RD0 (DS0)
  0004    2804     GOTO 0x4           38:         goto     $          ; wait here


Now take a look at a program that does exactly the same job in ‘C’. I wrote this using the free SourceBoost ‘C’ compiler:

//
// HiWorld.c
// JWB 2009
//
#include <system.h>

// Target PIC16F887 configuration words

#pragma DATA _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

#pragma DATA _CONFIG2, _WRT_OFF & _BOR21V

void main( void )
{

      // note that I've copied the assembler statements exactly

      set_bit (status, RP0);   // select register bank 1
      clear_bit (trisd, 0);    // make IO pin RD0 an output
      clear_bit (status, RP0); // back to register bank 0
      set_bit (portd, 0);      // turn on LED on RD0

	while( 1 ); // wait here forever

}


Now take a look at the machine-code produced by the ‘C’ compiler:

	ORG 0x00000000
0000  280B  	GOTO	_startup
	ORG 0x00000003
0003        main
0003        ; { main ; function begin
0003  1683  	BSF gbl_status,5
0004  1683  	BSF STATUS, RP0
0005  1303  	BCF STATUS, RP1
0006  1008  	BCF gbl_trisd,0
0007  1283  	BCF gbl_status,5
0008  1283  	BCF STATUS, RP0
0009  1408  	BSF gbl_portd,0
000A        label1
000A  280A  	GOTO	label1
000B        ; } main function end

	ORG 0x0000000B
000B        _startup
000B  118A  	BCF PCLATH,3
000C  120A  	BCF PCLATH,4
000D  2803  	GOTO	main
	ORG 0x00002007
2007  20C4  	DW 0x20C4
2008  3EFF  	DW 0x3EFF


Note the duplication of the machine-code for the setting and clearing of the STATUS register, bit 5 (RP0)
SourceBoost ‘C’ makes an assumption that you will ‘forget’ to switch banks if you are going to clear a bit in TRISD, so it does the switching for you, either side of your: clear_bit(trisd,0) statement. In other words, you don’t need to concern yourself about bank-switching - not in this case anyway, so try building the program again with the two bank-switch statements commented, and this is what you will get:

	ORG 0x00000000
0000  2809  	GOTO	_startup
	ORG 0x00000003
0003        main
0003        ; { main ; function begin
0003  1683  	BSF STATUS, RP0
0004  1303  	BCF STATUS, RP1
0005  1008  	BCF gbl_trisd,0
0006  1283  	BCF STATUS, RP0
0007  1408  	BSF gbl_portd,0
0008        label1
0008  2808  	GOTO	label1
0009        ; } main function end

	ORG 0x00000009
0009        _startup
0009  118A  	BCF PCLATH,3
000A  120A  	BCF PCLATH,4
000B  2803  	GOTO	main
	ORG 0x00002007
2007  20C4  	DW 0x20C4
2008  3EFF  	DW 0x3EFF

Hmm, better I hope you will agree, but still a little verbose compared with the assembler version. I would judge this as ‘just good enough’, the trade-off in bigger programs will be that they are easier to write in ‘C’, but will almost always be larger in terms of the memory they occupy.
So with the unwanted lines removed, the final ‘C’ program looks like this:

//
// HiWorld.c
// JWB 2009

#include  <system.h>

// Target PIC16F877 configuration words

#pragma DATA _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

#pragma DATA _CONFIG2, _WRT_OFF & _BOR21V

void main( void )
{
     clear_bit (trisd, 0);    // make IO pin RD0 an output
     set_bit (portd, 0);      // turn on LED on RD0

     while( 1 ); // wait here forever
}

blah blah

  • Share/Save/Bookmark