找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 14400|回复: 3
收起左侧

STM32手机蓝牙控制实例分享

[复制链接]
ID:245534 发表于 2017-11-3 00:01 | 显示全部楼层 |阅读模式
分享一份很好的资料,关于怎么样使用手机来控制开发板的,里面有一些实例,都是汉德乐电子网络工作室,开人员自行编写的,通过这份资料相信你也能很好的将你的手机和开发板连接起来,下面不多讲,直接上资料!
1、首先介绍一下什么是透明传输:
透明传输是指不管所传数据是什么样的比特组合,都应当能够在链路上传送。当所传数据中的比特组合恰巧与某一个控制信息完全一样时,就必须采取适当的措施,使接收方不会将这样的数据误认为是某种控制信息。这样才能保证数据链路层的传输是透明的。发送方和接收方数据的长度和内容完全一致,相当于一条无形的传输线。
2、透传模块:
Handler_Bluetooth_PWM 平台上使用的是FBT_06(嵌入式近距离主从分离/一体式蓝牙串口通讯模块)
特点:
蓝牙2.0 带EDR, 2Mbps-3Mbps 调制度
内置2.4GHz 天线, 用户无需调试天线
外置8Mbit FLASH
低电压3.3V 工作(3.1V~4.2V)配对时30~40MA 波动,配对完毕通信8MA
可选PIO 控制
标准HCI 端口(UART or USB)
USB 协议: Full Speed USB1.1, Compliant With 2.0
发射功率属于class2(约15米)
3、Handler_Bluetooth_PWM 平台(引用)
该平台由Handler_Studio 设计,初始目的是专门为四轴飞行器等陆地、空中机器人提供处理核心,而后被大家将其扩展到了各个领域,进行了五花八门的设计应用。
该平台基于stm32 进行研发设计,接口人性化,相比市面上的常见的multiwill 平台的mega328、mega2560 等飞控具有主频高、处理速度快、性能稳定等显著优点。
该平台有多达10 通道的硬件PWM 输出,足以应付10 轴飞行器、机器人、机器手臂、蛇形机器人等涉及舵机、直流电机、步进电机、无刷电机等的控制工作。

亮点1:平台具有配套的安卓控制软件,并开源平台源码,方便二次开发!
亮点2:平台引出了常用的通信接口:如串口、SPI、IIC 等,这样极大地扩展了平台的适应性,方便我们的DIY!!
亮点3:平台不仅可以和手机、电脑进行通信,两平台之间也可通信:典型应用:无线串口!无线串口功能可应用在例如飞思卡尔智能车的调试当中。
4、蓝牙透传实例
本文档给大家讲述如何用借助Handler_Bluetooth_PWM 平台通过手机控制产生多路PWM 以控制舵机!
说明:
硬件平台:Handler_Bluetooth_PWM 飞控板
开发环境:keilMDK
Stm 库版本:V3.0.0
首先需要配置stm32 的串口:
1、使能时钟
2、使能串口中断
3、初始化串口
这里想重点说明一下串口数据的接收策略:
不怕麻烦举四个例子说明,如果很了解就跳过,初学者还是有作用的(欢迎拍砖)
实例一:
void USART1_IRQHandler(u8 GetData)
{
u8 BackData;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志.
GetData = UART1_GetByte(BackData); //也行GetData=USART1->DR;
USART1_SendByte(GetData); //发送数据
GPIO_SetBits(GPIOE, GPIO_Pin_8 ); //LED闪烁,接收成功发送完成
delay(1000);
GPIO_ResetBits(GPIOE, GPIO_Pin_8 );
}
}
这是最基本的,将数据接收完成后又发送出去,接收和发送在中断函数里执行,main 函数里无其他要处理的。
优点:简单,适合很少量数据传输。
缺点:无缓存区,并且对数据的正确性没有判断,数据量稍大可能导致数据丢失。
实例二:
void USART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
Uart2_Buffer[Uart2_Rx_Num] = USART_ReceiveData(USART2);
Uart2_Rx_Num++;
}
if((Uart2_Buffer[0] == 0x5A)&&(Uart2_Buffer[Uart2_Rx_Num-1] == 0xA5)) //判断最
后接收的数据是否为设定值,确定数据正确性
Uart2_Sta=1;
if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出
{
USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR
USART_ReceiveData(USART2); //读DR
}
}
if( Uart2_Sta )
{
for(Uart2_Tx_Num=0;Uart2_Tx_Num < Uart2_Rx_Num;Uart2_Tx_Num++)
USART2_SendByte(Uart2_Buffer[Uart2_Tx_Num]); //发送数据
Uart2_Rx_Num = 0; //初始化
Uart2_Tx_Num = 0;
Uart2_Sta = 0;
}
这是加了数据头和数据尾的接收方式,数据头和尾的个数可增加,此处只用于调试之用。中断函数用于接收数据以及判断数据的头尾,第二个函数在main 函数里按照查询方式执行。
优点:较简单,采用缓存区接收,对提高数据的正确行有一定的改善。
缺点:要是第一次数据接收错误,回不到初始化状态,必须复位操作。
实例三:
void USART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志.
Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);
Uart2_Rx++;
Uart2_Rx &= 0x3F; //判断是否计数到最大
}
if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出
{
USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR
USART_ReceiveData(USART2); //读DR
}
}
if( Uart2_Tx != Uart2_Rx )
{
USART2_SendByte(Uart2_Buffer[Uart2_Tx]); //发送数据
Uart2_Tx++;
Uart2_Tx &= 0x3F; //判断是否计数到最大
}
采用FIFO 方式接收数据,由0x3F 可知此处最大接收量为64 个,可变,中断函数只负责收,
另一函数在main 函数里执行,FIFO 方式发送。
优点:发送和接收都很自由,中断占用时间少,有利于MCU 处理其它。
缺点:对数据的正确性没有判断,一概全部接收。
实例四:
void USART2_IRQHandler()
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志
Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);
Uart2_Rx++;
Uart2_Rx &= 0xFF;
}
if(Uart2_Buffer[Uart2_Rx-1] == 0x5A) //头
Uart2_Tx = Uart2_Rx-1;
if((Uart2_Buffer[Uart2_Tx] == 0x5A)&&(Uart2_Buffer[Uart2_Rx-1] == 0xA5)) //
检测到头的情况下检测到尾
{
Uart2_Len = Uart2_Rx-1- Uart2_Tx; //长度
Uart2_Sta=1; //标志位
}
if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出
{
USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR
USART_ReceiveData(USART2); //读DR
}
}
if( Uart2_Sta )
{
for(tx2=0;tx2 <= Uart2_Len;tx2++,Uart2_Tx++)
USART2_SendByte(Uart2_Buffer[Uart2_Tx]); //发送数据
Uart2_Rx = 0; //初始化
Uart2_Tx = 0;
Uart2_Sta = 0;
}
数据采用数据包的形式接收,接收后存放于缓存区,通过判断数据头和数据尾(可变)来判
断数据的“包”及有效性,中断函数用于接收数据和判断头尾以及数据包长度,另一函数在
main 函数里执行,负责发送该段数据。
优点:适合打包传输,稳定性和可靠性很有保证,可随意发送,自动挑选有效数据。
缺点:缓存区数据长度要根据“包裹”长度设定, 要是多次接收后无头无尾,到有头有尾
的那一段数据恰好跨越缓存区最前和最后位置时,可能导致本次数据丢失,不过这种情况几
乎没有可能。
不论使用哪种方法,都是可以实现简单的功能的,一般来说中断里面只负责接收数据,而
不要在其进行数据的处理。

