以计算机相关专业角度学习单片机
本人为软件工程专业,接触计算机硬件学习较少,以一个嵌入式非专业角度,入门学习。对于一个要求红黑树都要能敲出来的专业,对于这种面向寄存器编程,是比较简单的,大家可以很快入门(写代码而言,想外设学习,自己组装硬件还学专业地学习电子相关知识)如有错误请见谅。使用普中51-单核-A2学习
单片机针脚
C51系列单片机有4*8=32个针脚。p0,p1,p2,p3三个寄存器,每个寄存器大小为8位,干什么用的呢?这些针脚只有两种状态即0(低电平)与1(高电平)。我们可以通过写程序,通过编译器生成CPU的一条条指令,烧录到里面去。我们通过对针脚的高低电平进行逻辑判断,再通过一系列状态对针脚状态做出改变。
举一个例子,比如查看某个针脚状态,读到了1,可以假想(另一个针脚上连着电机),我们改变针脚状态,让电机转起来。
C51程序界的Hello World
#include <REGX52.H>
void main()
{
P2=0xFE; //1111 1110
while(1)
{
}
}
我们通过控制寄存器存储的值,来改变针脚的状态,后面为什么有一个while(1){}呢,单片机程序与我们现在windows、Linux C程序不同,电脑上的程序运行一次就结束了。单片机如果程序运行完后、它会从头再来。所以我们让单片机卡死在哪里就好了。
LED灯闪烁实现
怎样实现延时呢?没错我们可以暴力解决,让它运行一段循环不就行了嘛。我们可以使用stc-isp软件来生成延时代码片段。我们要知道,晶振就像一个定时的东西,每隔一个固定的时间片段就会发出脉冲信号,让我们的单片机CPU指令,一条条执行(形象地描述,原理太复杂了,恐怕要读一整本计算机组成原理)
#include <REGX52.H>
#include <INTRINS.H>
void Delay500ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P2=0xFE; //1111 1110
Delay500ms();
P2=0xFF; //1111 1111
Delay500ms();
}
}
我们改变的个针脚的状态,交替执行,转态变化 0__1__0__1__0__1…,这样不就闪烁起来了吗。
LED流水灯实现
流水灯这不过,是一排灯按照按顺序点亮,灭掉,下一个点亮,灭掉…而已。我们按一定的逻辑来控制一个寄存器的位的状态就好了。
#include <REGX52.H>
#include <INTRINS.H>
void Delay500ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P2=0xFE;//1111 1110
Delay500ms();
P2=0xFD;//1111 1101
Delay500ms();
P2=0xFB;//1111 1011
Delay500ms();
P2=0xF7;//1111 0111
Delay500ms();
P2=0xEF;//1110 1111
Delay500ms();
P2=0xDF;//1101 1111
Delay500ms();
P2=0xBF;//1011 1111
Delay500ms();
P2=0x7F;//0111 1111
Delay500ms();
}
}
虽然这是一种傻瓜式流水灯,但我们能实现功能就好了。就像做实物一样,不管用了什么技术,就像有一个网站,java做服务端,C++做服务端。做为一个使用者,谁会没事关注这些东西呢。
延时函数可复用
我们不能需要用到多长时间的延时,都去创建一个函数吧,为何不把延时函数加个参数,来实现呢
#include <REGX52.H>
void Delay1ms(unsigned int xms); //@12.000MHz
void main()
{
while(1)
{
P2=0xFE;//1111 1110
Delay1ms(1000);
P2=0xFD;//1111 1101
Delay1ms(1000);
P2=0xFB;//1111 1011
Delay1ms(100);
P2=0xF7;//1111 0111
Delay1ms(100);
P2=0xEF;//1110 1111
Delay1ms(100);
P2=0xDF;//1101 1111
Delay1ms(100);
P2=0xBF;//1011 1111
Delay1ms(100);
P2=0x7F;//0111 1111
Delay1ms(100);
}
}
void Delay1ms(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
使用按键
如果一个开关一边接到了针脚上,另一端接上1(高电平),当我们地的开关闭合时,那么针脚状态就是1,开关断开,则是0 ,我们可以去检测针脚状态来进行逻辑控制
#include <REGX52.H>
void main()
{
while(1)
{
if(P3_1==0 || P3_0==0) //如果K1按键或K2按键按下
{
P2_0=0; //LED1输出0,点亮
}
else
{
P2_0=1; //LED1输出1,熄灭
}
}
}
使用按键保存状态
#include <REGX52.H>
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
while(1)
{
if(P3_1==0) //如果K1按键按下
{
/*
1\ /1
\ /
\ /
0\----/0
*/
Delay(20); //消除下坡上坡影响
while(P3_1==0); //松手检测
Delay(20); //延时消抖
P2_0=~P2_0; //LED1取反
}
}
}
利用左移右移
左移:右边补0,例:00000001<<3 -> 00000010;按位左移运算符。左操作数按位左移右操作数指定的位数(在低位补 0)。左移运算符,num << 1,相当于 num 乘以 2(每左移一位就相当于乘以一个 2)。按位右移运算符。左操作数按位右移右操作数指定的位数(如果该数为正数,则高位补 0 ,若为负数,则高位补 1)。右移运算符,num >> 1,相当于 num 除以 2(每右移一位相当于除以一个 2)。
#include <REGX52.H>
void Delay(unsigned int xms);
unsigned char LEDNum;
void main()
{
P2=~0x01; //上电默认LED1点亮
while(1)
{
if(P3_1==0) //如果K1按键按下
{
Delay(20);
while(P3_1==0);
Delay(20);
LEDNum++; //LEDNum自增
if(LEDNum>=8) //限制LEDNum自增范围
LEDNum=0;
P2=~(0x01<<LEDNum); //LED的第LEDNum位点亮
}
if(P3_0==0) //如果K2按键按下
{
Delay(20);
while(P3_0==0);
Delay(20);
if(LEDNum==0) //LEDNum减到0后变为7
LEDNum=7;
else //LEDNum未减到0,自减
LEDNum--;
P2=~(0x01<<LEDNum); //LED的第LEDNum位点亮
}
}
}
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
编写自己的头文件与多文件编译
与C语言知识相同、不做过多阐述。
转载:https://blog.csdn.net/qq_45812941/article/details/116900921