Điều khiển cánh tay robot bằng vi điều khiển PIC

Điều khiển cánh tay robot bằng vi điều khiển PIC

Từ dây chuyền lắp ráp các ngành công nghiệp sản xuất ô tô đến robot chế tạo vũ trụ trong vũ trụ, Robotic Arms sẽ được sử dụng ở khắp mọi nơi. Cơ chế của những robot này tương tự như con người có thể được lập trình cho chức năng tương tự và tăng khả năng liên tục. Chúng có thể được sử dụng để thực hiện các hành động lặp đi lặp lại nhanh hơn và chính xác hơn con người hoặc có thể được sử dụng trong môi trường khắc nghiệt mà không gây nguy hiểm đến tính mạng con người. Chúng tôi đã xây dựng một mô hình Robotic Arm bằng Arduino có thể được đào tạo để thực hiện một nhiệm vụ cụ thể và được thực hiện để lặp lại codei codei.

Trong hướng dẫn này, chúng tôi sẽ sử dụng Vi điều khiển 8 bit PIC16F877A tiêu chuẩn công nghiệp để điều khiển một cánh tay robot bằng biến trở. Thách thức với dự án này là PIC16F877A chỉ có hai chân có khả năng PWN, nhưng chúng ta cần điều khiển khoảng 5 động cơ servo cho robot, yêu cầu 5 chân PWM riêng lẻ. Vì vậy, chúng ta phải sử dụng các chân GPIO và tạo tín hiệu PWM trên các chân GPIO PIC bằng cách sử dụng các ngắt xung. Bây giờ, tất nhiên chúng ta có thể nâng cấp lên một vi điều khiển tốt hơn hoặc sử dụng IC đa kênh để tạo sản phẩm dễ dàng hơn.

Cấu trúc cơ học của cánh tay robot mà tôi đang sử dụng trong dự án này được in hoàn toàn bằng in 3D; bạn có thể tìm thấy các tập tin thiết kế hoàn chỉnh và quy trình lắp ráp trong web này.

Sơ đồ mạch

Sơ đồ mạch hoàn chỉnh cho Robotic Arm dựa trên Vi điều khiển PIC này được hiển thị bên dưới. Các sơ đồ đã được vẽ bằng EasyEDA.

Sơ đồ mạch khá đơn giản; dự án hoàn chỉnh được cung cấp bởi bộ chuyển đổi 12V . Bộ nguồn này sau đó được chuyển đổi thành + 5V bằng hai bộ điều chỉnh điện áp 7805. Một cái được dán nhãn là + 5V và cái còn lại được dán nhãn là + 5V (2). Lý do cần có hai bộ điều chỉnh là khi servo quay, nó sử dụng rất nhiều dòng điện tạo ra sụt áp. Sự sụt giảm điện áp này buộc PIC phải tự khởi động lại, do đó chúng ta không thể vận hành cả PIC và động cơ servo trên cùng một nguồn + 5V. Vì vậy, cái được dán nhãn là + 5V được sử dụng để cấp nguồn cho Vi điều khiển PIC, LCD và biến trở và một đầu ra bộ điều chỉnh riêng được gắn nhãn là + 5V (2) được sử dụng để cấp nguồn cho động cơ servo.

Năm chân đầu ra của biến trở cung cấp điện áp thay đổi từ 0V đến 5V được kết nối với các chân tương tự An0 đến AN4 của PIC. Vì chúng tôi đang có kế hoạch sử dụng để tạo ra PWM, động cơ servo có thể được kết nối với bất kỳ chân GPIO nào. Tôi đã chọn các chân từ RD2 đến RD6 cho các động cơ servo, nhưng nó có thể là bất kỳ GPIO nào bạn chọn.

Do chương trình liên quan đến việc gỡ lỗi rất nhiều, màn hình LCD 16×2 cũng được giao tiếp với portB của PIC. LED sẽ hiển thị chu kỳ làm việc của động cơ servo đang được điều khiển. Ngoài ra, tôi cũng đã mở rộng các kết nối cho tất cả các chân GPIO và analog, chỉ trong trường hợp nếu có bất kỳ cảm biến nào cần được giao tiếp trong tương lai. Cuối cùng tôi kết nối chân H1 để lập trình trực tiếp PIC với pickit3 bằng tùy chọn lập trình ICSP.