接下来需要设置stm32 的TIM 模块
由于平台上的处理器是STM32CBT6,定时器有4 个,分别是一个高级控制定时器TIM1,和三个通用定时器。
TIMER 输出PWM 实现步骤:
1. 设置RCC 时钟;
2. 设置GPIO 时钟;
3. 设置TIMx 定时器的相关寄存器;
4. 设置TIMx 定时器的PWM 相关寄存器。
附上源码(以TIM2 为例:两个通道)
/*
* 函数名:TIM2_GPIO_Config
* 描述:配置TIM3复用输出PWM时用到的I/O
* 输入:无
* 输出:无
* 调用:内部调用
*/
static void TIM2_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* TIM3 clock enable */
//PCLK1经过2倍频后作为TIM3的时钟源等于36MHz
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIOA and GPIOB clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
/*GPIOA Configuration: TIM3 channel 1 and 2 as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
static void TIM2_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* PWM信号电平跳变值*/
u16 CCR1_Val = 251;
u16 CCR2_Val = 251;
/* -----------------------------------------------------------------------
TIM2 Configuration: generate 4 PWM signals with 4 different duty cycles:
TIM2CLK = 36 MHz, Prescaler = 0x0, TIM3 counter clock = 36 MHz
TIM2 ARR Register = 999 => TIM2 Frequency = TIM2 counter clock/(ARR + 1)
TIM2 Frequency = 36 KHz.
----------------------------------------------------------------------- */
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 9999; //当定时器从0计数到999,即为1000
次,为一个定时周期
TIM_TimeBaseStructure.TIM_Prescaler = 144; //设置预分频:不预分频,即为
36MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分频系数:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //设置跳变值,当计数器计数到这个
值时,电平发生跳变
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //当定时器计数值小
于CCR1_Val时为高电平
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //使能通道1
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR2_Val; //设置通道2的电平跳变值,输出另外
一个占空比的PWM
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //使能通道2
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE); // 使能TIM2重载寄存器ARR
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE); //使能定时器2
}
/*
* 函数名:TIM2_Mode_Config
* 描述:TIM2 输出PWM信号初始化,只要调用这个函数
* TIM2的两个通道就会有PWM信号输出
* 输入:无
* 输出:无
* 调用:外部调用
*/
void TIM2_PWM_Init(void)
{
TIM2_GPIO_Config();
TIM2_Mode_Config();
}
在配置其他两个通用定时器时,配置几乎是相同的,但是在配置TIM1 的时候需要加这个函数以使能TIM1 的PWM 输出
TIM_CtrlPWMOutputs(TIM1, ENABLE);

这样这个工程的基本功能就都具备了,接下来只需要处理手机端发过来的数据进行调节PWM 输出即可。数据可以自定协议。这里简单的说明一下,由于平台上有10 个通道所以我规定手机端发送过来的数据在十个数据之间要有所区别,我设定发送格式为Aa1000B其中A 和B 是数据包的首和尾,a 代表第一通道,相应的有b 通道、c 通道...1000 代表占空比。
这样一个基于Handler_Bluetooth_PWM 平台的通过手机产生多路PWM 控制舵机的程序就完成了。
回复

使用道具 举报

ID:319334 发表于 2018-6-13 14:46 | 显示全部楼层
资料不错,值得学习
回复

使用道具 举报

ID:351587 发表于 2018-6-14 10:49 | 显示全部楼层
有参考价值,谢谢
回复

使用道具 举报

ID:243748 发表于 2018-6-25 06:42 来自手机 | 显示全部楼层
很详细的注译,谢谢楼主
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表