如何理解站在CPU角度和站在NOR FLASH角度对NOR FLASH进行操作_cpu和nor flash命令操作-程序员宅基地

技术标签: ARM9  S3C2440  LINUX  NOR FLASH  

前言:

我觉得,用s3c2440对NOR FLASH进行操作,在代码编写上没什么特别的难度,主要是NOR FLASH说明书的理解,还有根据代码整理一下如何理解站在CPU角度和站在NOR FLASH角度对地址进行操作。这里就不对NOR FLASH的原理进行介绍,直接进入主题。

NOR FLASH

CPU只能通过地址对NOR FLASH直接进行读取,无法像SDRAM一样通过寻址直接对NOR FLASH进行写入。比较一下NOR FLASH和NAND FLASH的优劣势,下一篇整理NAND FLASH的操作。
在这里插入图片描述
NOR FLASH的优势是稳定,保存的内容不会丢失,并且只能进行直接读取,所以适合用于保存执行代码。

S3C2440的NOR FLASH配置:

在这里插入图片描述
数据线看可以看出是一个16位的数据传送,A1-A0…A20-A19,S3C2440的A0没接,存储器的A20没接。存储器的A0虽然没接,但是有作为高低字节判断的作用,总共是A0~A20,21根线在起作用,所以这张芯片这样接,最多可以接收2M的不同数据。片选引脚是nGCS0,所以它在CPU内的地址范围是0x00000000-0x001FFFFF
在这里插入图片描述
我的NOR FLASH是挂在第0bank上的,压根就没有控制寄存器给你设置,只能通过硬件OM[1:0]进行选择16bit还是32bit,我这里是选择16bit。在这里插入图片描述
这是S3C2440对外部存储器的访问时许图,里边的这些时间可以根据外接的存储芯片厂商提供的时序图进行不同的修改。分为读时序和写时序设置。
读的时序一般是:设置地址=>拉低片选=>拉低读使能=>读取数据=>拉高读使能=>拉高片选
写的时序一般是:设置地址和写入数据=>拉低片选=>拉低写使能=>拉高写使能=>拉高片选在这里插入图片描述
在这里插入图片描述
从时序图可以读出,设置完地址后,经过等待Taa时间可以读取数据,这个Taa时间最大70ns,也就是说,最多等待70ns后,你读出来的数据就保证是有效的,你也可以在50ns的时候读,但不保证读出来的数据是有效的。
片选信号拉底后,经过等待Tce时间可以读取数据,这个Tce时间最大是70ns,也就是说,最多等待70ns后,你读出来的数据就保证是有效的
读取信号拉底后,经过等待Toe时间可以读取数据,这个Tce时间最大是30ns,也就是说,最多等待30ns后,你读出来的数据就保证是有效的
其实这个芯片,对于地址设置和CE使能的时间间隙,CE使能和OE使能的时间间隙并没有做出具体的限定,倒是全部对地址设置后Taa可以读取到有效数据,CE使能后Toe可以读取到有效数据,OE使能后Toe可以读取到数据做了具体的限定。所以干脆将地址置、CE使能、OE使能全部一起开始,然后取Taa,Tce,Toe的最大值70ns后,获取数据。也就是Tacs=0,Tcos=0,Tacc=70ns在这里插入图片描述
寄存器根据上面分析的Tacs=0,Tcos=0,Tacc=70ns照着填进去就对了。如果实在想省事,全部保持默认也可以,就是Tacc默认是14clocks,可能在读取程序运行代码的时候会慢一点,不过NOR FLASH不能随心所欲的进行写操作,大部分都是在启动的汇编文件里面,就把NOR FLASH的运行代码读出来,COPY到SDRAM上了。可能就是在读取跟COPY这段时间会长一点,不过如果代码量不是很大,基本也感觉不出来。

说明书概览:

