A scripting environment used to define precise output/input temporal relationships.

Dependencies:   SMARTWAV mbed HelloWorld

Dependents:   perturbRoom_legacy

Fork of HelloWorld by Simon Ford

main.cpp

Committer:
mkarlsso
Date:
2014-07-08
Revision:
2:298679fff37c
Parent:
0:fb6bbc10ffa0
Child:
3:ae33b7f5a7c1

File content as of revision 2:298679fff37c:

#include "mbed.h"
#include <stdint.h>
#include "behave.h"
#include <string.h>
#include <sstream>
#include "SMARTWAV.h"


uint32_t timeKeeper; //the main clock (updated every ms) 
bool resetTimer = false; //if true, the clock is reset 
bool clockSlave = false; //slave mode
bool changeToSlave = false;
bool changeToStandAlone = false;

//static char buf1[0x2000] __attribute__((section("AHBSRAM0")));
__attribute((section("AHBSRAM0"),aligned)) outputStream textDisplay(512);
__attribute((section("AHBSRAM0"),aligned)) char buffer[128];


__attribute((section("AHBSRAM1"),aligned)) event eventBlock[NUMEVENTS];

__attribute((section("AHBSRAM1"),aligned)) condition conditionBlock[NUMCONDITIONS];

__attribute((section("AHBSRAM1"),aligned)) intCompare intCompareBlock[NUMINTCOMPARE];

__attribute((section("AHBSRAM0"),aligned)) action actionBlock[NUMACTIONS];

__attribute((section("AHBSRAM0"),aligned)) portMessage portMessageBlock[NUMPORTMESSAGES];

//__attribute((section("AHBSRAM1"),aligned)) intVariable intVariableBlock[10];

__attribute((section("AHBSRAM0"),aligned)) intOperation intOperationBlock[NUMINTOPERATIONS];

__attribute((section("AHBSRAM0"),aligned)) displayAction displayActionBlock[NUMDISPLAYACTIONS];



Ticker clockBroadCast; //timer used when sending out timestamps on a GPIO
uint32_t currentBroadcastTime;
int currentBroadcastBit = 0;
bool broadcastHigh = false;

int currentDIOstate[2] = {0,0}; //the first number is a bit-wise representaion of the digital inputs, the 2nd is for the outputs 
bool digitalInChanged = false;
bool digitalOutChanged = false;
bool broadCastStateChanges = true;
bool textStreaming = true;
uint32_t changeTime;

LocalFileSystem local("local");  

digitalPort* portVector[NUMPORTS+1]; //create the digital ports

float brightness = 0.0;

//Define the digial ports

//Pins for clock syncing
InterruptIn clockResetInt(p5);
DigitalOut clockOutSync(p6);
DigitalOut clockOutSignal(p7);
InterruptIn clockExternalIncrement(p8);


//Pins for digital ports.  Each port has 1 out and 1 in
//DigitalOut out1(LED1); //route to LED for debugging
DigitalOut out1(p11);
DigitalIn in1(p12);
InterruptIn int1(p12);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port1(&out1, &in1);

DigitalOut out2(p13);
DigitalIn in2(p14);
InterruptIn int2(p14);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port2(&out2, &in2);


DigitalOut out3(p15);
DigitalIn in3(p16);
InterruptIn int3(p16);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port3(&out3, &in3);

DigitalOut out4(p18);
DigitalIn in4(p17);
InterruptIn int4(p17);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port4(&out4, &in4);

DigitalOut out5(p21);
DigitalIn in5(p22);
InterruptIn int5(p22);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port5(&out5, &in5);

DigitalOut out6(p23);
DigitalIn in6(p24);
InterruptIn int6(p24);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port6(&out6, &in6);

DigitalOut out7(p25);
DigitalIn in7(p26);
InterruptIn int7(p26);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port7(&out7, &in7);

DigitalOut out8(p29);
DigitalIn in8(p30);
InterruptIn int8(p30);
__attribute((section("AHBSRAM0"),aligned)) digitalPort port8(&out8, &in8);
//Serial communication
Serial pc(USBTX, USBRX); // tx, rx

//Main event queue
eventQueue mainQueue(portVector, &timeKeeper);

//The script parser 
scriptStream parser(&pc, portVector, NUMPORTS, &mainQueue);

//The sound output uses a SmartWav device and their simple serial library
SMARTWAV sWav(p9,p10,p19);    //(TX,RX,Reset);

