IMX6ULL鼠标光标显示到oled

获取鼠标的坐标信息,并在oled上对应位置显示光标。按下鼠标左键,oled不清屏显示鼠标轨迹。按下鼠标右键,清屏并不显示鼠标轨迹。

1、首先完成oled的驱动,采用的是0.96寸的oled屏幕,通过spi控制。

通过pinctrl子系统定义引脚

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {

		pinctrl_ecspi3:  ecspi3grp{
			fsl,pins = <
				MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0 						/* CS */
				MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1				 /* SCLK */
				MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1 						/* MISO */
				MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1 						/* MOSI */
			>;
		};

		pinctrl_oled96_reset: oled96resetgrp {
			fsl,pins = <
				/* used for oled96 reset */
				MX6UL_PAD_UART3_TX_DATA__GPIO1_IO24           0x10B0
			>;
		};

		pinctrl_oled96_DC: oled96dcgrp {
			fsl,pins = <
				/* used for oled96 DC */
				MX6UL_PAD_UART3_RX_DATA__GPIO1_IO25 					0x10B0
			>;
		};

添加设备节点

&ecspi3 {
		fsl,spi-num-chipselects = <1>;
		cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;
		dc-gpio = <&gpio1 25 GPIO_ACTIVE_LOW>;
		reset-gpio = <&gpio1 24 GPIO_ACTIVE_LOW>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_ecspi3
														&pinctrl_oled96_reset
														&pinctrl_oled96_DC>;
		status = "okay";

		spidev: oled96spi@0 {
			compatible = "zhongjingyuan, oled96spi";
			spi-max-frequency = <8000000>;
			reg = <0>;
		};
	};

编写oled设备驱动文件

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/spi/spi.h>



/***********************************
 * 版本:v1.0
 * 描述:SSD1306 SPI驱动程序
 * **********************************/

#define oled96_CNT  1
#define oled96_NAME "oled96spi"

#define OLED_CMD 0
#define OLED_DATA 1

static u8 OLED_GRAM[144][8];


/* leddev 设备结构体 */
struct oled96_dev{
    dev_t devid;                                            /* 设备号 */
    struct cdev cdev;                                   /* cdev */
    struct class *class;                                /* 类 */
    struct device *device;                          /* 设备 */
    int major;                                                  /* 主设备号 */ 
    void *private_data;                              /* 私有数据 */
    struct device_node *nd;               		/* 设备节点 */
    int cs_gpio;                                              /* 片选GPIO编号*/
    int dc_gpio;                                              /* 命令/数据选择GPIO编号*/
	int reset_gpio;											/* 片选所使用的GPIO编号		*/
};

struct oled96_dev oled96dev;

 /* 传统匹配方式 ID 列表 */
static const struct spi_device_id oled96_id[] = {
    {"zhongjingyuan, oled96spi", 0}, 
    {}
};
 
/* 设备树匹配列表 */
static const struct of_device_id oled96_of_match[] = {
    { .compatible = "zhongjingyuan, oled96spi" },
    { /* Sentinel */ }
};


static int oled96_write_regs(struct oled96_dev *dev, u8 *buf, u8 len)
{
	int ret;

	unsigned char txdata[len];
	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;

	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
	gpio_set_value(dev->cs_gpio, 0);	/* 片选拉低 */ 	

	t->tx_buf = buf;		
	t->len = len;				
	spi_message_init(&m);		
	spi_message_add_tail(t, &m);
	ret = spi_sync(spi, &m);

	kfree(t);				
	gpio_set_value(dev->cs_gpio, 1);/* 片选拉高,释放oled96 */ 
	return ret;
}

static void oled96_write_onereg(struct oled96_dev *dev, u8 value, u8 cmd)
{
	u8 buf = value;
	if(cmd)
		gpio_set_value(dev->dc_gpio, 1);
	else 
		gpio_set_value(dev->dc_gpio, 0);
	oled96_write_regs(dev, &buf, 1);
	gpio_set_value(dev->dc_gpio, 1);
}

static void OLED_Refresh(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
		oled96_write_onereg(&oled96dev, 0xb0+i,OLED_CMD); //设置行起始地址
		oled96_write_onereg(&oled96dev, 0x00,OLED_CMD);   //设置低列起始地址
		oled96_write_onereg(&oled96dev, 0x10,OLED_CMD);   //设置高列起始地址
		for(n=0;n<128;n++)
		  oled96_write_onereg(&oled96dev,OLED_GRAM[n][i],OLED_DATA);
	}
}

