provide one method to add gcode to blocks, reduce code duplication
[clinton/Smoothieware.git] / src / libs / SlowTicker.cpp
index e9aeffb..6961472 100644 (file)
@@ -12,45 +12,173 @@ using namespace std;
 #include "libs/Kernel.h"
 #include "SlowTicker.h"
 #include "libs/Hook.h"
-#include "system_LPC17xx.h" // mbed.h lib
+#include "modules/robot/Conveyor.h"
 
+#include <mri.h>
+
+// This module uses a Timer to periodically call hooks
+// Modules register with a function ( callback ) and a frequency, and we then call that function at the given frequency.
 
 SlowTicker* global_slow_ticker;
 
 SlowTicker::SlowTicker(){
-    this->max_frequency = 1;
+    max_frequency = 0;
     global_slow_ticker = this;
+
+    // Configure the actual timer
     LPC_SC->PCONP |= (1 << 22);     // Power Ticker ON
-    LPC_TIM2->MR0 = 10000;        // Initial dummy value for Match Register
+    LPC_TIM2->MR0 = 10000;          // Initial dummy value for Match Register
     LPC_TIM2->MCR = 3;              // Match on MR0, reset on MR0
     LPC_TIM2->TCR = 1;              // Enable interrupt
     NVIC_EnableIRQ(TIMER2_IRQn);    // Enable interrupt handler
+
+    // ISP button
+    ispbtn.from_string("2.10")->as_input()->pull_up();
+
+    // TODO: What is this ??
+    flag_1s_flag = 0;
+    flag_1s_count = SystemCoreClock>>2;
+
+    g4_ticks = 0;
+    g4_pause = false;
+}
+
+void SlowTicker::on_module_loaded(){
+    register_for_event(ON_IDLE);
+    register_for_event(ON_GCODE_RECEIVED);
+    register_for_event(ON_GCODE_EXECUTE);
 }
 
+// Set the base frequency we use for all sub-frequencies
 void SlowTicker::set_frequency( int frequency ){
-    LPC_TIM2->MR0 = int(floor((SystemCoreClock/4)/frequency));  // SystemCoreClock/4 = Timer increments in a second
+    this->interval = (SystemCoreClock >> 2) / frequency;   // SystemCoreClock/4 = Timer increments in a second
+    LPC_TIM2->MR0 = this->interval;
     LPC_TIM2->TCR = 3;  // Reset
     LPC_TIM2->TCR = 1;  // Reset
+    flag_1s_count= SystemCoreClock>>2;
 }
 
+// The actual interrupt being called by the timer, this is where work is done
 void SlowTicker::tick(){
 
-    LPC_GPIO1->FIODIR |= 1<<20;
-    LPC_GPIO1->FIOSET = 1<<20;
-
-    for (unsigned int i=0; i<this->hooks.size(); i++){
+    // Call all hooks that need to be called ( bresenham )
+    for (uint32_t i=0; i<this->hooks.size(); i++){
         Hook* hook = this->hooks.at(i);
-        hook->counter += ( hook->frequency / this->max_frequency );
-        if( hook->counter > 0 ){
-            hook->counter-=1;
+        hook->countdown -= this->interval;
+        if (hook->countdown < 0)
+        {
+            hook->countdown += hook->interval;
             hook->call();
         }
     }
 
-    LPC_GPIO1->FIOCLR = 1<<20;
+    // deduct tick time from secound counter
+    flag_1s_count -= this->interval;
+    // if a whole second has elapsed,
+    if (flag_1s_count < 0)
+    {
+        // add a second to our counter
+        flag_1s_count += SystemCoreClock >> 2;
+        // and set a flag for idle event to pick up
+        flag_1s_flag++;
+    }
+
+    // if we're counting down a pause
+    if (g4_ticks > 0)
+    {
+        // deduct tick time from timeout
+        if (g4_ticks > interval)
+            g4_ticks -= interval;
+        else
+            g4_ticks = 0;
+    }
+
+    // Enter MRI mode if the ISP button is pressed
+    // TODO: This should have it's own module
+    if (ispbtn.get() == 0)
+        __debugbreak();
 
 }
 
+bool SlowTicker::flag_1s(){
+    // atomic flag check routine
+    // first disable interrupts
+    __disable_irq();
+    // then check for a flag
+    if (flag_1s_flag)
+    {
+        // if we have a flag, decrement the counter
+        flag_1s_flag--;
+        // re-enable interrupts
+        __enable_irq();
+        // and tell caller that we consumed a flag
+        return true;
+    }
+    // if no flag, re-enable interrupts and return false
+    __enable_irq();
+    return false;
+}
+
+#include "gpio.h"
+extern GPIO leds[];
+void SlowTicker::on_idle(void*)
+{
+    static uint16_t ledcnt= 0;
+    if(THEKERNEL->use_leds) {
+        // flash led 3 to show we are alive
+        leds[2]= (ledcnt++ & 0x1000) ? 1 : 0;
+    }
+
+    // if interrupt has set the 1 second flag
+    if (flag_1s())
+        // fire the on_second_tick event
+        THEKERNEL->call_event(ON_SECOND_TICK);
+
+    // if G4 has finished, release our pause
+    if (g4_pause && (g4_ticks == 0))
+    {
+        g4_pause = false;
+        THEKERNEL->pauser->release();
+    }
+}
+
+// When a G4-type gcode is received, add it to the queue so we can execute it in time
+void SlowTicker::on_gcode_received(void* argument){
+    Gcode* gcode = static_cast<Gcode*>(argument);
+    // Add the gcode to the queue ourselves if we need it
+    if( gcode->has_g && gcode->g == 4 ){
+        THEKERNEL->conveyor->append_gcode(gcode);
+    }
+}
+
+// When a G4-type gcode is executed, start the pause
+void SlowTicker::on_gcode_execute(void* argument){
+    Gcode* gcode = static_cast<Gcode*>(argument);
+
+    if (gcode->has_g){
+        if (gcode->g == 4){
+            gcode->mark_as_taken();
+            bool updated = false;
+            if (gcode->has_letter('P')) {
+                updated = true;
+                g4_ticks += gcode->get_int('P') * ((SystemCoreClock >> 2) / 1000UL);
+            }
+            if (gcode->has_letter('S')) {
+                updated = true;
+                g4_ticks += gcode->get_int('S') * (SystemCoreClock >> 2);
+            }
+            if (updated){
+                // G4 Smm Pnn should pause for mm seconds + nn milliseconds
+                // at 120MHz core clock, the longest possible delay is (2^32 / (120MHz / 4)) = 143 seconds
+                if (!g4_pause){
+                    g4_pause = true;
+                    THEKERNEL->pauser->take();
+                }
+            }
+        }
+    }
+}
+
 extern "C" void TIMER2_IRQHandler (void){
     if((LPC_TIM2->IR >> 0) & 1){  // If interrupt register set for MR0
         LPC_TIM2->IR |= 1 << 0;   // Reset it