final
Dependencies: TextLCD mbed-rtos mbed
Fork of 541-pacemaker by
main.cpp
- Committer:
- ems316
- Date:
- 2016-12-12
- Revision:
- 5:b506b5bdeee7
- Parent:
- 4:a9c37c60425c
File content as of revision 5:b506b5bdeee7:
/* @Author: Grayson Honan, Phil Perilstein, Terry Fang, Eric Stahl @Course: CIS 541 @Due Date: 12 Dec 2016 @Assignment: Pacemaker Project - Pacemaker Component @Description: This code is representative of the functionality of a DDD pacemaker. The code was generated from an UPAAL model that describes the basic timing functionality of a pacemaker and the operation of system peripherals (lcd screen, alarm, keyboard). Main timing threads: PaceSense - handles triggering pace output events via timeouts PaceSignal - filters and accepts heart signal input events by synchronizing with interupt ins. Main Peripheral Threads: displayThread - displays current bpm and then resets the bpm and determines if abnormal bpm alarmThread - synchronizes with displayThread if abnormal bpm and displays warning message to lcd screen ledThread - synchronizes with PaceSense or PaceSignal on a Vsense, Asense, Vpace, Apace Rx_interrupt - interrupt that accepts keyboard input and filters for valid keyboard input. Synchrnoizes with the thread that the keyboard input is destined for via a Queue. Pace Mode Threads: Main - initializes to a Normal mode Pacemaker with an observation interval of 10 seconds. Synchronizes with the Rx_interrupt when valid keyboard input is determined. The main thread acts as the Choose_PaceMode automata. manualmode - disables pace events from the pacemaker & allows for user input to manually pace an atrial or ventrical event. Reference: https://developer.mbed.org/handbook/CMSIS-RTOS Run Instructions: Whenever I ran the program, I found I needed to first run screen to open up the fd pointing to the usb. I would then kill the screen process and run the ./start_game script after uncommenting the port selection in the script. */ #include "mbed.h" #include "rtos.h" #include "TextLCD.h" #include <stdio.h> // Input/Output declarations InterruptIn vsignal(p7); InterruptIn asignal(p8); DigitalOut Vpace(p5); DigitalOut Apace(p6); DigitalOut asense_led(LED1); DigitalOut vsense_led(LED2); DigitalOut apace_led(LED3); DigitalOut vpace_led(LED4); // thread definitions osThreadId signalTid; osThreadId senseTid; osThreadId displayTid; osThreadId pacemodeTid; osThreadId alarmTid; osThreadId ledTid; //peripheral API declarations TextLCD lcd(p15, p16, p17, p18, p19, p20, TextLCD::LCD16x2); RawSerial pc(USBTX, USBRX); //timing declarations Timer vClock; Timer aClock; Timer arpClock; //normal mode timing constants double LRI = 1000; double URI = 700; double VRP = 200; double ARP = 50; double AVI = 150; double PVARP = 300; double ratio; int wait_period = 10; double observation_interval = 10000; // In miliseconds int upperBound; //for mode changes int lowerBound; //for mode changes double heart_beats = 0; // Heart-Beats (sensed or paced) since the last observation interval Mutex hr_mutex; //hr_mutex.lock()/unlock() //message passing queues Queue<char,256> mode_q; Queue<char,256> signal_q; Queue<char,256> obsint_q; //keyboard interrupt variables volatile char c; volatile int mm = 0; volatile int om = 0; //flag to disable automatic pacing int mm_flag = 0; //init the ventrical timing values void initialize_intervals() { LRI = 1000; URI = 700; } //interrupt for keyboard input. Filters valid input. void Rx_interrupt() { while(pc.readable()) { c = pc.getc(); //synchronize mode switch thread for manual mode if(c == 'm' && om != 1) { mode_q.put((char*)c); mm = 1; //synchronize mode switch thread for normal, excercise or sleep mode } else if(c == 'n' || c == 'e' || c == 's' && om != 1) { mode_q.put((char*)c); mm = 0; //if manual mode, synchronize with manual mode thread for a or v signal } else if((c == 'a' || c == 'v') && mm) { signal_q.put((char*)c); //start accepting input to update the observation interval. Disable mode switching. } else if(c == 'o' && om != 1) { mode_q.put((char*)c); om = 1; //end receiving input to update observation interval. Enable mode switching. } else if (c == '\r' && om) { obsint_q.put((char*)c); om = 0; //updating observation interval, filter valid input. } else if ((int)c > 47 && (int)c < 58 && om) { obsint_q.put((char*)c); } } } // Function to toggle the LEDs 1,2,3,4 void ledThread(void const *args) { while (1) { //wait for synchronization for Apace, Vpace, Asense, Vsense osEvent ext_signal = osSignalWait(0, osWaitForever); int evt = ext_signal.value.signals; if (evt == 0xA) { //asense asense_led = 1; Thread::wait(wait_period); asense_led = 0; } else if (evt == 0xB) { //vsense vsense_led = 1; Thread::wait(wait_period); vsense_led = 0; } else if (evt == 0xC) { //apace apace_led = 1; Thread::wait(wait_period); apace_led = 0; } else if (evt == 0xD) { //vpace vpace_led = 1; Thread::wait(wait_period); vpace_led = 0; } } } //Synchronized with displayThread if alarmThread should alarm void alarmThread(void const *args) { while (1) { osEvent ext_signal = osSignalWait(0, osWaitForever); int evt = ext_signal.value.signals; if (evt == 0xb) { //upper bound violated lcd.printf("%s", "\nALARM HIGH"); } else if (evt == 0xc) { //lower bound violated lcd.printf("%s", "\nALARM LOW"); } } } //Thread for displaying bpm & alerts void displayThread(void const *args) { while (1) { Thread::wait(observation_interval); lcd.cls(); hr_mutex.lock(); //acquire lock because changing bpm double hr = (heart_beats*60) / (observation_interval / 1000); //calculate bpm heart_beats = 0; //reset bpm hr_mutex.unlock(); lcd.printf("%s%d%s","HR: ", (int)hr, " bpm"); //display bpm if (hr > upperBound) { //synchronize if upperBound violated osSignalSet(alarmTid, 0xb); } else if (hr < lowerBound) { //synchronize if lowerBound violated osSignalSet(alarmTid, 0xc); } } } // Incoming atrial signal from the heart void asignal_irq() { osSignalSet(signalTid, 0x1); //synchronize with pacesignal thread } // Incoming ventrical signal from the heart void vsignal_irq() { osSignalSet(signalTid, 0x2); //synchronize with pacesignal thread } //Main timing thread for filtering Asignal and Vsignal from heart and //generating Asense and Vsense to synchronize with the PaceSense thread. void PaceSignal(void const *args) { int pFlag1 = 0; //flag that allows us to move to the second UPPAAL state in PaceSignal int pFlag2 = 0; //flag that allows us to move to the first UPPAAL state in PaceSignal vClock.start(); aClock.start(); arpClock.start(); while(1) { while (!pFlag1) { osEvent ext_signal = osSignalWait(0, osWaitForever); int evt = ext_signal.value.signals; if (evt == 0x1 && vClock.read_ms() >= PVARP) { //aSense osSignalSet(senseTid, 0x1); aClock.reset(); arpClock.reset(); pFlag1 = 1; } else if(evt == 0x2 && vClock.read_ms() >= VRP) { //vSense hr_mutex.lock(); osSignalSet(senseTid, 0x2); //syncrhonize with PaceSense thread heart_beats++; //increment bpm vClock.reset(); aClock.reset(); arpClock.reset(); hr_mutex.unlock(); pFlag1 = 1; //progress to state 2 } else if (evt == 0x3) { //aPace pFlag1 = 1; //progress to state 2 } } pFlag1 = 0; while(!pFlag2) { osEvent ext_signal = osSignalWait(0, osWaitForever); int evt = ext_signal.value.signals; if (evt == 0x1 && arpClock.read_ms() >= ARP) { //aSense osSignalSet(senseTid, 0x1); arpClock.reset(); //determine valid consecutive a event } else if(evt == 0x2) { //vSense hr_mutex.lock(); osSignalSet(senseTid, 0x2); heart_beats++; //increment bpm vClock.reset(); aClock.reset(); arpClock.reset(); hr_mutex.unlock(); pFlag2 = 1; //progress to state 1 } else if (evt == 0x4) { //vPace pFlag2 = 1; //progress to state 1 } } pFlag2 = 0; } } void PaceSense(void const *args) { int pFlag1 = 0; //flag that allows us to move to the second UPPAAL state in PaceSense int pFlag2 = 0; //flag that allows us to move to the first UPPAAL state in PaceSense int time_sub = 0; //used to determine timeout for when to pace (this is our invariant) int evt = 0; while(1) { while (!pFlag1) { time_sub = LRI-AVI - vClock.read_ms(); //aPace at LRI-AVI default if (time_sub > 0 && !mm_flag) { osEvent ext_signal = osSignalWait(0, time_sub); //allow pacing evt = ext_signal.value.signals; } else if(mm_flag) { osEvent ext_signal = osSignalWait(0, osWaitForever); //disable pacing evt = ext_signal.value.signals; } else { evt = 0x0; //time_sub is less than 0 } if (evt == 0x0) { //aPace aClock.reset(); arpClock.reset(); Apace = 1; Thread::wait(1); Apace = 0; osSignalSet(signalTid, 0x3); osSignalSet(ledTid, 0xC); pFlag1 = 1; } else if (evt == 0x1) { //aSense osSignalSet(ledTid, 0xA); pFlag1 = 1; } else if(evt == 0x2) { //vSense osSignalSet(ledTid, 0xB); } else if(evt == 0x3) { //manual apace pFlag1 = 1; } } pFlag1 = 0; while(!pFlag2) { //vpace occurs at either URI or vclock + AVI time_sub = (vClock.read_ms() + AVI >= URI) ? AVI - aClock.read_ms() : URI - vClock.read_ms(); if (time_sub > 0 && !mm_flag) { //allow pacing osEvent ext_signal = osSignalWait(0, time_sub); evt = ext_signal.value.signals; } else if(mm_flag) { //disable pacing osEvent ext_signal = osSignalWait(0, osWaitForever); evt = ext_signal.value.signals; } else { evt = 0x0; //time_sub is negative } if (evt == 0x0) { //vPace hr_mutex.lock(); heart_beats++; vClock.reset(); aClock.reset(); arpClock.reset(); Vpace = 1; Thread::wait(1); Vpace = 0; osSignalSet(signalTid, 0x4); hr_mutex.unlock(); osSignalSet(ledTid, 0xD); pFlag2 = 1; } else if (evt == 0x1) { //aSense osSignalSet(ledTid, 0xA); } else if(evt == 0x2) { //vSense osSignalSet(ledTid, 0xB); pFlag2 = 1; } else if (evt == 0x4) { //manual vpace pFlag2 = 1; } } pFlag2 = 0; } } //update timing constraints for mode and reset bpm void normalmode(void const *args) { initialize_intervals(); upperBound = 100; //beats per msecond lowerBound = 40; //beats per msecond //hr_mutex.lock(); //reset bpm heart_beats = 0; hr_mutex.unlock(); vClock.reset(); aClock.reset(); } //update timing constraints for mode and reset bpm void exercisemode(void const *args) { initialize_intervals(); upperBound = 175; //beats per msecond lowerBound = 100; //beats per msecond ratio = (175.00/100.00 + 100.00/40.00) / 2.00; LRI /= ratio; URI /= ratio; //reset bpm hr_mutex.lock(); heart_beats = 0; hr_mutex.unlock(); vClock.reset(); aClock.reset(); } //update timing constraints for mode and reset bpm void sleepmode(void const *args) { initialize_intervals(); upperBound = 60; //beats per msecond lowerBound = 30; //beats per msecond v-v 0.5s ratio = (60.00/100.00 + 30.00/40.00) / 2.00; LRI /= ratio; URI /= ratio; hr_mutex.lock(); //reset bpm heart_beats = 0; hr_mutex.unlock(); vClock.reset(); aClock.reset(); } //handle manual pacing events void m_vpace() { vClock.reset(); aClock.reset(); arpClock.reset(); Vpace = 1; Thread::wait(1); Vpace = 0; osSignalSet(signalTid, 0x4); osSignalSet(senseTid, 0x4); hr_mutex.lock(); heart_beats++; hr_mutex.unlock(); osSignalSet(ledTid, 0xD); } //handle manual pacing events void m_apace() { aClock.reset(); arpClock.reset(); Apace = 1; Thread::wait(1); Apace = 0; osSignalSet(senseTid, 0x3); osSignalSet(signalTid, 0x3); osSignalSet(ledTid, 0xC); } //update timing constraints for mode and handle manual pace events void manualmode(void const *args) { upperBound = 175; //beats per msecond lowerBound = 30; //beats per msecond LRI = 2125; // max V-V (LRI) based on exercise mode URI = 675; // min V-V (URI) based on sleep mode while(1) { osEvent evt = signal_q.get(); if(evt.status == osEventMessage) { if((char)evt.value.p == 'v') { m_vpace(); } else if((char)evt.value.p == 'a') { m_apace(); } } } } //manage user input for updating the observation interval void obsinterval() { char newObsInt[8]; int isChangingObsInt = 1; int i = 0; char key = 'n'; while(isChangingObsInt) { osEvent evt = obsint_q.get(); if(evt.status == osEventMessage) { key = (char)evt.value.p; if(key != '\r' && i < 7 ) { //bound number of integers for overflow newObsInt[i] = key; i++; } else if((key == '\r') && (i > 0)) { //conver input to observatio interval heart_beats = 0; int obsint; newObsInt[i] = '\0'; sscanf(newObsInt, "%d", &obsint); //check bounds if(obsint < 300) { observation_interval = 300.0; } else if (obsint > 10000) { observation_interval = 10000.0; } else { observation_interval = (double)obsint; } isChangingObsInt = 0; } } } } //create thread definitions osThreadDef(PaceSignal, osPriorityNormal, DEFAULT_STACK_SIZE); osThreadDef(PaceSense, osPriorityNormal, DEFAULT_STACK_SIZE); osThreadDef(alarmThread, osPriorityBelowNormal, DEFAULT_STACK_SIZE); osThreadDef(ledThread, osPriorityBelowNormal, DEFAULT_STACK_SIZE); osThreadDef(displayThread, osPriorityLow, DEFAULT_STACK_SIZE); osThreadDef(manualmode, osPriorityNormal, DEFAULT_STACK_SIZE); int main() { //create thread ids alarmTid = osThreadCreate(osThread(alarmThread), NULL); senseTid = osThreadCreate(osThread(PaceSense), NULL); signalTid = osThreadCreate(osThread(PaceSignal), NULL); displayTid = osThreadCreate(osThread(displayThread), NULL); ledTid = osThreadCreate(osThread(ledThread), NULL); //start pacemaker in normal mode normalmode(NULL); //set interrupt ins on signaling inputs vsignal.rise(&vsignal_irq); asignal.rise(&asignal_irq); //clear lcd lcd.cls(); //set interrupt in for serial input pc.attach(&Rx_interrupt, RawSerial::RxIrq); while(true) { //handle mode switching synchronization osEvent evt = mode_q.get(); if(evt.status == osEventMessage) { switch((char)evt.value.p) { case('n'): //normal mode mm_flag = 0; osSignalSet(senseTid, 0x5); osThreadTerminate (pacemodeTid); osThreadTerminate (displayTid); normalmode(NULL); displayTid = osThreadCreate(osThread(displayThread), NULL); break; case('s'): //sleep mode mm_flag = 0; osSignalSet(senseTid, 0x5); osThreadTerminate (pacemodeTid); osThreadTerminate (displayTid); sleepmode(NULL); displayTid = osThreadCreate(osThread(displayThread), NULL); break; case('e'): //excercise mode mm_flag = 0; osSignalSet(senseTid, 0x5); osThreadTerminate (pacemodeTid); osThreadTerminate (displayTid); exercisemode(NULL); displayTid = osThreadCreate(osThread(displayThread), NULL); break; case('m'): //manual mode mm_flag = 1; osSignalSet(senseTid, 0x5); osThreadTerminate (pacemodeTid); pacemodeTid = osThreadCreate(osThread(manualmode), NULL); break; case('o'): //observation interval obsinterval(); osThreadTerminate (displayTid); displayTid = osThreadCreate(osThread(displayThread), NULL); break; } } } }