当前位置:   article > 正文

nRF52833-peripheral_the nrf52833 dk is a hardware development platform

the nrf52833 dk is a hardware development platform used to design and develo

目录

一、开发环境的搭建安装

二、NORDIC时钟框图 

2.1:时钟框图 

2.2:总线框图(部分)

2.3 USB框图

2.4 引脚、时钟

2.5 sdk_config.h配置文件 

三、SDK中提供52833的例程

四、外设使用

4.0 外设库函数配置的基本四步骤

4.1 PWM

 4.1.1复杂队列

4.1.2 独立模式

 4.1.3 带回调函数的独立通道

4.1.4 分组模式

 4.1.5 波形加载模式

4.2 定时器

4.2.1 定时器框图

 4.2.2 寄存器法配置

 4.2.3 库函数方式

4.3 GPIOTE 

4.3.1事件模式

 4.3.2 任务模式

4.4 PPI

4.4.1 普通PPI的配置

4.4.2  PPI-GROUP的管理

① CPU参与的方式 

 ②  不需要CPU参与,PPI的方式即把组的开启关闭当做是一个PPI的任务,通过PPI事件触发

 ③ PPI-fork从任务

4.5 PPI-TIMER

4.5.1 精确定时

4.5.2 软件PWM 

4.6 I²S 

4.6.1 时序图

4.6.2 结构体说明

4.6.3 官方例程分析

4.6.4 通过D类功放生成波形


一、开发环境的搭建安装

Nordic nRF5 SDK开发环境搭建(nRF51/nRF52芯片平台) - iini - 博客园 (cnblogs.com)

Keil环境下,只是用nordic的外设只需要安装两个包

CMSIS

② Device family pack(安装过程中有可能会报错,不要管它!

③ nordic板子名称和芯片对应关系

        pca10040 – nRF52832
        pca10040e – nRF52810
        pca10056 – nRF52840
        pca10056e – nRF52811
        pca10100 – nRF52833
        pca10100e – nRF52820

Softdevice命名规则一。Softdevice包括两种底层协议栈:BLE和ANT,BLE包括两种角色:central(又称master)和peripheral(又称slave),为此需要给这些不同类型的协议栈进行命名区分。协议栈命名格式为Sxyz,其中

  • x – 表示协议栈的类型,1表示BLE协议栈,2表示ANT协议栈,3表示同时支持BLE和ANT
  • y – 表示BLE角色,1表示从设备,2表示主设备,3表示同时支持主设备和从设备
  • z – 表示芯片类型,0表示nRF51系列,2表示nRF52系列
  • 比如S110,表示只支持从设备模式的nRF51 BLE协议栈 
  • 比如S130,表示既支持从设备模式又支持主设备模式的nRF51 BLE协议栈
  • 比如S132,表示既支持从设备模式又支持主设备模式的nRF52 BLE协议栈
  • 比如S212,表示nRF52 ANT协议栈
  • 比如S332,表示nRF52既支持BLE协议栈又支持ANT协议栈,而且BLE协议栈既支持从设备模式又支持主设备模式

5)        Softdevice命名规则二。大体上跟命名规则1相同,但是协议栈编号最后2位跟芯片型号一样,比如S140,代表这个协议栈专门用于nRF52840。由于52840 Flash空间很大,没有必要做各种细分的协议栈,S140协议栈是一个大而全的协议栈,包含蓝牙所有功能。

NORDIC BLE SoC 开发环境 – 烧录 - 物联网技术分享

nRF52开发板初步上手 - 中文社区博客 - 中文社区 - Arm Community

北欧半导体信息中心 (nordicsemi.com)

北欧开发区 (nordicsemi.com)

二、NORDIC时钟框图 

2.1:时钟框图 

2.2:总线框图(部分)

 其中 :

 Advanced High-performance Bus(AHB) runs at 64MHz, but the Advanced Peripheral Bus(APB) runs at 16MHz.

2.3 USB框图

64 MHz晶体振荡器(HFXO)由32 MHz外部晶振控制,再经过PLL提供48Mhz给USB

NRF52832时钟控制系统_f78fk_liuyu的博客-CSDN博客

USB 为什么一般选择48MHz

2.4 引脚、时钟

Nordic的引脚是可以自由定义的。只有SAADC接口是固定的那几个引脚,
数字引脚,PWM, I2C, UART, I2S都是可以自由定义的。 

58233的系统时钟固定在64Mhz不可变

2.5 sdk_config.h配置文件 

// <h> nRF_Drivers

// </h>

这一对表示一个名为nRF_Drivers的段落

// <e> GPIOTE_ENABLED - nrf_drv_gpiote - GPIOTE peripheral driver - legacy layer

// </e>

这一对表示一个名为 GPIOTE_ENABLED - nrf_drv_gpiote - GPIOTE peripheral driver - legacy layer的配置组,用来配置GPIOTE_ENABLED。

#ifndef GPIOTE_ENABLED

#define GPIOTE_ENABLED 1

#endif

表示默认配置GPIOTE_ENABLED为1(使能该配置组下的配置选项)

关闭后项目子选项不可修改

// <o> GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - Number of lower power input pins

#ifndef GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS

#define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1

#endif

表示配置GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 默认配置为1

// <o> GPIOTE_CONFIG_IRQ_PRIORITY  - Interrupt priority

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice

// <0=> 0 (highest)

// <1=> 1

// <2=> 2

// <3=> 3

// <4=> 4

// <5=> 5

// <6=> 6

// <7=> 7

#ifndef GPIOTE_CONFIG_IRQ_PRIORITY

#define GPIOTE_CONFIG_IRQ_PRIORITY 6

#endif

 表示配置GPIOTE_CONFIG_IRQ_PRIORITY 默认配置为6

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice 表示注释说明

// <o> GPIOTE_CONFIG_IRQ_PRIORITY  - Interrupt priority

// <0=> 0 (highest)

// <1=> 1

// <2=> 2

// <3=> 3

// <4=> 4

// <5=> 5

// <6=> 6

// <7=> 7

表示以下拉列表的方式配置

Configuration Wizard Annotations (open-cmsis-pack.github.io)

MDK中configuration wizard的使用_苍穹雄鹰007的博客-CSDN博客

三、SDK中提供52833的例程

 可以将部分52840例程在52833上跑(nRF52833 是 nRF52840 的子集)

如果移植不同的板,我应该改变什么?- 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)

nRF52833-DK 眨眼问题 - 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)

我可以将 PCA 10056 的示例代码用于 PCA 10100 吗?- 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)

四、外设使用

4.0 外设库函数配置的基本四步骤

(初始化实例→初始化外设结构体配置→初始化设备→触发)

4.1 PWM

 蓝牙芯片nRF52832之PWM的使用

NRF52832 PWM 占空比调整详解

  

 第一种赋值方式

第二种赋值方式(typedef uint16_t nrf_pwm_values_common_t;)

 4.1.1复杂队列
  1. 用的是nrf_drv_pwm_complex_playback不是nrf_drv_pwm_simple_playback();
  2. /********************seq0 渐变**********************/
  3. uint16_t value = 0;
  4. uint8_t i;
  5. //设置序列0的占空比,这个数组不能在堆栈上分配(因此是“静态的”),他必须在RAM中
  6. static nrf_pwm_values_common_t seq0_values[25];
  7. for (i = 0;i < 25 ; ++i)
  8. {
  9. value += 25000/25;
  10. seq0_values[i] = value;
  11. }
  12. //设置序列
  13. nrf_pwm_sequence_t const seq0 =
  14. {
  15. .values.p_common = seq0_values,
  16. .length = NRF_PWM_VALUES_LENGTH(seq0_values),
  17. .repeats = 0,
  18. .end_delay = 0
  19. };
  20. /********************seq1 暗灭**********************/
  21. static nrf_pwm_values_common_t seq1_values[] =
  22. {
  23. 0,
  24. 0x8000,
  25. 0,
  26. 0x8000,
  27. };
  28. nrf_pwm_sequence_t const seq1 =
  29. {
  30. .values.p_common = seq1_values,
  31. .length = NRF_PWM_VALUES_LENGTH(seq1_values),
  32. .repeats = 4,
  33. .end_delay = 0
  34. };
  35. (void)nrf_drv_pwm_complex_playback(&m_pwm0,&seq0,&seq1,1,NRF_DRV_PWM_FLAG_LOOP);

回放 当设置回放后,停止播放(如下为播放3次序列后停止)

(void)nrf_drv_pwm_complex_playback(&m_pwm0,&seq0,&seq1,5,NRF_DRV_PWM_FLAG_STOP);
4.1.2 独立模式

一个PWM模块的四个通道各自一个序列,实现4个LED依次亮灭

  1. static void my_demo(void)
  2. {
  3. nrf_drv_pwm_config_t const my_config =
  4. {
  5. .output_pins =
  6. {
  7. BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED,
  8. BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED,
  9. BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
  10. BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED,
  11. },
  12. .irq_priority = APP_IRQ_PRIORITY_LOWEST,
  13. .base_clock = NRF_PWM_CLK_1MHz,
  14. .count_mode = NRF_PWM_MODE_UP,
  15. .top_value = 25000,
  16. .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
  17. .step_mode = NRF_PWM_STEP_AUTO
  18. };
  19. APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,NULL));
  20. static nrf_pwm_values_individual_t seq1_values[] =
  21. {
  22. {0x8000,0,0,0},
  23. {0,0x8000,0,0},
  24. {0,0,0x8000,0},
  25. {0,0,0,0x8000}
  26. };
  27. nrf_pwm_sequence_t const seq1 =
  28. {
  29. .values.p_individual = seq1_values,
  30. .length = NRF_PWM_VALUES_LENGTH(seq1_values),
  31. .repeats = 10,
  32. .end_delay = 0
  33. };
  34. (void)nrf_drv_pwm_simple_playback(&m_pwm0,&seq1,1,NRF_DRV_PWM_FLAG_LOOP);
  35. }

 4.1.3 带回调函数的独立通道

