2

Linux帧缓冲注册OLED驱动

 1 year ago
source link: https://blog.51cto.com/u_15688123/5873857
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Linux帧缓冲注册OLED驱动

1.帧缓冲Framebuff

     在 linux 系统中 LCD 这类设备称为帧缓冲设备,英文 frameBuffer 设备。

       frameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。

       帧缓冲( framebuffer)是 Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。

       用户可以将 Framebuffer 看成是显示内存的一个映像, 将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。

       帧缓冲驱动是字符类设备的一种,主设备号为29,生成的设备节点为/dev/fb*。

       使用帧缓冲完成屏幕驱动注册,应用层只需调用open函数打开失败,再通过ioctl函数获取屏幕的参数信息,再调用mmap函数将屏幕显存地址映射到进程空间,接下来对地址的写入即是对屏幕的刷。

2.帧缓冲Framebuff应用编程

  •  帧缓冲应用层编程步骤
  1. 打开LCD设备open(“/dev/fb0”,2);
  2. 获取固定参数和可变参数ioctl;
  3. 将屏幕缓冲区映射到进程空间mmap;
  4. 实现屏幕最核心函数画点函数;

2.1 帧缓冲Framebuff设备节点

  通过帧缓冲完成屏幕驱动注册,会在/dev下生成设备节点,主设备号为29,注册的一个设备驱动为/dev/fb0,第二个为/dev/fb1,依此类推,最大可以注册32个设备。

2.2 固定参数

      FBIOGET_FSCREENINFO,固定参数结构体为struct fb_fix_screeninfo。在固定参数可获得的屏幕信息有:smem_len屏幕缓冲区大小、line_length一行的字节数。

#define FBIOGET_FSCREENINFO 0x4602 /*获取屏幕固定参数*/
/*固定参数结构体*/
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem 屏幕物理地址 */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem 屏幕缓冲区大小*/
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes 一行的字节数 */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
};

2.3 可变参数

      FBIOGET_VSCREENINFO,固定参数结构体为struct fb_fix_screeninfo。在固定参数可获得的屏幕信息有:屏幕宽度xres、屏幕高度yres、颜色位数bits_per_pixel。

#define FBIOGET_VSCREENINFO 0x4600 /*获取屏幕可变参数*/
/*可变参数结构体*/
struct fb_var_screeninfo {
__u32 xres; /* visible resolution屏幕宽度 */
__u32 yres; /*屏幕高度*/
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */

__u32 bits_per_pixel; /* guess what 颜色位数 */
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */

__u32 nonstd; /* != 0 Non standard pixel format */

__u32 activate; /* see FB_ACTIVATE_* */

__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */

__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */

/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};

2.4 将屏幕缓冲区映射到进空间

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
函数功能: 将文件映射到进程空间
形参: addr --映射的虚拟地址,一般填NULL,有系统自行分配
   length --要映射的空间大小
   prot --PROT_READ可读;PROT_WRITE可写
   flags --MAP_SHARED可读写,读写内容同步到文件;MAP_PRIVATE修改的内容不会同步到文件
   fd --文件描述符
   offset --一般填0,表示映射整个文件
返回值: 成功返回映射的地址
   失败返回-1


int munmap(void *addr, size_t length);
形参: addr --mamp函数返回值
   length --映射空间大小

2.5 帧缓冲获取固定参数和可变参数示例

int main()
{
/*1.打开设备*/
int fd=open("/dev/fb0", 2);
if(fd<0)
{
printf("打开设备失败\n");
}
/*2.获取固定参数*/
memset(&fb_fix,0, sizeof(fb_fix));
ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix);
printf("屏幕缓存大小:%d\n",fb_fix.smem_len);
printf("一行的字节数:%d\n",fb_fix.line_length);
/*3.获取屏幕可变参数*/
memset(&fb_var,0, sizeof(fb_var));
ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
printf("屏幕尺寸:%d*%d\n",fb_var.xres,fb_var.yres);
printf("颜色位数:%d\n",fb_var.bits_per_pixel);
/*4.将屏幕缓冲区映射到进程空间*/
lcd_p=mmap(NULL,fb_fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
if(lcd_p==(void *)-1)
{
printf("内存映射失败\n");
return 0;
}
memset(lcd_p,0xff,fb_fix.smem_len);//将屏幕清空为白色
//取消映射
munmap(lcd_p,fb_fix.smem_len);
return 0;
}
/*画点函数实现*/
static inline void LCD_DrawPoint(int x,int y,int c)
{
//获取要绘制的点的地址
unsigned int *p= (unsigned int *)(lcd_p+y*fb_fix.line_length+x*fb_var.bits_per_pixel/8);
*p=c;//写入颜色值
}

