C语言嵌入式系统编程修炼之一:背景篇!....1

C语言嵌入式系统编程修炼之二:软件架构篇!....3

C语言嵌入式系统编程修炼之三:内存操作!....10

C语言嵌入式系统编程修炼之四:屏幕操作!....17

C语言嵌入式系统编程修炼之五:键盘操作!....25

C语言嵌入式系统编程修炼之六:性能优化!....29

 

 

C语言嵌入式系统编程修炼之一:背景篇!

作者:宋宝华

      不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上,势必要求其编程语言具备较强的硬件直接操作能力。无疑,汇编语言具备这样的特质。但是,归因于汇编语言开发过程的复杂性,它并不是嵌入式系统开发的一般选择。而与之相比,C语言--一种"高级的低级"语言,则成为嵌入式系统开发的最佳选择。笔者在嵌入式系统项目的开发过程中,一次又一次感受到C语言的精妙,沉醉于C语言给嵌入式开发带来的便利。

图1给出了本文的讨论所基于的硬件平台,实际上,这也是大多数嵌入式系统的硬件平台。它包括两部分:

(1) 以通用处理器为中心的协议处理模块,用于网络控制协议的处理;

(2) 以数字信号处理器(DSP)为中心的信号处理模块,用于调制、解调和数/模信号转换。

本文的讨论主要围绕以通用处理器为中心的协议处理模块进行,因为它更多地牵涉到具体的C语言编程技巧。而DSP编程则重点关注具体的数字信号处理算法,主要涉及通信领域的知识,不是本文的讨论重点。

着眼于讨论普遍的嵌入式系统C编程技巧,系统的协议处理模块没有选择特别的CPU,而是选择了众所周知的CPU芯片--80186,每一位学习过《微机原理》的读者都应该对此芯片有一个基本的认识,且对其指令集比较熟悉。80186的字长是16位,可以寻址到的内存空间为1MB,只有实地址模式。C语言编译生成的指针为32位(双字),高16位为段地址,低16位为段内编译,一段最多64KB。


  协议处理模块中的FLASH和RAM几乎是每个嵌入式系统的必备设备,前者用于存储程序,后者则是程序运行时指令及数据的存放位置。系统所选择的FLASH和RAM的位宽都为16位,与CPU一致。

实时钟芯片可以为系统定时,给出当前的年、月、日及具体时间(小时、分、秒及毫秒),可以设定其经过一段时间即向CPU提出中断或设定报警时间到来时向CPU提出中断(类似闹钟功能)。

NVRAM(非易失去性RAM)具有掉电不丢失数据的特性,可以用于保存系统的设置信息,譬如网络协议参数等。在系统掉电或重新启动后,仍然可以读取先前的设置信息。其位宽为8位,比CPU字长小。文章特意选择一个与CPU字长不一致的存储芯片,为后文中一节的讨论创造条件。

UART则完成CPU并行数据传输与RS-232串行数据传输的转换,它可以在接收到[1~MAX_BUFFER]字节后向CPU提出中断,MAX_BUFFER为UART芯片存储接收到字节的最大缓冲区。

键盘控制器和显示控制器则完成系统人机界面的控制。

以上提供的是一个较完备的嵌入式系统硬件架构,实际的系统可能包含更少的外设。之所以选择一个完备的系统,是为了后文更全面的讨论嵌入式系统C语言编程技巧的方方面面,所有设备都会成为后文的分析目标。

嵌入式系统需要良好的软件开发环境的支持,由于嵌入式系统的目标机资源受限,不可能在其上建立庞大、复杂的开发环境,因而其开发环境和目标运行环境相互分离。因此,嵌入式应用软件的开发方式一般是,在宿主机(Host)上建立开发环境,进行应用程序编码和交叉编译,然后宿主机同目标机(Target)建立连接,将应用程序下载到目标机上进行交叉调试,经过调试和优化,最后将应用程序固化到目标机中实际运行。

CAD-UL是适用于x86处理器的嵌入式应用软件开发环境,它运行在Windows操作系统之上,可生成x86处理器的目标代码并通过PC机的COM口(RS-232串口)或以太网口下载到目标机上运行,如图2。其驻留于目标机FLASH存储器中的monitor程序可以监控宿主机Windows调试平台上的用户调试指令,获取CPU寄存器的值及目标机存储空间、I/O空间的内容。


  后续章节将从软件架构、内存操作、屏幕操作、键盘操作、性能优化等多方面阐述C语言嵌入式系统的编程技巧。软件架构是一个宏观概念,与具体硬件的联系不大;内存操作主要涉及系统中的FLASH、RAM和NVRAM芯片;屏幕操作则涉及显示控制器和实时钟;键盘操作主要涉及键盘控制器;性能优化则给出一些具体的减小程序时间、空间消耗的技巧。

在我们的修炼旅途中将经过25个关口,这些关口主分为两类,一类是技巧型,有很强的适用性;一类则是常识型,在理论上有些意义。

C语言嵌入式系统编程修炼之二:软件架构篇!

作者:宋宝华

模块划分

  模块划分的"划"是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到了>相对论),C语言模块化程序设计需理解如下概念:

