【问题标题】:ISR + Code, integrating count code with ISR routineISR + 代码,将计数代码与 ISR 例程集成
【发布时间】:2015-08-21 08:44:17
【问题描述】:

我有一段用于 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;
        }
    }
}

【问题讨论】:

  • ??一旦你取出调试 printf 等,唯一的“真实”代码就是一些端口读/写。为什么不能进入中断处理程序?包含计数等的控制/状态数据实际上应该在一个“TimerControlBlock”结构中,但这是一种很好的做法,不是必需的。
  • 嗨,马丁,我希望保留 printf 命令作为健康检查,甚至记录到串行文件以验证系统,但是我担心这种命令类型不属于 ISR 循环。我可以只增加 ISR 中的所有计数器,然后在外面做我的 printf 吗?我测量了这段代码的执行时间和它的 97 毫秒,但是这种方法有缺陷吗?
  • "...但是这种方法有缺陷吗?"不,它坏了。 1)不管怎样,在这里有这么多“printf”是无稽之谈。2)使用功能代码是无稽之谈。3)在测试后保留它们是完全无稽之谈。作为专业人士,你会赌博你的工作。
  • delay_whatever 非标准
  • 什么 PIC 设备?什么 PIC 编译器?

标签: c embedded pic isr


【解决方案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 填充事件队列的 some code I wrote 感兴趣。

【讨论】:

  • lasttick 应该被初始化为刻度,而不是零。
  • @Lundin 也一样,因为tick 有静态存储(隐式初始化为零)
  • 不,不是,因为从您初始化计时器的点到执行代码的点已经过去了可变的时间。此外,嵌入式系统不实现标准静态初始化是很常见的,在这种情况下实际上甚至不需要,因为tick的实际值与程序的功能无关。
  • @Lundin: 1. avr-libc 实现了它,如果它没有实现,你仍然可以将它初始化为 0, 2. 你通常在进入事件循环之前最后设置你的中断, 3. 使用这里的 0 允许设计仍然应延迟处理滴答声(设计决定,取决于您的需要)。因此,只需将细节留给读者,这是为了呈现总体思路。有关tick 实际值的可能相关性,请参阅内部循环中的注释。
  • 关于 lasttick 的初始化,我的建议永远不会造成任何伤害,但在某些情况下会导致代码更健壮。即使您在循环之前启用中断,您也会有 [定时器设置] + [循环开销] + [lasttick 初始化时间] + [ISR 调用开销] 的延迟,而不仅仅是 [ISR 调用开销]。请记住,这是一张图片,仍然是 80 年代最慢的废话。
【解决方案2】:

我想 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 可能是合适的。

【讨论】:

  • @olaf sprintfs 的输出用于记录数据以进行后期处理,缺少这些确实会导致我的工作出现问题,有来自 National Instruments 硬件的数据流,串行数据是核心对于我们的结果,sprintf 留下来了,,,,,,,感谢所有输入,我会整理我的代码,非常感谢先生们...
  • 我的代码中的主循环耗时不到 100 毫秒,所以我会将其放入整理后的代码 ISR 例程中,谢谢大家,,,,
  • @StevieB :通常,100 毫秒对于 ISR 代码来说太长了。较低优先级的中断将被延迟并变得不确定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多