nRF52832-基本外设使用-PPI使用

nRF52832-PPI使用

环境介绍

PC环境:Windows
IDE环境:ARM-MDK5
SDK环境:nRF5 SDK v15.1.0
硬件环境:nRF52832开发板(外设配置与官方pca10040开发板一致)

使用官方的pwm_library例程,该位于nRF5_SDK_15.1.0_a8c0c4d\examples\peripheral\ppi\pca10040\blank\arm5_no_packs目录下。
注:pca10040代表nRF52832例程,blank代表基础应用工程,ARM-MDK5选择arm5_no_packs

PPI使用配置

PPI配置步骤如下:

  1. 实例化PPI的通道
  2. 初始化PPI外设
  3. 为PPI通道申请内存,指定事件触发相关任务
  4. 使能PPI

PPI相关参数说明:

  • nRF52832的PPI通道由20个,在nrf52832_peripherals.h文件中定义
  • 使用SoftDevice时,timer0不能使用

官方源码分析

官方源码中PWM应用在main.c文件实现,本文根据个人理解作相应的注释,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#define PPI_EXAMPLE_TIMERS_PHASE_SHIFT_DELAY    (10)    // 1s = 10 * 100ms (定时器1和定时器2的启动时间间隔)
#define PPI_EXAMPLE_TIMER0_INTERVAL (100) // Timer0的定时时间 = 100ms
#define PPI_EXAMPLE_TIMER1_INTERVAL (2000) // Timer1的定时时间 = 2s
#define PPI_EXAMPLE_TIMER2_INTERVAL (2000) // Timer2的定时时间 = 2s


static const nrf_drv_timer_t m_timer0 = NRF_DRV_TIMER_INSTANCE(0); //实例化Timer0
static const nrf_drv_timer_t m_timer1 = NRF_DRV_TIMER_INSTANCE(1); //实例化Timer1
static const nrf_drv_timer_t m_timer2 = NRF_DRV_TIMER_INSTANCE(2); //实例化Timer1

static nrf_ppi_channel_t m_ppi_channel1; //PPI实例化channel1
static nrf_ppi_channel_t m_ppi_channel2; //PPI实例化channel2

static volatile uint32_t m_counter; //定义变量,技术Timer0的中断次数

//Timer0的回调函数
static void timer0_event_handler(nrf_timer_event_t event_type, void * p_context)
{
++m_counter;
}

/* Timer1和Timer2的回调函数,由于Timer1和Timer2只用于PPI,所以没有使用 */
static void empty_timer_handler(nrf_timer_event_t event_type, void * p_context)
{
}


/** @brief PPI初始化函数.
*/
static void ppi_init(void)
{
uint32_t err_code = NRF_SUCCESS;

err_code = nrf_drv_ppi_init(); //PPI初始化
APP_ERROR_CHECK(err_code);

//PPI的channel1初始化,通过Timer1的NRF_TIMER_EVENT_COMPARE0时间来使Timer0停止运行
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1); //PPI的channel1申请内存
APP_ERROR_CHECK(err_code);
//PPI的channel1分配任务,通过Timer1的NRF_TIMER_EVENT_COMPARE0时间来使Timer0停止运行
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1,
nrf_drv_timer_event_address_get(&m_timer1,
NRF_TIMER_EVENT_COMPARE0),
nrf_drv_timer_task_address_get(&m_timer0,
NRF_TIMER_TASK_STOP));
APP_ERROR_CHECK(err_code);


//PPI的channel2初始化,通过Timer2的NRF_TIMER_EVENT_COMPARE0时间来使Timer0恢复运行
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel2); //PPI的channel2申请内存
APP_ERROR_CHECK(err_code);
//PPI的channel2分配任务,通过Timer2的NRF_TIMER_EVENT_COMPARE0时间来使Timer0恢复运行
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel2,
nrf_drv_timer_event_address_get(&m_timer2,
NRF_TIMER_EVENT_COMPARE0),
nrf_drv_timer_task_address_get(&m_timer0,
NRF_TIMER_TASK_START));
APP_ERROR_CHECK(err_code);

// 使能PPI channels1、channels2
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel2);
APP_ERROR_CHECK(err_code);
}


