本帖最后由 大山一座 于 2016-7-23 13:29 编辑
今天是2016年7月23日,前段时间公司要做个蓝牙通讯的功能,因为我比较懒,我就在淘宝上买了个现成的模块,就是这玩意,很便宜, 才十几块钱,而且我试了下,10米内不丢包, 挺好用的,一直想配个天线,但是我不会量驻波比, 还是算了,反正10米也够用,拿回来之后我打算先 用TTl转USB试一下, 当然了,这是一篇比较基础的文章,希望能够帮助更多深陷痛苦的新手。 话不多说,第一步,先用蓝牙和电脑通讯。 蓝牙与串口的链接,因为他们不是透传(直连,所发即所收)关系所以要交叉连线,也就是串口上的RX接蓝牙上的TX,串口模块上的TX接蓝牙上的RX,注意串口提供的一定要是3.3V的电源,虽说这个模块可以耐压5V ,但淘宝货嘛……你懂的! 之后在电脑上打开串口调试软件,我用的是友善,因为这个比较稳定,用着舒服,串口掉线之后软件不出错,当然我是花了钱的,毕竟要尊重人家的劳动嘛! 因为这个串口是已经开发好的模块,内部有可以用的API,so……打开手册…… 找到串口的波特率:9600,N,8,1,带iBeacon(有了这个就可以做主,也可以做从) OK 调整串口软件的参数如下: 然后点打开,如果顺利的话下面会显示:
好了,那么更多的问题来了: 它的API在哪??该怎么给它发命令? 别着急,看datesheet! 模块使用的是非常简单的AT指令: 可以看到我发送AT,模块应答OK 好吧,说明这东西还没坏。 据说蓝牙默认是透传的,我想是否可以先试试? 我用我们公司的蓝牙APP试了下: 收发都很正常
这是发过来的信息。 注意:蓝牙4.0模块只支持安卓4.3及以上版本,并且在使用之前需要用AT+TYPE3设置为正常的配对并需要密码链接,这样会稳定一些。默认密码请查看收手册。 我更改一下蓝牙的名字吧,现在蓝牙的搜索名字叫:HMsoft,我改成了mountain 搞定了! 用现成的东西就是很舒服啦! 现在 我们用它来与stm32F4通讯咯! 这是我用的F4的板子,抱歉火逼,你的板子前段时间我测试电机的时候不小心接错了电源烧了,那个……我只好用公司的了,这里就不能给你做广告了!反正都是一样的啦! 不过我还是严重推荐火逼的F429! 性能杠杠的! 这个蓝牙模块分两块,一块是与手机通讯用的蓝牙,一块是串口模块,是TTL模块,TTL模块可以直接和STM32的USART通讯! 大概的理论就是这样,开始动手! 首先,打开我那个可爱的keil5,在此我要感谢火逼,因为我用的就是他的例程!(真的不是做广告……) 首先我们需要的是两个串口: 蓝牙用的是: 也就是F4上的串口2,(USART2),看下原理图: 这里,PD5是U2TX,PD6是U2RX。
OK引脚和功能都确定了,我们开始敲代码: #include"WIRELESS_CONTROL.h" uint16_tCommandResult[3]; uint8_tCommandResultCount = 0; uint32_tSpeaker_Long_DelayNum = 400; //ms uint32_tSpeaker_Short_DelayNum = 200; //ms uint32_tQuickStop_DelayNum = 10; //ms voidWIRELESS_init(u32 bound){ GPIO_InitTypeDefGPIO_InitStructure;//GPIO结构体 USART_InitTypeDefUSART_InitStructure;//串口结构体 NVIC_InitTypeDefNVIC_InitStructure;//定义一个中断向量的结构体 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);//开启串口所在时钟总线AHB1的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//开启串口外设时钟!! GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_USART2); GPIO_PinAFConfig(GPIOD,GPIO_PinSource6,GPIO_AF_USART2); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 |GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF;//串口设置为复用功能!这个很重要! GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType= GPIO_OType_PP; //推挽 GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP; GPIO_Init(GPIOD,&GPIO_InitStructure);//将指定的引脚注册串口 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(WIRELESS_INTERFACE,&USART_InitStructure); USART_Cmd(WIRELESS_INTERFACE, ENABLE); //串口使能语句!一定要记得使能…… USART_ITConfig(WIRELESS_INTERFACE,USART_IT_RXNE, ENABLE);//开启相关中断 NVIC_InitStructure.NVIC_IRQChannel =WIRELESS_INTERFACE_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority=3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 PID_Speaker_PWM_Controller(2-1,33600-1);//蜂鸣器初始化 TIM_SetCompare1(TIM9,1);//蜂鸣器PWM设置 TIM_Cmd(TIM9, DISABLE);//蜂鸣器关闭 TIM7_Button_TimerConter_Init(); printf("initializefinished"); } voidPID_WarningClear(void)//清警告 { GPIO_Setbits(GPIOD_BASE,GPIO_Pin_12,0);//清警告 GPIO_Setbits(GPIOD_BASE,GPIO_Pin_13,0);//清警告 delay_ms(QuickStop_DelayNum); GPIO_Setbits(GPIOD_BASE,GPIO_Pin_12,1);//清警告 GPIO_Setbits(GPIOD_BASE,GPIO_Pin_13,1);//清警告 PID_Speaker_Special_Tip(); } voidPID_MasterSwitch_Off(void)//关闭总开关 { GPIO_Setbits(GPIOD_BASE,GPIO_Pin_0,0);//关闭总开关 PID_Speaker_Special_Tip(); } voidPID_MasterSwitch_On(void)//打开总开关 { GPIO_Setbits(GPIOD_BASE,GPIO_Pin_0,1);//打开总开关 PID_Speaker_Special_Tip(); } voidPID_Speaker_Standard_Tip(void) { TIM_Cmd(TIM9, ENABLE); delay_ms(Speaker_Short_DelayNum); TIM_Cmd(TIM9, DISABLE); } voidPID_Speaker_Special_Tip(void) { delay_ms(Speaker_Long_DelayNum); TIM_Cmd(TIM9, DISABLE); delay_ms(Speaker_Short_DelayNum); TIM_Cmd(TIM9, ENABLE); delay_ms(Speaker_Short_DelayNum); TIM_Cmd(TIM9, DISABLE); delay_ms(Speaker_Short_DelayNum); TIM_Cmd(TIM9, ENABLE); delay_ms(Speaker_Short_DelayNum); TIM_Cmd(TIM9, DISABLE); } voidPID_Speaker_PWM_Controller(u32 arr,u32 psc) { //此部分需手动修改IO口设置 GPIO_InitTypeDefGPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // NVIC_InitTypeDefNVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE); //使能PORTF时钟 GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_TIM9); GPIO_InitStructure.GPIO_Pin= GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed= GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType= GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化PF9 TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式 TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM9,&TIM_TimeBaseStructure);//初始化定时器9 //初始化TIM9Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高 TIM_OC1Init(TIM9,&TIM_OCInitStructure); //根据T指定的参数初始化外设TIM9 TIM_OC1PreloadConfig(TIM9,TIM_OCPreload_Enable); //使能TIM9在CCR1上的预装载寄存器 TIM_ARRPreloadConfig(TIM9 ,ENABLE);//ARPE使能 TIM_Cmd(TIM9,ENABLE); //使能 } voidTIM7_Button_TimerConter_Init(void) { //此部分需手动修改IO口设置 NVIC_InitTypeDefNVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE); //TIMER时钟使能 TIM_TimeBaseStructure.TIM_Prescaler=8400; //定时器分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式 TIM_TimeBaseStructure.TIM_Period=4000; //自动重装载值=0.4 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM7,&TIM_TimeBaseStructure);//初始化定时器SPEED_COUNTER TIM_Cmd(TIM7,DISABLE); //使能 NVIC_InitStructure.NVIC_IRQChannel=TIM7_IRQn;//定时器SPEED_COUNTER中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x03;//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02;//子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); TIM7->CNT = 0; TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE);//允许定时器SPEED_COUNTER更新中断 } voidTIM7_IRQHandler (void)// { if(TIM_GetITStatus(TIM7,TIM_IT_Update)==SET)//溢出中断 { CommandResultCount =0;CommandResult[0] = 0;CommandResult[1] = 0; CommandResult[2] = 0; } TIM_ClearITPendingBit(TIM7,TIM_IT_Update); TIM_Cmd(TIM7, DISABLE); }/////////////////////////////////////长按判断中断 voidUSART2_IRQHandler(void) //串口1中断服务程序 { if(USART_GetITStatus(WIRELESS_INTERFACE,USART_IT_RXNE) != RESET) { if(CommandResultCount>2){CommandResultCount= 0;CommandResult[0] = 0;CommandResult[1] = 0; CommandResult[2] = 0;} CommandResult[CommandResultCount]=USART_ReceiveData(WIRELESS_INTERFACE); //读取接收到的数据 printf("%d \r\n",CommandResult[CommandResultCount]); CommandResultCount+=1; if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==145)&&CommandResultCount>1) {//↓↓↓↓↓↓↓前进↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("100"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑前进↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==146)&&CommandResultCount>1) {//↓↓↓↓↓↓↓后退↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("101"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑后退↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==148)&&CommandResultCount>1) {//↓↓↓↓↓↓↓左转↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("103"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑左转↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==149)&&CommandResultCount>1) {//↓↓↓↓↓↓↓右转↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("104"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑右转↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==150)&&CommandResultCount>1) {//↓↓↓↓↓↓↓停止↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("105"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑停止↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==151)&&CommandResultCount>1) {//↓↓↓↓↓↓↓复位↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("106"); PID_WarningClear(); PID_Speaker_Special_Tip(); }//↑↑↑↑↑↑↑复位↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==152)&&CommandResultCount>1) {//↓↓↓↓↓↓↓重启↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("107"); PID_MasterSwitch_Off(); delay_ms(3000); PID_MasterSwitch_On(); PID_Speaker_Special_Tip(); }//↑↑↑↑↑↑↑重启↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==153)&&CommandResultCount>1) {//↓↓↓↓↓↓↓加速↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("108"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑加速↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ if(((CommandResult[0]+CommandResult[1]+CommandResult[2])==154)&&CommandResultCount>1) {//↓↓↓↓↓↓↓减速↓↓↓↓↓↓↓↓↓↓↓↓↓↓\\ printf("109"); PID_Speaker_Standard_Tip(); }//↑↑↑↑↑↑↑减速↑↑↑↑↑↑↑↑↑↑↑↑↑↑\\ TIM_Cmd(TIM7,ENABLE); } USART_ClearITPendingBit(WIRELESS_INTERFACE,USART_IT_RXNE|USART_IT_ORE_RX|USART_IT_TXE|USART_IT_PE); } 上面是与蓝牙通讯用的串口模块的初始化与控制程序的实现,大家需要注意的是橙色的部分,我已经写好注释。 剩下的是控制相关,可以不用理会。 这个程序的功能是,串口判断蓝牙发过来的消息:比如109,然后进USART的中断,并且进行消息判断,并做出相应的STM32控制,控制语句我已经去掉了,因为实在无关紧要。 其中TIM7是用来定时清理多余消息的,起到了软滤噪的功能,speak大家肯定都明白,是个无源的蜂鸣器,这样可以通过蜂鸣器的叫声,确定是否执行了相应的命令。 蜂鸣器很简单的,就是一个TIM9,然后输出固定的PWM而已关键是蜂鸣器的频率要掌握好。 这里要重点说明的是一个问题: USART_ClearITPendingBit(WIRELESS_INTERFACE,USART_IT_RXNE|USART_IT_ORE_RX|USART_IT_TXE|USART_IT_PE); 这里!为什么需要清除USART_IT_ORE_RX|USART_IT_TXE|USART_IT_PE位呢?因为我发现,如果不清除这两个位,可能会一直进中断,因为串口初始化的时候,是设置了发送与接收功能的,而且可能出现连续接收的情况,所以这两个位一定要清除!我想另外一个原因可能是因为无硬件数据流控制的原因!但具体没有仔细调试,希望各位大神看过之后能够给我一个答案,为什么要清理这两个中断位,我QQ148099429。 OK我们把蓝牙连接到F4上去看下: 蓝牙已经链上了! 另外再给火逼做个广告:他的CMSIS-DAP真的很不错,WIN10能用而且STM32F7也能用!真不错! OK搞定了!!大家有什么问题可以在论坛里留言,我可能永远不会回复,但是我看到了肯定会回复…… 另外:支持共享,支持开源!反对无休止的商业化与利益化! |