This blog describes how infrared receivers such as the Vishay TSOP38238TSOP38238 work and rather than using a library or built-in hardware it describes an oddball way of wireless communication of my own design. A microcontroller will be used to turn UART transmissions into a modulated signal which can then be read into UART on the receiving microcontroller using the TSOP38328. While located with the Photonics Project14 Photonics entries, it is not an official entry and it is not my intention to have it eligible for a prize.
Background
I submitted a working project back in 2017 using this idea (my first ever project on Element 14). The concept was to build tiaras for my granddaughter's ballet class with LEDs that would blink in a coordinated fashion and change colors and patterns under control of the ballet instructor in a performance. It was written using the Arduino IDE (actually Energia, a fork of Arduino) to make it easier for non-programmers to modify. Although it was working code and hardware it was not used in the ballet production as her dad got a job in a new city and they moved away before the recital but the kids wore them at Halloween that year. It was fun making my granddaughters blink in unison. The short video below shows how it worked.
It fits in with this month's Photonics theme so here it is revisited and explained in greater detail. Rather than bit-banging output with Energia on a custom MSP430G2553 as done before I'll use the UART hardware on a MSP430F5529 LaunchPad and the TI DriverLib API just to be different. This would appear a goofy way to do it as the F5529 has hardware and the capability for IrDA wireless protocols. But I thought it would be interesting to try it this way.
The MSP430F5529 it is a nice microcontroller and I enjoy using it. DriverLib is easier for me than direct register programming because it requires less commenting and having to look up the registers that I can never remember. TI doesn't provide that many DriverLib examples for the F5529 so I developed my own core functions and examples when I first started using it which are reused in the code below.
TSOP 38238 IR Receiver
The diagram below is from the Vishay datasheet for the TSOP382.., TSOP384.. dated 11 Nov 15 and subject to their disclaimers.
The TSOP38238TSOP38238 is a 38 kHz version of the series of IR receivers for infrared remote control. It has a receiving diode with highest sensitivity at a wavelength of 940 nm and circuitry that provides demodulation of a narrow 38 kHz receiving frequency and a preamplifier. The receiver can be connected directly to a microcontroller for decoding. In order to understand how the sending unit is programmed and built it is necessary to understand the receiver in greater detail.
The current and voltage supply requirement is less than a milliamp and will work down to 2.5V. Great! This makes it a good choice for the coin cells used in the tiaras. The transmission distance is up to 45 meters typical with good directivity and by using more powerful transmitters can be even further. If indoors with reflective surfaces it does not even have to be line of sight from the transmitter. Note however from the spectral sensitivity shown below that it can't be just any IR LED. It needs to be 940 nm. There are other versions with less sensitivity to fluorescent lighting.
As noted above, the incoming signal needs to be modulated at 38 kHz for this particular receiver. This is done to filter out other ambient IR sources. The output is active low as shown below.
And the incoming signal must meet certain requirements.
The table states that the minimum burst length is 10 cycles at 38 kHz (38 kHz period is 26.3 micro seconds). Doing the math it can be seen if common serial frequencies are used then 2400 Baud is the maximum (38000 / 2400 = 15.8 cycles which is greater than the minimum of 10). So rounding off, we will use 16 cycles per burst with each cycle being 26.3 micro seconds long. If there is a start bit, 8 data bits, and a stop bit then a full character will take 1 / 2400 * 10 = 4.2 milliseconds and the total number of cycles is 10 x 16 = 160. Looking at the table above the minimum gap time between character bursts must be 4x the burst or 4 x 4.2 = 16.8 milliseconds. For safety, use 20 milliseconds between bursts. Phew... Pretty sure all that is correct. Now we are ready to design the transmitter.
TSAL6200TSAL6200 IR LED
For this design the Vishay TSAL6200TSAL6200, a 5 mm through hole LED is used. This is the same one used in the datasheet for the receiver. The basic characteristics taken from the datasheet dated 13 Mar 2014 are as follows:
Note that the peak wavelength is as desired and the spectral bandwidth is quite narrow as desired. The relative radiant intensity is fairly narrow which could be an issue but in my design I drove a number of LEDs with a MOSFET so there was increased power and no problems were encountered.
Microcontroller
The schematic is shown below for this demonstration:
The serial output from the microcontroller is routed to input P1.0 which triggers an interrupt whenever the input signal transitions from high to low or low to high. If it triggers high, than a PWM timer operating at 38 kHz outputs on pin P2.0 to the IR LED. If it triggers low, then the PWM timer on pin P2.0 is stopped. This modulated signal is then picked up on the TSOP 38238 which can be decoded by a microcontroller or in this case displayed on an oscilloscope.
Here is the code developed on the MSP-EXP430F5529 LaunchPad using CCS V9.1.0 and the TI v18.12.2.LTS compiler.
/* * IR_Transmit * Transmits ASCII at 2400 Baud over 38 kHz modulated IR LED on pin 2.0 * Uses USCI UART to buffer and transmit IR to TSOP38238 receiver * * MCLK and SMCLK running at approximately 8192/32 * 32768 = 8.388608 MHz * ACLK running at 32768 using the external watch crystal * * millisecond counter uses WDT * * Debug notes: * SMCLK can be measured by oscilloscope or logic analyzer on P2.2 * ACLK can be measured on P1.0 * The IR LED is connected to P2.0 * * Developed on MSP-EXP430F5529 LaunchPad Rev1.5 using CCS V9.1.0, TI v18.12.2.LTS * Fmilburn Feb 2020 */ #include "driverlib.h" // LED #define RED_LED GPIO_PORT_P1,GPIO_PIN0 // Clock #define MCLK_FREQ_KHZ 8192 #define FLLREF_KHZ 32 #define UCS_MCLK_FLLREF_RATIO MCLK_FREQ_KHZ/FLLREF_KHZ volatile unsigned int status; // oscillator fault status // PWM // Desired period = 1/38000 = 26.3 uSec // Clock tick = 1 / 8388608 Hz = .1192 uSec // TIMER_PERIOD = 26.3 / .1192 = 220 #define TIMER_PERIOD 220 #define DUTY_CYCLE 110 #define PWMOUT_PIN GPIO_PORT_P2,GPIO_PIN0 // Serial #define UCA0TXD_PIN GPIO_PORT_P3,GPIO_PIN3 #define UCA0RXD_PIN GPIO_PORT_P3,GPIO_PIN4 #define PWM_TRIGGER_PIN GPIO_PORT_P1,GPIO_PIN6 // Triggers PWM uint8_t receivedData = 0x00; uint8_t transmitData = 0x00; // milliSecs volatile unsigned long milliSeconds = 0; // function declarations void setupPMM(); void setupGPIO(); void setupUCS(); void setupPWM(); void setupUART(); void setupWDT(); //------------------------------- m a i n ---------------------------------- void main (void) { //Stop WDT WDT_A_hold(WDT_A_BASE); // Setup setupPMM(); setupGPIO(); setupUCS(); setupPWM(); setupUART(); setupWDT(); //For debugger __no_operation(); // Enable interrupts __enable_interrupt(); // Start loop and count from 0 to 255 and output on pin 2.0 over and over // Delay in between output to suit the IR receiver (at least 4x burst length // where burst lengths is 1/2400 * 10 for 2400 baud = 17 ms) unsigned long delay = 20; unsigned long finish = milliSeconds + delay; while(1){ // Loop forever more // can do stuff here if (milliSeconds >= finish){ // time to send a character USCI_A_UART_transmitData(USCI_A0_BASE, transmitData); while(USCI_A_UART_queryStatusFlags(USCI_A0_BASE,USCI_A_UART_BUSY)){ // Wait until complete } if (transmitData > 255){ transmitData = 0; } else{ transmitData++; } finish = milliSeconds + delay; } } } //---------------------------- s e t u p P M M ------------------------------- void setupPMM(void){ // set VCore = 0 for 8 MHz and below // = 1 for 12 MHz // = 2 for 20 MHz // = 3 for 25 MHz PMM_setVCore(PMM_CORE_LEVEL_3); } //--------------------------- s e t u p G P I O ------------------------------ void setupGPIO(void){ GPIO_setAsPeripheralModuleFunctionOutputPin( //P1.0 output ACLK for debug GPIO_PORT_P1, GPIO_PIN0); GPIO_setAsPeripheralModuleFunctionOutputPin( //P2.2 output SMCLK for debug GPIO_PORT_P2, GPIO_PIN2); GPIO_setAsPeripheralModuleFunctionInputPin( //Port select XT1 GPIO_PORT_P5, GPIO_PIN4 + GPIO_PIN5); // XT1 out on P5.5, XT1 in on P5.4 // Trigger pin causes the PWM to modulate serial output GPIO_setAsInputPinWithPullUpResistor(PWM_TRIGGER_PIN); GPIO_selectInterruptEdge(PWM_TRIGGER_PIN, GPIO_HIGH_TO_LOW_TRANSITION); GPIO_clearInterrupt(PWM_TRIGGER_PIN); GPIO_enableInterrupt(PWM_TRIGGER_PIN); // PWM output at the 38 kHz needed for IR transmission GPIO_setAsPeripheralModuleFunctionOutputPin(PWMOUT_PIN); //PWM (IR) // Plain old serial GPIO_setAsPeripheralModuleFunctionInputPin(UCA0TXD_PIN); // UART TX GPIO_setAsPeripheralModuleFunctionInputPin(UCA0RXD_PIN); // UART RX // Debug GPIO_setAsOutputPin(RED_LED); // LED GPIO_setOutputLowOnPin(RED_LED); } //---------------------------- s e t u p U C S ------------------------------- void setupUCS(void){ UCS_setExternalClockSource(32768,4000000); // Frequency in Hz of XT1CLK and XT2CLK UCS_initClockSignal( // use XT1CLK for ACLK (32768 Hz) UCS_ACLK, UCS_XT1CLK_SELECT, UCS_CLOCK_DIVIDER_1); UCS_turnOnLFXT1( UCS_XT1_DRIVE_0, // maximum drive strength (3) needed for 25 MHz MCLK UCS_XCAP_3); UCS_initClockSignal( // set DCO FLL reference to DFOCLK (32768 Hz) UCS_FLLREF, UCS_DCOCLK_SELECT, UCS_CLOCK_DIVIDER_1); UCS_initClockSignal( // use DFOCLK for SMCLK UCS_SMCLK, UCS_DCOCLK_SELECT, UCS_CLOCK_DIVIDER_1); UCS_initFLLSettle( // set MCLK frequency and ratio MCLK_FREQ_KHZ, UCS_MCLK_FLLREF_RATIO); } //---------------------------- s e t u p P W M ------------------------------- void setupPWM(void){ //Generate PWM - Timer runs in Up mode Timer_A_outputPWMParam param = {0}; param.clockSource = TIMER_A_CLOCKSOURCE_SMCLK; param.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1; param.timerPeriod = TIMER_PERIOD; param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1; param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET; param.dutyCycle = DUTY_CYCLE; Timer_A_outputPWM(TIMER_A1_BASE, ¶m); } //--------------------------- s e t u p U A R T ------------------------------ void setupUART(void){ // From TI Calculator // http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html // baudrate = 2400 // clock freq = 8.388 MHz // UCBRx = 218, UCBRFx = 7, UCBRSx = 0, UCOS16 = 1 USCI_A_UART_initParam param = {0}; param.selectClockSource = USCI_A_UART_CLOCKSOURCE_SMCLK; param.clockPrescalar = 218; // UCBRx param.firstModReg = 7; // UCBRFx param.secondModReg = 0; // UCBRSx param.parity = USCI_A_UART_NO_PARITY; param.msborLsbFirst = USCI_A_UART_LSB_FIRST; param.numberofStopBits = USCI_A_UART_ONE_STOP_BIT; param.uartMode = USCI_A_UART_MODE; param.overSampling = 1; // UCOS16 if(STATUS_FAIL == USCI_A_UART_init(USCI_A0_BASE, ¶m)) { GPIO_setOutputHighOnPin(RED_LED); return; } //Enable UART module for operation USCI_A_UART_enable(USCI_A0_BASE); //Enable Receive Interrupt USCI_A_UART_clearInterrupt(USCI_A0_BASE, USCI_A_UART_RECEIVE_INTERRUPT); USCI_A_UART_enableInterrupt(USCI_A0_BASE, USCI_A_UART_RECEIVE_INTERRUPT); } //---------------------------- s e t u p W D T ------------------------------- void setupWDT(void){ // Interrupts every millisecond when clock frequency is 8192 kHz WDT_A_initIntervalTimer(WDT_A_BASE, WDT_A_CLOCKSOURCE_SMCLK, WDT_A_CLOCKDIVIDER_8192); WDT_A_start(WDT_A_BASE); //Place WDT in timer interrupt mode SFR_clearInterrupt(SFR_WATCHDOG_INTERVAL_TIMER_INTERRUPT); SFR_enableInterrupt(SFR_WATCHDOG_INTERVAL_TIMER_INTERRUPT); } //----------------------------- N M I _ I S R -------------------------------- #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=UNMI_VECTOR __interrupt #elif defined(__GNUC__) __attribute__((interrupt(UNMI_VECTOR))) #endif void NMI_ISR(void) { do { // trap and wait here until oscillator fault flags are cleared status = UCS_clearAllOscFlagsWithTimeout(1000); } while(status != 0); } //----------------------------- U S C I _ A 0 -------------------------------- #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_A0_VECTOR __interrupt #elif defined(__GNUC__) __attribute__((interrupt(USCI_A0_VECTOR))) #endif void USCI_A0_ISR(void) { switch(__even_in_range(UCA0IV,4)) { //Vector 2 - RXIFG case 2: receivedData = USCI_A_UART_receiveData(USCI_A0_BASE); if(receivedData != transmitData) { // Loop forever GPIO_setOutputHighOnPin(RED_LED); // and turn on red LED while(1); } break; default: break; } } //--------------------------- W D T _ A _ I S R ---------------------------- #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=WDT_VECTOR __interrupt #elif defined(__GNUC__) __attribute__((interrupt(WDT_VECTOR))) #endif void WDT_A_ISR(void) { milliSeconds++; // counts milliseconds } //--------------------------- P o r t 1 I S R ------------------------------ #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(PORT1_VECTOR))) Port_1 (void) #else #error Compiler not supported! #endif { if (GPIO_getInputPinValue(PWM_TRIGGER_PIN) == 1){ // Serial has gone high GPIO_selectInterruptEdge(PWM_TRIGGER_PIN, GPIO_HIGH_TO_LOW_TRANSITION); GPIO_setAsPeripheralModuleFunctionOutputPin(PWMOUT_PIN); } else{ GPIO_selectInterruptEdge(PWM_TRIGGER_PIN, GPIO_LOW_TO_HIGH_TRANSITION); GPIO_setAsOutputPin(PWMOUT_PIN); GPIO_setOutputLowOnPin(PWMOUT_PIN); } GPIO_clearInterrupt(PWM_TRIGGER_PIN); } /* This code contains fragments and sections subject to the following * copyright: * * --COPYRIGHT--,BSD * Copyright (c) 2016, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * --/COPYRIGHT--*/
That took more coding effort than I thought it would. The output signal from the TSOP38238TSOP38238 is inverted but that is easily fixed in the GPIO interrupt of the transmitting F5529. It is pointed out again that this is not the most straight forward way to do IR communication and it evolved from a project with unusual design objectives. It isn't recommended for your project but does show how a number of modules in the F5529 can be programmed with DriverLib including the following:
- Clock Speed
- GPIO Input and Output
- Pulse Width Modulation / Timer
- UART
- Watch Dog Timer
Video Demonstration
The working demonstration is shown below.
Here is an oscilloscope screenshot that better shows the modulation and decoding.
Conclusion
I am a huge fan of the Project 14 competitions and wanted to contribute something even though it is not an official entry. Comments and suggestions are always welcome.
Top Comments