Linux应用项目之量产工具(一)——显示系统

目录

前言

项目特点及介绍

① 简单易用

② 软件可配置、易扩展

③ 纯 C 语言编程

软件总框架

显示系统

1.数据结构抽象

disp_manager.h

2.Framebuffer编程

framebuffer.c

3.显示管理

disp_manager.c

4.单元测试

disp_test.c

顶层目录Makefile

顶层目录Makefile.build

底层目录display中的Makefile

底层目录unittest中的Makefile


前言

        今天开始学习Linux的第一个实战项目——电子产品量产测试与烧写工具,简称量产工具。这是一套软件,用在我们的实际生产中,有如下特点:


项目特点及介绍

① 简单易用

        把这套软件烧写在 SD 卡上,插到 IMX6ULL 板子里并启动,它就会自动测试各个模块、烧写 EMMC 系统。
        工人只要按照说明接入几个模块,就可以完成整个测试、烧写过程。
        测试结果一目了然:等 LCD 上所有模块的图标都变绿时,就表示测试通过。

② 软件可配置、易扩展

        通过配置文件添加测试项,可以添加不限个数的测试项。
        每个测试项有自己的测试程序,测试通过后把结果发送给 GUI 即可。各个测试程序互不影响。

③ 纯 C 语言编程

        下图是这个工具的界面,它可以一边测试一边烧写:

        上图中的 led、speaker 按钮,可以点击:
① 当你看到 LED 闪烁时,就点击 led 按钮,它变成绿色表示测试通过;
② 当你从耳机里听到声音时,就点击 speaker 按钮,它变成绿色表示测试通过。
        其他按钮无法点击,接上对应模块后会自动测试,测试通过时图标就会变绿。
        上图中的蓝色按钮表示烧写 EMMC 的进度,烧写成功后它也会变绿。
        LCD 上所有图标都变绿时,就表示测试、烧写全部完成;某项保持红色的话,就表示对应模块测试失败。

        对这个项目,我们要拆分出多个子系统,并且这些子系统与业务无关,以后还可以应用在其他项目上;我们编写各个子系统的代码时,要抽象出它的对外接口,增加它的扩展性,减少和其他模块的耦合性。通过这个项目,能锻炼我们面向对象的编程思想以及对事物的抽象能力等等。这个项目可以无限扩展,比如:


软件总框架

        要想最大程度复用我们写的代码,就需要模块化编程,拿显示系统举例,我们可以通过Framebuffer把数据显示到屏幕上,我们就可以构造出一个结构体,调用结构体里的初始化函数等等来使用这个模块;对于有些设备,它可能不需要用到屏幕,而是用到web输出,通过网络的方式显示数据,在网页打印出来,假设我们以后对于显示系统还要添加更多的模块,对于每个模块我们都要构造出它的结构体吗?显然是不用的,我们可以抽象出同一个结构体类型


显示系统

1.数据结构抽象

        来看看这个框图,我们要先获得一个buffer,我们可以自己在这个buffer里绘制我们想要的图案,绘制好后可以通过Framebuffer刷到屏幕上显示,也可以通过网络传输在浏览器上显示出来。

我们可以定义出下面这个结构体:

disp_manager.h

#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H/*编译的时候报错说NULL未定义,我们干脆自己定义好了*/
#ifndef NULL
#define NULL (void *)0
#endif/*存放buffer的信息*/
typedef struct DispBuff {int iXres;/*x坐标的像素值*/int iYres;/*y坐标的像素值*/int iBpp;/*每个像素有多少位(像素的大小)*/char *buff;/*指向得到的buffer,然后就可以操作它了*/
}DispBuff, *PDispBuff;/*存放图像的区域信息*/
typedef struct Region {int iLeftUpX;/*区域左上角的x坐标*/int iLeftUpY;/*区域左上角的y坐标*/int iWidth;/*宽*/int iHeight;/*高*/
}Region, *PRegion;/*模块调用这个结构体里的函数,需要我们自己去实现里面的函数*/
typedef struct DispOpr {char *name;int (*DeviceInit)(void);//初始化函数int (*DeviceExit)(void);//退出函数int (*GetBuffer)(PDispBuff ptDispBuff);//获得buffer,以便绘制图像int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff);//将buffer刷到对应的设备
(屏幕/网页),将ptDispBuff结构体指针指向的结构体里的buffer信息刷到Region结构体里(存放区域的信息)
表示你想在哪里显示?struct DispOpr *ptNext;//用链表管理这些模块
}DispOpr, *PDispOpr;/*****************************************/
/** 这些声明的函数我们后面再写,我先给出源码,先忽略这部分*/
void RegisterDisplay(PDispOpr ptDispOpr);void DisplayInit(void);
int SelectDefaultDisplay(char *name);
int InitDefaultDisplay(void);
int PutPixel(int x, int y, unsigned int dwColor);
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff);
PDispBuff GetDisplayBuffer(void);#endif

