syncSlave for problem 3

timesync_slave.cpp

Committer:
the729
Date:
2010-12-03
Revision:
0:362932d519c6

File content as of revision 0:362932d519c6:

#include "mbed.h"
#include "timesync.h"
#include "timesync_slave.h"
#include "hdtimeval_math.h"

int32_t clock_sec;
DigitalOut led(LED1);
Serial uart_sync(p28,p27);
Timeout tmr_sync;
Timeout tmr_callback;

hdtimeval_t front_lt;
hdtimeval_t timeq[QUEUE_SIZE];
void (*funcq[QUEUE_SIZE])(void);
uint8_t qfront;
uint8_t qtail;

void (*trigf)(struct timeval *);
hdtimeval_t trigt;
//bool trigactive;

//state_t state;
synctype_t syncbusy;
synctype_t tmp_synctype;
hdtimeval_t txstamp;

coeff_t coeff;

void clock_init();
void tmr1_irq();
void uart_recv();
__INLINE void getHDTime(struct hdtimeval * hdtv);
void sync_req();
void update_data(synctype_t t, void * pkt_p, hdtimeval_t * rxstamp);
void getLocalTime(hdtimeval * gt, hdtimeval * lt);
void getGlobalTime(hdtimeval * lt, hdtimeval * gt);
void runTriggerFunc();

void timesync_init()
{
    wait_ms(100);
    coeff.v1div2.tv_sec = 0;
    coeff.v1div2.ticks = 0;
    coeff.w1div2.tv_sec = 0;
    coeff.w1div2.ticks = 0;
    coeff.beta1.tv_sec = 0;
    coeff.beta1.ticks = 1;
    coeff.beta2.tv_sec = 0;
    coeff.beta2.ticks = 1;
    coeff.size = 0;
    coeff.front = 0;
    trigf = NULL;
    
    clock_init();
    uart_sync.baud(115200);
    uart_sync.attach(&uart_recv, Serial::RxIrq);

    syncbusy = SYNCTYPE_NONE;
    tmp_synctype = SYNCTYPE_64;
    tmr_sync.attach(&sync_req, 0.1);
}
/*
void timesync_poll()
{
    //static last_poll;
    tmp_synctype = SYNCTYPE_24;
    sync_req();
}
*/
void clock_init()
{
    // select PCLK
    LPC_SC->PCLKSEL1 = (LPC_SC->PCLKSEL1 & ~(3<<CLK_TIMER)) | (PCLK_DEVIDER<<CLK_TIMER);
    // power on timer
    LPC_SC->PCONP |= (1<<PCTIM2);
    
    // reset timer
    TMR->TCR = 2;
    TMR->CTCR = 0;
    TMR->PR = 0;
    TMR->TC = 1;
    TMR->MR0 = SystemCoreClock - 1;
    TMR->MCR = 3;
    
    clock_sec = 0;

    // capture on CAP.0        
    TMR->CCR = 0x07;
    LPC_PINCON->PINSEL0 = LPC_PINCON->PINSEL0  | (3<<8);
    
    // setup interrupt
    __enable_irq();
    NVIC_SetVector(TIMER2_IRQn, (uint32_t)&tmr1_irq);
    NVIC_EnableIRQ(TIMER2_IRQn);
    
    NVIC_SetPriority(TIMER2_IRQn, 0);
    NVIC_SetPriority(UART2_IRQn, 1);
    NVIC_SetPriority(TIMER3_IRQn, 2);
    NVIC_SetPriority(UART0_IRQn, 2);
    NVIC_SetPriority(UART3_IRQn, 1);

    // begin freerun
    TMR->TCR = 1;
}

