AVR - Điều khiển động cơ SERVO siêu chuẩn với biên độ góc cực nhỏ!

Mô tả dự án: 

Tận dụng ưu thế của TIMER1 trên arduino, việc ứng dụng nó để xuất ra xung điều khiển servo với độ chính xác cao là rất khả thi. Nó cho phép servo quay với góc cực nhỏ, nhỏ đến cỡ nào ? BẠn hãy đọc bài này sẽ rõ. devil

Nhỏ nhất là bao nhiêu ?

Mình nói luôn nè, xung điều khiển xuất ra cho phép ta điều khiển servo với góc lệch nhỏ nhất là 0.006 độ.

Tìm hiểu xung điều khiển SERVO.

xung PPM (Cũng là một xung PWM ). Với độ rộng xung cao tỷ lệ thuận với góc lệch của SERVO.

 

Với độ rộng xung  từ 1ms (1000us) => 2ms (2000us) thì servo được đặt với góc lệch tương ứng trong khoảng 0 =>180 (độ). Xung có chu kì 20ms, cho phép đặt lên 10 kênh servo khác nhau.

Tạo xung điều khiển có độ chính xác cao hơn.

Ta đã biết ưu điểm của bộ TIMER 1 là cho phép đếm với độ phân giải 16bit. Cùng với đó là khả năng tùy chỉnh ngắt tràn (TOP_value).

Đề bài

Tạo xung chu kì 5ms, với độ rộng xung cao thay đổi được trong khoảng 1000us => 2000 us.

Code tạo xung trên pin ~9 theo đề bài


unsigned int Gia_tri_moi;
void setup()
{

    TCCR1A = 0;
    TCCR1B = 0;
    // RESET lại 2 thanh ghi
    DDRB |= (1 << PB1);
    // Đầu ra PB1 là OUTPUT ( pin 9)

    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12) | (1 << WGM13);
    // chọn Fast PWM, chế độ chọn TOP_value tự do  ICR1
    TCCR1A |= (1 << COM1A1);
    // So sánh thường( none-inverting)

    TCCR1B |= (1 << CS11);
    // P_clock=16mhz/8=2mhz
    // mỗi P_clock bằng 1/2mhz= 0.5 us
    OCR1A = 2000;
    Gia_tri_moi = OCR1A;
    // Value=2000 , tương đương xung có độ rộng 2000*0.5us=1000us (1ms)
    // Value=4000, tương đương xung có độ rộng 4000*0.5us=2000us (2ms)
    ICR1 = 40000;
    // xung răng cưa tràn sau 40000 P_clock, tương đương (20ms)
}

void loop()
{
    set(2000); // 0 độ
}
void set(unsigned int x)
{
    if (Gia_tri_moi != x) {
        OCR1A = x;
        Gia_tri_moi = OCR1A;
    }
    else {
        return; // thoát ngay
    }
    // x : 2000 - 4000
    //Độ rộng: 1ms - 2 ms
}

Test servo

Giữ nguyên code trên, sửa hàm loop thành:

void loop()
{
    set(2000); // 0 độ
    delay(1000);
    set(4000); // 180 độ
    delay(1000);
}

Kết quả

Servo chỉ lệch góc trong khoảng 0 đến 90 độ !!!!!!!!!!!!!!!!!!!

Code sai ??!!!

Đến đây, mình đã bối rối khi làm đúng lý thuyết mà nó lại không như lý thuyết tí nào .angry

Lý thuyết nói: Xung có độ rộng 1ms => lệch 0, xung rộng 2ms => lệch 180. Xung có tần số 50 hz. (xung PPM cần độ dài tối thiểu 20ms để (có thể) ghép tối đa 10 servo).

Tất nhiên mình đã đáp ứng đúng tiêu chí trên.

Sự thật đằng sau!

Đã vậy thì mình sẽ kiểm tra xung của thư viện điều khiển Servo (servo.h) xem có sự sai khác gì không.

#include <Servo.h>

