Hiển thị các bài đăng có nhãn LEARNING. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn LEARNING. Hiển thị tất cả bài đăng

Thứ Hai, 3 tháng 12, 2018

CHƯƠNG 8 : LÀM VIỆC VỚI NGẮT


I / CƠ CHẾ HOẠT ĐỘNG CỦA NGẮT :
1 / _ Ngắt 1 cấp :
_Trên PIC 14 , 12 ,10 ,tất cả các ngắt chỉ có 1 cấp ưu tiên . Nghĩa là ngắt nào đang được phục vụ thì không thể bị ngắt bởi 1 ngắt khác xảy ra . Cơ chế sinh mã cho ngắt của CCS như sau : nhảy đến địa chỉ ngắt , thường là 004h , sao lưu thanh ghi W, STATUS , PCLATCH , FSR, và nhiều thứ vớ vẫn khác, sau đó nó mới hỏi vòng xem cờ ngắt nào xảy ra thì nhảy đến hàm phục vụ ngắt đó . thực hiện xong thì phục hồi tất cả thanh ghi trên , rồi mới “RETFIE” – thoát ngắt . Số chu kỳ thực thi từ chỗ ngắt đến khi nhảy vào hàm ngắt cỡ 20 chu kỳ lệnh !, nhảy ra cũng cỡ đó .
_Điều gì xảy ra nếu chương trình dùng nhiều ngắt và khicó ngắt thì có 2 ngắt trở lên xảy ra đồng thời ? Nghĩa là : 2 ngắt xảy ra cùng lúc , hay khingắt A kích hoạt và CCS đang lưu các thanh ghi ( chưa tới hỏi vòng cờ ngắt ) thì ngắt B xảy ra , dĩ nhiên ngắt B không thể kích vector ngắt nhảy tới 004h vì bit cho phép ngắt toàn cục ( GIE ) bị khóa tự động khi có ngắt , chỉ có cờ ngắt B bật mà thôi. Sau khi lưu các thanh ghi , chương trình kiểm tra cờ ngắt , rõ ràng là nếu bit nào được kiểm tra trước thì phục vụ trước , dù nó xảy ra sau . Để tránh phục vụ không đúng chỗ , bạn dùng #priority để xác định ưu tiên ngắt ( xem phần chỉ thị tiền xử lý) . Ngắt ưu tiên nhất sẽ luôn được hỏi vòng trước .Sau khi xác định cờ ngắt cần phục vụ , nó sẽ thực thi hàm ngắt tương ứng .Xong thì xoá cờ ngắt đó và thoát ngắt . Phục vụ ngắt nào xong thì chỉ xoá cờ ngắt đó .Nếu A ưu tiên hơn B thì sau khi làm A , chương trình xoá cờ ngắt A , nhưng cờ B không xoá ( vì đâu có phục vụ ) , nên khi thoát ra ngắt A , nó sẽ lại ngắt tiếp ( vì cờ B đã bật ), lại hỏi vòng cờ ngắt từ đầu : nếu cờ A chưa bật thì xét B, lúc này B bật nên phục vụ B , xong thì xoá cờ B và thoát ngắt .
_Môt chương trình dùng nhiều ngắt phải lưu ý điều này , tránh trường hợp : ngắt xảy ra liên tục (tràn ngắt ) , 1 ngắt bị đáp ứng trễ , ngắt không đúng , . . .
2 / _ Ngắt 2 cấp :
_Chỉ có trên PIC 18 ( và dsPIC ) . Có 2 khái niệm : ngắt ưu tiên thấp (low priority) và ngắt ưu tiên cao ( high priority ) . 2 vector thực thi ngắt tương ứng thường là 0008h (high) và 0018h ( low ) . Một ngắt thấp đang được phục vụ sẽ bị ngưng và phục vụ ngắt cao ở 0008h nếu ngắt cao xảy ra . Ngược lại , ngắt cao đang xảy ra thì không bao giờ bị ngắt bởi ngắt thấp .
_Nếu viết hàm ngắt bình thường , không đòi hỏi ưu tiên gì thì CCS sinh mã để tất cả hàm ngắt đều là ngắt ưu tiên cao . Quy trình thựchiện ngắt sẽ như ngắt 1 cấp trên . #priority vẫn được dùng . Số chu kỳ thực thi từ 0008h đến khi nhảy vào thực thi hàm ngắt khoảng 30 chu kỳ , xong hàm ngắt tới khi kết thúc ngắt cũng mất khoảng 30 chu kỳ lệnh .
_Để sử dụng ngắt 2 cấp , khai báo #device phải có high_ints=true . Và hàm ngắt nào muốn ưu tiên cao thì thêm FAST theo sau chỉ thị tiền xử lý hàm đó . Lưu ý : chỉ có duy nhất 1 ngắt được ưu tiên cao ,đây có lẽ là hạn chế của CCS , do cách thức sinh mã .
VD :  #int_timer1 FAST
 Void xu_ly ( )    { . . .
}
_Cơ chế sinh mã như sau : có ngắt thấp thì nhảy tới 0018h , sao lưu W, STATUS , FSR0/1/2 ,. . . rồi mới hỏi vòng cờ ngắt thấp . chạy xong hàm ngắt thì phục hồi tất cả và “RETFIE 0 “ . Riêng ngắt 28 cao không sinh mã sao lưu gì cả mà nhảy thẳng vào hàm ngắt chạy luôn . Vậy thì trật lất rồi ? Mã chạy sai chăng ?
_Thực ra không phải vậy . PIC 18 và dsPIC có cơ chế lưu siêu tốc là FAST STACK REGISTER ( xem datasheet kỹ nhé ) . Khi xảy ra ngắt bất kỳ , W, S , BSR tự động lưu vào thanh ghi trên , PC counter lưu vào stack . xong ngắt thì pop ra . Vấn đề ở chỗ : khi ngắt thấp xảy ra , FAST STACK REGISTER tự động lưu W ,S , BSR , PC -> stack . Trong khi thực hiện hàm phục vụ ngắt thì trường hợp W, S , BSR thay đổi là có thể ( vì vậy mới sao lưu chứ ) . nhưng nếu xảy ra ngắt cao vào thời điểm đó ? FAST STACK REGISTER sẽ bị ghi đè -> mất data . Do đó , cơ chế sinh mã của CCS cần phải luôn đúng , nghĩa là : luôn tự sao lưu riêng W ,S , BSR, và các thanh ghi FSR nữa , khi thực thi ngắt thấp . Còn ngắt cao khi chạy xong sẽ “RETFIE 1 “ – tự động phục hồi W, S , BSR từ FAST STACK REGISTER. Có 2 trường hợp : 1 là chỉ có ngắt cao , thì không có vấn đề gì . 2 là ngắt cao ngắt 1 ngắt thấp đang chạy . Phân tích sẽ thấy rằng cho dù bị ngắt trong khi đang sao lưu ,hay chưa kịp sao lưu , hay đã sao lưu vào các biến riêng rồi , cuối cùng chương trình cũng quay ra đúng địa chỉ
ban đầu với các thanh ghi W, S , BSR như cũ .
_Tuân thủ nguyên tắc ngắt cao thực thi tức thời nên CCS chỉ cho 1 ngắt cao duy nhất bất kỳ hoạt động , nên không sinh mã hỏi vòng , sao lưu thêm gì cả . nếu bạn muốn có nhiều ngắt ưu tiên cao , thì phải tự viết mã riêng thôi ( khi có ngắt cao thì hỏi vòng các cờ ngắt , dùng lệnh ORG chiếm đoạn mã từ 0008h trở đi để viết mã xử lý riêng , trong chương trình không được viết bất kỳ hàm ngắt nào kể cả ngắt thấp mà chỉ viết hàm bình thường , . . . nói chung là tự xử lý hết mọi vấn đề ngắt , phức tạp lắm đấy ) .
II / KHAI BÁO NGẮT :
_Mỗi dòng VDK có số lượng ngắt khác nhau : PIC 14 có 14 ngắt , PIC 18 có 35 ngắt .
_Muốn biết CCS hỗ trợ những ngắt nào cho VDK củabạn , mở file *.h tương ứng , ở cuối file là danh sách các ngắt mà CCS hỗ trợ nó . Cách khác là vào CCS -> View -> Valid interrupts , chọn VDK muốn xem , nó sẽ hiển thị danh sách ngắt có thể có cho VDK đó .
_Sau đây là danh sách 1 số ngắt với chức năng tương ứng : 
#INT_GLOBAL  : ngắt chung , nghĩa là khi có ngắtxảy ra , hàm theo sau chỉ thị này được thực thi , bạn sẽ không được khai báo thêm chỉ thị ngắt nào khác khi sử dụng chỉ thị này . CCS không sinh bất kỳ mã lưu nào , hàm ngắt bắt đầu ngay tại vector ngắt . Nếu bật nhiều cờ cho phép ngắt , có thể bạn sẽ phải hỏi vòng để xác định ngắt nào . Dùng chỉ thị này tương đương viết hàm ngắt 1 cách thủ công mà thôi , như là viết hàm ngắt với ASM vậy . 
  • #INT_AD   : chuyển đổi A /D đã hoàn tất , thường thì không nên dùng
  • #INT_ADOF   : I don’t know
  • #INT_BUSCOL  : xung đột bus
  • #INT_BUTTON  : nút nhấn ( không biết hoạt động thế nào )
  • #INT_CCP1   : có Capture hay compare trên CCP1
  • #INT_CCP2   : có Capture hay compare trên CCP2
  • #INT_COMP   : kiểm tra bằng nhau trên Comparator
  • #INT_EEPROM  : hoàn thành ghi EEPROM
  • #INT_EXT   : ngắt ngoài
  • #INT_EXT1   : ngắt ngoài 1
  • #INT_EXT2   : ngắt ngoài 2
  • #INT_I2C   : có hoạt động I 2C
  • #INT_LCD   : có hoạt động LCD
  • #INT_LOWVOLT  : phát hiện áp thấp
  • #INT_PSP   : có data vào cổng Parallel slave
  • #INT_RB   : bất kỳ thay đổi nào trên chân B4 đến B7
  • #INT_RC   : bất kỳ thay đổi nào trên chân C4 đến C7
  • #INT_RDA   : data nhận từ RS 232 sẵn sàng
  • #INT_RTCC   : tràn Timer 0
  • #INT_SSP   : có hoạt động SPI hay I 2C
  • #INT_TBE   : bộ đệm chuyển RS 232 trống
  • #INT_TIMER0  : một tên khác của #INT_RTCC
  • #INT_TIMER1  : tràn Timer 1
  • #INT_TIMER2  : tràn Timer 2
  • #INT_TIMER3  : tràn Timer 3
  • #INT_TIMER5  : tràn Timer 5
  • #INT_OSCF : lỗi OSC
  • #INT_PWMTB : ngắt cuả PWM time base
  • #INT_IC3DR : ngắt đổi hướng ( direct ) của IC 3
  • #INT_IC2QEI : ngắt của QEI
  • #INT_IC1 : ngắt IC 1