void tmr1_irq()
{
    if (TMR->IR & (1<<4)) {
        if (trigf) {
            trigt.tv_sec = clock_sec;
            if ((TMR->IR & 0x01) && TMR->CR0 < 256)
                trigt.tv_sec = clock_sec+1;
            trigt.ticks = TMR->CR0;
            //runTriggerFunc();
            tmr_callback.attach_us(&runTriggerFunc, 10);
        }
        TMR->IR = (1<<4);
    }
    if (TMR->IR & 0x1) {
        // Overflow!
        clock_sec ++;
        TMR->IR = 0x1;
    }
    if (TMR->IR & 0x2) {
        if (clock_sec == front_lt.tv_sec) {
            hdtimeval_t * nowtv = timeq+qfront;
            funcq[qfront]();
            qfront = (qfront+1)%QUEUE_SIZE;
            while(qfront != qtail 
            && nowtv->tv_sec == timeq[qfront].tv_sec
            && nowtv->ticks == timeq[qfront].ticks) {
                funcq[qfront]();
                qfront = (qfront+1)%QUEUE_SIZE;
            }
            if (qfront == qtail) {
                TMR->MCR &= ~(1<<3);
            } else {
                getLocalTime(&timeq[qfront], &front_lt);
                TMR->MR1 = front_lt.ticks;
            }
        } else if (clock_sec > front_lt.tv_sec) {
            // we missed it!
            qfront = (qfront+1)%QUEUE_SIZE;
            if (qfront == qtail) {
                TMR->MCR &= ~(1<<3);
            } else {
                getLocalTime(&timeq[qfront], &front_lt);
                TMR->MR1 = front_lt.ticks;
            }
        }
        TMR->IR = 0x2;
    }
}

void runTriggerFunc()
{
    timeval_t t;
    getGlobalTime(&trigt, &trigt);
    hdtv_totv(&t, &trigt);
    trigf(&t);
}

void uart_recv()
{
    hdtimeval_t tstamp;
    getHDTime(&tstamp);
    
    //if (!(LPC_UART2->IIR & 0x01) && (LPC_UART2->LSR & 0x01)) {
    if (uart_sync.readable()) {
        sync_pkt64_t pkt64;
        sync_pkt32_t pkt32;
        sync_pkt24_t pkt24;
        char * data = NULL;
        void * pkt_p = NULL;
        uint8_t len = 0;
        
        if (!syncbusy) {
            len = uart_sync.getc();
            return;
        }
        
        switch(syncbusy) {
            case SYNCTYPE_64:
                data = pkt64.raw;
                pkt_p = (void *)&pkt64;
                len = 10;
                break;
            case SYNCTYPE_32:
                data = pkt32.raw;
                pkt_p = (void *)&pkt32;
                len = 6;
                break;
            case SYNCTYPE_24:
                pkt24.no_use = 0;
                pkt_p = (void *)&pkt24;
                data = pkt24.raw;
                len = 5;
                break;
        }
        while(len-->0) {
            //while(!(LPC_UART2->LSR & 0x01));
            *(data++)= uart_sync.getc(); //LPC_UART2->RBR;
        }
        
        update_data(syncbusy, pkt_p, &tstamp);
    }
    syncbusy = SYNCTYPE_NONE;
}

void sync_req()
{
    //if (syncbusy) return;
    __disable_irq();
    txstamp.tv_sec = clock_sec;
    txstamp.ticks = TMR->TC;
    uart_sync.putc(tmp_synctype);
    //LPC_UART2->THR = tmp_synctype;
    __enable_irq();
    syncbusy = tmp_synctype;
}

