Pages

Wednesday, April 27, 2011

PIC Tutorial - Part 1

Salam dan hello... Entri kali ini aku nak berkongsi sedikit tentang programming bagi microprocessor Peripheral Interface Controller (PIC).. Aku bukan pakar cuma sekadar ingin berkongsi.. Sesiapa yang rasa terdapat kesalahan atau ingin menambah dalam entri ini, feel free to drop ur comment.. Tutorial ini lebih kepada penggunaan PIC 16F877A seperti mana yang telah digunakan dalam mobile robot dalam projek mobile robot.. Tutorial ni juga adalah lebih kepada penggunaan PIC Compiler (PICC) atau PCWH..

sebelum kita mulakan eloklah kita kenal dulu jenis PIC yang kita nak guna.. dalam entri ni kita guna PIC 16F877A..


  • 33 I/O pin, 
  • 8 analog pin
  • 5 port (A, B, C, D, dan E),
  • 2 CCP
  • 256 data EEPROM
  • 367 data RAM
  • 4MHz internal oscillator




Pertama sekali dlm program mestilah mempunyai pic header. Contoh:

      #include <16f877a.h>

sekarang kita menggunakan pic 16f877a.. .h bermaksud header file.. header file PIC yang kita mahu guna mesti kena declare kerana dlm file tu dh di'declare' pin2 bagi pic tu dan juga declaration bg fungsi2 lain untuk PIC tu.. kalau anda right click kat perkataan 16f877a.h tu dan klik kat open file at cursor anda akan nampak seperti gambar dibawah ni:


selepas declare PIC, kemudian declare frequency bagi oscillator pula..
    
      #use delay(clock=20000000)

Ini bermaksud pic tu menggunakan crystal 20MHz. PIC 16F877A mempunyai internal oscillator setinggi 4MHz.. Jadi boleh lah pilih sama ada nak guna internal oscillator @ external oscillator.. kalau guna yg internal tukar 20000000 kepada 4000000.. boleh juga tulis 20000000 kepada 20M atau 4000000 kepada 4M..

      #fuses hs,noprotect,nowdt,nolvp,nobrownout

Seterusnya adalah declaration fuses untuk PIC.. Fuses ni adalah untuk set macam mana kita nak PIC tu berfungsi.. Aku xbrape tau sangat pasal fuses ni so aku xdapat nk terangkan lebih lanjut.. biasanya aku guna fuses macam yg kt atas bagi semua PIC yang aku guna seperti 18F458.. hs adalah kalau korang guna external oscillator.. kalau guna interna oscillator tukar kepada xt.. nowdt adalah no watch dog timer.. maksudnye korang xnak PIC tu restart pada masa2 tertentu.. nobrownout adalah kalau korang xnk PIC tu automatik reset kalau detect voltage tertentu..

      #define use_portd_lcd TRUE

 seterusnya adalah define port untuk paparan LCD.. untuk mobile robot aku guna lcd display 18x2. port untuk lcd boleh tukar. tidak semestinya guna port d. kalau nak guna port b, tukar perkataan 'portd' kepada 'portb'.

      #include <lcd.c>

kalau nak guna lcd mestilah masukkan sekali library untuk lcd. lcd.c tu adalah library untuk lcd. kat dalam library ni ada bagi tau macam mana nak sambung lcd dengan PIC. cuma perlu klik kanan kat perkataan lcd.c dan klik kat open file at cursor.. nanti korang akan nampak seperti gambar di bawah ni.. yang highlight tu adalah pin2 yang kena sambung antara lcd dan PIC..



       #byte porta=5
       #byte portb=6
       #byte portc=7
       #byte portd=8

seterusnya adalah declare address bagi port2 yang nak guna. yang ni memang dah tetap macam ni. yang ni kena letak bagi port yang nak guna je. kalau letak tapi tak guna takpe. tapi kalau nak guna tapi tak letak tak boleh.

       long s0,s1,s2,s3,s4;

