文章目录[隐藏]
开发板用的是正点原子的精英板 STM32F103ZET6 蓝牙模块 用的是HC08
蓝牙模块HC08的简单测试
刚拿到模块的时候,一定要先确定模块是否能正常工作,这能为后续减少很多不必要的麻烦。因为我购买的模块是汇承的,他有一个初学者套餐,也不贵就8,9块钱,对初学者还是很友好的。如果购买了那个的兄弟们,直接按照上面的字母对应接上就行了
如果没有这个支架也没有关系,这个支架就相当于一个USB转TTL模块,如果你手头上有USB转TTL也是可以直接拿来测试的。
VCC–>3.3V,GND–>GND.TX–>RX,RX–>TX 如下图所示,把线接好就可以插入电脑开始测试了。
什么!你告诉我你什么都没有,没关系,板子总有吧(前提是板子上有CH340)例如正点原子的精英板,你可以把PA9,PA10的跳线帽拔了,然后把蓝牙用杜邦线接上,TX–>RX,RX–>TX。测试好后记得把跳线帽插回去。接线如下图所示
把线接好后就可以测试了。打开串口调试助手,波特率选择为9600,把发送新行的勾取消掉然后发送AT 就能收到OK了。
AT指令
1. 测试指令
指令 | 说明 | 响应 |
---|---|---|
AT | 检测串口是否正常工作 | OK |
2. 查看当前基本参数
指令 | 说明 | 响应 |
---|---|---|
AT+RX | 查询模块的基本参数 | Name:HC-08 >>(蓝牙名称) Role:Slave >>>>(模块角色 主 M / 从 S) Baud:9600,NONE (串口波特率, 校验位) Addr:48,70,1E,24,16,27>>(蓝牙地址) PIN :000000>>>>[ 蓝牙密码 (密码无效) ] |
3. 查看当前基本参数
指令 | 响应 | 说明 | 说明 |
---|---|---|---|
AT+DEFAULT | OK | 恢复出厂设置) | 不会清除主机已记录的从机地址!若要清除请在 未连线状态下使用 AT+CLEAR 指令进行清除。模块会自动重启,重启 200ms 后可进行新的操作! |
4.模块重启指令
指令 | 响应 | 说明 | 详情 |
---|---|---|---|
AT+RESET | OK | 重启模块 | 模块会自动重启,重启 200ms 后 可进行新的操作! |
5.查看软件版本指令
指令 | 响应 | 说明 |
---|---|---|
AT+VERSION | HC-08 V3.3,2020-10-16 | 获取软件版本和发布日期 |
6.修改模块角色指令
指令 | 响应 | 说明 | 详情 |
---|---|---|---|
AT+ROLE=x | Master/Slave | 设置主机/从机 | 默认从机,设置后模块将自动重启, 重启 200ms 后可进行新的操作! |
7.修改蓝牙名称指令
8.修改蓝牙地址指令
9.修改射频功率指令
10.修改串口波特率指令
11.设置模块是否可连接指令
12.更新广播数据指令(仅从机可以设置)
手机与板子进行通信
蓝牙模块主要的功能就是拿来通信,目前我只是用手机和板子之间进行通信,因为手头资源有限,所以就没有用蓝牙进行两块板子之间的通信
板子,电脑,模块之间的关系
为了更方便,更直观的观察到接收的数据。这里将串口3接收到的数据通过串口1发给电脑,以至于能在电脑上显示蓝牙模块接收的数据
实验效果
手机发送特定的指令来控制板子上的LED亮灭,并且在电脑上显示手机发送的内容,电脑可以通过串口调试助手将数据发到手机上。
部分代码
串口3的配置
//串口接收缓存区
u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN个字节.
u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
//通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据.
//如果2个字符接收间隔超过10ms,则认为不是1次连续数据.也就是超过10ms没有接收到
//任何数据,则表示此次接收完毕.
//接收到的数据状态
//[15]:0,没有接收到数据;1,接收到了一批数据.
//[14:0]:接收到的数据长度
vu16 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) //使能定时器7的中断
{
TIM_Cmd(TIM7,ENABLE);//使能定时器7
}
USART3_RX_BUF[USART3_RX_STA++]=res; //记录接收到的值
}else
{
USART3_RX_STA|=1<<15; //强制标记接收完成
}
}
}
}
//初始化IO 串口3
//pclk1:PCLK1时钟频率(Mhz)
//bound:波特率
void usart3_init(u32 bound)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口3时钟使能
USART_DeInit(USART3); //复位串口3
//USART3_TX PB10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10
//USART3_RX PB11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11
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_Cmd(USART3, ENABLE); //使能串口
//使能接收中断
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断
//设置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
TIM7_Int_Init(99,7199); //10ms中断
USART3_RX_STA=0; //清零
TIM_Cmd(TIM7,DISABLE); //关闭定时器7
}
//串口3,printf 函数
//确保一次发送数据不超过USART3_MAX_SEND_LEN字节
void u3_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART3_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART3,USART3_TX_BUF[j]);
}
}
串口三中是通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据,因此还需要开启定时器
extern vu16 USART3_RX_STA;
//定时器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
}
}
//通用定时器7中断初始化
//这里时钟选择为APB1的2倍,而APB1为42M
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//通用定时器中断初始化
//这里始终选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
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 = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
主函数部分就很简单了,就像上图那样,将串口3接收到的数据,用串口1发出去,将电脑发的信息通过串口3发给模块,进而在手机上显示。
如果想做一个手机蓝牙控制开关灯的东西,那么只需要再加上一个pwm控制舵机的部分,就可以实现了
最后,第一次写,主要是记录自己的学习过程,如果那里有错的地方,还请大家指出,互相学习,共同进步
版权声明:本文为CSDN博主「释怀。。」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_52422778/article/details/122123388
暂无评论