To Have and to Hold Part 2 (PIC Microcontroller)

The process of reading from the flash Program store is similar to that of the EEPROM Data module but using double Data and Address registers. However, we are interacting with the same Program store from which code is being fetched into the execution unit. In consideration of this dualism, two dummy nop instructions should follow the instruction setting the RD bit in EECON1[0]. This gives our Read task list for the flash Program store.

1. Copy the target cell’s address to EEADRH:EEADR on entry.

2. Set RD to 1 to initiate the Read cycle.

3. Execute two dummy nop instructions.

4. RD is immediately cleared automatically and the 14-bit target datum can be read from EEDATH:EEDATA as convenient.

The subroutine FLASH_GET of Program 15.4 implements this process assuming that the cell address has been placed in EEADRH:EEADR on entry.

Program 15.4 Reading a word from the flash Program store.

Program 15.4 Reading a word from the flash Program store.

The Write cycle also is virtually identical to its Data module counterpart but with the addition of a double-nop relaxation phase. This gives us our flash Write cycle task list:


1. Copy the target cell address to EEADRH:EEADR.

2. Set WREN in EECON1[2] to enable the Write process.

3. Disable all interrupts.

4. Send 55hto EECON2.

5. Send AAhto EECON2.

6. Set WR to initiate the Write cycle.

7. Execute two dummy nop instructions.

8. Clear WREN.

9. Enable interrupts.

10. Wait until WR returns to zero, signalling the completion of the Write cycle, and exit.

The subroutine FLASH_PUT in Program 15.5 assumes that the cell address is in EEADRH:EEADR and 14-bit datum is in EEDATH:EEDATA on entry.

For our example we will design a subroutine that will return the square of an integer between 0 and 100 in EEDATH:EEDATA. We could of course calculate this by multiplication, but for the purposes of this exercise we will implement this exercise as a look-up table located in flash Program store. As this is a table of constants we can load the data into flash memory at the same time as the rest of the program code.

In Program 15.6 the table is located at 300h in the Program store. The directive dw (Data Word) is similar to de but each datum in the comma separated list is 14-bits. For convenience the radix directive is used to specify constants by default are treated as decimal.

Program 15.5 Writing to flash Program memory.

Program 15.5 Writing to flash Program memory.

Directly following the table is the executable code. In this manner Program 15.6 is comparable to a C++ class where a program object comprises both data members and member functions (subroutines).

The subroutine itself builds up the table element nn address by placing the integer passed in W in EEADR and the constant 03h in EEADRH. This gives the double-byte address as 3nnh. Once this is done, the subroutine FLASH_GET retrieves the 14-bit datum from the table. The subroutine then moves both bytes from EEDATH:EEDATA and returns the datum in the two file registers SQRH:SQRL in Bank0. Unlike the PIC16F8X, general-purpose file registers are not reflected across the various banks, so each byte copied from EEPROM SPRs in Bank 2 needs switching to Bank0 once the byte has reached the Working register.

When the program has been burnt into flash memory by the external programmer the Program store in the area around 300h will look like Fig. 15.6.

Program 15.6 Squaring an integer.

Program 15.6 Squaring an integer.

Like the PIC16F8X,the PIC16F87X line has code protection fuses in its configuration word – as shown in Fig. 15.7. The primary function of code protection is to prevent the external programmer reading code from the Program store to give a measure of security against unauthorized peeking at the code. In the case of the PIC16F87X devices two code bits (duplicated as bits 13:12 and 5:4) in the configuration word in the special/test configuration area at 2007h give protection for all the Program store (00), the top half of the store (01), the top 256 bytes only (10) or no protection (11); the default situation. If protection is given to any area of memory then the external programmer cannot subsequently write data into anywhere in the Program store nor erase it. Thus protection should be disabled during prototyping! However, reading is only inhibited in protected areas.

TABLE

TABLE

tmpA-108_thumb[2]

View of the flash Program module showing the look-up table and subroutine SQUARE.

Fig. 15.6 View of the flash Program module showing the look-up table and subroutine SQUARE.

Code protection also affects internal Writes into the Program store using code such as in Program 15.5. Internal Writes can be made into unprotected areas of Program memory provided that the WRT fuse is 1; its default setting. Setting this fuse to 0 (_WRT_ENABLE_OFF) will disable internal Writes irrespective of the main code protection setting. Internal Reads are not affected by code protection. Program 15.6 shows the _config directive used to disable all code protection in Program memory; which is actually superfluous as this is the default situation.

Unlike the PIC16F8X, the PIC16F87X devices can also protect the EEPROM Data module from internal Writes. The CPD fuse (Code Protection Data module) if set to 0 will inhibit any changes in this data.

Configuration word for the PIC16F87X devices.

Fig. 15.7 Configuration word for the PIC16F87X devices.

