Linux:LCD驱动开发

目录

1.不同接口的LCD硬件操作原理

应用工程师眼中看到的LCD

1.1像素的颜色怎么表示

​编辑 1.2怎么把颜色发给LCD

 驱动工程师眼中看到的LCD

统一的LCD硬件模型

8080接口

TFTRGB接口

什么是MIPI

Framebuffer驱动程序框架

 怎么编写Framebuffer驱动框架

硬件LCD时序分析

8080接口LCD

 TFTRGB接口LCD

IMX6ULL中的LCD控制器

一、LCD接口概述

 二、external Signal

 三、时钟

四、功能描述

单buffer和双biffer

使用双buffer


1.不同接口的LCD硬件操作原理

应用工程师眼中看到的LCD

LCD由一个一个像素组成:每行由xres个像素,有yres行,他的分辨率是:xres*yres.

只要我们能控制任意一个像素的颜色,就可以在LCD上绘制图片,文字

1.1像素的颜色怎么表示

用红绿蓝三种颜色来表示,可以用24位数据来表示红绿蓝,也可以用16位等等格式。比如:

  • bpp : bits per pixel ,每个像素用多少位来表示
  • 24bpp:实际上会用到32位,其中8位未使用,其余24位中分别用8位来表示红(R),绿(G),蓝(B)
  • 16bpp:有rbg565,rbg555
  1. rbg565:用5位表示红,用6位表示绿,用5位表示蓝
  2. rbg555:16位数据中用5位表示红,5位表示绿,5位表示蓝,浪费一位

 1.2怎么把颜色发给LCD

假设每个像素的颜色用16位来表示,那么一个LCD的所有像素点假设有xres*yres个,

需要的内存为:xres * yres *16/8 ,也就是要设置所有像素的颜色,需要这么大的内存

这块内存就被称为framebuffer:

  • Framebuffer中每块数据对应一个像素
  • 每块数据的大小可能是16位,32位,这跟LCD上的像素的颜色格式有关
  • 设置好LCD硬件后,只需要把颜色数据写入到Framebuffer即可

 驱动工程师眼中看到的LCD

驱动工程师对LCD的理解要深入硬件,必须要回答这几个问题:

  • Framebuffer在哪里
  • 谁把Framebuffer中的数据发给LCD

统一的LCD硬件模型

第一种情况:将显存,LCD控制器,LCD屏幕组成一个模组(LCM)

第二种情况:将LCD控制器,显存与LCD屏幕分隔开,LCD控制器是位于ARM芯片内部的,ARM                       芯片外接内存,从内存中分配出一块空间供显存使用

 第一种情况一般用于单片机MCU(F103):这种接口称为8080

第二种情况一般用于能运行Linux的高性能的芯片MPU:这种接口一般被称为TFTRGB接口

这两种接口是不一样的,但整个模块的原理是一样的

8080接口

用这种接口的单片机一般性能比较弱, 所以外接的模块用内存的接口最好,我们在访问内存时,一般用数据线访问(读信号,写信号,地址线,数据线,片选信号),这样的话需要的IO口太多了,如何进行精简呢?数据线不可少,所以只能精简地址线,可以通过将地址信号转换为数据通过数据线发送,但是怎么分辨发送的是数据还是地址呢,通过引出一个引脚来分辨(data/cmd)

这种显存在屏幕上,显存一般是SRAM,它比较贵,所以一般的 8080屏幕分辨率不高

要使用更高分辨率的屏幕,就可以用以下接口的方式,这种方式内存接口比较便宜

TFTRGB接口

LCD控制器在ARM芯片内部,它会自动获取Framebuffer中的数据,那它怎么将数据发送到LCD屏幕上边呢:

1.移动像素:DCLK

2.从最后跳到下一行:HSYNC(每来一个脉冲就执行一次)

3.跳回一帧的开始(从最后一个像素段跳到第一个像素点):VSYNC 

4.数据来源:RGB三组线(24条线)

5.当数据从最后跳到下一行和从会后一个像素点跳到第一个像素点的时候数据是无效的,所以使用:DE(来决定数据是否有效)

什么是MIPI

 实际上上边这两种接口的实质是一样的,这两种接口都可以归入一个标准:MIPI标准

MIPI表示“Mobile Industry Proc8080essor Interface”,即移动产业处理器接口,是MIPI联盟发起的为移动应用处理器制定的开放标准和一个规范。主要是手机内部的接口(摄像头,显示器接口,射频/基带接口)等标准化,从而减少手机内部接口的复杂程度及增加设计的灵活性

对于LCD,MIPI可以分为3类:

MIPI-DBI(Display Bus Interface)

既可以是Bus(总线),就是既能发送命令,常用的8080接口就是属于DBI接口

MIPI-DPI(Display Pixel Interface)

Pixel(像素),强调的是操作单个像素,在MPU上的LCD控制器就是这种接口 

MIPI-DSI(Display Serial Interface)

Serial,相比于DBI,DPI需要使用很多接口线,DSI需要的接口线大为减少 

Framebuffer驱动程序框架

它再怎么复杂,还是一个字符驱动程序

LCD驱动程序分为

1.上层比较通用性的代码(框架):fbmem.c

2.硬件驱动:对于不同的芯片还有不同的硬件相关的文件:s3c2410fb.c   STM32MP157_fb.c

调用关系:

示例1:
app: open("/dev/fb0", ...)  主设备号:29  , 次设备号: 0
---------------------------------------------------------------------------
kernel:
        fb_open
            int fbidx = iminor(inode);
            struct fb_info *info = = registered_fb[0];




示例2:
app:    read()
----------------------------------------------------------------------------
kernel:
        fb_read
            int fbidx = iminor(inode);
            struct fb_info *info = registered_fb[fbidx];
            if(info -> fbops -> fb_read)
                return info -> fbops -> fb_read(info, buf count ppos);
                
            src = (u32 __inomem *) (info -> screen_base + p);
            dst = buffer;
            *dst++ == fb_readl(src++);
            copy_to_user(buf, buffer, c);

 怎么编写Framebuffer驱动框架

核心:


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;	/* Current var */
	struct fb_fix_screeninfo fix;	/* Current 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
	union {
		char __iomem *screen_base;	/* Virtual address */
		char *screen_buffer;
	};
	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;

	bool skip_vt_switch; /* no VT switch on suspend/resume required */
};

1.分配fb_info:framebuffer_alloc

2.设置fb_info:var   ,    fbops       ,硬件相关操作

3.注册fb_info:register_framebuffer

4.进行硬件相关的操作

代码如下:

#include <linux/module.h>

#include <linux/compat.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/vt.h>
#include <linux/init.h>
#include <linux/linux_logo.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/console.h>
#include <linux/kmod.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/efi.h>
#include <linux/fb.h>

#include <asm/fb.h>

static strcut fb_info *myfb_info;

static struct fb_ops myfb_ops = {
	.owner = THIS_MODULE,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

/*入口函数*/
static int __init lcd_drv_init(void)
{
	dma_addr_t phy_addr;
	
	/*分配fb_info*/
	myfb_info = framebuffer_alloc(0,NULL);
	
	/*设置fb_info*/
	/*1.var: LCD的分辨率,颜色格式等*/
	myfb_info -> var.xres = 1024;
	myfb_info -> var.yres = 600;
	myfb_info -> var.bits_per_pixel = 16;
	
	myfb_info -> var.red.offset = 11;
	myfb_info -> var.red.length = 5;
	
	myfb_info -> var.green.offset = 5;
	myfb_info -> var.green.length = 6;

	myfb_info -> var.blue.offset = 0;
	myfb_info -> var.blue.length = 5;
	/*2.fix*/
	myfb_info -> fiX.smem_len = myfb_info -> var.xres * myfb_info -> var.yres * 4;
	if(myfb_info ->var.bits_per_pixel == 24)
	{
		myfb_inco -> fix.smem_len = myfb_info -> var.xres * myfb_info -> var.yres * myfb_info -> var.bits_per_pixel / 8
	}
	//framebuffer的虚拟地址
	myfb_info -> screen_base = dma_alloc_wc(NULL,myfb_info -> fix.smem_len, &phy_addr, GFP_KERNEL);
	myfb_info -> fix.smem_start = phy_addr;  //framebuffer的物理地址
	
	myfb_info -> fix.type = FB_TYPE_PACKED_PIXELS;
	myfb_info -> fix.visual = FB_VISUAL_TRUECOLOR;
	
	/*3.fbops*/
	myfb_info->fbops = &myfb_ops;
	
	/*注册fb_info*/
	register_framebuffer(myfb_info);

	/*硬件操作*/
	
	
	return 0;
}

/*出口函数*/
static void __exit lcd_drv_exit(void)
{
	/*反过来操作*/
	
	/*反注册fb_info*/
	unregister_framebuffer(myfb_info);

	/*释放fb_info*/
	framebuffer_release(myfb_info);
	
}


module_init(lcd_drv_init);
module_exit(lcd_drv_exit);
MODULE_AUTHOR("zt");
MODULE_DESCRIPTION("Framebuffer driver for the linux");
MODULE_LICENSE("GPL");

对于硬件相关的操作:

这里使用QEMU中简单的四个寄存器

struct lcd_regs{
    volatile unsigned int fb_base_phys;   //这个寄存器存放的是显存的物理地址
    volatile unsigned int fb_xres;             //这个寄存器存放的是X像素
    volatile unsigned int fb_yres;             //这个寄存器存放的是Y像素
    volatile unsigned int fb_bpp;              //这个寄存器存放的是颜色的位数
};

static struct lcd_regs *mylcd_regs;

/*硬件操作*/

    //因为驱动不能直接操作物理地址所以需要将物理地址映射为虚拟地址
    mylcd_regs = ioremap(0x021C8000,sizeof(struct lcd_regs));
    //地址寄存器存放的是之前myfb_info分配的物理地址(也就是显存的物理地址)
    mylcd_regs->fb_base_phys = phy_addr;
    mylcd_regs->fb_xres = 500;
    mylcd_regs->fb_yres = 300;
    mylcd_regs->fb_bpp  = 16;

硬件LCD时序分析

8080接口LCD

8080接口其实就是一般的内存接口

显存,LCD控制器,LCD屏幕放在是一块的(LCM)

1.接口原理图

数据线,读信号,写信号,地址/数据引脚(当这个引脚为高电平时,传输的为数据,为低电平时,传输的是地址),片选引脚(在Linux中内存空间都是分开的,当使用其中一段内存空间的时候,对应的片选引脚会自动有效)

 当位于第一块内存区间时,片选7自动有效,当位于第二块内存区间时,片选6自动有效

其引脚图如下:

 TFTRGB接口LCD

IMX6ULL中的LCD控制器

下面只是简单介绍一下,具体请看这篇文章:LCD控制器

 它是TFTRGB的接口,显存和LCD控制器在IMX6Ull中所以主要就是LCD控制器与LCD屏幕进行互动

一、LCD接口概述

IMX6ULL的控制器名称为eLCDIF(增强型LCD接口)主要特性如下:

1.支持MPU模式:有些显示屏自带显存,只需要把命令、数据发送给显示屏就可以

2.支持DOTCLK模式:RGB接口,本板子就是此模式

3.VSYNC模式:针对高速数据传输(行场信号)

4.支持ITU-R BT.656接口,可以把4:2:2YcbCr格式的数据转换为模拟电视信号

5.8/16/18/24/32 bit的bpp数据都支持,取决于IO的复用设置及寄存器配置

6.MPU模式,VSYNC模式,DOTCLK模式,都可以配置时序参数

 二、external Signal

 三、时钟

 LCD控制器有两个时钟域:外设总线时钟域,LCD像素时钟域。前者是用来让LCD控制器来正常工作,后者是用来控制电子枪移动

四、功能描述

 我们在内存中划出一块内存,称之为显存,软件把数据写入到显存中

设置好LCD控制器之后,它会通过AXI总线协议从显存把RGB数据读入FIFO,再到达LCD接口(LCD Interface)。上图的Read_Data操作,在MPU模式下才用到。

单buffer和双biffer

首先要知道为什么要区分出单Buffer和双buffer

因为在IMX6ULL中,显存和LCD控制器都是在屏幕外的,当使用单buffer时,因为应用层和LCD控制器都是操作的一块显存(framebuffer),如果应用层写入数据时过快,LCD控制器还没有将显存中的数据写到屏幕中,显存有刷新了,此时就会出现问题。

使用双buffer甚至是多buffer可以有效的解决这个问题,因为应用层和LCD控制器操作的不是同一块显存,设有显存1和显存2(这里是在驱动层直接分配了两个显存大小的内存空间{实际上在IMX6ULL中是直接分配了32Mb的内存空间,相当于13个显存,是非常豪横的!!!}),刚开始应用层先写显存1,LCD控制器读显存2的数据,再一次LCD控制器读显存1的数据,应用层向显存2写数据,这样往复操作,就不会出现单buffer时的情况

使用双buffer

#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 <time.h>

static int fd_fb;
static struct fb_fix_screeninfo fix;	/* Current fix */
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;

/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点),一个像素一个像素的放
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
void lcd_put_pixel(void *fb_base, int x, int y, unsigned int color)
{
	//
	unsigned char *pen_8 = fb_base+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 (var.bits_per_pixel)
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16:
		{
			/* 565 */
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = (color >> 0) & 0xff;
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	}
}

void lcd_draw_screen(void *fb_base, unsigned int color)
{
	int x, y;
	for (x = 0; x < var.xres; x++)
		for (y = 0; y < var.yres; y++)
			lcd_put_pixel(fb_base, x, y, color);
}


/* ./multi_framebuffer_test single
 * ./multi_framebuffer_test double
 */
int main(int argc, char **argv)
{
	int i;
	int ret;
	//这个参数是记录的
	int nBuffers;
	int nNextBuffer = 1;
	char *pNextBuffer;
	unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF};  /* 0x00RRGGBB */
	struct timespec time;

	time.tv_sec  = 0;
	time.tv_nsec = 100000000;

	if (argc != 2)
	{
		printf("Usage : %s <single|double>\n", argv[0]);
		return -1;
	}
	
	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_FSCREENINFO, &fix))
	{
		printf("can't get fix\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;

	nBuffers = fix.smem_len / screen_size;
	printf("nBuffers = %d\n", nBuffers);
	
	fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}

	if ((argv[1][0] == 's') || (nBuffers == 1))
	{
		while (1)
		{
			/* use single buffer */
			for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
			{
				lcd_draw_screen(fb_base, colors[i]);
				nanosleep(&time, NULL);
			}
		}
	}
	else
	{
		/* use double buffer */
		/* a. enable use multi buffers */
		var.yres_virtual = nBuffers * var.yres;
		ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);

		while (1)
		{
			for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
			{
				/* get buffer */
				pNextBuffer =  fb_base + nNextBuffer * screen_size;

				/* set buffer */
				lcd_draw_screen(pNextBuffer, colors[i]);

				/* switch buffer */
				var.yoffset = nNextBuffer * var.yres;
				ioctl(fd_fb, FBIOPAN_DISPLAY, &var);

				ret = 0;
				ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
				
				nNextBuffer = !nNextBuffer;
				nanosleep(&time, NULL);
			}
		}
		
	}
	
	munmap(fb_base , screen_size);
	close(fd_fb);
	
	return 0;	
}


当输入single时使用单buffer,当输入double时使用双buffer,经过对比会发现明显的差异,使用双buffer的刷屏会更加流畅,并且不会出现颜色覆盖的情况。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【经典机器学习算法】谱聚类算法及其实现(python)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. 前…

图文深入理解Oracle Network配置管理(一)

List item 本篇图文深入介绍Oracle Network配置管理。 Oracle Network概述 Oracle Net 服务 Oracle Net 监听程序 <oracle_home>/network/admin/listener.ora <oracle_home>/network/admin/sqlnet.ora建立网络连接 要建立客户机或中间层连接&#xff0c;Oracle…

【C++】C++基础

目录 一. C关键字(C98) 二、C的第一个程序 三、命名空间 3.1.namespace的价值 3.2.namespace的定义 3.2.命名空间使用 总结&#xff1a;在项目当中第一、第二种方法搭配使用&#xff0c;第三种冲突风险非常大&#xff0c;仅适合练习使用。 四、C输入&输出 五、缺省…

DRF笔记

参考资料 http://www.yuan316.com/post/DRF/ 全站最牛逼的DRF&#xff08;Django-restframework&#xff09;&#xff0c;没有之一&#xff01; 零、创建django项目 项目每次处相当于执行命令&#xff1a;django-admin startproject xxx 应用名称处&#xff1a;python manage.…

调用智谱AI,面试小助手Flask简单示例

文章目录 1.接入AI获取API密钥Python代码 2.小助手的实现流程3.Flask应用示例Python文件.pyindex.html运行Flask应用地址栏输入 http://localhost:5000/ 1.接入AI 获取API密钥 在智谱AI的官方网站上注册&#xff0c;右上角点击API密钥&#xff0c;新建并复制一个 API Key&…

奔驰EQS450suv升级增强AR抬头显示HUD案例分享

以下是奔驰 EQS450 SUV 升级增强版 AR 抬头显示的一般改装案例步骤及相关信息&#xff1a; 配件&#xff1a;通常包括显示屏、仪表模块、饰板等。 安装步骤&#xff1a; 1. 拆下中控的仪表。 2. 在仪表上预留位置切割出合适的孔位&#xff0c;用于安装显示器。 3. 将显示器…

【leetcode】 45.跳跃游戏 ||

如果我们「贪心」地进行正向查找&#xff0c;每次找到可到达的最远位置&#xff0c;就可以在线性时间内得到最少的跳跃次数。 例如&#xff0c;对于数组 [2,3,1,2,4,2,3]&#xff0c;初始位置是下标 0&#xff0c;从下标 0 出发&#xff0c;最远可到达下标 2。下标 0 可到达的…

Unity XR 环境检测