//Erases the input buffer for serial input
void eraseBuffer(char* buffer,int numToErase) {
    for (int i = 0; i < numToErase; i++) {
        buffer[i] = NULL;
    }
}


//Called by clockBroadCast timer to output a 32-bit timestamp.  When the timestmap has been
//sent, the function is detached from the timer.
void broadcastNextBit() {
       
    if (currentBroadcastBit < 32) {
        broadcastHigh = !broadcastHigh; //flip the sync signal
        if (broadcastHigh) {
            clockOutSync = 1;
            clockOutSignal = (currentBroadcastTime & ( 1 << currentBroadcastBit)) >> currentBroadcastBit;
            
        } else {
            clockOutSync = 0;
            clockOutSignal = 0;
            currentBroadcastBit++;
        }
    }
}


//intiatiation of timer2 (specific to the LPC17xx chip). This is used in 
//standalone mode to increment the clock every ms.
//we use lower-lever code here to get better control over the timer if we reset it
void timer0_init(void)
{
    //LPC_SC->PCLKSEL1 &= (3 << 12); //mask
    //LPC_SC->PCLKSEL1 |= (1 << 12); //sets it to 1*SystemCoreClock - table 42 (page 57 in user manual)
    
    //LPC_SC->PCLKSEL0 &= (3 << 3); //mask
    //LPC_SC->PCLKSEL0 |= (1 << 3); //sets it to 1*SystemCoreClock - table 42 (page 57 in user manual)
    LPC_SC->PCONP |=1<1;            //timer0 power on
    LPC_TIM0->MR0 = 23980;        //1 msec
    //LPC_TIM0->PR  = (SystemCoreClock / 1000000); //microsecond steps
    //LPC_TIM0->MR0 = 1000;        //100 msec
    //LPC_TIM0->MR0  = (SystemCoreClock / 1000000); //microsecond steps
    LPC_TIM0->MCR = 3;              //interrupt and reset control
                                    //3 = Interrupt & reset timer0 on match
                                    //1 = Interrupt only, no reset of timer0
    NVIC_EnableIRQ(TIMER0_IRQn);    //enable timer0 interrupt
    LPC_TIM0->TCR = 1;              //enable Timer0
    
    
    /*
    LPC_SC->PCONP |= (0x1<<22);     // turn on power for timer 2
    LPC_TIM2->TCR = 0x02;           // reset timer
    LPC_TIM2->PR  = (SystemCoreClock / 1000000); //microsecond steps
    LPC_TIM2->MR0 = 1000;            // 1000 microsecond interval interrupts
    LPC_TIM2->IR  = 0x3f;           // reset all interrrupts
    LPC_TIM2->MCR = 0x03;           // reset timer on match and generate interrupt (MR0)
    LPC_TIM2->TCR = 0x01;           // start timer
    
    NVIC_EnableIRQ(TIMER2_IRQn); // Enable the interrupt
    */
    //pc.printf("Done timer_init\n\r");
}

//This is the callback for timer2
extern "C" void TIMER0_IRQHandler (void) {

    if((LPC_TIM0->IR & 0x01) == 0x01) {  // if MR0 interrupt, proceed
    
        LPC_TIM0->IR |= 1 << 0;         // Clear MR0 interrupt flag
        timeKeeper++;
        
        if (resetTimer) {
            timeKeeper = 0;
            resetTimer = false; 
        }
       
        if (currentBroadcastBit > 31) {
            clockBroadCast.detach();
            currentBroadcastBit = 0;
        }
    
        //Every second, we broadcast out the current time 
        if ((timeKeeper % 1000) == 0) {
            currentBroadcastTime = timeKeeper;
            clockOutSync = 1;
        
            currentBroadcastBit = 0;
     
            clockOutSignal = (currentBroadcastTime & ( 1 << currentBroadcastBit)) >> currentBroadcastBit;
            broadcastHigh = true;
            clockBroadCast.attach_us(&broadcastNextBit, 1000);
        }               
    }
}


//In slave mode, the clock is updated with an external trigger every ms.  No need for
//100us resolution.
void callback_clockExternalIncrement(void) {
    
    timeKeeper++;
       
    if (resetTimer) {
        timeKeeper = 0;
        resetTimer = false; 
    }
      
    if (currentBroadcastBit > 31) {
        clockBroadCast.detach();
        currentBroadcastBit = 0;
    }
    
    //Every second, we broadcast out the current time 
    if ((timeKeeper % 1000) == 0) {
        currentBroadcastTime = timeKeeper;
        clockOutSync = 1;
        
        currentBroadcastBit = 0;
     
        clockOutSignal = (currentBroadcastTime & ( 1 << currentBroadcastBit)) >> currentBroadcastBit;
        broadcastHigh = true;
        clockBroadCast.attach_us(&broadcastNextBit, 1000);
    }
}