Servo s9, s10; // tạo 2 Object
void setup()
{
    // pin ra ~9, ~10
    s9.attach(9);
    s10.attach(10);
    // xuất xung
    s9.write(0); // 0 độ
    s10.write(180); //180 độ
}

void loop()
{
}

Kết quả

F_pwm=50hz.

Độ rộng xung: 530us (0.53ms) tương ứng góc lệch 0.

Độ rộng xung : 2410us (2.41ms) tương ứng góc lệc 180.

 

Đó, thực tế đó.! devil

Sửa lại độ rộng xung

unsigned int Gia_tri_moi;
void setup()
{

    TCCR1A = 0;
    TCCR1B = 0;
    // RESET lại 2 thanh ghi
    DDRB |= (1 << PB1);
    // Đầu ra PB1 là OUTPUT ( pin 9)

    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12) | (1 << WGM13);
    // chọn Fast PWM, chế độ chọn TOP_value tự do  ICR1
    TCCR1A |= (1 << COM1A1);
    // So sánh thường( none-inverting)

    TCCR1B |= (1 << CS11);
    // P_clock=16mhz/8=2mhz
    // mỗi P_clock bằng 1/2mhz= 0.5 us
    OCR1A = 1060;
    Gia_tri_moi = OCR1A;
    // Value=1060 , tương đương xung có độ rộng 1060*0.5us=530us (0.53ms)
    // Value=4820, tương đương xung có độ rộng 4820*0.5us=2410us (2,41ms)
    ICR1 = 40000;
    // xung răng cưa tràn sau 40000 P_clock, tương đương (20ms)
}

void set(unsigned int x)
{
    if (Gia_tri_moi != x) {
        OCR1A = x;
        Gia_tri_moi = OCR1A;
    }
    else {
        return; // thoát ngay
    }
    // x : 1060 - 4820
    //Độ rộng: 0.53ms - 2.41 ms
}

void loop()
{
    set(1060); // 0 độ
    delay(1000);
    set(4820); // 180 độ
    delay(1000);
}

 

OK, thành công òi.heart

TỐI ƯU HÓA

Sử dụng P_clock=16mhz nâng tối đa độ phân giải của Timer 16bit. Khi đó độ phân giải nhỏ nhất là 0.0625us trên 1 xung P_clock.  

unsigned int Gia_tri_moi;
void setup()
{

    TCCR1A = 0;
    TCCR1B = 0;
    // RESET lại 2 thanh ghi
    DDRB |= (1 << PB1);
    // Đầu ra PB1 là OUTPUT ( pin 9)

    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM12) | (1 << WGM13);
    // chọn Fast PWM, chế độ chọn TOP_value tự do  ICR1
    TCCR1A |= (1 << COM1A1);
    // So sánh thường( none-inverting)

    TCCR1B |= (1 << CS10);
    // P_clock=16mhz
    // mỗi P_clock bằng 1/16mhz= 0.0625 us
    // Hay 16  (P_clock ) bằng  1 us.
    // F_pwm=p_clock/50001=319.993hz
    OCR1A = 8480;
    // Value=8480 , tương đương xung có độ rộng 8484*0.0625us=530us (0.53ms)
    // Value=38550, tương đương xung có độ rộng 38550*0.0625us=2410us (2,41ms)
    ICR1 = 65535;
    // xung răng cưa tràn sau 65536 P_clock, tương đương (6,5 ms)
}

void set(unsigned int x)
{
    if (Gia_tri_moi != x) {
        OCR1A = x;
        Gia_tri_moi = OCR1A;
    }
    else {
        return; // thoát ngay
    }
    // x : 8480 - 38550
    //Độ rộng: 0.53ms - 2.41 ms
    // Góc lệch: 0 ->180 độ
}

void loop()
{
    set(8480); // 0 độ
    delay(2000);
    set(38550); // 180 độ
    delay(2000);
}

Nén thư viện và sử dụng.

1. Tải về thư viện