Tạo tín hiệu PWM trên chân GPIO cho Điều khiển động cơ Servo

Khi mạch đã sẵn sàng, chúng ta phải tìm ra cách tạo tín hiệu PWN trên chân GPIO của PIC để điều khiển động cơ servo. Chúng tôi đã sử dụng phương pháp ngắt Timer và đã thành công.

Tất cả các động cơ servo làm việc với tần số 50Hz. Có nghĩa là một chu kỳ xung hoàn chỉnh cho động cơ servo sẽ là 1/50 (F = 1 / T) là 20ms. Trong 20ms hoàn thành này, tín hiệu điều khiển chỉ từ 0 đến 2ms trong khi phần còn lại của tín hiệu luôn tắt. Hình dưới đây cho thấy thời gian BẬT chỉ thay đổi từ 0 đến 2ms để xoay động cơ từ 0 độ đến 180 độ trong tổng thời gian 20ms.

Với suy nghĩ này, chúng ta phải viết chương trình theo cách PIC đọc từ 0 đến 1204 từ biến trở và ánh xạ tới 0 đến 100, đây sẽ là chu kỳ làm việc của động cơ servo. Sử dụng chu kỳ này, chúng ta có thể tính toán thời gian BẬT của động cơ servo. Sau đó, chúng ta có thể khởi tạo ngắt timer theo một khoảng thời gian thông thường sao cho nó hoạt động tương tự như hàm millis () trong Arduino. Cùng với đó, chúng ta có thể chuyển đổi trạng thái chân GPIO ở mức cao trong khoảng thời gian mong muốn và tắt nó sau 20ms (một chu kỳ hoàn chỉnh) và sau đó lặp lại quy trình tương tự. Bây giờ, chúng ta đã nắm được logic, chúng ta hãy vào chương trình.

Lập trình PIC16F8771A cho Cánh tay robot

Bạn có thể tìm thấy chương trình hoàn chỉnh với Video ở cuối trang này, code cũng có thể được tải xuống từ đây với tất cả các tệp cần thiết . Trong phần này chúng ta sẽ thảo luận về logic đằng sau chương trình. Chương trình sử dụng mô-đun ADC , Mô-đun timer và Mô-đun LCD để điều khiển Cánh tay robot. Nếu bạn không biết cách sử dụng các tính năng ADC hoặc tính năng timer hoặc để giao tiếp với màn hình LCD với PIC, thì bạn có thể quay lại các liên kết tương ứng để tìm hiểu chúng. Giải thích dưới đây được đưa ra giả định rằng người đọc đã quen thuộc với các khái niệm này.

Cấu hình cổng Timer 0

Phần quan trọng nhất trong code là đặt Timer 0 thành biến cho mỗi delay cụ thể . Hàm để tính toán delay này là

Delay = ((256-REG_val)*(Prescal*4))/Fosc

Bằng cách sử dụng thanh ghi OPTION_REG và TMR0, chúng tôi đã đặt Timer 0 hoạt động với giá trị trước là 32 và giá trị REG được đặt thành 248. Tần số  (Fosc) được sử dụng trong phần cứng của chúng tôi là 20Mhz. Với các giá trị này, delay có thể được tính là

Delay = ((256-248)*(32*4)) / (20000000)

           = 0.0000512 seconds (or)

           = 0.05 msec

Vì vậy, bây giờ chúng tôi đã thiết lập xong bộ đếm xung ở mỗi 0,05ms . Code thực hiện tương tự được đưa ra dưới đây

/*****Port Configuration for Timer ******/

    OPTION_REG = 0b00000100; // Timer0 with external freq and 32 as prescalar // Also Enables PULL UPs

    TMR0=248;       // Load the time value for 0.0001s; delayValue can be between 0-256 only

    TMR0IE=1;       //Enable timer interrupt bit in PIE1 register

    GIE=1;          //Enable Global Interrupt

    PEIE=1;         //Enable the Peripheral Interrupt

    /***********______***********/

