0

我有一段用于 PIC 设备的 C 代码,它将 4 个单独的继电器驱动为预定义的模式,每个继电器都通过计算时间和发生频率来单独设置。这种模式无限期地继续下去,但发现标准 delay_ms 上的时间不够准确。我希望将其转换为 ISR 例程,我附上了下面依赖于标准延迟的代码供您阅读,我主要关心的是如何将此代码抽象为 ISR,因为我知道不要将所有这些代码粘贴到实际的 ISR 例程中,任何非常感谢您的建议。

__CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & CP_OFF & CPD_OFF &
        BOREN_OFF & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF);

//!!! use BORV_HI for latest PICC compiler
__CONFIG(WRT_OFF & PLLEN_OFF & STVREN_OFF & LVP_OFF);       

#define _XTAL_FREQ  4000000

unsigned long int Sample_Period =20;
unsigned long int Sample_Duration = 2;
unsigned long int Sample_ON = 0;
unsigned long int WriteTX_Period = 21;
unsigned long int WriteTX_Duration = 1; //  
unsigned long int WriteTX_ON = 0;
unsigned long int Depass_Period = 60; //every 7 days for depass event
unsigned long int Depass_Duration = 10 ; // depass for 120 seconds
unsigned long int Depass_ON = 0 ;
unsigned long int Depass_Counter = 0;
unsigned long int Sample_Counter = 0;
unsigned long int WriteTX_Counter = 0;
unsigned long int count = 0;
unsigned char input;
char data = 1;

void SkipLine(void){
    printf("\n");
    printf("\r");
}

void main() {
    INTCON = 0;                 // disable interrupts.
    ANSELA = 0x00;              // all digital IO
    ANSELB = 0x00;              // all digital IO
    TRISA = 0b00000000;         // Configure PORTA as output 
    PORTA = 0b00000111;         // Initialize PORTA, all load relays are off.
    ADCON0 = 7;                 // disables ADC
    CM1CON0 = 7;                // Disable analog comparators
    TRISB = 0b10111001;         // all outputs bar RB1/RX pin, and RB7 (Prog'd)

    init_comms();

    printf("\n");
    printf("\r");
    printf("*******************************");
    printf("\n");
    printf("\r");
    printf("*METROL RELAY CONTROLLER MK1.0*");
    printf("\n");
    printf("\r");
    printf("*******************************");
    printf("\n");
    printf("\r");

    printf("Default timings are :");
    printf("\n");
    printf("\r");
    printf("Sample Period = ");
    printf("%d seconds", Sample_Period);
    printf("\n");
    printf("\r");

    printf("\n");
    printf("\r");
    printf("Sample Duration = ");
    printf("%d seconds", Sample_Duration);
    printf("\n");
    printf("\r");

    printf("\n");
    printf("\r");
    printf("WriteTX Period = ");
    printf("%d seconds", WriteTX_Period);
    printf("\n");
    printf("\r");

    printf("\n");
    printf("\r");
    printf("WriteTX Duration = ");
    printf("%d seconds", WriteTX_Duration);
    SkipLine;

    printf("\n");
    printf("\r");
    printf("Depassivation Period (Days)= ");
    printf("%d Days", Depass_Period);
    printf("\n");
    printf("\r");
    printf("\n");
    printf("\r");
    printf("Depassivation Duration  = ");
    printf("%d seconds", Depass_Duration);
    printf("\n");
    printf("\r");

    if (RB7 == 1)
    {
        printf("requires set up");

        printf("\n");
        printf("\r");
        printf("\n");
        printf("\r");

        printf("Enter value for Sample Period in minutes <0-255> ");
        printf("\n");
        printf("\r");

        char str[50];

        printf("Enter a string : ");
        gets(str);

        printf("You entered: %s", str);
        int SamplePeriodVal;
        SamplePeriodVal = atoi(str);
        printf("Sample Period Value entered = %d\n", SamplePeriodVal);
    }

    printf("\n");
    printf("\r");
    printf("system already configured");
    printf("\n");
    printf("\r");

    unsigned int Sample_Period_Units;
    Sample_Period_Units = EEPROM_READ(0x00);
    printf("sample value held in first eeprom address 0x00 is %d", EEPROM_READ(0x00));

    printf("\n");
    printf("\r");
    printf("load profile starting.....");
    printf("\n");
    printf("\r");

    while (1) {
        printf("\n");
        printf("\r");
        printf("test!");
        printf("\n");
        printf("\r");

        __delay_ms(990);

        if (Sample_Counter >= Sample_Period){
            PORTA = 0b00000110; //set Sample relay ON

            Sample_ON++;

            if (Sample_ON > Sample_Duration){
                Sample_ON = 0;
                Sample_Counter = 0;
                PORTA = 0b00000111;

            }
        }

        if (WriteTX_Counter >= WriteTX_Period){
            PORTA = 0b00000100; //set Write relay ON

            WriteTX_ON++;

            if (WriteTX_ON > WriteTX_Duration){
                WriteTX_ON = 0;
                WriteTX_Counter = 0;
                PORTA = 0b00000111;

            }
        }

        if (Depass_Counter >= Depass_Period){
            PORTA = 0b00000011; //set Depass relay ON


            Depass_ON++;

            if (Depass_ON > Depass_Duration){
                Depass_ON = 0;
                Depass_Counter = 0;
                PORTA = 0b00000111;

            }
        }

        Sample_Counter++;
        WriteTX_Counter++;
        Depass_Counter++;
        count++;            // increment total count for system

        printf("\n");
        printf("\r");

        int SampleAct;
        SampleAct = RB3;
        printf("Port B sample value =%d  ", SampleAct);

        printf("\r");
        printf("\n");

        int WriteTXAct;
        WriteTXAct = RB4;
        printf("Port B WriteTX value =%d  ", WriteTXAct);

        printf("\r");
        printf("\n");

        int DepassAct;
        DepassAct = RB5;
        printf("Port B Depass value =%d  ", DepassAct);

        printf("\r");
        printf("\n");
        printf("%ld", count);

        int PortB_Val;

        PortB_Val = PORTB & 0b00111000;

        switch (PortB_Val)
        {

        case 0x28:
            RB6 = RB6;
            printf("\n");
            printf("\r");
            printf("Sample+Depass error");
            break;

        case 0x30:
            RB6 = RB6;
            printf("\n");
            printf("\r");
            printf("Write+Depass error");
            break;

        case 0x38:
            RB6 = RB6;
            printf("\n");
            printf("\r");
            printf("Write+Sample+Depass error");
            break;

        default:
            RB6 = !RB6;
        }
    }
}
4

