1 /*
2  * Copyright 2017, 2019 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_kpp.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.kpp"
17 #endif
18 
19 #define KPP_KEYPAD_SCAN_TIMES (3U)
20 /*******************************************************************************
21  * Prototypes
22  ******************************************************************************/
23 
24 /*******************************************************************************
25  * Variables
26  ******************************************************************************/
27 
28 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
29 /*! @brief Pointers to SEMC clocks for each instance. */
30 static const clock_ip_name_t s_kppClock[FSL_FEATURE_SOC_KPP_COUNT] = KPP_CLOCKS;
31 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
32 
33 /*! @brief Pointers to SEMC bases for each instance. */
34 static KPP_Type *const s_kppBases[] = KPP_BASE_PTRS;
35 
36 /*! @brief Pointers to KPP IRQ number for each instance. */
37 static const IRQn_Type s_kppIrqs[] = KPP_IRQS;
38 /*******************************************************************************
39  * Code
40  ******************************************************************************/
KPP_GetInstance(KPP_Type * base)41 static uint32_t KPP_GetInstance(KPP_Type *base)
42 {
43     uint32_t instance;
44 
45     /* Find the instance index from base address mappings. */
46     for (instance = 0; instance < ARRAY_SIZE(s_kppBases); instance++)
47     {
48         if (s_kppBases[instance] == base)
49         {
50             break;
51         }
52     }
53 
54     assert(instance < ARRAY_SIZE(s_kppBases));
55 
56     return instance;
57 }
KPP_Mdelay(uint64_t tickets)58 static void KPP_Mdelay(uint64_t tickets)
59 {
60     while ((tickets--) != 0UL)
61     {
62         __NOP();
63     }
64 }
65 
66 /*!
67  * brief KPP initialize.
68  * This function ungates the KPP clock and initializes KPP.
69  * This function must be called before calling any other KPP driver functions.
70  *
71  * param base KPP peripheral base address.
72  * param configure The KPP configuration structure pointer.
73  */
KPP_Init(KPP_Type * base,kpp_config_t * configure)74 void KPP_Init(KPP_Type *base, kpp_config_t *configure)
75 {
76     assert(configure);
77 
78     uint32_t instance = KPP_GetInstance(base);
79 
80 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
81     /* Un-gate sdram controller clock. */
82     CLOCK_EnableClock(s_kppClock[KPP_GetInstance(base)]);
83 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
84 
85     /* Clear all. */
86     base->KPSR &= (uint16_t)(~(KPP_KPSR_KRIE_MASK | KPP_KPSR_KDIE_MASK));
87 
88     /* Enable the keypad row and set the column strobe output to open drain. */
89     base->KPCR = KPP_KPCR_KRE(configure->activeRow);
90     base->KPDR = KPP_KPDR_KCD((uint8_t) ~(configure->activeColumn));
91     base->KPCR |= KPP_KPCR_KCO(configure->activeColumn);
92 
93     /* Set the input direction for row and output direction for column. */
94     base->KDDR = KPP_KDDR_KCDD(configure->activeColumn) | KPP_KDDR_KRDD((uint8_t) ~(configure->activeRow));
95 
96     /* Clear the status flag and enable the interrupt. */
97     base->KPSR = KPP_KPSR_KPKR_MASK | KPP_KPSR_KPKD_MASK | KPP_KPSR_KDSC_MASK | configure->interrupt;
98 
99     if ((configure->interrupt) != 0U)
100     {
101         /* Enable at the Interrupt */
102         (void)EnableIRQ(s_kppIrqs[instance]);
103     }
104 }
105 
106 /*!
107  * brief Deinitializes the KPP module and gates the clock.
108  * This function gates the KPP clock. As a result, the KPP
109  * module doesn't work after calling this function.
110  *
111  * param base KPP peripheral base address.
112  */
KPP_Deinit(KPP_Type * base)113 void KPP_Deinit(KPP_Type *base)
114 {
115     /* Disable interrupts and disable all rows. */
116     base->KPSR &= (uint16_t)(~(KPP_KPSR_KRIE_MASK | KPP_KPSR_KDIE_MASK));
117     base->KPCR = 0;
118 
119 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
120     /* Disable KPP clock. */
121     CLOCK_DisableClock(s_kppClock[KPP_GetInstance(base)]);
122 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
123 }
124 
125 /*!
126  * brief Keypad press scanning.
127  *
128  * This function will scanning all columns and rows. so
129  * all scanning data will be stored in the data pointer.
130  *
131  * param base  KPP peripheral base address.
132  * param data  KPP key press scanning data. The data buffer should be prepared with
133  * length at least equal to KPP_KEYPAD_COLUMNNUM_MAX * KPP_KEYPAD_ROWNUM_MAX.
134  * the data pointer is recommended to be a array like uint8_t data[KPP_KEYPAD_COLUMNNUM_MAX].
135  * for example the data[2] = 4, that means in column 1 row 2 has a key press event.
136  * param clockSrc_Hz Source clock.
137  */
KPP_keyPressScanning(KPP_Type * base,uint8_t * data,uint32_t clockSrc_Hz)138 void KPP_keyPressScanning(KPP_Type *base, uint8_t *data, uint32_t clockSrc_Hz)
139 {
140     assert(data);
141 
142     uint16_t kppKCO      = base->KPCR & KPP_KPCR_KCO_MASK;
143     uint8_t columIndex   = 0;
144     uint8_t activeColumn = (uint8_t)((base->KPCR & KPP_KPCR_KCO_MASK) >> KPP_KPCR_KCO_SHIFT);
145     uint8_t times;
146     uint8_t rowData[KPP_KEYPAD_SCAN_TIMES][KPP_KEYPAD_COLUMNNUM_MAX];
147     bool press = false;
148     uint8_t column;
149 
150     /* Initialize row data to zero. */
151     (void)memset(&rowData[0][0], 0, sizeof(rowData));
152 
153     /* Scanning. */
154     /* Configure the column data to 1 according to column numbers. */
155     base->KPDR = KPP_KPDR_KCD_MASK;
156     /* Configure column to totem pole for quick discharge of keypad capacitance. */
157     base->KPCR &= (uint16_t)(((uint16_t)~kppKCO) | KPP_KPCR_KRE_MASK);
158     /* Recover. */
159     base->KPCR |= kppKCO;
160     /* Three times scanning. */
161     for (times = 0; times < KPP_KEYPAD_SCAN_TIMES; times++)
162     {
163         for (columIndex = 0; columIndex < KPP_KEYPAD_COLUMNNUM_MAX; columIndex++)
164         {
165             column = activeColumn & (1U << columIndex);
166             if (column != 0U)
167             {
168                 /* Set the single column line to 0. */
169                 base->KPDR = KPP_KPDR_KCD(~(uint16_t)column);
170                 /* Take 100us delays. */
171                 KPP_Mdelay(((uint64_t)clockSrc_Hz / 10000000UL));
172                 /* Read row data. */
173                 rowData[times][columIndex] = (uint8_t)(~(base->KPDR & KPP_KPDR_KRD_MASK));
174             }
175             else
176             {
177                 /* Read row data. */
178                 rowData[times][columIndex] = 0;
179             }
180         }
181     }
182 
183     /* Return all columns to 0 in preparation for standby mode. */
184     base->KPDR &= (uint16_t)(~KPP_KPDR_KCD_MASK);
185 
186     /* Check if three time scan data is the same. */
187     for (columIndex = 0; columIndex < KPP_KEYPAD_COLUMNNUM_MAX; columIndex++)
188     {
189         if (((uint8_t)(rowData[0][columIndex] & rowData[1][columIndex]) & rowData[2][columIndex]) != 0U)
190         {
191             press = true;
192         }
193     }
194 
195     if (press)
196     {
197         (void)memcpy(data, &rowData[0][0], sizeof(rowData[0]));
198     }
199     else
200     {
201         (void)memset(data, 0, sizeof(rowData[0]));
202     }
203 }
204