|
蓝牙通信测试-利用适配器/手机/电脑自带蓝牙
http://www.yfrobot.com/forum.php?mod=viewthread&tid=599
(出处: arduino 单片机机器人爱好者之家)
BC-04蓝牙模块使用教程-AT指令的使用
http://www.yfrobot.com/forum.php?mod=viewthread&tid=4
(出处: arduino 单片机机器人爱好者之家)
通过对上两个帖子的学习,了解蓝牙的基本知识,实现手机串口与电脑串口之间的通信。
蓝牙的使用十分简单,不用把它想的多么复杂,当蓝牙通电后,手机蓝牙就可以连接到BC04,连接完成后蓝牙模块中的灯常亮,此时它就是一根线,一段接着STM32的串口2,另一端接着手机,这时,手机就可以向STM32发送指令了。
1、实验目的:利用STM32的串口2与BC04相连,实现与手机的通信,并通过手机串口助手发送“ON”与“OFF”,控制“STATE”灯的“亮灭”。
2、硬件连接方式:STM32核心板-->YFROBOT_BC04
PA2 -- RXD
PA3 -- TXD
GND -- GND
3.3V -- 3V3
3、新建工程
首先,我们初始化STM32的串口2(USART2),这样才能够保证与蓝牙的正常通信。
新建文件usart2.c
- #include "delay.h"
- #include "usart2.h"
- #include "stdarg.h"
- #include "stdio.h"
- #include "string.h" //这3个文件为标准库的头文件,具体用途请百度
- /***************************************************************
- Copyright (C), 2013-2023, YFRobot.
- www.yfrobot.com
- File:串口2驱动代码(PA2/U2_TX;PA3/U2_RX)
- Author:aosini Version:1.0 Data:2014/05/28
- Description:此函数对原子的串口驱动代码进行了修改,在此感谢正点原子@ALIENTEK开源
- 硬件与BC04的接线方式;PA2接蓝牙RXD;PA3接TXD。
- ***************************************************************/
- //串口发送缓存区
- __align(8) u8 USART2_TX_BUF[USART2_MAX_SEND_LEN]; //发送缓冲,最大USART2_MAX_SEND_LEN字节
- #ifdef USART2_RX_EN //如果使能了接收
- //串口接收缓存区
- u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //接收缓冲,最大USART2_MAX_RECV_LEN个字节.
- //通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据.
- //如果2个字符接收间隔超过10ms,则认为不是1次连续数据.也就是超过10ms没有接收到
- //任何数据,则表示此次接收完毕.
- //接收到的数据状态
- //[15]:0,没有接收到数据;1,接收到了一批数据.
- //[14:0]:接收到的数据长度
- u16 USART2_RX_STA=0;
- void USART2_IRQHandler(void)
- {
- u8 res;
- if(USART2->SR&(1<<5))//接收到数据
- {
- res=USART2->DR;
- if(USART2_RX_STA<USART2_MAX_RECV_LEN) //还可以接收数据
- {
- TIM1->CNT=0; //计数器清空
- if(USART2_RX_STA==0)TIM1_Set(1); //使能定时器4的中断
- USART2_RX_BUF[USART2_RX_STA++]=res; //记录接收到的值
- }else
- {
- USART2_RX_STA|=1<<15; //强制标记接收完成
- }
- }
- }
- //初始化IO 串口2
- //pclk1:PCLK1时钟频率(Mhz)
- //bound:波特率
- void USART2_Init(u32 pclk1,u32 bound)
- {
- RCC->APB2ENR|=1<<8; //使能PORTG口时钟
- GPIOG->CRH&=0XFFFFFF0F; //IO状态设置
- GPIOG->CRH|=0X00000030; //IO状态设置
- RCC->APB2ENR|=1<<2; //使能PORTA口时钟
- GPIOA->CRL&=0XFFFF00FF; //IO状态设置
- GPIOA->CRL|=0X00008B00; //IO状态设置
- RCC->APB1ENR|=1<<17; //使能串口时钟
- RCC->APB1RSTR|=1<<17; //复位串口2
- RCC->APB1RSTR&=~(1<<17);//停止复位
- //波特率设置
- USART2->BRR=(pclk1*1000000)/(bound);// 波特率设置
- USART2->CR1|=0X200C; //1位停止,无校验位.
- USART2->CR3=1<<7; //使能串口2的DMA发送
- UART_DMA_Config(DMA1_Channel7,(u32)&USART2->DR,(u32)USART2_TX_BUF);//DMA1通道7,外设为串口2,存储器为USART2_TX_BUF
- #ifdef USART2_RX_EN //如果使能了接收
- //使能接收中断
- USART2->CR1|=1<<8; //PE中断使能
- USART2->CR1|=1<<5; //接收缓冲区非空中断使能
- MY_NVIC_Init(2,3,USART2_IRQChannel,2);//组2,最低优先级
- TIM1_Init(99,7199); //10ms中断
- USART2_RX_STA=0; //清零
- TIM1_Set(0); //关闭定时器4
- #endif
- }
- //串口2,printf 函数
- //确保一次发送数据不超过USART2_MAX_SEND_LEN字节
- void u2_printf(char* fmt,...)
- {
- va_list ap;
- va_start(ap,fmt);
- vsprintf((char*)USART2_TX_BUF,fmt,ap);
- va_end(ap);
- while(DMA1_Channel7->CNDTR!=0); //等待通道7传输完成
- UART_DMA_Enable(DMA1_Channel7,strlen((const char*)USART2_TX_BUF)); //通过dma发送出去
- }
- //定时器1中断服务程序
- void TIM1_UP_IRQHandler(void)
- {
- if(TIM1->SR&0X01)//是更新中断
- {
- USART2_RX_STA|=1<<15; //标记接收完成
- TIM1->SR&=~(1<<0); //清除中断标志位
- TIM1_Set(0); //关闭TIM1
- }
- }
- //设置TIM1的开关
- //sta:0,关闭;1,开启;
- void TIM1_Set(u8 sta)
- {
- if(sta)
- {
- TIM1->CNT=0; //计数器清空
- TIM1->CR1|=1<<0; //使能定时器1
- }else TIM1->CR1&=~(1<<0);//关闭定时器1
- }
- //通用定时器中断初始化
- //这里始终选择为APB1的2倍,而APB1为36M
- //arr:自动重装值。
- //psc:时钟预分频数
- void TIM1_Init(u16 arr,u16 psc)
- {
- RCC->APB2ENR|=1<<11; //TIM1时钟使能
- TIM1->ARR=arr; //设定计数器自动重装值
- TIM1->PSC=psc; //预分频器
- TIM1->DIER|=1<<0; //允许更新中断
- TIM1->CR1|=0x01; //使能定时器1
- MY_NVIC_Init(1,3,TIM1_UP_IRQChannel,2);//抢占2,子优先级3,组2 在2中优先级最低
- }
- #endif
- ///////////////////////////////////////USART2 DMA发送配置部分//////////////////////////////////
- //DMA1的各通道配置
- //这里的传输形式是固定的,这点要根据不同的情况来修改
- //从存储器->外设模式/8位数据宽度/存储器增量模式
- //DMA_CHx:DMA通道CHx
- //cpar:外设地址
- //cmar:存储器地址
- void UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar)
- {
- RCC->AHBENR|=1<<0; //开启DMA1时钟
- delay_us(5);
- DMA_CHx->CPAR=cpar; //DMA1 外设地址
- DMA_CHx->CMAR=cmar; //DMA1,存储器地址
- DMA_CHx->CCR=0X00000000; //复位
- DMA_CHx->CCR|=1<<4; //从存储器读
- DMA_CHx->CCR|=0<<5; //普通模式
- DMA_CHx->CCR|=0<<6; //外设地址非增量模式
- DMA_CHx->CCR|=1<<7; //存储器增量模式
- DMA_CHx->CCR|=0<<8; //外设数据宽度为8位
- DMA_CHx->CCR|=0<<10; //存储器数据宽度8位
- DMA_CHx->CCR|=1<<12; //中等优先级
- DMA_CHx->CCR|=0<<14; //非存储器到存储器模式
- }
- //开启一次DMA传输
- void UART_DMA_Enable(DMA_Channel_TypeDef*DMA_CHx,u8 len)
- {
- DMA_CHx->CCR&=~(1<<0); //关闭DMA传输
- DMA_CHx->CNDTR=len; //DMA1,传输数据量
- DMA_CHx->CCR|=1<<0; //开启DMA传输
- }
复制代码 这部分代码,主要实现了1:串口 2 的初始化;2:实现了串口 2 的 printf 函数:u2_printf;3:串口 2 的接收处理。
串口 2 这里我们发送数据采用 DMA 发送,以提高系统实时性。串口2 的数据接收,采用了定时判断的方法,对于一次连续接收的数据,如果出现连续 10ms 没有接收到任何数据,则表示这次连续接收数据已经结束。
新建usart2.h
- #ifndef __USART2_H
- #define __USART2_H
- #include "sys.h"
- /***************************************************************
- Copyright (C), 2013-2023, YFRobot.
- www.yfrobot.com
- File:串口2驱动代码(PA2/U2_TX;PA3/U2_RX)
- Author:aosini Version:1.0 Data:2014/05/28
- Description:此函数对原子的串口驱动代码进行了修改,在此感谢正点原子@ALIENTEK分享
- 硬件与BC04的接线方式;PA2接蓝牙RXD;PA3接TXD。
- ***************************************************************/
-
- #define USART2_MAX_RECV_LEN 200 //最大接收缓存字节数
- #define USART2_MAX_SEND_LEN 200 //最大发送缓存字节数
- #define USART2_RX_EN 1 //0,不接收;1,接收.
- #define true 1
- #define false 0
- extern u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //接收缓冲,最大USART2_MAX_RECV_LEN字节
- extern u8 USART2_TX_BUF[USART2_MAX_SEND_LEN]; //发送缓冲,最大USART2_MAX_SEND_LEN字节
- extern u16 USART2_RX_STA; //接收数据状态
- void USART2_Init(u32 pclk2,u32 bound); //串口2初始化
- void TIM1_Set(u8 sta);
- void TIM1_Init(u16 arr,u16 psc);
- void UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar);
- void UART_DMA_Enable(DMA_Channel_TypeDef*DMA_CHx,u8 len);
- void u2_printf(char* fmt, ...);
- float myatof(const char *sptr);
- #endif
复制代码 初始化“STATE”,新建led.c
- #include <stm32f10x_lib.h>
- #include "led.h"
- //初始化PD2为输出口.并使能这个口的时钟
- //LED IO初始化
- void LED_Init(void)
- {
- RCC->APB2ENR|=1<<5; //使能PORTD时钟
- GPIOD->CRL&=0XFFFFF0FF;
- GPIOD->CRL|=0X00000300;//PD.2推挽输出
- GPIOD->ODR|=1<<2; //PD.2输出高
- }
复制代码 新建led.h
- #ifndef __LED_H
- #define __LED_H
- #include "sys.h"
- //LED端口定义
- #define LED1 PDout(2)// PD2
- void LED_Init(void);//初始化
- #endif
复制代码 这部分代码,主要实现LED1的初始话。
最后在main.c文件中编写如下代码
- #include<stm32f10x_lib.h>
- #include "sys.h"
- #include "usart.h"
- #include "delay.h"
- #include "led.h"
- #include "usart2.h"
- #include "string.h"
- /*********************************************************
- Copyright (C), 2013-2023, YFRobot.
- www.yfrobot.com
- File:BC04蓝牙串口模式实验(STM32/TIM1)
- Author:aosini Version:1.0 Data:2014/06/09
- Description:通过手机端Android串口,向蓝牙发送“ON”或“OFF”,
- 控制指示灯的“亮”与“灭”。
- **********************************************************/
- int main(void)
- {
- u8 reclen=0; //数据长度
- Stm32_Clock_Init(9); //系统时钟设置
- delay_init(72); //延时初始化
- uart_init(72,9600); //串口1初始化
- USART2_Init(36,9600);//串口2初始化
- LED_Init();
- while(1)
- {
- if(USART2_RX_STA&0X8000) //接收到一次数据了
- {
- reclen=USART2_RX_STA&0X7FFF; //得到数据长度
- USART2_RX_BUF[reclen]=0; //结尾加入结束符 这句很重要,可以保证这次收到的信息不受上次的影响,
- //没有这语句的时候,当上一条信息长于当前信息时,收到的信息是不正确的
- //可以将这条语句注释,发不同长度的信息,观察手机串口收到的信息。
- if(reclen==2||reclen==3) //控制DS1检测
- {
- if(strcmp((const char*)USART2_RX_BUF,"ON")==0) LED1=0; //打开LED1
- if(strcmp((const char*)USART2_RX_BUF,"OFF")==0)LED1=1; //关闭LED1
- }
- u2_printf("Receive:%s\r\n",USART2_RX_BUF); //通过串口2,发生蓝牙接收到的数据
- USART2_RX_STA=0;
- }
- }
- }
复制代码 这样整个工程就完成了,具体内容,程序中有详细说明。
4、下载验证
编译成功后,下载到stm32核心板上,此时灯“STATE”是灭的。将STM32与蓝牙正确连接,打开手机端串口助手,连接蓝牙,连接成功后,蓝牙模块上的灯常亮。
通过蓝牙串口发送“ON”,“STATE”灯亮,蓝牙串口收到“Receive:ON”;发送“OFF”,“STATE”灯灭,蓝牙串口收到“Receive:OFF”,发送其它数据,不影响“STATE”灯状态。
BC04蓝牙串口模块实验(STM32核心板).rar
(594.54 KB, 下载次数: 242)
|
|