linux下的键盘控制芯片CH455驱动io模拟IIC(基于AM335x)

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:[email protected]】 

由于项目需要一款小的3*2的矩阵键盘,所以选用一款数码管驱动及键盘控制芯片 CH455 ,该芯片其实实际能支持到最大7*8的矩阵键盘以及4个数码管,在这里我们只是用到了他的键盘驱动功能,该芯片的使用框架图如下:

linux下的键盘控制芯片CH455驱动io模拟IIC(基于AM335x)_第1张图片

在项目实际应用中,芯片的实际电路设计原理如下:

linux下的键盘控制芯片CH455驱动io模拟IIC(基于AM335x)_第2张图片

在驱动软件的设计上,我们采用io模拟iic通信,外加中断的方式搞定该芯片。通过芯片的datasheet,我们可以清除的看到该芯片的通信时序如下:

linux下的键盘控制芯片CH455驱动io模拟IIC(基于AM335x)_第3张图片

根据以上的时序,我们在linux下实现该芯片的驱动,代码如下:

//ch455.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

typedef unsigned char   UINT8;
typedef unsigned short  UINT16;	

#define  STATUS_SUCCESS	0
#define  STATUS_FAILURE -1

// 读取按键代码命令
#define CH455_GET_KEY	0x0700					// 获取按键,返回按键代码
// CH455接口定义
#define		CH455_I2C_ADDR		0x40			// CH455的地址
#define		CH455_I2C_MASK		0x3E			// CH455的高字节命令掩码

static struct class *ch455iic_class;
static struct device	*ch455iic_class_dev;
static int major;
static unsigned char TxBuf[24] = {0};
static unsigned char RxBuf[24] = {0};

//iic
#define SCL            GPIO_TO_PIN(1,14)
#define SCL_OUT        gpio_direction_output(SCL, 1)
#define SCL_L          gpio_set_value(SCL, 0)
#define SCL_H          gpio_set_value(SCL, 1)

#define SDA            GPIO_TO_PIN(2,1)
//#define SDA          GPIO_TO_PIN(2,13)
#define SDA_OUT        gpio_direction_output(SDA, 1)
#define SDA_IN         gpio_direction_input(SDA)
#define CH455_SDA_IN   gpio_get_value(SDA)
#define SDA_L  		   gpio_set_value(SDA, 0)
#define SDA_H  		   gpio_set_value(SDA, 1)

#define RST            GPIO_TO_PIN(2,13)
//#define RST            GPIO_TO_PIN(2,1)
#define RST_OUT        gpio_direction_output(RST, 1)
#define RST_IN         gpio_direction_input(RST)
#define CH455_RST_IN   gpio_get_value(RST)
#define RST_L          gpio_set_value(RST, 0) 
#define RST_H          gpio_set_value(RST, 1)


#define INT      	   GPIO_TO_PIN(0,20)
#define INT_OUT        gpio_direction_output(INT, 1)
#define INT_IN         gpio_direction_input(INT)
#define CH455_INT_IN   gpio_get_value(INT)
#define INT_L  	       gpio_set_value(INT, 0) 
#define INT_H  	       gpio_set_value(INT, 1) 

#define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))

#define I2C_DELAY 5

static int irq_num;

void CH455_I2c_Start(void)
{
    SDA_OUT;
    SCL_H;   //SCL = high;
    udelay(I2C_DELAY);
    SDA_H;   //SDA = high;
    udelay(I2C_DELAY);
    SDA_L;    //SDA = low;
    udelay(I2C_DELAY);
    SCL_L;    //SCL = low;
    udelay(I2C_DELAY);
}
 

void CH455_I2c_Stop(void)
{
    SDA_OUT;    
    SCL_L;     //SCL = low;
    udelay(I2C_DELAY);
    SDA_L;     //SDA = low;
    udelay(I2C_DELAY);
    SCL_H;    //SCL = high;
    udelay(I2C_DELAY);
    SDA_H;    //SDA = high;
    udelay(I2C_DELAY);
}
 

void CH455_I2c_WrByte(unsigned char IIC_Byte)
{
    unsigned char i;
    SDA_OUT;
    for(i = 0; i < 8; i++)
    {
        if(IIC_Byte & 0x80)
            SDA_H;   //SDA=high;
        else
            SDA_L;   //SDA=low;
        udelay(I2C_DELAY);
        SCL_H;      //SCL=high;
        udelay(I2C_DELAY);
        SCL_L;       //SCL=low;
        udelay(I2C_DELAY);
        IIC_Byte<<=1;
    }
    udelay(I2C_DELAY);
    SDA_H;          //SDA=1;
    udelay(I2C_DELAY);
    SCL_H;          //SCL=1;
    udelay(I2C_DELAY);
    SCL_L;           //SCL=0;
    udelay(I2C_DELAY);
}
 
