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 :

/media/uploads/mzcs/img_20181108_073655_hdr-a.jpg

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