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);
}