欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > 量产工具一一显示系统(一)

量产工具一一显示系统(一)

2026/5/1 13:40:25 来源:https://blog.csdn.net/m0_74712453/article/details/139216855  浏览:    关键词:量产工具一一显示系统(一)

目录

前言

一、项目介绍和应用

1.简单易用

2.软件可配置、易扩展

3.纯 C 语言编程

4.类似界面应用

二、项目总体框架

三、显示系统

1.显示系统数据结构抽象

(1)common.h

(2)disp_manager.h

2.Framebuffer编程

(1)framebuffer.c

3.显示管理

(1)disp_manager.c

4.测试单元

(1)测试代码

(2)通用Makefile

四、上机测试


前言

该项目是韦东山老师Linux入门基础课程的第一个项目,在这里我用的是IMX6ULL开发板。通过学习这个项目,可以学到良好的编程规范,面向对象的编程思想,对事物的抽象能力,对整个系统的把控能力。

一、项目介绍和应用

电子产品量产测试与烧写工具,这是一套软件,用在我们的实际生产中, 有如下特点:

1.简单易用

把这套软件烧写在 SD 卡上,插到 IMX6ULL 板子里并启动,它就会自动测试各个模块、烧写 EMMC 系统。

工人只要按照说明接入几个模块,就可以完成整个测试、烧写过程。

测试结果一目了然:等 LCD 上所有模块的图标都变绿时,就表示测试通过。

2.软件可配置、易扩展

通过配置文件添加测试项,可以添加不限个数的测试项。

每个测试项有自己的测试程序,测试通过后把结果发送给 GUI 即可。各个测试程序互不影响。

3.纯 C 语言编程

工具设计的界面,它可以一边测试一边烧写:

a18bbf83af014dcaaa2ef2f419c9805e.png

上图中的 led、speaker 按钮,可以点击: 

1.当你看到 LED 闪烁时,就点击 led 按钮,它变成绿色表示测试通过;

2. 当你从耳机里听到声音时,就点击 speaker 按钮,它变成绿色表示测试通过。

  • 其他按钮无法点击,接上对应模块后会自动测试,测试通过时图标就会变绿。
  • 上图中的蓝色按钮表示烧写 EMMC 的进度,烧写成功后它也会变绿。
  • LCD 上所有图标都变绿时,就表示测试、烧写全部完成;某项保持红色的话,就表示对应模块测试失败。

4.类似界面应用

二、项目总体框架

在软件编程当中,可以把一个项目拆分成各个子系统,并且这些子系统跟业务无关,以后还可以用在其他项目上。对于一个子系统,可以抽象出它的对外接口,减少与其他模块的耦合,方便扩展。

接下来先分析他的第一个框架,显示系统。

三、显示系统

1.显示系统数据结构抽象

我们添加的一个显示管理器中有Framebuflerweb输出,对于两个不同的设备我们可以抽象出同一个结构体类型。

(1)common.h

该头文件用来包含通用区域结构体

#ifndef _COMMON_H
#define _COMMON_H#ifndef NULL
#define NULL (void *)0#endiftypedef struct Region {int iLeftUpX;int iLeftUpY;int iWidth;int iHeigh;
}Region, *PRegion;#endif

(2)disp_manager.h

#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H#include <common.h>typedef struct DispBuff {int iXres;int iYres;int iBpp;char *buff;
}DispBuff, *PDispBuff;typedef struct DispOpr {char *name;int (*DeviceInit)(void);int (*DeviceExit)(void);int (*GetBuffer)(PDispBuff ptPDispBuff);int (*FlushRegion)(PRegion ptRegion, PDispBuff ptPDispBuff);struct DispOpr *ptNext;
}DispOpr, *PDispOpr;int PutPixel(int x, int y, unsigned int dwColor);
void RegisterDisplay(PDispOpr ptPDispOpr);
int SelectDefaultDisplay(char *name);
int InitDefaultDisplay(void);
PDispBuff GetDisplayBuffer(void);
int FlushDisplayRegion(PRegion ptPRegion, PDispBuff ptPDispBuff);
void DisplaySystemRegister(void);#endif

2.Framebuffer编程

Framebuffer编程原理和实操可以看:

(1)framebuffer.c

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>#include <disp_manager.h>static int fd_fb;
static struct fb_var_screeninfo var;	/* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;static int FbDeviceInit(void)
{fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb < 0){printf("can't open /dev/fb0\n");return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)){printf("can't get var\n");return -1;}line_width  = var.xres * var.bits_per_pixel / 8;pixel_width = var.bits_per_pixel / 8;screen_size = var.xres * var.yres * var.bits_per_pixel / 8;fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if (fb_base == (unsigned char *)-1){printf("can't mmap\n");return -1;}return 0;
}static int FbDeviceExit(void)
{munmap(fb_base, screen_size);close(fd_fb);return 0;
}/* 可以返回LCD的framebuffer, 以后上层APP可以直接操作LCD, 可以不用FbFlushRegion* 也可以malloc返回一块无关的buffer, 要使用FbFlushRegion*/static int FbGetBuffer(PDispBuff ptDispBuff)
{ptDispBuff->iXres = var.xres;ptDispBuff->iYres = var.yres;ptDispBuff->iBpp  = var.bits_per_pixel;ptDispBuff->buff  = (char *)fb_base;return 0;
}static int FbFlushRegion(PRegion ptRegion, PDispBuff ptPDispBuff)
{return 0;
}static DispOpr g_tFramebufferOpr = {.name        = "fb",.DeviceInit  = FbDeviceInit,.DeviceExit  = FbDeviceExit,.GetBuffer   = FbGetBuffer,.FlushRegion = FbFlushRegion,
};void FramebufferRegister(void)
{RegisterDisplay(&g_tFramebufferOpr);
}

