linux:framebuffer驱动之ssd1363驱动和fbtft驱动修改

环境:ubuntu18.04虚拟机和imx6q开发平台

目录

  • 前言
  • 1.添加ssd1363驱动到fbtft
  • 2.亮度调节


前言

手里有一块ssd1363芯片的屏幕,查了很多关于framebuffer驱动的资料,在linux里面有个fbtft驱动部分专门针对这种tftf屏幕做的驱动,于是照着fbtft里面的驱动添加ssd1363并编写应用程序修改亮度值


1.添加ssd1363驱动到fbtft

路径如下:

/drivers/staging/fbtft

在这里直接复制fb_ssd1306.c一份文件为fb_ssd1363.c,修改内容如下

#include 
#include 
#include 
#include 
#include 
#include "fbtft.h"
#define DRVNAME		"fb_ssd1363"
#define WIDTH		320
#define HEIGHT		160
#define CMD 		0
#define DATA 		1
#define DEFAULT_GAMMA	"f"
static uint8_t GRAY_LEVEL = 10;	//0-15
static uint8_t OLED_GRAM[160][160]={0};

static int init_display(struct fbtft_par *par)
{
	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
	par->fbtftops.reset(par);
	/* Write CMD */
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xAE);
	write_reg(par, 0xFD);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x12);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xA0);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x22);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x00);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xA1);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x00);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xA2);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x00);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xA6);
	write_reg(par, 0xAD);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x80);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xB1);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x92);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xB3);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x70);
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xB4);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x11);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0xC0);	
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xB6);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0xC8);	
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xB9);
	write_reg(par, 0xBA);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x02);		
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xBB);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x07);	
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xBE);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x07);	
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xC1);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x50);	
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xCA);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x9F);	
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0xAF);
	//开启显示	 
	return 0;
}
//设置每一页起始地址0-8
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
	fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
		"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
	/* Set Lower Column Start Address for Page Addressing Mode */
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0x15);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x00);	
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x4F);	
	/* Set Higher Column Start Address for Page Addressing Mode */
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0x75);
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x00);	
	gpio_set_value(par->gpio.dc, DATA);
	write_reg(par, 0x9F);	
}
static int blank(struct fbtft_par *par, bool on)
{
	fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
		__func__, on ? "true" : "false");
	if (on)
		write_reg(par, 0xAE);
	else
		write_reg(par, 0xAF);
	return 0;
}
/* Gamma is used to control Contrast */
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
	/* apply mask */
	curves[0] &= 0x0F;
	if(curves[0]<16)GRAY_LEVEL = curves[0];
	printk("\r\nset_gamma:%d\r\n",curves[0]);
	return 0;
}
//画点 
//x:0~319
//y:0~159
//gray:light
void OLED_DrawPoint(u16 x,u16 y,u8 gray)
{
	u8 pos,temp=0;
	if(x>319||y>159)return;//超出范围了
	pos=x/2;
	if((pos%2)==0)
	{
		pos+=1;
	}
	else
	{
		pos-=1;
	}
	temp = OLED_GRAM[y][pos];
	if((x%2)==0)
	{
		temp&=0xf0;
		temp|=gray;
	}
	else
	{
		temp&=0x0f;
		temp|=(gray<<4);	
	}
	OLED_GRAM[y][pos]=temp;
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
	u16 *vmem16 = (u16 *)par->info->screen_base;
	u8 *buf = par->txbuf.buf;
	int x, y, i;
	int ret = 0;
	fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
	//Data packages
	for (x = 0; x < par->info->var.xres; x++) {
		for (y = 0; y < par->info->var.yres/8; y++) {
			for (i = 0; i < 8; i++)
				{
					if(vmem16[(y*8+i)*par->info->var.xres+x])
					{
						OLED_DrawPoint(x,y*8+i,GRAY_LEVEL);
					}
					else
					{
						OLED_DrawPoint(x,y*8+i,0);
						/* code */
					}					
				}
		}
	}
	gpio_set_value(par->gpio.dc, CMD);
	write_reg(par, 0x5C);
	/* Write data */
	gpio_set_value(par->gpio.dc, DATA);
	ret = par->fbtftops.write(par, OLED_GRAM,sizeof(OLED_GRAM));
	if (ret < 0)
		dev_err(par->info->device, "write failed and returned: %d\n",
			ret);
	return ret;
}
static struct fbtft_display display = {
	.regwidth = 8,
	.width = WIDTH,
	.height = HEIGHT,
	.gamma_num = 1,
	.gamma_len = 1,
	.gamma = DEFAULT_GAMMA,
	.fbtftops = {
		.write_vmem = write_vmem,
		.init_display = init_display,
		.set_addr_win = set_addr_win,
		.blank = blank,
		.set_gamma = set_gamma,
	},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1363", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ssd1363");
