本专栏由非官方人员 王小小海 所著,其内容主要记录了在开发NCS的过程中遇到的一些问题和解决方法,还有一些应用的例程。作者本人也是在实践应用中遇到的问题,想着把这些问题分享给可能遇到的朋友。仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。
本专栏不涉及基础的安装和环境搭建问题,本例程开发使用NCS 3.0.0 以上开发,还请注意!。
本文代码位置
https://gitee.com/seliverwang/nrf54l_ppi_example.git
所有分享内容
笔记分享
- 【笔记分享】NCS下radio_test添加FEM
- 【笔记分享】5340基于 BLE LBS 自定义网络核固件点亮LED并合并固件
- 【笔记分享】5340基于LBS自定义网络核双核DFU实现
- 【笔记分享】5340 设置public address 和 random address
- 【笔记分享】NCS nRF52/53 添加LVGL组件驱动屏幕
- 【笔记分享】VirtualBox Ubuntu22.04 不能使用nrfjprog问题记录
- 【笔记分享】5340使用内部负载电容调频偏
- 【笔记分享】基于 LE Audio 例程移植到nRF52840上运行思路
- 【nRF52/53】【笔记分享】基于 BLE LBS DFU使用内部外部Flash 升级
- 【nRF54H20】基础介绍与使用介绍
- 【笔记分享】nRF54H20 SPI速率范围记录
- 【笔记分享】NCS/Zephyr 使能SPI SD卡方法介绍
- 【笔记分享】NCS/Zephyr USB MSC实现方法介绍
应用分享
暂无
前言
nRF54L系列出来了已经快一年了,很多朋友 苦于对 nRF54L的开发,有朋友咨询我能不能分享一下 nRF54L的 PPI绑定教程,之前52能用为什么54不能用?
我看了他发过来的代码,发现其实是因为 nRF54L PPI跨域的问题。
作用域介绍
打开 54L的数据手册,看到整体框图就可以知道,哪些外设是在哪些域里面了。不像之前52系列,所有外设不分域,想用PPI连接哪个,就可以连接哪个。
当然需要看是否存在跨域的现象,如果无跨域,那么使用还是跟之前52的相同只不过外设从,PPI名字变成了DPPIC而已。
那么跨域改怎么实现呢?主要还是需要看懂下面的图,并且结合上面的系统框架图。
举例解释
想实现 TIMER10的定时器事件,输出到 P1 的一个IO口 , 那么可以看到上面的整体系统框架图,TIMER10在 RADIO PD,而P1在 PERI PD,那么他们之间是无法直接通过PPI对应连接起来,必须通过PPIB11 去连接 PPIB21然后实现桥接的原理。
那么我们梳理一下:
- 初始化TIMER10的配置,1ms定时。
- 从DPPIC10中申请一个channel,然后绑定到TIMER10的 定时器捕捉事件上。我们假设命名为:dppic10_timer_channel。
- 将 dppic10_timer_channel 绑定到 ppib11上,ppib11有2种事件,一种是接受,一种是发送,我们是推送事件肯定就设置推送。
- 初始化P1的一个IO口,初始化为GPIOTE(只有GPIOTE才能通过其他事件触发IO口)。
- 从DPPIC20中申请一个channel,然后绑定到GPIOTE的 TASK事件上。我们假设命名为:dppic20_gpiote_channel。
- 将 dppic20_gpiote_channel 绑定到 ppib21上,我们是接收事件,就设置PPIB21的属性为接收。
- 开启 dppic10_timer_channel 和 dppic20_gpiote_channel 这两个通道就可以了。
以上是整个步骤的实现思路,下面让我们来具体实现吧!
操作步骤
步骤一
创建一个任意的工程,我就用 hallo_world例程,编译下来没问题吧?
步骤二
这是一个啥配置都没有,那么我们开始 开启一些需要的东西。 嫌麻烦的同学直接打开 gitee链接,抄抄抄~~
在添加pj.conf病开启PPI相关宏
#
# Copyright (c) 2019 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
######################################################
# Logging configuration
######################################################
CONFIG_NCS_BOOT_BANNER=n
CONFIG_BOOT_BANNER=n
CONFIG_PRINTK=n
CONFIG_LOG=y
CONFIG_LOG_SPEED=y
CONFIG_LOG_MODE_DEFERRED=y
CONFIG_LOG_BUFFER_SIZE=8192
CONFIG_LOG_BACKEND_UART=y
CONFIG_USE_SEGGER_RTT=n
CONFIG_LOG_BACKEND_RTT=n
CONFIG_RTT_CONSOLE=n
######################################################
# Peripheral configuration
######################################################
# Enable All necessary peripherals
# RGB
CONFIG_GPIO=y
# 通用PPIB使能
CONFIG_NRFX_GPPI=y
CONFIG_NRFX_PPIB00=y
CONFIG_NRFX_PPIB10=y
CONFIG_NRFX_PPIB11=y
CONFIG_NRFX_PPIB21=y
# GPIO2的时钟域
CONFIG_NRFX_DPPI00=y
# 不要更改, 由于RADIO和DPPI10在同一个时钟域.
CONFIG_NRFX_DPPI10=y
# GRTC的时钟域
# GPIO1的时钟域
CONFIG_NRFX_DPPI20=y
步骤三
编写绑定代码:
static void ppi_timer10_init(void)
{
NRF_TIMER10->PRESCALER = 4;
NRF_TIMER10->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
NRF_TIMER10->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
NRF_TIMER10->CC[0] = 1000;
NRF_TIMER10->TASKS_CLEAR = 1;
NRF_TIMER10->TASKS_START = 1;
}
static int timer_diff_domain_init(void)
{
int err;
// 跨域绑定流程.
//
uint8_t dppi10_timer_channnel;
uint8_t dppi20_gpiote_channnel;
uint8_t ppib_addr_ch;
uint8_t timer_gpio_channel;
nrfx_dppi_t dppic10 = NRFX_DPPI_INSTANCE(10);
nrfx_dppi_t dppic20 = NRFX_DPPI_INSTANCE(20);
nrfx_ppib_interconnect_t ppib11_21 = NRFX_PPIB_INTERCONNECT_INSTANCE(11, 21);
const nrfx_gpiote_t timer_gpiote = NRFX_GPIOTE_INSTANCE(NRF_DT_GPIOTE_INST(DT_NODELABEL(led1), gpios));
uint32_t timer_gpio_pin = NRF_DT_GPIOS_TO_PSEL(DT_NODELABEL(led1), gpios);
// 初始化GPIOTE事件.
err = nrfx_gpiote_channel_alloc(&timer_gpiote, &timer_gpio_channel);
if (err != NRFX_SUCCESS)
{
return -ENODEV;
}
nrf_gpiote_task_configure(timer_gpiote.p_reg, timer_gpio_channel, timer_gpio_pin, NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_LOW);
nrf_gpiote_task_enable(timer_gpiote.p_reg, timer_gpio_channel);
// 从DPPIC10 申请通道.
err = nrfx_dppi_channel_alloc(&dppic10, &dppi10_timer_channnel);
if (err != NRFX_SUCCESS)
{
return -ENODEV;
}
// 申请新的PPIB通道
err = nrfx_ppib_channel_alloc(&ppib11_21, &ppib_addr_ch);
if (err != NRFX_SUCCESS)
{
return -ENODEV;
}
// 从DPPIC20 申请一个通道.
err = nrfx_dppi_channel_alloc(&dppic20, &dppi20_gpiote_channnel);
if (err != NRFX_SUCCESS)
{
return -ENODEV;
}
// 绑定事件通道.
NRF_DPPI_ENDPOINT_SETUP(
nrf_timer_event_address_get(NRF_TIMER10, NRF_TIMER_EVENT_COMPARE0), dppi10_timer_channnel);
NRF_DPPI_ENDPOINT_SETUP(
nrf_gpiote_task_address_get(timer_gpiote.p_reg, nrf_gpiote_clr_task_get(timer_gpio_channel)), dppi20_gpiote_channnel);
NRF_DPPI_ENDPOINT_SETUP(
nrfx_ppib_send_task_address_get(&ppib11_21.left, ppib_addr_ch), dppi10_timer_channnel);
NRF_DPPI_ENDPOINT_SETUP(
nrfx_ppib_receive_event_address_get(&ppib11_21.right, ppib_addr_ch), dppi20_gpiote_channnel);
// 使能通道.
nrfx_dppi_channel_enable(&dppic10, dppi10_timer_channnel);
nrfx_dppi_channel_enable(&dppic20, dppi20_gpiote_channnel);
return 0;
}
由于我这不需要中断,我直接用寄存器写TIMER10配置了,原理都一样。
核心步骤是:timer_diff_domain_init 函数。其中最需要注意的是,
nrfx_dppi_t dppic10 = NRFX_DPPI_INSTANCE(10);
nrfx_dppi_t dppic20 = NRFX_DPPI_INSTANCE(20);
nrfx_ppib_interconnect_t ppib11_21 = NRFX_PPIB_INTERCONNECT_INSTANCE(11, 21);
const nrfx_gpiote_t timer_gpiote = NRFX_GPIOTE_INSTANCE(NRF_DT_GPIOTE_INST(DT_NODELABEL(led1), gpios));
uint32_t timer_gpio_pin = NRF_DT_GPIOS_TO_PSEL(DT_NODELABEL(led1), gpios);
由于开发板的led1是 P1.10,所以对应的是 dppic20,不然会没有作用!
结束语
上面配置和代码,基本上就清晰的描述了 本文的核心目标,可以清晰的看懂跨域绑定的流程,大家可以自己举一反三,我在git仓库放了 grtc 驱动同域的 led3,大家也可以参考学习一下。
有不明白的地方欢迎提问,也厚脸皮要个赞或者关注,谢谢各位啦。
本系列文章大多数是本人遇到和解决过的问题,难有疏忽之处,有什么问题或者不明白的地方,欢迎留言询问!