static unsigned char CH455_I2c_RdByte(void)		//读一个字节数据
{    
    unsigned char i = 0;
	unsigned char bytedata  = 0;
    SDA_IN;     //将数据设置为输入模式
    udelay(I2C_DELAY);
    SDA_H;      //数据线拉高
    udelay(I2C_DELAY);
    SCL_L;
    udelay(I2C_DELAY);
    for(i = 0; i < 8; i++)      //读8位数据
    {
        SCL_H;
        udelay(I2C_DELAY);
        bytedata <<= 1;
        udelay(I2C_DELAY);
        bytedata = bytedata | (CH455_SDA_IN);
        udelay(I2C_DELAY);
        SCL_L;
        udelay(I2C_DELAY);
    }
    SDA_OUT;            //数据线设置回输出模式
    SDA_H;          //SDA=1;
    udelay(I2C_DELAY);
    SCL_H;          //SCL=1;
    udelay(I2C_DELAY);
    SCL_L;           //SCL=0;
    udelay(I2C_DELAY);
    return(bytedata);//返回数据
}
 
static unsigned char CH455_Write( UINT16 cmd )	//写命令
{
	CH455_I2c_Start();               //启动总线
   	CH455_I2c_WrByte(((UINT8)(cmd>>7)&CH455_I2C_MASK)|CH455_I2C_ADDR);
   	CH455_I2c_WrByte((UINT8)cmd);     //发送数据
  	CH455_I2c_Stop();                 //结束总线 
	return 1;
}
 
static unsigned char CH455_Read( void )		//读取按键
{
	//disable_irq(irq_num);
	UINT8 keycode;
   	CH455_I2c_Start();                //启动总线
   	//CH455_I2c_WrByte((UINT8)((((CH455_GET_KEY>>7)& CH455_I2C_MASK)|0x01)|CH455_I2C_ADDR));
	
	CH455_I2c_WrByte((UINT8)(CH455_GET_KEY>>7)&CH455_I2C_MASK|0x01|CH455_I2C_ADDR);
   	keycode=CH455_I2c_RdByte();      //读取数据
	CH455_I2c_Stop();                //结束总线
	//enable_irq(irq_num);
	return keycode;
}
int ch455_flage = 0;
static irqreturn_t key_ch455_handler(int irq, void *dev_instance)
{
	int handled = 0;
	ch455_flage = 1;	
	return IRQ_RETVAL(handled);
}

static unsigned char init_ch455(void)
{
	int result;
	int retval = -ENODEV;
	/* Allocating GPIOs and setting direction */
	//iic
	result = gpio_request(SCL, "SCL");//usr1
	if (result != 0)
	printk("gpio_request(SCL) failed!\n");

	result = gpio_request(SDA, "SDA");//usr1
	if (result != 0)
	printk("gpio_request(SDA) failed!\n");

	result = gpio_request(RST, "RST");//usr1
	if (result != 0)
	printk("gpio_request(RST) failed!\n");

	result = gpio_request(INT, "INT");//usr1
	if (result != 0)
	printk("gpio_request(INT) failed!\n");

	
    SCL_OUT;
    SDA_OUT;
    //RST_OUT;
	//SDA_IN;
	//RST_IN;
    INT_IN;
	irq_num = gpio_to_irq(INT);
	retval = request_irq(irq_num, key_ch455_handler, 0, "irq_ch455", NULL);
	if (retval) {
		printk("Error requesting IRQ\n");
	}
	irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_FALLING);
	
    return (1);
}

static int ch455iic_drv_open(struct inode *inode, struct file *file)
{
	unsigned char flag = 0;
	flag = init_ch455();
	mdelay(100);
	if(flag == 0)
    {
		printk("uable to open device!\n");
		return -1;
    }
	printk("open ch455 driver\n");
	return 0;
}

ssize_t ch455iic_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	//printk("read driver\n");
	//RxBuf[0] = CH455_Read();
	//printk("read driver %02x\n",RxBuf[0]);
	//printk("ch455_flage %02x\n",ch455_flage);
	//printk("CH455_SDA_IN: %d\n",CH455_SDA_IN);
	//printk("CH455_RST_IN: %d\n",CH455_RST_IN);
	//printk("CH455_INT_IN: %d\n",CH455_INT_IN);
	if(ch455_flage)
	{
		RxBuf[0] = CH455_Read();
		ch455_flage = 0;
	}else{
		RxBuf[0] = 0xff;
	}
	if(copy_to_user(buf, RxBuf, size))
	{
		printk("read error!\n");
		return -EFAULT;	
	}else{
		 
		 //size = 1;
		//SPI_Read_Buf(RD_RX_PLOAD, RxBuf,size) ;
		return size;
	}
	return STATUS_SUCCESS;
}

void gpio_test(const char *TxBuf, const char size)
{
	switch(TxBuf[0])
	{
		case 'C':
		if(TxBuf[1])
		{
			printk("SCL_H!\n");
			SCL_H;
		}else{
			SCL_L;
			printk("SCL_L\n");
		}
		break;
		case 'D':
		if(TxBuf[1])
		{
			SDA_H;
		}else{
			SDA_L;
		}
		break;
		case 'R':
		if(TxBuf[1])
		{
			RST_H;
		}else{
			RST_L;
		}
		break;
		case 'N':
		if(TxBuf[1])
		{
			INT_H;
		}else{
			INT_L;
		}
		break;
		default:
		break;	
	}	
}
static ssize_t ch455iic_drv_write(struct file *file,const char __user *buf, size_t size, loff_t *ppos)
{
	UINT16 data = 0;
	if(copy_from_user( TxBuf, buf, size ))
	{		
		printk("send error!\n");
		return -EFAULT;
	}else{
       //gpio_test(TxBuf, size);
	   data = (TxBuf[1]<<8) | TxBuf[0];
	   CH455_Write(data);
	   //SPI_Write_Buf( TxBuf, size);
	   //printk("Write data :%02x\n",TxBuf[0]);
	}
    
	return STATUS_SUCCESS;
}

int ch455iic_drv_close(struct inode *inode, struct file *file)
{
	free_irq(irq_num, NULL);
	gpio_free(SCL);
	gpio_free(SDA);
	gpio_free(RST);
	gpio_free(INT);
	printk("close ch455 driver\n");
	return 0;
}

static long ch455iic_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd)
    {
		case 0x01 :  
		{
		   SCL_L;   //cmd mode
		   //printk("set cmd mode\n");
		}
		break;
		case 0x02 :
		{
			SCL_H;   //data mode
			//printk("set data mode\n");
		}
		break;
		case 0x03 :  
		{
		   SDA_L;   //cmd mode
		   //printk("set cmd mode\n");
		}
		break;
		case 0x04 :
		{
			SDA_H;   //data mode
			//printk("set data mode\n");
		}
		break;
		default : 
		 break;
    }
  return 0;
}
static const struct file_operations ch455iic_drv_fops = {
    .owner   =  THIS_MODULE,    
    .open    =  ch455iic_drv_open,     
	.read	 =	ch455iic_drv_read,
    .write	 =	ch455iic_drv_write,	
	.release =  ch455iic_drv_close,
	.unlocked_ioctl = ch455iic_drv_ioctl,
};
static int ch455iic_init(void)
{
	major = register_chrdev(0, "ch455iic_drv", &ch455iic_drv_fops);
	ch455iic_class = class_create(THIS_MODULE, "ch455iic_drv");
	//ch455iic_class_dev = class_device_create(ch455iic_class, NULL, MKDEV(major, 0), NULL, "ch455iic0.1"); /* /dev/ch455iic0.1 */
	ch455iic_class_dev = device_create(ch455iic_class, NULL, MKDEV(major, 0), NULL, "ch455iic0.1");
	printk("oledspi0.1 driver load ok!\n");
	return 0;
}
static void ch455iic_exit(void)
{
	unregister_chrdev(major, "ch455iic_drv");
	//class_device_unregister(oledspi_class_dev);
	device_unregister(ch455iic_class_dev);
	class_destroy(ch455iic_class);
	printk("ch455iic0.1 driver unload ok!\n");
 
}
module_init(ch455iic_init);
module_exit(ch455iic_exit);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("ch455iic0.1 driver for am335xd");
MODULE_LICENSE("GPL");

测试代码如下:

#include 
#include 
#include 
#include 
#include 

#define CH455_BIT_ENABLE	0x03		       // 开启/关闭位
#define CH455_BIT_SLEEP		0x04		      // 睡眠控制位

#define CH455_SYSOFF        0x0400                             // 关闭显示、关闭键盘
#define CH455_SYSON         (CH455_SYSOFF|CH455_BIT_ENABLE)   // 开启显示、键盘
#define CH455_SLEEPOFF	    CH455_SYSOFF	// 关闭睡眠
#define CH455_SLEEPON	    (CH455_SYSOFF|CH455_BIT_SLEEP)	// 开启睡眠

void ch455_write(unsigned char *Data,unsigned char len)
{
	write(fd,Data,len);
}

unsigned char ch455_read(void)
{
	unsigned char Data;
	read(fd, &Data, 1);
	return Data;
}

int main(int argc, char **argv)
{
	
	unsigned char test_val = 0;
	unsigned char test_cmd[2] = {0};
	unsigned char read_val;
	int ret;
	unsigned short Data;

	fd = open("/dev/ch455iic0.1", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
		return 0;
	}	
	Data = (unsigned short)CH455_SYSON;
	ch455_write((unsigned char*)(&Data),sizeof(Data));  //抗干扰,定时刷新CH455寄存器
	
	
	while (1)
	{
		//iotest();
		read_val = 0xff;
		read_val = ch455_read();
		if(read_val != 0xff)
		{
			printf("get key value:%02x\n",read_val);
			switch((read_val/8)%8)	
			{
				case 0:printf("0x00 key \n");break;
				case 1:printf("0x01 key \n");break;
				case 2:printf("0x02 key \n");break;
				case 3:printf("0x03 key \n");break;
				case 4:printf("0x04 key \n");break;
				case 5:printf("0x05 key \n");break;
				case 6:printf("0x06 key \n");break;
				default:
				break;
			}
		}
		usleep(10);		
	}
	
	return 0;
}

经过实际的测试,键盘键值获取无误。

你可能感兴趣的:(linux驱动开发)