1、编程和烧录
软件keil 5; 烧录软件 stc-isp
2、一个简单的流水灯程序:
#include<reg52.h> //包含单片机寄存器的头文件
#define uchar unsigned char
#define uint unsigned int
/* 函数申明 -----------------------------------------------*/
void delay(uint z);
/*
********************************************************************************
** 函数名称 : main(void)
** 函数功能 : 主函数
********************************************************************************
*/
void main()
{
uchar num,dat1,dat2;
delay(500);
P0=0;
while(1)
{
dat1=0xfe;
for(num=0;num<7;num++)
{
P1=dat1; //初始化赋值
dat1=dat1<<1; //右移一位
delay(500); //延时
}
}
}
/*
********************************************************************************
** 函数名称 : delay(uint z)
** 函数功能 : 延时函数
********************************************************************************
*/
void delay(uint z)
{
uchar j;
for(z;z>0;z--)
for(j=200;j>0;j--);
}
3、步进电机控制
通过 51 单片机可以方便地对其进行控制。下面将从硬件连接、驱动原理、代码实现等方面详细介绍如何使用 51 单片机控制 28BYJ - 48 步进电机。
硬件连接
控制上:单片机-》ULN2003 驱动板》步进电机
- 电源连接:
- 将 28BYJ - 48 步进电机的电源正极(红线)连接到 ULN2003 驱动板的电源正极,电源负极(橙线)连接到驱动板的电源负极。
- 给驱动板提供合适的电源,一般使用 5V 电源,可将电源正极连接到 51 单片机的 VCC,负极连接到 GND。
- 信号连接:
- 28BYJ - 48 步进电机有 4 根信号线(黄、粉、蓝、紫),分别连接到 ULN2003 驱动板的 IN1 - IN4 引脚。
- 将驱动板的 IN1 - IN4 引脚分别连接到 51 单片机的 4 个 I/O 引脚,例如 P1.0 - P1.3。
驱动原理
28BYJ - 48 步进电机采用单极性 5 线 4 相驱动方式,通过按一定顺序给 4 相绕组通电,可以使电机转动。常见的驱动方式有单四拍、双四拍和八拍,其中八拍驱动方式的精度较高,下面是八拍驱动的通电顺序:
节拍 | IN1 (P1.0) | IN2 (P1.1) | IN3 (P1.2) | IN4 (P1.3) |
---|---|---|---|---|
1 | 1 | 0 | 0 | 0 |
2 | 1 | 1 | 0 | 0 |
3 | 0 | 1 | 0 | 0 |
4 | 0 | 1 | 1 | 0 |
5 | 0 | 0 | 1 | 0 |
6 | 0 | 0 | 1 | 1 |
7 | 0 | 0 | 0 | 1 |
8 | 1 | 0 | 0 | 1 |
#include <reg52.h>
// 定义步进电机控制引脚
sbit IN1 = P1^0;
sbit IN2 = P1^1;
sbit IN3 = P1^2;
sbit IN4 = P1^3;
// 八拍驱动序列
unsigned char code StepTable[8] = {
0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09
};
// 延时函数
void delay(unsigned int time) {
unsigned int i, j;
for(i = 0; i < time; i++)
for(j = 0; j < 123; j++);
}
// 步进电机正转函数
void motor_forward(unsigned int steps) {
unsigned int i, j;
for(i = 0; i < steps; i++) {
for(j = 0; j < 8; j++) {
P1 = StepTable[j];
delay(5); // 调整延时时间可以改变电机转速
}
}
}
// 步进电机反转函数
void motor_backward(unsigned int steps) {
unsigned int i, j;
for(i = 0; i < steps; i++) {
for(j = 7; j >= 0; j--) {
P1 = StepTable[j];
delay(5); // 调整延时时间可以改变电机转速
}
}
}
void main() {
while(1) {
// 正转 512 步
motor_forward(512);
delay(1000); // 延时 1 秒
// 反转 512 步
// motor_backward(512);
// delay(1000); // 延时 1 秒
}
}
4、超声波检测
使用 51 单片机连接超声波传感器(以常见的 HC - SR04 为例)可以实现距离测量等功能。下面将从硬件连接、工作原理、代码实现等方面详细介绍如何完成连接与功能实现。
硬件连接
HC - SR04 超声波传感器有 4 个引脚,分别为 VCC(电源正极)、GND(电源负极)、Trig(触发信号输入)和 Echo(回响信号输出)。与 51 单片机的连接方式如下:
- 电源连接:
- 将 HC - SR04 的 VCC 引脚连接到 51 单片机的 VCC(+5V),为传感器提供电源。
- 将 GND 引脚连接到 51 单片机的 GND,形成电源回路。
- 信号连接:
- 将 Trig 引脚连接到 51 单片机的一个普通 I/O 引脚,例如 P36,用于向传感器发送触发信号。
- 将 Echo 引脚连接到 51 单片机的另一个普通 I/O 引脚,例如 P37,用于接收传感器返回的回响信号。
工作原理
HC - SR04 超声波传感器的工作原理是通过发射超声波并测量其从发射到遇到障碍物反射回来的时间,再根据声速计算出与障碍物之间的距离。具体步骤如下:
- 单片机向 Trig 引脚发送一个至少 10μs 的高电平脉冲,触发传感器发射超声波。
- 传感器发射超声波后,Echo 引脚会变为高电平,并开始计时。
- 当超声波遇到障碍物反射回来被传感器接收到时,Echo 引脚变为低电平,计时结束。
- 根据 Echo 引脚高电平的持续时间 t,利用公式 d=v×t/2(其中 v 为声速,常温下约为 340m/s)计算出与障碍物之间的距离 d。
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
// 定义 Trig 和 Echo 引脚
sbit Trig = P3^6;
sbit Echo = P3^7;
void delay(uint z)
{
uchar j;
for(z;z>0;z--)
for(j=2;j>0;j--);
}
// 测量距离函数
float measure_distance() {
unsigned int time;
float distance;
// 发送至少 10μs 的高电平脉冲
Trig = 0;
Trig = 1;
delay(10);
Trig = 0;
// 等待 Echo 引脚变为高电平
while(Echo == 0);
// 开启定时器 0 开始计时
TH0 = 0;
TL0 = 0;
TR0 = 1;
// 等待 Echo 引脚变为低电平
while(Echo == 1);
// 关闭定时器 0 停止计时
TR0 = 0;
// 获取定时器 0 的计数值
time = TH0 * 256 + TL0;
// 根据计数值计算距离
distance = (float)time * 0.017; // 声速 340m/s,转换为 cm/μs 为 0.034,再除以 2
return distance;
}
void main() {
float distance;
// 初始化定时器 0
TMOD = 0x01; // 定时器 0 工作在模式 1
while(1) {
// 测量距离
distance = measure_distance();
// 这里可以添加将距离值显示在数码管或通过串口发送出去的代码
// distance == 100时, 经测试89C52单片机上大概30厘米距离
if(distance >100)
P1 = 0x77;
else
{
P1 = 0x11;
}
// 延时一段时间后再次测量
delay(10000);
}
}
5、舵机控制
51单片机通过PCA9685控制舵机是一种常见的硬件控制方案,PCA9685是一个16通道的PWM(脉宽调制)控制器,可以同时控制多个舵机。以下是实现这一功能的详细步骤和注意事项:
硬件连接
- 51单片机与PCA9685:通过I2C总线连接。
- SDA(数据线)连接到51单片机的P2.0。
- SCL(时钟线)连接到51单片机的P2.1。
- VCC和GND分别连接到电源和地。VCC接入的是5V
- V+也需要接入一个5V的电压
- PCA9685与舵机:
- PCA9685的PWM输出引脚连接到舵机的舵机SG90PWM信号线(通常是黄色线)。
- 舵机SG90的电源(红色线)和地(棕色线)连接到外部电源(注意电压匹配)。
PCA9685配置
- I2C地址:PCA9685默认I2C地址为0x40,可以通过硬件引脚调整。
- PWM频率:舵机通常需要50Hz的PWM信号(周期20ms),可以通过设置PCA9685的预分频器实现。
- PWM占空比:舵机的角度由PWM信号的占空比控制,通常占空比范围为2.5%(0度)到12.5%(180度)。
51单片机程序实现
以下是51单片机通过I2C控制PCA9685的基本代码框架:
#include <reg52.h>
#include <intrins.h>
// PCA9685 相关定义
#define PCA9685_ADDR 0x40 // PCA9685的I2C地址
#define MODE1 0x00 // PCA9685模式寄存器1
#define PRE_SCALE 0xFE // 频率预分频寄存器
#define LED0_ON_L 0x06 // LED0的PWM起始低字节寄存器
// I2C引脚定义
sbit SDA = P2^0; // I2C数据线
sbit SCL = P2^1; // I2C时钟线
// I2C起始信号
void I2C_Start() {
SDA = 1; SCL = 1; _nop_();
SDA = 0; _nop_();
SCL = 0;
}
// I2C停止信号
void I2C_Stop() {
SDA = 0; SCL = 1; _nop_();
SDA = 1; _nop_();
}
// I2C写一个字节
void I2C_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
SDA = (dat & 0x80) ? 1 : 0;
SCL = 1; _nop_();
SCL = 0; _nop_();
dat <<= 1;
}
SDA = 1; SCL = 1; _nop_();
SCL = 0;
}
// 向PCA9685寄存器写入数据
void PCA9685_WriteReg(unsigned char reg, unsigned char dat) {
I2C_Start();
I2C_WriteByte(PCA9685_ADDR << 1);
I2C_WriteByte(reg);
I2C_WriteByte(dat);
I2C_Stop();
}
// 设置PCA9685的PWM输出
void PCA9685_SetPWM(unsigned char channel, unsigned int on, unsigned int off) {
PCA9685_WriteReg(LED0_ON_L + 4 * channel, on & 0xFF);
PCA9685_WriteReg(LED0_ON_L + 4 * channel + 1, on >> 8);
PCA9685_WriteReg(LED0_ON_L + 4 * channel + 2, off & 0xFF);
PCA9685_WriteReg(LED0_ON_L + 4 * channel + 3, off >> 8);
}
// 初始化PCA9685
void PCA9685_Init() {
PCA9685_WriteReg(MODE1, 0x10); // 进入睡眠模式
PCA9685_WriteReg(PRE_SCALE, 121); // 设置50Hz频率
PCA9685_WriteReg(MODE1, 0x80); // 退出睡眠模式
PCA9685_WriteReg(MODE1, 0x20); // 使能自动递增模式
}
// 设置舵机角度
void Set_Servo_Angle(unsigned char channel, float angle) {
unsigned int pulse_width;
pulse_width = (unsigned int)(150 + angle * 2.5); // 150对应0度,600对应180度
PCA9685_SetPWM(channel, 0, pulse_width);
}
// 延时函数(单位:毫秒)
void Delay_ms(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++) {
for (j = 0; j < 120; j++); // 基于11.0592MHz晶振的简单延时
}
}
// 主函数
void main() {
PCA9685_Init(); // 初始化PCA9685
while (1) {
unsigned int i;
// 控制云台底部的电机
for (i = 0; i < 90; ++i) {
Set_Servo_Angle(0, i); // 设置通道0的舵机角度
Delay_ms(15); // 延时15毫秒
}
Delay_ms(1500); // 延时1.5秒
for (i = 0; i < 90; ++i) {
Set_Servo_Angle(15, i); // 设置通道15的舵机角度
Delay_ms(15); // 延时15毫秒
}
Delay_ms(1500); // 延时1.5秒
for (i = 90; i > 0; --i) {
Set_Servo_Angle(15, i); // 设置通道15的舵机角度
Delay_ms(15); // 延时15毫秒
}
for (i = 90; i > 0; --i) {
Set_Servo_Angle(0, i); // 设置通道0的舵机角度
Delay_ms(15); // 延时15毫秒
}
Delay_ms(1500); // 延时1.5秒
}
}
6、控制马达
连接要点
电机连接
- OUT1 和 OUT2:将TT马达的正负极分别连接到L298N的OUT1和OUT2引脚。
- 极性正确:确保TT马达的正负极与L298N的输出引脚对应,避免反转。
电源连接
- 电压匹配:确保外部电源电压与TT马达的额定电压匹配。
- L298N的电源使用外部电源(有个地方要特别注意,接入+12伏是接入,+5是输出。电源输入不要(记住不要)接入+5V上)
- 单片机也可以独立供电
51单片机与L298N的连接
- P1.0 和 P1.1:分别连接到L298N的IN1和IN2引脚,用于控制TT马达的正反转。
- P1.2:连接到L298N的ENA引脚,用于控制TT马达的转速(PWM信号)。
- VCC:将L298N的VCC(+12v)引脚连接到外部电源(通常为5V~12V)。
- GND:将L298N的GND引脚连接到电源地,将L298N的GND需要和单片机的GND接起来。(注意,必须要接,否则不转)。
- 51单片机电源:将51单片机的VCC和GND引脚分别连接到电源和地
注意事项
- 共地连接:确保L298N、TT马达和外部电源的地线(GND)连接在一起。
- 接入+12伏接口是输入(输入电压5-12即可,实测7.4v锂电池可运行)
#include <reg52.h>
// 定义 L298N 控制引脚
sbit IN1 = P1^0; // 控制马达正反转的引脚1
sbit IN2 = P1^1; // 控制马达正反转的引脚2
sbit ENA = P1^2; // 控制马达转速的PWM使能引脚
// 定义 PWM 占空比
unsigned char pwm_duty = 30; // 初始占空比为30%
// 初始化定时器 0
void init_timer0() {
TMOD &= 0xF0; // 清除定时器 0 的模式位
TMOD |= 0x01; // 设置定时器 0 为模式 1(16 位定时器)
TH0 = 0xFF; // 设置定时器初值,定时 500us
TL0 = 0x12;
ET0 = 1; // 使能定时器 0 中断
EA = 1; // 使能全局中断
TR0 = 1; // 启动定时器 0
}
// 定时器 0 中断服务程序
void timer0_isr() interrupt 1 {
static unsigned int count = 0;
TH0 = 0xFF; // 重新加载定时器初值
TL0 = 0x12;
count++;
if (count < pwm_duty) {
ENA = 1; // PWM高电平,马达运行
} else {
ENA = 0; // PWM低电平,马达停止
}
if (count >= 100) {
count = 0; // 重置计数器
}
}
// 控制马达正转
void motor_forward() {
IN1 = 1; // 设置IN1为高电平
IN2 = 0; // 设置IN2为低电平
}
// 控制马达反转
void motor_backward() {
IN1 = 0; // 设置IN1为低电平
IN2 = 1; // 设置IN2为高电平
}
// 停止马达
void motor_stop() {
IN1 = 0; // 设置IN1为低电平
IN2 = 0; // 设置IN2为低电平
}
// 延时函数
void delay(unsigned int time) {
unsigned int i, j;
for (i = 0; i < time; i++) {
for (j = 0; j < 123; j++); // 简单延时循环
}
}
// 主函数
void main() {
// 初始化定时器 0
init_timer0();
while (1) {
// 正转 3 秒
motor_forward();
pwm_duty = 80; // 设置占空比为80%,提高转速
delay(6000); // 延时 3 秒
// 停止 1 秒
motor_stop();
delay(1000); // 延时 1 秒
// 反转 2 秒
motor_backward();
pwm_duty = 60; // 设置占空比为60%,降低转速
delay(2000); // 延时 2 秒
// 停止 1 秒
motor_stop();
delay(1000); // 延时 1 秒
}
}
7、红外模块控制
基于51单片机的红外探测代码示例,使用C语言编写。该代码通过红外传感器检测障碍物,并通过LED灯或蜂鸣器进行反馈。假设红外传感器连接到P3.2引脚,LED连接到P1.0引脚。
#include <reg51.h>
sbit IR_Sensor = P3^2; // 红外传感器连接到P3.2
sbit LED = P2^0; // LED连接到P2.0
void delay(unsigned int time) {
unsigned int i, j;
for (i = 0; i < time; i++)
for (j = 0; j < 120; j++);
}
void main() {
while (1) {
if (IR_Sensor == 1) { // 检测到障碍物(红外传感器输出低电平)
LED = 1; // 点亮LED
} else {
LED = 0; // 无障碍物时,LED保持熄灭
}
}
}