Jump to content
43oh

Consumer IR (CIR) capture


Recommended Posts

May I ask what is the purpose of your CIR capture device, please? Is it to help you identify an unknown protocol being used by a remote control?

 

It provides all the necessary info needed to reproduce a remote's transmission or respond to a remote control. So, if you want to make your own remote or make something that responds to an existing remote, then this device will provide essential information.

 

I guess I really meant to ask why you need the capability to measure the frequency? After you've identified a protocol (SIRC, RC5, etc.), can't you deduce the frequency?

Link to post
Share on other sites
No, the frequency is part of the protocol specification. Timing alone is not enough information.

 

Really! I didn't know that! I assumed the timing of the various protocols was enough. For example, the 12, 15, or 20 bits of SIRC with 2400/600 start bit, and 1200/600 and 600/600 data bits, was unique enough to identify it from other protocols.

 

Live and learn... Regards, Mike

Link to post
Share on other sites

The MSP430 could calculate the carrier frequency and durations as explained in the previous post, but there is no need for it to do that. All it has to do is send the IR pulse count to the PC and let the PC do the calculations and display the results. Doing this in real time requires that each count be sent in less than the 100 microsecond gate time. The lowest standard bit rate that can do this is 115.2 kbps. The MSP430 assembly code to do this is explained in this thread. The rest of the code is setup, reading the Timer and driving the status LED. The main loop executes in exactly 100 cycles, so a timer is not needed.

 

The UART on the Launchpad is limited to 9600 bps, so a board with a FTDI FT232R chip is used to transport the serial data to the PC.

Launchpad    FT232R
Gnd      --  Gnd
Tx P1.1  ->  Rx
FC P1.5  <-  RTS

 

How to wire it up...

 

Firmware for MSP430G2211

           .cdecls C, LIST, "msp430g2211.h"  ; Include device header file
;
;  1    Vcc      -
;  2    P1.0    In  PU  Alt IR LED
;  3    P1.1    Out --  IO  Txd
;  4    P1.2    In  PU  IO  Rxd
;  5    P1.3    Out --  IO  IR LED Gnd
;  6    P1.4    Out --  Alt SMCLK out
;  7    P1.5    In  PD  IO  Flow Control
;  8    P1.6    Out --  IO  Green LED
;  9    P1.7    Out --  IO  Not Used
; 10    RST     --- --  --  SBW
; 11    TEST    --- --  --  SBW
; 12    XOUT     -
; 13    XIN      -
; 14    Vss      -
;
           .text
           .global _main

_main       mov.w   #0x0280, SP         ; Initialize stackpointer
                                       ;
                                       ; Stop watchdog timer
           mov.w   #WDTPW + WDTHOLD, &WDTCTL
                                       ;
                                       ; Use 1 MHz calibration
           clr.b   &DCOCTL             ;
           mov.b   &CALBC1_1MHZ, &BCSCTL1
           mov.b   &CALDCO_1MHZ, R4    ;
           ;add.b   #7, R4              ; Tweak cal if necessary
           mov.b   R4, &DCOCTL         ;
                                       ;
                                       ;
           mov.b   #0xDA, &P1DIR       ; See chart above for I/O assignment
           mov.b   #0x25, &P1REN       ; Pull up resistor for IR LED, Rxd, Pull down for Flow Control
           mov.b   #0x07, &P1OUT       ; Pull up, serial idle high
           mov.b   #0x11, &P1SEL       ; Enable Timer A external input, SMCLK output
                                       ;
           mov.w   #0x0024, &TACTL     ; External clock, continuous mode
                                       ;
widget                                  ;
           mov     #0x20, R10          ; Setup flow control bitmask in R10
           mov     #0x40, R11          ; Setup status LED output bitmask in R11
           mov     #0x02, R15          ; Setup serial output bitmask in R15
           bic.b   R11, &P1OUT         ; Status LED off
                                       ;
wait        bit.b   R10, &P1IN          ; Check flow control input                                      
           jne     wait                ; Loop until low                                        
                                       ;
           clr     R14                 ; Clear previous count
           bis     #0x04, &TACTL       ; Reset counter
first       mov     TAR, R12            ; Get current count
           cmp     R12, R14            ; Compare current to previous
           jeq     first               ; Loop if no change...
           jmp     ledon               ; Send initial count...
                                       ;