这些结构体都是一步一步改善过来的,所以我尽量把注释写的详细一点,我们想要在屏幕或者网页(网页暂时没有实现)上显示,我们首先要实现 struct DispOpr 这个结构体里的各个函数,然后定义出各个模块的结构体,就可以调用定义出来的结构体里的函数实现各个功能。

2.Framebuffer编程

        对于Framebuffer编程不熟悉的同学可以去看我这篇博客Framebuffer应用编程,下面用到的函数都是直接复制之前写的代码,原理就不再讲解了。

framebuffer.c

        我们先定义出给LCD屏幕使用的结构体,再去实现底层的函数。以后上层APP就可以调用结构体里的 DeviceInit 等函数,不需要关心底层的驱动代码。这就是面向对象的编程思想。

源码如下:

#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)
{/*打开设备结点,获得LCD参数等等*/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;}/*使用mmap函数获得可以直接操作LCD的buffer*/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)
{/*释放buffer,关闭设备结点*/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;
}/*为了方便,我们直接得到可以操作LCD的buffer,因此不需要Flush函数*/
static int FbFlushRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{return 0;
}static DispOpr g_tFramebufferOpr = {.name        = "fb",.DeviceInit  = FbDeviceInit,.DeviceExit  = FbDeviceExit,.GetBuffer   = FbGetBuffer,.FlushRegion = FbFlushRegion,
};/*RegisterDisplay函数是将显示系统的所有设备注册到链表里统一管理,这在后面会讲,这里先忽略*/
void FramebufferInit(void)
{RegisterDisplay(&g_tFramebufferOpr);
}

        这里就是实现了底层的驱动函数,实现 DispOpr 结构里的函数。

3.显示管理

        我们的应用程序当然可以直接使用我们上面构造的结构体,但是我有多个显示模块,我们还需要一个公共的部分,来选择我们要使用的模块,所以需要还需要写出管理底层模块的代码。

框图如下:

        我们要写出 disp_manager.c 这个程序,它起到承上启下的作用,很多函数其实也只是调用最底层的代码,但是我们再经过一层封装,就非常方便的实现管理底层的模块 

disp_manager.c

#include <stdio.h>
#include <string.h>
#include <disp_manager.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;/*描点函数,是所有图像的基础,我之前写的Framebuffer应用编程里有讲*/
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 ptDispOpr)
{ptDispOpr->ptNext = g_DispDevs;g_DispDevs = ptDispOpr;
}/* 选择函数,传入想要操作的底层模块的名字,如"fb",表示使用Framebuffer在屏幕上显示* 遍历链表,如果模块注册进链表里并且找到了名字,g_DispDefault就会指向我们想操作的模块的结构体*/
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 -1;
}/*初始化函数,其实就是调用底层的初始化*/
int InitDefaultDisplay(void)
{int ret;/*调用g_DispDefault指向的结构体,调用底层的模块初始化函数*/ret = g_DispDefault->DeviceInit();if (ret){printf("DeviceInit err\n");return -1;}/*获得buffer,参数会保存在传入的结构体里面*/ret = g_DispDefault->GetBuffer(&g_tDispBuff);if (ret){printf("GetBuffer err\n");return -1;}/*这两个参数是为了给描点函数用的,看描点函数第一行计算pen_8的地址,需要这两个参数*/line_width  = g_tDispBuff.iXres * g_tDispBuff.iBpp/8;pixel_width = g_tDispBuff.iBpp/8;return 0;
}/*把获得到的buffer返回给上层代码*/
PDispBuff GetDisplayBuffer(void)
{return &g_tDispBuff;
}/*Flush函数,底层的Flush函数我们并没有写,这里可以忽略*/
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{return g_DispDefault->FlushRegion(ptRegion, ptDispBuff);
}/*这里的初始化函数是将模块的结构体注册到链表里*/
void DisplayInit(void)
{extern void FramebufferInit(void);FramebufferInit();
}

虽然代码封装了很多层,但是这样便于以后添加更多的模块,更加容易扩展和移植。

4.单元测试

disp_test.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>#define FONTDATAMAX 4096
/*略*/

        需要自己定义点阵字体,这里我就省略了,可以看我关于Framebuffer的博客,或者去看韦东山老师的相关课程,里面有讲到哪里可以找到这个字体库。