在回调函数里面对值的修改,该回调函数值得学习借鉴。

  1. /*************************************************************************/
  2. static nrf_pwm_values_individual_t my_demo1_seq_values;
  3. static uint8_t my_demo1_phase;
  4. static uint16_t const my_demo1_step = 200;
  5. static uint16_t const my_demo1_top = 10000;
  6. static nrf_pwm_sequence_t const my_demo1_seq =
  7. {
  8. .values.p_individual = &my_demo1_seq_values,
  9. .length = NRF_PWM_VALUES_LENGTH(my_demo1_seq_values),
  10. .repeats = 0,
  11. .end_delay = 0
  12. };
  13. static void my_demo1_handler(nrf_drv_pwm_evt_type_t event_type)
  14. {
  15. if (event_type == NRF_DRV_PWM_EVT_FINISHED)
  16. {
  17. //my_demol_phase 每次+1 后进行右移00,01,10,11,110,111 变为00(通道0递增),00(通道0递减),01(通道1递增),01(通道1递减),010,010,011,011
  18. uint8_t channel = my_demo1_phase >> 1;
  19. bool down = my_demo1_phase & 1; //0(递增),1(递减),0,1,0,1
  20. bool next_phase = false;
  21. uint16_t * p_channels = (uint16_t *)&my_demo1_seq_values;
  22. uint16_t value = p_channels[channel];
  23. if (down) //Decrement
  24. {
  25. value -= my_demo1_step;
  26. if (value == 0)
  27. {
  28. next_phase = true;
  29. }
  30. }
  31. else //Increase
  32. {
  33. value += my_demo1_step;
  34. if (value >= my_demo1_top)
  35. {
  36. next_phase = true;
  37. }
  38. }
  39. p_channels[channel] = value;
  40. if (next_phase)
  41. {
  42. if (++my_demo1_phase >= 2 * NRF_PWM_CHANNEL_COUNT)
  43. {
  44. my_demo1_phase = 0;
  45. }
  46. }
  47. }
  48. }
  49. static void my_demo(void)
  50. {
  51. #if 1 //独立模式含回调
  52. nrf_drv_pwm_config_t const my_config =
  53. {
  54. .output_pins =
  55. {
  56. BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED,
  57. BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED,
  58. BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED,
  59. BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED,
  60. },
  61. .irq_priority = APP_IRQ_PRIORITY_LOWEST,
  62. .base_clock = NRF_PWM_CLK_1MHz,
  63. .count_mode = NRF_PWM_MODE_UP,
  64. .top_value = my_demo1_top,
  65. .load_mode = NRF_PWM_LOAD_INDIVIDUAL,
  66. .step_mode = NRF_PWM_STEP_AUTO
  67. };
  68. APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,my_demo1_handler));
  69. //占空比值和极性的初始化
  70. my_demo1_seq_values.channel_0 = 0;
  71. my_demo1_seq_values.channel_1 = 0;
  72. my_demo1_seq_values.channel_2 = 0;
  73. my_demo1_seq_values.channel_3 = 0;
  74. my_demo1_phase = 0;
  75. (void)nrf_drv_pwm_simple_playback(&m_pwm0,&my_demo1_seq,1,NRF_DRV_PWM_FLAG_LOOP);
  76. }

效果渐亮到渐暗,然后下一个LED

 

4.1.4 分组模式
  1. static void my_demo(void)
  2. {
  3. #if 1 //分组加载模式
  4. nrf_drv_pwm_config_t my_config =
  5. {
  6. .irq_priority = APP_IRQ_PRIORITY_LOWEST,
  7. .count_mode = NRF_PWM_MODE_UP,
  8. .step_mode = NRF_PWM_STEP_AUTO
  9. };
  10. my_config.output_pins[0] = BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED;
  11. my_config.output_pins[1] = BSP_LED_1 | NRF_DRV_PWM_PIN_INVERTED;
  12. my_config.output_pins[2] = BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED;
  13. my_config.output_pins[3] = BSP_LED_3 | NRF_DRV_PWM_PIN_INVERTED;
  14. my_config.base_clock = NRF_PWM_CLK_1MHz;
  15. my_config.top_value = my_demo1_top;
  16. my_config.load_mode = NRF_PWM_LOAD_GROUPED;
  17. APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,NULL));
  18. static nrf_pwm_values_grouped_t seq1_values[] =
  19. {
  20. //1:灭亮灭亮
  21. //2:亮灭亮灭
  22. {0,0x8000},
  23. {0x8000,0},
  24. {0,0x8000},
  25. {0x8000,0}
  26. };
  27. nrf_pwm_sequence_t const seq1 =
  28. {
  29. .values.p_grouped = seq1_values,
  30. .length = NRF_PWM_VALUES_LENGTH(seq1_values),
  31. .repeats = 100,
  32. .end_delay = 0
  33. };
  34. (void)nrf_drv_pwm_simple_playback(&m_pwm0,&seq1,1,NRF_DRV_PWM_FLAG_LOOP);
  35. #endif
  36. }

现象LED1、2为一组 LED3、4为一组

//组1:灭亮灭亮//组2:亮灭亮灭

 4.1.5 波形加载模式
  1. static void my_demo(void)
  2. {
  3. #if 1 //波形模式
  4. nrf_drv_pwm_config_t const my_config =
  5. {
  6. .output_pins =
  7. {
  8. BSP_LED_0 | NRF_DRV_PWM_PIN_INVERTED,
  9. NRF_DRV_PWM_PIN_NOT_USED,
  10. NRF_DRV_PWM_PIN_NOT_USED,
  11. },
  12. .irq_priority = APP_IRQ_PRIORITY_LOWEST,
  13. .base_clock = NRF_PWM_CLK_1MHz,
  14. .count_mode = NRF_PWM_MODE_UP,
  15. //.top_value = 25000, 顶点值可以不用配置
  16. .load_mode = NRF_PWM_LOAD_WAVE_FORM,
  17. .step_mode = NRF_PWM_STEP_AUTO
  18. };
  19. APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0,&my_config,NULL));
  20. m_used |= USED_PWM(0); //使用PWM0模块
  21. //ram中包含占极性、空比、顶点值
  22. static nrf_pwm_values_wave_form_t seq1_values[] =
  23. {
  24. {0,0,0,0x4d09}, //0x4d09周期1
  25. {0x8000,0,0,0x4d09},
  26. {0,0,0,0x3d09}, //0x3d09周期2
  27. {0x8000,0,0,0x3d09},
  28. {0,0,0,0x1d09}, //0x1d09周期3
  29. {0x8000,0,0,0x1d09},
  30. };
  31. //设置序列
  32. nrf_pwm_sequence_t const seq0 =
  33. {
  34. .values.p_wave_form = seq1_values,
  35. .length = NRF_PWM_VALUES_LENGTH(seq1_values),
  36. .repeats = 0,
  37. .end_delay = 0
  38. };
  39. (void)nrf_drv_pwm_simple_playback(&m_pwm0,&seq0,1,NRF_DRV_PWM_FLAG_LOOP);
  40. #endif
  41. }

效果实现变周期 

4.2 定时器
4.2.1 定时器框图

fTIMER是定时器的定时频率即定时周期的倒数,模块会根据这个值来自动选择输入时钟

 4.2.2 寄存器法配置

配置ms级的定时器 

 4.2.3 库函数方式
  1. #include <stdbool.h>
  2. #include <stdint.h>
  3. #include "nrf.h"
  4. #include "nrf_drv_timer.h"
  5. #include "bsp.h"
  6. #include "app_error.h"
  7. const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(0); //配置哪一个定时器这里配置TIMER0
  8. /**
  9. * @brief Handler for timer events. //中断事件这里为比较事件
  10. */
  11. void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)
  12. {
  13. static uint32_t i;
  14. uint32_t led_to_invert = ((i++) % LEDS_NUMBER);
  15. switch (event_type)
  16. {
  17. case NRF_TIMER_EVENT_COMPARE0: //比较事件计时结束
  18. bsp_board_led_invert(led_to_invert);
  19. break;
  20. default:
  21. //Do nothing.
  22. break;
  23. }
  24. }
  25. /**
  26. * @brief Function for main application entry.
  27. */
  28. int main(void)
  29. {
  30. uint32_t time_ms = 500; //Time(in miliseconds) between consecutive compare events.//定时器比较事件事件的时间
  31. uint32_t time_ticks;
  32. uint32_t err_code = NRF_SUCCESS;
  33. //Configure all leds on board.
  34. bsp_board_init(BSP_INIT_LEDS);
  35. //Configure TIMER_LED for generating simple light effect - leds on board will invert his state one after the other.
  36. nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; //定时器结构体默认配置
  37. err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler); //初始化定时器
  38. APP_ERROR_CHECK(err_code);
  39. //计算CC寄存器中的值
  40. time_ticks = nrf_drv_timer_ms_to_ticks(&TIMER_LED, time_ms);
  41. //触发定时器比较
  42. nrf_drv_timer_extended_compare(
  43. &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
  44. //NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK 快捷方式会清除定时器的counter从而从新开始计数
  45. nrf_drv_timer_enable(&TIMER_LED);
  46. while (1)
  47. {
  48. __WFI();
  49. }
  50. }
4.3 GPIOTE 
4.3.1事件模式

eg:当按键按下会产生一个相应的中断,并在中断处理事件

  1. void giop_inint_test(void);
  2. int main(void)
  3. {
  4. giop_inint_test();
  5. while (true)
  6. {
  7. // Do Nothing - GPIO can be toggled without software intervention.
  8. }
  9. }
  10. void in_pin_handeer(nrf_drv_gpiote_pin_t pin,nrf_gpiote_polarity_t active)
  11. {
  12. if(nrf_gpio_pin_read(BUTTON_1) == 0) //按键消抖
  13. {
  14. nrf_gpio_pin_toggle(BSP_LED_0);
  15. }
  16. }
  17. void giop_inint_test(void)
  18. {
  19. nrf_gpio_cfg_output(BSP_LED_0);
  20. nrf_drv_gpiote_init();
  21. nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
  22. in_config.pull = NRF_GPIO_PIN_PULLUP;
  23. //设置GPIOTE输入,极性,模式
  24. //in_pin_handeer为回调函数,中断函数在nrf_drv_gpiote_in_init内部,发生中断后会调用回调函数
  25. nrf_drv_gpiote_in_init(BUTTON_1,&in_config,in_pin_handeer);
  26. nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
  27. }

 

