Skip to content

Commit

Permalink
Merge pull request ClemensElflein#7 from Apehaenger/feature/YFSAxPRO
Browse files Browse the repository at this point in the history
Feature/YardForce SAxPRO (Rev6) Dot-Matrix Display
  • Loading branch information
ClemensElflein authored Oct 9, 2023
2 parents 3a1df97 + 889205a commit 6e33024
Show file tree
Hide file tree
Showing 104 changed files with 12,295 additions and 486 deletions.
1 change: 1 addition & 0 deletions Firmware/CoverUI/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build/
.idea/
cmake-build-debug/
.vscode/

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions Firmware/CoverUI/.vscode/c_cpp_properties.json

This file was deleted.

22 changes: 0 additions & 22 deletions Firmware/CoverUI/.vscode/launch.json

This file was deleted.

7 changes: 0 additions & 7 deletions Firmware/CoverUI/.vscode/settings.json

This file was deleted.

28 changes: 0 additions & 28 deletions Firmware/CoverUI/.vscode/tasks.json

This file was deleted.

57 changes: 57 additions & 0 deletions Firmware/CoverUI/YardForce/ButtonDebouncer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @file ButtonDebouncer.hpp
* @author Apehaenger ([email protected])
* @brief YardForce Classic 500 CoverUI Button-Debouncer class for OpenMower https://github.com/ClemensElflein/OpenMower
* Debouncing is done by continuos simply shift the port states into an state array for later processing.
* See Jack Ganssle debouncing http://www.ganssle.com/debouncing-pt2.htm
* For code simplicity/speed, I debounce all pins, regardless if it has a button or not. Button separation has to be done by calling class.
* @version 0.2
* @date 2023-09-16
*
* @copyright Copyright (c) 2023
*
*/
#ifndef YFC500_BUTTONDEBOUNCER_HPP
#define YFC500_BUTTONDEBOUNCER_HPP

#include <Arduino.h>
#include <stdint.h>

#define NUM_BUTTON_STATES 8 // * 5ms timer = 40ms bouncing-button states = debounced after 40ms

class ButtonDebouncer
{
private:
uint16_t states_[NUM_BUTTON_STATES]; // GPIO port state recorder (every time interval = 5ms)
uint8_t state_index_ = 0; // Index for next states_ store positions
uint16_t state_debounced_; // Debounced buttons state
uint16_t state_changed_; // Just changed buttons

public:
void process_state(const uint32_t gpio_port) // Has to get called regulary i.e. by timer (5ms) and store the (buttons) port state within states_ array
{
#ifdef MCU_STM32
states_[state_index_] = ((GPIO_TypeDef *)gpio_port)->IDR ^ 0xFFFF; // XOR changes for pull-up states_
#else
states_[state_index_] = GPIO_ISTAT(gpio_port) ^ 0xFFFF; // XOR changes for pull-up states_
#endif

// Debounce
uint16_t laststate_debounced_ = state_debounced_;
uint i;
for (i = 0, state_debounced_ = 0xFFFF; i < NUM_BUTTON_STATES; i++)
state_debounced_ &= states_[i];

// Circular buffer index
state_index_++;
if (state_index_ >= NUM_BUTTON_STATES)
state_index_ = 0;

// Save what changed
state_changed_ = state_debounced_ ^ laststate_debounced_;
}
uint16_t get_status() { return state_debounced_; };
uint16_t get_pressed() { return (state_changed_ & state_debounced_); };
};

#endif /* YFC500_BUTTONDEBOUNCER_HPP */
209 changes: 209 additions & 0 deletions Firmware/CoverUI/YardForce/Buttons.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/**
* @file Buttons.hpp
* @author Apehaenger ([email protected])
* @brief YardForce CoverUI Buttons class for OpenMower https://github.com/ClemensElflein/OpenMower
* @version 0.4
* @date 2023-09-03
*
* @copyright Copyright (c) 2023
*
*/
#ifndef YARDFORCE_BUTTONS_HPP
#define YARDFORCE_BUTTONS_HPP

#include <Arduino.h>
#include <stdint.h>
#include <map>
#include "ButtonDebouncer.hpp"