/*********************************************************************** 函数名称: lcd_put_ascii* 功能描述: 在LCD指定位置上显示一个8*16的字符* 输入参数: x坐标,y坐标,ascii码* 输出参数: 无* 返 回 值: 无* 修改日期        版本号     修改人	      修改内容* -----------------------------------------------* 2020/05/12	     V1.0	  zh(angenao)	      创建***********************************************************************/ 
void lcd_put_ascii(int x, int y, unsigned char c)
{unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];int i, b;unsigned char byte;for (i = 0; i < 16; i++){byte = dots[i];for (b = 7; b >= 0; b--){if (byte & (1<<b)){/* show */PutPixel(x+7-b, y+i, 0xffffff); /* 白 */}else{/* hide */PutPixel(x+7-b, y+i, 0); /* 黑 */}}}
}int main(int argc, char **argv)
{Region region;PDispBuff ptBuffer;/*调用这个函数把所有模块注册到链表里(我们暂时只用到Framebuffer)*/DisplayInit();/*然后选择要操作的模块,传入名字*/SelectDefaultDisplay("fb");/*底层的初始化(打开设备节点等操作)*/InitDefaultDisplay();/*调用显示字符的函数,在这个函数里会调用的描点函数,所以我说描点函数是基础*/lcd_put_ascii(100, 100, 'A');/*这里是初始化一下要传入Flush函数的结构体*/region.iLeftUpX = 100;region.iLeftUpY = 100;region.iWidth   = 8;region.iHeigh   = 16;/*获得buffer*/ptBuffer = GetDisplayBuffer();/*刷到硬件上(暂时只有屏幕)*/FlushDisplayRegion(&region, ptBuffer);return 0;	
}

接下来给大伙看看函数的调用流程。

        多层函数的封装看起来会很乱,但如果看过我之前一篇博客UART开发基础,这篇博客也是我第一次接触这种面向对象的编程思想,像我之前写代码,也是直接APP调用底层函数,快准狠,但是如果以后工作,这种方法显然不可行,而且也锻炼不了什么事物抽象的能力,面对一些大型的项目可能会无从下手,但是这些结构体里的参数,也不是一下子就能想出来的,都是边写边补,缺啥补啥。

上传到Ubuntu时,要整个文件上传,如下:

        并且在要编译的文件夹下添加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)nm
 
STRIP        = $(CROSS_COMPILE)strip
OBJCOPY        = $(CROSS_COMPILE)objcopy
OBJDUMP        = $(CROSS_COMPILE)objdump
 
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
 
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
 
LDFLAGS := 
 
export CFLAGS LDFLAGS
 
TOPDIR := $(shell pwd)
export TOPDIR
 
TARGET := test
 
 
obj-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)
endif
 
 
PHONY += $(subdir-y)
 
 
__build : $(subdir-y) built-in.o
 
$(subdir-y):
    make -C $@ -f $(TOPDIR)/Makefile.build
 
built-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 


        量产工具的第一篇博客就到此为止,下一篇是量产工具的输入系统,希望大伙多多关注支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/468418.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企微SCRM价格解析及其性价比分析

内容概要 在如今的数字化时代&#xff0c;企业对于客户关系管理的需求日益增长&#xff0c;而企微SCRM&#xff08;Social Customer Relationship Management&#xff09;作为一款新兴的客户管理工具&#xff0c;正好满足了这一需求。本文旨在为大家深入解析企微SCRM的价格体系…

