Interfacing

Banana Pi IR Driver

22 20049
Thanks Crazyman's work.

1 About Infrared
I did not know anything about  infrared protocol  previously, and Google says there are many infrared protocol. So the only way is  to catch the infrared signal by myself to get it out. Below is an infrared signal caught by FPGA-signaltap, the sampling frequency is 28.57Khz.
You can see from the image above, IR signal start period is 9ms (256 * (1/28.57Khz))  Low, and then around 4ms High, after 0,1 signals to said pulse width 0 is 1ms pulse, 0 is a 2ms pulse .
IR协议.jpg

2 A20 Related Registers
a. First, configure an infrared device clock, it  needs to set  the APB0_GATING_REG and IR_0_CLK under System  CCU .
b. Configuring complex pin PB4 to IR function.
c. Configuring IR device registers under Interface

3 Write IR Driver
Add some interrupts to IO driver to form the IR driver. Most part of the code is copied from the kernel driver linux-sunxi/drivers/input/keyboard/sunxi_ir.c, The specific code is as below:
  1. /*
  2. * irdriver.c
  3. *
  4. *  Created on: April 26, 2014
  5. *      Author: crazy
  6. */
  7. #include <linux/init.h>
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <mach/gpio.h>
  11. #include <linux/fs.h>
  12. #include <linux/cdev.h>

  13. #include <linux/err.h>
  14. #include <linux/errno.h>

  15. #include <asm/io.h>
  16. #include <asm/uaccess.h>
  17. #include <linux/blkdev.h>
  18. #include <linux/vmalloc.h>

  19. #include<linux/proc_fs.h>
  20. #include<asm/system.h>
  21. #include<linux/fcntl.h>

  22. #include<linux/device.h>
  23. #include <linux/delay.h>
  24. #include <linux/interrupt.h>
  25. #include <asm/irq.h>
  26. #include <linux/timer.h>
  27. #include <mach/irqs.h>
  28. #include <mach/system.h>
  29. #include <mach/hardware.h>
  30. #include <plat/sys_config.h>
  31. #include <mach/clock.h>
  32. #include <linux/clk.h>

  33. #define DEVICE_NAME "irdriver"  //定¡§义°?ir设¦¨¨备À?名?   
  34. int ir_major = 333;
  35. int ir_minor = 0;
  36. int number_of_devices = 1;

  37. struct class *ir_class;
  38. struct cdev ircdev;
  39. dev_t dev;


  40. #define        IR_RAW_BUF_SIZE                128
  41. struct ir_raw_buffer
  42. {
  43.         unsigned long dcnt;                  /*Packet Count*/
  44.         unsigned char buf[IR_RAW_BUF_SIZE];
  45. };
  46. //Registers
  47. #define IR_REG(x)         (x)
  48. #define IR0_BASE                (0xf1c21800)
  49. #define IR1_BASE                (0xf1c21C00)
  50. #define IR_BASE                IR0_BASE
  51. #define IR_IRQNO        (SW_INT_IRQNO_IR0)

  52. //CCM register
  53. #define CCM_BASE        0xf1c20000
  54. //PIO register
  55. #define PI_BASE                0xf1c20800

  56. #define IR_CTRL_REG        IR_REG(0x00) //IR Control
  57. #define IR_RXCFG_REG        IR_REG(0x10) //Rx Config
  58. #define IR_RXCOUNT_REG        IR_REG(0x18) //Rx Count
  59. #define IR_RXDAT_REG        IR_REG(0x20) //Rx Data
  60. #define IR_RXINTE_REG        IR_REG(0x2C) //Rx Interrupt Enable
  61. #define IR_RXINTS_REG        IR_REG(0x30) //Rx Interrupt Status
  62. #define IR_SPLCFG_REG        IR_REG(0x34) //IR Sample Config

  63. //Bit Definition of IR_RXINTS_REG Register
  64. #define IR_RXINTS_RXOF                (0x1<<0)                //Rx FIFO Overflow
  65. #define IR_RXINTS_RXPE                (0x1<<1)                //Rx Packet End
  66. #define IR_RXINTS_RXDA                (0x1<<4)                //Rx FIFO Data Available
  67. #define IR_RXINTS_RRIS                (0x1<<2)                //Rx RIS
  68. #define IR_RXINTS_RCRC                (0x1<<3)                //Rx CRC
  69. //Frequency of Sample Clock = 23437.5Hz, Cycle is 42.7us
  70. //Pulse of NEC Remote >560us
  71. #define IR_RXFILT_VAL                (8)                        //Filter Threshold = 8*42.7 = ~341us        < 500us
  72. #define IR_RXIDLE_VAL                (2)           //Idle Threshold = (2+1)*128*42.7 = ~16.4ms > 9ms
  73. #define IR_FIFO_SIZE                (64)    //16Bytes

  74. #define IR_L1_MIN        (170)                //80*42.7 = ~3.4ms, Lead1(4.5ms) > IR_L1_MIN
  75. #define IR_L0_MIN        (88)                //40*42.7 = ~1.7ms, Lead0(4.5ms) Lead0R(2.25ms)> IR_L0_MIN
  76. #define IR_PMAX                (32)                //26*42.7 = ~1109us ~= 561*2, Pluse < IR_PMAX
  77. #define IR_DMID                (26)                //26*42.7 = ~1109us ~= 561*2, D1 > IR_DMID, D0 =< IR_DMID
  78. #define IR_DMAX                (53)                //53*42.7 = ~2263us ~= 561*4, D < IR_DMAX

  79. #define IR_ERROR_CODE                (0xffffffff)
  80. #define IR_REPEAT_CODE                (0x00000000)

  81. static wait_queue_head_t wait;
  82. static        int interupt_flag;
  83. static unsigned char simple_inc=0;  
  84. static struct ir_raw_buffer        ir_rawbuf;
  85. static unsigned int code_temp[IR_RAW_BUF_SIZE];

  86. static inline void ir_reset_rawbuffer(void)
  87. {
  88.         ir_rawbuf.dcnt = 0;
  89. }

  90. static inline void ir_write_rawbuffer(unsigned char data)
  91. {
  92.         if(ir_rawbuf.dcnt < IR_RAW_BUF_SIZE)
  93.         {
  94.                 ir_rawbuf.buf[ir_rawbuf.dcnt++] = data;
  95.         }
  96.         else
  97.         {
  98.                 printk("ir_write_rawbuffer: IR Rx Buffer Full!!\n");
  99.         }
  100. }

  101. static inline unsigned char ir_read_rawbuffer(void)
  102. {
  103.         unsigned char data = 0x00;

  104.         if(ir_rawbuf.dcnt > 0)
  105.         {
  106.                 data = ir_rawbuf.buf[--ir_rawbuf.dcnt];
  107.         }

  108.         return data;
  109. }

  110. static inline int ir_rawbuffer_empty(void)
  111. {
  112.         return (ir_rawbuf.dcnt == 0);
  113. }

  114. static inline int ir_rawbuffer_full(void)
  115. {
  116.         return (ir_rawbuf.dcnt >= IR_RAW_BUF_SIZE);
  117. }

  118. int ir_open(struct inode *inode, struct file *filp)  
  119. {  
  120.     if(simple_inc>0)return -ERESTARTSYS;  
  121.     simple_inc++;  
  122.     return 0;  
  123. }  
  124. int ir_release(struct inode *inode, struct file *filp)  
  125. {  
  126.     simple_inc--;  
  127.     return 0;  
  128. }  
  129. ssize_t ir_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)  
  130. {
  131.     interupt_flag = 0;
  132.     wait_event_interruptible(wait,interupt_flag);
  133.     interupt_flag = 0;
  134.     /* 把ã?数ºy据Y复¡ä制?到Ì?应®|用®?程¨¬序¨°空?间? */
  135.     if (copy_to_user(buf,code_temp,count))  
  136.     {  
  137.        count=-EFAULT;   
  138.     }
  139.     return count;  
  140. }  
  141. ssize_t ir_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)  
  142. {  
  143.     /* 把ã?数ºy据Y复¡ä制?到Ì?内¨²核?空?间? */  
  144.     /*if (copy_from_user(code_temp+*f_pos, buf, count))  
  145.     {  
  146.         
  147.         count = -EFAULT;  
  148.     }*/
  149.     return count;  
  150. }
  151. static inline unsigned char ir_get_data(void)
  152. {
  153.         return (unsigned char)(readl(IR_BASE + IR_RXDAT_REG));
  154. }

  155. static inline unsigned long ir_get_intsta(void)
  156. {
  157.         return (readl(IR_BASE + IR_RXINTS_REG));
  158. }

  159. static inline void ir_clr_intsta(unsigned long bitmap)
  160. {
  161.         unsigned long tmp = readl(IR_BASE + IR_RXINTS_REG);

  162.         tmp &= ~0xff;
  163.         tmp |= bitmap&0xff;
  164.         writel(tmp, IR_BASE + IR_RXINTS_REG);
  165. }

  166. static void ir_clk_cfg(void)
  167. {
  168.         unsigned long tmp = 0;
  169.         //Enable APB Clock for IR
  170.         tmp = readl(CCM_BASE + 0x68);
  171.         tmp |= 0x1<<6;  //IR
  172.         writel(tmp, CCM_BASE + 0x68);

  173.         //config Special Clock for IR        (24/8=3MHz)
  174.         tmp = readl(CCM_BASE + 0xB0);
  175.         tmp &= ~(0x3<<24);
  176.         //tmp |= (0x0<<24);           //Select 24MHz
  177.         tmp |= (0x1<<31);           //Open Clock
  178.         tmp &= ~(0x3<<16);
  179.         tmp |= (0x3<<16);                 //Divisor = 8
  180.         tmp &= ~0xf;
  181.         writel(tmp, CCM_BASE + 0xB0);
  182. }
  183. static void ir_sys_cfg(void)
  184. {
  185.         unsigned long tmp;
  186.         //config IO: PIOB4 to IR_Rx
  187.         tmp = readl(PI_BASE + 0x24); //PIOB_CFG0_REG
  188.         tmp &= ~(0xf<<16);
  189.         tmp |= (0x2<<16);
  190.         writel(tmp, PI_BASE + 0x24);

  191.         ir_clk_cfg();
  192. }
  193. static void ir_reg_cfg(void)
  194. {
  195.         unsigned long tmp = 0;
  196.         /*Enable IR Mode*/
  197.         tmp = 0x3<<4;
  198.         writel(tmp, IR_BASE+IR_CTRL_REG);

  199.         /*Config IR Smaple Register*/
  200.         tmp = 0x1<<0;         //Fsample = 3MHz/128 = 23437.5Hz (42.7us)
  201.         tmp |= (IR_RXFILT_VAL&0x3f)<<2;        //Set Filter Threshold
  202.         tmp |= (IR_RXIDLE_VAL&0xff)<<8; //Set Idle Threshold
  203.         writel(tmp, IR_BASE+IR_SPLCFG_REG);

  204.         /*Invert Input Signal*/
  205.         writel(0x1<<2, IR_BASE+IR_RXCFG_REG);

  206.         /*Clear All Rx Interrupt Status*/
  207.         writel(0xff, IR_BASE+IR_RXINTS_REG);
  208.         /*Set Rx Interrupt Enable*/
  209.         tmp = (0x1<<4)|0x3;
  210.         tmp |= ((IR_FIFO_SIZE>>2)-1)<<8; //Rx FIFO Threshold = FIFOsz/4;
  211.         writel(tmp, IR_BASE+IR_RXINTE_REG);

  212.         /*Enable IR Module*/
  213.         tmp = readl(IR_BASE+IR_CTRL_REG);
  214.         tmp |= 0x3;
  215.         writel(tmp, IR_BASE+IR_CTRL_REG);

  216. }

  217. static unsigned long ir_packet_handler(unsigned char *buf, unsigned long dcnt)
  218. {
  219.         unsigned long len;
  220.         unsigned char val = 0x00;
  221.         unsigned char last = 0x00;
  222.         unsigned long code = 0;
  223.         int bitCnt = 0;
  224.         unsigned long i=0;


  225.         /*Find Lead '1'*/
  226.         len = 0;
  227.         for(i=0; i<dcnt; i++)
  228.         {
  229.                 val = buf[i];
  230.                 if(val & 0x80)
  231.                 {
  232.                         len += val & 0x7f;
  233.                 }
  234.                 else
  235.                 {
  236.                         if(len > IR_L1_MIN)
  237.                           {
  238.                               break;
  239.                            }
  240.                         len = 0;
  241.                 }
  242.         }
  243.         if((val&0x80) || (len<=IR_L1_MIN))
  244.            return IR_ERROR_CODE; /*Invalid Code*/
  245.         /*Find Lead '0'*/
  246.         len = 0;
  247.         for(; i<dcnt; i++)
  248.         {
  249.                 val = buf[i];
  250.                 if(val & 0x80)
  251.                 {
  252.                         if(len > IR_L0_MIN)
  253.                                 break;

  254.                         len = 0;
  255.                 }
  256.                 else
  257.                 {
  258.                         len += val & 0x7f;
  259.                 }
  260.         }

  261.         if((!(val&0x80)) || (len<=IR_L0_MIN))
  262.                 return IR_ERROR_CODE; /*Invalid Code*/
  263.       
  264.         /*go decoding*/
  265.         code = 0;  /*0 for Repeat Code*/
  266.         bitCnt = 0;
  267.         last = 1;
  268.         len = 0;
  269.         for(; i<dcnt; i++)
  270.         {
  271.                 val = buf[i];
  272.                 if(last)
  273.                 {
  274.                         if(val & 0x80)
  275.                         {
  276.                                 len += val & 0x7f;
  277.                         }
  278.                         else
  279.                         {
  280.                                 if(len > IR_PMAX) /*Error Pulse*/
  281.                                 {
  282.                                         return IR_ERROR_CODE;
  283.                                 }
  284.                                 last = 0;
  285.                                 len = val & 0x7f;
  286.                         }
  287.                 }
  288.                 else
  289.                 {
  290.                         if(val & 0x80)
  291.                         {
  292.                                 if(len > IR_DMAX) /*Error Distant*/
  293.                                 {
  294.                                         return IR_ERROR_CODE;
  295.                                 }
  296.                                 else
  297.                                 {
  298.                                         if(len > IR_DMID)
  299.                                         {
  300.                                                 /*data '1'*/
  301.                                            code |= 1<<bitCnt;

  302.                                         }
  303.                                         bitCnt ++;
  304.                                         if(bitCnt == 32)
  305.                                                 break;  /*decode over*/
  306.                                 }
  307.                                 last = 1;
  308.                                 len = val & 0x7f;
  309.                         }
  310.                         else
  311.                         {
  312.                                 len += val & 0x7f;
  313.                         }
  314.                 }
  315.         }
  316.         return code;
  317. }


  318. static irqreturn_t ir_irq_service(int irqno, void *dev_id)
  319. {
  320.     unsigned long intsta = ir_get_intsta();
  321.    ir_clr_intsta(intsta);
  322.    unsigned long dcnt =  (ir_get_intsta()>>8) & 0x3f;
  323.    unsigned long i = 0;
  324.    //printk("rec data num=%x\n",dcnt);
  325.    
  326.    /*Read FIFO*/
  327.    for(i=0; i<dcnt; i++)
  328.     {
  329.         if(ir_rawbuffer_full())
  330.          {
  331.                                 
  332.           printk("ir_irq_service: Raw Buffer Full!!\n");
  333.                                 
  334.            break;
  335.          }
  336.         else
  337.          {
  338.           ir_write_rawbuffer(ir_get_data());
  339.          }
  340.    }
  341.    if(intsta & IR_RXINTS_RXPE)         /*Packet End*/
  342.   {
  343.      unsigned long code;
  344.      //printk("The Size of rawbuff=%x\n",ir_rawbuf.dcnt);
  345.      code = ir_packet_handler(ir_rawbuf.buf, ir_rawbuf.dcnt);
  346.      code_temp[0]=(unsigned int)(code>>16);
  347.      ir_rawbuf.dcnt = 0;
  348.      interupt_flag = 1;
  349.      wake_up(&(wait));

  350.   }        
  351.    if(intsta & IR_RXINTS_RXOF)  /*FIFO Overflow*/
  352.    {
  353.                 /*flush raw buffer*/
  354.                 ir_reset_rawbuffer();
  355.                 printk("ir_irq_service: Rx FIFO Overflow!!\n");
  356.    }
  357.    return IRQ_HANDLED;
  358. }
  359. struct file_operations ir_fops = {  
  360.     .owner =    THIS_MODULE,  
  361.     .read =     ir_read,  
  362.     .write =    ir_write,  
  363.     .open =     ir_open,  
  364.     .release =  ir_release,  
  365. };

  366. static int ir_init(void){   
  367. int ret; //根¨´据Y设¦¨¨备À?号?与®?设¦¨¨备À?名?注Á¡é册¨¢字Á?符¤?设¦¨¨备À?
  368. int result;
  369. interupt_flag = 0;
  370. dev = MKDEV (ir_major, ir_minor);
  371. init_waitqueue_head(&wait);
  372. result = register_chrdev_region (dev, number_of_devices, DEVICE_NAME);
  373. if (result<0) {
  374.            printk (KERN_WARNING "ir: can't get major number %d\n",ir_major);
  375.            return result;
  376. }
  377. cdev_init(&ircdev,&ir_fops);
  378. ircdev.owner=THIS_MODULE;
  379. ircdev.ops=&ir_fops;
  380. ret=cdev_add(&ircdev,dev,1);
  381. if(ret){
  382. printk("error %d add ir",ret);
  383. }
  384. ir_class = class_create(THIS_MODULE, DEVICE_NAME);
  385. if(IS_ERR(ir_class))
  386. {
  387.   printk("Err: failed in creating class.\n");
  388.   return -1;
  389. }
  390. device_create(ir_class, NULL, MKDEV(ir_major, 0),NULL, "%s", DEVICE_NAME);
  391. printk ("Registered character driver\n");
  392. ret=request_irq(IR_IRQNO, ir_irq_service, 0, DEVICE_NAME,1);
  393. if(ret<0){
  394. printk("request_irq fail!\n");
  395. }
  396. ir_sys_cfg();
  397. ir_reg_cfg();
  398. return 0;
  399. }
  400. static void ir_exit(void){
  401. free_irq(IR_IRQNO, 1);
  402. dev_t devno = MKDEV (ir_major, ir_minor);
  403. cdev_del (&ircdev);
  404. device_destroy(ir_class, MKDEV(ir_major, 0));         //delete device node under /dev
  405. class_destroy(ir_class);                               //delete class created by us
  406. unregister_chrdev_region (devno, number_of_devices);
  407. printk (KERN_INFO "char driver cleaned up\n");
  408. }
  409. module_init(ir_init);
  410. module_exit(ir_exit);

  411. MODULE_DESCRIPTION("ir driver");
  412. MODULE_AUTHOR("crazy <heboxiang@126.com>");
  413. MODULE_LICENSE("GPL");
