High-Level Language Part 2 (PIC Microcontroller)

In our example the logic of the program is unaffected if the operator is pre-decrement or post-decrement. However, the compiler in the latter case adds an extra instruction to bring n down into the Working register before it is decremented in situ as it thinks that some computation involving the original value of n is to be performed. }

The while loop is repeated by going back to the loop test, which is located starting at 009h.

tmp964_thumb_thumb

return sum;

At the end of a function returning a long object the CCS compiler places the two bytes in the fixed GPRs File0D:Eh ordered low:high. Thus this code fragment simply copies the two bytes in File 12:13h; i.e. sum, into the return locations.

tmp965_thumb_thumb


Specifically the main() function is terminated by the sleep instruction.Normally a function is terminated by a return to the caller function.

The final machine code file is shown in Table 9.1(b) and gives a total length of only 23 instruction including the one-off environment settings. This is similar to a handcrafted assembly-level equivalent in length and therefore execution time.

C-level programs can be compiled and simulated in the IDE environment of Microchip’s MPLAB.The screen shot of Fig. 9.3 shows windows into both the C-level source code and the resulting assembly-level code. Although simulation is at the latter level the C code is highlighted in the appropriate place corresponding to the simulated assembly-level instruction. The Watch window shows the state of the two C objects int n (corresponding to the assembly label MAIN.N) and long sum (i.e. MAIN.SUM). The compiler generates the system symbol _RETURN_ to label the two GPRs File 0D:0Eh and this can be monitored in the normal way.

In more complicated expressions the placement of the — decrement operator (and the analogous ++ operator) before or after the object can affect the outcome. Where it appears before, such as in:

tmp966_thumb_thumb

then the value of n is first decremented before being added to 4. In the following case:

tmp967_thumb_thumb

Simulating our example program in MPLAB.

Fig. 9.3 Simulating our example program in MPLAB.

Using C to implement source code gives the programmer access to structures, operators and libraries appropriate to a modern high-level language. Nevertheless, any coding language of use in an embedded MPU/MCU target must be able to address locations in Data memory and specific bits in a datum. This enables the programmer to get into a SPR and monitor and change flags. In Part 3 of this topic we will use both assembly and C codings to interact with internal and external hardware. However, it will be useful to introduce the use of C in a ‘bit banging’ role here.

Consider a program fragment that has to check the state of the Timer 0 (TMR0) SPR at File 01 h and if it is decimal 24 zero it. This is how it might be done in C.

tmp969_thumb[2]

The directive #define (all C directives are prefixed #) replaces the name TIMER0 with the incantation5 ^(unsigned int *)0×01 whenever it is used in following text. Normally such directives appear at the beginning of the code and may be #inc1uded as a header file in the same manner as Table 8.4. The replacement is the number 0×01 – 0x is the C language prefix for hexadecimal. This constant is converted into the form of an address, or in C terminology a pointer, using the cast (unsigned int *). This states reading right to left "pointer to an unsigned int". In the CCS compiler an int is an 8-bit object; i.e. a byte, and the * operator means "pointer to". The leading * operator coming to the left of the object reads "contents of". Thus the expression if(TMR0 == 24) implements the test "if the contents of TMR0 is equivalent to 24 THEN DO the following. The == operator means "equivalent to" and returns true or false. Finally the statement TMR0 = 0 replaces the contents of TMR0 by 00h. The single = operator having the normal arithmetic function of assignment as opposed to the == comparison operation. A table of all C operators is given in topic C for reference.

The assembly-level code generated by the CCS compiler for the above C code is:

tmp970_thumb[2]

as we would expect.

The C language has the normal bitwise logic operators of AND (&), Inclusive-OR (|), eXclusive-OR (") and NOT ("), as specified in topic C. As we can access specific Data store addresses these operations can be used to invert, set or clear any bit or bits in any File register as described in topic 2. For example, if we wish as part of an interrupt handler function to test the INTF flag (bit 1) of the INTCON SPR then we could use the following code:

tmp971_thumb[2]

by ANDING INTCON with the binary pattern 11111101b (that is the complement of INTF, i.e. ~INTF) bit 1 will be cleared.