Although the table has here been placed at an even 256-byte boundary for convenience, in practice it can be located anywhere in memory. In the general case the table offset nn will need to be added to the 14-bit address TABLE. SAQ 15.2 discusses how this address arithmetic could be done.

Examples

Example 15.1

The CCS compiler has the following built-in functions dealing with the EEPROM Data module:

tmpA111_thumb[2]

Reads a byte from the specified EEPROM address.

tmpA112_thumb[2]

Write the value to the specified address and returns only when the Write cycle has finished.

Write a C function to duplicate the odometer update implemented at assembler level in Program 15.3.

Solution

Like its assembly-level counterpart of Program 15.3, the function of Program 15.7 is divided into three phases. 1. This phase creates an array of three bytes named odometer[] which will act as a temporary store for the odometer count located in EEP-ROM. As the EEPROM Data module handles data in discrete byte packages, it is best to model the object in C in the same manner. The array is given the current reading by extracting the data one byte at a time from EEPROM locations 10:11:12b using the read_eeprom() function.

Program 15.7 C-based coding for the odometer.

Program 15.7 C-based coding for the odometer.

2. Once the 3-byte data is in the Data store it is incremented using an if-else tree:

(a) Increment low byte and check for zero. IF not zero THEN the overall addition is complete ELSE pass on carry to next byte.

(b) Increment the middle byte and check for zero. IF not zero THEN the overall addition is complete ELSE pass on carry to next byte.

(c) Increment most significant byte.

3. Finally each byte is written back into the EEPROM Data module using the write_eeprom() function.

Comparing the hand-coded assembly of Program 15.3 against the code generated from Program 15.7 gives 52 instructions against 91.

Example 15.2

A feasibility study is being undertaken in using a PIC-based Sauna controller. This is to monitor temperature and control heating and cooling units. There is also to be an over-temperature emergency alarm and shut-off.

One possibility is to use an 8-pin PIC with integral A/D conversion, such as the PIC12C672, together with an external temperature transducer. Someone has suggested that an efficient approach to this problem would be to use the variation in the internal Watchdog timer’s period with temperature as a cost effective, albeit crude, sensor.

Experimental data was collected using a sample of eight production devices from the same manufacturing lot, with a soak time of 30 minutes at each tested temperature and 500 uncalibrated periods averaged to produce the graph of Fig. 15.8.

The data presented in Fig. 15.8 is based on Microchip’s application note AN720 Measuring Temperature Using the Watch Dog Timer (WDT). The two loci give the maximum and minimum across the range of tested devices. The Watchdog period was measured in Timer 0 overflows counting an internal 4 MHz clock. The Watchdog timer used a 1:8 prescale ratio.

From this data it can be seen that there is a correlation between period and rising temperature. However, although the overall trend is predictable, different devices will have varying offsets and slopes. For example, within the eight devices tested here the scale factor (scalar) varies from 2.28-2.42 counts per degree Celsius. This will necessitate a calibration phase before the system is used. If the count at one temperature T0 and the scalar are known for any device, then for the specific case where a count COUNTn – COUNT0 is recorded:

tmpA114_thumb[2]Watchdog timer period versus temperature.

Fig. 15.8 Watchdog timer period versus temperature.

In order to calibrate these devices it has been decided to soak batches in a refrigerator at 0° C and save the 2-byte count in the EEPROM data module of a PIC16F83. The soak test is to be repeated in an oven at 30°C with the difference between this second count and the original value to be stored in a single EEPROM byte. Once this has been done, the device can be reprogrammed with the final running program overwriting the original calibrate program. This running program can then calculate temperature using the actual Watchdog period Count:

tmpA116_thumb[2]

where COUNT o is the 2-byte EEPROM datum showing the count at 0°C and Difference is the 1-byte EEPROM datum showing the change in count over 30°C.

Show how you could code the calibrate program to implement this specification.

Solution

Five tasks can be identified which must be undertaken at the end of a Watchdog period.

• Average the current Timer 0 roll-over count with the existing low-temperature (i.e. 0°C) count.

• Average the current Timer 0 roll-over count with the existing high-temperature (i.e. 30°C) count.

• Store the low-temperature count in EEPROM.

• Calculate the difference between low- and high-temperature count and store in EEPROM.

• Do nothing.

Port lines can be used to signal which of the first four active actions are to be actioned. For example; if RA0 is high then add the current Timer 0 rollover count to the existing 2-byte low-temperature count. Unless this is the first reading, divide by two to give an average. The complete batch of devices could have their RA0 pin held high for a few minutes 30 minutes after the refrigerator stabilizes at 0°C.

Bringing RA0 low and then RA2 high for a short time signals an EEPROM storage action. With all port lines low, no action is taken representing both time before temperature stabilization and after EEPROM programming.

