jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
Serial Communication via 38khz IR Carrier using DSM |
Posted: Fri Apr 26, 2013 8:20 pm |
|
|
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 |
|