3.显示管理

上层函数想要选择哪个设备进行显示,需要中间加一个函数进行选择,起到承上启下的作用,用来实现显示管理,是操作Framebuffer还是WEB设备,需要我们选择某个模块,以便于可以提供一些函数,描点等。

(1)disp_manager.c

#include <disp_manager.h>
#include <stdio.h>
#include <string.h>/* 管理底层的LCD、WEB */
static PDispOpr g_DispDevs = NULL;
static PDispOpr g_DispDefault = NULL;
static DispBuff g_tDispBuff;
static int line_width;
static int pixel_width;int PutPixel(int x, int y, unsigned int dwColor)
{unsigned char *pen_8 = (unsigned char *)(g_tDispBuff.buff+y*line_width+x*pixel_width);unsigned short *pen_16;	unsigned int *pen_32;	unsigned int red, green, blue;	pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;switch (g_tDispBuff.iBpp){case 8:{*pen_8 = dwColor;break;}case 16:{/* 565 */red   = (dwColor >> 16) & 0xff;green = (dwColor >> 8) & 0xff;blue  = (dwColor >> 0) & 0xff;dwColor = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = dwColor;break;}case 32:{*pen_32 = dwColor;break;}default:{printf("can't surport %dbpp\n", g_tDispBuff.iBpp);return -1;break;}}return 0;}void RegisterDisplay(PDispOpr ptPDispOpr)
{ptPDispOpr->ptNext = g_DispDevs;g_DispDevs = ptPDispOpr;
}int SelectDefaultDisplay(char *name)
{PDispOpr pTmp = g_DispDevs;while (pTmp){if (strcmp(name, pTmp->name) == 0){g_DispDefault = pTmp;return 0;}pTmp = pTmp->ptNext;}return 0;
}int InitDefaultDisplay(void)
{int ret;ret = g_DispDefault->DeviceInit();if (ret){printf("DeviceInit err\n");return -1;}ret = g_DispDefault->GetBuffer(&g_tDispBuff);if(ret){printf("GetBuffer err\n");return -1;}line_width  = g_tDispBuff.iXres * g_tDispBuff.iBpp / 8;pixel_width = g_tDispBuff.iBpp / 8;return 0;
}PDispBuff GetDisplayBuffer(void)
{return &g_tDispBuff;
}int FlushDisplayRegion(PRegion ptPRegion, PDispBuff ptPDispBuff)
{return g_DispDefault->FlushRegion(ptPRegion, ptPDispBuff);
}void DisplaySystemRegister(void)
{extern void FramebufferRegister(void);FramebufferRegister();
}

4.测试单元

display目录存放 framebuffer.c,disp_manager.c 文件

include目录存放头文件

unittest目录存放单元测试.c文件

(1)测试代码

disp_test.c

(2)通用Makefile

通用Makefile的原理和使用可以看:

顶层目录Makefile

CROSS_COMPILE ?= 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nmSTRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdumpexport AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMPCFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/includeLDFLAGS := export CFLAGS LDFLAGSTOPDIR := $(shell pwd)
export TOPDIRTARGET := testobj-y += display/
obj-y += unittest/all : start_recursive_build $(TARGET)@echo $(TARGET) has been built!start_recursive_build:make -C ./ -f $(TOPDIR)/Makefile.build$(TARGET) : built-in.o$(CC) -o $(TARGET) built-in.o $(LDFLAGS)clean:rm -f $(shell find -name "*.o")rm -f $(TARGET)distclean:rm -f $(shell find -name "*.o")rm -f $(shell find -name "*.d")rm -f $(TARGET)

顶层目录Makefile.build

PHONY := __build
__build:obj-y :=
subdir-y :=
EXTRA_CFLAGS :=include Makefile# obj-y := a.o b.o c/ d/
# $(filter %/, $(obj-y))   : c/ d/
# __subdir-y  : c d
# subdir-y    : c d
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y	+= $(__subdir-y)# c/built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)# a.o b.o
cur_objs := $(filter-out %/, $(obj-y))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))ifneq ($(dep_files),)include $(dep_files)
endifPHONY += $(subdir-y)__build : $(subdir-y) built-in.o$(subdir-y):make -C $@ -f $(TOPDIR)/Makefile.buildbuilt-in.o : $(cur_objs) $(subdir_objs)$(LD) -r -o $@ $^dep_file = .$@.d%.o : %.c$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<.PHONY : $(PHONY)

底层目录display中的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += disp_manager.o
obj-y += framebuffer.o

底层目录unittest中的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += disp_test.o

四、上机测试

打开ubuntu,将交叉编译编译成功的文件复制到nfs目录。

上电开发板,挂载 Ubuntu 的 NFS 目录,详细可看:

开发板挂载 Ubuntu 的 NFS 目录_开发板nfs挂载ubuntu-CSDN博客

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

进入NFS目录,编译测试:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词