//Every digital port's in pin has a hardware interrupt.  We use a callback stub for each port
//that routes the int_callback.
void int_callback(int portNum, int direction) {
    portVector[portNum]->addStateChange(direction, timeKeeper);   
}

//Callback stubs
void callback_port1_rise(void) { int_callback(1, 1); }
void callback_port1_fall(void) { int_callback(1, 0); }
void callback_port2_rise(void) { int_callback(2, 1); }
void callback_port2_fall(void) { int_callback(2, 0); }
void callback_port3_rise(void) { int_callback(3, 1); }
void callback_port3_fall(void) { int_callback(3, 0); }
void callback_port4_rise(void) { int_callback(4, 1); }
void callback_port4_fall(void) { int_callback(4, 0); }
void callback_port5_rise(void) { int_callback(5, 1); }
void callback_port5_fall(void) { int_callback(5, 0); }
void callback_port6_rise(void) { int_callback(6, 1); }
void callback_port6_fall(void) { int_callback(6, 0); }
void callback_port7_rise(void) { int_callback(7, 1); }
void callback_port7_fall(void) { int_callback(7, 0); }
void callback_port8_rise(void) { int_callback(8, 1); }
void callback_port8_fall(void) { int_callback(8, 0); }

//This function is attached to an interrupt pin for external clock reset
void callback_clockReset(void) {
    if (timeKeeper > 100) {
        LPC_TIM2->TCR = 0x02;    // reset timer
        timeKeeper = 0;
        pc.printf("%d Clock reset\r\n", timeKeeper);
    }      
}


