Fan Controller - PWM HF (25kHz) Type, w/ a Two Button Escalator & a 4-LED Binary State-Display for UI.
The Circuit, as Built on an Universal PCB, Ready For Installation (in a '3.5 Drive-Slot) - Using a Thermoplastic Carrier :
main.cpp
- Committer:
- mzcs
- Date:
- 2018-11-04
- Revision:
- 1:0a2f89da0616
- Parent:
- 0:437bb8e2f8a7
File content as of revision 1:0a2f89da0616:
/* Copyright (c) 2018 - Moran ZALTSMAN This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include "mbed.h" #include "mbed_mem_trace.h" /*#include "hal/pinmap.h"*/ #include "DebounceIn.h" // a Polling-based (IRQ-less) Edge-Detection Mechanism - Using DebounceIn Class & mBed-OS Thread(s) : // [ Note : Shared-Resource (e.g. : PWMOut, BusOut) Are At Least Thread-Safe ( See : https://docs.mbed.com/docs/mbed-os-handbook/en/latest/concepts/thread_safety/ ) ] DebounceIn btn_1(PA_11, PullUp), btn_2(PA_12, PullUp); // Inverted Logic : On -> "0", Off -> "1" PwmOut pwm_out_1(PA_8); BusOut bus_out_1(PA_15, PB_3, PB_4, PB_5); // a 4-bit Bus - For a 4 LED Status-Display Setup class PwmOut_Elevator { public: PwmOut_Elevator(DebounceIn *debounce_in, PwmOut *pwm_out, float step) { _debounce_in = debounce_in; _pwm_out = pwm_out; _step = step; }; void PwmOut_Elevator_Operator() /* To Be Run Inside a Thread */ { while (1) { ThisThread::sleep_for((_debounce_in->get_debounce_us() / 1000) - 1); // Sleep for a Single (1) DebounceIn Sampling-Period if ((_debounce_in->get_edge_direction() == 0) && (_debounce_in->get_edge_direction_acted_upon() == 0)) { if ((_pwm_out->read() + _step) >= 0.65f) { _pwm_out->write(_pwm_out->read() + _step); } else { _pwm_out->write(0.65f); } // <- a Fail-Safe Default _debounce_in->set_edge_direction_acted_upon(); } } } protected: DebounceIn *_debounce_in; PwmOut *_pwm_out; float _step; }; // class PwmOut_Elevator - Ends class Display { public: Display(BusOut *bus_out, PwmOut *pwm_out) { _bus_out = bus_out; _pwm_out = pwm_out; }; void Display_Operator(void) /* To Be Run Inside a Thread */ { while (1) { ThisThread::sleep_for(10); // 10ms -> a Projected 100-Hz Update Rate _bus_out->write( (uint8_t ((_pwm_out->read() - 0.65f)/0.05f)) + 1 ); // (65% -> 1) .. (100% -> 8), 5% Step } } protected: PwmOut *_pwm_out; BusOut *_bus_out; }; // class Display - Ends PwmOut_Elevator btn_1_pwmout_elevator(&btn_1, &pwm_out_1, 0.05f), btn_2_pwmout_elevator(&btn_2, &pwm_out_1, -0.05f); Display display_1(&bus_out_1, &pwm_out_1); // for Debugging Semaphore smph_debug_output(1, 1); // (a Binary Semaphore - e.g. : Effectively a Mutex) Serial port_serial_1(PA_9, PA_10); class Debug { public: Debug(Serial *port_serial) { _serial = port_serial; } void Debug_Output_Mem_Stats(void) /* To Be Run Inside a Thread */ { // allocate enough room for every thread's stack statistics // int8_t cnt = osThreadGetCount(); // mbed_stats_stack_t *stats = (mbed_stats_stack_t*) malloc(cnt * sizeof(mbed_stats_stack_t)); int8_t cnt; _serial->printf("Debug::Debug_Output_Mem_Stats() : Hello ! :)\r\n\r\n"); while (1) { smph_debug_output.wait(); cnt = osThreadGetCount(); // mbed_stats_stack_t *stats = (mbed_stats_stack_t*) malloc(cnt * sizeof(mbed_stats_stack_t)); mbed_stats_stack_t *stats = new mbed_stats_stack_t[cnt]; cnt = mbed_stats_stack_get_each(stats, cnt); for (int8_t i = 0; i < cnt; i++) { _serial->printf("Debug::Debug_Output_Mem_Stats(): Thread: 0x%lX, Stack size: %lu / %lu\r\n", stats[i].thread_id, stats[i].max_size, stats[i].reserved_size); } // free(stats); delete stats; // Grab the heap statistics mbed_stats_heap_t heap_stats; mbed_stats_heap_get(&heap_stats); _serial->printf("Debug::Debug_Output_Mem_Stats(): Heap size: %lu / %lu bytes\r\n\r\n", heap_stats.current_size, heap_stats.reserved_size); smph_debug_output.release(); ThisThread::sleep_for(250); } } void Debug_Output_General(void) /* To Be Run Inside a Thread */ { _serial->printf("Debug::Debug_Output_General() : Hello ! :)\r\n\r\n"); while (1) { smph_debug_output.wait(); _serial->printf("Debug::Debug_Output_General() : 'btn_1' Current Value = %d\r\n", btn_1.read()); _serial->printf("Debug::Debug_Output_General() : 'btn_1/edge_direction' Current Value = %d\r\n", btn_1.get_edge_direction()); _serial->printf("Debug::Debug_Output_General() : 'btn_1/edge_direction_acted_upon' Current Value = %d\r\n", btn_1.get_edge_direction_acted_upon()); _serial->printf("Debug::Debug_Output_General() : 'btn_2' Current Value = %d\r\n", btn_2.read()); _serial->printf("Debug::Debug_Output_General() : 'btn_2/edge_direction' Current Value = %d\r\n", btn_2.get_edge_direction()); _serial->printf("Debug::Debug_Output_General() : 'btn_2/edge_direction_acted_upon' Current Value = %d\r\n", btn_2.get_edge_direction_acted_upon()); _serial->printf("Debug::Debug_Output_General() : 'pwm_out_1' Current Value = %f\r\n", pwm_out_1.read()); _serial->printf("Debug::Debug_Output_General() : 'bus_out_1' Current Value = %d\r\n\r\n", bus_out_1.read()); smph_debug_output.release(); ThisThread::sleep_for(250); } } protected: Serial *_serial; }; // class Debug - Ends Debug debug_1(&port_serial_1); /// Thread btn_1_thr(osPriorityNormal, 240), btn_2_thr(osPriorityNormal, 240), display_thr(osPriorityNormal, 240), debug_general_thr(osPriorityNormal, 640), debug_mem_stats_thr(osPriorityNormal, 640); int main() { mbed_mem_trace_set_callback(mbed_mem_trace_default_callback); // Set PWM pwm_out_1.period_us(40); // 25 kHz Period pwm_out_1.write(0.65f); // 65% Duty-Cycle (65% Fan Speed : a Safe Default) /// debug_general_thr.start(callback(&debug_1, &Debug::Debug_Output_General)); port_serial_1.printf("main(): 'debug_general_thr' Started.\r\n"); debug_mem_stats_thr.start(callback(&debug_1, &Debug::Debug_Output_Mem_Stats)); port_serial_1.printf("main(): 'debug_mem_stats_thr' Started.\r\n"); port_serial_1.printf("main(): Going To Sleep For a %dms\r\n", ((btn_1.get_debounce_us()*btn_1.get_samples()/1000)+1)); ThisThread::sleep_for((btn_1.get_debounce_us()*btn_1.get_samples()/1000)+1); // Sleep for 251ms - To Get Initial Stable Input-Values of Buttons via Their DebounceIn Instances btn_1_thr.start(callback(&btn_1_pwmout_elevator, &PwmOut_Elevator::PwmOut_Elevator_Operator)); port_serial_1.printf("main(): 'btn_1_thr' Started.\r\n"); ThisThread::sleep_for(100); btn_2_thr.start(callback(&btn_2_pwmout_elevator, &PwmOut_Elevator::PwmOut_Elevator_Operator)); port_serial_1.printf("main(): 'btn_2_thr' Started.\r\n"); ThisThread::sleep_for(100); display_thr.start(callback(&display_1, &Display::Display_Operator)); port_serial_1.printf("main(): 'display_thr' Started.\r\n"); ThisThread::sleep_for(osWaitForever); }