找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4557|回复: 0
打印 上一主题 下一主题
收起左侧

51单片机开发经验总结

[复制链接]
跳转到指定楼层
楼主
                          Bit 用法
bit是C51编译器的一种扩充数据类型,利用它可定义一个位标量,但不能定义位指针,也不能定义位数组。它的值是一个二进制位,不是0就是1,类似一些高级语言中的Boolean类型中的True和False。事实上,bit和数据类型为int,char等具有许多相同特性。

1. 值域:
以char为例。Char为8位一个字节的数据类型,取值范围为 -128到+127。而bit只有1位,只能表示0和1两种值。通常bit定义的变量作为一个标志位用。

2.类型定义:
   例如一个数据为25,那么它就可以定义为char型、int型等。因为他们都在自己的值域内。同样一个在自己值域的数0和1就可以定义为bit型。Bit可以指定函数返回值的类型,如bit display(),也可对别的类型的数据进行数据强制转换,如x=(bit)y, (y为char,int型等)。此时,x的值便为0或1。那么如何确定x是0还是1呢?现在看下面例题:
bit lcd_busy()
{      
     bitresult;
   LCD_RS = 0;
    LCD_RW= 1;
   LCD_EN = 1;
   delayNOP();
   result = (bit)(P0&0x80);
   LCD_EN = 0;
   return(result);
  }
  在上面例题中我们可以看到,bit定义了一个函数返回值类型bit lcd_busy(),一个变量 result 和 一个运算表达式 P0&0x80 .由此可以bit的用法和char、int等相同。那么resulit的值如何确定呢? 这个和运算表达式 P0&0x80有关。如果表达式 P0&0x80的运算结果为非零的值,那么resulit的值为1,否则为0。



总结:由上面等内容可以看出,我们完全可以像charint那样使用bit。只是在使用过程要注意bit的取值范围以及bit对运算表达式进行数据强制转换时的规则和bit定义的变量作为标志位的特性。





        51系列中data,idata,xdata,pdata的区别
  
data: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。
  
idata: 固定指前面0x00-0xff的256个RAM,其中前128和dATa的128完全相同,只是因为访问的方式不同。idATa是用类似C中的指针方式 访问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idATa做指针式的访问效果很好)
  
xdata: 外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。
  
pdata: 外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。这个比较特殊,而且C51好象有对此BUG, 建议少用。但也有他的优点,具体用法属于中级问题,这里不提。
  
startup.a51 的作用,和汇编一样,在C中定义的那些变量和数组的初始化就在startup.a51中进行,如果你在定义全局变量时带有数值,如unsigned  char dATa xxx="100";,那startup.a51中就会有相关的赋值。如果没有=100,startup.a51就会把他清 0。(startup.a51==变量的初始化)。 这些初始化完毕后,还会设置SP指针。对非变量区域,如堆栈区,将不会有赋值或清零动作。
  
有 人喜欢改startup.a51,为了满足自己一些想当然的爱好,这是不必要的,有可能错误的。比如掉电保护的时候想保存一些变量, 但改startup.a51来实现是很笨的方法,实际只要利用非变量区域的特性,定义一个指针变量指向堆栈低部:0xff处就可实现。, 为什么还要去改? 可以这么说:任何时候都可以不需要改startup.a51,如果你明白它的特性。
  
bit
  
  
是在内部数据存储空间中 20H .. 2FH 区域中一个位的地址,这在DATA的20H以后以字节形式出现,可互相参照。另外加上8051 可寻址 的SFR,但刚刚试过,只是00H--7FH起作用,也就是说当数据有变化时颜色变红,以后的从80H到--FFH就不是位寻址区了,是位寻址的特殊寄存器,如涉及到了可位寻址的那11个当然会有反应。
  
复位后,程序计数器PC的内容为0000H,内部RAM各单元的值不确定。各功能寄存器的复位值如下:堆栈指针SP的复位值为07H,累加器ACC、寄存器B的复位值为00H,数据指针DPTR的复位值为0000H,而p0、p1、p2、p3四个口的复位值为0FFH。其他SFR如PSW、TCON、TMOD、TL0、TH0、TL1、TH1的复位值也为00H。
  
wave中是低128字节和高128字节(0-7FH),低128字节是片内RAM区,高128字节(80-FFH)是SFR(特殊功能寄存器)bit则是位于低128字节的20H  .. 2FH 区域,即data的20H .. 2FH 区域
  
  code
  
是在 0000H ..  0FFFFH 之间的一个代码地址。
  
我用
  ORG     5000H
  TAB:    DB       22H,3BH,43H,66H,5H,6DH,88H后,
  
CODE从5000H开始以后变成DB各位
  

  data
  
  
是在 0 到 127 之间的一个数据存储器地址,或者加 128 .. 255 范围内的一个特殊功能寄存器(SFR)地址。两者访问的方式不同。实际上由于PSW的复位设置PSW.3=RS0和PSW.4=RS1皆为0,所以通用工作寄存器区就是第0区,所以data的00--07H部分是与REG栏中的R0--R7对应的。以后的则仅代表低128字节的内部RAM。
  
  
  idata
  是 0 to 255 范围内的一个 idata 存储器地址。
  
idata与data重合低128字节,有的地方只有DATA表示256字节的片内RAM,
  xdata
是 0 to 65535 范围内的一个 xdata 存储器地址。
  