loop                                    ;
           mov     TAR, R12            ; Get current count
                                       ;
           cmp     R12, R14            ; Compare current to previous
           jne     ledon               ;
           bic.b   R11, &P1OUT         ; == LED off
                                       ; Start bit
           bic.b   R15, &P1OUT         ;  0
           jmp     bit0                ;
                                       ;
ledon       bis.b   R11, &P1OUT         ; != LED on
                                       ; Start bit
           bic.b   R15, &P1OUT         ;  0
           jmp     bit0                ;
                                       ;
bit0        rrc     R14                 ; Bit 0
           jc      bit0h               ;
           bic.b   R15, &P1OUT         ;  9
           nop                         ;
           rrc     R14                 ; Bit 1
           jc      bit1h               ;
           bic.b   R15, &P1OUT         ; 17
           jmp     bit2                ;
bit0h       bis.b   R15, &P1OUT         ;  9
           nop                         ;
                                       ;
bit1        rrc     R14                 ; Bit 1
           jc      bit1h               ;
           bic.b   R15, &P1OUT         ; 17
           jmp     bit2                ;
bit1h       bis.b   R15, &P1OUT         ; 17
           jmp     bit2                ;
                                       ;
bit2        rrc     R14                 ; Bit 2
           jc      bit2h               ;
           bic.b   R15, &P1OUT         ; 26
           jmp     bit3                ;
bit2h       bis.b   R15, &P1OUT         ; 26
           jmp     bit3                ;
                                       ;
bit3        rrc     R14                 ; Bit 3
           jc      bit3h               ;
           bic.b   R15, &P1OUT         ; 35
           nop                         ;
           rrc     R14                 ; Bit 4
           jc      bit4h               ;
           bic.b   R15, &P1OUT         ; 43    
           jmp     bit5                ;
bit3h       bis.b   R15, &P1OUT         ; 35
           nop                         ;
                                       ;
bit4        rrc     R14                 ; Bit 4
           jc      bit4h               ;
           bic.b   R15, &P1OUT         ; 43    
           jmp     bit5                ;
bit4h       bis.b   R15, &P1OUT         ; 43
           jmp     bit5                ;
                                       ;
bit5        rrc     R14                 ; Bit 5
           jc      bit5h               ;
           bic.b   R15, &P1OUT         ; 52
           jmp     bit6                ;
bit5h       bis.b   R15, &P1OUT         ; 52
           jmp     bit6                ;
                                       ;
bit6        rrc     R14                 ; Bit 6
           jc      bit6h               ;
           bic.b   R15, &P1OUT         ; 61
           nop                         ;
           rrc     R14                 ; Bit 7
           jc      bit7h               ;
           bic.b   R15, &P1OUT         ; 69
           jmp     bitstop             ;
bit6h       bis.b   R15, &P1OUT         ; 61
           nop                         ;
                                       ;
bit7        rrc     R14                 ; Bit 7
           jc      bit7h               ;
           bic.b   R15, &P1OUT         ; 69
           jmp     bitstop             ;
bit7h       bis.b   R15, &P1OUT         ; 69
           jmp     bitstop             ;
                                       ;
bitstop     nop                         ; Stop Bit
           jmp     $ + 2               ;
           bis.b   R15, &P1OUT         ; 78
                                       ;
           mov     R12, R14            ; Move current count to previous
                                       ;
           nop                         ;
                                       ;   
           bit.b   R10, &P1IN          ; Check flow control input
           jeq     loop                ; Keep sending data while low...                                        
           jmp     widget              ; Start over if high...                                 
                                       ;
                                       ;
                                       ; Interrupt Vectors
           .sect   ".reset"            ; MSP430 reset vector
           .short  _main               ;
                                       ;
           .end                        ;

 

Windows software

 

Next post will show how to use the captured data to reproduce the IR signal with an MSP430.

Link to post
Share on other sites
  • 2 weeks later...

The capture software (IR Scope) presents a graphic representation of the captured IR signal as previously shown. It also saves the capture to a text file.

 

post-2341-135135501962_thumb.png

The text file has a three line header followed by the on and off durations. The first line identifies the file as an ir capture with file format revision 0. The second line is the IR carrier frequency. The third line is the count of on/off durations that follow. An on duration is a positive integer followed by an optional cycle count. An off duration is a negative integer.

 

