summaryrefslogtreecommitdiffstats
path: root/bsps/arm/imxrt/nxp/devices/MIMXRT1052/drivers/fsl_kpp.c
blob: 8c0b1958be91e28fd96844ccb43af5a9a7a101db (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 * Copyright 2017, 2019 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_kpp.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.kpp"
#endif

#define KPP_KEYPAD_SCAN_TIMES (3U)
/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/*! @brief Pointers to SEMC clocks for each instance. */
static const clock_ip_name_t s_kppClock[FSL_FEATURE_SOC_KPP_COUNT] = KPP_CLOCKS;
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

/*! @brief Pointers to SEMC bases for each instance. */
static KPP_Type *const s_kppBases[] = KPP_BASE_PTRS;

/*! @brief Pointers to KPP IRQ number for each instance. */
static const IRQn_Type s_kppIrqs[] = KPP_IRQS;
/*******************************************************************************
 * Code
 ******************************************************************************/
static uint32_t KPP_GetInstance(KPP_Type *base)
{
    uint32_t instance;

    /* Find the instance index from base address mappings. */
    for (instance = 0; instance < ARRAY_SIZE(s_kppBases); instance++)
    {
        if (s_kppBases[instance] == base)
        {
            break;
        }
    }

    assert(instance < ARRAY_SIZE(s_kppBases));

    return instance;
}
static void KPP_Mdelay(uint64_t tickets)
{
    while ((tickets--) != 0UL)
    {
        __NOP();
    }
}

/*!
 * brief KPP initialize.
 * This function ungates the KPP clock and initializes KPP.
 * This function must be called before calling any other KPP driver functions.
 *
 * param base KPP peripheral base address.
 * param configure The KPP configuration structure pointer.
 */
void KPP_Init(KPP_Type *base, kpp_config_t *configure)
{
    assert(configure);

    uint32_t instance = KPP_GetInstance(base);

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Un-gate sdram controller clock. */
    CLOCK_EnableClock(s_kppClock[KPP_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

    /* Clear all. */
    base->KPSR &= (uint16_t)(~(KPP_KPSR_KRIE_MASK | KPP_KPSR_KDIE_MASK));

    /* Enable the keypad row and set the column strobe output to open drain. */
    base->KPCR = KPP_KPCR_KRE(configure->activeRow);
    base->KPDR = KPP_KPDR_KCD((uint8_t) ~(configure->activeColumn));
    base->KPCR |= KPP_KPCR_KCO(configure->activeColumn);

    /* Set the input direction for row and output direction for column. */
    base->KDDR = KPP_KDDR_KCDD(configure->activeColumn) | KPP_KDDR_KRDD((uint8_t) ~(configure->activeRow));

    /* Clear the status flag and enable the interrupt. */
    base->KPSR = KPP_KPSR_KPKR_MASK | KPP_KPSR_KPKD_MASK | KPP_KPSR_KDSC_MASK | configure->interrupt;

    if ((configure->interrupt) != 0U)
    {
        /* Enable at the Interrupt */
        (void)EnableIRQ(s_kppIrqs[instance]);
    }
}

/*!
 * brief Deinitializes the KPP module and gates the clock.
 * This function gates the KPP clock. As a result, the KPP
 * module doesn't work after calling this function.
 *
 * param base KPP peripheral base address.
 */
void KPP_Deinit(KPP_Type *base)
{
    /* Disable interrupts and disable all rows. */
    base->KPSR &= (uint16_t)(~(KPP_KPSR_KRIE_MASK | KPP_KPSR_KDIE_MASK));
    base->KPCR = 0;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Disable KPP clock. */
    CLOCK_DisableClock(s_kppClock[KPP_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}

/*!
 * brief Keypad press scanning.
 *
 * This function will scanning all columns and rows. so
 * all scanning data will be stored in the data pointer.
 *
 * param base  KPP peripheral base address.
 * param data  KPP key press scanning data. The data buffer should be prepared with
 * length at least equal to KPP_KEYPAD_COLUMNNUM_MAX * KPP_KEYPAD_ROWNUM_MAX.
 * the data pointer is recommended to be a array like uint8_t data[KPP_KEYPAD_COLUMNNUM_MAX].
 * for example the data[2] = 4, that means in column 1 row 2 has a key press event.
 * param clockSrc_Hz Source clock.
 */
void KPP_keyPressScanning(KPP_Type *base, uint8_t *data, uint32_t clockSrc_Hz)
{
    assert(data);

    uint16_t kppKCO      = base->KPCR & KPP_KPCR_KCO_MASK;
    uint8_t columIndex   = 0;
    uint8_t activeColumn = (uint8_t)((base->KPCR & KPP_KPCR_KCO_MASK) >> KPP_KPCR_KCO_SHIFT);
    uint8_t times;
    uint8_t rowData[KPP_KEYPAD_SCAN_TIMES][KPP_KEYPAD_COLUMNNUM_MAX];
    bool press = false;
    uint8_t column;

    /* Initialize row data to zero. */
    (void)memset(&rowData[0][0], 0, sizeof(rowData));

    /* Scanning. */
    /* Configure the column data to 1 according to column numbers. */
    base->KPDR = KPP_KPDR_KCD_MASK;
    /* Configure column to totem pole for quick discharge of keypad capacitance. */
    base->KPCR &= (uint16_t)(((uint16_t)~kppKCO) | KPP_KPCR_KRE_MASK);
    /* Recover. */
    base->KPCR |= kppKCO;
    /* Three times scanning. */
    for (times = 0; times < KPP_KEYPAD_SCAN_TIMES; times++)
    {
        for (columIndex = 0; columIndex < KPP_KEYPAD_COLUMNNUM_MAX; columIndex++)
        {
            column = activeColumn & (1U << columIndex);
            if (column != 0U)
            {
                /* Set the single column line to 0. */
                base->KPDR = KPP_KPDR_KCD(~(uint16_t)column);
                /* Take 100us delays. */
                KPP_Mdelay(((uint64_t)clockSrc_Hz / 10000000UL));
                /* Read row data. */
                rowData[times][columIndex] = (uint8_t)(~(base->KPDR & KPP_KPDR_KRD_MASK));
            }
            else
            {
                /* Read row data. */
                rowData[times][columIndex] = 0;
            }
        }
    }

    /* Return all columns to 0 in preparation for standby mode. */
    base->KPDR &= (uint16_t)(~KPP_KPDR_KCD_MASK);

    /* Check if three time scan data is the same. */
    for (columIndex = 0; columIndex < KPP_KEYPAD_COLUMNNUM_MAX; columIndex++)
    {
        if (((uint8_t)(rowData[0][columIndex] & rowData[1][columIndex]) & rowData[2][columIndex]) != 0U)
        {
            press = true;
        }
    }

    if (press)
    {
        (void)memcpy(data, &rowData[0][0], sizeof(rowData[0]));
    }
    else
    {
        (void)memset(data, 0, sizeof(rowData[0]));
    }
}