指针类型和存储区的关系详解
  
  一、存储类型与存储区关系
  
       data      --->     可寻址片内ram
       bdata      --->     可位寻址的片内ram
       idata      --->     可寻址片内ram,允许访问全部内部ram
       pdata      --->     分页寻址片外ram (MOVX @R0)  (256 BYTE/页)
       xdata      --->    可寻址片外ram (64k 地址范围FFFFH)
        code     --->    程序存储区 (64k 地址范围),对应MOVC @DPTR
  
  二、指针类型和存储区的关系
  
       对变量进行声明时可以指定变量的存储类型如:
       uchar data x和data uchar x相等价都是在内ram区分配一个字节的变量。
  
       同样对于指针变量的声明,因涉及到指针变量本身的存储位置和指针所指向的存储区位置不同而进行相应的存储区类型关键字的
  使用如:
  
       uchar xdata * data pstr
  
       是指在内ram区分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata区("*"前xdata关键字的作用),
  可能初学C51时有点不好懂也不好记。没关系,我们马上就可以看到对应“*”前后不同的关键字的使用在编译时出现什么情况。
  
       ......
       uchar xdata tmp[10];     //在外ram区开辟10个字节的内存空间,地址是外ram的0x0000-0x0009
       ......
  
       第1种情况:
  
       uchar data * data pstr;
       pstr="tmp";
  
       首先要提醒大家这样的代码是有bug的, 他不能通过这种方式正确的访问到tmp空间。 为什么?我们把编译后看到下面的汇编
  代码:
  
       MOV  0x08,#tmp(0x00)         ;0x08是指针pstr的存储地址
  
       看到了吗!本来访问外ram需要2 byte来寻址64k空间,但因为使用data关键字(在"*"号前的那个),所以按KeilC编译环境来说
  就把他编译成指向内ram的指针变量了,这也是初学C51的朋友们不理解各个存储类型的关键字定义而造成的bug。特别是当工程中的
  默认的存储区类为large时,又把tmp[10] 声明为uchar tmp[10] 时,这样的bug是很隐秘的不容易被发现。
  
       第2种情况:
  
       uchar xdata * data pstr;
       pstr = tmp;
  
       这种情况是没问题的,这样的使用方法是指在内ram分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向
  xdata区("*"前xdata关键字的作用)。编译后的汇编代码如下。
  
       MOV  0x08,#tmp(0x00)         ;0x08和0x09是在内ram区分配的pstr指针变量地址空间
       MOV 0x09,#tmp(0x00)
  
       这种情况应该是在这里所有介绍各种情况中效率最高的访问外ram的方法了,请大家记住他。
  
       第3种情况:
  
       uchar xdata * xdata pstr;
       pstr="tmp";
  
       这中情况也是对的,但效率不如第2种情况。编译后的汇编代码如下。
  
       MOV DPTR,  #0x000A         ;0x000A,0x000B是在外ram区分配的pstr指针变量地址空间
       MOV A, #tmp(0x00)
       MOV @DPTR, A
       INC DPTR
       MOV A, #tmp(0x00)
       MOVX @DPTR, A
  
       这种方式一般用在内ram资源相对紧张而且对效率要求不高的项目中。
  
       第4种情况:
  
       uchar data * xdata pstr;
       pstr="tmp";
  
       如果详细看了第1种情况的读者发现这种写法和第1种很相似,是的,同第1 种情况一样这样也是有bug的,但是这次是把pstr分
  配到了外ram区了。编译后的汇编代码如下。
  
       MOV DPTR,  #0x000A         ;0x000A是在外ram区分配的pstr指针变量的地址空间
       MOV A, #tmp(0x00)
       MOVX @DPTR, A
  
       第5种情况:
  
       uchar * data pstr;
       pstr="tmp";
  
       大家注意到"*"前的关键字声明没有了,是的这样会发生什么事呢?下面这么写呢!对了用齐豫的一首老歌名来说就是 “请跟我
  来”,请跟我来看看编译后的汇编代码,有人问这不是在讲C51吗?  为什么还要给我们看汇编代码。C51要想用好就要尽可能提升C51
  编译后的效率,看看编译后的汇编会帮助大家尽快成为生产高效C51代码的高手的。还是看代码吧!
  
       MOV 0x08,  #0X01              ;0x08-0x0A是在内ram区分配的pstr指针变量的地址空间
       MOV 0x09, #tmp(0x00)
       MOV 0x0A, #tmp(0x00)
  
       注意:这是新介绍给大家的,大家会疑问为什么在前面的几种情况的pstr指针变量都用2 byte空间而到这里就用3 byte空间了
  呢?这是KeilC的一个系统内部处理,在KeilC中一个指针变量最多占用 3 byte空间,对于没有声明指针指向存储空间类型的指针,
  系统编译代码时都强制加载一个字节的指针类型分辩值。具体的对应关系可以参考KeilC的help中C51 User's Guide。
  
       第6种情况:
  
       uchar * pstr;
       pstr="tmp";
  
       这是最直接最简单的指针变量声明,但他的效率也最低。还是那句话,大家一起说好吗!编译后的汇编代码如下。
  
       MOV DPTR,  #0x000A         ;0x000A-0x000C是在外ram区分配的pstr指针变量地址空间
       MOV A, #0x01
       MOV @DPTR, A
       INC DPTR
       MOV DPTR, #0x000A
       MOV A, #tmp(0x00)
       MOV @DPTR, A
       INC DPTR
       MOV A, #tmp(0x00)
       MOVX @DPTR, A
  
       这种情况很类似第5种和第3种情况的组合,既把pstr分配在外ram空间了又增加了指针类型的分辨值。
  







1 电感在电路中的作用
电感在电路中的作用
基本作用:滤波、振荡、延迟、陷波等

形象说法:“通直流,阻交流
通直流:所谓通直流就是指在直流电路中,电感的作用就相当于一根导线,不起任何作用。
阻交流:在交流电路中,电感会有阻抗,即XL,整个电路的电流会变小,对交流有一定的阻碍作用。
细化解说:在电子线路中,电感线圈对交流有限流作用,它与电阻器或电容器能组成高通或低通滤波器、移相电路及谐振电路等;

电感的作用是阻碍电流的变化,但是这种作用与电阻阻碍电流流通作用是有区别的。
电阻阻碍电流流通作用是以消耗电能为其标志,而电感阻碍电流的变化则纯粹是不让电流变化,当电流增加时电感阻碍电流的增加,当电流减小时电感阻碍电流的减小。电感阻碍电流变化过程并不消耗电能,阻碍电流增加时它将电的能量以磁场的形式暂时储存起来,等到电流减小时它也将磁场的能量释放出来,以结果来说,就是阻碍电流的变化。


2三极管在电路中的作用
在设计一些线路时。需用到npn/pnp。在哪种时候用呢?下面来介绍一下。
1.如果输入一个高电平,而输出需要一个低电平时,首选择npn。
2.如果输入一个低电平,而输出需要一个低电平时,首选择pnp。
3.如果输入一个低电平,而输出需要一个高电平时,首选择npn。
4.如果输入一个高电平,而输出需要一个高电平时,首选择pnp。
npn基极高电压,极电极与发射极短路,如果基极加低电压,极电极与发射极开路.也就是不工作。
pnp基极高电压.极电极与发射极开路,也就是不工作。如果基极加低电位,集电极与发射极短路。
即工作与不工作



                  电容在电路中的作用
熟悉电容器在不同电路中的名称意义,有助于我们读懂电子电路图。
电容器在电子电路中几乎是不可缺少的储能元件,它具有隔断直流、连通交流、阻止低频的特性。广泛应用在耦合、隔直、旁路、滤波、调谐、能量转换和自动控制等电路中。
1.滤波电容 它接在直流电源的正、负极之间,以滤除直流电源中不需要的交流成分,使直流电平滑。一般常采用大容量的电解电容器,也可以在电路中同时并接其他类型的小容量电容以滤除高频交流电。

2.退耦电容 并接于放大电路的电源正、负极之间,防止由电源内阻形成的正反馈而引起的寄生振荡。

3.旁路电容 在交、直流信号的电路中,将电容并接在电阻两端或由电路的某点跨接到公共电位上,为交流信号或脉冲信号设置一条通路,避免交流信号成分因通过电阻产生压降衰减。

4.耦合电容 在交流信号处理电路中,用于连接信号源和信号处理电路或者作两放大器的级间连接,用以隔断直流,让交流信号或脉冲信号通过,使前后级放大电路的直流工作点互不影响。

5.调谐电容 连接在谐振电路的振荡线圈两端,起到选择振荡频率的作用。

电解电容在电路中的作用
  
电解电容在电路中的作用
  
  1,滤波作用,在电源电路中,整流电路将交流变成脉动的直流,而在整流电路之后接入一个较大容量的电解电容,利用其充放电特性,使整流后的脉动直流电压变成相对比较稳定的直流电压。在实际中,为了防止电路各部分供电电压因负载变化而产生变化,所以在电源的输出端及负载的电源输入端一般接有数十至数百微法的电解电容.由于大容量的电解电容一般具有一定的电感,对高频及脉冲干扰信号不能有效地滤除,故在其两端并联了一只容量为0.001--0.lpF的电容,以滤除高频及脉冲干扰.
  2,耦合作用:在低频信号的传递与放大过程中,为防止前后两级电路的静态工作点相互影响,常采用电容藕合.为了防止信号中韵低频分量损失过大,一般总采用容量较大的电解电容。
  
二、电解电容的判断方法
  
电解电容常见的故障有,容量减少,容量消失、击穿短路及漏电,其中容量变化是因电解电容在使用或放置过程中其内部的电解液逐渐干涸引起,而击穿与漏电一般为所加的电压过高或本身质量不佳引起。判断电源电容的好坏一般采用万用表的电阻档进行测量.具体方法为:将电容两管脚短路进行放电,用万用表的黑表笔接电解电容的正极。红表笔接负极(对指针式万用表,用数字式万用表测量时表笔互调),正常时表
  
针应先向电阻小的方向摆动,然后逐渐返回直至无穷大处。表针的摆动幅度越大或返回的速度越慢,说明电容的容量越大,反之则说明电容的容量越小.如表针指在中间某处不再变化,说明此电容漏电,如电阻指示值很小或为零,则表明此电容已击穿短路.因万用表使用的电池电压一般很低,所以在测量低耐压的电容时比较准确,而当电容的耐压较高时,打时尽管测量正常,但加上高压时则有可能发生漏电或击穿现象.
  