The contents of the text file are simply reformatted for use in the MSP430 firmware. The carrier frequency is stored as (CPU_clock / IR_carrier) - 1. That gives the Timer A PWM period. Having the assembler do the calculation saves codes space and execution time. The on and off durations are the same as the capture except for values that will not fit in a 16 bit integer - they must be split up in to several words. The cycle count is not used. The list is terminated with a zero rather than storing the length- that makes manual editing easier.

 

   .word (1000000 / 38446) - 1  ; Carrier frequency
   .word 8952, -4422  ; On/off duration
   .word 578, -548
   .word 578, -496
   .word 604, -544
   .word 556, -1670
   .word 556, -544
   .word 582, -544
   .word 578, -522
   ; more...
   .word 0     ; End of code

Timer A PWM is used to generate the IR carrier frequency. A delay loop with one cycle granularity is used to time the on/off durations. The loop overhead has been adjusted so the timing is exact.

 

This code simply transmits one IR code when the P1.3 pushbutton on the Launchpad is pressed. It could easily be expanded to handle a keypad and any number of IR codes.

 

           .cdecls C, LIST, "msp430g2211.h"  ; Include device header file
;
;  1    Vcc      -
;  2    P1.0    In  PU  IO  Not Used
;  3    P1.1    Out --  IO  Txd
;  4    P1.2    In  PU  IO  Rxd
;  5    P1.3    In  PU  IO  Switch
;  6    P1.4    Out --  Alt SMCLK out
;  7    P1.5    Out --  IO  Not Used
;  8    P1.6    Out --  Alt IR Out
;  9    P1.7    Out --  IO  Not Used
; 10    RST     --- --  --  SBW
; 11    TEST    --- --  --  SBW
; 12    XOUT     -
; 13    XIN      -
; 14    Vss      -
;
;
           .text
           .global _main

_main       mov.w   #0x0280, SP         ; Initialize stackpointer
                                       ;
                                       ; Stop watchdog timer
           mov.w   #WDTPW + WDTHOLD, &WDTCTL
                                       ;
                                       ; Use 1 MHz calibration
           clr.b   &DCOCTL             ;
           mov.b   &CALBC1_1MHZ, &BCSCTL1
           mov.b   &CALDCO_1MHZ, R4    ;
           ;add.b   #7, R4              ;
           mov.b   R4, &DCOCTL         ;
                                       ;
                                       ;
           mov.b   #0xF2, &P1DIR       ; See chart above for I/O assignment
           mov.b   #0x0D, &P1REN       ; Pull up resistor for switch and Rxd
           mov.b   #0x0F, &P1OUT       ; Pull up, serial idle high
           mov.b   #0x50, &P1SEL       ; Enable Timer A output, SMCLK output
           mov     #0x0210, &TACTL     ; Timer A config: SMCLK, count up
                                       ;
                                       ;
wait        bit.b   #0x08, &P1IN        ; Wait for switch down
           jnz     wait                ;
           ;mov        #ir_test, R4        ; Setup pointer to IR data
           mov     #ir_ac_cool, R4     ; Setup pointer to IR data
           call    #tx_ir              ; Send IR
           .if 1
ir_rpt      mov     #ir_ac_rpt, R4      ; Setup pointer to IR repeat data
           call    #tx_ir              ; Send IR
           bit.b   #0x08, &P1IN        ; Check if switch down
           jz      ir_rpt              ; Yes, send repeat code again...
           .endif
           jmp     wait                ; Loop...
                                       ;
                                       ;
tx_ir       mov     @R4+, R12           ; Get carrier period
           mov     R12, &TACCR0        ; Setup Timer A period
           rra     R12                 ; Setup Timer A compare
           mov     R12, &TACCR1        ;
                                       ;
ir_tx_loop  mov     @R4+, R12           ; Get duration
           tst     R12                 ; Check if zero
           jeq     ir_done             ; Yes, exit...
           jn      ir_off              ; If negative, turn off ir...
                                       ;
           mov     #0x00E0, &TACCTL1   ; Turn on IR carrier (reset/set output mode)
           inv     R12                 ; Wait for on duration
           call    #delay              ;
           jmp     ir_tx_loop          ; Do next duration...
                                       ;
