DevCore: جزء برنامج من مشروع DevBoy

مرحبا اصدقاء!

نيكولاي معك مرة أخرى ، في المقالة الأخيرة " DevBoy - كيف أنشأت مشروع جهاز مفتوح المصدر وأطلقت مشروعًا على Kickstarter " ، كان التركيز أكثر على المظهر والأجهزة ، اليوم سنتحدث عن كيفية القيام بذلك "في الداخل " ونحلل جزء البرنامج.



من يهتم - أطلب قطة.

كما ذكرنا سابقًا ، يعتمد المشروع على متحكم دقيق STM32F415RG من STMicroelectronics على نواة ARM Cortex-M4. هناك العديد من IDEs المختلفة لتطوير المتحكمات الدقيقة لهذه المتحكم الدقيق ، ومع ذلك ، بالنسبة لمشروع مفتوح المصدر ، تحتاج على الأقل إلى IDE مجاني ، ويفضل أن يكون مفتوح المصدر. بالإضافة إلى ذلك ، لا يزال يجب دعم IDE في STM32CubeMX . في الوقت الذي بدأت فيه العمل في هذا المشروع ، كان هناك IDE واحد فقط استوفى جميع هذه المتطلبات - System Workbench لـ STM32 .

في الوقت الحالي ، هناك Atollic TrueStudio ، الذي أصبح مجانيًا بعد أن اشترته STMicroelectronics.

البرنامج التالي المستخدم هو STM32CubeMX . هذا البرنامج هو أداة لتكوين الأجهزة الطرفية متحكم باستخدام واجهة رسومية.

والنتيجة هي رمز يتضمن H ardware A bractionion L ayer (HAL). العديد من المبرمجين لا يحبون هذا " الخلق " حقًا ، فهو لا يخلو من الأخطاء ، ولكنه مع ذلك يبسط إلى حد كبير تطوير ويحسن قابلية نقل البرامج بين وحدات التحكم الدقيقة المختلفة من STMicroelectronics.

بالإضافة إلى ذلك ، أثناء التكوين ، يمكنك تحديد استخدام بعض برامج الجهات الخارجية مفتوحة المصدر مثل FreeRTOS و FatFS وبعض البرامج الأخرى.

لقد انتهينا من وصف البرنامج المستخدم ، فلننتقل الآن إلى الجزء الأكثر إثارة للاهتمام - DevCore . يأتي الاسم من " التنمية الأساسية " فلنذهب بالترتيب.

بادئ ذي بدء ، هو C ++ RTOS Wrapper ( FreeRTOS في هذه الحالة ). مطلوب Vrapper لسببين:

  • من الأفضل إنشاء كائن ، ثم استدعاء كائن المزامنة () ، على سبيل المثال ، بدلاً من إنشاء مقبض ، واستدعاء وظيفة الإنشاء ، ثم تمرير هذا المقبض إلى جميع وظائف كائن المزامنة
  • إذا كان من الضروري استبدال RTOS ، يكفي استبدال الغلاف ، وليس كل المكالمات إلى وظائف RTOS من التعليمات البرمجية

ليس من المنطقي إحضار كود الغلاف هنا ، من يهتم - نحن ننظر إلى GitHub ، وننتقل .

الجزء التالي هو إطار التطبيق . هذه هي الفئة الأساسية لجميع المهام. نظرًا لأن هذين ملفين صغيرين نسبيًا فقط ، فمن المنطقي إدراجهما بالكامل:

