From 55feb834c6717c48050d714bdf1a5e3d92f43bd8 Mon Sep 17 00:00:00 2001 From: Markus Lanthaler Date: Mon, 4 Jun 2012 16:40:13 +0800 Subject: [PATCH] Add support to IOIOLib and IOIO firmware to send infrared codes in Pronto format There's still a small bug in ir.c which required me to hardcode the carrier frequency. As soon as I use the one that's stored in ir_buffer, I get a duty cycle that's not 50% anymore. I couldn't find why the calculated pulse-width changed. The Pronto format is documented, e.g., here: http://www.remotecentral.com/features/irdisp1.htm --- firmware/app_layer_v1/ir.c | 146 ++++++++++++++++++ firmware/app_layer_v1/ir.h | 50 ++++++ .../app_layer_v1/nbproject/configurations.xml | 2 + firmware/app_layer_v1/protocol.c | 14 +- firmware/app_layer_v1/protocol_defs.h | 16 ++ software/IOIOLib/src/ioio/lib/api/IOIO.java | 13 +- .../src/ioio/lib/api/IrTransmitter.java | 75 +++++++++ .../IOIOLib/src/ioio/lib/impl/IOIOImpl.java | 9 ++ .../src/ioio/lib/impl/IOIOProtocol.java | 16 ++ .../src/ioio/lib/impl/IrTransmitterImpl.java | 80 ++++++++++ 10 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 firmware/app_layer_v1/ir.c create mode 100644 firmware/app_layer_v1/ir.h create mode 100644 software/IOIOLib/src/ioio/lib/api/IrTransmitter.java create mode 100644 software/IOIOLib/src/ioio/lib/impl/IrTransmitterImpl.java diff --git a/firmware/app_layer_v1/ir.c b/firmware/app_layer_v1/ir.c new file mode 100644 index 000000000..b278ee801 --- /dev/null +++ b/firmware/app_layer_v1/ir.c @@ -0,0 +1,146 @@ +/* + * Copyright 2012 Markus Lanthaler . All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED "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 ARSHAN POURSOHI 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +#include "Compiler.h" +#include "HardwareProfile.h" +#include "GenericTypeDefs.h" +#include "ir.h" +#include "pins.h" +#include "timer.h" +#include "logging.h" +#include "sync.h" + +#define delayMicroseconds(x) \ +{ \ + unsigned long _dcnt = (x)*((unsigned long)(0.000001/(1.0/GetInstructionClock())/6)); \ + while(_dcnt--); \ +} + +#define IR_BUFFFER_SIZE 256 // ir data buffer size + +static WORD ir_buffer[IR_BUFFFER_SIZE] __attribute__((far)); +static WORD ir_buffer_length = 0; + + +//-------------------------------------------------------------------------- +void bufferIrData(WORD data) { + if(ir_buffer_length < IR_BUFFFER_SIZE) { + ir_buffer[ir_buffer_length] = data; + ir_buffer_length++; + } else { + log_printf("IR buffer too small! Cleared it"); + ir_buffer_length = 0; + } +} + + +//-------------------------------------------------------------------------- +/** + * Send a burst-pair over infrared. + * + * This method sends a burst pair over IR. A burst pair is a on/off + * sequence with the specified duration. As result, this method first + * flashes the IR transmitter LED for "on_length" periods with a + * frequency of (1000 / (2 * pulse_width) kHz) and then it switches the IR + * transmitter LED of for "off_length" periods. + * + * @param pin the pin to which the LED is attached. + * @param pulse_width the pulse width of the carrier (= carrier frequency / 2) + * in microseconds. + * @param on_length the length of the high-signal to sent in number of carrier + * periods. + * @param off_length the length of the low-signal to sent in number of carrier + * periods. + */ +static inline void sendIrBurstPair(int pin, unsigned int pulse_width, WORD on_length, WORD off_length) { + // activate pulse modulated signal + for(; on_length > 0; on_length--) + { + PinSetLat(pin, 1); // set IR pin to HIGH + delayMicroseconds(pulse_width); + PinSetLat(pin, 0); // set IR pin to LOW + delayMicroseconds(pulse_width); + } + + // deactivate pulse modulated signal + for(; off_length > 0; off_length--) + { + PinSetLat(pin, 0); // set IR pin to LOW + delayMicroseconds(pulse_width); + PinSetLat(pin, 0); // do it again to get timing right + delayMicroseconds(pulse_width); + } +} + + +//-------------------------------------------------------------------------- +void sendBufferedIrData(int pin) { + if (ir_buffer_length < 4) { + log_printf("IR buffer empty"); + ir_buffer_length = 0; + return; + } + + unsigned int i = 0; + // TODO If the pulse width is taken from the array I get a 33% duty cycle instead of 50% - fix this + // The current value is tuned to a Pronto value of 006E + unsigned int pronto_frequency = 130; // ir_buffer[1]; + unsigned int sequence1_length = ir_buffer[2] * 2; + unsigned int sequence2_length = ir_buffer[3] * 2; + + if (ir_buffer_length < 4 + sequence1_length + sequence2_length) { + log_printf("IR data buffer does not contain all required data"); + ir_buffer_length = 0; + return; + } + + unsigned int pulse_width = (pronto_frequency * 0.241246) / 2; + + BYTE prev = SyncInterruptLevel(1); + + if(sequence1_length > 0) + { + for(i = 0; i < sequence1_length; i += 2) + { + sendIrBurstPair(pin, pulse_width, ir_buffer[4 + i], ir_buffer[5 + i]); + } + } + + if(sequence2_length > 0) + { + for(i = 0; i < sequence2_length; i += 2) + { + sendIrBurstPair(pin, pulse_width, ir_buffer[4 + sequence1_length + i], ir_buffer[5 + sequence1_length + i]); + } + } + + ir_buffer_length = 0; + + SyncInterruptLevel(prev); +} diff --git a/firmware/app_layer_v1/ir.h b/firmware/app_layer_v1/ir.h new file mode 100644 index 000000000..a169be82b --- /dev/null +++ b/firmware/app_layer_v1/ir.h @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Markus Lanthaler . All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED "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 ARSHAN POURSOHI 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ + +#ifndef __IR_H__ +#define __IR_H__ + + +/** + * Buffer a burst pair of infrared data before sending it to meet the required + * timing requirements. + * + * @param data the data to buffer. + */ +void bufferIrData(WORD data); + +/** + * Send buffered infrared data. + * + * @param pin the pin to which the LED is attached. + */ +void sendBufferedIrData(int pin); + + +#endif // __IR_H__ diff --git a/firmware/app_layer_v1/nbproject/configurations.xml b/firmware/app_layer_v1/nbproject/configurations.xml index 0c801b89f..233232301 100644 --- a/firmware/app_layer_v1/nbproject/configurations.xml +++ b/firmware/app_layer_v1/nbproject/configurations.xml @@ -36,6 +36,7 @@ sync.h timers.h uart.h + ir.h spi.c timers.c uart.c + ir.c . All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED "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 ARSHAN POURSOHI 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.api; + +import ioio.lib.api.exception.ConnectionLostException; + +/** + * A pin used for sending infrared codes. + *