The assembly-level code emitted by the CCS compiler for this C code fragment is:

tmp972_thumb[2]

This example involved testing and setting a single bit and the compiler used the PIC’s and wf instruction. However, the PIC and most other MCUs have specific bit twiddling instruction which are more effective than general logic instructions, such as AND, at testing and altering a single bit. C compilers usually have non-standardized extensions to specify single bits and force the compilers to use these more efficient instructions. In the case of the CCS compiler the code fragment above can be written:

tmp973_thumb[2]

which defines the bit INTF as being bit 1 of File 0Bh. The assembly-level code emitted in this case is:

tmp974_thumb[2]

a somewhat more satisfactory outcome.

Examples

Example 9.1

Write a C function to return the square root of a positive 16-bit integer. The algorithm of Fig. 6.9 is to be used to implement the conversion.

Solution

Modifying the task list of Example 6.5 to suit the structure of the C while loop gives:

1. Zero the loop count

2. Set variable i (the magic number) to 1

3. WHILE i is less than or equal to the number

(a) Take i from Number

(b) Add 2 to i

(c) Increment the loop count

4. Return loop count as VNumber

The function heading gives it its name sqr_root and defines the parameters to be passed to the function and the outcome. The form unsigned int sqr_root(unsigned long number) declares that it will return an unsigned int value and one unsigned long int object will be passed to it, which will be known as number within the function. On this basis the coding of Program 9.2 directly implements the task list. As the square root of a 16-bit object will fit into an 8-bit byte, the loop count is declared unsigned int. The magic number i will however be twice (plus one) that of count and is therefore defined as a unsigned long object. At the same time as these internal function variables are defined they are given their initial values.

The while loop is repeated until the value of the reducing number drops below the increasing value of i , at which point any further subtraction will drop the outcome below zero. The value of the loop count is the square root and is returned to the caller at the end of the function.

Program 9.2 Coding the square root function.

Program 9.2 Coding the square root function.

Example 9.2

Using the C compiler that is available to you compile Example 9.1 and compare the size of the resulting machine code with that of the assembly-level implementation of Program 6.11.

Solution

The CCS C compiler version 2.6 generated executable code with 35 instructions. That of the original assembly program occupied 21 Program store locations. The ratio here is 1.6:1 or efficiency ratio of 60%.

Example 9.3

A K-type thermocouple is characterized by the equation:

tmp976_thumb[2]

where t is the temperature difference across the thermocouple in degrees Celsius and v is the generated emf spanning the range 0-52,398 nV, represented by a 14-bit unsigned binary number, for a temperature range of 0-1300°C. Write a C function which will take as its input parameter a 14-bit output from an analog to digital converter and return the integer temperature in Celsius measured by the thermocouple.

Solution

Our function, named thermocoup1e() in line 1 of Program 9.3, takes one unsigned long integer (16-bit) parameter, named emf and returns a similar 16-bit value. The internal variable temperature is defined in line 3 to be a floating-point object6 to cope with the complex fractional mathematics of line 5.

Program 9.3 Linearizing a K-type thermocouple.

Program 9.3 Linearizing a K-type thermocouple.

As we are told that only the 14 lower bits of emf have any meaning, line 4 ANDs the 16-bit object with 3FFFh (0x3FFF) to clear the upper two bits. The 0x prefix is C’s way of denoting hexadecimal.

Finally an unsigned long version of the float object temperature is made and returned in line 8.

The resulting executable code running on a mid-range PIC core takes 667 program words; or around 3 of the Program store of a PIC16F84 device. Because of the size penalty of using floating-point objects, fixed-point arithmetic is used wherever possible in embedded microcontroller implementations.

Example 9.4

We implemented a root mean square program to implement the mathematical relationshiptmp978_thumb[2]Write a C function to implement this relationship, where the two 8-bit objects num_1, num_2 are passed to the function which returns the 8-bit value rms.

Solution

The solution shown in Program 9.4 uses the internal unsigned long 16-bit variable sum to hold the addition of the two squared 8-bit variables. The squaring operation is simply implemented using the C multiplication operator * rather than coding a squaring function of the manner of Program 8.3.The function developed in Program 9.2 is used to generate the square root of the 16-bit sum object and is called from the function variance() line 6 with the return value being assigned to the variable sum as part of the call. In compiling the source code using the CCS C compiler, 100 machine-level instructions are needed to implement this problem. This compares to 62 instruction for the assembly-code version of topic 8. This gives an efficiency ratio of 62%.

