C语言学习心得

时间:2019-05-11 23:55:01下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《C语言学习心得》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《C语言学习心得》。

第一篇:C语言学习心得

这个我从eehome贴过来的。写的非常的好。我们用学单片机不要停在演示的基础上。只能让单片机完成局部事。这样我们永远不会走出流水灯地狱!!

学习单片机也已经有几年了,藉此机会和大家聊一下我学习过程中的一些经历和想法吧。也感谢一线工人提供了这个机会。希望大家有什么好的想法和建议都直接跟帖说出来。毕竟只有交流才能够碰撞出火花来^_^。

。“卖弄”也好,“吹嘘”也罢,我只是想认真的写写我这一路走来历经的总总,把其中值得注意,以及经验的地方写出来,权当是我对自己的一个总结吧。而作为看官的你,如果看到了我的错误,还请一定指正,这样对我以及其它读者都有帮助,而至于你如果从中能够收获到些许,那便是我最大的欣慰了。姑妄言之,姑妄听之。如果有啥好的想法和建议一定要说出来。 几年前,和众多初学者一样,我接触到了单片机,立刻被其神奇的功能所吸引,从此不能自拔。很多个日夜就这样陪伴着它度过了。期间也遇到过非常多的问题,也一度被这些问题所困惑„„等到回过头来,看到自己曾经走过的路,唏嘘不已。经常混迹于论坛里,也看到了很多初学者发的求助帖子,看到他们走在自己曾走过的弯路上,忽然想到了自己的那段日子,心里竟然莫名的冲动,凡此总总,我总是尽自己所能去回帖。很多时候,都想写一点什么东西出来,希望对广大的初学者有一点点帮助。但总是不知从何处写起。今天借一线工人的台,唱一唱我的戏

一路学习过来的过程中,帮助最大之一无疑来自于网络了。很多时候,通过网络,我们都可以获取到所需要的学习资料。但是,随着我们学习的深入,我们会慢慢发现,网络提供的东西是有限度的,好像大部分的资料都差不多,或者说是适合大部分的初学者所需,而当我们想更进一步提高时,却发现能够获取到的资料越来越少,相信各位也会有同感,铺天盖地的单片机资料中大部分不是流水灯就是LED,液晶,而且也只是仅仅作功能性的演示。于是有些人选择了放弃,或者是转移到其他兴趣上面去了,而只有少部分人选择了继续摸索下去,结合市面上的书籍,然后在网络上锲而不舍的搜集资料,再从牛人的只言片语中去体会,不断动手实践,慢慢的,也摸索出来了自己的一条路子。当然这个过程必然是艰辛的,而他学会了之后也不会在网络上轻易分享自己的学习成果。如此恶性循环下去,也就不难理解为什么初级的学习资料满天飞,而深入一点的学习资料却很少的原因了。相较于其他领域,单片机技术的封锁更加容易。尽管已经问世了很多年了,有价值的资料还是相当的欠缺,大部分的资料都是止于入门阶段或者是简单的演示实验。但是在实际工程应用中却是另外一回事。有能力的高手无暇或者是不愿公开自己的学习经验。

很多时候,我也很困惑,看到国外爱好者毫不保留的在网络上发布自己的作品,我忽然感觉到一丝丝的悲哀。也许,我们真的该转变一下思路了,帮助别人,其实也是在帮助自己。啰啰嗦嗦的说了这么多,相信大家能够明白说的是什么意思。在接下来的一段日子里,我将会结合电子工程师之家举办的主题周活动写一点自己的想法。尽可能从实用的角度去讲述。希望能够帮助更多的初学者更上一层楼。而关于这个主题周的最大主题我想了这样的一个名字“从单片机初学者迈向单片机工程师”。名字挺大挺响亮,给我的压力也挺大的,但我会努力,争取使这样的一系列文章能够带给大家一点帮助,而不是看后大跌眼镜。这样的一系列文章主要的对象是初学者,以及想从初学者更进一步提高的读者。而至于老手,以及那些牛XX的人,希望能够给我们这些初学者更多的一些指点哈~@_@

我们首先来看第一章节

从这一章开始,我们开始迈入单片机的世界。在我们开始这一章具体的学习之前,有必要给大家先说明一下。在以后的系列文章中,我们将以51内核的单片机为载体,C语言为编程语言,开发环境为KEIL uv3。至于为什么选用C语言开发,好处不言而喻,开发速度快,效率高,代码可复用率高,结构清晰,尤其是在大型的程序中,而且随着编译器的不断升级,其编译后的代码大小与汇编语言的差距越来越小。而关于C语言和汇编之争,就像那个啥,每隔一段时间总会有人挑起这个话题,如果你感兴趣,可以到网上搜索相关的帖子自行阅读。不是说汇编不重要,在很多对时序要求非常高的场合,需要利用汇编语言和C语言混合编程才能够满足系统的需求。在我们学习掌握C语言的同时,也还需要利用闲余的时间去学习了解汇编语言。

1.从点亮LED(发光二极管)开始

在市面上众多的单片机学习资料中,最基础的实验无疑于点亮LED了,即控制单片机的I/O的电平的变化。

如同如下实例代码一般

void main(void){ LedInit();While(1){ LED = ON;DelayMs(500);LED = OFF;DelayMs(500);} }

程序很简单,从它的结构可以看出,LED先点亮500MS,然后熄灭500MS,如此循环下去,形成的效果就是LED以1HZ的频率进行闪烁。下面让我们分析上面的程序有没有什么问题。

看来看出,好像很正常的啊,能有什么问题呢?这个时候我们应该换一个思路去想了。试想,整个程序除了控制LED = ON ; LED = OFF; 这两条语句外,其余的时间,全消耗在了DelayMs(500)这两个函数上。而在实际应用系统中是没有哪个系统只闪烁一只LED就其它什么事情都不做了的。因此,在这里我们要想办法,把CPU解放出来,让它不要白白浪费500MS的延时等待时间。宁可让它一遍又一遍的扫描看有哪些任务需要执行,也不要让它停留在某个地方空转消耗CPU时间。

从上面我们可以总结出

(1)无论什么时候我们都要以实际应用的角度去考虑程序的编写。

(2)无论什么时候都不要让CPU白白浪费等待,尤其是延时(超过1MS)这样的地方。

下面让我们从另外一个角度来考虑如何点亮一颗LED。先看看我们的硬件结构是什么样子的。

我手上的单片机板子是电子工程师之家的开发的学习板。就以它的实际硬件连接图来分析吧。如下图所示

(原文件名:led.jpg)

引用图片

一般的LED的正常发光电流为10~20MA而低电流LED的工作电流在2mA以下(亮度与普通发光管相同)。在上图中我们可知,当Q1~Q8引脚上面的电平为低电平时,LED发光。通过LED的电流约为(VCC-Vd)/ RA2。其中Vd为LED导通后的压降,约为1.7V左右。这个导通压降根据LED颜色的不同,以及工作电流的大小的不同,会有一定的差别。下面一些参数是网上有人测出来的,供大家参考。红色的压降为1.82-1.88V,电流5-8mA,绿色的压降为1.75-1.82V,电流3-5mA,橙色的压降为1.7-1.8V,电流3-5mA 兰色的压降为3.1-3.3V,电流8-10mA,白色的压降为3-3.2V,电流10-15mA,(供电电压5V,LED直径为5mm)

74HC573真值表如下:

(原文件名:74hc573.jpg)

引用图片

通过这个真值表我们可以看出。当OutputEnable引脚接低电平的时候,并且LatchEnable引脚为高电平的时候,Q端电平与D端电平相同。结合我们的LED硬件连接图可以知道LED_CS端为高电平时候,P0口电平的变化即Q端的电平的变化,进而引起LED的亮灭变化。由于单片机的驱动能力有限,在此,74HC573的主要作用就是起一个输出驱动的作用。需要注意的是,通过74HC573的最大电流是有限制的,否则可能会烧坏74HC573这个芯片。

上面这个图是从74HC573的DATASHEET中截取出来的,从上可以看出,每个引脚允许通过的最大电流为35mA 整个芯片允许通过的最大电流为75mA。在我们设计相应的驱动电路时候,这些参数是相当重要的,而且是最容易被初学者所忽略的地方。同时在设计的时候,要留出一定量的余量出来,不能说单个引脚允许通过的电流为35mA,你就设计为35mA,这个时候你应该把设计的上限值定在20mA左右才能保证能够稳定的工作。

(设计相应驱动电路时候,应该仔细阅读芯片的数据手册,了解每个引脚的驱动能力,以及整个芯片的驱动能力)

了解了相应的硬件后,我们再来编写驱动程序。

