Vi Điều Khiển

Chào mừng các bạn đến với thế giới của Vi điều khiển!

--welcome to the world of microcontrollers^^ --

Điện Tử Cơ Bản

nơi khởi đầu

Lập Trình

linh hồn của phần cứng

Hiển thị các bài đăng có nhãn hướng dẫn lập trình CCS. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn hướng dẫn lập trình CCS. Hiển thị tất cả bài đăng

Chủ Nhật, 26 tháng 2, 2012

Hướng Dẫn CCS Cho PIC - Bài 7: Các Ngắt Trong PIC



Bài trước:+Bài 6: Giao Tiếp SPI

Bài 7: Các Ngắt Trong PIC



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à 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 ( 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ực hiệ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 hay HIGH theo sau chỉ thị tiền xử lý hàm đó .
Lưu ý : khi dùng FAST thì không nên dùng HIGH cho các ngắt khác thì mới có ý nghĩa và chỉ  có duy nhất 1 ngắt được ưu tiên FAST , nhưng  có thể có nhiều ngắt đặt ở mức HIGH .
VD :
#int_timer1  FAST 
 Void xu_ly ( ) 
  { . . . 
 } 

 #int_timer2  HIGH 
 Void dinh_thi () 
  { . . . 
 } 

 #int_timer5 HIGH  
 Void vong_lap() 
  { . . . 
 } 

+ 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 cao đánh dấu FAST không sinh mã sao lưu gì cả mà nhảy thẳng vào hàm ngắt chạy luôn . PIC 18 và dsPIC có cơ chế lưu siêu tốc là FAST STACK REGISTER ( xem datasheet ) . 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 FAST 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 FAST 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ì dùng HIGH , chương trình sao lưu bình thường như với ngắt thấp , nhưng khi đó ngắt đánh dấu FAST cũng mất tác dụng , CCS xem như là HIGH và xử lý bình thường .

_Như vậy dùng FAST hay HIGH đều có ý nghĩa riêng của nhà  lập trình .

II / KHAI BÁO NGẮT :
_Mỗi dòng VDK có số lượng nguồn ngắt 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ủa bạ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ắt xả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ài EXTx 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






(Nguồn:  TRẦN XUÂN TRƯỜNG)

Hướng Dẫn CCS Cho PIC - Bài 6: Giao Tiếp SPI



Bài trước: +Bài 5: Truyền Thông Nối Tiếp RS232 - Xử Lý Chuỗi Trong CCS
Bài sau: +Bài 7: Các Ngắt Trong PIC

Bài 6: Giao Tiếp SPI


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 - SCK 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 đó Ví dụ  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 ) .

Hướng Dẫn CCS Cho PIC - Bài 5: Truyền Thông Nối Tiếp RS232 - Xử Lý Chuỗi Trong CCS


Bài trước: 
+Bài 4: Chuyển Đổi ADC - Các Hàm Vào/Ra trong CCS
Bài sau: +Bài 6: Giao Tiếp SPI


Bài 5: Truyền Thông Nối Tiếp RS232 - Xử Lý Chuỗi Trong CCS




+ Phần này sẽ giúp bạn viết chương trình có sử dụng giao tiếp với máy tính (PC) . Điều này rất cần thiết khi bạn muốn VĐK khi hoạt động có thể truyền dữ liệu cho PC xử lý , hoặc nhận giá trị từ PC để xử lý và điều khiển ( dùng PC điều khiển động cơ , nhiệt độ , hay biến PC thành dụng cụ đo các đại lượng điện, Oscilocope , . . .) .

Hướng Dẫn CCS Cho PIC - Bài 4: Chuyển Đổi ADC - Các Hàm Vào/Ra trong CCS



Bài trước: +Bài 3: Các Hàm Xử Lý Số, Xử Lý Bit, Delay trong CCS
Bài sau: +Bài 5: Truyền Thông Nối Tiếp RS232 - Xử Lý Chuỗi Trong CCS


