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 :

Revision 0:437bb8e2f8a7, committed 2018-11-04
- Comitter:
- mzcs
- Date:
- Sun Nov 04 16:49:49 2018 +0000
- Child:
- 1:0a2f89da0616
- Commit message:
- 1st Publication.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/DebounceIn.h Sun Nov 04 16:49:49 2018 +0000
@@ -0,0 +1,208 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef AJK_DEBOUNCEIN_H
+#define AJK_DEBOUNCEIN_H
+
+#include "mbed.h"
+
+/** DebounceIn adds mechanical switch debouncing to DigitialIn.
+ *
+ * Example:
+ * @code
+ * #include "mbed.h"
+ * #include "DebounceIn.h"
+ *
+ * DebounceIn d(p5);
+ * DigitialOut led1(LED1);
+ * DigitialOut led2(LED2);
+ *
+ * int main() {
+ * while(1) {
+ * led1 = d;
+ * led2 = d.read();
+ * }
+ * }
+ * @endcode
+ *
+ * @see set_debounce_us() To change the sampling frequency.
+ * @see set_samples() To alter the number of samples.
+ *
+ * Users of this library may also be interested in PinDetect library:-
+ * @see http://mbed.org/users/AjK/libraries/PinDetect/latest
+ *
+ * This example shows one input displayed by two outputs. The input
+ * is debounced by the default 10ms.
+ */
+
+namespace AjK {
+
+class DebounceIn {
+ public:
+
+ friend class DigitalIn;
+ friend class Ticker;
+
+ /** set_debounce_us
+ *
+ * Sets the debounce sample period time in microseconds, default is 25000 (25ms)
+ *
+ * @param uint32_t i The debounce sample period time to set.
+ */
+// void set_debounce_us(int i) { _ticker.attach_us(this, &DebounceIn::_callback, i); }
+ void set_debounce_us(uint32_t i) { _debounce_us = i; _ticker->attach_us(callback(this, &DebounceIn::_callback), i); }
+
+ /** get_debounce_us
+ *
+ * Gets the debounce sample period time in microseconds
+ *
+ */
+ uint32_t get_debounce_us(void) { return _debounce_us; } // // //
+
+ /** set_samples
+ *
+ * Defines the number of samples before switching the shadow
+ * definition of the pin.
+ *
+ * @param int i The number of samples.
+ */
+ void set_samples(uint8_t i) { _samples = i; }
+
+ /** get_samples
+ *
+ * Gets the number of samples before switching the shadow
+ * definition of the pin.
+ *
+ */
+ uint8_t get_samples(void) { return _samples; }
+
+ /** read
+ *
+ * Read the value of the debounced pin.
+ */
+ int8_t read(void) { return this->_shadow; }
+
+#ifdef MBED_OPERATORS
+ /** operator int()
+ *
+ * Read the value of the debounced pin.
+ */
+ operator int() { return read(); }
+#endif
+
+ /** Set the pin mode.
+ *
+ * @see http://mbed.org/projects/libraries/api/mbed/trunk/DigitalInOut#DigitalInOut.mode
+ * @param PinMode m The mode to pass on to the DigitalIn
+ */
+ void mode(PinMode m) { _digital_in->mode( m ); }
+
+ /** get_edge_direction
+ *
+ * Gets the value of the debounced pin' edge direction (rising/falling).
+ */
+ int8_t get_edge_direction(void) { return _edge_direction; } // // //
+
+ /** get_edge_direction_acted_upon
+ *
+ * Gets the debounced pin edge direction' usage status (used/unused)
+ */
+ bool get_edge_direction_acted_upon(void) { return _edge_direction_acted_upon; } // // //
+
+ /** set_edge_direction_acted_upon
+ *
+ * Sets the status of the debounced pin edge direction' as used
+ */
+ void set_edge_direction_acted_upon(void) { _edge_direction_acted_upon = 1; } // // //
+
+ /** Constructor
+ *
+ * @param PinName pin The pin to assign as an input.
+ */
+ DebounceIn() { error("DebounceIn: You must supply a PinName"); }
+
+ DebounceIn(PinName pin) { _init(pin, PullNone); };
+
+ DebounceIn(PinName pin, PinMode mode) { _init(pin, mode); };
+
+ /** DebounceIn destructor
+ */
+ ~DebounceIn() {
+ if ( _ticker ) { delete( _ticker ); }
+ if ( _digital_in ) { delete( _digital_in ); }
+ }
+
+ protected:
+
+ /** initialise class
+ *
+ * @param PinName p is a valid pin that supports DigitalIn
+ * @param PinMode m The mode the DigitalIn should use.
+ */
+ void _init(PinName p, PinMode m) {
+
+ _counter = 0;
+ _samples = 10;
+ _shadow = -1;
+ _edge_direction = -1;
+ _edge_direction_acted_upon = 1;
+
+ _digital_in = new DigitalIn( p, m );
+ _ticker = new Ticker;
+
+ set_debounce_us(25000);
+ }
+
+ void _callback(void) {
+
+ if (_digital_in->read() == 1) {
+ if (_counter < _samples) _counter++;
+ if (_counter == _samples) {
+ if (_shadow == 0) { _edge_direction = 1; _edge_direction_acted_upon = 0; } // // //
+ _shadow = 1;
+ }
+ }
+ else {
+ if (_counter > 0) _counter--;
+ if (_counter == 0) {
+ if (_shadow == 1) { _edge_direction = 0; _edge_direction_acted_upon = 0; } // // //
+ _shadow = 0;
+ }
+ }
+ }
+
+ DigitalIn *_digital_in;
+ Ticker *_ticker;
+ int8_t _shadow;
+ uint32_t _debounce_us; // // //
+ int8_t _edge_direction; // // //
+ bool _edge_direction_acted_upon; // // //
+ uint8_t _counter;
+ uint8_t _samples;
+};
+
+}; // namespace AjK ends.
+
+using namespace AjK;
+
+#endif
+
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Sun Nov 04 16:49:49 2018 +0000
@@ -0,0 +1,200 @@
+#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);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Sun Nov 04 16:49:49 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/armmbed/mbed-os/#ed7fe215002a9c50452acfecda633ce47e00c614
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json Sun Nov 04 16:49:49 2018 +0000
@@ -0,0 +1,23 @@
+{
+ "macros": [
+ "MBED_HEAP_STATS_ENABLED=1",
+ "MBED_STACK_STATS_ENABLED=1",
+ "MBED_MEM_TRACING_ENABLED=1",
+ "MBED_TRAP_ERRORS_ENABLED=1",
+ "MBED_DEBUG=0"
+ ],
+ "target_overrides": {
+ "*": {
+ "platform.stdio-baud-rate": 115200,
+ "platform.stdio-convert-newlines": true
+ }
+ },
+ "config": {
+ "main-stack-size": {
+ "value": 720
+ },
+ "thread-stack-size": {
+ "value": 640
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json.txt Sun Nov 04 16:49:49 2018 +0000
@@ -0,0 +1,23 @@
+{
+ "macros": [
+ "MBED_HEAP_STATS_ENABLED=1",
+ "MBED_STACK_STATS_ENABLED=1",
+ "MBED_MEM_TRACING_ENABLED=1",
+ "MBED_TRAP_ERRORS_ENABLED=1",
+ "MBED_DEBUG=1"
+ ],
+ "target_overrides": {
+ "*": {
+ "platform.stdio-baud-rate": 115200,
+ "platform.stdio-convert-newlines": true
+ }
+ },
+ "config": {
+ "main-stack-size": {
+ "value": 720
+ },
+ "thread-stack-size": {
+ "value": 640
+ }
+ }
+}