CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Serial Communication via 38khz IR Carrier using DSM

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
jgschmidt



Joined: 03 Dec 2008
Posts: 184
Location: Gresham, OR USA

View user's profile Send private message Send e-mail Visit poster's website

Serial Communication via 38khz IR Carrier using DSM
PostPosted: Fri Apr 26, 2013 8:20 pm     Reply with quote

Below you will see the send and receive code that allows you to send serial data via infrared (IR) using a 38khz carrier wave and a typical 38khz IR receiver to capture the signal.

The key element here is the Data Signal Modulator (DSM) that is part of some PIC chips, in this case the 16F1825, that allows you to generate a carrier with the PWM module and then modulate it with the UART transmit signal. The only external parts needed are the IR LED and a transistor to drive it. I added some push buttons for testing.

CCS does not support the DSM for this part yet but it's really easy to set the registers to make this work. For details please see the comments in the code.
Code:
#case
// 1825Send.c
//
// 2013.04.26 - Jurgen G Schmidt    www.jgscraft.com
//
// 16F1825 Send serial data via IR using DSM module to generate 38khz carrier
//
// - All ports support internal weak pullups
// - 25mA sink/source on I/O pins
//
// Compiler:      CCS PCWH 4.130
// Programmer:    PICkit2
// Dependencies:  None
//
//    + VDD - 1   14 - VSS -
// OSC1/RA5 - 2   13 - RA0/DAT
// OSC2/RA4 - 3   12 - RA1/CLK
// MCLR/RA3 - 4   11 - RA2/CCP3
// CCP1/RC5 - 5   10 - RC0/SCL/SCK
//   TX/RC4 - 6    9 - RC1/SDA/SDI/CCP4
// CCP2/RC3 - 7    8 - RC2/    SDO
//
// RC4/MDOUT drives IR LED via 2N3904 transistor. For increased range you could
//      use several LEDs. My T1 LED worked up to 10 feet at 2400 baud.
//
//-----------------------------------------------------------------------------
#include <16f1825.h>

#FUSES INTRC_IO      //Internal RC Osc, no output
#FUSES NOWDT         //No Watch Dog Timer
#FUSES NOPUT         //No Power Up Timer
#FUSES NOMCLR        //Engages weak pullup
#FUSES NOBROWNOUT    //No brownout reset
//#FUSES CLKOUT        //Check FOSC/4 on pin 3 - should be .125us

#use delay(internal=32M)

// Serial debug data via SW. INVERT so can connect directly to legacy PC serial port
#use rs232( BAUD=9600, XMIT=PIN_C0, INVERT, STREAM=JGSDBG )

// Use UART1, will modulate the 38khz carrier
#use rs232( BAUD=2400, RCV=PIN_C5, XMIT=PIN_C4, STREAM=JGSDAT, ERRORS ) // default HW pins

//-----------------------------------------------------------------------------
//------ DSM Register Definitions

#byte MDCON    = 0x39C     //--Modulation control register--
#bit  MDEN     = MDCON.7      // DSM Enable 1=on
#bit  MDOE     = MDCON.6      // MDOUT Pin enable 1=on
#bit  MDSLR    = MDCON.5      // slew rate limiting enabled 1=on
#bit  MDOPOL   = MDCON.4      // output polarity 1=signal is inverted
#bit  MDOUT    = MDCON.3      // current value of MDOUT pin
#bit  MDBIT    = MDCON.0      // 1= Modulator uses high carrier source

#byte MDSRC    = 0x39D     //--Modulation source control register--
#bit  MDMSODIS = MDSRC.7      // 1= disable output signal driving peripheral out pin
#bit  MDMS3    = MDSRC.3      // Modulation source
#bit  MDMS2    = MDSRC.2      // 0000 = MDBIT of MDCON
#bit  MDMS1    = MDSRC.1      // 0001 = MDMIN port pin
#bit  MDMS0    = MDSRC.0      // 1010 = EUSART TX output