2 回答 2

1

您的代码仅取决于定期计时器滴答声,因此一般的想法是执行以下操作:

volatile static uint8_t tick;

ISR(TIMER_vec) /* whatever int vector is triggered by your timer */
{
    ++tick;
}


int main () {
    /* [...] */

    while (1)
    {
        uint8_t lasttick = 0;
        while (tick != lasttick)
        {
            lasttick = tick; /* or ++lasttick; for handling "missed" interrupts late */

            /*
             * do your periodic stuff here
             */
        }
        /* wait for next interrupt, e.g. by entering sleep state
           for AVR: */
        sleep_cpu();
    }
}

当然,您必须根据您的芯片安排获得定时器中断。如果事情变得更复杂,您可能会对我使用 ISR 编写的用于填充事件队列的一些代码感兴趣。

于 2015-08-21T10:07:52.587 回答
0

我想 delay_ms() 的精度完全取决于您的 XTAL 的稳定性 - 除非您在低精度 RC 振荡器上运行,否则这不太可能是您的问题,如果是这种情况,请使用计时器硬件或ISR 无济于事,因为它们都在同一个时钟上运行。

您的问题是您的设计和 printf() 的使用。如果 printf() 没有缓冲,或者您正在填充缓冲区,则循环时间将由调试输出控制 - 如果 printf() 输出是通过 UART 串​​行端口,则循环时间将由波特率决定该通道和文本输出量。

根本问题是你的循环时间是延迟加上执行循环体的时间的总和——这可能是可变的和不确定的,这取决于 printf 的实现、输出设备、它的数据速率以及是否有流量以控制为例。

更好的方法是轮询计时器并在需要执行循环体时而不是在固定延迟之后执行。计时器的实现是特定于平台的,但是假设您实现了一个计时器,其 ISR 每毫秒递增一个计数器,并且该计数器由一个函数读取gettime_ms(),那么您的循环将变为:

int start_1000 = gettime_ms() ;
int now = start ;
for(;;)
{
    now = gettime_ms() ;

    if( now - start_1000 >= 1000 )
    {
        start_1000 += 1000 ;

        //  Loop body here - will execute every 1000ms
        // so long as the loop body takes less than 1000ms in total.
        ...
    }
}

然后,您可以轻松地以不同的速率引入其他周期性操作:

int now = gettime_ms() ;
int start_1000 = now;
int start_50 = now ;

for(;;)
{
    now = gettime_ms() ;

    // Every second
    if( now - start_1000 >= 1000 )
    {
        start_1000 += 1000 ;

        // 1 second operations here
        ...
    }

    // Every 50ms
    if( now - start_50 >= 50)
    {
        start_50 += 50 ;

        // 50ms operations here 
        ...
    }
}

您还可以在周期性块之外执行快速的后台任务工作,以便充分利用可用的 CPU 时间。例如,此类任务可能包括安全监控——这可能不是您想要等待一秒钟的事情。

最终,如果要执行时间紧迫的任务和事件处理,那么 RTOS 可能是合适的。

于 2015-08-22T16:07:47.053 回答