环境:ubuntu18.04虚拟机和imx6q开发平台
手里有一块ssd1363芯片的屏幕,查了很多关于framebuffer驱动的资料,在linux里面有个fbtft驱动部分专门针对这种tftf屏幕做的驱动,于是照着fbtft里面的驱动添加ssd1363并编写应用程序修改亮度值
路径如下:
/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了
接下来还要修改设备树,我这里用的是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是有灰度调节的,这里我准备把它当成屏幕亮度来调节。
亮度调节比较麻烦,因为要从应用层去修改驱动层,不能直接去修改驱动层的变量,要通过内核层来修改,于是开始研究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
屏幕亮度成功修改,大功告成,哈哈