Copy the Code
Makefile is similar to IO driver.

4 Test Program
Test procedures for key coding is just for my infrared transmitter, you can make some changes according to your  infrared transmitter:
  1. /*
  2. * main.c
  3. *
  4. *  Created on: Apr 26, 2014
  5. *      Author: crazy
  6. */

  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <fcntl.h>
  10. #include <string.h>

  11. int irdriver_init(){
  12.         int ir_fd;
  13.         ir_fd=open("/dev/irdriver",O_RDWR);
  14.         if(ir_fd<0){

  15.                 printf("Can't open iodriver!\n");
  16.                 return -1;
  17.         }
  18.         return ir_fd;
  19. }

  20. int main(void){
  21.     int ir_fd;
  22.     int ret;
  23.     int key_num;
  24.     unsigned int read_ir_data;
  25.     ir_fd=irdriver_init();
  26.     if(ir_fd<0){
  27.     printf("Open driver error!\n");
  28.     return -1;
  29.     }
  30.     while(1){
  31.     ret=read(ir_fd,&read_ir_data,sizeof(read_ir_data));
  32.     if(read_ir_data==0xffff){  // Code is invalid
  33.             //printf("Code is invalid\n");
  34.     }
  35.     else
  36.     //printf("Received Code=%x\n",read_ir_data);
  37.     switch(read_ir_data){
  38.      case 0xe916:
  39.             key_num=0;
  40.             break;
  41.      case 0xf30c:
  42.         key_num=1;
  43.         break;
  44.      case 0xe718:
  45.         key_num=2;
  46.         break;
  47.      case 0xa15e:
  48.         key_num=3;
  49.         break;
  50.      case 0xf708:
  51.         key_num=4;
  52.         break;
  53.      case 0xe31c:
  54.         key_num=5;
  55.         break;
  56.      case 0xa55a:
  57.         key_num=6;
  58.         break;
  59.      case 0xbd42:
  60.         key_num=7;
  61.         break;
  62.      case 0xad52:
  63.         key_num=8;
  64.         break;
  65.      case 0xb54a:
  66.         key_num=9;
  67.         break;
  68.      default:
  69.         key_num=-1;
  70.         break;
  71.     }
  72.     if(key_num<0){
  73.       printf("You pressed the non-code button!\n");
  74.     }
  75.     else{
  76.      printf("You pressed button %d!\n",key_num);
  77.     }
  78.     }
  79.         return 0;
  80. }
Copy the Code
Test result:
ir_test.jpg
sogster  
Hi i and wondering how to install the IR driver so i can use LIRC.
Thanks

Ra:)  
Hello, Tony. Have You implemented this ir driver in latest lubuntu?
Because i see only default sunxi device, that gives nothing on the output.

This driver is just a test program for IR. I didn't involve it in the kernel. In the kernel, I remembered I have opened the default sunxi IR driver.

sogster  
Finally got the infrared working on the banana pi. Looks like there's a bug in all of the A20 script.bin files on github.  The line "ir_rx=" should be "ir0_rx=".

Ra:)  
So the IR section should be like this:
  1. [ir_para]
  2. ir_used = 1
  3. ir0_rx = port:PB04<2><default><default><default>
Copy the Code

Reply 5# sogster

Really? Did you test it?

nnn2  
Can confirm, this works. Thanks Ra

sogster  
Yes when you fix up the script.bin file it turns the infrared into a keyboard.
I still haven't got lirc working correctly as I need to edit the hal configuration file I think so Hal doesn't take control of the infrared port.
At the moment when you point a remote at the infrared port it just uses random keys and I don't know how to edit them. When lirc is in control of the port you can map the keys to what you want.
When i get it working I will put up a todo on the forum.

nnn2  
Reply 9# sogster


   You can use https://bitbucket.org/emiliolopez/keybinder.git and then map keycode:command to execute in keybinder.conf
Not as good as lirc but works

You have to log in before you can reply Login | Sign Up

Points Rules