_Hàm đi kèm phục vụ ngắt không cần tham số vì không có tác dụng .
_Sử dụng NOCLEAR sau #int_xxx để CCS không xoá cờ ngắt của hàm đó .
_Để cho phép ngắt đó hoạt động phải dùng lệnh enable_interrupts ( int_xxxx) và enable_interrupts ( global ) .
_Khoá FAST theo sau #int_xxxx để cho ngắt đó là ưu tiên cao, chỉ được 1 ngắt thôi , chỉ có ở PIC 18 và dsPIC .
VD : #int_timer0 FAST NOCLEAR
III / CÁC HÀM THIẾT LẬP HOẠT ĐỘNG NGẮT :
1 / enable_interrupts ( level )
_level là tên các ngắt đã cho ở trên hay là GLOBAL để cho phép ngắt ở cấp toàn cục .
_Mọi ngắt của VDK đều có 1 bit cờ ngắt , 1 bit cho phép ngắt . Khi có ngắt thì bit cờ ngắt bị set =1, nhưng ngắt có họat động được hay không tuỳ thuộc bit cho phép ngắt . enable_interrupts (int_xxx ) sẽ bật bit cho phép ngắt . Nhưng tất cả các ngắt đều không thể thực thi nếu bit cho phép ngắt toàn
cục = 0 , enable_interrupts( global ) sẽ bật bit này .
VD : để cho phép ngắt timer0 và timer1 hoạt động:
enable_interrupts (int_timer0);
enable_interrupts (int_timer1 ) ;
enable_interrupts ( global );   // chỉ cần dùng 1 lần trừ phi muốn có thay đổi đặc biệt
2 / disable_interrupts ( level )
_level giống như trên .
_Hàm này vô hiệu 1 ngắt bằng cách set bit cho phép ngắt = 0 .
_disable_interrupts ( global ) set bit cho phép ngắt toàn cục =0 , cấm tất cả các ngắt .
_Không dùng hàm này trong hàm phục vụ ngắt vì không có tác dụng , cờ ngắt luôn bị xoá tự động .
3 / clear_interupt ( level )
_level không có GLOBAL .
_Hàm này xoá cờ ngắt của ngắt được chỉ định bởi level .
4 / ext_int_edge ( source , edge )
_Hàm này thiết lập nguồn ngắt ngoàiEXTx là cạnh lên hay cạnh xuống .
_source : nguồn ngắt . Trên PIC 18 có 3 nguồn ngắt trên 3 chân EXT0 , EXT1 , EXT2 ứng với
source = 0 ,1 , 2 . Các PIC khác chỉ có 1 nguồn EXT nên source = 0 .
_edge : chọn cạnh kích ngắt , edge = L_TO_H nếu chọn cạnh lên ( từ mức thấp chuyển lên mức
cao ) hay H_TO_L nếu chọn cạnh xuống .
IV / CÁC CHƯƠNG TRÌNH VD VỀ NGẮT :
1 / _ #INT_RB :
_Sau đây là 1 chương trình điển hình về sử dụng ngắt khi có sự thay đổi trên chân B4-B7 .
_Mô tả : mỗi khi nhấn nút bất kỳ trên B4-B7 , sẽ kích ngắt RB , hàm phục vụ ngắt có tên RB_LED được thực thi , hàm này đơn giản là xuất ra LED ở vị trí tương ứng nhưng trên portD từ D4 – D7 .
_VDK là 16F877 .
                                                                                               
