代码:
链接:https://pan.baidu.com/s/1K9dg2NwRhy49db_O_hqv-g?pwd=1234
提取码:1234
一、路线
我在了解这个舵机之前最像想看到的是一个完全的路径。
比如我想学习b站上那个智能门锁,那就得每个模块的基本代码都会才能结合各个部分。那你第一步就是准备好硬件和软件环境,如果你备赛过蓝桥杯杯比赛的话,软件和基本代码知识这些不用说,硬件的话
1.去购物软件搜一下SG90舵机,一大堆,你想买哪个就买哪个;
2.找商家要资料,里面有参考代码,这对于快速上手很有帮助;
3.结合之前蓝桥杯比赛的代码知识,然后在csdn上搜原理是什么,笔者没附上原理讲解是因为别人写好的文章真的可以碾压我,适应别人的写法,然后自己重新默写一遍;
4.最最有意思的一步就是将你的智能门锁加上这个功能,门锁的第一步就是通过按键输入密码开门,你可以加上密码成功输入后就让舵机转过特定角度来开门。
路径就是这么个路径,其中笔者感觉难在你要复习之前的知识,不过很快上手就是了。
二、代码上的一些分享
新建工程
调用江科大写好的代码
LCD1602.c
#include <REGX52.H>
//引脚配置:
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);//光标复位,清屏
}
/**
* @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
实验现象:
按键部分
也可以直接使用江科大的代码,毕竟对按键没有时间上的精确需求。所以用while卡死的方法来操作的话确实是会卡死,看看后面会不会有好方法来解决吧。
key.c
#include "key.h"
unsigned char key_read(void)
{
unsigned char key_val=17;
P1=0xff;
P1_3=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);key_val=1;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);key_val=5;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);key_val=9;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);key_val=13;}
P1=0xff;
P1_2=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);key_val=2;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);key_val=6;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);key_val=10;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);key_val=14;}
P1=0xff;
P1_1=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);key_val=3;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);key_val=7;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);key_val=11;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);key_val=15;}
P1=0xff;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);key_val=4;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);key_val=8;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);key_val=12;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);key_val=16;}
return key_val;
}
key.h
#include <REGX52.H>
#include "tim.h"
unsigned char key_read(void);
舵机部分
这部分的原理和代码原理可以直接在csdn上搜到很详细的讲解,笔者就不在原理讲解上献丑了,商家也会给参考代码。注释乱码想看的话从百度网盘下笔者的来看吧
sg90.c
#include "sg90.h"
//ÏëҪдÕâÑùµÄÒ»¸öº¯Êý
//ûÓзµ»ØÖµ ²ÎÊýÊǽǶȣ¬Ö´ÐÐÖ®ºó¶æ»úת¶¯µ½ÒªÇóµÄ½Ç¶È£¬
//´ó¸Å¹ý¸öÒ»ÃëÖ®ºó£¬»Øµ½Áã¶È
sbit motor_pin=P2^0;
unsigned char pwm_count;
unsigned char pwm_vol;
void Timer0_Isr(void) interrupt 1
{
TL0 = (65526-100)%256;
TH0 = (65526-100)/256;//100΢ÃîÒç³öÒ»´Î
pwm_count++;
if(pwm_count>=100)
pwm_count=0;
if(pwm_count<=pwm_vol)
motor_pin=1;
else
motor_pin=0;
}
void sg90_init(void)//³õʼ»¯£¬Èöæ»ú¸´Î»
{
motor_pin=0;
Timer0_Init();
pwm_vol=4;
Delay(600);
}
void sg90_proc(unsigned char angle)//angle>=0&&angle<=180
{
//pwm_vol=5 45
//pwm_vol=10 90
//pwm_vol=15 135
//pwm_vol=20 180
if(angle!=180)
pwm_vol=angle/9+1;//ÓÐÎó²îËùÒÔ¼Ó¸ö1
else
pwm_vol=18;//µ«Êǵ½180¶ÈµÄ»°²»Äܳ¬¹ý20£¬¾¹ýµ÷ÊÔ18£¬¸Õ¸ÕºÃ
Delay(1000);
pwm_vol=4;//¸´Î»
Delay(600);
}
sg90.h
#include <REGX52.H>
#include "tim.h"
void sg90_proc(unsigned char angle);
void sg90_init(void);
tim.c
现在定时器配置好像都是直接用手动重装载来得方便。定时器1留给你们自己玩,目前是用不到的。
#include "tim.h"
void Delay(unsigned int xms) //@12.000MHz 1ms
{
unsigned char data i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void Timer1_Init(void) //1??@12.000MHz
{
TMOD &= 0x0F; //???????
TL1 = 0x18; //???????
TH1 = 0xFC; //???????
TF1 = 0; //??TF1??
TR1 = 1; //???1????
ET1 = 1; //?????1??
EA = 1;
}
void Timer0_Init(void) //100??@12.000MHz
{
TMOD &= 0xF0; //???????
TMOD |= 0x01;
TL0 = (65526-100)%256;
TH0 = (65526-100)/256;
TF0 = 0; //??TF0??
TR0 = 1; //???0????
ET0 = 1; //?????0??
EA = 1;
}
tim.h
#include <REGX52.H>
void Delay(unsigned int xms);
void Timer1_Init(void);
void Timer0_Init(void);
main文件部分
main.c
//Í·ÎļþÓÃÕâ¸ö¾Í¹»ÓÃÁ˸оõ£¬µ½Ê±²»¹»ÓÃÔÙÓÃÄǸöf2k60s2µÄÍ·Îļþ
#include <REGX52.H>
#include "LCD1602.h"
#include "key.h"
#include "sg90.h"
#include "tim.h"
//º¯ÊýÉùÃ÷
void Proc(void);
void Lcd_proc(void);
//±äÁ¿
unsigned char key_num;//¼üÂëÖµ
unsigned long password;//ÃÜÂëÖµ
unsigned char count;//λÊý³¬¹ýÁùλ×Ô¶¯ÖØÐÂÊäÈë
unsigned char mode;//ģʽ±äÁ¿
bit bit1=0;
//Ö÷º¯Êý
void main(void)
{
LCD_Init();
LCD_ShowString(1,1,"Smart Home");
LCD_ShowString(2,1,"S13-go to Input");
sg90_init();
while(1)
{
Lcd_proc();
Proc();
}
}
void Proc(void)
{
key_num=key_read();
if(key_num==13)//´ú±í'*'¼ü
{
mode=1;
}
if(mode==1)
{
if(key_num<10)
{
if(count<5)//ÃÜÂë²»³¬¹ýÎåλ
{
password*=10;//×óÒÆ
password+=key_num%10;
count++;
}
else
{
LCD_ShowNum(2,1,password,5);//³¬¹ýÎåλ֮ºó¾ÍÒ»Ö±ÏÔʾÊäÈëºÃµÄÃÜÂë
}
}
LCD_ShowNum(2,1,password,5);
if(key_num==14)//´ú±í¡°È·ÈÏ¡±¼ü
{
if(count==5&&password==12345)//ÃÜÂë׼ȷ
{
bit1=1;//±ê־λÖÃ1£¬
count=password=0;//¸´Î»
mode=2;//2ģʽ
LCD_ShowNum(2,1,password,5);//Ë¢ÐÂÃÜÂëÏÔʾ
}
else if(count==5&&password!=12345)//ÃÜÂë´íÎó
{
count=password=0;
mode=3;
LCD_ShowNum(2,1,password,5);//Ë¢ÐÂÃÜÂëÏÔʾ
}
}
}
}
//дÕâ¸öº¯ÊýÊǸúÀ¶Çű±¸ÈüµÄģʽ±äÁ¿·¨½áºÏÆðÀ´£¬¸Ð¾õÏÔʾºÍÅж϶ÀÁ¢¿ªÀ´»¹ÊDZȽϺõÄ
void Lcd_proc(void)
{
switch(mode)
{
case 0:
LCD_ShowString(1,1,"Smart Home ");
LCD_ShowString(2,1,"S13-go to Input");
break;
case 1:
LCD_ShowString(1,1,"Input Password");
LCD_ShowString(2,6," ");//°Ñ֮ǰÏÔʾµÄÄÚÈÝÇåÆÁ
break;
case 2:
if(bit1==1)//Èç¹ûÃÜÂë׼ȷ
{
LCD_ShowString(1,1,"Input Right! ");
bit1=0;//¸´Î»
sg90_proc(180);//¿ØÖƶæ»úת¶¯180¶È
}
LCD_ShowString(1,1,"Welcome Home! ");
Delay(1000);//ÔÚÏÔʾ»¶Ó»Ø¼ÒÖ®ºó¾Í»Øµ½ÆÁ±£½çÃæģʽ
mode=0;
break;
case 3:
LCD_ShowString(1,1,"Input Error! ");
Delay(1000);//ÔÚÏÔʾ»¶Ó»Ø¼ÒÖ®ºó¾Í»Øµ½ÆÁ±£½çÃæģʽ
mode=0;
break;
}
}
总结
简单说明一下实验现象:
1.上电之后默认处于屏保界面正常显示;
2.S13按下进入输密码模式,界面切换,第二行显示五个0表示五位密码;
3.1~9输入密码,五位输入完毕再输入无效,直到按下S14按键进行密码准确性判断;
4.如果密码正确,(密码默认为12345)LCD屏幕上先显示“Input Right!"再显示欢迎回家一秒后,回到屏保界面可以重新再次输入;
5.如果密码错误,也会在一秒之后返回屏保界面;
问题:舵机转动时会影响屏幕显示。
待开发的功能:
1.输入错误超过某个数值,让其处于屏保界面五秒后才能再次输入;
2.密码输入可以从屏幕左边开始输入,且不会显示五个0,而是输入一位就用一位数字覆盖空格,输入到哪一位,哪一位就有光标在闪烁;密码为六位;
3.屏保界面右上角显示温度值;
4.不同模式不同指示灯,正确指示灯常亮,错误闪烁;(蜂鸣器太吵了,虽说也可以);
5.加入其他模块;(ic卡,蓝牙。。。)
如若有代码更新,笔者会第一时间发出来。