static void OLED_Clear(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
	{
		for(n=0;n<128;n++)
			 {
			  OLED_GRAM[n][i]=0;//清除所有数据
			 }
   }
	OLED_Refresh();//更新显示
}

 //画点 
 //x:0~127
 //y:0~63
static void OLED_DrawPoint(u8 x,u8 y)
 {
	 u8 i,m,n;
	 i=y/8;
	 m=y%8;
	 n=1<<m;
	 OLED_GRAM[x][i]|=n;
 }

static void OLED_DrawCircle(u8 x, u8 y, u8 r)
{
	int a, b,num;
	a = 0;
	b = r;
	while(2 * b * b >= r * r)		
	{
		OLED_DrawPoint(x + a, y - b);
		OLED_DrawPoint(x - a, y - b);
		OLED_DrawPoint(x - a, y + b);
		OLED_DrawPoint(x + a, y + b);
  
		OLED_DrawPoint(x + b, y + a);
		OLED_DrawPoint(x + b, y - a);
		OLED_DrawPoint(x - b, y - a);
		OLED_DrawPoint(x - b, y + a);
		 
		a++;
		num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
		if(num > 0)
		{
			b--;
			a--;
		}
	}
}




//配置写入数据的起始位置
static void OLED_WR_BP(u8 x,u8 y)
{
	oled96_write_onereg(&oled96dev, 0xb0+y,OLED_CMD);//设置行起始地址
	oled96_write_onereg(&oled96dev, ((x&0xf0)>>4)|0x10,OLED_CMD);//设置低列起始地址
	oled96_write_onereg(&oled96dev, (x&0x0f)|0x01,OLED_CMD);//设置高列起始地址
}

static void OLED_ShowPicture(u8 x0,u8 y0,u8 x1,u8 y1,u8 BMP[])
{
	u32 j=0;
	u8 x=0,y=0;
	if(y%8==0)y=0;
	else y+=1;
	for(y=y0;y<y1;y++)
	{
		OLED_WR_BP(x0,y);
		for(x=x0;x<x1;x++)
		{
			oled96_write_onereg(&oled96dev, BMP[j],OLED_DATA);
			j++;
    	}
	}
}