(1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;

(2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;

(3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;

(4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:

/*module1.h*/
int a = 5; /*
在模块1.h文件中定义
int a */

/*module1 .c*/
#include "module1.h" /*
在模块1中包含模块1.h文件
*/

/*module2 .c*/
#include "module1.h" /*
在模块2中包含模块1.h文件
*/

/*module3 .c*/
#include "module1.h" /*
在模块3中包含模块1.h文件 */


  以上程序的结果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这个世界上从来不需要这样的程序。正确的做法是:

/*module1.h*/
extern int a; /*
在模块1.h文件中声明
int a */

/*module1 .c*/
#include "module1.h" /*
在模块1中包含模块1.h文件
*/
int a = 5; /*
在模块1.c文件中定义
int a */

/*module2 .c*/
#include "module1.h" /*
在模块2中包含模块1.h文件
*/

/*module3 .c*/
#include "module1.h" /*
在模块3中包含模块1.h文件 */


  这样如果模块1、2、3操作a的话,对应的是同一片内存单元。

一个嵌入式系统通常包括两类模块:

(1)硬件驱动模块,一种特定硬件对应一个模块;

(2)软件功能模块,其模块的划分应满足低偶合、高内聚的要求。

多任务还是单任务

  所谓"单任务系统"是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。

多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调度器,它使用任务控制块(TCB)来管理任务调度功能。TCB包括任务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时,要用到这些信息。此外,TCB还被用来存放任务的"上下文"(context)。任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的状态,也即各个寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存入TCB,并将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。

嵌入式多任务OS的典型例子有Vxworks、ucLinux等。嵌入式OS并非遥不可及的神坛之物,我们可以用不到1000行代码实现一个针对80186处理器的功能最简单的OS内核,作者正准备进行此项工作,希望能将心得贡献给大家。

究竟选择多任务还是单任务方式,依赖于软件的体系是否庞大。例如,绝大多数手机程序都是多任务的,但也有一些小灵通的协议栈是单任务的,没有操作系统,它们的主程序轮流调用各个软件模块的处理程序,模拟多任务环境。
单任务程序典型架构

  (1)从CPU复位时的指定地址开始执行;

(2)跳转至汇编代码startup处执行;

(3)跳转至用户主程序main执行,在main中完成:

a.初试化各硬件设备;

b.初始化各软件模块;

c.进入死循环(无限循环),调用各模块的处理函数

用户主程序和各模块的处理函数都以C语言完成。用户主程序最后都进入了一个死循环,其首选方案是:

while(1)
{
}


  有的程序员这样写:

for(;;)
{
}


  这个语法没有确切表达代码的含义,我们从for(;;)看不出什么,只有弄明白for(;;)在C语言中意味着无条件循环才明白其意。

下面是几个"著名"的死循环:

(1)操作系统是死循环;

(2)WIN32程序是死循环;

(3)嵌入式系统软件是死循环;

(4)多线程程序的线程处理函数是死循环。

你可能会辩驳,大声说:"凡事都不是绝对的,2、3、4都可以不是死循环"。Yes,you are right,但是你得不到鲜花和掌声。实际上,这是一个没有太大意义的牛角尖,因为这个世界从来不需要一个处理完几个消息就喊着要OS杀死它的WIN32 程序,不需要一个刚开始RUN就自行了断的嵌入式系统,不需要莫名其妙启动一个做一点事就干掉自己的线程。有时候,过于严谨制造的不是便利而是麻烦。君不见,五层的TCP/IP协议栈超越严谨的ISO/OSI七层协议栈大行其道成为事实上的标准?

经常有网友讨论:

printf("%d,%d",++i,i++); /*输出是什么?*/
c = a+++b; /* c=? */


  等类似问题。面对这些问题,我们只能发出由衷的感慨:世界上还有很多有意义的事情等着我们去消化摄入的食物。

实际上,嵌入式系统要运行到世界末日。

中断服务程序

  中断是嵌入式系统中重要的组成部分,但是在标准C中不包含中断。许多编译开发商在标准C上增加了对中断的支持,提供新的关键字用于标示中断服务程序 (ISR),类似于__interrupt、#program interrupt等。当一个函数被定义为ISR的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。

中断服务程序需要满足如下要求:

(1)不能返回值;

(2)不能向ISR传递参数;

(3) ISR应该尽可能的短小精悍;

(4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。

在某项目的开发中,我们设计了一个队列,在中断服务程序中,只是将中断类型添加入该队列中,在主程序的死循环中不断扫描中断队列是否有中断,有则取出队列中的第一个中断类型,进行相应处理。

/*存放中断的队列 */
typedef struct tagIntQueue
{
 int intType; /*中断类型
*/
 
struct tagIntQueue *next;
}IntQueue;

IntQueue lpIntQueueHead;

__interrupt ISRexample ()
{
 
int intType;
 
intType = GetSystemType();
 QueueAddTail(lpIntQueueHead, intType)/*在队列尾加入新的中断
*/
}


  在主程序循环中判断是否有中断:

While(1)
{
 
If( !IsIntQueueEmpty() )
 
{
  
intType = GetFirstInt();
  switch(intType) /*是不是很象WIN32程序的消息解析函数
? */
  
{
   /*对,我们的中断类型解析很类似于消息驱动
*/
   case xxx: /*我们称其为"中断驱动"吧?
*/
    

    
break;
   
case xxx:
    

    
break;
   

  
}
 
}
}


  按上述方法设计的中断服务程序很小,实际的工作都交由主程序执行了。
硬件驱动模块

  一个硬件驱动模块通常应包括如下函数:

(1)中断服务程序ISR

(2)硬件初始化

a.修改寄存器,设置硬件参数(如UART应设置其波特率,AD/DA设备应设置其采样速率等);

b.将中断服务程序入口地址写入中断向量表:

/*设置中断向量表 */
m_myPtr = make_far_pointer(0l); /*
返回void far型指针
void far * */
m_myPtr += ITYPE_UART; /* ITYPE_UART
uart中断服务程序
*/
/*
相对于中断向量表首地址的偏移
*/
*m_myPtr = &UART _Isr; /* UART _Isr
UART的中断服务程序 */


  (3)设置CPU针对该硬件的控制线

a.如果控制线可作PIO(可编程I/O)和控制信号用,则设置CPU内部对应寄存器使其作为控制信号;

b.设置CPU内部的针对该设备的中断屏蔽位,设置中断方式(电平触发还是边缘触发)。

(4)提供一系列针对该设备的操作接口函数。例如,对于LCD,其驱动模块应提供绘制像素、画线、绘制矩阵、显示字符点阵等函数;而对于实时钟,其驱动模块则需提供获取时间、设置时间等函数。

 C的面向对象化

  在面向对象的语言里面,出现了类的概念。类是对特定数据的特定操作的集合体。类包含了两个范畴:数据和操作。而C语言中的struct仅仅是数据的集合,我们可以利用函数指针将struct模拟为一个包含数据和操作的"类"。下面的C程序模拟了一个最简单的"类":

#ifndef C_Class
#define C_Class struct
#endif
C_Class A
{
 C_Class A *A_this; /* this指针
*/
 void (*Foo)(C_Class A *A_this); /*行为:函数指针
*/
 int a; /*数据
*/
 
int b;
};


  我们可以利用C语言模拟出面向对象的三个特性:封装、继承和多态,但是更多的时候,我们只是需要将数据与行为封装以解决软件结构混乱的问题。C模拟面向对象思想的目的不在于模拟行为本身,而在于解决某些情况下使用C语言编程时程序整体框架结构分散、数据和函数脱节的问题。我们在后续章节会看到这样的例子。

总结

  本篇介绍了嵌入式系统编程软件架构方面的知识,主要包括模块划分、多任务还是单任务选取、单任务程序典型架构、中断服务程序、硬件驱动模块设计等,从宏观上给出了一个嵌入式系统软件所包含的主要元素。

请记住:软件结构是软件的灵魂!结构混乱的程序面目可憎,调试、测试、维护、升级都极度困难。

 

 

 

 

C语言嵌入式系统编程修炼之三:内存操作!

作者:宋宝华

数据指针

  在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容的读写能力。以指针直接操作内存多发生在如下几种情况:

  (1)I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应于某特定地址;

  (2)两个CPU之间以双端口RAM通信,CPU需要在双端口RAM的特定单元(称为mail box)书写内容以在对方CPU产生中断;

  (3)读取在ROMFLASH的特定单元所烧录的汉字和英文字模。

  譬如:

unsigned char *p = (unsigned char *)0xF000FF00;
*p=11;


  以上程序的意义为在绝对地址0xF0000+0xFF00(80186使用16位段地址和16位偏移地址)写入11

  在使用绝对地址指针时,要注意指针自增自减操作的结果取决于指针指向的数据类别。上例中p++后的结果是p= 0xF000FF01,若p指向int,即:

int *p = (int *)0xF000FF00;


  p++(++p)的结果等同于:p = p+sizeof(int),而p-(-p)的结果是p = p-sizeof(int)

  同理,若执行:

long int *p = (long int *)0xF000FF00;


  则p++(++p)的结果等同于:p = p+sizeof(long int) ,而p-(-p)的结果是p = p-sizeof(long int)

  记住:CPU以字节为单位编址,而C语言指针以指向的数据类型长度作自增和自减。理解这一点对于以指针直接操作内存是相当重要的。

  函数指针

  首先要理解以下三个问题:

  (1C语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;

  (2)调用函数实际上等同于"调转指令+参数传递处理+回归位置入栈",本质上最核心的操作是将函数生成的目标代码的首地址赋给CPUPC寄存器;

  (3)因为函数调用的本质是跳转到某一个地址单元的code去执行,所以可以"调用"一个根本就不存在的函数实体,晕?请往下看:

  请拿出你可以获得的任何一本大学《微型计算机原理》教材,书中讲到,186 CPU启动后跳转至绝对地址0xFFFF0(对应C语言指针是0xF000FFF00xF000为段地址,0xFFF0为段内偏移)执行,请看下面的代码:

typedef void (*lpFunction) ( ); /*定义一个无参数、无返回类型的 */
/*
函数指针类型
*/
lpFunction lpReset = (lpFunction)0xF000FFF0; /*
定义一个函数指针,指向
*/
/* CPU
启动后所执行第一条指令的位置
*/
lpReset(); /*
调用函数 */


  在以上的程序中,我们根本没有看到任何一个函数实体,但是我们却执行了这样的函数调用:lpReset(),它实际上起到了"软重启"的作用,跳转到CPU启动后第一条要执行的指令的位置。

  记住:函数无它,唯指令集合耳;你可以调用一个没有函数体的函数,本质上只是换一个地址开始执行指令!

  数组vs.动态申请

  在嵌入式系统中动态内存申请存在比一般系统编程时更严格的要求,这是因为嵌入式系统的内存空间往往是十分有限的,不经意的内存泄露会很快导致系统的崩溃。

  所以一定要保证你的mallocfree成对出现,如果你写出这样的一段程序:

char * function(void)
{
 
char *p;
 
p = (char *)malloc(…);
 
if(p==NULL)
  
…;
  … /*一系列针对p的操作
*/
 
return p;
}


  在某处调用function(),用完function中动态申请的内存后将其free,如下:

char *q = function();

free(q);


  上述代码明显是不合理的,因为违反了mallocfree成对出现的原则,即"谁申请,就由谁释放"原则。不满足这个原则,会导致代码的耦合度增大,因为用户在调用function函数时需要知道其内部细节!

  正确的做法是在调用处申请内存,并传入function函数,如下:

char *p=malloc(…);
if(p==NULL)
…;
function(p);

free(p);
p=NULL;


  而函数function则接收参数p,如下:

void function(char *p)
{
 … /*一系列针对p的操作
*/
}


  基本上,动态申请内存方式可以用较大的数组替换。对于编程新手,笔者推荐你尽量采用数组!嵌入式系统可以以博大的胸襟接收瑕疵,而无法"海纳"错误。毕竟,以最笨的方式苦练神功的郭靖胜过机智聪明却范政治错误走反革命道路的杨康。

  给出原则:

  (1)尽可能的选用数组,数组不能越界访问(真理越过一步就是谬误,数组越过界限就光荣地成全了一个混乱的嵌入式系统);

  (2)如果使用动态申请,则申请后一定要判断是否申请成功了,并且mallocfree应成对出现!
关键字const

  const意味着"只读"。区别如下代码的功能非常重要,也是老生长叹,如果你还不知道它们的区别,而且已经在程序界摸爬滚打多年,那只能说这是一个悲哀:

const int a;   //不能改变其值
int const a;   //
不能改变其值
const int *a;  //
不能通过指针间接改变指向的变量的值 指针可以重新定义
int * const a; //
可以通过指针间接改变指向的变量的值但指针不可以重新定义
int const * a const; //


  (1)关键字const的作用是为给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const关键字意味着这个参数在函数体内不会被修改,属于"输入参数"。在有多个形参的时候,函数的调用者可以凭借参数前是否有const关键字,清晰的辨别哪些是输入参数,哪些是可能的输出参数。

  (2)合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,这样可以减少bug的出现。

  constC++语言中则包含了更丰富的含义,而在C语言中仅意味着:"只能读的普通变量",可以称其为"不能改变的变量"(这个说法似乎很拗口,但却最准确的表达了C语言中const的本质),在编译阶段需要的常数仍然只能以#define宏定义!故在C语言中如下程序是非法的:

const int SIZE = 10; //运行阶段才能确定其值?
char a[SIZE]; /*
非法:编译阶段不能用到变量 */


  关键字volatile

  C语言编译器会对用户书写的代码进行优化,譬如如下代码:

int a,b,c;
a = inWord(0x100); /*
读取I/O空间0x100端口的内容存入a变量
*/
b = a;
a = inWord (0x100); /*
再次读取I/O空间0x100端口的内容存入a变量
*/
c = a;


  很可能被编译器优化为:

int a,b,c;
a = inWord(0x100); /*
读取I/O空间0x100端口的内容存入a变量
*/
b = a;
c = a;


  但是这样的优化结果可能导致错误,如果I/O空间0x100端口的内容在执行第一次读操作后被其它程序写入新值,则其实第2次读操作读出的内容与第一次不同,bc的值应该不同。在变量a的定义前加上volatile关键字可以防止编译器的类似优化,正确的做法是:

volatile int a


  volatile变量可能用于如下几种情况:

  (1)并行设备的硬件寄存器(如:状态寄存器,例中的代码属于此类);

  (2)一个中断服务子程序中会访问到的非自动变量(也就是全局变量)

  (3)多线程应用中被几个任务共享的变量。

  CPU字长与存储器位宽不一致处理

  在背景篇中提到,本文特意选择了一个与CPU字长不一致的存储芯片,就是为了进行本节的讨论,解决CPU字长与存储器位宽不一致的情况。80186的字长为16,而NVRAM的位宽为8,在这种情况下,我们需要为NVRAM提供读写字节、字的接口,如下:

typedef unsigned char BYTE;
typedef unsigned int WORD;
/*
函数功能:读NVRAM中字节

*
参数:wOffset,读取位置相对NVRAM基地址的偏移

*
返回:读取到的字节值
*/
extern BYTE ReadByteNVRAM(WORD wOffset)
{
 LPBYTE lpAddr = (BYTE*)(NVRAM + wOffset * 2); /*为什么偏移要×2? */

 
return *lpAddr;
}

/*
函数功能:读NVRAM中字

*
参数:wOffset,读取位置相对NVRAM基地址的偏移
*
返回:读取到的字
*/
extern WORD ReadWordNVRAM(WORD wOffset)
{
 WORD wTmp = 0;
 
LPBYTE lpAddr;
 /*读取高位字节
*/
 lpAddr = (BYTE*)(NVRAM + wOffset * 2); /*为什么偏移要
×2? */
 
wTmp += (*lpAddr)*256;
 /*读取低位字节
*/
 lpAddr = (BYTE*)(NVRAM + (wOffset +1) * 2); /*为什么偏移要
×2? */
 
wTmp += *lpAddr;
 
return wTmp;
}

/*
函数功能:向NVRAM中写一个字节

*
参数:wOffset,写入位置相对NVRAM基地址的偏移

* byData
,欲写入的字节
*/
extern void WriteByteNVRAM(WORD wOffset, BYTE byData)
{
 
}

/*
函数功能:向NVRAM中写一个字
*/
*
参数:wOffset,写入位置相对NVRAM基地址的偏移

* wData
,欲写入的字
*/
extern void WriteWordNVRAM(WORD wOffset, WORD wData)
{
 
}


  子贡问曰:Why偏移要乘以2?

  子曰:请看图116801868NVRAM之间互连只能以地址线A1对其A0,CPU本身的A0NVRAM不连接。因此,NVRAM的地址只能是偶数地址,故每次以0x10为单位前进!


  子贡再问:So why 80186的地址线A0不与NVRAMA0连接?

  子曰:请看《IT论语》之《微机原理篇》,那里面讲述了关于计算机组成的圣人之道。

  总结

  本篇主要讲述了嵌入式系统C编程中内存操作的相关技巧。掌握并深入理解关于数据指针、函数指针、动态申请内存、constvolatile关键字等的相关知识,是一个优秀的C语言程序设计师的基本要求。当我们已经牢固掌握了上述技巧后,我们就已经学会了C语言的99%,因为C语言最精华的内涵皆在内存操作中体现。

  我们之所以在嵌入式系统中使用C语言进行程序设计,99%是因为其强大的内存操作能力!

  如果你爱编程,请你爱C语言;

  如果你爱C语言,请你爱指针;

  如果你爱指针,请你爱指针的指针!

 

 

C语言嵌入式系统编程修炼之四:屏幕操作!

作者:宋宝华

汉字处理

  现在要解决的问题是,嵌入式系统中经常要使用的并非是完整的汉字库,往往只是需要提供数量有限的汉字供必要的显示功能。例如,一个微波炉的LCD上没有必要提供显示"电子邮件"的功能;一个提供汉字显示功能的空调的LCD上不需要显示一条"短消息",诸如此类。但是一部手机、小灵通则通常需要包括较完整的汉字库。

如果包括的汉字库较完整,那么,由内码计算出汉字字模在库中的偏移是十分简单的:汉字库是按照区位的顺序排列的,前一个字节为该汉字的区号,后一个字节为该字的位号。每一个区记录94个汉字,位号则为该字在该区中的位置。因此,汉字在汉字库中的具体位置计算公式为:94*(区号-1)+位号-1。减1是因为数组是以0为开始而区号位号是以1为开始的。只需乘上一个汉字字模占用的字节数即可,即:(94*(区号-1)+位号-1)*一个汉字字模占用字节数,以16*16点阵字库为例,计算公式则为:(94*(区号-1)+(位号-1))*32。汉字库中从该位置起的32字节信息记录了该字的字模信息。

对于包含较完整汉字库的系统而言,我们可以以上述规则计算字模的位置。但是如果仅仅是提供少量汉字呢?譬如几十至几百个?最好的做法是:

定义宏:

# define EX_FONT_CHAR(value)
# define EX_FONT_UNICODE_VAL(value) (value),
# define EX_FONT_ANSI_VAL(value) (value),


  定义结构体:

typedef struct _wide_unicode_font16x16
{
 WORD value; /*内码
*/
 BYTE data[32]; /*字模点阵
*/
}Unicode;
#define CHINESE_CHAR_NUM … /*
汉字数量 */


  字模的存储用数组:

Unicode chinese[CHINESE_CHAR_NUM] =
{
{
EX_FONT_CHAR("
")
EX_FONT_UNICODE_VAL(0x4e1a)
{0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x04, 0x44, 0x44, 0x46, 0x24, 0x4c, 0x24, 0x48, 0x14, 0x50, 0x1c, 0x50, 0x14, 0x60, 0x04, 0x40, 0x04, 0x40, 0x04, 0x44, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00}
},
{
EX_FONT_CHAR("
")
EX_FONT_UNICODE_VAL(0x4e2d)
{0x01, 0x00, 0x01, 0x00, 0x21, 0x08, 0x3f, 0xfc, 0x21, 0x08, 0x21, 0x08, 0x21, 0x08, 0x21, 0x08, 0x21, 0x08,
0x3f, 0xf8, 0x21, 0x08, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00}
},
{
EX_FONT_CHAR("
")
EX_FONT_UNICODE_VAL(0x4e91)
{0x00, 0x00, 0x00, 0x30, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xfe, 0x03, 0x00, 0x07, 0x00,

0x06, 0x40, 0x0c, 0x20, 0x18, 0x10, 0x31, 0xf8, 0x7f, 0x0c, 0x20, 0x08, 0x00, 0x00}
},
{
EX_FONT_CHAR("
")
EX_FONT_UNICODE_VAL(0x4ef6)
{0x10, 0x40, 0x1a, 0x40, 0x13, 0x40, 0x32, 0x40, 0x23, 0xfc, 0x64, 0x40, 0xa4, 0x40, 0x28, 0x40, 0x2f, 0xfe,

0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40}
}
}


  要显示特定汉字的时候,只需要从数组中查找内码与要求汉字内码相同的即可获得字模。如果前面的汉字在数组中以内码大小顺序排列,那么可以以二分查找法更高效的查找到汉字的字模。

这是一种很有效的组织小汉字库的方法,它可以保证程序有很好的结构。

系统时间显示

  从NVRAM中可以读取系统的时间,系统一般借助NVRAM产生的秒中断 每秒读取一次当前时间 并在LCD上显示。关于时间的显示,有一个效率问题。因为时间有其特殊性,那就是60秒才有一次分钟的变化,60分钟才有一次小时变化,如果我们每次都将读取的时间在屏幕上完全重新刷新一次,则浪费了大量的系统时间。

一个较好的办法是我们在时间显示函数中以静态变量分别存储小时、分钟、秒,只有在其内容发生变化的时候才更新其显示。

extern void DisplayTime(…)
{
 
static BYTE byHour,byMinute,bySecond;
 
BYTE byNewHour, byNewMinute, byNewSecond;
 
byNewHour = GetSysHour();
 
byNewMinute = GetSysMinute();
 
byNewSecond = GetSysSecond();
 

 if(byNewHour!= byHour)
 
{
  … /*显示小时
*/
  
byHour = byNewHour;
 
}
 
if(byNewMinute!= byMinute)
 
{
  … /*显示分钟
*/
  
byMinute = byNewMinute;
 
}
 
if(byNewSecond!= bySecond)
 
{
  … /*显示秒钟
*/
  
bySecond = byNewSecond;
 
}
}


  这个例子也可以顺便作为C语言中static关键字强大威力的证明。当然,在C++语言里,static具有了更加强大的威力,它使得某些数据和函数脱离"对象"而成为"类"的一部分,正是它的这一特点,成就了软件的无数优秀设计。
动画显示

  动画是无所谓有,无所谓无的,静止的画面走的路多了,也就成了动画。随着时间的变更,在屏幕上显示不同的静止画面,即是动画之本质。所以,在一个嵌入式系统的LCD上欲显示动画,必须借助定时器。没有硬件或软件定时器的世界是无法想像的:

(1) 没有定时器,一个操作系统将无法进行时间片的轮转,于是无法进行多任务的调度,于是便不再成其为一个多任务操作系统;

(2) 没有定时器,一个多媒体播放软件将无法运作,因为它不知道何时应该切换到下一帧画面;

(3) 没有定时器,一个网络协议将无法运转,因为其无法获知何时包传输超时并重传之,无法在特定的时间完成特定的任务。

因此,没有定时器将意味着没有操作系统、没有网络、没有多媒体,这将是怎样的黑暗?所以,合理并灵活地使用各种定时器,是对一个软件人的最基本需求!

在80186为主芯片的嵌入式系统中,我们需要借助硬件定时器的中断来作为软件定时器,在中断发生后变更画面的显示内容。在时间显示"xx:xx"中让冒号交替有无,每次秒中断发生后,需调用ShowDot:

void ShowDot()
{
 static BOOL bShowDot = TRUE; /*再一次领略static关键字的威力
*/
 
if(bShowDot)
 
{
  
showChar(’:’,xPos,yPos);
 
}
 
else
 
{
  
showChar(’ ’,xPos,yPos);
 
}
 
bShowDot = ! bShowDot;
}


  
菜单操作

  无数人为之绞尽脑汁的问题终于出现了,在这一节里,我们将看到,在C语言中哪怕用到一丁点的面向对象思想,软件结构将会有何等的改观!

笔者曾经是个笨蛋,被菜单搞晕了,给出这样的一个系统:


  要求以键盘上的"← →"键切换菜单焦点,当用户在焦点处于某菜单时,若敲击键盘上的OK、CANCEL键则调用该焦点菜单对应之处理函数。我曾经傻傻地这样做着:

/*按下OK */
void onOkKey()
{
 /*判断在什么焦点菜单上按下Ok键,调用相应处理函数
*/
 
Switch(currentFocus)
 
{
  
case MENU1:
   
menu1OnOk();
   
break;
  
case MENU2:
   
menu2OnOk();
   
break;
  

 
}
}
/*
按下Cancel
*/
void onCancelKey()
{
 /*判断在什么焦点菜单上按下Cancel键,调用相应处理函数
*/
 
Switch(currentFocus)
 
{
  
case MENU1:
   
menu1OnCancel();
   
break;
  
case MENU2:
   
menu2OnCancel();
   
break;
  

 
}
}


  终于有一天,我这样做了:

/*将菜单的属性和操作"封装"在一起 */
typedef struct tagSysMenu
{
 char *text; /*菜单的文本
*/
 BYTE xPos; /*菜单在LCD上的x坐标
*/
 BYTE yPos; /*菜单在LCD上的y坐标
*/
 void (*onOkFun)(); /*在该菜单上按下ok键的处理函数指针
*/
 void (*onCancelFun)(); /*在该菜单上按下cancel键的处理函数指针
*/
}SysMenu, *LPSysMenu;


  当我定义菜单时,只需要这样:

static SysMenu menu[MENU_NUM] =
{
 
{
  
"menu1", 0, 48, menu1OnOk, menu1OnCancel
 
}
 
,
 
{
  
" menu2", 7, 48, menu2OnOk, menu2OnCancel
 
}
 
,
 
{
  
" menu3", 7, 48, menu3OnOk, menu3OnCancel
 
}
 
,
 
{
  
" menu4", 7, 48, menu4OnOk, menu4OnCancel
 
}
 

};


  OK键和CANCEL键的处理变成:

/*按下OK */
void onOkKey()
{
 
menu[currentFocusMenu].onOkFun();
}
/*
按下Cancel
*/
void onCancelKey()
{
 
menu[currentFocusMenu].onCancelFun();
}


  程序被大大简化了,也开始具有很好的可扩展性!我们仅仅利用了面向对象中的封装思想,就让程序结构清晰,其结果是几乎可以在无需修改程序的情况下在系统中添加更多的菜单,而系统的按键处理函数保持不变。

面向对象,真神了!
模拟MessageBox函数

  MessageBox函数,这个Windows编程中的超级猛料,不知道是多少入门者第一次用到的函数。还记得我们第一次在Windows中利用MessageBox输出 "Hello,World!"对话框时新奇的感觉吗?无法统计,这个世界上究竟有多少程序员学习Windows编程是从MessageBox ("Hello,World!",…)开始的。在我本科的学校,广泛流传着一个词汇,叫做"’Hello,World’级程序员",意指入门级程序员,但似乎"’Hello,World’级"这个说法更搞笑而形象。

  
2经典的Hello,World!


  图2给出了两种永恒经典的Hello,World对话框,一种只具有"确定",一种则包含"确定"、"取消"。是的,MessageBox的确有,而且也应该有两类!这完全是由特定的应用需求决定的。

嵌入式系统中没有给我们提供MessageBox,但是鉴于其功能强大,我们需要模拟之,一个模拟的MessageBox函数为:

/******************************************
/*
函数名称
: MessageBox
/*
功能说明:弹出式对话框,显示提醒用户的信息

/*
参数说明: lpStr ---提醒用户的字符串输出信息
/* TYPE ---
输出格式(ID_OK = 0, ID_OKCANCEL = 1)
/*
返回值:返回对话框接收的键值,只有两种
KEY_OK, KEY_CANCEL
/******************************************
typedef enum TYPE { ID_OK,ID_OKCANCEL }MSG_TYPE;
extern BYTE MessageBox(LPBYTE lpStr, BYTE TYPE)
{
 
BYTE keyValue = -1;

 ClearScreen(); /*清除屏幕
*/
 DisplayString(xPos,yPos,lpStr,TRUE); /*显示字符串
*/
 /*根据对话框类型决定是否显示确定、取消
*/
 
switch (TYPE)
 
{
  
case ID_OK:
   DisplayString(13,yPos+High+1, "确定
", 0);
   
break;
  
case ID_OKCANCEL:
   DisplayString(8, yPos+High+1, "确定
", 0);
   DisplayString(17,yPos+High+1, "取消
", 0);
   
break;
  
default:
   
break;
 
}
 DrawRect(0, 0, 239, yPos+High+16+4); /*绘制外框
*/
 /* MessageBox是模式对话框,阻塞运行,等待按键
*/
 
while( (keyValue != KEY_OK) || (keyValue != KEY_CANCEL) )
 
{
  
keyValue = getSysKey();
 
}
 /*返回按键类型
*/
 
if(keyValue== KEY_OK)
 
{
  
return ID_OK;
 
}
 
else
 
{
  
return ID_CANCEL;
 
}
}


  上述函数与我们平素在VC++等中使用的MessageBox是何等的神似啊?实现这个函数,你会看到它在嵌入式系统中的妙用是无穷的。

总结

  本篇是本系列文章中技巧性最深的一篇,它提供了嵌入式系统屏幕显示方面一些很巧妙的处理方法,灵活使用它们,我们将不再被LCD上凌乱不堪的显示内容所困扰。

屏幕乃嵌入式系统生存之重要辅助,面目可憎之显示将另用户逃之夭夭。屏幕编程若处理不好,将是软件中最不系统、最混乱的部分,笔者曾深受其害。

C语言嵌入式系统编程修炼之五:键盘操作!

作者:宋宝华

处理功能键

  功能键的问题在于,用户界面并非固定的,用户功能键的选择将使屏幕画面处于不同的显示状态下。例如,主画面如图1:


  当用户在设置XX上按下Enter键之后,画面就切换到了设置XX的界面,如图2:


  程序如何判断用户处于哪一画面,并在该画面的程序状态下调用对应的功能键处理函数,而且保证良好的结构,是一个值得思考的问题。

让我们来看看WIN32编程中用到的"窗口"概念,当消息(message)被发送给不同窗口的时候,该窗口的消息处理函数(是一个callback函数)最终被调用,而在该窗口的消息处理函数中,又根据消息的类型调用了该窗口中的对应处理函数。通过这种方式,WIN32有效的组织了不同的窗口,并处理不同窗口情况下的消息。

我们从中学习到的就是:

(1)将不同的画面类比为WIN32中不同的窗口,将窗口中的各种元素(菜单、按钮等)包含在窗口之中;

(2)给各个画面提供一个功能键"消息"处理函数,该函数接收按键信息为参数;

(3)在各画面的功能键"消息"处理函数中,判断按键类型和当前焦点元素,并调用对应元素的按键处理函数。

/*将窗口元素、消息处理函数封装在窗口中 */
struct windows
{
 
BYTE currentFocus;
 
ELEMENT element[ELEMENT_NUM];
 
void (*messageFun) (BYTE keyValue);
 

};
/*
消息处理函数
*/
void messageFunction(BYTE keyValue)
{
 
BYTE i = 0;
 /*获得焦点元素
*/
 
while ( (element [i].ID!= currentFocus)&& (i < ELEMENT_NUM) )
 
{
  
i++;
 
}
 /* "消息映射
" */
 
if(i < ELEMENT_NUM)
 
{
  
switch(keyValue)
  
{
   
case OK:
    
element[i].OnOk();
    
break;
   

  
}
 
}
}


  在窗口的消息处理函数中调用相应元素按键函数的过程类似于"消息映射",这是我们从WIN32编程中学习到的。编程到了一个境界,很多东西都是相通的了。其它地方的思想可以拿过来为我所用,是为编程中的"拿来主义"。

在这个例子中,如果我们还想玩得更大一点,我们可以借鉴MFC中处理MESSAGE_MAP的方法,我们也可以学习MFC定义几个精妙的宏来实现"消息映射"。
处理数字键

  用户输入数字时是一位一位输入的,每一位的输入都对应着屏幕上的一个显示位置(x坐标,y坐标)。此外,程序还需要记录该位置输入的值,所以有效组织用户数字输入的最佳方式是定义一个结构体,将坐标和数值捆绑在一起:

/*用户数字输入结构体 */
typedef struct tagInputNum
{
 BYTE byNum; /*接收用户输入赋值
*/
 BYTE xPos; /*数字输入在屏幕上的显示位置x坐标
*/
 BYTE yPos; /*数字输入在屏幕上的显示位置y坐标
*/
}InputNum, *LPInputNum;


  那么接收用户输入就可以定义一个结构体数组,用数组中的各位组成一个完整的数字:

InputNum inputElement[NUM_LENGTH]; /*接收用户数字输入的数组 */
/*
数字按键处理函数
*/
extern void onNumKey(BYTE num)
{
if(num==0|| num==1) /*
只接收二进制输入
*/
{
 /*在屏幕上显示用户输入
*/
 
DrawText(inputElement[currentElementInputPlace].xPos, inputElement[currentElementInputPlace].yPos, "%1d", num);
 /*将输入赋值给数组元素
*/
 
inputElement[currentElementInputPlace].byNum = num;
 /*焦点及光标右移
*/
 
moveToRight();
}
}


  将数字每一位输入的坐标和输入值捆绑后,在数字键处理函数中就可以较有结构的组织程序,使程序显得很紧凑。

整理用户输入

  继续第2节的例子,在第2节的onNumKey函数中,只是获取了数字的每一位,因而我们需要将其转化为有效数据,譬如要转化为有效的XXX数据,其方法是:

/*2进制数据位转化为有效数据:XXX */
void convertToXXX()
{
 
BYTE i;
 
XXX = 0;
 
for (i = 0; i < NUM_LENGTH; i++)
 
{
  
XXX += inputElement[i].byNum*power(2, NUM_LENGTH - i - 1);
 
}
}


  反之,我们也可能需要在屏幕上显示那些有效的数据位,因为我们也需要能够反向转化:

/*从有效数据转化为2进制数据位:XXX */
void convertFromXXX()
{
 
BYTE i;
 
XXX = 0;
 
for (i = 0; i < NUM_LENGTH; i++)
 
{
  
inputElement[i].byNum = XXX / power(2, NUM_LENGTH - i - 1) % 2;
 
}
}


  当然在上面的例子中,因为数据是2进制的,用power函数不是很好的选择,直接用"<< >>"移位操作效率更高,我们仅是为了说明问题的方便。试想,如果用户输入是十进制的,power函数或许是唯一的选择了。

总结

  本篇给出了键盘操作所涉及的各个方面:功能键处理、数字键处理及用户输入整理,基本上提供了一个全套的按键处理方案。对于功能键处理方法,将LCD屏幕与Windows窗口进行类比,提出了较新颖地解决屏幕、键盘繁杂交互问题的方案。

计算机学的许多知识都具有相通性,因而,不断追赶时髦技术而忽略基本功的做法是徒劳无意的。我们最多需要"精通"三种语言(精通,一个在如今的求职简历里泛滥成灾的词语),最佳拍档是汇编、C、C++(或JAVA),很显然,如果你"精通"了这三种语言,其它语言你应该是可以很快"熟悉"的,否则你就没有"精通"它们.

C语言嵌入式系统编程修炼之六:性能优化!

作者:宋宝华

使用宏定义

  在C语言中,宏是产生内嵌代码的唯一方法。对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法。

写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个:

错误做法:

#define MIN(A,B) ( A <= B ? A : B )


  正确做法:

#define MIN(A,B)((A<= (B) ? (A) : (B) )


  对于宏,我们需要知道三点:

(1)宏定义"像"函数;

(2)宏定义不是函数,因而需要括上所有"参数";

(3)宏定义可能产生副作用。

下面的代码:

least = MIN(*p++, b);


  将被替换为:

( (*p++) <= (b) ?(*p++):(b) )


  发生的事情无法预料。

因而不要给宏定义传入有副作用的"参数"。

使用寄存器变量

  当对一个变量频繁被读写时,需要反复访问内存,从而花费大量的存取时间。为此,C语言提供了一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写,从而提高效率。寄存器变量的说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量,而循环计数是应用寄存器变量的最好候选者。

(1) 只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量,包括:模块间全局变量、模块内全局变量、局部static变量;

(2) register是一个"建议"型关键字,意指程序建议该变量放在寄存器中,但最终该变量可能因为条件不满足并未成为寄存器变量,而是被放在了存储器中,但编译器中并不报错(在C++语言中有另一个"建议"型关键字:inline)。

下面是一个采用寄存器变量的例子:

/*1+2+3+….+n的值 */
WORD Addition(BYTE n)
{
 
register i,s=0;
 
for(i=1;i<=n;i++)
 
{
  
s=s+i;
 
}
 
return s;
}


  本程序循环n次,i和s都被频繁使用,因此可定义为寄存器变量。

内嵌汇编

  程序中对时间要求苛刻的部分可以用内嵌汇编来重写,以带来速度上的显著提高。但是,开发和测试汇编代码是一件辛苦的工作,它将花费更长的时间,因而要慎重选择要用汇编的部分。

在程序中,存在一个80-20原则,即20%的程序消耗了80%的运行时间,因而我们要改进效率,最主要是考虑改进那20%的代码。

嵌入式C程序中主要使用在线汇编,即在C程序中直接插入_asm{ }内嵌汇编语句:

/*把两个输入参数的值相加,结果存放到另外一个全局变量中 */
int result;
void Add(long a, long *b)
{
 
_asm
 
{
  
MOV AX, a
  
MOV BX, b
  
ADD AX, [BX]
  
MOV result, AX
 
}
}


  
利用硬件特性

  首先要明白CPU对各种存储器的访问速度,基本上是:

CPU内部RAM > 外部同步RAM > 外部异步RAM > FLASH/ROM

对于程序代码,已经被烧录在FLASH或ROM中,我们可以让CPU直接从其中读取代码执行,但通常这不是一个好办法,我们最好在系统启动后将FLASH或ROM中的目标代码拷贝入RAM中后再执行以提高取指令速度;

对于UART等设备,其内部有一定容量的接收BUFFER,我们应尽量在BUFFER被占满后再向CPU提出中断。例如计算机终端在向目标机通过RS-232传递数据时,不宜设置UART只接收到一个BYTE就向CPU提中断,从而无谓浪费中断处理时间;

如果对某设备能采取DMA方式读取,就采用DMA读取,DMA读取方式在读取目标中包含的存储信息较大时效率较高,其数据传输的基本单位是块,而所传输的数据是从设备直接送入内存的(或者相反)。DMA方式较之中断驱动方式,减少了CPU 对外设的干预,进一步提高了CPU与外设的并行操作程度。

活用位操作

  使用C语言的位操作可以减少除法和取模的运算。在计算机程序中数据的位是可以操作的最小数据单位,理论上可以用"位运算"来完成所有的运算和操作,因而,灵活的位操作可以有效地提高程序运行的效率。举例如下:

/*方法1 */
int i,j;
i = 879 / 16;
j = 562 % 32;
/*
方法
2 */
int i,j;
i = 879 >> 4;
j = 562 - (562 >> 5 << 5);


  对于以2的指数次方为"*"、"/"或"%"因子的数学运算,转化为移位运算"<< >>"通常可以提高算法效率。因为乘除运算指令周期通常比移位运算大。

C语言位运算除了可以提高运算效率外,在嵌入式系统的编程中,它的另一个最典型的应用,而且十分广泛地正在被使用着的是位间的与(&)、或(|)、非(~)操作,这跟嵌入式系统的编程特点有很大关系。我们通常要对硬件寄存器进行位设置,譬如,我们通过将AM186ER型80186处理器的中断屏蔽控制寄存器的第低6位设置为0(开中断2),最通用的做法是:

#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp &~INT_I2_MASK);


  而将该位设置为1的做法是:

#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp | INT_I2_MASK);


  判断该位是否为1的做法是:

#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
if(wTemp & INT_I2_MASK)
{
… /*
该位为
1 */
}


  上述方法在嵌入式系统的编程中是非常常见的,我们需要牢固掌握。

总结

  在性能优化方面永远注意80-20准备,不要优化程序中开销不大的那80%,这是劳而无功的。

宏定义是C语言中实现类似函数功能而又不具函数调用和返回开销的较好方法,但宏在本质上不是函数,因而要防止宏展开后出现不可预料的结果,对宏的定义和使用要慎而处之。很遗憾,标准C至今没有包括C++中inline函数的功能,inline函数兼具无调用开销和安全的优点。

使用寄存器变量、内嵌汇编和活用位操作也是提高程序效率的有效方法。

除了编程上的技巧外,为提高系统的运行效率,我们通常也需要最大可能地利用各种硬件设备自身的特点来减小其运转开销,例如减小中断次数、利用DMA传输方式等。

 

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

相关文章

  1. 精准分析区块链定义

    想知道了解更多区块链技术实战问题,请百度【链客区块链技术问答社区】 区块链就是一个具有共享状态的密码性安全交易的单机(cryptographically secure transactional singleton machine with shared-state)。[1]这有点长,是吧?让我们将它分开来看:“密码性安全(Cryptograph…...

    2024/4/18 11:15:43
  2. ●数据控件(Repeater)

    展示数据库相关的数据外观与数据分离。 外观代码:用模板来实现。(头,脚,项,交替项,分隔符模板)HeaderTemplate   头模板FooterTemplate    脚模板ItemTemplate     项模板AlternatingItemTemplate 交替项模板Separa…...

    2024/4/20 2:16:12
  3. 微信小程序开发教程手册文档

    微信小程序开发教程文档微信小程序是什么?微信小程序如何开发?微信小程序开发教程有哪些?本教程将从微信小程序本身,结合微信官方-微信小程序开发工具,带你一步步创建一个微信小程序,并在手机操作预览该小程序实际效果。这个小程序的首页将会显示欢迎语以及当前用户的微信…...

    2024/4/18 11:33:07
  4. 动什么别动感情 第33节:只属于情侣的姿势

    动什么别动感情 第33节:只属于情侣的姿势[@more@]佳期沉默不语。她好不容易才说服万征来见自己的家人,谁想到促成了旧情人相认的局面,又没自己事了。 "你接下来怎么办?就准备一棵树上吊死了?他不会对你好的。" 佳期一拍桌子:"我豁出去了,我这就出去约会…...

    2024/4/20 4:27:15
  5. 如何在手机上进行编程?

    这里介绍几个可以在手机上编程的软件,感兴趣的可以下载一下,试着操作一下:1.Python:这里推荐一个软件—QPython3,集成了Python3解释器、Console控制台和QEdit编辑器,可以直接编辑运行python代码,也可以进行简单的安卓开发,下面我简单介绍一下这个软件的安装和使用:安装…...

    2024/4/19 12:17:00
  6. 问答类产品竞品分析

    背景以及定位 竞品分析 1 目标产品 2 产品对比 21 业务角色 21 业务范围 21 业务流程 21 业务交互 21 功能体验总结 我以为的产品 问答功能 线上专题分享1. 背景以及定位公司业务主要以风电行业的服务支持为主,而当前的服务主要依托于即时通信工具,比如:RTX、微信、QQ、电话…...

    2024/4/23 13:52:30
  7. 微信小程序官方文档请求接口调用报错

    官方文档的请求接口没问题,但是会报错400 (Bad Request) 把application/json 改成 json 即可 wx.request({url: test.php, //仅为示例,并非真实的接口地址data: {x: ,y: },header: {content-type: json},success: function(res) {console.log(res.data)} })...

    2024/4/18 11:32:01
  8. GridView的时候用到FindControl()在TemplateField中发现总是Find不到

    对于在RowDataBound中Find,可以用if (e.Row.RowType == DataControlRowType.DataRow)来限制Find的范围,因为Find默认是在HeaderTemplate中找,如果不限定范围,在HeaderTemplate中找不到,自然就返回null,然后就出错了,DataControlRowType枚举中的DataRow确定是数据行.另一个是在R…...

    2024/4/18 23:12:54
  9. 视觉问答VQA知识资料全集

    视觉问答(Visual Question Answering,VQA)专知荟萃入门学习进阶论文Attention-BasedKnowledge-basedMemory NetworkVideo QA综述TutorialDatasetCode领域专家 入门学习基于深度学习的VQA(视觉问答)技术[https://zhuanlan.zhihu.com/p/22530291]视觉问答全景概述:从数据集…...

    2024/4/18 11:47:02
  10. 在window上部署python Django项目 采用apache+mod_wsgi方案

    刚学了一段时间的python,感觉挺方便的,写个简单的web,真的是太简单了,一行命令runserver就跑起来了,后来听说那是测试服务器,不安全,只能用于测试,不能用于生产上,好吧,虽然我的网站可能就我一个人访问而已,哈哈,但是还是得折腾一下。 由于我是window系统,结果在部…...

    2024/4/28 5:20:51
  11. 微信小程序官方示例文档免费下载文档

    链接:https://pan.baidu.com/s/1IXVDu_IspPsADqg20XtYxQ 密码:lrlx 如果需要了解小程序,可以联系我: QQ:1315371460...

    2024/4/28 2:16:51
  12. 启动Python

    启动Python有两种方法。一种方法是从IDLE启动: 在开始菜单中,可以看到“Python3.6.3”下面的“IDLE(Python GUI)”,打开IDLE窗口,如下: IDLE是一个Python shell。是一个通过键入文本与程序交互的途径,可以利用这个shell与Python交互。IDLE本身还是一个GUI(…...

    2024/4/19 20:06:13
  13. Repeater用法

    绑定数据源: DataClassesDataContext context = new DataClassesDataContext(); Repeater1.DataSource = context.Info; Repeater1.DataBind();造模板:头模板: <HeaderTemplate> </HeaderTemplate> 出现一次,标题行项模板: <…...

    2024/4/18 11:42:04
  14. 情人节:几个有趣网站(转)

    今天就是情人节了,推荐几个情人节相关的站点,想要示爱,赶紧行动!kitdas hit:最合适向情人表白的网站 这个站点如果在情人节推荐,那最合适不过了,估计普天下所有的情侣都会喜欢这个网站的,因为它提供了非常有趣的向情人表白的方式哦。咱们不但可以欣赏到其他情侣那…...

    2024/4/26 23:11:32
  15. 为什么建议电气人学习C语言编程?

    说起编程大家可能都听过,但编程究竟是怎么一回事你弄懂了吗? 编程=对计算机程序进行编写,这些程序可以是现在手里拿着的手机、办公的电脑、你点击的页面、浏览的网页,都是有程序让它执行你要它做的事情。 PLC与编程有什么样的联系? 其实在自动化、电气这一块都是需要对自动…...

    2024/4/18 11:30:04
  16. 知识图谱技术分享会----有关知识图谱构建的部分关键技术简介及思考

    昨天在北理工参加了一场由 雪晴数据网和北京理工大学大数据创新学习中心联合举办的知识图谱分享活动,聆听了一下午报告,可谓是受益匪浅。一下午时间安排的非常饱满,总共三场报告。不得不说首都的学校就是厉害啊,楼都这么漂亮。下面我就来分别说说重点和感想。1.佛学知识图谱…...

    2024/4/18 11:20:32
  17. 微信小程序官方组件/框架/API/服务端/工具/云开发/扩展能力开发文档

    微信小程序官方开发文档下载路径如下(选中对应名称,跳转至相应的文档): 一、0_微信小程序指南官方文档(带目录) 二、01_微信小程序官方开发指南文档(带目录) 三、1_微信小程序官方框架文档(带目录) 四、2_微信小程序官方组件文档(带目录) 五、3_微信小程序官方API文…...

    2024/4/18 11:48:08
  18. 微信小程序官方文档解读:如何才能避开小程序的规则“雷区”

    虽然微信小程序还处于内测阶段,但得益于各种开发者、行业人士的广泛讨论,对微信小程序有深入了解的开发者已经初具规模。微信小程序的出现体现了微信的创新思维,也继承了微信严密的内容管理风格,稍有不慎,就可能触碰到微信的底线。对于开发者来说,如何才能在开发中避免违…...

    2024/4/19 9:10:52
  19. 学习1-js开发实现简单贪吃蛇游戏

    学习1-js开发实现简单贪吃蛇游戏 <!doctype html> <html> <body> <canvas id="can" width="400" height="400" style="background:Black"></canvas> <script>var sn=[42,41],dz=43,fx=1,n,ctx=do…...

    2024/4/18 11:47:02
  20. C语言嵌入式系统编程修炼

    C语言嵌入式系统编程修炼之背景篇 作者:宋宝华出处:天极网责任编辑: 方舟 [ 2005-06-14 14:45 ]本文的讨论主要围绕以通用处理器为中心的协议处理模块进行,因为它更多地牵涉到具体的C语言编程技巧不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上,势必要求…...

    2024/4/18 11:58:03

最新文章

  1. 英语六级常用词汇2

    英语六级常用词汇1 blush [blʌʃ] 脸红&#xff08;名词、动词&#xff09;&#xff1a;面部因羞愧、激动或尴尬而变红。Example: She began to blush when they complimented her.翻译&#xff1a; 当他们称赞她时&#xff0c;她开始脸红。 pedal [ˈpɛdəl] 踏板&#xff…...

    2024/4/28 5:32:21
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. [C++][算法基础]模拟队列(数组)

    实现一个队列&#xff0c;队列初始为空&#xff0c;支持四种操作&#xff1a; push x – 向队尾插入一个数 x&#xff1b;pop – 从队头弹出一个数&#xff1b;empty – 判断队列是否为空&#xff1b;query – 查询队头元素。 现在要对队列进行 M 个操作&#xff0c;其中的每…...

    2024/4/22 21:35:57
  4. 文心一言 vs GPT-4 —— 全面横向比较

    对于文心一言和 GPT-4 这两者之间的全面横向比较&#xff0c;我们可以从多个方面来看待它们的区别和优劣势。 文心一言 文心一言是一款基于深度学习的中文文本生成模型&#xff0c;专注于生成优美的古风诗句和语录。以下是它的一些特点&#xff1a; 专注于古风诗句和语录: 文…...

    2024/4/24 17:52:27
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/26 18:09:39
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/28 3:28:32
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/27 4:00:35
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/27 9:01:45
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57