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

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