蓝牙控制51单片机程序

2015-09-30 21:59:18 qq_21792169 阅读数 7506

这个是我大二才学51单片机的时候做的一个项目,其实这个项目比较简单,只不过是我们自己把它想得太难了,下面我来总结一下具体设计过程,我会上传代码和研究报告:点击这里!

首先买一个蓝牙模块,我买的是HC-06,在手机上下载一个蓝牙串口调试助手,等同于单片机的UART串口通信,利用BUFF来获取接受到的数据,这里只需要重视一个问题,就是测试编码,如果APP发送0xFF,(APP蓝牙串口调试助手已经支持手动设置按钮名字和发送的字符了)但是蓝牙模块很有可能接受到的是0xF8,0xF2,0xFE。所以这里我们首先应该测试编码。HC-06一般有5个端口,RX,TX,VCC,GND,AT,AT这里我们没有用到,是指更改密码,默认密码一般是1234或者0000,把VCC和GND接好,然后RX和TX分别和单片机的TX和RX相连接(就是P3^0和P3^1,切记要相反连接),然后我们按照一般的UART串口通信就可以啦,检测接受到BUFF数据,然后switch就可以实现相应的功能。



#include <reg51.h>
#define  uchar  unsigned char
#define  uint   unsigned int


sbit PWM=P1^0;
sbit DSPORT=P3^7;


 void Ds18b20ReadTemp();
 void UsartConfiguration();


 unsigned int temp=0;
 unsigned char zhuanshu=20;
 unsigned int cnt=0;
 unsigned char receiveData;
 unsigned int dingshi=0;
 unsigned char min=1;
 
void main(void)
{
    unsigned int i=3000;
    UsartConfiguration();
while(1)

if(cnt>=3000)
{
 cnt=0;
 Ds18b20ReadTemp(); //3s检测一次温度
}
switch(receiveData)
{
 case 177:ET0=1;zhuanshu=20;receiveData=0;break;          // 数字1 风扇启动
 case 178:zhuanshu=0;while(i--);ET0=0;receiveData=0;break;        //数字2 风扇停止 由于IO口默认高电平  所以要让zhuanshu=0
 case 179:zhuanshu=30;break;//数字3 低挡
 case 180:zhuanshu=50;break;//数字4 中挡
 case 181:zhuanshu=70;break;// 数字5高挡
 case 182:                 // 数字6自动根据温度调节转速 
 {
if(temp<3200)
{
   zhuanshu=25;
}
if((temp>3200)&&(temp<3300))
{
   zhuanshu=30;
}
if((temp>3400)&&(temp<3600))
{
   zhuanshu=50;
}
if((temp>3200)&&(temp<3300))
{
  zhuanshu=60;
}
} break;                   
case 183: //定时时间数字7
{
  static dingshi=0;
   if(dingshi>=60000)  //1分钟一个基准
  {
     min--;
 dingshi=0;
 if(min==0)
 {
  ET0=0;
zhuanshu=0;
 }
  }
  break;
 case 184: receiveData=0;min++;break; //增加时间  数字8
 case 185: receiveData=0;min--;break; //减少时间  数字9
}
}
  }
}
  void UsartConfiguration()
{
SCON=0X50; //设置为工作方式1
TMOD=0X21; //设置计数器工作方式2
PCON=0X80; //波特率加倍
TH0 = 0xFC; 
TL0 = 0x66;
TH1=0XF9;    //计数器初始值设置,注意波特率是9600的
TL1=0XF9;
ET0=1;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1;
TR0=1;    //打开计数器
}




  void Usart() interrupt 4
{
receiveData=SBUF; //出去接收到的数据
RI = 0;           //清除接收中断标志位
}


void Time1(void) interrupt 1    //3 为定时器1的中断号  1 定时器0的中断号 0 外部中断1 2 外部中断2  4 串口中断
{
    static unsigned char timer1=0;
TH0 = 0xF6;  //重新赋初值
TL0 = 0x66;
timer1++; 
    cnt++;
dingshi++;
if(dingshi>=60050)
{
 dingshi=0;
}
if(timer1>100)  //PWM周期为100*0.5ms
{
timer1=0;
}
if(timer1 <zhuanshu)//改变zhunashu这个值可以改变直流电机的速度这个值越大转的越快
{
   PWM=1;
}
else
{
  PWM=0;
}  
}