ir_off      mov     #0x00A0, &TACCTL1   ; Turn off IR carrier (reset output mode)
           dec     R12                 ; Wait for off duration
           call    #delay              ;
           jmp     ir_tx_loop          ; Do next duration...
                                       ;
ir_done     mov     #0x00A0, &TACCTL1   ; Make sure IR carrier is off
           ret                         ; Return
                                       ;
                                       ;
                                       ;
delay       add     #32, R12            ; Adjust for overhead
delay4      nop                         ; 4 cycle loop
           add     #4, R12             ;
           jnc     delay4              ;
           rla     R12                 ; One to four cycle remainder
           add     R12, PC             ;
           nop                         ;
           nop                         ;
           nop                         ;
           ret                         ;
                                       ;
ir_test     .word   (1000000 / 40000) - 1 ; Carrier period
           .word   200, -200           ;
           .word   300, -300           ;
           .word   400, -400           ;
           .word   500, -500           ;
           .word   600, -600           ;
           .word   700, -700           ;
           .word   800, -800           ;
           .word   900, -900           ;
           .word   1000, -1000         ;
           .word   500, -10000         ;
           .word   0                   ;
                                       ;

ir_sony     .word   (1000000 / 40000) - 1 ; Carrier period
           .word   2400, -600          ; Lead in
           .word   1200, -600          ; 1
           .word    600, -600          ; 2
           .word   1200, -600          ; 3
           .word    600, -600          ; 4
           .word   1200, -600          ; 5
           .word    600, -600          ; 6
           .word   1200, -600          ; 7
           .word    600, -600          ; 8
           .word   1200, -600          ; 9
           .word    600, -600          ; 10
           .word   1200, -600          ; 11
           .word    600, -600          ; 12
           .word    600, -30000        ; Lead out
           .word   0                   ;
                                       ;

ir_ac_cool  .word   (1000000 / 38265) - 1 
           .word   8930, -4444
           .word   578, -548
           .word   578, -522
           .word   578, -544
           .word   556, -1670
           .word   556, -544
           .word   582, -544
           .word   578, -522
           .word   578, -548
           .word   578, -1644
           .word   556, -544
           .word   582, -1644
           .word   582, -544
           .word   578, -1648
           .word   552, -1670
           .word   556, -1670
           .word   556, -1670
           .word   556, -1670
           .word   552, -548
           .word   578, -522
           .word   578, -1644
           .word   582, -544
           .word   582, -518
           .word   582, -544
           .word   578, -522
           .word   578, -548
           .word   552, -1648
           .word   578, -1670
           .word   556, -544
           .word   582, -1644
           .word   578, -1648
           .word   578, -1644
           .word   582, -1644
           .word   582, -30518
           .word   -10000, 0

ir_ac_rpt   .word   (1000000 / 38265) - 1 
           .word   8930, -2244
           .word   556, -32000
           .word   -32000, -31170
           .word   0                           


                                       ; Interrupt Vectors
           .sect   ".reset"            ; MSP430 reset vector
           .short  _main               ;
                                       ;
           .end                        ;

 

Link to post
Share on other sites

Thanks for that oPossum.

 

It was fun reading the assembler code. I haven't done any assembler in a long time. I used to write in Z80, TMS9900 (on the TI-99/4A) and 8086 (moved to higher languages before the 80286 came out so haven't ever done anything with protected mode etc).

 

Nice to see I can still read and understand it all :) (although Im not sure I could write from scratch)

 

I assume that the lines starting with dots are compiler directives (.if 1).

 

One question though.. You have a bit commented out that Im curious about. You load the DCO calibration into R4 and have commented out where you add 7 to it. Why is this? Have you tested the calibrations and found that 7 was closer, or did adding 7 come closer to the carrier frequency you wanted?

 

Anyway, thanks again for a fun, educational read.

 

Good luck in POTM

Link to post
Share on other sites

The .if/.endif are equivalent to #if/#endif in C. They are assembler directives that allow conditional assembly. In this case it allows the code for NEC style repeating codes to be easily disabled when it is not needed.

 

I have tweaked the DCO value to be more accurate for the specific chip I am using. The calibration in flash is close, but tweaking it got the clock even closer to 1.00 MHz. The firmware enables SMCLK output on P1.4 so the clock frequency can be checked and adjusted if desired.

 