3.帧缓冲驱动编程

  帧缓冲驱动是属于字符类设备的一种,主设备号为29,生成的设备节点为/dev/fb*。实现帧缓冲驱动注册,只需要调用驱动注册函数register_framebuffer,驱动注册注销函数unregister_framebuffer。

  •  注册和注销驱动函数
#include <linux/fb.h>
int unregister_framebuffer(struct fb_info *fb_info);
int register_framebuffer(struct fb_info *fb_info);
  •  struct fb_info结构体

    struct fb_info结构体中需要关心的参数有:

       1. 屏幕固定参数结构体struct fb_fix_screeninfo fix、屏幕可变参数结构体struct fb_var_screeninfo var 为应用层提供屏幕信息。

       2.帧缓冲文件操作集合struct fb_ops *fbops,需要为应用层接口函数提供入口。

      3.屏幕的内核申请的虚拟地址char __iomem *screen_base,应用层mmap函数映射地址就是和该地址的连接桥梁。

struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* 可变参数 */
struct fb_fix_screeninfo fix; /* 固定参数 */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */

#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;

/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif

struct fb_ops *fbops;/*帧缓冲文件操作集合*/
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address虚拟地址 */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
  •  内核层申请物理地址dma_alloc_writecombine

 因为应用层是通过mmap内存映射方式将屏幕缓冲区映射到进程空间,因此驱动层需要调用dma_alloc_writecombine函数来实现分配屏幕的的物理缓冲区。

#include <linux/dma-mapping.h>
void *dma_alloc_writecombine(struct device *dev, size_t size,dma_addr_t *handle, gfp_t gfp)
函数功能: 内核层动态分配物理内存空间。
形参: dev --没有可直接填NULL
   size --要申请的空间大小
   dma_handle --申请的物理地址
   flag —GFP_KERNEL申请不到就阻塞
返回值: 成功返回申请成功的物理地址对应的虚拟地址
  •  内核层释放申请的物理空间dma_free_writecombine

     dma_free_writecombine函数来完成物理空间释放。

void dma_free_writecombine(struct device *dev, size_t size,void *cpu_addr, dma_addr_t handle)
形参:dev --没有可直接填NULL
   size --要申请的空间大小
   cpu_addr —dma_alloc_writecombine函数返回值
   handle --物理地址

3.1 OLED简介

       OLED,即有机发光二极管( Organic Light Emitting Diode)。 OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、 构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。

Linux帧缓冲注册OLED驱动_OLED屏幕

  本次选用OLED屏幕为0.96寸,驱动IC为SSD1306,驱动协议为SPI。分辨率为128*64;单色屏幕。采用页面寻址方式。

  • ​ 引脚说明

     GND 电源地

     VCC 电源正( 3~5.5V)

     D0 OLED 的 D0 脚,在 SPI 和 IIC 通信中为时钟管脚

     D1 OLED 的 D1 脚,在 SPI 和 IIC 通信中为数据管脚

     RES OLED 的 RES#脚,用来复位(低电平复位)

     DC OLED 的 D/C#E 脚, 数据和命令控制管脚

     CS OLED 的 CS#脚,也就是片选管脚

3.2 帧缓冲注册示例

硬件平台: tiny4412
开发平台: ubuntu18.04
交叉编译器: arm-linux-gcc
内核: linux3.5
OLED驱动IC: SSD1306
OLED驱动方式: SPI(采用SPI子系统实现)

  注册SPI子系统实现OLED屏幕驱动,OLED屏幕画点函数实现;通过帧缓冲驱动注册OLED驱动,在/dev下生成设备节点,实现应用层帧缓冲接口。