在这里插入图片描述
NOR FLASH除了直接读取以外,其他操作都需要写命令进行操作。
读取NOR FLASH的ID,写入,整片擦除,扇区擦除,切换到CFI模式,都需要根据表格中的命令进行写入。这里涉及到一个非常重要的点,当我们选择以word半字写入,例如在0x555写入0xAA那么这个地址0x555是从NOR FLASH角度来看的0x555地址,也就是说NOR FLASH的地址线需要接收到的地址电平是‭010101010101‬,但是,NOR FLASH在硬件连接上CPU的R1连接NOR FLASH的A0,所以NOR FLASH要得到0x555的电平,CPU发出的地址需要左移1个位。这是从站在NOR FLASH的角度看的地址。
在这里插入图片描述在这里插入图片描述
进入到CFI模式后,可以通过以上地址,读取一些NOR FLASH的基本信息,例如ID,容量,块区数量,以及每个块区的信息(每个块区的信息还要参考CFI publication 100)这个说明,截图了CFI publication 100里面我们要的信息:在这里插入图片描述
我这种菜B英语水平,半猜半读半翻译,大概意思就是:
[15:0] + 1 表示这个块区内有多少Block扇区,[31:16] * 256表示每个Block的大小是多少byte。NOR FLASH里面的每个块区里面包含的扇区数量都不一样,但是同一个块区里面的每个扇区的容量大小是一样的。例如,块区1可能包含5个Block,块区2可能包含2个Block,但是块区1里面的每个Block容量都是一样1M,块区2里面的每个Block容量都是
一样2M。

代码分析:

void norflash_test()
{
	char c;
	printf("NOR FLASH Menu :\r\n");
	printf("[s] Scan NOR FLASH\r\n");
	printf("[e] Earse NOR FLASH\r\n");
	printf("[w] Write NOR FLASH\r\n");
	printf("[r] Read NOR FLASH\r\n");
	printf("[q] quit Menu\r\n");
	printf(" Please Enter The Option :\r\n\r\n");

	c = getchar();		//获取输入字符
	putchar(c);			//回显
	
	while((c != 's') && (c != 'S') && (c != 'e') && (c != 'E') && (c != 'w') && (c != 'W') && (c != 'r') && (c != 'R') && (c != 'q') && (c != 'Q'))
	{
		printf(" \r\nPlease Enter validity Option :\r\n");
		c = getchar();		//获取输入字符
		putchar(c);			//回显
	}

	switch(c)
	{
		case 's':
		case 'S':
			do_scan_norflash();
			break;
		case 'e':
		case 'E':
			do_earse_norflash();
			break;
		case 'w':
		case 'W':
			do_Write_norflash();
			break;
		case 'r':
		case 'R':
			do_Read_norflash();
			break;
		case 'q':
		case 'Q':
			break;
	}
}

这个是一个测试菜单,会放到main的一个死循环里面,getchar();这个函数在没接收到串口数据时,就一直while(1)死循环了,也不会往下走了,写入什么字符switch跳转去执行什么功能子函数。这个不是重点,可以跳过不看。

#define	NORFLASH_BASE 0

static void Norflash_CMD(unsigned short int offset,unsigned short int data)
{
	volatile unsigned short int *p = (volatile unsigned short int *)(NORFLASH_BASE + (offset<<1));
	*p = data;
}


static int Norflash_READ(unsigned short int offset)
{
	volatile unsigned short int *p = (volatile unsigned short int *)(NORFLASH_BASE + (offset<<1));
	return *p;
}

static void wait_ready(unsigned int addr)
{
	unsigned int val;
	unsigned int pre;

	pre = Norflash_READ(addr>>1);
	val = Norflash_READ(addr>>1);
	while ((val & (1<<6)) != (pre & (1<<6)))
	{
		pre = val;
		val = Norflash_READ(addr>>1);		
	}
}

抽象出来写入、读取和判断标志位的函数。Norflash_CMD是站在NOR FLASH角度往地址里面写入数据。写入的地址是offset,因为NOR FLASH是挂在片选1上的,所以NORFLASH_BASE可加可不加,但是从框架上看,是最好要加上。函数参数offset可以修饰成unsigned short int,也可以是unsigned int,从表格上看那些指令地址反正都不超过16位。但是offset转换成指针,一定是要转换成16位的指针,NOR FLASH本身就是16位的数据读取,它指针的步长肯定是要16位的。
还有一点就是,地址在这里是站在NOR FLASH的角度看的,所以地址需要左移1位。
wait_ready(unsigned int addr)这个函数判断写入和擦除是否已经成功

static void do_scan_norflash()
{
	int i,j,r;
	unsigned int ManufacturerCode,DeviceCode,NorflashSize,regions,BlockNum,BlockSize,BlockStarAddr;
	short int RegionsBase = 0x2D;
	
	/* 打印厂家ID,设备ID */
	Norflash_CMD(0x555,0xAA);			//解锁
	Norflash_CMD(0x2AA,0x55);
	Norflash_CMD(0x555,0x90);			//命令
	ManufacturerCode = Norflash_READ(0x00);  //获取厂家ID
	DeviceCode = Norflash_READ(0x1);		//获取设备ID
	Norflash_CMD(0x0,0xF0);				//往任意地址写进F0退出
	
	Norflash_CMD(0x55,0x98);			//进入CFI模式
	NorflashSize = (1 << Norflash_READ(0x27)) / 1024 / 1024;	//获得Norflash大小
	regions = Norflash_READ(0x2C);      //regions数量
	printf("\r\nManufacturerCode: %x DeviceCode: %x RegionNum: %x NorflashSize: %dM\r\n",
		ManufacturerCode,DeviceCode,regions,NorflashSize);

	/* 前两个字节+1是每个regions里面的扇区Block的个数 
	   后两个字节*256是每个Block扇区的大小
	*/
	printf("Block Start Addresses :\r\n");
	for(i = 0, r = 0,BlockStarAddr = 0;i < regions;i++)
	{
		BlockNum = (Norflash_READ(RegionsBase) | (Norflash_READ(RegionsBase+1)<<8)) + 1;
		BlockSize = (Norflash_READ(RegionsBase+2) | (Norflash_READ(RegionsBase+3)<<8))*256;
		RegionsBase += 4;
		for(j = 0; j < BlockNum; j++)
		{
			//printf("%08x",BlockStarAddr);
			printHex(BlockStarAddr);
			printf("  ");
			r++;
			BlockStarAddr += BlockSize;
			if(r == 5)
			{
				r = 0;
				printf("\r\n");
			}
		}
	}
	Norflash_CMD(0x0,0xF0);				//往任意地址写进F0退出
}

do_scan_norflash()就是打印一些NOR FLASH的基本信息,这里难度比较大的就是通过进入CFI模式,读取多少个块区,每个块区里面多少个扇区,每个扇区的大小是多少,从而通过计算,打印出每个扇区的起始地址。上面的说明书分析已经有介绍了怎么去计算,再结合代码看一下,各位看官应该不难理解。
从串口里面得出的结果是这样的
在这里插入图片描述
块区region数量是4个,norflash的容量是2M,终于到了重点了,重点就是这个每一个扇区的起始地址,第0个扇区肯定是从0x00000000开始的,然后第0个扇区的大小是0x4000Byte(16K),所以第1个扇区的起始地址是0x00004000,以上所有的这些计算都是CPU自己通过读取ID信息,自己计算出来的,换句话说,就是CPU自己在意淫,人家NOR FLASH只给你提供了一些容量Size而已,是你自己非要这么算的,是站在CPU的角度来看,是CPU认为你的第1个扇区的地址应该是在CPU的0x00004000,这些地址不像指令表里面那些地址,是给你指定好是往NOR FLASH的哪个地址填入哪个值,指令表那些地址是站在NOR FLASH的角度来看的,所以站在CPU角度看地址和站在NOR FLASH角度看地址是我觉得整个NOR FLASH里面最难理解的地方。
结合擦除代码再来深刻理解一下:

static void do_earse_norflash()
{
	unsigned int BlockStartAddr;
	printf("\r\nPlease Input Earse Block Start Addresses:\r\n");
	BlockStartAddr = get_uint();		//获取输入的地址
	Norflash_CMD(0x555,0xAA);			//解锁
	Norflash_CMD(0x2AA,0x55);
	Norflash_CMD(0x555,0x80);			//命令
	Norflash_CMD(0x555,0xAA);			//解锁
	Norflash_CMD(0x2AA,0x55);
	//Norflash_CMD(BlockStartAddr >> 1,0x30);   //擦除,最后优化成下面这句
   *((volatile unsigned short int *)BlockStartAddr) = 0x30;//擦除
	wait_ready(BlockStartAddr);
	Norflash_CMD(0x0,0xF0);				//往任意地址写进F0退出
	printf("Earse Accomplish\r\n");
}

这是擦除扇区的功能函数。难点在于Norflash_CMD(BlockStartAddr >> 1,0x30);这个地址的选取。主要是BlockStartAddr这个地址,要站在什么角度看。例如我们要擦除的是第1块扇区的地址:
站在CPU的角度看:
我们上面已经从CPU的角度计算出来了,如果要擦除第1块扇区,那么扇区的首地址是
0x00004000,所以要往0x00004000地址里面写0x30,因为Norflash_CMD()这个函数里面有对地址进行向左移动一位,所以要写进0x00004000要先向右移动1位来抵消函数内向左移动一位
站在NORFLASH的角度看:
第0扇区一样都是从0x00000000开始,第0扇区的大小是16K,那么NOR FLASH里面每个地址是16bit的大小,0扇区有16K,计算一下是 (16 * 1024)byte,除以每个地址2byte,所以第1个扇区的起始地址是0x00002000。你也可以orflash_CMD(0x00002000, 0x30);那换算起来不是一样嘛,Norflash_CMD()里面对地址左移一位,不也是0x00004000。

但是后来调试的时候,发现了一个问题:
Norflash_CMD(BlockStartAddr >> 1,0x30);
改为 *((volatile unsigned short int *)BlockStartAddr) = 0x30;
Norflash_CMD(unsigned short int offset,unsigned short int data)这个函数的offset参数是无符号16bit的,如果BlockStartAddr超过16位,会被截取成16位,另外,BlockStartAddr >> 1可能会把第0位的值给移除没了,所以干脆直接往地址站在CPU角度的地址里面写值,就不再经过函数进行转换了。或者你可以把Norflash_CMD(unsigned short int offset,unsigned short int data)改成Norflash_CMD(unsigned int offset,unsigned short int data)。

擦除是按一整个Block进行擦除的,但是NOR FLASH有一个比较特殊的地方,就是例如擦除第29块BLOCK,该BLOCK的起始地址是0x001A0000,正常来讲要擦除第29块BLOCK,那么应该输入这个BLOCK的起始地址才能有效擦除,但是NOR FLASH的擦除,就算写入的不是起始地址,而是任何一个地址,这个地址属于哪一块BLOCK,哪一块BLOCK就会被擦除。例如擦除第29块BLOCK,你也可以写擦除地址为0x001A0004。这就更有必要优化上面的代码了,因为输入的地址可以不再是1024的整数倍,可以是任意地址,左移会把最低位给移除了。