三、电解电容的使用注意事项
  1、电解电容由于有正负极性,因此在电路中使用时不能颠倒联接。在电源电路中,输出正电压时电解电容的正极接电源输出端,负极接地,输出负电压时则负极接输出端,正极接地.当电源电路中的滤波电容极性接反时,因电容的滤波作用大大降低,一方面引起电源输出电压波动,另一方面又因反向通电使此时相当于一个电阻的电解电容发热.当反向电压超过某值时,电容的反向漏电电阻将变得很小,这样通电工作不久,即可使电容因过热而炸裂损坏.
  2.加在电解电容两端的电压不能超过其允许工作电压,在设计实际电路时应根据具体情况留有一定的余量,在设计稳压电源的滤波电容时,如果交流电源电压为220~时变压器次级的整流电压可达22V,此时选择耐压为25V的电解电容一般可以满足要求.但是,假如交流电源电压波动很大且有可能上升到250V以上时,最好选择耐压30V以上的电解电容。
  3,电解电容在电路中不应靠近大功率发热元件,以防因受热而使电解液加速干涸.
  4、对于有正负极性的信号的滤波,可采取两个电解电容同极性串联的方法,当作一个无极性的电容。
  


从名称认识电容在电路中的作用0

电容器在电子电路中几乎是不可缺少的储能元件,它具有隔断直流、连通交流、阻止低频的特性。广泛应用在耦合、隔直、旁路、滤波、调谐、能量转换和自动控制等电路中。熟悉电容器在不同电路中的名称意义,有助于我们读懂电子电路图。

    1.滤波电容  它接在直流电源的正、负极之间,以滤除直流电源中不需要的交流成分,使直流电平滑。一般常采用大容量的电解电容器,也可以在电路中同时并接其他类型的小容量电容以滤除高频交流电。

2.退耦电容     并接于放大电路的电源正、负极之间,防止由电源内阻形成的正反馈而引起的寄生振荡。

    3.旁路电容  在交、直流信号的电路中,将电容并接在电阻两端或由电路的某点跨接到公共电位上,为交流信号或脉冲信号设置一条通路,避免交流信号成分因通过电阻产生压降衰减。

    4.耦合电容  在交流信号处理电路中,用于连接信号源和信号处理电路或者作两放大器的级间连接,用以隔断直流,让交流信号或脉冲信号通过,使前后级放大电路的直流工作点互不影响。

    5.调谐电容  连接在谐振电路的振荡线圈两端,起到选择振荡频率的作用。

    6.衬垫电容  与谐振电路主电容串联的辅助性电容,调整它可使振荡信号频率范围变小,并能显著地提高低频端的振荡频率。适当地选定衬垫电容的容量,可以将低端频率曲线向上提升,接近于理想频率跟踪曲线。

    7.补偿电容 它是与谐振电路主电容并联的辅助性电容,调整该电容能使振荡信号频率范围扩大。

    8.中和电容  并接在三极管放大器的基极与发射极之间,构成负反馈网络,以抑制三极管极间电容造成的自激振荡。

    9.稳频电容  在振荡电路中,起稳定振荡频率的作用。

    10.定时电容  在RC时间常数电路中与电阻R串联,共同决定充放电时间长短的电容。

    11.加速电容 接在振荡器反馈电路中,使正反馈过程加速,提高振荡信号的幅度。

    12.缩短电容  在UHF高频头电路中,为了缩短振荡电感器长度而串接的电容。

    13.克拉泼电容  在电容三点式振荡电路中,与电感振荡线圈串联的电容,起到消除晶体管结电容对频率稳定性影响的作用。

    14.锡拉电容  在电容三点式振荡电路中,与电感振荡线圈两端并联的电容,起到消除晶体管结电容的影响,使振荡器在高频端容易起振。

    15.稳幅电容  在鉴频器中,用于稳定输出信号的幅度。

    16.预加重电容  为了避免音频调制信号在处理过程中造成对分频量衰减和丢失,而设置的RC高频分量提升网络电容。

    17.去加重电容  为恢复原伴音信号,要求对音频信号中经预加重所提升的高频分量和噪声一起衰减掉,设置在RC网络中的电容。

    18.移相电容  用于改变交流信号相位的电容。

    19.反馈电容  跨接于放大器的输入与输出端之间,使输出信号回输到输入端的电容。

    20.降压限流电容 串联在交流电回路中,利用电容对交流电的容抗特性,对交流电进行限流,从而构成分压电路。

    21.逆程电容  用于行扫描输出电路,并接在行输出管的集电极与发射极之间,以产生高压行扫描锯齿波逆程脉冲,其耐压一般在1500V以上。

    22.校正电容  串接在偏转线圈回路中,用于校正显像管边缘的延伸线性失真。

    23.自举升压电容  利用电容器的充、放电储能特性提升电路某点的电位,使该点电位达到供电端电压值的2倍。

    24.消亮点电容  设置在视放电路中,用于关机时消除显像管上残余亮点的电容。

    25.软启动电容  一般接在开关电源的开关管基极上,防止在开启电源时,过大的浪涌电流或过高的峰值电压加到开关管基极上,导致开关管损坏。

    26.启动电容  串接在单相电动机的副绕组上,为电动机提供启动移相交流电压。在电动机正常运转后与副绕组断开。

    27.运转电容  与单相电动机的副绕组串联,为电动机副绕组提供移相交流电流。在电动机正常运行时,与副绕组保持串接。  


单片机晶振的两个电容的作用  
这两个电容叫晶振的负载电容,分别接在晶振的两个脚上和对地的电容,一般在几十皮发。它会影响到晶振的谐振频率和输出幅度,一般订购晶振时候供货方会问你负载电容是多少。
    晶振的负载电容=[(Cd*Cg)/(Cd+Cg)]+Cic+△C式中Cd,Cg为分别接在晶振的两个脚上和对地的电容,Cic(集成电路内部电容)+△C(PCB上电容)经验值为3至5pf。
    各种逻辑芯片的晶振引脚可以等效为电容三点式振荡器。晶振引脚的内部通常是一个反相器, 或者是奇数个反相器串联。在晶振输出引脚 XO 和晶振输入引脚 XI 之间用一个电阻连接, 对于 CMOS 芯片通常是数 M 到数十M 欧之间. 很多芯片的引脚内部已经包含了这个电阻, 引脚外部就不用接了。这个电阻是为了使反相器在振荡初始时处与线性状态, 反相器就如同一个有很大增益的放大器, 以便于起振. 石英晶体也连接在晶振引脚的输入和输出之间, 等效为一个并联谐振回路, 振荡频率应该是石英晶体的并联谐振频率. 晶体旁边的两个电容接地, 实际上就是电容三点式电路的分压电容, 接地点就是分压点. 以接地点即分压点为参考点, 振荡引脚的输入和输出是反相的, 但从并联谐振回路即石英晶体两端来看, 形成一个正反馈以保证电路持续振荡. 在芯片设计时, 这两个电容就已经形成了, 一般是两个的容量相等, 容量大小依工艺和版图而不同, 但终归是比较小, 不一定适合很宽的频率范围. 外接时大约是数 PF 到数十 PF, 依频率和石英晶体的特性而定. 需要注意的是: 这两个电容串联的值是并联在谐振回路上的, 会影响振荡频率. 当两个电容量相等时, 反馈系数是 0.5, 一般是可以满足振荡条件的, 但如果不易起振或振荡不稳定可以减小输入端对地电容量, 而增加输出端的值以提高反馈量.
晶振起振后的频率准确与否,和选取的C1,C2是否与晶振规格要求的负载电容要求相吻合有直接关系,计算公式如下:CL=C1*C2/(C1+C2)+5pF;算出的CL值要与晶振规格要求的负载电容尽量接近才能获得晶振的标称频率的振荡频率.
一般电容的计算公式是:
两边电容为Cg,Cd,负载电容为Cl,则
cl=cg*cd/(cg+cd)+a
就是说负载电容15pf的话,两边个接27pf的差不多了,一般a为6.5~13.5pF
晶振的作用:单片机工作时,就必须要一个时钟信号,也是说它能分出一个时间单位能做多少事情(就像出操时的口令)才能步调一致,要过到这个条件,就必须要有一个人来吹这个口哨,所以我们就给他一个发指令的"人"(这就是我们常用的晶振),当然不用晶振也行,只要做个能发送稳定脉冲的电路送入CPU同样能工作(当然必须在工作频率范围之内)。


标题:单片机c语言教程第十六课 C51指针的使用
2009-04-06 08:12:00
  指针就是指变量或数据所在的存储区地址。如一个字符型的变量 STR 存放在内存单元DATA 区的 51H 这个地址中,那么 DATA 区的 51H 地址就是变量 STR 的指针。在 C 语言中 指针是一个很重要的概念,正确有效的使用指针类型的数据,能更有效的表达复杂的数据 结构,能更有效的使用数组或变量,能方便直接的处理内存或其它存储区。指针之所以 能这么有效的操作数据,是因为无论程序的指令、常量、变量或特殊寄存器都要存放在内 存单元或相应的存储区中,这些存储区是按字节来划分的,每一个存储单元都能用唯一的 编号去读或写数据,这个编号就是常说的存储单元的地址,而读写这个编号的动作就叫做寻 址,通过寻址就能访问到存储区中的任一个能访问的单元,而这个功能是变量或数组等 是不可能代替的。C 语言也因此引入了指针类型的数据类型,专门用来确定其他类型数据的 地址。用一个变量来存放另一个变量的地址,那么用来存放变量地址的变量称为指针变量 如用变量 STRIP 来存放文章开头的 STR 变量的地址 51H,变量 STRIP 就是指针变量。下面 用一个图表来说明变量的指针和指针变量两个不一样的概念。
  file:///Z:\TEMP\msohtmlclip1\01\clip_image001.png
  变量的指针就是变量的地址,用取地址运算符&取得赋给指针变量。&STR  就是把 变量 STR 的地址取得。用语句 STRIP  = &STR 就能把所取得的 STR 指针存放在 STRIP 针变量中。STRIP 的值就变为 51H。可见指针变量的内容是另一个变量的地址,地址所属的 变量称为指针变量所指向的变量。
  要访问变量 STR 除了能用STR这个变量名来访问之外,还能用变量地址来访 问。方法是先用&STR  取变量地址并赋于 STRIP 指针变量,然后就能用*STRIP 来对 STR 进行访问了。*是指针运算符,用它能取得指针变量所指向的地址的值。在上图中指针 变量 STRIP 所指向的地址是 51H,而 51H 中的值是 40H,那么*STRIP 所得的值就是 40H 使用指针变量之前也和使用其它类型的变量那样要求先定义变量,而且形式也相类似,
  一般的形式如下:
  数据类型 [存储器类型] * 变量名;
  unsigned char xdata *pi //指针会占用二字节,指针自身存放在编译器默认存储区,指
   xdata 存储区的 char 类型
  unsigned char xdata * data pi; //除指针自身指定在 data 区,其它同上
  int * pi; //定义为一般指针,指针自身存放在编译器默认存储区,占三个字节 在定义形式中数据类型是指所定义的指针变量所指向的变量的类型。存储器类型
  是编译器编译时的一种扩展标识,它是可选的。在没有存储器类型选项时,则定义为一
  般指针,如有存储器类型选项时则定义为基于存储器的指针。限于 51 芯片的寻址范围,
  
  指针变量最大的值为  0xFFFF,这样就决定了一般指针在内存会占用 3 个字节,第一字节存 放该指针存储器类型编码,后两个则存放该指针的高低位址。而基于存储器的指针因为不用 识别存储器类型所以会占一或二个字节,idata,data,pdata  存储器指针占一个字节,code,xdata 则会占二个字节。由上可知,明确的定义指针,能节省存储器的开销,这在严格要求程序 体积的项目中很有用处。
  指针的使用方法很多,限于篇幅以上只能对它做一些基础的介绍。下面用在讲述常量时 的例程改动一下,用以说明指针的基本使用方法。
  #include //预处理文件里面定义了特殊寄存器的名称如 P1 口定义为 P1
  void main(void)
  {
  //定义花样数据,数据存放在片内 CODE 区中
  unsigned char code  design[]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F,
  0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE,0xFF,
  0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x0,
  0xE7,0xDB,0xBD,0x7E,0xFF};
  unsigned int a; //定义循环用的变量
  unsigned char b;
  unsigned char code * dsi; //定义基于 CODE 区的指针
  
  do{
  dsi = &design[0]; //取得数组第一个单元的地址
  for (b=0; b<32; b++)
  {
  
  }
  }while(1);
  }
  for(a=0; a<30000; a++); //延时一段时间
  P1 = *dsi; //从指针指向的地址取数据到 P1
  dsi++; //指针加一,
  为了能清楚的了解指针的工作原理,能使用 keil  uv2 的软件仿真器查看各变量和存储器的
  值。编译程序并执行,然后打开变量窗口,如图。用单步执行,就能查到到指针的变量。 如图中所示的是程序中循环执行到第二次,这个时候指针 dsi 指向 c:0x0004 这个地址,这个地址 的值是 0xFE。在存储器窗口则能察看各地址单元的值。使用这种方法不但在学习时能 帮助更好的了解语法或程序的工作,而且在实际使用中更能让你更快更准确的编写程序或解 决程序中的问题。
  
  





  
  
  
   
      
在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数
      
        
   
  file:///Z:\TEMP\msohtmlclip1\01\clip_image003.png
  

  file:///Z:\TEMP\msohtmlclip1\01\clip_image004.png
  
  file:///Z:\TEMP\msohtmlclip1\01\clip_image002.png
  
  file:///Z:\TEMP\msohtmlclip1\01\clip_image005.png
  
  
  





在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。
1.函数指针定义
函数类型 *指针变量名)(形参列表)
函数类型说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的形参列表表示指针变量指向的函数所带的参数列表。
例如:
int (*f)(int x);
double (*ptr)(double x);
在定义函数指针时请注意:
   
函数指针和它指向的函数的参数个数和类型都应该是致的;
函数指针的类型和函数的返回值类型也必须是一致的。
2.函数指针的赋值
函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。
例如,
int func(int x);   /* 声明一个函数*/
int (*f) (int x);    /*声明一个函数指针 */
f=func;            /* func函数的首地址赋给指针f*/
赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。
3.通过函数指针调用函数
函数指针是通过函数名及有关参数进行调用的。
与其他指针变量相类似,如果指针变量pi是指向某整型变量i的指针,则*p等于它所指的变量i;如果pf是指向某浮点型变量f的指针,则*pf就等价于它所指的变量f。同样地,*f是指向函数func(x)的指针,则*f就代表它所指向的函数func。所以在执行了f=func;之后,(*f)func代表同一函数。
由于函数指针指向存储区中的某个函数,因此可以通过函数指针调用相应的函数。现在我们就讨论如何用函数指针调用函数,它应执行下面三步:
首先,要说明函数指针变量。
例如:int (*f)(int x);
其次,要对函数指针变量赋值。
例如: f=func;    (func(x)必须先要有定义)
最后,要用 (*指针变量)(参数表);调用函数。
例如:   (*f)(x);(x必须先赋值)

【例】任意输入n个数,找出其中最大数,并且输出最大数值。
main()
{
        int f();
        int iab;
        int(*p)();    /* 定义函数指针*/
        scanf('%d'&a);
        p=f;            /* 给函数指针p赋值,使它指向函数f*/
        for(i1;i<9;i++)
        {
                scanf('%d'
&b);
                a(*p)(ab);    /* 通过指针p调用函数f*/
        }
        printf('The MaxNumber is:%d'a)
}

f(int xint y)
{
    int z;
    z(x>y)?x:y;
    return(z);
}
运行结果为:
343 -45 4389 4235 1 -534 988 555 789↙
The Max Number is4389

【例】(*((void (*)())0))() 表示什么意思?
void (*)()   
声明函数指针
        
让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和右边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。
(void (*)())0  
0强制转换成函数指针
(*((void (*)())0))()  
调用

所以(*((void (*)())0))()的意思是:调用地址为0处的程序。


【指针函数】

一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。
        返回指针的函数,一般定义格式为:
        类型标识符    *函数名(参数表)
int *f(xy);
其中xy是形式参数,f是函数名,调用后返回一个指向整型数据的地址指针。f(xy)是函数,其值是指针。
如:char *ch();表示的就是一个返回字符型指针的函数,请看下面的例题:
【例】将字符串1(str1)复制到字符串2(str2),并输出字符串2.
#include 'stdio.h'
main()
{
    char *ch(char *char*);
    char str1[]='I amglad to meet you!';
    char str2[]='Welcomto study C!';
    printf('%s'ch(str1str2));
}
char *ch(char *str1char*str2)
{
    int i;
    char *p;
    p=str2
   
     if(*str2==NULL) exit(-1);

    do
    {
        *str2=*str1;
        str1++;
        str2++;
   }while(*str1!=NULL);
    return(p);
}