首先定义LED的接口 #define LED P0 然后为亮灭常数定义一个宏,由硬件连接图可以,当P0输出为低电平时候LED亮,P0输出为高电平时,LED熄灭。

#define LED_ON()LED = 0x00 //所有LED亮 #define LED_OFF()LED = 0xff //所有LED熄灭

下面到了重点了,究竟该如何释放CPU,避免其做延时空等待这样的事情呢。很简单,我们为系统产生一个1MS的时标。假定LED需要亮500MS,熄灭500MS,那么我们可以对这个1MS的时标进行计数,当这个计数值达到500时候,清零该计数值,同时把LED的状态改变。unsigned int g_u16LedTimeCount = 0;//LED计数器

unsigned char g_u8LedState = 0;//LED状态标志, 0表示亮,1表示熄灭

void LedProcess(void){ if(0 == g_u8LedState)//如果LED的状态为亮,则点亮LED { LED_ON();} else //否则熄灭LED { LED_OFF();} }

void LedStateChange(void){ if(g_bSystemTime1Ms)//系统1MS时标到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED计数器加一

if(g_u16LedTimeCount >= 500)//计数达到500,即500MS到了,改变LED的状态。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

上面有一个变量没有提到,就是g_bSystemTime1Ms。这个变量可以定义为位变量或者是其它变量,在我们的定时器中断函数中对其置位,其它函数使用该变量后,应该对其复位(清0)。我们的主函数就可以写成如下形式(示意代码)void main(void){ while(1){ LedProcess();LedStateChange();} }

因为LED的亮或者灭依赖于LED状态变量(g_u8LedState)的改变,而状态变量的改变,又依赖于LED计数器的计数值(g_u16LedTimeCount,只有计数值达到一定后,状态变量才改变)所以,两个函数都没有堵塞CPU的地方。让我们来从头到尾分析一遍整个程序的流程。

程序首先执行LedProcess();函数

因为g_u8LedState 的初始值为0(见定义,对于全局变量,在定义的时候最好给其一个确定的值)所以LED被点亮,然后退出LedStateChange()函数,执行下一个函数LedStateChange()在函数LedStateChange()内部首先判断1MS的系统时标是否到了,如果没有到就直接退出函数,如果到了,就把时标清0以便下一个时标消息的到来,同时对LED计数器加一,然后再判断LED计数器是否到达我们预先想要的值500,如果没有,则退出函数,如果有,对计数器清0,以便下次重新计数,同时把LED状态变量取反,然后退出函数。

由上面整个流程可以知道,CPU所做的事情,就是对一些计数器加一,然后根据条件改变状态,再根据这个状态来决定是否点亮LED。这些函数执行所花的时间都是相当短的,如果主程序中还有其它函数,则CPU会顺次往下执行下去。对于其它的函数(如果有的话)也要采取同样的措施,保证其不堵塞CPU,如果全部基于这种方法设计,那么对于不是非常庞大的系统,我们的系统依旧可以保证多个任务(多个函数)同时执行。系统的实时性得到了一定的保证,从宏观上看来,就是多个任务并发执行。

好了,这一章就到此为止,让我们总结一下,究竟有哪些需要注意的吧。

(1)无论什么时候我们都要以实际应用的角度去考虑程序的编写。

(2)无论什么时候都不要让CPU白白浪费等待,尤其是延时(超过1MS)这样的地方。(3)设计相应驱动电路时候,应该仔细阅读芯片的数据手册,了解每个引脚的驱动能力,以及整个芯片的驱动能力

(4)最重要的是,如何去释放CPU(参考本章的例子),这是写出合格程序的基础。

附完整程序代码(基于电子工程师之家的单片机开发板)

#include

sbit LED_SEG = P1^4;//数码管段选 sbit LED_DIG = P1^5;//数码管位选 sbit LED_CS11 = P1^6;//led控制位 sbit ir=P1^7;#define LED P0 //定义LED接口

bit g_bSystemTime1Ms = 0;// 1MS系统时标 unsigned int g_u16LedTimeCount = 0;//LED计数器

unsigned char g_u8LedState = 0;//LED状态标志, 0表示亮,1表示熄灭

#define LED_ON()LED = 0x00;//所有LED亮 #define LED_OFF()LED = 0xff;//所有LED熄灭

void Timer0Init(void){ TMOD &= 0xf0;TMOD |= 0x01;//定时器0工作方式1 TH0 = 0xfc;//定时器初始值 TL0 = 0x66;TR0 = 1;ET0 = 1;} void LedProcess(void){ if(0 == g_u8LedState)//如果LED的状态为亮,则点亮LED { LED_ON();} else //否则熄灭LED { LED_OFF();} }

void LedStateChange(void){ if(g_bSystemTime1Ms)//系统1MS时标到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED计数器加一

if(g_u16LedTimeCount >= 500)//计数达到500,即500MS到了,改变LED的状态。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

void main(void){ Timer0Init();EA = 1;LED_CS11 = 1;//74HC595输出允许

LED_SEG = 0;//数码管段选和位选禁止(因为它们和LED共用P0口)LED_DIG = 0;while(1){ LedProcess();LedStateChange();} }

void Time0Isr(void)interrupt 1 { TH0 = 0xfc;//定时器重新赋初值 TL0 = 0x66;g_bSystemTime1Ms = 1;//1MS时标标志位置位 }

“从单片机初学者迈向单片机工程师”

第三章----模块化编程初识

好的开始是成功的一半

通过上一章的学习,我想你已经掌握了如何在程序中释放CPU了。希望能够继续坚持下去。一个良好的开始是成功的一半。我们今天所做的一切都是为了在单片机编程上做的更好。

在谈论今天的主题之前,先说下我以前的一些经历。在刚开始接触到C语言程序的时候,由于学习内容所限,写的程序都不是很大,一般也就几百行而矣。所以所有的程序都完成在一个源文件里面。记得那时候大一参加学校里的一个电子设计大赛,调试了一个多星期,所有程序加起来大概将近1000行,长长的一个文件,从上浏览下来都要好半天。出了错误简单的语法错误还好定位,其它一些错误,往往找半天才找的到。那个时候开始知道了模块化编程这个东西,也尝试着开始把程序分模块编写。最开始是把相同功能的一些函数(譬如1602液晶的驱动)全部写在一个头文件(.h)文件里面,然后需要调用的地方包含进去,但是很快发现这种方法有其局限性,很容易犯重复包含的错误。

而且调用起来也很不方便。很快暑假的电子设计大赛来临了,学校对我们的单片机软件编程进行了一些培训。由于学校历年来参加国赛和省赛,因此积累了一定数量的驱动模块,那些日子,老师每天都会布置一定量的任务,让我们用这些模块组合起来,完成一定功能。而正是那些日子模块化编程的培训,使我对于模块化编程有了更进一步的认识。并且程序规范也开始慢慢注意起来。此后的日子,无论程序的大小,均采用模块化编程的方式去编写。很长一段时间以来,一直有单片机爱好者在QQ上和我一起交流。有时候,他们会发过来一些有问题的程序源文件,让我帮忙修改一下。同样是长长的一个文件,而且命名极不规范,从头看下来,着实是痛苦,说实话,还真不如我重新给他们写一个更快一些,此话到不假,因为手头积累了一定量的模块,在完成一个新的系统时候,只需要根据上层功能需求,在底层模块的支持下,可以很快方便的完成。而不需要从头到尾再一砖一瓦的重新编写。藉此,也可以看出模块化编程的一个好处,就是可重复利用率高。下面让我们揭开模块化神秘面纱,一窥其真面目。C语言源文件 *.c 提到C语言源文件,大家都不会陌生。因为我们平常写的程序代码几乎都在这个XX.C文件里面。编译器也是以此文件来进行编译并生成相应的目标文件。作为模块化编程的组成基础,我们所要实现的所有功能的源代码均在这个文件里。理想的模块化应该可以看成是一个黑盒子。即我们只关心模块提供的功能,而不管模块内部的实现细节。好比我们买了一部手机,我们只需要会用手机提供的功能即可,不需要知晓它是如何把短信发出去的,如何响应我们按键的输入,这些过程对我们用户而言,就是是一个黑盒子。

在大规模程序开发中,一个程序由很多个模块组成,很可能,这些模块的编写任务被分配到不同的人。而你在编写这个模块的时候很可能就需要利用到别人写好的模块的借口,这个时候我们关心的是,它的模块实现了什么样的接口,我该如何去调用,至于模块内部是如何组织的,对于我而言,无需过多关注。而追求接口的单一性,把不需要的细节尽可能对外部屏蔽起来,正是我们所需要注意的地方。C语言头文件 *.h 谈及到模块化编程,必然会涉及到多文件编译,也就是工程编译。在这样的一个系统中,往往会有多个C文件,而且每个C文件的作用不尽相同。在我们的C文件中,由于需要对外提供接口,因此必须有一些函数或者是变量提供给外部其它文件进行调用。假设我们有一个LCD.C文件,其提供最基本的LCD的驱动函数 LcdPutChar(char cNewValue);//在当前位置输出一个字符 而在我们的另外一个文件中需要调用此函数,那么我们该如何做呢?

头文件的作用正是在此。可以称其为一份接口描述文件。其文件内部不应该包含任何实质性的函数代码。我们可以把这个头文件理解成为一份说明书,说明的内容就是我们的模块对外提供的接口函数或者是接口变量。同时该文件也包含了一些很重要的宏定义以及一些结构体的信息,离开了这些信息,很可能就无法正常使用接口函数或者是接口变量。但是总的原则是:不该让外界知道的信息就不应该出现在头文件里,而外界调用模块内接口函数或者是接口变量所必须的信息就一定要出现在头文件里,否则,外界就无法正确的调用我们提供的接口功能。因而为了让外部函数或者文件调用我们提供的接口功能,就必须包含我们提供的这个接口描述文件----即头文件。同时,我们自身模块也需要包含这份模块头文件(因为其包含了模块源文件中所需要的宏定义或者是结构体),好比我们平常所用的文件都是一式三份一样,模块本身也需要包含这个头文件。

下面我们来定义这个头文件,一般来说,头文件的名字应该与源文件的名字保持一致,这样我们便可以清晰的知道哪个头文件是哪个源文件的描述。

于是便得到了LCD.C的头文件LCD.h 其内容如下。#ifndef _LCD_H_ #define _LCD_H_ extern LcdPutChar(char cNewValue);#endif

这与我们在源文件中定义函数时有点类似。不同的是,在其前面添加了extern 修饰符表明其是一个外部函数,可以被外部其它模块进行调用。#ifndef _LCD_H_ #define _LCD_H_ #endif

这个几条条件编译和宏定义是为了防止重复包含。假如有两个不同源文件需要调用LcdPutChar(char cNewValue)这个函数,他们分别都通过#include “Lcd.h”把这个头文件包含了进去。在第一个源文件进行编译时候,由于没有定义过 _LCD_H_ 因此 #ifndef _LCD_H_ 条件成立,于是定义_LCD_H_ 并将下面的声明包含进去。在第二个文件编译时候,由于第一个文件包含时候,已经将_LCD_H_定义过了。因此#ifndef _LCD_H_ 不成立,整个头文件内容就没有被包含。假设没有这样的条件编译语句,那么两个文件都包含了extern LcdPutChar(char cNewValue);就会引起重复包含的错误。

不得不说的typedef 很多朋友似乎了习惯程序中利用如下语句来对数据类型进行定义 #define uint unsigned int #define uchar unsigned char 然后在定义变量的时候 直接这样使用 uint g_nTimeCounter = 0;不可否认,这样确实很方便,而且对于移植起来也有一定的方便性。但是考虑下面这种情况你还会 这么认为吗?

#define PINT unsigned int * //定义unsigned int 指针类型 PINT g_npTimeCounter, g_npTimeState;那么你到底是定义了两个unsigned int 型的指针变量,还是一个指针变量,一个整形变量呢?而你的初衷又是什么呢,想定义两个unsigned int 型的指针变量吗?如果是这样,那么估计过不久就会到处抓狂找错误了。

庆幸的是C语言已经为我们考虑到了这一点。typedef 正是为此而生。为了给变量起一个别名我们可以用如下的语句

typedef unsigned int uint16;//给指向无符号整形变量起一个别名 uint16 typedef unsigned int * puint16;//给指向无符号整形变量指针起一个别名 puint16 在我们定义变量时候便可以这样定义了:

uint16 g_nTimeCounter = 0;//定义一个无符号的整形变量 puint16 g_npTimeCounter;//定义一个无符号的整形变量的指针

在我们使用51单片机的C语言编程的时候,整形变量的范围是16位,而在基于32的微处理下的整形变量是32位。倘若我们在8位单片机下编写的一些代码想要移植到32位的处理器上,那么很可能我们就需要在源文件中到处修改变量的类型定义。这是一件庞大的工作,为了考虑程序的可移植性,在一开始,我们就应该养成良好的习惯,用变量的别名进行定义。如在8位单片机的平台下,有如下一个变量定义 uint16 g_nTimeCounter = 0;如果移植32单片机的平台下,想要其的范围依旧为16位。

可以直接修改uint16 的定义,即

typedef unsigned short int uint16;这样就可以了,而不需要到源文件处处寻找并修改。

将常用的数据类型全部采用此种方法定义,形成一个头文件,便于我们以后编程直接调用。文件名 MacroAndConst.h 其内容如下:

#ifndef _MACRO_AND_CONST_H_ #define _MACRO_AND_CONST_H_

typedef unsigned int uint16;typedef unsigned int UINT;typedef unsigned int uint;typedef unsigned int UINT16;typedef unsigned int WORD;typedef unsigned int word;typedef int int16;typedef int INT16;typedef unsigned long uint32;

typedef unsigned long UINT32;typedef unsigned long DWORD;typedef unsigned long dword;typedef long int32;typedef long INT32;typedef signed char int8;typedef signed char INT8;typedef unsigned char byte;typedef unsigned char BYTE;typedef unsigned char uchar;typedef unsigned char UINT8;typedef unsigned char uint8;typedef unsigned char BOOL;#endif

至此,似乎我们对于源文件和头文件的分工以及模块化编程有那么一点概念了。那么让我们趁热打铁,将上一章的我们编写的LED闪烁函数进行模块划分并重新组织进行编译。

在上一章中我们主要完成的功能是P0口所驱动的LED以1Hz的频率闪烁。其中用到了定时器,以及LED驱动模块。因而我们可以简单的将整个工程分成三个模块,定时器模块,LED模块,以及主函数 对应的文件关系如下

main.c Timer.hTimer.c--Led.hLed.c--在开始重新编写我们的程序之前,先给大家讲一下如何在KEIL中建立工程模板吧,这个模板是我一直沿用至今。希望能够给大家一点启发。

下面的内容就主要以图片为主了。同时辅以少量文字说明。我们以芯片AT89S52为例。

(原文件名:1.jpg)

引用图片

(原文件名:2.jpg)

引用图片

(原文件名:3.jpg)

引用图片

(原文件名:4.jpg)

引用图片

(原文件名:5.jpg)

引用图片

(原文件名:6.jpg)

引用图片

(原文件名:7.jpg)

引用图片

(原文件名:8.jpg)

引用图片

(原文件名:9.jpg)

引用图片

(原文件名:10.jpg)

引用图片

(原文件名:11.jpg)

引用图片

(原文件名:12.jpg)

引用图片

(原文件名:13.jpg)

引用图片

(原文件名:14.jpg)

引用图片

(原文件名:15.jpg)

引用图片

(原文件名:16.jpg)

引用图片

(原文件名:17.jpg)

引用图片

(原文件名:18.jpg)

引用图片

(原文件名:19.jpg)

引用图片

(原文件名:20.jpg)

引用图片

(原文件名:21.jpg)

引用图片

(原文件名:22.jpg)

引用图片

OK,到此一个简单的工程模板就建立起来了,以后我们再新建源文件和头文件的时候,就可以直接保存到src文件目录下面了。

下面我们开始编写各个模块文件。

首先编写Timer.c 这个文件主要内容就是定时器初始化,以及定时器中断服务函数。其内容如下。#include

bit g_bSystemTime1Ms = 0;// 1MS系统时标

void Timer0Init(void){ TMOD &= 0xf0;TMOD |= 0x01;//定时器0工作方式1 TH0 = 0xfc;//定时器初始值 TL0 = 0x66;TR0 = 1;ET0 = 1;}

void Time0Isr(void)interrupt 1 { TH0 = 0xfc;//定时器重新赋初值 TL0 = 0x66;g_bSystemTime1Ms = 1;//1MS时标标志位置位 }

由于在Led.c文件中需要调用我们的g_bSystemTime1Ms变量。同时主函数需要调用Timer0Init()初始化函数,所以应该对这个变量和函数在头文件里作外部声明。以方便其它函数调用。

Timer.h 内容如下。#ifndef _TIMER_H_ #define _TIMER_H_

extern void Timer0Init(void);extern bit g_bSystemTime1Ms;#endif

完成了定时器模块后,我们开始编写LED驱动模块。Led.c 内容如下:

#include #include “MacroAndConst.h” #include “Led.h” #include “Timer.h”

static uint16 g_u16LedTimeCount = 0;//LED计数器 static uint8 g_u8LedState = 0;//LED状态标志, 0表示亮,1表示熄灭

#define LED P0 //定义LED接口

#define LED_ON()LED = 0x00;//所有LED亮 #define LED_OFF()LED = 0xff;//所有LED熄灭

void LedProcess(void){ if(0 == g_u8LedState)//如果LED的状态为亮,则点亮LED { LED_ON();} else //否则熄灭LED { LED_OFF();} }

void LedStateChange(void){ if(g_bSystemTime1Ms)//系统1MS时标到 { g_bSystemTime1Ms = 0;g_u16LedTimeCount++;//LED计数器加一

if(g_u16LedTimeCount >= 500)//计数达到500,即500MS到了,改变LED的状态。{ g_u16LedTimeCount = 0;g_u8LedState =!g_u8LedState;} } }

这个模块对外的借口只有两个函数,因此在相应的Led.h 中需要作相应的声明。Led.h 内容: #ifndef _LED_H_ #define _LED_H_

extern void LedProcess(void);extern void LedStateChange(void);#endif

这两个模块完成后,我们将其C文件添加到工程中。然后开始编写主函数里的代码。如下所示:

#include #include “MacroAndConst.h” #include “Timer.h” #include “Led.h”

sbit LED_SEG = P1^4;//数码管段选 sbit LED_DIG = P1^5;//数码管位选 sbit LED_CS11 = P1^6;//led控制位

void main(void){ LED_CS11 = 1;//74HC595输出允许

LED_SEG = 0;//数码管段选和位选禁止(因为它们和LED共用P0口)LED_DIG = 0;Timer0Init();EA = 1;while(1){ LedProcess();LedStateChange();} }

整个工程截图如下:

至此,第三章到此结束。

一起来总结一下我们需要注意的地方吧

[color=#FF0000]1.C语言源文件(*.c)的作用是什么 2.C语言头文件(*.h)的作用是什么 3.typedef 的作用 4.工程模板如何组织

5.如何创建一个多模块(多文件)的工程

“从单片机初学者迈向单片机工程师”之KEY主题讨论

按键程序编写的基础

从这一章开始,我们步入按键程序设计的殿堂。在基于单片机为核心构成的应用系统中,用户输入是必不可少的一部分。输入可以分很多种情况,譬如有的系统支持PS2键盘的接口,有的系统输入是基于编码器,有的系统输入是基于串口或者USB或者其它输入通道等等。在各种输入途径中,更常见的是,基于单个按键或者由单个键盘按照一定排列构成的矩阵键盘(行列键盘)。我们这一篇章主要讨论的对象就是基于单个按键的程序设计,以及矩阵键盘的程序编写。◎按键检测的原理

常见的独立按键的外观如下,相信大家并不陌生,各种常见的开发板学习板上随处可以看到他们的身影。

(原文件名:1.jpg)

引用图片

总共有四个引脚,一般情况下,处于同一边的两个引脚内部是连接在一起的,如何分辨两个引脚是否处在同一边呢?可以将按键翻转过来,处于同一边的两个引脚,有一条突起的线将他们连接一起,以标示它们俩是相连的。如果无法观察得到,用数字万用表的二极管挡位检测一下即可。搞清楚这点非常重要,对于我们画PCB的时候的封装很有益。

它们和我们的单片机系统的I/O口连接一般如下:

(原文件名:2.jpg)

引用图片

对于单片机I/O内部有上拉电阻的微控制器而言,还可以省掉外部的那个上拉电阻。简单分析一下按键检测的原理。当按键没有按下的时候,单片机I/O通过上拉电阻R接到VCC,我们在程序中读取该I/O的电平的时候,其值为1(高电平);当按键S按下的时候,该I/O被短接到GND,在程序中读取该I/O的电平的时候,其值为0(低电平)。这样,按键的按下与否,就和与该按键相连的I/O的电平的变化相对应起来。结论:我们在程序中通过检测到该I/O口电平的变化与否,即可以知道按键是否被按下,从而做出相应的响应。一切看起来很美好,是这样的吗?

◎现实并非理想

在我们通过上面的按键检测原理得出上述的结论的时候,其实忽略了一个重要的问题,那就是现实中按键按下时候的电平变化状态。我们的结论是基于理想的情况得出来的,就如同下面这幅按键按下时候对应电平变化的波形图一样:

(原文件名:3.jpg)

引用图片

而实际中,由于按键的弹片接触的时候,并不是一接触就紧紧的闭合,它还存在一定的抖动,尽管这个时间非常的短暂,但是对于我们执行时间以us为计算单位的微控制器来说,它太漫长了。因而,实际的波形图应该如下面这幅示意图一样。

(原文件名:4.jpg)

引用图片

这样便存在这样一个问题。假设我们的系统有这样功能需求:在检测到按键按下的时候,将某个I/O的状态取反。由于这种抖动的存在,使得我们的微控制器误以为是多次按键的按下,从而将某个I/O的状态不断取反,这并不是我们想要的效果,假如该I/O控制着系统中某个重要的执行的部件,那结果更不是我们所期待的。于是乎有人便提出了软件消除抖动的思想,道理很简单:抖动的时间长度是一定的,只要我们避开这段抖动时期,检测稳定的时候的电平不久可以了吗?听起来确实不错,而且实际应用起来效果也还可以。于是,各种各样的书籍中,在提到按键检测的时候,总也不忘说道软件消抖。就像下面的伪代码所描述的一样。(假设按键按下时候,低电平有效)

If(0 == io_KeyEnter)//如果有键按下了 { Delayms(20);//先延时20ms避开抖动时期

If(0 == io_KeyEnter)//然后再检测,如果还是检测到有键按下 { return KeyValue;//是真的按下了,返回键值 } else { return KEY_NULL //是抖动,返回空的键值 } while(0 == io_KeyEnter);//等待按键释放 }

所以合理的分配好微控制的处理时间,是编写按键程序的基础。乍看上去,确实挺不错,实际中呢?在实际的系统中,一般是不允许这么样做的。为什么呢?首先,这里的Delayms(20), 让微控制器在这里白白等待了20 ms 的时间,啥也没干,考虑我在《学会释放CPU》一章中所提及的几点,这是不可取的。其次while(0 == io_KeyEnter);更是程序设计中的大忌(极少的特殊情况例外)。任何非极端情况下,都不要使用这样语句来堵塞微控制器的执行进程。原本是等待按键释放,结果CPU就一直死死的盯住该按键,其它事情都不管了,那其它事情不干了吗?你同意别人可不会同意

◎消除抖动有必要吗? 的确,软件上的消抖确实可以保证按键的有效检测。但是,这种消抖确实有必要吗?有人提出了这样的疑问。抖动是按键按下的过程中产生的,如果按键没有按下,抖动会产生吗?如果没有按键按下,抖动也会在I/O上出现,我会立刻把这个微控制器锤了,永远不用这样一款微控制器。所以抖动的出现即意味着按键已经按下,尽管这个电平还没有稳定。所以只要我们检测到按键按下,即可以返回键值,问题的关键是,在你执行完其它任务的时候,再次执行我们的按键任务的时候,抖动过程还没有结束,这样便有可能造成重复检测。所以,如何在返回键值后,避免重复检测,或者在按键一按下就执行功能函数,当功能函数的执行时间小于抖动时间时候,如何避免再次执行功能函数,就成为我们要考虑的问题了。这是一个仁者见仁,智者见智的问题,就留给大家去思考吧。所以消除抖动的目的是:防止按键一次按下,多次响应。

“从单片机初学者迈向单片机工程师”之KEY主题讨论

基于状态转移的独立按键程序设计)的那种,有一个小液晶屏,还有四个按键,功能是时钟,闹钟以及秒表。在调整时间的时候,短按+键每次调整值加一,长按的时候调整值连续增加。小的时候很好奇,这样的功能到底是如何实现的呢,今天就让我们来剖析它的原理吧。 本章所描述的按键程序要达到的目的:检测按键按下,短按,长按,释放。即通过按键的返回值我们可以获取到如下的信息:按键按下(短按),按键长按,按键连_发,按键释放。不知道大家还记得小时候玩过的电子钟没有,就是外形类似于CALL 机(CALL 机,好像是很古老的东西了 状态在生活中随处可见。譬如早上的时候,闹钟把你叫醒了,这个时候,你便处于清醒的状态,马上你就穿衣起床洗漱吃早餐,这一系列事情就是你在这个状态做的事情。做完这些后你会去等车或者开车去上班,这个时候你就处在上班途中的状态„..中午下班时间到了,你就处于中午下班的状态,诸如此类等等,在每一个状态我们都会做一些不同的事情,而总会有外界条件促使我们转换到另外一种状态,譬如闹钟叫醒我们了,下班时间到了等等。对于状态的定义出发点不同,考虑的方向不同,或者会有些许细节上面的差异,但是大的状态总是相同的。生活中的事物同样遵循同样的规律,譬如,用一个智能充电器给你的手机电池充电,刚开始,它是处于快速充电状态,随着电量的增加,电压的升高,当达到规定的电压时候,它会转换到恒压充电。总而言之,细心观察,你会发现生活中的总总都可以归结为一个个的状态,而状态的变换或者转移总是由某些条件引起同时伴随着一些动作的发生。我们的按键亦遵循同样的规律,下面让我们来简单的描绘一下它的状态流程转移图。

(原文件名:1.jpg)

引用图片

下面对上面的流程图进行简要的分析。首先按键程序进入初始状态S1,在这个状态下,检测按键是否按下,如果有按下,则进入按键消抖状态2,在下一次执行按键程序时候,直接由按键消抖状态进入按键按下状态3,在此状态下检测按键是否按下,如果没有按键按下,则返回初始状态S1,如果有则可以返回键值,同时进入长按状态S4,在长按状态下每次进入按键程序时候对按键时间计数,当计数值超过设定阈值时候,则表明长按事件发生,同时进入按键连_发状态S5。如果按键键值为空键,则返回按键释放状态S6,否则继续停留在本状态。在按键连_发状态下,如果按键键值为空键则返回按键释放状态S6,如果按键时间计数超过连_发阈值,则返回连_发按键值,清零时间计数后继续停留在本状态。

看了这么多,也许你已经有一个模糊的概念了,下面让我们趁热打铁,一起来动手编写按键驱动程序吧。

下面是我使用的硬件的连接图。

(原文件名:2.jpg)

引用图片

硬件连接很简单,四个独立按键分别接在P3^0------P3^3四个I/O上面。

因为51单片机I/O口内部结构的限制,在读取外部引脚状态的时候,需要向端口写1.在51单片机复位后,不需要进行此操作也可以进行读取外部引脚的操作。因此,在按键的端口没有复用的情况下,可以省略此步骤。而对于其它一些真正双向I/O口的单片机来说,将引脚设置成输入状态,是必不可少的一个步骤。下面的程序代码初始化引脚为输入。void KeyInit(void){ io_key_1 = 1;io_key_2 = 1;io_key_3 = 1;io_key_4 = 1;} 根据按键硬件连接定义按键键值

#define KEY_VALUE_1 0x0e #define KEY_VALUE_2 0x0d #define KEY_VALUE_3 0x0b #define KEY_VALUE_4 0x07 #define KEY_NULL 0x0f 下面我们来编写按键的硬件驱动程序。

根据第一章所描述的按键检测原理,我们可以很容易的得出如下的代码: static uint8 KeyScan(void){ if(io_key_1 == 0)return KEY_VALUE_1;if(io_key_2 == 0)return KEY_VALUE_2;if(io_key_3 == 0)return KEY_VALUE_3;if(io_key_4 == 0)return KEY_VALUE_4;return KEY_NULL;} 其中io_key_1等是我们按键端口的定义,如下所示: sbit io_key_1 = P3^0;sbit io_key_2 = P3^1;sbit io_key_3 = P3^2;sbit io_key_4 = P3^3;

KeyScan()作为底层按键的驱动程序,为上层按键扫描提供一个接口,这样我们编写的上层按键扫描函数可以几乎不用修改就可以拿到我们的其它程序中去使用,使得程序复用性大大提高。同时,通过有意识的将与底层硬件连接紧密的程序和与硬件无关的代码分开写,使得程序结构层次清晰,可移植性也更好。对于单片机类的程序而言,能够做到函数级别的代码重用已经足够了。在编写我们的上层按键扫描函数之前,需要先完成一些宏定义。//定义长按键的TICK数,以及连_发间隔的TICK数 #define KEY_LONG_PERIOD 100 #define KEY_CONTINUE_PERIOD 25

//定义按键返回值状态(按下,长按,连_发,释放)#define KEY_DOWN 0x80 #define KEY_LONG 0x40 #define KEY_CONTINUE 0x20 #define KEY_UP 0x10

//定义按键状态

#define KEY_STATE_INIT 0 #define KEY_STATE_WOBBLE 1 #define KEY_STATE_PRESS 2 #define KEY_STATE_LONG 3 #define KEY_STATE_CONTINUE 4 #define KEY_STATE_RELEASE 5

接着我们开始编写完整的上层按键扫描函数,按键的短按,长按,连按,释放等等状态的判断均是在此函数中完成。对照状态流程转移图,然后再看下面的函数代码,可以更容易的去理解函数的执行流程。完整的函数代码如下: void GetKey(uint8 *pKeyValue){ static uint8 s_u8KeyState = KEY_STATE_INIT;static uint8 s_u8KeyTimeCount = 0;static uint8 s_u8LastKey = KEY_NULL;//保存按键释放时候的键值 uint8 KeyTemp = KEY_NULL;

KeyTemp = KeyScan();//获取键值

switch(s_u8KeyState){ case KEY_STATE_INIT : { if(KEY_NULL!=(KeyTemp)){ s_u8KeyState = KEY_STATE_WOBBLE;} } break;

case KEY_STATE_WOBBLE : //消抖 { s_u8KeyState = KEY_STATE_PRESS;} break;

case KEY_STATE_PRESS : { if(KEY_NULL!=(KeyTemp)){ s_u8LastKey = KeyTemp;//保存键值,以便在释放按键状态返回键值 KeyTemp |= KEY_DOWN;//按键按下 s_u8KeyState = KEY_STATE_LONG;} else { s_u8KeyState = KEY_STATE_INIT;} } break;

case KEY_STATE_LONG : { if(KEY_NULL!=(KeyTemp)){ if(++s_u8KeyTimeCount > KEY_LONG_PERIOD){ s_u8KeyTimeCount = 0;KeyTemp |= KEY_LONG;//长按键事件发生 s_u8KeyState = KEY_STATE_CONTINUE;} } else { s_u8KeyState = KEY_STATE_RELEASE;} } break;

case KEY_STATE_CONTINUE : { if(KEY_NULL!=(KeyTemp)){ if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD){ s_u8KeyTimeCount = 0;KeyTemp |= KEY_CONTINUE;} } else { s_u8KeyState = KEY_STATE_RELEASE;} } break;

case KEY_STATE_RELEASE : { s_u8LastKey |= KEY_UP;KeyTemp = s_u8LastKey;s_u8KeyState = KEY_STATE_INIT;} break;

default : break;} *pKeyValue = KeyTemp;//返回键值 } 关于这个函数内部的细节我并不打算花过多笔墨去讲解。对照着按键状态流程转移图,然后去看程序代码,你会发现其实思路非常清晰。最能让人理解透彻的,莫非就是将整个程序自己看懂,然后想象为什么这个地方要这样写,抱着思考的态度去阅读程序,你会发现自己的程序水平会慢慢的提高。所以我更希望的是你能够认认真真的看完,然后思考。也许你会收获更多。

不管怎么样,这样的一个程序已经完成了本章开始时候要求的功能:按下,长按,连按,释放。事实上,如果掌握了这种基于状态转移的思想,你会发现要求实现其它按键功能,譬如,多键按下,功能键等等,亦相当简单,在下一章,我们就去实现它。

在主程序中我编写了这样的一段代码,来演示我实现的按键功能。void main(void){ uint8 KeyValue = KEY_NULL;uint8 temp = 0;LED_CS11 = 1;//流水灯输出允许 LED_SEG = 0;LED_DIG = 0;Timer0Init();KeyInit();EA = 1;while(1){ Timer0MainLoop();KeyMainLoop(&KeyValue);

if(KeyValue ==(KEY_VALUE_1 | KEY_DOWN))P0 = ~1;if(KeyValue ==(KEY_VALUE_1 | KEY_LONG))P0 = ~2;if(KeyValue ==(KEY_VALUE_1 | KEY_CONTINUE)){ P0 ^= 0xf0;} if(KeyValue ==(KEY_VALUE_1 | KEY_UP))P0 = 0xa5;} } 按住第一个键,可以清晰的看到P0口所接的LED的状态的变化。当按键按下时候,第一个LED灯亮,等待2 S后第二个LED亮,第一个熄灭,表示长按事件发生。再过500 ms 第5~8个LED闪烁,表示连按事件发生。当释放按键时候,P0口所接的LED的状态为: 灭亮灭亮亮灭亮灭,这也正是P0 = 0xa5这条语句的功能。

第二篇:语言学习心得

汇编学习心得

08网工

(一)班 李锐 0804031002 在大三接触汇编语言之前,我们在计算机组成原理课程中就已经有所了解了,但也只是略微明白一些如jmp,mov这样的指令,极度缺乏系统性的学习。

在接触这门课程后,感到汇编语言并不是很容易就可以弄懂的。相比较以前学过的高级语言如C、C++等,电脑等于在迁就人的思维方式,但学汇编,人却必须要去迁就电脑的思维方式,要设身处地地用电脑的角度去思考问题,这就是我们学习汇编语言时遇到的最大的障碍。

另外,在C语言中不到10个语句构成的程序,用汇编语言却要好几十行甚至上百行。这不得不让我们对汇编产生一种恐惧感。事实上,这是完全不必要的。一旦对它的原理掌握后,编写程序就容易多了。另外,学习汇编语言能让我们更加了解计算机内部的组织结构,对我们计算机专业的学生来说,学习汇编也是提升综合能力的关键环节。

汇编的学习不仅仅是学习其语法,而更多的是学习计算机基本的体系结构。其中遇到很多新的概念,名字。如寄存器、中断、寻址方式等。这些概念在刚接触汇编这门课的时候难以理解,但在之后的学习中通过老师的讲解,自己亲手编程的方式也就渐渐清晰明了。

我们在学习之前都需要明确什么是汇编语言。计算机能够直接识别的数据是由二进制数0和1组成的代码。机器指令就是用二进制代码组成的指令,一条机器指令控制计算机完成一个基本操作。为了克服机器语言的缺点,人们采用助记符表示机器指令的操作码,用变量代替操作数的存放地址等,这样就形成了汇编语言。

经过一个学期的学习,我也慢慢摸出了汇编学习的规律。

首先,学习这门语言时如果能联系上以前学过的其他高级语言的知识,则会起到良好的效果。例如C语言程序的运行逻辑结构有顺序(按语句依次执行)、分支结构(IF...THEN...ELSE...),循环结构(FOR...NEXT)三种结构,也通过C语言了解并掌握了什么是子程序,什么是调用。事实上,汇编语言中有关程序结构,子程序等等的知识都是跟C语言十分相似的,只是在编程时用到的语言不同:汇编语言完全面向机器,需要指明数据在寄存器、内存中的流向。

第二,学习汇编语言,首要问题是学习80X86指令系统。如果能将指令系统中的各个助记符、格式等都能完全掌握并灵活运用,大部分工作就已经完成了。指令系统确定了CPU所能完成的功能,是用汇编语言进行程序设计的最基本部分。如果不熟悉汇编指令的功能及其有关规定,那肯定不能灵活使用汇编语言。

指令的种类十分繁杂,但其格式却是统一的。

其中方括号中的内容为可选项。指令助记符决定了指令的功能,对应一条二进制编码的机器指令。指令的操作数个数由该指令确定,可以没有操作数,也可以有一个或多个操作数,大多数指令要显示写出来,还有些操作数是隐含的。当指令包含操作数的时候,书写时必须遵守:

1、指令助记符和操作数之间有分隔符,比如几个空格;

2、如果含有多个操作数,操作数之间用逗号分隔。

现在简单总结汇编语言指令的分类:

1、数据传送指令;

2、标志位操作指令;3、算术运算指令;

4、逻辑运算指令;

5、移位运算指令;

6、位操作指令;

7、比较运算指令;

8、循环指令;

9、转移指令;

10、条件设置字节指令;

11、字符串操作指令;

12、BCD码运算调整指令;

13、处理器指令。

在编写汇编程序时,应该注意特别容易出现的错误,例如在编写数据传送指令时,目的操作数和源操作数的类型一定要匹配,CS不能作为目的操作数,offset后只能跟简单地址符号,等等。

汇编语言的寻址方式有直接寻址,寄存器寻址,基址变址寻址,相对基址变址寻址等等,掌握这些都是编写汇编程序很重要的环节。

汇编语言中的程序结构,子程序等知识也会帮助我们编写出一个更加完善的汇编程序。另外,我们还应该掌握DOS系统功能调用,宏汇编等知识。这里要对子程序和宏汇编加以区分,我的理解是:子程序是调用,而宏是替换,比如一个MAX宏,所有出现MAX的地方,都用宏代码代替。

在学习汇编语言时,指令的功能是学习和掌握的重点,要准确有效并合理的使用这些指令,必须了解一些使用的规则。现对汇编语言编程时的规则进行总结,归纳起来有三点:

1、要求指令操作数的寻址方式;

2、指令对标志位的影响和标志位对指令的影响;

3、指令的执行时间,对可完成同样功能的指令,要选用执行时间短的指令。

还有一点十分重要。和所有的程序设计语言一样,要学好汇编语言,实验是必不可少的环节。我们深有体会:书上的程序都能看懂,基本原理也都明白,但是在自己亲手编写程序时,却无从下手,甚至连第一句该怎么写都不知道。通过实验,可以在很大程度上加深印象。在书上看程序,一切都是理所当然,十分顺利,而自己动手,才会真正发现自己的不足之处。程序的编写在记事本中进行即可,掌握debug的使用对实验是有很大帮助的。

现在汇编语言的学课程已经告一段落了,学习过程中遇到的所有困难以及遗留的难点都需要我们在其余时间里逐步攻克,在打好基础的前提下,学习更加专业的汇编知识。

以上便是我在学习汇编语言过程中所有的心得体会,我们会在不断的学习与实践中向着更深入的层面迈进

第三篇:语言学习心得

汇编语言学习心得

在接触这门课之前就已对汇编语言有所了解,一方面是在计算机组成原理中有所涉及,虽然那时只知道简单的mov、add、jmp指令,不清楚其具体用法,缺乏系统性的学习;另一方面是在相关书籍中了解到汇编语言写驱动比较好但比较难,当时就觉得汇编是门很牛的语言,很想学习。接触这门课后,感到汇编语言的确不是很容易就可以弄懂的,相比较以前学过的高级语言如C、C++等,电脑等于在迁就人的思维方式,但学汇编,人却必须要去迁就电脑的思维方式,要设身处地地用电脑的角度去思考问题,这就是我们学习汇编语言时遇到的最大的障碍。通过这一学期的学习,对汇编有了初步的掌握,可以说是汇编的入门教程。

在接触汇编语言的时候,对汇编的第一印象就是觉得这种语言非常繁琐和松散,里面有很多细小的知识点,而且有很多规定,必须要根据严格的规则来编写才能够写出正确的程序,譬如一些指令的具体作用,如XLAT指令、CWB指令、LEA指令、OFFSET等的含义和功能,还有大于、小于、等于指令的写法都要记住,不能像高级语言里面用符号“< > =”就能够实现了,正是汇编中的这些规则让我觉得汇编真是特别繁琐,所以在刚刚开始学习汇编的时候经常觉得有些不知所措,特别是对于几种寻址方式,还有各个段的功能,以及各种寄存器的作用都觉得学的很混乱,虽然老师在上课时说的都很详细了,但是在当时好像听懂了,但是很快又弄不清了,还有一些指令的用法会很快就忘记,或者是记混了,但是慢慢接触多了,特别是在做实验的之后,学习得很快,因为要是把学过的知识具体应用到现实中,对于知识的掌握要求就更深一步了,在实验过程中,发现了很多问题,例如一些寻址方式的应用,字符串的定义末尾要加上$符号,在利用AX、BX、CX、DX这几个寄存器的时候注意考虑PUSH进栈对数据进行保护,通过在实验中不断出现错误并改正,对汇编语言的掌握和使用能力都有所提高了。

刚开始对汇编的最大感觉就是觉得用起来很麻烦,不像高级语言那么随意,寄存器就那么几个,用的时候要好好利用,而且还要考虑到计算机内部的工作情况,特别是数据在内存中的存储情况,但是经过学习,发现汇编语言对我们学习好其他语言是非常有用的,原来在C语言中一直不是非常清楚的数据和地址的区别也通过学习汇编而了解得很清楚,通过汇编对CPU里面的工作情况也有了一定的认识,写有些指令的时候要考虑到CPU里面的标志位的改变和利用标志位来实现一些功能,这些在高级语言中是没有的。对于汇编中的指令我很多记不住,由于课件中的程序的注释都是中文的,而课本上的注释都是英文的,刚开始觉得看课件比较好一些,但是后来我发现课本上的英文注释更加容易理解和记忆,和用到的指令是匹配的,因为指令也都是英文缩写方式的,而且都是一些简单的单词,阅读起来并不复杂。

和所有的程序设计语言一样,要学好汇编语言,实验是必不可少的环节。我深有体会:书上的程序都能看懂,基本原理也都明白,但是在自己亲手编写程序时,却无从下手,甚至连第一句该怎么写都不知道。后来随着老师的讲解和实验的进行,知道了程序的编写格式,包括数据段、堆栈段和代码段,其中数据段和堆栈段可以不定义,但代码段是必不可少的,也掌握了该怎么调试程序、运行程序等。特别是debug命令的使用,可以配合与list文件的使用,让我们可以调试排错、检查结果是否正确等,掌握debug的使用对实验有很大的帮助。程序的编写在记事本中进行即可,再在mask中编译,link中连接运行,当然也可以在专门的汇编工具中进行。通过实验,可以在很大程度上加深印象,在书上看程序,一切都是理所当然,十分顺利,而自己动手,才会真正发现自己的不足之处

老师经常说其实很多知识都是相通的,在学习了汇编之后对这点可以说是深有感触,而且在汇编也和很多其他课程息息相关,譬如本学期的微机接口课程里面所用得到的也是汇编语言,汇编虽然是比较低级的语言,但是许多工作还是需要通过汇编来完成,而且很好的掌握汇编后,能够对计算机硬件很好的支配,汇编很接近计算机的底层,总的来说汇编在今天的作用还是不容小觑的,学习好了汇编这门课程对于学习其他课程和进一步学好计算机都有很大的帮助。

14GB软件一班 01410271X07 肖雅丽

第四篇:语言学习心得

2010年6月英语四级、六级考试愈发临近,大家都希望掌握一些应试技巧,从而能够在四六级考试中取得比较满意的成绩。下面沪江网校的方辰晖老师从几个方面来谈一下语言学习以及四级六级应试技巧等问题。

语言是人类经过长期实践积累的,用于交流沟通的工具。历经无数代积累演变下来的语言,学好肯定不是一朝一夕之功,这就决定了,学习语言不可能一蹴而就,更不能闭门造车。但是,如果我们能提高对语言的敏感性,语言学习的进程还是可以大大加快的。

语言学习包括哪些?

任何语言都分为语法和词汇。基础是语法,但语法是有限的、固定的,可以在初级阶段学完,这里,可以建议大家在接触某种语言一年,对它有了初步的感性认识之后,选一本权威的语法书进行系统学习,在此不再赘述。关键是词汇,词汇是无限的,变化发展的,不断涌现的。无论对中小学生,还是专家学者,任何人在任何领域的学习工作中,都会遇到生词、新词。因此,学习语言,就是初级阶段学语法,高级阶段学词汇。语法学习的阶段是基础,万事开头难,但这个阶段,一般只要二到三年即可完成,因为语法是有限的。学习语言的大部分时间都耗在了后者上,而且词汇量的多寡往往就可以决定语言的水平。因此,对于想进一步提高语言水平的人来说,扩大词汇量就是学习语言的重中之重了。

关于词汇的认知

人类对于事物的认知有四个过程,即:

1.不知道不知道,自己没有接触过,根本没有意识到自己的无知。

2.知道不知道,开始意识到自己的无知,正设法去了解这一事物。

3.知道知道,经过学习,基本弄清了这一事物的来龙去脉。

4.不知道知道,对于这一事物早已了然于心,熟练的很,它已然成为自己不可或缺的一部分。

对于词汇的认知,也有一个类似的过程:

1.第一次听或第一次看到,不知其意,需要查阅词典。

2.查阅词典,了解意思后,再次听到或读到时,知其意,无需再查,但自己不会主动使用这个词到对话或写作之中。我们称其为阅读词汇或听力词汇。

3.会拼写,自己会主动使用这个词到对话或写作之中,但每次说或者写,都会在大脑中再现第一次学习这个词的情景,因为你需要回想这个词的用法。我们称其为写作词汇或口语词

汇。

4.彻底忘掉了第一次学习这个词的情景。只有达到这一步,我们才敢说你真正掌握了这个词汇。

在这里,我要声明一点,词汇的学习不是一次性完成的,因为这不符合人类认知的特点。我不提倡通过背词汇书学习语言,因为这种学习方法,记得快,忘得也快。在这种情况下,词汇在大脑的驻留时间仅仅能维持几天,就算应付得了后天的四级考试,肯定也无法应付后年的六级或八级考试。

词汇只能在实际运用中学习,我们和任何一个词的首次邂逅,都要在实际运用中发生,应该是第一次在电视电台里听到,或是第一次在报刊杂志里看到,肯定不是第一次在词汇书的背到。对于,第一次听到的单词,我们可能无法正确拼出,所以查阅这个词也就存在困难。我们要尽量把首次邂逅某个生词的机会放在阅读里。

如何通过阅读快速提高词汇量

大家知道,对于除专业领域的术语以外的词汇,从概率论上来说,任何一个词作为新词首次出现在普通文章中的概率是大致相同的。这就意味着,两个水平相当的学生,以学习相同数量的新词作为阅读目的的话,所需的阅读量也大致相同,假定他们的阅读速度相同的话,则耗时也大致相同。

任何一个单词,首次邂逅千万不要对其视而不见,我们必须认真查阅词典,可以使用电子词典,或在线词典,如新沪江小D。因为,这样可以节省很多时间。举个例子:有两个人,一个人见面两次才查生词,一个人首次见面即查生词,要学习同等数量的生词的话,前者所需时间是后者的两倍。

首次见面之后,最好尽快安排机会第二次相遇。方法就是,大量阅读同类文章,因为人类的大脑是会遗忘的,在遗忘之前,再次刺激大脑,会产生更有效的记忆。一般情况下,一个生词再短期内的第二次相遇以后,就会转化为阅读词汇。

成为阅读词汇的单词,要劲量找机会卖弄出去,在不同的机会卖弄几次以后,阅读词汇可以转化为写作词汇或口语词汇。当你在潜意识下写出或说出这个词的时候,恭喜你,这个词已经写进你的植物性神经了。

总而言之,通过阅读提高词汇量的捷径就是,见到生词立即查,似曾相识先回忆,学到立即要卖弄,卖弄多了可掌握。

关于大学的英语学习

大学四年,是学习英语的黄金时期。这个阶段,少了很多高中时的学习负担,英语学习不用以提高考试成绩为目的,考试真正成为了检验学习效果的手段。

大学时期的英语学习,目的各有不同,有人为了通过考试,如四六级、BEC、翻译考试、考研;有人为了出国,在国外深造、生存;有人为了日后在国内与客户交流。但无论如何,仅仅靠看书做题,是无法练就高水平英语的。英语学习在于厚积薄发,大三以前,应该注重积累,广泛涉猎。欧美文化、报刊杂志、流行歌曲、名人演讲、电台听力等等都可以尝试。有了这些,四六级的英语就不难准备了,各位同学就不必狂做四六级模拟题折磨自己了。我个人认为语言的学习,要从所有方面全面着手,仅仅练阅读,或仅仅练听力都是费时伤神,事倍功半的。人的精力有这样一个特点,长时间集中于某一种事物,就会容易疲劳,导致效率低下。对于英语,杂志看累了,我们可以听一些歌曲,歌曲听腻了,我们再看点美剧,万变不离其宗,人不累,效果也好,何乐而不为呢?(小编推荐:更有趣的外语学习,更人性化的学习体验, 更多好学、好玩的应用和功能,更完美的外语交流和互动,一切尽在沪江新部落哦!赶快戳我进啊>>>)

关于四六级的应试技巧

考试之前,我还是提倡做一套模拟试题的,不为提高英语技能,也不为发现错误日后改正,只为熟悉题型,提升应试技巧。快点我参加模考吧 >>

关于听力,一般考试都有一个试音时间,三到五分钟,这段时间一定要好好利用,姓名准考证号可以稍后再写,听力的选项一定要在第一时间通览一遍,时间还有富余的话,就再通览一遍,千万不要为了赶时间,去做阅读或干其他的事。听力考试过程中,要准备好笔纸,以备不时之需,即使没有内容需要记的,转转笔也可以缓解一下压力。

关于词汇题,这类题都很烦人,选项常常是些长得很像的词,这就没办法投机取巧了。只能靠平时积累。但不会选的题先可以不用猜,先把它标出来,后面做阅读的时候,说不定就些词,会给你灵感,这时再翻回去,选出来,准确性高点。

关于阅读,这就大有文章可做了。阅读里的文章可以给单选提供灵感,也可以给作文提供词句。至于答题技巧,就更有很多值得一提的了。四六级的文章一般有一篇记叙文,一篇新闻稿,一篇说明文,一篇议论文。后面的问题,都需要对通篇有正确的理解。因此,作者的观点非常重要,了解是褒是贬,还是中性,至少可以才对三分之一的问题。如何判定作者的态度呢?找中心句,一般出现在文章的第一句或结尾。但要注意的是,议论文类的文章,常常在开头放个“耙子”,然后对此反驳,当看到“Some people often hold the idea that….”时,我们一定要注意,这句话反过来理解才更接近作者的观点。

关于改错,这类题通常针对中国人的汉语固有思维设题,平时多读原汁原味的英语是很好的备考方法。在这里,我有另一种方法,可以供大家借鉴,就是看老师办公室,观摩老师如何批改学生作文的。仔细研读,老师批改后的作文。效果很不错哦。

最后,关于作文,分好提纲结构,很重要。提出问题,即摆事实。分析问题,即说原因。解决问题,即提方案。每个段落给几句模板试的句子,作文的一半不用临时去写了。另一半呢,需要临时结合题目写出来,要注意,避免使用大家都会用的词,如“I think”,“In my opinion”或普通句式,要是写成“I reckon”“Put in their shoes, I would….”这样阅卷老师必然会眼前一亮的。

第五篇:语言学习心得

汇编语言学习心得

在接触这门课之前就已对汇编语言有所了解,一方面是在计算机组成原理中有所涉及,虽然那时只知道简单的mov、add、jmp指令,不清楚其具体用法,缺乏系统性的学习;另一方面是在相关书籍中了解到汇编语言写驱动比较好但比较难,当时就觉得汇编是门很牛的语言,很想学习。接触这门课后,感到汇编语言的确不是很容易就可以弄懂的,相比较以前学过的高级语言如C、C++等,电脑等于在迁就人的思维方式,但学汇编,人却必须要去迁就电脑的思维方式,要设身处地地用电脑的角度去思考问题,这就是我们学习汇编语言时遇到的最大的障碍。通过这一学期的学习,对汇编有了初步的掌握,可以说是汇编的入门教程。首先主要学习了汇编语言的基本知识,介绍了汇编的背景知识及由来,让我知道了汇编语言是处在机器语言和高级语言中间的一种低级语言。由于计算机能够直接识别的数据是由二进制数0和1组成的代码。所以用机器语言编写的程序是计算机惟一能够直接识别并执行的程序,而用其他语言编写的程序必须经过翻译才能变换成机器语言程序,为了克服机器语言的缺点,人们采用助记符表示机器指令的操作码,用变量代替操作数的存放地址等,这样就形成了汇编语言。然后介绍了8086计算机组织,汇编的学习不仅仅是学习其语法,而更多的是学习计算机基本的体系结构,学到这还没见到很多的指令和语句,但这是学习汇编前必须要知道和掌握的,只有知道了汇编的实质、如何工作及一些基本概念,才能进行下一步的学习。直到后面终于学了80X86指令系统和寻址方式及后来的汇编语言格式,才真正进入了汇编的指令学习阶段,每一条指令的学习虽然简单但比较多,特别是有些指令的使用场合及错误用法等易犯错、易混淆,例如在编写数据传送指令时,目的操作数和源操作数的类型一定要匹配,CS不能作为目的操作数,offset后只能跟简单地址符号,等等。但总的归纳起来主要掌握三点:

1、要求指令操作数的寻址方式;

2、指令对标志位的影响和标志位对指令的影响;

3、指令的执行时间,对可完成同样功能的指令,要选用执行时间短的指令。这样学习起来方便的多,也更容易理解记住,这为以后的学习做了铺垫。

汇编的基本指令学完后,就开始进行应用了,首先学习的是循环与分支程序设计,第一个例子是简单的加法计算,虽然简单,却让我有点恐惧,一个在C语言中只要三四句就解决的问题,用汇编却写了将近20句,且还没有输出,也就是不知道结果是否正确。后来学习了dos系统功能调用,知道02号功能可以输出加法的结果,于是使用了,但输出的却是一个ASCII码值,又需要进行二进制到十六进制的转换,当时觉得很困惑,不知道为什么要转换,我们输入的不就是十六进制吗,算出来应该还是十六进制啊?随着继续深入的学习,才彻底明白为什么要转换,同时也了解了计算机中对于数字运算是如何工作的。这时发现汇编有很多高级语言所没有的优点,它能让我们更加了解计算机内部的组织结构,对我们计算机专业的学生来说,学习汇编也是提升综合能力的关键环节。一旦对

它的原理掌握后,编写程序就容易多了。后来学的越多,越复杂,程序也是越来越长,一堆的跳转指令,觉得很麻烦,且感觉整个程序无秩序,有时对于一段程序实现的功能还要研究半天。直到学习了子程序设计,一个过程定义对应一个功能,就像C语言中的函数,很有条理性。子程序设计能让一个大程序分成几个模块来做,提供了模块化程序设计的条件,可以节省存储空间及程序设计所花的时间,并且在主程序中进行call调用,十分方便,便于以后的维护、调试和修改。

最后学习了部分宏汇编,只是简单的几个用法并没有深入,但已经为我们做好了基础工作,便于我们自学后面的部分。宏(或宏指令)是源程序中一段有独立功能的程序代码,只需定义一次,可以多次调用。这似乎和子程序结构很像,但子程序有自己的优缺点,优点很明显,缺点是子程序为转子及返回、保存及恢复寄存器以及参数的传送等都要增加程序的开销,这些操作所消耗的时间以及它们所占用的存储空间,都是为取得子程序结构使程序模块化这一优点而增加的额外开销。所以,有时,特别在子程序本身较短或者是需要传送的参数较多的情况下,使用宏汇编更加有利。当然,在程序较复杂,实现的功能较多的情况下,子程序也必不可少。我的理解是:子程序是调用,而宏是替换,比如一个MAX宏,所有出现MAX的地方,都用宏代码代替。两者各有利弊,在掌握深入的基础上结合使用更好。

还有一点十分重要,和所有的程序设计语言一样,要学好汇编语言,实验是必不可少的环节。我深有体会:书上的程序都能看懂,基本原理也都明白,但是在自己亲手编写程序时,却无从下手,甚至连第一句该怎么写都不知道。后来随着老师的讲解和实验的进行,知道了程序的编写格式,包括数据段、堆栈段和代码段,其中数据段和堆栈段可以不定义,但代码段是必不可少的,也掌握了该怎么调试程序、运行程序等。特别是debug命令的使用,可以配合与list文件的使用,让我们可以调试排错、检查结果是否正确等,掌握debug的使用对实验有很大的帮助。程序的编写在记事本中进行即可,再在mask中编译,link中连接运行,当然也可以在专门的汇编工具中进行。通过实验,可以在很大程度上加深印象,在书上看程序,一切都是理所当然,十分顺利,而自己动手,才会真正发现自己的不足之处。

现在汇编语言的课程已经告一段落了,学习过程中遇到的所有困难以及遗留的难点都需要我们在其余时间里逐步攻克,在打好基础的前提下,学习更加专业的汇编知识来为我们所用。

下载C语言学习心得word格式文档
下载C语言学习心得.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐

    语言学习心得

    《汇编语言程序设计》学习总结与心得 班级:09计本(3)班学号:0姓名:刘得分: 本学期学习了汇编语言,汇编语言是面向机器的程序设计语言。在汇编语言中,用助记符代替操作码,用地址符号或......

    语言学习心得

    汇编语言学习体会 前段时间,刚从计算机系统组成与体系结构的稍微学习了汇编语言,只学习了简单的一些指令。通过自学《8086/8088 汇编语言程序设计》,虽然有了一定的了解,但是整......

    英语实用语言学习心得

    《英语实用语言》学习心得与体会 政法1102班黄知琴学号:2011102020249 英语,是我们大学生的必修课,也是让多数大学生都头疼的科目。作为东方人,习惯了汉语的思维方式,习惯了汉语......

    语言康复学习心得

    语言康复培训心得体会 非常感谢学校领导的安排,让我有机会参加了此次的语言康复训练培训班的学习。我非常珍惜这两天的学习时间,并认真做好了记录。 第一天由郭金美教授讲解了......

    语言领域学习心得

    读《3-6岁儿童学习与发展指南》 ——语言领域之心得体会 大四班易雁 本学月,我认真学习了《3-6岁儿童学习与发展指南》的语言领域,让我对这个领域有了新的认识和感悟。 在《3-......

    语言领域学习心得大全

    语言领域学习心得 在幼儿园语言教学中,培养幼儿学习语言的兴趣对发展其语言能力具有极大的促进作用。教师可从情境创设、欣赏并朗诵或讲述儿歌及故事、看图讲述、早期阅读等......

    编程语言学习心得

    程序语言学习的总结 通过两年的学习,我们基本上掌握了C语言,C++和C#三个编程课程。对于程序设计语言的学习,分为学习语法规定、掌握程序设计方法、提高程序开发能力,这些都必须......

    C++语言学习心得

    C++语言程序学习心得体会 通过一学期对C++语言的学习,我感觉C++语言是有一定难度却又是非常有趣的科目。也是很有帮助的,对我们未来工作学习而言。我们可以通过C++语言进行更......