求推荐关于C语言和单片机与C语言的适合自学的书

刚开始学单片机,看什么书好那?
[问题点数:40分,结帖人thz1598746]
本版专家分:0
结帖率 87.88%
CSDN今日推荐
本版专家分:37608
2013年6月 硬件/嵌入开发大版内专家分月排行榜第一2007年10月 硬件/嵌入开发大版内专家分月排行榜第一2007年6月 硬件/嵌入开发大版内专家分月排行榜第一2007年5月 硬件/嵌入开发大版内专家分月排行榜第一2007年4月 硬件/嵌入开发大版内专家分月排行榜第一2007年3月 硬件/嵌入开发大版内专家分月排行榜第一
2013年4月 硬件/嵌入开发大版内专家分月排行榜第二2007年9月 硬件/嵌入开发大版内专家分月排行榜第二
2013年5月 硬件/嵌入开发大版内专家分月排行榜第三2012年9月 硬件/嵌入开发大版内专家分月排行榜第三2007年11月 硬件/嵌入开发大版内专家分月排行榜第三
本版专家分:605
本版专家分:70
本版专家分:10
本版专家分:0
本版专家分:45
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:149
本版专家分:4495
2008年12月 VC/MFC大版内专家分月排行榜第二2003年12月 硬件/嵌入开发大版内专家分月排行榜第二
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:20
本版专家分:3
本版专家分:0
本版专家分:5
本版专家分:0
本版专家分:1518
本版专家分:0
本版专家分:0
匿名用户不能发表回复!|
其他相关推荐【图片】转自单片机吧的:单片机自学(c语言篇)【wit电协吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:118贴子:
转自单片机吧的:单片机自学(c语言篇)
有兴趣的可以去原帖地址查看本人是一个不入流二本大学的大三学渣,自动化专业。可能和很多大学生一样,大一大二浑浑噩噩的混日子,逃课去网吧玩游戏。对什么事也提不起来兴趣,也挂过科,蹉跎岁月,尽负流年。大三时看着自己挂了15分的成绩表(学校规定挂科够30分直接没有学位证书)我陷入沉思,恍惚记起曾经的自己。初中的优等生,老师眼中的标榜,考试要思考的不是如何不挂科,而是能否考第一。现在考前想着如何能打个小抄,寻个助攻。想想每次考完试都会暗下决心来年努力学习。可真实的情况却是“信誓旦旦给的承诺却被时间扑了空”。后来看了电视剧《雪豹》(很早就看过了),第一次看时只觉得主角很厉害,剧情很精彩也很感人。一次无聊我又翻开看了一遍,却得到很多启发。1、人不能只活在别人的思想下。主角是个孝顺的孩子,可他却几次违背自己的父亲的意愿,逃婚当兵。因为他有自己的思想。想想自己,从小父母老师教导我们好好学习,可他们从不告诉我们为何要好好学习,如果你再问他们为何要好好学习,他们或许回答说好好学习才是好孩子、学好了将来能找好工作、过上好日子、挣大钱等这些模糊的概念,于是未经世事的我们也不再深入思考:好孩子就要好好学习。可一细想,上学与挣钱有什么关系。可能如果有孩子问我这个问题我也是这样回答。因为他们这年龄也只能告诉他们这些,难道你要告诉他们现在学好数学为你以后学几何、物理、化学、打基础,当你上大学时要学微积分的,等你学会微积分你会在自动控制原理用到,等你自控原理学会······估计孩子头都大了。而现在的我已经不是孩子了。不能只为了别人的认可而学习。应该思考为什么学习了。为应付考试是一种态度,为充实自己又是一种状态,这两种学习状态可谓截然不同,以前的我是为了学习而学习,是被动的,学习成为了一种劳力。而当意识到学习是一种涉取,是一个主动的过程时,学习就已经不再是一种负担,而是一种获得的乐趣。这种乐趣是充实的,没有玩完游戏那种空虚感。2 人要学会变通,兵法云:出奇制胜。我认为这是主角每次都能胜利的一大法宝。而在学习时也要会变通。举个例子,前几天我们课设是cad,老师发了一个ppt,上面写着一大堆教程,我实在不想对着一堆文字学习,于是我上网下载了一个视频教程,只有一个多小时,看完后基本的画法也都差不多学会了。所以学习中应该找捷径,我认为网络本身就是一个最好的老师,只是很多时候我们向他请教的是网络游戏知识。3、做事前有计划,细节考虑要仔细。每次作战前主角都会下达作战计划,还有作战前要对表,为什么对表,因为作战时间一定要统一,一分一秒都不能有差错。学习也是这样,学习要有计划,首先应该确定一个方向,然后制定计划朝着方向努力。我就有自己的方向和计划,我们专业大三开设有单片机和plc,我经过学习还是觉得更适合学单片机,于是便决定先自学单片机,但我们专业讲的单片机是汇编,c语言应用更广泛,于是我就先学习汇编应付考试。利用课设这几天和寒假学习c语言。后面的计划暂时没有,因为我不能预料到学会了c部分该学哪个部分,网上虽有学习步骤,但我还是决定先学会一个,到时后思路定不相同。汇编我是完全靠自学的,因为我们单片机就要结课时我甚至不懂得org 0000h是什么意思。凭着一股热情,我用了不到半个月学汇编,虽然没学到精通,但对付考试还是绰绰有余。记得考试前老师还当众把我叫过去,因为5次作业我只交了一次,甚至提出要给我补单独课。我婉言谢绝了,老师失望的摇着头。我也没感觉什么,因为我知道这次考试不再担心挂科。现在我要自学c部分了。为此我下了郭天祥视频,下载了电子版新概念51单片机c语言教程。花了不到100大洋买了开发板。关于自学,忘了哪一天看到一个自学心得,就是不要把自己当作学习的学生,要当自己是个老师,学这些东西要给学生讲,那你学习的东西必定要细致。我觉得很有道理,但却很难实施。于是我决定把自己学习的东西整理下来发到网上让别人看,就有种教书的感觉。以后我会不定期把自己学习的总结和理解写下来。可能有很多知识点涉及不到,全凭是一个大概知识理解吧。更注重写学习的过程。
单片机是什么,我的理解它就是一个开关,而我们要做的就是控制它在合适的时间打开或者关闭。51单片机用到最多的是40引脚的,所以接下来我们来探讨一下40引脚的单片机。自己百度一张51单片机外部引脚图。首先要区分引脚序号,这些引脚都是逆时针排序的当找到第一个引脚后只要逆时针数下去便可以正确排序。接下来介绍各个引脚功能,图中P0.0~p0.7、P1.0~p1.7、P2.0~p2.7、P3.0~p3.7分别属于P0、P1、P2、P3。这些都是“开关”,决定单片机的输入输出,当然这些“开关”还有别的功能,这里先不做介绍。还有8个,其中9、20、40引脚英文字母分别是rst、gnd、Vcc,这些我们不陌生,分别为复位、接地、电源。再看18,19引脚,英文是xtal2和xtal1,xtal是什么?俗话说知之为知之,不知百度知。经过本学渣百度得英文是External Crystal Oscillator,再打开词典翻译对应为外部的 晶体 振荡器。那么问题来了,挖掘机技术到底哪家强...咳咳不好意思说顺口了。外部晶体振荡器是干什么的?简单地说,没有晶振,就没有时钟周期,没有时钟周期,就无法执行程序代码,单片机就无法工作。晶振好比人的心脏,只有心脏跳动,人体才会新陈代谢,才会有动力。单片机工作时,是一条一条地从RoM中取指令,然后一步一步地执行。单片机访问一次存储器的时间,称之为一个机器周期,这是一个时间基准。—个机器周期包括12个时钟周期。如果一个单片机选择了12MHZ晶振,它的时钟周期是1/12us,它的一个机器周期是12×(1/12)us,也就是1us。时钟有两种方式如图,方式一为片内时钟震荡,顾名思义就是不需要外部提供时钟,方式二需外部提供时钟为外部时钟方式。还有三个引脚是扩展用的,这里先不学。接下来是进制转换和重用逻辑运算,像与或非这些应该很简单就不细说了,这里只总结一下异或。两个结论 1、任何数与1异或结果取反,与0异或结果不变 2、两个相同的数异或结果为0,不同的数异或结果为1.
接下来就是写程序了,写程序的第一步就是写头文件,为什么写头文件呢?我们在用c语言编程是往往第一行就是reg51.h或者其他的自定义头文件?打开reg51.h 可以看到这样的一些内容:(此文件一般在C:\KEIL\C51\INC下)我摘录了一个:sfr P0 = 0x80;其中sfr英文意思为:special function register特殊功能寄存器,而这句话的意思就是规定P0与地址0x80对应(0x80 是C 语言中十六进制数的写法,相当于汇编语言中写80H)。sfr 并非标准C 语言的关键字,而是Keil 为能直接访问80C51 中的SFR 而提供了一个新的关键词,其用法是:sfr 变量名=地址值。头文件里还有一类sbit EA = IE^7;其中sbit为定义特殊功能寄存器的位变量,我们知道特殊功能寄存器有8位,如p0的8位分别为P0^0、 P0^1、P0^2、P0^3、P0^4、P0^5、P0^6、P0^7、而sbit就是为了定义这些位。如sbit P1_0=P1^0;就是定义用符号P1_0 来表示P1.0 引脚,如果你愿意也可以起P10 一类的名字,只要下面程序中也随之更改就行了。做一个不算很恰当的比喻,头文件就像是一本书中的目录(只有标题不具有具体页码信息),读者(链接器)通过目录(头文件+函数库导出符号表),可以很方便就查阅其需要的内容(函数库)。在一本书中,目录固然重要,但绝对不是一本书的核心的、最重要的部分。讲了这么多理论,可理论还没讲完,像编程里的运算、数据类型、基础语句。我想结合实例可能更好一些,那接下来就该试试我的开发板了实验1 点亮led小灯打开keil写程序代码,当然keil相信你会用,如果不会还是那句话“知之为知之,不知百度知”。代码如下:#include&reg52.h& //52系列单片机头文件sbit led1=P1^0; //声明p1口的第一位void main() //主函数{led1=0; //点亮第一个小灯}下载程序看见小灯亮了,如下图程序一共有3个句子需要说明。1、#include&reg52.h&,格式#include&头文件&为调用头文件的格式,我们在写程序时用到什么头文件里面的功能就需要调用这个头文件,以后还会碰到其它头文件再一一介绍。2、sbit led1=P1^0;意思是将P1的第0位重新命名为led1,以后操作P1^0时直接操作led1就行了。其它类同。3、led1=0;在数字电路中,电平只有两种状态:高电平1;低电平0.显然,该语句意思是让P1.0清零,即为低电平。输出电压=VCC就是高电平,输出电压=GND(一般是0V)就是低电平,分别用1和0来表示,这个是理想值。但实际上它也有一个范围,这个会在单片机的datasheet里说明。比如你的单片机供电压(VCC)=5V,那么,datasheet里会说明高电平最低要达到多少伏,比如给出的是3.7V,那么在3.7~VCC的范围内,单片机都会识别成高电平。低电平会说明最高能达到多少伏,比如0.8V,那么GND~0.8V这个范围内的电压都会被识别为低电平。中间状态(0.8V~3.7V)识别状态不定,可能是高电低也可能是低电平。由于没有操作其他口,所以其他口保持初始状态不变(单片机默认各个输出口输出为1),那么为什么让它输出低电平灯就亮了呢?那就需要看实验板电路图了,如下图1、排阻 就是一排电阻,图中数字2~9分别接8个1k欧姆的电阻的一端,8个电阻另一端接1,以上说的是排阻内部接线,外部接线如图,Vcc为5v。2led灯 发光二极管,它具有单向导电性,图中箭头表明电流方向,顺着箭头才可流入电流,逆着不会导通,一般输入5mA电路小灯会亮,控制电流为3~20mA,二极管发光时测得其两端电压为1.7v,为导通电压。3、c573锁存器 图中Q0~Q7为输入端,D0~D7为对应输出端,Vcc和GND不必说,当E输入高电平时无论输入端Q输入什么输出D输出都为高阻态Z,所以为了实现锁存功能E必须接地,L是实现锁存的关键,当L输入高电平时输出D与输入Q相等,当L输入低电平时输出D已知保持上次的输入状态,不管现在输入是什么。由于初始时候各个口输出默认为高电平,图中L连接P2^5,初始为高电平,故在此锁存器没有用到锁存功能,以后会再用到的。现在分析一下P1^0这一个分支,从输入Vcc开始,中间经过两个电阻排阻和发光二极管,终点是P1^0,由于Vcc=5v,而P1^0我们程序规定它为低电平,即接近0v,中间压降为5v,而二极管压降为1.7v,则排阻压降应该是5-1.7=3.3v,由于排阻电阻为1000欧姆,故这个串联电路电流为3.3/mA,故小灯亮了。P1^1~P1^7输出为高电平,中间压降几乎为0,电流几乎为0,所以灯不亮。当你不满足只点亮一个小灯时,便要用另一种总线操作法控制小灯了。如我想点亮第1、3、5、7个小灯该怎么写程序呢?代码如下#include&reg52.h& //52系列单片机头文件void main() //主函数{P1=0 }下载程序看见第1、3、5、7个小灯亮了(亮瞎了)P1=0xaa就是对单片机P1口8个i/o口同时控制,0x后面对应的数字就是十六进制数,aa化为二进制数为,对应二极管就是第1、3、5、7个小灯亮,2、4、6、8小灯灭。通过这种方法我们就可以任意控制8个小灯亮灭,只要改变P1的值就ok了。
关于直流电机控制原理需要用到一个L298N芯片,因为单片机不能直接驱动直流电机,所以加这个芯片图为L298N引脚图,可以控制两个直流电机。三个接地端,vss是芯片内部需要的电压,5v就够了,vs是电机需要的电压,必须小于46v,四个输入端对应四个输出端,IN1、IN2、IN3、IN4对应OUT1、OUT2、OUT3、OUT4。INA和INB分别为前两对和后两对的控制使能端,不知道控制使能端什么意思也没关系。当IN A输入高电平时如果IN1输入是高电平OUT1输出也是高电平,IN 1输入低电平时OUT 1输出也是低电平,就是OUT1输出状态与IN 1输入状态一致,同理,OUT2输出状态与IN 2输入状态一致.当IN A输入低电平时候,不管IN1和IN2输入什么OUT1 OUT2输出都为低电平。IN B控制着3和4,原理与IN A相同控制电机正反转只需要改变电机两端电压方向如本来电机1正极接高电平负极接低电平改为正极接低电平负极接高电平,就能实现电机反转。故只要控制IN1、IN2的电平可控制正反转。电机转速与加在它两端电压大小有关,不超过最高电压时电压越大转速越快,因为当INA输入高电平时电机工作,输入低电平时输出为0即为不工作状态,所以只要控制IN A以一定频率输入高低电平就能控制电压,这个方波占空比越大电压越大转速越快。占空比就是一段方波高电平占的比例,当全是高电平时候占空比为100%,全是低电平时为0%。所以只要控制IN A输入不同占空比方波就能调速
实验2之威力加强版无视题目吧,进入正题。我们已经学会如何让小灯闪烁了,其中用到的延时函数为for(i=500;i&0;i--)for(j=110;j&0;j--);延时大概为500ms即为半秒,(注:延时时间是可以算得,用到时钟周期和指令周期,只是我觉得没必要算的那么精确,算能算精确了还要定时器干嘛,了解个大概就行了)若我们想让小灯亮和灭的时间不同,那岂不是要写两个延时函数了?其实有个方法只写一个就可以了。那就是给我们的延时函数带上“瞄准镜”,指哪打哪。这个瞄准镜就是参数,规定参数大小就能控制延时时间。void delay(unsigned int k){unsigned int i,j;for(i=k;i&0;i--)for(j=110;j&0;j--);}上面延时子程序后面多了一个括号及其里面的内容unsigned int k,这就是这个延时子程序的参数,k的值在你主函数里调用时可以规定它的大小,当你规定它为500,延时就是500ms,同理规定它是300延时就是300ms;规定方法也很简单,调用时直接在子函数后面加个括号,里面写上数的大小,如delay(300);就规定了k为300.直接改动上个程序实现小灯亮200ms灭800ms#include&reg52.h&#define uint unsigned intvoid delay(unsigned int k);sbit led1=P1^0;void main(){while(1){led1=0;delay(200);led1=1;delay(800);}}void delay(uint k){uint i,j;for(i=k;i&0;i--)for(j=110;j&0;j--);}注意:1、声明带参数的子函数时参数也要声明,如第三行void delay(unsigned int k);2、这个程序用到了宏定义语句,即第二行“#define uint unsigned int”它的作用是“重命名”,如本例#define uint unsigned int就是用uint代替unsigned int,宏定义后程序里用到unsigned int的地方都可以用uint代替,一般把比较长用的有比较频繁的内容我们可以利用宏定义简化一下,方便编程。最后注意的是宏定义语句后面没有分号,即没有“;”现象如下实验三 流水灯的实现流水灯是什么? 就是一组灯,然后在控制系统的控制下按照设定的顺序和时间来发亮和熄灭,这样就能形成一定的视觉效果,很多街上的店面和招牌上面就安了流水灯,看上去更美观,我们先实现最简单的功能,让8个小灯循环点亮。实现方法:我们可以对8个i/o口控制它,如先让p1=,延时后让p1=再延时后让p1=...一直循环,由于p1的值一直是变的,我们总不能像傻瓜一样赋给p1一个值让它延时再赋值再延时,这样写下去只能君问归期未有期了;就要用到移位操作了。移位有四种:左移、右移、循环左移、循环右移。左移我们可以想象这样一幅画面:一列8节车厢火车向左徐徐穿过一个隧道,直到完全进入隧道。这个8节车厢的火车就是一个8位的字节, 每个车厢代表一位,每进入隧道一个车厢外面就少一个车厢(废话),左移就是这样的,如一个8位数为,左移一次就是 ,再左移一次就变为,总结一句话就是最低位补0,其它位左移一位。当然右移原理同左移。循环左移就是循环着移动了,还是,循环左移一次就变为,再循环左移一次为,总结一下就是最低位补左移前的最高位,其它位左移一位,循环右移类同。左移写法为&&&&右移写法“&&”很形象有木有。我们想让a左移一位就写a=a&&1;左移两位就是a=a&&2;循环左移写法“_crol_”循环右移写法“_cror_”,我们知道left为左,right为右,这两个指令唯一的区别就是最后一位一个是L,一个是R,明白了吧。同左移,我们想让a循环左移1位再赋值给a就写“a=_crol_(a,1)”.右移同理。需要特别强调的是“_crol_”和“_cror_”是由intrins.h这个头文件规定的,出于“版权”的考虑,我们还是在文件开头声明一下“由intrins.h出版社出版”开个玩笑;声明一下头文件就可以了,加一个#include&intrins.h&,还有一点前面忘说了,声明头文件和宏定义一样后面是不加分号的。罗嗦了这么多终于可以写流水灯实验程序了,代码如下:#include&reg52.h&#include&intrins.h&#define uint unsigned int#define uchar unsigned charvoid delay(unsigned int k);void main(){a=0xfe;while(1){P1=a;delay(500);a=_crol_(a,1);}}void delay(uint k){uint i,j;for(i=k;i&0;i--)for(j=110;j&0;j--);}程序思路:(看懂程序的童鞋就不必往下看了)先规定一个a为unsigned char类型变量(unsigned char为单字节即8位),进入主函数后赋值a=0xfe;化为二进制数为。然后加while(1){}大循环,在大循环里先把a赋值给P1使p1=,第一个小灯亮,延时后循环左移一次,并把左移后结果给a,然后继续循环又从P1=a开始,只不过这次a的值是左移过一次的结果了,即为,于是变成第一个灯灭,第二个灯亮了,一直循环下去,无休无止,没羞没臊的循环亮着,原谅我的用词不当吧,我只是由无休无止联想到的...最后是流水灯按照我的意志无休无止地流动着,有图有真相:
实验4、数码管显示实验数码管是什么?它就是一个小盒,里面放着7根条形发光二极管和一个点型二极管,条形管摆成8的形状,右下角放个点型二极管,为什么摆成8呢?我们小时候都玩过火柴拼字的游戏吧,这数码管就是这个原理,我们把7根二极管想象成7根火柴,是不是0到9都可以从摆好的8中抽几根变化而成呢,如把8中间的一横去掉就是0,当我们想让它显示几就控制对应的二极管亮灭就可以了,如下是常见数码管的内部结构先看他的引脚图,共有十个引脚,3引脚和8引脚接在一起做位选(稍后会说),a~dp共8个引脚与内部二极管一一对应,我们通过单片机控制这8个引脚,从而达到控制内部二极管亮灭,再看共阴极接法,它的8个阴极接在一起,所以说是共阴极接法,共阳极就是阳极接在一起,共阴极引脚接高电平对应二极管发光,共阳极引脚接低电平对应二极管发光,正好相反。在实验板上的数码管一般不止一个,本人实验板上就有6个数码管,以四个数码管为例连接如下图引入两个名词:位选和段选,位选,顾名思义就是选位的,让我们化身为一个酒楼的店小二,我们可以称这四个数码管为四位客官,位选就是选择哪位“客官”。段选就是选择让数码管里面哪几个二极管亮,以达到显示数字的目的。由图见四个数码管,那么就有四个位选端,p20~p23就是位选端,我们一般把数码管的段选连在一起,就是四个数码管的a与a对应连接起来,b与b对应连接,c与c......这样单片机只要输入一个段选信号就能控制四个数码管显示同样的内容。如不理解请看下图1 2 3 4上面对应四个数码管,里面都有a~h段对应其二极管,段选信号8位数字信号分别对应各个数码管里对应的8段,1 2 3 4 对应位选,只需要四位的位选信号就能随意控制哪个数码管工作。总结一下:位选信号可以控制第几个数码管显示,段选信号控制这几个数码管显示什么内容,注意,他们显示的内容都一样,这种显示方式叫做静态显示。下面以具体实例让6个数码管都显示1,代码如下#include&reg52.h&sbit dula=P2^6;sbit wela=P2^7;void main(){wela=1;P0=0xc0;wela=0;dula=1;P0=0x06;dula=0;while(1);}现象如下结果十分精简,可原理却不简单。sbit dula=P2^6;和sbit wela=P2^7;是声明段选和位选,我们知道段选应该是8位,位选应该是6位,可这里却要声明位选段选都是1位,这就跟单片机最小系统设计有关了,原来段选位选共需要8+6=14个口,为了节省i/o口,我们把段选线和位选线都接到p0口,那怎样区分输出的是位选信号还是段选信号呢?那就需要两个锁存器了,叫74hc573你可以感性理解如下图输入一个信号,当你打开位选开关时输出的值便是位选,打开段选时输出段选,这两个开关分别由p2.6和p2.7控制,深入理解的话你可以上网搜这个芯片,也可以直接问我。wela=1;P0=0xc0;wela=0;这三句就是打开位选——送入位选码——关闭位选,我们的最小系统位选由p0.0到p0.5六个口控制,且低电平有效,而oxc0化为二进制为,后六位是0就是六个数码管都被选中下三个是段选的代码,dula=1;P0=0x06;dula=0;观察数码管引脚a~h,为了显示1就是让f段和e段点亮,由于我们是共阴极接法,高电平对应的二极管发光,对应段码就是,化为16进制就是0x06.当然我们平常在编程时一定要了解他外部数码管接法,根据外部共阴极还是共阴极接法对应输入段选值,看位选是怎么接线来确定位选值
实验4之威力加强版依旧无视题目吧,这只是实验4的一个扩展实验,实现6个数码管0~9显示,根据前面所学已经足够想出程序思路了,思路:题目是让6个数码管显示,所以首先位选就不变了,我们要实现的是让段选隔一段时间变一次,这就和我们所学的流水灯有点像了,但流水灯延时后的变化通过左移和右移就能一直变化,而数码管延时后的变化是改变段码值,编程时总不能延时一次,手动送一个段选值吧,那该怎样解决这个问题?那就要用到了编码的知识了,格式如下:码值变量类型 code 编码名字[]={码值};如共阴极0~f的编码表为unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};我们在写编码时候,首先想我编的码数据类型是什么,然后自己给码起个名字方便调用,如上的table就是自己起的名字,中括号里可以写上码的个数,也可以不写,像我这么懒的当然一般就不写了。那么编码怎么就解决了刚才的问题了呢?我们在程序里调用这个编码里的内容,比如我想调用这组数里面的第五个数送给P0,即把0x66送给P0,我们就可以这样写P0=table[5];运用到这个程序,我们就可以事先把我们想让数码管显示数字的段选信号写好编成码,如本例先编个0~9的段码信号编码,然后定义一个变量初始值是0,写好延时程序,主函数里先规定位选信号,然后大循环里先把变量对应的码值以段选信号送入P0,延时后让变量自加一,判断当显示到9后让它再回到0,具体如下#include&reg52.h&sbit dula=P2^6;sbit wela=P2^7;unsigned char num=0;unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};void delay(){unsigned int i,j;for(i=500;i&0;i--)for(j=110;j&0;j--);}void main(){wela=1;P0=0xc0;wela=0;while(1){P0=table[num];delay();num=(num+1)%10;//num自加一,当加到10的倍数后自动清零}}总结:上述程序中加标注的那句num=(num+1)%10;这句里的%为取余符号,并非除号,c语言里除号都用/代替,取余顾名思义就是取余数,如5除以3等于1余2,用在c语言里就为2=5%3这个句子里num=(num+1)%10,num=(num+1)为实现num自加一,当num=0~9时候,如num=6,它对10取余还是6,而当num=9时,执行num=(num+1)%10;num就等于(9+1)%10,结果为0,就实现了num到9后下次不再加一而是清零。这个语句也可以用if(num==10)num=0;代替。不过我更喜欢前一个写法。因为那是我自己想的。呵呵写完程序后我对照了一下和书上标准程序对比,发现我的大循环里送段选值时没有先开段选再关段选,即没有这样写:dula=1;P0=table[num];dula=0;但运行正常,后来细一琢磨,单片机默认各个i/o口初始输出为1,程序一开始就送位选信号,关闭位选后段选就一直开着呢,所以不影响。实验5 动态显示前面我们介绍的数码管同时只能显示一个数字,即6个数码管显示的内容必须一样,那么how to 显示不同的数字呢?这就是动态显示了,动态显示利用人眼的视觉暂留和数码管留的余辉来实现不同数码管显示不同的数。举个例子,你看到你面前有只猫安静的晒着太阳,打着呼噜在睡觉,你也睡觉了,等你醒来后这只猫还在那睡觉,你就觉得它一直在睡觉,可就在你睡着时它已经把你桌子上的鱼吃的一干二净。把这个过程缩短到极短的时间,假如你从睡觉到醒来只需一刹那,而猫的敏捷程度也达到极致,从偷吃到回来继续睡觉的时间甚至比你从睡觉到醒来的时间都短,那你就会觉得这是一个只爱睡懒觉的猫,一直在睡觉,可事实上它是个大馋猫。运用到数码管,我们先只让第一个数码管显示1,延时后只让第二个显示2,再延时后让第三个显示3......一直到第6个显示6,然后循环上述过程,由于人眼视觉暂留需要一定时间才可以接收到下一刻画面,这个时间不同的人时间在0.1~0.4s之间。当我们把上述一个过程时间缩小到小于0.1s即100ms后人眼就看到数码管显示着1~6,却不知它一直在循环上述过程。程序如下#include&reg52.h&sbit dula=P2^6;sbit wela=P2^7;unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};void delay(){unsigned int i,j;for(i=500;i&0;i--)for(j=110;j&0;j--);}void main(){wela=1;P0=0xc0;wela=0;while(1){dula=1;P0=table[1];dula=0;P0=0Xwela=1;P0=0wela=0;delay();dula=1;P0=table[2];dula=0;P0=0Xwela=1;P0=0wela=0;delay();dula=1;P0=table[3];dula=0;P0=0Xwela=1;P0=0wela=0;delay();dula=1;P0=table[4];dula=0;P0=0Xwela=1;P0=0xf7;wela=0;delay();dula=1;P0=table[5];dula=0;P0=0Xwela=1;P0=0wela=0;delay();dula=1;P0=table[6];dula=0;P0=0Xwela=1;P0=0wela=0;delay();}}下面分别是延时500ms 50ms 10ms所对应现象。500ms50ms10ms注意:1.想实现不同的延时时间只需要改延时子程序里的那个数字,如上述程序i=500,延时大概500ms(12mhz时钟情况下)想延时多少ms就把i改成对应值。2.每次段选后都有一句P0=0X这叫消影,因为刚送完段选位,p0还保持着上次段选值,若不加P0=0X再往下执行就该打开位选了,那保留的段选值就送入位选锁存器了,然后又被新位选值代替,虽然这个过程时间很短,但在高速显示状态下我们就会看到显示很混乱。区别如下消影前消影后实验4威力加强版现象
实验6 中断终于学到中断了,好激动。中断是什么?先举个例子,假如你正在洗衣服,突然听到手机响了,你放下手中的活“喂,小丽啊...”经过一番缠绵你恋恋不舍的挂了电话,拿起衣服继续洗。这就完成了一个中断。拿刚才的例子类比单片机里面的中断,单片机在执行程序(你在洗衣服)时,有中断信号进来(电话响了),就要暂停现在执行的程序(放下手里的衣服)去执行中断程序(接电话,缠绵)执行完中断程序后(缠绵完)返回中断点继续执行原程序(继续洗衣服);上个例子是把你比作单片机,而且中断信号来的很突然,但我们可以有预谋的引入中断信号,就会有定时器中断,今天先不讲,下面我再说一个我的故事,那是很久以前,年轻气盛的我想刺杀希特勒,不为别的,就因为他老爱愤怒,开创了吐槽的先河(不懂的可以百度元首的愤怒),当然我为什么刺杀他并不重要,就当他借我5毛钱一直不还吧。听说他爱看各种流水灯,于是楼主我花费九九八十一天写了一个超级复杂的流水灯程序,又花费七七四十九天写成一个引爆炸药的程序,结合这两个程序实现制造了一个机器,外面就几个小灯和一个按钮,:平常正常显示流水灯,一旦这个按钮被按下就会爆炸。我拿着我的机器谨献给元首,他很高兴,重赏了我便送我回国,我坐着回国的油轮手里拿着一份报纸:元首昨夜被炸身亡...还有一点就是我在那个按钮下写了一句话,促使元首按下了按钮,我写了什么话呢?你猜啊?由于年代久远,那个复杂的流水灯程序我已经不记得了,那个爆炸程序我也忘了,所幸我还记得是怎样引爆的,现在就模拟一下这个程序,流水灯就用两个小灯循环点亮来模拟吧,至于引爆程序,就一最后一个小灯被点亮来模拟吧,小灯被点亮代表炸药被引爆。程序如下:#include&reg52.h&void delay(){unsigned int i,j;for(i=500;i&0;i--)for(j=110;j&0;j--);}sbit d1=P1^0;sbit d2=P1^1;sbit d3=P1^7;void main(){EA=1;EX0=1;while(1){d1=0;d2=1;delay();d1=1;d2=0;delay();}}void exter0() interrupt 0{d3=0;while(1);}结果如下图:程序解释:先写个延时子函数为了使小灯循环点亮,进入主函数后EA=1;EX0=1;先不解释,后面while(1)大循环里面实现小灯循环点亮,然后主函数结束,下面即为中断函数void exter0() interrupt 0,它可以看作一个特殊的子函数,明白了吧,其实我们平常所说的进入中断就是进入一个子函数,我们把进入中断后单片机要执行的程序写进这个子函数里就可以了。上面说了中断函数是一个特殊的子函数,他哪里特殊呢?总结如下1、格式,观察中断子函数比一般子函数后面多了一个“interrupt 0”,interrupt是打扰引申一下就是中断的意思,后面的0呢?单片机有6个中断方式,每个中断方式单片机都要分配一个入口,你用的中断方式几就从对应入口进入中断,那单片机怎样识别你用的中断方式来分配入口呢?就要你来声明了。今天我们就学第一个外部中断0,对应下表查得对应序号是0,这就是interrupt后面的“0”的来历。入口地址汇编语言会用到,而c语言无视吧。2、进入中断方式不同,平时的子函数我们在主程序里调用就可以进入,而中断子程序进入方式根据不同中断方式进入的。今天我们就讲外部中断0,它进入中断方式由名字联想,外部中断,就是从外面引入的信号使之进入中断,外面怎样引入中断呢?前面我们说过p3口还具有第二功能,看下面的一张单片机最小系统图p3口每个后面都有个斜杠,斜杠后面就是每个口对应的第二功能,就看p3.2写着p3.2/INT0,这就是外部中断0的引入口,当你打开中断后只要检测到p3.2为低电平就会进入中断,所以当我们按下p3.2对应按钮就进入了中断。可能你又要问了,p3有第二功能和第一功能,那我们平时用两个不会冲突吗?这就要引入特殊功能寄存器了,单片机内部有一部分存储空间是特殊功能寄存器,不同寄存器有着不同的特殊功能,控制中断的开启和关闭的寄存器叫IE,这个寄存器就可以理解为一个字节,有8位,单片机给他起名字叫IE,每位都代表着一个开关,我们想开哪个开关就让这位置1,关闭只需要清零这位即可,每个位都有名字对应,方便我们写程序时书写。IE每位对应的名字如下图我们把IE想成一个配电箱,我们知道配电箱有一个总开关,关掉它整个屋子都没电了,旁边还有几个开关,如空调开关,卧室开关,厨房开关...等等。IE里这几个字就对应不同中断方式开关,控制好这几个字就能掌握中断的开闭,我们逐个看7:(EA):全局中断允许位,相当于总闸,只有打开它其它字才会起作用,对应英文为enable all;enable是允许,all是全部,两个首字母就是EA了,下面的都可以这么记。(6):无效位,我在这写了个0表明默认它是0就可以了,不必理会。(5和3和1):ET2和ET1和ET0:ET,英文enable timer,timer,时间后面加r,是定时器的意思,定时器允许,后面我们会知道中断有定时器中断,定时器中断又有0 1 2方式,所以ET2,ET1,ET0分别对应三个定时器中断允许位。(4)ES enable serial,serial是连续的意思,引申过来就是串行允许位,后面会用到。(2和0)EX1和EX0 对应enable exterior ,exterior外部的,引申过来外部中断,EX1和EX0分别对应外部中断1和0允许位我还发现这即为除去(7)EA和(6)无效位0到5对应开关正好是中断优先级排序,如第0位EX0优先级最高,第5位ET2优先级最低,类比成一幅电路图如下现在再看程序里EA=1;EX0=1;分别是打开总中断和外部中断0,上图中就“通电”了。再回到那个问题:p3有第二功能和第一功能,那我们平时用两个不会冲突吗?当我们声明EA,EX0等这些开关后,p3.2第二功能就启用了,我们平常用不到中断时p3还是做普通的I/O口。我们把这个程序说完,中断里我让d3点亮,为了说明执行中断的时候主程序是暂停状态我加了个死循环while(1);使程序停在中断里,而刚才流水灯也不动了。
实验7定时器今天来学定时器,很多人学中断和这部分都感觉混乱,我觉得理清思路后这部分很简单,我一般遇上这种混乱的知识会用到一种方法:类比。就是把整个过程类比成另一个过程。废话不多说了,进入主题。相信大家都知道定时器是干什么的,就拿我们平时用的闹钟举例,他就是定时期的应用,我们平常怎样设置闹铃呢,无非就是先设置好时间,然后再按个按钮,你就可以进入梦乡了。定时器设置步骤和闹钟设置步骤一样,注意我说的是步骤而不是设置方式。单片机里怎样设置定时器呢?这就又要用到特殊功能寄存器了,类同中断里的IE,这个特殊功能寄存器叫TMOD,由名字联想T代表定时器,MOD模式的缩写,TMOD就是定时器模式的意思,TMOD也是8位的寄存器,观察D7~D4和D3~D0下面的英文一样,D7~D4控制定时器1,D3~D0控制的是定时器0.在51单片机里只有这两个定时器,52系列里还有T2,不过我们暂且不说。只看T0和T1。我们只需要掌握D3~D0这四位便可以类比到D7~D4。D3 gate门的意思,为门控位,当这位为0时候,定时器启动只受TR0控制,当这位为1时时器启动受两个条件控制1是外部中断引脚电平2是TR0,当然TR0待会儿会说,平常我们用一般就把这位清零就可以了,即只让定时器受TR0一个控制。D2 C/T为counter/timer,一个是计数器一个是定时器,这个就是切换计数器和定时器状用的这位是1时为counter模式,即计数器模式,0为定时器模式,今天我们讲的是定时器,所以这位也选择0,当用到计数器时候再具体讨论。D1 D0这两个必须放在一起使用,定时器有四种工作模式,我们用哪种模式由这两位,即M和M0决定,把M1M0组成的二进制数转换成十进制即为所对应的模式。如M1M0=00=0为方式0,M1M0=01=1为方式2.不同的工作方式如下表:说到定时器工作方式就要讲一下定时器定时的原理了,定时器在单片机内部与晶振通过定的方式连接并产生联系,拿方式1举例,由表格知道方式1为16位定时器,你可以这样理解,当用方式1时候定时器里有一个16位的二进制数,把它分两半,这个数由高八位和低位组成,晶振每传来一次信号这个数就加一,当加到最大时候即16位都是1时候定时时间到了,若我们知道晶振每传一次信号所需的时间以及这个数加了多少次就可以算出定时时间了。一个一个来,先说晶振,每隔一个机器周期就会传来一次信号,机器周期前面有过,这里再说明一遍。一个机器周期为12个时钟周期,时钟周期就是晶振频率的倒数,如12mhz的晶振时钟周期为1/12us,那机器周期就是1us。再说这个数加的次数,由于它加到最大并且再加1进位即16位全是1时候再进位定时时间到,这是终点,还好起点是由我们规定的,假如我们不规定起点,就是从0加到最大,加的次数就是2^16=65536次,如果机器周期是1us那么定时时间就是1us*ms,这就是方式1在12mhz晶振下最大定时时间。如果我们想定时50ms该怎样做呢,那就通过改变计数初值的方式,50ms=50000us,那么从=15536开始计数,到65536时正好计数50000次,即为50ms。前面所说这16位分高八位TH0和TL0,(H和L分别代表hight和low),TL0每计数达到最大时向TH0进位一次,即没计数2^8=256时候TH0加一,那TH0=,TL0=(%为取余)那么在程序里我们就可以这样写TH0=()/256;TL0=()%256;就规定好定时50ms的初值了。这只是工作方式1的算法,其它方式也是照猫画虎的算法。还有一点就是TMOD不能位寻址,即不能单独操作M0,M1,GATE.等这些位,只有整体制定TMOD的值。我个人理解是因为我们选择工作方式的M1和M0不能分开来选择工作方式,所以不能进行位寻址。前面我们介绍了定时器工作方式选择以及说明定时的时间大小的确定,类比闹钟我们现在只是调好了时间,还有一步就是按下按钮启动定时。同样单片机里也有一个特殊功能寄存器控制开启定时器,不过这个寄存器还有其它功能,下面介绍它TCON.如下表:类同TMOD。CON为control控制的缩写,起定时器控制作用的,它可以进行位操作。观察图表,又分为控制定时器0和定时器1的,我还是只说定时器0的。TF0,英文全称Timer0 flag T0,flag旗子的意思,引申一下就是标志,这叫T0中断标志位,他作用是什么。前面我们知道定时器的工作原理,当定时时间到了即16位数加到最大时,则TF0置1,这就是一个标志,标志着定时时间到了,就如同定时时间到了闹钟会响。TR0,英文全称Timer0 Run T0,运行控制位,这就是控制闹钟开启的那个按钮了,TR0=1则表示启动定时器。ITO,英文全称Interrupt0 touch中断触发方式位,IT0=1为下降沿触发,等于0时为电平触发方式,这两个触发方式有什么不同呢?下降沿触发方式时中断标志寄存器锁存了中断请求,在执行中断的这段时间不必一直触发中断,而电平触发方式没有经过锁存,当中断进行时如果撤去中断请求信号那么中断将停止,即没有发生中断。举个例子就是按钮,有种按钮按下后会弹起,弹起则代表不再连接,但弹起后没有断开,就是因为信号在进入经过一个跳变沿已经被锁存了。IE0,全称Interrupt0 exterior 外部中断0标志位,原理同TF0,当有外部中断触发信号时IE0=1;是不是有点乱?我们理一理。我画了原理图如下:先看外部中断0,中断信号由TCON决定其触发方式,当外部中断信号到达IE0=1,IE0=1向cpu申请中断,cpu若同意中断则EX0和EA闭合,则进入中断,注意当中断被允许时IE0被清零。外部中断1同理。再看定时器0,先有TMOD选择定时方式,TR0闭合打开定时器0,定时时间到后标志位TF0=1,向cpu申请中断,若cpu同意则ET0和EA闭合,则进入中断。定时器1同理。这个图只画了中断系统的部分内容,IE部分缺2个,还有定时器TMOD部分没有写全,但我希望你能理清这个大体思路,细节还得写程序来体现,我们就写一个小灯闪烁的程序,实现小灯亮1s灭1s循环。这里的1s就比较精确了,误差很小。我自学汇编语言写程序时书上有中断和查询方式编程,于是我就把思想移植了过来,分别用中断和查询方式编程,代码如下。中断方式#include&reg52.h&sbit d1=P1^0;void main(){TMOD=0X01;TH0=()/256;TL0=()%256;EA=1;ET0=1;TR0=1;while(1);}void timer0() interrupt 1{TH0=()/256;TL0=()%256;num=num+1;if(num==20){num=0;d1=~d1;}}查询方式#include&reg52.h&sbit d1=P1^0;void main(){TMOD=0X01;TH0=()/256;TL0=()%256;TR0=1;while(1){if(TF0==1){TF0=0;TH0=()/256;TL0=()%256;num=num+1;if(num==20){num=0;d1=~d1;}}}}现象一样如下思路分析:中断方式:主函数里TMOD=0X01;规定了用定时方式1,然后赋TH0,TL0初值,这是定时50ms的值,TR0=1;分别是打开总中断,允许定时器0中断,打开定时器while(1);死循环,让程序停在这里等等定时时间到以引发中断。进入中断后再赋初值来确定下次定时的时间,num自加一,因为我们想实现1s变化,而赋的初值为50ms,所以中断一次num自加一,当加到20次时即实现1s了,然后d1取反,注意d1=~d1;里~为按位取反。查询方式:前面一样,进入while(1)大循环后,判断TF0==1;这就是TF0的作用,当中断不被允许时,即没有EA=1;ET0=1;时候,查询到TF0==1就表示定时时间到了,下面思路与中断思路差不多。
贴吧热议榜
使用签名档&&
保存至快速回贴}

我要回帖

更多关于 51单片机C语言程序100例 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信