DevCore: DevBoy software part

    Hello friends!

    Nikolay again with you, in the last article " DevBoy - how I created a project with an open source device and launched a project on Kickstarter " the emphasis was more on appearance and hardware, today we'll talk about how it is done " inside " and analyze the software part.



    Who cares - I ask under the cat.

    As mentioned earlier, the project is based on STM32F415RG microcontroller from STMicroelectronics on the ARM Cortex-M4 core. For development for these microcontrollers, there are several different IDEs, but for an open project you need at least a free IDE, and better still Open Source. In addition, the IDE should still be supported in STM32CubeMX . At that time, when I started working on this project, there was only one IDE that meets all these requirements - System Workbench for STM32 .

    Atollic TrueStudio still exists, which became free after STMicroelectronics bought them.

    The next program used is STM32CubeMX. This program is a utility for configuring the periphery of the microcontroller using a graphical interface.

    The result is code that includes H ardware A bstraction L ayer (HAL). Many programmers are not very fond of this " creation ", it is not devoid of bugs, but, nevertheless, it greatly simplifies the development and improves the portability of programs between different microcontrollers from STMicroelectronics.

    In addition, the configuration can be set to use some third-party open source software such as FreeRTOS , FatFS, and some others.

    The description of the software used is finished, now we turn to the most interesting - toDevCore . The name comes from the " Core Development " let's go in order.

    First of all it is C ++ RTOS Wrapper ( FreeRTOS in this case ). Wrapper is needed for two reasons:

    • It is much more pleasant to create an object, and then call for example mutex.Take (), than create a handle, call the create function, and then pass this handle to all functions of working with mutexes.
    • If it is necessary to replace the RTOS, it is enough to replace the wrapper, and not all calls of the RTOS functions from the code

    There is no point in citing the wrapper code here, who cares - look at GitHub , and we move on.

    The next part is the Application Framework . This is the base class for all tasks. Since these are only two relatively small files, it makes sense to bring them in full:

    Header
    //******************************************************************************//  @file AppTask.h//  @author Nicolai Shlapunov////  @details DevCore: Application Task Base Class, header////  @section LICENSE////   Software License Agreement (Modified BSD License)////   Copyright (c) 2016, Devtronic & Nicolai Shlapunov//   All rights reserved.////   Redistribution and use in source and binary forms, with or without//   modification, are permitted provided that the following conditions are met:////   1. Redistributions of source code must retain the above copyright//      notice, this list of conditions and the following disclaimer.//   2. Redistributions in binary form must reproduce the above copyright//      notice, this list of conditions and the following disclaimer in the//      documentation and/or other materials provided with the distribution.//   3. Neither the name of the Devtronic nor the names of its contributors//      may be used to endorse or promote products derived from this software//      without specific prior written permission.//   4. Redistribution and use of this software other than as permitted under//      this license is void and will automatically terminate your rights under//      this license.////   THIS SOFTWARE IS PROVIDED BY DEVTRONIC ''AS IS'' AND ANY EXPRESS OR IMPLIED//   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF//   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.//   IN NO EVENT SHALL DEVTRONIC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,//   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR//   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY//   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING//   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS//   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.////  @section SUPPORT////   Devtronic invests time and resources providing this open source code,//   please support Devtronic and open-source hardware/software by//   donations and/or purchasing products from Devtronic.////******************************************************************************#ifndef AppTask_h#define AppTask_h// *****************************************************************************// ***   Includes   ************************************************************// *****************************************************************************#include"DevCfg.h"// *****************************************************************************// * AppTask class. This class is wrapper for call C++ function from class. ****// *****************************************************************************classAppTask
    {public:
        // *************************************************************************// ***   Init Task   *******************************************************// *************************************************************************virtualvoidInitTask(void){CreateTask();}
      protected:
        // *************************************************************************// ***   Constructor   *****************************************************// *************************************************************************
        AppTask(uint16_t stk_size, uint8_t task_prio, constchar name[],
                uint16_t queue_len = 0U, uint16_t queue_msg_size = 0U,
                void* task_msg_p = nullptr, uint32_t task_interval_ms = 0U) :
          ctrl_queue((queue_len + 2U), sizeof(CtrlQueueMsg)),
          task_queue(queue_len, queue_msg_size), task_msg_ptr(task_msg_p),
          timer(task_interval_ms, RtosTimer::REPEATING, TimerCallback, (void*)this),
          stack_size(stk_size), task_priority(task_prio), task_name(name) {};
        // *************************************************************************// ***   Virtual destructor - prevent warning   ****************************// *************************************************************************virtual ~AppTask() {};
        // *************************************************************************// ***   Create task function   ********************************************// *************************************************************************// * This function creates new task in FreeRTOS, provide pointer to function// * and pointer to class as parameter. When TaskFunctionCallback() called// * from FreeRTOS, it use pointer to class from parameter to call virtual// * functions.voidCreateTask();
        // *************************************************************************// ***   Setup function   **************************************************// *************************************************************************// * * virtual function - some tasks may not have Setup() actionsvirtual Result Setup(){return Result::RESULT_OK;}
        // *************************************************************************// ***   IntervalTimerExpired function   ***********************************// *************************************************************************// * Empty virtual function - some tasks may not have TimerExpired() actionsvirtual Result TimerExpired(){return Result::RESULT_OK;}
        // *************************************************************************// ***   ProcessMessage function   *****************************************// *************************************************************************// * Empty virtual function - some tasks may not have ProcessMessage() actionsvirtual Result ProcessMessage(){return Result::RESULT_OK;}
        // *************************************************************************// ***   Loop function   ***************************************************// *************************************************************************// * Empty virtual function - some tasks may not have Loop() actionsvirtual Result Loop(){return Result::RESULT_OK;}
        // *************************************************************************// ***   SendTaskMessage function   ****************************************// *************************************************************************Result SendTaskMessage(constvoid* task_msg, bool is_priority = false);
      private:
        // Task control queue message typesenum CtrlQueueMsgType
        {
           CTRL_TIMER_MSG,
           CTRL_TASK_QUEUE_MSG
        };
        // Task control queue message structstructCtrlQueueMsg
        {
          CtrlQueueMsgType type;
        };
        // Task control queue
        RtosQueue ctrl_queue;
        // Task queue
        RtosQueue task_queue;
        // Pointer to receive message buffervoid* task_msg_ptr;
        // Timer object
        RtosTimer timer;
        // Task stack sizeuint16_t stack_size;
        // Task priorityuint8_t task_priority;
        // Pointer to the task nameconstchar* task_name;
        // *************************************************************************// ***   IntLoop function   ************************************************// *************************************************************************Result IntLoop();
        // *************************************************************************// ***   TaskFunctionCallback   ********************************************// *************************************************************************staticvoidTaskFunctionCallback(void* ptr);
        // *************************************************************************// ***   IntervalTimerCallback function   **********************************// *************************************************************************staticvoidTimerCallback(void* ptr);
        // *************************************************************************// ***   SendControlMessage function   *************************************// *************************************************************************Result SendControlMessage(const CtrlQueueMsg& ctrl_msg, bool is_priority = false);
        // *************************************************************************// ***   Change counter   **************************************************// *************************************************************************staticvoidChangeCnt(bool is_up);
        // *************************************************************************// ***   Private constructor and assign operator - prevent copying   *******// *************************************************************************
        AppTask();
        AppTask(const AppTask&);
        AppTask& operator=(const AppTask&);
    };
    #endif
    Code
    //******************************************************************************//  @file AppTask.cpp//  @author Nicolai Shlapunov////  @details DevCore: Application Task Base Class, implementation////  @copyright Copyright (c) 2016, Devtronic & Nicolai Shlapunov//             All rights reserved.////  @section SUPPORT////   Devtronic invests time and resources providing this open source code,//   please support Devtronic and open-source hardware/software by//   donations and/or purchasing products from Devtronic.////******************************************************************************// *****************************************************************************// ***   Includes   ************************************************************// *****************************************************************************#include"AppTask.h"#include"RtosMutex.h"// *****************************************************************************// ***   Static variables   ****************************************************// *****************************************************************************static RtosMutex startup_mutex;
    staticuint32_t startup_cnt = 0U;
    // *****************************************************************************// ***   Create task function   ************************************************// *****************************************************************************void AppTask::CreateTask()
    {
      Result result = Result::RESULT_OK;
      // If interval timer period isn't zero or task queue presentif((timer.GetTimerPeriod() != 0U) || (task_queue.GetQueueLen() != 0U))
      {
        // Set Control Queue name
        ctrl_queue.SetName(task_name, "Ctrl");
        // Create control queue
        result = ctrl_queue.Create();
      }
      // If task queue presentif(task_queue.GetQueueLen() != 0U)
      {
        // Set Task Queue name
        task_queue.SetName(task_name, "Task");
        // Create task queue
        result |= task_queue.Create();
      }
      // If interval timer period isn't zeroif(timer.GetTimerPeriod() != 0U)
      {
        // Create timer
        result |= timer.Create();
      }
      // Create task: function - TaskFunctionCallback(), parameter - pointer to "this"
      result |= Rtos::TaskCreate(TaskFunctionCallback, task_name, stack_size, this, task_priority);
      // Check resultif(result.IsBad())
      {
        // TODO: implement error handling
        Break();
      }
    }
    // *****************************************************************************// ***   SendTaskMessage function   ********************************************// *****************************************************************************
    Result AppTask::SendTaskMessage(constvoid* task_msg, bool is_priority)
    {
      Result result = Result::RESULT_OK;
      // Send task message to front or back of task queueif(is_priority == true)
      {
        result = task_queue.SendToFront(task_msg);
      }
      else
      {
        result = task_queue.SendToBack(task_msg);
      }
      // If successful - send message to the control queueif(result.IsGood())
      {
        CtrlQueueMsg ctrl_msg;
        ctrl_msg.type = CTRL_TASK_QUEUE_MSG;
        result = SendControlMessage(ctrl_msg, is_priority);
      }
      return result;
    }
    // *****************************************************************************// ***   IntLoop function   ****************************************************// *****************************************************************************
    Result AppTask::IntLoop()
    {
      Result result = Result::RESULT_OK;
      while(result.IsGood())
      {
        // Buffer for control message
        CtrlQueueMsg ctrl_msg;
        // Read on the control queue
        result = ctrl_queue.Receive(&ctrl_msg, timer.GetTimerPeriod() * 2U);
        // If successfulif(result.IsGood())
        {
          // Check message typeswitch(ctrl_msg.type)
          {
            case CTRL_TIMER_MSG:
              result = TimerExpired();
              break;
             case CTRL_TASK_QUEUE_MSG:
             {
               // Non blocking read from the task queue
               result = task_queue.Receive(task_msg_ptr, 0U);
               // If successfulif(result.IsGood())
               {
                 // Process it!
                 result = ProcessMessage();
               }
               break;
             }
             default:
               result = Result::ERR_INVALID_ITEM;
               break;
          }
        }
      }
      return result;
    }
    // *****************************************************************************// ***   TaskFunctionCallback   ************************************************// *****************************************************************************void AppTask::TaskFunctionCallback(void* ptr)
    {
      Result result = Result::ERR_NULL_PTR;
      if(ptr != nullptr)
      {
        // Set good result
        result = Result::RESULT_OK;
        // Get reference to the task object
        AppTask& app_task = *(static_cast<AppTask*>(ptr));
        // Increment counter before call Setup()
        ChangeCnt(true);
        // Call virtual Setup() function from AppTask class
        app_task.Setup();
        // Decrement counter after call Setup()
        ChangeCnt(false);
        // Pause for give other tasks run Setup()
        RtosTick::DelayTicks(1U);
        // Pause while other tasks run Setup() before executing any Loop()while(startup_cnt) RtosTick::DelayTicks(1U);
        // If no timer or queue - just call Loop() functionif((app_task.timer.GetTimerPeriod() == 0U) && (app_task.task_queue.GetQueueLen() == 0U))
        {
          // Call virtual Loop() function from AppTask classwhile(app_task.Loop() == Result::RESULT_OK);
        }
        else
        {
          // Start task timer if neededif(app_task.timer.GetTimerPeriod() != 0U)
          {
            result = app_task.timer.Start();
          }
          // Check resultif(result.IsGood())
          {
            // Call internal AppTask function
            result = app_task.IntLoop();
          }
          // Stop task timer if neededif(app_task.timer.GetTimerPeriod() != 0U)
          {
            result |= app_task.timer.Stop();
          }
        }
      }
      // Check resultif(result.IsBad())
      {
        // TODO: implement error handling
        Break();
      }
      // Delete task after exit
      Rtos::TaskDelete();
    }
    // *****************************************************************************// ***   TimerCallback function   **********************************************// *****************************************************************************void AppTask::TimerCallback(void* ptr)
    {
      Result result = Result::ERR_NULL_PTR;
      if(ptr != nullptr)
      {
        // Get reference to the task object
        AppTask& task = *((AppTask*)ptr);
        // Create control timer message
        CtrlQueueMsg timer_msg;
        timer_msg.type = CTRL_TIMER_MSG;
        // Send message to the control queue
        result = task.SendControlMessage(timer_msg);
      }
      // Check resultif(result.IsBad())
      {
        // TODO: implement error handling
        Break();
      }
    }
    // *****************************************************************************// ***   SendControlMessage function   *****************************************// *****************************************************************************
    Result AppTask::SendControlMessage(const CtrlQueueMsg& ctrl_msg, bool is_priority)
    {
      Result result;
      if(is_priority == true)
      {
        result = ctrl_queue.SendToFront(&ctrl_msg);
      }
      else
      {
        result = ctrl_queue.SendToBack(&ctrl_msg);
      }
      return result;
    }
    // *****************************************************************************// ***   Change counter   ******************************************************// *****************************************************************************void AppTask::ChangeCnt(bool is_up)
    {
      // Take semaphore before change counter
      startup_mutex.Lock();
      // Check directionif(is_up == true)
      {
        // Increment counter
        startup_cnt++;
      }
      else
      {
        // Decrement counter
        startup_cnt--;
      }
      // Give semaphore after changes
      startup_mutex.Release();
    }
    

    Inherited classes can override 4 virtual functions:

    • Setup () is a function called before running the task. Code completion is guaranteed in all given functions of all tasks before starting the execution of the main loops.
    • Loop () is the main task loop, where the task itself organizes what it wants. Cannot be used with the following two functions.
    • TimerExpired () is a function called periodically at a specified interval. Convenient to implement a sensor survey for example.
    • ProcessMessage () is a function for processing messages from other tasks.

    The first two functions implement the " Arduino-Style " for tasks.

    The next two implement the " event-based " system that simplifies the interaction of tasks. With this approach, the task implements the external interface in the form of functions that send send data to the task through an internal mailbox. With this approach, the user using this interface does not need to worry in what context the actions are performed. True, this is only possible for setters or teams. For getters, it is best to use mutexes and copy data to prevent mutex capture for a long time.

    This approach was overlooked when I developed software for medical equipment. The microcontroller has one "watch dog" ( watchdog) and in the case of multiple tasks, you need to keep track of them all. For this, there was a separate task that served the watchdog and received messages from other tasks sent from the TimerExpired () function. If during the period of the timer of the task * n no messages came - the task died, then we extinguish the light to take action to turn off all the glands affecting the patient.

    All tasks are singletones, you cannot create them directly, but you can get a link to the task. To do this, each task implements the static method GetInstance () :

    // *****************************************************************************// ***   Get Instance   ********************************************************// *****************************************************************************
    Application& Application::GetInstance(void)
    {
       static Application application;
       return application;
    }

    Also included are tasks for audio output , for input modules and for screen maintenance.

    The task of sound output is quite simple - it receives an array of frequencies and durations and just periodically changes the timer settings to generate square-wave pulses of a certain frequency.

    Header
    //******************************************************************************//  @file SoundDrv.h//  @author Nicolai Shlapunov////  @details DevCore: Sound Driver Class, header////  @section LICENSE////   Software License Agreement (Modified BSD License)////   Copyright (c) 2016, Devtronic & Nicolai Shlapunov//   All rights reserved.////   Redistribution and use in source and binary forms, with or without//   modification, are permitted provided that the following conditions are met:////   1. Redistributions of source code must retain the above copyright//      notice, this list of conditions and the following disclaimer.//   2. Redistributions in binary form must reproduce the above copyright//      notice, this list of conditions and the following disclaimer in the//      documentation and/or other materials provided with the distribution.//   3. Neither the name of the Devtronic nor the names of its contributors//      may be used to endorse or promote products derived from this software//      without specific prior written permission.//   4. Redistribution and use of this software other than as permitted under//      this license is void and will automatically terminate your rights under//      this license.////   THIS SOFTWARE IS PROVIDED BY DEVTRONIC ''AS IS'' AND ANY EXPRESS OR IMPLIED//   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF//   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.//   IN NO EVENT SHALL DEVTRONIC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,//   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR//   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY//   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING//   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS//   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.////  @section SUPPORT////   Devtronic invests time and resources providing this open source code,//   please support Devtronic and open-source hardware/software by//   donations and/or purchasing products from Devtronic.////******************************************************************************#ifndef SoundDrv_h#define SoundDrv_h// *****************************************************************************// ***   Includes   ************************************************************// *****************************************************************************#include"DevCfg.h"#include"AppTask.h"#include"RtosMutex.h"#include"RtosSemaphore.h"// *****************************************************************************// ***   Sound Driver Class. This class implement work with sound.   ***********// *****************************************************************************classSoundDrv :public AppTask
    {
      public:
        // *************************************************************************// ***   Get Instance   ****************************************************// *************************************************************************// * This class is singleton. For use this class you must call GetInstance()// * to receive reference to Sound Driver classstatic SoundDrv& GetInstance(void);
        // *************************************************************************// ***   Init Sound Driver Task   ******************************************// *************************************************************************virtualvoidInitTask(TIM_HandleTypeDef *htm);
        // *************************************************************************// ***   Sound Driver Setup   **********************************************// *************************************************************************virtual Result Setup();
        // *************************************************************************// ***   Sound Driver Loop   ***********************************************// *************************************************************************virtual Result Loop();
        // *************************************************************************// ***   Beep function   ***************************************************// *************************************************************************voidBeep(uint16_t freq, uint16_t del, bool pause_after_play = false);
        // *************************************************************************// ***   Play sound function   *********************************************// *************************************************************************voidPlaySound(constuint16_t* melody, uint16_t size, uint16_t temp_ms = 100U, bool rep = false);
        // *************************************************************************// ***   Stop sound function   *********************************************// *************************************************************************voidStopSound(void);
        // *************************************************************************// ***   Mute sound function   *********************************************// *************************************************************************voidMute(bool mute_flag);
        // *************************************************************************// ***   Is sound played function   ****************************************// *************************************************************************boolIsSoundPlayed(void);
      private:
        // Timer handle
        TIM_HandleTypeDef* htim = SOUND_HTIM;
        // Timer channeluint32_t channel = SOUND_CHANNEL;
        // Ticks variableuint32_t last_wake_ticks = 0U;
        // Pointer to table contains melodyconstuint16_t* sound_table = nullptr;
        // Size of tableuint16_t sound_table_size = 0U;
        // Current positionuint16_t sound_table_position = 0U;
        // Current frequency delayuint16_t current_delay = 0U;
        // Time for one frequency in msuint32_t delay_ms = 100U;
        // Repeat flagbool repeat = false;
        // Mute flagbool mute = false;
        // Mutex to synchronize when playing melody frames
        RtosMutex melody_mutex;
        // Semaphore for start play sound
        RtosSemaphore sound_update;
        // *************************************************************************// ***   Process Button Input function   ***********************************// *************************************************************************voidTone(uint16_t freq);
        // *************************************************************************// ** Private constructor. Only GetInstance() allow to access this class. **// *************************************************************************
        SoundDrv() : AppTask(SOUND_DRV_TASK_STACK_SIZE, SOUND_DRV_TASK_PRIORITY,
                             "SoundDrv") {};
    };
    #endif


    The task of servicing modules is also quite simple. Of the interesting points is the autodetection of the module: first, using the ADC, we measure the voltage, if it is in the range from 25% to 75% of the supply voltage - an analog joystick is inserted, otherwise buttons or an encoder. If this is not a joystick, check the fourth line of the I / O module: if it is high, these are buttons ( all buttons are pulled up to power and when the button is pressed they close to ground ), if it’s low, it is an encoder ( small button is “pulled” to ground and when pressed closes on power ).

    Header
    //******************************************************************************//  @file InputDrv.h//  @author Nicolai Shlapunov////  @details DevCore: Input Driver Class, header////  @section LICENSE////   Software License Agreement (Modified BSD License)////   Copyright (c) 2016, Devtronic & Nicolai Shlapunov//   All rights reserved.////   Redistribution and use in source and binary forms, with or without//   modification, are permitted provided that the following conditions are met:////   1. Redistributions of source code must retain the above copyright//      notice, this list of conditions and the following disclaimer.//   2. Redistributions in binary form must reproduce the above copyright//      notice, this list of conditions and the following disclaimer in the//      documentation and/or other materials provided with the distribution.//   3. Neither the name of the Devtronic nor the names of its contributors//      may be used to endorse or promote products derived from this software//      without specific prior written permission.//   4. Redistribution and use of this software other than as permitted under//      this license is void and will automatically terminate your rights under//      this license.////   THIS SOFTWARE IS PROVIDED BY DEVTRONIC ''AS IS'' AND ANY EXPRESS OR IMPLIED//   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF//   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.//   IN NO EVENT SHALL DEVTRONIC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,//   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR//   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY//   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING//   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS//   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.////  @section SUPPORT////   Devtronic invests time and resources providing this open source code,//   please support Devtronic and open-source hardware/software by//   donations and/or purchasing products from Devtronic.////******************************************************************************#ifndef InputDrv_h#define InputDrv_h// *****************************************************************************// ***   Includes   ************************************************************// *****************************************************************************#include"DevCfg.h"#include"AppTask.h"// *****************************************************************************// * Input Driver Class. This class implement work with user input elements like // * buttons and encoders.classInputDrv :public AppTask
    {
      public:
        // *************************************************************************// ***   Enum with all buttons   *******************************************// *************************************************************************typedefenum 
        {
          EXT_LEFT,  // Left ext port
          EXT_RIGHT, // Right ext port
          EXT_MAX    // Ext port count
        } PortType;
        // *************************************************************************// ***   Enum with all devices types   *************************************// *************************************************************************typedefenum 
        {
          EXT_DEV_NONE, // No device
          EXT_DEV_BTN,  // Buttons(cross)
          EXT_DEV_ENC,  // Encoder
          EXT_DEV_JOY,  // Joystick
          EXT_DEV_MAX   // Device types count
        } ExtDeviceType;
        // *************************************************************************// ***   Enum with all buttons   *******************************************// *************************************************************************typedefenum 
        {
          BTN_UP,    // Up button
          BTN_LEFT,  // Left button
          BTN_DOWN,  // Down button
          BTN_RIGHT, // Right button
          BTN_MAX    // Buttons count
        } ButtonType;
        // *************************************************************************// ***   Enum with all encoder buttons   ***********************************// *************************************************************************typedefenum
        {
          ENC_BTN_ENT,   // Press on the knob
          ENC_BTN_BACK,  // Small button
          ENC_BTN_MAX    // Buttons count
        } EncButtonType;
        // *************************************************************************// ***   Get Instance   ****************************************************// *************************************************************************// * This class is singleton. For use this class you must call GetInstance()// * to receive reference to Input Driver classstatic InputDrv& GetInstance(void);
        // *************************************************************************// ***   Init Input Driver Task   ******************************************// *************************************************************************// * This function initialize Input Driver class. If htim provided, this // * timer will be used instead FreeRTOS task.virtualvoidInitTask(TIM_HandleTypeDef* htm, ADC_HandleTypeDef* had);
        // *************************************************************************// ***   Input Driver Setup   **********************************************// *************************************************************************virtual Result Setup();
        // *************************************************************************// ***   Input Driver Loop   ***********************************************// *************************************************************************// * If FreeRTOS task used, this function just call ProcessInput() with 1 ms// * period. If FreeRTOS tick is 1 ms - this task must have highest priority virtual Result Loop();
        // *************************************************************************// ***   Process Input function   ******************************************// *************************************************************************// * Main class function - must call periodically for process user input.// * If timer used, this function must be called from interrupt handler.voidProcessInput(void);
        // *************************************************************************// ***   Process Encoders Input function   *********************************// *************************************************************************voidProcessEncodersInput(void);
        // *************************************************************************// ***   Get device type   *************************************************// *************************************************************************ExtDeviceType GetDeviceType(PortType port);
        // *************************************************************************// ***   Get button state   ************************************************// *************************************************************************// Return button state: true - pressed, false - unpressedboolGetButtonState(PortType port, ButtonType button);
        // *************************************************************************// ***   Get button state   ************************************************// *************************************************************************// Return button state change flag: true - changed, false - not changedboolGetButtonState(PortType port, ButtonType button, bool& btn_state);
        // *************************************************************************// ***   Get encoder counts from last call   *******************************// *************************************************************************// * Return state of encoder. Class counts encoder clicks and stored inside.// * This function substract from current encoder counter last_enc_val and// * return it to user. Before return last_enc_val will be assigned to// * current encoder counter.int32_t GetEncoderState(PortType port, int32_t& last_enc_val);
        // *************************************************************************// ***   Get button state   ************************************************// *************************************************************************// Return button state: true - pressed, false - unpressedboolGetEncoderButtonState(PortType port, EncButtonType button);
        // *************************************************************************// ***   Get encoder button state   ****************************************// *************************************************************************// Return button state: true - pressed, false - unpressedboolGetEncoderButtonState(PortType port, EncButtonType button, bool& btn_state);
        // *************************************************************************// ***   Get joystick counts from last call   ******************************// *************************************************************************voidGetJoystickState(PortType port, int32_t& x, int32_t& y);
        // *************************************************************************// ***   SetJoystickCalibrationConsts   ************************************// *************************************************************************// * Set calibration constants. Must be call for calibration joystick.voidSetJoystickCalibrationConsts(PortType port, int32_t x_mid,
                                          int32_t x_kmin, int32_t x_kmax,
                                          int32_t y_mid, int32_t y_kmin,
                                          int32_t y_kmax);
        // *************************************************************************// ***   Get joystick button state   ***************************************// *************************************************************************// Return button state: true - pressed, false - unpressedboolGetJoystickButtonState(PortType port);
        // *************************************************************************// ***   Get joystick button state   ***************************************// *************************************************************************// Return button state: true - pressed, false - unpressedboolGetJoystickButtonState(PortType port, bool& btn_state);
      private:
        // How many cycles button must change state before state will be changed in// result returned by GetButtonState() function. For reduce debouncingconststaticuint32_t BUTTON_READ_DELAY = 4U;
        // Coefficient for calibrationconststaticint32_t COEF = 100;
        // ADC max value - 12 bitconststaticint32_t ADC_MAX_VAL = 0xFFF;
        // Joystich thresholdconststaticint32_t JOY_THRESHOLD = 1000;
        // Ticks variableuint32_t last_wake_ticks = 0U;
        // *************************************************************************// ***   Structure to describe button   ************************************// *************************************************************************typedefstruct
        {bool btn_state;           // Button state returned by GetButtonState() functionbool btn_state_tmp;       // Temporary button state for reduce debouncinguint8_t btn_state_cnt;    // Counter for reduce debouncing
          GPIO_TypeDef* button_port;// Button portuint16_t button_pin;      // Button pin
          GPIO_PinState pin_state;  // High/low on input treated as pressed
        } ButtonProfile;
        // *************************************************************************// ***   Structure to describe encoder   ***********************************// *************************************************************************typedefstruct
        {// Encoder rotationint32_t enc_cnt;            // Encoder counteruint8_t enc_state;          // Current state of encder clock & data pins
          GPIO_TypeDef* enc_clk_port; // Encoder clock portuint16_t enc_clk_pin;       // Encoder clock pin
          GPIO_TypeDef* enc_data_port;// Encoder data portuint16_t enc_data_pin;      // Encoder data pin
        } EncoderProfile;
        // *************************************************************************// ***   Structure to describe joysticks   *********************************// *************************************************************************typedefstruct
        {int32_t x_ch_val;     // Joystick X axis valueuint32_t x_channel;   // Joystick X axis ADC channel
          GPIO_TypeDef* x_port; // Joystick X axis portuint16_t x_pin;       // Joystick X axis pin int32_t bx;           // Joystick X offsetint32_t kxmin;        // Joystick X coefficientint32_t kxmax;        // Joystick X coefficientbool x_inverted;      // Joystick X inverted flagint32_t y_ch_val;     // Joystick Y axis valueuint32_t y_channel;   // Joystick Y axis ADC channel
          GPIO_TypeDef* y_port; // Joystick Y axis portuint16_t y_pin;       // Joystick Y axis pinint32_t by;           // Joystick Y offsetint32_t kymin;        // Joystick Y coefficientint32_t kymax;        // Joystick Y coefficientbool y_inverted;      // Joystick Y inverted flag
        } JoystickProfile;
        // *************************************************************************// ***   Structure to describe encoders   **********************************// *************************************************************************typedefstruct
        {
          EncoderProfile enc;
          ButtonProfile  btn[ENC_BTN_MAX];
        } DevEncoders;
        // *************************************************************************// ***   Structure to describe encoders   **********************************// *************************************************************************typedefstruct
        {
          JoystickProfile joy;
          ButtonProfile   btn;
        } DevJoysticks;
        // *************************************************************************// ***   Structure to describe buttons   ***********************************// *************************************************************************typedefstruct
        {
          ButtonProfile button[BTN_MAX];
        } DevButtons;
        // ***   Array describes types of connected devices  ***********************
        ExtDeviceType devices[EXT_MAX];
        // ***   Structures array for describe buttons inputs  *********************
        DevButtons buttons[EXT_MAX] =
        {
          // Left device
          {{{false, false, 0, EXT_L1_GPIO_Port, EXT_L1_Pin, GPIO_PIN_RESET},
            {false, false, 0, EXT_L2_GPIO_Port, EXT_L2_Pin, GPIO_PIN_RESET},
            {false, false, 0, EXT_L3_GPIO_Port, EXT_L3_Pin, GPIO_PIN_RESET},
            {false, false, 0, EXT_L4_GPIO_Port, EXT_L4_Pin, GPIO_PIN_RESET}}},
          // Right device
          {{{false, false, 0, EXT_R1_GPIO_Port, EXT_R1_Pin, GPIO_PIN_RESET},
            {false, false, 0, EXT_R2_GPIO_Port, EXT_R2_Pin, GPIO_PIN_RESET},
            {false, false, 0, EXT_R3_GPIO_Port, EXT_R3_Pin, GPIO_PIN_RESET},
            {false, false, 0, EXT_R4_GPIO_Port, EXT_R4_Pin, GPIO_PIN_RESET}}}
        };
        // ***   Structures array for describe encoders inputs  ********************
        DevEncoders encoders[EXT_MAX] =
        {
          // Left device
          {{0, 0, EXT_L1_GPIO_Port, EXT_L1_Pin, EXT_L2_GPIO_Port, EXT_L2_Pin}, // Encoder
           {{false, false, 0, EXT_L3_GPIO_Port, EXT_L3_Pin, GPIO_PIN_RESET},   // Button Enter
            {false, false, 0, EXT_L4_GPIO_Port, EXT_L4_Pin, GPIO_PIN_SET}}},   // Button Back// Right device
          {{0, 0, EXT_R1_GPIO_Port, EXT_R1_Pin, EXT_R2_GPIO_Port, EXT_R2_Pin}, // Encoder
           {{false, false, 0, EXT_R3_GPIO_Port, EXT_R3_Pin, GPIO_PIN_RESET},   // Button Enter
            {false, false, 0, EXT_R4_GPIO_Port, EXT_R4_Pin, GPIO_PIN_SET}}}    // Button Back
        };
        // ***   Structures array for describe encoders inputs  ********************
        DevJoysticks joysticks[EXT_MAX] =
        {
          // Left device
          {{0, ADC_CHANNEL_11, EXT_L2_GPIO_Port, EXT_L2_Pin, 0, COEF, COEF, false, // Joystick0, ADC_CHANNEL_10, EXT_L1_GPIO_Port, EXT_L1_Pin, 0, COEF, COEF, true},
           {false, false, 0, EXT_L3_GPIO_Port, EXT_L3_Pin, GPIO_PIN_RESET}},       // Button// Right device
          {{0, ADC_CHANNEL_13, EXT_R2_GPIO_Port, EXT_R2_Pin, 0, COEF, COEF, false, // Joystick0, ADC_CHANNEL_12, EXT_R1_GPIO_Port, EXT_R1_Pin, 0, COEF, COEF, true},
           {false, false, 0, EXT_R3_GPIO_Port, EXT_R3_Pin, GPIO_PIN_RESET}}        // Button
        };
        // Handle to timer used for process encoders input
        TIM_HandleTypeDef* htim = nullptr;
        // Handle to timer used for process encoders input
        ADC_HandleTypeDef* hadc = nullptr;
        // *************************************************************************// ***   Process Button Input function   ***********************************// *************************************************************************voidProcessButtonInput(ButtonProfile& button);
        // *************************************************************************// ***   Process Encoder Input function   **********************************// *************************************************************************voidProcessEncoderInput(EncoderProfile& encoder);
        // *************************************************************************// ***   Process Joystick Input function   *********************************// *************************************************************************voidProcessJoystickInput(JoystickProfile& joysticks, PortType port);
        // *************************************************************************// ***   Emulate buttons using joystick function   *************************// *************************************************************************voidEmulateButtonsByJoystick(PortType port);
        // *************************************************************************// ***   Emulate encoders using buttons function   *************************// *************************************************************************voidEmulateEncodersByButtons(PortType port);
        // *************************************************************************// ***   Configure inputs devices types   **********************************// *************************************************************************ExtDeviceType DetectDeviceType(PortType port);
        // *************************************************************************// ***   Configure ADC   ***************************************************// *************************************************************************voidConfigADC(ExtDeviceType dev_left, ExtDeviceType dev_right);
        // *************************************************************************// ***   Configure inputs for read digital/analog data   *******************// *************************************************************************voidConfigInputIO(bool is_digital, PortType port);
        // *************************************************************************// ** Private constructor. Only GetInstance() allow to access this class. **// *************************************************************************
        InputDrv() : AppTask(INPUT_DRV_TASK_STACK_SIZE, INPUT_DRV_TASK_PRIORITY,
                             "InputDrv") {};
    };
    #endif

    The screen maintenance task is the most interesting task. Let's start with the fact that the screen is 320x240x16bit, in total you need 153600 bytes for the framebuffer. This is not just a lot, it is just huge - in this microcontroller there is only 192k of RAM, and in microcontrollers it may simply not be the right size at all. How to be? The answer is simple: draw the screen in pieces! But you can draw in different ways ...

    The solution that I used for this task is just like all ingenious. It has a buffer on two screen lines. In one line we draw everything that should be, we send it to the screen via SPI in DMA mode, and at this time we can prepare another line.

    Where does the task know what should be in the string and how to draw it? And she does not know!But she has a list of objects that know how to draw herself. Each such object is inherited from the VisObject class .

    Header
    //******************************************************************************//  @file VisObject.h//  @author Nicolai Shlapunov////  @details DevCore: Visual Object Base Class, header////  @section LICENSE////   Software License Agreement (BSD License)////   Copyright (c) 2016, Devtronic & Nicolai Shlapunov//   All rights reserved.////   Redistribution and use in source and binary forms, with or without//   modification, are permitted provided that the following conditions are met://   1. Redistributions of source code must retain the above copyright//      notice, this list of conditions and the following disclaimer.//   2. Redistributions in binary form must reproduce the above copyright//      notice, this list of conditions and the following disclaimer in the//      documentation and/or other materials provided with the distribution.//   3. Neither the name of the Devtronic nor the names of its contributors//      may be used to endorse or promote products derived from this software//      without specific prior written permission.////   THIS SOFTWARE IS PROVIDED BY DEVTRONIC ''AS IS'' AND ANY EXPRESS OR IMPLIED//   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF//   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.//   IN NO EVENT SHALL DEVTRONIC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,//   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR//   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY//   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING//   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS//   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.////******************************************************************************#ifndef VisObject_h#define VisObject_h// *****************************************************************************// ***   Includes   ************************************************************// *****************************************************************************#include"DevCfg.h"// *****************************************************************************// * VisObject class. This class implements base Visual Objects properties.classVisObject
    {public:
        // *************************************************************************// ***   Action   **********************************************************// ************************************************************************* typedefenum
        {
          ACT_TOUCH,   // When object touched
          ACT_UNTOUCH, // When object detouched
          ACT_MOVE,    // When object moved on object
          ACT_MOVEIN,  // When object moved in to object
          ACT_MOVEOUT, // When object moved out of object
          ACT_MAX      // Total possible actions
        } ActionType;
        // *************************************************************************// ***   VisObject   *******************************************************// ************************************************************************* 
        VisObject() {};
        // *************************************************************************// ***   ~VisObject   ******************************************************// *************************************************************************// * Destructor. Call DelVisObjectFromList() from DisplayDrv class for// * remove from list before delete and delete semaphore.virtual ~VisObject();
        // *************************************************************************// ***   LockVisObject   ***************************************************// *************************************************************************voidLockVisObject();
        // *************************************************************************// ***   UnlockVisObject   *************************************************// *************************************************************************voidUnlockVisObject();
        // *************************************************************************// ***   Show   ************************************************************// *************************************************************************// * Show VisObject on screen. This function call AddVisObjectToList() from// * DisplayDrv class. When this function calls first time, user must// * provide Z level. In future user can call this function without// * parameters - previously set Z will be used.virtualvoidShow(uint32_t z_pos = 0);
        // *************************************************************************// ***   Hide   ************************************************************// *************************************************************************// * Hide VisObject from screen. This function call DelVisObjectFromList()// * from DisplayDrv class.virtualvoidHide(void);
        // *************************************************************************// ***   IsShow   **********************************************************// *************************************************************************// * Check status of Show Visual Object. Return true if object in DisplayDrv list.virtualboolIsShow(void);
        // *************************************************************************// ***   Move   ************************************************************// *************************************************************************// * Move object on screen. Set new x and y coordinates. If flag is set - // * move is relative, not absolute.virtualvoidMove(int32_t x, int32_t y, bool is_delta = false);
        // *************************************************************************// ***   DrawInBufH   ******************************************************// *************************************************************************// * Draw one horizontal line of object in specified buffer.// * Each derived class must implement this function.virtualvoidDrawInBufH(uint16_t* buf, int32_t n, int32_t row, int32_t start_y = 0)= 0;
        // *************************************************************************// ***   DrawInBufW   ******************************************************// *************************************************************************// * Draw one vertical line of object in specified buffer.// * Each derived class must implement this function.virtualvoidDrawInBufW(uint16_t* buf, int32_t n, int32_t line, int32_t start_x = 0)= 0;
        // *************************************************************************// ***   Action   **********************************************************// *************************************************************************virtualvoidAction(ActionType action, int32_t tx, int32_t ty);
        // *************************************************************************// ***   Return Start X coordinate   ***************************************// *************************************************************************virtual int32_t GetStartX(void){return x_start;};
        // *************************************************************************// ***   Return Start Y coordinate   ***************************************// *************************************************************************virtual int32_t GetStartY(void){return y_start;};
        // *************************************************************************// ***   Return End X coordinate   *****************************************// *************************************************************************virtual int32_t GetEndX(void){return x_end;};
        // *************************************************************************// ***   Return End Y coordinate   *****************************************// *************************************************************************virtual int32_t GetEndY(void){return y_end;};
        // *************************************************************************// ***   Return Width of object   ******************************************// *************************************************************************virtual int32_t GetWidth(void){return width;};
        // *************************************************************************// ***   Return Height of object   *****************************************// *************************************************************************virtual int32_t GetHeight(void){return height;};
      protected:
        // *************************************************************************// ***   Object parameters   ***********************************************// *************************************************************************// X and Y start coordinates of objectint16_t x_start = 0, y_start = 0;
        // X and Y end coordinates of objectint16_t x_end = 0, y_end = 0;
        // Width and Height of objectint16_t width = 0, height = 0;
        // Rotation of objectint8_t rotation = 0;
        // Object activebool active = false;
      private:
        // *************************************************************************// ***   Object parameters   ***********************************************// *************************************************************************// * Only base class and DisplayDrv have access to this parameters// Z position of objectuint16_t z = 0;
        // Pointer to next object. This pointer need to maker object list. Object// can be added only to one list.
        VisObject* p_next = nullptr;
        // Pointer to next object. This pointer need to maker object list. Object// can be added only to one list.
        VisObject* p_prev = nullptr;
        // DisplayDrv is friend for access to pointers and ZfriendclassDisplayDrv;
    };
    #endif

    The screen maintenance task for each line goes through the list of objects and calls the DrawInBufW () function, passing it a pointer to the buffer, the number of points, the line to be drawn and the starting position (the idea of ​​using the screen controller mode to update the window was not used yet ). In fact, each object draws itself on top of other objects that have already been drawn, and it is easy to arrange objects in the required order simply by placing them in the desired position in the list.

    In addition, this approach allows you to easily integrate the processing of active objects - having received the coordinates from the touchscreen controller, the screen maintenance task can go over the sheet from the end in search of the active object that falls into the coordinates of the touch. If such an object is found, the virtual Action () function is called for this object.

    At the moment there are objects for lines, primitives ( line, square, circle ), pictures and tile maps ( for creating games ).

    Also in DevCore enters another code for some elements of the UI ( such as a menu ), the interface for the I2C driver, I2C driver and libraries for working with sensor BME280 and EEPROM 24S256, but it is not so interesting, and yet I will not describe - and it turned out quite large .

    The full code is available without registration and SMS on GitHub : https://github.com/nickshl/devboy

    P.S. Apparently, the company goes to Epic Fail. For the first week there are only three Bakers, of which a dollar is from some kind of " Innovation Fund"$ 180 from a person who probably learned about this project from an article on Habré ( Thank you, Andrei! ) And the rest from my colleague from a neighboring kyubikl.

    Not raising money is not a problem. The problem is the lack of interest in the project ...

    Also popular now: