Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 5296

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

408 The Quintessential PIC Microcontroller

synonymously for bit 2. At this point the content of ADRES is the result of the conversion.

Rather than polling for completion, the end of conversion can be used to generate an interrupt. In particular if a conversion is to be done in the Sleep mode then this interrupt can be used to awaken the device.

INTerrupt CONtrol

File 0Bh/8Bh

0) (R/W

ADIF

1

08h File

ADCON0

7 6 5 4 3 2 1 0

GIE

ADIE

T0IE

INTE

RBIE

T0IF

INTF

RBIF

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W ?)

4

A/D Done interrupt

(a) PIC16C71 interrupt control

INTerrupt

CONtrol

File

0Bh/8Bh

7

6

5

4

3

2

1

0

GIE

PEIE

T0IE

INTE

RBIE

T0IF

INTF

RBIF

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W ?)

Peripheral Interrupt Register 1

File 0Ch

7 6 5 4 3 2 1 0

PSPIF

ADIF

RCIF

TXIF

SSPIF

CCP1IF

TMR2IF

TMR1IF

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

PSP

Parallel Slave Port

AD

A/D converter

RC

USART Receive

A/D Done interrupt

TX

USART Transmit

SSP

Synchronous Serial Port

CCP1 Capture/Compare 1

File 8Ch

TMR2 TiMeR 2

7

6

5

4

3

2

1

0

TMR1 TiMeR 1

PSPIE

ADIE

RCIE

TXIE

SSPIE

CCP1IE

TMR2IE

TMR1IE

CCP2

In PIR2/PIE2

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

(R/W 0)

Peripheral Interrupt Enable 1

(b) PIC16C73/4 interrupt control

Fig. 14.10 Interrupt control for the ADC module.

The procedure in obtaining a digitized outcome using the ADC interrupt is similar to the polling technique.

1.Configure ADC module (see Table 14.3).

2.Configure ADC interrupt.

• Set the ADIE local mask bit to enable the ADC module interrupt.


14. Take the Rough with the Smooth 409

Clear the ADIF flag bit.

Set the PEIE auxillary peripheral interrupt group mask bit (not the PIC16C71X line).

Set the GIE bit to globally enable all interrupts.

3.Wait for the required acquisition time, typically 12 µs.

4.Start conversion by setting the GO/DONE bit.

5.Continue ……until interrupted.

6.Interrupt service routine (ISR) checks ADIF bit and if set reads the ADRES register and clears the ADIF bit.

7.For next conversion go to Step 2 or 3 as required.

To illustrate the process consider an interrupt-driven equivalent to the subroutine of Program 14.1, where we wish to action the digitization of the analog voltage at channel n. There are three considerations for this situation.

Initialization code.

The conversion subroutine.

The interrupt service routine.

The initialization code depends a little on the target PIC device. The 18-pin footprint PIC16C71X line has a 4-channel ADC module as the counterpart to the PIC16F83/4’s Data EEPROM module. This can be seen by comparing the INTerrupt CONtrol register of the latter in Fig. 14.10(a) with the former shown in Fig. 7.4 on page 178. Here the ADC module’s Interrupt Enable mask bit ADIE replaces the EEIE mask as INTCON[6] and the ADC module’s Interrupt Flag is located as ADCON0[1] (see Fig. 14.8).

The PIC16C73/4 28/40-pin line has a much larger line-up of peripheral devices and use two pairs of additional registers to service these auxillary8 interrupt mask and flag bits. These Interrupt registers are called the Peripheral Interrupt and Peripheral Interrupt Enable registers. PIR1 and PIE1 are shown in Fig. 14.10(b). The former at File 0Ch holds eight interrupt flags, including ADIF at PIR1[6]. The latter at File 8Ch holds the corresponding mask bits. There is also a PIR2/PIE2 pair at File 0Dh:8Dh. Both these register pairs are enabled as a group by the PEIE (PEripheral Interrupt Enable) bit which takes the place of the single mask bit in INTCON[6] in the 18-pin footprint devices. Thus in enabling the ADC module for interrupt handling, the programmer has to enable both the ADIE local and the PEIE group masks.