yang ni pula adalah global variable. variable ni ada dua jenis. yang pertama adalah global, yang kedua adalah local variable. bezanya kalau declaration global variable adalah diluar function prototype. declaration local variable pula adalah dalam function prototype. global variable ni mana2 function prototype boleh guna variable tu tapi kalau local variable hanya hanya function prototype yang ada declare variable tu je yang boleh guna. jenis data variable seperti long kat atas tu boleh tukar mengikut kegunaan. long bermaksud nilai pada satu variable tu boleh sampai 16 bits.. jenis data variable yang lain dan range dia adalah macam kat bawah ni


type-specifier size unsigned signed
int1 1 bits 0 to 1 N/A
int8 8 bits 0 to 255 -128 to 127
int16 16 bits 0 to 65535 -32768 to 32767
int32 32 bits 0 to 4294967295 -2147483648 to 2147483647
float32 32 bits float -1.5x10^45 to 3.4x10^38

persamaan adalah seperti kat bawah ni:

C standard type Default type
short int1
char unsigned int8
int int8
long int16
long long int32
float float32

     
void ADC()
{
set_adc_channel( 0 );
delay_us(100);
s0 = read_adc();
delay_us(100);

lcd_gotoxy(1,1);
printf(lcd_putc,"0:%lu ",s0);

set_adc_channel( 1 );
delay_us(100);
s1 = read_adc();
delay_us(100);

lcd_gotoxy(6,1);
printf(lcd_putc,"1:%lu ",s1);

set_adc_channel( 2 );
delay_us(100);
s2 = read_adc();
delay_us(100);

lcd_gotoxy(12,1);
printf(lcd_putc,"2:%lu ",s2);

set_adc_channel( 3 );
delay_us(100);
s3 = read_adc();
delay_us(100);

lcd_gotoxy(1,2);
printf(lcd_putc,"3:%lu ",s3);

set_adc_channel( 4 );
delay_us(100);
s4 = read_adc();
delay_us(100);

lcd_gotoxy(7,2);
printf(lcd_putc,"4:%lu ",s4);

}

yang ni pula adalah function prototype yang dinamakan ADC. dia berfungsi untuk membaca nilai sensor analog. jika dirujuk balik entri Sempurna mobile robot ku , dapat lihat depan mobile robot tu ada 5 IR sensor. sensor2 tu la sensor analog. ADC bermaksud Analog Digital Converter. ADC ni bagi nilai 8 bits. maksudnya dia akan kembalikan dari 0 - 255. bagi PIC 16F877A boleh set sampai 10 bits. selepas #include <16f877a.h> ada satu lagi yang boleh diletak iaitu

      #device adc=8

nombor 8 tu bermaksud ADC tu kita nak dalam 8 bits. kalau kita nak 10 bits tukar 8 kepada 10. kalau 10 bits nilai yang dikembalikan adalah 2^10 = 1024.

      set_adc_channel( 0 );
      delay_us(100);
      s0 = read_adc();
      delay_us(100);

kod kat atas tu adalah untuk pembacaan sensor analog pada pin AN0 kat PIC. kalau nak baca untuk sensor kat pin lain, tukar no 0 kat  set_adc_channel( 0 ) kepada nombor pin yang nak dibaca. s0 tu adalah global variable yang dah diterangkan kat atas.
    
      lcd_gotoxy(1,1);
      printf(lcd_putc,"0:%lu ",s0);

yang ni pula adalah untuk paparan kat LCD.  lcd_gotoxy(1,1) adalah posisi huruf @ nombor pertama yang nak dipaparkan kat LCD. %lu tu adalah untuk paparkan nilai variable. dalam kes ni adalah untuk paparkan variable s0. untuk paparkan nilai variable juga perlu guna huruf2 tertentu. tak semua guna %lu. ikut kepada jenis variabe. jenis2 adalah seperti dibawah:

%c Character
%s String @ Character
%u unsigned int
%d signed int
%lu long unsigned int
%ld long signed int
%x hex int (huruf kecil)
%X hex int (huruf besar)
%lx hex long int (huruf kecil)
%lX hex long int (huruf besar)
%f Float with truncated decimal
%g Float with rounded decimal
%e Float in exponential format

