联调单片机,蓝牙点灯Demo
前面都是对基础的工程创建,本次实验结合外接单片机(stm32F4)和蓝牙(BG22)通过串口通讯传输信息,实现手机app发送信息个蓝牙,蓝牙芯片接收到信息以后把数据处理后通过串口转发给单片机。单片机解读蓝牙芯片BG22通过串口发送过来的信息,解读成为控制LED的信号。从而实现手机通过蓝牙控制单片机控制LED的亮灭。
同样新建工程,本次使用的芯片的型号为:
EFR32BG22C224F512GM32
需要在工程中重新配置:
本次使用的协议栈的版本为:
选择此空的工程:
配置GATT如下:
点击Generate 生成工程,打开后编译,下载测试一下。
如果编译出错,错误提示如下:
不要怕,这就是协议栈的毛病!
在init_mcu.c文件添加如下图的宏定义:
#define BSP_DCDC_INIT EMU_DCDCINIT_DEFAULT
再次点击编译,编译后没有错误。
点开app.c文件
在前面添加头文件和函数:
添加头文件内容如下:
/****************add here********************/
/*
* 增加如下改动
* */
#include "stdio.h"
#include "em_usart.h"
#include "em_device.h"
#include "em_chip.h"
#include "em_emu.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "em_core.h"
#define printLog printf
/**************/
添加串口服务函数如下:
/*增加如下代码段*/
/**************************************************************************//**
* @brief
* GPIO initialization
*****************************************************************************/
void initGpio(void)
{
// Configure PA5 as an output (TX)
GPIO_PinModeSet(gpioPortA, 5, gpioModePushPull, 0);
// Configure PA6 as an input (RX)
GPIO_PinModeSet(gpioPortA, 6, gpioModeInput, 0);
// Configure PB4 as output and set high (VCOM_ENABLE)
// comment out next line to disable VCOM
GPIO_PinModeSet(gpioPortB, 4, gpioModePushPull, 1);
}
/**************************************************************************//**
* @brief
* CMU initialization
*****************************************************************************/
void initCmu(void)
{
// Enable clock to GPIO and USART1
CMU_ClockEnable(cmuClock_GPIO, true);
CMU_ClockEnable(cmuClock_USART1, true);
}
/**************************************************************************//**
* @brief
* USART1 initialization
*****************************************************************************/
void initUsart1(void)
{
// Default asynchronous initializer (115.2 Kbps, 8N1, no flow control)
USART_InitAsync_TypeDef init = USART_INITASYNC_DEFAULT;
// Route USART1 TX and RX to PA5 and PA6 pins, respectively
// GPIO->USARTROUTE[1].TXROUTE = (gpioPortA << _GPIO_USART_TXROUTE_PORT_SHIFT)
// | (5 << _GPIO_USART_TXROUTE_PIN_SHIFT);
GPIO->USARTROUTE[1].RXROUTE = (gpioPortA << _GPIO_USART_RXROUTE_PORT_SHIFT)
| (6 << _GPIO_USART_RXROUTE_PIN_SHIFT);
// Enable RX and TX signals now that they have been routed
GPIO->USARTROUTE[1].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | GPIO_USART_ROUTEEN_TXPEN;
// Configure and enable USART1
USART_InitAsync(USART1, &init);
// Enable NVIC USART sources
NVIC_ClearPendingIRQ(USART1_RX_IRQn);
NVIC_EnableIRQ(USART1_RX_IRQn);
NVIC_ClearPendingIRQ(USART1_TX_IRQn);
NVIC_EnableIRQ(USART1_TX_IRQn);
}
uint8_t Buffer5[5] = {0x00,0x67,0x63,0x00,0xfd};
uint8_t buffer[80]; //串口接收到是数据
uint8_t USART1_RX_buffer[128] = {0};
uint32_t USART1_RX_NUM= 0;
bool receive_flag = true;
/**************************************************************************//**
* @brief
* The USART1 receive interrupt saves incoming characters.
*****************************************************************************/
void USART1_RX_IRQHandler(void)
{
uint32_t i;
// Get the character just received
buffer[USART1_RX_NUM] = USART1->RXDATA;
// Exit loop on new line or buffer full
if ((buffer[USART1_RX_NUM] != '\n') && (USART1_RX_NUM < 128))
USART1_RX_NUM++;
else
{
receive_flag = false; // Stop receiving on CR
// Enable receive data valid interrupt
USART_IntEnable(USART1, USART_IEN_RXDATAV);
USART1_RX_NUM = 0;
for(i=0;i<128;i++)
{
USART1_RX_buffer[i] = buffer[i];
buffer[i] = 0;
}
}
}
void Receive_serial_data(void)
{
uint32_t i;
/*根据接收到的指令进行行为判断*/
if (!receive_flag) //如果接收到了串口数据
{
if(USART1_RX_buffer[0] == 'A' && USART1_RX_buffer[1] == 'T' && USART1_RX_buffer[2] == '+' && USART1_RX_buffer[3] == '\r')
{
printf("ok\r\n");
printf("rev=");
printf("%s\r\n",USART1_RX_buffer);
Buffer5[2]++;
}
else if(USART1_RX_buffer[0] == 'A' && USART1_RX_buffer[1] == 'T' && USART1_RX_buffer[2] == '+' && USART1_RX_buffer[3] != '\r')
{
printf("ok\r\n");
printf("%s\r\n",USART1_RX_buffer);
}
else
{
printf("error=%s \r\n",USART1_RX_buffer);
}
for (i = 0; i < 128; i++) //清空接收数组
{
USART1_RX_buffer[i] = 0;
}
receive_flag = true;
}
}
void init_fun(void)
{
// Chip errata
CHIP_Init();
// Initialize GPIO and USART1
initCmu();
initGpio();
initUsart1();
// Enable receive data valid interrupt
USART_IntEnable(USART1, USART_IEN_RXDATAV);
printf("begin\r\n");
}
/********************************************************************************************************************************/
在appMain函数内添加定义:
long unsigned int x;
unsigned char i =0;
在appMain函数内的while循环之前添加串口的初始化函数:
init_fun(); //串口中断初始化
printLog("begin\r\n"); //打印信息
在appMain函数的while(1)循环内添加串口中断检测函数:
Receive_serial_data(); //接收串口中断的数据
注释掉等待函数,同时把协议栈的事件获取的运行方式从阻塞式改为非阻塞式:
evt = gecko_peek_event();//改变阻塞方式为非阻塞方式
改动后的appMain函数如下:
最后在后面增加case执行,对事件检测:
/* Add additional event handlers as your application requires */
case gecko_evt_gatt_server_characteristic_status_id:
{
if ((evt->data.evt_gatt_server_characteristic_status.characteristic == gattdb_temperature_measurement)&& (evt->data.evt_gatt_server_characteristic_status.status_flags == 0x01))
{
if (evt->data.evt_gatt_server_characteristic_status.client_config_flags == 0x02)
{
/* Indications have been turned ON - start the repeating timer. The 1st parameter '32768'
* tells the timer to run for 1 second (32.768 kHz oscillator), the 2nd parameter is
* the timer handle and the 3rd parameter '0' tells the timer to repeat continuously until
* stopped manually.*/
gecko_cmd_hardware_set_soft_timer(32768, 0, 0);
//printf("启动重复计时器\r\n");
}
else if (evt->data.evt_gatt_server_characteristic_status.client_config_flags == 0x00)
{
/* Indications have been turned OFF - stop the timer. */
gecko_cmd_hardware_set_soft_timer(0, 0, 0);
//printf("指示已关闭-停止计时器\r\n");
}
}
printLog("order = set set soft timer\r\n");
}
break;
case gecko_evt_hardware_soft_timer_id:
/* Measure the temperature as defined in the function temperatureMeasure() */
printf("order = sand indicate\r\n");
gecko_cmd_gatt_server_send_characteristic_notification(0xFF, gattdb_temperature_measurement, 5, Buffer5);
break;
/*检测在app内变量被更改*/
case 0xa00a0:
{
/*更改值*/
if (evt->data.evt_gatt_server_user_write_request.characteristic == gattdb_test)
{
// Write user supplied value to LEDs.
//printf("BG22 receive command is %c\n",evt->data.evt_gatt_server_attribute_value.value.data[0]);
//set_leds(evt->data.evt_gatt_server_attribute_value.value.data[0]);
gecko_cmd_gatt_server_send_user_write_response(evt->data.evt_gatt_server_user_write_request.connection, gattdb_test, bg_err_success);
//printf("BG22 send response to client.\n");
//判断电灯操作
if(evt->data.evt_gatt_server_attribute_value.value.data[0] == '1')
{
printf("LED=N\r\n");
}
else if(evt->data.evt_gatt_server_attribute_value.value.data[0] == '2')
{
printf("LED=F\r\n");
}
else
{
printf("get data = ");
for(i=0;i<8;i++)
{
if(evt->data.evt_gatt_server_attribute_value.value.data[i] != 0x00)
printf("%c",evt->data.evt_gatt_server_attribute_value.value.data[i]);
else
break;
}
printf("\r\n");
}
}
}
break;
手动分割线:——————————————————————————————————
下面是单片机上面的工程配置:
在单片机上我使用的两个串口,一个串口用来和PC链接显示打印信息,另一个串口和蓝牙BG22芯片连接进行数据通讯。同时还配置了一个LED。
这里配置的过程我也是直接参考了胡工的教程,感谢他❤
STM32CubeMx开发之路—4采用DMA方式收发数据
打开KEIL后,在while循环内添加代码:
if(Rx_Flag) // Receive flag
{
Rx_Flag=0; // clean flag
HAL_UART_Transmit_DMA(&huart6, Rx_Buf, Rx_Len);
if(Rx_Buf[0] == 'L' && Rx_Buf[1] == 'E' && Rx_Buf[2] == 'D' && Rx_Buf[3] == '=')//判断接收到的数据是不是正确
{
printf("usart0 sand data is LED\r\n");
if(Rx_Buf[4] == 'N')
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET); //打开灯
}
if(Rx_Buf[4] == 'F')
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET); //关闭灯
}
}
}
/*蓝牙部分接收到了数据,一方面将数据转发到串口,另一方面对数据进行判断,执行开关灯操作*/
if(Rx6_Flag) // Receive flag
{
Rx6_Flag=0; // clean flag
HAL_UART_Transmit_DMA(&huart2, Rx6_Buf, Rx6_Len);//把接收到的串口6的数据转发到串口2
if(Rx6_Buf[0] == 'L' && Rx6_Buf[1] == 'E' && Rx6_Buf[2] == 'D' && Rx6_Buf[3] == '=')//判断接收到的数据是不是正确
{
printf("usart0 sand data is LED\r\n");
if(Rx6_Buf[4] == 'N')
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET); //打开灯
}
if(Rx6_Buf[4] == 'F')
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET); //关闭灯
}
}
}
串口6会把接收到的信息转发给串口2打印到电脑上,同时还会检测是不是需要电灯。