In our example, we assume a PIC16C74 target device. Then our initialization code might be:

8That is, apart from the standard external INT, Port B Change and Timer 0 interrupt sources.

410 The Quintessential PIC Microcontroller

bsf

STATUS,RP0

;

Bank 1

clrf

ADCON1

;

All port inputs are analog

bsf

PIE1,ADIE

;

Enable ADC local interrupt (not PIC16C71)

bcf

STATUS,RP0

;

Back to Bank 0

movlw

b’10000001’ ;

AD clock /32, Ch0, no convert, ADON

movwf

ADCON0

bcf

PIR1,ADIF

;

ADCON0,ADIF for PIC16C71X

bsf

INTCON,PEIE ;

INTCON,ADIE for PIC16C71X

bsf

INTCON,GIE

;

Enable interrupt subsystem

The interrupt-driven version of our subroutine shown in Program 14.2 is virtually identical to Program 14.1 except that the polling loop at the exit point is eliminated and, of course, no value is returned. However, the 12 µs delay before commencing the process is still needed.

The ISR shown in Program 14.3 is entered when an interrupt (from any source) is generated. For simplicity we assume that there are no other sources of interrupt except from the ADC module. If this is the case, technically the check of the ADIF flag is redundant, although a spurious interrupt can never be ruled out. Where there are multiple sources of interrupt, then each flag can be tested in turn as shown on page 179.

In Program 14.3 a datum file register NEW is cleared to show the background program that the datum byte in ADRES has never been read. When the background routine fetches the digitized byte in ADRES it sets the flag byte NEW to a non-zero value. In a more sophisticated system a bu er of several digitized samples can he maintained by the ISR with NEW giving the number of samples in the bu er – see Example 14.1.

Program 14.2 Interrupt-driven subroutine to read channel n.

; ***********************************************************

; *

FUNCTION:

Analog/digital

conversion at channel n

*

; *

RESOURCE:

Subroutine

DELAY_12US, byte TEMP

*

;

*

ENTRY

:

Channel number

in W

*

;

*

EXIT

:

Conversion

initiated

*

; ***********************************************************

GET_ANALOG

andlw

b’0111’

; Isolate address bits

movwf

TEMP

; Channel number

bcf

STATUS,C

; Shift channel number left >>3

rlf

TEMP,f

rlf

TEMP,f

rlf

TEMP,w

; with outcome in W

bcf

ADCON0,CHS0

; Zero channel bits

bcf

ADCON0,CHS1

bcf

ADCON0,CHS2

addwf

ADCON0,f

call

DELAY_12US

; Wait 12us for things to stabilize

bsf

ADCON0,GO

; Start conversion

return


14. Take the Rough with the Smooth 411

Program 14.3 The ISR for our interrupt-driven ADC software.

; **********************************************************

; *

FUNCTION:

ISR to read the

ADC module at EOC

*

;

*

ENTRY

:

On an interrupt

*

;

*

EXIT

:

Set NEW to zero

to show new value

in ADRES

*

;**********************************************************

;First save context

A_D_ISR movwf

_work

;

Put

away W

swapf

STATUS,w

;

and

the Status register

movwf

_status

; ************************************************************

btfss

PIR1,ADIF

; Check;

has there been a

conversion

goto

ISR_EXIT

;

IF not

THEN false alarm

clrf

NEW

;

Show outside world a new outcome

bsf

PIR1,ADIF

; ************************************************************

ISR_EXIT swapf

_status,w ;

Untwist

the original Status reg

movwf

STATUS

swapf

_work,f

;

Get

the

original W reg back

swapf

_work,w

;

leaving

STATUS unchanged

retfie

;

and

return from interrupt