PORT模式应对GPIOTE只能绑定8个通道的问题(可以32个IO口通用一个通道)

  1. void giop_inint_test(void);
  2. int main(void)
  3. {
  4. giop_inint_test();
  5. while (true)
  6. {
  7. // Do Nothing - GPIO can be toggled without software intervention.
  8. }
  9. }
  10. void in_pin_handeer(nrf_drv_gpiote_pin_t pin,nrf_gpiote_polarity_t active)
  11. {
  12. if(pin == BUTTON_1) //按键消抖
  13. {
  14. nrf_gpio_pin_toggle(BSP_LED_0);
  15. }
  16. else if(pin == BUTTON_2) //按键消抖
  17. {
  18. nrf_gpio_pin_toggle(BSP_LED_1);
  19. }
  20. else if(pin == BUTTON_3) //按键消抖
  21. {
  22. nrf_gpio_pin_toggle(BSP_LED_2);
  23. }
  24. else if(pin == BUTTON_4) //按键消抖
  25. {
  26. nrf_gpio_pin_toggle(BSP_LED_3);
  27. }
  28. }
  29. void giop_inint_test(void)
  30. {
  31. nrf_gpio_cfg_output(BSP_LED_0);
  32. nrf_gpio_cfg_output(BSP_LED_1);
  33. nrf_gpio_cfg_output(BSP_LED_2);
  34. nrf_gpio_cfg_output(BSP_LED_3);
  35. nrf_drv_gpiote_init();
  36. //配置SENSE模式,选择false为sense配置
  37. nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
  38. in_config.pull = NRF_GPIO_PIN_PULLUP;
  39. //设置GPIOTE输入,极性,模式
  40. //in_pin_handeer为回调函数,中断函数在nrf_drv_gpiote_in_init内部,发生中断后会调用回调函数
  41. //配置按键0绑定POTR
  42. nrf_drv_gpiote_in_init(BUTTON_1,&in_config,in_pin_handeer);
  43. nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
  44. //配置按键1绑定POTR
  45. nrf_drv_gpiote_in_init(BUTTON_2,&in_config,in_pin_handeer);
  46. nrf_drv_gpiote_in_event_enable(BUTTON_2,true);
  47. //配置按键2绑定POTR
  48. nrf_drv_gpiote_in_init(BUTTON_3,&in_config,in_pin_handeer);
  49. nrf_drv_gpiote_in_event_enable(BUTTON_3,true);
  50. //配置按键3绑定POTR
  51. nrf_drv_gpiote_in_init(BUTTON_4,&in_config,in_pin_handeer);
  52. nrf_drv_gpiote_in_event_enable(BUTTON_4,true);
  53. }

 4.3.2 任务模式

eg:给LED灯绑定一个任务,当别的方法触发时LED做相应的电平变化(见下下图),可以同时触发多个(32个都可以?)

 

  1. int main(void)
  2. {
  3. giop_inint_test();
  4. nrf_gpio_cfg_output(BSP_LED_0);
  5. //初始化GPIOTE模块
  6. nrf_drv_gpiote_init();
  7. //定义GPIOTE输出初始化结构体,主要配置为翻转模式
  8. nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
  9. //给指定的GPIO口绑定任务
  10. nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
  11. //开始任务
  12. nrf_drv_gpiote_out_task_enable(BSP_LED_0);
  13. #endif
  14. while (true)
  15. {
  16. //不断触发任务
  17. nrf_drv_gpiote_out_task_trigger(BSP_LED_0);
  18. // Do Nothing - GPIO can be toggled without software intervention.
  19. }
  20. }
  21. void in_pin_handeer(nrf_drv_gpiote_pin_t pin,nrf_gpiote_polarity_t active)
  22. {
  23. if(pin == BUTTON_1) //按键消抖
  24. {
  25. //触发任务
  26. nrf_drv_gpiote_out_task_trigger(BSP_LED_0);
  27. }
  28. else if(pin == BUTTON_2) //按键消抖
  29. {
  30. nrf_gpio_pin_toggle(BSP_LED_1);
  31. }
  32. else if(pin == BUTTON_3) //按键消抖
  33. {
  34. nrf_gpio_pin_toggle(BSP_LED_2);
  35. }
  36. else if(pin == BUTTON_4) //按键消抖
  37. {
  38. nrf_gpio_pin_toggle(BSP_LED_3);
  39. }
  40. }

 配合上面的事件模式 ,实现按下按键,触发任务翻转LED

nRF5芯片外设GPIO和GPIOTE介绍

4.4 PPI

①  Programmable peripheral interconnect(PPI):即不通过CPU、中断(GPIOTE是触发中断)由一个事件触发一个任务。

②  共32个通道,可编程的20个通道,12个固定的事件任务对。

③  PPI通道可进行分组,将多个PPI通道分为一组进行统一管理,同时打开或者关闭组中的所以PPI通道,最多可实现6个组。一个事件触发一人组中的任务实现一对多。 统一开启组里面对应的PPI。

④   fork 从任务,即一个事件可以触发两个任务,一个主任务,一个从任务。

⑤   配合GPIOTE食用

使用的基本配置流程

4.4.1 普通PPI的配置

eg :按键触发LED寄存器版

 库函数

  1. void my_ppi_init(void);
  2. void gpiote_init(void);
  3. int main(void)
  4. {
  5. gpiote_init();
  6. my_ppi_init();
  7. while(true);
  8. }
  9. static nrf_ppi_channel_t my_ppi_channel;
  10. void gpiote_init(void)
  11. {
  12. ret_code_t err_code;
  13. //初始化GPIOTE
  14. err_code = nrf_drv_gpiote_init();
  15. APP_ERROR_CHECK(err_code);
  16. //配置LED端口翻转输出任务
  17. nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
  18. //绑定输出端口
  19. err_code = nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
  20. //配置为输出端口任务使能
  21. nrf_drv_gpiote_out_task_enable(BSP_LED_0);
  22. //配置按键端口高电平变低电平事件
  23. nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
  24. in_config.pull = NRF_GPIO_PIN_PULLUP;
  25. //绑定输入端口
  26. err_code = nrf_drv_gpiote_in_init(BUTTON_1,&in_config,NULL);
  27. APP_ERROR_CHECK(err_code);
  28. //配置输入事件使能
  29. nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
  30. }
  31. void my_ppi_init(void)
  32. {
  33. ret_code_t err_code;
  34. APP_ERROR_CHECK(err_code);
  35. //初始化PPI模块
  36. err_code = nrf_drv_ppi_init();
  37. APP_ERROR_CHECK(err_code);
  38. //配置PPI的频道
  39. err_code = nrfx_ppi_channel_alloc(&my_ppi_channel);
  40. APP_ERROR_CHECK(err_code);
  41. //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
  42. err_code = nrfx_ppi_channel_assign(my_ppi_channel,
  43. nrfx_gpiote_in_event_addr_get(BUTTON_1),
  44. nrfx_gpiote_out_task_addr_get(BSP_LED_0));
  45. APP_ERROR_CHECK(err_code);
  46. //使能PPI通道
  47. err_code = nrfx_ppi_channel_enable(my_ppi_channel);
  48. APP_ERROR_CHECK(err_code);
  49. }

 

4.4.2  PPI-GROUP的管理

GROUP 组的开启关闭配置

① CPU参与的方式 

 上述是吧按键1与LED0,按键2与LED1,各设置一组PPI通道0和通道1,再把通道0和通道1绑定的到PPI group0上。但按键3按下使能组(未使能前按下按键1或2无响应),使能后里面两个PPI通道就可以用,按键1按下LED0翻转,按键2按下LED1翻转。按键4按下使能组

库函数

  1. static nrf_ppi_channel_t my_ppi_channel;
  2. static nrf_ppi_channel_t my_ppi_channel_1;
  3. static nrf_ppi_channel_group_t my_ppi_group;
  4. int main(void)
  5. {
  6. nrf_gpio_cfg_output(LED_3); //配置P0.19为输出,驱动led3
  7. nrf_gpio_cfg_output(LED_4);//配置P0.20为输出,驱动led4
  8. nrf_gpio_pin_set(LED_3);//led3初始状态设置为熄灭
  9. nrf_gpio_pin_set(LED_4);//led4初始状态设置为熄灭
  10. nrf_gpio_cfg_input(BUTTON_3,NRF_GPIO_PIN_PULLUP);//配置P0.15为输入
  11. nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP);//配置P0.16为输入
  12. ret_code_t err_code;
  13. gpiote_init();
  14. my_ppi_init();
  15. while(true)
  16. {
  17. //检测按键3是否按下
  18. if(nrf_gpio_pin_read(BUTTON_3) == 0)
  19. {
  20. //等待按键释放
  21. while(nrf_gpio_pin_read(BUTTON_3) == 0);
  22. //使能GROUP
  23. err_code = nrfx_ppi_group_enable(my_ppi_group);
  24. //LED2亮LED3灭指示状态
  25. nrf_gpio_pin_set(BSP_LED_2);
  26. nrf_gpio_pin_clear(BSP_LED_3);
  27. }
  28. //检测按键4是否按下
  29. if(nrf_gpio_pin_read(BUTTON_4) == 0)
  30. {
  31. //等待按键释放
  32. while(nrf_gpio_pin_read(BUTTON_4) == 0);
  33. //使能GROUP
  34. err_code = nrfx_ppi_group_disable(my_ppi_group);
  35. //LED3亮LED2灭指示状态
  36. nrf_gpio_pin_set(BSP_LED_3);
  37. nrf_gpio_pin_clear(BSP_LED_2);
  38. }
  39. };
  40. }
  41. void gpiote_init(void)
  42. {
  43. ret_code_t err_code;
  44. //初始化GPIOTE
  45. err_code = nrf_drv_gpiote_init();
  46. APP_ERROR_CHECK(err_code);
  47. //配置LED端口翻转输出任务 按键1---LED0
  48. nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
  49. //绑定输出端口
  50. err_code = nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
  51. //配置为输出端口任务使能
  52. nrf_drv_gpiote_out_task_enable(BSP_LED_0);
  53. //配置按键端口高电平变低电平事件
  54. nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
  55. in_config.pull = NRF_GPIO_PIN_PULLUP;
  56. //绑定输入端口
  57. err_code = nrf_drv_gpiote_in_init(BUTTON_1,&in_config,NULL);
  58. APP_ERROR_CHECK(err_code);
  59. //配置输入事件使能
  60. nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
  61. //配置LED端口翻转输出任务 按键2---LED1
  62. nrf_drv_gpiote_out_config_t out_config_1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
  63. //绑定输出端口
  64. err_code = nrf_drv_gpiote_out_init(BSP_LED_1,&out_config_1);
  65. //配置为输出端口任务使能
  66. nrf_drv_gpiote_out_task_enable(BSP_LED_1);
  67. //配置按键端口高电平变低电平事件
  68. nrf_drv_gpiote_in_config_t in_config_1 = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
  69. in_config_1.pull = NRF_GPIO_PIN_PULLUP;
  70. //绑定输入端口
  71. err_code = nrf_drv_gpiote_in_init(BUTTON_2,&in_config_1,NULL);
  72. APP_ERROR_CHECK(err_code);
  73. //配置输入事件使能
  74. nrf_drv_gpiote_in_event_enable(BUTTON_2,true);
  75. }
  76. void my_ppi_init(void)
  77. {
  78. ret_code_t err_code;
  79. //APP_ERROR_CHECK(err_code); //此处开启会运行不了!!!!!!!!!!!!!!!!!
  80. //初始化PPI模块
  81. err_code = nrf_drv_ppi_init();
  82. APP_ERROR_CHECK(err_code);
  83. //配置PPI的频道 PPI通道1
  84. err_code = nrfx_ppi_channel_alloc(&my_ppi_channel);
  85. APP_ERROR_CHECK(err_code);
  86. //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
  87. err_code = nrfx_ppi_channel_assign(my_ppi_channel,
  88. nrfx_gpiote_in_event_addr_get(BUTTON_1),
  89. nrfx_gpiote_out_task_addr_get(BSP_LED_0));
  90. APP_ERROR_CHECK(err_code);
  91. /*************************/
  92. //配置PPI的频道 PPI通道2
  93. err_code = nrfx_ppi_channel_alloc(&my_ppi_channel_1);
  94. APP_ERROR_CHECK(err_code);
  95. //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
  96. err_code = nrfx_ppi_channel_assign(my_ppi_channel_1,
  97. nrfx_gpiote_in_event_addr_get(BUTTON_2),
  98. nrfx_gpiote_out_task_addr_get(BSP_LED_1));
  99. APP_ERROR_CHECK(err_code);
  100. /*************************/
  101. //申请一个PPI组
  102. err_code = nrfx_ppi_group_alloc(&my_ppi_group);
  103. APP_ERROR_CHECK(err_code);
  104. //将PPI通道1加入PPI组
  105. err_code = nrfx_ppi_channel_include_in_group(my_ppi_channel,my_ppi_group);
  106. APP_ERROR_CHECK(err_code);
  107. //将PPI通道2加入PPI组
  108. err_code = nrfx_ppi_channel_include_in_group(my_ppi_channel_1,my_ppi_group);
  109. APP_ERROR_CHECK(err_code);
  110. }

 ②  不需要CPU参与,PPI的方式即把组的开启关闭当做是一个PPI的任务,通过PPI事件触发

 ③ PPI-fork从任务

eg:按下按键同时翻转LED1、2

寄存器

  1. void gpiote_init(void);
  2. void my_ppi_init(void);
  3. int main(void)
  4. {
  5. nrf_gpio_cfg_output(LED_3); //配置P0.19为输出,驱动led3
  6. nrf_gpio_cfg_output(LED_4);//配置P0.20为输出,驱动led4
  7. nrf_gpio_pin_set(LED_3);//led3初始状态设置为熄灭
  8. nrf_gpio_pin_set(LED_4);//led4初始状态设置为熄灭
  9. nrf_gpio_cfg_input(BUTTON_3,NRF_GPIO_PIN_PULLUP);//配置P0.15为输入
  10. nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP);//配置P0.16为输入
  11. ret_code_t err_code;
  12. gpiote_init();
  13. my_ppi_init();
  14. while(true)
  15. {
  16. };
  17. }
  18. void gpiote_init(void)
  19. {
  20. nrf_gpio_cfg_input(BUTTON_1,NRF_GPIO_PIN_PULLUP);
  21. //配置一个GPIOTE输入任务
  22. NRF_GPIOTE->CONFIG[0] =
  23. (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos) //触发极性的设置
  24. |(BUTTON_1<<GPIOTE_CONFIG_PSEL_Pos) //配置事件输入
  25. |(GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos); //设置为事件模式
  26. //配置一个主任务
  27. NRF_GPIOTE->CONFIG[1] =
  28. (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos)
  29. |(LED_1<<GPIOTE_CONFIG_PSEL_Pos)
  30. |(GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos);
  31. //配置fork从任务
  32. NRF_GPIOTE->CONFIG[2] =
  33. (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos)
  34. |(LED_2<<GPIOTE_CONFIG_PSEL_Pos)
  35. |(GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos);
  36. }
  37. void my_ppi_init(void)
  38. {
  39. NRF_PPI ->CH[0].EEP = (uint32_t)(&NRF_GPIOTE->EVENTS_IN[0]);//配置输入事件
  40. NRF_PPI ->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[1]);//配置输出主任务
  41. NRF_PPI ->FORK[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[2]);//配置输出从任务
  42. //使能通道0
  43. NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos);
  44. }

库函数

  1. void gpiote_init(void);
  2. void my_ppi_init(void);
  3. static nrf_ppi_channel_t my_ppi_channel;
  4. int main(void)
  5. {
  6. nrf_gpio_cfg_output(LED_3); //配置P0.19为输出,驱动led3
  7. nrf_gpio_cfg_output(LED_4);//配置P0.20为输出,驱动led4
  8. nrf_gpio_pin_set(LED_3);//led3初始状态设置为熄灭
  9. nrf_gpio_pin_set(LED_4);//led4初始状态设置为熄灭
  10. nrf_gpio_cfg_input(BUTTON_3,NRF_GPIO_PIN_PULLUP);//配置P0.15为输入
  11. nrf_gpio_cfg_input(BUTTON_4,NRF_GPIO_PIN_PULLUP);//配置P0.16为输入
  12. ret_code_t err_code;
  13. gpiote_init();
  14. my_ppi_init();
  15. while(true)
  16. {
  17. };
  18. }
  19. void gpiote_init(void)
  20. {
  21. ret_code_t err_code;
  22. //初始化GPIOTE
  23. err_code = nrf_drv_gpiote_init();
  24. APP_ERROR_CHECK(err_code);
  25. //配置LED端口翻转输出任务 led1
  26. nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
  27. //绑定输出端口
  28. err_code = nrf_drv_gpiote_out_init(BSP_LED_0,&out_config);
  29. //配置为输出端口任务使能
  30. nrf_drv_gpiote_out_task_enable(BSP_LED_0);
  31. //配置LED端口翻转输出任务 led2
  32. nrf_drv_gpiote_out_config_t out_config_1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
  33. //绑定输出端口
  34. err_code = nrf_drv_gpiote_out_init(BSP_LED_1,&out_config_1);
  35. //配置为输出端口任务使能
  36. nrf_drv_gpiote_out_task_enable(BSP_LED_1);
  37. //配置按键端口高电平变低电平事件
  38. nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
  39. in_config.pull = NRF_GPIO_PIN_PULLUP;
  40. //绑定输入端口
  41. err_code = nrf_drv_gpiote_in_init(BUTTON_1,&in_config,NULL);
  42. APP_ERROR_CHECK(err_code);
  43. //配置输入事件使能
  44. nrf_drv_gpiote_in_event_enable(BUTTON_1,true);
  45. }
  46. void my_ppi_init(void)
  47. {
  48. ret_code_t err_code;
  49. //初始化PPI模块
  50. err_code = nrf_drv_ppi_init();
  51. APP_ERROR_CHECK(err_code);
  52. //配置PPI的频道
  53. err_code = nrfx_ppi_channel_alloc(&my_ppi_channel);
  54. APP_ERROR_CHECK(err_code);
  55. //设置PPI通道的EPP(事件)和TEP(任务) 两端对应的端口
  56. err_code = nrfx_ppi_channel_assign(my_ppi_channel,
  57. nrfx_gpiote_in_event_addr_get(BUTTON_1),
  58. nrfx_gpiote_out_task_addr_get(BSP_LED_0));
  59. APP_ERROR_CHECK(err_code);
  60. //PPI-fork
  61. err_code = nrfx_ppi_channel_fork_assign(my_ppi_channel,
  62. nrfx_gpiote_out_task_addr_get(BSP_LED_1));
  63. //使能PPI通道
  64. err_code = nrfx_ppi_channel_enable(my_ppi_channel);
  65. APP_ERROR_CHECK(err_code);
  66. }

4.5 PPI-TIMER
4.5.1 精确定时

通过PPI通道,让定时器1定时1秒后开启定时器0的计数,定时器2定时2秒关闭定时器0的计数。

寄存器

  1. void timer0_init(void)
  2. {
  3. //定时器0配置为计数模式
  4. NRF_TIMER0->MODE = TIMER_MODE_MODE_Counter;
  5. //设置定时器的分频
  6. NRF_TIMER0->PRESCALER = 9;
  7. //定时器位宽
  8. NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
  9. }
  10. void timer1_init(void)
  11. {
  12. //定时器1配置为定时模式,定时2秒(第2秒停止启动同时开始此时停止优先)
  13. //位宽 BITMODE = 16bit
  14. NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos);
  15. //定时器分频值 PRESCALER = 9
  16. NRF_TIMER1 ->PRESCALER = 9;
  17. //定时器比较清零计数器模式实现不停的定时,本地任务和事件的快捷方式
  18. NRF_TIMER1->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos);
  19. //定时器模式
  20. NRF_TIMER1->MODE = TIMER_MODE_MODE_Timer;
  21. //触发时间 = 0XFFFF/(SysClk/2^PERSCALER) = 65535/31250 = 2.097 sec
  22. NRF_TIMER1 ->CC[0] = 0xFFFFUL;
  23. }
  24. void timer2_init(void)
  25. {
  26. //定时器2配置为定时模式,定时1
  27. //位宽 BITMODE = 16bit
  28. NRF_TIMER2->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos);
  29. //定时器分频值 PRESCALER = 9
  30. NRF_TIMER2 ->PRESCALER = 9;
  31. //定时器比较清零计数器模式实现不停的定时,本地任务和事件的快捷方式
  32. NRF_TIMER2->SHORTS = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos);
  33. //定时器模式
  34. NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer;
  35. //触发时间 = 0XFFFF/(SysClk/2^PERSCALER) = 32767/31250 = 1.048 sec 触发比较事件
  36. NRF_TIMER2 ->CC[0] = 0x7FFFUL;
  37. }
  38. void ppi_init(void)
  39. {
  40. //配置PPI通道0,事件端为定时器1的比较定时事件,任务端为关闭定时器0
  41. NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER1->EVENTS_COMPARE[0]);
  42. NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_TIMER0->TASKS_STOP);
  43. //配置PPI通道1,事件端为定时器2的比较事件,任务端为开启定时器0
  44. NRF_PPI->CH[1].EEP = (uint32_t)(&NRF_TIMER2->EVENTS_COMPARE[0]);
  45. NRF_PPI->CH[1].TEP = (uint32_t)(&NRF_TIMER0->TASKS_START);
  46. //使能PPI通道01
  47. NRF_PPI->CHEN = (PPI_CHEN_CH0_Enabled<< PPI_CHEN_CH0_Pos) |(PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos);
  48. }
  49. /**
  50. * @brief Function for main application entry.
  51. */
  52. int main(void)
  53. {
  54. uint32_t err_code;
  55. bsp_board_init(BSP_INIT_LEDS);
  56. const app_uart_comm_params_t comm_params =
  57. {
  58. RX_PIN_NUMBER,
  59. TX_PIN_NUMBER,
  60. RTS_PIN_NUMBER,
  61. CTS_PIN_NUMBER,
  62. UART_HWFC,
  63. false,
  64. #if defined (UART_PRESENT)
  65. NRF_UART_BAUDRATE_115200
  66. #else
  67. NRF_UARTE_BAUDRATE_115200
  68. #endif
  69. };
  70. APP_UART_FIFO_INIT(&comm_params,
  71. UART_RX_BUF_SIZE,
  72. UART_TX_BUF_SIZE,
  73. uart_error_handle,
  74. APP_IRQ_PRIORITY_LOWEST,
  75. err_code);
  76. APP_ERROR_CHECK(err_code);
  77. #ifndef ENABLE_LOOPBACK_TEST
  78. int32_t timval;
  79. timer0_init();
  80. timer1_init();
  81. timer2_init();
  82. ppi_init();
  83. //启动定时器
  84. NRF_TIMER1 ->TASKS_START = 1;
  85. NRF_TIMER2 ->TASKS_START = 1;
  86. while (true)
  87. {
  88. //计数器加1
  89. NRF_TIMER0->TASKS_COUNT = 1;
  90. //捕获输出
  91. NRF_TIMER0->TASKS_CAPTURE[0] =1;
  92. //获取计数值
  93. timval = NRF_TIMER0->CC[0];
  94. printf("count value:%d\r\n",timval);
  95. nrf_delay_ms(1048);
  96. }
  97. #else
  98. // This part of the example is just for testing the loopback .
  99. while (true)
  100. {
  101. uart_loopback_test();
  102. }
  103. #endif
  104. }