#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/mutex.h>
/***************OLED gpio初始化************
**D0 --时钟线SPI0_SCLK --GPB_0
**D1 --主机输出线SPI0_MOSI --GPB_3
**RES --复位脚 GPB_4
**DC --数据命令选择脚 GPB_5
**CS --片选 SPI0_CS --GPB_1
**
******************************************/
#define OLED_DAT 1//发送数据
#define OLED_CMD 0//发送命令
struct spi_device *oled_spi;
static unsigned int *GPB_CON=NULL;
static unsigned int *GPB_DAT=NULL;
#define OLED_RES(x) if(x){*GPB_DAT|=1<<4;}else{*GPB_DAT&=~(1<<4);} //时钟脚 //复位脚
#define OLED_DC(x) if(x){*GPB_DAT|=1<<5;}else{*GPB_DAT&=~(1<<5);} //时钟脚 //数据命令选择脚

void OLED_Clear(u8 data);
void OLED_ClearGram(void);
void OLED_RefreshGram(void);

void OLED_GPIO_Init(void)
{

GPB_CON=ioremap(0x11400040, 8);//将物理地址映射为虚拟地址
GPB_DAT=GPB_CON+1;
*GPB_CON&=0xff00ffff;
*GPB_CON|=0x00110000;//配置为输出模式
//上拉
OLED_RES(1);
}
/*******************发送一个字节函数***************
**形参:u8 dat -- 要发送数据
** u8 cmd --0发送数据,1发送命令
**
****************************************************/
void OLED_SendByte(u8 dat,u8 cmd)
{
if(cmd)
{
OLED_DC(1);//发送数据
}
else
{
OLED_DC(0);//发送命令
}
spi_write(oled_spi,&dat,1);//发送一个字节
}
/****************OLED初始化***************/
void OLED_Init(void)
{
OLED_GPIO_Init();//OLED GPIO初始化
//软件复位
OLED_RES(1);
mdelay(200);
OLED_RES(0);
mdelay(200);
OLED_RES(1);
mdelay(200);
//OLED初始化序列
OLED_SendByte(0xAE,OLED_CMD); /*进入睡眠模式*/
OLED_SendByte(0x00,OLED_CMD); /*set lower column address*/
OLED_SendByte(0x10,OLED_CMD); /*set higher column address*/
OLED_SendByte(0x40,OLED_CMD); /*set display start line*/
OLED_SendByte(0xB0,OLED_CMD); /*set page address*/
OLED_SendByte(0x81,OLED_CMD); /*设置对比度*/
OLED_SendByte(0xCF,OLED_CMD); /*128*/
OLED_SendByte(0xA1,OLED_CMD); /*set segment remap*/
OLED_SendByte(0xA6,OLED_CMD); /*normal / reverse*/
OLED_SendByte(0xA8,OLED_CMD); /*multiplex ratio*/
OLED_SendByte(0x3F,OLED_CMD); /*duty = 1/64*/
OLED_SendByte(0xC8,OLED_CMD); /*Com scan direction*/
OLED_SendByte(0xD3,OLED_CMD); /*set display offset*/
OLED_SendByte(0x00,OLED_CMD);
OLED_SendByte(0xD5,OLED_CMD); /*set osc division*/
OLED_SendByte(0x80,OLED_CMD);
OLED_SendByte(0xD9,OLED_CMD); /*set pre-charge period*/
OLED_SendByte(0Xf1,OLED_CMD);
OLED_SendByte(0xDA,OLED_CMD); /*set COM pins*/
OLED_SendByte(0x12,OLED_CMD);
OLED_SendByte(0xdb,OLED_CMD); /*set vcomh*/
OLED_SendByte(0x30,OLED_CMD);
OLED_SendByte(0x8d,OLED_CMD); /*set charge pump enable*/
OLED_SendByte(0x14,OLED_CMD);
OLED_SendByte(0xAF,OLED_CMD); /*恢复正常模式*/
OLED_ClearGram();//清空缓冲区
OLED_RefreshGram();//更新显示
}
/****************清屏函数***********
**形参:u8 data -- 0全灭
** -- 0xff全亮
*************************************/
void OLED_Clear(u8 data)
{
u8 i,j;
for(i=0;i<8;i++)
{
OLED_SendByte(0xb0+i,OLED_CMD);//设置页地址
OLED_SendByte(0x10,OLED_CMD);//设置列高地址
OLED_SendByte(0x0,OLED_CMD);//设置列低地址
for(j=0;j<128;j++)OLED_SendByte(data,OLED_DAT);//写满一列
}
}
/******************OLED设置光标*************
**形参:u8 x -- x坐标(0~127)
** u8 y -- y坐标(0~7)
**
********************************************/
void OLED_SetCursor(u8 x,u8 y)
{
OLED_SendByte(0xb0+y,OLED_CMD);//设置页地址
OLED_SendByte(0x10|((x>>4)&0xf),OLED_CMD);//设置列的高位地址
OLED_SendByte(0x00|(x&0xf),OLED_CMD);
}