The core of the ISR is sandwiched by code to save and restore the Working and Status registers as described in Program 7.2 on page 183. As the PIR1 register is available in both Banks 0 & 1, the more complex context saving code illustated in Program 7.4 on page 191 is not required.

The ADC module can operate when the PIC is in its Sleep state. Indeed, a conversion during the (electrical) peace and quiet of sleep may well be preferrable than normal operation. When the ADC internal CR clock is used to sequence the analog module because the system clock is too low, then Sleep conversion is recommended as the CR and system clock are not synchronized and clock feed-through noise is a problem.

The following task list outlines the Sleep state conversion process.

The ADC clock source must be set to CR, ADCS1:0 = 11.

The ADIF flag must be cleared to prevent an immediate interrupt.

The ADIE mask bit must be set to enable the ADC interrupt to awaken the processor.

The GIE mask bit mask bit must be 0 unless the programmer wishes the processor to jump to an ISR when it awakens.

The GO/DONE bit in the ADCON0 register must be cleared to initialize the conversion followed immediately by the sleep instruction.

On awakening, the ADRES holds the digitized value.

Program 14.4 shows a Sleep state conversion, assuming that initialization code similar to the following has been executed.


412 The Quintessential PIC Microcontroller

include

"p16c71.inc"

bsf

STATUS,RP0

;

Bank 1

movlw

b’010’

;

RA1:0

set to AN1:0 analog with

movwf

ADCON1

;

PCFG1:0 = 10; rest of PortA digital

bcf

STATUS,RP0 ;

Back to Bank 0

movlw

b’11000001’;

RC clock, Ch0, no convert, ADIF = 0

movwf

ADCON0

;

and ADON

bcf

INTCON,ADIE;

Enable ADC interrupt in 16C71

bcf

INTCON,GIE ;

Disable interrupt subsystem

This code shows the PIC16C71 as the target processor with its two PFCG1:0 bits set up to configure RA1:0 as analog inputs. The internal CR ADC module’s oscillator has been chosen as the clock source and ADC Interrupt flag ADIF (in the iNTCON register for the PIC16C71X line) cleared. Setting ADIE enables the ADC module’s interrupt system. Where applicable the PEIE mask bit at INTCON[6] must also be set. If these are not enabled, then entering the Sleep state will turn o the ADC module in the normal Sleep manner, although the ADON bit reamins 1, and the conversion is aborted. This will also occur if the CR clock option was not choosen.

Program 14.4 Digitizing Channel 1 of a PIC16C71 device.

NEW_AD bsf

ADCON0,CHS0

; Select Channel 1

bcf

ADCON1,CHS1

; for conversion

call

DELAY_12US

; Wait for things to settle

bsf

ADCON0,GO

; Start conversion

sleep

; Go to sleep

; When A/D is

over, program

will continue here if GIE is 0

bcf

ADCON0,ADIF

; Clear interrupt flag and

movf

ADRES,w

; go get the value

return

Program 14.4 shows a conversion being implemented from Channel 1 (CHS1:0 = 01). After the channel has been set up and time allowed for settling9 the GO/DONE bit is cleared to start the conversion process. Where the CR oscillator option has been choosen, there is a 1-instruction cycle delay inserted to allow for the following sleep instruction to close down the processor.

After the PIC has awakened, the ADIF can be cleared and the digitized value read from the ADRES register.

As was the case for the other peripheral devices described in earlier chapters, C code may be used to interact with the ADC module. The

9If the main oscillator is low frequency, one or two nop instructions may be all that is needed. If the channel has not been changed and su cient time has elapsed since the last conversion, then no extra delay is needed.


14. Take the Rough with the Smooth 413

various configuration ports may be accessed in the same manner as at assembly level or, as in Program 14.5, the appropriate compiler built-in functions used to set up and manipulate the SPR control and status bits.

