单片机-C51学习日志2022/2.23

1.步进电机驱动

要实现的功能是:通过ULN2003 驱动模块控制28BYJ48步进电机运行方向及速度,当按下 KEY1 键可调节电机旋转方向;当按下 KEY2 键,电机加速;当按下 KEY3 键,电机减速.

驱动步进电机,需要设置好步数

#include "reg52.h"

//对按键进行定义
sbit key1 = P3^1;
sbit key2 = P3^0;
sbit key3 = P3^2;
sbit key4 = P3^3;

//对步进电机进行定义
sbit IN1_D = P1^0;
sbit IN2_C = P1^1;
sbit IN3_B = P1^2;
sbit IN4_A = P1^3;

//对步进电机最高速度和最低速度进行宏定义
#define MOTOR_MAXSPEED 1
#define MOTOR_MINSPEED 5

//设置延时函数us
void delay_us(unsigned int us)
{
	while(us--);
}

//设置延时函数ms
void delay_ms(unsigned int ms)
{
	unsigned int i,j;
	{
		for(i=ms;i>0;i--)
			for(j=110;j>0;j--);
	}
}

//检测按键按下
unsigned char KEY_SCAN()
{
	static unsigned char time = 1;
	if(time==1 && (key1==0 || key2==0 || key3==0 || key4==0))
	{
		time = 0;
		delay_us(100);//消抖
		if(key1==0)
			return 1;
		else if(key2==0)
			return 2;
		else if(key3==0)
			return 3;
		else if(key4==0)
			return 4;
	}
	else if(key1==1 && key2==1 && key3==1 && key4==1)
	{
		time = 1;
	}
	return 0;
}

//驱动步进电机转动
void STEP_MOTOR_STEP(unsigned char step)
{
	switch(step)
	{
		case 0:	IN1_D=0; IN2_C=1; IN3_B=1; IN4_A=1; break;
		case 1:	IN1_D=0; IN2_C=0; IN3_B=1; IN4_A=1; break;
		case 2:	IN1_D=1; IN2_C=0; IN3_B=1; IN4_A=1; break;
		case 3:	IN1_D=1; IN2_C=0; IN3_B=0; IN4_A=1; break;
		case 4:	IN1_D=1; IN2_C=1; IN3_B=0; IN4_A=1; break;
		case 5:	IN1_D=1; IN2_C=1; IN3_B=0; IN4_A=0; break;
		case 6:	IN1_D=1; IN2_C=1; IN3_B=1; IN4_A=0; break;
		case 7:	IN1_D=0; IN2_C=1; IN3_B=1; IN4_A=0; break;
	}
}

//驱动步进电机转动方向
unsigned char STEP_MOTOR_DIR(unsigned char dir,unsigned char step)
{
	unsigned char temp = step;
	if(dir == 0)
	{
		temp = 7 - step;
	}
	return temp;	
}

//驱动步进电机加速
unsigned char STEP_BOTOR_INCREASE_SPEED(unsigned char speed)
{
	unsigned char sp = speed;
	if(sp > MOTOR_MAXSPEED)
		sp -= 1;
	return sp;		
}

//驱动步进电机减速
unsigned char STEP_BOTOR_DECREASE_SPEED(unsigned char speed)
{
	unsigned char sp = speed;
	if(sp < MOTOR_MINSPEED)
		sp += 1;
	return sp;		
}

void main()
{
	unsigned char key = 0;
	unsigned char dir = 0;
	unsigned char step = 0;
	unsigned char temp = 0;
	unsigned char speed = 1;
	while(1)
	{
		key = KEY_SCAN();
		if(key == 1)
			dir = !dir;
		else if(key == 2)
			speed = STEP_BOTOR_INCREASE_SPEED(speed);
		else if(key == 3)
			speed = STEP_BOTOR_DECREASE_SPEED(speed);
		temp = STEP_MOTOR_DIR(dir,step++);
		if(step > 8)
			step = 0;
		STEP_MOTOR_STEP(temp);
		delay_ms(speed);
	}
}

2.中断系统

2.1外部中断

中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力。

对于单片机来讲,中断是指 CPU 在处理某一事件 A 时,发生了另一事件 B, 请求 CPU 迅速去处理(中断发生);CPU 暂时停止当前的工作(中断响应), 转去 处理事件 B(中断服务);待 CPU 将事件 B 处理完毕后,再回到原来事件 A 被 中断的地方继续处理事件 A(中断返回),这一过程称为中断。

