Gần như tất cả các hệ thống dựa trên vi điều khiển đều thực hiện nhiều hơn một tác vụ. Ví dụ hệ thống giám sát nhiệt độ tạo ra ba tác vụ lặp lại trong một thời gian ngắn đó là:
Tác vụ 1: Đọc nhiệt độ
Tác vụ 2: Định dạng nhiệt độ
Tác vụ 3: Hiển thị nhiệt độ
Hệ thống phức tạp với độ phức tạp cao thì sẽ có nhiều tác vụ phức tạp. Trong một hệ thống đa nhiệm,nhiều tác vụ đòi hỏi thời gian CPU, và do chỉ có một CPU, nên ta cần thiết có một số cách thức phối hợp và tổ chức để mỗi tác vụ có đủ thời gian CPU mà nó cần. Trong thực tế, mỗi tác vụ chiếm số lượng thời gian là rất ngắn, do đó dường như có vẻ như là tất cả các tác vụ được thực hiện song song và đồng thời.
Hầu như tất cả các hệ thống dựa trên vi điều khiển hoạt động trong thời gian thực. Một hệ thốngthời gian thực là một hệ thống đáp ứng thời gian nghĩa là hệ thống đó có thể đáp ứng với môi trường hoạt động của nó trong thời gian ngắn nhất có thể.
Thời gian thực không nhất thiết có nghĩa là các vi điều khiển sẽ hoạt động ở tốc độ cao. Thời gian đáp ứng nhanh chóng những gì là quan trọng trong một hệ thống thời gian thực, mặc dù tốc độ cao điều này có thể giúp ích được nhiều. Ví dụ, một hệ thống thời gian thực dựa trên vi điều khiển kết nối với các công tắc điều khiển ở bên ngoài được kỳ vọng sẽ đáp ứng ngay lập tức khi một công tắc được kích hoạt hoặc khi một số sự kiện khác xảy ra.
Thời gian thực không nhất thiết có nghĩa là các vi điều khiển sẽ hoạt động ở tốc độ cao. Thời gian đáp ứng nhanh chóng những gì là quan trọng trong một hệ thống thời gian thực, mặc dù tốc độ cao điều này có thể giúp ích được nhiều. Ví dụ, một hệ thống thời gian thực dựa trên vi điều khiển kết nối với các công tắc điều khiển ở bên ngoài được kỳ vọng sẽ đáp ứng ngay lập tức khi một công tắc được kích hoạt hoặc khi một số sự kiện khác xảy ra.
Một hệ điều hành thời gian thực (RTOS) là một đoạn mã (thường được gọi là các hạt nhân hay kernel) điều khiển phân công tác vụ khi vi điều khiển được hoạt động trong một môi trường đa tác vụ. Quyết định RTOS , ví dụ, trong đó xác định nhiệm vụ để chạy tiếp theo, làm thế nào để phối hợp ưu tiêncông việc, và làm thế nào để truyền dữ liệu và các thông điệp giữa các tác vụ.
Chương này tìm hiểu các nguyên tắc cơ bản của hệ thống nhúng đa nhiệm và đưa ra ví dụ của mộtRTOS được sử dụng trong các dự án đơn giản. Mã đa nhiệm và RTOS là những chủ đề phức tạp và rộng,và chương này mô tả các khái niệm ngắn gọn liên quan đến những công cụ này . Bạn đọc quan tâm có thể tham khảo nhiều sách và giấy tờ có sẵn về hệ điều hành, hệ thống đa tác vụ, và RTOS.
Có một số hệ thống RTOS thương mại có sẵn cho vi điều khiển PIC. Tại thời điểm viết bài, ngôn ngữmikroC không hỗ trợ cung cấp các hàm xây dựng sẳn cho RTOS. Hai hệ thống phổ biến cấp cao RTOS cho vi điều khiển PIC là Salvo (www.pumpkin.com) có thể được sử dụng với trình biên dịch Hi-Tech PIC C, vàtrình biên dịch CCS (Customer Computer Service) với các hàm hỗ trợ RTOS được xây dựng sẳn để sử dụng. Trong chương này, các ví dụ RTOS được viết dựa trên trình biên dịch CCS (www.ccsinfo.com) , một trong những trình biên dịch phổ biến PIC C phát triển cho loạt các vi điều khiển PIC16 và PIC18 .
Có một số hệ thống RTOS thương mại có sẵn cho vi điều khiển PIC. Tại thời điểm viết bài, ngôn ngữmikroC không hỗ trợ cung cấp các hàm xây dựng sẳn cho RTOS. Hai hệ thống phổ biến cấp cao RTOS cho vi điều khiển PIC là Salvo (www.pumpkin.com) có thể được sử dụng với trình biên dịch Hi-Tech PIC C, vàtrình biên dịch CCS (Customer Computer Service) với các hàm hỗ trợ RTOS được xây dựng sẳn để sử dụng. Trong chương này, các ví dụ RTOS được viết dựa trên trình biên dịch CCS (www.ccsinfo.com) , một trong những trình biên dịch phổ biến PIC C phát triển cho loạt các vi điều khiển PIC16 và PIC18 .
10.1 GIAI ĐOẠN MÁY (State Machines)
Giai đoạn máy là các cấu trúc đơn giản được sử dụng để thực hiện một số hoạt động, thường là theo một trình tự. Nhiều hệ thống đời sống thực rơi vào thể loại này. Ví dụ, các hoạt động của một máy giặt hay máy rửa bát là dễ dàng mô tả với một cấu trúc giai đoạn máy.
Có lẽ phương pháp đơn giản nhất để thực hiện một cấu trúc giai đoạn máy trong C là sử dụng một câu lệnh switch-case. Ví dụ, hệ thống giám sát nhiệt độ của chúng ta có ba tác vụ, tên Task 1, Task 2 và Task3 như trong hình 10.1. Giai đoạn máy thực hiện 3 tác vụ sử dụng câu lệnh switch-case được thể hiện trong hình 10.2 . Giai đoạn bắt đầu là 1, và mỗi nhiệm vụ gia tăng số giai đoạn lên một để chọn giai đoạn tiếptheo sẽ được thực thi. Giai đoạn cuối cùng chọn giai đoạn 1, và có một sự delay ở cuối câu lệnh switch-case. Cấu trúc giai đoạn máy được thực hiện liên tục bên trong một vòng lặp for vô tận.
Giai đoạn máy là các cấu trúc đơn giản được sử dụng để thực hiện một số hoạt động, thường là theo một trình tự. Nhiều hệ thống đời sống thực rơi vào thể loại này. Ví dụ, các hoạt động của một máy giặt hay máy rửa bát là dễ dàng mô tả với một cấu trúc giai đoạn máy.
Có lẽ phương pháp đơn giản nhất để thực hiện một cấu trúc giai đoạn máy trong C là sử dụng một câu lệnh switch-case. Ví dụ, hệ thống giám sát nhiệt độ của chúng ta có ba tác vụ, tên Task 1, Task 2 và Task3 như trong hình 10.1. Giai đoạn máy thực hiện 3 tác vụ sử dụng câu lệnh switch-case được thể hiện trong hình 10.2 . Giai đoạn bắt đầu là 1, và mỗi nhiệm vụ gia tăng số giai đoạn lên một để chọn giai đoạn tiếptheo sẽ được thực thi. Giai đoạn cuối cùng chọn giai đoạn 1, và có một sự delay ở cuối câu lệnh switch-case. Cấu trúc giai đoạn máy được thực hiện liên tục bên trong một vòng lặp for vô tận.
for(;;)
{
state = 1;
switch (state)
{
{
state = 1;
switch (state)
{
CASE 1:
implement TASK 1
state++;
break;
state++;
break;
CASE 2:
implement TASK 2
state++;
break;
state++;
break;
CASE 3:
implement TASK 3
state = 1;
break;
state = 1;
break;
}
Delay_ms(n);
}
Delay_ms(n);
}
Hình 10.2: Giai đoạn máy thực thi trong C
Trong nhiều ứng dụng, các giai đoạn không thực hiện theo trình tự, thay vào đó giai đoạn kế tiếp được lựa chọn bởi giai đoạn hiện tại bằng cách chọn trực tiếp hoặc bởi một vài điều kiện.
for(;;)
{
{
state = 1;
switch (state)
{
switch (state)
{
CASE 1:
implement TASK 1
state = 2;
break;
state = 2;
break;
CASE 2:
implement TASK 2
state = 3;
break;
state = 3;
break;
CASE 3:
implement TASK 3
state = 1;
break;
state = 1;
break;
}
Delay_ms(n);
Delay_ms(n);
}
Hình 10.3: lựa chọn trạng thái tiếp theo từ trạng thái hiện tại
Giai đoạn máy , mặt dù dễ thực hiện , là cơ bản nhưng có ứng dụng hạn chế. Nó có thể chỉ sử dụng trong hệ thống không đáp ứng thực sự nơi mà các nhiệm vụ hoạt động được xác định rõ ràng và cácnhiệm vụ không được ưu tiên.
Hơn nữa, một số tác vụ có thể quan trọng hơn những tác vụ khác. Chúng ta có thể muốn có một số tác vụ phải chạy bất cứ khi nào có đủ điều kiện. Ví dụ, trong một nhà máy sản xuất , một tác vụ thiết lập 1 cảnh báo phải chạy khi nhiệt độ quá nóng. Khi đó ta đòi hỏi phải sử dụng một hệ thống phức tạp nhưRTOS.
10.2 HỆ ĐIỀU HÀNH THỜI GIAN THỰC (RTOS)
Hệ điều hành thời gian thực được xây dựng xung quanh một hạt nhân đa nhiệm (multi-tasking kernel)nó có nhiệm vụ điều khiển phân bổ lát thời gian cho tác vụ. Một lát thời gian là khoảng thời gian mộtnhiệm vụ nhất định có để thực hiện trước khi nó dừng lại và thay thế bằng một tác vụ khác. Quá trình này,cũng được gọi là chuyển đổi bối cảnh, lặp đi lặp lại liên tục. Khi chuyển đổi bối cảnh xảy ra, tác vụ thực thi bị dừng lại, thanh ghi xử lý được lưu trong bộ nhớ, thanh ghi xữ lý của các công việc có sẵn tiếp theo được nạp vào CPU, và các nhiệm vụ mới bắt đầu thực hiện. Một RTOS cũng cung cấp các thông điệp qua lại giữa các tác vụ, đồng bộ hóa tác vụ, và phân bổ các nguồn tài nguyên được chia sẻ với các tác vụ.
Các phần của một RTOS là:
- Lập lịch trình
- Trình phục vụ RTOS
- Đồng bộ và công cụ thông điệp
10.2.1 Lập lịch trình
Lập lịch trình là trái tim của mọi RTOS, vì nó cung cấp các thuật toán để chọn nhiệm vụ để thực hiện. Ba trong số các thuật toán lập lịch trình thường gặp là:
Lập lịch trình hợp tác
Lập Lịch trình Round-robin
Lập lịch trình ưu tiên
Lập lịch trình là trái tim của mọi RTOS, vì nó cung cấp các thuật toán để chọn nhiệm vụ để thực hiện. Ba trong số các thuật toán lập lịch trình thường gặp là:
Lập lịch trình hợp tác
Lập Lịch trình Round-robin
Lập lịch trình ưu tiên
Lập lịch trình hợp tác: có lẽ là thuật toán lập lịch trình đơn giản nhất có sẵn. Mỗi nhiệm vụ chạy cho đến khi nó được hoàn thành và tự nguyện giải phóng CPU cho nhiệm vụ khác chạy. Lập lịch trình hợp táckhông thể đáp ứng thời gian thực hệ thống cần, vì nó không thể hỗ trợ các ưu tiên của nhiệm vụ theo tầm quan trọng. Ngoài ra, một tác vụ đơn có thể sử dụng CPU quá lâu, để lại quá ít thời gian cho các tác vụ khác. Và lịch trình không kiểm soát thời gian thực thi các tác vụ khác nhau . Một giai đoạn máy là một hình thức đơn giản của một kỹ thuật lập lịch trình hợp tác.
Lập Lịch round-robin: mỗi tác vụ được gán thời gian CPU bằng nhau (xem Hình 10.4). Một bộ đếm sẽ theo dõi các lát thời gian cho mỗi công việc. Khi một lát thời gian của một nhiệm vụ được hoàn thành, bộ đếm được xóa và tác vụ được đặt ở cuối chu kỳ. Nhiệm vụ mới được thêm vào được đặt ở cuối chu kỳvới bộ đếm của nó xóa về 0. Điều này, như lập lịch trình hợp tác, Nó không thật sự hữu ích trong một hệ thống thời gian thực, vì một số nhiệm vụ có thể chỉ tốn một vài miligiây trong khi những tác vụ khác yêu cầu hàng trăm mili giây hoặc nhiều hơn.
Lập Lịch round-robin: mỗi tác vụ được gán thời gian CPU bằng nhau (xem Hình 10.4). Một bộ đếm sẽ theo dõi các lát thời gian cho mỗi công việc. Khi một lát thời gian của một nhiệm vụ được hoàn thành, bộ đếm được xóa và tác vụ được đặt ở cuối chu kỳ. Nhiệm vụ mới được thêm vào được đặt ở cuối chu kỳvới bộ đếm của nó xóa về 0. Điều này, như lập lịch trình hợp tác, Nó không thật sự hữu ích trong một hệ thống thời gian thực, vì một số nhiệm vụ có thể chỉ tốn một vài miligiây trong khi những tác vụ khác yêu cầu hàng trăm mili giây hoặc nhiều hơn.
Hình 10.4 Lập lịch trình round-robin
Lập lịch trình ưu tiên: được coi là một thuật toán lập lịch trình thời gian thực. Nó dựa vào mức độ ưu tiên, và mỗi công việc được gán một mức ưu tiên (xem hình 10.5). Các nhiệm vụ với mức ưu tiên cao nhất có được thời gian CPU. Hệ thống thời gian thực thường hỗ trợ các mức ưu tiên trong dãi từ 0-255, trong đó 0 là ưu tiên cao nhất và 255 là thấp nhất.
Hình 10.4 Lập lịch trình ưu tiên
Trong một số hệ thống thời gian thực, nơi có nhiều tác vụ có cùng một mức ưu tiên , lập lịch trình ưu tiên kết hợp với lập lịch trình round-robin. Trong trường hợp như vậy, nhiệm vụ ở mức ưu tiên cao hơn chạy trước những ưu tiên thấp hơn, và các nhiệm vụ ở cùng cấp độ ưu tiên chạy bởi lập lịch trình round-robin.Nếu một nhiệm vụ được ưu tiên bởi một nhiệm vụ ưu tiên cao hơn, bộ đếm thời gian của nó được lưu lại và sau đó khôi phục khi nó lấy lại quyền kiểm soát của CPU.
Trong một số hệ thống một lớp ưu tiên thời gian thực nghiêm ngặt được xác định nơi các nhiệm vụ trênlớp này có thể chạy để hoàn thành (hoặc chạy cho đến khi tài nguyên không có sẵn) ngay cả khi có những nhiệm vụ khác ở mức độ ưu tiên như nhau.
Trong một số hệ thống một lớp ưu tiên thời gian thực nghiêm ngặt được xác định nơi các nhiệm vụ trênlớp này có thể chạy để hoàn thành (hoặc chạy cho đến khi tài nguyên không có sẵn) ngay cả khi có những nhiệm vụ khác ở mức độ ưu tiên như nhau.
Trong một hệ thống thời gian thực một tác vụ có thể rơi vào một trong các trạng thái sau đây (xem hình 10.6):
- Sẵn sàng để chạy.
- Chạy.- Bị chặn.
- Chạy.- Bị chặn.
Khi một tác vụ lúc đầu được tạo ra, nó thường ở trạng thái sẵn sàng để chạy và được nhập vào trongdanh sách tác vụ. Từ trạng thái này, tùy thuộc vào các thuật toán lập lịch trình, nhiệm vụ có thể trở thànhmột nhiệm vụ đang chạy. Theo các điều kiện của lịch ưu tiên, nhiệm vụ sẽ chạy nếu nó là nhiệm vụ ưu tiên cao nhất trong hệ thống và không phải đợi một nguồn tài nguyên. Một nhiệm vụ chạy trở thành một nhiệm vụ chặn nếu nó cần một nguồn tài nguyên chưa có sẵn. Ví dụ, một tác vụ có thể cần dữ liệu từ một bộ chuyển đổi A/D (bộ chuyển đổi từ tương tự sang số) và bị chặn cho đến khi bộ chuyển đổi A/D hoàn tất. Một khi các nguồn tài nguyên có thể được truy suất, các nhiệm vụ bị chặn trở thành một nhiệm vụchạy nếu nó là nhiệm vụ ưu tiên cao nhất trong hệ thống, nếu không nó chuyển sang trạng thái sẵn sàng.Chỉ một nhiệm vụ đang chạy có thể bị chặn. Một nhiệm vụ sẵn sàng chạy không thể bị chặn.
Khi một nhiệm vụ chuyển từ trạng thái này sang trạng thái khác, bộ vi xử lý lưu bối cảnh của nhiệm vụ đang chạy vào trong bộ nhớ, nạp bối cảnh nhiệm vụ mới từ bộ nhớ, và sau đó thực thi lệnh mới như yêu cầu.
Khi một nhiệm vụ chuyển từ trạng thái này sang trạng thái khác, bộ vi xử lý lưu bối cảnh của nhiệm vụ đang chạy vào trong bộ nhớ, nạp bối cảnh nhiệm vụ mới từ bộ nhớ, và sau đó thực thi lệnh mới như yêu cầu.
Hình 10.6 Trạng thái nhiệm vụ
Các hạt nhân kernel thường cung cấp một giao diện để thao tác các hoạt động công việc. Nhiệm vụ điển hình hoạt động là:
- Tạo một tác vụ- Xóa một tác vụ- Thay đổi các ưu tiên của một tác vụ- Thay đổi trạng thái của một tác vụ
10.3 Trình phục vụ RTOS
Trình phục vụ RTOS là tiện ích được cung cấp bởi các kernel có thể giúp các nhà phát triển tạo ra các tác vụ thời gian thực hiệu quả. Ví dụ, một công việc có thể sử dụng các trình phục vụ thời gian để có được ngày và thời gian hiện tại. Một số các trình phục vụ này là:
- Trình phục vụ ngắt
- Trình phục vụ thời gian- Trình phục vụ quản lý thiết bị- Trình phục vụ quản lý bộ nhớ- Trình phục vụ đầu vào-đầu ra
- Trình phục vụ thời gian- Trình phục vụ quản lý thiết bị- Trình phục vụ quản lý bộ nhớ- Trình phục vụ đầu vào-đầu ra
10.4 Đồng bộ hóa và Công cụ thông điệp
Đồng bộ hóa và công cụ thông điệp là các cấu trúc hạt nhân giúp các nhà phát triển tạo ra các ứng dụng thời gian thực. Một số các dịch vụ này là:
- Semaphores : cờ hiệu
- Event flags: Cờ sự kiện
- Event flags: Cờ sự kiện
- Mailboxes : hộp thư
- Pipes: Đường ống
- Message queues : hàng đợi thông điệp
Semaphores được sử dụng để đồng bộ truy cập vào tài nguyên chia sẻ, chẳng hạn như khu vực dữ liệu chung. Cờ sự kiện được sử dụng để đồng bộ hóa các hoạt động tác vụ nội bộ. Hộp thư, đường ống, vàhàng đợi thông điệp được sử dụng để gửi tin nhắn giữa các tác vụ.
10.5 Trình biên dịch PIC C CCS hỗ trợ RTOS
rtos_run () khởi tạo các hoạt động của RTOS. Tất cả các hoạt động kiểm soát tác vụ được thực thi sau khi gọi hàm này
rtos_terminate () Chức năng này kết thúc việc thực thi tất cả các tác vụ RTOS. Quyền kiểm soát được trả về chương trình gốc không có RTOS.
rtos_enable () nhận tên của một tác vụ như là một đối số. Hàm này cho phép một tác vụ thực thi tại tốc độ chỉ định. Tất cả tác vụ được cho phép theo mặc định.
rtos_disable() nhận tên của tác vụ như là một đối số. Hàm này vô hiệu hóa tác vụ để nó không còn được gọi bởi rtos_run() trừ khi nó được cho phép lại bằng hàm rtos_enable ()
rtos_ yield() khi gọi từ bên trong một tác vụ, trả lại điều khiển cho phần tiếp nhận. Tất cả các tác vụ nên gọi hàm này để giải phóng vi xữ lý để các tác vụ khác có thể sử dụng thời gian đê xữ lý tiếp
rtos_msg_send() nhận tên tác vụ và 1 byte làm đối số. Hàm này gửi byte đến tác vụ chỉ định, nó được đặt trong hàng đợi thông điệp của tác vụ.
rtos_msg_read() đọc byte đặt trong hàng đợi thông điệp của tác vụ
rtos_msg_poll () trả về true nếu có dữ liệu trong hàng đợi thông điệp của tác vụ. Hàm này nên gọi trước khi đọc 1 byte từ hàm đợi thông điệp của tác vụ
rtos_signal () nhận một tên cờ hiệu (semaphore) và tăng cờ hiệu.
rtos_wait() nhận một tên semaphore và chờ tài nguyên có liên quan đến cờ hiệu có giá trị. Giá trị cờ hiệu giảm để cho tác vụ có thể chờ tài nguyên
rtos_await() Chức năng này chỉ có thể được sử dụng trong một tác vụ RTOS. Chức năng này chờ biểu thức là đúng trước khi tiếp tục thực hiện phần mã còn lại của của tác vụ RTOS. Chức năng này cho phépcác tác vụ khác thực hiện nhiệm vụ trong khi chờ đợi cho biểu thức là đúng.
rtos_overrun () nhận một tên nhiệm vụ như một tham số, và hàm trả về true nếu công việc đó đã bị quáthời gian được phân bổ.
rtos_stats () trả về số liệu thống kê cụ thể về một nhiệm vụ cụ thể. Các số liệu thống kê thời gian chạynhiệm vụ tối thiểu và tối đa và tổng thời gian tác vụ chạy. Tên tác vụ và các loại thống kê được quy định như đối số cho hàm.
10.5.1 Chuẩn bị cho RTOS
Ngoài các chức năng trên, các lệnh tiền xử lý rtos #use () phải được quy định tại phần đầu của chương trình trước khi gọi bất kỳ chức năng RTOS. Các định dạng của lệnh tiền xử lý này là: rtos #use (timer = n,minor_cycle=m)
nơi timer được gán từ 0 đến 4 và xác định bộ định thời được sử dụng bởi RTOS và minor_cycle là thời gian dài nhất bất kỳ tác vụ sẽ chạy. Số đã nhập ở đây phải được theo sau bởi s, ms, us, hoặc ns.
Ngoài ra, một tùy chọn statistics (tùy chọn thống kê), khai báo sau tùy chọn minor_cycle, khi đó chương trình biên dịch sẽ theo dõi thời gian tối đa và tối thiểu tác vụ sử dụng tại mỗi lần được gọi và tổng thời gian tác vụ sử dụng
10.5.2 Khai báo tác vụ
#task(rate=n, max=m, queue=p)
Nơi Rate chỉ định độ thường xuyên mà tác vụ được gọi, đi kèm theo sau là đơn vị s, ms, us, hoặc ns. max chỉ định một tác vụ sẻ có bao nhiêu thời gian xữ lý sẻ sữ dụng trong một thực thi của tác vụ . Thời gian chỉ định ở đây phải bằng hoặc nhỏ hơn thời gian chỉ định trong minor_cycle. queue là một tùy chọn, nó quy định số bye để dành cho tác vụ để nhận thông điệp từ tác vụ khác. Giá trị mặt định là 0
Ví dụ minh họa dưới đây, một tác vụ gọi my_ticks sau mỗi 20 ms, và quy định không sử dụng quá 100 ms thời gian xữ lý , và không sử dụng tùy chọn queue
#task(rate=20ms, max=100ms)
void my_ticks()
{
...........
...........
}
void my_ticks()
{
...........
...........
}
Ví dụ 10.1 - LEDs
Ví dụ đơn giản dựa trên RTOS, bốn LED đơn kết nối 4 chân thấp PORTB của vi điều khiển PIC18F452. Phần mềm bao gồm 4 tác vụ, mỗi tác vụ có nhiệm vụ điều khiển nhấp nháy một đèn led với tốc độ khác nhau
Tác vụ 1 , gọi task_B0, nhấp nháy led kết nối đến port RB0 tại tốc độ 250ms
Tác vụ 2 , gọi task_B1, nhấp nháy led kết nối đến port RB1 tại tốc độ 500ms
Tác vụ 3, gọi task_B2, nhấp nháy led kết nối đến port RB2 mỗi một giây
Tác vụ 4, gọi task_B3, nhấp nháy led kết nối đến port RB3 mỗi 2 giây
Hình 10.7 sơ đồ kết nối . Mạch sử dụng thạch anh 4 MHZ các led nối với chân RB0-RB3 thông qua điện trở hạn dòng
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SIMPLE RTOS EXAMPLE
// ----------------------------------
//
// This is a simple RTOS example. 4 LEDs are connected to lower half of
// PORTB of a PIC18F452 microcontroller. The program consists of 4
// tasks:
//
// Task task_B0 flashes the LED connected to port RB0 every 250ms.
// Task task_B1 flashes the LED connected to port RB1 every 500ms.
// Task task_B2 flashes the LED connected to port RB2 every second
// Task task_B3 flashes the LED connected to port RB3 every 2 seconds.
//
// The microcontroller is operated from a 4MHz crystal
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS1.C
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "C:\NEWNES\PROGRAMS\rtos.h"
#use delay (clock=4000000)
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=10ms)
//
// Declare TASK 1 - called every 250ms
//
//
// SIMPLE RTOS EXAMPLE
// ----------------------------------
//
// This is a simple RTOS example. 4 LEDs are connected to lower half of
// PORTB of a PIC18F452 microcontroller. The program consists of 4
// tasks:
//
// Task task_B0 flashes the LED connected to port RB0 every 250ms.
// Task task_B1 flashes the LED connected to port RB1 every 500ms.
// Task task_B2 flashes the LED connected to port RB2 every second
// Task task_B3 flashes the LED connected to port RB3 every 2 seconds.
//
// The microcontroller is operated from a 4MHz crystal
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS1.C
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "C:\NEWNES\PROGRAMS\rtos.h"
#use delay (clock=4000000)
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=10ms)
//
// Declare TASK 1 - called every 250ms
//
#task(rate=250ms, max=10ms)
void task_B0()
{
output_toggle(PIN_B0); // Toggle RB0
}
//
// Declare TASK 2 - called every 500ms
//
#task(rate=500ms, max=10ms)
void task_B1()
{
output_toggle(PIN_B1); // Toggle RB1
}
//
// Declare TASK 3 - called every second
void task_B0()
{
output_toggle(PIN_B0); // Toggle RB0
}
//
// Declare TASK 2 - called every 500ms
//
#task(rate=500ms, max=10ms)
void task_B1()
{
output_toggle(PIN_B1); // Toggle RB1
}
//
// Declare TASK 3 - called every second
//
#task(rate=1s, max=10ms)
void task_B2()
{
output_toggle(PIN_B2); // Toggle RB2
}
//
// Declare TASK 4 - called every 2 seconds
//
#task(rate=2s, max=10ms)
void task_B3()
{
output_toggle(PIN_B3); // Toggle RB3
}
//
// Start of MAIN program
//
void main()
{
set_tris_b(0); // Configure PORTB as outputs
rtos_run(); // Start RTOS
}
#task(rate=1s, max=10ms)
void task_B2()
{
output_toggle(PIN_B2); // Toggle RB2
}
//
// Declare TASK 4 - called every 2 seconds
//
#task(rate=2s, max=10ms)
void task_B3()
{
output_toggle(PIN_B3); // Toggle RB3
}
//
// Start of MAIN program
//
void main()
{
set_tris_b(0); // Configure PORTB as outputs
rtos_run(); // Start RTOS
}
Ví dụ 10.3 Bộ tạo số ngẫu nhiên:
Trong ví dụ RTOS hơi phức tạp này, một số ngẫu nhiên trong khoảng 0 và 255 được tạo. 8 led đơn kết nối đến portB của pic 18f452. Nút nhấn kết nối đến chân RD0 và 1 led đơn kết nối đến chân RD7
Có ba task được sử dụng trong ví dụ này: Live, Generator, và Display
Task Live chạy sau mỗi 200 ms và nhấp nháy đèn LED kết nối đến chân RD7 để báo rằng hệ thống đang hoạt động.
Task Live chạy sau mỗi 200 ms và nhấp nháy đèn LED kết nối đến chân RD7 để báo rằng hệ thống đang hoạt động.
Task Generator tăng biến từ 0 đến 255 và kiểm trạng thái của nút nhấn. Khi nút nhấn được nhấn giá trị hiện tại của bộ đếm được gửi đến task display sử dụng hàng đợi thông điệp
Task Display đọc số từ hàng đợi thông điệp và gửi byte nhận đến PORTB. Do đó khi nút nhấn được nhấn thì trạng thái các led kết nối với PORTB thay đổi tương ứng với số ngẫu nhiên.
Hình 10.9 Sơ đồ khối
Hình 10.10 sơ đồ mạch
Timer 0 được sử dụng như là bộ định thời RTOS , và minor_cycle được thiết lập là 1s. Chương trình bao gồm 3 task
Task Live chạy sau mỗi 200 ms và nhấp nháy đèn LED kết nối đến chân RD7 để báo rằng hệ thống đang hoạt động.
Task Generator chạy mỗi một millisecond và gia tăng biến tên count. Khi nút nhấn được nhấn, Pin 0 của PORTD (RD0) kéo xuống mức thấp. Tại thời điểm này , giá trị hiện tại của biến count được gửi đến tác vụ Display bằng cách gọi hàm rtos_msg_send(display, count) nơi Display là tên của tác vụ nơi thông điệp được gửi và Count là biến được gửi.
Task Display chạy sau mỗi 10 ms tác vụ này kiểm tra có thông điệp trong hàng đợi không nếu có thông điệp được trích xuất bằng cách gọi hàm rtos_msg_read(), và byte đọc khi đó được đưa đến port PORTB, do đó các led kết nối với PORTB hiển thị theo giá trị nhị phân của giá trị biến count khi phím được nhấn. Hàm đợi thông điệp nên kiểm tra bằng hàm rtos_msg_poll(). Nếu cố gắng đọc hàm đợi khi không có bất cứ byte nao trong hàm đợi có thể gây dừng chương trình
Đoạn chương trình:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SIMPLE RTOS EXAMPLE - RANDOM NUMBER GENERATOR
// ------------------------------------------------------------------------------------
//
// This is a simple RTOS example. 8 LEDs are connected to PORTB
// of a PIC18F452 microcontroller. Also, a push-button switch is
// connected to port RC0 of PORTC, and an LED is connected to port
// RC7 of the microcontroller. The push-button switch is normally at logic 1.
//
// The program consists of 3 tasks called "Generator", "Display", and "Live".
//
// Task "Generator" runs in a loop and increments a counter from 0 to 255.
// This task also checks the state of the push-button switch. When the
// push-button switch is pressed, the task sends the value of the count to the
// "Display" task using messaging passing mechanism. The “Display” task
// receives the value of count and displays it on the PORTB LEDs.
//
// Task "Live" flashes the LED connected to port RC7 at a rate of 250ms.
// This task is used to indicate that the system is working and is ready for
// the user to press the push-button.
//
// The microcontroller is operated from a 4MHz crystal
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS2.C
//
//////////////////////////////////////////////////////////////////////////////////
#include "C:\NEWNES\PROGRAMS\rtos.h"
#use delay (clock=4000000)
int count;
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=1ms)
//
// Declare TASK "Live" - called every 200ms
// This task flashes the LED on port RC7
//
#task(rate=200ms, max=1ms)
void Live()
{
output_toggle(PIN_D7);
}
//
//
// SIMPLE RTOS EXAMPLE - RANDOM NUMBER GENERATOR
// ------------------------------------------------------------------------------------
//
// This is a simple RTOS example. 8 LEDs are connected to PORTB
// of a PIC18F452 microcontroller. Also, a push-button switch is
// connected to port RC0 of PORTC, and an LED is connected to port
// RC7 of the microcontroller. The push-button switch is normally at logic 1.
//
// The program consists of 3 tasks called "Generator", "Display", and "Live".
//
// Task "Generator" runs in a loop and increments a counter from 0 to 255.
// This task also checks the state of the push-button switch. When the
// push-button switch is pressed, the task sends the value of the count to the
// "Display" task using messaging passing mechanism. The “Display” task
// receives the value of count and displays it on the PORTB LEDs.
//
// Task "Live" flashes the LED connected to port RC7 at a rate of 250ms.
// This task is used to indicate that the system is working and is ready for
// the user to press the push-button.
//
// The microcontroller is operated from a 4MHz crystal
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS2.C
//
//////////////////////////////////////////////////////////////////////////////////
#include "C:\NEWNES\PROGRAMS\rtos.h"
#use delay (clock=4000000)
int count;
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=1ms)
//
// Declare TASK "Live" - called every 200ms
// This task flashes the LED on port RC7
//
#task(rate=200ms, max=1ms)
void Live()
{
output_toggle(PIN_D7);
}
//
// Declare TASK "Display" - called every 10ms
//
#task(rate=10ms, max=1ms, queue=1)
void Display()
{
if(rtos_msg_poll() > 0) // Is there a message ?
{
output_b(rtos_msg_read()); // Send to PORTB
}
}
//
// Declare TASK "Generator" - called every millisecond
//
#task(rate=1ms, max=1ms)
void Generator()
{
count++; // Increment count
if(input(PIN_D0) == 0) // Switch pressed ?
{
rtos_msg_send(Display,count); // send a message
}
}
//
// Start of MAIN program
//
void main()
{
set_tris_b(0); // Configure PORTB as outputs
set_tris_d(1); // RD0=input, RD7=output
rtos_run(); // Start RTOS
}
//
#task(rate=10ms, max=1ms, queue=1)
void Display()
{
if(rtos_msg_poll() > 0) // Is there a message ?
{
output_b(rtos_msg_read()); // Send to PORTB
}
}
//
// Declare TASK "Generator" - called every millisecond
//
#task(rate=1ms, max=1ms)
void Generator()
{
count++; // Increment count
if(input(PIN_D0) == 0) // Switch pressed ?
{
rtos_msg_send(Display,count); // send a message
}
}
//
// Start of MAIN program
//
void main()
{
set_tris_b(0); // Configure PORTB as outputs
set_tris_d(1); // RD0=input, RD7=output
rtos_run(); // Start RTOS
}
Ví dụ 10.3 Đo điện áp và xuất đến cổng RS232
Trong ví dụ này, có nhiều phức tạp hơn hai ví dụ trước, điện áp đọc bằng cách sử dụng bộ chuyển đổi A/D gửi đến máy tính thông qua cổng nối tiếp. Ví dụ này sử dụng ba tác vụ: Live, Get_voltage, và To_RS232
Task Live chạy mỗi 20 ms, nhấp nháy đèn led kết nối đến port RD7 của vi điều khiển để báo rằng hệ thống đang hoạt động.
Task Get_Voltage đọc kênh 0 của bộ chuyển đổi A/D, nơi kết nối đến nguồn điện áp cần đo. Giá trị đọc sẽ được định dạng và được lưu trong biến. Tác vụ này chạy sau mỗi 2 giây.
Task To_RS232 đọc giá trị điện áp đo được và gửi đến cổng nối tiếp đến máy tính sau mỗi một giây.
Hình 10.12 là sơ đồ khối của ví dụ. Sơ đồ mạch trình bày như hình 10.13. ví dụ sử dụng Vi điều khiển pic 18f8520 và thạch anh 10 MHZ (ta có thể sử dụng các Pic 18F khác). Điện áp cần đo được kết nối đến chân analog AN0 của vi điều khiển. Chân RC6 (chân truyền dữ liệu của port nối tiếp) kết nối đến MAX232 để chuyển mức tín hiệu sau đó truyền đến PC (ví dụ Com1) sử dụng đầu kết nối 9 chân.
Hình 10.12
Hình 10.13
Trong chương trình chính PORTD cấu hình như là port xuất, và các chân được xóa về mức 0, PORT A thiết lập như là ngõ nhập, và cấu hình như là ngõ nhập analog, thiết lập xung clock A/D, chọn kênh AN0, khi đó chạy RTOS bằng cách gọi hàm rtos_run().
Chương trình bao gồm ba tác vụ:
Task Live chạy mỗi 20 ms, nhấp nháy đèn led kết nối đến port RD7 của vi điều khiển để báo rằng hệ thống đang hoạt động.
Task Get_voltage đọc điện áp analoge từ kênh 0 (chân RA0 hay AN0) của vi điều khiển. giá trị khi đó chuyển thành miivoltage bởi nhân cho 5000 và chia cho 1024. Điện áp lưu trữ ở biến toàn cục tên Volts.
Task To_RS232 đọc điện áp lưu ở biến Volts và gửi đến port RS232 bằng cách sử dụng lệnh printf . Kết quả được gửi đến máy tính theo định dạng:
Measuredvoltage = nnnn mV
Ta sử dụng chương trình HyperTerminal chạy trên máy tính để nhận dữ liệu từ vi điều khiển gửi đến
Đoạn chương trình:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SIMPLE RTOS EXAMPLE - VOLTMETER WITH RS232 OUTPUT
// ---------------------------------------------------------------------------------------
//
// This is a simple RTOS example. Analog voltage to be measured (between 0V
// and +5V) is connected to analog input AN0 of a PIC18F8520 type
// microcontroller. The microcontroller is operated from a 10MHz crystal. In
// addition, an LED is connected to port in RD7 of the microcontroller.
//
// RS232 serial output of the mirocontroller (RC6) is connected to a MAX232
// type RS232 voltage level converter chip. The output of this chip can be
// connected to the serial input of a PC (e.g., COM1) so that the measured
// voltage can be seen on the PC screen.
//
// The program consists of 3 tasks called "live", "Get_voltage", and “To_RS232”.
//
// Task "Live" runs every 200ms and it flashes the LED conencted to port pin
// RD7 of the microcontroller to indicate that the program is running and is
// ready to measure voltages.
//
// task "Get_voltage" reads analog voltage from port AN0 and then converts
// the voltage into millivolts and stores in a variable called Volts.
//
// Task "To_RS232" gets the measured voltage, converts it into a character
// array and then sends to the PC over the RS232 serial line. The serial line
// is configured to operate at 2400 Baud (higher Baud rates can also be used if
// desired).
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS3.C
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <18F8520.h>
#device adc=10
#use delay (clock=10000000)
#use rs232(baud=2400,xmit=PIN_C6,rcv=PIN_C7)
unsigned int16 adc_value;
unsigned int32 Volts;
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=100ms)
//
// Declare TASK "Live" - called every 200ms
// This task flashes the LED on port RD7
//
#task(rate=200ms, max=1ms)
void Live()
{
output_toggle(PIN_D7); // Toggle RD7 LED
}
//
// Declare TASK "Get_voltage" - called every 10ms
//
#task(rate=2s, max=100ms)
void Get_voltage()
{
adc_value = read_adc(); // Read A/D value
Volts = (unsigned int32)adc_value*5000;
Volts = Volts / 1024; // Voltage in mV
}
//
// Declare TASK "To_RS232" - called every millisecond
//
#task(rate=2s, max=100ms)
void To_RS232()
{
printf("Measured Voltage = %LumV\n\r",Volts); // send to RS232
}
//
// Start of MAIN program
//
void main()
{
set_tris_d(0); // PORTD all outputs
output_d(0); // Clear PORTD
set_tris_a(0xFF); // PORTA all inputs
setup_adc_ports(ALL_ANALOG); // A/D ports
setup_adc(ADC_CLOCK_DIV_32); // A/D clock
set_adc_channel(0); // Select channel 0 (AN0)
delay_us(10);
rtos_run(); // Start RTOS
}
//
#task(rate=200ms, max=1ms)
void Live()
{
output_toggle(PIN_D7); // Toggle RD7 LED
}
//
// Declare TASK "Get_voltage" - called every 10ms
//
#task(rate=2s, max=100ms)
void Get_voltage()
{
adc_value = read_adc(); // Read A/D value
Volts = (unsigned int32)adc_value*5000;
Volts = Volts / 1024; // Voltage in mV
}
//
// Declare TASK "To_RS232" - called every millisecond
//
#task(rate=2s, max=100ms)
void To_RS232()
{
printf("Measured Voltage = %LumV\n\r",Volts); // send to RS232
}
//
// Start of MAIN program
//
void main()
{
set_tris_d(0); // PORTD all outputs
output_d(0); // Clear PORTD
set_tris_a(0xFF); // PORTA all inputs
setup_adc_ports(ALL_ANALOG); // A/D ports
setup_adc(ADC_CLOCK_DIV_32); // A/D clock
set_adc_channel(0); // Select channel 0 (AN0)
delay_us(10);
rtos_run(); // Start RTOS
}
Sử dụng Semaphore:
Chương trình ở trên làm việc và hiển thị điện áp đo lường trên màn hình máy tính. Chương trình có thể cải thiện một chúc bằng cách sử dụng Semaphore để đồng bộ hiển thị điện áp đo lường với bộ lấy mẫu A/D. Hoạt động của chương trình mới được hoạt động như sau:
Chương trình ở trên làm việc và hiển thị điện áp đo lường trên màn hình máy tính. Chương trình có thể cải thiện một chúc bằng cách sử dụng Semaphore để đồng bộ hiển thị điện áp đo lường với bộ lấy mẫu A/D. Hoạt động của chương trình mới được hoạt động như sau:
Biến semaphore (sem) được bật bằng 1 tại lúc bắt đầu chương trình
Task Get_voltage giảm biến semaphore (calls rtos_wait) để task To_RS232 bị khóa (biến semaphore sem=0) và không thể gửi dữ liệu đến PC. Khi lấy mẫu A/D mới đã sẳn sàng biến semaphore được tăng lên (calls rtos_signal) và Task To_RS232 có thể tiếp tục. Khi đó Task To_RS232 gửi điện áp đến PC và tăng biến semaphore để chỉ ra rằng nó có quyền truy cập dưx liệu. Task Get_voltage có thể thực hiện một lấy mẫu mới. Tiến trình lặp đi lặp lại liên tục
Đoạn chương trình:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SIMPLE RTOS EXAMPLE - VOLTMETER WITH RS232 OUTPUT
// ---------------------------------------------------------------------------------------
//
// This is a simple RTOS example. Analog voltage to be measured (between 0V
// and +5V) is connected to analog input AN0 of a PIC18F8520 type
// microcontroller. The microcontroller is operated from a 10MHz crystal. In
// addition, an LED is connected to port in RD7 of the microcontroller.
//
// RS232 serial output of the mirocontroller (RC6) is connected to a MAX232
// type RS232 voltage level converter chip. The output of this chip can be
// connected to the serial input of a PC (e.g., COM1) so that the measured
// voltage can be seen on the PC screen.
//
// The program consists of 3 tasks called "live", "Get_voltage", and "To_RS232".
//
// Task "Live" runs every 200ms and it flashes the LED connected to port RD7
// of the microcontroller to indicate that the program is running and is ready to
// measure voltages.
//
// task "Get_voltage" reads analog voltage from port AN0 and then converts the
// voltage into millivolts and stores in a variable called Volts.
//
// Task "To_RS232" gets the measured voltage and then sends to the PC over
// the RS232 serial line. The serial line is configured to operate at 2400 Baud
// (higher Baud rates can also be used if desired).
//
// In this modified program, a semaphore is used to synchronize
// the display of the measured value with the A/D samples.
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS4.C
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <18F8520.h>
#device adc=10
#use delay (clock=10000000)
#use rs232(baud=2400,xmit=PIN_C6,rcv=PIN_C7)
unsigned int16 adc_value;
unsigned int32 Volts;
int8 sem;
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=100ms)
//
// SIMPLE RTOS EXAMPLE - VOLTMETER WITH RS232 OUTPUT
// ---------------------------------------------------------------------------------------
//
// This is a simple RTOS example. Analog voltage to be measured (between 0V
// and +5V) is connected to analog input AN0 of a PIC18F8520 type
// microcontroller. The microcontroller is operated from a 10MHz crystal. In
// addition, an LED is connected to port in RD7 of the microcontroller.
//
// RS232 serial output of the mirocontroller (RC6) is connected to a MAX232
// type RS232 voltage level converter chip. The output of this chip can be
// connected to the serial input of a PC (e.g., COM1) so that the measured
// voltage can be seen on the PC screen.
//
// The program consists of 3 tasks called "live", "Get_voltage", and "To_RS232".
//
// Task "Live" runs every 200ms and it flashes the LED connected to port RD7
// of the microcontroller to indicate that the program is running and is ready to
// measure voltages.
//
// task "Get_voltage" reads analog voltage from port AN0 and then converts the
// voltage into millivolts and stores in a variable called Volts.
//
// Task "To_RS232" gets the measured voltage and then sends to the PC over
// the RS232 serial line. The serial line is configured to operate at 2400 Baud
// (higher Baud rates can also be used if desired).
//
// In this modified program, a semaphore is used to synchronize
// the display of the measured value with the A/D samples.
//
// Programmer: Dogan Ibrahim
// Date: September, 2007
// File: RTOS4.C
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <18F8520.h>
#device adc=10
#use delay (clock=10000000)
#use rs232(baud=2400,xmit=PIN_C6,rcv=PIN_C7)
unsigned int16 adc_value;
unsigned int32 Volts;
int8 sem;
//
// Define which timer to use and minor_cycle for RTOS
//
#use rtos(timer=0, minor_cycle=100ms)
//
// Declare TASK "Live" - called every 200ms
// This task flashes the LED on port RD7
//
#task(rate=200ms, max=1ms)
void Live()
{
output_toggle(PIN_D7); // Toggle RD7 LED
}
//
// Declare TASK "Get_voltage" - called every 10ms
//
#task(rate=2s, max=100ms)
void Get_voltage()
{
rtos_wait(sem); // decrement semaphore
adc_value = read_adc(); // Read A/D value
Volts = (unsigned int32)adc_value*5000;
Volts = Volts / 1024; // Voltage in mV
rtos_signal(sem); // increment semaphore
}
//
// Declare TASK "To_RS232" - called every millisecond
//
#task(rate=2s, max=100ms)
void To_RS232()
{
rtos_wait(sem); // Decrement semaphore
printf("Measured Voltage = %LumV\n\r",Volts); // Send to RS232
rtos_signal(sem); // Increment semaphore
}
//
// Start of MAIN program
//
void main()
{
set_tris_d(0); // PORTD all outputs
output_d(0); // Clear PORTD
set_tris_a(0xFF); // PORTA all inputs
setup_adc_ports(ALL_ANALOG); // A/D ports
setup_adc(ADC_CLOCK_DIV_32); // A/D clock
set_adc_channel(0); // Select channel 0 (AN0)
delay_us(10);
sem = 1; // Semaphore is 1
rtos_run(); // Start RTOS
}
// Declare TASK "Live" - called every 200ms
// This task flashes the LED on port RD7
//
#task(rate=200ms, max=1ms)
void Live()
{
output_toggle(PIN_D7); // Toggle RD7 LED
}
//
// Declare TASK "Get_voltage" - called every 10ms
//
#task(rate=2s, max=100ms)
void Get_voltage()
{
rtos_wait(sem); // decrement semaphore
adc_value = read_adc(); // Read A/D value
Volts = (unsigned int32)adc_value*5000;
Volts = Volts / 1024; // Voltage in mV
rtos_signal(sem); // increment semaphore
}
//
// Declare TASK "To_RS232" - called every millisecond
//
#task(rate=2s, max=100ms)
void To_RS232()
{
rtos_wait(sem); // Decrement semaphore
printf("Measured Voltage = %LumV\n\r",Volts); // Send to RS232
rtos_signal(sem); // Increment semaphore
}
//
// Start of MAIN program
//
void main()
{
set_tris_d(0); // PORTD all outputs
output_d(0); // Clear PORTD
set_tris_a(0xFF); // PORTA all inputs
setup_adc_ports(ALL_ANALOG); // A/D ports
setup_adc(ADC_CLOCK_DIV_32); // A/D clock
set_adc_channel(0); // Select channel 0 (AN0)
delay_us(10);
sem = 1; // Semaphore is 1
rtos_run(); // Start RTOS
}
Không có nhận xét nào:
Đăng nhận xét