Bài 4: Chuyển Đổi ADC - Các Hàm Vào/Ra trong CCS




I /XỬ LÝ ADC : 
+ PIC có nhiều chân phục vụ  xử lý ADC với nhiều cách thức khác nhau . Để dùng ADC , bạn phải có khai báo #DEVICE cho biết dùng ADC mấy bit ( tuỳ chip hỗ trợ , thường là 8 hay 10 bit hoặc hơn) . Bạn cần lưu ý là: 1 VDK hỗ trợ ADC 10 bit thì giá trị vào luôn là 10 bit , nhưng chia cho 4 thì còn 8 bit . Do đó 1 biến trở chiết áp cấp cho ngõ vào ADC mà bạn chọn chế độ 10 bit thì sẽ rất nhạy so với chế độ 8 bit ( vì 2 bit cuối có thay đổi cũng không ảnh hưởng giá trị 8 bit cao và do đó kết quả 8 bit ADC ít thay đổi ) , nếu chương trình có chế độ kiểm tra ADC để cập nhật tính toán , hay dùng ngắt ADC ,  thì nó sẽ chạy hoài thôi . Dùng ADC 8 bit sẽ hạn chế điều này . Do đó mà CCS cung cấp chọn lựa ADC 8 hay 10 bit tùy mục đích sử dụng .


Cấu hình bộ ADC : 
+ Thông dụng nhất khi dùng ADC là sử dụng 1 biến trở , điều chỉnh bởi 1 nút vặn , qua đó thu được 1 điện áp nhỏ hơn điện áp tham chiếu ( Vref – áp max ) , đưa vào chân biến đổi ADC , kết quả cho 1 giá trị số ADC 8 bit ( 0-255 ) hay ADC 10 bit (0-1023 ) . Thường thì áp Vref lấy bằng Vdd ( 5V ).
+ Trên các PIC có ngõ AVdd và AVss ( PIC 18 ) , thường thì bạn luôn nối AVdd tới Vdd , AVss tới Vss để đảm bảo họat động cho lập trình qua ICD 2 .


Các hàm sau phục vụ ADC : 
1 /   Setup_ADC ( mode  ) : 
+ Không trả về trị . Dùng xác định cách thức hoạt động bộ biến đổi ADC . Tham số mode tuỳ thuộc file thiết bị *.h có tên tương ứng tên chip bạn đang dùng , nằm trong thư mục DEVICES của CCS . Muốn biết có bao nhiêu tham số có thể dùng cho chip đó , bạn mở file tương ứng đọc , tìm tới chỗ các định nghĩa cho chức năng ADC dùng cho chip đó tương ứng với hàm này . Sau đây là các  giá trị mode của 16F877 , ( 1 số  khác có thể không có hoặc có thêm như  16F877A có thêm 1 số thứ là ADC_CLOCK_DIV_2/4/8/16/32/64 . . .) :


ADC_OFF  : tắt hoạt động ADC ( tiết kiệm điện , dành chân  cho hoạt động khác ) .
ADC_CLOCK_INTERNAL  : thời gian lấy mẫu bằng xung clock IC  ( mất 2-6 us ) thường là  chung cho các chip .
ADC_CLOCK_DIV_2  : thời gian lấy mẫu bằng xung clock / 2  ( mất 0.4 us trên thạch anh  20MHz )
ADC_CLOCK_DIV_8  : thời gian lấy mẫu bằng xung clock / 8  ( 1.6 us )
ADC_CLOCK_DIV_32  : thời gian lấy mẫu bằng xung clock / 32 ( 6.4 us )





2 / Setup_ADC_ports ( value ) 
+ Xác định chân lấy tín hiệu analog và điện thế chuẩn sử dụng . Tùy thuộc bố trí chân trên chip , số chân  và chân nào dùng cho ADC  và số chức  năng ADC mỗi chip mà value có thể có những giá trị khác nhau. Xem file tương ứng trong thư mục DEVICES để biết số chức năng tương ứng chip đó . Để tương thích chương trình viết cho phiên bản cũ , 1 số tham số  có 2 tên khác nhau ( nhưng cùng chức năng do định nghĩa cùng địa chỉ ) , ở đây dùng  phiên bản 3.227 .Lưu ý : Vref : áp chuẩn ,  Vdd : áp nguồn .


Sau đây là các giá trị cho value ( chỉ dùng 1 trong các giá trị )  của 16F877 :
ALL_ANALOGS  .........: dùng tất cả chân sau làm analog  : A0  A1  A2  A3  A5   E0  E1  E2  (Vref=Vdd)
NO_ANALOG  ..............................................................: không dùng analog , các chân đó sẽ là chân I /O .
AN0_AN1_AN2_AN4_AN5_AN6_AN7_VSS_VREF .:  A0 A1 A2 A5 E0 E1 E2 VRefh=A3
AN0_AN1_AN2_AN3_AN4     .....................................:  A0 A1 A2 A3 A5
( tên thì giống nhau cho tất cả thiết bị nhưng 16F877 chỉ có portA có 5 chân nên A0 , A1 , A2 , A5  được dùng , A6 , A7 không có )
AN0_AN1_AN3      .......................................................:  A0 A1 A3  , Vref = Vdd
AN0_AN1_VSS_VREF    ..............................................:  A0 A1 VRefh = A3
AN0_AN1_AN4_AN5_AN6_AN7_VREF_VREF  ......:  A0 A1 A5 E0 E1 E2 VRefh=A3 , VRefl=A2 .
AN0_AN1_AN2_AN3_AN4_AN5    ............................:  A0 A1 A2 A3 A5 E0
AN0_AN1_AN2_AN4_AN5_VSS_VREF  .................. :  A0 A1 A2 A5 E0 VRefh=A3
AN0_AN1_AN4_AN5_VREF_VREF   .........................:  A0 A1 A5 E0 VRefh=A3 VRefl=A2
AN0_AN1_AN4_VREF_VREF   .................................. :  A0 A1 A5 VRefh=A3 VRefl=A2
AN0_AN1_VREF_VREF     ...........................................:  A0 A1 VRefh=A3 VRefl=A2
AN0   ..............................................................................:  A0
AN0_VREF_VREF    ..................................................... :  A0 VRefh=A3 VRefl=A2


VD :  setup_adc_ports (AN0_AN1_AN3 ) ;  // A0 , A1 , A3 nhận  analog , áp nguồn +5V cấp cho IC sẽ là điện áp chuẩn .


3 / Set_ADC_channel ( channel ) : 
+ Chọn chân để đọc vào giá trị analog bằng lệnh Read_ADC ( ) . Giá trị channel tuỳ số chân chức năng  ADC mỗi chip .Với 16F877 , channel có giá trị từ  0 -7 :
   0-chân A0, 1-chân A1, 2-chân A2,  3-chân A3, 4-chân A5, 5-chân E0, 6-chân E1, 7-chân E2  
+Hàm không trả về trị . Nên delay 10 us sau hàm này rồi mới dùng hàm read_ADC ( ) để bảo đảm kết quả đúng . Hàm chỉ hoạt động với A /D phần cứng trên chip.


4 / Read_ADC ( mode ) : 
+ Dùng đọc  giá trị ADC từ thanh ghi (/ cặp thanh ghi ) chứa kết quả biến đổi ADC . Lưu ý hàm này sẽ hỏi vòng cờ cho tới khi cờ này báo đã hoàn thành biến đổi ADC ( sẽ mất vài us ) thì xong hàm .
+ Nếu giá trị ADC là 8 bit như khai báo trong chỉ thị #DEVICE , giá trị trả về của hàm là 8 bit , ngược lại là 16 bit nếu khai báo #DEVICE sử dụng ADC 10 bit trở lên .
+ Khi dùng hàm này , nó sẽ lấy ADC từ chân bạn chọn trong hàm Set_ADC_channel( ) trước đó . Nghĩa là mỗi lần chỉ đọc 1 kênh  Muốn đổi sang đọc chân nào , dùng hàm set_ADC_channel( ) lấy chân đó . Nếu không có đổi chân , dùng read_ADC( ) bao nhiêu lần cũng được .