العنوان
//****************************************************************************** // @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. **** // ***************************************************************************** class AppTask { public: // ************************************************************************* // *** Init Task ******************************************************* // ************************************************************************* virtual void InitTask(void) {CreateTask();} protected: // ************************************************************************* // *** Constructor ***************************************************** // ************************************************************************* AppTask(uint16_t stk_size, uint8_t task_prio, const char 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. void CreateTask(); // ************************************************************************* // *** Setup function ************************************************** // ************************************************************************* // * * virtual function - some tasks may not have Setup() actions virtual Result Setup() {return Result::RESULT_OK;} // ************************************************************************* // *** IntervalTimerExpired function *********************************** // ************************************************************************* // * Empty virtual function - some tasks may not have TimerExpired() actions virtual Result TimerExpired() {return Result::RESULT_OK;} // ************************************************************************* // *** ProcessMessage function ***************************************** // ************************************************************************* // * Empty virtual function - some tasks may not have ProcessMessage() actions virtual Result ProcessMessage() {return Result::RESULT_OK;} // ************************************************************************* // *** Loop function *************************************************** // ************************************************************************* // * Empty virtual function - some tasks may not have Loop() actions virtual Result Loop() {return Result::RESULT_OK;} // ************************************************************************* // *** SendTaskMessage function **************************************** // ************************************************************************* Result SendTaskMessage(const void* task_msg, bool is_priority = false); private: // Task control queue message types enum CtrlQueueMsgType { CTRL_TIMER_MSG, CTRL_TASK_QUEUE_MSG }; // Task control queue message struct struct CtrlQueueMsg { CtrlQueueMsgType type; }; // Task control queue RtosQueue ctrl_queue; // Task queue RtosQueue task_queue; // Pointer to receive message buffer void* task_msg_ptr; // Timer object RtosTimer timer; // Task stack size uint16_t stack_size; // Task priority uint8_t task_priority; // Pointer to the task name const char* task_name; // ************************************************************************* // *** IntLoop function ************************************************ // ************************************************************************* Result IntLoop(); // ************************************************************************* // *** TaskFunctionCallback ******************************************** // ************************************************************************* static void TaskFunctionCallback(void* ptr); // ************************************************************************* // *** IntervalTimerCallback function ********************************** // ************************************************************************* static void TimerCallback(void* ptr); // ************************************************************************* // *** SendControlMessage function ************************************* // ************************************************************************* Result SendControlMessage(const CtrlQueueMsg& ctrl_msg, bool is_priority = false); // ************************************************************************* // *** Change counter ************************************************** // ************************************************************************* static void ChangeCnt(bool is_up); // ************************************************************************* // *** Private constructor and assign operator - prevent copying ******* // ************************************************************************* AppTask(); AppTask(const AppTask&); AppTask& operator=(const AppTask&); }; #endif 
كود
 //****************************************************************************** // @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; static uint32_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 present if((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 present if(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 zero if(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 result if(result.IsBad()) { // TODO: implement error handling Break(); } } // ***************************************************************************** // *** SendTaskMessage function ******************************************** // ***************************************************************************** Result AppTask::SendTaskMessage(const void* task_msg, bool is_priority) { Result result = Result::RESULT_OK; // Send task message to front or back of task queue if(is_priority == true) { result = task_queue.SendToFront(task_msg); } else { result = task_queue.SendToBack(task_msg); } // If successful - send message to the control queue if(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 successful if(result.IsGood()) { // Check message type switch(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 successful if(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() function if((app_task.timer.GetTimerPeriod() == 0U) && (app_task.task_queue.GetQueueLen() == 0U)) { // Call virtual Loop() function from AppTask class while(app_task.Loop() == Result::RESULT_OK); } else { // Start task timer if needed if(app_task.timer.GetTimerPeriod() != 0U) { result = app_task.timer.Start(); } // Check result if(result.IsGood()) { // Call internal AppTask function result = app_task.IntLoop(); } // Stop task timer if needed if(app_task.timer.GetTimerPeriod() != 0U) { result |= app_task.timer.Stop(); } } } // Check result if(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 result if(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 direction if(is_up == true) { // Increment counter startup_cnt++; } else { // Decrement counter startup_cnt--; } // Give semaphore after changes startup_mutex.Release(); } 

يمكن للفصول الموروثة تجاوز 4 وظائف افتراضية:

  • Setup () هي وظيفة يتم استدعاؤها قبل بدء المهمة. يتم ضمان اكتمال الرمز في جميع هذه الوظائف لجميع المهام قبل تنفيذ الدورات الرئيسية.
  • Loop () - دورة المهمة الرئيسية ، حيث تنظم المهمة نفسها ما تريد. لا يمكن استخدامها مع الدالتين التاليتين.
  • TimerExpired () - دالة تسمى بشكل دوري بفاصل زمني معين. مناسب لتنفيذ اقتراع جهاز استشعار على سبيل المثال.
  • ProcessMessage () - وظيفة لمعالجة الرسائل من المهام الأخرى.