/*
* @description : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件,file 结构体有个叫做 pr 似有 ate_data 的成员变量
* 一般在 open 的时候将 private_data 似有向设备结构体。
* @return : 0 成功;其他 失败
*/
static int oled96_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &oled96dev; /* 设置私有数据 */
	return 0;
}

 /*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t oled96_read(struct file *filp, char __user *buf,  size_t cnt, loff_t *off)
{
	return 0;
}

/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param – offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t oled96_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	struct mice_oled{
		char abs_Xpos;
		char abs_Ypos;
		char click_event;
		char clear;
	};

	//使用结构体write()传参失败
	//struct mice_oled *miceoled = (struct mice_oled *)buf;
	//databuf[0] x的绝对坐标,databuf[1] y的绝对坐标, databuf[2] 按键事件类型, databuf[3] 是否选择清屏写
	unsigned char databuf[4];
	long err = 0; 
	struct oled96_dev *dev = (struct oled96_dev *)filp->private_data;

	err = copy_from_user(databuf, buf, sizeof(cnt));

	if(err < 0){
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	if(databuf[3] == 0){
		OLED_Clear();
		OLED_DrawCircle(databuf[0], databuf[1], 2);
	}
	else{
		OLED_DrawCircle(databuf[0], databuf[1], 2);
	}
	OLED_Refresh();

	return 0;	
}


/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int oled96_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static const struct file_operations oled96_ops = {
	.owner = THIS_MODULE,
	.open = oled96_open,
	.read = oled96_read,
	.write = oled96_write,
	.release = oled96_release,
};

/* 
*初始化SSD1306寄存器
*移植自stm32例程的初始化,修改了函数调用
*/
void oled96_reginit(struct oled96_dev *dev)
{	
	gpio_set_value(dev->reset_gpio, 1);
	mdelay(100);
	gpio_set_value(dev->reset_gpio, 0);
	mdelay(200);
	gpio_set_value(dev->reset_gpio, 1);

	oled96_write_onereg(&oled96dev, 0xAE,OLED_CMD);//--turn off oled panel
	oled96_write_onereg(&oled96dev, 0x00,OLED_CMD);//---set low column address
	oled96_write_onereg(&oled96dev, 0x10,OLED_CMD);//---set high column address
	oled96_write_onereg(&oled96dev, 0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	oled96_write_onereg(&oled96dev, 0x81,OLED_CMD);//--set contrast control register
	oled96_write_onereg(&oled96dev, 0xCF,OLED_CMD);// Set SEG Output Current Brightness
	oled96_write_onereg(&oled96dev, 0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	oled96_write_onereg(&oled96dev, 0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	oled96_write_onereg(&oled96dev, 0xA6,OLED_CMD);//--set normal display
	oled96_write_onereg(&oled96dev, 0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	oled96_write_onereg(&oled96dev, 0x3f,OLED_CMD);//--1/64 duty
	oled96_write_onereg(&oled96dev, 0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	oled96_write_onereg(&oled96dev, 0x00,OLED_CMD);//-not offset
	oled96_write_onereg(&oled96dev, 0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	oled96_write_onereg(&oled96dev, 0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	oled96_write_onereg(&oled96dev, 0xD9,OLED_CMD);//--set pre-charge period
	oled96_write_onereg(&oled96dev, 0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	oled96_write_onereg(&oled96dev, 0xDA,OLED_CMD);//--set com pins hardware configuration
	oled96_write_onereg(&oled96dev, 0x12,OLED_CMD);
	oled96_write_onereg(&oled96dev, 0xDB,OLED_CMD);//--set vcomh
	oled96_write_onereg(&oled96dev, 0x40,OLED_CMD);//Set VCOM Deselect Level
	oled96_write_onereg(&oled96dev, 0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	oled96_write_onereg(&oled96dev, 0x02,OLED_CMD);//
	oled96_write_onereg(&oled96dev, 0x8D,OLED_CMD);//--set Charge Pump enable/disable
	oled96_write_onereg(&oled96dev, 0x14,OLED_CMD);//--set(0x10) disable
	oled96_write_onereg(&oled96dev, 0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	oled96_write_onereg(&oled96dev, 0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	oled96_write_onereg(&oled96dev, 0xAF,OLED_CMD);
	OLED_Clear();
}




/*
* @description : spi 驱动的 probe 函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - client : spi 设备
* @param - id : spi 设备 ID
*/
static int oled96_probe(struct spi_device *spi)
{
	int ret;
	/* 1、构建设备号 */
	if (oled96dev.major) {
		oled96dev.devid = MKDEV(oled96dev.major, 0);
		register_chrdev_region(oled96dev.devid, oled96_CNT, oled96_NAME);
	} else {
		alloc_chrdev_region(&oled96dev.devid, 0, oled96_CNT, oled96_NAME);
		oled96dev.major = MAJOR(oled96dev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&oled96dev.cdev, &oled96_ops);
	cdev_add(&oled96dev.cdev, oled96dev.devid, oled96_CNT);

	/* 3、创建类 */
	oled96dev.class = class_create(THIS_MODULE, oled96_NAME);
	if (IS_ERR(oled96dev.class)) {
		return PTR_ERR(oled96dev.class);
	}

	/* 4、创建设备 */
	oled96dev.device = device_create(oled96dev.class, NULL, oled96dev.devid, NULL, oled96_NAME);
	if (IS_ERR(oled96dev.device)) {
		return PTR_ERR(oled96dev.device);
	}

	/*获取spi设备节点*/
	oled96dev.nd = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000");
	if(oled96dev.nd == NULL){
		printk("\r\necspi3 node not find! \r\n");
		return -EINVAL;
	}

	/*获取GPIO信息*/
	oled96dev.cs_gpio = of_get_named_gpio(oled96dev.nd, "cs-gpio", 0);
	if(oled96dev.cs_gpio < 0){
		printk("can't get cs-gpio\r\n");
		return -EINVAL;
	} 
	oled96dev.reset_gpio = of_get_named_gpio(oled96dev.nd, "reset-gpio", 0);
	if(oled96dev.reset_gpio < 0) {
		printk("can't get reset-gpio");
		return -EINVAL;
	}
	oled96dev.dc_gpio = of_get_named_gpio(oled96dev.nd, "dc-gpio", 0);
	if(oled96dev.dc_gpio < 0){
		printk("can't get dc-gpio\r\n");
		return -EINVAL;
	} 

	//输出GPIO
	ret = gpio_direction_output(oled96dev.cs_gpio, 1);
	if(ret < 0){
		printk("can't set cs-gpio\r\n");
	}
	ret = gpio_direction_output(oled96dev.dc_gpio, 1);
	if(ret < 0){
		printk("can't set dc-gpio\r\n");
	}
	ret = gpio_direction_output(oled96dev.reset_gpio, 1);
	if(ret < 0) {
		printk("can't set res gpio!\r\n");
	}

	/*初始化 spi_device */
	spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
	spi_setup(spi);
	oled96dev.private_data = spi; /* 设置私有数据 */

	/* 初始化SSD1306 内部寄存器 */
	oled96_reginit(&oled96dev);
	OLED_DrawCircle(30,30,20);
	OLED_Refresh();
	printk("show a Circle\r\n");
	return 0;
}

/*
* @description : spi 驱动的 remove 函数,移除spi 驱动的时候此函数会执行
* @param - client : spi 设备
* @return : 0,成功;其他负值,失败
*/
static int oled96_remove(struct spi_device *spi)
{
	/* 删除设备 */
	cdev_del(&oled96dev.cdev);
	unregister_chrdev_region(oled96dev.devid, oled96_CNT);

	/* 注销掉类和设备 */
	device_destroy(oled96dev.class, oled96dev.devid);
	class_destroy(oled96dev.class);
	return 0;
}


/* SPI 驱动结构体 */
static struct spi_driver oled96_driver = {
	.probe = oled96_probe,
	.remove = oled96_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "oled96spi",
		   	.of_match_table = oled96_of_match, 
			},
	.id_table = oled96_id,
};

/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init oled96_init(void)
{
    return spi_register_driver(&oled96_driver);
}

/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit oled96_exit(void)
{
    spi_unregister_driver(&oled96_driver);
}

module_init(oled96_init);
module_exit(oled96_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cr");



2、usb鼠标对应驱动在linux内核中已经支持,通过编译内核选项选择是否编译。

linux中键盘鼠标等输入设备通过input子系统实现设备驱动,驱动程序完成向系统报告输入事件,input子系统完成了文件操作接口。进入/dev/input目录下,查看新增的文件。

mouse_fd = open(DEV_MOUSE, O_RDONLY);

read(mouse_fd, data, sizeof(data));
data 数组的第0个字节:bit 0、1、2、3、4分别代表左、右、中、SIDE、EXTRA键的按下情况;  
data 数组的第1个字节:表示鼠标的相对水平位移;  
data 数组的第2个字节:表示鼠标的相对垂直位移;  
data 数组的第3个字节:REL_WHEEL位移。  

data 数组的第4个字节:中间滚轮事件。   

/* 调用oledspi驱动,显示鼠标方位
使用结构体write()传参失败*/

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#define DEV_MOUSE "/dev/input/mouse0"
#define DEV_OLEDSPI "/dev/oled96spi"
#define EVENT_MOUSEI "dev/input/event1"

struct mice_oled{
    char abs_Xpos;
    char abs_Ypos;
    char click_event;
    char clear;
};



int main(void)
{
    int mouse_fd,oled96spi_fd;
    int ret,i;
    signed char rel_position_pre[3];
    signed char rel_position_now[3];

    //1 x绝对位置;2 y绝对位置;3 按键事件; 4 清屏命令
    signed char miceoled_dev[4] = {64, 32, 0, 0};

    struct mice_oled miceoled;    
    miceoled.abs_Xpos = 64;
    miceoled.abs_Ypos = 32;

    mouse_fd = open(DEV_MOUSE, O_RDONLY);
    if(mouse_fd < 0){
        perror("mouse file open failed");
        _exit(-1);
    }

    oled96spi_fd = open(DEV_OLEDSPI, O_RDWR);
    if(oled96spi_fd < 0){
        close(mouse_fd);
        perror("oled file open failed");
        _exit(-1);
    }

    while(1)
    {
        for(i=0; i<sizeof(rel_position_now); i++){
            rel_position_pre[i] = rel_position_now[i];
        }

        //读取mouse事件数据
        ret = read(mouse_fd, rel_position_now, sizeof(rel_position_now));
        if(-1 == ret ){
            perror("read file is failed");
            close(mouse_fd);
            close(oled96spi_fd);
            _exit(-1);
        }
        miceoled_dev[2] = rel_position_now[0];


        /*如果相对位置数据有变化,就打印出来
        并且在oled上刷新显示出来 */
        for(i=0; i<sizeof(rel_position_now); i++){
            if(rel_position_pre[i] != rel_position_now[i]){

                //确定x的绝对位置
                if(rel_position_now[1]<0){
                    miceoled_dev[0] = miceoled_dev[0] + rel_position_now[1]/4;
                    if(miceoled_dev[0] <= 1){
                        //printf("x less 0\r\n");
                        miceoled_dev[0] = 2;
                    }
                }
                if(rel_position_now[1]>0){
                    miceoled_dev[0] = miceoled_dev[0] + rel_position_now[1]/5;
                    if(miceoled_dev[0] < 0){
                        //printf("x exceed 127\r\n");
                        miceoled_dev[0] = 127;
                    }
                }

                //确定y的绝对位置
                miceoled_dev[1] = miceoled_dev[1] - rel_position_now[2]/4;
                if(miceoled_dev[1] <= 2){
                    //printf("y less 0\r\n");
                    miceoled_dev[1] = 2;
                    }
                else if (miceoled_dev[1] >= 63){
                    //printf("y exceed 63\r\n");
                    miceoled_dev[1] = 63;   
                }

                //判断鼠标是否按下
                if((miceoled_dev[2]&15) == 10){
                    miceoled_dev[3] = 0;
                }
                else if((miceoled_dev[2]&15) == 9){ 
                    miceoled_dev[3] = 1;
                }
                

                ret = write(oled96spi_fd, miceoled_dev, sizeof(miceoled_dev));
	            if(ret < 0){
		            printf("oled Control Failed!\r\n");
		            close(mouse_fd);
                    close(oled96spi_fd);
		            return -1;
	            }
                printf("x is: %d, y is: %d\r\n", miceoled_dev[0], miceoled_dev[1]);
                //printf("rel_x is: %d, rel_y is: %d\r\n", rel_position_now[1], rel_position_now[2]);
                printf("click value: %d\r\n",miceoled_dev[2]&15);
                break;
            }
        }   
    }
    close(mouse_fd);
    close(oled96spi_fd);
    return 0;
}

 注意的问题,在交叉编译器中,char类型定义的是默认无符号型的char类型,使用signed char定义有符号类型

猜你喜欢

转载自blog.csdn.net/qq_62426375/article/details/123113906