需求&#xff1a; 检测环境是XR还是手机 代码&#xff1a; using UnityEngine.XR;public class EnvmentUtility {/// <summary>/// 是否是XR环境/// </summary>/// <returns>如果是XR&#xff0c;返回true&#xff0c;否则false</returns>public sta…

聊聊晶圆厂中的常见口语(1)

知识星球里的学员问&#xff1a;半导体公司的工程师总爱用一些英语代替中文&#xff0c;比如care,show&#xff0c;用这种简单的单词代替中文&#xff0c;能不能给我们总结工程师常用的英语单词&#xff0c;比较口语化的&#xff01; 为什么晶圆厂会用很多英文口语&#xff1f…

CompletableFuture常用方法

一、获得结果和触发计算 1.获取结果 &#xff08;1&#xff09;public T get() public class CompletableFutureAPIDemo{public static void main(String[] args) throws ExecutionException, InterruptedException{CompletableFuture<String> completableFuture Com…

c++进阶之多态讲解

这篇文章和大家一起学习一下c中的多态 多态的概念 多态的概念&#xff1a;通俗来讲&#xff0c;就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态)。 什么是静态多态 前⾯讲的函数重载和函数模板&#xff0c;它们传不同类型的参数就可以调用不同的函数&…

vue3学习记录-computed

vue3学习记录-computed 1.为什么要用computed2.使用方法2.1 基本实例2.2 可写计算属性 1.为什么要用computed 写个购物车的案例 <script setup> import { ref, reactive,computed } from "vue" const tableData reactive([{ name: 商品1, price: 10, num: 1…

Labview helper

IMAQ Advanced Setup Learn Geometric Pattern 2 VI 参数说明Curve Extraction Mode (0)指定VI如何识别图像中的曲线。如果您希望VI不对图像中对象的均匀性或图像背景做出任何假设&#xff0c;请将此选项设置为正常。如果您希望VI假定图像中的对象或图像背景由均匀的像素值组成…

《蓝桥杯算法入门》(C/C++、Java、Python三个版本)24年10月出版

推荐&#xff1a;《算法竞赛》&#xff0c;算法竞赛大全书&#xff0c;网购&#xff1a;京东 天猫  当当 文章目录 《蓝桥杯算法入门》内容简介本书读者对象作者简介联系与交流《蓝桥杯算法入门 C/C》版目录 《蓝桥杯算法入门 Java》版目录 《蓝桥杯算法入门 Python》版目录 …

【Python】Uvicorn:Python 异步 ASGI 服务器详解

Uvicorn 是一个为 Python 设计的 ASGI&#xff08;异步服务器网关接口&#xff09;Web 服务器。它填补了 Python 在异步框架中缺乏一个最小化低层次服务器/应用接口的空白。Uvicorn 支持 HTTP/1.1 和 WebSockets&#xff0c;是构建现代异步Web应用的强大工具。 ⭕️宇宙起点 &a…

找到字符串中第一个匹配项的下标(c语言)

1./给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 //示例 1&#xff1a; //输入&#xff1a;haystac…

【深度学习】(7)--神经网络之保存最优模型

文章目录 保存最优模型一、两种保存方法1. 保存模型参数2. 保存完整模型 二、迭代模型 总结 保存最优模型 我们在迭代模型训练时&#xff0c;随着次数初始的增多&#xff0c;模型的准确率会逐渐的上升&#xff0c;但是同时也随着迭代次数越来越多&#xff0c;由于模型会开始学…

STM32 软件触发ADC采集

0.91寸OLED屏幕大小的音频频谱&#xff0c;炫酷&#xff01; STM32另一个很少人知道的的功能——时钟监测 晶振与软件的关系&#xff08;深度理解&#xff09; STM32单片机一种另类的IO初始化方法 ADC是一个十分重要的功能&#xff0c;几乎任何一款单片机都会包含这个功能&a…

C++ 游戏开发

C游戏开发 C 是一种高效、灵活且功能强大的编程语言&#xff0c;因其性能和控制能力而在游戏开发中被广泛应用。许多著名的游戏引擎&#xff0c;如 Unreal Engine、CryEngine 和 Godot 等&#xff0c;都依赖于 C 进行核心开发。本文将详细介绍 C 在游戏开发中的应用&#xff0…

网页钓鱼---钓鱼网页的制作与危害上线

免责声明: 本文仅供了解攻击方攻击手法反制使用&#xff0c;切勿用于非法用途 前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 基础能力要求 如果仅仅造个页面的话&#xff0c;不需要什么基础。但是如果想要让这个钓鱼页面能够真正实现攻击&#xff0c;得要求制…