static u8 OLED_GRAM[8][128];//定义屏幕缓冲区大小
/****************封装画点函数**************
**形参:u8 x -- x坐标0~127
** u8 y -- y坐标:0~63
** u8 c -- 1,亮 ,0灭
**假设:x,y (5,6),9
*******************************************/
void OLED_DrawPoint(u8 x,u8 y,u8 c)
{
u8 page=0;
page=y/8;//y坐标对应在哪一页
//y=12,y/8=1,y%8=12%8=1....4
y=y%8;//对应页上的哪一行6%8=0---6
if(c)OLED_GRAM[page][x]|=1<<y;//点亮对应的点阵
else OLED_GRAM[page][x]&=~(1<<y);//关闭对应的点阵
}
/**************更新数据到屏幕*****************/
void OLED_RefreshGram(void)
{
u8 i,j;
for(i=0;i<8;i++)
{
OLED_SendByte(0xb0+i,OLED_CMD);//设置页地址
OLED_SendByte(0x10,OLED_CMD);//设置列高地址
OLED_SendByte(0x0,OLED_CMD);//设置列低地址
for(j=0;j<128;j++)OLED_SendByte(OLED_GRAM[i][j],OLED_DAT);
}
}
/*******************清屏函数*****************/
void OLED_ClearGram(void)
{
memset(OLED_GRAM,0x0,sizeof(OLED_GRAM));//清除缓冲区
}

#define OLED_REFLASH 0X80
static int oled_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
{
int i,j;
int w,h;
w=info->var.xres;//屏幕宽
h=info->var.yres;
char *p=info->screen_base;
//printk("w=%d,h=%d\n",w,h);
switch(cmd)
{
case OLED_REFLASH://更新数据到屏幕
for(i=0;i<h;i++)
{
for(j=0;j<w;j++)
{
if(*p)
{
OLED_DrawPoint(j,i,1);
}
else
{
OLED_DrawPoint(j,i,0);
}
p++;
}
}
OLED_RefreshGram();//更新显示
break;
}

return 0;
}
/*帧缓冲文件操作集合*/
static struct fb_ops oled_fbops=
{
.fb_ioctl=oled_ioctl

};

static struct fb_info fb_info=
{
.var=
{
.xres=128,
.yres=64,
.bits_per_pixel=8,//8位表示一个像素点
},
.fix=
{
.id="ssd1306",
//.smem_start= //物理地址
.smem_len=128*64,//屏幕缓冲区大小
.line_length=128,//一行的字节数

},
.fbops=&oled_fbops,//文件操作集合
//screen_base 虚拟地址
};
static int oled_probe(struct spi_device *spi)
{
printk("资源匹配成功\n");
printk("name=%s,频率=%d,模式=%d,数据位数:%d\n",spi->modalias,spi->max_speed_hz,spi->mode,spi->bits_per_word);
spi->max_speed_hz=20*1000*1000;//工作频率为20Mhz
spi->bits_per_word=8;//数据8位
spi_setup(spi);//设置SPI参数
oled_spi=spi;

OLED_Init();

/*dma申请物理空间*/
fb_info.screen_base=dma_alloc_writecombine(NULL,fb_info.fix.smem_len,(dma_addr_t *)&fb_info.fix.smem_start,GFP_KERNEL);
/*注册帧缓冲驱动*/
register_framebuffer(&fb_info);

return 0;
}
static int oled_remove(struct spi_device *spi)
{
printk("资源释放成功\n");
/*注销帧缓冲设备*/
unregister_framebuffer(&fb_info);
/*释放空间*/
dma_free_writecombine(NULL,fb_info.fix.smem_len,fb_info.screen_base,fb_info.fix.smem_start);
iounmap(GPB_CON);//取消映射
return 0;
}

