【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:[email protected]】
由于项目需要一款小的3*2的矩阵键盘,所以选用一款数码管驱动及键盘控制芯片 CH455 ,该芯片其实实际能支持到最大7*8的矩阵键盘以及4个数码管,在这里我们只是用到了他的键盘驱动功能,该芯片的使用框架图如下:
在项目实际应用中,芯片的实际电路设计原理如下:
在驱动软件的设计上,我们采用io模拟iic通信,外加中断的方式搞定该芯片。通过芯片的datasheet,我们可以清除的看到该芯片的通信时序如下:
根据以上的时序,我们在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;
}
经过实际的测试,键盘键值获取无误。