The Timer 0 and Watchdog timers are initialized together with the count values on a once-only basis on Power-up reset. This will typically only occur when the devices are powered up when in the temperature bath. Subsequent resets will normally be due to Watchdog time-outs. The state of the Status register’s TO flag can be used to ascertain the source of reset.

Program 15.8 shows the routine used to initialize the timers and variables entered if TO is 1 on reset; that is on Power-up. Once this is done, the system enters an endless loop goto $ (the assembler replaces the label $ by the instruction’s address) which simply keeps going to itself!

Also shown is the ISR servicing a Timer 0 interrupt. This increments the double-byte variable ROLL_OVER:ROLL_OVER+1 and this is the count value read by the system on a Watchdog reset giving a numerical value for period.

Eventually the Watchdog timer will time-out and reset the processor. This time TO will be 0 and the routine labelled READING in Program 15.9 will be entered. This checks the state of each of the four RA3:0 pins in turn, executing one of the four listed tasks. If no pin is high, the program simply clears ROLL_OVER:ROLL_OVER+1 and Timer 0, the Watchdog timer is restarted and an endless loop entered. This READING_EXIT routine is also entered at the end of the four tasks.

The first two tasks are shown in Program 15.9. Here the 2-byte Timer 0 roll-over count is either added to the existing value LO_TEMP:LO_TEMP+1 or HI_TEMP:HI_TEMP+1 as appropriate and the outcome shifted once right to divide by two to give the average. As the count total is modest, 2-byte arithmetic is sufficient to avoid overflow. If this is repeated over a duration of several minutes an averaged value will result.

If this is the very first time a reading has been made then the divide by two operation is skipped and the flag variable FIRST_LO or FIRST_HI as appropriate is made non zero.

The core routine with respect to this topic is given in Program 15.10. If RA2 is 1 the 2-byte low-temperature count LO_TEMP:LO_TEMP+1 is copied into the bottom two bytes of the EEPROM Data module using the EE_PUT subroutine of Program 15.2.

Program 15.8 The Sauna Power-up reset sequence and ISR.

Program 15.8 The Sauna Power-up reset sequence and ISR.

If RA3 is 1 the difference between the 2-byte high and low temperatures is then calculated. With the data shown in Fig. 15.8 it can be seen that a 30°C difference will not exceed a byte’s worth of storage so only the lower byte of the subtraction is implemented. This single byte difference is then written into EEPROM in the normal way.

Program 15.9 Reading a new period count.

Program 15.9 Reading a new period count.

Program 15.10 Updating the Sauna EEPROM.

Program 15.10 Updating the Sauna EEPROM.

Self-assessment questions

15.1 Good program practice dictates that the datum written into Data EEP-ROM should be verified as the value that was intended to be written. Show how you could modify the EE_PUT subroutine of Program 15.2 to return a value – 1 in a file register if the action is not successful, otherwise zero.

15.2 In Program 15.6 we placed the look-up table at a 256-byte boundary in the Program store (specifically 300b) to simplify the computation of the 1-byte table index. Thus to look up table entry nn we simply place the address 3nnb in the EEADRH:EEADR register pair.

Placing program segments at user-defined addresses is never a good idea, as subsequential program alterations can cause code to overlap unless care is taken. Letting the assembler sort out locations of labels is much more reliable. However, in our case we would need to add nn to the address the assembler selects for the label TABLE. Unfortunately Program store addresses are 13-bits wide and PIC arithmetic is only 8-bit. Microchip compatible assemblers have the directives high and low to separate the upper and lower bytes parts of a label; eg. movlw low TABLE. Using these directives modify the subroutine SQUARE if the directive org 300h is removed.

15.3 Microchip-compatible assemblers have the directive da (DAta) which can be used to store strings of character codes in Program memory. For example:

tmpA-120_thumb[2]

which places the codes characters in quotes coded in 7-bit ASCII code packed two at a time in each 14-bit word followed by all zeros. The \n escape character means New Line – ASCII code 0Ah.

Assuming that this is done in a PIC16F87X device, write a subroutine called PDATA (Print DATA) to fetch each character from Program memory and transmit to a terminal using the subroutine PUTCHAR of Program 12.11.

15.4 A certain hotel security system is to use a PIC-based reprogrammable smart card for electronic guest room locks. On registration the card is to be charged up with the following details:

1. A 4-digit room number, eg. 1311.

2. Start data, eg. 13072000.

3. End date, eg. 15072000.

Assume that the PIC has an integral EEPROM Data module and communicates with the receptionist’s terminal via a serial input subroutine, such as described in Program 12.11. Data is coded in ASCII in the order outlined, preceded with the character STX, terminated by ETX and delimited by SP – see Table 1.1. Design a routine to interpret the data and store them in EEPROM.

Next post:

Previous post: