• AVR Freaks

Hot!38KHz with Timer0 PIC16F648A

Page: < 123 > Showing page 2 of 3
Author
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/12 09:42:32 (permalink)
0
ounvmeHere are the last few bits of the signal. The duty cycle is not 50% it is about 22%. The new chip will be here tomorrow so I will go with better hardware and PWM as recommended.
I expect the duty cycle is used to control the overall power to the LED IR emitter.  Really it is of no significance as long as the frequency is roughly 38KHz, and that you do not exceed the peak forward current in the datasheet.


Better hardware will make it easier, but is by no means required.  As I said I have coded IR remote and receivers using PIC12 parts with no IRQs*.
* Edit: admittedly in assembler.
post edited by pcbbc - 2019/03/12 09:43:36
#21
ounvme
Starting Member
  • Total Posts : 65
  • Reward points : 0
  • Joined: 2012/03/10 13:35:40
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/12 11:13:49 (permalink)
0
Since I have a day before the other chip arrives, I might as well practice and get it working on the existing chip.
 
Any more efficient way to scan the button input than using IF statements. each button pulls 2 input pins high and will provide the 3 timings out. Switch can only have 1 input. The highlighted functions are crossed on the cheap remote and need to be moved to different buttons.
post edited by ounvme - 2019/03/12 11:14:50

Attached Image(s)

#22
dan1138
Super Member
  • Total Posts : 3121
  • Reward points : 0
  • Joined: 2007/02/21 23:04:16
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/12 15:49:43 (permalink)
0
@ounvme, As long as you are going to fiddle with obsolete hardware.

This is PIC16F648A code for proof of concept that continuously sends the VOL+ pattern:
/*
 * File:     main.c
 * Target:   PIC16F648A
 * Compiler: XC8 v2.00
 * IDE:      MPLABX v5.10
 *
 *                       PIC16F648A/P
 *                 ____________  ___________
 *                |            \/           |
 *             1 <> RA2/AN2         AN1/RA1 <> 18
 *                |                         |    
 *             2 <> RA3/AN3         AN0/RA0 <> 17
 *                |                         |    
 *             3 <> RA4            OSC1/RA7 <> 16 8MHz crystal
 *                |                         |    
 * 10K pull-up 4 -> MCLR           OSC2/RA6 <> 15 8MHz crystal
 *                |                         |    
 *         GND 5 -> VSS                 VDD <- 14 5v0
 *                |                         |    
 *             6 <> RB0/INT         PGD/RB7 <> 13 Drive IR LED
 *                |                         |    
 *             7 <> RB1/RXD         PGC/RB6 <> 12
 *                |                         |    
 *             8 <> RB2/TXD             RB5 <> 11
 *                |                         |    
 *             9 <> RB3/CCP1        PGM/RB4 <> 10
 *                |_________________________|
 */
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT enabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has PGM function, low-voltage programming enabled)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define _XTAL_FREQ (8000000ul)
#define T0_EDGE_RATE (76000ul)
#define T0_RELOAD_VALUE (_XTAL_FREQ/(4*T0_EDGE_RATE))

#define MODULATION_ON  (0x80)
#define MODULATION_OFF (0x00)
/*
 *
 */
void Init_PIC( void )
{
    INTCON     = 0;          // Disable all interrupts
    PIE1       = 0;
    CMCON      = 0x07;       // Disable comparator analog inputs
    VRCON      = 0x00;       // Disable voltage reference analog I/O
    OPTION_REG = 0b11111111; // Set OPTIONS to default value
}
/*
 * Drive IR LED at 38KHz.
 *
 * This is a hack to get around the insane amount of
 * context saving that XC8(free mode) does for an ISR.
 *
 * WARNING: The only interrupt allowed is TIMER0. NO OTHER INTERRUPT SOURCES CAN BE HANDLED!
 * WARNING: If the T0_RELOAD_VALUE is too small (<21) this function will lock up.
 */