int main() {
    timeKeeper = 0; //set main clock to 0;
    sWav.reset(); 
    pc.baud(115200);
    //pc.baud(9600);
   
    for (int i = 0; i < 9; i++) {
        portVector[i] = NULL;
    }
    //We keep portVector 1-based to eliminate confusion
    portVector[1] = &port1;
    portVector[2] = &port2;
    portVector[3] = &port3;
    portVector[4] = &port4;
    portVector[5] = &port5;
    portVector[6] = &port6;
    portVector[7] = &port7;
    portVector[8] = &port8;
   
    //Callback to update the main clock 
    //timeTick1.attach_us(&incrementTime, 100);
    
    timer0_init();
    
    
    //Set up callbacks for the port interrupts   
    int1.rise(&callback_port1_rise);
    int1.fall(&callback_port1_fall);
    int2.rise(&callback_port2_rise);
    int2.fall(&callback_port2_fall);
    int3.rise(&callback_port3_rise);
    int3.fall(&callback_port3_fall);
    int4.rise(&callback_port4_rise);
    int4.fall(&callback_port4_fall);
    int5.rise(&callback_port5_rise);
    int5.fall(&callback_port5_fall);
    int6.rise(&callback_port6_rise);
    int6.fall(&callback_port6_fall);
    int7.rise(&callback_port7_rise);
    int7.fall(&callback_port7_fall);
    int8.rise(&callback_port8_rise);
    int8.fall(&callback_port8_fall);
    
    clockResetInt.rise(&callback_clockReset);
    clockResetInt.mode(PullDown);
    
    clockExternalIncrement.mode(PullDown);
      
    //The inputs are set for pull-up mode (might need to change this)
    in1.mode(PullDown);
    in2.mode(PullDown);
    in3.mode(PullDown);
    in4.mode(PullDown);
    in5.mode(PullDown);
    in6.mode(PullDown);
    in7.mode(PullDown);
    in8.mode(PullDown);
     
    //Set up input buffer for the serial port
    //char buffer[128];
    int bufferPos = 0;
    eraseBuffer(buffer,128);
    
    ostringstream timeConvert;   // stream used for the conversion
    ostringstream stateConvert; 
    char junkChar;
    int tmpChar;
    
    while (pc.readable()) {
        junkChar = pc.getc();
    }   
         
    FILE *fp = fopen("/local/STARTUP.TXT", "r");
    if (fp != NULL) {
        pc.printf("Executing startup script...\r\n");  
        do { 
            tmpChar = fgetc(fp);
            if ((tmpChar >= 32) && (tmpChar <= 126)) {
                buffer[bufferPos] = tmpChar;
                bufferPos++;
            }
            if ((tmpChar == 13) || (tmpChar == 10)) { //carrriage return
                parser.addLineToCurrentBlock(buffer);                          
                bufferPos = 0;
                eraseBuffer(buffer,128);             
            }            
            //pc.putc(tmpChar);
        } while (tmpChar != EOF);
        
        buffer[bufferPos] = 59;
        parser.addLineToCurrentBlock(buffer);
        eraseBuffer(buffer,128);  
        fclose(fp);
    } else {
        pc.printf("No startup script found.\r\n"); 
    }
    
    //main loop
    while(1) {
       //check the main event queue to see if anything needs to be done
       mainQueue.check();
       
       //check if anything has been written to the serial input
       if (pc.readable()) {
                
            buffer[bufferPos] = pc.getc();
            bufferPos++;
            
            //'Return' key pressed
            if ((buffer[bufferPos-1] == 13) || (buffer[bufferPos-1] == 10)) {
                //pc.printf("\r\n");
                buffer[bufferPos-1] = '\0';
                parser.addLineToCurrentBlock(buffer);              
                bufferPos = 0;
                eraseBuffer(buffer,128);
                
            } else {
                //pc.putc(buffer[bufferPos-1]);
                //Backspace was pressed
                if ((buffer[bufferPos-1] == 8) && (bufferPos > 0)) {                              
                    bufferPos = bufferPos-2;                   
                } 
            }
       }
       
      // __disable_irq();
       
       
       //Check all the digital ports to see if anything has changed. In the update routine, the port's
       //script callbacks are called if the port was triggered 
       digitalInChanged = false;
       digitalOutChanged = false;
       changeTime = timeKeeper;
       for (int i = 0; i < NUMPORTS; i++) {
             if (portVector[i+1]->update()) {
                digitalInChanged = true;
                changeTime = min(changeTime,portVector[i+1]->lastChangeTime);
                
                //The input state of all the ports in condensed into one number (each bit contains the info)
                if (portVector[i+1]->getLastChangeState() == 1) {
                    currentDIOstate[0] = currentDIOstate[0] | (1 << i);
                } else {
                    currentDIOstate[0] = currentDIOstate[0] & (255^(1 << i));
                }
             }
             if (portVector[i+1]->outStateChanged) {
                digitalOutChanged = true;
                changeTime = min(changeTime,portVector[i+1]->lastOutChangeTime);
                //The out state of all the ports in condensed into one number (each bit contains the info)
                if (portVector[i+1]->outState == 1) {
                    currentDIOstate[1] = currentDIOstate[1] | (1 << i);
                } else {
                    currentDIOstate[1] = currentDIOstate[1] & (255^(1 << i));
                }
                portVector[i+1]->outStateChanged = false;
             }
       }
           
       //If anything changed, we write the new values to the serial port (this can be turned off 
       //with broadCastStateChanges)
       if ( (digitalInChanged||digitalOutChanged) && broadCastStateChanges) {
            timeConvert << changeTime; //broadcast the earliest timestamp when a change occured
            //stateConvert << currentDIOstate[0] << " " << currentDIOstate[1];
            stateConvert << currentDIOstate[0] << " " << currentDIOstate[1] << "       ";
            textDisplay.send(timeConvert.str() + " " + stateConvert.str() + "\r\n");
            timeConvert.clear();
            timeConvert.seekp(0);
            stateConvert.clear();
            stateConvert.seekp(0);
            digitalInChanged = false;
            digitalOutChanged = false;
       }
       
       //We use a buffer to send text via the serial port.  For every loop
       //in the main loop, we send one character if there is enything to send.
       //This way, outputting text to serial does not hold up other time-sensitive
       //things in the event queue
       if ((textDisplay.unsentData) && (textStreaming)) {
            pc.printf("%c", textDisplay.getNextChar());
       }
       
       //Here is how we toggle between standalone and slave mode for the clock updating.
       if (changeToSlave) {
            //timeTick1.detach();
            NVIC_DisableIRQ(TIMER2_IRQn); // Disable the interrupt
            clockExternalIncrement.rise(&callback_clockExternalIncrement);
            clockSlave = true;
            changeToSlave = false;
            changeToStandAlone = false;
       } else if (changeToStandAlone) {
            //timeTick1.attach_us(&incrementTime, 100);
            timer0_init();         
            clockExternalIncrement.rise(NULL); //remove the callback to the external interrupt
            clockSlave = false;
            changeToSlave = false;
            changeToStandAlone = false;
       }
                    
       //__enable_irq();
       
    }
}