void update_data(synctype_t t, void * pkt_p, hdtimeval_t * rxstamp)
{
    hdtimeval_t v, w;
    hdtimeval_t master_rxt, master_txt;
    int32_t ticks_diff;
    hdtv_add(&v, &txstamp, rxstamp);
    switch (t) {
        case SYNCTYPE_64: {
            sync_pkt64_t * p = (sync_pkt64_t *)pkt_p;
            master_rxt = p->rx_time;
            master_txt.tv_sec = master_rxt.tv_sec;
            master_txt.ticks = (master_rxt.ticks & 0xFFFF0000) + p->tx_time;
        } break;
        case SYNCTYPE_32: {
            sync_pkt32_t * p = (sync_pkt32_t *)pkt_p;
            hdtimeval_t tmp;
            getGlobalTime(&txstamp, &tmp);
            if (tmp.ticks > MAX_TICK/2) {
                if (p->rx_time > tmp.ticks-MAX_TICK/2) {
                    master_rxt.tv_sec = tmp.tv_sec;
                } else {
                    master_rxt.tv_sec = tmp.tv_sec +1;
                }
            } else {
                if (p->rx_time > tmp.ticks+MAX_TICK/2) {
                    master_rxt.tv_sec = tmp.tv_sec-1;
                } else {
                    master_rxt.tv_sec = tmp.tv_sec;
                }
            }
            master_rxt.ticks = p->rx_time;
            master_txt.tv_sec = master_rxt.tv_sec;
            master_txt.ticks = (master_rxt.ticks & 0xFFFF0000) + p->tx_time;
        } break;
        case SYNCTYPE_24: {
            sync_pkt24_t * p = (sync_pkt24_t *)pkt_p;
            hdtimeval_t tmp;
            getGlobalTime(&txstamp, &tmp);
            p->rx_time >>= 8;
            if (tmp.ticks > (1<<23)) {
                tmp.ticks = tmp.ticks-(1<<23);
                master_rxt.ticks = (tmp.ticks & 0xFF000000) + p->rx_time;
                master_rxt.tv_sec = tmp.tv_sec;
            } else {
                tmp.ticks = tmp.ticks+MAX_TICK-(1<<23);
                master_rxt.ticks = (tmp.ticks & 0xFF000000) + p->rx_time;
                master_rxt.tv_sec = tmp.tv_sec-1;
            }
            if (master_rxt.ticks < tmp.ticks) {
                master_rxt.ticks += 0x1000000;
                if (master_rxt.ticks > MAX_TICK) {
                    master_rxt.ticks &= 0xFFFFFF;
                    master_rxt.tv_sec ++;
                }
            }
            master_txt.tv_sec = master_rxt.tv_sec;
            master_txt.ticks = (master_rxt.ticks & 0xFFFF0000) + p->tx_time;            
        } break;
    }
    //printf("t1,t2: %d,%d, %d,%d \r\n",master_rxt.tv_sec, master_rxt.ticks>>24, master_rxt.ticks & 0xFFFFFF, master_txt.ticks);
    if (master_rxt.ticks > master_txt.ticks) {
        master_txt.ticks += 0x10000;
        if (master_txt.ticks >= MAX_TICK) {
            master_txt.ticks &= 0xFFFF;
            master_txt.tv_sec ++;
        }
    }
    hdtv_add(&w, &master_rxt, &master_txt);

    hdtv_div2(&v, &v);
    hdtv_div2(&w, &w);
    // evaluate performance
    {
        hdtimeval_t tmp;
        getGlobalTime(&v, &tmp);
        hdtv_sub(&tmp, &tmp, &w);
        if (tmp.tv_sec > 10) 
            ticks_diff = 0x7FFFFFFF;
        else {
            ticks_diff = tmp.tv_sec * MAX_TICK + tmp.ticks;
        }
        //printf("perf: %d \r\n", ticks_diff);
    }
    if (coeff.size)
        if (coeff.size<HISTORY_LEN) {
            hdtv_sub(&coeff.beta1, &w, &(coeff.hisw[0]));
            hdtv_sub(&coeff.beta2, &v, &(coeff.hisv[0]));
        } else {
            hdtv_sub(&coeff.beta1, &w, &(coeff.hisw[coeff.front]));
            hdtv_sub(&coeff.beta2, &v, &(coeff.hisv[coeff.front]));
        }
    /*if(coeff.size>=8) {
        hdtimeval_t tmp;
        hdtv_div8(&tmp, &(coeff.hisw[coeff.front]));
        hdtv_sub(&coeff.w1div2, &coeff.w1div2, &tmp);
        hdtv_div8(&tmp, &(coeff.hisv[coeff.front]));
        hdtv_sub(&coeff.v1div2, &coeff.v1div2, &tmp);
        hdtv_div8(&tmp, &w);
        hdtv_add(&coeff.w1div2, &coeff.w1div2, &tmp);
        hdtv_div8(&tmp, &v);
        hdtv_add(&coeff.v1div2, &coeff.v1div2, &tmp);
    } else {*/
        coeff.v1div2 = v;
        coeff.w1div2 = w;
    //}
    coeff.hisw[coeff.front] = w;
    coeff.hisv[coeff.front] = v;
    coeff.front = (coeff.front+1) % HISTORY_LEN;
    if (coeff.size<=HISTORY_LEN) coeff.size++;
    /*if (coeff.size==8) {
        hdtimeval_t tmp;
        coeff.w1div2.tv_sec = 0;
        coeff.w1div2.ticks = 0;
        coeff.v1div2.tv_sec = 0;
        coeff.v1div2.ticks = 0;
        for (uint8_t i=0; i<8; i++) {
            hdtv_div8(&tmp, &(coeff.hisw[i]));
            hdtv_add(&coeff.w1div2, &coeff.w1div2, &tmp);
            hdtv_div8(&tmp, &(coeff.hisv[i]));
            hdtv_add(&coeff.v1div2, &coeff.v1div2, &tmp);
        }
        printf("updated!\r\n");
    }*/
    //printf("w/v: %d, %d \r\n", coeff.w1div2.ticks, coeff.v1div2.ticks);
    if (qfront != qtail) {
        getLocalTime(&timeq[qfront], &front_lt);
        TMR->MR1 = front_lt.ticks;
        TMR->MCR |= (1<<3);
    }
    // schedule next sync
    ticks_diff = abs(ticks_diff);
    if (ticks_diff < 200 && coeff.size>8) {
        tmp_synctype = SYNCTYPE_24;
        tmr_sync.attach(&sync_req, 60);
    } else if (ticks_diff < 500 && coeff.size>4) {
        tmp_synctype = SYNCTYPE_24;
        tmr_sync.attach(&sync_req, 30);
    } else if (ticks_diff < 800 && coeff.size>2) {
        tmp_synctype = SYNCTYPE_24;
        tmr_sync.attach(&sync_req, 20);
    } else if (ticks_diff < 5000) {
        tmp_synctype = SYNCTYPE_32;
        tmr_sync.attach(&sync_req, 10);
    } else {
        tmp_synctype = SYNCTYPE_64;
        tmr_sync.attach(&sync_req, 5);
    }
    led = !led;
}

