Thông báo

Collapse
No announcement yet.

Cơ bản về tính toán PWM họ 89xx

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • Cơ bản về tính toán PWM họ 89xx

    Lý thuyết về PWM thì chắc các bạn nhiều bạn đã được học rồi nên mình xin phép không nhắc ở đây "thực ra mình có biết đâu mà nhắc "
    ở đây mình xin trình bày các phương pháp thực hiện PWM 1 bằng phần mềm cho họ 89xxx. Đầu tiên phải nói đến tạo xung (dao động) bằng 89xxx
    sau đây là chương trình đơn giản tạo dao động xung vuông tần số 1000Hz bằng 89xx code cho "Phần cơ bản về timer thì tự bạn đọc sách nhé"
    PHP Code:
    #include <at89x52.h>

    sbit LED=P1.0;

    #define FREQ 1000ul
    #define TEMP (65536-(1000000ul/(FREQ*2)))
    #define tiHigh (TEMP>>8)
    #define tiLow (TEMP & 0xff)

    void conf_main(void);
    void timer0_int(void);

    void main(void)
    {
        
    conf_main();
        
    TR0=1;
        
    EA=1;
        while(
    1)
        {
            
    PCON=IDL_;
        }
    }

    void conf_main(void)
    {
        
    TMOD&=0xf0;
        
    TMOD|=0x01;
        
    TH0=tiHigh;
        
    TL0=tiLow;
        
    ET0=1;
    }

    void timer0_int(voidinterrupt TF0_VECTOR
    {
        
    // cặp TRx={0,1} có thể có có thể không, cái này để dừng đếm timer
        // Nếu bạn dùng timer với số lần tràn thấp thì đừng cho vào để timer không bị sai số
        // Nếu bạn để timer tràn với tốc độ cao khoảng cỡ 10k trở nên và thực hiện nhiều tính toán trong timer
        // thì hãy cho vào để tránh bị ngắt đè ngắt.
        // Ở đây mình mạn phép bỏ đi vì tính toán trong timer này rất ít và tốc độ tràn thấp -> độ chính xác rất cao
        // Mình đã thử tạo dao động 40Khz (80.000) lần tràn timer mà sai số chỉ là 1 xung (39.999 - 40.001)
        // Cái này đã được mình đo bằng thực tế dùng chính 89xx hiển thị lên ma trận 16x32
        
        //TR0=0;
        
    LED=~LED;
        
    TH0=tiHigh;
        
    TL0=tiLow;
        
    //TR0=1;

    Không biết các bạn được học tạo PWM như thế nào, nhưng theo mình thì có ít nhất 2 cách để tạo PWM bằng phần mềm
    cách 1: xê dịch thời gian cho mỗi nửa chu kỳ sao cho vẫn đảm bảo tần số cần tạo mà tạo được sự khác nhau giữa 2 nửa chu kỳ của, với cách này số lần tràn của timer = 2 lần tần số dao động cần tạo
    cách 2: tăng tần số giao động gấp nhiều lần hơn so với cách 1 sau đó "bạt bớt đỉnh dương hoặc đỉnh âm để tạo duty cần thiết"

    Mỗi cách sẽ có ưu điểm và trường hợp áp dụng riêng tùy trường hợp nên dùng cách nào, với cách 1 thì dùng cho hầu hết mọi trường hợp nhưng tính toán và code sẽ khổ hơn 1 chút và độ chính xác sẽ phụ thuộc vào khả năng tối ưu của các bạn vì nếu tối ưu không tốt thì sai số rất nhiều.
    Với cách 2 thì độ chính xác cao hơn nhưng không áp dụng cho mọi trường hợp được. Dưới đây là code theo cách 1 còn cách 2 thì xin phép chỉ các bạn đến 1 thread mà mình viết bài ở đó cho đỡ phải ngồi đăng lại.
    PHP Code:
    #include <at89x52.h>

    // CLOCK_FREQ được tính bằng tần số thạch anh bạn dùng /12 (đối với họ 89xx)
    // Ví dụ nếu bạn dùng thạch anh 24Mhz-> CLOCK_FREQ=24.000.000/12=2.000.000
    // CLOCK_FREQ là số chu kỳ máy thực tế mà bạn có
    #define CLOCK_FREQ 1000000ul

    // Đây là tần số dao động bạn cần tạo
    #define FREQ 10000ul

    /*
       Để tạo ra  tần số dao động là bao nhiêu thì bạn lấy số đó x2 sẽ được
       Tần số tràn thực tế của timer do vậy tần số thực tế bạn cần tạo là 
       gấp 2 lần so với tần số mong muốn tạo ra.
       Chỗ này dùng để tính thời gian giữa 2 lần tràn của timer (2 lần tràn -> 1 xung)
    */
    #define TEMP1 (CLOCK_FREQ/(FREQ*2))

    // Đây là thời gian để nạp vào timer
    #define TEMP2 (65536-TEMP1)

    // Tách lấy phần cao để nạp vào cho THx
    #define tiHigh (TEMP2>>8)

    // Lấy phần thấp để nạp vào TLx
    #define tiLow (TEMP2 & 0xff)

    // Cổng nhận xung PWM
    #define LED P1_0

    // Lưu thời gian cần nạp vào timer để dùng tính toán sau này
    int time=TEMP2;

    // Thời gian 1% duty
    float _1percent=(TEMP1/100.0);

    // Duty bạn muốn tạo
    char duty=50;

    // Biến này lưu giá trị để nạp thêm hoặc giảm đi tạo duty
    int plustime;

    //  Những cài đặt cơ bản nhất
    void conf_main(void);
    void timer0(void);

    int main(void)
    {
        
    conf_main();
        
    TR0=1;
        
    EA=1;
        while(
    1)
        {
            
    PCON=IDL_;
        }
    }

    void timer0(voidinterrupt TF0_VECTOR
    {
        
    volatile int tmp;
        ;
        
    LED=~LED;
        
    TR0=0;
        if(
    LED)
        {
            
    tmp=time+plustime;
            
    TH0=tmp >> 8;
            
    TL0=tmp 0xff;
        }
        else
        {
            
    tmp=time-plustime;
            
    TH0=tmp >> 8;
            
    TL0=tmp 0xff;
        }
        
    TR0=1;
    }

    void conf_main(void)
    {
        
    TMOD=0x01;
        
    ET0=1;
        
    TH0=tiHigh;
        
    TL0=tiLow;
        
    LED=0;
        
        
    // Tính thời gian cần cộng thêm hoặc trừ bớt đi để tạo duty
        
    plustime=(100-duty) * _1percent;


  • #2
    Đây là chủ để mà mình đưa ra cách thứ 2, cách thứ 2 có lợi điểm là tính toán rất ít, độ chính xác cực cao nhưng phải là những trường hợp có yêu cầu duty cụ thể nhất ở những bậc chẵn dạng như (20, 40, 60, 80) và ít mức thì có thể áp dụng vì code quá đơn giản.

    Comment


    • #3
      Với đoạn code theo cách 1 ở trên mình tạo ra Xung theo tính toán là 10Khz với duty=50%, nhưng các bạn hoàn toàn có thể thay thế bằng các con số bất kỳ có thể nằm trong khoảng từ (0-100) đây là khoảng an toàn , nếu ngoài khoảng thì mình chưa thử. Với code ở trên có thể nâng tầm lên 1 chút để có độ chính xác cao hơn vì thực hiện ít tính toán hơn. Dưới đây là 1 chút thay đổi, có thể bạn sẽ bảo là không cần thiết, nhưng thực ra nếu tạo dao động tần số cao 1 chút bạn sẽ hiểu chỉ cần thêm 1 phép tính là độ chính xác bị giảm đi 1 chút.
      PHP Code:
      #include <at89x52.h>

      // CLOCK_FREQ được tính bằng tần số thạch anh bạn dùng /12 (đối với họ 89xx)
      // Ví dụ nếu bạn dùng thạch anh 24Mhz-> CLOCK_FREQ=24.000.000/12=2.000.000
      // CLOCK_FREQ là số chu kỳ máy thực tế mà bạn có
      #define CLOCK_FREQ 1000000ul

      // Đây là tần số dao động bạn cần tạo
      #define FREQ 10000ul

      /*
         Để tạo ra  tần số dao động là bao nhiêu thì bạn lấy số đó x2 sẽ được
         Tần số tràn thực tế của timer do vậy tần số thực tế bạn cần tạo là 
         gấp 2 lần so với tần số mong muốn tạo ra.
         Chỗ này dùng để tính thời gian giữa 2 lần tràn của timer (2 lần tràn -> 1 xung)
      */
      #define TEMP1 (CLOCK_FREQ/(FREQ*2))

      // Đây là thời gian để nạp vào timer
      #define TEMP2 (65536-TEMP1)

      // Cổng nhận xung PWM
      #define LED P1_0

      // Lưu thời gian cần nạp vào timer để dùng tính toán sau này
      int time=TEMP2;

      // Thời gian 1% duty
      float _1percent=(TEMP1/100.0);

      // Duty bạn muốn tạo
      char duty=50;

      // Biến này lưu giá trị để nạp thêm hoặc giảm đi tạo duty
      int plustime;

      unsigned char xTH0xTH1xTL0xTL1;

      //  Những cài đặt cơ bản nhất
      void conf_main(void);
      void timer0(void);

      int main(void)
      {
          
      conf_main();
          
      TR0=1;
          
      EA=1;
          while(
      1)
          {
              
      PCON=IDL_;
          }
      }

      void timer0(voidinterrupt TF0_VECTOR
      {
          
      LED=~LED;
          
      TR0=0;
          if(
      LED)
          {
              
      TH0=xTH0;
              
      TL0=xTL0;
          }
          else
          {
              
      TH0=xTH1;
              
      TL0=xTL1;
          }
          
      TR0=1;
      }

      void conf_main(void)
      {
          
      volatile int tmp;
          ;
          
      TMOD=0x01;
          
      ET0=1;
          
      TH0=time >> 8;
          
      TL0=time 0xff;
          
      LED=0;
          
          
      // Tính thời gian cần cộng thêm hoặc trừ bớt đi để tạo duty
          
      plustime=(100-duty) * _1percent;
          
      // Bất cứ khi nào có sự thay đổi duty thì bạn cho đoạn code này vào
          
      tmp=time+plustime;
          
      xTH0=tmp>>8;
          
      xTL0=tmp&0xff;
          
          
      tmp=time-plustime;
          
      xTH1=tmp>>8;
          
      xTL1=tmp&0xff;

      Comment


      • #4
        Với đoạn code theo cách 1 ở trên mình tạo ra Xung theo tính toán là 10Khz với duty=50%, nhưng các bạn hoàn toàn có thể thay thế bằng các con số bất kỳ có thể nằm trong khoảng từ (0-100) đây là khoảng an toàn , nếu ngoài khoảng thì mình chưa thử. Với code ở trên có thể nâng tầm lên 1 chút để có độ chính xác cao hơn vì thực hiện ít tính toán hơn. Dưới đây là 1 chút thay đổi, có thể bạn sẽ bảo là không cần thiết, nhưng thực ra nếu tạo dao động tần số cao 1 chút bạn sẽ hiểu chỉ cần thêm 1 phép tính là độ chính xác bị giảm đi 1 chút.
        PHP Code:
        #include <at89x52.h>

        // CLOCK_FREQ được tính bằng tần số thạch anh bạn dùng /12 (đối với họ 89xx)
        // Ví dụ nếu bạn dùng thạch anh 24Mhz-> CLOCK_FREQ=24.000.000/12=2.000.000
        // CLOCK_FREQ là số chu kỳ máy thực tế mà bạn có
        #define CLOCK_FREQ 1000000ul

        // Đây là tần số dao động bạn cần tạo
        #define FREQ 10000ul

        /*
           Để tạo ra  tần số dao động là bao nhiêu thì bạn lấy số đó x2 sẽ được
           Tần số tràn thực tế của timer do vậy tần số thực tế bạn cần tạo là 
           gấp 2 lần so với tần số mong muốn tạo ra.
           Chỗ này dùng để tính thời gian giữa 2 lần tràn của timer (2 lần tràn -> 1 xung)
        */
        #define TEMP1 (CLOCK_FREQ/(FREQ*2))

        // Đây là thời gian để nạp vào timer
        #define TEMP2 (65536-TEMP1)

        // Cổng nhận xung PWM
        #define LED P1_0

        // Lưu thời gian cần nạp vào timer để dùng tính toán sau này
        int time=TEMP2;

        // Thời gian 1% duty
        float _1percent=(TEMP1/100.0);

        // Duty bạn muốn tạo
        char duty=50;

        // Biến này lưu giá trị để nạp thêm hoặc giảm đi tạo duty
        int plustime;

        unsigned char xTH0xTH1xTL0xTL1;

        //  Những cài đặt cơ bản nhất
        void conf_main(void);
        void timer0(void);

        int main(void)
        {
            
        conf_main();
            
        TR0=1;
            
        EA=1;
            while(
        1)
            {
                
        PCON=IDL_;
            }
        }

        void timer0(voidinterrupt TF0_VECTOR
        {
            
        LED=~LED;
            
        TR0=0;
            if(
        LED)
            {
                
        TH0=xTH0;
                
        TL0=xTL0;
            }
            else
            {
                
        TH0=xTH1;
                
        TL0=xTL1;
            }
            
        TR0=1;
        }

        void conf_main(void)
        {
            
        volatile int tmp;
            ;
            
        TMOD=0x01;
            
        ET0=1;
            
        TH0=time >> 8;
            
        TL0=time 0xff;
            
        LED=0;
            
            
        // Bất cứ khi nào có sự thay đổi duty thì bạn cho đoạn code này vào
            // Tính thời gian cần cộng thêm hoặc trừ bớt đi để tạo duty
            
        plustime=(100-duty) * _1percent;
            
            
        tmp=time+plustime;
            
        xTH0=tmp>>8;
            
        xTL0=tmp&0xff;
            
            
        tmp=time-plustime;
            
        xTH1=tmp>>8;
            
        xTL1=tmp&0xff;

        Comment


        • #5
          góp cùng với myth-coder 1 đoạn code cùi bắp. dùng thạch anh 12M

          Code:
           #include<regx52.h>
          sbit 	LED=P2^0;
          void delayus(int time)
          {	
          	int n;
          	for(n=0;n<time;n++);
          }
          void sangdan()
          {	
          	int n,time;
          	time=350;	
          	for(n=1;n<time;n++)
          	{
          		LED=0;delayus(time-n);
          		LED=1;delayus(n);
          	}
          }
          void toidan()
          {	
          	int n,time;
          	time=350;	
          	for(n=1;n<time;n++)
          	{
          		LED=0;delayus(n);
          		LED=1;delayus(time-n);
          	}
          }
          main()
          {	
          	for(;;)
          	{
                          sangdan();
                          toidan();
          	}
          }
          chỉ có tâm hồn là nơi duy nhất: có thể biến thiên đường thành địa ngục và ngược lại có thể biến địa ngục hóa thiên đường
          Everything should be made as simple as possible, but not simpler

          Comment


          • #6
            Nguyên văn bởi daohuytien Xem bài viết
            góp cùng với myth-coder 1 đoạn code cùi bắp. dùng thạch anh 12M
            Code:
             #include<regx52.h>
            sbit 	LED=P2^0;
            void delayus(int time)
            {	
            	int n;
            	for(n=0;n<time;n++);
            }
            void sangdan()
            {	
            	int n,time;
            	time=350;	
            	for(n=1;n<time;n++)
            	{
            		LED=0;delayus(time-n);
            		LED=1;delayus(n);
            	}
            }
            void toidan()
            {	
            	int n,time;
            	time=350;	
            	for(n=1;n<time;n++)
            	{
            		LED=0;delayus(n);
            		LED=1;delayus(time-n);
            	}
            }
            main()
            {	
            	for(;;)
            	{
                            sangdan();
                            toidan();
            	}
            }
            , Ý của cái này không phải chỉ là sáng dần tối dần đâu bác ạ, mà ta có thể chọn trực tiếp độ sáng "nếu muốn" cơ, nó giống như volume cho TIVI chẳng hạn mình không phải chỉ cứ cho to dần rồi nhỏ dần mà còn muốn nó giữ ở 1 mức âm lượng nào đó tùy thích nữa cơ. Nói chung đây là những tính toán PWM căn bản nhất mà em ngộ ra được, có thể còn thiếu xót nhưng giờ em mới đi tới đó thôi .

            Comment


            • #7
              Code:
              	for(n=1;n<time;n++)
              	{
              		LED=0;delayus(n);
              		LED=1;delayus(time-n);
              	}
              bạn muốn nó sáng ở mức nào đó thì chỉ cần chọn cái " n " này ở một mức nào đó trong dải 1 to time.
              trong cái hàm trên nó đã bao gồm rất nhiều mức sáng khác nhau rồi. Mình quen làm việc thực hành, còn lý thuyết chắc không nhuần nhuyễn. Có gì sai sót xin được lượng thứ.
              chỉ có tâm hồn là nơi duy nhất: có thể biến thiên đường thành địa ngục và ngược lại có thể biến địa ngục hóa thiên đường
              Everything should be made as simple as possible, but not simpler

              Comment


              • #8
                Có cái nì nèhttp://www.dientuvietnam.net/forums/...93/#post621935

                Email:
                Tel: 0983.497.310

                Comment


                • #9
                  Có vẻ như bạn đang nói đến cái này ? Nhưng cái LED sao băng thì đâu có phải dùng đến cái khổng lồ như ở đây đâu? Cái này có thể được dùng cho rất nhiều mục đích với độ tin cậy cao hơn chứ không phải chỉ là cái sao băng bé xíu xiu. Tại vì thấy trên diễn đàn có người hỏi mà bê 1 đoạn code chẳng rõ ràng gì và comment bằng tiếng anh với những tính toán không thực sự dễ hiểu, nên mình mới viết cái này để đưa ra cách tính toán cơ bản nhất về PWM mà mình đúc kết ra thôi.

                  Comment


                  • #10
                    các huynh nói toàn về IC họ AT thui hả, e chỉ mới học về PIC, e k pk là lập trình cho AT có giống cho PIC k mấy huynh

                    Comment


                    • #11
                      anh myth-code rat hay em chi moi hoc chut it thoi xin hoc hoi o anh nhieu. ma anh co code trai tren vi deo khong cho em nhe em thay thich that co the gui email:hoangnhan1129@gmail.com eam on anh nhieu nhe

                      Comment


                      • #12
                        Nguyên văn bởi hoangnhan29 Xem bài viết
                        anh myth-code rat hay em chi moi hoc chut it thoi xin hoc hoi o anh nhieu. ma anh co code trai tren vi deo khong cho em nhe em thay thich that co the gui email:hoangnhan1129@gmail.com eam on anh nhieu nhe
                        Hướng dẫn và sườn code thì được chứ không bao giờ tớ cho ai code cả, cái sườn đối với tớ nó là của chung còn ý tưởng sáng tạo thì của riêng mỗi người. Nếu chăm học thì tớ không phải cho cũng làm được, còn nếu lười thì tớ cho cũng chỉ làm đồ trang trí cho các bạn chứ chẳng phát triển được gì cả.

                        Comment


                        • #13
                          anh myth-code anh có thể hướng dẫn em làm mạch trái không e thấy anh làm rất hay nếu được em rất cám ơn anh

                          Comment


                          • #14
                            anh myth-code anh có thể hướng dẫn em làm mạch trái không e thấy anh làm rất hay nếu được em rất cám ơn anh
                            email:hoangnhan1129@gmail.com

                            Comment


                            • #15
                              bạn giải thích cho mình đoạn code trên được k

                              Comment

                              Về tác giả

                              Collapse

                              myth-coder Tìm hiểu thêm về myth-coder

                              Bài viết mới nhất

                              Collapse

                              Đang tải...
                              X