#byte MDCARH   = 0x39F     //--Modulation high carrier control register--
#bit  MDCHODIS = MDCARH.7     // 1= disable output signal driving peripheral out pin
#bit  MDCHPOL  = MDCARH.6     // output polarity 1=signal is inverted
#bit  MDCHSYNC = MDCARH.5     // Modulator high carrier synchronization 1=on
#bit  MDCH3    = MDCARH.3     // Data high carrier selection bits
#bit  MDCH2    = MDCARH.2     // 0000 = Vss
#bit  MDCH1    = MDCARH.1     // 0100 = CCP1
#bit  MDCH0    = MDCARH.0     // 0001 = MDCIN1 port pin (C2 / pin 8)
                              // set to: 10100100

#byte MDCARL   = 0x39E     //--Modulation low carrier control register--
#bit  MDCLODIS = MDCARL.7     // 1= disable output signal driving peripheral out pin
#bit  MDCLPOL  = MDCARL.6     // output polarity 1=signal is inverted
#bit  MDCLSYNC = MDCARL.5     // Modulator low carrier synchronization 1=on
#bit  MDCL3    = MDCARL.3     // Data low carrier selection bits
#bit  MDCL2    = MDCARL.2     // 0000 = Vss
#bit  MDCL1    = MDCARL.1     // 0100 = CCP1
#bit  MDCL0    = MDCARL.0     // 0001 = MDCIN1 port pin (C2 / pin 8)
                              // set to: 0b10100000

// Test 1 - use MDMIN pin (pin 7) to toggle carrier output on MDOUT (pin 6)  WORKS!!!!!
//
// MDCON  = 0b11000001;    // DSM enable, MDOUT enable, no slew, not inverted, modulator uses high carrier source
// MDSRC  = 0b10000001;    // disable peripheral, data from MDMIN pin (so we can see triggering on OSCOPE)
// MDCARH = 0b10100100;    // disable peripheral, not inverted, synced, data high = CCP1 (38khz carrier)
// MDCARL = 0b10100000;    // disable peripheral, not inverted, synced, data low = Vss
//
// Test 2 - use EUSART TX to drive carrier output on MDOUT (pin 6)  WORKS!!!!!
//
// MDCON  = 0b11000000;
// MDSRC  = 0b10001010;    // set source to EUSART, always disable peripheral pin
// MDCARH = 0b10000000;    // the secret here is that data high = Vss
// MDCARL = 0b10000100;    //                     and data low = CCP1 !!!
//

//-----------------------------------------------------------------------------
//------ CONSTANT definitions

//------ PINS ------
#define  PB0      PIN_A5
#define  PB1      PIN_A4
#define  PB2      PIN_C5

#define  LED      PIN_C2     

#define  MINDB    5               // debounce count required for button press

//-----------------------------------------------------------------------------
//------ setups and initializations ------
//-----------------------------------------------------------------------------

void main()
{
   int16  b0,b1,b2;                 // push button debounce counters
   
   setup_adc_ports( NO_ANALOGS);    // ALWAYS, Always, always do this !!!
   
   //----------------76543210
   port_a_pullups( 0b00110000 );
   port_c_pullups( 0b00100000 );

   output_low( LED );


   setup_timer_2(T2_DIV_BY_1, 210, 1 );           
   setup_ccp1( CCP_PWM );           
   set_pwm1_duty( 422L );     // square wave output - 50% duty           

   //---------76543210
   MDCON  = 0b11000000;       // set DSM registers
   MDSRC  = 0b10001010;
   MDCARH = 0b10000000;
   MDCARL = 0b10000100;

   //--------------------------------------------------------------------------
   //------ Big Loop ------

   while( TRUE )
   {
      //------ get button press ------

      if( input( PB0 ) == 0) {
         b0++;
      } else {
         b0=0;
      }   

      if( input( PB1 ) == 0) {
         b1++;
      } else {
         b1=0;
      }   

      if( input( PB2 ) == 0) {
         b2++;
      } else {
         b2=0;
      }   

      //------ process the button press ------
     
      if( b0 == MINDB ) {
         output_high( LED );
         putc('A',JGSDBG);
         fprintf(JGSDAT,"Hello World!");
      }
     
      if( b1 == MINDB ) {
         output_high( LED );
         putc('B',JGSDBG);
         fprintf(JGSDAT,"Testing...");
      }
     
      if( b2 == MINDB ) {
         output_high( LED );
         putc('C',JGSDBG);
         fprintf(JGSDAT,"Goodbye.");
      }

      delay_ms( 10 );
      output_low( LED );

   }//------ end of big loop
}
//-----------------------------------------------------------------------------
//------ end of 1825Send.c ------