库函数

  1. const nrf_drv_timer_t timer0 = NRF_DRV_TIMER_INSTANCE(0);
  2. const nrf_drv_timer_t timer1 = NRF_DRV_TIMER_INSTANCE(1);
  3. const nrf_drv_timer_t timer2 = NRF_DRV_TIMER_INSTANCE(2);
  4. nrf_ppi_channel_t my_ppi_channel=NRF_PPI_CHANNEL0 ;
  5. nrf_ppi_channel_t my_ppi_channel2=NRF_PPI_CHANNEL1;
  6. void timer0_init(void)
  7. {
  8. uint32_t time_ticks;
  9. ret_code_t err_code;
  10. nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
  11. timer_cfg.mode = NRF_TIMER_MODE_COUNTER;
  12. err_code = nrf_drv_timer_init(&timer0,&timer_cfg,NULL);
  13. APP_ERROR_CHECK(err_code);
  14. }
  15. void timer1_init(void)
  16. {
  17. uint32_t time_ticks;
  18. ret_code_t err_code;
  19. nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
  20. err_code = nrf_drv_timer_init(&timer1,&timer_cfg,NULL);
  21. APP_ERROR_CHECK(err_code);
  22. nrf_drv_timer_extended_compare(&timer1,NRF_TIMER_CC_CHANNEL0,0xFFFFUL,NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,false);
  23. nrf_drv_timer_enable(&timer1);
  24. }
  25. void timer2_init(void)
  26. {
  27. uint32_t time_ticks;
  28. ret_code_t err_code;
  29. nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
  30. err_code = nrf_drv_timer_init(&timer2,&timer_cfg,NULL);
  31. APP_ERROR_CHECK(err_code);
  32. nrf_drv_timer_extended_compare(&timer2,NRF_TIMER_CC_CHANNEL0,0x7FFFUL,NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,false);
  33. nrf_drv_timer_enable(&timer2);
  34. }
  35. void ppi_init(void)
  36. {
  37. ret_code_t err_code;
  38. err_code = nrf_drv_ppi_init();
  39. APP_ERROR_CHECK(err_code);
  40. err_code = nrf_drv_ppi_channel_alloc(&my_ppi_channel);
  41. APP_ERROR_CHECK(err_code);
  42. err_code = nrf_drv_ppi_channel_assign(my_ppi_channel,nrf_drv_timer_event_address_get(&timer2,NRF_TIMER_EVENT_COMPARE0)
  43. ,nrf_drv_timer_task_address_get(&timer0,NRF_TIMER_TASK_START));
  44. APP_ERROR_CHECK(err_code);
  45. err_code = nrf_drv_ppi_channel_enable(my_ppi_channel);
  46. APP_ERROR_CHECK(err_code);
  47. err_code = nrf_drv_ppi_channel_alloc(&my_ppi_channel2);
  48. APP_ERROR_CHECK(err_code);
  49. err_code = nrf_drv_ppi_channel_assign(my_ppi_channel2,nrf_drv_timer_event_address_get(&timer1,NRF_TIMER_EVENT_COMPARE0)
  50. ,nrf_drv_timer_task_address_get(&timer0,NRF_TIMER_TASK_STOP));
  51. APP_ERROR_CHECK(err_code);
  52. err_code = nrf_drv_ppi_channel_enable(my_ppi_channel2);
  53. APP_ERROR_CHECK(err_code);
  54. }
  55. /**
  56. * @brief Function for main application entry.
  57. */
  58. int main(void)
  59. {
  60. uint32_t err_code;
  61. bsp_board_init(BSP_INIT_LEDS);
  62. const app_uart_comm_params_t comm_params =
  63. {
  64. RX_PIN_NUMBER,
  65. TX_PIN_NUMBER,
  66. RTS_PIN_NUMBER,
  67. CTS_PIN_NUMBER,
  68. UART_HWFC,
  69. false,
  70. #if defined (UART_PRESENT)
  71. NRF_UART_BAUDRATE_115200
  72. #else
  73. NRF_UARTE_BAUDRATE_115200
  74. #endif
  75. };
  76. APP_UART_FIFO_INIT(&comm_params,
  77. UART_RX_BUF_SIZE,
  78. UART_TX_BUF_SIZE,
  79. uart_error_handle,
  80. APP_IRQ_PRIORITY_LOWEST,
  81. err_code);
  82. APP_ERROR_CHECK(err_code);
  83. #ifndef ENABLE_LOOPBACK_TEST
  84. int32_t timval;
  85. timer0_init();
  86. timer1_init();
  87. timer2_init();
  88. ppi_init();
  89. //启动定时器0
  90. nrf_drv_timer_enable(&timer0);
  91. while (true)
  92. {
  93. //计数器加1
  94. nrfx_timer_increment(&timer0);
  95. //获取计数值
  96. timval = nrfx_timer_capture(&timer0,NRF_TIMER_CC_CHANNEL0);
  97. printf("count value:%d\r\n",timval);
  98. nrf_delay_ms(1048);
  99. }
  100. #else
  101. // This part of the example is just for testing the loopback .
  102. while (true)
  103. {
  104. uart_loopback_test();
  105. }
  106. #endif
  107. }

 

4.5.2 软件PWM 

实现方法通过定时器触发GPIOTE的输出电平变化来实现,定时器作为任务,触发的GPIOTE作为事件。

eg:使用一个定时器通过不同的CC[],编写两路PWM输出,需要4路PPI通道。每两路PPI通道产生一个PWM输出,其中一路作为占空比的输出控制,另外一路作为PWM周期的控制整个过程CPU不参与其中。

① eg:固定周期不同占空比的两路PWM 

寄存器

  1. /**
  2. * @brief Function for application main entry.
  3. */
  4. void timer0_init(void)
  5. {
  6. NRF_TIMER0 ->PRESCALER = 4; //2^4 16分频成1M时钟源
  7. NRF_TIMER0 ->MODE = 0; //time模式
  8. NRF_TIMER0 ->BITMODE = 3; //32bit
  9. NRF_TIMER0->CC[1] = 5000; //cc[1]的值等于5ms,这里相当于方波的周期为5ms
  10. NRF_TIMER0->CC[0] = 100; //cc[0]为占空比值
  11. NRF_TIMER0->CC[2] = 5000; //cc[2]的值等于5ms,这里相当于方波的周期为5ms
  12. NRF_TIMER0->CC[3] = 4900; //cc[3]为占空比值
  13. NRF_TIMER0->SHORTS = 1<<1; //设置倒计时到CC1中的值时自动清零重新开始计数;
  14. NRF_TIMER0->SHORTS = 1<<2; //设置倒计时到CC2中的值时自动清零重新开始计数;
  15. NRF_TIMER0->TASKS_START = 1; //开启timer
  16. }
  17. //电平不停翻转,配置GPIOTE 01
  18. void gpoite_init(void)
  19. {
  20. NRF_GPIOTE->CONFIG[0] = ( 3 << 0 ) //作为task模式
  21. | ( BSP_LED_0 << 8) //设置PWM输出引脚
  22. | ( 3 << 16 ) //设置task为翻转PWM引脚的电平
  23. | ( 1 << 20); //初始输出电平为高
  24. NRF_GPIOTE->CONFIG[1] = ( 3 << 0 ) //作为task模式
  25. | ( BSP_LED_1 << 8) //设置PWM输出引脚
  26. | ( 3 << 16 ) //设置task为翻转PWM引脚的电平
  27. | ( 1 << 20); //初始输出电平为高
  28. }
  29. void ppi_set(void)
  30. {
  31. //配置每个PPI对应的事件和任务
  32. NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[0]);
  33. NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);
  34. NRF_PPI->CH[1].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[1]);
  35. NRF_PPI->CH[1].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);
  36. NRF_PPI->CH[2].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[2]);
  37. NRF_PPI->CH[2].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[1]);
  38. NRF_PPI->CH[3].EEP = (uint32_t)(&NRF_TIMER0->EVENTS_COMPARE[3]);
  39. NRF_PPI->CH[3].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[1]);
  40. //任务绑定
  41. //两个通道的task端绑定的都是翻转电平的task
  42. //使能PPI通道 0 和 通道1 1111
  43. NRF_PPI->CHENSET = 0xf;
  44. }
  45. int main(void)
  46. {
  47. timer0_init();
  48. gpoite_init();
  49. ppi_set();
  50. /* Toggle LEDs. */
  51. while (true)
  52. {
  53. }
  54. }