Having a precise clock is usually not necessary for sending IR, but it is very desirable for IR capture.

 

The TMS430 has a small and simple instruction set, so it is easy to learn. I think it is a good chip to learn assembly programming. For low power, high performance or cost sensitive applications it can be very useful to program in assembly or a mix of C/C++ and assembly.

Link to post
Share on other sites
  • 3 weeks later...

You probably meant to write "the MSP430 has a small and simple instruction set .." :-)

 

So far I either used an oscilloscope or a photo diode hooked up to the audio input. Of course, those often have decoupling capacitors and filters so the signal will be distorted as any DC component will 'fade out'. With 38 kHz and a high sampling rate it works OK though.

 

How did you create the signal imagery (something like http://drawtiming.sourceforge.net/)?

 

Just a couple of other IR resources:

 

LaunchPad IR Receiver viewtopic.php?f=9&t=358

 

"The code watches for pulses on P1.6 (IR_BIT) and accumulates pulse timings into an array. Once enough have been gathered they

Link to post
Share on other sites

Problem (see solution below)

When I try to build this as "main.asm" with TI's Code Composer Studio (CCS) inside an "empty assembly-only project" project template, I get lots of errors. Build output first lines below:

**** Build of configuration Debug for project irscope ****

C:\Program Files\Texas Instruments\ccsv4\utils\gmake\gmake -k all 
'Building file: ../main.asm'
'Invoking: Compiler'
"C:/Program Files/Texas Instruments/ccsv4/tools/compiler/MSP430 Code Generation Tools 3.3.3/bin/cl430" -vmsp -g --define=__MSP430G2211__ --include_path="C:/Program Files/Texas Instruments/ccsv4/msp430/include" --include_path="C:/Program Files/Texas Instruments/ccsv4/tools/compiler/MSP430 Code Generation Tools 3.3.3/include" --diag_warning=225 --printf_support=minimal --preproc_with_compile --preproc_dependency="main.pp"  "../main.asm"
"../main.asm", ERROR!   at line 21: [E0002] Illegal mnemonic specified
    _main       mov.w   #0x0280, SP         ; Initialize stackpointer

Also errors:

[E0001] Undefined symbol
[E0004] Non-local label

 

Solution

 

When copying oPossum's embedded source code from this forum, everything was indented. Labels must not be indented! Everything else should be.

irscope.asm.txt

irscope.txt

Link to post
Share on other sites

I was able to capture an Apple Remote 2G with an OSRAM LD274 IRED ( http://catalog.osram-os.com/catalogue/c ... owBookmark ) at very close range.

A 5 mm IRED of unknown type did not work, neither did a large photodiode, which demodulated the signal (see oPossum's remark on the TIA).

 

So thanks, nice project, and a well-written explanation.

 

Some ideas in return (you probably thought of this):

Ideally, it would be possible to use just a LaunchPad with the IRED to receive and send, using a cross-platform software :-)

While the frequency is interesting, the pulse count can be estimated from burst times.

Also, it would probably be practical if the SPACEs had an equivalent pulse count displayed.

 

As I've not used MS VC before - maybe someone knows how to solve this problem?

Can't import with Visual C++ 2010 Express :

End tag 'xs:choice' does not match the start tag 'xs:sequence'.

Link to post
Share on other sites

Unfortunately the Windows software uses MFC, so it can't be build with the Express version of Visual C. I am working on new software that uses Qt, and will be testing on Windows and Mac.

 

Interesting that the LED of unknown type did not work. I have used a variety of IR LEDs from various mfg and they all worked.

 

Large photodiodes have substantial capacitance, so no surprise it didn't work. An external pullup resistor may help compensate for the capacitance.

 

I have developed a novel and very simple method to send IR, but haven't had time to do a write up. I have it working on PIC12F and could easily port it to MSP430.

 

Thanks for the comments and feedback. Good to know it was useful to someone.

 

The IR Widget has become quite popular with the JP1 hackers. One forum member even sells them.

Link to post
Share on other sites
  • 9 months later...

Hi oPossum,

 

Fantastic work. You posted asm code for the sending of data. I'm only familiar with C code in CCS. Is there a way for you to convert that to C code and post it here, so I can understand what's going on and integrate this into some code I'm working on?

 

Best regards,

D

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...