Chú ý:

  • Thư viện này chỉ sử dụng cho 2 pin là ~9 (PB1)~10 (PB2)
  • (Ở phiên bản này) Bạn sẽ không thể sử dụng thư viện <Servo.h> để điều khiển Servo ở các pin còn lại. (Tranh chấp timer1)

Link: <Tải lên lần 1 . 3:29PM 8/1/2017>

 https://drive.google.com/file/d/0BzMEcyRK_uUFc245U0lfZU9MODA/view?usp=sharing (mirror)

2. Giới thiệu thư viện

Thực chất việc cài thư viện  chỉ là Inlude các hàm .

-- void  port_attach(select_port);

Gọi hàm này 1 lần để cài đặt đầu ra cho chân PORT.

   “select_port” chỉ được phép mang 2 giá trị là “PB1” tương ứng pin ~9, và “PB2” tương ứng pin ~10.

Ví dụ: port_attach(PB1);// chọn pin ~9 làm đàu ra điều khiển Servo

-- void correct_write(select_port val);

 

 
 Tương ứng trong đoạn
val
8480 - > 38550
Góc lệch
0.000->180.000 độ
Độ rộng xung
0.53ms - 2.41 ms

Ví dụ

correct_write(PB1,8480);// pin~9, servo lệch góc 0.000 độ

Mỗi đơn vị của “val” tương ứng với góc lệch 0.006 độ. Đây cũng là độ chia nhỏ nhất mà Servo có thể nhích được nhờ thư viện này .

-- void write(select_portgoc);

Một kiểu khác để điều khiển thay cho hàm correct_write.

 
 Tương ứng trong đoạn
goc
0.000 - > 180.000
Góc lệch
0.000->180.000 độ
Độ rộng xung
0.53ms - 2.41 ms

“goc” có kiểu dữ liệu ở dạng số thực. Float.

Ví dụ:

write(PB1, 30.005);// pin~9, servo lệch góc 30,005 độ

3. Code ví dụ

Sử dụng 1 servo

write

Correct_write

#include <TIMER1_SERVO.h>

void setup()
{
    port_attach(PB1); // chọn pin ~9
}
void loop()
{
    write(PB1, 90.005); // pin ~9  // góc hiện tại 90.005 độ
    delay(5000); //5s
}
#include <TIMER1_SERVO.h>

void setup()
{
    port_attach(PB2); // chọn pin ~10
}
void loop()
{
    // val: 8480 đến 38550
    correct_write(PB2, 8480); // pin ~10 , // góc hiện tại 0.000 độ
    delay(5000); //5s
}

Sử dụng 2 servo

#include <TIMER1_SERVO.h>

void setup()
{
    port_attach(PB1); // chọn pin ~9
    port_attach(PB2); // chọn pin ~10
}
void loop()
{
    // góc : 0.000 đến 180.000
    write(PB1, 90.005); // pin ~9 trên arduino  UNO R3
    write(PB2, 30.005); // pin ~10 trên arduino  UNO R3
    delay(5000); //5s
}

Sử dụng 2 thư viện một cách an toàn. ? 

Do có sự tranh chấp TIMER 1, cách duy nhất để sử dụng cùng lúc 2 thư viện là ta cần phải đặt attach/detach mỗi lần sử dụng.

#include <Servo.h>
Servo myservo;
#include <TIMER1_SERVO.h>
void setup()
{
    pinMode(13, 1); //led
    port_attach(PB2); // chọn pin ~10
    myservo.attach(3); // pin 3 (Servo khác)
}
void loop()
{

    // điều khiển servo pin ~10, vô hiệu hóa thư viện " Servo.h"
    correct_write(PB2, 8800); // pin ~10 , servo quay ở góc 0.000 độ
    digitalWrite(13, 1);
    delay(5000);

    // điều khiển servo pin ~3, vô hiệu hóa thư viện " TIMER1_SERVO.h"
    myservo.attach(3); // đặt
    myservo.write(90);
    digitalWrite(13, 0);
    delay(5000);
    myservo.detach(); // gỡ
}

