A scripting environment used to define precise output/input temporal relationships.
Dependencies: SMARTWAV mbed HelloWorld
Dependents: perturbRoom_legacy
Fork of HelloWorld by
Diff: main.cpp
- Revision:
- 2:298679fff37c
- Parent:
- 0:fb6bbc10ffa0
- Child:
- 3:ae33b7f5a7c1
--- a/main.cpp Sun Jan 01 20:57:57 2012 +0000 +++ b/main.cpp Tue Jul 08 21:51:16 2014 +0000 @@ -1,12 +1,479 @@ #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; + } +} + -DigitalOut myled(LED1); +//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) { - myled = 1; - wait(0.2); - myled = 0; - wait(0.2); + //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(); + } }