1 /***************************************************************************//**
2  * @file
3  * @brief Keyscan (KEYSCAN) peripheral API
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "sl_hal_keyscan.h"
32 #if defined(KEYSCAN_COUNT) && (KEYSCAN_COUNT > 0)
33 #include "sl_assert.h"
34 #include "em_bus.h"
35 
36 /***************************************************************************//**
37  * @addtogroup keyscan KEYSCAN - Keyboard Scan
38  * @brief Keyscan (KEYSCAN) Peripheral API
39  * @details
40  *  This module contains functions to control the KEYSCAN peripheral of Silicon
41  *  Labs 32-bit MCUs and SoCs. The KEYSCAN module connects through rows and
42  *  columns of GPIOs to an external mechanical keypad.
43  * @{
44  ******************************************************************************/
45 
46 /***************************************************************************//**
47  * Initializes KEYSCAN module.
48  ******************************************************************************/
sl_hal_keyscan_init(const sl_hal_keyscan_config_t * p_config)49 void sl_hal_keyscan_init(const sl_hal_keyscan_config_t *p_config)
50 {
51   // Wait to be ready
52   sl_hal_keyscan_wait_ready();
53 
54   if (KEYSCAN->EN == KEYSCAN_EN_EN) {
55     // Disable KEYSCAN module
56     KEYSCAN->EN_CLR = KEYSCAN_EN_EN;
57     while (KEYSCAN->EN & _KEYSCAN_EN_DISABLING_MASK) {
58       // Wait for disabling to finished
59     }
60   }
61 
62   // A sanity check of configuration parameters.
63   EFM_ASSERT(p_config->clock_divider <= _KEYSCAN_CFG_CLKDIV_MASK);
64   EFM_ASSERT(p_config->column_number <= KEYSCAN_COLNUM);
65   EFM_ASSERT(p_config->row_number <= KEYSCAN_ROWNUM);
66   EFM_ASSERT(p_config->scan_delay <= SL_HAL_KEYSCAN_DELAY_32MS);
67   EFM_ASSERT(p_config->debounce_delay <= SL_HAL_KEYSCAN_DELAY_32MS);
68   EFM_ASSERT(p_config->stable_delay <= SL_HAL_KEYSCAN_DELAY_32MS);
69 
70   // Set configuration
71   KEYSCAN->CFG = ((p_config->clock_divider) << _KEYSCAN_CFG_CLKDIV_SHIFT)
72                  | ((p_config->auto_start_enable) << _KEYSCAN_CFG_AUTOSTART_SHIFT)
73                  | ((p_config->single_press_enable) << _KEYSCAN_CFG_SINGLEPRESS_SHIFT)
74                  | ((p_config->column_number - 1) << _KEYSCAN_CFG_NUMCOLS_SHIFT)
75                  | ((p_config->row_number - 1) << _KEYSCAN_CFG_NUMROWS_SHIFT);
76 
77   KEYSCAN->DELAY = ((p_config->scan_delay) << _KEYSCAN_DELAY_SCANDLY_SHIFT)
78                    | ((p_config->debounce_delay) << _KEYSCAN_DELAY_DEBDLY_SHIFT)
79                    | ((p_config->stable_delay) << _KEYSCAN_DELAY_STABDLY_SHIFT);
80 }
81 
82 /***************************************************************************//**
83  * Enables KEYSCAN module.
84  ******************************************************************************/
sl_hal_keyscan_enable(void)85 void sl_hal_keyscan_enable(void)
86 {
87   if (KEYSCAN->EN != 0U) {
88     // Wait for synchronization before writing to EN register
89     sl_hal_keyscan_wait_sync();
90   }
91 
92   // Enable KEYSCAN module
93   KEYSCAN->EN_SET = KEYSCAN_EN_EN;
94 }
95 
96 /***************************************************************************//**
97  * Disables KEYSCAN module.
98  ******************************************************************************/
sl_hal_keyscan_disable(void)99 void sl_hal_keyscan_disable(void)
100 {
101   // Quick exit if we want to disable KEYSCAN and it's already disabled.
102   if (KEYSCAN->EN == 0U) {
103     return;
104   }
105 
106   // Stop scan if running
107   if (KEYSCAN->STATUS & _KEYSCAN_STATUS_RUNNING_MASK) {
108     sl_hal_keyscan_stop_scan();
109   }
110 
111   // Wait for synchronization to complete
112   sl_hal_keyscan_wait_sync();
113 
114   // Disable module
115   KEYSCAN->EN_CLR = KEYSCAN_EN_EN;
116 }
117 
118 /***************************************************************************//**
119  * Restores KEYSCAN to its reset state.
120  *
121  * @note
122  *   The register STATUS get reset value after enabling the module because
123  *   it is of type RSYNC
124  ******************************************************************************/
sl_hal_keyscan_reset(void)125 void sl_hal_keyscan_reset(void)
126 {
127   // Stop scan if running
128   if (KEYSCAN->STATUS & _KEYSCAN_STATUS_RUNNING_MASK) {
129     sl_hal_keyscan_stop_scan();
130   }
131 
132   sl_hal_keyscan_enable();
133 
134   // Wait for synchronization to complete
135   sl_hal_keyscan_wait_sync();
136 
137   // Software reset command
138   KEYSCAN->SWRST_SET = KEYSCAN_SWRST_SWRST;
139 }
140 
141 /** @} (end addtogroup keyscan) */
142 #endif /* defined(KEYSCAN_COUNT) && (KEYSCAN_COUNT > 0) */
143