Tổng kết

  • Thư viện này chỉ sử dụng cho 2 pin là ~9 (PB1) và ~10 (PB2)
  • Bạn cần khéo léo khi muốn sử dụng cùng lúc 2 thư viện “Servo.h” và” TIMER1_SERVO.h”
  • (38550-4880=30070),  Tương đương 30070/180=167 xung / 1 độ, Tức 1 xung cho ta góc lệch nhỏ nhất là 0,006 độ (tương ứng độ rộng 0.0625us).
  • Servo có thể đáp ứng được góc lệch 0.006 độ ?
    • Như ta đã biết, mỗi lần Val tăng thêm 1 giá trị thì độ rộng xung điều khiển cũng tăng thêm  0.0625us, nếu như servo nhận ra sự khác biệt đó rồi đáp tuyến lại thì điều này là có thể. Tuy nhiên, đối với các loại servo mô hình như đang dùng, chúng không được thiết kế để phản ứng quay ở góc lệch nhỏ như vậy . Điều này cũng có nghĩa,  bạn phải tăng ít nhất 0.200 -> 0.500 độ thì mới thấy Servo nhích (nhìn vào trục của môtơ). Việc sử dụng thư viện trên là quá tiên tiến đối với một Servo mô hình.
  • Trong thư viện này: Xung điều khiển có chu kì 6,5 ms (ngắt TOP_val), ( chu kì tối thiểu là 5ms)
  •  Sau bài này, cả mình và bạn cần mở rộng thêm quy ước về độ rộng xung điều khiển SERVO:  lấy 1,5ms làm trung tâm, khi đó 0->180 tương ứng với xung 0.5ms->2,50ms hoặc  1ms -> 2ms (Tùy từng loại Servo).  Vấn đề này được nói rõ ở đây: http://www.opencircuits.com/Servo_control. Bạn có thể đạt được điều này bằng cách cho hàm correct_write(8000 ->40000); hoặc correct_write(16000->32000);

Code test trong video

#include <TIMER1_SERVO.h>
void setup()
{
    pinMode(13, 1);
    port_attach(PB1); //pin 9
}
void loop()
{
    for (float goc = 0.0; goc < 180; goc += 0.500) {
        write(PB1, goc);
        digitalWrite(13, 1);
        delay(500); //5s
        digitalWrite(13, 0);
        delay(500); //5s
    }
}

Và đây là chiến lợi phẩm ^^

Mình rất khuyến khích các bạn newbei chủ động tìm đọc những bài như thế này. Có gì chưa hiểu về phần Code thì bạn hãy xem lại bài hướng dẫn này nhé:

AVR của arduino còn làm được gì mạnh mẽ hơn nữa không?  -> Có đấy ! Đón đọc các bài viết về AVR nhé bạn ^^

Tác giả:  Thái Sơn.

lên
13 thành viên đã đánh giá bài viết này hữu ích.
Từ khóa: 
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

Xây dựng Rada trên board arduino và hiển thị trên LCD - Dự án RADUINO

Đã có nhiều bài viết về cách làm rada hiển thị lên màn hình máy tính. Tương tự, mình cũng sẽ làm một chiếc rada cảnh báo hiển thị lên một vài LCD thông dụng hiện nay.

lên
8 thành viên đã đánh giá bài viết này hữu ích.

AVR-OSCILLOSCOPE - Tự làm Máy hiện sóng điện tử trên nền ARDUINO với giá chưa đến 300 nghìn

Ở phiên bản này, máy hiện sóng AVR-OSCILLOSCOPE của mình có thể được hiển thị lên 2 lcd thông dụng là NOKIA5110 hoặc lcd ST7565 Homephone .

Đây là thiết bị hiện sóng đa năng, mạnh mẽ và vô cùng gọn nhẹ. Các bạn hoàn toàn tự làm nó ngay lại công xưởng chế tạo tại nhà của mình với giá thành cực rẻ (chưa đến 300k - tính luôn Arduino Uno). 

lên
9 thành viên đã đánh giá bài viết này hữu ích.