中断请求具有优先度排序,默认的优先级是:

INT0>Time0>INT1>Time1>RX/TX

外部中断0>定时器中断0>外部中断1>定时器中断1>串口中断


  • INT0 对应的是 P3.2 口的附加功能,可由 IT0(TCON.0)选择其为低电平有效还是下降沿有效。当 CPU 检测到P3.2 引脚上出现有效的中断信号时,中断标志 IE0(TCON.1)置 1,向 CPU 申请中断。
  • INT1 对应的是 P3.3 口的附加功能,可由 IT1(TCON.2)选择其为低电平有效还是下降沿有效。当 CPU 检测到 P3.3 引脚上出现有效的中断信号时,中断标志 IE1(TCON.3)置1,向 CPU 申请中断。
  • T0 对应的是 P3.4 口的附加功能,TF0(TCON.5),片内定时/计数器 T0 溢出中断请求标志。当定时/计数器 T0 发生溢出时,置位 TF0,并向 CPU 申请中断。
  • T1 对应的是 P3.5 口的附加功能,TF1(TCON.7),片内定时/计数器 T1 溢出中断请求标志。当定时/计数器 T1 发生溢出时,置位 TF1,并向 CPU 申请中 断。
  • RXD 和 TXD 对应的是 P3.0 和 P3.1 口的附加功能,RI(SCON.0)或 TI (SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向 CPU 申请中断。
//实验现象:
//按下按键key3,实现INT0中断,中断中的程序就是反转led1电平,亮灭循环
//按下按键key4,实现INT1中断,中断中的程序就是反转led2电平,亮灭循环


#include "reg52.h"

typedef unsigned char u8;
typedef unsigned int u16;

//定义按键
sbit key3 = P3^2;
sbit key4 = P3^3;

//定义led灯
sbit led1 = P2^0;
sbit led2 = P2^1;

//设置延时函数
void delay_us(u16 us)
{
	while(us--);
}

//按下按键key3点亮led1
void KEY3_PRESS_LED1()
{
	delay_us(100);
	if(key3 == 0)
		led1 = !led1;
}

//按下按键key4点亮led2
void KEY4_PRESS_LED2()
{
	delay_us(100);
	if(key4 == 0)
		led2 = !led2;
}

//配置外部中断0
void EXIT_INT0()
{
	EA = 1;//打开总中断
	EX0 = 1;//打开INT0的中断允许
	IT0 = 1;//跳变沿触发方式(下降沿)
}

//配置外部中断1
void EXIT_INT1()
{
	EA = 1;//打开总中断
	EX1 = 1;//打开INT1的中断允许
	IT1 = 1;//跳变沿触发方式(下降沿)
}

//设置外部中断0函数
void EX_INT0() interrupt 0
{
	KEY3_PRESS_LED1();
}

//设置外部中断1函数
void EX_INT1() interrupt 2
{
	KEY4_PRESS_LED2();
}

void main()
{
	EXIT_INT0();
	EXIT_INT1();
	while(1)
	{
		
	}
} 

启用外部中断需要配置:

1.打开总中断

2.打开对应外部中断INT1/INT0的中断允许

3.设置外部中断的跳变沿触发方式(一般都是下降沿触发)

2.2定时器中断

51 单片机定时器/计数器内部结构如下所示:


工作方式寄存器 TMOD 用于设置定时/计数器的工作方式,低四位用于 T0,高四位用于 T1。其格式如下:

GATE 是门控位,

GATE=0 时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使 TCON 中的 TR0 或 TR1 为 1,就可以启动定时/计数器工作;

GATA=1 时,要用软件使 TR0 或 TR1 为 1,同时外部中断引脚 INT0/1 也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了 INT0/1 引脚为高电平这一条件。

C/T:定时/计数模式选择位。C/T=0 为定时模式;C/T =1 为计数模式。

M1M0:工作方式设置位。定时/计数器有四种工作方式。


  • TCON 的低 4 位用于控制外部中断
  • TCON 的高 4 位用于控制定 时/计数器的启动和中断申请。
  • 其格式如下:

TF1(TCON.7):T1 溢出中断请求标志位。T1 计数溢出时由硬件自动置 TF1 为 1。CPU 响应中断后 TF1 由硬件自动清 0。T1 工作时,CPU 可随时查询 TF1 的 状态。所以,TF1 可用作查询测试的标志。TF1 也可以用软件置 1 或清 0,同硬 件置 1 或清 0 的效果一样。

TR1(TCON.6):T1 运行控制位。TR1 置 1 时,T1 开始工作;TR1 置 0 时, T1 停止工作。TR1 由软件置 1 或清 0。所以,用软件可控制定时/计数器的启动 与停止。

TF0(TCON.5):T0 溢出中断请求标志位,其功能与 TF1 类同。

TR0(TCON.4):T0 运行控制位,其功能与 TR1 类同。

//实验现象
//通过定时器0中断控制D1灯间隔1s闪烁


#include "reg52.h"

typedef unsigned int u16;
typedef unsigned char u8;

//对led1进行定义
sbit led1 = P2^0;

//配置定时器0
void TIME0()
{	
	
	EA = 1;//打开总中断
	ET0 = 1;//打开定时器0中断允许
	TR0 = 1;//打开定时器0
	TMOD |= 0x01;//这里使用或等符号,可以在不影响定时器1的情况下配置定时器0
	//TH0 = 0xFC;
	//TL0 = 0x66;这里可以不给定时器赋初值,1s=1000ms,要进行多次计数才可以实现
}

void TIME0_INT() interrupt 1
{
	static u16 i = 0;
	static u8 a = 1;
	TH0 = 0xFC;
	TL0 = 0x66;//在这里给定时器赋初值1ms,01模式下定时器不会自动重新赋值,需要手动赋值
	i++;
	if(i == 1000)
	{	
		i = 0;
		led1 = !led1;
		a++;
	}
}

void main()
{	
	TIME0();
	while(1)
	{	
		
	}
}

启用定时器需要配置:

1.打开总中断

2.打开对应定时器的中断允许

3.开启对应定时器T0/T1

4.配置定时器TMOD(通常使用或等符号|=,这样在配置定时器的时候就不会影响其他的定时器)

5.给定时器赋初值

2.3串口通信

串口内部结构:

2.3.1串口寄存器SCON

SM0和SM1为工作方式选择位:

  1. SM2:主要用于方式2和方式3,一般都是使用方式1,所以SM2=0;
  2. REN:允许串行接收位。由软件置 REN=1,则启动串行口接收数据;若软件置 REN=0,则禁止接收。
  3. TB8:方式2和3中,是发送数据的第九位,在方式1中未用到,置0;
  4. RB8:方式1中未用到,置0;
  5. TI:发送中断标志位。在方式1,串行发送停止位的开始时,由内部硬件使 TI 置1,向 CPU 发中断申请。在中断服务程序中,必须用软件将其清 0,取消此中断申请。
  6. RI:接收中断标志位。在方式1,串行接收停止位的中间时,由内部硬件使 RI 置1,向 CPU 发中断申请。也必须在中断服务程序中,用软件将其清 0,取消此中断申请

2.3.2电源控制位PCON

SMOD:波特率倍增位。在串口方式 1、方式 2、方式 3 时,波特率与 SMOD 有 关,当 SMOD=1 时,波特率提高一倍。复位时,SMOD=0。 


//实验现象:
//与串口助手进行通信,将串口助手发送的数据原封不动的传回

#include "reg52.h"

typedef unsigned int u16;
typedef unsigned char u8;

void UART_INT()
{
	ES = 1;//打开接收中断
	EA = 1;//打开总中断
	TMOD |= 0x20;//设置计时器1工作在方式2
	SCON = 0x50;//设置串口控制寄存器工作在方式1
	PCON = 0x80;//开启波特率倍增
	TH1 = 0xfa;
	TL1 = 0xfa;//计数器初始值设置
	TR1 = 1;//打开串口计数器,在其他都配置好之后再开始串口通信
}

void UART()	interrupt 4
{
	u8 i = 0;
	RI = 0;
	i = SBUF;
	SBUF = i;
	while(!TI);
	TI = 0;
}

void main()
{
	UART_INT();
	while(1)
	{
		
	}
}

设置串口通信需要配置:

  1. 打开总中断
  2. 打开串口接收中断
  3. 设置定时器1的工作方式TMOD |= 0x20 ,使其工作在方式1上
  4. 设置串口控制寄存器工作在方式1上,也就是10位收发
  5. 设置是否开启波特率倍增
  6. 设置计数器的初始值
  7. 打开串口(在其他配置都配置好后再打开串口)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值