#include < 16F877.h >
#device PIC16F877 *=16
#use delay (clock = 20000000 )  //thêm khai báo này nếu ctrình có dùng hàm delay,OSC=20 Mhz
#byte portb = 0x06      //tạo tên danh định portb thay thế địa chỉ portB là 06h
#byte portd = 0x08      //tạo tên danh định portd thay thế địa chỉ portD là 08h 
#INT_RB
Void RB_LED ( )      // hàm phục vụ ngắt
{
portd=portb;
}
void main ( )
{  set_tris_b ( 0xF0 ) ;    // portB = 11110000 , B4-B7 là ngõ vào , B0-B3 là ngõ ra
set_tris_d ( 0x00 ) ;    // portD = 00000000 , D0-D7 đều là ngõ ra
enable_interrupts ( INT_RB ) ;  // cho phép ngắt RB
enable_interrupts ( GLOBAL ) ;  // cho phép ngắt toàn cục
// do chương trình không làm gì khác ngoài việc chờ ngắt nên vòng while này trống không
while( true )
{ //có thể thêm mã xử lý ở đây . . .
}
} //main 

CHƯƠNG 7 : MODULE PWM / CAPTURE / COMPARE


I/ _TỔNG QUÁT PHẦN CỨNG:
_Module này có mặt ở hầu hết các dòng PIC 16 và PIC 18 , và thường chỉ có 2 chân cho module này, ký hiệu là CCP1 / CCP2 . Cần phân biệt với module PWM chuyên dụng trên PIC 18 và dsPIC vốn có 6 tới 8 chân PWMx , cũng phục vụ cho điều chế độ rộng xung nhưng chuyên dụng cho điều khiển
động cơ AC . Một số PIC 14 và PIC 18 có module ECCP cũng dùng module CCP này nhưng lại chuyên dụng cho điều khiển bộ biến đổi áp DC->DC , dùng cho điều khiển động cơ DC . Ở đây chưa đề cập đến ECCP . Chỉ đề cập CCP , và thường chỉ có 2 chân là CCP1 và CCP2 .
_Module có 3 chức năng và khi hoạt động ta chỉ dùng được 1 chức năng ứng với 1 chân . Ngoài ra
nếu không dùng thì có thể set nó thành chân I /O .
_Mỗi module chứa 1 thanh ghi 16 bit , là kết hợp của 2 thanh ghi 8 bit : CCPR1L(byte thấp ) và
CCPR1H ( byte cao ) của CCP1 , CCPR2L và CCPR2H của CCP2 .
_Mỗi chức năng của CCPx đều đòi hỏi 1 bộ đếm để hoạt động : Capture / Compare đòi hỏi Timer1 ,
còn PWM đòi hỏi Timer2 .                        → Bảng trên ( trong datasheet PIC16F877 ) cho thấy : nếu cả 2 module dùng cùng chức năng Capture
thì sẽ dùng chung bộ đếm timer1 , nghĩa là mọi họat động và điều chỉnh Timer1 đều ảnh hưởng tới
cả 2 module . Tương tự nếu dùng cùng chức năng PWM sẽ dùng chung Timer2 . Còn nếu 1 module
là Capture , module kia là Compare thì phải lưu ý là chức năng compare có thể clear Timer1 , và do
đó cũng ảnh hưởng chức năng còn lại . Còn 2 TH cuối thì không có vấn đề gì .
_Ở chế độ Capture : mỗi khi có cạnh lên của xung vào chân CCPx thì giá trị Timer1 (16 bit )sẽ được
copy vào thanh ghi CCP ( 16 bit ) .
_Ở chế độ Compare : khi giá trị nạp CCP bằng giá trị đếm Timer1 thì các sư kiện được chỉ định
trước xảy ra :chân CCPx được láira mức thấp / cao / không có gì nhưng có ngắt hay biến đổi AD .
_Chế độ PWM ( pulse width modulation – điều chế độ rộng xung ) : xuất xung vuông bằng phần
cứng .
_Bạn sử dụng hàm setup_ccpX() để xác định chức năng muốn dùng trên module CCP . tham số cụ
thể có thể tra trong HELP , hoặcxem từng TH cụ thể dưới đây .
II / _CHẾ ĐỘ CAPTURE :
_Dùng để xác định tốc độ quay của motor . Giả sử ta có 1 thiết bị đo tốc độ quay ( cảm biến Hall )
mà mỗi khi motor quay 1 vòng thì thiết bị phát 1 xung vuông ( cạnh lên rồi cạnh xuống hay xung
mức 1 , hay cạnh xuống rồi cạnh lên tức là xung mức 0 ) . Ở đây ta giả thiết xung mức 1 . Xung này
dẫn vào module capture ( có thể qua cách ly an toàn ) và giả thiết ta chọn chế độ bắt cạnh lên .
Nghĩa là cứ mỗi 1 cạnh lên của xung vào , giá trị của timer1 copy vào CCP . Vì motor không phải
quay đều nên ta thường chọn số lần capture là vài chục lần .
_Việc Capture thường đi kèm 1 cái hàm ngắt . Cách thức chương trình hoạt động như sau : xung vào
Capture sẽ kích ngắt , trong hàm ngắt ,ta lấy giá trị CCPx tính ra được thời gian cho 1 vòng quay
,cộng dồn nó vào 1 biến để tính thời gian tổng , set Timer1 về 0 , tăng giá trị biến đếm vòng lên 1 ,
rồi thoát ngắt . Với 1 động cơ đang chạy khoảng 1200vòng / phút , tuỳ thuộc vào tần suất cập nhật
giá trị số vòng quay ( để hiển thị raLED 7 đoạn hay LCD ) bạn cần đo 1 số lượng vòng nhất định
để đảm bảo chính xác , VD đo 1500 vòng , tức là biến đếm phải đếm tới 1500 , tính tổng thới gian
đó , VD là 80 s , thì tốc độ động cơ = ( 1500 / 80 ) * 60 = 1125 vòng / phút .
_Hãy quên module này đi nếu bạn dùng 1 encoder để đo tốc , vốn phát ra tới 500-2000 xung / vòng
.Nghĩa là cần chừng đó lần ngắt để chỉ đo 1 vòng → VDK sẽ phải dùng toàn bộ thời gian hoạt động
để đếm , có khi không kịp . Việc chương trình chính không thể chạy vì ngắt cứ xảy ra liên tục gọi là
tràn , nó sẽ chẳng làm được gì cả . Để giải quyết , người tadùng module QEI hay IC3 vốn chỉ có
trên PIC 18 như 18F4431 .
_Capture còn dùng để đo độ rộng xung . Sử dụng CCP1 lấy cạnh lên , CCP2 lấy cạnh xuống của
cùng 1 xung ,tức là 2 CCP nối nhau . 1 xung vuông đi vào kích CCP1 trước ( cạnh lên ), thu được giá
trị timer lúc , cạnh xuống của xungkích CCP2 thu được giá trị timer lúc xuống cạnh . Lấy 2 giá trị
trừ nhau được độ rộng xung . chỉ cần 1 ngắt CCP2 là đủ .
III / _CHẾ ĐỘ COMPARE :
_Hoạt động : khi timer1 đếm lên tới khi bằng giá trị mà ta trữ trong CCPx, thì chân CCPx sẽ xuất ra
mức cao / thấp / hay không có nhưng có ngắt .
IV / _CHẾ ĐỘ PWM :
_Xuất chuỗi xung vuông , độ rộng hiệu chỉnh được dễ dàng . Thường dùng để điều chỉnh điện áp
DC . Xung ra sẽ đóng cắt 1 linh kiện như là SCR,với độ rộng xung xác định sẽ tạo ra 1 điện áp
trung bình xác định . Thay đổi độ rộng xung sẽ thayđổi điện áp này , do đó có thể dùng điều khiển
động cơ DCnhỏ 
V / _DANH SÁCH HÀM:
_ CCS luôn tạo sẵn các tên danh định C như là các biến trỏ tới CCP1 và CCP2 là : CCP_1( 16 bit )
CCP_2(16 bit ) , CCP_1_HIGH( byte cao của CCP1 ) , CCP_1_LOW , CCP_2_HIGH ,
CCP_2_LOW , bạn không cần khai báo . Dùng luôn các tên đó để lấy trị khi dùng module Cap , hay
gán trị khi dùng Compare . Bạn có thể thấy điều này khi mở mục RAM symbol mapquan sát phân
bổ bộ nhớ . 
1 / _ Setup_CCPx ( mode ) : 
_Dùng trước tiên để thiết lập chế độ hoạt động hay vô hiệu tính năng CCP .
X= 1,2, . . .tên chân CCP có trên chip .
 + Mode là 1 trong các hằng số sau : ( các hằng số khác có thể có thêm trong file *. h và tuỳ VDK) 
  • CCP_OFF : tắt chức năng CCP, RC sẽ là chân I /O .
  • CCP_CAPTURE_RE  : capture khi có cạnh lên  
  • CCP_CAPTURE_FE  : capture khi có cạnh xuống  
  • CCP_CAPTURE_DIV_4  : chỉ capture sau khi đếm đủ 4 cạnh lên ( 4 xung ).  
  • CCP_CAPTURE_DIV_16  : chỉ capture sau khi đếm đủ 16 cạnh lên ( 16 xung ). 