تقوم الدالتان الأوليان بتنفيذ " Arduino-Style " للمهام.

يقوم الاثنان التاليان بتنفيذ نظام " الحدث " الذي يبسط تفاعل المهام. باستخدام هذا النهج ، تنفذ المهمة واجهة خارجية في شكل وظائف ترسل بيانات لإرسالها إلى المهمة من خلال صندوق بريد داخلي. باستخدام هذا النهج ، لا يحتاج المستخدم الذي يستخدم هذه الواجهة إلى القلق في أي سياق يتم تنفيذ الإجراءات. صحيح ، هذا ممكن فقط للمستوطنين أو الفرق. بالنسبة إلى getters ، من الأفضل استخدام نسخ المزامنة ونسخ البيانات لمنع التقاطها لفترة طويلة.

تم رصد هذا النهج عندما كنت أقوم بتطوير برنامج للمعدات الطبية. يحتوي المتحكم الدقيق على جهاز مراقبة واحد ، وفي حالة العديد من المهام ، تحتاج إلى تتبعها جميعًا. لهذا ، كانت هناك مهمة منفصلة تخدم المراقبة وتلقى رسائل من المهام الأخرى المرسلة من وظيفة TimerExpired (). إذا لم تكن هناك رسائل خلال الفترة الزمنية للمهمة * ، ماتت المهمة ، نطفئ الضوء ونتخذ إجراءات لإيقاف جميع الغدد التي تؤثر على المريض.

جميع المهام فردية ، لا يمكنك إنشاؤها مباشرة ، ولكن يمكنك الحصول على رابط للمهمة. للقيام بذلك ، تطبق كل مهمة أسلوب GetInstance () ثابت:

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

تتضمن أيضًا مهام إخراج الصوت ووحدات الإدخال وصيانة الشاشة.

إن مهمة إخراج الصوت بسيطة للغاية - فهي تتلقى مجموعة من الترددات والمدد وتغيير إعدادات المؤقت بشكل دوري لتوليد نبضات مستطيلة لتردد معين.

العنوان
 //****************************************************************************** // @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. *********** // ***************************************************************************** class SoundDrv : public AppTask { public: // ************************************************************************* // *** Get Instance **************************************************** // ************************************************************************* // * This class is singleton. For use this class you must call GetInstance() // * to receive reference to Sound Driver class static SoundDrv& GetInstance(void); // ************************************************************************* // *** Init Sound Driver Task ****************************************** // ************************************************************************* virtual void InitTask(TIM_HandleTypeDef *htm); // ************************************************************************* // *** Sound Driver Setup ********************************************** // ************************************************************************* virtual Result Setup(); // ************************************************************************* // *** Sound Driver Loop *********************************************** // ************************************************************************* virtual Result Loop(); // ************************************************************************* // *** Beep function *************************************************** // ************************************************************************* void Beep(uint16_t freq, uint16_t del, bool pause_after_play = false); // ************************************************************************* // *** Play sound function ********************************************* // ************************************************************************* void PlaySound(const uint16_t* melody, uint16_t size, uint16_t temp_ms = 100U, bool rep = false); // ************************************************************************* // *** Stop sound function ********************************************* // ************************************************************************* void StopSound(void); // ************************************************************************* // *** Mute sound function ********************************************* // ************************************************************************* void Mute(bool mute_flag); // ************************************************************************* // *** Is sound played function **************************************** // ************************************************************************* bool IsSoundPlayed(void); private: // Timer handle TIM_HandleTypeDef* htim = SOUND_HTIM; // Timer channel uint32_t channel = SOUND_CHANNEL; // Ticks variable uint32_t last_wake_ticks = 0U; // Pointer to table contains melody const uint16_t* sound_table = nullptr; // Size of table uint16_t sound_table_size = 0U; // Current position uint16_t sound_table_position = 0U; // Current frequency delay uint16_t current_delay = 0U; // Time for one frequency in ms uint32_t delay_ms = 100U; // Repeat flag bool repeat = false; // Mute flag bool mute = false; // Mutex to synchronize when playing melody frames RtosMutex melody_mutex; // Semaphore for start play sound RtosSemaphore sound_update; // ************************************************************************* // *** Process Button Input function *********************************** // ************************************************************************* void Tone(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 


