Dson

1

Thanh ghi (Register): Thanh ghi được đặt trong PIC, nó có thể được ghi, đọc. Hãy tưởng tượng các thanh ghi giống như các mẩu giấy mà chúng ta có thể đọc hay viết thông tin lên nó. Hình bên dưới mô tả file thanh ghi (register file) được ánh xạ vào PIC16F84. PIC được chia làm 2 phần, Bank0 và Bank1. Bank1 dùng để điều khiển các hoạt động của PIC, ví dụ như nói cho nó biết những bit nào trên PortA là đi vào (Input) và những bit nào xuất ra (Output). Bank0 dùng để thao tác trên dữ liệu, ví dụ ta muốn làm cho bit nào đó trên PortA lên mức cao, đầu tiên ta ta phải chuyển đến Bank1 để set 1 bit của 1 chân cụ thể nào đó trên PortA trở thành Output, sau đó ta chuyển đến Bank0 và gởi mức 1 tới chân đó. Những thanh ghi thông thường nhất trên Bank1 mà chúng ta sẽ sử dụng là các thanh ghi STATUS, TRISA and TRISB. Đầu tiên chúng ta hãy quay vào Bank1, thanh ghi TRISA cho phép ta chọn chân nào đó trên PortA làm ngõ Output hay Input, thanh ghi TRISB cho phép ta chọn chân nào đó trên PortB làm ngõ Output hay Input, thanh ghi STATUS cho phép chọn sử dụng Bank0 hay Bank1. STATUS: Để thay đổi từ Bank0 sang Bank1 ta sử dụng thanh ghi trạng thái STATUS, set bit5 của thanh ghi trạng thái lên1 để chọn Bank1 hoặc xoá bit5 về 0 để chọn Bank0, thanh ghi STATUS có địa chỉ 03H. TRISA và TRISB: 2 thanh ghi TRISA and TRISB đặt tại địa chỉ 85H và 86H, để lập trình cho các chân trên 2 thanh ghi này thông thường người ta gởi mức 0 hay 1 đến các bit tương ứng trên thanh ghi, có thể làm điều này trong cả 2 dạng hoặc là bằng số binary (bin) hay hex. Dùng kiểu binary thì rõ ràng hơn là kiểu hex nhưng mà trông lượm thượm hơn !. Trên PortA ta có 5 chân tương ứng 5 bit, nếu muốn đặt 1 trong 5 chân này thành Output ta phải gởi 1 đến bit tương ứng với nó, những bít này có tên bit đúng chính xác với tên của nó, nói cách khác bit0 là RA0, bit1 là RA1, bit2 là RA2…. Hãy xem ví dụ: Nếu ta muốn set RA0, RA3 và RA4 thành Output và RA1, RA2 thành Inputs, ta phải gởi 00110 (=06h), nên nhớ bit thấp nằm bên phải, xem hình: Port A Pin Bit Number Binary RA4 4 0 RA3 3 0 RA2 2 1 RA1 1 1 RA0 0 0

Dson

2

Tương tự chúng ta cũng làm như vậy cho TRISB. PORTA và PORTB: Để làm cho 1 trong những chân Output lên mức cao ta gởi 1 đến bit tương ứng trên thanh ghi PORTA hoặc PORTB, giống như cách làm trên thanh ghi TRISA và TRISB, có thể kiểm tra lại trên từng chân của Port. Thanh ghi W: Thanh ghi W là là thanh ghi mụch đích chung mà có thể đặt lên nó bất kỳ giá trị nào ta muốn, khi gán cho thanh ghi W một giá trị nào đó, ta có thể cộng nó với 1 giá trị khác hoặc có thể copy nó (Mov). Nếu bạn gán 1 giá trị nào đó lên thanh ghi W thì nội dung trước đó của nó sẽ bị ghi đè lên. Xem ví dụ sử dụng PortA: Đầu tiên chúng ta cần chọn Bank0 hoặc Bank1 bằng cách set trên thanh ghi STATUS, địa chỉ của STATUS là 03H và hãy set bit5 của nó lên 1 theo cách sau: BSF 03h,5

BSF có nghĩa là Bit Set F, từ F nghĩa là chúng ta sẽ sử dụng một vị trí nào đó trong memory hoặc trong thanh ghi, 2 con số “03H” sau câu lệnh BSF nghĩa là địa chỉ của thanh ghi STATUS, con số “5” tức là bit5 của nó, như vậy ý nghĩa của câu lệnh trên là set bit5 của STATUS lên 1. Bây giờ chúng ta thao tác trong Bank1. MOVLW b'00110'

Ta đã đặt giá trị binary 00110 vào trong thanh ghi mụch đích chung W, chữ b có nghĩa là binary, dĩ nhiên ta cũng có thể viết lại trong dạng số hex, nó như sau: MOVLW 06h

MOVLW có nghĩa là là ‘Move Literal Value Into W’ tạm dịch là di chuyển giá trị của Literal vào thanh ghi W, để rõ ràng hơn ta có thể nói là “ đặt giá trị trực tiếp sau đây (06H) vào trong thanh ghi W “ Bây giờ ta tiếp tục đặt giá trị đó vào trong thanh ghi TRISA để thiết lập trạng thái cho Port: MOVWF 85h

Lệnh này có nghĩa là “MOV nội dung của W vào (thanh ghi có) địa chỉ 85h”, trong trường hợp này con trỏ địa chỉ sẽ trỏ tới TRISA, thanh ghi TRISA bây giờ chứa giá trị 00110, xem lại mô tả các câu lệnh bằng hình sau: Port A Pin Binary Input/Output RA4 0 O RA3 0 O RA2 1 I RA1 1 I RA0 0 O

Bây giờ chúng ta sẽ thiết lập các chân trên PORTA, hãy quay về Bank0 để thao tác trên các dữ liệu. BCF 03h,5

Dson

3

Lệnh BCF thì đối nghịch với BSF, nó có nghĩa là “ Bit Clear F” tạm dịch là xoá bit nào đó trong vùng memory hay trong thanh ghi nào đó, trong trường hợp này là thanh ghi STATUS (vì địa chỉ của nó là 03H) và lệnh này xoá bit5 của STATUS. Bên dưới là đoạn code: BSF MOVLW MOVWF BCF 03h,5 06h 85h 03h,5 ; vào Bank 1 ; Đặt giá trị 00110 vào W ; Move 00110 vào trong TRISA ; Quay trở về Bank 0

Hãy đọc kỹ đoạn code trên cho đến khi nào bạn hiểu nó đang làm cái gì. Ghi lên Port: Trong phần trên chúng ta đã nói đến làm thế nào để thiết lập các chân của Port trở thành Input hay Output, trong phần này ta sẽ nói tiếp làm sao có thể gởi data tới Port và trong phần kế tiếp chúng ta sẽ kết thúc với một đoạn code làm cho đèn Led chớp tắt với cả sơ đồ mạch để có thể hiểu rõ con Pic làm việc chính xác đến mức độ nào, đừng có thử compile và nạp đoạn code vào con Pic của bạn vì nó chỉ là ví dụ mà thôi. Đầu tiên hãy setup bit2 của Port A thành Output. Bsf Movlw Movwf bcf 03h,5 00h 85h 03h,5 ; Vào Bank 1 ; Đặt giá trị 00000 vào trong W ; Copy 00000 vào trong TRISA, tất ; cả các chân bây giờ sẽ trở ;thànhOutput ; Quay trở về Bank0

Đoạn code trên là những gì đã nói trong phần trước, chỉ khác là bây giờ ta set tất cả các chân của PortA trở thành Output bằng cách gởi giá trị 0 đến thanh ghi w (thanh ghi W là loại thanh ghi có 3 trạng thái tri-state register). Bây giờ những gì mà ta muốn con Pic phải làm là bật tất cả Led lên, để làm điều này ta phải gởi mức 1 đến các chân Led, hãy xem làm như thế nào đây: movlw 02h ; Ghi 02h vào thanh ghi W. nó là 00010 nếu ; viết theo dạng binary, như vậy nó đặt 0 vào ; bit 2 (chân 18) trong khi giữ các chân khác ở ; ;mức 0. ;Bây giờ copy nội dung của W (02H) vào ;PortA (địa chỉ là 05H).

movwf

05h

Con Led bây giờ đã bật on, chúng ta thử tắt nó xem: movlw movwf 00h 05h ; Ghi 00h vào thanh ghi W. nó là 00000 nếu ; viết theo dạng binary, như vậy nó đặt 0 vào ; tất cả các chân. ; Bây giờ copy nội dung của W ( 02H) vào ; PortA

Bây giờ Led đã bị tắt. Để làm cho led sáng, tắt liên tục chúng ta phải làm cho chương trình quay trở lại điểm bắt đầu bằng cách đặt nhãn cho chương trình và nói cho nó biết đó là điểm bắt đầu mà nó phải quay lại thực hiện lần nữa. Rất đơn giản, hãy đặt 1 cái nhãn có tên là START ngay tại điểm bắt đầu của đoạn code.

với lệnh EQU bạn có thể gán bất kỳ địa chỉ thanh ghi nào bằng 1 cái tên gợi nhớ hoặc gán một cái tên cho một hằng số trong đoạn chương trình. Bây giờ hãy xoá hết các ghi chú sau các câu lệnh. EQU đơn giản là thay một cái gì đó bằng một cái gì đó !. nó không phải là câu lệnh của con PIC mà nó là câu lệnh của assembler.Dson 4 Start movlw movwf movlw movwf goto Bây giờ hãy xem lại đoạn code: Bsf Movlw Movwf bcf Start movlw Movwf Movlw Movwf Goto 03h. while keeping the other pins to ‘0’ . This puts a . PortA . Write 02h to the W register. . . onto the PortA. Now move the contents of W (02h) . các giá trị hằng số phải được định nghĩa trước khi đặt vào chương trình và hãy nhớ phải luôn đặt chúng vào vị trí bắt đầu của chương trình. which is the address of the Tri-State register for . STATUS TRISA PORTA equ equ equ 03h 85h 05h .5 00h 85h 03h. which is the address of the STATUS register. this is 00010. các Port …. Bây giờ hãy thiếp lập các giá trị hằng số và đặt chúng vào chương trình.This assigns the word PORTA to 05h which is the . This assigns the word TRISA to the value of 85h. In binary . . bạn muốn hiểu được nó thì phải nhớ hết tất cả những địa chỉ của các thanh ghi. whose address is 05h . Goto where we say Start Chúng ta chỉ nhìn thấy toàn những con số. Hãy thử gán vài hằng số bằng những cái tên bạn sẽ thấy nó dể đọc đến như thế nào. address of Port A. bạn thử nhìn xem có dể dàng hiểu được đoạn code trên khi không có các dòng ghi chú: STATUS TRISA PORTA equ equ equ 03h 85h 05h . whose address is 05h . để giải quyết vấn đề này hãy gán cho các con số địa chỉ bằng 1 cái tên bằng lệnh EQU. Now move the contents of W (0h) onto . . 0’ on all pins. the Port A. Nhưng ngay cả khi bạn nhớ được tất cả thì một đoạn code ngắn nhất như trên cũng có thể làm bạn bối rối.5 02h 05h 00h 05h Start 02h 05h 00h 05h Start . this assigns the word STATUS to the value of 03h. Write 00h to the W register. which puts a ‘1’ on pin2 .

nếu ta sử dụng thạch anh 4MHz thì mỗi lệnh mất 4/4MHz hay 1us. Lệnh EQU gán 1 giá trị cho 1 thanh ghi. kế đến chúng ta phải xác định bộ đếm sẽ thực hiện đếm bao nhiêu. Bây giờ. Thật là quỷ quyệt. Delay Loops. bạn đừng lo lắng. số lớn nhất mà ta có thể dùng cho bộ đếm là 255 hoặc số hex là FFh. trong đoạn code trên. chúng ta không thể làm: COUNT EQU 85h Bởi vì 85h là vị trí của thanh ghi xuất (out) 3 trạng thái (Tri-State register) của PORTA.5 00h TRISA STATUS.5 02h PORTA 00h PORTA Start Hy vọng rằng bạn có thể hiểu được đoạn code trên ngay cả khi không có các ghi chú cho các câu lệnh. giả sử nếu bạn muốn COUNT có giá trị là 85h. không sao. nó sẽ trỏ tới vị trí thanh ghi mụch đích chung. Cái mà chúng ta phải làm là: Movlw Movwf 85h 08h . có phải không ?! . Đầu tiên chúng ta phải định nghĩa một hằng số dùng trong bộ đếm. Kế đến copy giá trị tới thanh ghi 08h. cái mà ta cần là làm cho khoảng thời gian giữa sáng và tắt của led kéo dài ra. ta sử dụng 5 lệnh như vậy mất 5us để thực thi hoàn toàn. giá trị zero báo cho biết dừng chương trình delay và sẽ tiếp tục thực thi lại nếu ta muốn. Nếu thử gán giá trị FFh cho COUNT ta sẽ nhận được thông báo lỗi khi compile chương trình bởi vì địa chỉ FFH đã được dùng cho mụch đích khác và chúng ta không thể truy cập tới nó. Mỗi lệnh thực thi mất 1 chu kỳ xung clock.Dson 5 Bsf movlw movwf bcf Start movlw Movwf movlw movwf goto STATUS. như vậy chúng ta phải gán một con số như thế nào cho hợp lệ ?. Cơ bản của 1 chương trình Delay là cho đếm ngược lại giá trị đã đặt trước đó. ví dụ 08h. Có một chút rắc rối về đoạn code chớp tắt đèn led mà ta đã xem bên trên. Đầu tiên đặt giá trị 85h vào thanh ghi W. và khi nó đến zero (0) thì ta cho dừng bộ đếm. nói cách khác là làm trễ (Delay). sẽ có cách giải quyết. nếu vậy thì điều mà chúng ta phải làm là MOV giá trị của bạn tới vị trí này. Nếu chúng ta gán COUNT cho 1 địa chỉ nào đó. Bây giờ tôi lại đang nghe bạn “khóc” rằng làm sao mà gán COUNT bằng một số nào đó có giá trị trùng với 1 trong các địa chỉ của các thanh ghi đã sử dụng?. nhưng mà giá trị mặc nhiên sau khi mở nguồn của những vị trí không dùng đến là FFh vì vậy nếu COUNT trỏ tới 08h thì nó sẽ có giá trị FFh. tạm thời chúng ta gọi là hằng số COUNT. khi chúng ta nói: COUNT equ 08h Thì COUNT sẽ tương đương với giá trị 85h. . điều này có nghĩa là bất kỳ con số nào mà ta gán cho COUNT thì COUNT sẽ có giá trị bằng với nội dung của địa chỉ đó. quá nhanh để mắt người có thể nhìn thấy đèn led chớp tắt trong khỏang thời gian ngắn ngủi như vậy.