static struct spi_driver sdrv=
{
.probe =oled_probe,
.remove =oled_remove,
.driver=
{
.name="spidev",
},
};
static int __init wbyq_oled_init(void)
{
spi_register_driver(&sdrv);//驱动注册
return 0;
}
/*驱动释放*/
static void __exit wbyq_oled_cleanup(void)
{
spi_unregister_driver(&sdrv);//驱动注销
printk("驱动出口,驱动注销成功\n");
}
module_init(wbyq_oled_init);//驱动入口函数
module_exit(wbyq_oled_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 oled Driver");

3.3 帧缓冲应用层

  通过LCD应用编程实现OLED应用程序编写,调用矢量字库实现字符串显示,移植第三方数码管显示示例实现动态数码管式时间显示。

#include <stdio.h>
#include <linux/fb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "./freetype/freetype.h"
#include "SuperNumber/SuperNumber.h"
#define OLED_REFLASH 0X80
typedef unsigned char u8;
typedef unsigned short u16;
void sDynamicClockInitial(void);
void sDynamicClockProcess(void);
int imag_w,imag_h;
static unsigned char *lcd_p=NULL;//屏幕缓存地址
static struct fb_fix_screeninfo fb_fix;//固定参数结构体
static struct fb_var_screeninfo fb_var;//可变参数结构体
/*LCD画点函数*/
void LCD_DrawPoint(int x,int y,int c)
{
if(fb_var.bits_per_pixel==8)
{
//获取要绘制的点的地址
unsigned char *p= (unsigned char *)(lcd_p+y*fb_fix.line_length+x*fb_var.bits_per_pixel/8);
*p=c;//写入颜色值
}
else
{
//获取要绘制的点的地址
unsigned int *p= (unsigned char *)(lcd_p+y*fb_fix.line_length+x*fb_var.bits_per_pixel/8);
*p=c;//写入颜色值

}
}
int fd;
int main(int argc,char *argv[])
{
if(argc!=2)
{
printf("格式:./a.out </dev/fbx>\n");
return 0;
}
/*1.打开设备*/
fd=open(argv[1], 2);
if(fd<0)
{
printf("打开设备失败\n");
}
/*2.获取固定参数*/
memset(&fb_fix,0, sizeof(fb_fix));
ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix);
printf("屏幕缓存大小:%d\n",fb_fix.smem_len);
printf("一行的字节数:%d\n",fb_fix.line_length);
/*3.获取屏幕可变参数*/
memset(&fb_var,0, sizeof(fb_var));
ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
printf("屏幕尺寸:%d*%d\n",fb_var.xres,fb_var.yres);
printf("颜色位数:%d\n",fb_var.bits_per_pixel);
imag_w=fb_var.xres;
imag_h=fb_var.yres;
/*4.将屏幕缓冲区映射到进程空间*/
lcd_p=mmap(NULL,fb_fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(lcd_p==(void *)-1)
{
printf("内存映射失败\n");
return 0;
}
memset(lcd_p,0x00,fb_fix.smem_len);//将屏幕清空为白色
if(InitConfig_FreeType("msyhbd.ttc"))//初始化freetype
{
printf("字库打开失败\n");
return 0;
}
sDynamicClockInitial();
sDynamicClockProcess();

FreeType_Config();//释放freetype
AA:
//取消映射
munmap(lcd_p,fb_fix.smem_len);
return 0;
}
//一个电子钟包括8个部分
sSuperNum stSuperNum1;
sSuperNum stSuperNum2;
sSuperNum stSuperNum3;
sSuperNum stSuperNum4;
sSuperNum stSuperNum5;
sSuperNum stSuperNum6;
sSuperNum stSuperNum7;
sSuperNum stSuperNum8;

//特效状态转移查询库
uint8_t SegAction[MAX_SEG_STATUE][MAX_SEG_STATUE][SEG_NUM];
/*************************************************************************
** Function Name: sDynamicClockInitial
** Purpose: 初始化时钟的各个数码段部分
** Params:
** @
** Return:
** Notice: None.
** Author: 公众号:最后一个bug
*************************************************************************/
void sDynamicClockInitial(void)
{
#define NUM_OFFSET (19)

uint16_t x_Location = 5;
uint16_t y_Location = 20;

stSuperNum1.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum1,x_Location,y_Location,10,10,2);
InitialSegShowAction(&stSuperNum1,(uint8_t*)SegAction);

x_Location += NUM_OFFSET;

stSuperNum2.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum2,x_Location,y_Location,10,10,2);
InitialSegShowAction(&stSuperNum2,(uint8_t*)SegAction);

