Ge mig råd angående “multitasking”...

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
dangraf
Inlägg: 530
Blev medlem: 9 juni 2003, 15:30:56
Ort: göteborg

Inlägg av dangraf »

sodjan skrev:> när PIC:en ska utföra flera olika saker ”samtidigt”

Det kan den inte. En PIC kan bara göra *EN* sak i taget.

Eftersom detta påstående uppkommit flera gånger och jag irriterar mig på det ska jag försöka förklara att en PIC faktiskt KAN göra flera saker samtidigt..

Med hjälp av perferienheterna så kan faktiskt en PIC precis bli klar med en AD omvandling samtidigt som TMR0 räknar upp , sammtidigt som den innbygda UARTen skickar en 1a och dessutom har ALUn blivit klar med en uträkning och sparar värdet i arbetsminnet. Däremot kan programräknaren bara peka på EN adress i koden, om man skall vara petnoga (hoppas jag inte sagt för mycket nu).

Det jag vill komma fram till är att om man har ett multitaskande system behöver man inte starta en AD omvandling, vänta till den blir klar, därefter skicka en byte över uarten, vänta tills den blir klar osv, utan man kan administrera ut uppgifterna och på så sätt få mer gjort under kortare tid om man är effektiv. får man inte PIC en till att göra flera saker samtidigt på detta vis??


Jag känner mig på lite bråkhumör idag och kunde inte hålla tyst :wink:

// Daniel
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Jo visst ! Helt rätt.

Och genom att alla periferienheter har egna interrupt, så kan man få det
hela att löpa riktigt smidigt utan att någon enhet behöver "veta" att de
andra "samtidigt" håller på med något annat (d.v.s sin interna AD-omvandling,
sänd/mott på USART o.s.v). Har man dessutom PWM-modulen igång så
går ju den hela tiden "samtidigt" som själva koden körs... :-)

*Mitt* påpekande är naturligtsivs lite hårddraget. Det jag vill är att man
säger "inom 100us" (eller vad det nu är som gäller) istället för "samtidigt"
eller "så snabbt som möjligt". D.v.s att man *själv* ska tänka igenom vilka
krav man har på applikationen *innan* man frågar här. Det är ändå ingen
annan som kan speca kraven...
jonte_s
Inlägg: 88
Blev medlem: 11 februari 2006, 17:13:48
Ort: Stockholm

Inlägg av jonte_s »

Hej.

Jag har problem med koden till min manchesterkod-sändare. Har suttit länge, länge nu med samma problem.

Beskrivning av koden:
Hela sändarkoden ligger i interrupt rutinen för TMR1. Sändaren kallas för task0. När sändaren är aktiv och sänder ett meddelande så sätts flaggan ”transmiter_is_busy = TRUE”, vilket hindrar att vi ändrar på de bitar vi skall sända.
TMR1 interruptar var 889us, vilket är längden på varje ”sampel”/bit som sänds. (skriver sampel eftersom i manchesterkod består ju en bit av två bitar/”sampel”…)
Jag ligger inte kvar i interruptrutinen under hela den tid som krävs för att sända ett meddelande, utan det i interruptrutinen tar runt 30us, sedan hoppar vi tillbaka till main().

I main har jag en ADC som läser av en potentiometer kontinuerligt och omvandlar ADC-värdet (0-1023) till 0-255. (blir 0-64… varför vet jag inte, men det är ett senare problem.)
Om det omvandlade värdet är mindre eller större än 30 så skickas två olika meddelanden (”byte_to_send”) ut.
”byte_to_send” som är 8 bitar manchesterkodas i manchester_encode() och vi får ”byte_to_send_encoded_temp” som är 16 bitar och är det som egentligen sänds ut av sändaren i interruptrutinen.

Problemet är som följer: När jag i main() sätter ett värde på ”byte_to_send_encoded_temp” direkt, utan att använda ”byte_to_send” + manchester_encode(), så funkar allt prima. Jag får en stabil bild av meddelandet på oscilloskopet. MEN, när jag använder ”byte_to_send” + manchester_encode(), så blir det inte alls bra. Bilden/bitarna på oscilloskopet ändrar sig med jämna mellanrum, precis som om att jag ändrar på ”byte_to_send_encoded_temp” eller något. Startbitarna sänds bra hela tiden, men inte ”byte_to_send_encoded_temp”.

Hmm. Om någon har en ide av vad det är som spökar, så vore jag tacksam för förslag. Kan vara vad som helst. Något som kanske är självklart, men som jag inte har tänkt på.

/ Jonas

Hela koden:

Kod: Markera allt

void setup_multitasking(void);
void interrupt(void);
void PWMInit(void);
void LCDInit(void);
void manchester_encode(void);

