Banana Pi IR Driver

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 .

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:

/*
 * irdriver.c
 *
 *  Created on: April 26, 2014
 *      Author: crazy
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <mach/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>

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

#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/blkdev.h>
#include <linux/vmalloc.h>

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

#include<linux/device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <linux/timer.h>
#include <mach/irqs.h>
#include <mach/system.h>
#include <mach/hardware.h>
#include <plat/sys_config.h>
#include <mach/clock.h>
#include <linux/clk.h>

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

struct class *ir_class; 
struct cdev ircdev;
dev_t dev;


#define        IR_RAW_BUF_SIZE                128
struct ir_raw_buffer
{
        unsigned long dcnt;                  /*Packet Count*/
        unsigned char buf[IR_RAW_BUF_SIZE];
};
//Registers
#define IR_REG(x)         (x)
#define IR0_BASE                (0xf1c21800)
#define IR1_BASE                (0xf1c21C00)
#define IR_BASE                IR0_BASE
#define IR_IRQNO        (SW_INT_IRQNO_IR0)

//CCM register
#define CCM_BASE        0xf1c20000
//PIO register
#define PI_BASE                0xf1c20800

#define IR_CTRL_REG        IR_REG(0x00) //IR Control
#define IR_RXCFG_REG        IR_REG(0x10) //Rx Config
#define IR_RXCOUNT_REG        IR_REG(0x18) //Rx Count
#define IR_RXDAT_REG        IR_REG(0x20) //Rx Data
#define IR_RXINTE_REG        IR_REG(0x2C) //Rx Interrupt Enable
#define IR_RXINTS_REG        IR_REG(0x30) //Rx Interrupt Status
#define IR_SPLCFG_REG        IR_REG(0x34) //IR Sample Config

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

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

#define IR_ERROR_CODE                (0xffffffff)
#define IR_REPEAT_CODE                (0x00000000)

static wait_queue_head_t wait;
static        int interupt_flag;
static unsigned char simple_inc=0;  
static struct ir_raw_buffer        ir_rawbuf;
static unsigned int code_temp[IR_RAW_BUF_SIZE];

static inline void ir_reset_rawbuffer(void)
{
        ir_rawbuf.dcnt = 0;
}

static inline void ir_write_rawbuffer(unsigned char data)
{
        if(ir_rawbuf.dcnt < IR_RAW_BUF_SIZE)
        {
                ir_rawbuf.buf[ir_rawbuf.dcnt++] = data;
        }
        else
        {
                printk("ir_write_rawbuffer: IR Rx Buffer Full!!\n");
        }
}

static inline unsigned char ir_read_rawbuffer(void)
{
        unsigned char data = 0x00;

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

        return data;
}

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

static inline int ir_rawbuffer_full(void)
{
        return (ir_rawbuf.dcnt >= IR_RAW_BUF_SIZE);
}
 
int ir_open(struct inode *inode, struct file *filp)  
{  
    if(simple_inc>0)return -ERESTARTSYS;  
    simple_inc++;  
    return 0;  
}  
int ir_release(struct inode *inode, struct file *filp)  
{  
    simple_inc--;  
    return 0;  
}  
ssize_t ir_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)  
{ 
    interupt_flag = 0;
    wait_event_interruptible(wait,interupt_flag);
    interupt_flag = 0;
 
    if (copy_to_user(buf,code_temp,count))  
    {  
       count=-EFAULT;   
    }
    return count;  
}  
ssize_t ir_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)  
{  
    /* 把ã?数ºy据Y复¡ä制?到Ì?内¨²核?空?间? */  
    /*if (copy_from_user(code_temp+*f_pos, buf, count))  
    {  
        
        count = -EFAULT;  
    }*/ 
    return count;  
}
static inline unsigned char ir_get_data(void)
{
        return (unsigned char)(readl(IR_BASE + IR_RXDAT_REG));
}

static inline unsigned long ir_get_intsta(void)
{
        return (readl(IR_BASE + IR_RXINTS_REG));
}

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

        tmp &= ~0xff;
        tmp |= bitmap&0xff;
        writel(tmp, IR_BASE + IR_RXINTS_REG);
}

