新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 【51单片机】学习总结

【51单片机】学习总结

作者: 时间:2016-11-28 来源:网络 收藏
1. Ctrl+S为保存当前文件,如图

打开了两个文件,可以看到有个文件是带星号且有个红叉的,有个红叉说明它为当前打开的文件,也就是正在编辑的文件。当按下Ctrl+S时只保存了当前文件。另一个文件没有保存,当你的电脑意外断电或死机时,如果你在那之前按了Ctrl+S那么可以挽回点损失,可是另一个文件的信息可能就丢失了。所以推荐按F7(对所以文件进行编译,并且保存),这样就可以在编译的同时将所以文件保存一次。所以在真正做工程的时候就要学会定期按F7,不是说一定要程序编写完整后才按F7。
2. 默认情况下头文件和源文件应该放在同一个文件目录下,如果将头文件放在一个独立的文件夹中如名为include的文件夹,此时就读取不出。要进行一下设置才能读取出来。如图:

在C51选项中的Include Paths 选项中要设置头文件的路径后方能打开头文件,对头文件进行编译!
3. 对于不变化的变量,如数码管显示的数组,要通过前缀code将它放在ROM中避免占用了RAM的空间。
4. 宏定义的内容全用大写字母,当有两个单词时用_将他们分开,如#define LED_SEG P1_1;在特殊情况下才用小写字母。
5. 变量名的定义一般为 i , Led , DispBuff 即单个字母时小写,多个字母一个单词时首字母大写,多个单词时连写且每个单词的首字母大写。
6. 函数名的定义一般为 delay ,write_byte 即单个单词时全小写,多个单词时也是小写,但是单词间用 _ 隔开。
7. 不必纠结于该换行多少,空格多少,只要自己知道哪里该换行,哪里改空格就行了,像这些东西的尺度只有自己真正实践的时候就能领悟出来。当然这没有绝对的标准,就算是非常出色的工程师也很难做到精确的换行,当是他们的程序整体看起来整齐有可读性高就OK了。要懂得把精力放在恰当的地方。
8. 不要太死板,一定要照着那些条条框框,但我相信小宋老师我需要向他学习,他的程序有很高的价值。
9. 中断函数书写

10. 在函数中无论是被调函数还是主函数都要懂得对一些变量进行初始化,这是一个好习惯,别以为没必要,或者繁琐。这其实不是繁琐,而是严谨。写程序就应该有严谨的态度。
11.main函数的标准写法为:
int main(void)
{
……
return 0;
}
12. 在定时器溢出进入中断时,在执行中断时TF0或TF1会硬件(自动)清0,从新开始计数。但在串口发送或接受标志位置1并引起中断时,在执行中断过程中TI或RI 需软件(手动)清0.
13. 有关UART通信定时器1模式2中 TH1 与 TL1(与TH1相等)的初始化。在实际计算中TH1 = 256 - 11059200/(12*32*9600); 可以计算得 TH1 = 0xFD; 但是当你把TH1 = 256 - 11059200/(12*32*9600); 写入编译器中让编译器来完成计算时,那么这样写就是错误的了,在keil里默认整数常量是 uint16 类型,取值为0~65535.故应写为TH1 = 256 – 11059200UL/(12*32*9600UL);要注意的是keil 对这类错误不报错!
14. 在串口通讯中,如:

串口调试助手一般选择“字符格式显示”。如果想显示数字则写入语句UART_send_byte(‘8’) ,要注意显示数字时只能显示一位即0~9;想显示字母时可写入语句UART_send_byte(‘A’)。注意别掉了‘’尤其是写数字时。写字母丢失的话还会报错。写数字丢失的话就不会报错。此时显示的是数字相应的ASII码。
15. 若uint8 temp = ‘A’; 则 temp = ‘A’ + 1 时temp = ‘B’ , 即字符与数字相加为对应ASII码相加。
16. 在串口调试助手中,要发送字符时不用带‘’因为软件已经默认带上了。
17. 关于对数组各元素赋值,有两种方式,如uint8 table [11] = “how are you”; 另一种是 table[0] = ‘h’; table[1] = ‘o’;……其中第一种赋值只能在定义数据类型时使用,如上所示。
在数据类型定义完毕后不能通过table [11] = “how are you”;来改变数组里面的元素,这样做在keil 下会有警告!但不报错,实际是不正确的。那么要改变数组的元素只能通过对里面元素逐个赋值。
18. UART通讯是从低位开始接数据,而I2C通讯是从高位开始接数据。
19. 关于bit 类型的函数。如
bit i2c_read_buf(uint8 *buf, uint8 addr, uint8 len)
{
while (len--)
{
if (i2c_readbyte(addr++, buf++))
return 1;
}
return 0;
}
在主函数中可以直接写为bit i2c_read_buf(&a, b, c); 也可以写为 y = bit i2c_read_buf(&a, b, c),将函数的返回值给了y。
20. uint8 table[] = “I”; 与 uint8 table[]={‘I’}; 两者使用时等同,但是前者为字符串,故比后者多一个字节。
21. 结构体分配的地址是连续的。如:

22. 关于结构体指针问题。先看看以下一些程序通过结果进行分析得出结论.程序如下:

(1)结果为:44
是否就说明结构体指针 p 指向了整个 int a 呢?
(可以从第一个字节地址开始逐个指向下一个字节地址直到第4个为止)
(2)接着把tmp={44}该为tmp={127};
结果为:127
依然正确
(3)继续改为tem={128};
结果为:-128
显然不是我们想要的,倘若 p 指向了整个 int a,那么结果应该为 128 才对,显然*p 只是指向int a的第一个地址。不会自动指向下一地址继续将剩下的3个字节读完,只有将剩下的三个字节读完才能读取一个完整的INT型数据。以上是在指针为char 类型的时候。(4)承接上面的,继续把char *p 改为 int *p
结果为 128
正常了。为什么?因为char类型指针一次只能放一个地址(一个字节一个地址),
而int *p 一次能放4个地址,所以此时 p 可以一次指向4个int a 的四个地址
23. *p+1 的运算顺序是先 *p 后 +1,不要误以为是*(p+1).
24. 如有数组 int table[]={111,323,3423,3322}; 指针 char *p; 且 p = &table;
那么 p 指向了一个地址(table的首地址),至于可以指向几个地址这个由指针的类型决定。现在指针类型为char而char占一个字节只能放一个地址,所以 p 一次只能指向一个地址,所以 *p != 111,因为*p只读取了111的第一个地址的数据,还有三个地址的数据没读。注意此时 p+1不指向table[1]这个元素的首地址。而是指向第一个元素的第二个地址。可以做一下改善来一次读取4个地址,即让指针一次指向int类型数据的4个地址。
把 char *p改成 int *p,这样就可以一次指向四个地址了。则 *p = 111, *(p+1) = 232;注意括号一定要带上,不然结果是 112.
可以得出结论。p不是指向一个地址,指向几个地址由p 的数据类型决定。p + 1不是代表下一个地址。如 int *p,则p+1代表下一段连续的4个地址。可以理解为 p+1指向了4个连续的地址。在进行*(p+1)运算后将4个地址的数据连续读取出来,合成了一个完整的int类型的数据。
25. 有关数组的长度。如 uint8 table[]={‘a’}; 与 uint8 table[]=”a”; 后者是用字符串形式来进行初始化。需要注意的是 若有 uint8 *p=&table. 对于两者均有 *p=‘a’;可是对于 *(p+1)两者的情况却是不同的,后者有 *(p+1)= ‘’; 而前者却没有这种情况,在实际试验中是要推后几位才有这种情况。
26. 有关二进制(BCD码)与十六进制的相互转换,0xFF—0b1111 1111 ,0xFE—0b1111 1110.
分别写成第一个数字或字母所对应的二进制数即可。至于二进制到16进制的转化也是如此,倒过来就是了。反正记着,4位二进制数对应一位十六进制数。
27. 字符类型数据(uint8)在与整数运算时都是以ASII码形式或十进制数存在的。若uint8 型变量里面放的是字母或2个BCD码(8421),8位二进制数,在于整数进行运算时可以看出是ASII码或十进制数的运算。
28. 别误把if 语句当成循环语句了,if语句在执行时先判断括号内的语句是否成立,成立的话就执行{}里的内容(if语句嵌套的内容)。如果成立那么执行{}里的内容,执行完后不再进行一次if的判断,而是执行下一语句,即便判断仍然成立。主要还是要说明if不是循环语句,只判断一次,无论成立与否,在执行完所需内容后就直接执行下一语句。
29. 关于取地址符号&的使用,变量本来就表示地址了,那么就不能再加&这样会报错。特殊例子是数组名a,它本身就代表第一个元素的首地址。但可以使用取地址符号,且不会报错。加去地址符号后&a表示的含义发生了变化而是代表该数组的首地址,数值上似乎与a的相同,但含义却是有差别的。
30. *p+1等价于(*p)+1,因为*优先级高于+;*p++等价于*(p++),因为++优先级高于*;
&a+1等价于(&a)+1.不能出现&(a+1)的情况,系统不认同,会报错!
31. 对于数组 int a[4]={1,2,3,4}; 其中 a+1表示第二个元素的首地址(第一个字节的地址)。而(int)a+1表示第一个元素第二个字节的地址。
32. 关于结构体变量命名。如图

这种命名方法在KEIL下会报错,然而在VC++下却没问题。当你把code改成其他字母(不要与原来的code一样就行),你会发现都没问题。可以看出并不是命名的大小写出问题。原因是KEIL不认同以结构体小写字母来命名(这里是指全小写),当你把其中一个字母该为大写时就没问题了。如把最后一行改成 CODE Code 让变量名的第一个字母为大写就行了。
33. 单片机要进行通信时要注意先关掉所有中断(EA=0),在通信结束后才打开(EA=1).由于通信时通常要遵守时序,而中断一旦被触发便会打乱时序,使得传输数据失败或者异常!注意通信中时序是非常重要的!
34. 在通信过程中需要注意,比如在传输一个字节数据时是一位一位地传让后凑够8位构成一字节的。要从低位开始传还是高位开始传,要传低位开始接还是高位开始接。这些不能处理好的话会直接影响到结果。也就是说发送端和接收端要保持一致,有两种方式,一是从低位开始发(发送方),然后从低位开始接(接收方)。另一种就是从高位开始发(发送方),然后从高位开始接(接收方)。还有一个字节接收和发送的方向也要一致,只有这些保证了,才能保证信息准确传输。比如:在UART通信中,如果你要用单片机IO口模拟UART通信就需要注意这些,一般采取UART通信的统一标准,从低位开始传输。这样跟标准UART器件就能很容易接上,不需要再作处理。当两个通信器件都接标准UART接口时,那么就不用考虑这里了,这个在传输过程中他们自己会进行处理,我们无需考虑。只要把字节的排序弄好就行了。位的处理他们会自己完成。
35. 定时器1工作在模式1时的溢出是指在TH1=255(0xFF),TL1 =0的情况下,TL1达到255再 +1 就溢出了,此时溢出标志位TF1=1,需要清零才能让定时器继续工作。否则不定时,而是处于等待状态,等待TF1=0,然后才开始定时。要弄清什么时候才会溢出!
还有就是TL1每加1表示已经定时一个机器周期,在12T/11.0592MHz的单片机中机器周期为12/11059200 s 大约为1us.也就是说TL1每加1表示已经定时1us 。而TH1加1就表示已经经历了256个机器周期。这里说的+1并不会使定时器溢出,只有TH1达到255了那么接下来的256个机器周期后就会溢出了!


关键词: 51单片机学习总

评论


技术专区

关闭