void Delay1ms(uint y) //延时程序
{
uint x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}


uchar Ds18b20Init()  //温度的子函数
{
uchar i;
DSPORT = 0; //将总线拉低480us~960us
i = 70;
while(i--);//延时642us
DSPORT = 1; //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
i = 0;
while(DSPORT) //等待DS18B20拉低总线
{
i++;
if(i>5)//等待>5MS
{
return 0;//初始化失败
}
Delay1ms(1);
}
return 1;//初始化成功
}






void Ds18b20WriteByte(uchar dat)
{
uint i, j;


for(j=0; j<8; j++)
{
DSPORT = 0;      //每写入一位数据之前先把总线拉低1us
i++;
DSPORT = dat & 0x01;  //然后写入一个数据,从最低位开始
i=6;
  while(i--); //延时68us,持续时间最少60us
DSPORT = 1; //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
dat >>= 1;
}
}






uchar Ds18b20ReadByte()
{
uchar byte, bi;
uint i, j;
for(j=8; j>0; j--)
{
DSPORT = 0;//先将总线拉低1us
i++;
DSPORT = 1;//然后释放总线
i++;
i++;//延时6us等待数据稳定
bi = DSPORT; //读取数据,从最低位开始读取
/*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
byte = (byte >> 1) | (bi << 7); 
i = 4; //读取完之后等待48us再接着读取下一个数
while(i--);
}
return byte;
}


void  Ds18b20ChangTemp()
{
Ds18b20Init();
Delay1ms(1);
Ds18b20WriteByte(0xcc);//跳过ROM操作命令 
Ds18b20WriteByte(0x44);   //温度转换命令
// Delay1ms(100);//等待转换成功,而如果你是一直刷着的话,就不用这个延时了
   
}




void  Ds18b20ReadTempCom()
{


Ds18b20Init();
Delay1ms(1);
Ds18b20WriteByte(0xcc);//跳过ROM操作命令
Ds18b20WriteByte(0xbe);//发送读取温度命令
}




void Ds18b20ReadTemp()
{
   float tp; 
int temp1 = 0;
uchar tmh, tml;
Ds18b20ChangTemp();//先写入转换命令
Ds18b20ReadTempCom();//然后等待转换完后发送读取温度命令
tml = Ds18b20ReadByte();//读取温度值共16位,先读低字节
tmh = Ds18b20ReadByte();//再读高字节
temp1 = tmh;
temp1 <<= 8;
temp1 |= tml;
   if(temp1< 0)
  {
temp1=temp1-1;
temp1=~temp1;
tp=temp1;
temp1=tp*0.0625*100+0.5;
  }
  else
  {
tp=temp1;
temp1=tp*0.0625*100+0.5;
}


  temp=temp1%10000;
   
}
2013-08-04 17:32:52 czjsky818 阅读数 12909

STC51单片机一般是通过串口线下载程序到MCU。但是,有时候单片机放在作品上,串口线不够长,不方便下载,或者频繁拔插单片机,也不方便。

本方法,通过笔记本蓝牙,蓝牙串口模块HC-05,即可实现STC51单片机无线下载程序到MCU。


需要以下准备:

  1. 有蓝牙模块的笔记本。

  2. 蓝牙串口模块HC-05

  3. STC51单片机

  4. STC-ISP V6.53(只要能任意选择COM口的都行。)

  5. USB转串口模块


第一步:

 打开笔记本蓝牙模块。首先你要确定你有蓝牙模块和装好相应的驱动,这里我就不详细展开了。不同笔记本打开方式不同。我的联想Y笔记本如图1:


第二步:

将USB转串口和HC-05的VCC,GND,相连,TXD接RXD,RXD接TXD。将USB转串口模块插入笔记本。此时HC-05指示灯应该是快速闪烁。

如上图,点击添加设备,搜索到HC-05后连接,输入设备的配对码 1234 ,如图2,


然后就连接成功,在 控制面板\硬件和声音\设备和打印机 里便可以看到HC-05了。在HC-05上右键,属性,硬件,便可以看到COM口号,COM28,如图3。



第三步:

将HC-05模块的PIO11置高(我买的模块有个按键),上电,模块便进入AT命令响应模式。此时HC-05指示灯应该是慢速地闪烁。

此时可以打开任意串口助手软件,如STC-ISP的串口助手,设置波特率38400,数据位8位,停止位1位,无校验位,无流控制。

串口发送字符: AT(后面要加个回车键,我之前不知道,以为没进入AT模式),成功则返回OK。

接下来设置HC-05工作模式:波特率9600,数据位8位,停止位1位,偶校验,无流控制。串口发送字符:AT+UART=9600,0,2

然后就可以关闭串口了。



第四步:

将HC-05插到单片机上,还是一样,VCC,GND,相连,TXD接RXD,RXD接TXD。

硬件部分就完成了。


第五步:

在单片机程序中加入ISP.c,ISP.h,在程序开关调用函数 UARTInit(); 实现STC51单片机的ISP。

/******************************************************************************
* 文    件: ISP.c
* 原作者: 李锋源
* 修  改: ZhnJa 
* 创建日期: 2011-7-15
* 修改日期: 2013-8-01
* 说 明: 原文件为阿土开发板的Driver.c,提取出来的ISP下载程序。
******************************************************************************/

#include "ISP.h"
#include "STC12C5A60S2.h"
#include <intrins.h> 

#ifdef	Self_Define_ISP_Download			//如果有自定义ISP下载功能
unsigned char bufptr;
code unsigned char passward[]={ICPCODE};
unsigned char buf[sizeof(passward)*2];		//静态串口缓冲区
#endif
 
/******************************************************************************
*                               UART初始化            
*描    述:串口初始化函数, 通常是在使用串口前调用本函数来进行初始化, 通常是在
*          main函数中调用。
*入口参数:无
*返    回:无
*注    意:串口使用的是中断模式
******************************************************************************/
void UARTInit(void)
{
	#define Fclk 11059200UL	//晶振 11.059M 不可以写成11059200
	#define BitRate 9600UL	//9600b/s
	unsigned char i;
	unsigned int j;
#ifdef	Self_Define_ISP_Download   //自定义下载使用到
	bufptr = 0;
	for( i=0; i<sizeof(buf); i++)
	{
		buf[i] = 0;
	}
#endif
	EA=0; 							//暂时关闭中断
	TMOD &= 0x0F;
	TMOD |=0x20;    				//定时器1工作在模式2,自动重装模式
	SCON=0x50;     					//串口工作在模式1
	TH1=256-(Fclk/(BitRate*12*16)); //计算定时器重装值
	TL1=256-(Fclk/(BitRate*12*16));
	PCON|=0x80;    					//串口波特率加倍
   // ES=1;         				//串行中断允许
	TR1=1;        					//启动定时器1
	REN=1;        					//允许接收 
	EA=1;         					//允许中断 

  	for(i=0;i<8;i++)			 	//短暂延时判断有无ISP下载命令
	{	
	 	for(j=0;j<40000;j++)
		{
		 	if(RI)
			{
				RI = 0;
				IAP_CONTR = 0x60;	 //复位到IAP
			}
		}
	}
	ES=1;         					//串行中断允许
}


/******************************************************************************
*                               延时1s函数            
*描    述:延时1s函数,在UartInit()调用供ISP延时。
*入口参数:无
*返    回:无
*注    意:
******************************************************************************/
void delay1s(void)
{
 	unsigned char i,j,k;
	for(i=0;i<200;i++)
	{
	 	for(j=0;j<200;j++)
		{
			k = 10;
			while(k--);
		}
	}
}
/******************************************************************************
*                           发送一个字符            
*描    述:向串口发送一个字符。
*入口参数:要发送的字符
*返    回:无
*注    意:
******************************************************************************/
void SendByte(unsigned char c)	 
{
    SBUF = c;
    while(!TI);
    TI = 0;
}
/******************************************************************************
*                          发送一个字符串            
*描    述:向串口发送一个字符串
*入口参数:*s要发送的字符串
*返    回:无
*注    意:
******************************************************************************/
void SendStr(char *s)			
{
	while(*s)
	{
		SendByte(*s++);
	}
}

/******************************************************************************
*                               串口0中断            
*描    述:串口0(UART0)中断。
*入口参数:无
*返    回:无
*注    意:
******************************************************************************/
void UartISR(void) interrupt 4
{
#ifdef Self_Define_ISP_Download
	
	unsigned char ptScr,ptDst;
	if(RI)
	{
		RI = 0;				//清标志位
		buf[bufptr] = SBUF;
		ptScr = bufptr;	
		if(bufptr==sizeof(buf))
		{
			bufptr = 0;
		}
		else
		{
			bufptr++;
		}
		while(buf[ptScr] == passward[ptDst])
		{
			if(ptScr == 0)
			{
				ptScr = sizeof(buf)-1;
			}
			else
			{
				ptScr--;
			}
			if(ptDst == 0)
			{
			//	reset();
			delay1s();            
			//复位前提示程序
			IAP_CONTR = 0x60;	 	//复位到IAP
			}
			else
			{
				ptDst--;
			}	
		}
		//用户程序开始
		
		
		//用户程序结束
	}//End if(RI) 
	if(TI)							//发送完成
	{		   	
	 	TI = 0;						//清标志位
	}
#else		   						//如果不使用自定义ISP下载程序
	if(RI)
	{
		RI = 0;
	}
	else
	{
	 	TI = 0;
	}
#endif
}

/*======================End Of File====================*/

/******************************************************************************
* 文    件: ISP.h
* 原作者: 李锋源
* 修  改: ZhnJa 
* 创建日期: 2011-7-15
* 修改日期: 2013-8-01
******************************************************************************/
#ifndef	__ISP_H__
#define __ISP_H__

#include "STC12C5A60S2.h"
//系统配置
#define Self_Define_ISP_Download			//ISP下载
#define ICPCODE		0x12,0x34,0x56,0x78,0x90,0xAB,0xCD,0xEF
#define FOSC	11059000UL
#define T100HZ  (FOSC/12/100)


//函数声明

//串口
void UARTInit(void);
void UARTInit(void);

void SendByte(unsigned char c);
void SendStr(char *s);

void delay1s(void);


#endif

第六步:

先用普通的正常下载方式将包含有ISP功能的程序下载到STC51单片机上。

打开STC-ISP(V6.53),设置如图,点击发送自定义下载命令即可实现远程下载。

补充:最高,最低波特率要设置为9600



说明1:
ISP,即In-System Programming,在线编程。
具有ISP功能的单片机芯片,可以通过简单的下载线直接在电路板上给芯片写入或者擦除程序,并且支持在线调试。
说明2:
须先下载一次有ISP功能的程序到单片机,之后才能实现冷启动下载程序,即ISP功能。

2018-08-23 12:26:41 padingdun 阅读数 12005
              ****这是我写的第一篇博客,如果有问题欢迎指正*****    
之前早就做好了,但一直没有时间发表,现在就把我做的过程和思路分享给大家。

【硬件部分】

小车模型一个(直流电机一对)
这里写图片描述

电池18650一对
这里写图片描述

HC-08蓝牙模块
这里写图片描述

蓝牙模块主要要掌握其基本参数以及工作原理即可,附下图。
这里写图片描述

这里写图片描述

L298模块
这里写图片描述

l298内部包含4通道逻辑驱动电路,是二相和四相电机的专用驱动器,即内含两个H桥的高电压大电流全桥式驱动器,接收标准TTL逻辑电平,可驱动46V,2A以下的电机。

这里写图片描述

内部逻辑图如下

这里写图片描述

L7805芯片

L7805CV是输出电压为4.75-5.25V,静态电流为4.2-8mA的正电压稳压器。其输出电流可达1.5A,不需外接补偿元件,内含限流保护电流,防止负载短路烧毁元件。
这里写图片描述

51单片机

芯片采用STC12C5A60S2.
这里写图片描述

【软件部分】
部分代码如下

``PWM输出程序

这里写图片描述

控制PWM程序

这里写图片描述

串口中断程序

这里写图片描述

主程序

这里写图片描述

2018-02-22 12:17:38 qq_40277973 阅读数 70272

不久前开始学习使用蓝牙模块,在模块与51单片机连接的过程中出现了非常多的问题,我想应该也是很多新手和我一样会遇到这样的问题,因此特地写这篇文章,想分享下在学习过程中遇到的问题以及解决方法。


此次学习用到模块是HC-06蓝牙模块,如下图:

该模块某宝有售,价格约为20RMB。某宝上的HC-06有两种,分别是带引脚不带引脚的,建议新手购买带引脚的。我从试验开始到成功,一共使用了四块蓝牙模块。第一次买的是带引脚的,但是模块本身是坏的;第二次买的是不带引脚的,但是由于自身的焊功有限,导致模块损坏,无法使用;第三次是朋友送的蓝牙4.0,由于某些原因无法使用,在此也特别感谢朋友送我蓝牙;第四次购买,就是上图所示的蓝牙,才最终完成了试验。

总结:在某宝购买时,最好货比三家,虽然模块不值钱,但是在购买过程遇到问题会耽误时间,影响开发,非常麻烦。

单片机用了两个,分别是新手常用的开发板还有一个单片机最小模块,两者有什么区别我稍后会说明。

开发板:


单片机最小模块:


我特别标注了两者的晶振,分别为12MHZ11.0594MHZ,就是晶振的不同导致我在学习中问题的发生。以下是学习试验过程。


蓝牙模块的调试:

接线,蓝牙模块的RX接转换模块的TX蓝牙模块的TX接转换模块的RX,如下图所示:


接入电脑,在PC端下载好串口调试助手,软件自搜,此处不再赘述。

附可能会用到的驱动:链接:https://pan.baidu.com/s/1bpYLfCr 密码:yabv

进入到调试助手,其实基本不怎么用调参数了,蓝牙模块基本都默认设置好波特率为9600,因此直接启动软件调试即可。具体调参数的方法可以自行百度其他文章,都有很详细的介绍。

启动串口,成功后左下角显示成功:


发送AT指令,返回OK:


表明串口正常,此时用手机连接蓝牙模块。手机端也是用到调试助手,请自行下载。

搜索蓝牙模块:

备注:我的蓝牙模块此前已经被我改名为Ezio,未改名前默认为HC06。


连接成功:


尝试发送消息hello:


此时在PC端的串口助手上,可以收到来自手机端发送的消息:


在此说明一点,在蓝牙模块上电以后,模块上的LED灯为闪烁状态,此时处于从机模式,与手机成功连接后,LED灯会变为常亮。自此,蓝牙模块调试成功,可以与单片机连接进行试验


蓝牙模块与51单片机接线:

和连接转换模块一样,蓝牙模块的RX连接单片机的TX,蓝牙模块的TX连接单片机的RX,此处说明单片机的RX和TX引脚分别为P3.0和P3.1,如图(图片来自网络):


正确接线后,向单片机中写入程序,程序如下:

#include <reg52.h>

sbit P1_0 = P1^0;	//测试口,可用可不用
sbit P1_3 = P1^3;	//输出口

unsigned char tempbuf;	//存储接收到的信息

/*初始化串口*/
void BlueteethInit()
{
	SCON = 0x50;	//串口模式1,允许接收
	TMOD = 0x20;	//T1工作模式为2,自动重装
	PCON = 0x00;	//波特率不倍增

	REN = 1;

	TH1 = 0xfd;		//设置波特率为9600
	TL1 = 0xfd;

	RI = 0;

	EA = 1;
	ES = 1;

	TR1 = 1;
}

void main()
{
	BlueteethInit();
	P1_0 = 0;
	P1_3 = 0;
	TI = 0;
	while(1)
	{
		if(tempbuf == 0x31)	//可以使用
			P1_3 = 1;
		if(tempbuf == 0)	//不可以使用
			P1_3 = 0;
		if(tempbuf == 'A')	//可以使用
			P1_3 = 1;
		if(tempbuf == 'B')	//可以使用
			P1_3 = 0;
	}
}

void Serial(void) interrupt 4
{
	tempbuf = SBUF;
	RI = 0;	//读标志清零
	SBUF = tempbuf;	//将内容返回到手机端,可在手机查看发送的内容
	while(!TI);
	TI = 0;	//写标志清零
}

该程序为最简单的测试程序,利用蓝牙接收手机发来的信息,控制P1.3口输出高或者低电平,以测试是否正确接收到信息。


第一步,用蓝牙模块与开发板接线,并成功用手机与蓝牙模块连接,尝试发送信息,过程如图所示:


无论是发送数字或者是其他字符,都可以看见返回的是乱码,因此可以知道,单片机接收的也是乱码,故程序中的判断:

while(1)
	{
		if(tempbuf == 0x31)	//可以使用
			P1_3 = 1;
		if(tempbuf == 0)	//不可以使用
			P1_3 = 0;
		if(tempbuf == 'A')	//可以使用
			P1_3 = 1;
		if(tempbuf == 'B')	//可以使用
			P1_3 = 0;
	}

无法正确执行,P1.3口自然也无法根据需要来输出高或者低电平。

第二步,用蓝牙模块与单片机最小模块接线,成功用手机连接收尝试发送信息,如下图所示:


可见,此时返回的内容与发出的内容相同,经测试此时程序也可以正确执行,使用万用表可以检查出P1.3口输出电平的变化,表明此时蓝牙模块可以正常使用。

特别说明:

if(tempbuf == 0x31)	//可以使用
	P1_3 = 1;
if(tempbuf == 0)	//不可以使用
	P1_3 = 0;

当发送数字消息时,应为十六进制,因此在判断时,如接收到1,应判断是否等于0x31,而不是判断是否等于1。此处经过测试,发送1时,判断tempbuf == 0x31,该判断有效;当发送0时,判断tempbuf == 0,判断无效。判断字符加单引号即可。

第三步,为什么使用两个相同的单片机会导致结果不同?这也是困扰了我很久的问题,后来经过检查,才知道原来就是晶振的问题。此处PO一下大神关于晶振的说明,暂时未看懂:https://www.zhihu.com/question/30930577

但可以得出的结论就是,如果使用串口通信,应使用的晶振为11.0594MHZ,否则可能出现乱码的情况。

另附:开发板上的晶振如图:


是可以更换的,某宝也有售,可以根据需要的晶振购买。


2019-04-01 17:18:05 qq_41865502 阅读数 1406

在这里插入图片描述

蓝牙模块:HC05
波特率:9600
校验位:无
停止位:1

HC05与电脑的蓝牙配对后生成传入传出两个COM口,使用传出COM口

蓝牙与单片机接线:
5V–5V
GND–GND
TX–3.0
RX–3.1

第一次需要用常规方式将下方代码写入

main.c
#include <reg52.h>
#include “stc_isp_download.h”

sbit LED = P0^0;

void main()
{
Init_Iap_Download();
LED = 1;
while (1)
{
LED = 0;
}
}

stc_isp_download.c
#include “stc_isp_download.h”

void Init_Iap_Download()
{
TMOD=0X20;
SM0=0;
SM1=1;
REN=1;
TH1=0XFD;
TL1=0XFD; //波特率 9600
TR1=1; //校验位 无
EA=1; //停止位 1
ES=1;
}
void Rceive()interrupt 4
{
unsigned char i=0;
unsigned int j,k;

if(TI==1)
{
	TI=0;
}
else
{
	RI=0;
	i=SBUF;
	if(i==Download_command)
	{
		for(j=1000;j>0;j--)
		for(k=123;k>0;k--);
		Iap_Download =0x60;
	}
}

}

stc_isp_download.h
#ifndef stc_isp_download
#define stc_isp_download

#include <reg52.h>
#define Download_command 0xf1 //功能码
sfr Iap_Download=0xE7;//软复位地址
void Init_Iap_Download();

#endif