Một điều tôi thấy hữu ích trên một số máy là trình chuyển đổi ngăn xếp đơn giản. Tôi chưa thực sự viết một cái cho PIC, nhưng tôi hy vọng cách tiếp cận sẽ hoạt động tốt trên PIC18 nếu cả hai / tất cả các luồng sử dụng tổng cộng 31 hoặc ít hơn các cấp độ ngăn xếp. Trên 8051, thói quen chính là:
_taskswitch:
xch a, SP
xch a, _altSP
xch a, SP
nghỉ
Trên PIC, tôi quên tên của con trỏ ngăn xếp, nhưng thường trình sẽ là một cái gì đó như:
_taskswitch:
Movlb _altSP >> 8
Movf _altSP, w, b
Movff _STKPTR, altSP
Movwf _STKPTR, c
trở về
Khi bắt đầu chương trình của bạn, hãy gọi một thường trình task2 () tải altSP với địa chỉ của ngăn xếp thay thế (16 có thể sẽ hoạt động tốt cho PIC18Fxx) và chạy vòng lặp task2; thói quen này không bao giờ trở lại nếu không mọi thứ sẽ chết một cái chết đau đớn. Thay vào đó, nó nên gọi _taskswitch bất cứ khi nào nó muốn mang lại quyền kiểm soát cho nhiệm vụ chính; sau đó, nhiệm vụ chính sẽ gọi _taskswitch bất cứ khi nào nó muốn chuyển sang nhiệm vụ phụ. Thông thường, người ta sẽ có những thói quen nhỏ dễ thương như:
void delay_t1 (val ngắn không dấu)
{
làm
nhiệm vụ ();
while (không dấu ngắn) (mili giây_clock - val)> 0xFF00);
}
Lưu ý rằng trình chuyển đổi tác vụ không có bất kỳ phương tiện nào để thực hiện bất kỳ 'chờ điều kiện'; tất cả những gì nó hỗ trợ là một spinwait. Mặt khác, công tắc tác vụ nhanh đến mức cố gắng thực hiện một tác vụ chuyển đổi tác vụ () trong khi tác vụ khác đang chờ đồng hồ hết hạn sẽ chuyển sang tác vụ khác, kiểm tra bộ hẹn giờ và chuyển trở lại nhanh hơn trình chuyển đổi tác vụ thông thường sẽ xác định rằng nó không cần taskswitch.
Lưu ý rằng đa nhiệm hợp tác có một số hạn chế, nhưng nó tránh được nhu cầu khóa và mã liên quan đến đột biến khác trong trường hợp bất biến tạm thời bị xáo trộn có thể được thiết lập lại nhanh chóng.
(Chỉnh sửa): Một vài cảnh báo về các biến tự động và như vậy:
- nếu một thường trình sử dụng chuyển đổi tác vụ được gọi từ cả hai luồng, thông thường sẽ cần phải biên dịch hai bản sao của thường trình (có thể bằng cách bao gồm cùng một tệp nguồn hai lần, với các câu lệnh #define khác nhau). Bất kỳ tệp nguồn đã cho nào cũng sẽ chỉ chứa mã cho một luồng hoặc nếu không sẽ chứa mã sẽ được biên dịch hai lần - một lần cho mỗi luồng - vì vậy tôi có thể sử dụng các macro như "#define delay (x) delay_t1 (x)" hoặc #define delay (x) delay_tx (x) "tùy thuộc vào chủ đề tôi đang sử dụng.
- Tôi tin rằng các trình biên dịch PIC không thể "nhìn thấy" một hàm được gọi sẽ cho rằng một hàm như vậy có thể bỏ qua bất kỳ và tất cả các thanh ghi CPU, do đó tránh việc phải lưu bất kỳ thanh ghi nào trong thói quen chuyển đổi tác vụ [một lợi ích tốt so với đa nhiệm ưu tiên]. Bất cứ ai xem xét một trình chuyển đổi tác vụ tương tự cho bất kỳ CPU nào khác đều cần phải biết về các quy ước đăng ký đang sử dụng. Đẩy các thanh ghi trước khi chuyển đổi tác vụ và bật chúng sau là một cách dễ dàng để xử lý mọi thứ, giả sử có đủ không gian ngăn xếp.
Đa nhiệm hợp tác không cho phép một người hoàn toàn thoát khỏi các vấn đề về khóa và như vậy, nhưng nó thực sự đơn giản hóa rất nhiều thứ. Ví dụ, trong một RTOS ưu tiên với bộ thu gom rác nén, cần phải cho phép các đối tượng được ghim. Khi sử dụng trình chuyển đổi hợp tác, điều này không cần thiết với điều kiện là mã giả định các đối tượng GC có thể di chuyển bất kỳ khi nào tác vụ chuyển đổi () được gọi. Một bộ sưu tập nén mà không phải lo lắng về các đối tượng được ghim có thể đơn giản hơn nhiều so với một đối tượng.