static void do_Write_norflash()
{
	unsigned char i = 0, j = 0;
	unsigned short int*  WriteAddr = (void*)0;
	unsigned char WriteData[100];
	printf("\r\nPlease Input Write Addresses:\r\n");
	WriteAddr = (unsigned short int*)get_uint();		//获取输入的地址
	printf("\r\nPlease Input Write Data:\r\n");
	gets(WriteData);
	while((*(WriteData + j) != '\0') && (*(WriteData + j + 1) != '\0'))
	{
		Norflash_CMD(0x555,0xAA);			//解锁
		Norflash_CMD(0x2AA,0x55);
		Norflash_CMD(0x555,0xA0);			//命令
		//写入内容要拼凑成16位的
		*(WriteAddr+i+NORFLASH_BASE) = (unsigned short int)(*(WriteData+j) | (*(WriteData+j+1)<<8));
		wait_ready((unsigned int)WriteAddr+i+NORFLASH_BASE);
		i += 1;
		j += 2;
		Norflash_CMD(0x0,0xF0);				//往任意地址写进F0退出
	}
	/* 停止可能:前一个字节是'\0'
		    后一个字节才是'\0' */
	Norflash_CMD(0x555,0xAA);			//解锁
	Norflash_CMD(0x2AA,0x55);
	Norflash_CMD(0x555,0xA0);			//命令
	if(*(WriteData + j) == '\0')   *(WriteAddr+i+NORFLASH_BASE) = '\0';
	else if (*(WriteData + j + 1) == '\0') *(WriteAddr+i+NORFLASH_BASE) = (unsigned short int)(*(WriteData + j) | ('\0' << 8));
	wait_ready((unsigned int)WriteAddr+i+NORFLASH_BASE);
	Norflash_CMD(0x0,0xF0);				//往任意地址写进F0退出
	printf("Write Accomplish\r\n");
}

这是一个写数据到NOR FLASH的函数。get_uint()这个函数功能是从串口获取一组数字,获取到的这串数字强制转换成(unsigned short int*),一定是要转换成16位的,gets(WriteData);是从串口获取一串字符串,字符串的每个字符存入数组WriteData里面。NOR FLASH的写入是解锁一次,写入一个半字,然后再解锁再写入。这里需要注意的是,我们获取到的字符串拆分成一个一个字符存进数组里,数组是一个unsigned char型,但是写进NOR FLASH要写进16bit的数据,所以写之前,要先把8bit拼凑成16bit。这里的地址是站在CPU的角度看的,所以解锁完,我干脆直接用寻址的方式直接写入了,不去调用Norflash_CMD()函数了,每次写完,数组的偏移j 是加2,地址的偏移i是加1(因为WriteAddr被定义为unsigned short int*)。
到了最后,因为数组是两两拼凑,有可能是前一个字节就是结束符,也有可能是后一个字符才是结束符,所以要区分对末尾的结束符进行处理。
NOR FLASH的写,可以连着写,编程不需要去考虑换Block区域的操作,直接写NOF FLASH就会自动换页了。

static void do_Read_norflash()
{
	char i,j;
	unsigned int ReadAddress;
	unsigned char GetDataArray[16];
	volatile unsigned char *p = (void*)0;
	printf("\r\nPlease Input Read Addresses:\r\n");
	ReadAddress = get_uint();		//获取输入的地址
	p = (volatile unsigned char *)ReadAddress;
	for(i = 0;i < 4;i++)
	{
		for(j = 0;j < 16;j++)
		{
			GetDataArray[j] = *p++;
			printf("%02x ",GetDataArray[j]);
		}
		printf("  ;  ");
		for(j = 0;j < 16;j++)
		{
			if((GetDataArray[j] >= 32) && (GetDataArray[j] <= 126))
					printf("%c",GetDataArray[j]);
			else printf(".");
		}
		printf("\r\n");
	}

读是不需要解锁就可以直接读了,站在CPU的角度进行读取的,所以直接寻址直接读了,没再调用Norflash_READ函数。这里读的我是按字节读出来的,所以p是(volatile unsigned char *)类型(虽然R0没接,但是是起到对16bit数据高低位判断的作用,所以是可以以byte进行读取的,内存控制器会自动处理)。然后每读取16个字节,打印显示的字节16进制数以外,还打印了可显示字符,ASCII里面可显示字符的范围是32~126。
这是读出来的数据的一个效果图:
在这里插入图片描述
在理解站在CPU角度还是NOR FLASH角度看待地址的理解上,我也找了不少资料,但是很少有讲的清楚的,以上纯粹是我自己的个人理解,仅供大家参考一下,如果大家有更好的,一针见血的理解方式,欢迎评论讨论一下。
以上很多知识都是通过看视频,和查找资料,外加自己的理解总结而成的,图片也是自己画的和截图的,肯定存在些许错误,如果哪位大神有发现错误的地方,请及时指正。
——冷亦花烟_CYB(菜蔡)
2019.2.25 22:34

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_31216691/article/details/87926500

智能推荐

Redis面试篇-程序员宅基地

文章浏览阅读1.3k次,点赞29次,收藏20次。缓存穿透:查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都会去查数据库,数据库的压力增大。缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮。缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。客户端对数据库中的数据主要有两类操作,读(select)与写(DML)。缓存由于其高并发和高性能的特性,已经在项目中被广泛使用。

这种格式的时间 2022-08-12 12:13:00 ,判断它是今天还是明天,还是几天后_["2023-12-08 13:45","2023-12-09 13:45"]取时间-程序员宅基地

文章浏览阅读98次。这种格式的时间 2022-08-12 12:13:00 ,判断它是今天还是明天,还是几天后_["2023-12-08 13:45","2023-12-09 13:45"]取时间

【MySQL】Error 2006:MYSQL server has gone away_mysql5.7 [err] 2006 - mysql server has gone awa-程序员宅基地

文章浏览阅读369次。问题的描述MFC应用程序远程访问MYSQL数据库,程序初始化时创建一个连接池,通常保存10个连接用于访问数据库使用。该程序平时运行正常,但是程序正常使用后闲置一个晚上不关闭,第二天第一次使用报错如标题。分析使用命令查询数据库超时参数,服务器安装MYSQL 数据库版本为5.7.22。查询命令:show global variables like '%timeout';..._mysql5.7 [err] 2006 - mysql server has gone awa

最强Android入门开发指南,帮你打通Android的任督二脉_android 开发指南-程序员宅基地

文章浏览阅读687次。Android 新手想要入门,很容易会遇到各类困难和学习瓶颈。没有一个好学的学习方向,学习规划,学习教程,这都是新手会面临的问题。 很多人会在百度上搜索,查阅相关资料。但是网上搜索的很多资料,都是断片式的学习,缺乏完整性和系统性。那么新手应该从何学起?这样学习呢?这里给大家一份最强Android入门指南:_android 开发指南

PHP程序运行流程:词法分析(Lexing,Tokenizing,Scanning)_phpscanning-程序员宅基地

文章浏览阅读1k次。在不开启 Opcache 的情况下,PHP解释器在解释PHP脚本的时候,首先会经过词法分析(Lexing),而词法分析的具体实现就是将PHP代码转换成 Tokens,此过程成为 Lexing / Tokenizing / Scanning 。那么 Tokens 是啥样的呢,Lex就是一个词法分析的依据表。 Zend/zend_language_scanner.c会根据Zend/zend_language_scanner.l (Lex文件),来输入的 PHP代码进行词法分析,从而得到一个一个的“词”,PHP_phpscanning

编程语言数值型和字符型数据的概念_数值 字符-程序员宅基地

文章浏览阅读1.7k次。在编程语言中区分变量的数据类型;最简单的是数值型和字符型;以SQL为例;新建一个表如下图;name列是字符型,age列是数值型;保存表名为pp;录入如下图的数据;看这里name列输入的‘123’、'789',这些是字符型的数据;age输入的内容是数值型;显示结果如下;因为age列是数值型,输入的 009 自动变为了 9;写查询语句时字符型数据按语法规则是用引号括起来;如果如下图写也可以运行出结果;是因为sqlserver本身具有一定的智能识别功能;写比较长的SQL语句_数值 字符

随便推点

算法中的各种距离(欧式距离,马氏距离,闵可夫斯基距离......)_马氏距离中间那个矩阵是啥-程序员宅基地

文章浏览阅读1.9w次,点赞7次,收藏60次。在做分类时常常需要估算不同样本之间的相似性度量(SimilarityMeasurement),这时通常采用的方法就是计算样本间的“距离”(Distance)。采用什么样的方法计算距离是很讲究,甚至关系到分类的正确与否。  本文的目的就是对常用的相似性度量作一个总结。本文目录:1.欧氏距离2.曼哈顿距离3. 切比雪夫距离4. 闵可夫斯基距离_马氏距离中间那个矩阵是啥

kettle 提交数据量_kettle——入门操作(表输出)详细-程序员宅基地

文章浏览阅读820次。表输出控件如下1)步骤名称,2)数据库连接,前面有过部分解释3)目标模式,数据库中的概念,引用:https://www.cnblogs.com/csniper/p/5509620.html(感谢)4)目标表:数据库中的表,这里有两种方式:(1) 应用数据库中已经存在的表,浏览表选中对应表即可,下图有部分sql功能。ddl可以执行ddl语句。(2) 创建新的表,填写表的名字,点击下面的sql就可以执..._kettle 步骤 提交