unsigned char __at(0x70) WREG_SAVE;
unsigned char __at(0x71) STATUS_SAVE;
volatile unsigned char __at(0x72) ModulationBit;
volatile unsigned char __at(0x73) EdgeCounter;
void __at(0x0004) IR_38KHz_pulse( void )
{
    __asm("movwf _WREG_SAVE");          /* Save the minimum context for the interrupt.   */
    __asm("movf   STATUS,W");           /*                                               */
    __asm("movwf _STATUS_SAVE");        /*                                               */
    
    INTCONbits.T0IF = 0;
    TMR0 -= T0_RELOAD_VALUE-3;          /* XC8 selects BANK0 because of this statement.  */
    
    __asm("movf  _ModulationBit,W");    /* This sequence of inline assembly replaces     */
    __asm("btfsc  STATUS,2");           /* crap code generated from XC8(free) for:       */
    __asm("bcf    PORTB,7");            /*   if (!ModulationBit) RB7 = 0;                */
    __asm("xorwf  PORTB,F");            /*   PORTB ^= ModulationBit;                     */
    __asm("incf  _EdgeCounter,F");      /*   EdgeCounter++                               */
    
    __asm("movf  _STATUS_SAVE,W");      /* Restore the saved interrupt context.          */
    __asm("movwf  STATUS");             /*                                               */
    __asm("swapf _WREG_SAVE,F");        /*                                               */
    __asm("swapf _WREG_SAVE,W");        /*                                               */
    __asm("retfie");                    /* Exit with interrupts enabled.                 */
}
/*
 *
 */
void Init_TIMER0( void )
{
    INTCONbits.T0IE = 0;        // Disable TIMER0 interrupts
    TRISBbits.TRISB7 = 1;       // Turn off IR LED driver
    ModulationBit = MODULATION_OFF;
    EdgeCounter = 0;
    OPTION_REGbits.PSA = 1;     // TIMER0 presacle is 1:1
    OPTION_REGbits.T0CS = 0;    // TIMER0 clock source is _XTAL_FREQ/4
    TMR0 = (unsigned char)(0-T0_RELOAD_VALUE);
    IR_38KHz_pulse();           // Hack to force XC8 to keep the TIMER0 ISR function
    RB7 = 0;
    TRISBbits.TRISB7 = 0;       // Turn on IR LED driver
    INTCONbits.T0IF = 0;        // Clear TIMER0 interrupt
    INTCONbits.T0IE = 1;        // Enable TIMER0 interrupts
}
/*
 *
 */
unsigned char OutBits[] = { /* VU output pattern */
    (192.375/13.0),(1048.875/13.0),     /* Bit 0 */
    (192.375/13.0),(2000.5/13.0),       /* Bit 1 */
    (192.375/13.0),(777.41/13.0),       /* Bit 2 */
    (192.375/13.0),(2815.916/13.0),     /* Bit 3 */
    (192.416/13.0),(1864.666/13.0),     /* Bit 4 */
    (192.583/13.0),(1864.375/13.0),     /* Bit 5 */
    (192.375/13.0),(777.041/13.0),      /* Bit 6 */
    (192.375/13.0),(2544.0/13.0),       /* Bit 7 */
    (192.375/13.0),(2000.0/13.0),       /* Bit 8 */
};
/*
 *
 */
void main(void)
{
    unsigned char T0_EdgeCount;
    unsigned char GapCount;
    unsigned char Index;
    unsigned char CurrentCount;

    Init_PIC();
    Init_TIMER0();
    
    while (1)
    {
        T0_EdgeCount = EdgeCounter;
        for(Index=0; Index < sizeof(OutBits);)
        {
            ModulationBit = MODULATION_ON;
            CurrentCount = OutBits[Index++];
            while((unsigned char)(EdgeCounter-T0_EdgeCount) < CurrentCount);
            T0_EdgeCount += CurrentCount;

            ModulationBit = MODULATION_OFF;
            CurrentCount = OutBits[Index++];
            while((unsigned char)(EdgeCounter-T0_EdgeCount) < CurrentCount);
            T0_EdgeCount += CurrentCount;
        }
        for(GapCount=0; GapCount < 30; GapCount++)
        {
            while((unsigned char)(EdgeCounter-T0_EdgeCount) < 200);
            T0_EdgeCount += 200;
        }
    }
}

post edited by dan1138 - 2019/03/13 08:59:33
#23
ounvme
Starting Member
  • Total Posts : 65
  • Reward points : 0
  • Joined: 2012/03/10 13:35:40
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 10:48:26 (permalink)
0
dan1138
@ounvme, As long as you are going to fiddle with obsolete hardware.

This is PIC16F648A code for proof of concept that continuously sends the VOL+ pattern:
/*
 * File:     main.c
 * Target:   PIC16F648A
 * Compiler: XC8 v2.00
 * IDE:      MPLABX v5.10
 *
 *                       PIC16F648A/P
 *                 ____________  ___________
 *                |            \/           |
 *             1 <> RA2/AN2         AN1/RA1 <> 18
 *                |                         |    
 *             2 <> RA3/AN3         AN0/RA0 <> 17
 *                |                         |    
 *             3 <> RA4            OSC1/RA7 <> 16 8MHz crystal
 *                |                         |    
 * 10K pull-up 4 -> MCLR           OSC2/RA6 <> 15 8MHz crystal
 *                |                         |    
 *         GND 5 -> VSS                 VDD <- 14 5v0
 *                |                         |    
 *             6 <> RB0/INT         PGD/RB7 <> 13 Drive IR LED
 *                |                         |    
 *             7 <> RB1/RXD         PGC/RB6 <> 12
 *                |                         |    
 *             8 <> RB2/TXD             RB5 <> 11
 *                |                         |    
 *             9 <> RB3/CCP1        PGM/RB4 <> 10
 *                |_________________________|
 */
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT enabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has PGM function, low-voltage programming enabled)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define _XTAL_FREQ (8000000ul)
#define T0_EDGE_RATE (76000ul)
#define T0_RELOAD_VALUE (_XTAL_FREQ/(4*T0_EDGE_RATE))

#define MODULATION_ON  (0x80)
#define MODULATION_OFF (0x00)
/*
 *
 */
void Init_PIC( void )
{
    INTCON     = 0;          // Disable all interrupts
    PIE1       = 0;
    CMCON      = 0x07;       // Disable comparator analog inputs
    VRCON      = 0x00;       // Disable voltage reference analog I/O
    OPTION_REG = 0b11111111; // Set OPTIONS to default value
}
/*
 * Drive IR LED at 38KHz.
 *
 * This is a hack to get around the insane amount of
 * context saving that XC8(free mode) does for an ISR.
 *
 * WARNING: The only interrupt allowed is TIMER0. NO OTHER INTERRUPT SOURCES CAN BE HANDLED!
 * WARNING: If the T0_RELOAD_VALUE is too small (<21) this function will lock up.
 */
unsigned char __at(0x70) WREG_SAVE;
unsigned char __at(0x71) STATUS_SAVE;
volatile unsigned char __at(0x72) ModulationBit;
volatile unsigned char __at(0x73) EdgeCounter;
void __at(0x0004) IR_38KHz_pulse( void )
{
    __asm("movwf _WREG_SAVE");          /* Save the minimum context for the interrupt.   */
    __asm("movf   STATUS,W");           /*                                               */
    __asm("movwf _STATUS_SAVE");        /*                                               */
    
    INTCONbits.T0IF = 0;
    TMR0 -= T0_RELOAD_VALUE-3;          /* XC8 selects BANK0 because of this statement.  */
    
    __asm("movf  _ModulationBit,W");    /* This sequence of inline assembly replaces     */
    __asm("btfsc  STATUS,2");           /* crap code generated from XC8(free) for:       */
    __asm("bcf    PORTB,7");            /*   if (!ModulationBit) RB7 = 0;                */
    __asm("xorwf  PORTB,F");            /*   PORTB ^= ModulationBit;                     */
    __asm("incf  _EdgeCounter,F");      /*   EdgeCounter++                               */
    
    __asm("movf  _STATUS_SAVE,W");      /* Restore the saved interrupt context.          */
    __asm("movwf  STATUS");             /*                                               */
    __asm("swapf _WREG_SAVE,F");        /*                                               */
    __asm("swapf _WREG_SAVE,W");        /*                                               */
    __asm("retfie");                    /* Exit with interrupts enabled.                 */
}
/*
 *
 */
void Init_TIMER0( void )
{
    INTCONbits.T0IE = 0;        // Disable TIMER0 interrupts
    TRISBbits.TRISB7 = 1;       // Turn off IR LED driver
    ModulationBit = MODULATION_OFF;
    EdgeCounter = 0;
    OPTION_REGbits.PSA = 1;     // TIMER0 presacle is 1:1
    OPTION_REGbits.T0CS = 0;    // TIMER0 clock source is _XTAL_FREQ/4
    TMR0 = (unsigned char)(0-T0_RELOAD_VALUE);
 
    IR_38KHz_pulse();           // Hack to force XC8 to keep the TIMER0 ISR function
    RB7 = 0;
    TRISBbits.TRISB7 = 0;       // Turn on IR LED driver
    INTCONbits.T0IF = 0;        // Clear TIMER0 interrupt
    INTCONbits.T0IE = 1;        // Enable TIMER0 interrupts
}
/*
 *
 */
unsigned char OutBits[] = { /* VU output pattern */
    (192.375/13.0),(1048.875/13.0),     /* Bit 0 */
    (192.375/13.0),(2000.5/13.0),       /* Bit 1 */
    (192.375/13.0),(777.41/13.0),       /* Bit 2 */
    (192.375/13.0),(2815.916/13.0),     /* Bit 3 */
    (192.416/13.0),(1864.666/13.0),     /* Bit 4 */
    (192.583/13.0),(1864.375/13.0),     /* Bit 5 */
    (192.375/13.0),(777.041/13.0),      /* Bit 6 */
    (192.375/13.0),(2544.0/13.0),       /* Bit 7 */
    (192.375/13.0),(2000.0/13.0),       /* Bit 8 */
};
/*
 *
 */
void main(void)
{
    unsigned char T0_EdgeCount;
    unsigned char GapCount;
    unsigned char Index;
    unsigned char CurrentCount;

    Init_PIC();
    Init_TIMER0();
    
    while (1)
    {
        T0_EdgeCount = EdgeCounter;
        for(Index=0; Index < sizeof(OutBits);)
        {
            ModulationBit = MODULATION_ON;
            CurrentCount = OutBits[Index++];
            while((unsigned char)(EdgeCounter-T0_EdgeCount) < CurrentCount);
            T0_EdgeCount += CurrentCount;

            ModulationBit = MODULATION_OFF;
            CurrentCount = OutBits[Index++];
            while((unsigned char)(EdgeCounter-T0_EdgeCount) < CurrentCount);
            T0_EdgeCount += CurrentCount;
        }
        for(GapCount=0; GapCount < 30; GapCount++)
        {
            while((unsigned char)(EdgeCounter-T0_EdgeCount) < 200);
            T0_EdgeCount += 200;
        }
    }
}





I will play with this tonight, I got caught up painting yesterday, YAY me. Would it still be bangable at 4MHz using the hack?
The goal is to make the replacement chip drop in so I don't want to remap pins or change crystals. I have a couple of audio nerd buddies that also need this and I do not trust their soldering skills. If it was just for me I could care less and would have swapped RB7 for the PWM pin in the 16F648A.
#24
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 11:15:57 (permalink)
0
This is absoultely bit bangable using the code I posted in post 19.
 
4MHz clock = 1us per instruction
38KHz = 26.3uS per cycle, or 13.1uS per ON/OFF pulse
8 x 26uS = 38KHz for 200uS
 
Code the following assembler in your ISR:
        asm("  banksel 0");
        asm("  movlw 8");
        asm("loop:");
        asm("  bsf PORTB, 7");  // 1uS    38KHz ON
        //Repeat NOPs as necessary for 13us delay, less 1 tick
        asm("  nop");        // 2uS
        asm("  nop");        // 3uS
        asm("  nop");        // 4uS
        asm("  nop");        // 5uS
        asm("  nop");        // 6uS
        asm("  nop");        // 7uS
        asm("  nop");        // 8uS
        asm("  nop");        // 9uS
        asm("  nop");        //10uS
        asm("  nop");        //11uS
        asm("  nop");        //12uS
        asm("  nop");        //13uS
        asm("  bcf PORTB, 7");  // 1uS    38KHz OFF
        //Repeat NOPs as necessary for 13us delay, less 4 ticks
        asm("  nop");        // 2uS
        asm("  nop");        // 3uS
        asm("  nop");        // 4uS
        asm("  nop");        // 5uS
        asm("  nop");        // 6uS
        asm("  nop");        // 7uS
        asm("  nop");        // 8uS
        asm("  nop");        // 9uS
        asm("  nop");        //10uS
        asm("  addlw 0xFF");    //11uS
        asm("  skipz");        //12uS
        asm("  goto loop");    //13/14uS

Now just get your timer ISR to fire ONCE every time you need a 200uS pulse of 38KHz.
Your ISR will be locked up for that 200uS, but in this application that is not important.
Ample time between pulses to do other processing in mainline.
#25
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 11:23:52 (permalink)
0
Sorry, there's one two many NOPs in the 38KHz off section (for 14uS total).
Unfortunately forum won't let me edit my post.
 
Should be 13uS, not 14uS, so please remove one NOP.
Probably would still have worked.  27 clocks would give 37KHz and IR receivers have quite a wide tolerance.
As is 26 clocks is slightly over frequency at 38.46KHz.
 
Edit: If you want to emulate the duty cycle of the original remote (22%) move NOPs from the ON delay to the OFF section.
post edited by pcbbc - 2019/03/14 11:26:55
#26
Ian.M
Super Member
  • Total Posts : 13225
  • Reward points : 0
  • Joined: 2009/07/23 07:02:40
  • Location: UK
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 11:29:06 (permalink)
0
Edit: crossed posts with PBCC above who's just given you most of the inline assembler subroutine required.  
Probably not without resorting to assembler to control the exact edge timing or tying it to an exact compiler version and optimisation level and padding with _delay() experimentally till the timing is correct.  A fully optimised simple empty while loop waiting on a timer interrupt flag (or other single bit) has a minimum jitter of 3 Tcy, which at 4MHz is 3us.  Assuming you want to match the duty cycle, that's half of the six cycle on time required in the 26 cycle period to get 23% duty cycle @38.4 KHz.
 
You'd need to write a function using inline assembler to reliably generate N cycles of 23% duty cycle @38.4 KHz. with entry/exit overhead compensated for so repeated calls to it don't glitch the frequency then string together calls to it and to _delay() too generate each code required.
post edited by Ian.M - 2019/03/14 11:33:42

--
NEW USERS: Posting images, links and code - workaround for restrictions.
I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
#27
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 11:37:26 (permalink)
0
Ian.MYou'd need to write a function using inline assembler to reliably generate N cycles of 23% duty cycle @38.4 KHz. with entry/exit overhead compensated for so repeated calls to it don't glitch the frequency then string together calls to it and to _delay() too generate each code required.
As per the code I posted.
Then use the timer IRQ automatically generate the periods between each 200uS 38KHz pulse.
 
If he does that, no need to compensate for call delay as subtracting from the TMR0 value will automatically adjust.
The minimum delay between IRQs is then over 900uS (200uS of which the ISR spends locked in the assembler loop).
#28
ounvme
Starting Member
  • Total Posts : 65
  • Reward points : 0
  • Joined: 2012/03/10 13:35:40
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 11:42:20 (permalink)
0
I have 3 EDIT:4 different approaches to make this work so as an exercise I will do them all. Right now I have the 16F1827 on my desk. I have the PWM mapped to RB7 and a 38KHz pulse coming out at 35% duty which matches what I logged with the Logic Analyzer. I set a button on RB4 to toggle the value of CCPR1L between 8 and 0. When toggled to 0, the pulse width does not stop it just shortens to almost 0. I have a green LED on the pin and I can see it dim but it does not turn off with the button held. How do I stop the PWM aside from disabling it? 
if (RB4 == 1) 
        {
            CCPR1L = 0;
            //CCP1CONbits.CCP1M = 0b0000;
        }
        else
        {
            CCPR1L = 8;
            //CCP1CONbits.CCP1M = 0b1100;
        }

post edited by ounvme - 2019/03/14 11:47:46
#29
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 11:54:56 (permalink)
0
There are 2 extra bits of duty cycle in DC1B[1:0] in CCP1CON.
What do you have them set to?
#30
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 12:22:44 (permalink)
0
pcbbc
Sorry, there's one two many NOPs in the 38KHz off section (for 14uS total).
Unfortunately forum won't let me edit my post.

It's the live link to post#19 triggering the firewall when you edit.
 

Nearly there...
#31
dan1138
Super Member
  • Total Posts : 3121
  • Reward points : 0
  • Joined: 2007/02/21 23:04:16
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 13:45:52 (permalink)
+2 (2)
ounvme
Would it still be bangable at 4MHz using the hack?

It is not possible to use the method with TIMER0 and interrupts with a system clock slower than 8MHz.
 
Code done without interrupts based on creating the exact timing using only instruction cycle times is possible in assembly language. Doing the same thing with XC8 in C would not be practical because the number of instruction cycles between output events depends too much on how the XC8 optimizer generates the object code.
 
I suspect that you have made some errors in analyzing how the buttons of your remote are connected to the PIC16F648A. You have enumerated 32 types of buttons that are connected to 11 of the GPIO pins of the PIC. Without external components like diodes it does not seem possible to scan a key matrix larger than 30 key with 11 GPIO pins.
 
Note that given 'p' GPIO pins the largest matrix of keys is 'n*m' where 'n' and 'm' are as large as possible and 'n+m' is less than or equal to 'p'. For 11 GPIO pins this is '5*6' or 30 keys.
 
If we presume that all 32 buttons on your remote control function correctly then you must have errors with how you worked out the circuit connections.
#32
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 14:55:18 (permalink)
0
It is possible with only passive hardware, but with certain limitations and you need some clever software.
 
You can add an extra row and have it permanently connected to 0v, so it doesn't need a row driver.  I call this the "ghost row".
Now if you tristate your row drivers, you can tell if any of the keys in the "ghost row" are pressed because it will pull a column pin low in the normal way (and that's the only way that can happen if your driven rows are all tristate).
If you detect nothing is pressed in the "ghost row," scan the first row by driving its row low.
Check if anything is pressed.  If it is, it might be a key is pressed, or it might just be that someone just pressed a key in the "ghost row".  So tristate the row again and check for a ghost key.  If the key is no longer pressed, then you know it's just a valid key in the first row.  Otherwise it's a ghost key.
Proceed through all the driven rows, checking on each before and after that nothing in the ghost row is, or has been pressed.
Implement some de-bounce and you are done.
 
Basically, in the steady state condition (after you have adjusted for possibility of contact bounce):
If every time you toggle a row driver a column goes low then high = key pressed on that row
If every time you toggle a row driver the column instead stays low = key pressed in the ghost row
 
One of the limitations is that keys in the ghost row take precedence over any other keys in the same column.  You can't detect if a ghost key AND a key in the same column are pressed at the same time, and will need to assume it is just the ghost key.
 
Having said this, I'm actually with you on this one.  I'm not convinced the OP has they matrix entirely correct.  I would expect a more even distribution of X's in all columns.  5's and 6's assuming a maximal matrix of 5x6 keys.
#33
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 15:32:55 (permalink)
0
The following keys have identical pairs of pins assigned in your table...
0 / LEFT
RIGHT / VOL-
RESET / DECIMAL
LIGHT / TEST
NIGHT / MOVIE
SET+ / ACTION
VOL+ / JAZZ|CLASSIC
Something is definitely up.
#34
ounvme
Starting Member
  • Total Posts : 65
  • Reward points : 0
  • Joined: 2012/03/10 13:35:40
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 16:29:45 (permalink)
0
pcbbc
The following keys have identical pairs of pins assigned in your table...
0 / LEFT
RIGHT / VOL-
RESET / DECIMAL
LIGHT / TEST
NIGHT / MOVIE
SET+ / ACTION
VOL+ / JAZZ|CLASSIC
Something is definitely up.


The duplicate pins were mapping errors in the POS REDI REMOTE. Some buttons share the same IO pins.
 
dan1138
 
I suspect that you have made some errors in analyzing how the buttons of your remote are connected to the PIC16F648A. You have enumerated 32 types of buttons that are connected to 11 of the GPIO pins of the PIC. Without external components like diodes it does not seem possible to scan a key matrix larger than 30 key with 11 GPIO pins.
 
Note that given 'p' GPIO pins the largest matrix of keys is 'n*m' where 'n' and 'm' are as large as possible and 'n+m' is less than or equal to 'p'. For 11 GPIO pins this is '5*6' or 30 keys.
 
If we presume that all 32 buttons on your remote control function correctly then you must have errors with how you worked out the circuit connections.


If I have 11GPIO as inputs and 2 pins must be pulled high for a function to work, wouldn't that be 11*10 combinations on the PIC? I didn't trace the circuits on the remote but there are at least 7 pull down resistors going to the grid in an array and several diodes as well.
 
 Using the ISR from PCBBC what is the best way to send the TIMER0 timings? It would seem that I would need to use the ISR with an array and global variable to be able to step through them. Using any delays makes using the timer seem redundant. Any tips? 
 
post edited by ounvme - 2019/03/14 16:36:14

Attached Image(s)

#35
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 17:56:07 (permalink)
0
ounvmeIf I have 11GPIO as inputs and 2 pins must be pulled high for a function to work, wouldn't that be 11*10 combinations on the PIC?

No, because that's not how a matrix keyboard works.  It's not about connecting two pins to a third voltage.
It's about a switch connecting two pins of the micro controller together.
 
And when you do that you need to designate some pins and inputs and some as outputs, and you can't drive two outputs at different voltages or you will get a short.  So with 11 pins a 6x5 matrix and 30 keys is the best you can do without external components (or at a push 6x6 and 36 keys using the method I explained).
 
I didn't trace the circuits on the remote but there are at least 7 pull down resistors going to the grid in an array and several diodes as well.
Pullups may be necessary if the PIC doesn't have internal configurable pullups on it's ports - I admit I didn't check.
Diodes can be used to increase the number of keys above the 30 (5x6) available with 11 pins.
 
Using the ISR from PCBBC what is the best way to send the TIMER0 timings? It would seem that I would need to use the ISR with an array and global variable to be able to step through them.
I gave an array and some code for stepping through on each ISR in my first post.
 
You don't necessarily need an array of the timings for every code.  If you can establish the pattern it should be possible to generate quite a compact coding scheme, and then generate the pulse intervals "on the fly".  That coding scheme could be 16 bits or less per key.  Of course just big array saves that reverse engineering - you just record the delays, add them to the array.  So it's easier, assuming you have the space!
 
Using any delays makes using the timer seem redundant. Any tips?
It does.  Either way will work.  It's not necessary to use IRQs at all for this.  Although I think it makes things easier in the long run as your mainline code can just concentrate of scanning the keyboard, and the ISR takes care of the IR timings.  That's just my opinion though.
#36
Ian.M
Super Member
  • Total Posts : 13225
  • Reward points : 0
  • Joined: 2009/07/23 07:02:40
  • Location: UK
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 19:12:09 (permalink)
0
There's no technical reason why a conductive elastomer contact button cant connect three contact pads on the PCB together.  If you have one contact supplying the keyscan output signal and two feeding separate sets of button down inputs, 11 I/O lines could be sufficient to scan a 4x4x3 matrix for a total of 48 buttons.  Obviously one would reject partial button presses where only one set of button down input registered a button press.      However I've worked in the service trade and had a lot of consumer, prosumer and special application remotes cross my bench for repair and I've *NEVER* seen a 3D three contact matrix like I've just described in the wild. 
post edited by Ian.M - 2019/03/14 19:51:29

--
NEW USERS: Posting images, links and code - workaround for restrictions.
I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
#37
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 19:19:01 (permalink)
0
So bottom line, the OP does need to trace out the circuit.
Just guessing the connections from measurements isn't giving a true picture of how it works.
 

Nearly there...
#38
dan1138
Super Member
  • Total Posts : 3121
  • Reward points : 0
  • Joined: 2007/02/21 23:04:16
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 19:37:28 (permalink)
0
@ounvme,

The photo you posted shows 58 positions for buttons. There are 11 diodes that look to be connected to the buttons.

The method used to scan all the buttons is not obvious from that photo.

You will need an accurate and complete circuit diagram to be able to create the code you need.
#39
pcbbc
Super Member
  • Total Posts : 1102
  • Reward points : 0
  • Joined: 2014/03/27 07:04:41
  • Location: 0
  • Status: offline
Re: 38KHz with Timer0 PIC16F648A 2019/03/14 19:37:41 (permalink)
0
What you have here are two matrices of 5x6, with diodes and pullup (or down) resistors on all pins.  The diodes allow the matrix to be driven in either direction.


In one configuration 6 of the pins are configured as outputs, for example A,B,C,D,E,F and the remainder as inputs, 3,8,9,10,11.

Then in the other reverse configuration the opposite, so G,H,I,J,K are outputs, and then the other set as inputs, 1,2,4,5,6,7

That gives you a total possible of 60 keys, of which 2 are unused (H2 and I5).  There are 58 keys on your keyboard.

Attached is an updated image where I have labelled all the pads for you and traced back as best I can.  You will see that each and every pad is connected one side (lettered) to a diode and then to a pin, and the other side (numbered) direct to a pin with a pullup resistor.

Apologies about my letter and number allocations.  I would have been more consistent (A1, B2, C3, etc) but it wasn't until I started tracing everything out that I worked out what was going on.

HTH.





post edited by pcbbc - 2019/03/14 20:07:25

Attached Image(s)

#40
Page: < 123 > Showing page 2 of 3
Jump to:
© 2019 APG vNext Commercial Version 4.5