linux下的oled驱动gpio模拟spi(基于AM335X)
使用的OLED型号为:HGS256642
它的应用电路如下
属于四线SPI通信方式,但是它的MISO端口没有被接出来,所以spi的读取是不用实现的,但是又多出来一条线(RS),这个端口的作用是为OLED进行写命令或者写数据的控制。
时序如下所示:
spi控制时序如下:
由于基于的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");
驱动加载后测试波形如下:
由于模拟SPI的限制,它的波特率并不能像MCU提供的spi控制器那样做的很高,当前做到的稳定的最大波特率只能到400K左右,当然了,采取一些特殊的手段也可以达到最大2M,但是结果是不稳定。由于这个OLED自身的限制,它的spi速率也不能超过2M,经过实际测试,超过了就不能正常控制了。
经过编写应用层代码进行测试oled被成功点亮并控制。