MODULE_ALIAS("platform:ssd1363");
MODULE_DESCRIPTION("ssd1363 OLED Driver");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

另外还要修改Kconfig和Makefile文件

config FB_TFT_SSD1363
	tristate "FB driver for the SSD1363 OLED Controller"
	depends on FB_TFT
	help
	  Framebuffer support for SSD1363
obj-$(CONFIG_FB_TFT_SSD1363)     += fb_ssd1363.o

添加完成后就可以在make menuconfig中配置fbtft了
linux:framebuffer驱动之ssd1363驱动和fbtft驱动修改_第1张图片
linux:framebuffer驱动之ssd1363驱动和fbtft驱动修改_第2张图片
接下来还要修改设备树,我这里用的是ecspi2

&ecspi2 {
	fsl,spi-num-chipselects = <1>;
	cs-gpios = <&gpio2 27 0>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi2>;
	status = "okay";

	ssd1363@0{ 
		compatible = "solomon,ssd1363";
		dc-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>;
		buswidth = <8>;
 		spi-max-frequency = <10000000>;
 		reg = <0>;
 	};
};

这里离成功更近一步了,编译内核和设备树,通过tftp传输到板子上,nfs挂载文件系统,系统启动后会有如下日志打印

set_gamma:15
Console: switching to colour frame buffer device 40x20
graphics fb0: fb_ssd1363 frame buffer, 320x160, 100 KiB video memory, 4 KiB DMA buffer memory, fps=20, spi1.0 at 10 MHz

如果在uboot的bootargs中设置了console=tty1的话,这是屏幕上就会显示了,虽然屏幕正常显示了,但是ssd1363是有灰度调节的,这里我准备把它当成屏幕亮度来调节。

2.亮度调节

亮度调节比较麻烦,因为要从应用层去修改驱动层,不能直接去修改驱动层的变量,要通过内核层来修改,于是开始研究framebuffer的整个初始化注册过程,发现注册过程大概如下
在fbtft-core.c文件中
fbtft采用fbtft_register_framebuffer函数注册fb驱动
这里调用ret = register_framebuffer(fb_info);函数完成注册
发现内核注册的时候fb_info传递给了内核,于是在内核里面去查看
应用层一般可以通过ioctl函数操作读取fb的信息,我就想能不能通过ioctl函数来操作驱动修改亮度值
在fbmem.c文件中

static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)

这个函数就是ioctl最终实际调用的函数
在fb.h中添加一个宏定义用来改变亮度值

#define FBIOSET_DISPLIGHT        0x4619

修改do_fb_ioctl内容

1.添加头文件
#include "../drivers/staging/fbtft/fbtft.h"
2.添加指针初始化
struct fbtft_par *par = info->par;
unsigned long gamma=arg;
3.添加 FBIOSET_DISPLIGHT 的 case
case FBIOSET_DISPLIGHT:
		if (!lock_fb_info(info))
			return -ENODEV;
		ret = par->fbtftops.set_gamma(par, &gamma);  
		unlock_fb_info(info);
break;

这样一来就可以通过应用层调用set_gamma函数了,在fb_ssd1363.c中写好了set_gamma函数,这个本来是修改伽马值的,被我用来修改亮度用,算是投机取巧吧

static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
	fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
	/* apply mask */
	curves[0] &= 0x0F;
	if(curves[0]<16)GRAY_LEVEL = curves[0];
	printk("\r\nset_gamma:%d\r\n",curves[0]);
	return 0;
}

接下来编写应用程序如下

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FBIOSET_DISPLIGHT        0x4619

static char *fbp = 0;
int main ( int argc, char *argv[] )
{
	int fbfd = 0;
    unsigned long light ;
    printf("argc:%d argv0:%s argv1:%s\n",argc,argv[0],argv[1]);
    if(argc != 2){
		printf("Error Usage!\r\n");
		return -1;
	}
    light = atoi(argv[1]);
    printf("light:%d\r\n",light);
	//打开显示设备
	fbfd = open("/dev/fb0", O_RDWR);
		if (ioctl(fbfd, FBIOSET_DISPLIGHT, light))
	{
		printf("Error: reading light information.\n");
		exit(1);
	}
	close(fbfd);
	return 0;
}

然后编译生成文件

arm-linux-gnueabihf-gcc ssd1363_app.c -o ssd1363_app

把文件放到挂载的nfs系统里面,运行如下

/app # ./ssd1363_app 10
argc:2 argv0:./ssd1363_app argv1:
set_gamma:10
10
light:10

屏幕亮度成功修改,大功告成,哈哈

你可能感兴趣的:(linux,linux,驱动开发,framebuffer)