#ifdef MCU_GD32
#define GPIOA_BASE GPIOA
#define GPIOB_BASE GPIOB
#define GPIOC_BASE GPIOC
#define GPIOF_BASE GPIOF
#endif

#ifdef MDL_C500 // Model Classic 500
#define BTN_CLK_PIN PF4
#define BTN_OK_PIN PF5
#define BTN_S1_PIN PB2
#define BTN_S2_PIN PB10
#define BTN_LOCK_PIN PB11
#define BTN_MON_PIN PB12
#define BTN_TUE_PIN PB13
#define BTN_WED_PIN PB14
#define BTN_THU_PIN PB15
#define BTN_FRI_PIN PC6
#define BTN_SAT_PIN PC7
#define BTN_SUN_PIN PC8
#define BTN_PLAY_PIN PA11
#define BTN_HOME_PIN PA12

#define NUM_BUTTONS 14
#define NUM_GPIO_PORTS 4

#elif defined(MDL_SAXPRO) // Model SAxPRO
#define BTN_PLAY_PIN PC0 // or Start
#define BTN_HOME_PIN PC1
#define BTN_UP_PIN PB14
#define BTN_DOWN_PIN PB13
#define BTN_OK_PIN PB12 // or Enter
#define BTN_BACK_PIN PB15

#define NUM_BUTTONS 6
#define NUM_GPIO_PORTS 2
#endif

// Logic button numbers. Take attention that OM known buttons need to have the same logic number!
// 0 is reserved for no-button return
#define BTN_CLK_NUM 1
#define BTN_HOME_NUM 2
#define BTN_PLAY_NUM 3 // or Start
#define BTN_S1_NUM 4
#define BTN_S2_NUM 5
#define BTN_LOCK_NUM 6
#define BTN_OK_NUM 7 // or Enter
#define BTN_MON_NUM 8
#define BTN_TUE_NUM 9
#define BTN_WED_NUM 10
#define BTN_THU_NUM 11
#define BTN_FRI_NUM 12
#define BTN_SAT_NUM 13
#define BTN_SUN_NUM 14
// OM unknown buttons. Let's start from 0x20 to have some free space for more (future) OM supported buttons
#define BTN_UP_NUM 32
#define BTN_DOWN_NUM 33
#define BTN_BACK_NUM 34

#ifndef NUM_GPIO_PORTS
#define NUM_GPIO_PORTS 0
#endif