kế đến đặt 1 cái nhãn ngay bên cạnh lệnh defsz. trong ví dụ này đơn vị là 1. trong ví dụ này nó sẽ thực thi lệnh GOTO để quay về lại điểm bắt đầu ( là LABEL). Như bạn đã thấy.Dson 6 Tiếp tục. nếu chúng ta muốn thời gian trễ lớn hơn chúng ta phải làm một vòng trễ kiểu khác.1 sẽ giảm giá trị của COUNT xuống 1 và lưu giá trị đã giảm trở vào trong COUNT. Bây giờ hãy đặt chúng vào trong chương trình và kết thúc chương trình. Mất nhiều lời để giải thích cho 1 lệnh đơn có phải không?.*****Set up the Constants**** STATUS TRISA PORTA COUNT1 COUNT2 equ equ equ equ equ 03h 85h 05h 08h 09h . nhưng mà cũng dể dàng để hiểu ra rằng có nhiều Loop hơn thì thời gian sẽ trễ lâu hơn. Nếu nó giảm tới zero chương trình sẽ bỏ qua lệnh kế tiếp để nhảy đến thực thi lệnh thứ 2. Lệnh decfsz COUNT.Switch to Bank 1 .Address of the STATUS register . lệnh đơn được dùng là: DECFSZ COUNT.). chúng ta đã làm cho chương trình lưu lại một thời gian trước khi nó tiếp tục làm việc gì đó tiếp theo. nó cũng sẽ kiểm tra xem COUNT = 0 chưa. nếu chưa nó sẽ cho chương trình thực thi lệnh kế tiếp. trong ví dụ này chương trình sẽ nhảy đến nơi có chữ ‘Carry on here’. nhớ thêm các ghi chú. : : : Điều mà chúng ta phải làm đầu tiên là gán hằng số COUNT = 255. COUNT equ 08h LABEL decfsz COUNT.1 Goto LABEL Carry on here. đầu tiên ta định nghĩa cho một hằng số COUNT equ 08h kế đến giảm COUNT xuống 1 cho đến khi nó = 0.Address of the tristate register for Port A .5 00h TRISA .First counter for our delay loops . chúng ta cần ít nhất là 2 Loop như trên nếu muốn nhìn thấy đèn Led chớp.1 Lệnh DECFSZ sẽ giảm thanh ghi ( trong trường hợp này là COUNT) xuống một đơn vị được điền sau dấu phẩy (. hãy xem cái gì xảy ra khi ta đặt nó vào chương trình. nếu COUNT = 0 thì nó sẽ cho chương trình bỏ qua lệnh kế tiếp và nhảy đến lệnh thứ 2. chỉ cần 1 lệnh đơn để làm việc này với sự hỗ trợ của lệnh GOTO và một cái nhãn.Second counter for our delay loops .Address of Port A .to Output. cái này gọi là vòng trễ (Delay loop).****Set up the Port**** bsf movlw movwf STATUS. .Set the Port A pins . .

bạn vừa mới viết xong 1 chương trình cho con PIC và đã làm cho nó hoạt động theo mong đợi.us to see it turned off .****Add another delay**** Loop2 decfsz Goto decfsz goto COUNT1. nhưng mà như vậy bạn vẫn chưa thể điều khiển được các Port I/O của nó.go back to Start and turn LED .and also just in case we miss .****Start of the delay loop 1**** Loop1 decfsz Goto Decfsz Goto COUNT1. Cho đến bây giờ bạn đã học được 7 trong số 35 lệnh của con PIC rồi đấy.****Delay finished.it into the w register and then .Needed by some compilers. Tại sao bạn không thử thay đổi Delay Loop cho nó nhanh hơn để biết giá trị Delay Loop tối thiểu mà mắt người có thể nhìn thấy đèn Led chớp tắt và thay đổi tốc độ chớp tắt của Led. ở đây có sẵn sơ đồ mạch cho bạn.This second loop keeps the .****End of the program**** end .the goto instruction.****Turn the LED on**** Start movlw movwf 02h PORTA .1 Loop2 .1 Loop2 COUNT2. Start . carry on.Turn the LED on by first putting . Trong phần tiếp theo chúng ta sẽ bàn đến cái gì gọi là thủ tục con (subroutine) để giúp chúng ta tiếp tục viết các chương trình nhỏ và thông thường nhất. dĩ nhiên là bạn sẽ muốn thử cho nó hoạt động. Xin chúc mừng.****Now go back to the start of the program goto .Subtract 1 from 255 .5 .1 Loop1 COUNT2.Switch back to Bank 0 . 255 times .on again Bạn có thể cpmpile chương trình này và nạp nó vào con PIC. it into the w register and then onthe Port .Dson 7 bcf STATUS. Thủ tục con (subroutine): .Subtract 1 from 255 . .255 to zero. Trong trường hợp này bạn cần phải thử thay đổi các giá trị hằng số COUNT khác nhau của mỗi Delay Loop. now turn the LED off**** movlw movwf 00h PORTA .1 Loop1 . ví dụ mỗi lần là 1 giây.If COUNT is zero.This delay counts down from . .LED turned off long enough for .Go back to the start of our loop.on the Port .Turn the LED off by first putting . . .

sau đó viết đoạn chương trình mà tôi muốn nó thực hiện.Address of Port A . tôi chọn tên ROUTINE. Cái thuận tiện của một thủ tục con là bạn có thể thay đổi giá trị bên trong nó sau mỗi lần thực thi. nếu không nó sẽ thực thi bất kỳ lệnh nào mà nó bắt gặp bất kể bạn có muốn hay không bởi vì con Pic không phân biệt được đâu là chương trình chính đâu là subroutine.First counter for our delay loops . Subroutine sẽ thực thi đoạn code bên trong nó cho đến khi nó gặp lệnh RETURN thì dừng lại.Address of the STATUS register . nhưng quan trọng nhất của một thủ tục con là bạn có thể tiết kiệm bộ nhớ chương trình chiếm đóng trong con Pic.1 LABEL . Vấn đề thứ hai rất quan trọng là bạn phải bảo đảm đặt subroutine sau lệnh RETURN của chương trình chính trừ phi trong chương trình chính bạn dùng lệnh GOTO để nhảy qua subroutine. đó là lý do tại sao người ta sử dụng subroutine để giảm độ dài của chương trình. tức là làm đi làm lại chức năng đó.Address of the tristate register for Port A . cuối cùng tôi kết thúc subroutine bằng lệnh RETURN. chương trình sẽ tự động quay về chương trình chính đúng tại nơi mà nó gọi subroutine và thực thi lệnh kế tiếp sau lệnh CALL. ví dụ bạn có thể thay đổi 10 lần gía trị của nó nếu cần thiết. Hãy xem lại đoạn chương trình chớp Led nhưng mà lần này ta sử dụng subroutine cho Delay Loop bạn sẽ thấy chương trình đơn giản đến mức nào và xem subroutine làm việc ra sao.****Set up the Port**** . ví dụ như Delay Loop. để mọi thứ ở cùng một nơi thì dể tìm kiến hơn.*****Set up the Constants**** STATUS TRISA PORTA COUNT1 COUNT2 equ equ equ equ equ 03h 85h 05h 08h 09h . Một thủ tục con được sử dụng khi mà bạn muốn thực thi một chức năng nào đó nhiều hơn 1 lần. Bạn có thể đặt subroutine này bất cứ nơi nào trong chương trình chính (MAIN) và khi muốn nó thực thi bạn chỉ cần gọi nó bằng lệnh CALL theo sau là tên của subroutine. tôi viết lại chương trình Led chớp tắt như phần trên. tuy nhiên tôi lại khuyên bạn nên khai báo mọi thứ tại đầu chương trình chính vì như bạn đã biết. Bạn có thể CALL nhiều lần để thực thi cùng một subroutine nếu bạn muốn.Dson 8 Một thủ tục con là một phần của một đoạn code hay một phần của một chương trình mà bạn có thể gọi nó thực thi bất kỳ lúc nào cần thiết. có phải không?. thứ nhất là bất kỳ hằng số nào cũng phải được khai báo trước khi bạn sử dụng nó nhưng mà trong trường hợp subroutine bạn có thể khai báo ngay trong bản thân nó hoặc ngay tại đầu chương trình chính như thông thường. .Second counter for our delay loops COUNT decfsz Goto equ 255 COUNT. Tuy nhiên có hai thứ mà bạn phải nghĩ đến. Hãy xem một subroutine sau: ROUTINE LABEL RETURN Đầu tiên chúng ta phải đặt cho subroutine một cái tên.

to Output.into the w register and then on the Port . có phải không Trong phần kế tiếp chúng ta sẽ tìm hiểu làm sao mà đọc được Port.Dson 9 bsf movlw movwf Bcf STATUS. and .1 Loop1 COUNT2.****Now go back to the start of the program goto Start .Switch back to Bank 0 02h . Trước tiên cần kết nối các chân Port tới mạch bên ngoài và theo dõi hoạt động tại đây.5 .see it turned off .This second loop keeps the LED . Đọc Port (Reading from the I/O Ports): Cho đến bây giờ bạn đã có thể ghi lên Port để làm cho Led chớp tắt.****Add a delay call Delay .turned off long enough for us to . Rõ ràng kích thước chương trình đã giảm đi nhiều khi sử dụng subroutine cho Delay Loop.****Delay finished.****Here is our Subroutine Delay Loop1 decfsz Goto Decfsz Goto Return COUNT1.Turn the LED off by first putting it .into the w register and then on the Port .****Add another delay**** call Delay .go back to Start and turn LED on again .1 Loop1 .also .Needed by some compilers.just in case we miss the goto instruction.Turn the LED on by first putting it PORTA . nhưng khi sử dụng subroutine nó chỉ còn cần 103byte.Switch to Bank 1 . Nếu không sử dụng subroutine chương trình chớp Led trên có thể cần đến 120byte bộ nhớ chương trình. ta chỉ cần gọi subroutine Delay.****End of the program**** end . . now turn the LED off**** movlw movwf 00h PORTA . còn tiếp theo chúng ta sẽ tìm cách đọc lại nội dung trên chân I/O của Port. Tại điểm kết thúc subroutine chương trình sẽ quay trở về ngay sau dòng lệnh CALL. . . nhưng mà bạn chỉ có 1024byte để chứa chương trình trong con Pic thì việc tiết kiệm được số byte như vậy quả là không uổng công nặn óc để làm subroutine.5 00h TRISA STATUS. mỗi lần ta muốn thực hiện Delay để làm cho Led ON hoặc cho Led Off. thật ra số byte chênh lệch như vậy cũng không phải là vấn đề quan trọng lắm.****Turn the LED on**** Start movlw movwf .Set the Port A pins .

nó có nghĩa là làm động tác thử xem 1 bit được chỉ định trên thanh ghi có = 1 hay không.Address of Port A .Address of the STATUS register . hãy xem cái này: Code here : BTFSS PortA.. quá đơn giản !. nhưng mà nếu đóng 1 cái Switch nào đó thì đèn Led sẽ chớp chậm hơn ½.Switch to Bank 1 . chúng gởi 0 vào thanh ghi TrisA và để nó trở thành Input ta phải gởi 1 đến thanh ghi TrisA. .5 . để làm được điều này ta sử dụng lệnh BTFSC và lệnh BTFSS.Switch back to Bank 0 Bây giờ chúng ta đặt bit0 của PortA trở thành Input.Switch to Bank 1 Để gán cho Port trở thành Output. Lệnh BTFSC có nghĩa là làm động tác thử xem 1 bit được chỉ định trên thanh ghi có = 0 hay không. cái mà ta phải làm bây giờ là kiểm tra lại xem chân này đang ở mức cao hay thấp (mức1 hay mức 0). nếu là 0 thì bỏ qua lệnh kế tiếp. Movlw Movwf Bcf 01h TRISA STATUS. Chúng ta sẽ sử dụng lệnh nào?. Chúng ta sử dụng cùng một mạch giống như phần trên nhưng mà thêm một cái Switch có một đầu nối vào chân RA0 của con Pic còn đầu kia mắc lên nguồn.0 Goto start Carry on here : : Chương trình sẽ chỉ nhảy đến dòng “Carry on here” nếu bit0 của PortA = 1.to Input. .****Set up the Port**** bsf STATUS.5 .Address of the tristate register for Port A . cái này còn tuỳ thuộc vào bạn mong đợi chương trình đọc được cái gì trên Port.bit 1to Output. Bạn hoàn toàn có thể tự làm được mà.Dson 10 Nếu bạn còn nhớ những thứ đã nói đến trong các phần trước. Lệnh BTFSS thì ngược lại.Set the Port A pins . Ví dụ: Nếu bạn đang mong đợi ngõ Input là 1 thì hãy dùng lệnh BTFSS. để setup I/O Port chúng ta phải chuyển từ Bank0 sang Bank1. hãy làm cái này trước: STATUS TRISA PORTA Bsf equ 03h equ 85h equ 05h STATUS.Set the Port A pins: .*****Set up the Constants**** STATUS equ 03h TRISA equ 85h PORTA equ 05h COUNT1 equ 08h COUNT2 equ 09h .Address of Port A .Second counter for our delay loops .Address of the tristate register for Port A . nếu là 1 thì bỏ qua lệnh kế tiếp.5 movlw 01h movwf TRISA . . Bây giờ bạn hãy viết lại chương trình đèn Led chớp ở 1 tốc độ cố định.First counter for our delay loops .Address of the STATUS register . bit 0 to Input. đừng có nhìn vào đoạn Code bên dưới xem sao.

1 .Needed by some compilers.This second loop keeps the LED Goto Loop1 .a zero. nhan chậm đèn Led !.BIT 0.routine . and also .If is is a 1.0 call Delay .then add an extra delay routine .go back to Start and turn LED on again . thật phí phạm thời gian có phải không ?.****Check if the switch is still closed BTFSC PORTA. kế đến kiểm tra xem cái Switch có đóng không.into the w register and then on the Port .If is a 1. then add anextra delay . .5 .Get the value from PORT A .****Here is our Subroutine Delay Loop1 Decfsz COUNT1.0 . Call Delay . nó sẽ làm tương tự như vậy cho trường hợp Led Off.Switch back to Bank 0 . phải có cách gì đó làm cho hay hơn.****Check if the switch is closed BTFSC PORTA.****Now go back to the start of the program goto Start .turned off long enough for us to decfsz COUNT2.Turn the LED off by first putting it movwf PORTA .see it turned off goto Loop1 . thời gian Delay giống y như trước nhưng mà gọi subroutine thực thi 2 lần.just in case we miss the goto instruction. vì vậy cũng đừng có thất vọng nếu mà bạn đem khoe với những người thân trong gia đình rồi chỉ cho họ làm sao cho con Led chớp châm đi ….Get the value from PORT ABIT 0. toàn bộ những thứ mà bạn làm sẽ không gây ấn tượng cho bất kỳ ai không thích thú với lập trình cho vi xử lý. now turn the LED off**** movlw 00h .Turn the LED on by first putting it . đó là những kinh nghiệm xương máu của tôi !.Dson 11 Bcf STATUS.****Delay finished.****Add a delay call Delay . If it is a zero. Bây giờ bạn hãy compile chương trình và cho con Pic chạy thử. Nếu bạn theo sát từ đầu đến giờ thì bạn đã biết tổng cộng 10 trong số 35 lệnh của con Pic 16F84 rồi đấy và tất cả những thứ mà bạn biết chỉ đơn giản là làm cho con Led chớp tắt !. carry on as normal.****End of the program**** end .1 . return .carry on as normal.****Add another delay**** call Delay . If it is a zero . nếu nó đóng chương trình sẽ gọi Delay subroutine.****Turn the LED on**** Start movlw Movwf 02h PORTA . nhưng mà tôi có một lời cảnh cáo bạn rằng.into the w register and then on the Port . . Đầu tiên chương trình bật Led on.họ sẽ chỉ giả vờ ngạc nhiên thích thú mà thôi !. còn tôi thì nghĩ thật là phí phạm bộ nhớ của con Pic nếu phải viết chương trình dài như vậy chỉ để chớp tắt.

5 . nó mới thật sự là một chương trình làm đèn Led chớp tắt. đó là sử dụng lệnh XORF.1 Đầu tiên nạp vào W giá trị 02h sau đó thực hiện lệnh XORF cho data trên PortA với giá trị 1. movlw movwf movlw movlw 02h PORTA 00h PORTA Đầu tiên ta nạp vào thanh ghiW giá trị 02h.Address of the STATUS register . có đấy. chúng ta phải viết 2 lệnh MOV cho Led Off và 2 lệnh MOV cho Led on. Lệnh XORF thực hiện hàm XOR cho data chứa trong thanh ghi. đoạn code thứ hai là viết lại nhưng dùng lệnh XORF.Address of the tristate register for Port A . Ở giữa chương trình ta phải gọi subroutine để cho đèn chớp tắt. Như vậy để bật Led On/Off chúng ta chỉ cần 2 dòng Lệnh. nhanh chậm. Hãy xem mô tã lại những gì mà chúng ta đã nói: PORTA 00010 xorwf xorwf xorwf xorwf 00000 00010 00000 00010 Thật ra chúng ta không cần phải nạp mỗi lần cùng một giá trị vào trong thanh ghi W bởi vì có thể làm điều này ngay lúc bắt đầu chương trình. 1 lần Delay cho Led on và 1 lần cho Led Off. ta Nạp gía trị 00h vào thanh ghi W sau đó copy nó tới thanh ghi PortA. tại sao vậy?. Hãy xem 2 đoạn code mới. Có cách nào khác đơn giản hơn không ?. đoạn code thứ nhất viết theo kiểu như ban đầu.Switch to Bank 1 . .Second counter for our delay loops .Address of Port A . nếu hiện tại PortA có giá trị 1 thì nó sẽ thay đổi thành 0 còn nếu PortA đang là 0 sau khi lệnh XORF thực hiện nó sẽ trở thành 1. bởi vì khi mới cấp nguồn cho con Pic thì PortA mặc nhiên đã = 1 rồi chúng ta chỉ cần lật qua lật lại cho PortA =0 rồi =1 mà thôi. Sau đó ta gọi 2 lần Delay subroutine.****Set up the Port**** bsf STATUS. ngay cả khi ban đầu PortA = 0 thì chúng ta cũng sẽ vẫn làm như vậy. MOVLW XORWF 02h PORTA. Để tắt nó.Dson 12 Hãy xem ví dụ bên dưới. sau đó copy nó sang thanh ghi PortA để bật Led on. 2 lệnh MOV sẽ thực hiện lần lượt ghi data vào thanh ghi W rồi chuyển vào PORTA.First counter for our delay loops . chắc là không cần phải giải thích hàm XOR cho bạn phải không ?. chỉ cần quay trở về lệnh lật ngược PortA lại mà thôi.*****Set up the Constants**** STATUS equ 03h TRISA equ 85h PORTA equ 05h COUNT1 equ 08h COUNT2 equ 09h . Ngoài ra chúng ta cũng không cần phải gán một giá trị cho thanh ghi PortA.

Toggle the LED .Get the value from PORT A BIT 0.Second counter for our delay loops .zero.*******Flashing LED With Switch: .****End of the program**** end . and also .just in case we miss the goto instruction.****Add a delay call Delay .5 movlw 02h .****Check if the switch is still closed BTFSC PORTA.turned off long enough for us to decfsz COUNT2.Set the Port A pins: movwf TRISA .Address of Port A COUNT1 equ 08h . If is a 1.carry on as normal. then add an .Switch to Bank 1 movlw 01h .bit 1to Output.****Add a delay call Delay .Needed by some compilers.carry on as normal.****Turn the LED on and off**** Start Xorwf PORTA.0 . then add an .5 .1 .First counter for our delay loops COUNT2 equ 09h .****Here is our Subroutine Delay Loop1 decfsz COUNT1.****Turn the LED on and off**** Start xorwf PORTA.0 .1 . If it is a .zero call Delay .Dson 13 movlw 00h movwf TRISA Bcf STATUS. call Delay .Toggle the LED . bit 0 to Input.Set the Port A pins .5 .This second loop keeps the LED Goto Loop1 . .Get the value from PORT A BIT 0.see it turned off goto Loop1 .****Now go back to the start of the program goto Start . If is a 1.Address of the STATUS register TRISA equ 85h .Switch back to Bank 0 movlw 02h .to Output.extra delay routine . return .1 . Bcf STATUS.Address of the tristate register for Port A PORTA equ 05h .go back to Start and turn LED on again . Set up our w register with 02h .****Set up the Port**** bsf STATUS.Set up our w register with 02h .****Check if the switch is closed BTFSC PORTA.1 .Switch back to Bank 0 .If it is a . .*******Set up the Constants**** STATUS equ 03h .

ví dụ như với PortA.turned off long enough for us to decfsz COUNT2. Toán hạng Logic và Số học: Trong phần trên chúng ta được giới thiệu lệnh XORF và cách sử dụng nó.Dson 14 .****End of the program**** end .This second loop keeps the LED goto Loop1 . thực hiện vài thuật toán thông thường trên dữ liệu.****Add another delay**** call Delay . Thực sự ta đã giảm được bao nhiêu byte khi viết lại các chương trình bằng các lệnh đơn giản.Needed by some compilers.1 . cú pháp là: ANDWF <register>. Chúng ta không chỉ học vài lệnh mới mà còn học cách làm sao giảm kích thước của chương trình. cú pháp là: ANDLW <number> <number> là cái mà ta sẽ AND với nội dung trong thanh ghi W. Lệnh ANDWF cho phép ta AND thanh ghi W với một thanh ghi khác. trong phần này sẽ nói tiếp các toán hạng và lệnh Logic mà con Pic có hỗ trợ.extra delay routine . kết quả AND sẽ lưu trong thanh ghi W. đó là lệnh ANDLW và ANDWF.go back to Start and turn LED on again . Lệnh ANDLW cho phép ta AND nội dung trong thanh ghi W với một con số xác định. and also . hãy xem thống kê: Program Flashing LED Flashing LED Flashing LED LED With Switch LED With Switch Change Original Subroutine Added XOR Function Used Original XOR Function Used Size (Bytes) 120 103 91 132 124.d . Bây giờ ta sẽ nói làm sao thao tác trên các bit riêng rẽ.see it turned off goto Loop1 . return .****Here is our Subroutine Delay Loop1 Decfsz COUNT1. Chỉ cần dùng các lệnh đơn giản chúng ta có thể giảm kích thước của chương trình. Sẽ không có ví dụ nữa nhưng mà sẽ giải thích cặn kẽ làm thế nào dùng các toán hạng trong các đoạn code nhỏ.****Now go back to the start of the program goto Start .just in case we miss the goto instruction. Lệnh ANDLW và ANDWF: Con Pic cho ta 2 món được chế biến từ hàm AND.1 .

xem như bạn đã đoán ra. Lệnh SUBLW and SUBWF: Hàm SUB. d=1 kết quả lưu trong thanh ghi ta chỉ định (tức là <register>). Lệnh ADDLW cộng nội dung của thanh ghi W với một số xác định. Ví dụ ta muốn cộng số 1 với nội dung của địa chỉ 0Ch. sau đó dùng lệnh ADDWF <register>. cú pháp thì giống y như là những món của hàm ADD nhưng mà thay vì cộng thì nó trừ. Nếu d=0 thì kết quả lưu vào thanh ghi W và d=1 thì kết quả lưu vào thanh ghi đứng trước d.d <register> là thanh ghi mà chúng ta chỉ định và d nói cho con Pic biết nơi lưu kết quả. d nói cho Pic biết nơi lưu kết quả. Cờ CARRY có địa chỉ byte 03h và nằm tại bit0. nếu kết quả lớn hơn 8bit thì cờ CARRY sẽ được set lên 1 ngược lại nó =0. đó là SUBLW and SUBWF. Một lần nữa con Pic lại cho ta 2 món được chế biến từ hàm SUB. tôi dám đánh cược bạn không thể đoán được hàm này làm cái gì ?!. cú pháp là: ADDLW <number> Lệnh ADDWF cộng nội dung của thanh ghi W với một thanh ghi bất kỳ. Hai đoạn code bên dưới sẽ mô tã 1 ví dụ cho mỗi hàm AND. đó là ADDLW và ADDWF. ví dụ PortA. đơn giản ta sử dụng hàm ADD và số 1. cú pháp là: INCF <register>. Một lần nữa con Pic cho ta 2 món chế biến từ hàm ADD. đầu tiên phải đặt số 1 vào thanh ghi W. ta phải viết đoạn code sau: Movlw addwf 01 0c. hàm SUB này trừ 1bit với 1bit khác. Lệnh INCF và INCFSZ: Nếu chúng ta muốn cộng 1 với một số trong Pic. thôi được rồi. ANDLW 1100 Lệnh IOR: Lệnh IOR đơn giản như là một hàm OR.d . ngược lại sẽ =0. cú pháp là: ADDWF <register>. khi một trong hai bit =1 hoặc cả hai đều = 1 mà OR với nhau sẽ cho kết quả = 1. Nếu ta chỉ muốn cộng số 1 vào một thanh ghi bất kỳ thì còn tồi tệ hơn.Dson 15 <register> là thanh ghi mà ta chỉ định.0 Ví dụ thứ hai sẽ kiểm tra nội dung trong W.1 Có một cách tốt hơn cách này đó là dùng lệnh INCF trong con Pic. kết quả lưu trong W.1. Đầu tiên kiểm tra trạng thái PortA là nơi mà ta cần biết ngõ vào có = 1100 hay không và đặt kết quả vào trong thanh ghi W. Lệnh ADDLW và ADDWF: ADD là một hàm cộng 2 số với nhau. Movlw 1100 ANDWF 05h. cái bất tiện là đầu tiên ta phải bỏ con số 1 vào trong thanh ghi W. sau đó dùng lệnh ADDLW 1 để tăng nó lên 1. bạn cũng có thể đoán rằng nó cũng tương tự như các hàm ở trên. Nếu d=0 kết quả lưu trong thnh ghi W.

d. BCF là lệnh xoá 1 bit được chỉ định trong thanh ghi. nếu d=1 thì kết quả lưu trong thanh ghi chỉ định nằm trước d ( tức là <register>). trong trường hợp ví dụ trên nó bỏ qua lệnh GOTO Loop để thực thi tiếp đoạn code còn lại. địa chỉ 0Ch sẽ tăng lên 1 sau đó chương trình nói cho con Pic quay về nhãn Loop và gia tăng 0Ch lên lần nữa. SET và CLEAR bit trong thanh ghi hoặc những địa chỉ được chỉ định. Có một lệnh increment khác. Nếu d=0 thì kết quả lưu trong thanh ghi W. nội dung của 0Ch sẽ =0.Dson 16 Với <register> là thanh ghi. bây giờ ta sẽ không nhắc lại nữa. nhưng nếu thanh ghi này =0 sau khi thực thi lệnh ( xảy ra khi cộng 1 vào FFh) thì con Pic sẽ bỏ qua lệnh kế tiếp. nó làm tiếp tục như vậy cho tới khi 0Ch =127 (FFh). Lệnh COMF: Lệnh cuối cùng trong nhóm này là lệnh COMF. Với <register> là thanh ghi mà ta muốn đảo và d nói cho con Pic bíêt nơi lưu kết quả.1 0C = 00110011 Cái này rất tiện lợi khi mà bạn muốn nhanh chóng bật các chân của Port từ Output trở thành Input hoặc ngược lại. Lệnh INCFSZ sẽ nói cho con Pic bíêt hãy bỏ qua lệnh kế tiếp. cú pháp là: 0C Loop . Xem mô tả sau đây: 0C = 11001100 COMF 0C. Lệnh DECFSZ: Lệnh DECFSZ đã bàn trong các ví dụ trước. cú pháp là: COMF <register>. Nếu ta muốn kết quả lưu trong W thì sử dụng ví dụ trên sau đó thêm một lệnh khác để MOV nội dung trong địa chỉ 0Ch trở vào trong thanh ghi W sau đó đặt vào thanh ghi 0Ch bất cứ cái gì. đó là INCFSZ. đoạn code bên dưới sẽ mô tả lệnh này: Loop Incfsz Goto : : Rest of program. Lệnh BCF: Trong các phần trước chúng ta đã xem một số lệnh thực thi trên bit. Nếu d=0 kết quả lưu trong thanh ghi W. Toán hạng trên Bit: Các toán hạng dùng cho Bit cho phép chúng ta thao tác trên các bit đơn lẽ trong byte. phần cuối của tutorial này ta sẽ trình bày một chương trình làm cho con Led sáng chạy theo nhiều cách khác nhau. còn d thì nói cho con Pic biết nơi đặt kết quả. nếu d=1 kết quả sẽ được lưu trong thanh ghi chỉ định nằm trước nó (tức là <register>) Bằng cách này ta có thể tiết kiệm ½ bộ nhớ của Pic. Lần này khi tăng lên 1. Trong đoạn code trên. lệnh này sẽ tăng thanh ghi mà ta chỉ định lên 1. nó đảo ngược (Compliment) tất cả các bit trong thanh ghi được chỉ định. hoặc địa chỉ mà ta chỉ định. trong phần này ta sẽ xem một số lệnh còn lại tác động lên bit như thế nào. nó cho phép MOV.

0 Nếu cờ Carry=1 thì chương trình thực thi tiếp lệnh đứng kế tiếp. cú pháp là: CLRF <register> Chúng ta đã dùng lệnh này trước đây để Clear ngõ ra Output của Port về 0 bằng cách dùng câu lệnh: CLRF 05h lệnh CLRW: Lệnh này giống y như lệnh CLRF nhưng mà chỉ khác là nó chỉ Clear thanh ghi W. Nó giống như là lệnh BTFSC nhưng mà chỉ khác là con Pic sẽ bỏ qua lệnh kế tiếp nếu bit=1.<bit> Chúng ta đã sử dụng lệnh này trong phần trước để thay đổi từ Bank1 sang Bank0 bằng cách xoá bit trong thanh ghi STATUS. ví dụ. Lệnh CLRF: Lệnh này sẽ Clear nội dung trong thanh ghi hiện hành về 0. xem đoạn code sau: Loop : : : BTFSC Goto Loop 03. nói cách khác nếu cờ Carry=0 lệnh GOTO sẽ được thực hiện. ta sẽ dùng lệnh này để kiểm tra một cái cờ (flag) nào đó ví dụ như cờ Carry. nó có thể Set 1 bit lên 1 tại bất kỳ bit nào trong bất kỳ thanh ghi nào. nếu bạn muốn thử bit cờ Carry =1 chưa sau khi bạn cộng 2 byte với nhau. Lệnh BTFSS: Lệnh này có nghĩa là “Bit Test Register F.<bit> Cách dùng BSF giống y như cách dùng BCF. con Pic sẽ đi ra khỏi Loop nếu bit0 trong thanh ghi STATUS ( hay cờ Carry) bị xoá về 0. nó được gọi là “Bit Test Register F and Skip If It Is Clear”. quá rõ ràng rồi. cú pháp thì hoàn toàn đơn giản: CLRW . ta đã dùng cái này trong phần trước để nhảy từ Bank0 sang Bank1.Dson 17 BCF <register>. nó tránh cho ta khỏi phải đọc thanh ghi STATUS để tìm xem trạng thái của từng bit như thế nào. Lệnh BTFSC: Chúng ta đã có thể Set bit và Clear bit trong thanh ghi. không cần phải giải thích gì thêm nữa phải không ?!. And Skip If Set” tạm dịch là kiểm tra bit trong thanh ghi F và bỏ qua lệnh kế nếu=1.0 Trong đoạn code trên. tạm dịch là “lệnh thử kiểm tra bit trong thanh ghi và bỏ qua lệnh kế nếu bit = 0”. cú pháp là: BSF <register>. chúng ta cũng có thể Clear 1 bit về 0 tại bất kỳ bit nào trong bất kỳ thanh ghi nào. rất đơn giản. nếu bạn muốn Clear bit thứ 3 trong thanh ghi 0Ch có nội dung = 11001101. nếu Carry=0 nó sẽ bỏ qua lệnh kế tiếp. nhưng mà nếu bạn chỉ muốn thử xem bit nào đó trong thanh ghi là = 1 hay = 0 thì sao. Ví dụ. bạn có thể làm như sau: BCF 0C. hảy dùng lệnh BTFSC.03 Lệnh BSF: Lệnh BSF ngược lại. bạn hãy thử làm cái này: BTFSC 03h.

Port A address. Bây giờ hãy xem cái gì sẽ xảy ra nếu bạn có 10000000 và tiếp tục thực thi lệnh RLF?. . đừng có hốt hoảng. Loop register. các con số 7654321 là thứ tự từ cao xuống thấp của 8bit trong thanh ghi. then return to STATUS. C 76543210 0 00000001 RLF 0 00000010 RLF 0 00000100 RLF 0 00001000 RLF 0 00010000 RLF 0 00100000 RLF 0 01000000 RLF 0 10000000 RLF 1 00000000 RLF 0 00000001 Chương trình Test: Bây giờ bạn sẽ xem một ví dụ.Dson 18 Lệnh RLF và RRF: Lệnh này sẽ dịch bit trong thanh ghi sang vị trí bên trái (RLF) hoặc bên phải (RRF) của thanh ghi đó. Port B Tristate address. ví dụ bạn có 00000001 và bạn dùng lệnh RLF thì bạn sẽ nhận được 00000010. . and set up . TIME PORTB TRISB PORTA TRISA STATUS COUNT1 COUNT2 BSF MOVLW MOVWF MOVLW MOVWF EQU EQU EQU EQU EQU EQU EQU EQU 9FH 06H 86H 05H 85H 03H 0CH 0DH . to Output. Variable for the delay loop. Port B address. Bạn hãy kết nối các con Led vào chân Port rồi cho chạy chương trình. Port A Tristate address. Loop register. bạn có nhình thấy chữ C là ký hiệu của cờ Carry. Go to page 1 . Chương trình này làm cho đèn chạy bắt đầu từ bit0 của PortA sang tới bit8 của PortB rồi quay về thực thi lại từ đầu. . nếu muốn bạn có thể compile và cho nó chạy thử. . both Ports A and B . . . bạn sẽ nhìn thấy các bit hoạt động như những gì mà ta đã nói từ trước đến giờ. . Page select register.5 00H TRISB 00H TRISA . Ví dụ bên dưới biểu diễn lệnh RLF. Mọi thứ sẽ diễn ra đúng như vậy đối với lệnh RRF nhưng mà bit sẽ di chuyển theo chiều bên phải. bit 1 của bạn sẽ đi sang cờ Carry. nếu bạn lại tiếp tục RLF thì bit 1 sẽ quay trở về vị trí 0 trong byte. . .

This moves the bit into the carry flag . . page 0.1 CALL DELAY CALL DELAY RLF PORTB. . RLF PORTA. then pause. Wait a while . Set the first bit . on Port B.Dson 19 BCF STATUS. and move the bit left.5 MOVLW 00H MOVWF PORTA .1 CALL DELAY CALL DELAY RLF PORTA. Now move onto Port A.1 CALL DELAY CALL DELAY RLF PORTB.1 CALL DELAY CALL DELAY RLF PORTB.1 CALL DELAY CALL DELAY RLF PORTB.1 CALL DELAY CALL DELAY RLF PORTB.1 CALL DELAY CALL DELAY . Move the bit back on Port A RRF PORTA.1 CALL DELAY CALL DELAY RLF PORTB.1 . Start of main program RUN MOVLW 01H MOVWF PORTB CALL DELAY CALL DELAY . This moves the bit from the zero flag into PortA CALL DELAY CALL DELAY RLF PORTA. Move the bit on Port B left.1 CALL DELAY CALL DELAY RLF PORTB.1 .1 CALL DELAY CALL DELAY . Clear Port A. . RLF PORTB.1 CALL DELAY CALL DELAY RLF PORTA. . .

Decrement 1 from the delay time until it GOTO LOOP1 .1 CALL DELAY CALL DELAY RRF PORTB.1 CALL DELAY CALL DELAY RRF PORTB.1 CALL DELAY CALL DELAY . MOVWF COUNT1 . DECFSZ COUNT1 . LOOP1. GOTO LOOP2 .1 DELAY DELAY PORTA. LOOP2 .1 CALL DELAY CALL DELAY RRF PORTB. Subroutine to give a delay between bit movements. Get the delay time. RETURN . Now we are back where we started.1 CALL DELAY CALL DELAY RRF PORTB. . Now move the bit back on Port B RRF PORTB. .Dson 20 RRF CALL CALL RRF CALL CALL RRF PORTA. and put it into a variable. .1 . and repeat the count down. let's go again. GOTO RUN . DECFSZ COUNT1 .1 DELAY DELAY PORTA.1 CALL DELAY CALL DELAY RRF PORTB. DELAY MOVLW TIME . reaches zero. End of subroutine. Get the delay time again. This moves the bit into the zero flag . END .1 CALL DELAY CALL DELAY RRF PORTB. MOVWF COUNT1 .

Cái mà thực sự con Pic đã dùng đó là bộ đếm dòng lệnh bên trong còn gọi là bộ đếm chương trình Program Counter. Program Counter viết . Cái này rất tiện dụng bởi vì chúng ta không biết được hiện tại con số đếm đã là bao nhiêu cho đến khi con Pic dừng lại. Bây giờ trước khi giải thích bảng tra dữ liệu làm việc ra sao ta sẽ bàn xem con Pic bám theo chổ nào trong chương trình trong lúc chương trình đang chạy. nếu đúng nó tiếp tục đi tới dòng 20.Dson 21 Bảng dữ liệu ( Data Table): Có một điểm rất đặc biệt trong tập lệnh mà nó cho phép bạn truy xuất dữ liệu theo kiểu tra bảng (data table). Con Pic có sử dụng những cái nhãn để nhảy qua lại các vị trí hay không?. bạn có một con Pic và bạn muốn đếm số lần ngõ vào Input được nâng lên mức cao trong thời gian 1giây là bao nhiêu sau đó hiễn thị lên Led 7 đoạn. Mỗi lần thời gian bắt đầu tính. sau 1 giây nó hiễn thị con số nó đếm được tương ứng với số lần ngõ vào Input được nâng lên mức cao. mỗi lần K =0 nó tiến tới dòng 11. Ví dụ. Một bảng dữ liệu thông thường là một danh sách liệt kê các giá trị của dữ liệu. ở đâu và nói cho con Pic biết con đường nó phải đi. Nếu bạn đã từng lập trình trong BASIC thì đở mệt nhọc hơn. bạn sẽ vẫn tìm thấy các khái niệm ở đây. chúng ta dùng những cái nhãn nhận dạng vì vậy chúng ta biết những thứ gì. bằng cách sử dụng bảng tra dữ liệu chúng ta có thể cho phép con Pic quýêt định con số nào nó cần hiễn thị. BASIC sử dụng con số thứ tự dòng để giúp cho lập trình viên bám theo chương trình một khi những cái nhãn nhận dạng không cho phép sử dụng trong BASIC. còn nếu không bạn cũng đừng lo lắng. Hãy tưỡng tượng chúng ta có một chương trình BASIC như chương trình bên dưới: 10 11 12 20 21 LET K=0 K=K+1 IF K>10 THEN GOTO 20 ELSE GOTO 11 PRINT K END Chương trình bắt đầu tại dòng số 10. con Pic sẽ đếm số lần ngõ Input vào được nâng lên mức cao trong thời gian 1 giây. nếu sai nó quay trở lại dòng 11. sau khi cộng thêm 1 cho K nó di chuyển đến dòng 12. Ở đây chúng ta hỏi K có lớn hơn 10 không ?. mỗi giá trị được đọc phụ thuộc vào việc phải thoả mãn vài tiêu thức nào đó. dòng 20 sẽ xuất giá trị của K và dòng 21 sẽ kết thúc chương trình.

điều này làm cho PC tăng lên 3. tại vị trí này ta có lệnh movlw 03. . tương đương với việc PC sẽ đi xuống 3 dòng. PC 0000 0001 0002 0003 04 Instruction movlw movwf decfsc goto end 03 0C 0C Loop Loop Trong ví dụ trên. và chúng ta không cần lo lắng con Pic đang làm cái gì cho tới khi chúng ta cần kiểm soát nó như trong trường hợp của bảng tra dữ liệu. lệnh Goto loop nói con Pic hãy quay lại vị trí 0002. nếu nội dung trong địa chỉ 0C không = 0 con Pic sẽ tăng PC lên 1 và đọc lệnh kế tiếp. Lưu ý là có 2 động tác được thực hiện trong lệnh RETLW. như vậy nó sẽ đến vị trí 0004. Bây giờ ta làm một lệnh gọi bảng tra dữ liệu. lệnh này chuyển giá trị đứng sau nó vào thanh ghi W rồi quay trở về lại subroutine. kế bên là các dòng lệnh. lần này nó thấy lệnh decfsc 03.Dson 22 tắt là PC dò tìm các vị trí trong bộ nhớ để tìm kiếm vị trí hiện tại của câu lệnh mà chương trình đang thực thi. Khi PC xuống dòng thứ 3 con Pic trông thấy lệnh reltw. tại đây là điểm kết thúc của chương trình. Điều này giống y như cái cách mà chúng ta đọc chương trình trong BASIC Bên dưới là đoạn code và các vị trí bộ nhớ hay nói cách khác chính là nội dung trong PC. Nếu nội dung trong 0C là 0 thì con Pic nói PC phải tăng lên 2 hay nói cách khác là bỏ qua lệnh kế tiếp nó. ta set PC tới 0000. Cách tốt nhất để giải thích bảng dữ liệu làm việc ra sao là hãy chấm dứt ngay cái ví dụ này và xem cái bên dưới đây !. nó bíêt vị trí của cái nhãn này trong bộ nhớ và nó gia tăng PC lên cho tới khi nó đọc được vị trí đó. Các vị trí được thiết lập bởi assembler. nó lại tăng PC lên một lần nữa. Khi ta đang đứng trong một subroutine ta cần có một lệnh quay về để thoát ra khỏi subroutine đó là lệnh RET. sau đó chúng ta đặt giá trị của thanh ghi 03h vào trong thanh ghi W. Khi chúng ta nói cho con Pic bíêt phải đi đến cái nhãn nào đó. PC Movlw Call : table retlw retlw retlw retlw retlw retlw retlw return equ 02 03 table addwf PC 01 02 03 04 05 06 07 Lệnh đầu tiên gán cái nhãn PC có địa chỉ của Program Counter (02h). Khi con Pic thực thi lệnh này nó tăng PC lên và vì vậy nó đọc tiếp lệnh kế. Lệnh RETLW có nghĩa là quay về và trả giá trị phía sau nó về thanh ghi W. Dòng đầu tiên trong subroutine bảng dữ liệu sẽ công nội dung của thanh ghi W (03h) với PC. ở đây con Pic lại thấy lệnh movwf 0C.

Chính vì điều này mà người ta hay đặt bảng tra dữ liệu ở cuối của chương trình. Bây giờ chúng ta cần nói cho con Pic biết sẽ khởi động Interrupt bằng cạnh lên (từ 0V lên 5V) hay cạnh xuống ( từ 5V xuống 0V) của tín hiệu vào chân Interrupt. như vậy nếu bị lọt ra khỏi subroutine thì sẽ đến điểm kết thúc chương trình (End). Đầu tiên ta cần nói cho con Pic biết rằng ta sẽ sử dụng Interrupt. 2 Interrups bên trong Pic sẽ nói đến trong phần Timers và lưu trữ Data. rồi bạn đang tán gẫu với ai đó. khi kết thúc cuộc nói chuyện bằng điện thoại bạn quay về “chương trình chính” để tiếp tục tán gẫu. nhặt điện thoại lên và nói chuyện với người gọi đến. nhưng khi Interrupt xảy ra chương trình chính sẽ tạm ngưng và ngay lúc đó một thủ tục khác được thực hiện. giả sử bạn đang ngồi ở nhà. nếu ta đi lọt ra ngoài subroutine thì có thể sẽ làm cho con Pic không thể thực hiện bất kỳ phần nào của chương trình nữa. Bit4 của INTCON gọi là INTE có nghĩa là INTerrupt Enable tạm dịch là cho phép Interrupt.Dson 23 Phía sau lệnh RETLW là một con số. bạn ngưng cuộc nói chuyện lại. Bây giờ bạn hãy tưởng tượng. Ngắt (Interrupt): Chủ đề nói về các Ngắt (Interrupts) hầu như là dài nhất và khó hiểu nhất. Bây giờ thì con Pic đã biết và theo dõi khi nào chân này lên cao hay xuống thấp. một Interrupt là một tác vụ xử lý hay là một tín hiệu xử lý mà nó có thể bắt con Pic dừng lại những gì đang làm để làm một công việc khác. thực hiện một vài chức năng nào đó trên mạch điện. Trong con Pic có 1 thanh ghi gọi là INTCON. Khi bạn kết thúc cuộc nói chuyện bằng điện thoại bạn lại quay trở về và tiếp tục tán gẩu với người đã nói chuyện với bạn trước khi điện thoại reo. ký hiệu INT là ký hiệu chức năng Interrupt ngoài. Một chương trình chính đang chạy. không phải dể để giải thích về ngắt cho người nào đó hiểu. Ngắt (interrupt) là cái gì vậy ?. trong thanh ghi này có 8bit có thể thiết lập chế độ cho phép hay không cho phép. Trước khi sử dụng Interrupt hay dùng nó như là Port in out thông thường chúng ta cần phải làm 2 việc. chương trình chính là quá trình tán gẫu của bạn với người bạn ngồi ở nhà. trong trường hợp này nó là số 03. set bit này lên 1 sẽ cho phép chân RB0 trở thành chân Interrupt. kế đến ta cần xác định chân nào của PortB sẽ dùng như Interrupt. Ví dụ này giải thích chính xác một Interrups tạo ra một tiến trình xử lý như thế nào. Nếu bạn quan sát trên sơ đồ chân của Pic bạn sẽ thấy chân số 6 có ghi là RB0/INT. thình lình chuông điện thoại reo. trong ví dụ trên. Con Pic có 4 Interrupt. Ngoài ra các chân từ 10 đến 13 ( bit 4 tới 7 của PortB) cũng có thể sử dụng cho Interrupt. nó biết cần phải dừng chương trình chính lại khi nào để quay ra phục vụ thủ tục của Interrupt. Bit3 còn gọi là bit RBIE nếu được set=1 sẽ báo cho con Pic biết ta sẽ sử dụng từ bit4 cho đến bit7 của PortB. con số này là thứ mà ta sẽ đặt vào trong thanh ghi W. RB0 là bit0 của PortB. khi thủ tục này kết thúc con Pic sẽ lại quay về chương trình chính. nó thật sự có ý nghĩa giống như tên gọi của nó vậy. 2 Interrupts phục vụ cho các thiết bị kết nối ngoại vi và 2 Interrupts cho bên trong nó. hãy lấy sinh hoạt hàng ngày của bạn. Trước tiên ta hãy nói về 2 Interrupts bên ngoài. Nếu set bit này lên 1 con Pic sẽ cho phép sử dụng Interrupt. Đầu tiên. Chúng ta sẽ chia phần này thành 2 phần nhỏ. nhưng mà hy vọng sau khi kết thúc phần này chúng ta có thể áp dụng ngắt vào trong chương trình của chúng ta. Một ví dụ dể hiểu. địa chỉ là 0Bh. Chúng ta có thể gán cho thanh ghi W bất kỳ giá trị nào nhưng phải chắc chắn rằng con số này sau khi cộng với PC trong subroutine bảng tra dữ liệu sẽ tìm ra được một lệnh RETLW. Bit7 của INTCON được gọi là GIE có nghĩa là Global Interrngupt Enable tạm dịch là chân cho phép sử dụng toàn bộ Interrup. điện thoại reo tạo ra một Interrupt và thủ tục (routine) Interrups là cuộc nói chuyện với người ở đầu dây bên kia. điều này có nghĩa là ta có thể có bất kỳ con số nào từ 1 đến 7. nó có thể được chia thành 2 nhóm. Nói cách . mụch đích là để cho bạn nghỉ giải lao !.

Có một mánh lới để làm tất cả chuyện này trên Bank1 như là thiết lập các chân Port. nếu setbit6=1 sẽ thiết lập interrupt tích cực ở cạnh lên của tín hiệu vào (trạng thái default) . Nếu bạn muốn con Pic thiết lập interrupt xảy ra ở cạnh lên của tín hiệu thì bạn không cần phải làm gì trên bit6 của thanh ghi OPTION. mặc dù con Pic tự động set cờ này lên 1 nhưng nó lại từ chối trách nhiệm Clear cái cờ này về 0 ! vì vậy mà trách nhiệm cao cả này được trao cho người lập trình viên !. Cờ Ngắt (Interrupt Flag): Trong thanh ghi INTCON bit1 chính là cờ báo Interrupt gọi là INTF. nếu cái cờ vì lý do gì đó không thể set lên 1 và con Pic đang thực thi chương trình Interrupt thì tín hiệu đổ vào liên tục tại chân Interrupt sẽ liên tục gây ra Interrupt trên con Pic làm cho nó phải liên tục quay trở về điểm bắt đầu của chương trình (routine) Interrupt và sẽ không bao giờ nó có thể kết thúc được chương trình Interrupt này. vì vậy bạn phải làm động tác di chuyển từ Bank0 sang Bank1 sau đó Set bit6 trên thanh ghi OPTION rồi lại quay trở về Bank0. Trong khi cờ Interrupt được set lên 1 thì con Pic sẽ không thể và không bao giờ đáp ứng bất kỳ một Interrupt nào nữa. Mặc nhiên sau khi bật nguồn con Pic sẽ thiết lập chế độ Interrupt cạnh lên. Địa chỉ bộ nhớ: Memory Location Lần đầu tiên mở nguồn hoặc khi reset con Pic. có nghĩa là interrup xảy ra khi tín hiệu vào thay đổi từ thấp lên cao (cạnh lên) Thanh ghi OPTION ở địa chỉ 81h chính là thanh ghi thiết lập chế độ cho Interrupt tích cực ở cạnh lên hay cạnh xuống của tín hiệu vào. bit6 của thanh ghi OPTION được gọi là INTEDG. Tại sao không phải là sau khi kết thúc cuộc chuyện trò với người thứ nhất bạn lại nhặt điện thoại lên một lần nữa để nói chuyện với ngưòi thứ hai. điều này thì rất dễ làm có phải không ?.Dson 24 khác. trước khi có Interrupt xảy ra nó =0. cờ này sẽ được set lên 1. Được rồi. hãy xem từng vấn đề như thế nào. cho đến giờ chúng ta đã biết chân nào của con PIC sẽ trở thành Interrupt và tích cực cạnh nào của tín hiệu. nó giống như là bạn vừa nhặt điện thoại lên định nói chuyện thì chuông lại reo lần nữa vì có ai đó cũng đang muốn nói chuyện với bạn!. thứ hai bộ đếm chương trình (program counter) trỏ đến một địa chỉ đặc biệt trong con Pic. or start tạm dịch là điểm khởi đầu hay điểm khởi động. Có một trở ngại nhỏ trên cái cờ này. khi có Interrupt xảy ra. Bộ đếm chương trình (Program Counter) trỏ đến địa chỉ 000h. lệnh này nghĩa là Origin. Cái cờ được set lên 1 và con Pic sẽ thực thi chương trình (routine) của Interrupt. Có 2 thứ xảy ra. Bởi vì con Pic khởi động tại 0000h nên chúng ta viết: . nếu Clear bit6=0 sẽ thiết lập interrupt tích cực ở cạnh xuống của tín hiệu vào. quay trở vào Bank0 !. ta muốn con Pic phục vụ Interrupt khi tín hiệu vào thay đổi từ thấp lên cao hay từ cao xuống thấp. Bây giờ quay lại ví dụ về chuyện tán gẫu và cuộc nói chuyện điện thoại của bạn. có phải tốt hơn không!. Thật không may mắn. thanh ghi OPTION lại nằm trên Bank1. (dầu tiên chúng ta khởi động chương trình bằng lệnh ORG. nếu không thì sẽ không bao giờ có interrupt xảy ra nữa. thứ nhất là có 1 cờ ‘flag’ được set để nói cho con Pic biết rằng có 1 Interrupt đã xảy ra. Tuy nhiên hki có Interrupt xảy ra thì PC sẽ trỏ đến địa chỉ 0004h. theo sau ORG là một địa chỉ xác định. vì vậy khi viết chương trình mà có s73 dụng Interrupt thì đầu tiên chúng ta phải nói cho con Pic nhảy (jump) đến địa chỉ 0004h và tách riêng chương trình Interrupt ( bắt đầu tại 0004h) với các chương trình khác. đó chính là điểm bắt đầu của bộ nhớ chương trình. Cái này thì dể dàng thôi và tôi chắn chắn rằng bạn sẽ làm được. tôi đoán đó là lý do tại sao mà điện thoại không thể reo trong khi bạn đã nhấc ống nghe. cái gì sẽ xảy ra trong chương trình và Interrupt xảy ra khi nào.

Goto our main program . tôi chọn và khuyên bạn cũng nên chọn 4 chu kỳ lệnh giữa 2 interrupt cho chắc ăn !.Dson 25 ORG 000h Kế đến chúng ta cần nhảy qua khỏi địa chỉ 0004h. phải có ít nhất là 3 đến 4 chu kỳ lệnh giữa 2 interrupt. Như vậy. con Pic có một bộ dao động bên trong hoạt động bằng cách mắc với bên ngoài hoặc dùng thạch anh hoặc dùng mạch RC.PIC starts here on power up and reset . set cờ interrupt.This is the start of our main program. Sau đó ta đặt tiếp một ORG khác.This is our interrupt routine that we . đó là khi bạn sử dụng từ bit4 đến bit7 của PortB như Interrupt thì bạn không thể chọn riêng từng chân trên PortB để nó làm việc như Interrupt. RTFIE có nghĩa là return from the interrupt routine tạm dịch quay trở về từ chương trình Interrupt.want the PIC to do when it receives . và rồi. tần số dao động này được chia 4 bên trong để tạo ra xung Clock làm nhịp cho 1 chu kỳ lệnh. thoát ra khỏi chương trình interrupt. Có 2 điều quan trọng mà bạn cần chú ý khi sử dụng Interrupt: Thứ nhất. hãy xem một đoạn code ngắn bên dưới đây: ORG GOTO ORG : : : RETFIE start 0000h start 0004h . dựa trên những gì đã bàn trong phần trên. bạn hãy dùng lệnh GOTO để làm điều này và theo sau GOTO là 1 cái nhãn mà nó sẽ trỏ tới điểm bắt đầu của đoạn code của chương trình chính.The PIC will come here on an interrupt . Ví dụ: nếu thạch anh là 4MHz kết nối với con Pic thì 1 chu kỳ lệnh là: 4MHz/4 = 1MHz Bây giờ hãy xem hướng dẫn sử dụng cho con Pic của nhà sản xuất. Thứ hai. như bạn biết. Bây giờ lại có một thứ cần phải nhớ. bạn phải lưu ý khi sử dụng mạch kết nối với các thiết bị ngoại vi kích hoạt interrupt của con Pic. nếu bạn sử dụng cùng một thanh ghi cho chương trình chính và cho Interrupt thì rất có thể nội dung của thanh ghi này bị thay đổi khi Interrupt xảy ra. khi bạn quay về chương trình chính bạn lại gởi nội dung này vào PortA thay vì một nội dung khác trước khi Interrupt xảy ra. Cách đơn giản để tránh thảm hoạ này là bạn hãy lưu thanh ghi W vào vị trí tạm nào đấy và dùng nó lại sau khi chương trình Interrupt kết thúc. Lý do mà con Pic cần thời gian nghĩ giữa 2 lần Interrupt là nó phải làm đủ thứ chuyện như là nhảy đến địa chỉ Interrupt. nếu bạn không cẩn thận thì thanh ghi W sẽ chứa giá trị cuối cùng trong chương trình Interrupt (khi interrupt xảy ra). nếu bạn cho phép (enable) những chân này thì bạn đã cho phép tất cả Trong phần tiếp theo chúng ta sẽ viết chương trình cho Interrupt . Để chấm dứt 1 chương trình Interrupt ta cần đặt lệnh RTFIE tại cuối chương trình Interrupt đó. Viết chương trình Interrupt theo sau ORG 0004h Hay dùng lệnh GOTO để nhảy đến 1 chương trình Interrupt đặt ở đâu đó thật sự là vấn đề để chọn lựa. khi con Pic nhìn thấy lệnh RTFIE nó báo cho Program Counter biết để dời tới vị trí lần cuối cùng nó đứng trong chương trình chính trước khi Interrupt xảy ra.an interrupt .End of the interrupt routine . vì ta đang nói đến Interrupt nên bạn phải đặt ORG 0004h Theo sau lệnh ORG 0004h chúng ta sẽ viết chương trình Interrupt hoặc có thể đặt 1 lệnh GOTO để nhảy đến chương trình Interrupt đặt ở đâu đó. đó là thời gian nghỉ bắt buộc giữa 2 lần interrupt xảy ra liên tiếp. ví dụ: bạn sử dụng thanh ghi W để gởi Data tới PortA trong chương trình chính và cũng dùng thanh ghi W trong Interrupt để di chuyển nội dung từ nơi này đến nơi khác.

Nhưng mà PortA có 5 bit. bây giờ ta nói cho con Pic biết cái gì sẽ làm khi Interrutp xảy ra.finished and the PC will point back to the main program .This is where our interrupt routine will start .1 . bsf bsf INTCON.Dson 26 Interrupts – Chương trình Interrupt: Chương trình mà ta sẽ viết là đếm số lần 1 cái Switch bật on rồi hiễn thị con số đó. hãy lưu ý chúng ta sẽ sử dụng 1 cách khác để biểu diễn số Hex. hiễn thị lên 4 Led dưới dạng Binary.Goto our main program .Move the contents of COUNT into W . bcf INTCON. loop movf movwf goto end COUNT.Now move it to Port A . .This is the start of our main program Bây giờ chúng ta cần nói cho con Pic biết rằng chúng ta sẽ sử dụng Interrupt và sử dụng RB0 (chân 6) như là chân Interrupt.0 PORTA loop .Clear flag bit just in case Và bây giờ setup 2 Port.INTF .5 0x01 TRISB 0x10 TRISA STATUS. bây giờ chúng ta viết lại là 0xF9. Org 0x00 Goto main Org 0x04 Retfie main .Keep on doing this . Trước đây chúng ta hay viết F9h với h có nghĩa là hexadecimal. Bsf Movw Movwf Movlw Movwf Bcf STATUS.This tells the PIC that the interrupt routine has .End of our program Chương trình chính đã có.5 . nếu chúng ta chỉ đơn giản tăng Port lên 1 thì chúng ta sẽ có số đếm tối đa là 31.Set RB0 as Input . Đầu tiên ta cần phải báo cho con Pic nhảy đến địa chỉ mà bộ đếm chương trình sẽ trỏ đến khi Interrupt xảy ra. bạn có thể hỏi tại sao chúng ta không làm đơn giản là tăng giá trị của PortA rồi đọc lại giá trị này. nhớ rằng khi chúng ta sử dụng RB0 như một Interrupt thì ta phải setup nó như một ngõ vào Input.RB0 interrupt enable (1=enable) Kế đến chúng ta xoá cờ Interrupt.GIE – Global interrupt enable (1=enable) . ngõ vào interrupt là RB0. mặc dù chúng ta đã nói khi mở nguồn lần đầu tiên thì cờ Interrupt mặc nhiên bị xoá về 0.4 .This is where the PC points to on power up and reset .Switch to Bank 1 . và cái này chính là dạng mà chúng ta sẽ viết từ giờ trở đi. nhưng bạn sẽ biết lý do tại sao mà tôi sử dụng biến COUNT khi viết chương trình Interrupt. nhưng mà tôi chưa bao giờ tin vào bất kỳ điều gì !.7 INTCON. trong trường hơp này Interrupt của chúng ta sẽ là cái Switch. Chúng ta muốn con Pic cộng thêm 1 vào biến COUNT mỗi lần cái Switch đóng lại.Come back to Bank 0 Chúng ta sẽ sử dụng biến COUNT để lưu số lần Switch On. CHương trình sẽ đếm từ 0 đến 9.Set the first 4 pins on PortA as Output . .INTE . .

ngược lại sẽ quay về chương trình chính để xuất giá trị COUNT ra PortA Lệnh BTFSS như bạn biết là nó sẽ bỏ qua lệnh kế nếu cờ Carry =1.If COUNT is >9. nếu không làm vậy có thể ta sẽ tải nội dung khác lên PortA chứ không phải COUNT.Set COUNT back to 0 .Come out of the interrupt routine Bây giờ hãy ráp lại tất cả các đoạn code lại với nhau.We need to clear this flag to enable . Thứ hai. Trong truờng hợp này nếu COUNT=10: goto goto carry_on bcf movfw retfie clear clrf bcf retfie COUNT INTCON.Increment COUNT by 1.Store w register in a temporary location Kế tiếp ta muốn công 1 vào biến COUNT: Incf .Subtract w from COUNT. then we can carry on . . . Movlw Subwf 0x0A COUNT. ngoài ra cờ Carry cũng sẽ được set lên 1 khi chúng ta trừ 2 số bằng nhau.Restore w to the value before the interrupt .Come out of the interrupt routine carry_on clear INTCON. and put the result back into .We need to clear this flag to enable . Movwf TEMP .0 . nếu COUNT lớn hơn 9 thì đặt lại giá trị 0 cho nó.Check the Carry flag.COUNT Kế đến chúng ta muốn kiểm tra xem COUNT đã lớn hơn 9 chưa bằng cách là lấy COUNT trừ cho 10. mà thông thường nó chỉ biểu diễn được từ 0 đến 15 ( từ 0 đến Fh).0x01 TEMP . nếu ta lấy một số nhỏ trừ cho số lớn hơn thì cờ Carry sẽ set lên 1. Cái đầu tiên chúng ta cần làm là lưu nội dung của thanh ghi W vào chỗ tạm thời vì chúng ta sẽ dùng W để tải nội dung của COUNT vào PortA.and will be set as a result of the subwf instruction Chúng ta muốn. or is greater than w.1 .Dson 27 Có 2 lý do mà tôi chọn không tăng lên đến 31. It will be set if . Bên dưới là 1 chương trình hoàn chỉnh.COUNT is equal to. mỗi lần bạn cho Switch On đèn Led sẽ đếm theo số Binary từ 0000 đến 1010 rồi quay trở về 0000.more interrupts . Btfss STATUS. mạch điện trình bày sau chương trình này.Move the value 10 into w .more interrupts . Thứ nhất chúng ta dùng Led 7 đoạn. and put the result in w COUNT. tôi cũng muốn biểu diễn vài thuật toán thông thường để bạn hiểu những thứ sẽ trình bày trong phần cuối của cuốn sách này.0 .If COUNT is <10. then we need to clear it .1 Trong các phần trước bạn đã biết.

INTF . or is greater than w.0x01 .Come out of the interrupt routine .Restore w to the value before the interrupt retfie .Subtract w from COUNT.1 .We need to clear this flag to enable more interrupts retfie .COUNT is equal to. then we can carry on goto clear .*******************Set Up The Ports****************** bsf STATUS.*******************Main Program********************* main .Interrupt Control Register PORTB EQU 0x06 .7 .Temporary store for w register Goto main . and put the result in w btfss STATUS.Switch to Bank 1 movlw 0x01 movwf TRISB .GIE – Global interrupt enable (1=enable) bsf INTCON.TrisB register address STATUS EQU 0X03 .Jump over the interrupt address . It will be set if .0 .This will be our counting variable TEMP EQU 0x0d . and will be set .1 . then we need to clear it carry_on bcf INTCON.Check the Carry flag.INTE .This is where PC points on an interrupt movwf TEMP .If COUNT is >9.as a result of the subwf instruction goto carry_on .Keep on doing this end .*******************Set Up The Interrupt Registers**** bsf INTCON.5 .End Of Program .Clear FLag Bit Just In Case .Port A register address TRISA EQU 0x85 .1 .back into COUNT movlw 0x0A .Increment COUNT by 1.We need to clear this flag to enable more interrupts movfw TEMP .Move the value 10 into w subwf COUNT.Come out of the interrupt routine clear clrf COUNT .Store the value of w temporarily incf COUNT.Port B register address PORTA EQU 0x05 .Set RB0 as Input movlw 0x10 movwf TRISA .Set R 0 to RA3 on PortA as Output bcf STATUS.Set COUNT back to 0 bcf INTCON.Move the contents of Count into W movwf PORTA .*******************Now Send The Value Of COUNT To Port A loop movf COUNT.Dson 28 org 0x00 .Now move it to Port A goto loop .0 .RB0 Interrupt Enable (1=enable) bcf INTCON.If COUNT is <10.0 .TrisA register address TRISB EQU 0x86 . and put the result .*******************SETUP CONSTANTS******************* INTCON EQU 0x0B .Status register address COUNT EQU 0x0c .Come back to Bank 0 .This is where we come on power up and reset .4 .5 .***************INTERRUPT ROUTINE*************** org 0x04 .

reset lại hay vẫn để cho nó bị kẹt không thoát ra được ?.Dson 29 Sơ đồ mạch: Bên dưới là sơ đồ mạch mà nó sẽ làm việc với đoạn code bên trên. và ngay cả bạn đã cho chạy mô phỏng từng bước. cái này là một chút mẹo vặt. có vẽ như mọi chuyện đều tốt. Mạch watchdog thì không phải là mới mẽ gì. Watchdog Timer: Bây giờ chúng ta bàn về một bộ định thời bên trong Pic gọi là Watchdog Timer. Thứ hai có một mạch chống rung cho các cái Switch. bạn compiled nó thành công. Sau một thời gian chạy thử. Nhưng mà hãy xem một trường hợp: Giả sử chương trình kiểm tra một chân input. bằng MPLAB chẳng hạn. đó là mụch đích của mạch watchdog. từng bước một trên máy tính. có 2 thứ mà sơ đồ mạch đã “ném” ra cho bạn. khi bạn ấn Switch tụ điện sẽ nạp khi bạn nhả Switch ra tụ điện sẽ xả từ từ. thời gian xả của tụ điện sẽ bỏ qua các lần rung của Switch. nó có thể sẽ bị thay đổi tuỳ theo cấu hình của mạch điện. có rất nhiều microprocessors và microcontrollers đã có mạch watchdog. con Pic thình lình bị kẹt vào nơi nào đó trong chương trình mà không thể thoát ra được trạng thái hiện tại. vì khi bạn ấn Switch nó sẽ bị rung. giả sử như bạn viết một chương trình. vậy Watchdog Timer là cái gì? Giả sử bạn viết một chương trình. con Pic sẽ ngồi đó chờ và nó sẽ chỉ thoát ra khỏi chỗ ngồi của nó nếu chân input thứ hai lên mức cao. bởi vì chúng ta sử dụng các điện dung tản mạn giữa chân dao động của con Pic và mass trên mạch điện để thay thế các tụ điện mắc trong mạch dao động. . Điều gì là cần thiết để giải quyết hai trường hợp trên. như vậy bạn phải làm một vòng lặp để khi chương trình chạy đến điểm cuối thì nó lại quay trở về điểm bắt đầu. như vậy điện trở và điện dung tản mạn trên mạch tạo thành khung dao động RC. nếu chân input thứ hai không lên mức cao. Bây giờ hãy xem một trường hợp khác. thứ nhất là mạch này không có tụ điện trong mạch dao động. bạn mong đợi chương trình này sẽ chạy nếu không có gì trục trặc xảy ra thì nó sẽ không bao giờ dừng lại. cái này thật sự cần thiết. Với mạch chống rung này. nhưng mà nó làm việc ra sao?. lúc đóng lúc hở và con Pic có thể hiểu nhầm rằng bạn đã ấn Switch rất nhiều lần. bạn đem nạp vào con Pic. nếu nó lên mức cao thì con Pic sẽ tiếp tục kiểm tra một chân input thứ hai có lên mức cao hay không.

Khi con Pic bị kẹt không thể thoát ra khỏi tình trạng hiện tại thì WDT vẫn tiếp tục đếm mà không bị bất kỳ điều gì ngăn cấm nó đếm tới FF và đến FF+1. Mặc nhiên prescaler được gán cho một bộ định thời khác. nguồn cung cấp. tuy nhiên nó cũng phụ thuộc vào vài yếu tố. Giá trị gần nhất mà ta có theo bảng trên là 576mS hoặc 0. Cuối cùng.This is the Option Register Quá đơn giản !.1Seconds 2. vì vậy ta phải thay đổi toàn bộ WDT. Để sử dụng WDT chúng ta cần làm 3 việc.This is 0x05 in Hex . có một mẹo nhỏ. mạch này cung cấp 1 xung Clock độc lập với bất kỳ xung Clock nào cung cấp cho Pic. như sau: movlw movwf b’101’ 81h . chúng ta chia RC Clock càng nhiều thì thời gian WDT reset càng dài. Khi Watchdog Timer (viết tắt là WDT) được cho phép (enabled). Bcf Clrf STATUS.make sure we are in Bank 0 . bên dưới là bảng chia tỷ lệ thời gian WDT. Prescaler nằm trên thanh ghi OPTION có địa chỉ 81h từ bit0 đến bit2. Bây giờ bạn hãy xem từng cái một: Trong Datasheet của con Pic có nói rằng. Bên trong con Pic có một cái gọi là Prescaler tạm dịch là đặt tỷ lệ. nó xác định bằng thời gian thực chứ không phải đếm chu kỳ xung clock. Đầu tiên chúng ta gởi giá trị b’101’ tới thanh ghi OPTION.Dson 30 Bên trong con Pic có một mạch RC. vì vậy nó sẽ reset con Pic làm cho chương trình phải khởi động lại từ đầu. Hãy xem ví dụ WDT sẽ reset con Pic trong khoảng ½ giây khi con Pic bị kẹt. nhiệt độ của con Pic bởi vì mạch dao động của WDT là RC. làm sao xoá WDT?. chỉ có 1 cách là ngăn không cho WDT đếm tới 00. chúng ta có thể lập trình để chia xung Clock của mạch RC. khi nó tăng từ FFh đến 00 ( FFh+1) thì con Pic sẽ bị Reset bất kể đang làm gì.address of the other timer – TMR0 . Trước tiên phải reset một bộ đếm khác tới giá trị 0. Thứ nhất. nó sẽ đếm bắt đầu từ 00 và tăng lên 1 cho đến FFh. đoạn code bên dưới với xx là giá trị ta sẽ chọn cho prescaler. chúng ta phải nói cho con Pic biết chương trình cho phép WDT hoạt động. bây giờ.3Seconds Hãy nhớ rằng các khoảng thời gian này không phụ thuộc vào tần số xung Clock bên ngoài. cần thời gian bao lâu để reset WDT?. sau đó chuyển sang Bank1 để gán prescaler cho WDT và thiết lập thời gian rồi sau đó lại quay về Bank0. WDT có thời gian từ lúc Start cho đến khi kết thúc là 18ms.576 seconds. Tuy nhiên chúng ta cũng có thể làm cho thời gian dài hơn.0 01h . Bit 2 0 0 0 0 1 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 0 1 0 1 0 1 Rate 1:1 1:2 1:4 1:8 1:16 1:32 1:64 1:128 WDT Time 18mS 36mS 72mS 144mS 288mS 576mS 1. Thứ hai.

1 chu kỳ. giả sử tất cả các lệnh đều mất 1 chu kỳ.Select the new prescaler value and assign .0 .Dson 31 Bsf Clrwdt movlw movwf bcf STATUS. Cách để nhớ thì hoàn toàn đơn giản. ví dụ: Lệnh DECFSZ sẽ giảm giá trị trong thanh ghi F xuống 1. lệnh GOTO mất 2 chu kỳ bởi vì nó làm cho Program Counter nhảy tới nơi nào đó trong chương trình. BTFSC và BTFSS. vì vậy khi ta đặt 02 vào COUNT thì chương trình này mất 7 chu kỳ.reset the WDT and prescaler . ví dụ: lệnh MOVWF mất 1 chu kỳ bởi vì lệnh này chỉ mang data từ nơi này sang nơi khác. cái này mất 1 chu kỳ. Nếu chương trình của bạn dài. Bây giờ chúng ta phải tìm cho ra đoạn code của chúng thực thi trong thời gian thực là bao lâu. Ví dụ bạn sử dụng giá trị default mặc nhiên là 18ms thì phải bảo đảm rằng chương trình sẽ nhìn thấy lệnh CLRWDT sau mỗi 18ms. đầu tiên nó giảm COUNT xuống 1. Tuy nhiên có 4 lệnh mà nó có thể mất 1 hoặc 2 chu kỳ. Lệnh RETURN cũng mất 2 chu kỳ bởi vì nó làm cho PC quay trở về đầu chương trình. vì vậy nó đi tiếp tới lệnh kế. Để rõ ràng hơn. hãy xem ví dụ bên dưới Movlw movwf decfsz goto end 02 COUNT COUNT loop loop Lệnh đầu tiên mov giá trị 02 vào thanh ghi W. trong trường hợp này lệnh thực thi mất 2 chu kỳ lý do là nó thay đổi giá trị của PC. Lệnh thứ 3. bạn hãy xem trong tập lệnh của Pic để biết thêm chi tiết. vì vậy nó mất 1 chu kỳ. lệnh thứ hai cũng tương tự. nhưng mà nếu lệnh đó làm cho chương trình nhảy tới nơi nào đó thì sẽ mất 2 chu kỳ. trong trường hợp đầu tiên thì chưa xảy ra COUNT =0 . nó mất 1 chu kỳ. hành động bỏ qua lệnh kế tiếp được thực hiện trong 1 chu kỳ khác. Chúng ta quay trở lại lệnh thứ 3 decfsz COUNT lần này sau khi giảm COUNT xuống 1 thì COUNT =0. một số lệnh chỉ thực thi mất 1 chu kỳ trong khi một số lệnh khác mất 2 chu kỳ để thực thi hoàn toàn. Thời gian thực thi Lệnh (Instruction Timing): Như bạn đã biết.come back to Bank 0 Lệnh CLRWDT là để xoá WDT. những lệnh này có một điểm chung đó là nó sẽ bỏ qua lệnh kế tiếp nếu nó thoả một điều kiện nào đó. nếu thạch anh là 4MHZ thì: . xung nhịp bên trong của Pic được gọi là chu kỳ lệnh. nếu kết quả khác 0 thì lệnh kế tiếp được thực thi. lệnh thứ 4 nhảy đến một cái nhãn.it to WDT . lệnh kế tiếp sẽ bị bỏ qua và nó nhảy đến End chấm dứt chương trình. sau đó nó thử xem COUNT =0 chưa. vì vậy nó mất 2 chu kỳ.switch to Bank 1 . nhưng nếu kết quả là 0 thì lệnh kế tiếp bị bỏ qua để thực thi lệnh đứng sau kế.0 b’1xxx’ OPTION STATUS. chúng ta phải làm điều này trước khi nó reset con Pic. nó cần 1 chu kỳ để thực hiện hàm và 1 chu kỳ nữa để thay đổi PC đến vị trí thoả điều kiện của hàm. đó là DECFSZ. có thể phải đặt hơn 1 lệnh CLRWDT trong chương trình. nguyên lý thì rất đơn giản nhưng mà có thể làm cho bạn dựng cả tóc lên đấy !. INCFSZ. chúng ta cần tính toán nơi nào trong chương trình mà bộ đếm của WDT sẽ tràn để đặt lệnh CLRWDT trước thời điểm này để bảo đảm con Pic không reset. nếu dùng thạch anh 4MHz thì 1 chu kỳ lệnh là 1/(4MHz/4) = 1uS.

3. 1 cycle. có 2 cách để làm. To tell the programming software manually. Port B address. which is linked on my main page. TIME equ 9FH PORTB equ 06H TRISB equ 86H PORTA equ 05H TRISA equ 85H STATUS equ 03H COUNT1 equ 0CH COUNT2 equ 0DH bsf STATUS. and the number of clock cycles at each line. . a running total of the time from the start (assuming a 8KHz clock). . BTFSC và BTFSS. .5 movlw 00H .5 movlw 00H movwf TRISB movlw 00H movwf TRISA bcf STATUS. by clicking on the box next to it. As I am using the PICALLW software. and you can easily see when the PIC is reset. then right to left. Port B Tristate address. Port A Tristate address. 0.Dson 32 1/(4MHz/4) = 1uS / chu kỳ 7 chu kỳ mất 7 x 1uS = 7uS Như vậy khi viết chương trình liên quan đến thời gian thực thi. This clock speed will allow us to actually see the LEDs moving one by one. I will explain how to do change fuses within this program. I chose this program because it is slow enough for us to play with the WDT. Bên trong con Pic có một thứ gọi là ‘Fuses’ tạm dịch là cầu chì. Port A address. to show that the program works. 2. 1 cycle. Then you can select the fuse you want enabled.0mS . . bạn phải tính toán cẩn thận khi dùng các lệnh DECFSZ. The documentation that came with the programmer should tell you how to do this.0mS . Page select register. and then remove the CLRWDT command to show that the PIC will indeed reset. 1.5mS . Sample Program Let us write a program.5mS . The program I have chosen is the one used in tutorial 9 where we cause a row of LEDs to light up one at a time from left to right. Loop register. 1 cycle. Variable for the delay loop. . Cách thứ hai là nói cho con Pic biết cái fuses nào được enable. when we look at including other files and macros. . 1 cycle.5mS . Cách thứ nhất là viết 2 dòng lệnh tại phần đầu chương trình để nói cho Pic biết enable hay disable cái fuses nào đó. 1. varies from program to program. . Loop register. .5mS . I have removed the original comments. 1 cycle. The circuit is shown below. and I have replaced them with a description of the WDT lines. and with the RC values shown will give us a clock frequency of 8KHz. 1 cycle. nó không giống như cầu chì fuses bảo vệ của ổ điện nhà mà nó giống như một cái Switch điện tử được đóng hay mở bởi lập trình viên. . INCFSZ.0mS . We will first of all periodically clear the WDT. We will look at getting your program to instruct the programming software in a later tutorial. 3. 1 cycle. where we will turn on the WDT. 2. and let the PIC perform a function. or clicking on the ‘Config’ button. in this case the WDT. The fuses are configured by pressing the F3 key. Làm sao mà những cái Fuses này được đóng hay mở để cho WDT hoạt động.

22S call DELAY .82S rlf PORTB. 2 cycles. 8.78S rlf PORTB. 2. 13. 2 cycles. 1 cycle. 2 cycles. 2 cycles.52S rrf PORTA. 2 cycles. 967mS . 12.11S call DELAY . 1 cycle.5mS movwf PORTB .85S rlf PORTB.1 .0mS . 2 cycles. 12. then pause.70S rlf PORTB. 2 cycles. 10. 2 cycles. 5. 2 cycles. Start of main program RUN movlw 01H .1 . rlf PORTA.1 .48S .1 . 2 cycles. Now move onto Port A.30S call DELAY .0mS call DELAY .93S call DELAY . 5.1 . 1 cycle.1 . 2 cycles. 4.89S call DELAY . 2 cycles. 9.52S call DELAY . 2 cycles. 1 cycle. 1 cycle. 486mS call DELAY . 2 cycles.63S rlf PORTA. 7. 10. 1 cycle. 2 cycles.78S call DELAY . 1 cycle. and move the bit left.74S rlf PORTB.34S call DELAY . 8. 1 cycle. 2 cycles. 3.1 .1 . 1. 1. 8.74S call DELAY .1 .41S call DELAY . 1. 2 cycles. Move the bit back on Port A rrf PORTA. 2. 6. 4. 1 cycle.93S rlf PORTB.1 . 967. 2 cycles. 5. 1 cycle. 11. 2 cycles.59S call DELAY . 7. 3.67S call DELAY .55S call DELAY . rlf PORTB.5mS call DELAY . 9. 1 cycle. 1 cycle. 6. 1 cycle. 1 cycle.37S call DELAY .1 .67S rlf PORTA. Move the bit on Port B left. 4. 7. 7. 2 cycles.99S call DELAY . 11.70S .70S call DELAY . 1 cycle. 12. 6. 2 cycles.59S rlf PORTA. 2 cycles. 1 cycle.45S call DELAY .26S call DELAY .1 .1 . 2 cycles.85S call DELAY .1 . 3.89S rlf PORTB. 5. 11. 2 cycles. 4. 1 cycle.04S call DELAY . 2 cycles.63S call DELAY . 2 cycles.Dson 33 movwf PORTA .48S rrf PORTA. 1 cycle.19S call DELAY . 12.55S .07S call DELAY . 10. 2 cycles. 2 cycles. 13. 9. 4.82S call DELAY .15S call DELAY . 2.1 .

1 cycle . 480mS DELAY movlw TIME .1 .33S rrf PORTB. 19.44S call DELAY . 1 cycle. 1 cycle.44S .1 seconds. 1 cycle. 21. Subroutine to give a delay between bit movements.96S call DELAY .25S call DELAY . 1 cycle movwf COUNT1 .1 .89S call DELAY .40S rrf PORTB. 2 cycles . 2 cycles. 15. 9E x 2 cycles = 316 cycles . 18. and the next one down form this is 1. Now.92S call DELAY . 2 cycles. We could make a call to a subroutine to clear .44S rrf PORTA. 1 cycle. 19. 2 cycles. 17. we need to periodically reset the WDT. and it takes a total of about 21 seconds to run from one end to the other and back again i.25S rrf PORTB. The delay routine takes 480mS. 15.18S goto RUN .22S call DELAY . 18. 20.84S call DELAY .37S rrf PORTB. . 2 cycles. 15.1 . With an 8KHz clock.Total of 957 cycles. 2 cycles.29S call DELAY . 17. 9E x 2 cycles = 316 cycles .40S call DELAY . 2 cycles. 14. 14. 1 cycle.3 seconds. 2 cycles. 14. 2 cycles. The largest time we can set the WDT is 2.70S call DELAY . We have two options here. 16. 1 cycle.Dson 34 call DELAY . 2 cycles. 17. 1 cycle LOOP1 decfsz COUNT1 goto LOOP1 movwf COUNT1 LOOP2 decfsz COUNT1 goto LOOP2 return END . 2 cycles.18S . 21. 19.1 . 20. 2 cycles. it takes just under 1 second for the next LED illuminates. 1 cycle.1 . 13. 9F x 1 cycle + 1 cycle = 160 cycles . 18.33S call DELAY . 9F x 1 cycle + 1 cycle = 256 cycles . 2 cycles. 2 cycles.22S rrf PORTB. . 2 cycles.1 .73S call DELAY .81S call DELAY . 16.77S call DELAY . to go through the routine once only.37S call DELAY . 2 cycles. 2 cycles.29S rrf PORTB. 20. 14. 1 cycle.1 . Now move the bit back on Port B rrf PORTB. 16. 2 cycles.e. and we are calling it twice before moving the bit on the Ports. .1 .

. rlf PORTB. .************** rlf PORTB. call DELAY . Option Register to control the WDT .5 00H PORTA . call DELAY .Switch to Bank 1 clrwdt . TIME PORTB TRISB PORTA TRISA STATUS COUNT1 COUNT2 OPT equ equ equ equ equ equ equ equ equ 9FH 06H 86H 05H 85H 03H 0CH 0DH 81h .Come back to Bank 0 . movwf PORTB . . Variable for the delay loop. call DELAY .1 . . Loop register. . call DELAY . then pause. rlf PORTB.*************Set up the Ports.1 . call DELAY . or we could incorporate the CLRWDT within the delay itself. Page select register. . . Port A Tristate address. call DELAY . *************Move the bit on Port B left. Now set up the Ports . .1 . . WDT and prescaler****************** clrf 01h . to incorporate the CLRWDT within the delay loop.*************Start of main program***************************** RUN movlw 01H . I have decided. call DELAY .Select the new prescaler value and assign movwf OPT . Loop register. call DELAY . for no real reason at all.1 . rlf PORTB. . call DELAY . rlf PORTB. Port B address.5 . .reset the WDT and prescaler movlw b’1101’ . . Port B Tristate address.Dson 35 the WDT after the two delays have finished.1 . call DELAY . Port A address. . call DELAY . call DELAY .it to WDT movlw movwf movlw movwf bcf movlw movwf 00H TRISB 00H TRISA STATUS. . .Clear TMR0 bsf STATUS.

1 . call DELAY . rrf PORTA. rlf PORTA.1 . rrf PORTB. call DELAY . call DELAY .1 . .Dson 36 rlf PORTB. call DELAY . rlf PORTA. call DELAY .1 . rrf PORTA.1 .************** Move the bit back on Port A************************ rrf PORTA. call DELAY . call DELAY . call DELAY . call DELAY . rrf PORTB. call DELAY . call DELAY . call DELAY .*********** rlf PORTA.****************** Now move the bit back on Port B****************** rrf PORTB.1 . call DELAY . call DELAY . rrf PORTB. rrf PORTB. call DELAY . *************Now move onto Port A. call DELAY . call DELAY . call DELAY . . call DELAY . call DELAY . call DELAY .1 . rlf PORTB.1 . call DELAY .1 .1 . call DELAY . call DELAY . call DELAY . .1 . call DELAY .1 . call DELAY .1 . rrf PORTB. call DELAY . .1 . and move the bit left. rlf PORTA.1 . rlf PORTB.1 . call DELAY . rrf PORTB. rrf PORTA. call DELAY .1 .1 .

LOOP1 decfsz COUNT1 goto LOOP1 movwf COUNT1 LOOP2 decfsz COUNT1 goto LOOP2 . .................. ...***************Return from our original DELAY routine*************** return .. clrwdt . This part resets the WDT ........ ...... movwf COUNT1 ........ ....... .............. or remove the CLRWDT command.......... ............. It should reset the PIC .This simply resets the WDT..................... ............ in action......... .................... ... ..... ........ If you comment out.. With the CLRWDT in place........... END ...... ... you will find that the PIC will not go past lighting the second LED.........Comment out or remove this command to see the WDT ....... ... This is because the WDT is resetting the PIC...Dson 37 call call goto DELAY DELAY RUN ...****** DELAY movlw TIME .......... .... the program works as it should.......... ......... ******************Subroutine to give a delay between bit movements..........