获取鼠标的坐标信息,并在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定义有符号类型