/* * pwm_softblinker.xc * * Created on: 22. juni 2020 * Author: teig */ #include #include // slice #include // delay_milliseconds(200), XS1_TIMER_HZ etc #include // uint8_t #include // printf #include // memcpy #include // REFERENCE_PARAM(my_app_ports_t, my_app_ports) -> my_app_ports_t &my_app_ports #include // not etc. //#include "_globals.h" // ..then this #include "_lib_pwm_softblinker_params.h" #include // --- // Control printing // See https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c // --- #define DEBUG_PRINT_TEST 1 #define debug_print(fmt, ...) do { if((DEBUG_PRINT_TEST==1) and (DEBUG_PRINT_GLOBAL_APP==1)) printf(fmt, __VA_ARGS__); } while (0) // Code proper: // in_range_signed is in "math.xc", not included here // signed in_range_signed_ (const signed value, const signed lowest, const signed highest) { signed return_in_range; if (value > highest) { return_in_range = highest; } else if (value < lowest) { return_in_range = lowest; } else { return_in_range = value; } return return_in_range; } // This task does not have any "global_resign_barrier" since we have assured at user level that if "synch_active" // then a real termination to "synch_none" is not done before acknowledge (ie. started and completed) // // [[combinable]] compiles, but [[combine]] par not accepted, so won't be built as combinable void barrier_do_chan_task ( chanend c_barrier[CONFIG_NUM_SOFTBLIKER_LEDS], // Send and receive id_task_t out buffered port:1 outP_external_blue_led_high) // led_on_high_t { bool num_synched = 0; id_task_t id_task [CONFIG_NUM_SOFTBLIKER_LEDS]; for (unsigned ix=0; ix id_task[iof_client]: { // query num_synched++; debug_print ("%s%u c_barrier num %u\n", (num_synched==1) ? "\n" : "", iof_client, num_synched); // Empty line above if 1 if (num_synched == CONFIG_NUM_SOFTBLIKER_LEDS) { for (unsigned ix=0; ix 0); // On while barrier is used } break; } } } #define INC_ONE_UP 1 #define DEC_ONE_DOWN (-1) #define PERIOD_MS_TO_ONE_STEP_TICKS(period,steps,value) do {value=(period*XS1_TIMER_KHZ)/(steps*2); } while (0) unsigned period_ms_to_one_step_ticks ( const unsigned period_ms, const intensity_steps_e intensity_steps) { return (period_ms * XS1_TIMER_KHZ) / (intensity_steps * 2); // 200 * 100 * 1000 / 1000 * 2 divides // 200 * 100 * 1000 / 600 * 2 does not divide // 200 * 100 * 1000 / 500 * 2 divides } typedef struct synch_context_t { bool do_next_intensity_at_intervals_pending; // To avoid it start again when ordered stopped synch_e do_multipart_synch; synch_e do_multipart_synch_pending; // To avoid it deadlock on the barrier. ALL barrier parts must finish synch! bool awaiting_synchronized; } synch_context_t; // Introducing a context struct seems to build less code void start_synch_chan_barrier ( const id_task_t id_task, synch_context_t &sync_ct, const extremals_e extremals, chanend c_barrier, out buffered port:1 out_port_toggle_on_direction_change) { // If period_ms differ then the longest period will rule. // The shortest will get its PWM done, then wait. // For the longest this waiting could last // (SOFTBLINK_PERIOD_MAX_MS - SOFTBLINK_PERIOD_MIN_MS)/2 = 4.9 seconds?) if (extremals == is_anywhere) { c_barrier <: id_task; // sync_ct.do_multipart_synch_pending will be set later // Leave ports undefined, since they are } else if (extremals == is_max) { #if (DO_PULSE_ON_START_SYNCH == 1) #if (WARNINGS==1) #warning DO_PULSE_ON_START_SYNCH #endif out_port_toggle_on_direction_change <: pin_low; c_barrier <: id_task; out_port_toggle_on_direction_change <: pin_high; #else c_barrier <: id_task; #endif sync_ct.do_multipart_synch_pending = synch_active; // may be set to synch_none in set_LED_period_linear_ms } else if (extremals == is_min) { #if (DO_PULSE_ON_START_SYNCH == 1) out_port_toggle_on_direction_change <: pin_high; c_barrier <: id_task; out_port_toggle_on_direction_change <: pin_low; #else c_barrier <: id_task; #endif sync_ct.do_multipart_synch_pending = synch_active; // may be set to synch_none in set_LED_period_linear_ms } sync_ct.do_next_intensity_at_intervals_pending = true; // may be set to false in set_LED_intensity_range sync_ct.awaiting_synchronized = true; } typedef struct softblinker_context_t { timer tmr; time32_t timeout; bool do_next_intensity_at_intervals; unsigned one_step_at_intervals_ticks; signed now_intensity; intensity_t max_intensity; intensity_t min_intensity; signed inc_steps; transition_pwm_e transition_pwm; intensity_steps_e intensity_steps; unsigned frequency_Hz; } softblinker_context_t; // Introducing a context struct seems to build less code [[combinable]] void softblinker_task ( const id_task_t id_task, client pwm_if if_pwm, server softblinker_if if_softblinker, out buffered port:1 out_port_toggle_on_direction_change) // Toggle when LED max { debug_print ("%u softblinker_task started\n", id_task); softblinker_context_t soft_ctx; // --- soft_ctx.do_next_intensity_at_intervals = false; soft_ctx.now_intensity = DEFAULT_FULL_INTENSITY; soft_ctx.max_intensity = DEFAULT_FULL_INTENSITY; soft_ctx.min_intensity = DEFAULT_DARK_INTENSITY; soft_ctx.inc_steps = DEC_ONE_DOWN; soft_ctx.transition_pwm = DEFAULT_TRANSITION_PWM; soft_ctx.intensity_steps = DEFAULT_INTENSITY_STEPS; soft_ctx.frequency_Hz = DEFAULT_PWM_FREQUENCY_HZ; soft_ctx.one_step_at_intervals_ticks = period_ms_to_one_step_ticks (DEFAULT_SOFTBLINK_PERIOD_MS, soft_ctx.intensity_steps); soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; while (1) { select { case (soft_ctx.do_next_intensity_at_intervals) => soft_ctx.tmr when timerafter(soft_ctx.timeout) :> void: { soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; // Both min_intensity, now_intensity and max_intensity are set outside this block // That's why both tests include "above" (>) and "below" (<) if (soft_ctx.now_intensity >= soft_ctx.max_intensity) { soft_ctx.inc_steps = DEC_ONE_DOWN; soft_ctx.now_intensity = soft_ctx.max_intensity; out_port_toggle_on_direction_change <: pin_low; } else if (soft_ctx.now_intensity <= soft_ctx.min_intensity) { soft_ctx.inc_steps = INC_ONE_UP; soft_ctx.now_intensity = soft_ctx.min_intensity; out_port_toggle_on_direction_change <: pin_high; } else {} soft_ctx.now_intensity += soft_ctx.inc_steps; // [1..100] [99..0] (Example for steps_0100) if_pwm.set_LED_intensity ( soft_ctx.frequency_Hz, soft_ctx.intensity_steps, (intensity_t) soft_ctx.now_intensity, soft_ctx.transition_pwm); } break; // timerafter case if_softblinker.set_LED_intensity_range ( const unsigned frequency_Hz, // 0 -> actives port const intensity_steps_e intensity_steps, // [1..] const intensity_t min_intensity, // [0..x] const intensity_t max_intensity) -> bool ok : // [x..intensity_steps_] { ok = (min_intensity <= max_intensity); if (ok) { soft_ctx.intensity_steps = intensity_steps; soft_ctx.min_intensity = (intensity_t) in_range_signed_ ((signed) min_intensity, DEFAULT_DARK_INTENSITY, soft_ctx.intensity_steps); soft_ctx.max_intensity = (intensity_t) in_range_signed_ ((signed) max_intensity, DEFAULT_DARK_INTENSITY, soft_ctx.intensity_steps); soft_ctx.frequency_Hz = frequency_Hz; if (soft_ctx.max_intensity == soft_ctx.min_intensity) { // No INC_ONE_UP or INC_ONE_DOWN of sensitivity soft_ctx.do_next_intensity_at_intervals = false; soft_ctx.now_intensity = soft_ctx.max_intensity; if_pwm.set_LED_intensity (soft_ctx.frequency_Hz, soft_ctx.intensity_steps, soft_ctx.max_intensity, soft_ctx.transition_pwm); } else if (soft_ctx.do_next_intensity_at_intervals == false) { // Not running, make it run: soft_ctx.do_next_intensity_at_intervals = true; soft_ctx.tmr :> soft_ctx.timeout; // immediate timeout } else { // do_next_intensity_at_intervals already // No code // Don't disturb running timerafter } } else { // No code, no warning! Not according to protocol } // Printing disturbs update messages above, so it will appear to "blink" debug_print ("%u set_LED_intensity steps ok %u steps %u (n %u, i %d) min %u now %d max %u freq %u\n", id_task, // ## ## ## ## ## ## ## ## ok, soft_ctx.intensity_steps, soft_ctx.do_next_intensity_at_intervals, soft_ctx.inc_steps, soft_ctx.min_intensity, soft_ctx.now_intensity, soft_ctx.max_intensity, soft_ctx.frequency_Hz); } break; case if_softblinker.set_LED_period_linear_ms ( const unsigned period_ms_, // See Comment in the header file const start_LED_at_e start_LED_at, const transition_pwm_e transition_pwm, const const synch_e do_multipart_synch_not_used) : { // It seems like linear is ok for softblinking of a LED, ie. "softblink" is soft // I have not tried any other, like sine. I would assume it would feel like dark_LED longer const bool ok_running = soft_ctx.do_next_intensity_at_intervals; unsigned period_ms; if (ok_running) { // Normalise to set period // const unsigned period_ms__ = in_range_signed_ (period_ms_, SOFTBLINK_PERIOD_MIN_MS, SOFTBLINK_PERIOD_MAX_MS); const intensity_t range_intensity_steps = soft_ctx.max_intensity - soft_ctx.min_intensity; period_ms = (period_ms__ * soft_ctx.intensity_steps) / range_intensity_steps; // Now as range decreases, period increases if (start_LED_at == dark_LED) { soft_ctx.now_intensity = DEFAULT_DARK_INTENSITY; } else if (start_LED_at == full_LED) { soft_ctx.now_intensity = soft_ctx.intensity_steps; } else { // continuous_LED, no code } soft_ctx.one_step_at_intervals_ticks = period_ms_to_one_step_ticks (period_ms, soft_ctx.intensity_steps); soft_ctx.transition_pwm = transition_pwm; } else { period_ms = 0; // Just some value, for printing } // Printing disturbs update messages above, so it will appear to "blink" debug_print ("%u set_LED_period_linear_ms period %u->%u (ticks %u) (%u, %d) min %u now %d max %u\n", id_task, // ## ## ## ## ## ## ## ## period_ms_, period_ms, soft_ctx.one_step_at_intervals_ticks, soft_ctx.do_next_intensity_at_intervals, soft_ctx.inc_steps, soft_ctx.min_intensity, soft_ctx.now_intensity, soft_ctx.max_intensity); } break; } } } // [[combinable]] compiles, but [[combine]] par not accepted, so won't be built as combinable void softblinker_task_chan_barrier ( // USED WITH STANDARD CONFIG const id_task_t id_task, client pwm_if if_pwm, server softblinker_if if_softblinker, out buffered port:1 out_port_toggle_on_direction_change, chanend c_barrier) { debug_print ("%u softblinker_task_chan_barrier started\n", id_task); softblinker_context_t soft_ctx; synch_context_t sync_ct; // --- soft_ctx.do_next_intensity_at_intervals = false; soft_ctx.now_intensity = DEFAULT_FULL_INTENSITY; soft_ctx.max_intensity = DEFAULT_FULL_INTENSITY; soft_ctx.min_intensity = DEFAULT_DARK_INTENSITY; soft_ctx.inc_steps = DEC_ONE_DOWN; soft_ctx.transition_pwm = DEFAULT_TRANSITION_PWM; soft_ctx.intensity_steps = DEFAULT_INTENSITY_STEPS; soft_ctx.frequency_Hz = DEFAULT_PWM_FREQUENCY_HZ; sync_ct.do_next_intensity_at_intervals_pending = soft_ctx.do_next_intensity_at_intervals; sync_ct.do_multipart_synch = DEFAULT_SYNCH; sync_ct.do_multipart_synch_pending = DEFAULT_SYNCH; sync_ct.awaiting_synchronized = false; soft_ctx.one_step_at_intervals_ticks = period_ms_to_one_step_ticks (DEFAULT_SOFTBLINK_PERIOD_MS, soft_ctx.intensity_steps); soft_ctx.tmr :> soft_ctx.timeout; soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; while (1) { select { case (soft_ctx.do_next_intensity_at_intervals) => soft_ctx.tmr when timerafter(soft_ctx.timeout) :> void: { soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; // Both min_intensity, now_intensity and max_intensity are set outside this block // That's why both tests include "above" (>) and "below" (<) if (soft_ctx.now_intensity >= soft_ctx.max_intensity) { soft_ctx.inc_steps = DEC_ONE_DOWN; soft_ctx.now_intensity = soft_ctx.max_intensity; if (sync_ct.do_multipart_synch == synch_active) { start_synch_chan_barrier (id_task, sync_ct, is_max, c_barrier, out_port_toggle_on_direction_change); soft_ctx.do_next_intensity_at_intervals = false; } else {} out_port_toggle_on_direction_change <: pin_low; } else if (soft_ctx.now_intensity <= soft_ctx.min_intensity) { soft_ctx.inc_steps = INC_ONE_UP; soft_ctx.now_intensity = soft_ctx.min_intensity; if (sync_ct.do_multipart_synch == synch_active) { start_synch_chan_barrier (id_task, sync_ct, is_min, c_barrier, out_port_toggle_on_direction_change); soft_ctx.do_next_intensity_at_intervals = false; } else {} out_port_toggle_on_direction_change <: pin_high; } else {} soft_ctx.now_intensity += soft_ctx.inc_steps; // [1..100] [99..0] (Example for steps_0100) if_pwm.set_LED_intensity ( soft_ctx.frequency_Hz, soft_ctx.intensity_steps, (intensity_t) soft_ctx.now_intensity, soft_ctx.transition_pwm); } break; // timerafter case if_softblinker.set_LED_intensity_range ( const unsigned frequency_Hz, // 0 -> actives port const intensity_steps_e intensity_steps, // [1..] const intensity_t min_intensity, // [0..x] const intensity_t max_intensity) -> bool ok : // [x..intensity_steps_] { ok = (min_intensity <= max_intensity); if (ok) { soft_ctx.intensity_steps = intensity_steps; soft_ctx.min_intensity = (intensity_t) in_range_signed_ ((signed) min_intensity, DEFAULT_DARK_INTENSITY, soft_ctx.intensity_steps); soft_ctx.max_intensity = (intensity_t) in_range_signed_ ((signed) max_intensity, DEFAULT_DARK_INTENSITY, soft_ctx.intensity_steps); soft_ctx.frequency_Hz = frequency_Hz; if (soft_ctx.max_intensity == soft_ctx.min_intensity) { // No INC_ONE_UP or INC_ONE_DOWN of sensitivity if (sync_ct.awaiting_synchronized) { sync_ct.do_next_intensity_at_intervals_pending = false; } else { soft_ctx.do_next_intensity_at_intervals = false; } soft_ctx.now_intensity = soft_ctx.max_intensity; if_pwm.set_LED_intensity (soft_ctx.frequency_Hz, soft_ctx.intensity_steps, soft_ctx.max_intensity, soft_ctx.transition_pwm); } else if (soft_ctx.do_next_intensity_at_intervals == false) { // Not running, make it run: if (sync_ct.awaiting_synchronized) { sync_ct.do_next_intensity_at_intervals_pending = true; // later } else { soft_ctx.do_next_intensity_at_intervals = true; soft_ctx.tmr :> soft_ctx.timeout; // immediate timeout } } else { // do_next_intensity_at_intervals already // No code // Don't disturb running timerafter } } else { // No code, no warning! Not according to protocol } // Printing disturbs update messages above, so it will appear to "blink" debug_print ("%u set_LED_intensity steps ok %u sync %u steps %u (n %u, i %d) min %u now %d max %u freq %u\n", id_task, // ## ## ## ## ## ## ## ## ## ok, sync_ct.awaiting_synchronized, soft_ctx.intensity_steps, sync_ct.awaiting_synchronized ? sync_ct.do_next_intensity_at_intervals_pending : soft_ctx.do_next_intensity_at_intervals, soft_ctx.inc_steps, soft_ctx.min_intensity, soft_ctx.now_intensity, soft_ctx.max_intensity, soft_ctx.frequency_Hz); } break; case if_softblinker.set_LED_period_linear_ms ( const unsigned period_ms_, // See Comment in the header file const start_LED_at_e start_LED_at, const transition_pwm_e transition_pwm, const const synch_e do_multipart_synch) : { // It seems like linear is ok for softblinking of a LED, ie. "softblink" is soft // I have not tried any other, like sine. I would assume it would feel like dark_LED longer const bool ok_running = soft_ctx.do_next_intensity_at_intervals; unsigned period_ms; if (ok_running) { // Normalise to set period // const unsigned period_ms__ = in_range_signed_ (period_ms_, SOFTBLINK_PERIOD_MIN_MS, SOFTBLINK_PERIOD_MAX_MS); const intensity_t range_intensity_steps = soft_ctx.max_intensity - soft_ctx.min_intensity; period_ms = (period_ms__ * soft_ctx.intensity_steps) / range_intensity_steps; // Now as range decreases, period increases if (start_LED_at == dark_LED) { soft_ctx.now_intensity = DEFAULT_DARK_INTENSITY; } else if (start_LED_at == full_LED) { soft_ctx.now_intensity = soft_ctx.intensity_steps; } else { // continuous_LED, no code } soft_ctx.one_step_at_intervals_ticks = period_ms_to_one_step_ticks (period_ms, soft_ctx.intensity_steps); soft_ctx.transition_pwm = transition_pwm; } else { period_ms = 0; // Just some value, for printing } unsigned branch = 0; // For log if (sync_ct.awaiting_synchronized) { sync_ct.do_multipart_synch_pending = do_multipart_synch; // later } else if ((sync_ct.do_multipart_synch == synch_active) and (do_multipart_synch == synch_none)) { // Ending synch, clean up // Starting synch here is just to save time. It could take several seconds before is_min or is:max is reached start_synch_chan_barrier (id_task, sync_ct, is_anywhere, c_barrier, out_port_toggle_on_direction_change); soft_ctx.do_next_intensity_at_intervals = false; sync_ct.do_multipart_synch_pending = synch_none; // later branch = 1; } else { sync_ct.do_multipart_synch = do_multipart_synch; // now branch = 2; } // Printing disturbs update messages above, so it will appear to "blink" debug_print ("%u set_LED_period_linear_ms sync %u:%u branch %u period %u->%u (ticks %u) (%u, %d) min %u now %d max %u\n", id_task, // ## ## ## ## ## ## ## ## ## ## ## sync_ct.awaiting_synchronized, sync_ct.awaiting_synchronized ? sync_ct.do_multipart_synch_pending : sync_ct.do_multipart_synch, branch, period_ms_, period_ms, soft_ctx.one_step_at_intervals_ticks, soft_ctx.do_next_intensity_at_intervals, soft_ctx.inc_steps, soft_ctx.min_intensity, soft_ctx.now_intensity, soft_ctx.max_intensity); } break; case c_barrier :> id_task_t id_task_ : { debug_print ("%u/%u synchronized synch %u cont %u\n", id_task, id_task_, sync_ct.do_multipart_synch_pending, sync_ct.do_next_intensity_at_intervals_pending); sync_ct.awaiting_synchronized = false; sync_ct.do_multipart_synch = sync_ct.do_multipart_synch_pending; soft_ctx.do_next_intensity_at_intervals = sync_ct.do_next_intensity_at_intervals_pending; if (soft_ctx.do_next_intensity_at_intervals) { soft_ctx.tmr :> soft_ctx.timeout; // restart timer soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; } else { // do_next_intensity_at_intervals is false // No code, do not "override" do_next_intensity_at_intervals_pending } } break; } } } // Standard type PWM based on timeouts // But there would be another way to do this: // This does not attach a timer to the port itself and use the @ number-of-ticks and timed output feature of XC. More about that here: // XS1 Ports: use and specification MS_PER_PERCENT_TO_PERIOD_MS_FACTOR8 see https://www.xmos.com/file/xs1-ports-specification/ // Introduction to XS1 ports 2010 see https://www.xmos.com/file/xs1-ports-introduction/ // XMOS Programming Guide 2015 see https://www.xmos.com/download/XMOS-Programming-Guide-(documentation)(F).pdf // typedef enum {activated, deactivated} port_is_e; #define ACTIVATE_PORT(theport,sign) do {theport <: (1 xor sign);} while (0) // to activated: 0 = 1 xor 1 = [1 xor active_low] #define DEACTIVATE_PORT(theport,sign) do {theport <: (0 xor sign);} while (0) // to deactivated: 1 = 0 xor 1 = [0 xor active_low] // typedef struct pwm_context_t { timer tmr; time32_t timeout; intensity_t intensity_port_activated; // Normalised to intensity_steps (allways ON when intensity_port_activated == intensity_steps) intensity_t intensity_unit_ticks; // Normalised to intensity_steps (so many ticks == one step) intensity_steps_e intensity_steps; port_pin_sign_e port_pin_sign; port_is_e port_is; bool pwm_running; } pwm_context_t; // Introducing a context struct seems to build less code void func_set_LED_intensity ( pwm_context_t &pwm_ctx, out buffered port:1 out_port_LED, const unsigned frequency_Hz, // 0 -> actives port const intensity_steps_e intensity_steps_, const intensity_t intensity, // Normalised to intensity_steps (always ON when intensity == intensity_steps_) const transition_pwm_e transition_pwm) { const bool pwm_running_pre = pwm_ctx.pwm_running; pwm_ctx.intensity_steps = intensity_steps_; pwm_ctx.intensity_port_activated = intensity; if (frequency_Hz == 0) { pwm_ctx.pwm_running = false; ACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); } else if (pwm_ctx.intensity_port_activated == pwm_ctx.intensity_steps) { // (First this..) No need to involve any timerafter and get a short "off" blip pwm_ctx.pwm_running = false; ACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); } else if (transition_pwm == slide_transition_pwm) { // (..then this, since transition_pwm not set with set_LED_intensity_range) pwm_ctx.pwm_running = true; // else lock_transition_pwm: } else if (pwm_ctx.intensity_port_activated == DEFAULT_DARK_INTENSITY) { // No need to involve any timerafter and get a short "on" blink pwm_ctx.pwm_running = false; DEACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); } else if (not pwm_ctx.pwm_running) { pwm_ctx.pwm_running = true; } else { // pwm_running already // No code // Don't disturb running timerafter, just let it use the new intensity_port_activated when it gets there } // PWM=012 This solved the problem // Had to increase xta "set required - 1.0 us" to 1.1 us if ((pwm_running_pre == false) and (pwm_ctx.pwm_running == true)) { // Always avoid time32_t half 21.47483648 secs delay pwm_ctx.tmr :> pwm_ctx.timeout; // immediate timeout } else {} if (pwm_ctx.pwm_running) { #define XTA_TEST_SET_LED_INTENSITY 0 // USE 0. Values from version 0023 (below) #if (XTA_TEST_SET_LED_INTENSITY == 0) // Pass with 14 unknowns, Num Paths: 7, Slack: 470.0 ns, Required: 1.0 us, Worst: 530.0 ns, Min Core Frequency: 265 MHz pwm_ctx.intensity_unit_ticks = (XS1_TIMER_MHZ * 1000000U) / (frequency_Hz * pwm_ctx.intensity_steps); #elif (XTA_TEST_SET_LED_INTENSITY == 1) // Pass with 14 unknowns, Num Paths: 7, Slack: 250.0 ns, Required: 1.0 us, Worst: 750.0 ns, Min Core Frequency: 375 MHz const unsigned period_us_ticks = (XS1_TIMER_MHZ * 1000000U) / pwm_ctx.frequency_Hz; // 1M/f us and * for ticks pwm_ctx.intensity_unit_ticks = pwm_ctx.period_us_ticks / pwm_ctx.intensity_steps; #else #error XTA_TEST_SET_LED_INTENSITY value #endif } else {} } [[combinable]] void pwm_for_LED_task ( // USED WITH STANDARD CONFIG const id_task_t id_task, // For printing only server pwm_if if_pwm, out buffered port:1 out_port_LED) // LED { pwm_context_t pwm_ctx; debug_print ("%u pwm_for_LED_task started\n", id_task); pwm_ctx.port_pin_sign = PWM_PORT_PIN_SIGN; pwm_ctx.pwm_running = false; pwm_ctx.port_is = activated; ACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); while (1) { // #pragma ordered // May be used if not [[combinable]] to assure priority of the PWM, if that is wanted #pragma xta endpoint "start" // in "my_script.xta" script at user: // analyze loop start // set required - 1.0 us select { // ----------------------------------------------------- // THIS IS THE PWM. ALL THE REST IS JUST CONTROLLING IT // ----------------------------------------------------- // case (pwm_ctx.pwm_running) => pwm_ctx.tmr when timerafter(pwm_ctx.timeout) :> void: { unsigned delta; if (pwm_ctx.port_is == deactivated) { #pragma xta endpoint "stop" ACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); delta = (pwm_ctx.intensity_port_activated * pwm_ctx.intensity_unit_ticks); pwm_ctx.port_is = activated; } else { DEACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); delta = ((pwm_ctx.intensity_steps - pwm_ctx.intensity_port_activated) * pwm_ctx.intensity_unit_ticks); pwm_ctx.port_is = deactivated;; } // Test, but did not solve PWM=012 // if (delta >= 0x80000000) { // delta = 0; // } else {} pwm_ctx.timeout += (time32_t) delta; } break; // THIS IS ALL THE REST: CONTROLLING THE PWM case if_pwm.set_LED_intensity ( const unsigned frequency_Hz, // 0 -> actives port const intensity_steps_e intensity_steps_, const intensity_t intensity, // Normalised to intensity_steps (always ON when intensity == intensity_steps_) const transition_pwm_e transition_pwm) : { // Moving the code into a function takes XTA analysis: Required: 1.0 us to // Worst: 984.0 ns, 492 MHz with function call // Worst: 816.0 ns, 408 MHz with in-line code // COST: 168.0 ns func_set_LED_intensity ( // USED WITH STANDARD CONFIG pwm_ctx, out_port_LED, frequency_Hz, intensity_steps_, intensity, transition_pwm); } break; } } } [[combinable]] void pwm_softblinker_combined_task ( const id_task_t id_task, server softblinker_if if_softblinker, out buffered port:1 out_port_LED, out buffered port:1 out_port_toggle_on_direction_change) { debug_print ("%u pwm_softblinker_combined_task started\n", id_task); softblinker_context_t soft_ctx; pwm_context_t pwm_ctx; // --- soft_ctx.do_next_intensity_at_intervals = false; soft_ctx.now_intensity = DEFAULT_FULL_INTENSITY; soft_ctx.max_intensity = DEFAULT_FULL_INTENSITY; soft_ctx.min_intensity = DEFAULT_DARK_INTENSITY; soft_ctx.inc_steps = DEC_ONE_DOWN; soft_ctx.transition_pwm = DEFAULT_TRANSITION_PWM; soft_ctx.intensity_steps = DEFAULT_INTENSITY_STEPS; soft_ctx.frequency_Hz = DEFAULT_PWM_FREQUENCY_HZ; soft_ctx.one_step_at_intervals_ticks = period_ms_to_one_step_ticks (DEFAULT_SOFTBLINK_PERIOD_MS, soft_ctx.intensity_steps); pwm_ctx.port_pin_sign = PWM_PORT_PIN_SIGN; pwm_ctx.pwm_running = false; pwm_ctx.port_is = activated; ACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); soft_ctx.tmr :> soft_ctx.timeout; soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; while (1) { #pragma xta endpoint "start2" select { // == Basically same cide as in pwm_for_LED_task, but without case if_pwm.set_LED_intensity since that's now a function == // // ------------------------------------------------------------------- // -> NOT USED WITH STANDARD CONFIG // THIS IS THE PWM THAT'S COMBINED WITH pwm_softblinker_combined_task // ALL THE REST IS JUST CONTROLLING IT // ------------------------------------------------------------------- // case (pwm_ctx.pwm_running) => pwm_ctx.tmr when timerafter(pwm_ctx.timeout) :> void: { if (pwm_ctx.port_is == deactivated) { #pragma xta endpoint "stop2" ACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); pwm_ctx.timeout += (pwm_ctx.intensity_port_activated * pwm_ctx.intensity_unit_ticks); pwm_ctx.port_is = activated; } else { DEACTIVATE_PORT(out_port_LED, pwm_ctx.port_pin_sign); pwm_ctx.timeout += ((pwm_ctx.intensity_steps - pwm_ctx.intensity_port_activated) * pwm_ctx.intensity_unit_ticks); pwm_ctx.port_is = deactivated;; } } break; // == Basically same code as in softblinker_task == // case (soft_ctx.do_next_intensity_at_intervals) => soft_ctx.tmr when timerafter(soft_ctx.timeout) :> void: { soft_ctx.timeout += soft_ctx.one_step_at_intervals_ticks; // Both min_intensity, now_intensity and max_intensity are set outside this block // That's why both tests include "above" (>) and "below" (<) if (soft_ctx.now_intensity >= soft_ctx.max_intensity) { soft_ctx.inc_steps = DEC_ONE_DOWN; soft_ctx.now_intensity = soft_ctx.max_intensity; out_port_toggle_on_direction_change <: pin_low; } else if (soft_ctx.now_intensity <= soft_ctx.min_intensity) { soft_ctx.inc_steps = INC_ONE_UP; soft_ctx.now_intensity = soft_ctx.min_intensity; out_port_toggle_on_direction_change <: pin_high; } else {} soft_ctx.now_intensity += soft_ctx.inc_steps; // [1..100] [99..0] (Example for steps_0100) func_set_LED_intensity ( pwm_ctx, out_port_LED, soft_ctx.frequency_Hz, soft_ctx.intensity_steps, (intensity_t) soft_ctx.now_intensity, soft_ctx.transition_pwm); } break; // timerafter case if_softblinker.set_LED_intensity_range ( const unsigned frequency_Hz, // 0 -> actives port const intensity_steps_e intensity_steps, // [1..] const intensity_t min_intensity, // [0..x] const intensity_t max_intensity) -> bool ok : // [x..intensity_steps_] { ok = (min_intensity <= max_intensity); if (ok) { soft_ctx.intensity_steps = intensity_steps; soft_ctx.min_intensity = (intensity_t) in_range_signed_ ((signed) min_intensity, DEFAULT_DARK_INTENSITY, soft_ctx.intensity_steps); soft_ctx.max_intensity = (intensity_t) in_range_signed_ ((signed) max_intensity, DEFAULT_DARK_INTENSITY, soft_ctx.intensity_steps); soft_ctx.frequency_Hz = frequency_Hz; if (soft_ctx.max_intensity == soft_ctx.min_intensity) { // No INC_ONE_UP or INC_ONE_DOWN of sensitivity soft_ctx.do_next_intensity_at_intervals = false; soft_ctx.now_intensity = soft_ctx.max_intensity; func_set_LED_intensity ( pwm_ctx, out_port_LED, soft_ctx.frequency_Hz, soft_ctx.intensity_steps, soft_ctx.max_intensity, soft_ctx.transition_pwm); } else if (soft_ctx.do_next_intensity_at_intervals == false) { // Not running, make it run: soft_ctx.do_next_intensity_at_intervals = true; soft_ctx.tmr :> soft_ctx.timeout; // immediate timeout } else { // do_next_intensity_at_intervals already // No code // Don't disturb running timerafter } } else { // No code, no warning! Not according to protocol } // Printing disturbs update messages above, so it will appear to "blink" debug_print ("%u set_LED_intensity steps ok %u steps %u (n %u, i %d) min %u now %d max %u freq %u\n", id_task, // ## ## ## ## ## ## ## ## ok, soft_ctx.intensity_steps, soft_ctx.do_next_intensity_at_intervals, soft_ctx.inc_steps, soft_ctx.min_intensity, soft_ctx.now_intensity, soft_ctx.max_intensity, soft_ctx.frequency_Hz); } break; case if_softblinker.set_LED_period_linear_ms ( const unsigned period_ms_, // See Comment in the header file const start_LED_at_e start_LED_at, const transition_pwm_e transition_pwm, const const synch_e do_multipart_synch_not_used) : { // It seems like linear is ok for softblinking of a LED, ie. "softblink" is soft // I have not tried any other, like sine. I would assume it would feel like dark_LED longer const bool ok_running = soft_ctx.do_next_intensity_at_intervals; unsigned period_ms; if (ok_running) { // Normalise to set period // const unsigned period_ms__ = in_range_signed_ (period_ms_, SOFTBLINK_PERIOD_MIN_MS, SOFTBLINK_PERIOD_MAX_MS); const intensity_t range_intensity_steps = soft_ctx.max_intensity - soft_ctx.min_intensity; period_ms = (period_ms__ * soft_ctx.intensity_steps) / range_intensity_steps; // Now as range decreases, period increases if (start_LED_at == dark_LED) { soft_ctx.now_intensity = DEFAULT_DARK_INTENSITY; } else if (start_LED_at == full_LED) { soft_ctx.now_intensity = soft_ctx.intensity_steps; } else { // continuous_LED, no code } soft_ctx.one_step_at_intervals_ticks = period_ms_to_one_step_ticks (period_ms, soft_ctx.intensity_steps); soft_ctx.transition_pwm = transition_pwm; } else { period_ms = 0; // Just some value, for printing } // Printing disturbs update messages above, so it will appear to "blink" debug_print ("%u set_LED_period_linear_ms period %u->%u (ticks %u) (%u, %d) min %u now %d max %u\n", id_task, // ## ## ## ## ## ## ## ## period_ms_, period_ms, soft_ctx.one_step_at_intervals_ticks, soft_ctx.do_next_intensity_at_intervals, soft_ctx.inc_steps, soft_ctx.min_intensity, soft_ctx.now_intensity, soft_ctx.max_intensity); } break; } } }