通过分析可得
函数指针是一个指向函数的指针,而指针函数只是说明他是一个返回值为指针的函数,
函数指针可以用来指向一个函数。



三极管的主要特点是具有电流放大功能,以共发射极接法为例(信号从基极输入,从集电极输出,发射极接地),当基极电压UB有一个微小的变化时,基极电流IB也会随之有一小的变化,受基极电流IB的控制,集电极电流IC会有一个很大的变化,基极电流IB越大,集电极电流IC也越大,反之,基极电流越小,集电极电流也越小,即基极电流控制集电极电流的变化。但是集电极电流的变化比基极电流的变化大得多,这就是三极管的放大作用。IC 的变化量与IB变化量之比叫做三极管的放大倍数β(β=ΔIC/ΔIB, Δ表示变化量。),三极管的放大倍数β一般在几十到几百倍。



几乎在所有的电子电路中,都要用到半导体二极管,它在许多的电路中起着重要的作用,它是诞生最早的半导体器件之一,其应用也非常广泛。

二极管的工作原理

晶体二极管为一个由p型半导体和n型半导体形成的p-n结,在其界面处两侧形成空间电荷层,并建有自建电场。当不存在外加电压时,由于p-n 结两边载流子浓度差引起的扩散电流和自建电场引起的漂移电流相等而处于电平衡状态。

当外界有正向电压偏置时,外界电场和自建电场的互相抑消作用使载流子的扩散电流增加引起了正向电流。

当外界有反向电压偏置时,外界电场和自建电场进一步加强,形成在一定反向电压范围内与反向偏置电压值无关的反向饱和电流I0。

当外加的反向电压高到一定程度时,p-n结空间电荷层中的电场强度达到临界值产生载流子的倍增过程,产生大量电子空穴对,产生了数值很大的反向击穿电流,称为二极管的击穿现象。

二极管的类型

二极管种类有很多,按照所用的半导体材料,可分为锗二极管(Ge管)和硅二极管(Si管)。根据其不同用途,可分为检波二极管、整流二极管、稳压二极管、开关二极管等。按照管芯结构,又可分为点接触型二极管、面接触型二极管及平面型二极管。点接触型二极管是用一根很细的金属丝压在光洁的半导体晶片表面,通以脉冲电流,使触丝一端与晶片牢固地烧结在一起,形成一个“PN结”。由于是点接触,只允许通过较小的电流(不超过几十毫安),适用于高频小电流电路,如收音机的检波等。

面接触型二极管的“PN结”面积较大,允许通过较大的电流(几安到几十安),主要用于把交流电变换成直流电的“整流”电路中。

平面型二极管是一种特制的硅二极管,它不仅能通过较大的电流,而且性能稳定可靠,多用于开关、脉冲及高频电路中。

二极管的导电特性

二极管最重要的特性就是单方向导电性。在电路中,电流只能从二极管的正极流入,负极流出。下面通过简单的实验说明二极管的正向特性和反向特性。

正向特性
在电子电路中,将二极管的正极接在高电位端,负极接在低电位端,二极管就会导通,这种连接方式,称为正向偏置。必须说明,当加在二极管两端的正向电压很小时,二极管仍然不能导通,流过二极管的正向电流十分微弱。只有当正向电压达到某一数值(这一数值称为“门槛电压”,锗管约为0.2V,硅管约为0.6V)以后,二极管才能直正导通。导通后二极管两端的电压基本上保持不变(锗管约为0.3V,硅管约为0.7V),称为二极管的“正向压降”。

2、反向特性

在电子电路中,二极管的正极接在低电位端,负极接在高电位端,此时二极管中几乎没有电流流过,此时二极管处于截止状态,这种连接方式,称为反向偏置。二极管处于反向偏置时,仍然会有微弱的反向电流流过二极管,称为漏电流。当二极管两端的反向电压增大到某一数值,反向电流会急剧增大,二极管将失去单方向导电特性,这种状态称为二极管的击穿。

二极管的主要参数

用来表示二极管的性能好坏和适用范围的技术指标,称为二极管的参数。不同类型的二极管有不同的特性参数。对初学者而言,必须了解以下几个主要参数:

1、额定正向工作电流

是指二极管长期连续工作时允许通过的最大正向电流值。因为电流通过管子时会使管芯发热,温度上升,温度超过容许限度(硅管为140左右,锗管为90左右)时,就会使管芯过热而损坏。所以,二极管使用中不要超过二极管额定正向工作电流值。例如,常用的IN4001-4007型锗二极管的额定正向工作电流为1A。

2、最高反向工作电压

加在二极管两端的反向电压高到一定值时,会将管子击穿,失去单向导电能力。为了保证使用安全,规定了最高反向工作电压值。例如,IN4001二极管反向耐压为50V,IN4007反向耐压为1000V。

3、反向电流

反向电流是指二极管在规定的温度和最高反向电压作用下,流过二极管的反向电流。反向电流越小,管子的单方向导电性能越好。值得注意的是反向电流与温度有着密切的关系,大约温度每升高10,反向电流增大一倍。例如2AP1型锗二极管,在25时反向电流若为250uA,温度升高到35,反向电流将上升到500uA,依此类推,在75时,它的反向电流已达8mA,不仅失去了单方向导电特性,还会使管子过热而损坏。又如,2CP10型硅二极管,25时反向电流仅为5uA,温度升高到75时,反向电流也不过160uA。故硅二极管比锗二极管在高温下具有较好的稳定性。

测试二极管的好坏

初学者在业余条件下可以使用万用表测试二极管性能的好坏。测试前先把万用表的转换开关拨到欧姆档的RX1K档位(注意不要使用RX1档,以免电流过大烧坏二极管),再将红、黑两根表笔短路,进行欧姆调零。

1、正向特性测试

把万用表的黑表笔(表内正极)搭触二极管的正极,,红表笔(表内负极)搭触二极管的负极。若表针不摆到0值而是停在标度盘的中间,这时的阻值就是二极管的正向电阻,一般正向电阻越小越好。若正向电阻为0值,说明管芯短路损坏,若正向电阻接近无穷大值,说明管芯断路。短路和断路的管子都不能使用。

2、反向特性测试

把万且表的红表笔搭触二极管的正极,黑表笔搭触二极管的负极,若表针指在无穷大值或接近无穷大值,管子就是合格的。

二极管的应用

1、整流二极管

利用二极管单向导电性,可以把方向交替变化的交流电变换成单一方向的脉动直流电。

2、开关元件

二极管在正向电压作用下电阻很小,处于导通状态,相当于一只接通的开关;在反向电压作用下,电阻很大,处于截止状态,如同一只断开的开关。利用二极管的开关特性,可以组成各种逻辑电路。

3、限幅元件

二极管正向导通后,它的正向压降基本保持不变(硅管为0.7V,锗管为0.3V)。利用这一特性,在电路中作为限幅元件,可以把信号幅度限制在一定范围内。

4、继流二极管

在开关电源的电感中和继电器等感性负载中起继流作用。

5、检波二极管

在收音机中起检波作用。

6、变容二极管

使用于电视机的高频头中。





经验

浮点转十六进制:
/*作用:浮点数---转---十六进制 */
union fnum
{
long int m;
float f;
};
然后需要在引用的函数内声明共用体比如:
union fnum num;
用法:共用体是将 多个成员变量共用一个地址,并且同一时刻只允许1个成员变量被赋值,当某个成员变量修改后,其他的成员立刻改变,
比如:num.f=6.91;那么num.m=0x40DD1EB8;
当num.f=6.0时,另一个成员变量也改变num.m=40C00000
因为,单片机的十进制和十六进制都可以进行直接计算,所以用以上办法就可以实现浮点数转十六进制.
通俗的讲:共用体就像一个容器,成员变量就是不同形状的容器出口,当我们把1个圆形的物体放进去,如果这时我们像要一个方形的物体,那么就从方形的出口将物体取出,就得到方形了,
同理,我们要浮点转十六进制,就把浮点放进共用体(num.f=6.91),然后从整形取出( Rec=num.m )
细节问题:
因为浮点数转成十六进制后,通常都是32位数据,所以我们在取出整形数的时候要 用一个32位的变量(long int,或者u32 ) 去存.
2. 共用体和结构体的区别  共用体和结构体有下列区别:
  1. 共用体和结构体都是由多个不同的数据类型成员组成,
  但在任何同一时刻, 共用体只存放了一个被选中的成员, 而结构体的所有成员都存在。
  2. 对于共用体的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了,
  而对于结构体的不同成员赋值是互不影响的。 另外要注意的是,



计算串口接收到的浮点数:
我们通常在接收到串口的数字时, 基本上都是文本类型的数字, 并且是一位一位的文本,
比如6.19就是 ‘6’, ’.’ , ’1’ , ’9’
我们将它重新变为6.19需要进行一下操作:
Arr[]={‘6’,’.’,’1’,’9’}
1.所有文本类型的数字只有 减零 之后才可以进行计算,否则是文本型.
  Arr[0]-‘0’;
2. 特别注意


sum_num= (float)(arr[6]-'0')+
(float)(arr[8]-'0')/10 +(float)(arr[9]-'0')/ 100;
在计算的时候,小数部分要转换成 小数,然后进行相加,
注意的是 在(float) (arr[8]-'0')/10的时候, 前面要先强制装换成(float)之后 才可以/10,这样才是把 1变成0.1,
如果在/10之前 没有(float)的话,意味着取10的倍数,
如果是 1/10,不加(float),那就等于0;最后相加就等于6.00000;
正确:

3. 注意细节:
成功将Arr[]={‘6’,’.’,’1’,’9’}
组合成sum_num =6.19之后, 在进行判断sum_num时:
if(sum_num == 62.91f) { LED1=0; }
注意这里的62.91后面加f,如果不加会报警告:
:单精度操作数隐式转换为双精度
当if(sum_num == 62.91) { LED1=0; }
这里的sum_num是浮点型,而62.91默认是双精度类型的;
当2个不同类型的变量 相加减时,会将二者都转换成较大一方的类型,
所以 浮点数 和 双精度进行判断时:浮点数会被转换成双精度.所以会报警告,告诉你这里进行了这样的操作.
当if(sum_num == 62.91f) { LED1=0; }这里的62.91f
是将62.91转换成浮点数
浮点数 和 浮点数进行判断 或者 计算,并不会转换成双精度,警告自然没了



原创:http://www.51hei.com/bbs/dpj-128076-1.html
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表