volatile enum State {StartSequence, Data, StopBit};
volatile enum State nextState = StartSequence;
volatile char counter444us = 0;
volatile char byte_to_send;
volatile signed char counter_in_transmitFkn = 3;
volatile char startBits = 0b1010;
volatile int byte_to_send_encoded_temp = 0x0000;
volatile unsigned int AD_value;
//(c)Shane Tolmie, http://www.microchipc.com/, distribute freely for non commercial use on the condition that you include this web link somewhere in your document.
//*****
//multitasking system – handle multiple tasks with one microprocessor
#define TRUE 1
#define FALSE 0
//task counters used to tell when a task is ready to be executed
unsigned char task0_counter=0;

//Note: every variable referenced in both interrupt and main() must be declared volatile. You have been warned!
//this enables/disables a task
volatile unsigned char task0_enable=TRUE;    // We set this to TRUE when we want to send something.


volatile unsigned char transmiter_is_busy = TRUE;
volatile unsigned char counter_30ms_isReady = TRUE;

void setup_multitasking(void){
    /*We want to wait 2248 clock cycles, or 889us @ 10MHz (instructions are 1/4 speed of clock).  Timer 1 interrupts when it gets to 0xFFFF or 65535.
    #define  TICKS_BETWEEN_INTERRUPTS      2248         // Gives 889us
    #define  INTERRUPT_OVERHEAD            19
    #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
    #define TMR1RESET_HIGH TMR1RESET >> 8
    #define TMR1RESET_LOW TMR1RESET & 0xFF
    
    T1CON.TMR1CS = 0;          // timer mode
    T1CON.T1CKPS0 = 0;
    T1CON.T1CKPS1 = 0;         // prescaler

    T1CON.TMR1ON = 0;
    TMR1H=TMR1RESET_HIGH;
    TMR1L=TMR1RESET_LOW;
    T1CON.TMR1ON = 1;
    PIR1.TMR1IF = 0;            // clear TMR1IF
    PIE1.TMR1IE  =   1;         // enable interrupts
    INTCON.PEIE=1;            // pheripheral enable interrupt. Uncomment this and PORTD.F3 no longer blink.
    INTCON.GIE=1;            // global enable interrupt
    //  INTCON = 0b11000000;        // GIE PEIE TMR0IE INTE RBIE TMR0IF INTF RBIF
}

void LCDInit(void){
  LCD_Init(&PORTB);         // Initialize LCD connected to PORTB
  LCD_Config(&PORTB, 2, 3, 1, 7, 6, 5, 4);
  LCD_Cmd(LCD_CLEAR);       // Clear display
  LCD_Cmd(LCD_CURSOR_OFF);  // Turn cursor off
}

void PWMInit(void){
    TRISC = 0;             // CCP1 (PortC.2 = Output)
    PR2 = 65;                 // Set PWM Period for approximately 38KHz (37.9KHz)
    CCPR1L = 33;               // Set PWM Duty-Cycle to 50%
    CCP1CON = 0b00001100;     // Mode select = PWM  + 2bit of dutycucle.
    T2CON = 0b00000100;       // Timer2 ON + 1:1 prescale
}

// Inparameter: byte_to_send
// Outparameter: byte_to_send_encoded_temp
// Denna tar 565.6us
void manchester_encode(void){
    char i;
    char bit;
    char temp1;
    //  volatile int temp2;       // volatile så att jag kan se var i watch wimdow. annars så optimerar komplilatorn bort den

    for(i=0; i<8; i++){
        // picks out bit nr. i from byte_to_send.
        temp1 = (0b1<<i);
        bit = temp1 & byte_to_send;
        bit = bit >> i;
        if(bit==1){
            byte_to_send_encoded_temp = (0b01<<i*2) | byte_to_send_encoded_temp;
        }
        if(bit==0){
            byte_to_send_encoded_temp = (0b10<<i*2) | byte_to_send_encoded_temp;
        }
    }
    byte_to_send_encoded_temp = ~byte_to_send_encoded_temp;  //inverterar alla bitar för att tramsmit() vill ha det så.
                                                             // (TRSISC=1 stänger av PWM o ger därmed en nolla.
}

void interrupt(void){
    //one tick every 889us at 10Mhz
    if(PIR1.TMR1IF == 1){
        INTCON.T0IF = 0;      // reset flag

        //set up timer 1 again to interrupt 899us in future
        PIR1.TMR1IF = 0;
        T1CON.TMR1ON = 0;
        TMR1H=TMR1RESET_HIGH;
        TMR1L=TMR1RESET_LOW;
        T1CON.TMR1ON = 1;
//....................... task0 is the transmitter .............................
        task0_counter++;   //counts times we enter interrupt. zeros at the end of transmission.
        
        // if(time >= 30ms since we last transmitted a byte) then we can transmitt a new byte.
        if(task0_counter >= 33){
            counter_30ms_isReady = TRUE;  // i.e we can send a new message after 30ms (reciver needs this "cool time").
        }

        if(task0_enable==TRUE && counter_30ms_isReady == TRUE){

            transmiter_is_busy = TRUE;  // this tells main() that transmitter is transmitting(we can not accept new byte)
            switch(nextState){
                case StartSequence:
                    TRISC.F2 = startBits >> counter_in_transmitFkn; //TRISC.F2 = 1    stänger av PWM, dvs vi får en nolla.
                    counter_in_transmitFkn--;
                    if(counter_in_transmitFkn < 0){
                        nextState = Data;
                        counter_in_transmitFkn = 15;
                    }
                break;
                
                case Data:
                    TRISC.F2 = byte_to_send_encoded_temp >> counter_in_transmitFkn;
                    counter_in_transmitFkn--;
                    if(counter_in_transmitFkn < 0){
                        nextState = StopBit;
                    }
                break;
                
                // Sends one sampel of zero, so that the reciever end gets right.
                // OBS! not one bit i.e 01 or 10 is send, only one "sampel" 0!.
                case StopBit:
                    TRISC.F2 = 1;  //stänger av PWM, dvs vi får en nolla.

                    //we are finsihed we our transmission. The next byte cannot
                    //be sent until approx 30ms has passed.
                    //when task0_counter=33 approx. 30ms have passed and counter_30ms_isReady = TRUE.
                    counter_30ms_isReady = FALSE;
                    task0_counter=0;

                    transmiter_is_busy = FALSE;
                    nextState = StartSequence;
                    counter_in_transmitFkn = 3;
                break;
            }

        }
//..........................task0 end...........................................
    } // if(PIR1.TMR1IF == 1){
} //interrupt routine

void main(void){
    char textInt[7];
    char textChar[4];
    volatile char value255Scaled;
    char old_value255Scaled;
    TRISC = 0;               //outputs. PWM here
    TRISD = 0;           //outputs
    LCDInit();
    PWMInit();
    setup_multitasking();
    
    while(1) {
        AD_value = ADC_Read(0);   // Get results of AD conversion. A value from 0 to ca 1023.

        // Scale our AD-result to 0 to 255 values, which confines within one byte.
        value255Scaled = (AD_value * 255)/1023;
        ByteToStr(value255Scaled, textChar);
        LCD_Out(2,1, textChar);

        // we can accept new byte if transmitter not busy.
        if(transmiter_is_busy == FALSE){
            task0_enable = FALSE;

            if(value255Scaled > 30){
//                byte_to_send_encoded_temp = ~0b1010101010101010;  //allt funkar när man kör denna direkt,
                                                                    //utan att köra manchester_encode().
                byte_to_send = 0b00000000;
                manchester_encode();
            }
            if(value255Scaled < 30){
//                byte_to_send_encoded_temp = ~0b0110011001100110;
                byte_to_send = 0b10101010;
                manchester_encode();
            }
            task0_enable = TRUE;
        }
    }
}

Användarvisningsbild
Micke_s
EF Sponsor
Inlägg: 6741
Blev medlem: 15 december 2005, 21:31:34
Ort: Malmö

Inlägg av Micke_s »

Vilken jobbig bred kod.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Det är inte så att manchester_encode() ändrar ett meddelandet samtidigt
som samma meddelande håller på att sändas?

För övrigt tror jag inte någon tänker kolla så det mycket kod.
Skala ner det till ett litet "test-case" som tydligt visar ditt problem...
jonte_s
Inlägg: 88
Blev medlem: 11 februari 2006, 17:13:48
Ort: Stockholm

Inlägg av jonte_s »

JIHO!!!
Jag hitta felet. Jag har fan kollat igenom manchester_encode() sjuttioelva gånger, men inte hittat något. Har hela tiden trott att det var där felet låg. Så efter att Sodjan också trodde det, så nog fan hitta jag felet. OMG!!!

Felet låg iof inte att jag ändrar ”byte_to_send_encoded_temp” under själva sändningen (för det ser ”transmiter_is_busy = TRUE” till), utan mellan sändningarna...
Det jag la till var att jag nollställer ”byte_to_send_encoded_temp” innan for-loopen i manchester_encode(), för annars blir OR:ningarna fel...

Micke_s:
Menar du att tabbarna är för breda, eller att kommentarerna blir lite breda?
Tips på någon bra "mall" på c-kod?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Ah, där ser man... :-)

När det gäller koden så är det mest att vissa kommentarer drar iväg
och raderan bryts lite hur som helst...
Skriv svar