Matrix Switch

Thread Starter

beeson76

Joined Apr 19, 2010
211
I have a post in the microcontroller section of the forums with my code and schematic. But I have a few quetions just about matrix switches. The matrix switch I am messing around with is 3 columns and 4 rows. I have it connected to PORTB using the internal pullups.

From what I am reading, I have to pulse PORTB high (or at least the pins connected to my columns (which in this case would be RB0, RB1, RB7)). I simply do this with the statement(s):

Rich (BB code):
RB0 = 1;
RB1 = 1;
RB7 = 1;
Is that correct?

Then I have to read the status of the rows (RB3..RB6) to see which one is high. Should I use maybe an if statement, to read that status?

Thanks for any help given.

Much appreciated.
 

DonQ

Joined May 6, 2009
321
Set only one of them and read all the inputs. Then set the next one of them and read all the inputs again. Rotate through all the columns. Only when a column that is activated has a closed switch will you see it on reading the inputs.

Generally, you delay slightly between scans, and even between setting an output and reading inputs so that the signals can settle.
 

eblc1388

Joined Nov 28, 2008
1,542
I have it connected to PORTB using the internal pullups.
OK. You have enable pullups on all port B input pins so these pin will normally read a HIGH. Now makes RB0, RB1 and RB7 an output and set to HIGH too.

So without any key press, a read from port B pins should be all HIGH.

From what I am reading, I have to pulse PORTB high
Now there is a logic problem here. All yours portB input pins are at logic HIGH because of internal pullups, if there is no key presses.

What you should do is to place(output) a LOW on one of the RB0, RB1 or RB7, then read the port again.

If there is any LOW on any of the RB3~RB6, then a key is being pressed on that row. If there isn't any LOW, moves to another column and do the same check.
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
I appreciate the replies. I am looking forward to getting this thing programmed. I will post the program through its various stages here so if anyone needs it to use or for an example, it will be here. I find that C examples for matrix switches on PICs are very hard to come by. Even information about PICs with matrix switches is very hard to find, though you can find generic information here and there.

But I appreciate the replies and help. Thanks.
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Here is what I have programmed so far. This is only for Column 1 Rows 1-4. So eventually I will program Columns 2 through 3 pretty much the exact same. By this program, what it is doing is displaying Switch 1, Switch 2, etc on the LCD every second without any key press. In other words, it is mainly scrolling through the Switch names every second.

What I did is make all Columns high and as inputs. And at the same time I made all Rows Low and as outputs.

I then made Column 1 low, and read the outputs. If any ROW (output) is a low, I then displayed that on the LCD.

I understand making Columns inputs and Rows output, but what I don't understand is that wouldn't the weak pullups make all Pins on PORTB HIGH? So should I make all PINS on PORTB high instead of making Columns HIGH and Rows LOW?

I am keeping is very simple for right now using an IF statement. Don't really want any macros for right because they make following the program harder.

Thanks for your replies.

Rich (BB code):
#include <htc.h>
#include "lcd.h"

#define	COL_1	RB0
#define COL_2	RB1
#define	COL_3	RB7
#define	ROW_1	RB3
#define ROW_2	RB4
#define	ROW_3	RB5
#define	ROW_4	RB6
#define  DelayS(T)				{unsigned char i; for (i = 0; i < T * 10; i++) __delay_ms(100);}	//Delay Macro
#define  _XTAL_FREQ				4000000								//Needs to be set for __delay_ms

__CONFIG (MCLREN & INTIO & WDTDIS & PWRTEN & BORDIS & LVPDIS & UNPROTECT);

main()
{
	
PORTA = 0x00;										//PORTA is cleared and set low

PORTB = 0x83; 					//(0b10000011 binary) (0x83 hex)--COLUMNS set to HIGH  
													//PORTB7:B0 is set to high,low,low,low,low,low,high,high
													//ROWS set to LOW
PORTC = 0x00;										//PORTC is cleared and set low
TRISA = 0x00;										//Set PORTA to outputs

TRISB = 0x7C;										//(0b01111100 binary) (0x7C hex)--COLUMNS set to INPUT
													//PORTB7:B0 is set to output,input,input,input,input,input,output,output,

TRISC = 0x00;										//Set PORTC to outputs
RBPU = 0;											//PORTB Weak Internal Pullups enabled
ANSEL = 0;											//Initialize A/D Ports off
ANSELH = 0;											//Initialize ........
CM1CON0 = 0;										//Initialize Comparator 1 off
CM2CON0 = 0;										//Initialize Comparator 2 off

OPTION = 0b01010101;								//OPTION REG
													//xbxxxxx101  1:64
													//xbxxxx0xxx  Prescaler set to Timer0
													//xbxxx1xxxx  (T0SE) set to Increment on high-to-low transition on T0CKI pin
													//xbxx0xxxxx  (T0CS) Internal instruction cycle clock
													//xbx1xxxxxx  (INTEDG) Interrupt on rising edge of INT pin
													//xb0xxxxxxx  (RBPU) PORTB pull-ups are enabled by individual PORT latch values

lcd_init();											//LCD Display is Initialized...See LCD.C file and LCD.h

while (1)
	{
		COL_1 = 0;									//COLUMN 1 is set LOW
		if (ROW_1 = 0)								//If ROW 1 is low...
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 1");				//Display "Switch 1"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_2 = 0)								//if ROW 2 is low...
			{										//...
				lcd_clear();						//...		
				lcd_goto(0);						//...
				lcd_puts("Switch 4");				//Display "Switch 4"
				__delay_ms(150);			
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_3 = 0)								//If ROW 3 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 7");				//Display "Switch 7"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_4 = 0)								//if ROW 4 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 10");				//Display "Switch 10"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
	}
}
 
Last edited:

Thread Starter

beeson76

Joined Apr 19, 2010
211
I just read in the PIC16F886 data sheet that when a pin is configured as an output, then the weak pullups are automatically disabled. So if I have my ROWS set as output (the weak pullups are disabled on it). So keep from having to put external resistors on it, wouldn't I have to have all of PORTB as input, and then pull my Columns LOW and then read which ROW is low and then display that on the LCD?
 

eblc1388

Joined Nov 28, 2008
1,542
You have made the Global /RBPU active in OPTION register but have you set the correct bits of WPUB register to activate the individual pull up inside port B?

 

Attachments

Markd77

Joined Sep 7, 2009
2,806
To activate the pullups for PORTB wouldn't I use the statement:

RBPU = 0;
Yes, and the default for WPUB is 1 (the 1 in the R/W-1 in post 7) so you don't need to do anything unless you want to disable some of them.
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Thanks Markd77. Can you see if this is correct then.

Here is my latest code. I added the WPUB statements and I changed some things around as far as how the code runs--Im read the COLUMNS first then the ROWS second. Here is what I am doing:

1. I set ROWS as INPUTS (RB3-RB6)
2. I set ROWS as HIGH
3. I set COLUMNS as OUTPUTS (RB0,RB1,RB7)
4. I set COLUMNS as HIGH
5. I enable Weak Pullups on ROWS. This will make these PINs HIGH because Weak PULLUPS pull HIGH these PINS
6. In the code I make COL_1 LOW and read the ROW to see if any are LOW.
7. If any are LOW I display that switch on the LCD.

Rich (BB code):
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//This program controls a Matrix Switch.  The Switch consists of 3 columns and 4 rows.
//Whenever a button is pushed, that button is displayed on an LCD Display.
//PIC16F886 Microcontroller from Microchip is being used.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <htc.h>
#include "lcd.h"

#define	COL_1	RB0
#define COL_2	RB1
#define	COL_3	RB7
#define	ROW_1	RB3
#define ROW_2	RB4
#define	ROW_3	RB5
#define	ROW_4	RB6
#define  DelayS(T)				{unsigned char i; for (i = 0; i < T * 10; i++) __delay_ms(100);}	//Delay Macro
#define  _XTAL_FREQ				4000000								//Needs to be set for __delay_ms

__CONFIG (MCLREN & INTIO & WDTDIS & PWRTEN & BORDIS & LVPDIS & UNPROTECT);

main()
{
	
PORTA = 0x00;										//PORTA is cleared and set low

PORTB = 0xFF;										//(0b11111111 binary) (0xFF hex)--ROWS set to HIGH, COLUMNS set to HIGH  
													//PORTB7:B0 is set to high,high,high,high,high,high,high,high
													
PORTC = 0x00;										//PORTC is cleared and set low
TRISA = 0x00;										//Set PORTA to outputs

TRISB = 0x7C;										//(0b01111100 binary) (0x7C hex)--ROWS set to INPUT, COLUMNS set to OUTPUT
													//PORTB7:B0 is set to output,input,input,input,input,input,output,output,
													//ROWS set to INPUT

TRISC = 0x00;										//Set PORTC to outputs


ANSEL = 0;											//Initialize A/D Ports off
ANSELH = 0;											//Initialize ........
CM1CON0 = 0;										//Initialize Comparator 1 off
CM2CON0 = 0;										//Initialize Comparator 2 off

OPTION = 0b01010101;								//OPTION REG
													//xbxxxxx101  1:64
													//xbxxxx0xxx  Prescaler set to Timer0
													//xbxxx1xxxx  (T0SE) set to Increment on high-to-low transition on T0CKI pin
													//xbxx0xxxxx  (T0CS) Internal instruction cycle clock
													//xbx1xxxxxx  (INTEDG) Interrupt on rising edge of INT pin
													//xb0xxxxxxx  (RBPU) PORTB pull-ups are enabled by individual PORT latch values
RBPU = 0;											//Don't think I need this, but to be safe...PORTB Weak Internal Pullups enabled
WPUB0 = 0;											//COL_1 Weak pullup is individually DISABLED--OUTPUT 
WPUB1 = 0;											//COL_2 Weak Pullup is inidivdually DISABLED--OUTPUT
WPUB2 = 0;											//Not being used...Weak Pullup is individually DISABLED
WPUB3 = 1;											//ROW_1 Weak Pullup is individually ENABLED--INPUT
WPUB4 = 1;											//ROW_2 Weak Pullup is individually ENABLED--INPUT
WPUB5 =	1; 											//ROW_3 Weak Pullup is individually ENABLED--INPUT
WPUB6 = 1;											//ROW_4 Weak Pullup is individually ENABLED--INPUT
WPUB7 = 0;											//COL_3 Weak Pullup is individually DISABLED--INPUT


lcd_init();											//LCD Display is Initialized...See LCD.C file and LCD.h

while (1)
	{
		lcd_clear;									//Clear LCD
		lcd_goto(0);								//Go to Line 1 Position 1
		lcd_puts("Matrix Switch");					//Display "Matrix Switch"
		lcd_goto(40);								//Go to Line 2 Position 1
		lcd_puts("Press Button");					//Display "Press Button"
		
		COL_1 = 0;									//COLUMN 1 is set LOW
		if (ROW_1 = 0)								//and If ROW 1 is LOW...
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 1");				//Display "Switch 1"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
				
		if (ROW_2 = 0)								//if ROW 2 is LOW...
			{										//...
				lcd_clear();						//...		
				lcd_goto(0);						//...
				lcd_puts("Switch 4");				//Display "Switch 4"
				__delay_ms(150);			
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_3 = 0)								//If ROW 3 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 7");				//Display "Switch 7"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_4 = 0)								//If ROW 4 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 10");				//Display "Switch 10"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
	}
}
What happens when I run this code is that it constantly scrolls through the switches on LCD -- Switch 1, Switch 4 etc. with 1 second delays. Then when I push a switch (say switch 1) it skips that switch -- goes from switch 10 to switch 4 when it scrolls. So It is reading the push, just isn't displaying it right.
 

Thread Starter

beeson76

Joined Apr 19, 2010
211
Thanks Alberto for the reply. I will look into what you suggested.

Here is what I have, and it seems to work great. I am going to try to make it more efficient code now, maybe incorporating some macros and functions in it.

Rich (BB code):
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//This program controls a Matrix Switch.  The Switch consists of 3 columns and 4 rows.
//Whenever a switch is pushed, that switches name is displayed on an LCD Display.
//The PIC16F886 Microcontroller from Microchip is being used.
//2x16 LCD Display being used
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <htc.h>
#include "lcd.h"

#define	COL_1			RB0
#define COL_2			RB1
#define	COL_3			RB7
#define	ROW_1			RB3
#define ROW_2			RB4
#define	ROW_3			RB5
#define	ROW_4			RB6
#define  DelayS(T)		{unsigned char i; for (i = 0; i < T * 10; i++) __delay_ms(100);}	//Delay Macro
#define  _XTAL_FREQ				4000000								//Needs to be set for __delay_ms


//Master Clear Reset enabled & Internal RC No Clock & Watchdog Timer Disable & Power Up Timer On & Brown Out Reset Disabled &
// Low Voltage Porgramming Disabled & Code Unprotect
__CONFIG (MCLREN & INTIO & WDTDIS & PWRTEN & BORDIS & LVPDIS & UNPROTECT);

main()
{
	
PORTA = 0x00;										//PORTA is cleared and set low
PORTB = 0xFF;										//(0b11111111 binary) (0xFF hex)--ROWS set to HIGH, COLUMNS set to HIGH  
													//PORTB7:B0 is set to high,high,high,high,high,high,high,high
													
PORTC = 0x00;										//PORTC is cleared and set low
TRISA = 0x00;										//Set PORTA to outputs for LCD--RA1, RA2, RA4 are Control lines for LCD

TRISB = 0x7C;										//(0b01111100 binary) (0x7C hex)--ROWS set to INPUT, COLUMNS set to OUTPUT
													//PORTB7:B0 is set to output,input,input,input,input,input,output,output,
													//ROWS set to INPUT

TRISC = 0x00;										//Set PORTC to outputs for LCD--RC0:RC3 are Data Lines for LCD


ANSEL = 0;											//Initialize A/D Ports off
ANSELH = 0;											//Initialize ........
CM1CON0 = 0;										//Initialize Comparator 1 off
CM2CON0 = 0;										//Initialize Comparator 2 off

OPTION = 0b01010101;								//OPTION REG
													//xbxxxxx101  1:64
													//xbxxxx0xxx  Prescaler set to Timer0
													//xbxxx1xxxx  (T0SE) set to Increment on high-to-low transition on T0CKI pin
													//xbxx0xxxxx  (T0CS) Internal instruction cycle clock
													//xbx1xxxxxx  (INTEDG) Interrupt on rising edge of INT pin
													//xb0xxxxxxx  (RBPU) PORTB pull-ups are enabled by individual PORT latch values
RBPU = 0;											//Don't think I need this, but to be safe...PORTB Weak Internal Pullups enabled
WPUB0 = 0;											//COL_1 Weak pullup is individually DISABLED--OUTPUT 
WPUB1 = 0;											//COL_2 Weak Pullup is inidivdually DISABLED--OUTPUT
WPUB2 = 0;											//Not being used...Weak Pullup is individually DISABLED
WPUB3 = 1;											//ROW_1 Weak Pullup is individually ENABLED--INPUT
WPUB4 = 1;											//ROW_2 Weak Pullup is individually ENABLED--INPUT
WPUB5 =	1; 											//ROW_3 Weak Pullup is individually ENABLED--INPUT
WPUB6 = 1;											//ROW_4 Weak Pullup is individually ENABLED--INPUT
WPUB7 = 0;											//COL_3 Weak Pullup is individually DISABLED--INPUT


lcd_init();											//LCD Display is Initialized...See LCD.C file and LCD.h

while (1)
	{
		lcd_clear;									//Clear LCD
		lcd_goto(0);								//Go to Line 1 Position 1
		lcd_puts("Matrix Switch");					//Display "Matrix Switch"
		lcd_goto(40);								//Go to Line 2 Position 1
		lcd_puts("Press Button");					//Display "Press Button"
		
		COL_1 = 0;									//COLUMN 1 is set LOW
		if (ROW_1 == 0)								//and If ROW 1 is LOW...
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 1");				//Display "Switch 1"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
				
		if (ROW_2 == 0)								//if ROW 2 is LOW...
			{										//...
				lcd_clear();						//...		
				lcd_goto(0);						//...
				lcd_puts("Switch 4");				//Display "Switch 4"
				__delay_ms(150);			
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_3 == 0)								//If ROW 3 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 7");				//Display "Switch 7"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_4 == 0)								//If ROW 4 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 10");				//Display "Switch 10"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
			
		COL_1 = 1;									//COLUMN 1 is set HIGH again
		COL_2 = 0;									//COLUMN 2 is set LOW
		if (ROW_1 == 0)								//and If ROW 1 is LOW...
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 2");				//Display "Switch 2"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
				
		if (ROW_2 == 0)								//if ROW 2 is LOW...
			{										//...
				lcd_clear();						//...		
				lcd_goto(0);						//...
				lcd_puts("Switch 5");				//Display "Switch 5"
				__delay_ms(150);			
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_3 == 0)								//If ROW 3 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 8");				//Display "Switch 8"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_4 == 0)								//If ROW 4 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 11");				//Display "Switch 11"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		COL_2 = 1;									//COLUMN 2 is set HIGH again
		COL_3 = 0;									//COLUMN 3 is set LOW
		if (ROW_1 == 0)								//and If ROW 1 is LOW...
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 3");				//Display "Switch 3"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
				
		if (ROW_2 == 0)								//if ROW 2 is LOW...
			{										//...
				lcd_clear();						//...		
				lcd_goto(0);						//...
				lcd_puts("Switch 6");				//Display "Switch 6"
				__delay_ms(150);			
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_3 == 0)								//If ROW 3 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 9");				//Display "Switch 9"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		if (ROW_4 == 0)								//If ROW 4 is LOW
			{										//...
				lcd_clear();						//...
				lcd_goto(0);						//...
				lcd_puts("Switch 12");				//Display "Switch 12"
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(150);
				__delay_ms(100);
			}
		COL_3 = 1;									//COLUMN 3 is set HIGH again
	}
}
If you see anything that needs to be changed, please let me know. Hopefully this code can be of use to someone who needs a place to start. Thanks again for all the help.
 
Top