__INLINE void getHDTime(struct hdtimeval * hdtv)
{
    __disable_irq();
    hdtv->tv_sec = clock_sec;
    hdtv->ticks = TMR->TC;
    __enable_irq();
    return;
}

void getTime(struct timeval * tv)
{
    hdtimeval_t tstamp, gstamp, tstamp1; 
    static hdtimeval_t comp_delay = {0,0};

    getHDTime(&tstamp);
    
    hdtv_add(&gstamp, &tstamp, &comp_delay);
    getGlobalTime(&gstamp, &gstamp);
    
    getHDTime(&tstamp1);
    hdtv_sub(&comp_delay, &tstamp1, &tstamp);
    //printf("delay: %d\r\n", comp_delay.ticks);

    tv->tv_sec = gstamp.tv_sec;
    tv->tv_usec = ((gstamp.ticks+48) >> 5) / 3;
    return; 
}

void getGlobalTime(hdtimeval * lt, hdtimeval * gt)
{
    hdtimeval_t tmp;
    hdtv_sub(&tmp, lt, &coeff.v1div2);
    hdtv_muldiv(&tmp, &tmp, &coeff.beta1, &coeff.beta2);
    hdtv_add(gt, &tmp, &coeff.w1div2);
}

void getLocalTime(hdtimeval * gt, hdtimeval * lt)
{
    hdtimeval_t tmp;
    hdtv_sub(&tmp, gt, &coeff.w1div2);
    hdtv_muldiv(&tmp, &tmp, &coeff.beta2, &coeff.beta1);
    hdtv_add(lt, &tmp, &coeff.v1div2);
}

int enqueue(void (*schedFunc)(void), hdtimeval_t *tv)
{
    uint8_t p = qtail;
    if ((qtail+1)%QUEUE_SIZE == qfront) {
        return 1;
    }
    while(p != qfront) {
        uint8_t prev = (p+QUEUE_SIZE-1)%QUEUE_SIZE;
        if (tv->tv_sec > timeq[prev].tv_sec 
        || (tv->tv_sec == timeq[prev].tv_sec && tv->ticks >= timeq[prev].ticks)) break;
        timeq[p] = timeq[prev];
        funcq[p] = funcq[prev];
        p = prev;
    } 
    timeq[p] = *tv;
    funcq[p] = schedFunc;
    qtail = (qtail+1)%QUEUE_SIZE;
    return 0;
}

int runAtTime(void (*schedFunc)(void), struct timeval *tv)
{
    hdtimeval_t hdtv;
    int ret;
    hdtv.tv_sec = tv->tv_sec;
    hdtv.ticks = tv->tv_usec * 96;
    //getLocalTime(&hdtv, &hdtv);
    ret = enqueue(schedFunc, &hdtv);
    if (qfront != qtail) {
        getLocalTime(&timeq[qfront], &front_lt);
        TMR->MR1 = front_lt.ticks;
        TMR->MCR |= (1<<3);
    }
    return ret;
}


void runAtTrigger(void (*trigFunc)(struct timeval *tv))
{
    trigf = trigFunc;
}