| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- /*
- * Copyright (C) 2007 by Przemyslaw Rudy. 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.
- * 3. Neither the name of the copyright holders nor the names of
- * 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.
- *
- */
- /*!
- * \file arch/avr/dev/irblast.c
- * \brief AVR IR transmitter support.
- *
- * This driver sends infrared codes. The hardware pwm feature is used thus output
- * is generated on dedicated pin PB7.
- * Frequency and codes are given in timer OCR value form. For user convenience
- * two helpers are provided, carrier frequency can be given in kHz form (30-50)
- * and code itself is given in carrier frequency periods (1-1000).
- * In short 38 stands for 38kHz, period 100 stands for 100 * (1/38000) = 2.6ms,
- * period 200 stands for 200 * (1/38000) = 5.2ms etc.
- *
- * Example:
- * 1. Driver Init:
- * FILE * irblast_hdl = 0;
- * NutRegisterDevice(&devIrblast0, 0, 0);
- * irblast_hdl = fopen("irblast0", "w");
- *
- * 2. Code Trnasmit:
- * #define CODE_LENGTH 4
- * const uint16_t freqCode[CODE_LENGTH] PROGMEM = {100, 200, 200, 200};
- * uint16_t ocrCode[CODE_LENGTH];
- * uint32_t speed;
- *
- * speed = (uint32_t)IrblastFreq2Ocr(38);
- * _ioctl(_fileno(irblast_hdl), IRBLAST_SETFREQ, &speed);
- * memcpy_P(ocrCode, freqCode, CODE_LENGTH<<1);
- * if(PwmPeriod2Ocr((u_char)38, CODE_LENGTH, ocrCode) == CODE_LENGTH)
- * {
- * fwrite((uint16_t *)ocrCode, sizeof(uint16_t), CODE_LENGTH, irblast_hdl);
- * fflush(irblast_hdl);
- * }
- *
- * \verbatim
- * $Id: irblast.c 5472 2013-12-06 00:16:28Z olereinhardt $
- * \endverbatim
- */
- #include <compiler.h>
- #include <string.h>
- #include <dev/irqreg.h>
- #include <sys/atom.h>
- #include <sys/event.h>
- #include <sys/thread.h>
- #include <sys/device.h>
- #include <arch/timer.h>
- #include <dev/irblast.h>
- typedef struct _IRBLASTDCB IRBLASTDCB;
- struct _IRBLASTDCB {
- /* Queue of threads waiting for output buffer empty.
- * Threads are added to this queue when calling IrblastFlush().
- */
- HANDLE dcb_tx_rdy;
- /* Next output index */
- volatile uint8_t if_tx_idx;
- /* Next write index */
- uint8_t if_wr_idx;
- /* Set if transmitter running */
- volatile uint8_t if_tx_act;
- /* Output buffer */
- uint16_t if_tx_buf[256]; // 256*2 = 512 bytes...
- };
- static IRBLASTDCB dcb_pwm0;
- static NUTFILE file;
- /*!
- * \brief Converts carrier frequency in form of kHz to the timer OCR form.
- *
- * \param freqKHz frequency in kHz, range 30-50.
- *
- * \return OCR form of the frequency used by the driver or 0.
- */
- uint8_t IrblastFreq2Ocr(uint8_t freqKHz)
- {
- uint32_t div;
- uint8_t ocr = 0;
- if ((freqKHz >= 30) && (freqKHz <= 50)) {
- /* prescaler = f/1 */
- div = 2000UL * (uint32_t) freqKHz;
- ocr = (uint8_t) ((NutGetCpuClock() / div) & 0x000000ff) - 1;
- }
- return ocr;
- }
- /*!
- * \brief Converts ircode from carrier frequency periods form to the timer OCR form.
- *
- * Given array of ir code in form of carrier frequency periods will be converted
- * (in place) to the form of OCR periods used by the driver.
- *
- * \param freqKHz frequency in kHz, range 30-50, for 0 pulse time =10us will be used
- * \param entries ir code length.
- * \param pCode pointer to the array with ir code, each entry in range 1-999.
- *
- * \return number of entries properly converted or -1 on error.
- */
- int IrblastPeriod2Ocr(uint8_t freqKHz, int entries, uint16_t * pCode)
- {
- uint32_t div, sClk, freq;
- int i = -1;
- if ((freqKHz < 30) && (freqKHz > 50)) {
- freqKHz = 100; /* use pulse 10us time */
- }
- /* prescaler = f/8 */
- sClk = NutGetCpuClock() / 10UL;
- freq = 800UL * (uint32_t) (freqKHz);
- for (i = 0; i < entries; ++i) {
- if ((pCode[i] == 0) || (pCode[i] > 1000)) {
- return -1;
- }
- div = sClk * (uint32_t) pCode[i];
- div = div / freq;
- pCode[i] = (unsigned int) (div & 0x0000ffff) - 1;
- }
- return i;
- }
- /*!
- * \brief Timer1 output compare routine.
- *
- * This interrupt routine is called after when previous timer perios has
- * been finished. It sets new timer period, or finishes counting.
- *
- * \param arg void* cast of NUTDEVICE.
- */
- static void IrblastOutComp1CInt(void *arg)
- {
- NUTDEVICE *dev = (NUTDEVICE *) arg;
- IRBLASTDCB *dcb = dev->dev_dcb;
- if (dcb->if_tx_idx != dcb->if_wr_idx) {
- /* Set period */
- OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
- OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
- ++(dcb->if_tx_idx);
- } else {
- dcb->if_tx_act = 0;
- /* TMR1 output compare A match interrupt disable */
- TIMSK &= ~_BV(OCIE1C);
- TCCR1B &= ~_BV(CS11); /* stop counting */
- TCCR1A &= ~_BV(COM1C0);
- TCCR1A |= _BV(COM1C1); /* clrar OC pin on compare */
- TCCR1C = _BV(FOC1C); /* trigger pin - clear it */
- NutEventPostFromIrq(&dcb->dcb_tx_rdy);
- }
- }
- /*!
- * \brief Initiate output.
- *
- * This function checks the output buffer for any data. If
- * the buffer contains at least one character, the transmitter
- * is started, if not already running. The function returns
- * immediately, without waiting for the character being
- * completely transmitted. Any remaining characters in the
- * output buffer are transmitted in the background.
- *
- * \param dev Indicates the device.
- *
- * \return 0.
- */
- static int IrblastOutput(NUTDEVICE * dev)
- {
- IRBLASTDCB *dcb = dev->dev_dcb;
- if ((dcb->if_tx_act == 0) && (dcb->if_tx_idx != dcb->if_wr_idx)) {
- dcb->if_tx_act = 1;
- TCCR1A &= ~_BV(COM1C1);
- TCCR1A |= _BV(COM1C0); /* toggle OC pin on compare */
- TCCR1C = _BV(FOC1C); /* trigger pin - toggle it */
- /* Clear Counter register (16bit) */
- TCNT1 = 0;
- /* Set period */
- OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
- OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
- ++(dcb->if_tx_idx);
- /* TMR1 output compare A match interrupt enable */
- ETIMSK |= _BV(OCIE1C);
- TCCR1B |= _BV(CS11); /* start counting f/8 */
- }
- return 0;
- }
- /*!
- * \brief Wait for output buffer empty.
- *
- * If the output buffer contains any data, the calling
- * thread is suspended until all data has been transmitted.
- *
- * \param dev Indicates the device.
- *
- * \return 0.
- */
- static int IrblastFlush(NUTDEVICE * dev)
- {
- IRBLASTDCB *dcb = dev->dev_dcb;
- /* Start any pending output */
- IrblastOutput(dev);
- /* Wait until output buffer empty */
- while (dcb->if_tx_idx != dcb->if_wr_idx) {
- NutEventWaitNext(&dcb->dcb_tx_rdy, 100);
- }
- /* TMR1 output compare A match interrupt disable */
- ETIMSK &= ~_BV(OCIE1C);
- TCCR1B &= ~_BV(CS11); /* stop counting */
- TCCR1A &= ~_BV(COM1C0);
- TCCR1A |= _BV(COM1C1); /* clrar OC pin on compare */
- TCCR1C = _BV(FOC1C); /* trigger pin - clear it */
- return 0;
- }
- /*!
- * \brief Moves data to the output buffer.
- *
- * If the output buffer contains any data, the calling
- * thread is suspended until all data has been transmitted.
- *
- * \param dev Indicates the device.
- * \param buffer data buffer.
- * \param len is length in bytes!.
- * \param pflag 0 - indicates input is ram, !=0 - input is progmem.
- *
- * \return number of bytes sent.
- */
- static int IrblastPut(NUTDEVICE * dev, const void *buffer, int len, int pflg)
- {
- int rc = 0;
- IRBLASTDCB *dcb = dev->dev_dcb;
- const uint16_t *cp;
- uint16_t ch;
- /* Call without data pointer starts transmission */
- if (buffer == 0) {
- IrblastFlush(dev);
- }
- /* Put data in transmit buffer,
- for us buffer points to table of 'uint16_t' type data */
- cp = buffer;
- /* len is length in bytes, so it must be divided by 2 */
- len >>= 1;
- for (rc = 0; rc < len;) {
- if ((uint8_t) (dcb->if_wr_idx + 1) == dcb->if_tx_idx) {
- IrblastFlush(dev);
- }
- ch = pflg ? PRG_RDB(cp) : *cp;
- dcb->if_tx_buf[dcb->if_wr_idx] = ch;
- ++(dcb->if_wr_idx);
- ++cp;
- ++rc;
- }
- /* multiply result by 2 to return the number of sent bytes */
- return (rc << 1);
- }
- /*!
- * \brief Moves data from ram to the output buffer.
- *
- * \param fp file pointer.
- * \param buffer data buffer.
- * \param len is length in bytes!.
- *
- * \return number of bytes sent.
- */
- static int IrblastWrite(NUTFILE * fp, const void *buffer, int len)
- {
- return IrblastPut(fp->nf_dev, buffer, len, 0);
- }
- /*!
- * \brief Moves data from progmem to the output buffer.
- *
- * \param fp file pointer.
- * \param buffer data buffer.
- * \param len is length in bytes!.
- *
- * \return number of bytes sent.
- */
- static int IrblastWrite_P(NUTFILE * fp, PGM_P buffer, int len)
- {
- return IrblastPut(fp->nf_dev, (const char *) buffer, len, 1);
- }
- /*!
- * \brief Asynchronous control interface.
- *
- * \param dev Indicates the device.
- * \param req request type.
- * \param conf request parameter.
- *
- * \return 0-success, -1-error.
- */
- static int IrblastIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- uint8_t *usp = (uint8_t *) conf;
- switch (req) {
- case IRBLAST_SETFREQ:
- if (*usp == 0) {
- /* disable compare modulator hardware functionality and timer at all */
- TCCR2 &= ~(_BV(WGM21) | _BV(COM20));
- } else {
- OCR2 = *usp;
- /* Clear Counter register (8bit) */
- TCNT2 = 0;
- /* enable compare modulator hardware functionality and timer itself in CTC mode */
- TCCR2 |= _BV(WGM21) | _BV(COM20);
- }
- break;
- case IRBLAST_GETFREQ:
- *usp = OCR2;
- break;
- default:
- return -1;
- }
- return 0;
- }
- /*!
- * \brief Driver Open Funtion.
- *
- * \param dev Indicates the device.
- * \param name unused.
- * \param mode unused.
- * \param acc unused.
- *
- * \return 0-success, -1-error.
- */
- static NUTFILE *IrblastOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- file.nf_dev = dev;
- file.nf_fcb = NULL;
- return &file;
- }
- /*!
- * \brief Driver Close Funtion.
- *
- * \param fp file pointer.
- *
- * \return 0.
- */
- static int IrblastClose(NUTFILE * fp)
- {
- return 0;
- }
- /*!
- * \brief Timer1 Initialization.
- *
- * Timer 1 counts ir periods.
- */
- static void IrblastTmr1Init(void)
- {
- /* TMR1 runs in CTC mode */
- TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0) | _BV(WGM11) | _BV(WGM10)); /* CTC mode */
- TCCR1A |= _BV(COM1C1); /* clrar OC pin on compare */
- TCCR1B &= ~(_BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS11) | _BV(CS10)); /* f = off */
- TCCR1B |= _BV(WGM12); /* CTC */
- TCCR1C = _BV(FOC1C); /* trigger pin - clear it */
- /* TMR1 output compare A match interrupt disable */
- ETIMSK &= ~_BV(OCIE1C);
- }
- /*!
- * \brief Timer2 Initialization.
- *
- * Timer 1 serves ir carrier frequency.
- */
- static void IrblastTmr2Init(void)
- {
- /* TMR2 is off - must be started with ioctl call */
- TCCR2 = _BV(CS20); /* f=clk/1 - do not enable compare modulator hardware functionality */
- }
- /*!
- * \brief Driver Initialization Funtion.
- *
- * \param dev Indicates the device.
- *
- * \return 0-success, -1-error.
- */
- static int IrblastInit(NUTDEVICE * dev)
- {
- IRBLASTDCB *dcb = dev->dev_dcb;
- /* Initialize Driver Control Block */
- memset(dcb, 0, sizeof(IRBLASTDCB));
- /* Register interrupt handlers */
- if (NutRegisterIrqHandler(&sig_OUTPUT_COMPARE1C, IrblastOutComp1CInt, dev))
- return -1;
- /* Init Timer2 - carrier frequency generator */
- IrblastTmr2Init();
- /* Init Timer1 - AM modulation of Timer3 output pin */
- IrblastTmr1Init();
- /* This pin is used by hardware mux */
- sbi(DDRB, PORTB7);
- return 0;
- }
- NUTDEVICE devIrblast0 = {
- 0, /* Pointer to next dev */
- {'i', 'r', 'b', 'l', 'a', 's', 't', '0', 0}
- , /* Unique device name */
- IFTYP_STREAM, /* Type of dev, IFTYP_CHAR ? */
- 0, /* Base address */
- 0, /* First irq # */
- 0, /* I/f control block */
- &dcb_pwm0, /* Dev control block */
- IrblastInit,
- IrblastIOCtl,
- 0,
- IrblastWrite,
- IrblastWrite_P,
- IrblastOpen,
- IrblastClose,
- 0,
- 0, /* Select function, optional, not yet implemented */
- };
|