BÀI 4: CÁC PHƯƠNG PHÁP ĐO TẦN SỐ TÍN HIỆU

Thảo luận trong 'KIT PIC V3' bắt đầu bởi quang.dt7bk, 22 Tháng hai 2013.

Users Viewing Thread (Users: 0, Guests: 0)

Lượt xem: 7,872

  1. quang.dt7bk

    quang.dt7bk Admin

    XIN CHÀO CÁC BẠN!
    Trong bài này mình sẽ hướng dẫn các bạn những phương pháp đo tần số tín hiệu với PIC 16F877A, giá trị tần số sẽ được hiển thị lên màn hình LCD1602. (Với các kiến thức của bài viết này, mình hy vọng giúp các bạn giải quyết được bài toán đo tốc độ động cơ DC encoder với PIC :) ).

    I.CÁC PHƯƠNG PHÁP ĐO TẦN SỐ TÍN HIỆU.
    Để đo tần số của một tín hiệu bất kỳ với vi điều khiển PIC 16F877A chúng ta sẽ có 2 cách làm như sau:
    ØSử dụng chế độ Input Capture được tích hợp sẵn trong PIC 16F877A.
    ØĐếm số xung của tín hiệu trong thời gian mẫu 1s (số xung đấy chính là tần số tín hiệu).
    Dưới đây mình sẽ hướng dẫn các bạn từng phương pháp cũng như ưu điểm, nhược điểm của nó trong việc đo tần số tín hiệu.

    II.ĐO TẦN SỐ TÍN HIỆU VỚI PIC 16F877A.
    1.Chế độ Input Capture.
    Các bạn có thể tham khảo thêm chế độ này dưới đây:
    Với chế độ này, việc đo tần số tín hiệu nhỏ trở nên rất đơn giản, tuy nhiên một vấn đề đặt ra là khi tần số tín hiệu quá lớn, giả sử như xung tín hiệu trả về từ ENCODER trong bài toán đo tốc độ động cơ DC. Khi đó Capture xảy ra liên tục đồng nghĩa chương trình cũng phải liên tục nhảy vào thực hiện ngắt Input Capture, điều này làm cho vi điều khiển không còn đủ thời gian để thực hiện các yêu cầu khác.
    Do đó trong bài toán đo tốc độ động cơ nếu sử dụng Input Capture hệ thống điều khiển của bạn có thể bị đơ.

    2.Đo số xung của tín hiệu trong thời gian lấy mẫu 1s.
    Với phương pháp này, số xung của tín hiệu đếm được trong 1s chính là tần số của tín hiệu. Để thực hiện phương pháp đo này cùng những tài nguyên của PIC 16F877A, mình sẽ giới thiệu với các bạn 2 cách thực hiện:
    ØSử dụng 1 bộ Timers và 1 ngắt ngoài: trong đó, bộ Timer dùng để tạo thời gian mẫu 1s, ngắt ngoài để đếm số xung của tín hiệu.
    ØSử dụng 2 bộ Timers: trong đó, 1 bộ Timer dùng để tạo thời gian lấy mẫu 1s, 1 bộ Timer được sử dụng như chức năng đếm sự kiện.
    Ưu điểm của phương pháp này là đo được tần số tín hiệu lớn, đến đây bài toán đo tốc độ động cơ sử dụng Encoder đã được giải quyết.

    Trong bài viết này, mình sẽ hướng dẫn các bạn sử dụng 1 bộ Timer và 1 ngắt ngoài để đo tần số (cách sử dụng 2 bộ Timer các bạn có thể về tìm hiểu thêm). Cụ thể là bộ Timer 1Ngắt ngoài trên chân RB0.
    Khi có một sự thay đổi mức tín hiệu (từ cao à thấp, thấp à cao) trên chân RB0 sẽ làm tăng giá trị của biến đếm so_xung. Khi Timer1 đếm đủ 1s, mình chỉ cần lưu lại giá trị của biến đếm và hiển thị lên LCD1602.

    Dưới đây mình sẽ giới thiệu các hàm xử lý cho Timer và ngắt mà trình biên dịch CCS hỗ trợ:
    TIMER1:
    ØSetup_timer_1(mode): cấu hình tần số hoạt động cho bộ Timer1.
    ØSet_timer1(value): cấu hình giá trị cho thanh ghi đếm của bộ Timer1.
    ØGet_timer1(): Hàm trả về giá trị của thanh ghi đếm của bộ Timer1.

    INTERRUPTS :
    Các bạn có thể vào View à Valid Interrupts sau đó chọn PIC 16F877A để biết được các ngắt mà 16F877A có. Trong đó, ngắt ngoài là #INT_EXT, ngắt tràn Timer 1 là #INT_TIMER1. Các hàm xử lý liên quan đến ngắt:
    ØChỉ thị #INT_EXT: sau chỉ thị này sẽ là chương trình con phục vụ khi xảy ra ngắt ngoài (tương tự với #INT_TIMER1: sau chỉ thị là chương trình phục vụ ngắt tràn TIMER1).
    ØEnable_interrupts(tên ngắt): cho phép ngắt hoạt động.
    ØDisable_interrupts(tên ngắt): không cho phép ngắt hoạt động.
    ØExt_int_edge(mode): lựa chọn dạng xung tín hiệu tạo ra ngắt ngoài trên chân RB0/INT, mode ở đây có thể là: H_TO_L (cạnh xuống), L_TO_H (cạnh lên).

    Các bạn tham khảo file main.c của chương trình:
    HTML:
    #include <main.h>
    #include <var.h>
    #include <lcd16x2\lcd_16x2.c>
     
    #INT_EXT
    void dem_xung()  // Trinh phuc vu ngat ngoai
    {
      so_xung+=1;
    }
     
    #INT_TIMER1
    void tao_tre_1s() // Trinh phuc vu ngat tran TIMER1
    {
      setup_timer_1(T1_DISABLED);
      count_t1++;
      if(count_t1==38)  // Tao khoang thoi gian 1s
      {
          tan_so = so_xung;
          so_xung = 0;
          count_t1 = 0;
          enable_display = 1;
      }
      set_timer1(0x0000);
      setup_timer_1(T1_INTERNAL|T1_DIV_BY_2);
    }
     
    void main()
    {
      DDRD = DDROUT;
     
      enable_interrupts(INT_TIMER1);  // Cho phep ngat tran TIMER1
      ext_int_edge(H_TO_L);            // Cau hinh ngat ngoai theo canh xuong
      enable_interrupts(INT_EXT);      // Cho phep ngat ngoai
      enable_interrupts(GLOBAL);      // Cho phep ngat toan cuc
      setup_timer_1(T1_INTERNAL|T1_DIV_BY_2);  // Cau hinh bo chia cho TIMER1
      set_timer1(0x0000);              // Cau hinh gia tri cho thanh ghi TMR1
     
      LCD_Init();
      LCD_Clear();
      LCD_Gotoxy(0,0);
      sprintf(lcd_buff," MINHHAGROUP");
      LCD_Puts(lcd_buff);
      while(TRUE)
      {
          if(enable_display)
          {
            LCD_Gotoxy(0,1);
            sprintf(lcd_buff,"TAN SO: %6lu",tan_so);
            LCD_Puts(lcd_buff);
          }
      }
    }
    Code và mô phỏng các bạn có thể download ở cuối bài.
    CHÚC CÁC BẠN THÀNH CÔNG!

    Các file đính kèm:

  2. Các file đính kèm:

  3. Các file đính kèm:

  4. an.dxuan

    an.dxuan Admin

    Chào bạn !Mình có dow code và mô phỏng của bạn xuống xem thì mình thấy bạn dùng ngắt ngoài để đếm số xung trả về nhưng mô phỏng chưa đúng nhé ,PIC16F887 chân ngắt ngoài là RB0 nhưng trong mô phỏng bạn đang kết nối với chân RC0 ,nữa là khi dùng ngắt ngoài thì bạn khai báo trong chương trình chân đó là input nhé ,mình thấy trong chương trình của bạn cũng chưa có ,bạn vẽ lại mô phỏng và sửa lại thuật toán cho đúng theo mục đích của bạn nhé . Chúc bạn thành công!
    lâm nhật quân thích bài này.
  5. an.dxuan

    an.dxuan Admin

    Chào bạn !Mình có dow code và mô phỏng của bạn xuống xem thì mình thấy bạn dùng ngắt ngoài để đếm số xung trả về nhưng mô phỏng chưa đúng nhé ,PIC16F887 chân ngắt ngoài là RB0 nhưng trong mô phỏng bạn đang kết nối với chân RC0 ,nữa là khi dùng ngắt ngoài thì bạn khai báo trong chương trình chân đó là input nhé ,mình thấy trong chương trình của bạn cũng chưa có ,bạn vẽ lại mô phỏng và sửa lại thuật toán cho đúng theo mục đích của bạn nhé . Chúc bạn thành công!
    lâm nhật quân thích bài này.
  6. Cảm ơn bạn đã góp ý :D
  7. Cảm ơn an.dxuan đã xem qua và góp ý :D , mình mô phỏng và chạy đựợc nhưng kết quả đếm lại không chính xác ở tần số cao:eek: ,
    lệnh setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
    set_timer1(15536);
    Mình dùng thạch anh 16m timer1 đếm xung nội , giá trị ban đầu là 15536 như vậy đếm đựợc 50 ngàn xung , 1 chu kì lệnh là 16M/4 sau đó chia cho 8 thì đc 2us , như vậy timer 1 đếm là 50 ngàn X 2us là đc 100ms , sau đó mình nhân thêm 10 để đc delay 1s nhưng kết quả đếm lại không đúng
    một vấn đề nữa là ở chỗ
    DEM_T1++;

    if(DEM_T1=10)
    {
    TANSO = DEM_XUNG;
    DEM_XUNG = 0;
    DEM_T1 = 0;
    ENABLE_DISPLAY = 1;
    }
    mình đã thay đổi giá trị DEM_T1 trong if ex : if(DEM_T1=6) mà giá trị tần số vẫn ko thay đổi
    Mình gởi lại cái file mới sữa bạn có thể xem giúp mình tí nha : :)
    thân !!!

    Các file đính kèm:

  8. quang.dt7bk

    quang.dt7bk Admin

    Chào bạn!
    Xem qua code của bạn, mình có nhận xét như sau:
    - Trong trình phục vụ ngắt tràn timer1, dòng lệnh if(DEM_T1=10) là sai, phải là if(DEM_T1==10), toán tử logic.
    - Trong chương trình chính, bạn nên cấu hình phần cứng cho pic trước (cụ thể ở đây là cấu hình cho bộ timer1, ngắt tràn timer...) rồi mới cấu hình cho các thiết bị ngoại vi (ở đây là LCD).
    - Như mình đã nói ở trên, không nhất thiết bạn phải tạo khoảng thời gian lấy mẫu 1s vì như vậy sai số bạn đo càng lớn và sẽ tốn thời gian thực thi của pic. Bạn có thể chọn thời gian lấy mẫu nhỏ xuống, giả sử là 100ms, sau đó lấy số xung đếm được nhân với 10 sẽ được tần số, điều này giúp bạn giảm sai số phép đo.
    Chúc bạn thành công!
  9. đã test xong mạch đo tần số , chạy ok , thank mọi người , hiện tại mình muốn giao tiếp máy tính qua cổng rs232 , dùng c#
    nhấn start , đưa giá trị tần số đo được hiển thị trên textbox , theo mình hiểu khi nhấn start , cho phép gởi 1 kí tự đến pic, khi pic nhận đc thì truyền dữ liệu lên máy tính
    đây là code của mình , trong case '1' mình cần viết ntn để truyền đc dl . mong mọi ng giúp đỡ
    #include <main.h>
    #include <var.h>
    #include <lcd16x2\lcd_16x2.c>
    #INT_RDA // ngat nhan du lieu tu may tinh
    void ngat_noitiep()
    {
    char kitu;
    kitu=getc();
    switch(kitu)
    {
    case '0': //xoa man hinh LCD
    {
    LCD_Clear();
    break;
    }
    case '1':// truyen len may tinh gia tri do duoc
    {
    .....
    }
    }
    }
    #INT_EXT
    void dem_xung() // Trinh phuc vu ngat ngoai
    {
    so_xung+=1;
    }

    #INT_TIMER1
    void tao_tre_1s() // Trinh phuc vu ngat tran TIMER1
    {
    setup_timer_1(T1_DISABLED);
    count_t1++;
    if(count_t1==39) // Tao khoang thoi gian 1s
    {
    tan_so = so_xung;
    so_xung = 0;
    count_t1 = 0;
    enable_display = 1;
    }
    set_timer1(1434);
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_2);
    }
    void locso(unsigned int16 so)
    {
    tp=so%10+48;
    so=so/10;
    dv=so%10+48;
    so=so/10;
    ch=so%10+48;

    }
    void main()
    {
    DDRD = DDROUT;
    DDRE = DDROUT;

    enable_interrupts(int_rda); //cho phep ngat noi tiep nhan
    enable_interrupts(INT_TIMER1); // Cho phep ngat tran TIMER1
    ext_int_edge(H_TO_L); // Cau hinh ngat ngoai theo canh xuong
    enable_interrupts(INT_EXT); // Cho phep ngat ngoai
    enable_interrupts(GLOBAL); // Cho phep ngat toan cuc
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_2); // Cau hinh bo chia cho TIMER1
    set_timer1(1434); // Cau hinh gia tri cho thanh ghi TMR1

    LCD_Init();
    LCD_Clear();
    LCD_Gotoxy(0,0);
    sprintf(lcd_buff,"DO_AM_MOI_TRUONG");
    LCD_Puts(lcd_buff);
    while(TRUE)
    {
    if(enable_display)
    {
    if(tan_so<5000)
    {
    DO_AM=0;
    LCD_Gotoxy(0,1);
    sprintf(lcd_buff,"GIA TRI BI LOI: %10luHz",DO_AM);// cái này không dùng
    LCD_Puts(lcd_buff);
    enable_display = 0;
    }
    else if(5000<=tan_so<=10000)
    {
    tam=(tan_so-5000)/5;
    locso(tam);
    command_write(0xc0);
    data_write('G');
    data_write('I');
    data_write('A');
    data_write(' ');
    data_write('T');
    data_write('R');
    data_write('I');
    data_write(':');
    data_write(' ');
    data_write(' ');
    data_write(' ');
    data_write(ch);
    data_write(dv);
    data_write('.');
    data_write(tp);
    data_write('%');
    }
    }
    }
    }
  10. đã test xong mạch đo tần số , chạy ok , thank mọi người , hiện tại mình muốn giao tiếp máy tính qua cổng rs232 , dùng c#
    nhấn start , đưa giá trị tần số đo được hiển thị trên textbox , theo mình hiểu khi nhấn start , cho phép gởi 1 kí tự đến pic, khi pic nhận đc thì truyền dữ liệu lên máy tính
    đây là code của mình , trong case '1' mình cần viết ntn để truyền đc dl . mong mọi ng giúp đỡ
    #include <main.h>
    #include <var.h>
    #include <lcd16x2\lcd_16x2.c>
    #INT_RDA // ngat nhan du lieu tu may tinh
    void ngat_noitiep()
    {
    char kitu;
    kitu=getc();
    switch(kitu)
    {
    case '0': //xoa man hinh LCD
    {
    LCD_Clear();
    break;
    }
    case '1':// truyen len may tinh gia tri do duoc
    {
    .....
    }
    }
    }
    #INT_EXT
    void dem_xung() // Trinh phuc vu ngat ngoai
    {
    so_xung+=1;
    }

    #INT_TIMER1
    void tao_tre_1s() // Trinh phuc vu ngat tran TIMER1
    {
    setup_timer_1(T1_DISABLED);
    count_t1++;
    if(count_t1==39) // Tao khoang thoi gian 1s
    {
    tan_so = so_xung;
    so_xung = 0;
    count_t1 = 0;
    enable_display = 1;
    }
    set_timer1(1434);
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_2);
    }
    void locso(unsigned int16 so)
    {
    tp=so%10+48;
    so=so/10;
    dv=so%10+48;
    so=so/10;
    ch=so%10+48;

    }
    void main()
    {
    DDRD = DDROUT;
    DDRE = DDROUT;

    enable_interrupts(int_rda); //cho phep ngat noi tiep nhan
    enable_interrupts(INT_TIMER1); // Cho phep ngat tran TIMER1
    ext_int_edge(H_TO_L); // Cau hinh ngat ngoai theo canh xuong
    enable_interrupts(INT_EXT); // Cho phep ngat ngoai
    enable_interrupts(GLOBAL); // Cho phep ngat toan cuc
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_2); // Cau hinh bo chia cho TIMER1
    set_timer1(1434); // Cau hinh gia tri cho thanh ghi TMR1

    LCD_Init();
    LCD_Clear();
    LCD_Gotoxy(0,0);
    sprintf(lcd_buff,"DO_AM_MOI_TRUONG");
    LCD_Puts(lcd_buff);
    while(TRUE)
    {
    if(enable_display)
    {
    if(tan_so<5000)
    {
    DO_AM=0;
    LCD_Gotoxy(0,1);
    sprintf(lcd_buff,"GIA TRI BI LOI: %10luHz",DO_AM);// cái này không dùng
    LCD_Puts(lcd_buff);
    enable_display = 0;
    }
    else if(5000<=tan_so<=10000)
    {
    tam=(tan_so-5000)/5;
    locso(tam);
    command_write(0xc0);
    data_write('G');
    data_write('I');
    data_write('A');
    data_write(' ');
    data_write('T');
    data_write('R');
    data_write('I');
    data_write(':');
    data_write(' ');
    data_write(' ');
    data_write(' ');
    data_write(ch);
    data_write(dv);
    data_write('.');
    data_write(tp);
    data_write('%');
    }
    }
    }
    }
  11. tại sao khi dùng ngắt nối tiếp giá trị tần số lại ko chính xác nhỉ

    Các file đính kèm:

  12. tại sao khi dùng ngắt nối tiếp giá trị tần số lại ko chính xác nhỉ , bỏ đoạn code ngắt nối tiếp thì lại chạy đc
  13. quang.dt7bk

    quang.dt7bk Admin

    Chào bạn!
    Mình đã xem qua code của bạn, khi sử dụng thêm ngắt nối tiếp, chương trình có cảnh báo: Interrupts disabled during call to prevent re-entrancy lỗi này tức là thời gian xử lý trong ngắt của bạn quá lâu dẫn đến việc các ngắt có thể trùng nhau, cụ thể ở đây, thời gian xử lý trong ngắt nối tiếp của bạn quá lâu, có thể trùng với ngắt ngoài hoặc ngắt timer. Đây có thể là lý do việc đo tần số của bạn bị sai, bạn nên xem lại chỗ này nhé.
    Chúc bạn thành công!
  14. làm sao khắc phục đc lỗi này vậy :( . mình cũng mới làm quen với pic, mong quang.dt7bk khắc phục giúp mình cái
    mình còn 1 vấn đề này nữa mình muốn làm mạch LDR giao tiếp với pic , tạo ra 4 mức ánh sáng , code thì chạy nhưng mình không hiểu tại sao kết quả đọc đc từ adc lại chia cho 0x32 để ra giá trị thập phân của điện áp ngõ ra LDR
    thank nhiều
  15. tam762410

    tam762410 Thành Viên Nổi bật

    bạn cấu hình timer1 lạ nhỉ.. thương thì setup_timrer_1 đặt trong chương trình chính.
  16. tam762410

    tam762410 Thành Viên Nổi bật

    bạn cấu hình timer1 lạ nhỉ.. thương thì setup_timrer_1 đặt trong chương trình chính.
  17. wang ding xiao

    wang ding xiao New Member

    Kết quả mô phỏng natf có được không
    tn31.GIF
    tn3.GIF
  18. wang ding xiao

    wang ding xiao New Member

  19. transon

    transon New Member

    bạn ơi cho mình xin fille code và mô phỏng này được ko
  20. navypro_hvhq

    navypro_hvhq Member

    mình đang làm một con LC metter nên cần có phần đo tần số cộng hưởng các bạn ah mọi người hỗ trợ mình nhé

Chia sẻ trang này

Lên trên