For our example we are going to code a 20 MHz PIC16C74 to act as a comparator in the manner of the Example 11.2 on page 291. Here we want to compare the parallel-input 8-bit word N at Port B with the analog input at Channel 1. Outputs at RC2:0 are to represent Analog Lower Than N, Equivalent and Higher Than N respectively. The comparator is to have a hysteresis of ±1 bit. That is, if previous comparisons showed Analog < N then the trigger level is N + 1 for equality. Similarily, on a downward trajectory the trigger level is decreased to N − 1 for equality.

The function compare() of Program 14.5 assumes that initialization code of the form:

#include

<16c74.h>

#use

delay(clock=20000000)

#define

PORT_B

*(unsigned int *)0x06

#define

PORT_C

*(unsigned int *)0x07

void compare(unsigned int delta); int main()

{

unsigned int hysteresis = 0; set_tris_c(0xF8); setup_adc(ADC_CLOCK_DIV_32); setup_adc_ports(RA0_RA1_RA3_ANALOG); set_adc_channel(1);

delay_us(12);

has already been executed.

The key internal functions used here are:

setup_adc(ADC_CLOCK_DIV_32);

This function configures bits ADCS1:0 in the ADCON0 to select the module’s clock source; here the processor oscillator/32. The script ADC_CLOCK_INTERNAL may be used to select the internal CR oscillator.

setup_adc_ports(RA0_RA1_RA3_ANALOG);

This configures bits PCFG2:0 in ADCON1 to select which port pins are analog, which are digital and if an external Vref is to be used. The script RA0_RA1_RA3_ANALOG indicates that port lines RA3 and RA1:0 are to be analog with an internal Vref with the rest being digital (PCFG2:0 = 100, see Table 14.3. The equivalent script using an external Vref at RA3 is RA0_RA1_ANALOG_RA3_REF. Scripts appropriate to any aprticular device are stored in the corresponding header file, in this case 16c74.h. All devices with an analog module have scripts ALL_ANALOG and NO_ANALOGS.

set_adc_channel(n);

Used to set up the channel number n in ADCON0, bits CHS2:0.

414 The Quintessential PIC Microcontroller

read_adc();

Activates GO/DONE in ADCON0 and returns with the digitized value from

ADRES when GO/DONE goes to 1.

delay_us(n);

Not explicitely an analog module function; this delays by n µs. The #use delay(clock=20000000) declaration is used by this function to give the delay appropriate for the processor clock frequency.

If the channel number remains constant and the compare() function is not called up at intervals less than the 12 µs settling time (the normal situation) then delay_us(12); can be omitted.

Program 14.5 A digital/analog comparator with hysteresis.

void compare(unsigned int delta)

{

unsigned int analog; analog = read_adc();

if(analog > PORT_B + delta) {PORT_C = 0x04; delta = 0xff;} if(analog == PORT_B) {PORT_C = 0x02;}

else {PORT_C = 0x01; delta = 1;} return delta;

}

The function compare() in Program 14.5 expects the value of the hysteresis, which here is either +1 or −1 (FFh). Internally this is called delta. After the ADC module is read, the digitized value analog is compared with the contents of Port B plus delta and the three Port C bits (RC2:0) set to their appropriate state.

At the same time as the comparison is resolved, delta will be updated to reflect the outcome (i.e. +1 if analog < (PORT_B + delta), −1 if analog > (PORT_B + delta)). The value delta is returned by the function to allow the caller function to update its variable hysteresis. Thus to activate the comparator outputs and also update hysteresis at the same time the caller might have a statement such as hysteresis = compare(hysteresis);. An alternative would be to define the variable hysteresis before the main function main() making it global; that is known to all functions. In this situation its value need not be passed by the caller back and forth to any appropriate function.

The declarations #use fast_io(n); (see page 296) have not been used here as the input(pin) and output_bit(pin,value) internal functions have not been used and the ports have been treated as simple memory bytes.

Conversion from a digital quantity to an analog equivalent is somewhat simpler than the converse and not so commonly required. Perhaps