+ mode có thể có hoặc không , gồm có :
ADC_START_AND_READ  :  giá trị mặc  định
ADC_START_ONLY  :  bắt đầu chuyển đổi và trả về
ADC_READ_ONLY   :  đọc kết quả chuyển đổi lần cuối



#DEVCE  8 bit  10 bit  11 bit  16 bit 
ADC=8  0-255  0-255  00-255  00-255 
ADC=10  x  0-1023  x  x 
ADC=11  x  x  0-2047  x 
ADC=16  0-65280  0-65472  0-65504  0-65535 
+ 16F877 chỉ hỗ trợ ADC 8 và 10 bit . 
VD : 
setup_adc(  ADC_CLOCK_INTERNAL  ); 
setup_adc_ports( ALL_ANALOG ); 
set_adc_channel(1); 
while ( input(PIN_B0) )  
{ 
   delay_ms( 5000 ); 
   value = read_adc(); 
   printf("A/D value = %2x\n\r", value); 
} 
read_adc(ADC_START_ONLY); 
sleep(); 
value=read_adc(ADC_READ_ONLY); 

+ Lưu ý : trên PIC 18 , cấu trúc ADC tương đối phức tạp , đa năng hơn  như là cho phép lấy 2 mẫu cùng lúc , . . . cũng sử dụng với các hàm trên , có nhiều thông số trong file *.h , sẽ đề cập sau . 

5 / _ Ví dụ : 
+ Chương trình sau lấy ADC 8 bit , đọc và xuất ra dãy led ở port B , và xuất ra màn hình máy tính . 
+ Kết nối chân trên 16F877 : RA0 là chân lấy Analog vào , áp chuẩn là nguồn +5V , mass=0 V




Hướng Dẫn CCS Cho PIC - Bài 3: Các Hàm Xử Lý Số, Xử Lý Bit, Delay trong CCS



Bài trước: +Bài 2: Sử dụng Biến và Hàm, Cấu trúc lệnh, Chỉ thị tiền xử lý trong CCS
Bài sau: +Bài 4: Chuyển Đổi ADC - Các Hàm I/O


Bài 3: Các Hàm Xử Lý Số, Xử Lý Bit, Delay trong CCS


I / CÁC HÀM XỬ LÝ SỐ : 
+ Bao gồm các hàm:


Sin() cos() tan()  Asin()  acos()  atan() 
Abs() : lấy trị tuyệt đối
Ceil( ) :làm tròn theo hướng tăng
Floor ( ) : làm tròn theo hướng giảm
Exp ( ) : tính e^x
Log ( ) :tính log
Log10 ( ) : log10
Pow ( ) : tính luỹ thừa
Sqrt ( ) :căn thức

Hướng Dẫn CCS Cho PIC - Bài 2: Sử dụng Biến và Hàm, Cấu trúc lệnh, Chỉ thị tiền xử lý



Bài trước: +Bài 1: Hướng dẫn lập trình cho PIC bằng CCS ver3.242
Bài tiếp: +Bài 3: Các Hàm Xử Lý Số, Xử Lý Bit, Delay trong CCS





Bài 2: Sử dụng Biến và Hàm, Cấu trúc lệnh, Chỉ thị tiền xử lý trong CCS


I / KHAI BÁO VÀ SỬ DỤNG BIẾN , HẰNG , MẢNG : 


1 /Khai báo biến  , hằng  ,mảng : 


+ Các loại biến sau được hỗ trợ : 