x_Location += NUM_OFFSET;
stSuperNum3.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum3,x_Location,y_Location,2,10,2);
InitialSegShowAction(&stSuperNum3,(uint8_t*)SegAction);

x_Location += NUM_OFFSET/2 + 2;
stSuperNum4.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum4,x_Location,y_Location,10,10,2);
InitialSegShowAction(&stSuperNum4,(uint8_t*)SegAction);

x_Location += NUM_OFFSET;
stSuperNum5.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum5,x_Location,y_Location,10,10,2);
InitialSegShowAction(&stSuperNum6,(uint8_t*)SegAction);

x_Location += NUM_OFFSET;
stSuperNum6.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum6,x_Location,y_Location,2,10,2);
InitialSegShowAction(&stSuperNum6,(uint8_t*)SegAction);

x_Location += NUM_OFFSET/2+2;
stSuperNum7.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum7,x_Location,y_Location+10,5,5,2);
InitialSegShowAction(&stSuperNum7,(uint8_t*)SegAction);

x_Location += NUM_OFFSET/2+4;
stSuperNum8.pDrawPoint = LCD_DrawPoint;
InitialSuperNum(&stSuperNum8,x_Location,y_Location+10,5,5,2);
InitialSegShowAction(&stSuperNum8,(uint8_t*)SegAction);

}


/*************************************************************************
** Function Name: sDynamicClockProcess
** Purpose: 动态时钟处理
** Params:
** @
** Return:
** Notice: None.
** Author: 公众号:最后一个bug
*************************************************************************/
void sDynamicClockProcess(void)
{
static timerCnt = 0;
static uint16_t DPoint = 11;
static uint16_t CurrHour = 23; //当前小时
static uint16_t CurrMin = 59; //当前分钟
static uint16_t CurrSec = 50; //当前s
static uint16_t CurrSecOld = 0xFFFF;//保存的s
static uint16_t SecondPoint = 0;
time_t timep,timep2;//保存当前系统秒单位时间
struct tm result;//保存时间结构体
while(1)
{
timep=time(NULL);
if(timep!=timep2)
{
timep2=timep;
localtime_r(&timep,&result);//将秒单位时间转换为时间结构体
CurrHour=result.tm_hour;
CurrMin=result.tm_min;
CurrSec=result.tm_sec;
}
//下面是更新显示处理
if(CurrSecOld != CurrSec)
{
if(CurrSecOld == 0xFFFF) //表示开机第1s不处理
{
CurrSecOld = 0xFFFE;
}
else
{
CurrSecOld = CurrSec;//更新
DPoint = ((DPoint == 11)?(DPoint = 10):(DPoint = 11)); //点闪烁
}
}

if(CurrSecOld < 60)
{
SuperNumActionPlay(&stSuperNum1,(uint8_t*)SegAction,CurrHour/10);
SuperNumActionPlay(&stSuperNum2,(uint8_t*)SegAction,CurrHour%10);
SuperNumActionPlay(&stSuperNum3,(uint8_t*)SegAction,DPoint);
SuperNumActionPlay(&stSuperNum4,(uint8_t*)SegAction,CurrMin/10);
SuperNumActionPlay(&stSuperNum5,(uint8_t*)SegAction,CurrMin%10);
SuperNumActionPlay(&stSuperNum6,(uint8_t*)SegAction,DPoint);
SuperNumActionPlay(&stSuperNum7,(uint8_t*)SegAction,CurrSecOld/10);
SuperNumActionPlay(&stSuperNum8,(uint8_t*)SegAction,CurrSecOld%10);
ioctl(fd,OLED_REFLASH);
}
}
}
Linux帧缓冲注册OLED驱动_Tiny4412_02
Linux帧缓冲注册OLED驱动_OLED屏幕_03
Linux帧缓冲注册OLED驱动_SPI子系统_04

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK