Nut/OS  4.10.3
API Reference
irblast.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 by Przemyslaw Rudy. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  */
00031 
00032 /*
00033  * $Log$
00034  * Revision 1.4  2009/01/17 11:26:38  haraldkipp
00035  * Getting rid of two remaining BSD types in favor of stdint.
00036  * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
00037  *
00038  * Revision 1.3  2008/08/11 06:59:17  haraldkipp
00039  * BSD types replaced by stdint types (feature request #1282721).
00040  *
00041  * Revision 1.2  2007/08/17 11:16:29  haraldkipp
00042  * Flush timeout fixed. Thanks to Przemek.
00043  *
00044  * Revision 1.1  2007/05/24 07:42:42  haraldkipp
00045  * New driver from Przemyslaw Rudy sends modulated infrared remote control
00046  * codes using a simple IR LED. Cool.
00047  *
00048  */
00049 
00050 #include <compiler.h>
00051 #include <string.h>
00052 #include <dev/irqreg.h>
00053 #include <sys/atom.h>
00054 #include <sys/event.h>
00055 #include <sys/thread.h>
00056 #include <sys/device.h>
00057 #include <arch/timer.h>
00058 #include <dev/irblast.h>
00059 
00060 /* This driver sends infrared codes. The hardware pwm feature is used thus output
00061  * is generated on dedicated pin PB7.
00062  * Frequency and codes are given in timer OCR value form. For user convenience
00063  * two helpers are provided, carrier frequency can be given in kHz form (30-50)
00064  * and code itself is given in carrier frequency periods (1-1000).
00065  * In short 38 stands for 38kHz, period 100 stands for 100 * (1/38000) = 2.6ms,
00066  * period 200 stands for 200 * (1/38000) = 5.2ms etc.
00067  *
00068  * Example:
00069  * 1. Driver Init:
00070  * FILE * irblast_hdl  = 0; 
00071  * NutRegisterDevice(&devIrblast0, 0, 0);
00072  * irblast_hdl = fopen("irblast0", "w");
00073  *
00074  * 2. Code Trnasmit:
00075  * #define CODE_LENGTH 4
00076  * const uint16_t freqCode[CODE_LENGTH] PROGMEM = {100, 200, 200, 200};
00077  * uint16_t ocrCode[CODE_LENGTH];
00078  * uint32_t speed;
00079  * 
00080  * speed = (uint32_t)IrblastFreq2Ocr(38);
00081  * _ioctl(_fileno(irblast_hdl), IRBLAST_SETFREQ, &speed);
00082  * memcpy_P(ocrCode, freqCode, CODE_LENGTH<<1);
00083  * if(PwmPeriod2Ocr((u_char)38, CODE_LENGTH, ocrCode) == CODE_LENGTH)
00084  * {
00085  *    fwrite((uint16_t *)ocrCode, sizeof(uint16_t), CODE_LENGTH, irblast_hdl);
00086  *    fflush(irblast_hdl);
00087  * }
00088  *
00089  */
00090 
00091 typedef struct _IRBLASTDCB IRBLASTDCB;
00092 struct _IRBLASTDCB {
00093     /* Queue of threads waiting for output buffer empty.
00094      * Threads are added to this queue when calling IrblastFlush().
00095      */
00096     HANDLE dcb_tx_rdy;
00097 
00098     /* Next output index */
00099     volatile uint8_t if_tx_idx;
00100     /* Next write index */
00101     uint8_t if_wr_idx;
00102     /* Set if transmitter running */
00103     volatile uint8_t if_tx_act;
00104     /* Output buffer */
00105     uint16_t if_tx_buf[256];     // 256*2 = 512 bytes...   
00106 };
00107 
00108 static IRBLASTDCB dcb_pwm0;
00109 static NUTFILE file;
00110 
00111 
00119 uint8_t IrblastFreq2Ocr(uint8_t freqKHz)
00120 {
00121     uint32_t div;
00122     uint8_t ocr = 0;
00123 
00124     if ((freqKHz >= 30) && (freqKHz <= 50)) {
00125         /* prescaler = f/1 */
00126         div = 2000UL * (uint32_t) freqKHz;
00127         ocr = (uint8_t) ((NutGetCpuClock() / div) & 0x000000ff) - 1;
00128     }
00129     return ocr;
00130 }
00131 
00144 int IrblastPeriod2Ocr(uint8_t freqKHz, int entries, uint16_t * pCode)
00145 {
00146     uint32_t div, sClk, freq;
00147     int i = -1;
00148 
00149     if ((freqKHz < 30) && (freqKHz > 50)) {
00150         freqKHz = 100;          /* use pulse 10us time */
00151     }
00152 
00153     /* prescaler = f/8 */
00154     sClk = NutGetCpuClock() / 10UL;
00155     freq = 800UL * (uint32_t) (freqKHz);
00156 
00157     for (i = 0; i < entries; ++i) {
00158         if ((pCode[i] == 0) || (pCode[i] > 1000)) {
00159             return -1;
00160         }
00161         div = sClk * (uint32_t) pCode[i];
00162         div = div / freq;
00163         pCode[i] = (unsigned int) (div & 0x0000ffff) - 1;
00164     }
00165     return i;
00166 }
00167 
00168 
00177 static void IrblastOutComp1CInt(void *arg)
00178 {
00179     NUTDEVICE *dev = (NUTDEVICE *) arg;
00180     IRBLASTDCB *dcb = dev->dev_dcb;
00181 
00182     if (dcb->if_tx_idx != dcb->if_wr_idx) {
00183         /* Set period */
00184         OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
00185         OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
00186         ++(dcb->if_tx_idx);
00187     } else {
00188         dcb->if_tx_act = 0;
00189 
00190         /* TMR1 output compare A match interrupt disable */
00191         TIMSK &= ~_BV(OCIE1C);
00192 
00193         TCCR1B &= ~_BV(CS11);   /* stop counting */
00194 
00195         TCCR1A &= ~_BV(COM1C0);
00196         TCCR1A |= _BV(COM1C1);  /* clrar OC pin on compare */
00197 
00198         TCCR1C = _BV(FOC1C);    /* trigger pin - clear it */
00199 
00200         NutEventPostFromIrq(&dcb->dcb_tx_rdy);
00201     }
00202 }
00203 
00204 
00219 static int IrblastOutput(NUTDEVICE * dev)
00220 {
00221     IRBLASTDCB *dcb = dev->dev_dcb;
00222 
00223     if ((dcb->if_tx_act == 0) && (dcb->if_tx_idx != dcb->if_wr_idx)) {
00224         dcb->if_tx_act = 1;
00225 
00226         TCCR1A &= ~_BV(COM1C1);
00227         TCCR1A |= _BV(COM1C0);  /* toggle OC pin on compare */
00228 
00229         TCCR1C = _BV(FOC1C);    /* trigger pin - toggle it */
00230 
00231         /* Clear Counter register (16bit) */
00232         TCNT1 = 0;
00233 
00234         /* Set period */
00235         OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
00236         OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
00237 
00238         ++(dcb->if_tx_idx);
00239 
00240         /* TMR1 output compare A match interrupt enable */
00241         ETIMSK |= _BV(OCIE1C);
00242 
00243         TCCR1B |= _BV(CS11);    /* start counting f/8 */
00244     }
00245     return 0;
00246 }
00247 
00248 
00259 static int IrblastFlush(NUTDEVICE * dev)
00260 {
00261     IRBLASTDCB *dcb = dev->dev_dcb;
00262 
00263     /* Start any pending output */
00264     IrblastOutput(dev);
00265 
00266     /* Wait until output buffer empty */
00267     while (dcb->if_tx_idx != dcb->if_wr_idx) {
00268         NutEventWaitNext(&dcb->dcb_tx_rdy, 100);
00269     }
00270 
00271     /* TMR1 output compare A match interrupt disable */
00272     ETIMSK &= ~_BV(OCIE1C);
00273 
00274     TCCR1B &= ~_BV(CS11);       /* stop counting */
00275 
00276     TCCR1A &= ~_BV(COM1C0);
00277     TCCR1A |= _BV(COM1C1);      /* clrar OC pin on compare */
00278 
00279     TCCR1C = _BV(FOC1C);        /* trigger pin - clear it */
00280 
00281     return 0;
00282 }
00283 
00284 
00298 static int IrblastPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00299 {
00300     int rc = 0;
00301     IRBLASTDCB *dcb = dev->dev_dcb;
00302     CONST uint16_t *cp;
00303     uint16_t ch;
00304 
00305     /* Call without data pointer starts transmission */
00306     if (buffer == 0) {
00307         IrblastFlush(dev);
00308     }
00309 
00310     /* Put data in transmit buffer,
00311        for us buffer points to table of 'uint16_t' type data */
00312     cp = buffer;
00313 
00314     /* len is length in bytes, so it must be divided by 2 */
00315     len >>= 1;
00316 
00317     for (rc = 0; rc < len;) {
00318         if ((uint8_t) (dcb->if_wr_idx + 1) == dcb->if_tx_idx) {
00319             IrblastFlush(dev);
00320         }
00321         ch = pflg ? PRG_RDB(cp) : *cp;
00322         dcb->if_tx_buf[dcb->if_wr_idx] = ch;
00323         ++(dcb->if_wr_idx);
00324         ++cp;
00325         ++rc;
00326     }
00327 
00328     /* multiply result by 2 to return the number of sent bytes */
00329     return (rc << 1);
00330 }
00331 
00332 
00342 static int IrblastWrite(NUTFILE * fp, CONST void *buffer, int len)
00343 {
00344     return IrblastPut(fp->nf_dev, buffer, len, 0);
00345 }
00346 
00356 static int IrblastWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00357 {
00358     return IrblastPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00359 }
00360 
00370 static int IrblastIOCtl(NUTDEVICE * dev, int req, void *conf)
00371 {
00372     uint8_t *usp = (uint8_t *) conf;
00373 
00374     switch (req) {
00375     case IRBLAST_SETFREQ:
00376         if (*usp == 0) {
00377             /* disable compare modulator hardware functionality and timer at all */
00378             TCCR2 &= ~(_BV(WGM21) | _BV(COM20));
00379         } else {
00380             OCR2 = *usp;
00381 
00382             /* Clear Counter register (8bit) */
00383             TCNT2 = 0;
00384 
00385             /* enable compare modulator hardware functionality and timer itself in CTC mode */
00386             TCCR2 |= _BV(WGM21) | _BV(COM20);
00387         }
00388         break;
00389 
00390     case IRBLAST_GETFREQ:
00391         *usp = OCR2;
00392         break;
00393 
00394     default:
00395         return -1;
00396     }
00397     return 0;
00398 }
00399 
00410 static NUTFILE *IrblastOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00411 {
00412     file.nf_next = 0;
00413     file.nf_dev = dev;
00414     file.nf_fcb = 0;
00415     return &file;
00416 }
00417 
00425 static int IrblastClose(NUTFILE * fp)
00426 {
00427     return 0;
00428 }
00429 
00435 static void IrblastTmr1Init(void)
00436 {
00437     /* TMR1 runs in CTC mode */
00438     TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0) | _BV(WGM11) | _BV(WGM10));   /* CTC mode */
00439     TCCR1A |= _BV(COM1C1);      /* clrar OC pin on compare */
00440 
00441     TCCR1B &= ~(_BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS11) | _BV(CS10));   /* f = off */
00442     TCCR1B |= _BV(WGM12);       /* CTC */
00443 
00444     TCCR1C = _BV(FOC1C);        /* trigger pin - clear it */
00445 
00446     /* TMR1 output compare A match interrupt disable */
00447     ETIMSK &= ~_BV(OCIE1C);
00448 }
00449 
00455 static void IrblastTmr2Init(void)
00456 {
00457     /* TMR2 is off - must be started with ioctl call */
00458     TCCR2 = _BV(CS20);          /* f=clk/1 - do not enable compare modulator hardware functionality */
00459 }
00460 
00461 
00469 static int IrblastInit(NUTDEVICE * dev)
00470 {
00471     IRBLASTDCB *dcb = dev->dev_dcb;
00472 
00473     /* Initialize Driver Control Block */
00474     memset(dcb, 0, sizeof(IRBLASTDCB));
00475 
00476     /* Register interrupt handlers */
00477     if (NutRegisterIrqHandler(&sig_OUTPUT_COMPARE1C, IrblastOutComp1CInt, dev))
00478         return -1;
00479 
00480     /* Init Timer2 - carrier frequency generator */
00481     IrblastTmr2Init();
00482 
00483     /* Init Timer1 - AM modulation of Timer3 output pin */
00484     IrblastTmr1Init();
00485 
00486     /* This pin is used by hardware mux */
00487     sbi(DDRB, PORTB7);
00488 
00489     return 0;
00490 }
00491 
00492 
00493 
00494 NUTDEVICE devIrblast0 = {
00495     0,                          /* Pointer to next dev */
00496     {'i', 'r', 'b', 'l', 'a', 's', 't', '0', 0}
00497     ,                           /* Unique device name */
00498     IFTYP_STREAM,               /* Type of dev, IFTYP_CHAR ? */
00499     0,                          /* Base address */
00500     0,                          /* First irq # */
00501     0,                          /* I/f control block */
00502     &dcb_pwm0,                  /* Dev control block */
00503     IrblastInit,
00504     IrblastIOCtl,
00505     0,
00506     IrblastWrite,
00507     IrblastWrite_P,
00508     IrblastOpen,
00509     IrblastClose,
00510     0
00511 };