int1  số  1 bit = true hay false ( 0 hay 1)
int8  số  nguyên 1 byte ( 8 bit)
int16  số nguyên 16 bit
int32  số nguyên 32 bit
char  ký tự 8 bit
float  số thực 32 bit
short  mặc định như  kiểu int1
byte  mặc định như kiểu int8
int  mặc định như kiểu int8
long  mặc định như kiểu int16


+ Thêm signed hoặc unsigned phía trước để chỉ đó là số có dấu hay không dấu .Khai báo như trên mặc định là không dấu . 4 khai báo cuối không nên dùng vì dễ nhầm lẫn . Thay vào đó nên dùng 4 khai báo đầu .




VD : 
Signed int8  a ; // số a là 8 bit dấu ( bit 7 là bit dấu ). 
Signed int16 b , c , d ; 
Signed int32 , . . .  


+ Phạm vi biến :  


 Int8 :0 , 255  signed int8 : -128 , 127
 Int16 : 0 ,2^15-1 signed int16 : -2^15 , 2^15-1
 Int32 : 0 , 2^32-1 signed int32 : -2^31 , 2^31-1


+ Khai báo hằng : 
VD : 
 Int8 const  a=231 ; 


+ Khai báo 1 mảng hằng số : 
VD :
Int8 const a[5] = { 3,5,6,8,6 } ;  //5 phần tử , chỉ số mảng bắt đầu từ 0 : a[0]=3 


+ Một mảng hằng số có kích thước tối đa tuỳ thuộc loại VĐK: 


*NếuVĐK là PIC 14 ( VD :16F877 )  : bạn chỉ được khai báo 1 mảng hằng số  có kích thước tối đa là 256byte .


 Các khai báo sau là hợp lệ :
Int8 const a[5]={ . . .};  // sử dụng 5 byte , dấu . . . để bạn điền số vào  
Int8 const a[256]={ . . .};  // 256 phần tử x 1 byte = 256 byte  
Int16 const a[12] = { . . . };  // 12 x 2= 24 byte 
Int16 const a[128] = { . . . }; // 128 x 2= 256 byte 
không hợp lệ :


Int16 const a[200] = { . . . }; // 200 x 2 =400 byte


*Nếu VĐK là PIC 18 : khai báo mảng hằng số thoải mái , không giới hạn kích thước .


+ Lưu ý : nếu đánh không đủ số phần tử vào trong ngoặc kép như  đã khai báo , các phần tử còn lại sẽ là 0 . Truy xuất giá trị vượt quá chỉ số mảng khai báo sẽ làm chương trình chạy vô tận .
VD :
int8 const a [7] = { 0 , 3,5 ,9 }   // các phần tử a[4] ,a[5],a[6] đều =0 


+ Mảng hằng số thường  dùng làm bảng tra (ví dụ bảng tra sin ) , viết dễ dàng và nhanh chóng , gọn hơn so với khi dùng ASM để viết .


+ Khai báo 1 biến mảng : kích thước tuỳ thuộc khai báo con trỏ trong #device  và loại VDK:


*PIC 14 : Nếu bạn khai báo con trỏ 8 bit : VD: # device *=8  : không gian bộ nhớ chỉ có 256 byte cho tất cả các biến chương trình bất chấp VĐK của bạn có hơn 256 byte RAM (Vd : 368 , . . .)  và biến mảng có kích thước tối đa tuỳ thuộc độ phân mảnh bộ nhớ , với 16F877 có 368 byte ram , thường thì kích thước không quá 60 byte ,có khi dưới 40 byte , nếu khai báo lớn hơn sẽ gặp lỗi vô duyên : "not enough ram for all variable"  trong khi thực sự VDK còn rất nhiều RAM . Nếu khai báo con trỏ 16 bit : VD : #device *=16 , không gian bộ nhớ là đầy đủ ( trừ đi 1 ít RAM do CCS chiếm làm biến tạm ) .VD : với 16F877  bạn dùng đủ 368 byte RAM . Nhưng kích thước mảng cũng không quá 60 byte .