إن مهمة صيانة وحدات المياه بسيطة للغاية. من النقاط المثيرة للاهتمام ، يتم اكتشاف الوحدة تلقائيًا: أولاً ، باستخدام ADC ، نقوم بقياس الجهد ، إذا كان في النطاق من 25 ٪ إلى 75 ٪ من جهد الإمداد ، يتم إدخال عصا التحكم التناظرية ، وإلا الأزرار أو التشفير. إذا لم يكن عصا التحكم ، تحقق من السطر الرابع من وحدة الإدخال / الإخراج: إذا كانت على مستوى عالٍ ، فهذه الأزرار ( يتم سحب جميع الأزرار حتى التشغيل ، وعند الضغط على الأزرار ، يتم إغلاقها على الأرض ) ، إذا كانت منخفضة ، فهي أداة ترميز ( يتم سحب زر صغير لأعلى) على الأرض وعندما تضغط على السلطة ).

العنوان
 //****************************************************************************** // @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. class InputDrv : public AppTask { public: // ************************************************************************* // *** Enum with all buttons ******************************************* // ************************************************************************* typedef enum { EXT_LEFT, // Left ext port EXT_RIGHT, // Right ext port EXT_MAX // Ext port count } PortType; // ************************************************************************* // *** Enum with all devices types ************************************* // ************************************************************************* typedef enum { 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 ******************************************* // ************************************************************************* typedef enum { 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 *********************************** // ************************************************************************* typedef enum { 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 class static InputDrv& GetInstance(void); // ************************************************************************* // *** Init Input Driver Task ****************************************** // ************************************************************************* // * This function initialize Input Driver class. If htim provided, this // * timer will be used instead FreeRTOS task. virtual void InitTask(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. void ProcessInput(void); // ************************************************************************* // *** Process Encoders Input function ********************************* // ************************************************************************* void ProcessEncodersInput(void); // ************************************************************************* // *** Get device type ************************************************* // ************************************************************************* ExtDeviceType GetDeviceType(PortType port); // ************************************************************************* // *** Get button state ************************************************ // ************************************************************************* // Return button state: true - pressed, false - unpressed bool GetButtonState(PortType port, ButtonType button); // ************************************************************************* // *** Get button state ************************************************ // ************************************************************************* // Return button state change flag: true - changed, false - not changed bool GetButtonState(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 - unpressed bool GetEncoderButtonState(PortType port, EncButtonType button); // ************************************************************************* // *** Get encoder button state **************************************** // ************************************************************************* // Return button state: true - pressed, false - unpressed bool GetEncoderButtonState(PortType port, EncButtonType button, bool& btn_state); // ************************************************************************* // *** Get joystick counts from last call ****************************** // ************************************************************************* void GetJoystickState(PortType port, int32_t& x, int32_t& y); // ************************************************************************* // *** SetJoystickCalibrationConsts ************************************ // ************************************************************************* // * Set calibration constants. Must be call for calibration joystick. void SetJoystickCalibrationConsts(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 - unpressed bool GetJoystickButtonState(PortType port); // ************************************************************************* // *** Get joystick button state *************************************** // ************************************************************************* // Return button state: true - pressed, false - unpressed bool GetJoystickButtonState(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 debouncing const static uint32_t BUTTON_READ_DELAY = 4U; // Coefficient for calibration const static int32_t COEF = 100; // ADC max value - 12 bit const static int32_t ADC_MAX_VAL = 0xFFF; // Joystich threshold const static int32_t JOY_THRESHOLD = 1000; // Ticks variable uint32_t last_wake_ticks = 0U; // ************************************************************************* // *** Structure to describe button ************************************ // ************************************************************************* typedef struct { bool btn_state; // Button state returned by GetButtonState() function bool btn_state_tmp; // Temporary button state for reduce debouncing uint8_t btn_state_cnt; // Counter for reduce debouncing GPIO_TypeDef* button_port;// Button port uint16_t button_pin; // Button pin GPIO_PinState pin_state; // High/low on input treated as pressed } ButtonProfile; // ************************************************************************* // *** Structure to describe encoder *********************************** // ************************************************************************* typedef struct { // Encoder rotation int32_t enc_cnt; // Encoder counter uint8_t enc_state; // Current state of encder clock & data pins GPIO_TypeDef* enc_clk_port; // Encoder clock port uint16_t enc_clk_pin; // Encoder clock pin GPIO_TypeDef* enc_data_port;// Encoder data port uint16_t enc_data_pin; // Encoder data pin } EncoderProfile; // ************************************************************************* // *** Structure to describe joysticks ********************************* // ************************************************************************* typedef struct { int32_t x_ch_val; // Joystick X axis value uint32_t x_channel; // Joystick X axis ADC channel GPIO_TypeDef* x_port; // Joystick X axis port uint16_t x_pin; // Joystick X axis pin int32_t bx; // Joystick X offset int32_t kxmin; // Joystick X coefficient int32_t kxmax; // Joystick X coefficient bool x_inverted; // Joystick X inverted flag int32_t y_ch_val; // Joystick Y axis value uint32_t y_channel; // Joystick Y axis ADC channel GPIO_TypeDef* y_port; // Joystick Y axis port uint16_t y_pin; // Joystick Y axis pin int32_t by; // Joystick Y offset int32_t kymin; // Joystick Y coefficient int32_t kymax; // Joystick Y coefficient bool y_inverted; // Joystick Y inverted flag } JoystickProfile; // ************************************************************************* // *** Structure to describe encoders ********************************** // ************************************************************************* typedef struct { EncoderProfile enc; ButtonProfile btn[ENC_BTN_MAX]; } DevEncoders; // ************************************************************************* // *** Structure to describe encoders ********************************** // ************************************************************************* typedef struct { JoystickProfile joy; ButtonProfile btn; } DevJoysticks; // ************************************************************************* // *** Structure to describe buttons *********************************** // ************************************************************************* typedef struct { 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, // Joystick 0, 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, // Joystick 0, 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 *********************************** // ************************************************************************* void ProcessButtonInput(ButtonProfile& button); // ************************************************************************* // *** Process Encoder Input function ********************************** // ************************************************************************* void ProcessEncoderInput(EncoderProfile& encoder); // ************************************************************************* // *** Process Joystick Input function ********************************* // ************************************************************************* void ProcessJoystickInput(JoystickProfile& joysticks, PortType port); // ************************************************************************* // *** Emulate buttons using joystick function ************************* // ************************************************************************* void EmulateButtonsByJoystick(PortType port); // ************************************************************************* // *** Emulate encoders using buttons function ************************* // ************************************************************************* void EmulateEncodersByButtons(PortType port); // ************************************************************************* // *** Configure inputs devices types ********************************** // ************************************************************************* ExtDeviceType DetectDeviceType(PortType port); // ************************************************************************* // *** Configure ADC *************************************************** // ************************************************************************* void ConfigADC(ExtDeviceType dev_left, ExtDeviceType dev_right); // ************************************************************************* // *** Configure inputs for read digital/analog data ******************* // ************************************************************************* void ConfigInputIO(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 