static void ir_clk_cfg(void)
{
        unsigned long tmp = 0;
        //Enable APB Clock for IR
        tmp = readl(CCM_BASE + 0x68);
        tmp |= 0x1<<6;  //IR
        writel(tmp, CCM_BASE + 0x68);

        //config Special Clock for IR        (24/8=3MHz)
        tmp = readl(CCM_BASE + 0xB0);
        tmp &= ~(0x3<<24);
        //tmp |= (0x0<<24);           //Select 24MHz
        tmp |= (0x1<<31);           //Open Clock
        tmp &= ~(0x3<<16);
        tmp |= (0x3<<16);                 //Divisor = 8
        tmp &= ~0xf;
        writel(tmp, CCM_BASE + 0xB0);
}
static void ir_sys_cfg(void)
{
        unsigned long tmp;
        //config IO: PIOB4 to IR_Rx
        tmp = readl(PI_BASE + 0x24); //PIOB_CFG0_REG
        tmp &= ~(0xf<<16);
        tmp |= (0x2<<16);
        writel(tmp, PI_BASE + 0x24);

        ir_clk_cfg();
}
static void ir_reg_cfg(void)
{
        unsigned long tmp = 0;
        /*Enable IR Mode*/
        tmp = 0x3<<4;
        writel(tmp, IR_BASE+IR_CTRL_REG);

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

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

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

        /*Enable IR Module*/
        tmp = readl(IR_BASE+IR_CTRL_REG);
        tmp |= 0x3;
        writel(tmp, IR_BASE+IR_CTRL_REG);

}

static unsigned long ir_packet_handler(unsigned char *buf, unsigned long dcnt)
{
        unsigned long len;
        unsigned char val = 0x00;
        unsigned char last = 0x00;
        unsigned long code = 0;
        int bitCnt = 0;
        unsigned long i=0;


        /*Find Lead '1'*/
        len = 0;
        for(i=0; i<dcnt; i++)
        {
                val = buf[i];
                if(val & 0x80)
                {
                        len += val & 0x7f;
                }
                else
                {
                        if(len > IR_L1_MIN)
                          {
                              break;
                           }
                        len = 0;
                }
        }
        if((val&0x80) || (len<=IR_L1_MIN))
           return IR_ERROR_CODE; /*Invalid Code*/
        /*Find Lead '0'*/
        len = 0;
        for(; i<dcnt; i++)
        {
                val = buf[i];
                if(val & 0x80)
                {
                        if(len > IR_L0_MIN)
                                break;

                        len = 0;
                }
                else
                {
                        len += val & 0x7f;
                }
        }

        if((!(val&0x80)) || (len<=IR_L0_MIN))
                return IR_ERROR_CODE; /*Invalid Code*/
       
        /*go decoding*/
        code = 0;  /*0 for Repeat Code*/
        bitCnt = 0;
        last = 1;
        len = 0;
        for(; i<dcnt; i++)
        {
                val = buf[i];
                if(last)
                {
                        if(val & 0x80)
                        {
                                len += val & 0x7f;
                        }
                        else
                        {
                                if(len > IR_PMAX) /*Error Pulse*/
                                {
                                        return IR_ERROR_CODE;
                                }
                                last = 0;
                                len = val & 0x7f;
                        }
                }
                else
                {
                        if(val & 0x80)
                        {
                                if(len > IR_DMAX) /*Error Distant*/
                                {
                                        return IR_ERROR_CODE;
                                }
                                else
                                {
                                        if(len > IR_DMID)
                                        {
                                                /*data '1'*/
                                           code |= 1<<bitCnt;

                                        }
                                        bitCnt ++;
                                        if(bitCnt == 32)
                                                break;  /*decode over*/
                                }
                                last = 1;
                                len = val & 0x7f;
                        }
                        else
                        {
                                len += val & 0x7f;
                        }
                }
        }
        return code;
}


static irqreturn_t ir_irq_service(int irqno, void *dev_id)
{ 
    unsigned long intsta = ir_get_intsta();
   ir_clr_intsta(intsta);
   unsigned long dcnt =  (ir_get_intsta()>>8) & 0x3f;
   unsigned long i = 0;
   //printk("rec data num=%x\n",dcnt);
   
   /*Read FIFO*/
   for(i=0; i<dcnt; i++)
    {
        if(ir_rawbuffer_full())
         {
                                
          printk("ir_irq_service: Raw Buffer Full!!\n");
                                
           break; 
         }
        else
         {
          ir_write_rawbuffer(ir_get_data());
         }
   }
   if(intsta & IR_RXINTS_RXPE)         /*Packet End*/
  {
     unsigned long code;
     //printk("The Size of rawbuff=%x\n",ir_rawbuf.dcnt);
     code = ir_packet_handler(ir_rawbuf.buf, ir_rawbuf.dcnt);
     code_temp[0]=(unsigned int)(code>>16); 
     ir_rawbuf.dcnt = 0;
     interupt_flag = 1;
     wake_up(&(wait));

  }        
   if(intsta & IR_RXINTS_RXOF)  /*FIFO Overflow*/
   {
                /*flush raw buffer*/
                ir_reset_rawbuffer();
                printk("ir_irq_service: Rx FIFO Overflow!!\n");
   }
   return IRQ_HANDLED;
}
struct file_operations ir_fops = {  
    .owner =    THIS_MODULE,  
    .read =     ir_read,  
    .write =    ir_write,  
    .open =     ir_open,  
    .release =  ir_release,  
}; 

static int ir_init(void){   
int ret; //根¨´据Y设¦¨¨备À?号?与®?设¦¨¨备À?名?注Á¡é册¨¢字Á?符¤?设¦¨¨备À? 
int result;
interupt_flag = 0;
dev = MKDEV (ir_major, ir_minor); 
init_waitqueue_head(&wait);
result = register_chrdev_region (dev, number_of_devices, DEVICE_NAME);
if (result<0) {
           printk (KERN_WARNING "ir: can't get major number %d\n",ir_major);
           return result;
}
cdev_init(&ircdev,&ir_fops);
ircdev.owner=THIS_MODULE;
ircdev.ops=&ir_fops;
ret=cdev_add(&ircdev,dev,1);
if(ret){
printk("error %d add ir",ret);
}
ir_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(ir_class)) 
{
  printk("Err: failed in creating class.\n");
  return -1; 
}
device_create(ir_class, NULL, MKDEV(ir_major, 0),NULL, "%s", DEVICE_NAME);
printk ("Registered character driver\n");
ret=request_irq(IR_IRQNO, ir_irq_service, 0, DEVICE_NAME,1);
if(ret<0){
 printk("request_irq fail!\n");
}
ir_sys_cfg();
ir_reg_cfg();
return 0;
}
static void ir_exit(void){
free_irq(IR_IRQNO, 1);
dev_t devno = MKDEV (ir_major, ir_minor); 
cdev_del (&ircdev);
device_destroy(ir_class, MKDEV(ir_major, 0));         //delete device node under /dev
class_destroy(ir_class);                               //delete class created by us
unregister_chrdev_region (devno, number_of_devices);
printk (KERN_INFO "char driver cleaned up\n");
}
module_init(ir_init);
module_exit(ir_exit);

MODULE_DESCRIPTION("ir driver");
MODULE_AUTHOR("crazy <[email protected]>");
MODULE_LICENSE("GPL");

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:

/*
 * main.c
 *
 *  Created on: Apr 26, 2014
 *      Author: crazy
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

int irdriver_init(){
        int ir_fd;
        ir_fd=open("/dev/irdriver",O_RDWR);
        if(ir_fd<0){

                printf("Can't open iodriver!\n");
                return -1;
        }
        return ir_fd;
}

int main(void){
    int ir_fd;
    int ret;
    int key_num;
    unsigned int read_ir_data;
    ir_fd=irdriver_init();
    if(ir_fd<0){
    printf("Open driver error!\n");
    return -1;
    }
    while(1){
    ret=read(ir_fd,&read_ir_data,sizeof(read_ir_data));
    if(read_ir_data==0xffff){  // Code is invalid
            //printf("Code is invalid\n");
    }
    else
    //printf("Received Code=%x\n",read_ir_data);
    switch(read_ir_data){
     case 0xe916:
            key_num=0;
            break;
     case 0xf30c:
        key_num=1;
        break;
     case 0xe718:
        key_num=2;
        break;
     case 0xa15e:
        key_num=3;
        break;
     case 0xf708:
        key_num=4;
        break;
     case 0xe31c:
        key_num=5;
        break;
     case 0xa55a:
        key_num=6;
        break;
     case 0xbd42:
        key_num=7;
        break;
     case 0xad52:
        key_num=8;
        break;
     case 0xb54a:
        key_num=9;
        break;
     default:
        key_num=-1;
        break;
    }
    if(key_num<0){
      printf("You pressed the non-code button!\n");
    }
    else{
     printf("You pressed button %d!\n",key_num);
    }
    }
        return 0;
}

Test result:

link: http://www.lemaker.org/thread-281-1-1.html

Is there any possibility to use a Logitech Harmony remote for BPI?