leetcode92:反转链表||

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;[1,4,3,2…

Python——数列1/2,2/3,3/4,···,n/(n+1)···的一般项为Xn=n/(n+1),当n—>∞时,判断数列{Xn}是否收敛

没注释的源代码 from sympy import * n symbols(n) s n/(n1) print(数列的极限为&#xff1a;,limit(s,n,oo))

多线程的创建方式以及及Thread类详解

目录 一.线程的创建方法&#xff1a;&#xff08;重点&#xff09; 一&#xff1a;继承Thread类 写法一&#xff1a;正常写法 写法二&#xff1a;匿名内部类 二.实现Runnable接口 写法一&#xff1a;正常写法 写法二&#xff1a;匿名内部类 三. 实现 Callable 接口 ​…

成功解决WSL2上的Ubuntu22.04执行sudo apt-get update指令报错问题

问题&#xff1a;输入sudo apt-get update指令会显示如下报错 问题所在&#xff1a;Temporary failure in name resolution 显然是系统无法解析域名。这可能是 DNS 配置问题。 解决方案&#xff1a; 临时修改 DNS 配置 尝试手动修改 /etc/resolv.conf 文件来使用公共 DNS 服务…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-19

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

ffmpeg 视频滤镜:屏蔽边框杂色- fillborders

滤镜描述 fillborders 官网链接 > FFmpeg Filters Documentation fillborders滤镜有几种方式帮你屏蔽边框的杂色、不好的图案。 滤镜使用 参数 left <int> ..FV.....T. set the left fill border (from 0 to INT_MAX) (default 0)right …

Unity性能优化 -- 性能分析工具

Stats窗口Profiler窗口Memory Profiler其他性能分析工具&#xff08;Physica Debugger 窗口&#xff0c;Import Activity 窗口&#xff0c;Code Coverage 窗口&#xff0c;Profile Analyzer 窗口&#xff0c;IMGUI Debugger 窗口&#xff09; Stats 统级数据窗口 game窗口 可…

【Ant.designpro】上传图片

文章目录 一、前端二、后端 一、前端 fieldProps:可以监听并且获取到组件输入的内容 action{“/api/upload_image”} 直接调用后端接口 <ProFormUploadButtonlabel{"上传手续图片"}name{"imgs"}action{"/api/upload_image"}max{5} fieldPro…

王珊数据库系统概论第六版PDF+第五版课后答案+课件

为了保持科学性、先进性和实用性&#xff0c; 编者在第5版教材基础上对全书内容进行了修改、更新和充实。在科学性方面&#xff0c; 编者在系统篇中增加了第9章关系数据库存储管理&#xff0c; 讲解数据库的逻辑与物理组织方式及索引结构。增加这部分内容有助于学生更好地理解关…

胡闹厨房练习(一)

参考教程 参考教程 学习视频 目录 准备工作 一、创建项目 二、处理预置文件和设置 三、编辑器布局 四、Visual Studio设置 五、导入资源 六、设置源素材 七、视觉效果&#xff08;后期处理&#xff09; 角色控制器 一、角色 二、制作动画 三、动画控制 四、运动脚…

「QT」几何数据类 之 QVector2D 二维向量类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…

Spring中的过滤器和拦截器

Spring中的过滤器和拦截器 一、引言 在Spring框架中&#xff0c;过滤器&#xff08;Filter&#xff09;和拦截器&#xff08;Interceptor&#xff09;是实现请求处理的两种重要机制。它们都基于AOP&#xff08;面向切面编程&#xff09;思想&#xff0c;用于在请求的生命周期…

网站架构知识之Ansible模块(day021)

1.Ansible模块 作用:通过ansible模块实现批量管理 2.command模块与shell模块 command模块是ansible默认的模块&#xff0c;适用于执行简单的命令&#xff0c;不支持特殊符号 案列01&#xff0c;批量获取主机名 ansible all -m command -a hostname all表示对主机清单所有组…

requestAnimationFrame与setInterval的抉择

&#x1f64c; 如文章有误&#xff0c;恳请评论区指正&#xff0c;谢谢&#xff01; ❤ 写作不易&#xff0c;「点赞」「收藏」「转发」 谢谢支持&#xff01; 背景 在之前的业务中遇到有 JS 动画的实现场景&#xff0c;但当电脑打开太多网页或是同时启动很多应用时&#xff0c…

高性能分布式缓存Redis-分布式锁与布隆过滤器

一、分布式锁 我们先来看一下本地锁 在并发编程中&#xff0c;我们通过锁&#xff0c;来避免由于竞争而造成的数据不一致问题。通常&#xff0c;我们以 synchronized 、Lock 来使用它&#xff08;单机情况&#xff09; 来看这段代码 Autowired RedisTemplate<String,Str…

Flutter运行App时出现“Running Gradle task ‘assembleDebug“问题解决

在参考了众多解决办法中最有用并且最快的方法 Gradle Distributions 在这个地方下载对应你这个文件中的gradle版本 然后将 最后一行本来不是这样的,我们把下载好的zip包保存到本地,然后用这个代替网址,最后成功运行

【CUDA】认识CUDA

目录 一、CUDA编程 二、第一个CUDA程序 三、CUDA关键字 四、device管理 4.1 初始化 4.2 Runtime API查询GPU信息 4.3 决定最佳GPU CUDA C 编程指南CUDA C在线文档&#xff1a;CUDA C 编程指南 CUDA是并行计算的平台和类C编程模型&#xff0c;能很容易的实现并行算法。只…

【优选算法篇】微位至简,数之恢宏——解构 C++ 位运算中的理与美

文章目录 C 位运算详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;位运算基础应用1.1 判断字符是否唯一&#xff08;easy&#xff09;解法&#xff08;位图的思想&#xff09;C 代码实现易错点提示时间复杂度和空间复杂度 1.2 丢失的数字&#xff08;easy&#xff0…

从0开始学习机器学习--Day21--算法的评估标准

准确率和召回率(precision and recall) 在上一章我们提到了在每次运行算法时通过返回一个实数值来判断算法的好坏&#xff0c;但是我们该如何构建这个实数的计算公式呢&#xff0c;毕竟这关乎于我们对算法的判断&#xff0c;不能过于夸大或贬低。有一个典型的会被影响的很大例…