51单片机学习笔记(郭天祥版)(2)——第一课作业、流水灯、蜂鸣器
时间:2022-09-11 03:30:00
免费样品申请(暂时无用,不说,很简单)
作业讲解
1-5点亮灯太简单了,别说了
6:试着让第一个发光管闪烁
闪烁,就是亮,灭,再亮,再灭这样一个循环的过程,就是写程序控制某个循环IO口先输出低电平,然后输出高电平,然后输出低电平。在这样的循环中,它将持续一段时间而不改变状态(即延迟)。使用简单的延迟while或者for循环就可以了。
1 #include 2 sbit p1_1=P1^0; 3 unsigned int a; 4 void main() 5 { 6 a=50000; 7 p1_1=0; 8 while(a--); 9 a=50000; 10 p1_1=1; 11 while(a--); 12 }
定义一个unsigned int变量类型(无符号整形)a,其范围为0-65535,如果超过此值,则取余,如70000-65535后。
这择这两个,这样如果一直插入单片机,然后更改代码并编译,就会自动下载到单片机。
你可能会遵循上述方法,但这是错误的,不标准的。因为在正常执行c语言代码时,从进入main函数开始,执行完main内部代码已经结束,但这里重新执行,因为单片机会已经重复执行main函数,但有时会出错。
正确的写法
1 #include 2 sbit p1_1=P1^0; 3 unsigned int a; 4 void main() 5 { 6 while(1) 7 { 8 a=50000; 9 p1_1=0; 10 while(a--); 11 a=50000; 12 p1_1=1; 13 while(a--); 14 } 15 }
在main再写一个函数内部while(1)将需要循环执行的代码放入while(1)花括号内,使单片机始终循环while(1)内部内容不出错。
学习软件调试
像放大镜一样点击打开调试,调试前先编译。再次单击并关闭。
黄色箭头指着下一个要执行的句子
这是复位,让调试重新开始。
全速执行。
停止全速执行,只有在全速执行后才会变红,才能使用。
进入函数内部。
执行下一调语句。
点击Peripheral(硬件)
Interrupt:中断
I/O-Ports:IO口
Serial:串行端口
Timer:定时器
选择我们现在使用的东西IO口的P1口Port 1
可以看到P1上电后为oxFF,上电后一切IO口为高电平(IO口上电后为1,其他寄存器上电后为0,上电后会自动复位,所以这里软件的复位可以作为上电),相应的后8位都打了√,如果√不,相应的一个变成了0。操作单片机是内部寄存器(P1)操作。
点击View,选上call stack window
看,执行的句子变绿了,没有执行的是灰色,这里while(a--)要完成执行,需要单步执行5万次(即一次执行一句),重点是5万次,非常慢,所以可以设置断点。
断点是红色出现在前面,有些版本是红色的圆,设置断点的方法是用鼠标单击所需句子前的灰色部分。
现在让我们谈谈上述全速执行。全速执行总是执行的,而且执行速度很快。你不需要一个点,但你不知道在哪里执行,所以我们需要使用断点。我们不想在这里点击5万次,所以我们在while(a--)后面的句子设置断点,然后打开全速执行,然后程序将全速执行到断点位置,然后停止,而不是断点句子。
在这里执行完全速后,a变成65535(也是满的,unsigned int类型的最大值,满和空是一个概念),意思是减空,从原来的5万,减到0后再减一次就是65535。
硬件调试(在线调试)(模拟芯片) 不买,先不看
仿真芯片有助于您了解程序是如何执行的
点开设置
点到debug这一栏。
原本是use Simulator,现在我们点击右边的Use,选择keil Monitor-51 Driver,再点击Settings设置
选择com用于下载程序com这里就选几个com然后点击ok。
之后这里全部打钩,再确定。
模拟时必须先接通电源
然后点击debug调试
连接将显示在下面(Connected to Monitor-51)
然后按照刚才的方法调试。不同的是,在这里调试时,单片机会一步一步地跟着。
您可以知道每个指令何时执行,每个变量如何变化。
调制结束时,先按下单片机上的复位按钮,然后点击debug结束
接下来,学习如何设置更准确的延迟时间
首先,实验板的晶振频率为11.0592MHz,计算机单片机的运行速度由其决定
以下内容只需要知道一个机器周期是12个时钟周期,一个时钟周期是冲击源周期。
调整时间时,需要调整时间keil晶振频率与单片机相同
打开调试状态
当程序在这里运行时,你会看到0.00042209s,运行到灯亮
0.00042752s,然后我们运行到它熄灭之前,看看中间有多长时间。这是我们想要的延迟时间。在这里设置断点,否则while(a--)我们需要手动50000次...
时间变为了0.48893555s,离500ms最近,在我们的微调下,把a变成51000,然后运行到这里几乎是一样的。下面也改了。
让我们看看如果没有11.0592MHz晶振频率,默认24MHz会怎么样
频率24是11的2倍左右,所以时间几乎是1/2。
但这里只是模拟,对单片机没有影响,单片机本身仍然使用11.0592来计算,但这样你就不准了。
接下来学习子函数(以后写程序都写在函数里,然后main全是调用)
首先介绍宏定义define,相当于一个新名字,用法#define new_name old_name
1 #include 2 #define uint unsigned int//宏定义 3 #define uchar unsigned char 4 5 void delay();//声明函数 6 sbit D1=P1^0; 7 void main() 8 { 9 while(1) 10 { 11 D1=0; 12 delay(); 13 D1=1; 14 delay(); 15 } 16 } 17 18 void delay()//定义函数,大约是500ms的延时, 19 { 20 uint x,y; 21 for(x=100;x>0;x--) 22 { 23 for(y=600;y>0;y--) 24 { 25 26 } 27 } 28 }
这里for上面还出现了循环while循环,当没有内部句子时,可以省略{},直接写分号
1 void delay(uint n) 2 { 3 uint x,y; 4 for(x=n;x>0;x--) 5 { 6 for(y=600;y>0;y--); 7 } 8 }
其他人自己理解,不再重复
子函数带参数
1 #include 2 #define uint unsigned int 3 #define uchar unsigned char 4 5 void delay(uint n); 6 //void delay(uint );这里的声明也可以写成这样(不写)n),但建议写上面的,方便,上面的只需要复制函数定义,不会出错。 7 sbit D1=P1^0; void main()
9 {
10 while(1)
11 {
12 D1=0;
13 delay(100);
14 D1=1;
15 delay(100);
16 }
17 }
18
19 void delay(uint n)
20 {
21 uint x,y;
22 for(x=n;x>0;x--)
23 {
24 for(y=600;y>0;y--)
25 {
26
27 }
28 }
29 }
函数定义和函数声明的参数叫做形参(形式参数),函数调用时给的数值是实参(实际参数),因为函数定义时函数是不执行的,而只有调用时,函数才执行,其中的参数会使用你传的参数,也就是实参。
接下来我们用stc-isp来生成延时函数
打开后点击红框内的三角箭头,找到软件延时计算器
先选择晶振频率,再设置延时的时间,再选择指令集STC-Y1,然后生成c代码,最后复制就可以了
1 #include
2 #define uint unsigned int
3 #define uchar unsigned char
4
5 void Delay1ms();
6 void delay(uint n);
7 sbit D1=P1^0;
8 void main()
9 {
10 while(1)
11 {
12 D1=0;
13 delay(200);
14 D1=1;
15 delay(200);
16 }
17 }
18
19 void delay(uint n)
20 {
21 while(n--)
22 {
23 Delay1ms();
24 }
25 }
26 void Delay1ms() //@12.000MHz
27 {
28 unsigned char i, j;
29
30 i = 2;
31 j = 239;
32 do
33 {
34 while (--j);
35 } while (--i);
36 }
定义一个延时1ms的函数,然后再定义一个带参数的函数,调用延时1ms的函数n次,就产生了延时n ms的函数了
接下来是流水灯
找到C51文件夹中Hlp文件夹下的c51帮助文档,在Reference里能看到库函数,都是写好的可以直接调用的函数
这里我们要用到_crol_
点开后可以看到介绍和例子,需要用到头文件intrins.h,将变量c循环左移b位,返回移动后的结果。a=0xA5(1010 0101),循环左移一位变成(0100 1011),循环左移第二位(1001 0110),循环左移第三位(0010 1101)即0x2D,循环左移一次就是整体左移一次,把最高位的放到最低位。
1 #include
2 #include
3
4 #define uint unsigned int
5 #define uchar unsigned char
6
7 uchar temp;//为什么用uchar而不用uint?
8
9 void Delay1ms();
10 void delay(uint n);
11
12 void main()
13 {
14 temp=0xfe;
15 P1=temp;//因为uchar有8位,而uint有16位,每组IO口也是8位,所以如果我们要用uchar
16 while(1)//先让P1=0xfe(1111 1110)
17 {
18 delay(300);//延时300ms
19 temp=_crol_(temp,1);//改变temp的值
20 P1=temp;//改变P1的值
21 }
22 }
23
24 void delay(uint n)
25 {
26 while(n--)
27 {
28 Delay1ms();
29 }
30 }
31 void Delay1ms() //@12.000MHz
32 {
33 unsigned char i, j;
34
35 i = 2;
36 j = 239;
37 do
38 {
39 while (--j);
40 } while (--i);
41 }
其实temp这里多余,只是想让你知道定义变量要注意的地方,比如为什么不用unsigned int而用unsigned char
1 void main()
2 {
3 P1=0xfe;
4 while(1)//先让P1=0xfe(1111 1110)
5 {
6 delay(300);//延时300ms
7 P1=_crol_(temp,1);//改变P1的值
8
9 }
10 }
main可以直接改成这样
课后作业
补充一下蜂鸣器
蜂鸣器原理图
(我的板子是第二种)(继电器也挺重要,但暂时不说了)
FM就是P2^3口,然后接一个电阻,这里用了一个三极管,用了它的开关作用(它还有放大作用),(蜂鸣器内阻很小(忽略)),当给P2^3低电平时,ce就导通了,就有从VCC流经蜂鸣器然后经过ce的电流。
因此蜂鸣器的使用也很简单,0就响,1就停止。
1 #include
2
3 sbit beep=P2^3;
4
5 void main()
6 {
7
8 while(1)
9 {
10 beep=0;
11 }
12 }
这样就响了
接下里是响、停循环进行
1 #include
2
3 #define uint unsigned int
4 #define uchar unsigned char
5
6 sbit beep=P2^3;
7
8 void Delay1ms();
9 void delay(uint n);
10
11 void main()
12 {
13
14 while(1)
15 {
16 beep=0;
17 delay(300);
18 beep=1;
19 delay(300);
20 }
21 }
22
23 void delay(uint n)
24 {
25 while(n--)
26 {
27 Delay1ms();
28 }
29 }
30 void Delay1ms() //@12.000MHz
31 {
32 unsigned char i, j;
33
34 i = 2;
35 j = 239;
36 do
37 {
38 while (--j);
39 } while (--i);
40 }
这里在提一下前面说到的~(取反),原本是0,取反就是1,原本是1,取反就是0,用这个会很简单。
1 #include
2
3 #define uint unsigned int
4 #define uchar unsigned char
5
6 sbit beep=P2^3;
7
8 void Delay1ms();
9 void delay(uint n);
10
11 void main()
12 {
13
14 while(1)
15 {
16 beep=~beep;
17 delay(300);
18 }
19 }
20
21 void delay(uint n)
22 {
23 while(n--)
24 {
25 Delay1ms();
26 }
27 }
28 void Delay1ms() //@12.000MHz
29 {
30 unsigned char i, j;
31
32 i = 2;
33 j = 239;
34 do
35 {
36 while (--j);
37 } while (--i);
38 }