技术标签: 单片机
单片机的世界从点灯开始。
RTL8762是BLE蓝牙芯片,SOC,是性能比51单片机高出很多的存在,甚至不弱于一些ARM32位单片机,不用来点灯真是太可惜。
关于蓝牙开始很多人都有误解,认为蓝牙就是可以连接电脑,播放歌曲。那是传统蓝牙给人们的印象太深刻了。蓝牙3.0以下版本都是传统蓝牙,追求的是HIFI,可以连续播放歌曲,而不用考虑功率。
蓝牙4.0版本(BLE蓝牙低功耗)以上是用来传少量的数据控制指令,而不是传歌曲这种流量大户,4.0、5.0不是3.0传统蓝牙的替代,是共存的。并且4.0、5.0在低功耗领域为蓝牙技术开辟了新的战场,以适应物联网时代的到来。物联网时代要时时在线,低功耗是必须的选择。
BLE可以干什么?
点灯---蓝牙点灯,或者叫无线点灯
RTL8762的世界也是从点灯开始。
本文所用源码基于BEE2-SDK-v1.2.0修改而来,BEE2-SDK-v1.2.0是RTL8762的SDK,官方管RTL8762系列叫小蜜蜂(BEE),非常巧合Zigbee也带一个BEE,看来物联网应该是蜜蜂建立的。BEE2-SDK-v1.2.0可从www.realmcu.com获得
原始工程路径:BEE2-SDK-v1.2.0\board\evb\ble_peripheral\peripheral.uvprojx
官方SDK有很多工程例子,本文用的是ble_peripheral
peripheral是外设意思,ble+peripheral
1、工程中的文件夹:
1、include(一些头文件,不需要修改)
2、lib(静态库,不需要修改)
3、cmsis(ARM的比较基础的东西,不需要修改)
4、peripheral(RTL8762的外设,sdk里有各种外设驱动源码,按需添加)
5、profile(一般不用修改,GATT的配置文件,可以建立自己的profile)
6、app(主要的应用实现,重点修改文件都在这里)
2、 peripheral下的文件
我加的这几个文件,源文件都在BEE2-SDK-v1.2.0\src\mcu\peripheral下,用什么加什么
rtl876x_io_dlps.c
rtl876x_uart.c
rtl876x_rcc.c
rtl876x_gpio.c
uart用来串口通讯,gipo用来点灯
3、app下的文件
ancs.c(原工程自带)
app_task.c(原工程自带,rtos任务创建)
main.c(原工程自带,主文件,初始化调度)
peripheral_app.c(原工程自带,消息处理)
overlay_mgr.c(原工程自带)
uart.c
只有UART.C是后来添加的,实现了UART基本封装,其他都是原有工程自带
BLE编程主要是处理各类消息,由peripheral_app.c负责。
main.c是应用入口,实现了main函数如下:
int main(void)
{
extern uint32_t random_seed_value;
srand(random_seed_value);
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_le_gap_init();
app_le_profile_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
这里的main函数完全不用修改,所有的RTL8762的程序都是这么写的。
board_init()是硬件初始化
*gap*是GAP初始化,GAP是BLE的重要概念
app_le_profile_init()是GATT初始化,GATT是BLE另一个重要概念
task_init()是OS_IF(freertos)的任务初始化,实时操作系统的任务创建
常用的应该就是这么几个
打个比方如果把蓝牙设备比作商店,那么GAP就是商店的基本信息,比如商店名,地理位置等。而GATT就是商店提供的服务,比如某一类商品,商品的价格,款式,尺寸。根据需要还可以上架新的GATT服务。
如果用BLE手机调试工具来看如下图所示:
其中,Generic Access就是GAP,Unknown Service 和Battery Service 是GATT
为什么BLE手机调试工具可以识别Battery Service,而不能识别Unknown Service。那是因为蓝牙标准组织给GATT服务提供了统一的编号,Battery Service有统一的编号,所以被识别出来,而Unknown Service本来就是自定义的服务,程序提供的编号不在统一编码内,BLE手机调试工具也就无法识别。
main.c 文件include了board.h文件 ,board.h文件是开发板定义文件。
其中以下定义根据需要开启,本文用到了UART和GPIO,所以USE_UART_DLPS和USE_GPIO_DLPS赋值为1,此处很关键,否则程序无法正常运行:
/* if use any peripherals below, #define it 1 */
#define USE_I2C0_DLPS 0
#define USE_I2C1_DLPS 0
#define USE_TIM_DLPS 0
#define USE_QDECODER_DLPS 0
#define USE_IR_DLPS 0
#define USE_RTC_DLPS 0
#define USE_UART_DLPS 1
#define USE_ADC_DLPS 0
#define USE_SPI0_DLPS 0
#define USE_SPI1_DLPS 0
#define USE_SPI2W_DLPS 0
#define USE_KEYSCAN_DLPS 0
#define USE_DMIC_DLPS 0
#define USE_GPIO_DLPS 1
#define USE_PWM0_DLPS 0
#define USE_PWM1_DLPS 0
#define USE_PWM2_DLPS 0
#define USE_PWM3_DLPS 0
点灯用P4_0和P4_1
#define GPIO_OUTPUT_PIN_0 P4_0
#define GPIO_OUTPUT_PIN_1 P4_1
#define GPIO_PIN_OUTPUT GPIO_GetPin(GPIO_OUTPUT_PIN_0)
#define GPIO_PIN_OUTPUT1 GPIO_GetPin(GPIO_OUTPUT_PIN_1)
UART用P3_0和P3_1
#define UART_TX_PIN P3_0
#define UART_RX_PIN P3_1
board初始化: PAD(管脚)、UART
void board_init(void)
{
/**
* @ led gpio init
*/
Pad_Config(GPIO_OUTPUT_PIN_0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE,
PAD_OUT_HIGH);
Pinmux_Config(GPIO_OUTPUT_PIN_0, DWGPIO);
Pad_Config(GPIO_OUTPUT_PIN_1, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE,
PAD_OUT_HIGH);
Pinmux_Config(GPIO_OUTPUT_PIN_1, DWGPIO);
/**
* @ uart
*/
board_uart_init();
}
void board_uart_init(void)
void board_uart_init(void)
{
Pad_Config(UART_TX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(UART_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pinmux_Config(UART_TX_PIN, UART0_TX);
Pinmux_Config(UART_RX_PIN, UART0_RX);
}
设备初始化:GPIO、UART
void driver_init(void)
{
/* Initialize GPIO */
RCC_PeriphClockCmd(APBPeriph_GPIO, APBPeriph_GPIO_CLOCK, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_PIN_OUTPUT|GPIO_PIN_OUTPUT1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_ITCmd = DISABLE;
GPIO_Init(&GPIO_InitStruct);
//GPIO_InitStruct.GPIO_Pin = GPIO_PIN_OUTPUT1;
//GPIO_Init(&GPIO_InitStruct);
//lightUpLed();
/* uart init*/
driver_uart_init();
}
void driver_uart_init(void)
void driver_uart_init(void)
{
UART_DeInit(UART);
RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE);
/* uart init */
UART_InitTypeDef UART_InitStruct;
UART_StructInit(&UART_InitStruct);
/* Config uart baudrate */
UART_InitStruct.div = BaudRate_Table[BAUD_RATE_115200].div;
UART_InitStruct.ovsr = BaudRate_Table[BAUD_RATE_115200].ovsr;
UART_InitStruct.ovsr_adj = BaudRate_Table[BAUD_RATE_115200].ovsr_adj;
UART_InitStruct.parity = UART_PARITY_NO_PARTY;
UART_InitStruct.stopBits = UART_STOP_BITS_1;
UART_InitStruct.wordLen = UART_WROD_LENGTH_8BIT;
UART_InitStruct.rxTriggerLevel = 16; //1~29
UART_InitStruct.idle_time = UART_RX_IDLE_2BYTE; //idle interrupt wait time
UART_Init(UART, &UART_InitStruct);
uart_sendString1("#### # ##### # # # #### ##### # ##### #### \r\n");
uart_sendString1(" # # # # # # # # # # # # # # # \r\n");
uart_sendString1(" # # # # # # # # # # # # # # # \r\n");
uart_sendString1(" ### # #### # # # # #### # # #### # # \r\n");
uart_sendString1(" # # # # # # ##### # # # # # # # \r\n");
uart_sendString1(" # # # # # # # # # # # # # # # \r\n");
uart_sendString1("#### ##### ##### ### # # # # # ##### ##### #### \r\n");
uart_sendString1(" ##### ##### \r\n");
uart_sendString1("Sarting......");
}
/*低电平点灯*/
void lightUpLed(void)
{
/* Light up LED0 */
GPIO_WriteBit(GPIO_PIN_OUTPUT, (BitAction)(0));
GPIO_WriteBit(GPIO_PIN_OUTPUT1,(BitAction)(0));
}
/*高电平灭灯*/
void lightDownLed(void)
{
/* Light down LED0 */
GPIO_WriteBit(GPIO_PIN_OUTPUT, (BitAction)(1));
GPIO_WriteBit(GPIO_PIN_OUTPUT1,(BitAction)(1));
}
/*闪灯*/
void flashLed(void)
{
for(uint32_t j=0;j<5;j++)
{
lightUpLed();
for (uint32_t i = 0; i < 100000; i++);
lightDownLed();
}
}
其中uart_senddata_continuous是官方DEMO里的函数
void uart_sendString1(char* str)
{
uint16_t demo_str_len = 0;
demo_str_len = strlen(str);
memcpy(String_Buf1, str, demo_str_len);
/* Send demo tips */
uart_senddata_continuous(UART, String_Buf1, demo_str_len);
}
在peripheral_app.c中,查找“BAS_READ_BATTERY_LEVEL”做如下修改
改成静态变量:static uint8_t battery_level = 90;
添加:battery_level++;
添加用于点灯:lightUpLed();
添加用于UART输出信息:uart_sendString1("Get BATTERY LEVEL\r\n");
case SERVICE_CALLBACK_TYPE_READ_CHAR_VALUE:
{
if (p_bas_cb_data->msg_data.read_value_index == BAS_READ_BATTERY_LEVEL)
{
static uint8_t battery_level = 90;
APP_PRINT_INFO1("BAS_READ_BATTERY_LEVEL: battery_level %d", battery_level);
uart_sendString1("Get BATTERY LEVEL\r\n");
bas_set_parameter(BAS_PARAM_BATTERY_LEVEL, 1, &battery_level);
battery_level++;
//trun_led(0);
lightUpLed();
}
}
app_task.c中增加timer的任务,回调函数是timer_callback;
另外用task方式建了一个uart任务,实验task方式处理UART。
void app_task_init()
{
// void *p_handle=NULL;
os_task_create(&app_task_handle, "app", app_main_task, 0, APP_TASK_STACK_SIZE,
APP_TASK_PRIORITY);
//timer task
if(os_timer_create(&p_handle,"timer",0,1000,true,timer_callback )== true)
{
os_timer_start(&p_handle);
}
else
{
//Timer failed to creat.
}
//uart task
os_task_create(&uart_task_handle, "uart", uart_task, 0, APP_TASK_STACK_SIZE,
APP_TASK_PRIORITY);
}
timer的任务的处理函数,回调函数负责闪烁LED(用了PAD方式,不是GPIO),timer_stop负责停止定时,闪烁停止;timer_restart负责重新定时,继续闪烁。
oid timer_callback(void *p_handle)
{
static uint8_t count =0;
if(count == 0)
{
Pad_Config(P4_0,PAD_SW_MODE,PAD_IS_PWRON,PAD_PULL_NONE,PAD_OUT_ENABLE,PAD_OUT_LOW);
count = 1;
}
else{
Pad_Config(P4_0,PAD_SW_MODE,PAD_IS_PWRON,PAD_PULL_NONE,PAD_OUT_ENABLE,PAD_OUT_HIGH);
count = 0;
}
}
void timer_stop(void){
os_timer_stop(&p_handle);
Pad_Config(P4_0,PAD_SW_MODE,PAD_IS_PWRON,PAD_PULL_NONE,PAD_OUT_ENABLE,PAD_OUT_HIGH);
}
void timer_restart(void){
os_timer_restart(&p_handle,100);
}
在peripheral_app.c中,查找“GAP_CONN_STATE_DISCONNECTED”,增加timer_restart(),实现在蓝牙断开后,LED继续闪烁。在“GAP_CONN_STATE_CONNECTED”后面增加timer_stop();,实现蓝牙连接后,LED停止闪烁。
case GAP_CONN_STATE_DISCONNECTED:
{
if ((disc_cause != (HCI_ERR | HCI_ERR_REMOTE_USER_TERMINATE))
&& (disc_cause != (HCI_ERR | HCI_ERR_LOCAL_HOST_TERMINATE)))
{
APP_PRINT_ERROR1("app_handle_conn_state_evt: connection lost cause 0x%x", disc_cause);
}
le_adv_start();
timer_restart();
}
break;
case GAP_CONN_STATE_CONNECTED:
{
uint16_t conn_interval;
uint16_t conn_latency;
uint16_t conn_supervision_timeout;
uint8_t remote_bd[6];
T_GAP_REMOTE_ADDR_TYPE remote_bd_type;
le_get_conn_param(GAP_PARAM_CONN_INTERVAL, &conn_interval, conn_id);
le_get_conn_param(GAP_PARAM_CONN_LATENCY, &conn_latency, conn_id);
le_get_conn_param(GAP_PARAM_CONN_TIMEOUT, &conn_supervision_timeout, conn_id);
le_get_conn_addr(conn_id, remote_bd, &remote_bd_type);
APP_PRINT_INFO5("GAP_CONN_STATE_CONNECTED:remote_bd %s, remote_addr_type %d, conn_interval 0x%x, conn_latency 0x%x, conn_supervision_timeout 0x%x",
TRACE_BDADDR(remote_bd), remote_bd_type,
conn_interval, conn_latency, conn_supervision_timeout);
timer_stop();
}
1、源码这里下载https://download.csdn.net/download/weixin_44067125/87995309
2、主要实现以下功能:
BATTERY LEVEL 读取电量自增1、点亮LED
自定义characteristic 发送HEX数据:0A点亮led,发送14熄灭led,发送其他LED闪烁
蓝牙未连接时,LED闪烁,连接时LED熄灭
3、BLE基本概念需要了解
4、RTOS实时操作系统要了解一些,至少知道创建task
5、KEIL编译,MPTOOL烧写
6、手机上的BLE调试工具需要安一个,我用的是沁恒家的
7、板子:RTL8762C 开发板 - 嘉立创EDA开源硬件平台
RTL8762的手册和SDK前前后后看了一个月,终于可以点灯。小白自学没办法,都是新知识,卡在一个地方好久都出不来。
UART串口助手接收信息:
发送14(HEX)灭LED
接收电量信息,每次加1,LED亮
发送0A(HEX),LED亮
文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99
文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效
文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是
文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件
文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件
文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码
文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware
文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停
文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待
文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析
文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code
文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象