【自学51单片机】4---不同进制之间转换、C语言变量种类和运算符、keil的Debug用法及应用 和 流水灯的实现
目录
目录
1、二进制、十进制、十六进制转换
1.1 进制介绍
- 十进制:逢十进位,一个为有十个值:0~~9。
- 二进制:逢二进位,它的一个位只有两个值:0 和 1,是实现计算机系统的最基本的理论基础,计算机(包括单片机)芯片是基于成万上亿个的开关管组合而成的,他们每一个都只能有开和关两种状态,对应于二进制的 1 和 0 两个值,书写二进制数据时需加前缀 0b,每一位的值只能是 0 或 1。八位二进制称为一个字节。
- 十六进制:把 4 个二进制位组合为一位来表示,用 0~9 再加上 A~F(或 a~f)表示,逢十六进位,是二进制的一种缩写形式,也是我们程序编写中常用的形式。书写十六进制数据时需加前缀 0x,两位十六进制称为一个字节。
下表为三种进制间对应关系:
- 进制说明:对于进制来说,只是数据的表现形式,而数据的大小不会因为进制表现形式不同而不同,比如二进制的 0b1、十进制的 1、十六进制的 0x01,他们本质上是数值大小相等的同一个数据。我们在进行 C 语言编程的时候,我们只用到十进制和十六进制。
1.2 进制转换
-
二进制转十六进制
16进制有16个数,0–15,用二进制表示15的方法就是1111,从而可以推断出,16进制用2进制可以表现成0000~1111,顾名思义,也就是每四个为一位。举例:
00111101可以这样分:
0011|1101(最高位不够可用零代替),对照着二进制的表格, 8 4 2 1 (一般例举这么多就够了,如果有小数的话就继续往右边列举,如0.5 0.25 0.125 0.0625……)
8 4 2 1
0 0 1 1| 1 1 0 1
左半边=2+1=3 右半边=8+4+1=13=D
结果,00111101就可以换算成16进制的3D。 -
二进制转十进制
从最低位(最右)算起,位上的数字乘以本位的权重,权重就是2的第几位的位数减一次方。比如第2位就是2的(2-1次)方,就是2;第8位就是2的(8-1)次方是128。把所有的值加起来。
2(1-1)代表2的0次方,就是1;其他类推
比如二进制1101,换算成十进制就是:12(1-1)+02(2-1)+12(3-1)+12(4-1)=1+0+4+8=13。 -
十六进制到二进制
由于在二进制的表示方法中,每四位所表示的数的最大值对应16进制的15,即16进制每一位上最大值,所以,我们可以得出简便的转换方法,将16进制上每一位分别对应二进制上四位进行转换,即得所求:
例:2AF5换算成2进制:
第0位: (5)16 = (0101) 2
第1位: (F)16 = (1111) 2
第2位: (A) 16 = (1010) 2
第3位: (2) 16 = (0010) 2
得:(2AF5)16=(0010|1010|1111|0101) -
十进制转二进制:
连续除以2举个例子把
13(10进制)
转化为二进制,我们需要做的就是
13 / 2 = 6 … 1
6 / 2 = 3…0
3 / 2 = 1…1
1 / 2 = 0… 1
然后自下而上写出来: ob1101。 -
十六进制转十进制
16进制数的第0位的权值为16的0次方,第1位的权值为16的1次方,第2位的权值为16的2次方……
所以,在第N(N从0开始)位上,如果是是数 X (X 大于等于0,并且X小于等于 15,即:F)表示的大小为 X * 16的N次方。
例:2AF5换算成10进制:
用竖式计算:
第0位: 5 * 16^0 = 5
第1位: F * 16^1 = 240
第2位: A * 16^2= 2560
第3位: 2 * 16^3 = 8192
直接计算就是:
5 * 16^0 + F * 16^1 + A * 16^2 + 2 * 16^3 = 10997(十进制) -
十进制转十六进制
十六进制转换有16进制每一位上可以是从小到大为0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F16个大小不同的数,即逢16进1,其中用A,B,C,D,E,F(字母不区分大小写)这六个字母来分别表示10,11,12,13,14,15。
例如:(1)1=1;
(2)2=2;
(3)30=30/16=1余下14,14对应的是E。所以30=1E
(4)500=500/16=31余下4,所以个位是4
第二步将31化为16进制,31=31/16=1余下15,找出15对应的数字是F
所以 500=1F4
(5)321=321/16=20余下1,即各位数是1
又将20化为16进制,20=20/16=1余下4
所以321=141
(6)1024=1024/16=64余下0
再将64化为16进制,64=64/16=4余下0
所以1024=400
(7)2000=2000/16=125余下0
再将125化为16进制,125=125/16=7余下13,找到13对应的数是D
所以2000=7D0
对于10进制化为16进制,当数字大于16的时候就一直除以16,从低位往高位写。
2、C语言基础
2.1 C语言变量类型及在C51的范围
-
变量类型:C 语言的数据基本类型分为字符型、整型、长整型以及浮点型,每个基本类型又包含了两个类型。字符型、整型、长整型,除了可表达的数值大小范围不同之外,都只能表达整数,表达小数的话,必须用浮点型。
-
变量在C51的范围:见下表
(注:C51指51单片机里的C语言标准) -
编程宗旨:能用小的变量类型不用大的。 1 个字节 char 解决问题的就不定义成 int,一方面节省 RAM 空间,另外一方面,程序占空间小运算速度更快。
2.2 C语言运算符
- 左移 << 右移 >> (注:移位都是二进制移位,左移,最低位填0补充;右移,最高位填0补充),举例说明用法,a = 0x01 << 2; 就是将0x01转换为二进制数据0b0000 0001左移2位移完右边补两个0,即(二进制下)0b0000 0100 (十六进制下)0x04
- 按位取反符号 ~ : 取反后1变成0,0变成1(注:理按位取反也是针对二进制而言)。比如 a = ~(0x01); 0x01 的二进制是 0b00000001,按位取反后就是 0b11111110,那么a 的值就是 0xFE 了。
3、keil软件的Debug教程
3.1 延时引入
- 在上节闪烁LED小灯中我们知道程序中的
for(i=0; i<30000;i++);
用于小灯延时,除了这种延时方法外,还有3中常见的延时方法。见下图:
for(i=0; i<30000;i++);
用循环来达到延时小灯目的,延时时长随i的变化而变化,那么如何确保延时的时长足以让小灯闪烁呢?这时可以程序Debug就可以观察小灯延时时长。
3.2程序Debug的应用
- 观察非精确延长时间
步骤:1. 选择 Keil 菜单项 Project–>Options forTarget ‘Target1’…—>打开 Target 这个选项卡,找到里边的 Xtal(MHz)这个位置,这是填写我们进行模拟时间的晶振选项,填写自己单片机所使用的晶振 (博主的STC89C52为11.0592MHz)–>找到 Debug 这个选项卡,选择左侧的 Use Simulator,然后点击最下边的 OK 就可以了.如下图:
2. 点击选择菜单项 Debug–>Start/Stop Debug Session,如图:
窗口介绍:最左侧Register窗口为显示单片机一些寄存器的当前值和系统信息,通过这个窗口sec 选项就可以观察C语言代码运行的时间。上侧Disassembly窗口为keil将C语言转换为汇编语言的代码。
三个按钮介绍:三个按钮如下图所示
第一个是复位按钮,点击一下之后,程序就会跑到最开始的位置运行;第二个是全速运行按钮,点击一下程序就会全速跑起来;再第三个是停止按钮。
3. 点击复位按钮,然后双击需要观察时间的代码前后行设置断点,如下图所示:
4. 点击全速运行按钮后并观察记录程序运行时间1,然后再次点击全速运行按钮并观察记录程序运行时间2,程序运行时间2 减去 程序运行时间1 就是for(i=0; i<30000;i++);
的延时时间。如下图所示:
通过计算for(i=0; i<30000;i++);
的延时时间大概在163ms。
(注:非精确延时时间受Xtal模拟单片机晶振及程序优化等级影响)
- 观察寄存器和变量的数值变化
点击 View 菜单里的 Watch Windows–>Watch 1,可以通过双击或按 F2 键,输入我们想观察的变量或寄存器的名字,后边就会显示出它的数值。如下图:
3.3 断点设置
双击需要观察代码前后行即可,但有些地方设置不了断点,这是因为keil软件本身有程序优化功能,想在所有代码设置断点,选择 Keil 菜单项 Project–>Options forTarget ‘Target1’…—>打开C51选项把level优化等级设置为0即可,如下图所示:
4、 流水灯的实现
- 介绍:第三节中我们知道引脚 P0.0 经过 74HC245 控制了 DB0,P0.1 控制DB1……P0.7 控制 DB7,一个字节八位,那么一个P0就代表 P0.0到 P0.7 的全部 8 个位。我们写 P0 = 0xFE;转换成二进制就是 0b11111110,就能点亮 LED2。下面0xFE、0xFD、0xFB、0xF7、0xEF、0xDF、0xBF、0x7F分别代表点亮LED2 到LED9。
通过前面的硬件知识学习,及C语言运算符的介绍,下面来写流水灯程序。
- 流水灯左移程序:
#include<reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
void main()
{
unsigned int i = 0;//定义循环变量i,用于软件延时
unsigned char cnt = 0;//定义计算变量cnt,用于位移控制
ENLED = 0;
ADDR3 = 1;//开启三八译码器
ADDR2 = 1;
ADDR1 = 1;
ADDR0 = 0;//导通三极管
while(1)//主循环,程序无限循环执行该循环体语句
{
P0 = ~(0x01 << cnt);//P0等于1左移cnt位,控制8个LED小灯
for(i = 0; i < 30000; i++);//软件延时
cnt++; //位移计数变量自加1
if(cnt > 7) //位移计数超过7后,再重新从0开始计数
{
cnt = 0;
}
}
}
- 流水灯右移程序
只需把流水灯左移的程序中P0 = ~(0x01 << cnt);
修改成P0 = ~(0x80 >> cnt);
即可实现流水灯右移。
- 左移后右移花样流水灯程序
#include<reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
void main()
{
unsigned int i = 0;//定义循环变量i,用于软件延时
unsigned char cnt = 0;//定义计算变量cnt,用于位移控制
unsigned char sign = 0;// 流水灯移向标志,0代表左移,1代表右移
ENLED = 0;
ADDR3 = 1;//开启三八译码器
ADDR2 = 1;
ADDR1 = 1;
ADDR0 = 0;//导通三极管
while(1)//主循环,程序无限循环执行该循环体语句
{
//左移部分
if(sign == 0)
{
P0 = ~(0x01 << cnt);//P0等于1左移cnt位,控制8个LED小灯
for(i = 0; i < 30000; i++); //软件延时
cnt++;//位移计数变量自加1
if(cnt > 7) //位移计数超过7后,再重新从0开始计数
{
sign = 1;//当左移到最后一个LED小灯,移向标志赋值为1让小灯右移
cnt = 0;
}
}
//右移部分
if(sign == 1)
{
P0 = ~(0x80 >> cnt);//P0等于1右移cnt位,控制8个LED小灯
for(i = 0; i < 30000; i++);
cnt++;
if(cnt > 7)
{
sign = 0;//当右移到最后一个LED小灯,移向标志赋值为0让小灯左移
cnt = 0;
}
}
}
}
- 右移后左移花样流水灯程序
只需把左移后右移花样流水灯的程序中unsigned char sign = 0;
修改成unsigned char sign = 1;
并将左移代码部分和右移代码部分调换顺序即可实现先右移后左移花样流水灯。
5、收获
重温第四章内容搞懂了如何进制转换、C51的意思和更加熟悉了keil的Debug运用,在流水灯程序方面有了前面C语言的学习,写起来很流畅。争取一天一章,奥里给!!!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)