1. Trang chủ
  2. » Công Nghệ Thông Tin

PROGRAMMING AND CUSTOMIZING THE PIC MICROCONTROLLER 3rd phần 10 ppt

123 680 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 123
Dung lượng 2,75 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

unsigned char EEByteWrite# Write a single byte to the Microchip EEPROMunsigned char control, I2C memory device.. unsigned char address, unsigned char dataunsigned int Read a single byte

Trang 1

TABLE F.8 MICROCHIP C18 C COMPILER LIBRARY FUNCTIONS (CONTINUED)

void Delay100TCYx(unsigned Delay in multiples of 100 instruction cycles.char units)

void Delay1KTCYx(unsigned Delay in multiples of 1,000 instruction cycles.char unit)

void Delay10KTCYx(unsigned Delay in multiples of 10,000 instruction cycles.char unit)

void DisablePullups(void) Disable PORTB’s internal pull-up resistors.unsigned char EEAckPolling(#) Generate acknowledge for Microchip EEPROM(unsigned char control) I2C memory devices

unsigned char EEByteWrite(#) Write a single byte to the Microchip EEPROM(unsigned char control, I2C memory device

unsigned char address, unsigned char data)unsigned int Read a single byte from the Microchip EEPROM EECurrentAddRead(#) I2C memory device

(unsigned char control)unsigned char EEPageWrite(#) Write a string of data to the Microchip EEPROM(unsigned char control, I2C memory device

unsigned char address,unsigned char * wrptr)unsigned int EERandomRead(#) Read a single byte from an arbitrary address(unsigned char control, from a Microchip EEPROM I2C memory unsigned char address) device

unsigned char Read a string of data lengthlong starting at anEESequentialRead(#) arbitrary address in a Microchip EEPROM I2C(unsigned char control, memory device

unsigned char address,unsigned char * rdptr,unsigned char length)void EnablePullups(void) Enable PORTB’s internal pull-up resistors.unsigned char getsI2C(#) Read a fixed-length string from the I2C(#) bus(unsigned char * rdptr, operating in master I2C mode

unsigned char length)void getsMwire(#) Read a string from the Microwire device.(unsigned char * rdptr,

unsigned char length)void getsSPI(#) Read a string from the SPI bus

(unsigned char * rdptr,unsigned char length)

(Continued )

Trang 2

TABLE F.8 MICROCHIP C18 C COMPILER LIBRARY FUNCTIONS (CONTINUED)

void getsUART(char * Read a string from the software USART.buffer, unsigned char len)

void gets(#)USART(char * Return a string from the specified USART.buffer, unsigned char

unsigned char Return nonzero if the character is an

isupper(unsigned char ch) upper-case alphabetic character

unsigned char Return nonzero if the character is a

isxdigit(unsigned char ch) hexadecimal digit

char isWDTTO(void) Return nonzero if reset was caused by

watchdog timer timeout

char isWDTWU(void) Return nonzero if reset was caused by wakeup

caused by the watchdog timer

char isWU(void) Return nonzero if reset was caused by sleep

wakeup through MCLR or interrupt

Trang 3

TABLE F.8 MICROCHIP C18 C COMPILER LIBRARY FUNCTIONS (CONTINUED)

char * itoa(int value, Convert a 16-bit signed integer to a string.char * string)

char * ltoa(long value, Convert a signed long integer to a string.char * string)

void NotAckI2C(#)(void) Generate I2C not acknowledge condition.void OpenADC(unsigned char Select the pin to connect the ADC and enableconfig, unsigned char config2) the ADC in the correct mode; see

documentation for config and config2 valuesfor different devices

void OpenCapture#(unsigned Configure and enable input capture #

char config)void OpenECapture# Configure and enable enhanced input

(unsigned char config) capture #

void OpenI2C(#) Configure the SSP(#) module

(unsigned char sync_mode,unsigned char slew)void OpenMwire(#) Configure the SSP(#) module for Microwire (unsigned char sync_mode) operations

void OpenPORTB Configure PORTB’s interrupts and internal(unsigned char config) pull-up resistors

void Enable PORTB interrupt request hardware.OpenPORTB#INT(unsigned

char config)void OpenEPWM1(char period) Configure the enhanced PWM channel

void OpenPWM#(char period) Configure the specified PWM channel

void OpenSPI(#) Initialize SSP(#) module for SPI

(unsigned char sync_mode, communications

unsigned char bus_mode)void OpenSWSPI(void) Configure the I/O pins of the software SPI.void OpenTimer# Configure and enable timer#

(unsigned char config)void OpenUART(void) Configure the I/O pins for the software USART.void Open(#)USART Configure and enable the specified USART.(unsigned char config,

unsigned int spbrg)

(Continued )

Trang 4

TABLE F.8 MICROCHIP C18 C COMPILER LIBRARY FUNCTIONS (CONTINUED)

void OpenXLCD Configure PIC MCU I/O pins and initialize LCD (unsigned char lcdtype) controller

unsigned char putsI2C(#) Write a data string to the I2C(#) bus in either(unsigned char * wrptr) master or slave mode

void putsSPI(#) Write a string to the SPI(#) device

(unsigned char * wrptr)

void putsUART(char * buffer) Write a string to the software USART

void puts(#)USART(char * Write ASCIIZ string of characters to the

void putrs(#)USART Write ASCIIZ string, defined in program memory,(const rom char * data) of characters to the specified USART

void putsXLCD(char * buffer) Write a string to LCD controller

int rand(void) Return a pseudorandom integer

int ReadADC(void) Return the 16-bit result of the ADC operation.unsigned char Return the address byte from the LCD

ReadAddrXLCD(void) controller

void ReadCapture# Read the result of a capture event from input

capture #

void ReadECapture# Read the result of a capture event from

enhanced input capture #

char ReadDataXLCD(void) Read a data byte from the LCD controller.unsigned char ReadI2C(#) Read a single byte from the I2C(#) bus; this (void) function also may be known as getcI2C(#).unsigned char ReadSPI(#) Read a single byte from the SPI(#) device; this (void) function also may be known as getcSPI(#).unsigned int ReadTimer# Read the value of Timer#

(void)

char Read(#)USART(void) Read a byte from the specified USART; this

function also may be known as getc(#)USART.char ReadUART(void) Read a byte from the software USART; this

function also may be known as getcUSART.unsigned char ReadWire(#) Read a single byte from the Microwire(#) (unsigned char high_byte, device; this function also may be known as unsigned char low_byte) getcMwire(#)

void RestartI2C(#)(void) Generate restart condition for the I2C(#) bus

Trang 5

TABLE F.8 MICROCHIP C18 C COMPILER LIBRARY FUNCTIONS (CONTINUED)

void SetCGRamAddr Specify the LCD character generator address.(unsigned char addr)

void SetChanADC Select the channel to be used for the ADC (unsigned char channel) operation

void SetCSSWSPI(void) Set the software SPI interface’s chip select pin.void SetDDRamAddr Specify the LCD display data area address.(unsigned char addr)

void SetOutputPWM# Set the PWM output configuration bits for ECCP.(unsigned char outputconfig,

unsigned char outputmode)void SetOutputEPWM1 Set the enhanced PWM output configuration (unsigned char outputconfig, bits for ECCP

unsigned char outputmode)void srand(unsigned int seed) Specify the starting seed for the randfunction.void StartI2C(#)(void) Generate a start condition for the I2C(#) bus.void StopI2C(#)(void) Generate stop operation for I2C(#) bus

char SWAckI2C(void) Generate an I2C bus acknowledge

char SWGetsI2C(unsigned char Read a string from the I2C bus

*rdptr, unsigned char length)char SWNotAckI2C(void) Generate an I2C bus not acknowledge

char SWPutsI2C(unsigned char Write a string to the I2C bus

*wrptr)char SWReadIC(void) Read a byte from the I2C bus; this function also

may be known as SWGetcI2C.void SWRestartI2C(void) Generate an I2C bus restart condition

void SWStartI2C(void) Generate an I2C bus start condition

void SWStopI2C(void) Generates an I2C bus stop condition

char Write a byte to the I2C bus; this function alsoSWWriteI2C(unsigned char may be known as SWPutcI2C

dataout)char tolower(char c) Convert upper-case character to lower case or

leave alone

char toupper(char c) Convert lower-case character to upper case or

leave alone

(Continued )

Trang 6

TABLE F.8 MICROCHIP C18 C COMPILER LIBRARY FUNCTIONS (CONTINUED )

void WriteCmdXLCD Write a command to the LCD controller.(unsigned char cmd)

void WritedataXLCD Write a byte to the LCD data area

(char data)

unsigned char WriteI2C(#) Write a single byte to the I2C(#) bus

(unsigned char data_out)

unsigned char WriteMwire(#) Write a single byte to the Microwire(#) device.(unsigned char data_out)

unsigned char WriteSPI(#) Write a single byte to the SPI(#) bus; this (unsigned char data_out) function also may be known as putcSPI(#).char WriteSWSPI(char data) Write a byte to the software SPI; this function

also may be known as putcSWSPI

void WriteTimer# Write a value to timer#

(unsigned int tmr_value)

void WriteUART(char data) Write a byte to the software USART; this

function also may be known as putcUSART.void Write(#)USART(char data) Write a byte to the specified USART; this

function also may be known as putc(#)USART

Trang 7

G

REUSE, RETURN, AND RECYCLE

The most efficient programmers I have ever met never write any new code Instead, they

have built up a library of code snippets that can be reused for different applications These snippets are usually well known by the programmer in terms of operation, parameters, and peculiarities In this appendix I have included a fair number of snippets and macros that I have found to be useful in developing my own PIC®microcontroller applications One of the biggest strengths of the PIC microcontroller is its ability to provide digi- tal input/output (I/O) efficiently in a number of different ways This flexibility can be used to implement I/O functions in the PIC microcontroller that weren’t designed in orig- inally As part of this appendix, I present a number of simple I/O “bit banging” func- tions that provide commonly required functions for the PIC microcontroller that can be included into your code along with the other snippets provided

The different features of the snippets can be modified for use in your applications with

a bit of cut and pasting in an editor You also should be able to find useful and clever code from other sources, including magazines, other books, and the Internet This recy- cling of code is why I have named this appendix after the conservationist’s “three R’s.” While I’m not a total “free software” advocate, I do believe that small pieces of useful code should be shared freely to help and encourage others In this spirit, I ask that if you find or develop a useful function out of a few instructions, please share it with other people If you don’t know how to do this, you can share the code with a list-server (such

as the PICList) community and let other people put it up on the Web for you I’ll always

be happy to look at what you have come up with

The most important piece of hardware in the PIC microcontroller used in applications

is the I/O pins When accessing the I/O pins, remember that there are three different ways

of reading and writing them The byte or port wide access method uses the movf and movwf instructions to read and write to the full I/O port Along with these instructions, the arithmetic and bitwise instructions can be used with the port as the result’s desti- nation to simplify and optimize the operations

The second method is to access the I/O port pins individually using the bcf, bsf, btfsc, and btfss instructions These instructions work well, but when using bcf

Copyright © 2008, 2002, 1997 by The McGraw-Hill Companies, Inc Click here for terms of use

Trang 8

and bsf, remember that they also can change the output value of a bit if the port bit is

at an unexpected or unwanted value

The last method is to use the rotate instructions rlf and rrf to pass data in and out

of the I/O ports using the STATUS register’s carry flag Using this method means that either bit 0 or bit 7 of the I/O port will be accessed, but this method is very effective and efficient To do this, the I/O situation has to be planned so that shifting data in and out does not affect the operation of the output values of the I/O port This is not hard to do—the most obvious way of doing it is to set the rest of the bits on a port as input With

a bit of planning, shifting can be done on mixed input and output very effectively

Useful Snippets

Here are a number of useful pieces of code that you can use in your applications In some cases, I am the originator of the code; in others, when I know who the originator was,

I have given him or her credit

NEGATING THE CONTENTS OF A FILE REGISTER

Converting the contents of a file register to its twos complement value without ing the w register is simply accomplished by

affect-comf Reg, f ; Invert the bits in the Register

incf Reg, f ; Add One to them to turn into 2’s

; Complement

This code should not be used on any special hardware control registers

NEGATING THE CONTENTS OF THE W REGISTER

If you have to negate the contents of the w register, you could use the preceding code (after saving the value in w into a register), or you could use for low-end devices (16C5x

or 12C5xx)

addwf Reg, w ; w = w + Reg

subwf Reg, w ; w = Reg - w

; w = Reg - ( w + Reg )

; w = -w

Any file register can be used for this code because its contents are never changed

In mid-range parts, the single instruction

sublw 0 ; w = 0 - w

could be used

Trang 9

INCREMENTING/DECREMENTING THE W REGISTER

Here are a couple of snippets that will allow you to increment and decrement the w ister without affecting any file registers if you don’t have addlw/sublw instructions (i.e., in the case of low-end processors) Reg can be any register that does not change during execution of the three instructions For low-end parts, any file register can be used because there is no danger of them being updated by an interrupt handler

reg-To increment:

xorlw 0x0FF ; Get 1s Complement of Number

addwf Reg, w ; w = Reg + (w^0x0FF)

subwf Reg, w ; w = Reg + ((Reg + (w^0x0FF))^0x0FF) + 1

; w = w + 1

To decrement, the instructions are rearranged:

subwf Reg, w ; w = Reg + (2^0x0FF) + 1

xorlw 0x0FF ; Get 1s Complement of Result

addwf Reg, w ; w = w - 1

ROTATING A BYTE IN PLACE

These two lines will rotate the contents of a file register in a low-end or mid-range PIC microcontroller without losing data in the carry flag When working with the PIC18, the rlncf and rrncf instructions can be used for the same function Rotates right and left can be implemented with this snippet Note that the carry flag is changed

rlf Register, w ; Load Carry with the high bit

rlf Register, f ; Shift over with high bit going low

COPY BITS FROM ONE REGISTER TO ANOTHER

Here is a fast way to save specific bits from one register into another:

movf Source, w

xorwf Destination, w

andlw B’xxxxxxxx’ ; Replace “x” with “1” to Copy the Bit

xorwf Destination, f

CONVERTING A NYBBLE TO ASCII

This is a question that comes up all the time when the contents of a byte are to be displayed/output The most obvious way of doing this is to use a table read:

NybbletoASCII:

addwf PCL, f ; Add the Contents of the Nybble to PCL/

dt “0123456789ABCDEF” ; return the ASCII as a Table Offset

Trang 10

However, I think a much better way of doing this is

NybbletoASCII: ; Convert a Nybble in “w” to ASCII

addlw 0x036 ; Add ‘0’ + 6 to Value

btfsc STATUS, DC ; If Digit Carry Set, then ‘A’ - ‘F’

addlw 7 ; Add Difference Between ‘9’ and ‘A’

addlw 0-6

return ; Return the ASCII of Digit in “w”

This method will take three instruction cycles longer than the previous code, but it requires 12 fewer instructions

CONVERTING AN ASCII BYTE TO A HEX NYBBLE

The code below is really a rearrangement of the preceding snippet Using the aspect that the high nybble of ASCII A to F is one greater than the high nybble of 0 to 9, a value is conditionally added to make the result 0x000 to 0x00F

ASCIItoNybble:

addlw 0x0C0 ; If “A” to “F”, Set the Carry Flag

btfss STATUS, C ; If Carry Set, then ‘A’ - ‘F’

addlw 7 ; Add Difference Between ‘9’ and ‘A’

addlw 9

return ; Return the ASCII of Digit in “w”

Note that ASCII characters other than 0 to 9 and A to F will result in an incorrect result.

USING T0CKI AS AN INTERRUPT SOURCE PIN

Some time ago, a question came up on the PICList asking if the timer input pin could

be used as an interrupt source pin The answer to this is yes—if the timer (and prescaler)

is set up so that the next transition will increment the timer and cause an interrupt Here’s some code to do it in a mid-range PIC microcontroller:

movlw B’11000000’ ; First Setup with Instruction Clock

; NOTE - Executing this Instruction

; after “decf” will Load the

; Synchronizer with a “1”

Trang 11

movlw 0x0A0 ; Enable TMR0 Overflow Interrupt

movwf INTCON ; Interrupt will occur when edge received

This code also can be used on a low-end PIC microcontroller to monitor when an input changes instead of continuously polling the input pin

DIVIDING BY 3

As much as you try to avoid it, sometimes you have to divide Here’s an algorithm from Andy Warren for dividing a positive value by 3 by knowing that divide by 3 can be rep- resented by the series

Value = int(Value / 2); // Quotient – 1/4

if (Value != 0) Quotient = Quotient - Value;

}}

return Quotient;

} // End DivideBy3

can be implemented in mid-range PIC microcontrollers as

Div3: ; Divide Contents of “w” by 3

movwf Dividend

clrf Quotient

Div3_Loop: ; Loop Until the Dividend == 0

Trang 12

bcf STATUS, C

rrf Dividend, f ; Dividend /2 (ie “x/2” in Series)

movf Dividend, w ; Is it Equal to Zero?

btfsc STATUS, Z

goto Div3_Done ; If it is, then Stop

addwf Quotient ; Add the Value to the Quotient

rrf Dividend, f ; Dividend /2 (ie “x/4” in Series)

SIXTEEN-BIT COUNTER WITH A CONSTANT LOOP DELAY

When I first started working with the PIC microcontroller, I thought I was exceedingly clever when I came up with

movlw HiDlay ; Load the Delay Values

Then Marc Heuler showed the code

movlw HiDlay ; Load the Delay Values

Trang 13

This loop takes five cycles to execute regardless of whether or not HiCount is to be decremented (and uses one less file register than my method above) The actual time delay is calculated using the 16-bit number from

Time Dlay = 16BitDlay * 5 ins/loop * 4 clocks/ins / clock frequency

This formula is a lot easier to use than having to (correctly) figure out the delay for the Dlay loop code given above this In the first edition of this book, it took three print- ings to get the first Dlay loop’s delay printed correctly

The first method is useful if you require approximately a 200,000-instruction-cycle delay To implement this, rather than loading HiDlay and LoDlay with constants, simply clear them before entering the two decfsz instruction loops.

SIXTEEN-BIT PULSE MEASUREMENT WITH FIVE-CYCLE DELAY

This is an improvement on the 16-bit delay code that I presented in the first edition

of this book PulseWidth is a 16-bit value that contains the number of times through the loop The code that measures the pulse width for a high pulse is

clrf PulseWidth ; Reset the Timer

DETECT A CHANGE IN A REGISTER

Bob Fehrenbach has passed on a number of his snippets for inclusion on my Web page, and they have shown up in this book The first detects the change in a bit and saves the new data for later execution This code can be used to detect changes in the I/O ports, timers, and other registers that can be updated externally to the software execution

movf Reg, w

andlw Mask ; Mask out unused bits

xorwf old, w ; Compare to previous value

btfsc STATUS, Z ; If Zero set, bits are the Same

goto no_change

xorwf old ; Bits are different, Store New

; pattern in “old”

Trang 14

TEST A BYTE WITHIN A RANGE

This is an algorithm that I continually reinvent (although I don’t think I’ve ever come

up with anything as efficient as Bob Fehrenbach’s routine):

movf Num, w

addlw 255 - hi_lim ; “Num” is equal to -hi_lim

addlw hi_lim - lo_lim + 1 ; “Num” is > 255 if it is above

btfsc STATUS, C ; the lo-lim

goto in_range

SWAP THE CONTENTS OF W WITH A REGISTER

I know that this one has been around forever, but it never hurts to be reminded of it and

to have it written down for easy access

xorwf Reg, f ; w = w, Reg = Reg ^ w

xorwf Reg, w ; w = w ^ (Reg ^ w), Reg = Reg ^ w

; w = Reg, Reg = Reg ^ wxorwf Reg, f ; w = Reg, Reg = Reg ^ w ^ Reg

; w = Reg, Reg = w

This algorithm for swapping values without a temporary value code can be mented in C as

imple-VariableA = imple-VariableA ^ VariableB; // A = A ^ B

VariableB = VariableB ^ VariableA; // B = B ^ A

// = B ^ (A ^ B)// = A

VariableA = VariableA ^ VariableB; // A = (A ^ B) ^ A

// = B

SWAP THE CONTENTS OF TWO REGISTERS

Using the algorithm from the preceding point, here’s a fast snippet to swap the contents

of two file registers:

movf X, w

subwf Y, w ; W = Y - X

addwf X, f ; X = X + (Y - X)

subwf Y, f ; Y = Y - (Y - X)

COMPARE AND SWAP IF Y < X

Here’s a compare and swap (uses the swap presented earlier):

Trang 15

movf X, w

subwf Y, w ; Is Y >= X?

btfsc STATUS, C ; If Carry Set, Yes

goto $ + 2 ; Don’t Swap

addwf X, f ; Else, X = X + (Y - X)

subwf Y, f ; Y = Y - (Y - X)

CONVERT ASCII TO UPPER CASE

This is a practical application of the preceding snippet I think that this subroutine demonstrates how the code works quite well

ToUpper:

addlw 255 - ‘z’ ; Get the High limit

addlw ‘z’ - ‘a’ + 1 ; Add Lower Limit to Set Carry

btfss STATUS, C ; If Carry Set, then Lower Case

addlw h’20’ ; Carry NOT Set, Restore Characteraddlw ‘A’ ; Add ‘A’ to restore the Character

return

COUNTING THE NUMBER OF 1S IN A BYTE

If you’re a regular on the PICList, you will know of Dmitry Kiryashov and his interest

in providing the most efficient routines possible for carrying out operations The code below is his optimization of the classic problem of counting the number of 1s in a byte

in 12 instructions/12 cycles

; (c) 1998 by Dmitry Kirashovrrf X, w ; “X” Contains Byte

andlw 0x55 ; -a-c-e-g

subwf X, f ; ABCDEFGH

; where AB=a+b, etc

; the same trick as in example_1movwf X

andlw 0x33 ; CD GH

addwf X, f

rrf X, f ; 0AB00EF0

; 00CD00GHaddwf X, f ; 0AB00EF0

; 0CD00GH0rrf X, f ; 0ABCD.0EFGH

swapf X, w

addwf X, w

andlw 0x0F ; Bit Count in “w”

Trang 16

GENERATING PARITY FOR A BYTE

The six instructions below (provided by John Payson) will calculate the even parity for

a byte At the end of the routine, bit 0 of X will have the even parity bit of the original number Even parity means that if all the 1s in the byte are summed along with the parity bit, an even number will be produced

KEEPING A VARIABLE WITHIN A RANGE

Sometimes when handling data you will have to keep integers within a range The four instructions below will make sure that the variable Temp always will be in the range of

SWAPPING BIT PAIRS

Another of Dmitry Kiryashov’s routines is this sequence of instructions for swapping bit pairs in a byte in five instructions/cycles

; (c) 1998 by Dmitry Kirashovmovwf X ; Save the Incoming Byte in

; a temporary register

; w = X = ABCDEFGHandlw 0x055 ; w = 0B0D0F0H

Trang 17

micro-the actual operations are pretty easy to implement Note that micro-these routines should not

be used for changing I/O port values because there may be an incorrect intermediate value If you are using this code for changing an I/O port or hardware control register bit, make sure that you read the register’s contents into the w register and use the andlw and iorlw instructions to change the bit before writing the new value back

to the register The intermediate value could initiate some hardware operation that is not desired

Setting a bit by ANDing two others together is accomplished by

bsf Result ; Assume the result is True

btfsc BitA ; If BitA != 1 then result is False

btfss BitB ; If BitB == 0 then result is False

bcf Result ; Result is False, Reset the Bit

To show how this operation could be accomplished on an I/O port bit, I have included the code

movf PORTB, w ; Store PORTB in “w” for “AND” Op’n

iorlw 1 << Result ; Assume the Result is True

btfsc BitA ; If BitA != 1 then result is False

btfss BitB ; If BitB == 0 then result is False

andlw 0x0FF ^ (1 << Result) ; Result is False, Reset the Bit

movwf PORTB ; Save the Result

ORing two bits together is similar to the AND operation, except that the result is expected to be false, and when either bit is set, the result is true

bcf Result ; Assume the result is False

btfss BitA ; If BitA != 0 then result is True

btfsc BitB ; If BitB == 0 then result is False

bsf Result ; Result is True, Set the Bit

The final operation is the NOT There are two ways of implementing this operation based on where the input value is relative to the output value If they are the same (i.e., the operation is to complement a specific bit), the code to be used is simply

movlw 1 << BitNumber ; Complement Specific Bit for “NOT”

xorwf BitRegister, f

If the bit is in another register, then the value stored is the complement of it:

bcf Result ; Assume that the Input Bit is Set

btfss Bit ; - If it is Set, then Result Correct

bsf Result ; Input Bit Reset, Set the Result

Trang 18

I want to introduce you some basic programming and I/O functions for low-end and range PIC microcontrollers When I first started writing this, I was trying to figure out how to best present them so that they could be used easily without you having to figure out how to modify code that is cut and pasted into your application The solution I came

mid-up with is to provide macros for these functions

The macros listed below are examples of quite advanced macros Along with providing different methods of interfacing, I also use the expected PIC microcontroller’s clock speed to calculate delays within the macros For the most part, they can be used with- out modification, but you should read through the accompanying text to make sure that you are aware of any issues with the macros on different devices

When this code was written, it was designed for low-end and mid-range PIC controllers You will find that for initializing interfaces, I have used the mid-range TRIS registers instead of the common tris instructions This was done because of the ease with which specific pins can access specific tris bits using the bsf and bcf instruc- tions For low-end PIC microcontrollers, you will have to come up with your own TRIS register values based on the I/O pins you are going to access

micro-All these macros, including the structured programming macros I presented earlier

in this book, are bundled up together in what I have immodestly called mykemacs.inc.

This file is located in the C:\PICDwnLd\Macros\mykemacs subdirectory of your PC’s PIC Microcontroller directory To use mykemacs.inc in your applications, copy the file into your PC’s C:\Program Files\Microchip\MPASM Suite folder

When mykemacs.inc is to be used in your application, include the file using the statement

include “mykemacs.inc”

When you want to use a function, you can select it from the invocation instructions given

in the following sections

SIMPLE DELAY

As I look through this book, I realize that I have not given you a good, simple delay that will work in the generic case The important point of the preceding sentence is the

term generic case—often a delay is needed that does not affect any other registers

or the PIC microcontroller’s STATUS bits The DlayMacro that I have provided below will allow you to delay any number of cycles (up to a limit of 777 cycles) and provides the calculations to a specific instruction cycle The macro is

DlayMacro Macro Cycles ; Delay Macro for Edges

variable i, TCycles, Value, TFlag

TCycles = Cycles

Value = 1 << 7

Trang 19

The basis of the delay code is the three-instruction-cycle loop:

decfsz DlayCount, f

goto $ - 1

Each time this loop executes, three cycles are taken up To set the initial DlayCount’ value, the number of cycles to delay is compared against the maximum value possible for a bit value in DlayCount For example, setting bit 2 of DlayCount will cause the loop code to execute four times If bit 6 is used, DlayCount will loop 64 times Instead of dividing the total number by 3 (which is the number of loops required) and then loading the w register with the value (which changes the w register) and then writing

to the DlayCount variable, I set the appropriate bit for the count in DlayCount.

Trang 20

This is a bit hard to understand (and pretty hard to see in the macro code above) To try to make the operation more obvious, I want to show some pseudocode that better illustrates how the delay value is calculated The output of the code is assumed to be the DlayCount variable with the appropriate bits set

TotalDelay = RequestedDelay;

DlayCount = 0; // Final Value to Delay

Value = 1 << 7; // Test Value

TFlag = 0;

for (i = 0; i < 8; i++ ) { // Repeat for all 8 Bits

if ((TFlag == 0) && (TotalDelay > (Value * 3))) {

DlayCount = DlayCount | Value; // Mark the Correct Bit Value

TotalDelay = TotalDelay - (Value * 3);

TFlag = 1; // Mark that Value Changes

} else if ((TFlag != 0) && (TotalDelay > ((Value * 3) + 1))) {DlayCount = DlayCount | Value; // Mark the Correct Bit Value

TotalDelay = TotalDelay - ((Value * 3) + 1);

Trang 21

This probably seems a bit cumbersome to work with, but the macro takes care of the calculation for you

After the loop has finished, the code will check for any left-over instruction cycles and insert goto $ + 1 or nop instructions as required so that the DlayMacro exe- cutes for exactly the specified number of instruction cycles When using this macro, you will have to declare DlayCount and make sure that it is accessible from within what- ever register banks are active when the macro is invoked and that it is cleared before its first use On exit from the DlayMacro code, DlayCount will always be zero To ensure that DlayCount is always zero, never change its value

You might be surprised to see that there is an initial check for the DlayMacro loop so that it only executes if the number of cycles to delay is greater than five The reason for this

is that a combination of goto $ + 1 and nop instructions can be implemented more efficiently than the delay loop The macro will produce the most efficient code that it can When you look at this code, you might be wondering just how efficient the code is.

By explicitly testing the value for a specific bit value and putting it into the delay, you might feel like I’m not being that efficient, especially in the case of making the loop

movlw InitialValue ; Delay = InitialValue * 3 + 1

which has a worst-case size of six instructions (the DlayMacro has a worst-case size

of 12 instructions) The only comment I can make to this is DlayMacro does not change the w register, and you will find that for many cases DlayMacro will end up produc- ing a comparable number of instructions

For example, invoking the macro

which is comparable in size with what the other code will do but does not change the

w register This feature of not changing the w register will be useful in the following example code In the worst case, the DlayMacro will produce 12 instructions (to the other method’s six) This happens in only one instance, and you will find that for most instruction cycle delays this macro produces code that is within the four to six instruc- tions of the other case

Trang 22

LCD INTERFACES

To simplify the work required for adding LCDs to your applications, I wanted to create

a series of four macros that would allow you to interface with LCDs in either low-end

or mid-range PIC microcontrollers using one of four subroutines By using a consistent interface, the same software can be used for a variety of different situations without requiring that the application code be changed—the vision was that just a new macro would needed to be invoked to access the LCD

The four LCD subroutines are

LCDPORTInit—Used in mid-range devices to define the I/O ports that interface to the LCD

LCDInit—Put the LCD into 2 line mode

LCDChar—Send an ASCII character to the LCD

worst-LCD8 DataPort, EPort, EPin, RSPort, RSPin, RWPort, RWPin, Frequency

is put in where DataPort is the 8-bit I/O port EPort and EPin are the E clock inition RSPort and RSPin are the RS LCD data type input RWPort and RWPin are the pins used to poll the LCD for data reply (and are essentially unused) Frequency is the PIC microcontroller operating speed and is used to calculate the delay values The only variable required for the LCD8 and LCD8Poll macros is the 8-bit variable Dlay.

def-This macro should work with any low-end or mid-range PIC microcontroller Note that the LCDPORTInit subroutine cannot be used with low-end PIC micro- controllers; to set up the I/O ports, you will have to create your own tris state- ments The macro is

LCD8 Macro DataPort, EPort, EPin, RSPort, RSPin, RWPort, RWPin,Freq

variable Dlay5Value, Dlay160Value, Dlay160Bit1 = -1, Dlay160Bit2 = -1variable BitCount = 0

variable Value = 128, Bit = 7

Dlay5Value = ((5007 * (Freq / 1000) / 4000) / 7) + 256

Trang 23

Dlay160Value = (163 * (Freq / 1000) / 4000) / 3

while (Bit >= 0) ; Find the Number of Bits and their

; positions in “Dlay160Value”

if ((Dlay160Value & Value) != 0)

if (Dlay160Bit1 == -1) ; Set the Upper Bit

Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1

Dlay160Bit2 = -1 ; Delete Second

else

Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1

endif

endif

Dlay5 ; Delay 5 msecs

movlw (Dlay5Value & 0x0FF00) >> 8

LCDPORTInit ; Initialize the I/O Ports

bsf STATUS, RP0 ; ONLY used by mid-range

Trang 25

by the LCD8 macro Instead of providing hard-coded delays in the application, the code polls the LCD to see if the operation is complete before continuing This is done by

Trang 26

putting the DataPort into input mode and then strobing the E bit (with RS reset and

RW set) and looking at bit 7 of the I/O port The macro code is

LCD8Poll Macro DataPort, EPort, EPin, RSPort, RSPin, RWPort, RWPin,Freq

variable Dlay5Value, Dlay160Value, Dlay160Bit1 = -1, Dlay160Bit2

if ((Dlay160Value & Value) != 0)

if (Dlay160Bit1 == -1) ; Set the Upper Bit

Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1

Dlay160Bit2 = -1 ; Delete Second

else

Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1

endif

endif

Dlay5 ; Delay 5 msecs

movlw (Dlay5Value & 0x0FF00) >> 8

Trang 27

LCDPORTInit ; Initialize the I/O Ports

bsf STATUS, RP0 ; ONLY used by mid-range

Trang 28

LCDInit ; Do the 8 Bit Initialization

call Dlay5 ; Wait 15 msecs

call Dlay5

call Dlay5

movlw 0x030

movwf DataPort

Trang 29

if (Freq > 8000000) ; Make Sure Proper Delay is In Place

bcf EPort, EPin ; Send the Reset Instruction

bsf Dlay, Dlay160Bit1 ; Delay 160 usecs

Trang 30

title “LCD8Poll - Test out the 8 Bit LCD Interface”

;

; This Code Sends the data to the LCD in 8 Bit mode with

; Polling and sends an ASCII “A” to the LCD

;

;

; Hardware Notes:

; The PIC is a 16C84 Running at 4 MHz

; Reset is tied directly to Vcc and PWRT is Enabled

; PortB is the Data Port

; RA0 is the “E” Bit

CBLOCK 0x00C ; Start Registers at End of the Values

Dlay ; 8 Bit Delay Variable

ENDC

PAGE

CONFIG _CP_OFF & _XT_OSC & _PWRTE_ON & _WDT_OFF

; Note that the WatchDog Timer is OFF

; Demo Code, Loop Forever Toggling PA0 (Flashing the LED)

org 0

call LCDPORTInit

Trang 31

call LCDInit ; Initialize the LCD Port

movlw “A” ; Put the “A” on the LCD

if ((DataBit != 0) && (DataBit != 4))

error “Invalid ‘DataBit’ Specification - Can only be ‘0’ or ‘4’”endif

while (Bit >= 0) ; Find the Number of Bits and their

; Positions in “Dlay160Value”

if ((Dlay160Value & Value) != 0)

if (Dlay160Bit1 == -1) ; Set the Upper Bit

Trang 32

Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1

Dlay160Bit2 = -1 ; Delete Second

else

Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1

endif

endif

Dlay5 ; Delay 5 msecs

movlw (Dlay5Value & 0x0FF00) >> 8

LCDPORTInit ; Initialize the I/O Ports

bsf STATUS, RP0 ; ONLY used by mid-range

Trang 34

LCDChar ; Send the Character to the LCD

movwf LCDTemp ; Save the Value

Trang 36

bsf EPort, EPin ; Send Another Reset Instruction

if (Freq > 8000000) ; Make Sure Proper Delay is In Place

bsf EPort, EPin ; Send the Third Reset Instruction

if (Freq > 8000000) ; Make Sure Proper Delay is In Place

Trang 37

The last LCD interface macro that I am providing you with is the two-wire LCD face This interface is quite a bit slower than the others that I have presented, but it uses the fewest PIC microcontroller I/O pins The circuit schematic for this interface was shown earlier in the book (as are the schematics for all the LCD interfaces) The LCD2 macro requires only the Dlay and LCDTemp variables

inter-LCD2 Macro ClockPort, ClockPin, DataPort, DataPin, Freq

variable Dlay5Value, Dlay160Value, Dlay160Bit1 = -1, Dlay160Bit2

if ((Dlay160Value & Value) != 0)

if (Dlay160Bit1 == -1) ; Set the Upper Bit

Dlay160Bit1 = Bit

Trang 38

Dlay160Bit1 = Dlay160Bit1 + 1 ; Shift Top up by 1

Dlay160Bit2 = -1 ; Delete Second

else

Dlay160Bit2 = Dlay160Bit2 + 1 ; Shift Bottom up by 1

endif

endif

Dlay5 ; Delay 5 msecs

movlw (Dlay5Value & 0x0FF00) >> 8

LCDPORTInit ; Initialize the I/O Ports

bsf STATUS, RP0 ; ONLY used by mid-range

Trang 39

movf LCDTemp, w ; Shift out the Upper 4 Bitsswapf LCDTemp, f

bsf LCDTemp, 5 ; Make LCDTemp Correct for Shiftingbcf LCDTemp, 4 ; This is “RS” Bit

bcf DataPort, DataPin ; Shift Out Each Bit

btfsc LCDTemp, 5 ; 5 is the Current MSB

bsf DataPort, DataPin ; Shift Out the Next Highest Bitbsf ClockPort, ClockPin

bcf ClockPort, ClockPin

rlf LCDTemp, f

decfsz Dlay, f

goto $ - 7

bsf DataPort, DataPin ; Latch in the Data

if (Freq > 8000000) ; Make Sure Proper Delay is In Place

movwf LCDTemp ; Shift out the Low Nybble

bsf Dlay, 2 ; Dlay = 6 for Shift Out

bsf Dlay, 1

bsf LCDTemp, 5 ; Make LCDTemp Correct for Shiftingbcf LCDTemp, 4 ; This is “RS” Bit

bcf DataPort, DataPin ; Shift Out Each Bit

btfsc LCDTemp, 5 ; 5 is the Current MSB

bsf DataPort, DataPin ; Shift Out the Next Highest Bitbsf ClockPort, ClockPin

bcf ClockPort, ClockPin

rlf LCDTemp, f

decfsz Dlay, f

goto $ - 7

bsf DataPort, DataPin ; Latch in the Data

if (Freq > 8000000) ; Make Sure Proper Delay is In Place

Trang 40

bsf Dlay, Dlay160Bit1 ; Delay 160 usecs

movwf Dlay ; w still equals 6

movf LCDTemp, w ; Shift out the Upper 4 Bitsswapf LCDTemp, f

bsf LCDTemp, 5 ; Make LCDTemp Correct for Shiftingbsf LCDTemp, 4 ; This is “RS” Bit

bcf DataPort, DataPin ; Shift Out Each Bit

btfsc LCDTemp, 5 ; 5 is the Current MSB

bsf DataPort, DataPin ; Shift Out the Next Highest Bitbsf ClockPort, ClockPin

bcf ClockPort, ClockPin

rlf LCDTemp, f

decfsz Dlay, f

goto $ - 7

bsf DataPort, DataPin ; Latch in the Data

if (Freq > 8000000) ; Make Sure Proper Delay is In Place

Ngày đăng: 12/08/2014, 23:21

TỪ KHÓA LIÊN QUAN