固件库模板
MDK5开发环境
stm32参考手册
UART串口协议
stm32中断概念
HC-05命令集
利用固件库模板点灯工程(下面第三行,手动狗头)
CH340 USB→TTL模块
蓝牙HC-05模块
手机软件
实验程序已经发布到百度网盘,本文末有链接可以自取
串口协议查看这篇博客USART串口协议
stm32中断概念STM32中断应用概括
HC-05命令文件:HC-05命令
串口调试工具
https://pan.baidu.com/s/1V-jRJzb0INDXDLo9I4CJ6A 提取码:0000
手机软件管家下载下面两个工具

| 标号 | PIN | 引脚说明 |
|---|---|---|
| 1 | STATE | 状态引出引脚(未连接时输出低电平,连接时输出高电平) |
| 2 | RXD | 接收端 |
| 3 | TXD | 发送端 |
| 4 | GND | 模块供电负极 |
| 5 | VCC | 模块供电正极 |
| 6 | EN | 使能端,需要进入命令模式时接3.3V |
本模块使用USART协议,具体请看下面两个博客,这里就不继续讲解了
USART串口协议
四、stm32-USART串口通讯(重定向、接发通信、控制LED亮灭)
工作模式: 自动连接(automatic connection) ,又称为透传模式;命令回应(order-response) ,又称为AT模式(AT mode)
进入 AT 命令有二种方法:
正常模式是9600、AT模式波特率固定38400,8位数据位、1位停止位、无奇偶校验的通信格式。
(1)发送AT\r\n,回复OK;
(2)发送AT+UART?\r\n,回复+UART 9600,0,0
(3)发送AT+UART=115200,0,0\r\n,回复OK。即为波特率配置成功。
0——指定蓝牙地址连接模式(指定蓝牙地址由绑定指令设置) 1——任意蓝牙地址连接模式(不受绑定指令设置地址的约束)2——回环角色(Slave-Loop) 默认连接模式:0) 。使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟;
初始化 GPIO,并将 GPIO 复用到 USART 上;
配置 USART 参数;
配置中断控制器并使能 USART 接收中断;
使能 USART;
在 USART 接收中断服务函数实现数据接收和发送。
四、stm32-USART串口通讯(重定向、接发通信、控制LED亮灭)




这里已经勾选发送新行,直接AT就行,否则为AT\r\n
4. 发送AT,回复OK;
5. 发送AT+UART?,回复+UART 9600,0,0
6. 发送AT+UART=115200,0,0,回复OK。即为波特率配置成功。
7. AT+NAME= "XXX"修改蓝牙模块名称为XXX
8. AT+ROLE=0 蓝牙模式为从模式
9. AT+CMODE=1蓝牙连接模式为任意地址连接模式,也就是说该模块可以被任意蓝牙设备连接
10. AT+PSWD= "1234"蓝牙配对密码为1234
11. AT+UART=9600,0,0 蓝牙通信串口波特率为9600,停止位1位,无校验位.
配置结束,需带电重启一次。




stm32f103c8t6自带一个led灯,使用PC13引脚就行了,
切记尽量避免使用PB3、PB4,具体看stm32f103c8t6使用PB3和PB4做普通GPIO使用时发现异常
#include "led.h" //绑定led.h
void LED_GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStruct; //初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。
//开启RCC时钟
RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE);
//配置初始化,推挽输出方式和LED_G_GPIO_PIN管脚、赫兹
GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO口初始化
GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);
}
#ifndef __LED_H_
#define __LED_H_
#include "stm32f10x.h"
#include "sys.h"
#define LED_G_GPIO_PIN GPIO_Pin_13
#define LED_G_GPIO_PORT GPIOC
#define LED_G_GPIO_CLK RCC_APB2Periph_GPIOC
//使用位带操作来实现操作某个IO口的 1个位,由sys.h实现
#define LED PCout(13)
void LED_GPIO_Config(void); //函数定义
#endif
在USART1_IRQHandler方法下面添加 usart2的初始化和中断
struct STRUCT_USARTx_Fram strHC05_Fram_Record = { 0 };
void uart2_init(u32 bound) {
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
//USART2_TX GPIOA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA2
//USART2_RX GPIOA.3初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3
//Usart2 NVIC 配置
NVIC_PriorityGroupConfig(macNVIC_PriorityGroup_x);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
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(USART2, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//使能串口总线空闲中断
USART_Cmd(USART2, ENABLE);
}
void USART2_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART2); //读取接收到的数据
//USART_SendData(USART1,Res);
if ( strHC05_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) ) //预留1个字节写结束符
strHC05_Fram_Record .Data_RX_BUF [ strHC05_Fram_Record .InfBit .FramLength ++ ] = Res;
}
if ( USART_GetITStatus( USART2, USART_IT_IDLE ) == SET ) //数据帧接收完毕
{
strHC05_Fram_Record .InfBit .FramFinishFlag = 1;
Res = USART_ReceiveData( USART2 );
}
}
添加strHC05_Fram_Record结构体和uart2_init(u32 bound)函数
extern struct STRUCT_USARTx_Fram //串口数据帧的处理结构体
{
//STRUCT_USARTx_Fram 读取的数据
char Data_RX_BUF [ RX_BUF_MAX_LEN ];
union {
__IO u16 InfAll;
struct {
__IO u16 FramLength :15; // 14:0
__IO u16 FramFinishFlag :1; // 15
} InfBit;
};
}strUSART_Fram_Record,strHC05_Fram_Record;
//如果想串口中断接收,请不要注释以下宏定义
void uart1_init(u32 bound);
void uart2_init(u32 bound);
#include "hc_05.h"
#include "delay.h"
#include "usart.h"
#include "stdbool.h"
#include
bool HC05_AT_Test ( void )
{
char count = 0;
printf("\r\nAT测试.....\r\n");
Delay_ms ( 2000 );
while ( count < 10 )
{
printf("\r\nAT测试次数 %d......\r\n", count);
if( HC05_Set_Cmd ( "AT", "OK", NULL, 500 ) )
{
printf("\r\nAT测试启动成功 %d......\r\n", count);
return 1;
}
++ count;
}
return 0;
}
/**
* @brief HC05 配置测试函数
* @param 无
* @retval 无
*/
void HC05_ConfigTest(void)
{
printf( "\r\n正在配置 HC05 ......\r\n" );
printf( "\r\n请按住按钮 ......\r\n" );
while( ! HC05_AT_Test() );
printf ( "\r\n连接成功......\r\n" );
}
/*
* 函数名:hc05_Cmd
* 描述 :对hc05模块发送AT指令
* 输入 :cmd,待发送的指令
* reply1,reply2,期待的响应,为NULL表不需响应,两者为或逻辑关系
* waittime,等待响应的时间
* 返回 : 1,指令发送成功
* 0,指令发送失败
* 调用 :被外部调用
*/
bool HC05_Set_Cmd( char *cmd, char *reply1, char *reply2, u32 waittime )
{
strHC05_Fram_Record .InfBit .FramLength = 0; //从新开始接收新的数据包
hc05_Usart ( "%s\r\n", cmd );
if ( ( reply1 == 0 ) && ( reply2 == 0 ) ) //不需要接收数据
return true;
Delay_ms ( waittime ); //延时
//增加一个结束符。
strHC05_Fram_Record .Data_RX_BUF [ strHC05_Fram_Record .InfBit .FramLength ] = '\0';
//将USART2接收到的东西全部打印出来,接收到的保存在这个数组里,
//通过串口1发送出来,这样就能看到,我们发送的指令的响应,是否出错,
//例如:发送AT,响应是OK, HC05_USART接收到的数据是AT OK.在交给USART1发送,在PC机的串口调试助手可以看到这些信息,
PC_Usart ( "%s", strHC05_Fram_Record .Data_RX_BUF );
strHC05_Fram_Record .InfBit .FramLength = 0; //清除接收标志
strHC05_Fram_Record.InfBit.FramFinishFlag = 0;
if ( ( reply1 != 0 ) && ( reply2 != 0 ) )
return ( ( bool ) strstr ( strHC05_Fram_Record .Data_RX_BUF, reply1 ) ||
( bool ) strstr ( strHC05_Fram_Record .Data_RX_BUF, reply2 ) );
else if ( reply1 != 0 )
return ( ( bool ) strstr ( strHC05_Fram_Record .Data_RX_BUF, reply1 ) );
else
return ( ( bool ) strstr ( strHC05_Fram_Record .Data_RX_BUF, reply2 ) );
}
#ifndef __HC_05_H_
#define __HC_05_H_
#include "stm32f10x.h"
#include "stdbool.h"
#include "sys.h"
/*********************************************** HC05 函数宏定义 *******************************************/
#define hc05_Usart( fmt, ... ) USART_printf ( USART2, fmt, ##__VA_ARGS__ )
#define PC_Usart( fmt, ... ) printf ( fmt, ##__VA_ARGS__ )
//#define macPC_Usart( fmt, ... )
bool HC05_Set_Cmd( char *cmd, char *reply1, char *reply2, u32 waittime );
bool HC05_AT_Test ( void );
void HC05_ConfigTest(void);
#endif
/******************************** END OF FILE *********************************/
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stm32f10x.h"
#include "led.h"
#include "hc_05.h"
/*
CH340的TXD-----USART1的RX引脚相连(c8t6 的PA10)
CH340的RXD-----USART1的TX引脚相连(c8t6 的PA9)
HC-05的TXD-----USART2的RX引脚相连(c8t6 的PA3)
HC-05的RXD-----USART2的TX引脚相连(c8t6 的PA2)
*/
int main()
{
/********************************************************************************
* Delay_init(); //本实验使用的是SysTick时钟
* CPU_TS_TmrInit(); //已经使能宏,不需要初始化
* uart1_init(115200); //串口初始化为115200,需要在usart.h中使能
********************************************************************************/
/* 初始化 */
uart1_init(115200); //usart1初始化,
uart2_init(9600); //usart2初始化,
LED_GPIO_Config(); //初始化led使用的GPIO口
printf("--HC_05初始化完成--");
HC05_ConfigTest(); //看看HC-05AT命令是否有效
while (1)
{
if(strHC05_Fram_Record .InfBit .FramFinishFlag) //如果串口接收到数据,并结束
{
//增加一个结束符。
strHC05_Fram_Record .Data_RX_BUF [ strHC05_Fram_Record .InfBit .FramLength ] = '\0';
//将HC05接收到的东西全部打印出来,接收到的保存在这个数组里,再通过串口1发送出来,这样就能看到,我们发送的指令的响应,是否出错,
printf( "\r\n%s\r\n", strHC05_Fram_Record .Data_RX_BUF );
if(strstr ( strHC05_Fram_Record .Data_RX_BUF, "LED=1" )) //如果手机发送的数据存在LED=1,则if成立
{
PCout(13) = 1; //PC口13引脚输出,高电平
printf("\r\nLED灭\r\n");
Delay_ms(500); //已经在delay.h中初始化
}
else if(strstr ( strHC05_Fram_Record .Data_RX_BUF, "LED=0" ))
{
PCout(13) = 0; //PC口13引脚输出,低电平
printf("\r\nLED亮\r\n");
Delay_ms(500);
}
strHC05_Fram_Record .InfBit .FramLength = 0; //清除接收标志
strHC05_Fram_Record.InfBit.FramFinishFlag = 0;
}
}
}
编译成功

本文选择的是ST_Link烧录工具


如果没有ID号看博客:ST-Link V2烧录问题(已解决)





这里PC13已经亮灭了就没有上传截图,大家自己可以试试
链接:https://pan.baidu.com/s/1QkiPecASH7St9YDvSWHfpA 提取码:0000