مهمة صيانة الشاشة هي المهمة الأكثر إثارة للاهتمام. بادئ ذي بدء ، تبلغ الشاشة 320 × 240 × 16 بت ، لذلك تحتاج إلى 153600 بايت لـ Framebuffer. هذا ليس كثيرًا فحسب ، بل إنه ضخم للغاية - في وحدة التحكم الدقيقة هذه يوجد 192 كيلو فقط من ذاكرة الوصول العشوائي ، وفي وحدات التحكم الدقيقة قد يكون من الأسهل ألا يكون لديك الحجم المناسب على الإطلاق. كيف تكون؟ الجواب بسيط: ارسم الشاشة في أجزاء! ولكن يمكنك رسم شيء بطرق مختلفة ...

الحل الذي تقدمت به لهذه المهمة هو مثل كل عبقري. يحتوي على مخزن مؤقت على خطين للشاشة. في سطر واحد ، نرسم كل ما يجب أن يكون ، نرسله إلى الشاشة عبر SPI في وضع DMA ، وفي هذا الوقت يمكننا إعداد خط آخر.

كيف تعرف المهمة ما يجب أن يكون في السطر وكيف ترسمه؟ لكنها لا تعرف!ولكن لديها قائمة من الأشياء التي تعرف كيف ترسم نفسها. كل كائن من هذا القبيل موروث من فئة VisObject .

