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: irblast.c,v $
00034  * Revision 1.3  2008/08/11 06:59:17  haraldkipp
00035  * BSD types replaced by stdint types (feature request #1282721).
00036  *
00037  * Revision 1.2  2007/08/17 11:16:29  haraldkipp
00038  * Flush timeout fixed. Thanks to Przemek.
00039  *
00040  * Revision 1.1  2007/05/24 07:42:42  haraldkipp
00041  * New driver from Przemyslaw Rudy sends modulated infrared remote control
00042  * codes using a simple IR LED. Cool.
00043  *
00044  */
00045 
00046 #include <compiler.h>
00047 #include <string.h>
00048 #include <dev/irqreg.h>
00049 #include <sys/atom.h>
00050 #include <sys/event.h>
00051 #include <sys/thread.h>
00052 #include <sys/device.h>
00053 #include <arch/timer.h>
00054 #include <dev/irblast.h>
00055 
00056 /* This driver sends infrared codes. The hardware pwm feature is used thus output
00057  * is generated on dedicated pin PB7.
00058  * Frequency and codes are given in timer OCR value form. For user convenience
00059  * two helpers are provided, carrier frequency can be given in kHz form (30-50)
00060  * and code itself is given in carrier frequency periods (1-1000).
00061  * In short 38 stands for 38kHz, period 100 stands for 100 * (1/38000) = 2.6ms,
00062  * period 200 stands for 200 * (1/38000) = 5.2ms etc.
00063  *
00064  * Example:
00065  * 1. Driver Init:
00066  * FILE * irblast_hdl  = 0; 
00067  * NutRegisterDevice(&devIrblast0, 0, 0);
00068  * irblast_hdl = fopen("irblast0", "w");
00069  *
00070  * 2. Code Trnasmit:
00071  * #define CODE_LENGTH 4
00072  * const uint16_t freqCode[CODE_LENGTH] PROGMEM = {100, 200, 200, 200};
00073  * uint16_t ocrCode[CODE_LENGTH];
00074  * uint32_t speed;
00075  * 
00076  * speed = (uint32_t)IrblastFreq2Ocr(38);
00077  * _ioctl(_fileno(irblast_hdl), IRBLAST_SETFREQ, &speed);
00078  * memcpy_P(ocrCode, freqCode, CODE_LENGTH<<1);
00079  * if(PwmPeriod2Ocr((u_char)38, CODE_LENGTH, ocrCode) == CODE_LENGTH)
00080  * {
00081  *    fwrite((uint16_t *)ocrCode, sizeof(uint16_t), CODE_LENGTH, irblast_hdl);
00082  *    fflush(irblast_hdl);
00083  * }
00084  *
00085  */
00086 
00087 typedef struct _IRBLASTDCB IRBLASTDCB;
00088 struct _IRBLASTDCB {
00089     /* Queue of threads waiting for output buffer empty.
00090      * Threads are added to this queue when calling IrblastFlush().
00091      */
00092     HANDLE dcb_tx_rdy;
00093 
00094     /* Next output index */
00095     volatile uint8_t if_tx_idx;
00096     /* Next write index */
00097     uint8_t if_wr_idx;
00098     /* Set if transmitter running */
00099     volatile uint8_t if_tx_act;
00100     /* Output buffer */
00101     uint16_t if_tx_buf[256];     // 256*2 = 512 bytes...   
00102 };
00103 
00104 static IRBLASTDCB dcb_pwm0;
00105 static NUTFILE file;
00106 
00107 
00115 uint8_t IrblastFreq2Ocr(uint8_t freqKHz)
00116 {
00117     uint32_t div;
00118     uint8_t ocr = 0;
00119 
00120     if ((freqKHz >= 30) && (freqKHz <= 50)) {
00121         /* prescaler = f/1 */
00122         div = 2000UL * (uint32_t) freqKHz;
00123         ocr = (uint8_t) ((NutGetCpuClock() / div) & 0x000000ff) - 1;
00124     }
00125     return ocr;
00126 }
00127 
00140 int IrblastPeriod2Ocr(uint8_t freqKHz, int entries, uint16_t * pCode)
00141 {
00142     uint32_t div, sClk, freq;
00143     int i = -1;
00144 
00145     if ((freqKHz < 30) && (freqKHz > 50)) {
00146         freqKHz = 100;          /* use pulse 10us time */
00147     }
00148 
00149     /* prescaler = f/8 */
00150     sClk = NutGetCpuClock() / 10UL;
00151     freq = 800UL * (uint32_t) (freqKHz);
00152 
00153     for (i = 0; i < entries; ++i) {
00154         if ((pCode[i] == 0) || (pCode[i] > 1000)) {
00155             return -1;
00156         }
00157         div = sClk * (uint32_t) pCode[i];
00158         div = div / freq;
00159         pCode[i] = (u_int) (div & 0x0000ffff) - 1;
00160     }
00161     return i;
00162 }
00163 
00164 
00173 static void IrblastOutComp1CInt(void *arg)
00174 {
00175     NUTDEVICE *dev = (NUTDEVICE *) arg;
00176     IRBLASTDCB *dcb = dev->dev_dcb;
00177 
00178     if (dcb->if_tx_idx != dcb->if_wr_idx) {
00179         /* Set period */
00180         OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
00181         OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
00182         ++(dcb->if_tx_idx);
00183     } else {
00184         dcb->if_tx_act = 0;
00185 
00186         /* TMR1 output compare A match interrupt disable */
00187         TIMSK &= ~_BV(OCIE1C);
00188 
00189         TCCR1B &= ~_BV(CS11);   /* stop counting */
00190 
00191         TCCR1A &= ~_BV(COM1C0);
00192         TCCR1A |= _BV(COM1C1);  /* clrar OC pin on compare */
00193 
00194         TCCR1C = _BV(FOC1C);    /* trigger pin - clear it */
00195 
00196         NutEventPostFromIrq(&dcb->dcb_tx_rdy);
00197     }
00198 }
00199 
00200 
00215 static int IrblastOutput(NUTDEVICE * dev)
00216 {
00217     IRBLASTDCB *dcb = dev->dev_dcb;
00218 
00219     if ((dcb->if_tx_act == 0) && (dcb->if_tx_idx != dcb->if_wr_idx)) {
00220         dcb->if_tx_act = 1;
00221 
00222         TCCR1A &= ~_BV(COM1C1);
00223         TCCR1A |= _BV(COM1C0);  /* toggle OC pin on compare */
00224 
00225         TCCR1C = _BV(FOC1C);    /* trigger pin - toggle it */
00226 
00227         /* Clear Counter register (16bit) */
00228         TCNT1 = 0;
00229 
00230         /* Set period */
00231         OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
00232         OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
00233 
00234         ++(dcb->if_tx_idx);
00235 
00236         /* TMR1 output compare A match interrupt enable */
00237         ETIMSK |= _BV(OCIE1C);
00238 
00239         TCCR1B |= _BV(CS11);    /* start counting f/8 */
00240     }
00241     return 0;
00242 }
00243 
00244 
00255 static int IrblastFlush(NUTDEVICE * dev)
00256 {
00257     IRBLASTDCB *dcb = dev->dev_dcb;
00258 
00259     /* Start any pending output */
00260     IrblastOutput(dev);
00261 
00262     /* Wait until output buffer empty */
00263     while (dcb->if_tx_idx != dcb->if_wr_idx) {
00264         NutEventWaitNext(&dcb->dcb_tx_rdy, 100);
00265     }
00266 
00267     /* TMR1 output compare A match interrupt disable */
00268     ETIMSK &= ~_BV(OCIE1C);
00269 
00270     TCCR1B &= ~_BV(CS11);       /* stop counting */
00271 
00272     TCCR1A &= ~_BV(COM1C0);
00273     TCCR1A |= _BV(COM1C1);      /* clrar OC pin on compare */
00274 
00275     TCCR1C = _BV(FOC1C);        /* trigger pin - clear it */
00276 
00277     return 0;
00278 }
00279 
00280 
00294 static int IrblastPut(NUTDEVICE * dev, CONST void *buffer, int len, int pflg)
00295 {
00296     int rc = 0;
00297     IRBLASTDCB *dcb = dev->dev_dcb;
00298     CONST uint16_t *cp;
00299     uint16_t ch;
00300 
00301     /* Call without data pointer starts transmission */
00302     if (buffer == 0) {
00303         IrblastFlush(dev);
00304     }
00305 
00306     /* Put data in transmit buffer,
00307        for us buffer points to table of 'uint16_t' type data */
00308     cp = buffer;
00309 
00310     /* len is length in bytes, so it must be divided by 2 */
00311     len >>= 1;
00312 
00313     for (rc = 0; rc < len;) {
00314         if ((uint8_t) (dcb->if_wr_idx + 1) == dcb->if_tx_idx) {
00315             IrblastFlush(dev);
00316         }
00317         ch = pflg ? PRG_RDB(cp) : *cp;
00318         dcb->if_tx_buf[dcb->if_wr_idx] = ch;
00319         ++(dcb->if_wr_idx);
00320         ++cp;
00321         ++rc;
00322     }
00323 
00324     /* multiply result by 2 to return the number of sent bytes */
00325     return (rc << 1);
00326 }
00327 
00328 
00338 static int IrblastWrite(NUTFILE * fp, CONST void *buffer, int len)
00339 {
00340     return IrblastPut(fp->nf_dev, buffer, len, 0);
00341 }
00342 
00352 static int IrblastWrite_P(NUTFILE * fp, PGM_P buffer, int len)
00353 {
00354     return IrblastPut(fp->nf_dev, (CONST char *) buffer, len, 1);
00355 }
00356 
00366 static int IrblastIOCtl(NUTDEVICE * dev, int req, void *conf)
00367 {
00368     uint8_t *usp = (uint8_t *) conf;
00369 
00370     switch (req) {
00371     case IRBLAST_SETFREQ:
00372         if (*usp == 0) {
00373             /* disable compare modulator hardware functionality and timer at all */
00374             TCCR2 &= ~(_BV(WGM21) | _BV(COM20));
00375         } else {
00376             OCR2 = *usp;
00377 
00378             /* Clear Counter register (8bit) */
00379             TCNT2 = 0;
00380 
00381             /* enable compare modulator hardware functionality and timer itself in CTC mode */
00382             TCCR2 |= _BV(WGM21) | _BV(COM20);
00383         }
00384         break;
00385 
00386     case IRBLAST_GETFREQ:
00387         *usp = OCR2;
00388         break;
00389 
00390     default:
00391         return -1;
00392     }
00393     return 0;
00394 }
00395 
00406 static NUTFILE *IrblastOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00407 {
00408     file.nf_next = 0;
00409     file.nf_dev = dev;
00410     file.nf_fcb = 0;
00411     return &file;
00412 }
00413 
00421 static int IrblastClose(NUTFILE * fp)
00422 {
00423     return 0;
00424 }
00425 
00431 static void IrblastTmr1Init(void)
00432 {
00433     /* TMR1 runs in CTC mode */
00434     TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0) | _BV(WGM11) | _BV(WGM10));   /* CTC mode */
00435     TCCR1A |= _BV(COM1C1);      /* clrar OC pin on compare */
00436 
00437     TCCR1B &= ~(_BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS11) | _BV(CS10));   /* f = off */
00438     TCCR1B |= _BV(WGM12);       /* CTC */
00439 
00440     TCCR1C = _BV(FOC1C);        /* trigger pin - clear it */
00441 
00442     /* TMR1 output compare A match interrupt disable */
00443     ETIMSK &= ~_BV(OCIE1C);
00444 }
00445 
00451 static void IrblastTmr2Init(void)
00452 {
00453     /* TMR2 is off - must be started with ioctl call */
00454     TCCR2 = _BV(CS20);          /* f=clk/1 - do not enable compare modulator hardware functionality */
00455 }
00456 
00457 
00465 static int IrblastInit(NUTDEVICE * dev)
00466 {
00467     IRBLASTDCB *dcb = dev->dev_dcb;
00468 
00469     /* Initialize Driver Control Block */
00470     memset(dcb, 0, sizeof(IRBLASTDCB));
00471 
00472     /* Register interrupt handlers */
00473     if (NutRegisterIrqHandler(&sig_OUTPUT_COMPARE1C, IrblastOutComp1CInt, dev))
00474         return -1;
00475 
00476     /* Init Timer2 - carrier frequency generator */
00477     IrblastTmr2Init();
00478 
00479     /* Init Timer1 - AM modulation of Timer3 output pin */
00480     IrblastTmr1Init();
00481 
00482     /* This pin is used by hardware mux */
00483     sbi(DDRB, PORTB7);
00484 
00485     return 0;
00486 }
00487 
00488 
00489 
00490 NUTDEVICE devIrblast0 = {
00491     0,                          /* Pointer to next dev */
00492     {'i', 'r', 'b', 'l', 'a', 's', 't', '0', 0}
00493     ,                           /* Unique device name */
00494     IFTYP_STREAM,               /* Type of dev, IFTYP_CHAR ? */
00495     0,                          /* Base address */
00496     0,                          /* First irq # */
00497     0,                          /* I/f control block */
00498     &dcb_pwm0,                  /* Dev control block */
00499     IrblastInit,
00500     IrblastIOCtl,
00501     0,
00502     IrblastWrite,
00503     IrblastWrite_P,
00504     IrblastOpen,
00505     IrblastClose,
00506     0
00507 };

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/