目录
1. 进入 AT 状态 有 2 种方法使模块进入 AT 指令状态:
一.HC-05 介绍
1.1外观介绍
ATK-HC05 模块非常小巧(16mm*32mm),模块通过 6 个 2.54mm 间距的排针与外部连 接,模块外观如图 1所示:
图1 HC05 模块外观图
图中从右到左,依次为模块引出的 PIN1~PIN6 脚,各引脚的详细描述如表 2所示:
图2 HC05 模块各引脚功能描述
另外,模块自带了一个状态指示灯:STA。该灯有 3 种状态,分别为:
1,在模块上电的同时(也可以是之前),将 KEY 设置为高电平(接 VCC),此时 STA 慢闪(1 秒亮 1 次),模块进入 AT 状态,且此时波特率固定为 38400。
2,在模块上电的时候,将 KEY 悬空或接 GND,此时 STA 快闪(1 秒 2 次),表示模块 进入可配对状态。如果此时将 KEY 再拉高,模块也会进入 AT 状态,但是 STA 依旧保 持快闪。
3,模块配对成功,此时 STA 双闪(一次闪 2 下,2 秒闪一次)
1.2 模块使用说明
ATK-HC05 蓝牙串口模块所有功能都是通过 AT 指令集控制,这里仅介绍用户常用的 几个 AT 指令,详细的指令集,可以网上搜索资料
1. 进入 AT 状态 有 2 种方法使模块进入 AT 指令状态:
A.上电同时/上电之前将 KEY 设置为 VCC, 上电后,模块即进入 AT 指令状态。
B.模块上电后,通过将 KEY 接 VCC,使模块进 入 AT 状态。
注:方法 1(推荐)进入 AT 状态后,模块的波特率为:38400(8 位数据位,1 位停止 位)。方法 2 进入 AT 状态后,模块波特率和通信波特率一致。
2, 指令结构
模块的指令结构为:AT+<=PARAM>,其中 CMD(指令)和 PARAM(参 数)都是可选的,不过切记在发送末尾添加回车符(\r\n),否则模块不响应。
比如我要查看模块的版本:
串口发送:AT+VERSION?\r\n
模块回应:+VERSION:2.0-20100601
3, 常用指令说明及测试
1.修改模块主从指令 AT+ROLE=0 或 1,该指令来设置模块为从机或主机,并且可以通过 AT+ROLE?来 查看模块的主从状态,
2.设置记忆指令 AT+CMODE=1,该指令设置模块可以对任意地址的蓝牙模块进行配对,模块默认 设置为该参数。 AT+CMODE=0,该指令设置模块为指定地址配对,
注意,如果先设置模块为任意地址, 然后配对,接下去使用该指令,则模块会记忆最后一次配对的地址,下次上电会一直搜 索该地址的模块,直到搜索到为止
3.修改通信波特率指令 AT+UART=<param1>,<param2><param3>, 该指令用于设置串口波特率、停止位、 校验位等。
Param1 为波特率,可选范围为:4800、9600、19200、38400、57600、115200、 230400、460800、921600、1382400;
Param2 为停止位选择,0 表示 1 位停止位,1 表 示 2 位停止位;
Param3 为校验位选择,0 表示没有校验位(None),1 表示奇校验(Odd), 2 表示偶校验(Even)。
比如发送:AT+UART=9600,0,0,则是设置通信波特率为 9600,1 位停止位, 没有校验位,这也是模块的默认设置。
4.修改密码指令 AT+PSWD=<password>,该指令用于设置模块的配对密码,password 必须为 4 个字 节长度。
5,修改蓝牙模块名字 AT+NAME=<name>,该指令用于设置模块的名字,name 为你要设置的名字,必须 为 ASCII 字符,且最长不能超过 32 个字符。模块默认的名字为 ATK-HC05。比如发送: AT+NAME=GUANG ZHOU,即可设置模块名字为“GUANG ZHOU”。
二.硬件设计
1.1模块与单片机连接
模块与单片机连接最少只需要 4 根线即可:VCC、GND、TXD、RXD,VCC 和 GND 用于给模块供电,模块 TXD 和 RXD 则连接单片机的 RXD 和 TXD 即可。该模块兼容 5V 和 3.3V 单片机系统。下图是常见的连接方式
图 HC05 模块与单片机系统连接示意图
图中虚线连接表示可有可无,这个大家根据自己的需要选择性的使用即可。
三.软件设计
3.1 模块与开发板连接
开发板主控是stm32F407ZET6,需要用到USART3,TIM7,引脚连接对应关系如下:
KEY---------------PF6
LED---------------PC0
RXD---------------PB10
TXD---------------PB11
GND---------------GND
VCC---------------VCC
3.2代码框架及思路
1.由于HC-05使用串口透传模式,在接收数据时,串口接收是一个字节一个字节的接收,所以在接收数据时需要判断是不是一起发送过来的。实现的方法是开启一个定时器,溢出时间是10ms,如果串口前一个字节的数据和后一个数据时间间隔超过10ms,则认为两个数据是不同批次发送过来的,则在定时器里面标记接收数据完成。
2.在发送AT指令时,需要判断模块返回的数据是否正确。及判断模块的数据是否带有OK。
3.3代码实现
1.开启定时器
extern vu16 USART3_RX_STA;//用于标记接收完成和计数,该变量在usart3.c中定义
//定时器7中断服务程序
void TIM7_IRQHandler(void)
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断
{
USART3_RX_STA|=1<<15; //标记接收完成
TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志
TIM_Cmd(TIM7, DISABLE); //一次完整的数据接收完成,关闭TIM7 ,等下一次接收到数据在开启
}
}
void TIM7_Int_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能
//定时器TIM7初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断
TIM_Cmd(TIM7,ENABLE);//使能定时器7
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
2.串口配置
#define USART3_MAX_RECV_LEN 200
#define USART3_MAX_SEND_LEN 200
u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];//接收缓冲区
u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];//发送缓冲区
void USART3_init(u32 bound)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
USART_DeInit(USART3); //复位串口3
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOB11和GPIOB10初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10
GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11复用为USART3
GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10复用为USART3
USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART3, &USART_InitStructure); //初始化串口3
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART3, ENABLE); //使能串口
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
TIM7_Int_Init(100-1,8400-1); //10ms中断一次
TIM_Cmd(TIM7, DISABLE); //关闭定时器7
USART3_RX_STA=0; //清零
}
//串口3发送一个字节
void USART3_SendByte(u8 Byte)
{
USART_SendData(USART1, (u8)Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
//重定义printf
int fputc(int ch,FILE *f)/*重定向*/
{
USART3_SendByte(ch);
return ch;
}
//接收数据中断函数
//接收到的数据状态
//[15]:0,没有接收到数据;1,接收到了一批数据.
//[14:0]:接收到的数据长度
u16 USART3_RX_STA=0;
void USART3_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到数据
{
res =USART_ReceiveData(USART3);
if((USART3_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据
{
if(USART3_RX_STA<USART3_MAX_RECV_LEN) //还可以接收数据
{
TIM_SetCounter(TIM7,0);//计数器清空
if(USART3_RX_STA==0)
TIM_Cmd(TIM7, ENABLE); //使能定时器7
USART3_RX_BUF[USART3_RX_STA++]=res; //记录接收到的值
}else
{
USART3_RX_STA|=1<<15; //强制标记接收完成
}
}
}
}
3. HC-05 配置
#define HC05_KEY PFout(6) //蓝牙控制KEY信号
#define HC05_LED PCin(0) //蓝牙连接状态信号
//初始化ATK-HC05模块
//返回值:0,成功;1,失败.
u8 HC05_Init(void)
{
u8 retry=10,t;
u8 temp=1;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOC, ENABLE);//使能GPIOC,GPIOF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //LED对应引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//KEY对应引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure); //根据设定参数初始化PF6
GPIO_SetBits(GPIOF,GPIO_Pin_6);
USART3_init(9600); //初始化串口3为:9600,波特率.
while(retry--)//尝试初始化次数
{
HC05_KEY=1; //KEY置高,进入AT模式
delay_ms(10);
printf("AT\r\n"); //发送AT测试指令
HC05_KEY=0; //KEY拉低,退出AT模式
for(t=0;t<10;t++) //最长等待50ms,来接收HC05模块的回应
{
if(USART3_RX_STA&0X8000)break;
delay_ms(5);
}
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
temp=USART3_RX_STA&0X7FFF; //得到数据长度
USART3_RX_STA=0; //清除接收完成标记,以及接收数据长度
if(temp==4&&USART3_RX_BUF[0]=='O'&&USART3_RX_BUF[1]=='K')
{
temp=0;//接收到OK响应
break;
}
}
}
if(retry==0)temp=1; //检测失败
return temp;
}
//获取HC05模块的角色
//返回值:0,从机;1,主机;0XFF,获取失败.
u8 HC05_Get_Role(void)
{
u8 retry=0X0F;
u8 temp,t;
while(retry--)
{
HC05_KEY=1; //KEY置高,进入AT模式
delay_ms(10); //需要等待模块进入AT模式
printf("AT+ROLE?\r\n"); //查询角色
for(t=0;t<20;t++) //最长等待200ms,来接收HC05模块的回应
{
delay_ms(10);
if(USART3_RX_STA&0X8000)break;
}
HC05_KEY=0; //KEY拉低,退出AT模式
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
temp=USART3_RX_STA&0X7FFF; //得到数据长度
USART3_RX_STA=0;
if(temp==13&&USART3_RX_BUF[0]=='+')//接收到正确的应答了
{
temp=USART3_RX_BUF[6]-'0';//得到主从模式值
break;
}
}
}
if(retry==0)temp=0XFF;//查询失败.
return temp;
}
//HC05发送命令函数
//此函数用于设置HC05,适用于仅返回OK应答的AT指令
//atstr:AT指令串.比如:"AT+RESET"/"AT+UART=9600,0,0"/"AT+ROLE=0"等字符串
//返回值:0,设置成功;其他,设置失败.
u8 HC05_Send_Cmd(u8* atstr)
{
u8 retry=0X0F;
u8 temp,t;
while(retry--)
{
HC05_KEY=1; //KEY置高,进入AT模式
delay_ms(10); //需要等待模块进入AT模式
printf("%s\r\n",atstr); //发送AT字符串
HC05_KEY=0; //KEY拉低,退出AT模式
for(t=0;t<20;t++) //最长等待100ms,来接收HC05模块的回应
{
if(USART3_RX_STA&0X8000)break;
delay_ms(5);
}
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
temp=USART3_RX_STA&0X7FFF; //得到数据长度
USART3_RX_STA=0;
if(temp==4&&USART3_RX_BUF[0]=='O')//接收到正确的应答了
{
temp=0;
break;
}
}
}
if(retry==0)temp=0XFF;//设置失败.
return temp;
}
4.主函数
int main(void)
{
while(HC05_Init()); //初始化HC05模块 ,返回0才是初始化完成。
HC05_Set_Cmd("AT+ROLE=0"); //设置为从模式
while(1)
{
printf("ALIENTEK HC05 \r\n");//发送数据到蓝牙模块
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
reclen=USART3_RX_STA&0X7FFF; //得到数据长度
USART3_RX_BUF[reclen]=0; //加入结束符
//处理接收到的数据
//。。。。。。
}
}
}
总结:
本文用于学习笔记,且参考了正点原子的stm32开发指南。