Proteus仿真51单片机控制L298N PWM电机调速
本文介绍了51单片机的核心知识及其与L298N电机驱动模块的应用。主要内容包括:1)51单片机概述,重点说明其8位CPU结构、存储器配置和典型型号;2)L298N驱动模块特性,详述其双H桥设计、引脚功能及电机控制逻辑;3)系统实现方案,包含最小系统电路设计、Proteus仿真及PWM调速代码实现(含LCD显示)。通过8051与L298N的配合,可精准控制直流电机转速和转向,适用于智能小车等嵌入式项
51单片机概述
51单片机是指Intel公司开发的MCS-51系列单片机及其兼容产品,是应用最广泛的8位单片机系列之一。其名称来源于该系列的第一款芯片8051。
主要特点:
-
8位CPU:处理数据宽度为8位
-
哈佛结构:程序存储器和数据存储器分开
-
40引脚DIP封装:标准双列直插式封装
-
4KB ROM:早期型号,现代兼容型号容量更大
-
128B RAM:内部数据存储器
-
32个I/O口:4个8位并行I/O端口
-
2个16位定时器/计数器
-
1个全双工串行通信口
-
5个中断源:2个外部中断,2个定时器中断,1个串口中断
常见51单片机型号
| 型号 | 主要特性 |
|---|---|
| 8051 | 原始型号,4KB ROM,128B RAM |
| 8031 | 无片内ROM |
| 89C51 | Atmel公司产品,Flash ROM |
| 89S51 | 支持ISP在线编程 |
| STC89C52 | 宏晶科技产品,8KB Flash,512B RAM |
| STC12C5A60S2 | 增强型51,1T周期,60KB Flash,1280B RAM |
51单片机内部结构
1. CPU结构
-
算术逻辑单元(ALU):执行算术和逻辑运算
-
累加器(ACC):主要寄存器,用于运算和数据传输
-
B寄存器:用于乘除法运算
-
程序状态字(PSW):包含各种状态标志位
-
程序计数器(PC):16位,指向下一条要执行的指令
2. 存储器结构
-
程序存储器(ROM/Flash):存储程序代码
-
内部数据存储器(RAM):128字节(52系列为256字节)
-
特殊功能寄存器(SFR):控制各功能模块
-
外部扩展存储器:可扩展至64KB
3. 输入输出端口
-
P0口:8位开漏双向I/O口,兼作地址/数据总线
-
P1口:8位准双向I/O口
-
P2口:8位准双向I/O口,兼作高8位地址总线
-
P3口:8位准双向I/O口,具有第二功能
51单片机最小系统
一个能工作的51单片机最小系统需要:
-
单片机芯片(如STC89C52)
-
时钟电路:通常11.0592MHz晶振+两个30pF电容
-
复位电路:10kΩ电阻+10μF电容构成上电复位
-
电源电路:5V稳定电源,加滤波电容
L298N介绍

L298N是一种双H桥电机驱动芯片,能够驱动两个直流电机或一个步进电机,广泛应用于机器人、智能小车等项目中。
主要特性
-
工作电压:5V-35V
-
最大电流:2A(单桥)
-
逻辑电压:5V(兼容TTL电平)
-
驱动方式:双H桥驱动
-
可同时控制:2个直流电机或1个两相/四相步进电机
模块引脚说明

电源部分
-
+12V/VCC:电机驱动电源输入(5-35V)
-
GND:电源地
-
+5V:输出5V(当使能跳线帽接通时)或5V输入(当断开跳线帽时)
控制部分
-
ENA/ENB:PWM使能端(A/B通道)
-
IN1/IN2:A通道控制输入端
-
IN3/IN4:B通道控制输入端
与51单片机的连接方式
51单片机引脚连接L298N: - P1.0 → L298N ENA (PWM输入A) - P1.1 → L298N IN1 - P1.2 → L298N IN2 - (如果需要控制第二个电机) - P1.3 → L298N ENB (PWM输入B) - P1.4 → L298N IN3 - P1.5 → L298N IN4
电机控制逻辑
单个直流电机控制
| IN1 | IN2 | ENA | 电机状态 |
|---|---|---|---|
| 0 | 0 | X | 停止 |
| 1 | 0 | PWM | 正转(PWM调速) |
| 0 | 1 | PWM | 反转(PWM调速) |
| 1 | 1 | X | 刹车 |
PWM调速原理
通过改变ENA/ENB引脚PWM信号的占空比来调节电机速度:
-
占空比高 → 平均电压高 → 转速快
-
占空比低 → 平均电压低 → 转速慢
典型应用电路
-
电源部分:
-
电机电源与单片机电源共地
-
建议电机电源与逻辑电源分开供电
-
大容量滤波电容靠近L298N电源引脚
-
-
保护电路:
-
电机两端并联续流二极管
-
必要时添加散热片
-
proteus仿真图