现象 LED1与LED2的亮度不一样

 ② eg:变占空比动态调节LED亮度\

  1. APP_PWM_INSTANCE(PWM1,1); // 创建一个使用定时器1产生PWM波的实例
  2. static volatile bool ready_flag; // 使用一个标志位表示PWM状态
  3. void pwm_ready_callback(uint32_t pwm_id) // PWM callback function
  4. {
  5. ready_flag = true;
  6. }
  7. int main(void)
  8. {
  9. ret_code_t err_code;
  10. //配置2个通道的PWM ,200Hz(50000us = 5ms),通过LED01管脚输出
  11. app_pwm_config_t pwm1_cfg = APP_PWM_DEFAULT_CONFIG_2CH(5000L,BSP_LED_0,BSP_LED_1);
  12. //初始输出电平配置为高
  13. pwm1_cfg.pin_polarity[1] = APP_PWM_POLARITY_ACTIVE_HIGH;
  14. //初始和使能PWM
  15. err_code = app_pwm_init(&PWM1,&pwm1_cfg,pwm_ready_callback);
  16. APP_ERROR_CHECK(err_code);
  17. app_pwm_enable(&PWM1);
  18. uint32_t value;
  19. while (true)
  20. {
  21. for (uint8_t i = 0; i < 40; ++i)
  22. {
  23. value = (i < 20) ? (i * 5) : (100 - (i - 20) * 5);
  24. ready_flag = false;
  25. /* 设置占空比 - 不停设置直到PWM准备好. */
  26. while (app_pwm_channel_duty_set(&PWM1, 0, value) == NRF_ERROR_BUSY);
  27. /* 等待回调 */
  28. while (!ready_flag);
  29. APP_ERROR_CHECK(app_pwm_channel_duty_set(&PWM1, 1, value));
  30. nrf_delay_ms(25);
  31. }
  32. }
  33. }

4.6 I²S 
4.6.1 时序图

4.6.2 结构体说明

#define NRFX_I2S_DEFAULT_CONFIG                                   \
{                                                                 \
    .sck_pin      = NRFX_I2S_CONFIG_SCK_PIN,                      \
    .lrck_pin     = NRFX_I2S_CONFIG_LRCK_PIN,                     \
    .mck_pin      = NRFX_I2S_CONFIG_MCK_PIN,                      \
    .sdout_pin    = NRFX_I2S_CONFIG_SDOUT_PIN,                    \
    .sdin_pin     = NRFX_I2S_CONFIG_SDIN_PIN,                     \
    .irq_priority = NRFX_I2S_CONFIG_IRQ_PRIORITY,                 \
    .mode         = (nrf_i2s_mode_t)NRFX_I2S_CONFIG_MASTER,       \
    .format       = (nrf_i2s_format_t)NRFX_I2S_CONFIG_FORMAT,     \
    .alignment    = (nrf_i2s_align_t)NRFX_I2S_CONFIG_ALIGN,       \
    .sample_width = (nrf_i2s_swidth_t)NRFX_I2S_CONFIG_SWIDTH,     \
    .channels     = (nrf_i2s_channels_t)NRFX_I2S_CONFIG_CHANNELS, \
    .mck_setup    = (nrf_i2s_mck_t)NRFX_I2S_CONFIG_MCK_SETUP,     \
    .ratio        = (nrf_i2s_ratio_t)NRFX_I2S_CONFIG_RATIO,       \
}

NRFX_I2S_CONFIG_LRCK_PIN : 配置LRCK管脚

#define NRFX_I2S_CONFIG_LRCK_PIN 30:系统默认配置为30

LRCK:字段选择信号WS,也叫LRCLK,用于切换左右声道的数据。WS的频率 = 采样频率。


    nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;
    // In Master mode the MCK frequency and the MCK/LRCK ratio should be
    // set properly in order to achieve desired audio sample rate (which
    // is equivalent to the LRCK frequency).
    // For the following settings we'll get the LRCK frequency equal to
    // 15873 Hz (the closest one to 16 kHz that is possible to achieve).
    config.sdin_pin  = I2S_SDIN_PIN;
    config.sdout_pin = I2S_SDOUT_PIN;
    config.mck_pin = 29;
    config.mck_setup = NRF_I2S_MCK_32MDIV21;
    config.ratio     = NRF_I2S_RATIO_96X;
    config.channels  = NRF_I2S_CHANNELS_STEREO;
    err_code = nrf_drv_i2s_init(&config, data_handler);

LRCK(采样频率)= NRF_I2S_MCK_32MDIV21/NRF_I2S_RATIO_96X = 32Mhz/21/96 = 15873Hz ≈16K

NRFX_I2S_CONFIG_SCK_PIN  :配置SCK的管脚

#define NRFX_I2S_CONFIG_SCK_PIN 31:系统默认配置为31

SCK:也叫位时钟BCLK。对应数字音频的每一位数据,SCK都有1个脉冲。SCK的频率 = 声道数 * 采样频率 * 采样位数。这里的声道数为2

默认配置的为右边声道,16位采样数。
// <0=> Stereo 
// <1=> Left 
// <2=> Right 

#ifndef NRFX_I2S_CONFIG_CHANNELS
#define NRFX_I2S_CONFIG_CHANNELS 1
#endif

// <0=> 8 
// <1=> 16 
// <2=> 24 

#ifndef NRFX_I2S_CONFIG_SWIDTH
#define NRFX_I2S_CONFIG_SWIDTH 1
#endif

SCK = 2 *16*16 = 512 Khz

.mck_pin      = NRFX_I2S_CONFIG_MCK_PIN,                      \

// <o> NRFX_I2S_CONFIG_MCK_PIN - MCK pin 
#ifndef NRFX_I2S_CONFIG_MCK_PIN
#define NRFX_I2S_CONFIG_MCK_PIN 255
#endif

MCK,主时钟,也叫作系统时钟,是采样频率的256倍、384倍、512倍或者768倍,频率范围再0.256~16MHz。

这里指定为 PIN255  (我给他改成29口输出)并观察

MCK的频率是之前配置的 NRF_I2S_MCK_32MDIV21 对应的是1.52Mhz

或者用 LRCK*CONGIFG  = 16*96=1536Khz = 1.536Mhz

 

    .sdout_pin    = NRFX_I2S_CONFIG_SDOUT_PIN,                    \
    .sdin_pin     = NRFX_I2S_CONFIG_SDIN_PIN,                     \

配置I²S的数据输入输出管脚

这里配置输出为 27管脚可以捕获到有电平变化输出

输入管脚是作为数据输入端没有电平变化 

.irq_priority = NRFX_I2S_CONFIG_IRQ_PRIORITY,                 \中断的优先级

.mode         = (nrf_i2s_mode_t)NRFX_I2S_CONFIG_MASTER,       \ 配置为主机模式

\配置为I2S通信他还支左对齐或右对齐格式    

.format       = (nrf_i2s_format_t)NRFX_I2S_CONFIG_FORMAT,    

\ 数据左对格式上面已经配置为标准的I2S格式所有这里没有起效
 .alignment    = (nrf_i2s_align_t)NRFX_I2S_CONFIG_ALIGN,  

\ 配置位宽默认16bits  
.sample_width = (nrf_i2s_swidth_t)NRFX_I2S_CONFIG_SWIDTH,  

\默认配置声道为左声道 一个LRCLK周期(1/Fs)包括发送左声道和右声道数据。
.channels     = (nrf_i2s_channels_t)NRFX_I2S_CONFIG_CHANNELS

 \MCK的设置
 .mck_setup    = (nrf_i2s_mck_t)NRFX_I2S_CONFIG_MCK_SETUP,  

\比特率
.ratio        = (nrf_i2s_ratio_t)NRFX_I2S_CONFIG_RATIO,  

4.6.3 官方例程分析

官方提供了一个I2S的回环数据传输,下面分析是如何实现双缓冲的乒乓结构

