This repository provides an MPLAB® X project showcasing the implementation of a key scanner using the Configurable Logic Block (CLB), Timer0 (TMR0) and Timer1 (TMR1) peripherals. This implementation supports input from a 4x4 keypad.
The CLB peripheral is a collection of logic elements that can be programmed to perform a wide variety of digital logic functions. The logic function may be completely combinatorial, sequential, or a combination of the two, enabling users to incorporate hardware-based custom logic into their applications.
More details and code examples on the PIC16F13276 can be found at the following links:
- MPLAB® X IDE v6.30 or newer or MPLAB® Tools for VS Code®
- MPLAB® XC8 v3.10 or newer
- PIC16F1xxxx_DFP v1.29.444 or newer
-
The PIC16F13276 Curiosity Nano Development board is used as a test platform

To program the Curiosity Nano board with this MPLAB X project, follow the steps provided in the How to Program the Curiosity Nano Board chapter.
This example demonstrates the capabilities of the CLB, a Core Independent Peripheral (CIP), that is used to scan rows, debounce columns and generate an interrupt when a valid key press is detected on the keypad. The decoded key is then sent on serial port using the Enhanced Universal Synchronous Asynchronous Receiver Transmitter (EUSART) peripheral.
The schematic of the keypad used is shown in the figure below. The 16 keys are arranged as four rows and four columns. Instead of providing a dedicated wire for each key, the keypad is accessed using eight shared signal lines: R0-R3 for the rows and C0-C3 for the columns. Each key is a switch that connects one row line to one column line when pressed. A typical scanning approach drives one row at a time while reading the columns inputs, allowing the microcontroller to determine which row-column intersection is closed and identify the pressed key.

The scanning mechanism is implementated in hardware using the CLB peripheral and it is shown in the figure below. The CLB continuously scans the four row lines and debounces the four columns inputs, and raises an interrupt when it has detected a valid key press.

The internal hardware counter is used to cycle through the four rows and drive them low one by one. The TMR0 peripheral, configured for a 25 ms overflow period, is used to set the scan rate, resulting in a 100 ms period for a full scan of all four lines. The CLB PPS_OUT0-PPS_OUT3 outputs are connected to the RC0-RC3 pins of the MCU to implement row selection. The CLB also controls the output-enable state of each row pin using the internal TRISC0-TRISC3 CLB output ports. Using this implementation, only one row is actively driven low at a certain time, while the remaining rows are tri-stated, meaning their output buffer is disabled and they acts as inputs. The internal weak pull-ups are enabled on these pins so that, when a row is tri-stated (TRISCx is logic '0'), it is held at a stable logic '1' by the pull-up resistor. When a row is selected for scanning (TRISCx is logic '1'), the CLB enables the output buffer and drives the pin low. The weak pull-up is automatically disabled if the pin is configured as an output.
The four keypad column lines are connected to the RD0-RD3 pins of the MCU and are routed internally via Peripheral Pin Select (PPS) to the CLB CLBIN0PPS-CLBIN3PPS inputs. The internal weak pull-ups are also enabled for these pins, meaning that their idle state is logic '1'. When a key on the currently-driven row is pressed, it connects that row to a column line, pulling the corresponding column low.
A debouncing mechanism is implemented for each column line using the TMR1 peripheral. The XNOR gates are used to detect whether a column input has changed state by comparing the current sampled value with the previous stored value. A 2:1 demultiplexer and a D flip-flop (with enable and reset) implement a timed, two-staged debounce sequence. The stage selection is driven by the TMR1 overflow, configured for 15 ms, which defines the debounce interval.
- Stage 1 - Change detection: If the XNOR comparison indicates a state change on a column input (logic '1' to '0' or '0' to '1'), the corresponding D flip-flop (with enable and reset) is set to mark that a transition occured
- Stage 2 - Validation: On the next TMR1 overflow, an AND gate checks whether the column input has remained in the new state for the entire debounce interval. If the new state is still present, the debounced output, stored in the final flip-flop, is updated. Otherwise, the stored state remains unchanged.
The debounced column signals are combined using the AND gate so the CLB can generate a single event when any column is asserted. Because the columns are active-low, the four column signals are first combined and then inverted so that a column going low produces a rising edge. The rising edge is used to trigger a CLB interrupt using the CLB_IRQ0 port.
When a CLB interrupt occurs, the callback function (CLB_IRQ0_Handler) reads the current states of the row pins (RC0-RC3) and column pins (RD0-RD3) into two 4-bit masks. Because the keypad signals are active-low, the masks are inverted so that the active row/column becomes a logic '1' in the corresponding bit position. The callback also sets a flag to notify that a key event occured.
void CLB_IRQ0_Handler(void)
{
rowsBits = (~(PORTCbits.RC0 | (PORTCbits.RC1 << 1) | (PORTCbits.RC2 << 2) | (PORTCbits.RC3 << 3))) & 0x0F;
colsBits = (~(PORTDbits.RD0 | (PORTDbits.RD1 << 1) | (PORTDbits.RD2 << 2) | (PORTDbits.RD3 << 3))) & 0x0F;
keyPressedFlag = true;
}
In the main loop, after a key pressed event, the captured masks for rows and columns are used to decode the actual key that was pressed. The masks are converted into row/column indices and used to access a look-up table (LUT) that maps each row/column intersection to its corresponding character. The decoded character is transmitted on the serial port using the EUSART2 peripheral.
char keyPad[ROWS_NO][COLS_NO] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
Other keypads may also be used; in this case, the debouncing interval and scan rate must be adjusted to align with the new requirements.
The following peripheral and clock configurations are set up using MPLAB Code Configurator (MCC) Melody for the PIC16F13276:
-
Configurations Bits:
-
Clock Control:
-
CLB Synthesizer Library:
-
CLB1:
-
TMR0:
-
TMR1:
-
UART2:
-
CRC:
- Auto-configured by the CLB
-
Pin Grid View:
Different key presses can be observed in the next demonstration.

This example demonstrates the capabilities of the CLB, a CIP that can be used to scan a 4x4 keypad matrix.
This chapter demonstrates how to use the MPLAB X IDE to program a PIC® device with an Example_Project.X. This is applicable to other projects.
-
Connect the board to the PC.
-
Open the
Example_Project.Xproject in MPLAB X IDE. -
Set the
Example_Project.Xproject as main project.
Right click the project in the Projects tab and click Set as Main Project.
-
Clean and build the
Example_Project.Xproject.
Right click theExample_Project.Xproject and select Clean and Build.
-
Select PICxxxxx Curiosity Nano in the Connected Hardware Tool section of the project settings:
a. Right click the project and click Properties.
b. Click the arrow under the Connected Hardware Tool.
c. Select PICxxxxx Curiosity Nano (click the SN), click Apply and then click OK.
-
Program the project to the board.
Right click the project and click Make and Program Device.













