irblast.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. * Copyright (C) 2007 by Przemyslaw Rudy. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. */
  31. /*!
  32. * \file arch/avr/dev/irblast.c
  33. * \brief AVR IR transmitter support.
  34. *
  35. * This driver sends infrared codes. The hardware pwm feature is used thus output
  36. * is generated on dedicated pin PB7.
  37. * Frequency and codes are given in timer OCR value form. For user convenience
  38. * two helpers are provided, carrier frequency can be given in kHz form (30-50)
  39. * and code itself is given in carrier frequency periods (1-1000).
  40. * In short 38 stands for 38kHz, period 100 stands for 100 * (1/38000) = 2.6ms,
  41. * period 200 stands for 200 * (1/38000) = 5.2ms etc.
  42. *
  43. * Example:
  44. * 1. Driver Init:
  45. * FILE * irblast_hdl = 0;
  46. * NutRegisterDevice(&devIrblast0, 0, 0);
  47. * irblast_hdl = fopen("irblast0", "w");
  48. *
  49. * 2. Code Trnasmit:
  50. * #define CODE_LENGTH 4
  51. * const uint16_t freqCode[CODE_LENGTH] PROGMEM = {100, 200, 200, 200};
  52. * uint16_t ocrCode[CODE_LENGTH];
  53. * uint32_t speed;
  54. *
  55. * speed = (uint32_t)IrblastFreq2Ocr(38);
  56. * _ioctl(_fileno(irblast_hdl), IRBLAST_SETFREQ, &speed);
  57. * memcpy_P(ocrCode, freqCode, CODE_LENGTH<<1);
  58. * if(PwmPeriod2Ocr((u_char)38, CODE_LENGTH, ocrCode) == CODE_LENGTH)
  59. * {
  60. * fwrite((uint16_t *)ocrCode, sizeof(uint16_t), CODE_LENGTH, irblast_hdl);
  61. * fflush(irblast_hdl);
  62. * }
  63. *
  64. * \verbatim
  65. * $Id: irblast.c 5472 2013-12-06 00:16:28Z olereinhardt $
  66. * \endverbatim
  67. */
  68. #include <compiler.h>
  69. #include <string.h>
  70. #include <dev/irqreg.h>
  71. #include <sys/atom.h>
  72. #include <sys/event.h>
  73. #include <sys/thread.h>
  74. #include <sys/device.h>
  75. #include <arch/timer.h>
  76. #include <dev/irblast.h>
  77. typedef struct _IRBLASTDCB IRBLASTDCB;
  78. struct _IRBLASTDCB {
  79. /* Queue of threads waiting for output buffer empty.
  80. * Threads are added to this queue when calling IrblastFlush().
  81. */
  82. HANDLE dcb_tx_rdy;
  83. /* Next output index */
  84. volatile uint8_t if_tx_idx;
  85. /* Next write index */
  86. uint8_t if_wr_idx;
  87. /* Set if transmitter running */
  88. volatile uint8_t if_tx_act;
  89. /* Output buffer */
  90. uint16_t if_tx_buf[256]; // 256*2 = 512 bytes...
  91. };
  92. static IRBLASTDCB dcb_pwm0;
  93. static NUTFILE file;
  94. /*!
  95. * \brief Converts carrier frequency in form of kHz to the timer OCR form.
  96. *
  97. * \param freqKHz frequency in kHz, range 30-50.
  98. *
  99. * \return OCR form of the frequency used by the driver or 0.
  100. */
  101. uint8_t IrblastFreq2Ocr(uint8_t freqKHz)
  102. {
  103. uint32_t div;
  104. uint8_t ocr = 0;
  105. if ((freqKHz >= 30) && (freqKHz <= 50)) {
  106. /* prescaler = f/1 */
  107. div = 2000UL * (uint32_t) freqKHz;
  108. ocr = (uint8_t) ((NutGetCpuClock() / div) & 0x000000ff) - 1;
  109. }
  110. return ocr;
  111. }
  112. /*!
  113. * \brief Converts ircode from carrier frequency periods form to the timer OCR form.
  114. *
  115. * Given array of ir code in form of carrier frequency periods will be converted
  116. * (in place) to the form of OCR periods used by the driver.
  117. *
  118. * \param freqKHz frequency in kHz, range 30-50, for 0 pulse time =10us will be used
  119. * \param entries ir code length.
  120. * \param pCode pointer to the array with ir code, each entry in range 1-999.
  121. *
  122. * \return number of entries properly converted or -1 on error.
  123. */
  124. int IrblastPeriod2Ocr(uint8_t freqKHz, int entries, uint16_t * pCode)
  125. {
  126. uint32_t div, sClk, freq;
  127. int i = -1;
  128. if ((freqKHz < 30) && (freqKHz > 50)) {
  129. freqKHz = 100; /* use pulse 10us time */
  130. }
  131. /* prescaler = f/8 */
  132. sClk = NutGetCpuClock() / 10UL;
  133. freq = 800UL * (uint32_t) (freqKHz);
  134. for (i = 0; i < entries; ++i) {
  135. if ((pCode[i] == 0) || (pCode[i] > 1000)) {
  136. return -1;
  137. }
  138. div = sClk * (uint32_t) pCode[i];
  139. div = div / freq;
  140. pCode[i] = (unsigned int) (div & 0x0000ffff) - 1;
  141. }
  142. return i;
  143. }
  144. /*!
  145. * \brief Timer1 output compare routine.
  146. *
  147. * This interrupt routine is called after when previous timer perios has
  148. * been finished. It sets new timer period, or finishes counting.
  149. *
  150. * \param arg void* cast of NUTDEVICE.
  151. */
  152. static void IrblastOutComp1CInt(void *arg)
  153. {
  154. NUTDEVICE *dev = (NUTDEVICE *) arg;
  155. IRBLASTDCB *dcb = dev->dev_dcb;
  156. if (dcb->if_tx_idx != dcb->if_wr_idx) {
  157. /* Set period */
  158. OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
  159. OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
  160. ++(dcb->if_tx_idx);
  161. } else {
  162. dcb->if_tx_act = 0;
  163. /* TMR1 output compare A match interrupt disable */
  164. TIMSK &= ~_BV(OCIE1C);
  165. TCCR1B &= ~_BV(CS11); /* stop counting */
  166. TCCR1A &= ~_BV(COM1C0);
  167. TCCR1A |= _BV(COM1C1); /* clrar OC pin on compare */
  168. TCCR1C = _BV(FOC1C); /* trigger pin - clear it */
  169. NutEventPostFromIrq(&dcb->dcb_tx_rdy);
  170. }
  171. }
  172. /*!
  173. * \brief Initiate output.
  174. *
  175. * This function checks the output buffer for any data. If
  176. * the buffer contains at least one character, the transmitter
  177. * is started, if not already running. The function returns
  178. * immediately, without waiting for the character being
  179. * completely transmitted. Any remaining characters in the
  180. * output buffer are transmitted in the background.
  181. *
  182. * \param dev Indicates the device.
  183. *
  184. * \return 0.
  185. */
  186. static int IrblastOutput(NUTDEVICE * dev)
  187. {
  188. IRBLASTDCB *dcb = dev->dev_dcb;
  189. if ((dcb->if_tx_act == 0) && (dcb->if_tx_idx != dcb->if_wr_idx)) {
  190. dcb->if_tx_act = 1;
  191. TCCR1A &= ~_BV(COM1C1);
  192. TCCR1A |= _BV(COM1C0); /* toggle OC pin on compare */
  193. TCCR1C = _BV(FOC1C); /* trigger pin - toggle it */
  194. /* Clear Counter register (16bit) */
  195. TCNT1 = 0;
  196. /* Set period */
  197. OCR1A = dcb->if_tx_buf[dcb->if_tx_idx];
  198. OCR1C = dcb->if_tx_buf[dcb->if_tx_idx];
  199. ++(dcb->if_tx_idx);
  200. /* TMR1 output compare A match interrupt enable */
  201. ETIMSK |= _BV(OCIE1C);
  202. TCCR1B |= _BV(CS11); /* start counting f/8 */
  203. }
  204. return 0;
  205. }
  206. /*!
  207. * \brief Wait for output buffer empty.
  208. *
  209. * If the output buffer contains any data, the calling
  210. * thread is suspended until all data has been transmitted.
  211. *
  212. * \param dev Indicates the device.
  213. *
  214. * \return 0.
  215. */
  216. static int IrblastFlush(NUTDEVICE * dev)
  217. {
  218. IRBLASTDCB *dcb = dev->dev_dcb;
  219. /* Start any pending output */
  220. IrblastOutput(dev);
  221. /* Wait until output buffer empty */
  222. while (dcb->if_tx_idx != dcb->if_wr_idx) {
  223. NutEventWaitNext(&dcb->dcb_tx_rdy, 100);
  224. }
  225. /* TMR1 output compare A match interrupt disable */
  226. ETIMSK &= ~_BV(OCIE1C);
  227. TCCR1B &= ~_BV(CS11); /* stop counting */
  228. TCCR1A &= ~_BV(COM1C0);
  229. TCCR1A |= _BV(COM1C1); /* clrar OC pin on compare */
  230. TCCR1C = _BV(FOC1C); /* trigger pin - clear it */
  231. return 0;
  232. }
  233. /*!
  234. * \brief Moves data to the output buffer.
  235. *
  236. * If the output buffer contains any data, the calling
  237. * thread is suspended until all data has been transmitted.
  238. *
  239. * \param dev Indicates the device.
  240. * \param buffer data buffer.
  241. * \param len is length in bytes!.
  242. * \param pflag 0 - indicates input is ram, !=0 - input is progmem.
  243. *
  244. * \return number of bytes sent.
  245. */
  246. static int IrblastPut(NUTDEVICE * dev, const void *buffer, int len, int pflg)
  247. {
  248. int rc = 0;
  249. IRBLASTDCB *dcb = dev->dev_dcb;
  250. const uint16_t *cp;
  251. uint16_t ch;
  252. /* Call without data pointer starts transmission */
  253. if (buffer == 0) {
  254. IrblastFlush(dev);
  255. }
  256. /* Put data in transmit buffer,
  257. for us buffer points to table of 'uint16_t' type data */
  258. cp = buffer;
  259. /* len is length in bytes, so it must be divided by 2 */
  260. len >>= 1;
  261. for (rc = 0; rc < len;) {
  262. if ((uint8_t) (dcb->if_wr_idx + 1) == dcb->if_tx_idx) {
  263. IrblastFlush(dev);
  264. }
  265. ch = pflg ? PRG_RDB(cp) : *cp;
  266. dcb->if_tx_buf[dcb->if_wr_idx] = ch;
  267. ++(dcb->if_wr_idx);
  268. ++cp;
  269. ++rc;
  270. }
  271. /* multiply result by 2 to return the number of sent bytes */
  272. return (rc << 1);
  273. }
  274. /*!
  275. * \brief Moves data from ram to the output buffer.
  276. *
  277. * \param fp file pointer.
  278. * \param buffer data buffer.
  279. * \param len is length in bytes!.
  280. *
  281. * \return number of bytes sent.
  282. */
  283. static int IrblastWrite(NUTFILE * fp, const void *buffer, int len)
  284. {
  285. return IrblastPut(fp->nf_dev, buffer, len, 0);
  286. }
  287. /*!
  288. * \brief Moves data from progmem to the output buffer.
  289. *
  290. * \param fp file pointer.
  291. * \param buffer data buffer.
  292. * \param len is length in bytes!.
  293. *
  294. * \return number of bytes sent.
  295. */
  296. static int IrblastWrite_P(NUTFILE * fp, PGM_P buffer, int len)
  297. {
  298. return IrblastPut(fp->nf_dev, (const char *) buffer, len, 1);
  299. }
  300. /*!
  301. * \brief Asynchronous control interface.
  302. *
  303. * \param dev Indicates the device.
  304. * \param req request type.
  305. * \param conf request parameter.
  306. *
  307. * \return 0-success, -1-error.
  308. */
  309. static int IrblastIOCtl(NUTDEVICE * dev, int req, void *conf)
  310. {
  311. uint8_t *usp = (uint8_t *) conf;
  312. switch (req) {
  313. case IRBLAST_SETFREQ:
  314. if (*usp == 0) {
  315. /* disable compare modulator hardware functionality and timer at all */
  316. TCCR2 &= ~(_BV(WGM21) | _BV(COM20));
  317. } else {
  318. OCR2 = *usp;
  319. /* Clear Counter register (8bit) */
  320. TCNT2 = 0;
  321. /* enable compare modulator hardware functionality and timer itself in CTC mode */
  322. TCCR2 |= _BV(WGM21) | _BV(COM20);
  323. }
  324. break;
  325. case IRBLAST_GETFREQ:
  326. *usp = OCR2;
  327. break;
  328. default:
  329. return -1;
  330. }
  331. return 0;
  332. }
  333. /*!
  334. * \brief Driver Open Funtion.
  335. *
  336. * \param dev Indicates the device.
  337. * \param name unused.
  338. * \param mode unused.
  339. * \param acc unused.
  340. *
  341. * \return 0-success, -1-error.
  342. */
  343. static NUTFILE *IrblastOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
  344. {
  345. file.nf_dev = dev;
  346. file.nf_fcb = NULL;
  347. return &file;
  348. }
  349. /*!
  350. * \brief Driver Close Funtion.
  351. *
  352. * \param fp file pointer.
  353. *
  354. * \return 0.
  355. */
  356. static int IrblastClose(NUTFILE * fp)
  357. {
  358. return 0;
  359. }
  360. /*!
  361. * \brief Timer1 Initialization.
  362. *
  363. * Timer 1 counts ir periods.
  364. */
  365. static void IrblastTmr1Init(void)
  366. {
  367. /* TMR1 runs in CTC mode */
  368. TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0) | _BV(WGM11) | _BV(WGM10)); /* CTC mode */
  369. TCCR1A |= _BV(COM1C1); /* clrar OC pin on compare */
  370. TCCR1B &= ~(_BV(WGM13) | _BV(WGM12) | _BV(CS12) | _BV(CS11) | _BV(CS10)); /* f = off */
  371. TCCR1B |= _BV(WGM12); /* CTC */
  372. TCCR1C = _BV(FOC1C); /* trigger pin - clear it */
  373. /* TMR1 output compare A match interrupt disable */
  374. ETIMSK &= ~_BV(OCIE1C);
  375. }
  376. /*!
  377. * \brief Timer2 Initialization.
  378. *
  379. * Timer 1 serves ir carrier frequency.
  380. */
  381. static void IrblastTmr2Init(void)
  382. {
  383. /* TMR2 is off - must be started with ioctl call */
  384. TCCR2 = _BV(CS20); /* f=clk/1 - do not enable compare modulator hardware functionality */
  385. }
  386. /*!
  387. * \brief Driver Initialization Funtion.
  388. *
  389. * \param dev Indicates the device.
  390. *
  391. * \return 0-success, -1-error.
  392. */
  393. static int IrblastInit(NUTDEVICE * dev)
  394. {
  395. IRBLASTDCB *dcb = dev->dev_dcb;
  396. /* Initialize Driver Control Block */
  397. memset(dcb, 0, sizeof(IRBLASTDCB));
  398. /* Register interrupt handlers */
  399. if (NutRegisterIrqHandler(&sig_OUTPUT_COMPARE1C, IrblastOutComp1CInt, dev))
  400. return -1;
  401. /* Init Timer2 - carrier frequency generator */
  402. IrblastTmr2Init();
  403. /* Init Timer1 - AM modulation of Timer3 output pin */
  404. IrblastTmr1Init();
  405. /* This pin is used by hardware mux */
  406. sbi(DDRB, PORTB7);
  407. return 0;
  408. }
  409. NUTDEVICE devIrblast0 = {
  410. 0, /* Pointer to next dev */
  411. {'i', 'r', 'b', 'l', 'a', 's', 't', '0', 0}
  412. , /* Unique device name */
  413. IFTYP_STREAM, /* Type of dev, IFTYP_CHAR ? */
  414. 0, /* Base address */
  415. 0, /* First irq # */
  416. 0, /* I/f control block */
  417. &dcb_pwm0, /* Dev control block */
  418. IrblastInit,
  419. IrblastIOCtl,
  420. 0,
  421. IrblastWrite,
  422. IrblastWrite_P,
  423. IrblastOpen,
  424. IrblastClose,
  425. 0,
  426. 0, /* Select function, optional, not yet implemented */
  427. };