为RaspberryPi编写OLED驱动程序

时间:2016-04-03 14:05:24

标签: linux-kernel raspberry-pi driver led

我正在为我的128x64 OLED(ssd1306)编写一个驱动程序模块,它将用于我的覆盆子pi。它适用于4线SPI模式。首先,我阅读了ssd1306的数据表并编写了一些基本功能。然后我将这个OLED连接到Arduino板,运行代码来测试它的正确性,我发现它工作得很好。 这是我的arduino代码:

//oled.ino
#define clearPin(a) digitalWrite(a, LOW)
#define setPin(a) digitalWrite(a, HIGH)

char* hello = "Hello World!";

// visible ascii chars
const unsigned char F6x8[][6] =
{
  //... 
};
const unsigned char F8X16[]=
{
  //...
};

// pin assignments
int CS = 53;
int DIN = 51;
int CLK = 52;
int DC = 3;
int RES = 2;

//***********************************
int writeCommand(byte command)
{
  int i;
  clearPin(CS);
  clearPin(DC);
  for(i = 0; i < 8; i++)
  {
    clearPin(CLK);
    delay(1);
    if((command >> (7-i)) & 0x1)
      setPin(DIN);
    else
      clearPin(DIN);
    delay(1);
    setPin(CLK);
    delay(1);
  }
  setPin(CS);
}

int writeData(byte data)
{
  int i;
  clearPin(CS);
  setPin(DC);
  for(i = 0; i < 8; i++)
  {
    clearPin(CLK);
    delay(1);
    if((data >> (7 - i)) & 0x1)
      setPin(DIN);
    else
      clearPin(DIN);
    delay(1);
    setPin(CLK);
    delay(1);
  }
  setPin(CS);
}

void setPostion(byte x, byte y)
{
    writeCommand(0xB0 | y);    // go to page y
    writeCommand(0x00 | (x & 0x0f)); // setPin lower column address
    writeCommand(0x10 | ((x >> 4) & 0x0f)); // setPin higher column address
}

void write6x8Char(byte x, byte y, char ch)
{
    setPostion(x, y);
    int i, c = ch - 32;
    for(i = 0; i < 6; i++)
    writeData(F6x8[c][i]);
}

void write6x8String(byte x, byte y, char* str)
{
    //...
}

void write8x16Char(byte x, byte y, char ch)
{
    setPostion(x, y);
    int i, c = ch - 32;
    for(i = 0; i < 8; i++)
        writeData(F8X16[c * 16 + i]);
    setPostion(x, y+1);
    for(i = 8; i < 16; i++)
        writeData(F8X16[c * 16 + i]);
}

void write8x16String(byte x, byte y, char* str)
{
    //...
}

void clearScreen()
{
    fill(0);
}

void fill(unsigned char bmp_data)
{
  unsigned char y,x;

  for(y = 0; y < 8; y++)
  {
    writeCommand(0xb0 + y);
    writeCommand(0x00);
    writeCommand(0x10);
    for(x = 0; x < 128; x++)
      writeData(bmp_data);
  }
} 

void setDisplayOn(byte on)
{
  writeCommand(0xAE | on);
}

//***********************************************

void initialize()
{
    setPin(CLK);
    clearPin(RES);
    delay(50);
    setPin(RES);
    setDisplayOn(1);
    fill(0x00);
}

void setup()
{
  pinMode(CS, OUTPUT);
  pinMode(DIN, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DC, OUTPUT);
  pinMode(RES, OUTPUT);
  initialize();
}


void loop()
{
  write8x16String(0, 0, hello);
}

所以我将它们直接移动到我的驱动程序模块文件中,几乎没有必要的更改。这是我的oled.c文件和Makefile:

//oled.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define DRIVER_NAME "OLED"
#define DEVICE_NAME "OLED"

#define clearGPIO(a) gpio_set_value(a, 0)
#define setGPIO(a) gpio_set_value(a, 1)

const int DIN = 5;
const int CLK = 6;
const int CS = 13;
const int DC = 19;
const int RES = 26;

const unsigned char F6x8[][6] =
{
    //...
};
const unsigned char F8X16[]=
{
    //...
};

//*****************************************
void delay(int ms)
{
    int i, j;
    for(i = 0; i < ms; i++)
    {
        j = 100000;
        while(j--);
    }
}
//...Same functions with the arduino program... 

//*****************************************

static dev_t oled_devno;
static struct class *oled_class;
static struct cdev oled_dev;
static int flag = 0;