class Buttons
{
public:
typedef uint8_t ButtonNum;
struct ButtonDef
{
uint8_t debouncer_index; // Debouncer index as defined in debouncers_ array
uint8_t digital_pin;
};

#ifdef MDL_C500 // Model Classic 500
const uint8_t kOMButtonNrs[14] = { // Logic button numbers supported by OM. Use same order as in OM FW so that they get scanned in the same order!
BTN_CLK_NUM, BTN_HOME_NUM, BTN_PLAY_NUM, BTN_S1_NUM, BTN_S2_NUM, BTN_LOCK_NUM, BTN_OK_NUM,
BTN_SUN_NUM, BTN_MON_NUM, BTN_TUE_NUM, BTN_WED_NUM, BTN_THU_NUM, BTN_FRI_NUM, BTN_SAT_NUM};
#elif defined(MDL_SAXPRO) // Model SAxPRO
const uint8_t kOMButtonNrs[3] = { // Logic button numbers supported by OM. Use same order as in OM FW so that they get scanned in the same order!
BTN_HOME_NUM, BTN_PLAY_NUM, BTN_OK_NUM};
#endif

/**
* @brief Setup GPIOs
*
*/
void setup()
{
printf("Buttons Setup\n");

for (auto const &btn_def : kButtonNum2DefMap)
pinMode(btn_def.second.digital_pin, INPUT_PULLUP);
};

/**
* @brief Process GPIO states by debouncer. Has to get called regulary i.e. by timer (5ms)
*
*/
void process_states()
{
for (uint i = 0; i < NUM_GPIO_PORTS; i++)
debouncers_[i]->process_state(kGpioPorts[i]);
};

/**
* @brief Return boolean true if the given button number is pressed.
* Take into notice that the returned state is already debounced.
*
* @param button_nr
* @return true = pressed
* @return false = not pressed
*/
bool is_pressed(ButtonNum button_nr)
{
try
{
const ButtonDef btn_def = kButtonNum2DefMap.at(button_nr);
return get_status(btn_def.debouncer_index) & digitalPinToBitMask(btn_def.digital_pin);
}
catch (const std::out_of_range &e)
{
return false;
}
};

/**
* @brief Return ButtonNum of the first detected pressed button.
* Take into notice that the returned state is already debounced.
*
* @return ButtonNum 0 = none pressed, >0 = ButtonNum
*/
ButtonNum is_pressed()
{
for (auto const &btn_def : kButtonNum2DefMap)
if(get_status(btn_def.second.debouncer_index) & digitalPinToBitMask(btn_def.second.digital_pin))
return btn_def.first;
return 0;
};

private:
// Somehow static initialization...
// All ports with a button get debounced per port, via timer callback
#ifdef MDL_C500 // Model Classic 500
const uint32_t kGpioPorts[NUM_GPIO_PORTS] = {GPIOA_BASE, GPIOB_BASE, GPIOC_BASE, GPIOF_BASE};
ButtonDebouncer *debouncers_[NUM_GPIO_PORTS] = { // Debouncer obj for each port in the same order as kGpioPorts
new ButtonDebouncer(), new ButtonDebouncer(),
new ButtonDebouncer(), new ButtonDebouncer()};
// Map logic button number to Button-definiton (Again: Some-how static with debouncer_index)
const std::map<ButtonNum, ButtonDef> kButtonNum2DefMap = {
{BTN_CLK_NUM, {3, BTN_CLK_PIN}},
{BTN_HOME_NUM, {0, BTN_HOME_PIN}},
{BTN_PLAY_NUM, {0, BTN_PLAY_PIN}},
{BTN_S1_NUM, {1, BTN_S1_PIN}},
{BTN_S2_NUM, {1, BTN_S2_PIN}},
{BTN_LOCK_NUM, {1, BTN_LOCK_PIN}},
{BTN_OK_NUM, {3, BTN_OK_PIN}},
{BTN_MON_NUM, {1, BTN_MON_PIN}},
{BTN_TUE_NUM, {1, BTN_TUE_PIN}},
{BTN_WED_NUM, {1, BTN_WED_PIN}},
{BTN_THU_NUM, {1, BTN_THU_PIN}},
{BTN_FRI_NUM, {2, BTN_FRI_PIN}},
{BTN_SAT_NUM, {2, BTN_SAT_PIN}},
{BTN_SUN_NUM, {2, BTN_SUN_PIN}}};
#elif defined(MDL_SAXPRO) // Model SAxPRO
const uint32_t kGpioPorts[NUM_GPIO_PORTS] = {GPIOB_BASE, GPIOC_BASE};
ButtonDebouncer *debouncers_[NUM_GPIO_PORTS] = { // Debouncer obj for each port in the same order as kGpioPorts
new ButtonDebouncer(), new ButtonDebouncer()};
// Map logic button number to Button-definiton (Again: Some-how static with debouncer_index)
const std::map<ButtonNum, ButtonDef> kButtonNum2DefMap = {
{BTN_HOME_NUM, {1, BTN_HOME_PIN}},
{BTN_PLAY_NUM, {1, BTN_PLAY_PIN}},
{BTN_OK_NUM, {0, BTN_OK_PIN}},
{BTN_UP_NUM, {0, BTN_UP_PIN}},
{BTN_DOWN_NUM, {0, BTN_DOWN_PIN}},
{BTN_BACK_NUM, {0, BTN_BACK_PIN}}};
#endif

/**
* @brief Get (debounced) status of the whole GPIO port by debouncer index.
* See kGpioPorts definition for the GPIO debouncer indexes.
*
* @param gpio_index
* @return uint16_t
*/
uint16_t get_status(uint8_t debouncer_index)
{
return debouncers_[debouncer_index]->get_status();
};
};

#endif // YARDFORCE_BUTTONS_HPP
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class Emergency
*/
void periodic_send()
{
static uint8_t call_cnt_ = 0;
static uint call_cnt_ = 0;

call_cnt_++;

Expand Down
Loading

0 comments on commit 6e33024

Please sign in to comment.