→ sử dụng để làm dãn thời gian VDK để dành cho công việc khác thay vì cứ update từng xung .
 + Chế độ compare :
  • CCP_COMPARE_SET_ON_MATCH  : xuất xung mức cao khi TMR1=CCPx
  • CCP_COMPARE_CLR_ON_MATCH  : xuất xung mức thấp khi TMR1=CCPx
  • CCP_COMPARE_INT : ngắt khi TMR1=CCPx
  • CCP_COMPARE_RESET_TIMER   : reset TMR1 =0 khi TMR1=CCPx 
  + Chế độ PWM :
  • CCP_PWM : bật chế độ PWM
  • CCP_PWM_PLUS_1    : không rõ chức năng
  • CCP_PWM_PLUS_2   : không rõ chức năng
  • CCP_PWM_PLUS_3   : không rõ chức năng
2 / _ Set_CCPx_duty ( value ) : 
Value : biến hay hằng , giá trị 8 hay 16 bit .
x= 0 ,1 ,2 . . . :tên chân CCPx
_Dùng set duty của xung trong chế độ PWM . Nó ghi 10 bit giá trị vào thanh ghi CCPx . Nếu value
chỉ là 8 bit, nó dịch thêm 2 để đủ 10 bit nạp vào CCPx .
_Tuỳ độ phân giải mà giá trị của value không phải lúc nào cũng đạt tới 1023 . Do đó , value = 512
không có nghĩa là duty = 50 % . 