العنوان
 //****************************************************************************** // @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. class VisObject { public: // ************************************************************************* // *** Action ********************************************************** // ************************************************************************* typedef enum { 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 *************************************************** // ************************************************************************* void LockVisObject(); // ************************************************************************* // *** UnlockVisObject ************************************************* // ************************************************************************* void UnlockVisObject(); // ************************************************************************* // *** 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. virtual void Show(uint32_t z_pos = 0); // ************************************************************************* // *** Hide ************************************************************ // ************************************************************************* // * Hide VisObject from screen. This function call DelVisObjectFromList() // * from DisplayDrv class. virtual void Hide(void); // ************************************************************************* // *** IsShow ********************************************************** // ************************************************************************* // * Check status of Show Visual Object. Return true if object in DisplayDrv list. virtual bool IsShow(void); // ************************************************************************* // *** Move ************************************************************ // ************************************************************************* // * Move object on screen. Set new x and y coordinates. If flag is set - // * move is relative, not absolute. virtual void Move(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. virtual void DrawInBufH(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. virtual void DrawInBufW(uint16_t* buf, int32_t n, int32_t line, int32_t start_x = 0) = 0; // ************************************************************************* // *** Action ********************************************************** // ************************************************************************* virtual void Action(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 object int16_t x_start = 0, y_start = 0; // X and Y end coordinates of object int16_t x_end = 0, y_end = 0; // Width and Height of object int16_t width = 0, height = 0; // Rotation of object int8_t rotation = 0; // Object active bool active = false; private: // ************************************************************************* // *** Object parameters *********************************************** // ************************************************************************* // * Only base class and DisplayDrv have access to this parameters // Z position of object uint16_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 Z friend class DisplayDrv; }; #endif 

تمر مهمة صيانة الشاشة لكل سطر من خلال قائمة الكائنات وتستدعي وظيفة DrawInBufW () ، وتمريرها إلى المخزن المؤقت ، وعدد النقاط ، والخط المراد رسمه وموضع البداية ( حتى تم استخدام فكرة استخدام وضع وحدة تحكم الشاشة لتحديث النافذة ). في الواقع ، كل كائن يرسم نفسه فوق الكائنات الأخرى المرسومة بالفعل ومن السهل ترتيب الكائنات بالترتيب المطلوب ببساطة عن طريق وضعها في الموضع المطلوب في القائمة.

بالإضافة إلى ذلك ، فإن هذا النهج يجعل من السهل دمج معالجة الكائنات النشطة - بعد تلقي الإحداثيات من وحدة تحكم الشاشة التي تعمل باللمس ، يمكن لمهمة صيانة الشاشة الانتقال من خلال ورقة العمل من النهاية بحثًا عن الكائن النشط الذي يقع في إحداثيات الضغط. إذا تم العثور على مثل هذا الكائن ، يتم استدعاء الوظيفة الافتراضية Action () للكائن المحدد.

في الوقت الحالي ، هناك كائنات للخطوط والبدائية ( الخط والمربع والدائرة ) والصور وخرائط البلاط ( لإنشاء الألعاب ).

أيضا في DevCore يدخل رمز آخر لبعض عناصر واجهة المستخدم ( مثل قائمة )، واجهة للسائق I2C، سائق I2C والمكتبات للعمل مع جهاز استشعار BME280 وEEPROM 24S256، ولكنها ليست مثيرة للاهتمام لذلك، وبعد وأنا لن وصف - واتضح كبيرة جدا .

الكود الكامل متاح بدون تسجيل ورسائل نصية قصيرة على GitHub : https://github.com/nickshl/devboy

PS يبدو أن الشركة تذهب إلى Epic Fail. في الأسبوع الأول ، ثلاثة خبازين فقط ، بما في ذلك دولار من نوع ما من " صندوق الابتكار ""، 180 دولارًا من شخص ربما تعلم عن هذا المشروع من مقال عن حبري ( شكرًا لك ، أندري! ) والباقي من زميلي من مكعب مجاور.

عدم جمع المال ليس مشكلة. المشكلة هي عدم الاهتمام بالمشروع ...

Source: https://habr.com/ru/post/ar424309/


All Articles