/** @brief Timer 0 初始化函数.
* @details 实现功能:100ms触发一次NRF_TIMER_CC_CHANNEL0事件
*/
static void timer0_init(void)
{
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.frequency = NRF_TIMER_FREQ_31250Hz;
ret_code_t err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer0_event_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_timer_extended_compare(&m_timer0,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_ms_to_ticks(&m_timer0,
PPI_EXAMPLE_TIMER0_INTERVAL),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
}

/** @brief Timer 1 初始化函数.
* @details 实现功能:2s触发一次NRF_TIMER_CC_CHANNEL0事件,通过PPI使Timer0停止运行
*/
static void timer1_init(void)
{
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.frequency = NRF_TIMER_FREQ_31250Hz;
ret_code_t err_code = nrf_drv_timer_init(&m_timer1, &timer_cfg, empty_timer_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_timer_extended_compare(&m_timer1,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_ms_to_ticks(&m_timer1,
PPI_EXAMPLE_TIMER1_INTERVAL),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
false);
}


/** @brief Timer 2 初始化函数.
* @details 实现功能:2s触发一次NRF_TIMER_CC_CHANNEL0事件,通过PPI使Timer0恢复运行
*/
static void timer2_init(void)
{
// Check TIMER2 configuration for details.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.frequency = NRF_TIMER_FREQ_31250Hz;
ret_code_t err_code = nrf_drv_timer_init(&m_timer2, &timer_cfg, empty_timer_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_timer_extended_compare(&m_timer2,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_ms_to_ticks(&m_timer2,
PPI_EXAMPLE_TIMER2_INTERVAL),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
false);
}

/**
* @brief Function for application main entry.
*/
int main(void)
{
uint32_t old_val = 0;
uint32_t err_code;

err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);

NRF_LOG_DEFAULT_BACKENDS_INIT();

ppi_init(); //PPI初始化
timer0_init(); // Timer 0初始化
timer1_init(); // Timer 1初始化
timer2_init(); // Timer 2初始化

NRF_LOG_INFO("PPI example started."); //打印信息

nrf_drv_timer_enable(&m_timer0); //使能Timer0

nrf_delay_us(5); //延迟5us,保障Timer0能在PPI触发前执行中断
nrf_drv_timer_enable(&m_timer1); //使能Timer1

m_counter = (uint32_t)-PPI_EXAMPLE_TIMERS_PHASE_SHIFT_DELAY;

while (m_counter != 0) //这里目的使等到10s,m_counter = -10,通过Timer0每100ms加一
{
// just wait
}
nrf_drv_timer_enable(&m_timer2); //使能Timer2,奇数秒Timer1触发停止Timer0,偶数秒Timer2触发运行Timer0

while (true)
{
uint32_t counter = m_counter;
if (old_val != counter) //Timer0使能时,每100ms打印一次counter值
{
old_val = counter;

NRF_LOG_INFO("Current count: %u", counter);
NRF_LOG_FLUSH();
}
}
}

官方的例程实现过程以及效果:

  • 实例化Timer0(100ms)、Timer1(2s)、Timer2(2s)
  • 实例化PPI的channel1、channel2
  • 实现Timer0回调函数,Timer1和Timer2回调函数(没有实际应用)
  • 初始化PPI
    • 配置PPI的channel1用于Timer2事件触发停止Timer0
    • 配置PPI的channel1用于Timer2事件触发运行Timer0
  • 使能PPI的channel1、channel2
  • 初始化Timer
    • Timer0每100ms触发NRF_TIMER_CC_CHANNEL0事件,m_counter变量加1
    • Timer1每2s触发NRF_TIMER_CC_CHANNEL0事件
    • Timer2每2s触发NRF_TIMER_CC_CHANNEL0事件
  • 主函数实现奇数秒Timer1触发停止Timer0,偶数秒Timer2触发运行Timer0,Timer0使能时,每100ms打印一次counter值

实现效果:串口一秒打印十次counter值,然后停止一秒,依次循环
串口配置:

  • 波特率:115200
  • 8位数据位
  • 1位停止位
  • 无奇偶校验
  • 无流控

使用串口助手查看的效果如下:

资源参考

官方文档:http://infocenter.nordicsemi.com/index.jsp

例程说明位于官方文档目录:Software Development Kit > nRF5 SDK > nRF5 SDK v15.1.0 > Examples > Hardware peripheral examples > PPI Example

timer相关API函数说明位于官方目录:Software Development Kit > nRF5 SDK > nRF5 SDK v15.1.0 > API Reference > Peripheral drivers > Peripheral drivers > PPI

代码烧录

  1. 擦除FLASH
  2. 烧写应用程序

具体操作参考:nRF52832-程序下载

本文标题:nRF52832-基本外设使用-PPI使用

文章作者:LGG001

发布时间:2018年09月09日 - 15:09

最后更新:2019年01月21日 - 20:01

原始链接:http://yoursite.com/2018/09/09/nRF52832-基本外设使用-PPI使用/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
Thank You For Your Approval !