CHƯƠNG 6 : GIAO TIẾP SPI – I2C VÀ PARALLEL


I / GIAO TIẾP SPI :
ây là giao tiếp dễ dùng nhất , đơn giản nhất , tốc độ cao nhất trong nhóm . hoạt động theo cơ chế 
hand-shaking , bắt tay . Giả sử có 2 VDK , thì 1 là master , 1 là slave . Khi master truyền 1 byte cho 
slave , nó phát 8 xung clock qua đường clock nối tới slave , đồng thời truyền 8 bit data từ chân SDO 
tới chân SDI của slave . Không kiểm tra chẵn lẻ , lỗi . Do đó Vdụ nếu đang truyền được 3 bit mà 
master reset hay hở dây clock thì data bị mất, slave sẽ không nhận đủ 8 bit và do đó nếu tiếp tục 
nhận nó sẽ lấy 5 bit ở byte kế tiếp đưa vào thanh ghi nhận để đủ 8 bit ( và để kích ngắt ) . Từ đó trở 
đi là mọi giá trị nhận là sai bét trừ phi chấm dứt và sau đó thiết lập lại giao tiếp này ( ở cả hai ) .
_Giao tiếp này cần ít nhất 2 dây trở lên . Nếu 1 VDK chỉ cần gởi data thì chỉ cần dây clock và SDO 
.VDK nhận sẽ dùng SDI và dây clock . Dây clock là nối chung .
_Nếu có gởi và nhận ở cả 2 VDK thì : dây clock chung , master có SDO nối tớiSDI của slave , SDO 
của slave nối tới SDI của master .
_Nếu master cần truyền data cho nhiều slave trở lên thì SDO master nối tới các SDI của slave .
_Chân SS là slave select .
_SPI hoạt động từ phần cứng , vì nó có sẵn thanh ghigởi và nhận , nhận đủ giá trị thì có cờ ngắt 
phục vụ .
_Danh sách các hàm : 
1 / Setup_spi (mode )   Setup_spi2 (mode ) 
_Dùng thiết lập giao tiếp SPI . Hàm thứ 2 dùng với VDK có 2 bộ SPI . 
_Tham số mode :là các hằng số sau , có thể OR giữa các nhóm bởi dấu | 
       →SPI_MASTER , SPI_SLAVE , SPI_SS_DISABLED 
       →SPI_L_TO_H , SPI_H_TO_L 
       →SPI_CLK_DIV_4 , SPI_CLK_DIV_16 , SPI_CLK_DIV_64 , SPI_CLK_T2 
