You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PIC18 C定时到期任务实现:自动复位输出位及任务跟踪

Alright, let's break down how to implement this delayed task system for your PIC18 using ANSI C, building on your existing TMR4 10ms timestamp setup. The core idea is to create a lightweight task scheduler that tracks pending actions, checks them on each timer tick, and executes them when their time is up.

1. Define a Task Structure to Track Pending Actions

First, we need a way to store all the details of each delayed task—when it should run, what action to take, and any parameters needed. We'll use a struct with a union to handle different task types (resetting a bit or running a custom function):

// Adjust this based on how many concurrent delayed tasks you need
#define MAX_TASKS 8

// Enum to distinguish between different task types
typedef enum {
    TASK_RESET_BIT,
    TASK_CUSTOM_FUNCTION
} TaskType;

// Struct to hold all info for a delayed task
typedef struct {
    unsigned int trigger_timestamp;  // When the task should execute (based on your global timestamp)
    TaskType type;                   // What kind of task this is
    union {
        // Parameters for resetting a specific bit
        struct {
            int byte;
            int bitnr;
        } bit_reset;
        // Parameters for running a custom function
        struct {
            void (*func)(void*);
            void* arg;
        } custom;
    } params;
    unsigned char active;            // Flag to mark if the task is waiting to run
} DelayedTask;
2. Initialize the Task Queue

We'll use an array of the above struct as our task queue. Initialize all tasks as inactive on startup:

DelayedTask task_queue[MAX_TASKS];

void init_task_scheduler(void) {
    for (int i = 0; i < MAX_TASKS; i++) {
        task_queue[i].active = 0;  // Mark all slots as empty initially
    }
}
3. Implement the resetBit Function (and Custom Task Scheduling)

Now, let's build the resetBit function you mentioned—it will add a bit-reset task to the queue. We'll also add a helper for scheduling custom functions, since you mentioned that as an alternative.

First, make sure you have your global timestamp variable (updated every 10ms by TMR4) declared as an extern here:

extern unsigned int global_timestamp;  // This should be updated in your TMR4 ISR

Then the resetBit function:

void resetBit(int byte, int bitnr, int time) {
    // Calculate when the task should run: current timestamp + (time in ms / 10ms per tick)
    unsigned int trigger_time = global_timestamp + (time / 10);
    
    // Find an empty slot in the task queue
    for (int i = 0; i < MAX_TASKS; i++) {
        if (!task_queue[i].active) {
            // Fill in the task details
            task_queue[i].trigger_timestamp = trigger_time;
            task_queue[i].type = TASK_RESET_BIT;
            task_queue[i].params.bit_reset.byte = byte;
            task_queue[i].params.bit_reset.bitnr = bitnr;
            task_queue[i].active = 1;
            return;
        }
    }
    
    // If we hit this point, the queue is full—add error handling here (e.g., log, ignore, or overwrite)
}

And a helper for scheduling custom functions:

void schedule_custom_task(void (*func)(void*), void* arg, int time) {
    unsigned int trigger_time = global_timestamp + (time / 10);
    
    for (int i = 0; i < MAX_TASKS; i++) {
        if (!task_queue[i].active) {
            task_queue[i].trigger_timestamp = trigger_time;
            task_queue[i].type = TASK_CUSTOM_FUNCTION;
            task_queue[i].params.custom.func = func;
            task_queue[i].params.custom.arg = arg;
            task_queue[i].active = 1;
            return;
        }
    }
    // Queue full error handling here
}
4. Process Tasks in the TMR4 Interrupt

The final piece is checking the task queue on every TMR4 tick (every 10ms) and executing tasks when their time comes. Add this logic inside your TMR4 ISR:

void __interrupt() isr(void) {
    if (PIR3bits.TMR4IF) {  // Check if TMR4 triggered the interrupt
        // Update your global timestamp (you should already have this line)
        global_timestamp++;
        
        // Loop through all tasks and check if any are ready to run
        for (int i = 0; i < MAX_TASKS; i++) {
            if (task_queue[i].active) {
                // Handle timestamp overflow (for 16-bit timestamps, they wrap after ~655 seconds)
                unsigned char task_ready = 0;
                if (global_timestamp >= task_queue[i].trigger_timestamp) {
                    task_ready = 1;
                } else if (task_queue[i].trigger_timestamp - global_timestamp > 32768) {
                    // If the trigger time is "behind" current time but the difference is large, it's an overflow
                    task_ready = 1;
                }
                
                if (task_ready) {
                    switch (task_queue[i].type) {
                        case TASK_RESET_BIT:
                            // Replace this with your actual bit-reset logic
                            // Example: If 'byte' maps to output registers like PORTB/PORTC
                            // volatile unsigned char* output_regs[] = {&PORTB, &PORTC, ...};
                            // output_regs[task_queue[i].params.bit_reset.byte] &= ~(1 << task_queue[i].params.bit_reset.bitnr);
                            break;
                        case TASK_CUSTOM_FUNCTION:
                            // Run the custom function with its argument
                            task_queue[i].params.custom.func(task_queue[i].params.custom.arg);
                            break;
                    }
                    
                    // Mark the task as completed (inactive)
                    task_queue[i].active = 0;
                }
            }
        }
        
        PIR3bits.TMR4IF = 0;  // Clear the interrupt flag to re-enable future ticks
    }
    
    // Handle other interrupts here if needed
}
Key Notes to Keep in Mind
  • Timestamp Overflow: The code above includes basic handling for 16-bit timestamp overflow. If you're using a larger timestamp type (like 32-bit), you can skip this check.
  • ISR Efficiency: Keep the code inside the ISR as lean as possible. If your custom function is long, consider setting a flag in the ISR and running the function in your main loop instead—this avoids blocking other interrupts.
  • Register Safety: When accessing PIC18 I/O registers, use volatile to prevent the compiler from optimizing away your writes/reads.
  • Queue Size: Adjust MAX_TASKS to match how many concurrent delayed actions you expect to have.

内容的提问来源于stack exchange,提问作者Rian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:27:02