* PIC 18 : kích thước mảng không giới hạn, xài hết RAM thì thôi . Với khai báo con trỏ 8 bit , bạn chỉ được xài tối đa 256 byte RAM , nếu khai báo con trỏ 16 bit , bạn xài trọn bộ nhớ RAM thực sự . VD: Khai báo biến mảng : int16  a[125] ; // biến mảng 126 phần tử , kích thước 252 byte ram . 






2 / Cách sử dụng biến : 


+ Khi sử dụng các phép toán cần lưu ý : sự tràn số , tính toán với số âm , sự chuyển kiểu và ép kiểu .


A ) Một vài ví dụ  về tràn số  , làm tròn :  
VD :  
Int8 a=275;  // a =275-256=19 
Int8 const a=275 //a=19 
Int8 a=40 , b=7 , c; 
C=a * b ;  //c=280-256=24 
C=a / b ;  //c=5 


+ Bạn có thể ép kiểu , thường là tiết kiệm ram , hay muốn tiết kiệm thời gian tính , . . .. VD : 
Int8 a =8 , b=200; 
Int16 c ; 
C=  ( int16) a * b ; 
// c= 1600 , a chuyển sang 16 bit , 16bit*8bit => b tự động chuyển sang 16 bit , kết quả là 16 bit trong c , lưu ý biến a , b vẫn là 8 bit .  


+ 8bit * 8bit  =>  phép nhân là 8 bit , KQ là 8 bit
+ 16bit * 8 bit  =>  phép nhân là 16 bit , KQ là 16 bit
+ 32bit * 16 bit  =>  phép nhân là 32 bit , KQ là 32 bit
+ 16bit * 16 bit  =>  phép nhân là 16 bit , KQ là 16 bit
. . . v . v . . .


+ Có thể ép kiểu kết quả : VD : 16b*8b => 16bit , nếu gán vào biến 8 bit thì KQ sẽ cắt bỏ 8 bit cao .


B )Phạm vi sử dụng  biến : 


+ Giống  như C trong lập trình C cho máy tính . Biến có thể được khai báo như toàn cục hay cục bộ . Biến khai báo trong hàm sẽ là cục bộ và sẽ chỉ dùng được trong hàm đó , kể cả trong hàm main() . Ngoài ra còn có thể khai báo ngay trong 1 khối lệnh , và cũng chỉ tồn tại trong khối lệnh đó . Do vậy nếu dùng MPLAB để mô phỏng , thì khi nhảy vào hàm hay khối lệnh có chứa khai báo biến đó thì biến đó mới có giá trị , có khi nhảy ra ngoài hàm thì biến đó sẽ là” out of scope” khi ta quan sát chúng trong cửa sổ Watch.
+ Chi tiết về phạm vi biến xem tài liệu lập trình C trên máy tính .
+ CCS có hỗ trợ cả con trỏ , tuy nhiên ít dùng .
+ CCs không hỗ trợ lập trình hướng đối tượng như C++ . Tuy vậy CCS có hỗ trợ các biến cấu trúc .


3 / Các phép toán , sự thực thi và vấn đề tối ưu mã , chương trình: 


Thứ Sáu, 24 tháng 2, 2012

Hướng dẫn lập trình cho PIC bằng CCS ver3.242





Bài 1: Hướng dẫn lập trình cho PIC bằng CCS ver3.242

1. Tổng quan về CCS

1.1. Vì sao ta sử dụng CCS?

