nRF52832调试相关记录
nRF52832调试相关记录
背景
以前业余用nRF52832做了个物联网小项目(蓝牙微微网),做了些相关的调试记录
nRF52832寄存器类型
- Task: 任务寄存器,可以由程序或事件触发
- Event: 事件寄存器,事件可以产生中断和触发任务
- Register: 普通寄存器,和一般单片机的寄存器一样
Jlink Keil
keil5带的驱动过高为v6.16,手头上的jlink固件为V8,所以要下载4.9的驱动,最后用的是V6.12j,可以在jlink官网上下载。
替换MDK(MDK524)安装目录下的Segger文件夹,
报Clone,重刷jlink的V8固件,自定义ID,SN
- 在KEIL中设置中使用jink报错:
error:cannot load driver".....JL2CM3.dll"
:
将keil安装目录的Segger路径,如D:\Keil_v5\ARM\Segger
添加到系统环境变量
用Sergger包覆盖Keil安装目录下的
nRFgo Studio 和 Nrfjprog 无法找到JLinkARM.dll的解决方法:
手动修改注册表jlink安装的相应字段HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\SEGGER\J-Link\InstallPath
HKEY_CURRENT_USER\Software\SEGGER\J-Link\InstallPath
因为卸载/更新Segger的时候,注册表的相关的字段是不会被修改的
编译问题
警告
keil编译C++代码时,出现警告: >#1035-D: single-precision operand implicitly converted to double-precision
float代表浮点型数据类型,浮点型数据又分为单精度和双精度两种,1.0小写f或者大写F代表他是单精度的,如果1.0后面跟的是小写d后者大写D代表他是双精度的。
可以忽略这个警告,也可以在所有的浮点数字后面加上f,警告就会消失。比如:float a = 1.01f;
NRF52 SDK16.0编译问题:
https://blog.csdn.net/mygod2008ok/article/details/103444490
使用NordicSemiconductor.nRF_DeviceFamilyPack.8.29.0
来代替
select software packs:
选择fixed和excluded,点OK确定
先将原先的Device下的Startup和StartupConfig不选中,点击OK确定,然后再次打开此窗口
manage run…:
选中Device下的选项,点OK确定
编译报错:
将nRF5_SDK_16.0.0_98a08e2\modules\nrfx\mdk
目录下的system_nrf52.c
文件复制到nRF5_SDK_16.0.0_98a08e2\examples\ble_peripheral\ble_app_uart\pca10040\s112\arm5_no_packs\RTE\Device\nRF52832_xxAA
目录下并替换掉此目录下的system_nrf52.c
文件,然后重新编译
微微网
SDK16:
主机demo:examples\ble_central\ble_app_multilink_central
从机demo:examples\ble_peripheral\ble_app_blinky
sdk_config.h
配置文件使能相应模块,可在下面切换成configuration Wizard
选项卡勾选
添加相应的库或驱动文件到工程,并添加相应的文件路径(Options for Target选项卡的C/C++中)
DEBUG
RTT:
sdk_config.h
文件中配置:NRF_LOG_BACKEND_RTT_ENABLED
置1NRF_LOG_BACKEND_UART_ENABLED
置0
- keil配置
Debug->Trace 中的 Enable 打钩
打开MDK魔术棒,C/C++选项,Define中添加一个DEBUG,定义宏DEBUG - 使用J-Link RTT Viewer查看
注:还是不行的话,重新插拔下JLINK,或者重启下电脑
J-Link安装的地方
NRF_LOG_INFO打印浮点数:NRF_LOG_INFO("Lux:" NRF_LOG_FLOAT_MARKER "\r\n", NRF_LOG_FLOAT(*lux));
PPI
ADC:
PPI双缓冲中断采样ADC
在BLE中使用注意timer不能用Timer0,协议栈用了
https://blog.csdn.net/Smile_Smilling/article/details/91352391
双缓冲PPI采样:设置采样时间(这个采样时间是采样一个点的时间,如果采样时间设置为400ms,采集5个点,则全部的采样时间为400*5=2s。如果为两个通道,则400ms取两个点,取五次)
同时将ADC,PPI,TIMER都关掉,可以有效降低功耗
启动ADC
void ADC_PWM_START(void)
{
nrf_saadc_enable();
nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
nrf_drv_timer_enable(&m_timer);
}
关闭ADC,需要将相关标志位清楚,不然会导致错误
void ADC_PWM_STOP(void)
{
nrf_drv_timer_disable(&m_timer);
nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
nrf_saadc_disable();
}
- 单次模式
- 连续模式
使用ADC内部定时器实现定时采样
使用nRF52832的通用定时器定时同PPI触发采样,实现连续采样 - 扫描模式
当使能一个ADC通道,ADC工作于单次模式,当使能的通道数量大于1个,ADC进入扫描模式
配置双缓冲区域,这个比较有意思,采样是按你上面初始化了的通道顺序循环的,这里我建议设置成前面通道数的倍数,不然你分不清是哪个通道的数据。比如这里我有3个通道,缓冲区域大小为6,那么每采集两组以后才会触发回调函数,采集之中CPU是不会工作的,而双缓冲的设置还保证了连续性
电池电量采集及多通道采集
RTC
- 新建一个1秒定时的APP_TIMER。
优点:创建方便。
缺点:实时性太差。
APP_TIMER采用轮询执行而非抢占的方式,假如其它APP_TIMER耗时较长,例如有一个APP_TIMER在采集心率,万年历的APP_TIMER就必须等采集心率完成才能执行。 - 在APP_TIMER的中断中插入计算时间戳的代码
优点:修改代码较少。
缺点:代码耦合性高,易出现未知问题。
我们都知道APP_TIMER的实现就是使用了RTC1,那么我们就可以利用上RTC1的特点,适当修改代码来实现我们的需求,但这样容易造成代码耦合性高,不易维护和管理。 - 用一个新的RTC来实现。
优点:不会影响原来APP_TIMER的功能,模块独立,方便管理。
缺点:需要深入了解芯片的RTC,根据RTC特性来实现自己的功能。
协议栈也是用到LFCLK时钟,所以在ble_stack_init函数中LFCLK已经被使能了
如实现8HZ的频率,则PRESCALER 寄存器应该设为
32768/8-1 = 4095
https://www.jianshu.com/p/1afe0d565825
内部温度采集
- 初始化
///内部温度传感器初始化 nrf_temp_init();
- 如果使用了SoftDevice,就要如下调用:
int32_t temp; while (sd_temp_get(&temp)!=NRF_SUCCESS); // Die temperature in 0.25 degrees celsius. printf("Temp:%d", (int)temp*25/100);
- 如果使用裸机,则如下调用:
//开始温度测量 NRF_TEMP->TASKS_START = 1; while(NRF_TEMP->EVENTS_DATARDY == 0) { } NRF_TEMP->EVENTS_DATARDY = 0; in_temp = nrf_temp_read()/4; /*寄存器0.25摄氏度一个进位级*/ NRF_TEMP->TASKS_STOP = 1; /** Stop the temperature measurement. */
获取从机MAC
sd_ble_gap_addr_get( &addr);
https://www.jianshu.com/p/f56e0d9e9432
cJson
拷贝cJSON.c
、cJSON.h
、cJSON_Utils.c
、cJSON_Utils.h
到工程中,malloc.h
常运行cJSON的测试例程需要3KB的heap,如果芯片内存足够,可以在启动文件(startup_XXX.s)里修改Heap_Size
串口-UARTE
https://blog.csdn.net/qq_36347513/article/details/104478737
芯片唯一ID
获取自己的MAC
#include "ble_gap.h"
ble_gap_addr_t addr;
uint32_t err_code = sd_ble_gap_addr_get(&addr);
APP_ERROR_CHECK(err_code);
addr.addr[5 - i];
时钟选择
低频时钟源
NRF_SDH_CLOCK_LF_SRC
// <0=> NRF_CLOCK_LF_SRC_RC
// <1=> NRF_CLOCK_LF_SRC_XTAL
// <2=> NRF_CLOCK_LF_SRC_SYNTH
GPIO输入中断接口使用
添加相关nrf_drv_gpiote.h
uint32_t err_code = NRF_SUCCESS;
err_code = nrf_drv_gpiote_init(); // GPIOE驱动初始化,如有其它GPIO中断只调用一次
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(false);//// 双边沿中断触发 //true表示高精度,false表示低精度
in_config.pull = NRF_GPIO_PIN_PULLUP;
//in_config.sense = NRF_GPIOTE_POLARITY_TOGGLE;
err_code = nrf_drv_gpiote_in_init(KEY1, &in_config, key_event_handler);
err_code = nrf_drv_gpiote_in_init(KEY2, &in_config, key_event_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(KEY1, true);
nrf_drv_gpiote_in_event_enable(KEY2, true);
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
nrf_drv_gpiote_out_toggle(LED_2);
}
低功耗
2.6mA->(切换为DCDC、使用内部RC、关闭Log)->2.1mA->(动态关闭ADC)->180uA->(去掉LDO)->13uA->去掉LED初始化(GPIO)->3uA
默认PIN码:0000
串口不定长接收
https://blog.csdn.net/qq_33784286/article/details/104899319
DMA的一次接收的数据长度还是一次发送数据长度都不得大于255
libuarte:examples\peripheral\libuarte\main.c
定时器:
- 加入驱动文件
- sdk_config.h中打开配置
watchdog
- sdk_config.h中打开配置
WDT_ENABLED
- 添加看门狗驱动
modules\nrfx\drivers\src\nrfx_wdt.c
- 添加头文件
nrf_drv_wdt.h
integration\nrfx\legacy\nrf_drv_wdt.h
- 添加相关初始化和处理代码
nrf_drv_wdt_channel_id m_channel_id; void wdt_event_handler(void) { bsp_board_leds_off(); //NOTE: The max amount of time we can spend in WDT interrupt is two cycles of 32768[Hz] clock - after that, reset occurs } /************************************************************* * @brief 看门狗初始化 * @param * @param * @retval None *************************************************************/ void WDT_Init(void) { uint32_t err_code = NRF_SUCCESS; //Configure WDT. nrf_drv_wdt_config_t config = NRF_DRV_WDT_DEAFULT_CONFIG; err_code = nrf_drv_wdt_init(&config, wdt_event_handler); APP_ERROR_CHECK(err_code); err_code = nrf_drv_wdt_channel_alloc(&m_channel_id); APP_ERROR_CHECK(err_code); nrf_drv_wdt_enable();// 使能WDT } /************************************************************* * @brief 喂狗函数 * @param * @param * @retval None *************************************************************/ void FEED_DOG(void) { nrf_drv_wdt_channel_feed(m_channel_id); }
WDT_CONFIG_RELOAD_VALUE
为喂狗最大周期,单位为ms
注意喂狗时机
扫描
扫描函数sd_ble_gap_scan_start
https://blog.csdn.net/weixin_42396877/article/details/85327788
注意:
调用函数sd_ble_gap_scan_start()
时,需要应用通过p_adv_report_buffer
保持 memory pointed, 直到p_adv_report_buffer
被释放。当扫描被停止,或这个功能被调用,且有另一个缓冲区,p_adv_report_buffer
将被释放。
在下列情况,扫描将被自动停止.
sd_ble_gap_scan_stop()
函数被调用sd_ble_gap_connect()
数被调用- BLE_GAP_EVT_TIMEOUT被设置,且 BLE_GAP_TIMEOUT_SRC_SCAN 事件出现
- BLE_GAP_EVT_ADV_REPORT 事件出现,且 ble_gap_adv_report_type_t::status 没有被设置为 BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA;此条件下,扫描是被暂停,以便应用获取接收到的数据。应用需调用继续扫描,或是调用 sd_ble_gap_scan_stop() 来停止扫描。
BLE_GAP_EVT_ADV_REPORT 事件出现,且 ble_gap_adv_report_type_t::status 被设置为 BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA;此条件下,扫描继续进行,应用将从广播事件中获取更多报告。这些报告将包括旧的和新的接收到数据。
主机的扫描参数
static ble_gap_scan_params_t m_scan_param = /**< Scan parameters requested for scanning and connection. */
{
.active = 0x01, // 主动扫描
.interval = NRF_BLE_SCAN_SCAN_INTERVAL, // 扫描间隔
.window = NRF_BLE_SCAN_SCAN_WINDOW, // 扫描窗口
.filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL,
.timeout = NRF_BLE_SCAN_SCAN_DURATION, // 扫描超时
.scan_phys = BLE_GAP_PHY_1MBPS,
.extended = true,
};
- active:是否主动扫描,配置为1则是主动扫描,0则是被动扫描
- interval:扫描间隔,控制器间隔多长时间扫描一次,也就是两个连续的扫描窗口开始时间的时间间隔。在 NRF 上设置为 0x0004 and 0x4000 in 0.625ms units(2.5ms 到 10.24s)
- window:扫描窗口,每次扫描所持续的时间,在持续时间内,扫描设备一直在广播信道上运行。在 NRF 上设置为 0x0004 and 0x4000 in 0.625ms units(2.5ms 到 10.24s),两个连续的扫描窗口的起始时间之间的时间差,包括扫描休息的时间和扫描进行的时间
- filter_policy:扫描筛选策略,也就是说接受任何广播数据或者仅仅接受白名单设备的广播数据包。实际上就决定是否使用白名单过滤广播数据包。这里注意一点,如果定向广播数据包中的目的地址并非是自己的,那么该数据必须抛弃,即使广播数据包的发送者在自己的白名单中。
- timeout:扫描超时,超过指定的时间后,没有扫描到设备将停止扫描。在 NRF 上设置为 0x0001 and 0xFFFF in seconds,设置为 0 则认为没有 timeout
- scan_phys:扫描的物理层速度
从机广播间隔
主机连接间隔
MIN_CONN_INTERVAL
https://blog.csdn.net/sinat_23338865/article/details/51533312
nrf52832 连接参数更新过程
ble 连接参数更新过程如下, 一般分三个过程:
- 主机发起连接(带有一个连接参数,一般都是 7.5ms)
- 主机更新连接参数 (举例:NRF CONNECT 安卓app软件 45ms)。
- 从机更新连接参数(一般有延时机制,4S,更新 程序设置的 最大连接间隔 MAX_CONN_INTERVAL)
nRF5 SDK 中 有决定 第三步 是否更新连接参数宏NRF_BLE_CONN_PARAMS_ENABLED
sdk_config.h
文件中。
cJSON
堆栈大小
内存分配
字节对齐
cJSON_Delete使用
慎用动态分配内存
LOG
NRF_LOG_FINAL_FLUSH();
主要是在sdk_config.h中配置两个宏:
- NRF_LOG_ENABLED
- NRF_LOG_BACKEND_RTT_ENABLED
将这两个值 的宏定义从0改为1,然后程序中使用 NRF_LOG_INFO(“test info”),即可在debug时在调试窗口看到打印的log。
最后别忘了在主循环中使用:NRF_LOG_PROCESS(); 这个函数,否则也不会有打印信息出来。
nRF_Log还有一个功能:如果不使能Deferred,那么调用NRF_LOG_INFO等API的时候,立马就Flush,即把日志打印出去;如果使能了Deferred,那么调用NRF_LOG_INFO等API的时候,只是把打印数据放在RAM中,真正的打印由main函数中的NRF_LOG_PROCESS完成,相关宏可以在sdk_config.h中找到 , 即NRF_LOG_DEFERRED
原文链接:https://blog.csdn.net/behold1942/article/details/90692763
报错
<error> nrf_ble_gq: SD GATT procedure (1) failed on connection handle 1 with error: 0x00000008.
<error> app: ERROR 8 [NRF_ERROR_INVALID_STATE] at ..\..\..\ble_btf.c:174