//open
static int oled_open(struct inode *inode, struct file *filp)
{
    int err;
    printk(KERN_INFO"Initializing GPIOs...\n");
    err = gpio_request_one(CS, GPIOF_OUT_INIT_LOW, "CS");
    if(err){
        printk(KERN_ERR"[%s %d]:Request led gpio failed\n", __func__, __LINE__);
        gpio_free(CS);
    }
    err = gpio_request_one(DC, GPIOF_OUT_INIT_LOW, "DC");
    if(err){
        printk(KERN_ERR"[%s %d]:Request led gpio failed\n", __func__, __LINE__);
        gpio_free(DC);
    }
    err = gpio_request_one(RES, GPIOF_OUT_INIT_LOW, "RES");
    if(err){
        printk(KERN_ERR"[%s %d]:Request led gpio failed\n", __func__, __LINE__);
        gpio_free(RES);
    }
    err = gpio_request_one(DIN, GPIOF_OUT_INIT_LOW, "DIN");
    if(err){
        printk(KERN_ERR"[%s %d]:Request led gpio failed\n", __func__, __LINE__);
        gpio_free(DIN);
    }
    err = gpio_request_one(CLK, GPIOF_OUT_INIT_LOW, "CLK");
    if(err){
        printk(KERN_ERR"[%s %d]:Request led gpio failed\n", __func__, __LINE__);
        gpio_free(CLK);
    }
    flag = 1;
    printk(KERN_INFO"Initializing OLED...\n");
    oled_initialize();
    printk(KERN_INFO"OLED open\n");
    return 0;
}

//release
static int oled_release(struct inode *inode, struct file *filp)
{
    if(flag)
    {
        gpio_free(CS);
        gpio_free(DC);
        gpio_free(RES);
        gpio_free(DIN);
        gpio_free(CLK);
    }
    printk(KERN_INFO"OLED release\n");
    return 0;
}

//write
ssize_t oled_write(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    char* hello = "Hello driver";
    printk(KERN_INFO"I am writing data!\n");
    write8x16String(0, 0, hello);
    return 0;
}

static struct file_operations oled_fops = {
    .owner = THIS_MODULE,
    .open  = oled_open,
    .release  = oled_release,
    .write = oled_write,
};

static int __init oled_init(void)
{
    int err;

    printk(KERN_INFO"OLED Init \n");

    err = alloc_chrdev_region(&oled_devno,0,1,DRIVER_NAME);   
    if(err < 0){
        goto err;
    }
    cdev_init(&oled_dev,&oled_fops);

    err = cdev_add(&oled_dev,oled_devno,1);

    if(err < 0)
    {
        printk(KERN_ERR"[%s,%d]add cdev failed\n",__func__,__LINE__);
        goto FREE_DEVNO;
    }
    //
    oled_class = class_create(THIS_MODULE,DEVICE_NAME);
    if(IS_ERR(oled_class))
    {
        printk(KERN_ERR"[%s,%d]class create failed\n",__func__,__LINE__);
        goto DEV_FREE;
    }
    device_create(oled_class,NULL,oled_devno,NULL,DEVICE_NAME);

    return 0;
DEV_FREE:
    cdev_del(&oled_dev);
FREE_DEVNO:
    unregister_chrdev_region(oled_devno, 1);
err:
    return err;
}

static void oled_exit(void)
{
    if(flag)
    {
        gpio_free(CS);
        gpio_free(DC);
        gpio_free(RES);
        gpio_free(DIN);
        gpio_free(CLK);
    }
    device_destroy(oled_class,oled_devno);
    class_destroy(oled_class);
    cdev_del(&oled_dev);
    unregister_chrdev_region(oled_devno, 1);
    printk(KERN_INFO"OLED exit\n");
}

module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");

生成文件:

//Makefile
ifneq ($(KERNELRELEASE),)
    obj-m := oled.o
else
    KERNELDIR:=/home/rpi/linux/
    PWD := $(shell pwd)
    CROSS_COMPILE := arm-linux-gnueabihf-
default:
    $(MAKE) CROSS_COMPILE=$(CROSS_COMPILE) ARCH=arm -C $(KERNELDIR) M=$(PWD) modules
clean:
    rm -rf *.o *.ko *.mod.c *.order *.symvers   
endif

但是,当我编写一个打开设备并在其中写入字符的程序时,此OLED没有响应。并且没有错误记录到kern.log中。我真的不明白为什么他们在相同的控制功能下表现不同?我忽略了一些致命的东西吗?

0 个答案:

没有答案