加了分析注释的源代码

  1. #include <stdio.h>
  2. #include "nrf_drv_i2s.h"
  3. #include "nrf_delay.h"
  4. #include "app_util_platform.h"
  5. #include "app_error.h"
  6. #include "boards.h"
  7. #include "nrf_log.h"
  8. #include "nrf_log_ctrl.h"
  9. #include "nrf_log_default_backends.h"
  10. #define LED_OK BSP_BOARD_LED_0
  11. #define LED_ERROR BSP_BOARD_LED_1
  12. #define I2S_DATA_BLOCK_WORDS 512 //一个块的大小
  13. static uint32_t m_buffer_rx[2][I2S_DATA_BLOCK_WORDS];
  14. static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];
  15. // Delay time between consecutive I2S transfers performed in the main loop
  16. // (in milliseconds).
  17. #define PAUSE_TIME 500
  18. // Number of blocks of data to be contained in each transfer.
  19. #define BLOCKS_TO_TRANSFER 20 //传多少个块
  20. static uint8_t volatile m_blocks_transferred = 0; //有多少个块被传输
  21. static uint8_t m_zero_samples_to_ignore = 0; //开始传输时会先传两个字节全0的数据流,并非我们要发送的数据
  22. static uint16_t m_sample_value_to_send;
  23. static uint16_t m_sample_value_expected;
  24. static bool m_error_encountered;
  25. static uint32_t * volatile mp_block_to_fill = NULL;
  26. static uint32_t const * volatile mp_block_to_check = NULL;
  27. static void prepare_tx_data(uint32_t * p_block)
  28. {
  29. // These variables will be both zero only at the very beginning of each
  30. // transfer, so we use them as the indication that the re-initialization
  31. // should be performed.
  32. if (m_blocks_transferred == 0 && m_zero_samples_to_ignore == 0)
  33. {
  34. // Number of initial samples (actually pairs of L/R samples) with zero
  35. // values that should be ignored - see the comment in 'check_samples'.
  36. m_zero_samples_to_ignore = 2;
  37. m_sample_value_to_send = 0xCAFE;
  38. m_sample_value_expected = 0xCAFE;
  39. m_error_encountered = false;
  40. }
  41. // [each data word contains two 16-bit samples]
  42. uint16_t i;
  43. for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
  44. {
  45. uint16_t sample_l = m_sample_value_to_send - 1;
  46. uint16_t sample_r = m_sample_value_to_send + 1;
  47. ++m_sample_value_to_send;
  48. uint32_t * p_word = &p_block[i];
  49. ((uint16_t *)p_word)[0] = sample_l;
  50. ((uint16_t *)p_word)[1] = sample_r;
  51. }
  52. }
  53. static bool check_samples(uint32_t const * p_block)
  54. {
  55. // [each data word contains two 16-bit samples]
  56. uint16_t i;
  57. for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
  58. {
  59. uint32_t const * p_word = &p_block[i];
  60. uint16_t actual_sample_l = ((uint16_t const *)p_word)[0];
  61. uint16_t actual_sample_r = ((uint16_t const *)p_word)[1];
  62. // Normally a couple of initial samples sent by the I2S peripheral
  63. // will have zero values, because it starts to output the clock
  64. // before the actual data is fetched by EasyDMA. As we are dealing
  65. // with streaming the initial zero samples can be simply ignored.
  66. if (m_zero_samples_to_ignore > 0 &&
  67. actual_sample_l == 0 &&
  68. actual_sample_r == 0)
  69. {
  70. --m_zero_samples_to_ignore;
  71. }
  72. else
  73. {
  74. m_zero_samples_to_ignore = 0;
  75. uint16_t expected_sample_l = m_sample_value_expected - 1;
  76. uint16_t expected_sample_r = m_sample_value_expected + 1;
  77. ++m_sample_value_expected;
  78. if (actual_sample_l != expected_sample_l ||
  79. actual_sample_r != expected_sample_r)
  80. {
  81. NRF_LOG_INFO("%3u: %04x/%04x, expected: %04x/%04x (i: %u)",
  82. m_blocks_transferred, actual_sample_l, actual_sample_r,
  83. expected_sample_l, expected_sample_r, i);
  84. return false;
  85. }
  86. }
  87. }
  88. NRF_LOG_INFO("%3u: OK", m_blocks_transferred);
  89. return true;
  90. }
  91. static void check_rx_data(uint32_t const * p_block)
  92. {
  93. ++m_blocks_transferred;
  94. if (!m_error_encountered)
  95. {
  96. m_error_encountered = !check_samples(p_block);
  97. }
  98. if (m_error_encountered)
  99. {
  100. bsp_board_led_off(LED_OK);
  101. bsp_board_led_invert(LED_ERROR);
  102. }
  103. else
  104. {
  105. bsp_board_led_off(LED_ERROR);
  106. bsp_board_led_invert(LED_OK);
  107. }
  108. }
  109. static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
  110. uint32_t status)
  111. {
  112. // 'nrf_drv_i2s_next_buffers_set' is called directly from the handler
  113. // each time next buffers are requested, so data corruption is not
  114. // expected.
  115. ASSERT(p_released);
  116. // When the handler is called after the transfer has been stopped
  117. // (no next buffers are needed, only the used buffers are to be
  118. // released), there is nothing to do.
  119. if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
  120. {
  121. NRF_LOG_INFO("----STOP-----");
  122. return;
  123. }
  124. //p_released 指向结构的指针,该结构指向以前传递给驱动程序的缓冲区的指针,
  125. //该驱动程序将不再被它访问(现在可以安全地释放它们或将其用于其他目的,特别是用于传输的下一部分)
  126. // First call of this handler occurs right after the transfer is started.
  127. // No data has been transferred yet at this point, so there is nothing to
  128. // check. Only the buffers for the next part of the transfer should be
  129. // provided.
  130. if (!p_released->p_rx_buffer)
  131. {
  132. //在开始传输后首次调用处理程序时,此结构中的两个指针均为 NULL,因为此时尚未传输任何数据。
  133. //此时p_released = NULL,下一个buff指向m_buffer_tx[1]。
  134. NRF_LOG_INFO("----DONE-----");
  135. nrf_drv_i2s_buffers_t const next_buffers = {
  136. .p_rx_buffer = m_buffer_rx[1],
  137. .p_tx_buffer = m_buffer_tx[1],
  138. };
  139. //指定下一个buff
  140. APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
  141. mp_block_to_fill = m_buffer_tx[1];
  142. }
  143. else
  144. {
  145. if(p_released->p_tx_buffer == m_buffer_tx[0])
  146. {
  147. NRF_LOG_INFO("-----m_buffer_tx[0]-----");
  148. }
  149. else if(p_released->p_tx_buffer == m_buffer_tx[1])
  150. {
  151. NRF_LOG_INFO("-----m_buffer_tx[1]-----");
  152. }
  153. //在所有连续调用中,指针指定刚刚完成的传输部分中已发送的内容 (TX) 和已接收的内容 (RX)
  154. NRF_LOG_INFO("----NEXT-----");
  155. mp_block_to_check = p_released->p_rx_buffer;
  156. // The driver has just finished accessing the buffers pointed by
  157. // 'p_released'. They can be used for the next part of the transfer
  158. // that will be scheduled now.
  159. //此时m_buffer_tx[0] 传输完毕,p_released指向的是m_buffer_tx[0],下一个buff指向m_buffer_tx[0]。
  160. APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
  161. // The pointer needs to be typecasted here, so that it is possible to
  162. // modify the content it is pointing to (it is marked in the structure
  163. // as pointing to constant data because the driver is not supposed to
  164. // modify the provided data).
  165. mp_block_to_fill = (uint32_t *)p_released->p_tx_buffer;
  166. }
  167. }
  168. void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
  169. {
  170. bsp_board_leds_on();
  171. app_error_save_and_stop(id, pc, info);
  172. }
  173. int main(void)
  174. {
  175. uint32_t err_code = NRF_SUCCESS;
  176. bsp_board_init(BSP_INIT_LEDS);
  177. err_code = NRF_LOG_INIT(NULL);
  178. APP_ERROR_CHECK(err_code);
  179. NRF_LOG_DEFAULT_BACKENDS_INIT();
  180. NRF_LOG_INFO("#########################1");
  181. nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;
  182. // In Master mode the MCK frequency and the MCK/LRCK ratio should be
  183. // set properly in order to achieve desired audio sample rate (which
  184. // is equivalent to the LRCK frequency).
  185. // For the following settings we'll get the LRCK frequency equal to
  186. // 15873 Hz (the closest one to 16 kHz that is possible to achieve).
  187. config.sdin_pin = I2S_SDIN_PIN;
  188. config.sdout_pin = I2S_SDOUT_PIN;
  189. config.mck_setup = NRF_I2S_MCK_32MDIV21;
  190. config.ratio = NRF_I2S_RATIO_96X;
  191. config.channels = NRF_I2S_CHANNELS_STEREO;
  192. err_code = nrf_drv_i2s_init(&config, data_handler);
  193. APP_ERROR_CHECK(err_code);
  194. for (;;)
  195. {
  196. NRF_LOG_INFO("#########################2");
  197. m_blocks_transferred = 0;
  198. mp_block_to_fill = NULL;
  199. mp_block_to_check = NULL;
  200. prepare_tx_data(m_buffer_tx[0]);
  201. nrf_drv_i2s_buffers_t const initial_buffers = {
  202. .p_tx_buffer = m_buffer_tx[0],
  203. .p_rx_buffer = m_buffer_rx[0],
  204. };
  205. err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
  206. APP_ERROR_CHECK(err_code);
  207. do {
  208. // Wait for an event.
  209. __WFE();
  210. // Clear the event register.
  211. __SEV();
  212. __WFE();
  213. if (mp_block_to_fill)
  214. {
  215. prepare_tx_data(mp_block_to_fill);
  216. mp_block_to_fill = NULL;
  217. }
  218. if (mp_block_to_check)
  219. {
  220. check_rx_data(mp_block_to_check);
  221. mp_block_to_check = NULL;
  222. }
  223. } while (m_blocks_transferred < BLOCKS_TO_TRANSFER);
  224. nrf_drv_i2s_stop();
  225. NRF_LOG_FLUSH();
  226. bsp_board_leds_off();
  227. nrf_delay_ms(PAUSE_TIME);
  228. }
  229. }
  230. /** @} */

 输出结果

 分析

 nRF5 SDK v15.3.0: I2S Loopback Example

nRF52832 — 使用nRF52832的I2S播放音频_52832 蓝牙语音_文化人Sugar的博客-CSDN博客

一文搞懂I2S通信总线_不脱发的程序猿的博客-CSDN博客

【通信协议】I2S/IIS总线介绍_iis接口_努力努力再努力~~的博客-CSDN博客

I2S的理解_i2s通信的详细讲解_wholetus的博客-CSDN博客

4.6.4 通过D类功放生成波形

三角波,正弦波,方波数据通过I2S传输到MAX98357生成对应波形。

  1. #define PIN_MCK (13)
  2. #define PIN_SCK (14)
  3. #define PIN_LRCK (15)
  4. #define PIN_SDOUT (16)
  5. void sin_text();
  6. int main(void)
  7. {
  8. sin_text();
  9. }
  10. void sin_text()
  11. {
  12. // Sine table (stereo, so every value is duplicated)
  13. int16_t sine_table[] = {
  14. 2047,2447,2831,3185,3498,3750,3939,
  15. 4056,4095,4056,3939,3758,3495,3185,
  16. 2831,2447,2047,1647,1263,909,599,344,
  17. 155,38,0,38,155,344,599,909,1263,1647};
  18. int16_t triangle_table[] = {
  19. 0,256,512,768,1024,1279,1535,1791,
  20. 2047,2303,2559,2815,3071,3326,3582,3838,
  21. 4095,3838,3582,3326,3071,2815,2559,2303,
  22. 2047,1791,1535,1279,1024,768,512,256};
  23. int16_t square_table[] = {
  24. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  25. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  26. 4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,
  27. 4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,4095,};
  28. // Enable transmission
  29. NRF_I2S->CONFIG.TXEN = (I2S_CONFIG_TXEN_TXEN_ENABLE << I2S_CONFIG_TXEN_TXEN_Pos);
  30. // Enable MCK generator
  31. NRF_I2S->CONFIG.MCKEN = (I2S_CONFIG_MCKEN_MCKEN_ENABLE << I2S_CONFIG_MCKEN_MCKEN_Pos);
  32. // MCKFREQ = 4 MHz
  33. NRF_I2S->CONFIG.MCKFREQ = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV11 << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos;
  34. // Ratio = 64
  35. NRF_I2S->CONFIG.RATIO = I2S_CONFIG_RATIO_RATIO_64X << I2S_CONFIG_RATIO_RATIO_Pos;
  36. // Master mode, 16Bit, left aligned
  37. NRF_I2S->CONFIG.MODE = I2S_CONFIG_MODE_MODE_MASTER << I2S_CONFIG_MODE_MODE_Pos;
  38. NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16BIT << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
  39. NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_LEFT << I2S_CONFIG_ALIGN_ALIGN_Pos;
  40. // Format = I2S
  41. NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos;
  42. // Use stereo
  43. NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_STEREO << I2S_CONFIG_CHANNELS_CHANNELS_Pos;
  44. // Configure pins
  45. NRF_I2S->PSEL.MCK = (PIN_MCK << I2S_PSEL_MCK_PIN_Pos);
  46. NRF_I2S->PSEL.SCK = (PIN_SCK << I2S_PSEL_SCK_PIN_Pos);
  47. NRF_I2S->PSEL.LRCK = (PIN_LRCK << I2S_PSEL_LRCK_PIN_Pos);
  48. NRF_I2S->PSEL.SDOUT = (PIN_SDOUT << I2S_PSEL_SDOUT_PIN_Pos);
  49. NRF_I2S->ENABLE = 1;
  50. // Configure data pointer
  51. NRF_I2S->TXD.PTR = (uint32_t)&square_table[0];
  52. NRF_I2S->RXTXD.MAXCNT = sizeof(square_table) / sizeof(uint32_t);
  53. // Start transmitting I2S data
  54. NRF_I2S->TASKS_START = 1;
  55. // Since we are not updating the TXD pointer, the sine wave will play over and over again.
  56. // The TXD pointer can be updated after the EVENTS_TXPTRUPD arrives.
  57. while (1)
  58. {
  59. __WFE();
  60. }
  61. }
  • D类功放输出的是包含音频信息的调制方波,而非直接的音频波形数据所以示波器无法直接看输出的波形。详见

  • 所以我是通过音频采集器用AU来采集
  • 方波
  • 三角波
  • 正弦波 

