C51实现串口控制两个舵机转动角度,并显示在LCE1602上面

目录

硬件准备:

软件环境:

作品现象:

实物:

仿真:

硬件连线:

引脚说明

代码实现


硬件准备:

一个C51单片机

两个舵机(SG90)

一个USB转TTL

软件环境:

Keil5,STC-ISP,Proteus 8 Professional,Configure Virtual Serial Port Driver

作品现象:

实物:

通过电脑串口输入转动角度来分别控制两个舵机的转动角度,输入一个数据控制第一个舵机,输入第二个数据控制第二个舵机角度,循环往复

仿真:

通过虚拟串口实现在仿真上的串口发送接收

硬件连线:

引脚说明

C51Servo1
P2^3信号线(黄色)
C51servo2
P2^4信号线(黄色)

棕色是GND接C51的GND,红色是VCC接C51的VCC(SG90需要5V电压,驱动多个舵机时,C51的5V引脚不够,①要么单独供电,②要么使用面包板之类的将5V引脚增多,③要么使用舵机驱动板,如PCA9685舵机驱动模块,否则舵机要么带不动,要么会发生抽搐)

C51LCD1602
RWP2^5
RSP2^6
EP2^7
GNDVSS、V0
P1^0D0
P1^1D1
P1^2D2
P1^3D3
P1^4D4
P1^5D5
P1^6D6
P1^7D7
VCCA
GNDK
VCCVDD

C51USB转TTL
GNDGND
P3^0TX
P3^1RX
VCCVCC

串口通信中是(TX连RX;RX连TX

代码实现

main.c

#include <reg51.h>
#include "LCD1602.h"

// 定义舵机控制引脚
sbit servo1 = P2^3;
sbit servo2 = P2^4;

// 定义全局变量
unsigned char angle1 = 0;  // 舵机1的角度
unsigned char angle2 = 0;  // 舵机2的角度
unsigned char count = 0;   // 定时器计数

// 函数声明
void UART_Init();
void Timer0_Init();
void delay_ms(unsigned int ms);
void set_angle(unsigned char *angle, unsigned char new_angle);

// 主函数
void main() {
    UART_Init();  // 初始化串口
    Timer0_Init();// 初始化定时器0
    EA = 1;       // 全局中断使能

    LCD_Init(); // 初始化 LCD1602

    while(1) {
        // 显示舵机1的角度
        LCD_ShowString(1, 1, "Servo1:");
        LCD_ShowNum(1, 8, angle1, 3);

        // 显示舵机2的角度
        LCD_ShowString(2, 1, "Servo2:");
        LCD_ShowNum(2, 8, angle2, 3);

        delay_ms(100); // 适当延时,避免刷新过快
    }
}

// 串口初始化函数
void UART_Init() {
    TMOD |= 0x20;  // 设置定时器1为模式2
    TH1 = 0xFD;    // 波特率9600
    TL1 = 0xFD;
    SCON = 0x50;   // 8位数据,可变波特率
    TR1 = 1;       // 启动定时器1
    ES = 1;        // 使能串口中断
}

// 定时器0初始化函数
void Timer0_Init() {
    TMOD |= 0x01;  // 设置定时器0为模式1
    TH0 = 0xFF;    // 定时器初值,定时约100us
    TL0 = 0x9C;
    ET0 = 1;       // 使能定时器0中断
    TR0 = 1;       // 启动定时器0
}

// 串口中断服务函数
void UART_ISR() interrupt 4 {
    static unsigned char servo_select = 0;  // 新增变量,用于选择控制的舵机
    if (RI) {
        RI = 0;    // 清除接收中断标志
        if (SBUF < 181) {  // 确保角度在0-180度范围内
            if (servo_select == 0) {
                angle1 = SBUF;
                servo_select = 1;  // 下次控制舵机2
            } else {
                angle2 = SBUF;
                servo_select = 0;  // 下次控制舵机1
            }
            SBUF = SBUF;   // 回显接收到的数据
            while (!TI);   // 等待发送完成
            TI = 0;        // 清除发送中断标志
        }
    }
}

// 定时器0中断服务函数
void Timer0_ISR() interrupt 1 {
    TH0 = 0xFF;    // 重新加载定时器初值
    TL0 = 0x9C;
    count++;

    if (count < 200) {  // 20ms周期
        if (count < (angle1 / 9 + 5)) {  // 计算占空比
            servo1 = 1;
        } else {
            servo1 = 0;
        }
        if (count < (angle2 / 9 + 5)) {  // 计算占空比
            servo2 = 1;
        } else {
            servo2 = 0;
        }
    } else {
        count = 0;
    }
}

// 延时函数
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 123; j++);
    }
}

// 设置舵机角度函数
void set_angle(unsigned char *angle, unsigned char new_angle) {
    if (new_angle < 181) {
        *angle = new_angle;
    }
}

LCD1602.c代码如下

#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P1

//函数定义:
/**
  * @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);//光标复位,清屏
}

/**
  * @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_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

实物演示如下:(在STC-ISP串口助手中发送转动角度即可,注意C51接收的是16进制,即发送的角度会被转化为16进制的角度)

串口控制舵机转动角度

仿真如下:

在Protues中虚拟串口的位置如下:

演示视频如下:

虚拟串口仿真

虚拟串口要想实现,需要先配置虚拟串口,利用Configure Virtual Serial Port Driver软件(确保自己没有安装过该环境,否则再安装一次会发生奇奇怪怪的问题)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值