_Nhóm 1 xác định VDK là master hay slave ,slave select 
_Nhóm 2 xác định clock cạnh lên hay xuống . 
_Nhóm 3 xác định tần số xung clock , SPI_CLK_DIV_4 ngĩa là tần số = FOSC / 4 , tương ứng 1 chu 
kỳ lệnh / xung . 
_Hàm không trả về trị . 
_Ngoài ra ,tuỳ VDK mà có thêm 1 số tham số khác , xem file * .h .
2 / Spi_read ( data )  Spi_read2 ( data ) 
_data có thể có thêm và là số 8 bit . Hàm thứ 2 cho bộ SPI thứ 2 . 
_Hàm trả về giá trị 8 bit value = spi_read ( ) 
_Hàm trả về giá trị đọc bởi SPI . Nếu value phù hợp SPI_read ( ) thì data sẽ được phát xung ngoài 
và data nhận được sẽ được trả về . Nếu không có data sẵn sàng , spi_read ( ) sẽ đợi data . 
_Hàm chỉ dùng cho SPI hardware ( SPI phần cứng ) .
3 / spi_write ( value )  Spi_write2 ( value ) 
_Hàm không trả về trị . value là giá trị 8 bit . 
_Hàm này gửi value ( 1 byte ) tới SPI , đồng thời tạo 8 xung clock . 
_ Hàm chỉ dùng cho SPI hardware ( SPI phần cứng ) . 
4 / spi_data_is_in ( )   Spi_data_is_in2 ( ) 
_Hàm trả về TRUE ( 1 ) nếu data nhận được đầy đủ ( 8 bit ) từ SPI , trả về false nếu chưa nhận đủ . 
_Hàm này dùng kiểm tra xem giá trị nhận về SPI đã đủ 1 byte chưa để dùng hàm spi_read ( ) đọc 
data vào biến .