contoh kalau variable s0 tu adalah jenis float, jadi untuk paparkannya adalah camni

      printf(lcd_putc,"0:%f ",s0);

untuk variable float, biasanya paparannya adalah sehingga 8 titik perpuluhan (0.00000000). kalau kita nak paparkan sehingga 2 titik perpuluhan (0.00) je boleh tulis camni

      printf(lcd_putc,"0:%0.2f ",s0);

kalau variable type int biasanya akan paparkan nilainya terus. contoh kalau variable tu kita buat counter, paparannya adalah seperti 8,9,10,11,.....kita boleh juga paparkan seperti 08,09,10,11,... caranya dengan tulis macam kat bawah ni:

      printf(lcd_putc,"0:%02lu ",s0);

kalau nombor 02 tu kita tulis 03 akan papar seperti 008,009,010,011,... camtu la seterusnya.. kalau kita tulis nombor 2 je akan papar _8,_9,10,11,... bukan papar underscore (_) tu. tapi akan ada gap untuk 1 nombor depan nombor 8 dan 9 tu..

void linefollowing()
{
   if((s0<100)&&(s1<100)&&(s2>100)&&(s3<100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(250); set_pwm2_duty(250);
            /* Kiri                 Kanan*/
      }
       else if((s0<100)&&(s1>100)&&(s2>100)&&(s3<100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(240); set_pwm2_duty(250);
            /* Kiri                 Kanan*/
      }
      else if((s0<100)&&(s1>100)&&(s2<100)&&(s3<100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(200); set_pwm2_duty(240);
            /* Kiri                 Kanan*/
      }
      else if((s0>100)&&(s1>100)&&(s2<100)&&(s3<100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(100); set_pwm2_duty(200);
            /* Kiri                 Kanan*/
      }
      else if((s0>100)&&(s1<100)&&(s2<100)&&(s3<100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(100); set_pwm2_duty(250);
            /* Kiri                 Kanan*/
      }
      //////////////
      else if((s0<100)&&(s1<100)&&(s2>100)&&(s3>100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(250); set_pwm2_duty(240);
            /* Kiri                 Kanan*/
      }
      else if((s0<100)&&(s1<100)&&(s2<100)&&(s3>100)&&(s4<100))
      {
         portc=0b1010000;
         set_pwm1_duty(240); set_pwm2_duty(200);
            /* Kiri                 Kanan*/
      }
      else if((s0<100)&&(s1<100)&&(s2<100)&&(s3>100)&&(s4>100))
      {
         portc=0b1010000;
         set_pwm1_duty(200); set_pwm2_duty(100);
            /* Kiri                 Kanan*/
      }
      else if((s0<100)&&(s1<100)&&(s2<100)&&(s3<100)&&(s4>100))
      {
         portc=0b1010000;
         set_pwm1_duty(250); set_pwm2_duty(100);
            /* Kiri                 Kanan*/
      }
}

kod kat atas adalah function prototype yang dinamakan line following. function prototype ni la bertanggungjawab untuk menggerakkan mobile robot mengikut garisan hitam berlatarbelakang putih.. function prototype ni menggunakan loop if...else.. kalau keadaan 'if' pertama tak padan dengan keadaan semasa, program akan teruskan dengan 'if' yang ke dua dan begitulah seterusnya.. mana 'else' yang terakhir?? kalau ikutkan akhir sekali mesti berakhir dengan 'else' kan.. aku sengaja tak letak 'else' yang terakhir sebab kalau semua keadaan tak padan dengan keadaan semasa, mobile robot tu akan melakukan keadaan terakhir yang dia pernah buat.. kalau dia akhir sekali dia pusing kanan, maka dia akan pusing kanan je sampai dia jumpa keadaan lain..

       if((s0<100)&&(s1<100)&&(s2>100)&&(s3<100)&&(s4<100))
       {
              portc=0b1010000;
               set_pwm1_duty(250); set_pwm2_duty(250);
                           /* Kiri                 Kanan*/
       }

contoh kita ambil 'if' yang pertama.. diingatkan kembali dalam function prototype ADC, variable s0 adalah untuk sensor analog pada pin AN0.. begitu juga untuk variable s1, s2, s3, dan s4 adalah untuk sensor analog pada pin AN1, AN2, AN3, dan AN4.. maksudnya kita ada 5 sensor analog.. kalau rujuk balik entri Mobile Robot (Test Sensor) , aku ada bagi tau yang kalau sensor analog tu kesan permukaan putih, nilai ADC dia lebih kurang 10.. kalu kesan garisan hitam atau tak kesan apa2, nilai ADC dia akan mendekati 255.. kalau tengok kod yang kat atas ni, kita boleh tau yang syarat tu adalah untuk sensor analog pada pin AN2 kesan garisan hitam dan yang lain2 kesan permukaan putih.. sebabnya adalah hanya sensor pada pin AN2 yang nilai ADC lebih 100.. yang lain semua kurang 100.. bayangkan bahawa susunan sensor adalah pin AN0 sebelah paling kiri dan pin AN4 adalah untuk yang sebelah paling kanan.. diingatkan juga bahawa mobile robot perlu mengekalkan garisan pada sensor tengan iaitu sensor pada pin AN2.. kalau garisan tu selekoh kiri dan sensor yang sebelah kiri kesan garisan tu, mobile robot perlulah pusing sebelah kiri.. kelajuan pusing tu adalah bedasarkan sensor mana yang kesan garisan hitam tu. kalu yang ke 2 kiri dia pusing perlahan. kalau yang paling kiri, dy akan pusing laju.

      portc=0b1010000;

kod kat atas adalah keadaan output pada port C yang bermaksud pin RB7 dan pin RB5 pada keadaan 'high' ataupun mengeluarkan voltage 5V. pin2 yang lain pada port C akan keadaan 'low' ataupun dalam keadaan ground @ 0V.. '0b' bermaksud kita tulis dalam base binary. kita juga bole tulis dalam base hex, dan juga decimal.. contoh adalah macam kat bawah:

       binary   : portc=0b10100000;
       hex       : portc=0xA0;
       decimal : portc=160;
     
semua kat atas ni adalah perkara yang sama. cara nak tukarnya adalah macam blajar dalam math sekolah menengah dulu tukar base nombor.. aku ingat dh caranya jadi aku tekan kalkulator je.. haha.. tapi elok korang guna dalam base binary je.. senang buat, senang nak tengok pin mana 'high' @ 'low'.. tapi ada la satu cara yang lagi bagus iaitu dengan declare secara satu per satu pin.. driver untuk motor biasanya hanya guna 2 pin untuk kawal arah untuk 1 motor.. untuk mobile robot kita guna 2 motor, jadi guna 4 pin.. dalam kes ni kita guna pin RC7, RC6, RC5, dan RC4.. jadi kalau iut kes kat atas declare dia cam kat bawah ni:

      output_high(pin_c7);
      output_low(pin_c6);
      output_high(pin_c5);
      output_low(pin_c4);

bagi pin2 port c yang tak declare tu akan berada dalam keadaan float iaitu sama ada 'high' atau 'low'..

      set_pwm1_duty(250); set_pwm2_duty(250);

 kod kat atas adalah untuk set kelajuan motor.. kelajuan motor boleh set dari 0 hingga 255.. 255 adalah kelajuan penuh.. biasanya pada 70 - 80 motor dah tak gerak.. kelajuan ni adalah dikawal oleh pin ccp1 untuk pwm1 dan ccp2 untuk pwm2.. pin ccp teletak kat pin RC1 untuk ccp2 dan RC2 untuk ccp1..

void main()
{
      lcd_init();

      SETUP_ADC_PORTS(ALL_ANALOG );
      SETUP_ADC(ADC_CLOCK_INTERNAL);

      setup_ccp1(CCP_PWM); // Configure CCP1 as PWM
      setup_ccp2(CCP_PWM); // configure CCP2 as PWM
      setup_timer_2(T2_DIV_BY_4, 255, 1); //20khz frequency

      set_tris_c(0x00);

      while(1)
      {

            ADC();
            linefollowing();

      }
}

yang kat atas ni adalah main function.. program akan baca function yang ni dulu sebelum baca function prototype yang lain..

      lcd_init();

yang atas ni perlu declare untuk memastikan LCD boleh diguna. kalau tak declare ni, LCD tak dapat papar apa2.. nak tau lagi boleh tengok dalam library lcd.c..


      SETUP_ADC_PORTS(ALL_ANALOG );
        SETUP_ADC(ADC_CLOCK_INTERNAL);

yang atas ni adalah cara untuk declare ADC port.. dalam kes ni semua  port dari AN0 hingga AN7 adalah dalam mode ADC.. bole juga buat sesetengah port je yang mood ADC.. dalam kes ni rujuk dalam file header PIC 16F877A kat bahagian ADC..


contohnya kalau kita nak port AN0, AN1, dan AN3 je yang analog.. yang lain semua digital.. jadi kena tulis camni:

       SETUP_ADC_PORTS(AN0_AN1_AN3 );

lain PIC kadang2 lain caranya, jadi kenalah rujuk kat header PIC tu.. 

      setup_ccp1(CCP_PWM); // Configure CCP1 as PWM
      setup_ccp2(CCP_PWM); // configure CCP2 as PWM
      setup_timer_2(T2_DIV_BY_4, 255, 1); //20khz frequency

yang kat atas ni pula adalah untuk set pin PWM.. kalau tak declare yang ni, pin RC1 dan RC2 hanyalah pin I/O biasa.. pin RC1 dan RC2 adalah pin Capture/Compare/PWM (ccp) dan kita nak guna mode PWM..

       set_tris_c(0x00);

yang ni pula adalah untuk set I/O port. bagi kes ni adalah bagi port C. diingatkan bahawa 1 adalah untuk set pin sebagai input dan 0 adalah untuk set pin sebagai output.. diingatkan juga bahawa yang set I/O port ni dengan set output yang dibincang tadi adalah perkara berlainan..

      'set_tris_c(0x00);' tak sama dengan ' portc=0b1010000;'

tapi cara untuk declare pin adalah sama sama ada nak guna dalam base bin, hex, atau decimal.. tapi yang ni tak boleh declare satu per satu pin..

       hex       : set_tris_c(0x00);
       binary   : set_tris_c(0b00000000);
       decimal : set_tris_c(0);

yang kat atas tu adalah perkara yang sama je. cuma base je yang berbeza.. kalau nak buat 1 port tu hanya input atau hanya output, senang guna base hex atau decimal je macam kes mobile robot ni.. tapi kalau 1 port tu nak buat ada input dan output elok guna base binary.. contoh kalau aku nak buat port B pin RB7, RB5, RB3, dan RB1 adalah output, yang lain2 adalah input, jadi aku kena tulis cmni:

      set_tris_b(0b01010101);

kalau nak tulis dalam base binary @ decimal pun boleh juga.. kena tukar la ke base yang nak guna tu..

      while(1)
      {

            ADC();
            linefollowing();

      }

yang ni adalah sangat penting.. 'while(1)' bermaksud infinite loop. maksudnya program akan baca function dalam kurungan bawah 'whihe(1)' tu sehingga sistem off.. andai kata sebelum 'while(1)' tu ada function lain, maka function tu hanya dijalankan sekali sahaja untuk sekali sistem on.. 'ADC();' dan 'linefollowing();' tu adalah call function bagi function prototype ADC dan linefollowing yang kita bincangkan kat atas tadi.. program hanya baca main function dahulu. jika dalam main function ada nama function prototype yang kita buat barulah program akan baca program prototype tu..

    kita juga boleh letak function prototype tu kat bawah main function.. sebenarnya lagi selamat letak function prototype bawah main function terutama bila function prototype tu perlu kembalikan nilai2 variable.. untuk pindah function prototype bawah main function, nama function prototype tu mestilah declare dulu selepas global variable.. dalam kes ni, selepas global variable, kenalah tulis

      void ADC();
      void linefollowing();

setakat sini dulu untuk entri ni.. semoga entri ni memberikan manfaat untuk korang semua...


0 comments:

Post a Comment