+ * An infrared transmitter pin can be used to send infrared signals. + * IrTransmitter instances are obtained by calling + * {@link IOIO#openIrOutput(int pin)}. + *

+ * The infrared code is send by calling {@link #transmitIrCommand(int[]}. The + * data must encoded as + * {@link http://www.remotecentral.com/features/irdisp2.htm Pronto} burst-pair + * stream, i.e., two bytes are combined into one integer and form a burst-pair. + *

+ * The instance is alive since its creation. If the connection with the IOIO + * drops at any point, the instance transitions to a disconnected state, in + * which every attempt to use the pin (except {@link #close()}) will throw a + * {@link ConnectionLostException}. Whenever {@link #close()} is invoked the + * instance may no longer be used. Any resources associated with it are freed + * and can be reused. + *

+ * Typical usage: + * + *

+ * IrTransmitter ir = ioio.openIrOutput(10);  // IR LED anode on pin 10.
+ * ir.transmitIrCommand(prontoCode);  // send Pronto encoded IR signal.
+ * ...
+ * ir.close();  // pin 10 can now be used for something else.
+ * 
+ * + * @author Markus Lanthaler + * @author Andrej Eisfeld + * @author Sergej Proskurin + */ +public interface IrTransmitter extends Closeable { + /** + * Transmit a Pronto-encoded IR command. + * + * @param data + * The data to transmit. + * @throws ConnectionLostException + * The connection with the IOIO has been lost. + */ + public void transmitIrCommand(int[] data) throws ConnectionLostException; +} diff --git a/software/IOIOLib/src/ioio/lib/impl/IOIOImpl.java b/software/IOIOLib/src/ioio/lib/impl/IOIOImpl.java index ea7aeb192..cc38023ee 100644 --- a/software/IOIOLib/src/ioio/lib/impl/IOIOImpl.java +++ b/software/IOIOLib/src/ioio/lib/impl/IOIOImpl.java @@ -35,6 +35,7 @@ import ioio.lib.api.DigitalOutput; import ioio.lib.api.IOIO; import ioio.lib.api.IOIOConnection; +import ioio.lib.api.IrTransmitter; import ioio.lib.api.IcspMaster; import ioio.lib.api.PulseInput; import ioio.lib.api.PulseInput.ClockRate; @@ -697,4 +698,12 @@ public synchronized void endBatch() throws ConnectionLostException { throw new ConnectionLostException(e); } } + + @Override + public IrTransmitter openIrOutput(int pin) throws ConnectionLostException { + DigitalOutput out = openDigitalOutput(pin, false); + IrTransmitterImpl irOut = new IrTransmitterImpl(this, out, pin); + return irOut; + } + } diff --git a/software/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java b/software/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java index 8d8a40a80..271caa141 100644 --- a/software/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java +++ b/software/IOIOLib/src/ioio/lib/impl/IOIOProtocol.java @@ -91,6 +91,8 @@ class IOIOProtocol { static final int SET_PIN_INCAP = 0x1C; static final int INCAP_REPORT = 0x1C; static final int SOFT_CLOSE = 0x1D; + static final int IR_BUFFER_DATA = 0x1E; + static final int IR_SEND_DATA = 0x1F; static final int[] SCALE_DIV = new int[] { 0x1F, // 31.25 @@ -221,6 +223,20 @@ synchronized public void setDigitalOutLevel(int pin, boolean level) endBatch(); } + synchronized public void irData(int data) throws IOException { + beginBatch(); + writeByte(IR_BUFFER_DATA); + writeTwoBytes(data); + endBatch(); + } + + synchronized public void irStartTransmission(int pin) throws IOException { + beginBatch(); + writeByte(IR_SEND_DATA); + writeByte(pin & 0x3F); + endBatch(); + } + synchronized public void setPinPwm(int pin, int pwmNum, boolean enable) throws IOException { beginBatch(); diff --git a/software/IOIOLib/src/ioio/lib/impl/IrTransmitterImpl.java b/software/IOIOLib/src/ioio/lib/impl/IrTransmitterImpl.java new file mode 100644 index 000000000..5d95b1606 --- /dev/null +++ b/software/IOIOLib/src/ioio/lib/impl/IrTransmitterImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012 Markus Lanthaler . All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED "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 ARSHAN POURSOHI 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. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied. + */ +package ioio.lib.impl; + +import ioio.lib.api.DigitalOutput; +import ioio.lib.api.IrTransmitter; +import ioio.lib.api.exception.ConnectionLostException; + +import java.io.IOException; + +import android.util.Log; + +public class IrTransmitterImpl extends AbstractResource implements IrTransmitter { + private DigitalOutput outPin; + private int pinNum_; + + public IrTransmitterImpl(IOIOImpl ioio, DigitalOutput tx, int pinNum) throws ConnectionLostException { + super(ioio); + outPin = tx; + pinNum_ = pinNum; + } + + @Override + public void close() { + outPin.close(); + } + + private void sendIrData(int data) { + try { + ioio_.protocol_.irData(data); + } catch (IOException e) { + Log.e("IrImpl", e.getMessage()); + } + } + + private void startTransmission() { + try { + ioio_.protocol_.irStartTransmission(pinNum_); + } catch (IOException e) { + Log.e("IrImpl", e.getMessage()); + } + } + + @Override + public void transmitIrCommand(int[] data) { + + for (int i = 0; i < data.length; i++) + { + sendIrData(data[i]); + } + + startTransmission(); + } +}