CHƯƠNG 5 : NGẮT VỚI VI ĐIỀU KHIỂN PIC CHƯƠNG TRÌNH BIÊN DỊCH CCS


Ngắt đến từ nhiều nguồn gây ra sự kiện ngắt bên trong Pic cũng như các sự kiện gây ngắt do bên ngoài. Khi một ngắt xảy ra, trình xử lý ngắt diễn ra như hình bên dưới

Ngắt với vi điều khiển pic

Thực hiện bởi phần cứng: là quá trình thực thi được thực hiện bởi phần cứng của vi xử lý để xử lý ngắt một cách tự động, không phải do phần mềm.
Thực hiện bởi phần mềm: Là chương trình do ta viết để xử lý ngắt đó. Ví dụ như sao lưu các thanh ghi, hỏi vòng cờ ngắt, nhảy đến thực thi chương trình xử lý ngắt tương ứng 
Quy trình xử lý ngắt diễn ra như sau:
Khi ngắt xảy ra, phần cứng tự động xóa bit cho phép ngắt toàn cục GIE=0, lưu địa chỉ của con trỏ PC vào ngăn xếp stack, nạp địa chỉ 0x04 vào thanh ghi PC , sao lưu thanh ghi W, STATUS..., sau đó nó mới hỏi vòng xem cờ ngắt nào xảy ra thì nhảy đến hàm phục vụ ngắt đó. Sau khi xử lý ngắt xong, chương trình phục hồi lại các thanh ghi trên, thực thi lệnh RETFIE để nạp lại địa chỉ cho thanh ghi PC từ bộ nhớ stack. Bật bit ngắt toàn cục GIE=1 là tiếp tục thực thi công việc tiếp theo tại thời điểm trước khi xãy ra ngắt.
Điều gì xảy ra nếu chương trình dùng nhiều ngắt và khi có ngắt thì có 2 ngắt trở lên xảy ra đồng thời ? Nghĩa là : 2 ngắt xảy ra cùng lúc , hay khi ngắt A kích hoạt và CCS đang lưu các thanh ghi ( chưa tới hỏi vòng cờ ngắt ) thì ngắt B xảy ra , dĩ nhiên ngắt B không thể kích vector ngắt nhảy tới 004h vì bit cho phép ngắt toàn cục ( GIE ) bị khóa tự động khi có ngắt , chỉ có cờ ngắt B bật mà thôi. Sau khi lưu các thanh ghi , chương trình kiểm tra cờ ngắt, rõ ràng là nếu bit nào được kiểm tra trước thì phục vụ trước, dù nó xảy ra sau . Để tránh phục vụ không đúng chỗ, bạn dùng #priority để xác định ưu tiên ngắt . Ngắt ưu tiên nhất sẽ luôn được hỏi vòng trước. Sau khi xác định cờ ngắt cần phục vụ, nó sẽ thực thi hàm ngắt tương ứng. Xong thì xoá cờ ngắt đó và thoát ngắt. Phục vụ ngắt nào xong thì chỉ xoá cờ ngắt đó.  Nếu A ưu tiên hơn B thì sau khi làm A, chương trình xoá cờ ngắt A, nhưng cờ B không xoá ( vì đâu có phục vụ ), nên khi thoát ra ngắt A, nó sẽ lại ngắt tiếp ( vì cờ B đã bật ), lại hỏi vòng cờ ngắt từ đầu : nếu cờ A chưa bật thì xét B, lúc này B bật nên phục vụ B , xong thì xoá cờ B và thoát ngắt. Một chương trình dùng nhiều ngắt phải lưu ý điều này , tránh trường hợp : ngắt xảy ra liên tục (tràn ngắt ), 1 ngắt bị đáp ứng trễ, ngắt không đúng , . . .
Lệnh tiền xử lý #priority để thiết lập thứ tự ưu tiên ngắt:
Ví dụ:
#priority rtcc, rb, tmr0, portb //Ngắt nào đứng trước sẽ được ưu tiên hỏi vòng kiểm tra cờ ngắt trước
Ví dụ một chương trình ngắt nhận dữ liệu cổng nối tiếp trong CCS như sau:
 #INT_GLOBE : Ta sử dụng chỉ thị này để tự mình quản lý xử lý ngắt theo cách riêng. Khi có chỉ thị này chương trình không tự động tạo code để lưu các thanh ghi khi có ngắt, phục hồi các thanh ghi khi kết thúc ngắt bị vô hiệu hóa, do vậy ta tự viết code riêng để xữ lý.