相关文章

可生成采样率对照表

生成44.1Khz

可参考案例

原始数据不能用const修饰,因为EDMA只能读取内存中的数据,不能读取FLASH中的

输中使用的uint32_t常量指针类型,不能使用的uint8_t常量指针类型

I2S模块为双缓冲,可以在写入电流的同时准备下一个缓冲器,每个缓冲器的最大大小为8192bytes

通过nRF52840 I2S传输正弦波,使用简单的DMA和MAX98357A编解码器 - 北欧问答 - 北欧开发区 - 北欧开发区 (nordicsemi.com)

4.6.5 通过D类功播放音乐

这个折腾了2天参照例程和资料

  • 截取了一段1S钟16位44.1Khz的WAV音频数据;
  • 用的I2S的双缓冲,这里我设置为10K的缓冲太小了会卡顿。
  1. #include "wav.h" //wav数组
  2. #define I2S_DATA_BLOCK_WORDS 10*1024 //buff大小
  3. static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];
  4. static uint32_t * volatile mp_block_to_fill = NULL;
  5. uint32_t length;
  6. static void prepare_tx_data(uint32_t * p_block)
  7. {
  8. memcpy(p_block,wave_data+length,I2S_DATA_BLOCK_WORDS);
  9. length +=I2S_DATA_BLOCK_WORDS;
  10. if(length >= 39396) length = 0;
  11. }
  12. static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
  13. uint32_t status)
  14. {
  15. ASSERT(p_released);
  16. if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
  17. {
  18. return;
  19. }
  20. if (!p_released->p_tx_buffer)
  21. {
  22. nrf_drv_i2s_buffers_t const next_buffers = {
  23. .p_tx_buffer = m_buffer_tx[1],
  24. };
  25. APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
  26. mp_block_to_fill = m_buffer_tx[1];
  27. }
  28. else
  29. {
  30. APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
  31. mp_block_to_fill = (uint32_t *)p_released->p_tx_buffer;
  32. }
  33. }
  34. void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
  35. {
  36. bsp_board_leds_on();
  37. app_error_save_and_stop(id, pc, info);
  38. }
  39. void sin_text();
  40. #define PIN_MCK (13)
  41. #define PIN_SCK (14)
  42. #define PIN_LRCK (15)
  43. #define PIN_SDOUT (16)
  44. #define I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV3 (0x50000000UL)
  45. int main(void)
  46. {
  47. #if 1
  48. uint32_t err_code = NRF_SUCCESS;
  49. bsp_board_init(BSP_INIT_LEDS);
  50. err_code = NRF_LOG_INIT(NULL);
  51. APP_ERROR_CHECK(err_code);
  52. NRF_LOG_DEFAULT_BACKENDS_INIT();
  53. nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;
  54. config.sck_pin = PIN_SCK;
  55. config.lrck_pin = PIN_LRCK;
  56. config.sdout_pin = PIN_SDOUT;
  57. config.mode = NRF_I2S_MODE_MASTER;
  58. config.format = NRF_I2S_FORMAT_I2S;
  59. config.alignment = NRF_I2S_ALIGN_LEFT;
  60. config.sample_width = NRF_I2S_SWIDTH_16BIT;
  61. config.channels = NRF_I2S_CHANNELS_LEFT;
  62. config.mck_setup = I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV3;
  63. config.ratio = NRF_I2S_RATIO_256X;
  64. err_code = nrf_drv_i2s_init(&config, data_handler);
  65. APP_ERROR_CHECK(err_code);
  66. mp_block_to_fill = NULL;
  67. prepare_tx_data(m_buffer_tx[0]);
  68. nrf_drv_i2s_buffers_t const initial_buffers = {.p_tx_buffer = m_buffer_tx[0]};
  69. err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
  70. APP_ERROR_CHECK(err_code);
  71. for (;;)
  72. {
  73. // Wait for an event.
  74. __WFE();
  75. // Clear the event register.
  76. __SEV();
  77. __WFE();
  78. if (mp_block_to_fill)
  79. {
  80. prepare_tx_data(mp_block_to_fill);
  81. mp_block_to_fill = NULL;
  82. }
  83. }
  84. #endif
  85. }

 原始音频波形

 生成的

4.7 SAADC
4.7.1 框图

4.7.2 主要配置项 

①:采样模式

②:信号增益

③:参考电压

④:采样精度

⑤:工作模式

  结合PPI,双Buff,EDMA来运行;

nRF52832学习记录

实战经验,Nordic 52832 低功耗模式与唤醒机制 (360doc.com)

STM32Cube MX USB双设备MSC+CDC 实现虚拟U盘+虚拟串口_stm32 usb虚拟串口和u盘_Pzkkkkk的博客-CSDN博客

4.8 读取HT11温度
4.8.1 DHT11单总线时序

 

 

4.8.2 DHT11驱动代码
  1. #include "dht11.h"
  2. #include "nrf_delay.h"
  3. /*
  4. *开始信号
  5. *开始信号输出后从机会回应一个应答信号
  6. */
  7. void dht_Rst(void)
  8. {
  9. dht_IO_OUT(); //IO口配置为输出
  10. dht_data_OUT_0; //输出0
  11. nrf_delay_ms(25); //输出25ms
  12. dht_data_OUT_1; //输出1
  13. nrf_delay_us(30); //输出30us
  14. }
  15. /*
  16. *检测应答信号
  17. *正确应答返回0错误应答返回1
  18. */
  19. uint8_t dht_Check(void)
  20. {
  21. uint8_t time=0;
  22. dht_IO_IN(); //输入模式
  23. while (dht_data_IN&&time<100) //输入电平为低且时机小于100us为应答信号
  24. {
  25. time++;
  26. nrf_delay_us(1);
  27. }
  28. if(time>=100) //时间大于100us应答错误
  29. {
  30. printf("error\r\n");
  31. return 1;
  32. }
  33. else time=0;
  34. while (!dht_data_IN&&time<100)//输入电平为高且时机小于100us为数据开始传输
  35. {
  36. time++;
  37. nrf_delay_us(1);
  38. }
  39. if(time>=100)
  40. {
  41. printf("error 2\r\n");
  42. return 1;
  43. }
  44. return 0; //数据开始传输
  45. }
  46. /*
  47. *读取一个bit的数据
  48. */
  49. uint8_t dht_Read_Bit(void)
  50. {
  51. uint8_t time=0;
  52. while(dht_data_IN&&time<100) //数据输的开始位为低电平(50us)
  53. {
  54. time++;
  55. nrf_delay_us(1);
  56. }
  57. time=0;
  58. while(!dht_data_IN&&time<100) //开始读取高电平时间,判断数据为1还是0
  59. {
  60. time++;
  61. nrf_delay_us(1);
  62. }
  63. nrf_delay_us(40); //延迟70毫秒后若还为高电平则为1否则为0
  64. if(dht_data_IN)return 1;
  65. else return 0;
  66. }
  67. /*
  68. *读取一个字节的数据
  69. */
  70. uint8_t dht_Read_Byte(void)
  71. {
  72. uint8_t i,dat;
  73. dat=0;
  74. for (i=0;i<8;i++)
  75. {
  76. dat<<=1;
  77. dat|=dht_Read_Bit();
  78. }
  79. return dat;
  80. }
  81. uint8_t dht_Read_Data(uint8_t *temp,uint8_t *humi)
  82. {
  83. uint8_t dat[5];
  84. uint8_t i;
  85. dht_Rst();
  86. if(dht_Check()==0)
  87. {
  88. for(i=0;i<5;i++)
  89. {
  90. dat[i]=dht_Read_Byte();
  91. }
  92. if((dat[0]+dat[1]+dat[2]+dat[3])==dat[4]) //校验
  93. {
  94. *humi=dat[0]; //温度整数位
  95. *temp=dat[2]; //湿度整数位
  96. }
  97. }else return 1;
  98. return 0;
  99. }
  100. uint8_t dht_Init(void)
  101. {
  102. dht_Rst(); //开始信号
  103. return dht_Check(); //检测是否有响应
  104. }
4.8.3 主程序
  1. int main(void)
  2. {
  3. uint32_t err_code;
  4. bsp_board_init(BSP_INIT_LEDS);
  5. const app_uart_comm_params_t comm_params =
  6. {
  7. RX_PIN_NUMBER,
  8. TX_PIN_NUMBER,
  9. RTS_PIN_NUMBER,
  10. CTS_PIN_NUMBER,
  11. UART_HWFC,
  12. false,
  13. #if defined (UART_PRESENT)
  14. NRF_UART_BAUDRATE_115200
  15. #else
  16. NRF_UARTE_BAUDRATE_115200
  17. #endif
  18. };
  19. APP_UART_FIFO_INIT(&comm_params,
  20. UART_RX_BUF_SIZE,
  21. UART_TX_BUF_SIZE,
  22. uart_error_handle,
  23. APP_IRQ_PRIORITY_LOWEST,
  24. err_code);
  25. APP_ERROR_CHECK(err_code);
  26. #ifndef ENABLE_LOOPBACK_TEST
  27. int32_t timval;
  28. timer0_init();
  29. timer1_init();
  30. timer2_init();
  31. ppi_init();
  32. //启动定时器0
  33. nrf_drv_timer_enable(&timer0);
  34. while(dht_Init())
  35. {
  36. nrf_delay_ms(1000);
  37. }
  38. printf("succed\r\n");
  39. uint8_t temp;
  40. uint8_t humi;
  41. while (true)
  42. {
  43. // //计数器加1
  44. // nrfx_timer_increment(&timer0);
  45. // //获取计数值
  46. // timval = nrfx_timer_capture(&timer0,NRF_TIMER_CC_CHANNEL0);
  47. // printf("count value:%d\r\n",timval);
  48. bsp_board_led_invert(1);
  49. dht_Read_Data(&temp,&humi);
  50. printf("t:%d h:%d\r\n",temp,humi);
  51. nrf_delay_ms(1048);
  52. }
  53. #else
  54. // This part of the example is just for testing the loopback .
  55. while (true)
  56. {
  57. uart_loopback_test();
  58. }
  59. #endif
  60. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/194249
推荐阅读
相关标签
  

闽ICP备14008679号