The receiver uses the same PIC, even though any with builtin UART will do. I'm using the sisr.c code from CCS to power my interrupt-driven receive buffer. That code is left out, per CCS rules, but you should be able to figure out how to put it back in.
Code:
#case
// 1825Receive.c
//
// 2013.04.26 - Jurgen G Schmidt    www.jgscraft.com
//
// 16F1825 Receive serial data via IR 38khz receiver
//
// - All ports support internal weak pullups
// - 25mA sink/source on I/O pins
//
// Compiler:      CCS PCWH 4.130
// Programmer:    PICkit2
// Dependencies:  CCS example sisr.c for interruptd-driven buffered serial read
//
//    + VDD - 1   14 - VSS -
// OSC1/RA5 - 2   13 - RA0/DAT
// OSC2/RA4 - 3   12 - RA1/CLK
// MCLR/RA3 - 4   11 - RA2/CCP3
// CCP1/RC5 - 5   10 - RC0/SCL/SCK
//   TX/RC4 - 6    9 - RC1/SDA/SDI/CCP4
// CCP2/RC3 - 7    8 - RC2/    SDO
//
// RC5/RX is default UART1 receive pin connected directly to data pin of TSOP34838
//
//-----------------------------------------------------------------------------
#include <16f1825.h>

#FUSES INTRC_IO      //Internal RC Osc, no output
#FUSES NOWDT         //No Watch Dog Timer
#FUSES NOPUT         //No Power Up Timer
#FUSES NOMCLR        //Engages weak pullup
#FUSES NOBROWNOUT    //No brownout reset
//#FUSES CLKOUT        //Check FOSC/4 on pin 3 - should be .125us

#use delay(internal=32M)

// Serial debug data via SW. INVERT so can connect directly to legacy PC serial port
#use rs232( BAUD=9600, XMIT=PIN_A2, INVERT, STREAM=JGSDBG )

#use rs232( BAUD=2400, RCV=PIN_C5, XMIT=PIN_C4, STREAM=JGSDAT, ERRORS )

//-----------------------------------------------------------------------------
//------ Serial ISR Setup

      //----------------------------------------------------
      //
      //      See CCS examples/sisr.c for what you need here
      //
      //      DON'T FORGET TO USE FGETC AND USE THE STREAM
      //      THIS IS DIFFERENT FROM THE CCS EXAMPLE !
      //
      //-----------------------------------------------------

//-----------------------------------------------------------------------------
//------ setups and initializations ------
//-----------------------------------------------------------------------------

void main()
{
   setup_adc_ports( NO_ANALOGS);    // ALWAYS, Always, always do this !!!

   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   delay_ms( 100 );                           // allow serial port to warm up :)

   fprintf(JGSDBG,"\r\n\1825Receive...\r\n");

   do {
      output_toggle( PIN_C0 );
      delay_ms(5000);
      fprintf(JGSDBG, "\r\nReceived data => ");
      while(bkbhit)
        fputc(bgetc(), JGSDBG );
   } while (TRUE);
   
   while( TRUE ) ;
}
//-----------------------------------------------------------------------------
//------ end of 1825Receive.c ------


I've run this up to 4800 baud 6 inches apart with the one T1 IR LED without problems. The same LED at 2400 baud goes about 10 feet just fine.

The DSM module is available in the following parts that I've been able to find so far:
    12F18xx and 16F18xx
    18LFxxK80 family - but can't program with PicKit 2, need PicKit 3
    18F97J94 family
    24FJ64/128GA306/308/310 <- supported by PCD 4.135

Let me know if you find any others.

Enjoy!
_________________
Jürgen
www.jgscraft.com
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group