#INT_DEFAULT: Hàm theo sau chỉ thị này sẽ được gọi nếu Pic trigger một ngắt nhưng không có cờ ngắt nào được set. nếu cờ ngắt được thiết lập nhưng không có trigger ngắt hàm này cũng sẽ được gọi. 

#int_default
default_isr()
{
   printf("Unexplained interrupt ");
}
 #INT_xxxx: 
với xxxx là các loại ngắt, đi sau chỉ thị tiền xữ lý này là hàm xữ lý ngắt cho loại ngắt đó ví dụ:
Ví dụ:
#int_rda
rs232_handler() //chương trình xữ lý ngắt nhận cổng nối tiếp
{
b=getch(); //load character
Buffer[Buff+1]=b; //store character
Buff++; //increment pointer
}
main()
{
enable_interrupts(INT_RDA); // cho phép ngắt nhận
enable_interrupts(GLOBAL); // cho phép ngắt toàn cục
do { while(True); }
}
 Các chỉ thị ngắt thông dụng
  • #INT_AD   : chuyển đổi A /D đã hoàn tất , thường thì không nên dùng
  • #INT_ADOF   : I don’t know
  • #INT_BUSCOL  : xung đột bus
  • #INT_BUTTON  : nút nhấn ( không biết hoạt động thế nào )
  • #INT_CCP1   : có Capture hay compare trên CCP1
  • #INT_CCP2   : có Capture hay compare trên CCP2
  • #INT_COMP   : kiểm tra bằng nhau trên Comparator
  • #INT_EEPROM  : hoàn thành ghi EEPROM
  • #INT_EXT   : ngắt ngoài
  • #INT_EXT1   : ngắt ngoài 1
  • #INT_EXT2   : ngắt ngoài 2
  • #INT_I2C   : có hoạt động I 2C
  • #INT_LCD   : có hoạt động LCD
  • #INT_LOWVOLT  : phát hiện áp thấp
  • #INT_PSP   : có data vào cổng Parallel slave
  • #INT_RB   : bất kỳ thay đổi nào trên chân B4 đến B7
  • #INT_RC   : bất kỳ thay đổi nào trên chân C4 đến C7
  • #INT_RDA   : data nhận từ RS 232 sẵn sàng
  • #INT_RTCC   : tràn Timer 0
  • #INT_SSP   : có hoạt động SPI hay I 2C
  • #INT_TBE   : bộ đệm chuyển RS 232 trống
  • #INT_TIMER0  : một tên khác của #INT_RTCC
  • #INT_TIMER1  : tràn Timer 1
  • #INT_TIMER2  : tràn Timer 2
  • #INT_TIMER3  : tràn Timer 3
  • #INT_TIMER5  : tràn Timer 5
  • #INT_OSCF : lỗi OSC
  • #INT_PWMTB : ngắt cuả PWM time base
  • #INT_IC3DR : ngắt đổi hướng ( direct ) của IC 3
  • #INT_IC2QEI : ngắt của QEI
  • #INT_IC1 : ngắt IC 1
_Hàm đi kèm phục vụ ngắt không cần tham số vì không có tác dụng .
_Sử dụng NOCLEAR sau #int_xxx để CCS không xoá cờ ngắt của hàm đó .
_Để cho phép ngắt đó hoạt động phải dùng lệnh enable_interrupts ( int_xxxx) và enable_interrupts ( global ) .
_Khoá FAST theo sau #int_xxxx để cho ngắt đó là ưu tiên cao, chỉ được 1 ngắt thôi , chỉ có ở PIC 18 và dsPIC .
VD : #int_timer0 FAST NOCLEAR

Bài đăng mới nhất

Valdes Fernando - Microcontrollers Applications With Pic

Bài đăng phổ biến