Trong tổng số cửa sổ điều khiển 0ms đến 2ms của động cơ servo, chúng ta có thể điều khiển nó với độ phân giải 0,05msec, cho phép chúng ta có (2 / 0,05) 40 vị trí khác nhau cho động cơ trong khoảng từ 0 độ đến 180 độ. Bạn có thể giảm giá trị này hơn nữa nếu MCU của bạn có thể hỗ trợ nó để có được nhiều vị trí hơn và điều khiển chính xác.

Interrupt Service Routine (ISR)

Bây giờ chúng ta có Timer 0 được đặt thành biến cho mỗi 0,05ms, chúng ta sẽ có ngắt TMR0IF được đặt thành 0,05ms. Vì vậy, bên trong hàm ISR chúng ta có thể thiết lập lại nó và tăng một biến gọi là biến đếm + 1. Vì vậy, bây giờ biến này sẽ tăng thêm 1 cho mỗi 0,05ms.

void interrupt timer_isr()

{

    if(TMR0IF==1) // Timer flag has been triggered due to timer overflow -> set to overflow for every 0.05ms

    {

        TMR0 = 248;     //Load the timer Value

        TMR0IF=0;       // Clear timer interrupt flag

        count++; //Count increments by 1 for every 0.05ms

    }

Tính chu kỳ và xung

Tiếp theo, chúng ta phải tính toán chu kỳ làm việc và xung cho cả năm động cơ servo. Chúng tôi có năm động cơ servo, mỗi động cơ được sử dụng để điều khiển từng phần của cánh tay. Vì vậy, chúng ta phải đọc giá trị ADC của cả năm và tính chu kỳ làm việc và xung cho từng loại.

Giá trị ADC sẽ nằm trong phạm vi từ 0 đến 1024, có thể được chuyển đổi thành chu kỳ 0% đến 100% bằng cách nhân 0,0976 (100/1024 = 0,0976) với giá trị thu được. Chu kỳ 0 đến 100% này sau đó phải được chuyển đổi thành thời gian BẬT. Chúng tôi biết rằng ở chu kỳ 100%, thời gian BẬT phải là 2ms (đối với 180 độ) nên nhân 0,02 (2/100 = 0,02) sẽ chuyển đổi 0 thành 100 chu kỳ thành 0 đến 2ms. Nhưng sau đó, số lượng biến thời gian của chúng tôi được thiết lập để tăng một lần cho mỗi 0,05ms. Điều này có nghĩa là giá trị của số đếm sẽ là 20 (1 / 0,05 = 20) cho mỗi 1ms. Vì vậy, chúng tôi phải nhân 20 với 0,02 để tính chính xác thời gian cho chương trình của chúng tôi, nó sẽ cho chúng tôi giá trị 0,4 (0,02 * 20 = 0,4). Code được hiển thị dưới đây, bạn có thể thấy nó được lặp lại 5 lần bằng cách sử dụng một vòng lặp for. Các giá trị kết quả được lưu trữ trong mảng T_ON.

for (int pot_num=0; pot_num<=3; pot_num++)

        {

        int Pev_val = T_ON[pot_num];

        POT_val = (ADC_Read(pot_num)); //Read the value of POT using ADC

        Duty_cycle = (POT_val * 0.0976); //Map 0 to 1024 to 0 to 100

        T_ON[pot_num] = Duty_cycle* 0.4;//20*0.02

Chọn động cơ để quay

Chúng ta không thể điều khiển tất cả năm động cơ cùng nhau vì nó sẽ làm cho code ISR kéo theo chậm toàn bộ vi điều khiển. Vì vậy, chúng tôi phải quay chỉ một động cơ servo tại một thời điểm. Để chọn servo nào để xoay vi điều khiển sẽ theo dõi thời gian BẬT của tất cả năm động cơ servo và so sánh nó với thời gian trước đó. Nếu có thay đổi về thời gian BẬT thì chúng ta có thể xuất rằng servo cụ thể phải được di chuyển. Code được hiển thị dưới đây.

if (T_ON[pot_num] != Pev_val)

        {

             Lcd_Clear();

            servo = pot_num;

            Lcd_Set_Cursor(2,11); Lcd_Print_String("S:");Lcd_Print_Char(servo+'0');

        if (pot_num==0)

        {Lcd_Set_Cursor(1,1); Lcd_Print_String("A:");}

        else if (pot_num==1)

        {Lcd_Set_Cursor(1,6); Lcd_Print_String("B:");}

        else if (pot_num==2)

        {Lcd_Set_Cursor(1,11); Lcd_Print_String("C:");}

        else if (pot_num==3)

        {Lcd_Set_Cursor(2,1); Lcd_Print_String("D:");}

        else if (pot_num==4)

        {Lcd_Set_Cursor(2,6); Lcd_Print_String("E:");}

        char d2 = (Duty_cycle) %10;

        char d1 = (Duty_cycle/10) %10;

        Lcd_Print_Char(d1+'0');Lcd_Print_Char(d2+'0');

Chúng tôi cũng in chu kỳ hoạt động của servo trên màn hình LCD để người dùng có thể biết vị trí hiện tại của nó. Dựa trên sự thay đổi thời gian BẬT, biến servo được cập nhật với các số từ 0 đến 4 đại diện cho các động cơ riêng lẻ.

Điều khiển động cơ Servo bên trong ISR

Bên trong ISR, chúng ta có số lượng biến tăng dần cho mỗi 0,05ms, điều này có nghĩa là cứ sau 1ms, biến sẽ được tăng thêm 20. Sử dụng điều này, chúng ta phải điều khiển các chân để tạo tín hiệu PWM. Nếu giá trị đếm nhỏ hơn thời gian thì GPIO của động cơ đó được bật bằng cách sử dụng dòng dưới đây

PORTD = PORTD | servo_code [servo];

Ở đây, mảng servo_code [] có chi tiết chân của tất cả năm động cơ servo và dựa trên giá trị trong biến servo, code cho động cơ servo cụ thể đó sẽ được sử dụng. Sau đó, nó là biến OR (|) với các bit PORTD hiện có để chúng ta không gây ảnh hưởng các giá trị của động cơ khác và chỉ cập nhật động cơ cụ thể này. Tương tự như vậy để tắt chân

PORTD = PORTD & ~ (servo_code [servo]);

Chúng tôi đã đảo ngược giá trị bit bằng cách sử dụng toán tử nghịch đảo logic (~) và sau đó đã thực hiện thao tác AND (&) trên PORTD để tắt chân mong muốn trong khi để các chân khác ở trạng thái trước đó. Đoạn code hoàn chỉnh được hiển thị bên dưới.

void interrupt timer_isr()

{

    if(TMR0IF==1) // Timer flag has been triggered due to timer overflow -> set to overflow for every 0.05ms

    {

        TMR0 = 248;     //Load the timer Value

        TMR0IF=0;       // Clear timer interrupt flag

        count++; //Count increments by 1 for every 0.05ms -> count will be 20 for every 1ms (0.05/1 = 20))

    }

    int servo_code[] = {0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100 };

    if (count >= 20*20)

    count=0;

     if (count <= (T_ON[servo]) )

         PORTD = PORTD | servo_code[servo];

     else

         PORTD = PORTD & ~(servo_code[servo]);

}

Chúng tôi biết rằng tổng số chu kỳ phải kéo dài trong 20ms trước khi chân GPIO được bật lại. Vì vậy, chúng tôi kiểm tra xem số đếm có vượt quá 20ms hay không bằng cách so sánh giá trị của số đếm với 400 (tính toán tương tự như đã thảo luận ở trên) và nếu có, chúng tôi phải khởi tạo lại số đếm bằng 0.

Mô phỏng code PIC Robot

Sẽ tốt hơn nếu mô phỏng code trước khi đưa nó vào phần cứng thực sự. Vì vậy, tôi đã sử dụng Proteus để mô phỏng code của mình và xác minh nó hoạt động chính xác chưa. Mạch được sử dụng cho mô phỏng được hiển thị bên dưới, Chúng tôi đã sử dụng oscilloscope để kiểm tra xem các tín hiệu PWM có được tạo ra theo yêu cầu hay không. Ngoài ra, chúng tôi có thể xác minh xem động cơ LCD và Servo có quay như mong đợi hay không.

Như bạn có thể thấy màn hình LCD hiển thị chu kỳ làm việc của động cơ D là 07 dựa trên giá trị pot là động cơ thứ 3 . Tương tự nếu một pot khác được di chuyển chu kỳ của pot đó và số động cơ của nó sẽ được hiển thị trên màn hình LCD. Tín hiệu PWM hiển thị trên máy hiện sóng được hiển thị bên dưới.

Tổng chu kỳ được đo là 22,2ms bằng cách sử dụng tùy chọn con trỏ trên oscilloscope, rất gần với 20ms mong muốn. Cuối cùng, chúng tôi đã chắc chắn rằng code hoạt động, vì vậy để tiếp tục với mạch, chúng tôi có thể hàn nó trên một board hoàn hảo hoặc sử dụng PCB. Nó sẽ không hoạt động dễ dàng trên Breadboard vì POT luôn có xu hướng đưa ra một số vấn đề do kết nối kém.

Thiết kế PCB sử dụng EasyEDA

Để thiết kế  PIC Robotic Arm này , chúng tôi đã chọn  công cụ EDA trực tuyến có tên EasyEDA . Tôi đã sử dụng nó trong một thời gian dài và thấy nó rất thuận tiện vì tính sẵn có rộng lớn của nó và dễ sử dụng. Sau khi thiết kế PCB, chúng tôi có thể đặt hàng các mẫu PCB bằng các dịch vụ chế tạo PCB chi phí thấp của họ  . Họ cũng cung cấp  dịch vụ tìm nguồn cung ứng linh kiện  nơi họ có một lượng lớn linh kiện điện tử và người dùng có thể đặt hàng các linh kiện cần thiết cùng với đơn đặt hàng PCB.

Trong khi thiết kế mạch và PCB của bạn, bạn cũng có thể công khai mạch và thiết kế PCB của mình để người dùng khác có thể sao chép hoặc chỉnh sửa chúng và có thể hưởng lợi từ công việc của bạn, chúng tôi cũng đã công khai toàn bộ bố cục Mạch và PCB cho mạch này liên kết dưới đây:

https://easyeda.com/circuitdigest/pic-development-board-for-robotic-arm

Sử dụng liên kết này, bạn có thể đặt hàng trực tiếp cùng loại PCB mà chúng tôi đang sử dụng trong dự án này và sử dụng nó. Sau khi thiết kế hoàn thành, board có thể được xem là mô hình 3D , điều này sẽ rất hữu ích trong việc hình dung board sẽ xuất hiện như thế nào sau khi chế tạo. Mô hình 3D của board mà chúng tôi đang sử dụng được hiển thị bên dưới. Ngoài ra, bạn cũng có thể xem lớp trên và dưới của board để kiểm tra xem có như mong đợi hay không.

Tính toán và đặt hàng mẫu trực tuyến

Sau khi hoàn thành thiết kế của  PIC Robot  PCB này, bạn có thể đặt hàng PCB thông qua  JLCPCB.com . Để đặt hàng PCB từ JLCPCB, bạn cần có tệp Gerber. Để tải xuống các tệp Gerber của PCB của bạn, chỉ cần nhấp vào nút  Generate Fabrication File  trên   trang soạn thảo EasyEDA , sau đó tải xuống tệp Gerber từ đó hoặc bạn có thể nhấp vào  Order at JLCPCB như trong hình bên dưới. Điều này sẽ chuyển hướng bạn đến JLCPCB.com, nơi bạn có thể chọn số lượng PCB bạn muốn đặt hàng, số lượng lớp đồng bạn cần, độ dày PCB, trọng lượng đồng và thậm chí màu PCB, như ảnh chụp nhanh được hiển thị bên dưới:

Sau khi bạn đã chọn tất cả các tùy chọn, hãy nhấp vào “Save to Cart” và sau đó bạn sẽ được đưa đến trang nơi bạn có thể tải lên Tệp Gerber mà chúng tôi đã tải xuống từ EasyEDA. Tải lên tệp Gerber của bạn và nhấp vào “Save to Cart”. Và cuối cùng nhấp vào Checkout An toàn để hoàn tất đơn đặt hàng của bạn, sau đó bạn sẽ nhận được PCB của mình một vài ngày sau đó. Họ tạo PCB với chi phí rất thấp là 2 đô la. Thời gian thực hiện của họ cũng rất ít, đó là 48 giờ với DHL giao hàng 3-5 ngày, về cơ bản, bạn sẽ nhận được PCB trong vòng một tuần kể từ khi đặt hàng.

Sau khi đặt hàng PCB, bạn có thể  kiểm tra  Tiến độ sản xuất  PCB của mình  theo ngày và giờ. Bạn kiểm tra nó bằng cách vào trang Tài khoản và nhấp vào “Production Progress”.

Sau vài ngày đặt hàng PCB, tôi đã nhận được các mẫu PCB trong bao bì đẹp như trong hình dưới đây.

Và sau khi có được những mảnh này, tôi đã hàn tất cả các linh kiện cần thiết qua PCB. Tôi cũng hàn POT trực tiếp thay vì sử dụng dây nối mà ban đầu tôi sử dụng nó cho ra điện áp đầu ra không chính xác có thể là do tiếp xúc bị lỏng. Khi tất cả các linh kiện được lắp ráp, PCB của tôi trông giống như thế này.

Bạn có thể nhận thấy rằng chỉ có một 7805 trên board này. Đó là bởi vì ban đầu tôi nghĩ rằng tôi có thể dùng bộ điều chỉnh để cấp nguồn cho cả động cơ PIC và servo và sau đó tôi nhận ra mình cần hai cái. Vì vậy, tôi đã sử dụng một mạch bên ngoài để cấp nguồn cho động cơ servo thông qua các dây màu xanh lá cây mà bạn thấy ở đây.

Tuy nhiên, bạn không phải lo lắng nhiều về nó bởi vì; Tôi đã thực hiện các thay đổi cho PCB. Bạn có thể sử dụng PCB đã được sửa đổi và hàn cả hai bộ điều chỉnh với nhau.

Cách hoạt động của PIC Robot Arm

Hàn tất cả các linh kiện trên board và tải chương trình lên bộ điều khiển PIC. Code hoàn chỉnh được đưa ra dưới đây hoặc có thể được tải xuống từ đây . Trình kết nối lập trình được cung cấp trên board sẽ giúp bạn tải lên chương trình trực tiếp bằng Pickit 3 mà không gặp nhiều rắc rối. Khi chương trình được tải lên, bạn sẽ thấy màn hình LCD hiển thị servo hiện đang được điều khiển.

Bây giờ, bạn có thể chỉ cần xoay biến trở và kiểm tra xem động cơ servo phản ứng với từng biến trở như thế nào. Khi bạn hiểu nguyên lý hoạt động của nó, bạn có thể điều khiển cánh tay robot để thực hiện bất kỳ hành động nào bạn cần để thực hiện và vui chơi.

CODE:

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#define _XTAL_FREQ 20000000 //Crystak Freq is 20MHz
#define PWM_Frequency 0.05 // in KHz (50Hz)

//Define LCD pins 
#define RS RB1
#define EN RB2
#define D4 RB3
#define D5 RB4
#define D6 RB5
#define D7 RB6

#include <xc.h>
#include "lcd.h" //Header for using LCD module

int POT_val; //variable to store value from ADC
int count; //timer variable 
//int T_TOTAL = (1/PWM_Frequency); //calculate Total Time from frequency (in milli sec)) //20msec
int Duty_cycle; //Duty cycle value
int T_ON[4];

char servo;

void ADC_Initialize() //Prepare the ADC module 
{
  ADCON0 = 0b01000001; //ADC ON and Fosc/16 is selected
  ADCON1 = 0b11000000; // Internal reference voltage is selected
}

unsigned int ADC_Read(unsigned char channel) //Read from ADC
{
  ADCON0 &= 0x11000101; //Clearing the Channel Selection Bits
  ADCON0 |= channel<<3; //Setting the required Bits
  __delay_ms(2); //Acquisition time to charge hold capacitor
  GO_nDONE = 1; //Initializes A/D Conversion
  while(GO_nDONE); //Wait for A/D Conversion to complete
  return ((ADRESH<<8)+ADRESL); //Returns Result
}

void interrupt timer_isr()
{  
    if(TMR0IF==1) // Timer flag has been triggered due to timer overflow -> set to overflow for every 0.05ms
    {
        TMR0 = 248;     //Load the timer Value
        TMR0IF=0;       // Clear timer interrupt flag
        count++; //Count increments by 1 for every 0.05ms 
    } 
    
    int servo_code[] = {0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100 };
    
    if (count >= 20*20)
    count=0;
    
     if (count <= (T_ON[servo]) )
         PORTD = PORTD | servo_code[servo];
     else 
         PORTD = PORTD & ~(servo_code[servo]);
}

void main()
{
    /*****Port Configuration for Timer ******/
    OPTION_REG = 0b00000100;  // Timer0 with external freq and 32 as prescalar // Also Enables PULL UPs
    TMR0=248;       // Load the time value for 0.0001s; delayValue can be between 0-256 only
    TMR0IE=1;       //Enable timer interrupt bit in PIE1 register
    GIE=1;          //Enable Global Interrupt
    PEIE=1;         //Enable the Peripheral Interrupt
    /***********______***********/   
 
    /*****Port Configuration for I/O ******/
    TRISB = 0x00; //PORT B is output since LCd is connected
    PORTB=0x00; //Initialize all pins to 0
    TRISD = 0x00; //PORT D is output since servo is connected 
    PORTD=0x00; //Initialize all pins to 0
    /***********______***********/   
   
    
    Lcd_Start(); //Initialize LCD module
    ADC_Initialize(); //Initialize ADC module 
    
   // Lcd_Clear();
   // Lcd_Set_Cursor(1,1);
    //Lcd_Print_String("Circuit Digest");
    //Lcd_Set_Cursor(2,1);
    //Lcd_Print_String("WORKING!!");
    //__delay_ms(500);
  
    while(1)
    {
        for (int pot_num=0; pot_num<=3; pot_num++)
        {
        int Pev_val = T_ON[pot_num];
        
        POT_val = (ADC_Read(pot_num)); //Read the value of POT using ADC
        Duty_cycle = (POT_val * 0.0976); //Map 0 to 1024 to 0 to 100
        T_ON[pot_num] = Duty_cycle* 0.4;//((Duty_cycle * T_TOTAL) / 1000)*10; //Calculate On Time from 0ms to 2ms for 0-100 Duty Cycle //*10 is multiplication factor
        
        if (T_ON[pot_num] != Pev_val)
        {
             Lcd_Clear();
            servo = pot_num;
            Lcd_Set_Cursor(2,11); Lcd_Print_String("S:");Lcd_Print_Char(servo+'0');
            
        if (pot_num==0)
        {Lcd_Set_Cursor(1,1); Lcd_Print_String("A:");}
        else if (pot_num==1)
        {Lcd_Set_Cursor(1,6); Lcd_Print_String("B:");}
        else if (pot_num==2)
        {Lcd_Set_Cursor(1,11); Lcd_Print_String("C:");}
        else if (pot_num==3)
        {Lcd_Set_Cursor(2,1); Lcd_Print_String("D:");}
        else if (pot_num==4)
        {Lcd_Set_Cursor(2,6); Lcd_Print_String("E:");}
        
        
        char d2 = (Duty_cycle) %10;
        char d1 = (Duty_cycle/10) %10;
        Lcd_Print_Char(d1+'0');Lcd_Print_Char(d2+'0');
        
        }
        
       }
          
    }
}

VIDEO:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Trả lời

Email của bạn sẽ không được hiển thị công khai.