代码实现
主函数
#include <REGX52.H>
#include <intrins.h>
#include "LCD1602.h"
#include "public.h"
#include "stdio.h"
char war[32];
sbit IN1 = P1^1;
sbit IN2 = P1^2;
sbit ENA = P1^0; // PWM输出引脚
unsigned char PWM = 0; // PWM占空比 0-100
bit DIR = 0; // 方向 0或1
void Timer0Init() // 100us@11.0592MHz
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x02; // 8位自动重装模式
TH0 = 0xA4; // 重装值
TL0 = 0xA4; // 初始值
TR0 = 1; // 启动定时器
ET0 = 1; // 使能定时器中断
EA = 1; // 开启总中断
}
void main()
{
Timer0Init();
LCD_Init();
LCD_Clear();
IN1 = 1;
IN2 = 0; // 初始方向
PWM =20;
while(1)
{
// 这里可以添加按键扫描或其他控制逻辑来改变PWM值
// 例如:
// if(按键加按下) PWM++;
// if(按键减按下) PWM--;
// if(按键方向按下) DIR = !DIR;
if(P1_4 ==0){Delay_ms(100);if(P1_4 ==0) { PWM++; }}
if(P1_5 ==0){Delay_ms(100);if(P1_5 ==0) { PWM--; }}
if(P1_6 ==0){Delay_ms(100);if(P1_6 ==0) { DIR = !DIR; }}
// 限制PWM范围
if(PWM > 100) PWM = 100;
if(PWM < 0) PWM = 0;
// 设置方向
if(DIR)
{
IN1 = 1;
IN2 = 0;
}
else
{
IN1 = 0;
IN2 = 1;
}
sprintf(war,"PWM:%03d DIR:%d",(int)PWM,(int)DIR);
LCD_ShowString(2,1,war);
}
}
void Timer0_ISR() interrupt 1
{
static unsigned char count = 0;
count++;
if(count >= 100)
{
count = 0;
}
if(count < PWM)
{
ENA = 1; // 高电平期间电机通电
}
else
{
ENA = 0; // 低电平期间电机断电
}
}
LCD1602.c
#include <REGX52.H>
//引脚配置:
//sbit LCD_RS=P1^2;
//sbit LCD_RW=P1^3;
//sbit LCD_EN=P1^4;
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
void LCD_Clear()
{
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
///**
// * @brief 返回值=X的Y次方
// */
//int LCD_Pow(int X,int Y)
//{
// unsigned char i;
// int Result=1;
// for(i=0;i<Y;i++)
// {
// Result*=X;
// }
// return Result;
//}
///**
// * @brief 在LCD1602指定位置开始显示所给数字
// * @param Line 起始行位置,范围:1~2
// * @param Column 起始列位置,范围:1~16
// * @param Number 要显示的数字,范围:0~65535
// * @param Length 要显示数字的长度,范围:1~5
// * @retval 无
// */
//void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
//{
// unsigned char i;
// LCD_SetCursor(Line,Column);
// for(i=Length;i>0;i--)
// {
// LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
// }
//}
///**
// * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
// * @param Line 起始行位置,范围:1~2
// * @param Column 起始列位置,范围:1~16
// * @param Number 要显示的数字,范围:-32768~32767
// * @param Length 要显示数字的长度,范围:1~5
// * @retval 无
// */
//void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
//{
// unsigned char i;
// unsigned int Number1;
// LCD_SetCursor(Line,Column);
// if(Number>=0)
// {
// LCD_WriteData('+');
// Number1=Number;
// }
// else
// {
// LCD_WriteData('-');
// Number1=-Number;
// }
// for(i=Length;i>0;i--)
// {
// LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
// }
//}
///**
// * @brief 在LCD1602指定位置开始以十六进制显示所给数字
// * @param Line 起始行位置,范围:1~2
// * @param Column 起始列位置,范围:1~16
// * @param Number 要显示的数字,范围:0~0xFFFF
// * @param Length 要显示数字的长度,范围:1~4
// * @retval 无
// */
//void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
//{
// unsigned char i,SingleNumber;
// LCD_SetCursor(Line,Column);
// for(i=Length;i>0;i--)
// {
// SingleNumber=Number/LCD_Pow(16,i-1)%16;
// if(SingleNumber<10)
// {
// LCD_WriteData(SingleNumber+'0');
// }
// else
// {
// LCD_WriteData(SingleNumber-10+'A');
// }
// }
//}
///**
// * @brief 在LCD1602指定位置开始以二进制显示所给数字
// * @param Line 起始行位置,范围:1~2
// * @param Column 起始列位置,范围:1~16
// * @param Number 要显示的数字,范围:0~1111 1111 1111 1111
// * @param Length 要显示数字的长度,范围:1~16
// * @retval 无
// */
//void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
//{
// unsigned char i;
// LCD_SetCursor(Line,Column);
// for(i=Length;i>0;i--)
// {
// LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
// }
//}
lcd1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_Clear();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
//void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
//void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
//void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
//void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
#ifndef _public_H
#define _public_H
#include "REGX52.h"
#define u16 unsigned int //对系统默认数据类型进行重定义
#define u8 unsigned char
void Delay_us(u16 ten_us);
void Delay_ms(u16 ms);
#endif
#include "public.h"
/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void Delay_us(u16 ten_us)
{
while(ten_us--);
}
/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时函数,ms=1时,大约延时1ms
* 输 入 : ms:ms延时时间
* 输 出 : 无
*******************************************************************************/
void Delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
效果演示

更多推荐



所有评论(0)