Program 9.4 Generating the root-mean square value of two variables.

Program 9.4 Generating the root-mean square value of two variables.

Self-assessment questions

9.1 The coding of Program 9.2 can be simplified if it is observed that the variable i is always twice count plus one, so count is not needed. Instead, on return the 16-bit value i can be logic shifted once right and the 8-bit cast version of the remainder is the equivalent of the absent count. In shifting right the datum is divided by two and by throwing away the one that pops out, effectively subtracts by one (i is always odd and so its least significant bit is always 1 ). Try coding this alternative arrangement. The C operator to shift right by n places is >> n. If the datum is unsigned then the shift is a logic shift right. See Example 9.3 to see how to cast a datum to another type.

9.2 A PIC-based digital thermometer is to display temperatures between 0° C and 100° C. To be able to market the device to USA the thermometer is to have the option to display the temperature in Fahrenheit. Write a function for a PIC-based thermometer that is to convert Celsius integers to the equivalent Fahrenheit integer. The input is to be an unsigned int byte representing Celsius and the return Fahrenheit is also to be an unsigned int datum. The relationship is:

tmp980_thumb[2]

and the arithmetic should be done in 16-bit precision to avoid over-range.

9.3 A cold-weather indicator in an automobile dashboard display comprises three LEDs, which are connected to the lower three bits of Port A. Bit 2 of this location is connected to the red LED, which is to light if the Fahrenheit temperature is less than 30. Bit 1 is the yellow LED for temperatures below 40°F, and bit 0 is the green LED. Write a function, whose input is °F, that activates the appropriate LED.

Access to the LEDs through Port A at File 05h in C can be accomplished by placing the following line of code at the head of the program: You may assume that a logic 0 at the appropriate Port A pin

tmp981_thumb[2]tmp982_thumb[2]

9.4 Arrays of objects can be defined in C using the notation fred[n] where fred is the name of the array and n is the nth element. For example an array of ten values making up the decimal 7-segment patterns described in Fig. 6.6 can be defined and initialized as:

tmp983_thumb[2]

These ten values for 7_seg[0] through 7_seg[9] will be placed in ten sequential file registers. As most PICs have a severally limited number of GPRs and this example is a array of constants it makes more sense to place these ten constant bytes in Program ROM. Using the key word const in front of the array definition tells the compiler to initialize Program store locations instead.

tmp984_thumb[2]

As data in the Program store is not directly accessible in the low- and mid-range PIC Harvard architecture the compiler will actually place a series of retlw <byte> instructions in ROM as described in Table 6.4.

Based on the above array definition, code a C functionto return the 7-segment code equivalent of an unsigned int n passed to the function.

9.5 As part of a digital game a PIC is to drive an active-low 7-LED display to implement an electronic die. Our problem, outlined in Fig. 9.4, is to convert a ‘throw’ number n between 1 and 6 to the 7-bit display.

Whereupon the variable LED can be altered like any other variable. You will also need to use the if-else conditional construction:

The active-low die patterns.

Fig. 9.4 The active-low die patterns.

9.6 Driving the die requires seven parallel port lines and the electronic game requires to drive two die displays. By inspection of the patterns of Fig. 9.4 how could you reduce the requirement to four bits only?

9.7 As part of the same electronic game a function is to be written to return the next pseudo random number in the 127 sequence defined by the generator configuration of Fig. 3.12.The current number is to be passed to the function and the next number in the sequence returned. It may be assumed that this passed datum is never zero.

How could you modify the function to send the sequence of all 127 pseudo random numbers out of Port B beginning with the passed number?

9.8 To integrate the outcome to the pseudo random function with the die display we need to map the set of 127 numbers to the range one to six. Devise a modified function to implement the mapping. Hint: What simple mathematical operation would map any number to the range zero to five?

Code a C function converting n, which is passed to the function, and returning the appropriate code pattern.

Next post:

Previous post: