PIC Development Board with PK3

Thread Starter

Djsarakar

Joined Jul 26, 2020
489
@JohnInTX ,

It's been a long time I couldn't return to my work due to busy life. I will try my best to be persistent.

debounced logic for matrix keypad is not implemented in the post #536 so I am trying to implement it. I'm stuck in this because the debounce routine for push-buttons won't work here.and i don't understand how to debounced any key in matrix keypad
 

Thread Starter

Djsarakar

Joined Jul 26, 2020
489
Welcome back.

What you need to do is well described in my posts before #536. Read through those and think about how to do the debouncing. It’s not that difficult.
Some of my thoughts for the Matrix keypad state machine.

Execute the state machine once every 1ms,

state 1 : Key not pressed
stay in state 1 until the key is not pressed. Move to state 2 if key press is detected

state 2 : Key pressed
stay in sate 2 if key press is detected. Move to state 3 if key is still pressed.

state 3 : Key debouncing
stay in state 3 if key is debouncing. Move to state 4 if bouncing has been stopped

state 4 : Valid Key
stay in state 4 if bouncing has been stopped. Move to state 5

state 5 : Key released
Move back to state 1 when the button is released
 

djsfantasi

Joined Apr 11, 2010
9,237
state 1 : Key not pressed
stay in state 1 until the key is not pressed. Move to state 2 if key press is detected
You have some logical errors in your state descriptions.

  • Key not pressed
  • Stay in state 1 until the key is not pressed (but it is not pressed, that’s why you’re here. So you immediately leave state 1)
  • Move to state 2 if key is pressed (where do you go if key is not pressed?)
Did you mean “while” instead of “until”? Your other state descriptions have the same issue.
 

JohnInTX

Joined Jun 26, 2012
4,787
You have some logical errors in your state descriptions.

  • Key not pressed
  • Stay in state 1 until the key is not pressed (but it is not pressed, that’s why you’re here. So you immediately leave state 1)
  • Move to state 2 if key is pressed (where do you go if key is not pressed?)
Did you mean “while” instead of “until”? Your other state descriptions have the same issue.
The hope is that a good state diagram by TS will reveal those issues and indicate the way forward for coding.
 

Thread Starter

Djsarakar

Joined Jul 26, 2020
489
OK. Draw and post the state diagram.


STATE MACHINE.jpg

  1. Stay in STATE 1 until the key is not pressed. if key is pressed leave STATE 1 and Enter into STATE 2
  2. Stay in STATE 2 if key is still pressed, increment count if key is still pressed (1ms = 1 count). leave the STATE 2 if key is pressed for 15ms and Enter into STATE 3
  3. Stay in STATE 3 if debouncing completed and Enter to STATE 1 when the key is released
 

JohnInTX

Joined Jun 26, 2012
4,787
We had program in #536. Have we lost our way because aim is to write program for matrix keypad.
We have not lost our way - we are right on track. The code in #536 scans the keypad and as you reported, returns correct key codes. It does not debounce the matrix and that is what you need to do next.

But first:
Look at void scanKeyPad(void) lines 260-286. This function is not well written ( I can say that because I wrote it) because it combines 2 functions, scanning and state machine, that we now need to be separate. We need to rewrite a few things.
1) the scan part in lines 260-265 should be all that is in scanKeyPad()
2) the rest of it in lines 267-284 will be deleted and replaced by your 3 state machine above to incorporate debouncing.

Again, you are not debouncing 16 individual switches, you are debouncing ONE scan code from the matrix of switches.

1635201985293.png
Start writing your state machine using a switch statement. The example in on the Microchip forum I referenced shows a good way to do it.
On each timer interval, run the switch (state) statement.
State 1 in your diagram calls scanKeyPad ONCE per call, inspects the scan code and waits while the scan code is No_Key. Get that going first.
Then add code to go to state 2 when the scan code is NOT No_Key. That is the entry to the debouncer.
For now, make state 2 just call scanKeyPad once each time and return to state 1 when the scan code is No_Key again.
When the 2 state switch statement is doing this, you will have replicated the actions done previously by flags in lines 267-284.

As before, use breakpoints to inspect the state changes and key codes as you press and release the buttons.

That is enough for the first pass. Don't worry about state 3 or the incomplete state 2 right now.
 
Last edited:

Thread Starter

Djsarakar

Joined Jul 26, 2020
489
Look at void scanKeyPad(void) lines 260-286. This function is not well written ( I can say that because I wrote it) because it combines 2 functions, scanning and state machine, that we now need to be separate. We need to rewrite a few things.
1) the scan part in lines 260-265 should be all that is in scanKeyPad()
2) the rest of it in lines 267-284 will be deleted and replaced by your 3 state machine above to incorporate debouncing.

Start writing your state machine using a switch statement. The example in on the Microchip forum I referenced shows a good way to do it.
On each timer interval, run the switch (state) statement.
State 1 in your diagram calls scanKeyPad ONCE per call, inspects the scan code and waits while the scan code is No_Key. Get that going first.
I set break point at line 288 and when i run program without pressing any key program should be stop at line 288 but it doesn't happening
When I set break point at line 296 I get key 1, 5, 9 and 13 I don't get any other code
C:
#define _XTAL_FREQ 8000000

// PIC18F45K80 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1L
#pragma config RETEN = OFF      // VREG Sleep Enable bit (Ultra low-power regulator is Disabled (Controlled by REGSLP bit))
#pragma config INTOSCSEL = HIGH // LF-INTOSC Low-power Enable bit (LF-INTOSC in High-power mode during Sleep)
#pragma config SOSCSEL = HIGH   // SOSC Power Selection and mode Configuration bits (High Power SOSC circuit selected)
#pragma config XINST = OFF       // Extended Instruction Set (Enabled)
// CONFIG1H
#pragma config FOSC = INTIO2    // Oscillator (Internal RC oscillator)
#pragma config PLLCFG = OFF     // PLL x4 Enable bit (Disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor (Disabled)
#pragma config IESO = OFF       // Internal External Oscillator Switch Over Mode (Disabled)
// CONFIG2L
#pragma config PWRTEN = OFF     // Power Up Timer (Disabled)
#pragma config BOREN = SBORDIS  // Brown Out Detect (Enabled in hardware, SBOREN disabled)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (1.8V)
#pragma config BORPWR = ZPBORMV // BORMV Power level (ZPBORMV instead of BORMV is selected)
// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer (WDT disabled in hardware; SWDTEN bit disabled)
#pragma config WDTPS = 1048576  // Watchdog Postscaler (1:1048576)
// CONFIG3H
#pragma config CANMX = PORTB    // ECAN Mux bit (ECAN TX and RX pins are located on RB2 and RB3, respectively)
#pragma config MSSPMSK = MSK7   // MSSP address masking (7 Bit address masking mode)
#pragma config MCLRE = ON       // Master Clear Enable (MCLR Enabled, RE3 Disabled)
// CONFIG4L
#pragma config STVREN = ON      // Stack Overflow Reset (Enabled)
#pragma config BBSIZ = BB2K     // Boot Block Size (2K word Boot Block size)
// CONFIG5L
#pragma config CP0 = OFF        // Code Protect 00800-01FFF (Disabled)
#pragma config CP1 = OFF        // Code Protect 02000-03FFF (Disabled)
#pragma config CP2 = OFF        // Code Protect 04000-05FFF (Disabled)
#pragma config CP3 = OFF        // Code Protect 06000-07FFF (Disabled)
// CONFIG5H
#pragma config CPB = OFF        // Code Protect Boot (Disabled)
#pragma config CPD = OFF        // Data EE Read Protect (Disabled)
// CONFIG6L
#pragma config WRT0 = OFF       // Table Write Protect 00800-01FFF (Disabled)
#pragma config WRT1 = OFF       // Table Write Protect 02000-03FFF (Disabled)
#pragma config WRT2 = OFF       // Table Write Protect 04000-05FFF (Disabled)
#pragma config WRT3 = OFF       // Table Write Protect 06000-07FFF (Disabled)
// CONFIG6H
#pragma config WRTC = OFF       // Config. Write Protect (Disabled)
#pragma config WRTB = OFF       // Table Write Protect Boot (Disabled)
#pragma config WRTD = OFF       // Data EE Write Protect (Disabled)
// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protect 00800-01FFF (Disabled)
#pragma config EBTR1 = OFF      // Table Read Protect 02000-03FFF (Disabled)
#pragma config EBTR2 = OFF      // Table Read Protect 04000-05FFF (Disabled)
#pragma config EBTR3 = OFF      // Table Read Protect 06000-07FFF (Disabled)
// CONFIG7H
#pragma config EBTRB = OFF      // Table Read Protect Boot (Disabled)

#include <xc.h>
#include <stdint.h>

__bit Interrupt_Flag_1ms;


enum Key_STATE{STATE1, STATE2, STATE3} STATE;

#define DEBOUNCE_TIKS     10

//----------------- INTERRUPT SERVICE  ---------------------------
void __interrupt(high_priority) tcInt(void)
{
  if (TMR0IF == 1) // Timer0 overflow interrupt flag bit
  {
      TMR0IF = 0;
      TMR0L = 250;    //Timer0 Register Low Byte
      Interrupt_Flag_1ms = 1;

  }
}

void Port_Initialized (void)
{
    // LATx registers
    LATA =  0x00;
    LATB =  0x00;
    LATC =  0x00;
    LATD =  0xFF;
    LATE =  0x00;

//  TRISx registers
    TRISA = 0x00;      // All are output, Unused
    TRISB = 0x0F;      // Button connected to RB0-RB3
    TRISC = 0x00;      // all are output, Unused
    TRISD = 0x0F;      // R is out, C is in
    TRISE = 0x00;      // All are output, Unused

    ANCON0 = 0x00;     // set to digital port
    ANCON1 = 0x00;     // Set to digital port
    CM1CON = 0x00;     // Comparator off
    CM2CON = 0x00;     // Comparator off
    ADCON0 = 0x00;     // A/D conversion Disabled
    ADCON1 = 0x00;     // A/D conversion Disabled
    ADCON2 = 0x00;     // A/D conversion Disabled
}

void Timer0_Initialized (void)
{
     TMR0L = 250;    //Timer0 Register Low Byte

    //T0CON: TIMER0 CONTROL REGISTER
     TMR0ON = 1; //Timer0 On
     T08BIT = 1; // Timer0 is configured as an 8-bit timer/counter0
     T0CS = 0;   // Internal instruction cycle clock (CLKO)
     T0SE = 1;    //Increments on high-to-low transition on T0CKI pin0
     PSA = 0; //Timer0 prescaler is assigned; Timer0 clock input comes from prescaler output
     //1:8 Prescale value
     T0PS2 = 1;
     T0PS1 = 1;
     T0PS0 = 1;
     //INTCON: INTERRUPT CONTROL

     PEIE = 0;   //Disables all peripheral interrupts
     TMR0IE = 1; //Enables the TMR0 overflow interrupt
     INT0IE = 0; //Disables the INT0 external interrupt
     RBIE = 0; //Disables the RB port change interrupt
     TMR0IF = 0;// cleared timer overflow flag
     INT0IF = 0; // disabled external interrupt
     RBIF = 0; //disabled Port Change Interrupt Flag bit
     GIE = 1;    // Enable Global Interrupt Enable bit
}

//==================== KEYPAD =======================================

#define No_Key 0xff     // key code for no key or invalid key

uint8_t Rout, Cin;      // current R and C bit images
uint8_t scan_code;      // combined R + C into one byte
uint8_t KBDkey;         // valid key looked up from scan code
uint8_t KBDout;         // test value for final output
__bit NewKey;           // set to indicate a new key


// ---------------- OUTPUT THE NEXT ROW WITH WRAP AROUND  --------
// Outputs the next row.  Returns rrrr0000

uint8_t nextRout(uint8_t out){
#pragma warning push                // tell XC8 to STFU
#pragma warning disable 373
    out |= 0x08;                    // ensure a 1 gets shifted to bit 4
    out = out << 1;                 // shift the 0 to the next R output bit
    out &= 0xf0;                    // normalize the 4 LSBs for tests   
    if (out == 0b11110000)          // if bit 7 got shifted out..
        out =  0b11100000;          // .. re-init the R image
    LATD = (out |0x0f);             // output to LATD with C bits set to 1
    return (out);                   // return current R as rrrr0000
#pragma warning pop                 // restore normal warnings
}

// --------------  READ THE C INPUTS  --------------------------
// Returns the value of the 4 C inputs as 0000cccc
uint8_t KBD_readC(void)
{
#pragma warning push       
#pragma warning disable 373
    return (PORTD & 0x0f);
#pragma warning pop
}

//-------------  COMPUTE THE SCAN CODE FROM R and C  --------------
// Combines R and C images to one 8 bit scan code as rrrrcccc
uint8_t MakeScanCode(uint8_t R, uint8_t C)
{
#pragma warning push       
#pragma warning disable 373     // tell XC8 to STFU
    return R | C;
#pragma warning pop
}

//-------------  CONVERT SCAN CODE TO KEY VALUE  ---------------------
// Looks up the key value corresponding to scan code.  Returns key value
// or No_Key if no valid scan code - no key pressed or invalid key(s)
// Key value corresponds to what is printed on the PCB 1-16

uint8_t KBD_lookupScanCode (uint8_t scan_code)
  {
   uint8_t Key;
   switch(scan_code)
   {
    case 0x0F:      // no key in R1-R4 // looks for button release
    case 0xEF:      // no key in R1    // these look for no button in one of the 4 rows
    case 0xDF:      // no key in R2
    case 0xBF:      // no key in R3
    case 0x7F:      // no key in R4
        Key=No_Key;
        break;
    case 0xEE:       // R1 x C1 is closed
       Key= 1;
       break;
    case 0xED:       // R1 x C2 is closed
       Key= 5;
       break;
    case 0xEB:       // R1 x C3 is closed
       Key= 9;
       break;
    case 0xE7:       // R1 x C4 is closed
       Key= 13;
       break;
     case 0xDE:      // R2 x C1 is closed
       Key= 2;
       break;
    case 0xDD:       // R2 x C2 is closed
       Key= 6;
       break;
    case 0xDB:       // R2 x C3 is closed
       Key=10;
       break;
    case 0xD7:       // R2 x C4 is closed
       Key= 14;
       break;
    case 0xBE:       // R3 x C1 is closed
       Key= 3;
       break;
    case 0xBD:      // R3 x C2 is closed
       Key= 7;
       break;
    case 0xBB :      // R3 x C3 is closed
       Key= 11;
       break;
    case 0xB7:       // R3 x C4 is closed
       Key= 15;
       break;
    case 0x7E:     // R4 x C1 is closed
       Key=4;
       break;
    case 0x7D:     // R4 x C2 is closed
       Key=8;
       break;
    case 0x7B:     //  R4 x C3 is closed
       Key=12;
       break;
    case 0x77:      // R4 x C4 is closed
       Key=16;
       break;
    default:
       Key= No_Key;
   }
   return Key;
  }

//-------------------- SCAN KEY PAD  --------------------------------
// Test routine that scans the keypad to get ONE key and monitors
// for release

void scanKeyPad(void)
{
    Rout = nextRout(Rout);              // output the next R
    Cin = KBD_readC();                  // read the C inputs
    scan_code = MakeScanCode(Rout,Cin); // combine R and C to get scan code
    KBDkey = KBD_lookupScanCode(scan_code); // look up key value   
}

// -------------- INITIALIZE THE KEYPAD SCANNER  ----------------
void KBDinit(void){
    Rout = 0xf0;        // no R out
    Cin = 0x0f;         // assume no C in
    scan_code = MakeScanCode(Rout,Cin);
    NewKey = 0;
    KBDout = KBDkey = No_Key;
}

//====================== MAIN PROGRAM  ============================
void main(void)
{
    Port_Initialized ();
    Timer0_Initialized ();
    KBDinit();

    PADCFG1 = 0x80;

    while (1)
    {
        if (Interrupt_Flag_1ms == 1)
        {
               Interrupt_Flag_1ms = 0;  // clear the flag for next time                                      // and run all of the 1ms tasks
                    
               switch (STATE)
                {     
                   case STATE1 :       // waiting for a key press
                        scanKeyPad();  // run the keypad scanner
                        if(KBDkey == No_Key)
                        {
                          NewKey = 0;
                        }
                        STATE = STATE2;
                    
                   case STATE2 :
                          
                              if(KBDkey != No_Key)
                              {
                               NewKey = 1;      // set break point here, examine values
                                STATE = STATE3;
                              }
                    
                   case STATE3 :   
                         STATE = STATE1;
                 }
                 break;
        }
      
    }
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
On each timer interval, run the switch (state) statement.
State 1 in your diagram calls scanKeyPad ONCE per call, inspects the scan code and waits while the scan code is No_Key. Get that going first.
Then add code to go to state 2 when the scan code is NOT No_Key. That is the entry to the debouncer.
For now, make state 2 just call scanKeyPad once each time and return to state 1 when the scan code is No_Key again.
When the 2 state switch statement is doing this, you will have replicated the actions done previously by flags in lines 267-284.
As @trebla notes, you are not following either the state diagram nor the description I posted. Fix the state machine.
There is nothing in the state diagram referencing the variable NewKey. Remove all of that. The code must conform to the state diagram.

When you get the state machine states working as described, you can debug the codes generated, if necessary.
 

JohnInTX

Joined Jun 26, 2012
4,787
Is the state diagram shown by me wrong, or does it need further correction?
It is OK for now. Just implement states 1 and a part of 2 as I described in the quote in #554. You are looking for calling the scan function and staying in state 1 while No_Key and state 2 while !No_Key. As you press and release keys it should move from state 1 to state 2 and back as the keys are released. Get that working first.
 

Thread Starter

Djsarakar

Joined Jul 26, 2020
489
It is OK for now. Just implement states 1 and a part of 2 as I described in the quote in #554. You are looking for calling the scan function and staying in state 1 while No_Key and state 2 while !No_Key. As you press and release keys it should move from state 1 to state 2 and back as the keys are released. Get that working first.
sorry but i don't understand what to do. Does the variable No_Key have to be shown in the state machine?
 

JohnInTX

Joined Jun 26, 2012
4,787
Technically the test between states 1 and 2 is KBDkey != No_Key and KBDkey == No_Key.
’Key’ is not significant between states 2 and 3 (why?)
’Counter‘ is not significant in state 3. 'Key' should be KBDkey != No_Key.

On a properly drawn state diagram each condition that causes the system to remain in the state must have a corresponding arrow out of the state (to show where it goes when that condition is no longer true). For example, you have Counter=15 in state 3 but no arrow describing what happens when the counter != 15. That tells you that the state diagram is logically inconsistent and in fact the counter is not used after state 2.

Clean that up and fix the coding for the minimal state 1 and 2 as described earlier.
 
Last edited:
Top