Sublime 多行编辑快捷键_submlite 同时操作多行 macos-程序员宅基地

文章浏览阅读4.4k次,点赞2次,收藏2次。鼠标选中多行,按下 widows 下 Ctrl Shift L( Mac下 Command Shift L)即可同时编辑这些行;鼠标选中文本,反复按widows 下CTRL D(Mac下 Command D)即可继续向下同时选中下一个相同的文本进行同时编辑;鼠标选中文本,按下Alt F3(Win)或Ctrl Command G(Mac)即可一次性选择全部的相同文本进行同时编辑;..._submlite 同时操作多行 macos

如何双启动Linux和Windows-程序员宅基地

文章浏览阅读252次。尽管Linux是具有广泛硬件和软件支持的出色操作系统,但现实是有时您必须使用Windows,这可能是由于关键应用程序无法在Linux下运行。 幸运的是,双重引导Windows和Linux非常简单-本文将向您展示如何使用Windows 10和Ubuntu 18.04进行设置。 在开始之前,请确保已备份计算机。 尽管双启动设置过程不是很复杂,但是仍然可能发生事故。 因此,请花点时间备份您的重要..._windows linux双启动

【flink番外篇】1、flink的23种常用算子介绍及详细示例(1)- map、flatmap和filter_flink 常用的分类和计算-程序员宅基地

文章浏览阅读1.6w次,点赞25次,收藏20次。本文主要介绍Flink 的3种常用的operator(map、flatmap和filter)及以具体可运行示例进行说明.将集合中的每个元素变成一个或多个元素,并返回扁平化之后的结果。按照指定的条件对集合中的元素进行过滤,过滤出返回true/符合条件的元素。本文主要介绍Flink 的3种常用的operator及以具体可运行示例进行说明。这是最简单的转换之一,其中输入是一个数据流,输出的也是一个数据流。下文中所有示例都是用该maven依赖,除非有特殊说明的情况。中了解更新系统的内容。中了解更新系统的内容。_flink 常用的分类和计算

(转)30 IMP-00019: row rejected due to ORACLE error 12899-程序员宅基地

文章浏览阅读590次。IMP-00019: row rejected due to ORACLE error 12899IMP-00003: ORACLE error 12899 encounteredORA-12899: value too large for column "CRM"."BK_ECS_ORDER_INFO_00413"."POSTSCRIPT" (actual: 895, maximum..._row rejected due to oracle

推荐文章

热门文章

相关标签