linux下的oled驱动gpio模拟spi(基于AM335X)

使用的OLED型号为:HGS256642

它的应用电路如下

linux下的oled驱动gpio模拟spi(基于AM335X)

属于四线SPI通信方式,但是它的MISO端口没有被接出来,所以spi的读取是不用实现的,但是又多出来一条线(RS),这个端口的作用是为OLED进行写命令或者写数据的控制。

时序如下所示:

linux下的oled驱动gpio模拟spi(基于AM335X)

spi控制时序如下:

linux下的oled驱动gpio模拟spi(基于AM335X)

由于基于的linux版本为3.2.0,并且这个驱动非常简单,所以使用独立的模块式的驱动编写方法,而没有使用linux提供的总线设备驱动框架的驱动编写方式,代码如下(其中包含了gpio的测试代码):

//oledspi.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <mach/gpio.h>
#include <plat/mux.h>
#include <linux/gpio.h>

#define  STATUS_SUCCESS	0
#define  STATUS_FAILURE -1

static struct class *oledspi_class;
static struct device	*oledspi_class_dev;
static int major;
static unsigned char TxBuf[24] = {0};
//spi
#define CSN            GPIO_TO_PIN(0,9)
#define CSN_OUT        gpio_direction_output(CSN, 1)
#define CSN_L          gpio_set_value(CSN, 0)
#define CSN_H          gpio_set_value(CSN, 1)

#define RS             GPIO_TO_PIN(0,8)
#define RS_OUT        gpio_direction_output(RS, 1)
#define RS_L  		   gpio_set_value(RS, 0)
#define RS_H  		   gpio_set_value(RS, 1)

#define SCK            GPIO_TO_PIN(2,17)
#define SCK_OUT       gpio_direction_output(SCK, 1);
#define SCK_L          gpio_set_value(SCK, 0) 
#define SCK_H          gpio_set_value(SCK, 1)


#define MOSI      	   GPIO_TO_PIN(2,16)
#define MOSI_OUT       gpio_direction_output(MOSI, 1)
#define MOSI_L  	   gpio_set_value(MOSI, 0) 
#define MOSI_H  	   gpio_set_value(MOSI, 1) 

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

static unsigned char SPI_RW(unsigned char tmp)
{
    unsigned char bit_ctr;

    for(bit_ctr=0 ;bit_ctr<8 ;bit_ctr++) // output 8-bit
    {
		SCK_L;
        if(tmp & 0x80)         // output 'tmp', MSB to MOSI
            MOSI_H;
        else
            MOSI_L;

		tmp = tmp<<1;           // shift next bit into MSB..
        ndelay(1);
        SCK_H;                   // ..then set SCK low again
        ndelay(1);
    }
    return(tmp);                    // return read tmp 
}

static unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char uchars)
{
    unsigned char status,uint8_ctr;
    
    CSN_L;                            // Set CSN low, init SPI tranaction
    ndelay(60);
    status = SPI_RW(reg);               // Select register to write to and read status unsigned char
    
    for(uint8_ctr=0;uint8_ctr<uchars;uint8_ctr++)
    {
        pBuf[uint8_ctr] = SPI_RW(0);    
        ndelay(20);
    }
    
    CSN_H;
    ndelay(60);
    
    return(status);                  
}

static unsigned char SPI_Write_Buf( unsigned char const *pBuf, unsigned char uchars)
{
    CSN_L;           
        SPI_RW(*pBuf);
	RS_H;
    CSN_H; 
    return(0);  
}

static unsigned char init_oled(void)
{
	int result;
	/* Allocating GPIOs and setting direction */
	//spi
	result = gpio_request(CSN, "CSN");//usr1
	if (result != 0)
	printk("gpio_request(CSN) failed!\n");

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

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

	result = gpio_request(RS, "RS");//usr1
	if (result != 0)
	printk("gpio_request(RS) failed!\n");
	
    RS_OUT;
    CSN_OUT;
    SCK_OUT;
    MOSI_OUT;
    return (1);
}

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

ssize_t oledspi_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{

	printk("read driver\n");
	if(copy_to_user(buf, TxBuf, size))
	{
		printk("read error!\n");
		return -EFAULT;	
	}else{
		//SPI_Read_Buf(RD_RX_PLOAD, RxBuf,size) ;
		return size;
	}
	return STATUS_SUCCESS;
}

static ssize_t gpio_test(const  unsigned char  *TxBuf, size_t size)
{
	printk("send %c,%d\n",TxBuf[0],TxBuf[1]);
	switch(TxBuf[0])
	{
		case 's':
			if(TxBuf[1])
			{
				CSN_H;
			}else{
				CSN_L;
			}
		break;
		case 'r':
			if(TxBuf[1])
			{
				RS_H;
			}else{
				RS_L;
			}
		break;
		case 'c':
			if(TxBuf[1])
			{
				SCK_H;
			}else{
				SCK_L;
			}
		break;
		case 'm':
			if(TxBuf[1])
			{
				MOSI_H;
			}else{
				MOSI_L;
			}
		break;
		
		//iic
		case 'C':
			if(TxBuf[1])
			{
				SCKIIC_H;
			}else{
				SCKIIC_L;
			}
		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;
	}
	return STATUS_SUCCESS;
}


static ssize_t oledspi_drv_write(struct file *file,const char __user *buf, size_t size, loff_t *ppos)
{
	if(copy_from_user( TxBuf, buf, size ))
	{		
		printk("send error!\n");
		return -EFAULT;
	}else{
       //gpio_test(TxBuf, size);
	   SPI_Write_Buf( TxBuf, size);
	   //printk("Write data :%02x\n",TxBuf[0]);
	}
    
	return STATUS_SUCCESS;
}

int oledspi_drv_close(struct inode *inode, struct file *file)
{

	printk("close driver\n");
	return 0;
}

static long oledspi_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd)
    {
		case 0x01 :  
		{
		   RS_L;   //cmd mode
		   //printk("set cmd mode\n");
		}
		break;
		case 0x00 :
		{
			RS_H;   //data mode
			//printk("set data mode\n");
		}
		break;
		default : 
		 break;

    }
  return 0;
}
static const struct file_operations oledspi_drv_fops = {
    .owner   =  THIS_MODULE,    
    .open    =  oledspi_drv_open,     
	.read	 =	oledspi_drv_read,
    .write	 =	oledspi_drv_write,	
	.release =  oledspi_drv_close,
	.unlocked_ioctl = oledspi_drv_ioctl,
};
static int oledspi_init(void)
{
	major = register_chrdev(0, "oledspi_drv", &oledspi_drv_fops);
	oledspi_class = class_create(THIS_MODULE, "oledspi_drv");
	//oledspi_class_dev = class_device_create(oledspi_class, NULL, MKDEV(major, 0), NULL, "oledspi0.1"); /* /dev/oledspi0.1 */
	oledspi_class_dev = device_create(oledspi_class, NULL, MKDEV(major, 0), NULL, "oledspi0.1");
	printk("oledspi0.1 driver load ok!\n");
	return 0;
}
static void oledspi_exit(void)
{
	unregister_chrdev(major, "oledspi_drv");
	//class_device_unregister(oledspi_class_dev);
	device_unregister(oledspi_class_dev);
	class_destroy(oledspi_class);
	printk("oledspi0.1 driver unload ok!\n");
 
}
module_init(oledspi_init);
module_exit(oledspi_exit);

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

驱动加载后测试波形如下:

linux下的oled驱动gpio模拟spi(基于AM335X)

由于模拟SPI的限制,它的波特率并不能像MCU提供的spi控制器那样做的很高,当前做到的稳定的最大波特率只能到400K左右,当然了,采取一些特殊的手段也可以达到最大2M,但是结果是不稳定。由于这个OLED自身的限制,它的spi速率也不能超过2M,经过实际测试,超过了就不能正常控制了。

经过编写应用层代码进行测试oled被成功点亮并控制。