Sự ra đời của một loại vi điều khiển đi kèm với việc phát triển phần mềm ứng dụng cho việc lập trình cho con vi điều khiển đó. Vi điều khiển chỉ hiểu và làm việc với hai con số 0 và 1. Ban đầu để việc lập trình cho VĐK là làm việc với dãy các con số 0 và 1. Sau này khi kiến trúc của Vi điều khiển ngày càng phức tạp, số luợng thanh ghi lệnh nhiều lên, việc lập trình với dãy các số 0 và 1 không còn phù hợp nữa, đòi hỏi ra đời một ngôn ngữ mới thay thế. Và ngôn ngữ lập trình Assembly. Ở đây ta không nói nhiều đến Assmebly. Sau này khi lập trình cho Vi điều khiển một cách ngắn gọn và dễ hiểu hơn đã dẫn đến sự ra đời củangôn ngữ C ra đời, nhu cầu dùng ngôn ngữ C đề thay cho ASM trong việc mô tả các lệnh nhiều chương trình soạn thảo và biên dịch C cho Vi điều khiển : Keil C, HT‐PIC, MikroC,CCS…
Tôi chọn CCS cho bài giới thiệu này vì CCS là một công cụ lập trình C mạnh cho Vi điều khiển PIC. Những ưu và nhược điểm của CCS sẽ được đề cập đến trong các phần dưới đây.

1.2. Giới thiệu về CCS

CCS là trình biên dịch lập trình ngôn ngữ C cho Vi điều khiển PIC của hãng Microchip.
Chương trình là sự tích hợp của 3 trình biên dich riêng biết cho 3 dòng PIC khác nhau đó là:
‐ PCB cho dòng PIC 12‐bit opcodes
‐ PCM cho dòng PIC 14‐bit opcodes
‐ PCH cho dòng PIC 16 và 18‐bit
Tất cả 3 trình biên dich này đuợc tích hợp lại vào trong một chương trình bao gồm cả trình soạn thảo và biên dịch là CCS, phiên bản mới nhất là PCWH Compiler Ver 3.227.


Giống như nhiều trình biên dich C khác cho PIC, CCS giúp cho người sử dụng nắm bắt nhanh được vi điều khiển PIC và sử dụng PIC trong các dự án. Các chương trình diều khiển sẽ được thực hiện nhanh chóng và đạt hiệu quả cao thông qua việc sử dụng ngôn ngữ lạp trình cấp cao – Ngôn ngữ C.Tài liệu hướng dẫn sử dụng có rất nhiều, nhưng chi tiết nhất chính là bản Help đi kèm theo phần mềm (tài liệu Tiếng Anh). Trong bản trợ giúp nhà sản xuất đã mô tả rất nhiều về hằng, biến, chỉ thị tiền xủa lý, cấu trúc các câu lệnh trong chương trình, các hàm tạo sẵn cho người sử dụng…

2. Tạo PROJECT đầu tiên trong CCS

Để tạo một Project trong CCS có nhiều cách, có thể dùng Project Wizard, Manual Creat, hay đơn giản là tạo một Files mới và thêm vào đó các khai báo ban đầu cần thiết và “bắt buộc”.
Dưới đây sẽ trình bày cách tạo một project hợp lệ theo cả 3 phương pháp. Một điều ta cần chú ý khi tạo một Project đó là: khi tạo bắt cứ một Project nào mới thì ta nên tạo một thư mục mới với tên liên quan đến Project ta định làm, rồi lưu các files vào đó. Khi lập trình và biên dịch, CCS sẽ tạo ra rất nhiều files khác nhau, do đó nếu để chung các Project trogn một thư mục sẽ rất mất thời gian trong việc tìm kiếm sau này. Đây cũng là quy tắc chung khi ta làm việc với bất kỳ phần mềm nào, thiết kế mạch hay lập trình.
Việc đầu tiên bạn cần làm là khởi động máy tính và bật chương trình PIC C Compiler.

2.1. Tạo một PROJECT sử dụng PIC Wizard

Trước hết bạn khởi động chương trình làm việc PIC C Compiler. Từ giao diện chương trình bạn di chuột chọn Project ‐> New ‐> PIC Wizard nhấn nút trái chuột chọn.