Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 5359
Скачиваний: 0
330 The Quintessential PIC Microcontroller
bus runs and few Slave devices this value of resistance can be increased by up to a factor of ten to reduce energy dissipation when an output pin is low.
In implementing the I2C timings, a PIC with a crystal above 3.2 MHz, with an execution time of less than 1.25 µs, may need to insert short delays between actions. For example, a 20 MHz crystal driven PIC implementing the instruction pair:
bcf |
TRISA,SCL |
|
; |
Drag Clock low by making pin an output to logic 0 |
|
bsf |
TRISA,SCL |
|
; |
Float clock high by making pin an input |
|
would give high and low durations of only 0.2 µs. Short delays are conveniently implemented using nop (No OPeration) instructions; each taking one instruction cycle (Fosc/4). For example, to give a nominally 400 kHz clock at 20 MHz we have:
bcf |
PORTA,SCL |
; Clock low |
nop |
; 0.2us |
|
nop |
; 0.4us |
|
nop |
; 0.6us |
|
nop |
; 0.82us |
|
nop |
; 1.0us |
|
nop |
; 1.2us |
|
bsf |
PORTA,SCL |
; Clock high |
nop |
; 1.6us |
|
nop |
; 1.8us |
|
nop |
; 2.0us |
|
nop |
; 2.2us |
|
nop |
; 2.4us |
|
nop |
; 2.6us |
|
Of course slower clock speeds require less nops but rather than tailor our subroutines for one particular crystal we will use the assembler macro called Delay_600, coded in Program 12.6, that will expand to the appropriate number of nops to give a nominal 600 ns (0.6 µs) delay, depending on the value of the constant XTAL defined by the programmer at the head of the source file.
For example to alter the coding of Program 12.7 to suit a 12 MHz crystal system then the one line #define XTAL 20 should be altered to #define XTAL 12 and the program reassembled.
The coding of Program 12.6 makes use of the conditional assembly directive if – endif. This is similar to the C language statement if(true){do this;} of page 249 in that all instructions down to the following endif are implemented if the argument of the if directive is true. For example, if((XTAL>6)&&(XTAL<=13)) states that if the constant XTAL is greater than 6 AND less than or equal to 13 then insert
12. One Bit at a Time 331
Program 12.6 A crystal frequency-independent short delay macro.
Delay_600 macro |
; Delays by nominally 0.6us |
if (XTAL <= 6) |
|
nop |
; One nop if XTAL is less than 6MHz |
endif |
|
if ((XTAL > 6) && (XTAL <= 13)) |
|
nop |
; Two nops delays if |
nop |
; XTAL is between 6 & 13MHz |
endif |
|
if (XTAL > 13) |
|
nop |
; Three nop delays if |
nop |
; XTAL is above 13MHz |
nop |
|
endif |
|
endm |
|
two nop instructions. At 13 MHz this will be approximately 600 ns. In practice, extra delays will be introduced by instructions toggling the bus lines and executing housekeeping tasks. Thus some fine tuning can be undertaken if maximum speed is a criterion.
Based on the macro of Program 12.6 and the following initialization code:
include |
"p16f84a.inc" |
||
#define |
XTAL 20 |
||
SCL |
equ |
0 |
|
SDA |
equ |
1 |
|
MAIN |
movlw |
TRISA |
; Set up the File Select Register |
movwf |
FSR |
; to point to TRISA |
|
bcf |
PORTA,SCL |
; Preset Clock & Data pins to 0 |
|
bcf |
PORTA,SDA |
; so that line can be dragged low |
|
bsf |
INDF,SCL |
; Float Clock line high |
|
bsf |
INDF,SDA |
; and the Data line to Idle state |
|
which assumes that we are using Port A bits 0 and 1 of a 20 MHz PIC16F84A to implement our SCL and SDA lines, we can code the three subroutines outlined in Program 12.7 to allow us to communicate with the I2C MAX518.
START
This subroutine releases both the SCL and SDA lines which are then pulled high to ensure the bus is in its Idle state for the minimum duration 1.3 µs tBUF. Bringing SDA low gives the characteristic Start \ , which is fol-
lowed by a 0.6 µs delay to implement tHD;STA (see Fig. 12.17) before the subroutine exits with both SCL and SDA low.
332 The Quintessential PIC Microcontroller
Program 12.7 Low-level I2C subroutines.
; *************************************************************
; * |
FUNCTION: |
Outputs the Start |
condition |
* |
||||
; |
* |
ENTRY |
: |
FSR points to the |
I2C port’s |
TRIS |
register |
* |
; |
* |
EXIT |
: |
Start condition and SCL, SDA |
pins |
low |
* |
|
; *************************************************************
START |
bsf |
INDF,SDA |
; Ensure that we start with the |
bsf |
INDF,SCL |
; Data and Clock lines pulled hi |
|
Delay_600 |
; 1.3us delay in Idle state |
||
Delay_600 |
|||
bcf |
INDF,SDA |
; Low-going edge on Data line |
|
Delay_600 |
; Wait for Slave to detect this |
||
bcf |
INDF,SCL |
; Exit with the Clock line low |
|
return |
|||
; *************************************************************
; * |
FUNCTION: |
Outputs the Stop condition |
* |
||
; |
* |
ENTRY |
: |
FSR points to the I2C port’s TRIS register |
* |
; |
* |
EXIT |
: |
Stop condition and SCL, SDA pins high (Idle) |
* |
; *************************************************************
STOP |
bcf |
INDF,SCL |
; Make sure that Clock line is low |
bcf |
INDF,SDA |
; and the Data line is low |
|
bsf |
INDF,SCL |
; Bring Clock line high |
|
Delay_600 |
; for a minimum of 0.6us |
||
bsf |
INDF,SDA |
; Rising edge on Data signals Stop |
|
return |
; including the return time |
||
;*************************************************************
;* FUNCTION: Transmits byte to Slave and monitors Acknowledge*
; * |
ENTRY |
: |
8-bit data |
to be TXed is in DATA_OUT |
* |
|||
; * |
RESOURCE: |
START and STOP subroutines |
* |
|||||
; |
* |
EXIT |
: |
Byte |
transmitted. ERROR |
is 01 IF no Ack received* |
||
; |
* |
EXIT |
: |
from |
Slave |
ELSE 00. SCL |
low |
* |
; *************************************************************
I2C_OUT bcf |
INDF,SCL |
; Make sure that Clock line is low |
clrf |
ERR |
; Start with no error |
movlw |
8 |
; Loop counter = 8 |
movwf |
COUNT |
|
I2C_OUT_LOOP |
||
bcf |
INDF,SDA |
; Data bit low? |
rlf |
DATA_OUT,f |
; Shift data left once into Carry |
btfsc |
STATUS,C |
; Is C 0 or 1 |
bsf |
INDF,SDA |
; IF the latter THEN make Data hi |
Delay_600 |
; Delay plus xtra instructions OK |
|
Delay_600 |
||
bsf |
INDF,SCL |
; Bring Clock pin high |
Delay_600 |
; for at least 0.6us |
|
bcf |
INDF,SCL |
; Bring Clock low |
decfsz |
COUNT,f |
; Decrement loop count |
goto |
I2C_OUT_LOOP |
; and repeat eight times |
; Now check Acknowledge from Slave |
||
bsf |
INDF,SDA |
; Release Data line |
Delay_600 |
; Keep Clock line low |
|
Delay_600 |
; long enough for Slave to respond |
|
bsf |
INDF,SCL |
; Bring Clock line high |
btfsc |
INDF,SDA |
; Check if Data is low from Slave |
incf |
ERR,f |
; IF not THEN ERROR1 |
bcf |
INDF,SCL |
; Now finish ACK by bringing Ck lo |
return |
||
12. One Bit at a Time 333
STOP
The Stop condition is implemented by ensuring that both SCL and SDA lines are low (which should be the case after an Acknowledge condition) and then releasing the SCL line which is then pulled high. After a 0.6 µs
delay to implement tSU;STO SDA is released to give the characteristic Stop / . The subroutine exits with both lines released and the bus Idling
in preparation for the next Start condition.
I2C_OUT
This subroutine clocks out the eight bits placed in DATA_OUT by the caller, MSB first, and then checks that the Slave has Acknowledged the transaction.
The first part of this process is implemented by repetitively shifting the datum in DATA_OUT and inspecting the Carry flag. SDA is set to mirror
C and the SCL line toggled to accord with the tLOW and tHIGH parameters illustrated in Fig. 12.17.
Once the loop count reaches zero, the Data line is released with SCL
low for the duration tLOW. SCL is then released high and the state of SDA, which should have been dragged low by the Slave, checked. If not low, the
No ACKnowledge (NACK) situation is returned with ERR = 01h; otherwise it will be zero.
Our use of errors here is very rudimentary. For instance, errors can also occur if some other device has locked either line low; that is the bus is busy.
We have not coded a Master-Receive I2C counterpart to subroutine I2C_OUT, as the MAX518 only demands a Master-Transmit data interchange. However, Program 12.14 gives the I2C_IN mirror.
As our example we will send the contents of File 20h to the MAX518 Channel 0 and then the contents of File 21h to Channel 1; at that point updating both DAC registers and hence simultaneously outputting the
analog equivalent of File 20h to pin Vout0 and File 21h to pin Vout1. We assume that both AD0 and AD1 pins are connected to Ground.
Our implementation will involve the transmission of a packet of five bytes of information sandwiched between a Stop and a Start condition.
1.Start condition.
2.Address byte: 01011000b
Slave address 01011(00), Write.
3.Command byte 1: 00000XX0b
No ReSeT, no Power Down, Channel 0.
4.Data byte 1: Contents of File 20h.
5.Command byte 2: 00000XX1b
No ReSeT, no Power Down, Channel 1.
6.Data byte 2: Contents of File 21h.
7.Stop condition.